diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 590d7834b2b8be..9f808af38e69df 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -6,7 +6,7 @@ ENV WASI_SDK_VERSION=20 ENV WASI_SDK_PATH=/opt/wasi-sdk ENV WASMTIME_HOME=/opt/wasmtime -ENV WASMTIME_VERSION=9.0.1 +ENV WASMTIME_VERSION=14.0.4 ENV WASMTIME_CPU_ARCH=x86_64 RUN dnf -y --nodocs --setopt=install_weak_deps=False install /usr/bin/{blurb,clang,curl,git,ln,tar,xz} 'dnf-command(builddep)' && \ diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1925363cbeb46e..aa6d937d9cbc31 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -190,3 +190,7 @@ Doc/howto/clinic.rst @erlend-aasland # WebAssembly /Tools/wasm/ @brettcannon + +# SBOM +/Misc/sbom.spdx.json @sethmlarson +/Tools/build/generate_sbom.py @sethmlarson diff --git a/.github/workflows/add-issue-header.yml b/.github/workflows/add-issue-header.yml index 1ef9178b95e5f6..570b8779994a0f 100644 --- a/.github/workflows/add-issue-header.yml +++ b/.github/workflows/add-issue-header.yml @@ -19,7 +19,7 @@ jobs: permissions: issues: write steps: - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 with: # language=JavaScript script: | diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4210194b978749..cfb36c8c32e18d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -190,7 +190,7 @@ jobs: build_windows_free_threaded: name: 'Windows (free-threaded)' needs: check_source - if: needs.check_source.outputs.run_tests == 'true' && contains(github.event.pull_request.labels.*.name, 'topic-free-threaded') + if: needs.check_source.outputs.run_tests == 'true' uses: ./.github/workflows/reusable-windows.yml with: free-threaded: true @@ -206,7 +206,7 @@ jobs: build_macos_free_threaded: name: 'macOS (free-threaded)' needs: check_source - if: needs.check_source.outputs.run_tests == 'true' && contains(github.event.pull_request.labels.*.name, 'topic-free-threaded') + if: needs.check_source.outputs.run_tests == 'true' uses: ./.github/workflows/reusable-macos.yml with: config_hash: ${{ needs.check_source.outputs.config_hash }} @@ -228,7 +228,7 @@ jobs: build_ubuntu_free_threaded: name: 'Ubuntu (free-threaded)' needs: check_source - if: needs.check_source.outputs.run_tests == 'true' && contains(github.event.pull_request.labels.*.name, 'topic-free-threaded') + if: needs.check_source.outputs.run_tests == 'true' uses: ./.github/workflows/reusable-ubuntu.yml with: config_hash: ${{ needs.check_source.outputs.config_hash }} @@ -521,10 +521,7 @@ jobs: uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe with: allowed-failures: >- - build_macos_free_threaded, - build_ubuntu_free_threaded, build_ubuntu_ssltests, - build_windows_free_threaded, cifuzz, test_hypothesis, allowed-skips: >- diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 405511ca6820b3..792903a90a4880 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -8,6 +8,8 @@ on: pull_request: paths: - ".github/workflows/mypy.yml" + - "Lib/test/libregrtest/**" + - "Tools/build/generate_sbom.py" - "Tools/cases_generator/**" - "Tools/clinic/**" - "Tools/peg_generator/**" @@ -32,6 +34,8 @@ jobs: strategy: matrix: target: [ + "Lib/test/libregrtest", + "Tools/build/", "Tools/cases_generator", "Tools/clinic", "Tools/peg_generator", diff --git a/.github/workflows/new-bugs-announce-notifier.yml b/.github/workflows/new-bugs-announce-notifier.yml index 4599b21ef35f05..9f1a8a824e5f19 100644 --- a/.github/workflows/new-bugs-announce-notifier.yml +++ b/.github/workflows/new-bugs-announce-notifier.yml @@ -18,7 +18,7 @@ jobs: node-version: 20 - run: npm install mailgun.js form-data - name: Send notification - uses: actions/github-script@v6 + uses: actions/github-script@v7 env: MAILGUN_API_KEY: ${{ secrets.MAILGUN_PYTHON_ORG_MAILGUN_KEY }} with: diff --git a/.gitignore b/.gitignore index 8c8273fc7a3aa3..c424a894c2a6e0 100644 --- a/.gitignore +++ b/.gitignore @@ -125,6 +125,7 @@ Tools/unicode/data/ /config.status.lineno # hendrikmuhs/ccache-action@v1 /.ccache +/cross-build/ /platform /profile-clean-stamp /profile-run-stamp diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 62d87d898e682c..834aae9372fe3b 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -413,7 +413,7 @@ API Functions than a variable number of arguments. -.. c:function:: int PyArg_ParseTupleAndKeywords(PyObject *args, PyObject *kw, const char *format, char *keywords[], ...) +.. c:function:: int PyArg_ParseTupleAndKeywords(PyObject *args, PyObject *kw, const char *format, char * const *keywords, ...) Parse the parameters of a function that takes both positional and keyword parameters into local variables. @@ -424,15 +424,24 @@ API Functions Returns true on success; on failure, it returns false and raises the appropriate exception. + .. note:: + + The *keywords* parameter declaration is :c:expr:`char * const *` in C and + :c:expr:`const char * const *` in C++. + This can be overridden with the :c:macro:`PY_CXX_CONST` macro. + .. versionchanged:: 3.6 Added support for :ref:`positional-only parameters `. .. versionchanged:: 3.13 + The *keywords* parameter has now type :c:expr:`char * const *` in C and + :c:expr:`const char * const *` in C++, instead of :c:expr:`char **`. Added support for non-ASCII keyword parameter names. -.. c:function:: int PyArg_VaParseTupleAndKeywords(PyObject *args, PyObject *kw, const char *format, char *keywords[], va_list vargs) + +.. c:function:: int PyArg_VaParseTupleAndKeywords(PyObject *args, PyObject *kw, const char *format, char * const *keywords, va_list vargs) Identical to :c:func:`PyArg_ParseTupleAndKeywords`, except that it accepts a va_list rather than a variable number of arguments. @@ -505,6 +514,19 @@ API Functions PyArg_ParseTuple(args, "O|O:ref", &object, &callback) +.. c:macro:: PY_CXX_CONST + + The value to be inserted, if any, before :c:expr:`char * const *` + in the *keywords* parameter declaration of + :c:func:`PyArg_ParseTupleAndKeywords` and + :c:func:`PyArg_VaParseTupleAndKeywords`. + Default empty for C and ``const`` for C++ + (:c:expr:`const char * const *`). + To override, define it to the desired value before including + :file:`Python.h`. + + .. versionadded:: 3.13 + --------------- Building values diff --git a/Doc/c-api/frame.rst b/Doc/c-api/frame.rst index 1accee2767a485..6bb1e9b5803b58 100644 --- a/Doc/c-api/frame.rst +++ b/Doc/c-api/frame.rst @@ -50,7 +50,7 @@ See also :ref:`Reflection `. .. c:function:: PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) - Get the *frame*'s ``f_builtins`` attribute. + Get the *frame*'s :attr:`~frame.f_builtins` attribute. Return a :term:`strong reference`. The result cannot be ``NULL``. @@ -81,7 +81,7 @@ See also :ref:`Reflection `. .. c:function:: PyObject* PyFrame_GetGlobals(PyFrameObject *frame) - Get the *frame*'s ``f_globals`` attribute. + Get the *frame*'s :attr:`~frame.f_globals` attribute. Return a :term:`strong reference`. The result cannot be ``NULL``. @@ -90,7 +90,7 @@ See also :ref:`Reflection `. .. c:function:: int PyFrame_GetLasti(PyFrameObject *frame) - Get the *frame*'s ``f_lasti`` attribute. + Get the *frame*'s :attr:`~frame.f_lasti` attribute. Returns -1 if ``frame.f_lasti`` is ``None``. @@ -120,7 +120,7 @@ See also :ref:`Reflection `. .. c:function:: PyObject* PyFrame_GetLocals(PyFrameObject *frame) - Get the *frame*'s ``f_locals`` attribute (:class:`dict`). + Get the *frame*'s :attr:`~frame.f_locals` attribute (:class:`dict`). Return a :term:`strong reference`. diff --git a/Doc/c-api/function.rst b/Doc/c-api/function.rst index 5857dba82c11c6..0a18e63c7e7a2c 100644 --- a/Doc/c-api/function.rst +++ b/Doc/c-api/function.rst @@ -37,7 +37,7 @@ There are a few functions specific to Python functions. The function's docstring and name are retrieved from the code object. *__module__* is retrieved from *globals*. The argument defaults, annotations and closure are set to ``NULL``. *__qualname__* is set to the same value as the code object's - ``co_qualname`` field. + :attr:`~codeobject.co_qualname` field. .. c:function:: PyObject* PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname) @@ -45,7 +45,7 @@ There are a few functions specific to Python functions. As :c:func:`PyFunction_New`, but also allows setting the function object's ``__qualname__`` attribute. *qualname* should be a unicode object or ``NULL``; if ``NULL``, the ``__qualname__`` attribute is set to the same value as the - code object's ``co_qualname`` field. + code object's :attr:`~codeobject.co_qualname` field. .. versionadded:: 3.3 diff --git a/Doc/c-api/hash.rst b/Doc/c-api/hash.rst index 4dc121d7fbaa9b..91d88ae27bc9f4 100644 --- a/Doc/c-api/hash.rst +++ b/Doc/c-api/hash.rst @@ -45,4 +45,17 @@ See also the :c:member:`PyTypeObject.tp_hash` member. Get the hash function definition. + .. seealso:: + :pep:`456` "Secure and interchangeable hash algorithm". + .. versionadded:: 3.4 + + +.. c:function:: Py_hash_t Py_HashPointer(const void *ptr) + + Hash a pointer value: process the pointer value as an integer (cast it to + ``uintptr_t`` internally). The pointer is not dereferenced. + + The function cannot fail: it cannot return ``-1``. + + .. versionadded:: 3.13 diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst index 137780cc359cf9..51c20b202f091c 100644 --- a/Doc/c-api/import.rst +++ b/Doc/c-api/import.rst @@ -154,7 +154,7 @@ Importing Modules :class:`~importlib.machinery.SourceFileLoader` otherwise. The module's :attr:`__file__` attribute will be set to the code object's - :attr:`!co_filename`. If applicable, :attr:`__cached__` will also + :attr:`~codeobject.co_filename`. If applicable, :attr:`__cached__` will also be set. This function will reload the module if it was already imported. See diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index e89641f74c7491..f8fd48e781d6da 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1662,7 +1662,8 @@ Python-level trace functions in previous versions. The value passed as the *what* parameter to a :c:type:`Py_tracefunc` function (but not a profiling function) when a line-number event is being reported. - It may be disabled for a frame by setting :attr:`f_trace_lines` to *0* on that frame. + It may be disabled for a frame by setting :attr:`~frame.f_trace_lines` to + *0* on that frame. .. c:var:: int PyTrace_RETURN @@ -1694,7 +1695,7 @@ Python-level trace functions in previous versions. The value for the *what* parameter to :c:type:`Py_tracefunc` functions (but not profiling functions) when a new opcode is about to be executed. This event is not emitted by default: it must be explicitly requested by setting - :attr:`f_trace_opcodes` to *1* on the frame. + :attr:`~frame.f_trace_opcodes` to *1* on the frame. .. c:function:: void PyEval_SetProfile(Py_tracefunc func, PyObject *obj) diff --git a/Doc/c-api/refcounting.rst b/Doc/c-api/refcounting.rst index 68119a27b18ec2..75e1d46474f1e7 100644 --- a/Doc/c-api/refcounting.rst +++ b/Doc/c-api/refcounting.rst @@ -34,6 +34,9 @@ of Python objects. Set the object *o* reference counter to *refcnt*. + On :ref:`Python build with Free Threading `, if + *refcnt* is larger than ``UINT32_MAX``, the object is made :term:`immortal`. + This function has no effect on :term:`immortal` objects. .. versionadded:: 3.9 diff --git a/Doc/c-api/set.rst b/Doc/c-api/set.rst index 09c0fb6b9c5f23..cba823aa027bd6 100644 --- a/Doc/c-api/set.rst +++ b/Doc/c-api/set.rst @@ -147,7 +147,7 @@ subtypes but not for instances of :class:`frozenset` or its subtypes. Return ``1`` if found and removed, ``0`` if not found (no action taken), and ``-1`` if an error is encountered. Does not raise :exc:`KeyError` for missing keys. Raise a - :exc:`TypeError` if the *key* is unhashable. Unlike the Python :meth:`~set.discard` + :exc:`TypeError` if the *key* is unhashable. Unlike the Python :meth:`~frozenset.discard` method, this function does not automatically convert unhashable sets into temporary frozensets. Raise :exc:`SystemError` if *set* is not an instance of :class:`set` or its subtype. diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index 25cb4ed40f63e7..528813c255c0a5 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -592,7 +592,7 @@ Macro name C type Python type (*): Zero-terminated, UTF8-encoded C string. With :c:macro:`!Py_T_STRING` the C representation is a pointer; - with :c:macro:`!Py_T_STRING_INLINE` the string is stored directly + with :c:macro:`!Py_T_STRING_INPLACE` the string is stored directly in the structure. (**): String of length 1. Only ASCII is accepted. diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 10c05beda7c66f..8a26f237652d12 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -343,13 +343,13 @@ slot typedefs | | :c:type:`PyTypeObject` * | | | | :c:type:`Py_ssize_t` | | +-----------------------------+-----------------------------+----------------------+ -| :c:type:`destructor` | void * | void | +| :c:type:`destructor` | :c:type:`PyObject` * | void | +-----------------------------+-----------------------------+----------------------+ | :c:type:`freefunc` | void * | void | +-----------------------------+-----------------------------+----------------------+ | :c:type:`traverseproc` | .. line-block:: | int | | | | | -| | void * | | +| | :c:type:`PyObject` * | | | | :c:type:`visitproc` | | | | void * | | +-----------------------------+-----------------------------+----------------------+ @@ -426,7 +426,7 @@ slot typedefs | | :c:type:`PyObject` * | | | | :c:type:`Py_buffer` * | | +-----------------------------+-----------------------------+----------------------+ -| :c:type:`inquiry` | void * | int | +| :c:type:`inquiry` | :c:type:`PyObject` * | int | +-----------------------------+-----------------------------+----------------------+ | :c:type:`unaryfunc` | .. line-block:: | :c:type:`PyObject` * | | | | | diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index e654412965a727..5541eaa521803b 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -75,19 +75,19 @@ Python: The following APIs are C macros and static inlined functions for fast checks and access to internal read-only data of Unicode objects: -.. c:function:: int PyUnicode_Check(PyObject *o) +.. c:function:: int PyUnicode_Check(PyObject *obj) - Return true if the object *o* is a Unicode object or an instance of a Unicode + Return true if the object *obj* is a Unicode object or an instance of a Unicode subtype. This function always succeeds. -.. c:function:: int PyUnicode_CheckExact(PyObject *o) +.. c:function:: int PyUnicode_CheckExact(PyObject *obj) - Return true if the object *o* is a Unicode object, but not an instance of a + Return true if the object *obj* is a Unicode object, but not an instance of a subtype. This function always succeeds. -.. c:function:: int PyUnicode_READY(PyObject *o) +.. c:function:: int PyUnicode_READY(PyObject *unicode) Returns ``0``. This API is kept only for backward compatibility. @@ -97,17 +97,17 @@ access to internal read-only data of Unicode objects: This API does nothing since Python 3.12. -.. c:function:: Py_ssize_t PyUnicode_GET_LENGTH(PyObject *o) +.. c:function:: Py_ssize_t PyUnicode_GET_LENGTH(PyObject *unicode) - Return the length of the Unicode string, in code points. *o* has to be a + Return the length of the Unicode string, in code points. *unicode* has to be a Unicode object in the "canonical" representation (not checked). .. versionadded:: 3.3 -.. c:function:: Py_UCS1* PyUnicode_1BYTE_DATA(PyObject *o) - Py_UCS2* PyUnicode_2BYTE_DATA(PyObject *o) - Py_UCS4* PyUnicode_4BYTE_DATA(PyObject *o) +.. c:function:: Py_UCS1* PyUnicode_1BYTE_DATA(PyObject *unicode) + Py_UCS2* PyUnicode_2BYTE_DATA(PyObject *unicode) + Py_UCS4* PyUnicode_4BYTE_DATA(PyObject *unicode) Return a pointer to the canonical representation cast to UCS1, UCS2 or UCS4 integer types for direct character access. No checks are performed if the @@ -129,18 +129,18 @@ access to internal read-only data of Unicode objects: ``PyUnicode_WCHAR_KIND`` has been removed. -.. c:function:: int PyUnicode_KIND(PyObject *o) +.. c:function:: int PyUnicode_KIND(PyObject *unicode) Return one of the PyUnicode kind constants (see above) that indicate how many - bytes per character this Unicode object uses to store its data. *o* has to + bytes per character this Unicode object uses to store its data. *unicode* has to be a Unicode object in the "canonical" representation (not checked). .. versionadded:: 3.3 -.. c:function:: void* PyUnicode_DATA(PyObject *o) +.. c:function:: void* PyUnicode_DATA(PyObject *unicode) - Return a void pointer to the raw Unicode buffer. *o* has to be a Unicode + Return a void pointer to the raw Unicode buffer. *unicode* has to be a Unicode object in the "canonical" representation (not checked). .. versionadded:: 3.3 @@ -168,25 +168,25 @@ access to internal read-only data of Unicode objects: .. versionadded:: 3.3 -.. c:function:: Py_UCS4 PyUnicode_READ_CHAR(PyObject *o, Py_ssize_t index) +.. c:function:: Py_UCS4 PyUnicode_READ_CHAR(PyObject *unicode, Py_ssize_t index) - Read a character from a Unicode object *o*, which must be in the "canonical" + Read a character from a Unicode object *unicode*, which must be in the "canonical" representation. This is less efficient than :c:func:`PyUnicode_READ` if you do multiple consecutive reads. .. versionadded:: 3.3 -.. c:function:: Py_UCS4 PyUnicode_MAX_CHAR_VALUE(PyObject *o) +.. c:function:: Py_UCS4 PyUnicode_MAX_CHAR_VALUE(PyObject *unicode) Return the maximum code point that is suitable for creating another string - based on *o*, which must be in the "canonical" representation. This is + based on *unicode*, which must be in the "canonical" representation. This is always an approximation but more efficient than iterating over the string. .. versionadded:: 3.3 -.. c:function:: int PyUnicode_IsIdentifier(PyObject *o) +.. c:function:: int PyUnicode_IsIdentifier(PyObject *unicode) Return ``1`` if the string is a valid identifier according to the language definition, section :ref:`identifiers`. Return ``0`` otherwise. @@ -358,9 +358,9 @@ APIs: .. versionadded:: 3.3 -.. c:function:: PyObject* PyUnicode_FromStringAndSize(const char *u, Py_ssize_t size) +.. c:function:: PyObject* PyUnicode_FromStringAndSize(const char *str, Py_ssize_t size) - Create a Unicode object from the char buffer *u*. The bytes will be + Create a Unicode object from the char buffer *str*. The bytes will be interpreted as being UTF-8 encoded. The buffer is copied into the new object. The return value might be a shared object, i.e. modification of the data is @@ -369,16 +369,16 @@ APIs: This function raises :exc:`SystemError` when: * *size* < 0, - * *u* is ``NULL`` and *size* > 0 + * *str* is ``NULL`` and *size* > 0 .. versionchanged:: 3.12 - *u* == ``NULL`` with *size* > 0 is not allowed anymore. + *str* == ``NULL`` with *size* > 0 is not allowed anymore. -.. c:function:: PyObject *PyUnicode_FromString(const char *u) +.. c:function:: PyObject *PyUnicode_FromString(const char *str) Create a Unicode object from a UTF-8 encoded null-terminated char buffer - *u*. + *str*. .. c:function:: PyObject* PyUnicode_FromFormat(const char *format, ...) @@ -646,29 +646,29 @@ APIs: .. versionadded:: 3.3 -.. c:function:: PyObject* PyUnicode_Substring(PyObject *str, Py_ssize_t start, \ +.. c:function:: PyObject* PyUnicode_Substring(PyObject *unicode, Py_ssize_t start, \ Py_ssize_t end) - Return a substring of *str*, from character index *start* (included) to + Return a substring of *unicode*, from character index *start* (included) to character index *end* (excluded). Negative indices are not supported. .. versionadded:: 3.3 -.. c:function:: Py_UCS4* PyUnicode_AsUCS4(PyObject *u, Py_UCS4 *buffer, \ +.. c:function:: Py_UCS4* PyUnicode_AsUCS4(PyObject *unicode, Py_UCS4 *buffer, \ Py_ssize_t buflen, int copy_null) - Copy the string *u* into a UCS4 buffer, including a null character, if + Copy the string *unicode* into a UCS4 buffer, including a null character, if *copy_null* is set. Returns ``NULL`` and sets an exception on error (in particular, a :exc:`SystemError` if *buflen* is smaller than the length of - *u*). *buffer* is returned on success. + *unicode*). *buffer* is returned on success. .. versionadded:: 3.3 -.. c:function:: Py_UCS4* PyUnicode_AsUCS4Copy(PyObject *u) +.. c:function:: Py_UCS4* PyUnicode_AsUCS4Copy(PyObject *unicode) - Copy the string *u* into a new UCS4 buffer that is allocated using + Copy the string *unicode* into a new UCS4 buffer that is allocated using :c:func:`PyMem_Malloc`. If this fails, ``NULL`` is returned with a :exc:`MemoryError` set. The returned buffer always has an extra null code point appended. @@ -683,7 +683,7 @@ The current locale encoding can be used to decode text from the operating system. .. c:function:: PyObject* PyUnicode_DecodeLocaleAndSize(const char *str, \ - Py_ssize_t len, \ + Py_ssize_t length, \ const char *errors) Decode a string from UTF-8 on Android and VxWorks, or from the current @@ -788,7 +788,7 @@ conversion function: Accepts a :term:`path-like object`. -.. c:function:: PyObject* PyUnicode_DecodeFSDefaultAndSize(const char *s, Py_ssize_t size) +.. c:function:: PyObject* PyUnicode_DecodeFSDefaultAndSize(const char *str, Py_ssize_t size) Decode a string from the :term:`filesystem encoding and error handler`. @@ -804,7 +804,7 @@ conversion function: handler>` is now used. -.. c:function:: PyObject* PyUnicode_DecodeFSDefault(const char *s) +.. c:function:: PyObject* PyUnicode_DecodeFSDefault(const char *str) Decode a null-terminated string from the :term:`filesystem encoding and error handler`. @@ -841,17 +841,17 @@ wchar_t Support :c:type:`wchar_t` support for platforms which support it: -.. c:function:: PyObject* PyUnicode_FromWideChar(const wchar_t *w, Py_ssize_t size) +.. c:function:: PyObject* PyUnicode_FromWideChar(const wchar_t *wstr, Py_ssize_t size) - Create a Unicode object from the :c:type:`wchar_t` buffer *w* of the given *size*. + Create a Unicode object from the :c:type:`wchar_t` buffer *wstr* of the given *size*. Passing ``-1`` as the *size* indicates that the function must itself compute the length, - using wcslen. + using :c:func:`!wcslen`. Return ``NULL`` on failure. -.. c:function:: Py_ssize_t PyUnicode_AsWideChar(PyObject *unicode, wchar_t *w, Py_ssize_t size) +.. c:function:: Py_ssize_t PyUnicode_AsWideChar(PyObject *unicode, wchar_t *wstr, Py_ssize_t size) - Copy the Unicode object contents into the :c:type:`wchar_t` buffer *w*. At most + Copy the Unicode object contents into the :c:type:`wchar_t` buffer *wstr*. At most *size* :c:type:`wchar_t` characters are copied (excluding a possibly trailing null termination character). Return the number of :c:type:`wchar_t` characters copied or ``-1`` in case of an error. Note that the resulting :c:expr:`wchar_t*` @@ -915,10 +915,10 @@ Generic Codecs These are the generic codec APIs: -.. c:function:: PyObject* PyUnicode_Decode(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_Decode(const char *str, Py_ssize_t size, \ const char *encoding, const char *errors) - Create a Unicode object by decoding *size* bytes of the encoded string *s*. + Create a Unicode object by decoding *size* bytes of the encoded string *str*. *encoding* and *errors* have the same meaning as the parameters of the same name in the :func:`str` built-in function. The codec to be used is looked up using the Python codec registry. Return ``NULL`` if an exception was raised by @@ -941,13 +941,13 @@ UTF-8 Codecs These are the UTF-8 codec APIs: -.. c:function:: PyObject* PyUnicode_DecodeUTF8(const char *s, Py_ssize_t size, const char *errors) +.. c:function:: PyObject* PyUnicode_DecodeUTF8(const char *str, Py_ssize_t size, const char *errors) Create a Unicode object by decoding *size* bytes of the UTF-8 encoded string - *s*. Return ``NULL`` if an exception was raised by the codec. + *str*. Return ``NULL`` if an exception was raised by the codec. -.. c:function:: PyObject* PyUnicode_DecodeUTF8Stateful(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeUTF8Stateful(const char *str, Py_ssize_t size, \ const char *errors, Py_ssize_t *consumed) If *consumed* is ``NULL``, behave like :c:func:`PyUnicode_DecodeUTF8`. If @@ -1004,7 +1004,7 @@ UTF-32 Codecs These are the UTF-32 codec APIs: -.. c:function:: PyObject* PyUnicode_DecodeUTF32(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeUTF32(const char *str, Py_ssize_t size, \ const char *errors, int *byteorder) Decode *size* bytes from a UTF-32 encoded buffer string and return the @@ -1031,7 +1031,7 @@ These are the UTF-32 codec APIs: Return ``NULL`` if an exception was raised by the codec. -.. c:function:: PyObject* PyUnicode_DecodeUTF32Stateful(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeUTF32Stateful(const char *str, Py_ssize_t size, \ const char *errors, int *byteorder, Py_ssize_t *consumed) If *consumed* is ``NULL``, behave like :c:func:`PyUnicode_DecodeUTF32`. If @@ -1054,7 +1054,7 @@ UTF-16 Codecs These are the UTF-16 codec APIs: -.. c:function:: PyObject* PyUnicode_DecodeUTF16(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeUTF16(const char *str, Py_ssize_t size, \ const char *errors, int *byteorder) Decode *size* bytes from a UTF-16 encoded buffer string and return the @@ -1082,7 +1082,7 @@ These are the UTF-16 codec APIs: Return ``NULL`` if an exception was raised by the codec. -.. c:function:: PyObject* PyUnicode_DecodeUTF16Stateful(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeUTF16Stateful(const char *str, Py_ssize_t size, \ const char *errors, int *byteorder, Py_ssize_t *consumed) If *consumed* is ``NULL``, behave like :c:func:`PyUnicode_DecodeUTF16`. If @@ -1105,13 +1105,13 @@ UTF-7 Codecs These are the UTF-7 codec APIs: -.. c:function:: PyObject* PyUnicode_DecodeUTF7(const char *s, Py_ssize_t size, const char *errors) +.. c:function:: PyObject* PyUnicode_DecodeUTF7(const char *str, Py_ssize_t size, const char *errors) Create a Unicode object by decoding *size* bytes of the UTF-7 encoded string - *s*. Return ``NULL`` if an exception was raised by the codec. + *str*. Return ``NULL`` if an exception was raised by the codec. -.. c:function:: PyObject* PyUnicode_DecodeUTF7Stateful(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeUTF7Stateful(const char *str, Py_ssize_t size, \ const char *errors, Py_ssize_t *consumed) If *consumed* is ``NULL``, behave like :c:func:`PyUnicode_DecodeUTF7`. If @@ -1126,11 +1126,11 @@ Unicode-Escape Codecs These are the "Unicode Escape" codec APIs: -.. c:function:: PyObject* PyUnicode_DecodeUnicodeEscape(const char *s, \ +.. c:function:: PyObject* PyUnicode_DecodeUnicodeEscape(const char *str, \ Py_ssize_t size, const char *errors) Create a Unicode object by decoding *size* bytes of the Unicode-Escape encoded - string *s*. Return ``NULL`` if an exception was raised by the codec. + string *str*. Return ``NULL`` if an exception was raised by the codec. .. c:function:: PyObject* PyUnicode_AsUnicodeEscapeString(PyObject *unicode) @@ -1146,11 +1146,11 @@ Raw-Unicode-Escape Codecs These are the "Raw Unicode Escape" codec APIs: -.. c:function:: PyObject* PyUnicode_DecodeRawUnicodeEscape(const char *s, \ +.. c:function:: PyObject* PyUnicode_DecodeRawUnicodeEscape(const char *str, \ Py_ssize_t size, const char *errors) Create a Unicode object by decoding *size* bytes of the Raw-Unicode-Escape - encoded string *s*. Return ``NULL`` if an exception was raised by the codec. + encoded string *str*. Return ``NULL`` if an exception was raised by the codec. .. c:function:: PyObject* PyUnicode_AsRawUnicodeEscapeString(PyObject *unicode) @@ -1167,10 +1167,10 @@ These are the Latin-1 codec APIs: Latin-1 corresponds to the first 256 Unicode ordinals and only these are accepted by the codecs during encoding. -.. c:function:: PyObject* PyUnicode_DecodeLatin1(const char *s, Py_ssize_t size, const char *errors) +.. c:function:: PyObject* PyUnicode_DecodeLatin1(const char *str, Py_ssize_t size, const char *errors) Create a Unicode object by decoding *size* bytes of the Latin-1 encoded string - *s*. Return ``NULL`` if an exception was raised by the codec. + *str*. Return ``NULL`` if an exception was raised by the codec. .. c:function:: PyObject* PyUnicode_AsLatin1String(PyObject *unicode) @@ -1187,10 +1187,10 @@ These are the ASCII codec APIs. Only 7-bit ASCII data is accepted. All other codes generate errors. -.. c:function:: PyObject* PyUnicode_DecodeASCII(const char *s, Py_ssize_t size, const char *errors) +.. c:function:: PyObject* PyUnicode_DecodeASCII(const char *str, Py_ssize_t size, const char *errors) Create a Unicode object by decoding *size* bytes of the ASCII encoded string - *s*. Return ``NULL`` if an exception was raised by the codec. + *str*. Return ``NULL`` if an exception was raised by the codec. .. c:function:: PyObject* PyUnicode_AsASCIIString(PyObject *unicode) @@ -1211,10 +1211,10 @@ decode characters. The mapping objects provided must support the These are the mapping codec APIs: -.. c:function:: PyObject* PyUnicode_DecodeCharmap(const char *data, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeCharmap(const char *str, Py_ssize_t length, \ PyObject *mapping, const char *errors) - Create a Unicode object by decoding *size* bytes of the encoded string *s* + Create a Unicode object by decoding *size* bytes of the encoded string *str* using the given *mapping* object. Return ``NULL`` if an exception was raised by the codec. @@ -1241,7 +1241,7 @@ These are the mapping codec APIs: The following codec API is special in that maps Unicode to Unicode. -.. c:function:: PyObject* PyUnicode_Translate(PyObject *str, PyObject *table, const char *errors) +.. c:function:: PyObject* PyUnicode_Translate(PyObject *unicode, PyObject *table, const char *errors) Translate a string by applying a character mapping table to it and return the resulting Unicode object. Return ``NULL`` if an exception was raised by the @@ -1266,13 +1266,13 @@ use the Win32 MBCS converters to implement the conversions. Note that MBCS (or DBCS) is a class of encodings, not just one. The target encoding is defined by the user settings on the machine running the codec. -.. c:function:: PyObject* PyUnicode_DecodeMBCS(const char *s, Py_ssize_t size, const char *errors) +.. c:function:: PyObject* PyUnicode_DecodeMBCS(const char *str, Py_ssize_t size, const char *errors) - Create a Unicode object by decoding *size* bytes of the MBCS encoded string *s*. + Create a Unicode object by decoding *size* bytes of the MBCS encoded string *str*. Return ``NULL`` if an exception was raised by the codec. -.. c:function:: PyObject* PyUnicode_DecodeMBCSStateful(const char *s, Py_ssize_t size, \ +.. c:function:: PyObject* PyUnicode_DecodeMBCSStateful(const char *str, Py_ssize_t size, \ const char *errors, Py_ssize_t *consumed) If *consumed* is ``NULL``, behave like :c:func:`PyUnicode_DecodeMBCS`. If @@ -1318,7 +1318,7 @@ They all return ``NULL`` or ``-1`` if an exception occurs. Concat two strings giving a new Unicode string. -.. c:function:: PyObject* PyUnicode_Split(PyObject *s, PyObject *sep, Py_ssize_t maxsplit) +.. c:function:: PyObject* PyUnicode_Split(PyObject *unicode, PyObject *sep, Py_ssize_t maxsplit) Split a string giving a list of Unicode strings. If *sep* is ``NULL``, splitting will be done at all whitespace substrings. Otherwise, splits occur at the given @@ -1326,10 +1326,10 @@ They all return ``NULL`` or ``-1`` if an exception occurs. set. Separators are not included in the resulting list. -.. c:function:: PyObject* PyUnicode_Splitlines(PyObject *s, int keepend) +.. c:function:: PyObject* PyUnicode_Splitlines(PyObject *unicode, int keepends) Split a Unicode string at line breaks, returning a list of Unicode strings. - CRLF is considered to be one line break. If *keepend* is ``0``, the line break + CRLF is considered to be one line break. If *keepends* is ``0``, the Line break characters are not included in the resulting strings. @@ -1339,28 +1339,28 @@ They all return ``NULL`` or ``-1`` if an exception occurs. Unicode string. -.. c:function:: Py_ssize_t PyUnicode_Tailmatch(PyObject *str, PyObject *substr, \ +.. c:function:: Py_ssize_t PyUnicode_Tailmatch(PyObject *unicode, PyObject *substr, \ Py_ssize_t start, Py_ssize_t end, int direction) - Return ``1`` if *substr* matches ``str[start:end]`` at the given tail end + Return ``1`` if *substr* matches ``unicode[start:end]`` at the given tail end (*direction* == ``-1`` means to do a prefix match, *direction* == ``1`` a suffix match), ``0`` otherwise. Return ``-1`` if an error occurred. -.. c:function:: Py_ssize_t PyUnicode_Find(PyObject *str, PyObject *substr, \ +.. c:function:: Py_ssize_t PyUnicode_Find(PyObject *unicode, PyObject *substr, \ Py_ssize_t start, Py_ssize_t end, int direction) - Return the first position of *substr* in ``str[start:end]`` using the given + Return the first position of *substr* in ``unicode[start:end]`` using the given *direction* (*direction* == ``1`` means to do a forward search, *direction* == ``-1`` a backward search). The return value is the index of the first match; a value of ``-1`` indicates that no match was found, and ``-2`` indicates that an error occurred and an exception has been set. -.. c:function:: Py_ssize_t PyUnicode_FindChar(PyObject *str, Py_UCS4 ch, \ +.. c:function:: Py_ssize_t PyUnicode_FindChar(PyObject *unicode, Py_UCS4 ch, \ Py_ssize_t start, Py_ssize_t end, int direction) - Return the first position of the character *ch* in ``str[start:end]`` using + Return the first position of the character *ch* in ``unicode[start:end]`` using the given *direction* (*direction* == ``1`` means to do a forward search, *direction* == ``-1`` a backward search). The return value is the index of the first match; a value of ``-1`` indicates that no match was found, and ``-2`` @@ -1369,20 +1369,20 @@ They all return ``NULL`` or ``-1`` if an exception occurs. .. versionadded:: 3.3 .. versionchanged:: 3.7 - *start* and *end* are now adjusted to behave like ``str[start:end]``. + *start* and *end* are now adjusted to behave like ``unicode[start:end]``. -.. c:function:: Py_ssize_t PyUnicode_Count(PyObject *str, PyObject *substr, \ +.. c:function:: Py_ssize_t PyUnicode_Count(PyObject *unicode, PyObject *substr, \ Py_ssize_t start, Py_ssize_t end) Return the number of non-overlapping occurrences of *substr* in - ``str[start:end]``. Return ``-1`` if an error occurred. + ``unicode[start:end]``. Return ``-1`` if an error occurred. -.. c:function:: PyObject* PyUnicode_Replace(PyObject *str, PyObject *substr, \ +.. c:function:: PyObject* PyUnicode_Replace(PyObject *unicode, PyObject *substr, \ PyObject *replstr, Py_ssize_t maxcount) - Replace at most *maxcount* occurrences of *substr* in *str* with *replstr* and + Replace at most *maxcount* occurrences of *substr* in *unicode* with *replstr* and return the resulting Unicode object. *maxcount* == ``-1`` means replace all occurrences. @@ -1418,9 +1418,9 @@ They all return ``NULL`` or ``-1`` if an exception occurs. .. versionadded:: 3.13 -.. c:function:: int PyUnicode_CompareWithASCIIString(PyObject *uni, const char *string) +.. c:function:: int PyUnicode_CompareWithASCIIString(PyObject *unicode, const char *string) - Compare a Unicode object, *uni*, with *string* and return ``-1``, ``0``, ``1`` for less + Compare a Unicode object, *unicode*, with *string* and return ``-1``, ``0``, ``1`` for less than, equal, and greater than, respectively. It is best to pass only ASCII-encoded strings, but the function interprets the input string as ISO-8859-1 if it contains non-ASCII characters. @@ -1428,7 +1428,7 @@ They all return ``NULL`` or ``-1`` if an exception occurs. This function does not raise exceptions. -.. c:function:: PyObject* PyUnicode_RichCompare(PyObject *left, PyObject *right, int op) +.. c:function:: PyObject* PyUnicode_RichCompare(PyObject *left, PyObject *right, int op) Rich compare two Unicode strings and return one of the following: @@ -1446,29 +1446,29 @@ They all return ``NULL`` or ``-1`` if an exception occurs. ``format % args``. -.. c:function:: int PyUnicode_Contains(PyObject *container, PyObject *element) +.. c:function:: int PyUnicode_Contains(PyObject *unicode, PyObject *substr) - Check whether *element* is contained in *container* and return true or false + Check whether *substr* is contained in *unicode* and return true or false accordingly. - *element* has to coerce to a one element Unicode string. ``-1`` is returned + *substr* has to coerce to a one element Unicode string. ``-1`` is returned if there was an error. -.. c:function:: void PyUnicode_InternInPlace(PyObject **string) +.. c:function:: void PyUnicode_InternInPlace(PyObject **p_unicode) - Intern the argument *\*string* in place. The argument must be the address of a + Intern the argument :c:expr:`*p_unicode` in place. The argument must be the address of a pointer variable pointing to a Python Unicode string object. If there is an - existing interned string that is the same as *\*string*, it sets *\*string* to + existing interned string that is the same as :c:expr:`*p_unicode`, it sets :c:expr:`*p_unicode` to it (releasing the reference to the old string object and creating a new :term:`strong reference` to the interned string object), otherwise it leaves - *\*string* alone and interns it (creating a new :term:`strong reference`). + :c:expr:`*p_unicode` alone and interns it (creating a new :term:`strong reference`). (Clarification: even though there is a lot of talk about references, think of this function as reference-neutral; you own the object after the call if and only if you owned it before the call.) -.. c:function:: PyObject* PyUnicode_InternFromString(const char *v) +.. c:function:: PyObject* PyUnicode_InternFromString(const char *str) A combination of :c:func:`PyUnicode_FromString` and :c:func:`PyUnicode_InternInPlace`, returning either a new Unicode string diff --git a/Doc/conf.py b/Doc/conf.py index f1b411126c4e87..f2d36fdc70430c 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -24,7 +24,13 @@ 'sphinx.ext.doctest', ] -# Skip if downstream redistributors haven't installed it +# Skip if downstream redistributors haven't installed them +try: + import notfound.extension +except ImportError: + pass +else: + extensions.append('notfound.extension') try: import sphinxext.opengraph except ImportError: @@ -157,6 +163,13 @@ ('envvar', 'USER'), ('envvar', 'USERNAME'), ('envvar', 'USERPROFILE'), + # Deprecated function that was never documented: + ('py:func', 'getargspec'), + ('py:func', 'inspect.getargspec'), + # Undocumented modules that users shouldn't have to worry about + # (implementation details of `os.path`): + ('py:mod', 'ntpath'), + ('py:mod', 'posixpath'), ] # Temporary undocumented names. diff --git a/Doc/extending/extending.rst b/Doc/extending/extending.rst index 1ee7f28b2ba220..745fc10a22d161 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -735,7 +735,7 @@ Keyword Parameters for Extension Functions The :c:func:`PyArg_ParseTupleAndKeywords` function is declared as follows:: int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict, - const char *format, char *kwlist[], ...); + const char *format, char * const *kwlist, ...); The *arg* and *format* parameters are identical to those of the :c:func:`PyArg_ParseTuple` function. The *kwdict* parameter is the dictionary of diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst index 9f166eb8a4c3ff..7a92b3257c6cd3 100644 --- a/Doc/extending/newtypes.rst +++ b/Doc/extending/newtypes.rst @@ -296,7 +296,7 @@ An interesting advantage of using the :c:member:`~PyTypeObject.tp_members` table descriptors that are used at runtime is that any attribute defined this way can have an associated doc string simply by providing the text in the table. An application can use the introspection API to retrieve the descriptor from the -class object, and get the doc string using its :attr:`__doc__` attribute. +class object, and get the doc string using its :attr:`!__doc__` attribute. As with the :c:member:`~PyTypeObject.tp_methods` table, a sentinel entry with a :c:member:`~PyMethodDef.ml_name` value of ``NULL`` is required. diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 6b517b95f97013..601443d5aade94 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -151,9 +151,9 @@ Glossary A :term:`file object` able to read and write :term:`bytes-like objects `. Examples of binary files are files opened in binary mode (``'rb'``, - ``'wb'`` or ``'rb+'``), :data:`sys.stdin.buffer`, - :data:`sys.stdout.buffer`, and instances of :class:`io.BytesIO` and - :class:`gzip.GzipFile`. + ``'wb'`` or ``'rb+'``), :data:`sys.stdin.buffer `, + :data:`sys.stdout.buffer `, and instances of + :class:`io.BytesIO` and :class:`gzip.GzipFile`. See also :term:`text file` for a file object able to read and write :class:`str` objects. @@ -304,8 +304,9 @@ Glossary :ref:`class definitions ` for more about decorators. descriptor - Any object which defines the methods :meth:`__get__`, :meth:`__set__`, or - :meth:`__delete__`. When a class attribute is a descriptor, its special + Any object which defines the methods :meth:`~object.__get__`, + :meth:`~object.__set__`, or :meth:`~object.__delete__`. + When a class attribute is a descriptor, its special binding behavior is triggered upon attribute lookup. Normally, using *a.b* to get, set or delete an attribute looks up the object named *b* in the class dictionary for *a*, but if *b* is a descriptor, the respective @@ -319,7 +320,8 @@ Glossary dictionary An associative array, where arbitrary keys are mapped to values. The - keys can be any object with :meth:`__hash__` and :meth:`__eq__` methods. + keys can be any object with :meth:`~object.__hash__` and + :meth:`~object.__eq__` methods. Called a hash in Perl. dictionary comprehension @@ -383,7 +385,7 @@ Glossary file object An object exposing a file-oriented API (with methods such as - :meth:`read()` or :meth:`write()`) to an underlying resource. Depending + :meth:`!read` or :meth:`!write`) to an underlying resource. Depending on the way it was created, a file object can mediate access to a real on-disk file or to another type of storage or communication device (for example standard input/output, in-memory buffers, sockets, pipes, @@ -502,7 +504,7 @@ Glossary .. index:: single: generator expression generator expression - An expression that returns an iterator. It looks like a normal expression + An :term:`expression` that returns an :term:`iterator`. It looks like a normal expression followed by a :keyword:`!for` clause defining a loop variable, range, and an optional :keyword:`!if` clause. The combined expression generates values for an enclosing function:: @@ -559,8 +561,9 @@ Glossary hashable An object is *hashable* if it has a hash value which never changes during - its lifetime (it needs a :meth:`__hash__` method), and can be compared to - other objects (it needs an :meth:`__eq__` method). Hashable objects which + its lifetime (it needs a :meth:`~object.__hash__` method), and can be + compared to other objects (it needs an :meth:`~object.__eq__` method). + Hashable objects which compare equal must have the same hash value. Hashability makes an object usable as a dictionary key and a set member, @@ -646,7 +649,8 @@ Glossary iterables include all sequence types (such as :class:`list`, :class:`str`, and :class:`tuple`) and some non-sequence types like :class:`dict`, :term:`file objects `, and objects of any classes you define - with an :meth:`__iter__` method or with a :meth:`~object.__getitem__` method + with an :meth:`~iterator.__iter__` method or with a + :meth:`~object.__getitem__` method that implements :term:`sequence` semantics. Iterables can be @@ -655,7 +659,7 @@ Glossary as an argument to the built-in function :func:`iter`, it returns an iterator for the object. This iterator is good for one pass over the set of values. When using iterables, it is usually not necessary to call - :func:`iter` or deal with iterator objects yourself. The ``for`` + :func:`iter` or deal with iterator objects yourself. The :keyword:`for` statement does that automatically for you, creating a temporary unnamed variable to hold the iterator for the duration of the loop. See also :term:`iterator`, :term:`sequence`, and :term:`generator`. @@ -666,8 +670,8 @@ Glossary :func:`next`) return successive items in the stream. When no more data are available a :exc:`StopIteration` exception is raised instead. At this point, the iterator object is exhausted and any further calls to its - :meth:`__next__` method just raise :exc:`StopIteration` again. Iterators - are required to have an :meth:`__iter__` method that returns the iterator + :meth:`!__next__` method just raise :exc:`StopIteration` again. Iterators + are required to have an :meth:`~iterator.__iter__` method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted. One notable exception is code which attempts multiple iteration passes. A container object (such as a @@ -681,7 +685,7 @@ Glossary .. impl-detail:: CPython does not consistently apply the requirement that an iterator - define :meth:`__iter__`. + define :meth:`~iterator.__iter__`. key function A key function or collation function is a callable that returns a value @@ -875,7 +879,8 @@ Glossary Old name for the flavor of classes now used for all class objects. In earlier Python versions, only new-style classes could use Python's newer, versatile features like :attr:`~object.__slots__`, descriptors, - properties, :meth:`__getattribute__`, class methods, and static methods. + properties, :meth:`~object.__getattribute__`, class methods, and static + methods. object Any data with state (attributes or value) and defined behavior @@ -955,7 +960,7 @@ Glossary finders implement. path entry hook - A callable on the :data:`sys.path_hook` list which returns a :term:`path + A callable on the :data:`sys.path_hooks` list which returns a :term:`path entry finder` if it knows how to find modules on a specific :term:`path entry`. @@ -1089,18 +1094,18 @@ Glossary sequence An :term:`iterable` which supports efficient element access using integer indices via the :meth:`~object.__getitem__` special method and defines a - :meth:`__len__` method that returns the length of the sequence. + :meth:`~object.__len__` method that returns the length of the sequence. Some built-in sequence types are :class:`list`, :class:`str`, :class:`tuple`, and :class:`bytes`. Note that :class:`dict` also - supports :meth:`~object.__getitem__` and :meth:`__len__`, but is considered a + supports :meth:`~object.__getitem__` and :meth:`!__len__`, but is considered a mapping rather than a sequence because the lookups use arbitrary :term:`immutable` keys rather than integers. The :class:`collections.abc.Sequence` abstract base class defines a much richer interface that goes beyond just - :meth:`~object.__getitem__` and :meth:`__len__`, adding :meth:`count`, - :meth:`index`, :meth:`__contains__`, and - :meth:`__reversed__`. Types that implement this expanded + :meth:`~object.__getitem__` and :meth:`~object.__len__`, adding + :meth:`count`, :meth:`index`, :meth:`~object.__contains__`, and + :meth:`~object.__reversed__`. Types that implement this expanded interface can be registered explicitly using :func:`~abc.ABCMeta.register`. diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index 90d67d84d0ef08..f732aaea729d40 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -521,11 +521,11 @@ everyday Python programs. Descriptor protocol ------------------- -``descr.__get__(self, obj, type=None) -> value`` +``descr.__get__(self, obj, type=None)`` -``descr.__set__(self, obj, value) -> None`` +``descr.__set__(self, obj, value)`` -``descr.__delete__(self, obj) -> None`` +``descr.__delete__(self, obj)`` That is all there is to it. Define any of these methods and an object is considered a descriptor and can override default behavior upon being looked up @@ -1013,17 +1013,23 @@ here is a pure Python equivalent: if obj is None: return self if self.fget is None: - raise AttributeError(f"property '{self._name}' has no getter") + raise AttributeError( + f'property {self._name!r} of {type(obj).__name__!r} object has no getter' + ) return self.fget(obj) def __set__(self, obj, value): if self.fset is None: - raise AttributeError(f"property '{self._name}' has no setter") + raise AttributeError( + f'property {self._name!r} of {type(obj).__name__!r} object has no setter' + ) self.fset(obj, value) def __delete__(self, obj): if self.fdel is None: - raise AttributeError(f"property '{self._name}' has no deleter") + raise AttributeError( + f'property {self._name!r} of {type(obj).__name__!r} object has no deleter' + ) self.fdel(obj) def getter(self, fget): @@ -1054,6 +1060,11 @@ here is a pure Python equivalent: def delx(self): del self.__x x = Property(getx, setx, delx, "I'm the 'x' property.") + no_getter = Property(None, setx, delx, "I'm the 'x' property.") + no_setter = Property(getx, None, delx, "I'm the 'x' property.") + no_deleter = Property(getx, setx, None, "I'm the 'x' property.") + no_doc = Property(getx, setx, delx, None) + # Now do it again but use the decorator style @@ -1092,6 +1103,32 @@ here is a pure Python equivalent: >>> hasattr(ccc, 'x') False + >>> cc = CC() + >>> cc.x = 33 + >>> try: + ... cc.no_getter + ... except AttributeError as e: + ... e.args[0] + ... + "property 'no_getter' of 'CC' object has no getter" + + >>> try: + ... cc.no_setter = 33 + ... except AttributeError as e: + ... e.args[0] + ... + "property 'no_setter' of 'CC' object has no setter" + + >>> try: + ... del cc.no_deleter + ... except AttributeError as e: + ... e.args[0] + ... + "property 'no_deleter' of 'CC' object has no deleter" + + >>> CC.no_doc.__doc__ is None + True + The :func:`property` builtin helps whenever a user interface has granted attribute access and then subsequent changes require the intervention of a method. diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst index a136c76303c8ef..1e9ac9b6761b64 100644 --- a/Doc/howto/enum.rst +++ b/Doc/howto/enum.rst @@ -868,7 +868,7 @@ Others While :class:`IntEnum` is part of the :mod:`enum` module, it would be very simple to implement independently:: - class IntEnum(int, Enum): + class IntEnum(int, ReprEnum): # or Enum instead of ReprEnum pass This demonstrates how similar derived enumerations can be defined; for example @@ -876,8 +876,8 @@ a :class:`FloatEnum` that mixes in :class:`float` instead of :class:`int`. Some rules: -1. When subclassing :class:`Enum`, mix-in types must appear before - :class:`Enum` itself in the sequence of bases, as in the :class:`IntEnum` +1. When subclassing :class:`Enum`, mix-in types must appear before the + :class:`Enum` class itself in the sequence of bases, as in the :class:`IntEnum` example above. 2. Mix-in types must be subclassable. For example, :class:`bool` and :class:`range` are not subclassable and will throw an error during Enum @@ -961,30 +961,34 @@ all the members are created it is no longer used. Supported ``_sunder_`` names """""""""""""""""""""""""""" -- ``_name_`` -- name of the member -- ``_value_`` -- value of the member; can be set / modified in ``__new__`` +- :attr:`~Enum._name_` -- name of the member +- :attr:`~Enum._value_` -- value of the member; can be set in ``__new__`` +- :meth:`~Enum._missing_` -- a lookup function used when a value is not found; + may be overridden +- :attr:`~Enum._ignore_` -- a list of names, either as a :class:`list` or a + :class:`str`, that will not be transformed into members, and will be removed + from the final class +- :meth:`~Enum._generate_next_value_` -- used to get an appropriate value for + an enum member; may be overridden +- :meth:`~Enum._add_alias_` -- adds a new name as an alias to an existing + member. +- :meth:`~Enum._add_value_alias_` -- adds a new value as an alias to an + existing member. See `MultiValueEnum`_ for an example. -- ``_missing_`` -- a lookup function used when a value is not found; may be - overridden -- ``_ignore_`` -- a list of names, either as a :class:`list` or a :class:`str`, - that will not be transformed into members, and will be removed from the final - class -- ``_order_`` -- used in Python 2/3 code to ensure member order is consistent - (class attribute, removed during class creation) -- ``_generate_next_value_`` -- used by the `Functional API`_ and by - :class:`auto` to get an appropriate value for an enum member; may be - overridden + .. note:: -.. note:: + For standard :class:`Enum` classes the next value chosen is the highest + value seen incremented by one. - For standard :class:`Enum` classes the next value chosen is the last value seen - incremented by one. + For :class:`Flag` classes the next value chosen will be the next highest + power-of-two. - For :class:`Flag` classes the next value chosen will be the next highest - power-of-two, regardless of the last value seen. + .. versionchanged:: 3.13 + Prior versions would use the last seen value instead of the highest value. .. versionadded:: 3.6 ``_missing_``, ``_order_``, ``_generate_next_value_`` .. versionadded:: 3.7 ``_ignore_`` +.. versionadded:: 3.13 ``_add_alias_``, ``_add_value_alias_`` To help keep Python 2 / Python 3 code in sync an :attr:`_order_` attribute can be provided. It will be checked against the actual order of the enumeration @@ -1439,7 +1443,6 @@ alias:: Traceback (most recent call last): ... ValueError: aliases not allowed in DuplicateFreeEnum: 'GRENE' --> 'GREEN' - Error calling __set_name__ on '_proto_member' instance 'GRENE' in 'Color' .. note:: @@ -1448,6 +1451,29 @@ alias:: disallowing aliases, the :func:`unique` decorator can be used instead. +MultiValueEnum +^^^^^^^^^^^^^^^^^ + +Supports having more than one value per member:: + + >>> class MultiValueEnum(Enum): + ... def __new__(cls, value, *values): + ... self = object.__new__(cls) + ... self._value_ = value + ... for v in values: + ... self._add_value_alias_(v) + ... return self + ... + >>> class DType(MultiValueEnum): + ... float32 = 'f', 8 + ... double64 = 'd', 9 + ... + >>> DType('f') + + >>> DType(9) + + + Planet ^^^^^^ diff --git a/Doc/library/abc.rst b/Doc/library/abc.rst index fb4f9da169c5ab..c073ea955abaa4 100644 --- a/Doc/library/abc.rst +++ b/Doc/library/abc.rst @@ -21,7 +21,7 @@ The :mod:`collections` module has some concrete classes that derive from ABCs; these can, of course, be further derived. In addition, the :mod:`collections.abc` submodule has some ABCs that can be used to test whether a class or instance provides a particular interface, for example, if it is -:term:`hashable` or if it is a mapping. +:term:`hashable` or if it is a :term:`mapping`. This module provides the metaclass :class:`ABCMeta` for defining ABCs and @@ -30,7 +30,7 @@ a helper class :class:`ABC` to alternatively define ABCs through inheritance: .. class:: ABC A helper class that has :class:`ABCMeta` as its metaclass. With this class, - an abstract base class can be created by simply deriving from :class:`ABC` + an abstract base class can be created by simply deriving from :class:`!ABC` avoiding sometimes confusing metaclass usage, for example:: from abc import ABC @@ -38,11 +38,11 @@ a helper class :class:`ABC` to alternatively define ABCs through inheritance: class MyABC(ABC): pass - Note that the type of :class:`ABC` is still :class:`ABCMeta`, therefore - inheriting from :class:`ABC` requires the usual precautions regarding + Note that the type of :class:`!ABC` is still :class:`ABCMeta`, therefore + inheriting from :class:`!ABC` requires the usual precautions regarding metaclass usage, as multiple inheritance may lead to metaclass conflicts. One may also define an abstract base class by passing the metaclass - keyword and using :class:`ABCMeta` directly, for example:: + keyword and using :class:`!ABCMeta` directly, for example:: from abc import ABCMeta @@ -65,7 +65,7 @@ a helper class :class:`ABC` to alternatively define ABCs through inheritance: implementations defined by the registering ABC be callable (not even via :func:`super`). [#]_ - Classes created with a metaclass of :class:`ABCMeta` have the following method: + Classes created with a metaclass of :class:`!ABCMeta` have the following method: .. method:: register(subclass) @@ -86,7 +86,7 @@ a helper class :class:`ABC` to alternatively define ABCs through inheritance: Returns the registered subclass, to allow usage as a class decorator. .. versionchanged:: 3.4 - To detect calls to :meth:`register`, you can use the + To detect calls to :meth:`!register`, you can use the :func:`get_cache_token` function. You can also override this method in an abstract base class: @@ -96,10 +96,10 @@ a helper class :class:`ABC` to alternatively define ABCs through inheritance: (Must be defined as a class method.) Check whether *subclass* is considered a subclass of this ABC. This means - that you can customize the behavior of ``issubclass`` further without the + that you can customize the behavior of :func:`issubclass` further without the need to call :meth:`register` on every class you want to consider a subclass of the ABC. (This class method is called from the - :meth:`__subclasscheck__` method of the ABC.) + :meth:`~class.__subclasscheck__` method of the ABC.) This method should return ``True``, ``False`` or ``NotImplemented``. If it returns ``True``, the *subclass* is considered a subclass of this ABC. @@ -142,7 +142,7 @@ a helper class :class:`ABC` to alternatively define ABCs through inheritance: The ABC ``MyIterable`` defines the standard iterable method, :meth:`~iterator.__iter__`, as an abstract method. The implementation given - here can still be called from subclasses. The :meth:`get_iterator` method + here can still be called from subclasses. The :meth:`!get_iterator` method is also part of the ``MyIterable`` abstract base class, but it does not have to be overridden in non-abstract derived classes. @@ -153,14 +153,14 @@ a helper class :class:`ABC` to alternatively define ABCs through inheritance: Finally, the last line makes ``Foo`` a virtual subclass of ``MyIterable``, even though it does not define an :meth:`~iterator.__iter__` method (it uses - the old-style iterable protocol, defined in terms of :meth:`__len__` and + the old-style iterable protocol, defined in terms of :meth:`~object.__len__` and :meth:`~object.__getitem__`). Note that this will not make ``get_iterator`` available as a method of ``Foo``, so it is provided separately. -The :mod:`abc` module also provides the following decorator: +The :mod:`!abc` module also provides the following decorator: .. decorator:: abstractmethod @@ -168,19 +168,19 @@ The :mod:`abc` module also provides the following decorator: Using this decorator requires that the class's metaclass is :class:`ABCMeta` or is derived from it. A class that has a metaclass derived from - :class:`ABCMeta` cannot be instantiated unless all of its abstract methods + :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. :func:`!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 - inheritance; "virtual subclasses" registered with the ABC's :meth:`register` - method are not affected. + :func:`!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 :func:`!abstractmethod` is applied in combination with other method descriptors, it should be applied as the innermost decorator, as shown in the following usage examples:: @@ -216,7 +216,7 @@ The :mod:`abc` module also provides the following decorator: In order to correctly interoperate with the abstract base class machinery, the descriptor must identify itself as abstract using - :attr:`__isabstractmethod__`. In general, this attribute should be ``True`` + :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:: @@ -236,7 +236,7 @@ The :mod:`abc` module also provides the following decorator: super-call in a framework that uses cooperative multiple-inheritance. -The :mod:`abc` module also supports the following legacy decorators: +The :mod:`!abc` module also supports the following legacy decorators: .. decorator:: abstractclassmethod @@ -323,7 +323,7 @@ The :mod:`abc` module also supports the following legacy decorators: ... -The :mod:`abc` module also provides the following functions: +The :mod:`!abc` module also provides the following functions: .. function:: get_cache_token() diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst index d8186b6ce75c79..0736e783bbc8c8 100644 --- a/Doc/library/asyncio-stream.rst +++ b/Doc/library/asyncio-stream.rst @@ -204,6 +204,10 @@ StreamReader directly; use :func:`open_connection` and :func:`start_server` instead. + .. method:: feed_eof() + + Acknowledge the EOF. + .. coroutinemethod:: read(n=-1) Read up to *n* bytes from the stream. diff --git a/Doc/library/cmd.rst b/Doc/library/cmd.rst index fd5df96dfd0b3d..1318ffe5a48d53 100644 --- a/Doc/library/cmd.rst +++ b/Doc/library/cmd.rst @@ -26,6 +26,13 @@ interface. key; it defaults to :kbd:`Tab`. If *completekey* is not :const:`None` and :mod:`readline` is available, command completion is done automatically. + The default, ``'tab'``, is treated specially, so that it refers to the + :kbd:`Tab` key on every :data:`readline.backend`. + Specifically, if :data:`readline.backend` is ``editline``, + ``Cmd`` will use ``'^I'`` instead of ``'tab'``. + Note that other values are not treated this way, and might only work + with a specific backend. + The optional arguments *stdin* and *stdout* specify the input and output file objects that the Cmd instance or subclass instance will use for input and output. If not specified, they will default to :data:`sys.stdin` and @@ -35,6 +42,9 @@ interface. :attr:`use_rawinput` attribute to ``False``, otherwise *stdin* will be ignored. + .. versionchanged:: 3.13 + ``completekey='tab'`` is replaced by ``'^I'`` for ``editline``. + .. _cmd-objects: diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst index 17dd6da7479e50..233b2c6a771f4a 100644 --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -981,6 +981,10 @@ field names, the method and attribute names start with an underscore. Named tuples are also supported by generic function :func:`copy.replace`. + .. versionchanged:: 3.13 + Raise :exc:`TypeError` instead of :exc:`ValueError` for invalid + keyword arguments. + .. attribute:: somenamedtuple._fields Tuple of strings listing the field names. Useful for introspection diff --git a/Doc/library/contextlib.rst b/Doc/library/contextlib.rst index f6ebbfacfba509..aab319cbe7405e 100644 --- a/Doc/library/contextlib.rst +++ b/Doc/library/contextlib.rst @@ -106,8 +106,8 @@ Functions and classes provided: This function is a :term:`decorator` that can be used to define a factory function for :keyword:`async with` statement asynchronous context managers, - without needing to create a class or separate :meth:`__aenter__` and - :meth:`__aexit__` methods. It must be applied to an :term:`asynchronous + without needing to create a class or separate :meth:`~object.__aenter__` and + :meth:`~object.__aexit__` methods. It must be applied to an :term:`asynchronous generator` function. A simple example:: @@ -616,12 +616,12 @@ Functions and classes provided: asynchronous context managers, as well as having coroutines for cleanup logic. - The :meth:`close` method is not implemented, :meth:`aclose` must be used + The :meth:`~ExitStack.close` method is not implemented; :meth:`aclose` must be used instead. .. coroutinemethod:: enter_async_context(cm) - Similar to :meth:`enter_context` but expects an asynchronous context + Similar to :meth:`ExitStack.enter_context` but expects an asynchronous context manager. .. versionchanged:: 3.11 @@ -630,16 +630,16 @@ Functions and classes provided: .. method:: push_async_exit(exit) - Similar to :meth:`push` but expects either an asynchronous context manager + Similar to :meth:`ExitStack.push` but expects either an asynchronous context manager or a coroutine function. .. method:: push_async_callback(callback, /, *args, **kwds) - Similar to :meth:`callback` but expects a coroutine function. + Similar to :meth:`ExitStack.callback` but expects a coroutine function. .. coroutinemethod:: aclose() - Similar to :meth:`close` but properly handles awaitables. + Similar to :meth:`ExitStack.close` but properly handles awaitables. Continuing the example for :func:`asynccontextmanager`:: diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index e44fa46b5d4145..0d93bc9f5da774 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -51,6 +51,11 @@ interpreter. transparent for forward jumps but needs to be taken into account when reasoning about backward jumps. + .. versionchanged:: 3.13 + The output shows logical labels rather than instruction offsets + for jump targets and exception handlers. The ``-O`` command line + option and the ``show_offsets`` argument were added. + Example: Given the function :func:`!myfunc`:: def myfunc(alist): @@ -62,12 +67,12 @@ the following command can be used to display the disassembly of .. doctest:: >>> dis.dis(myfunc) - 2 0 RESUME 0 + 2 RESUME 0 - 3 2 LOAD_GLOBAL 1 (len + NULL) - 12 LOAD_FAST 0 (alist) - 14 CALL 1 - 22 RETURN_VALUE + 3 LOAD_GLOBAL 1 (len + NULL) + LOAD_FAST 0 (alist) + CALL 1 + RETURN_VALUE (The "2" is a line number). @@ -80,7 +85,7 @@ The :mod:`dis` module can be invoked as a script from the command line: .. code-block:: sh - python -m dis [-h] [-C] [infile] + python -m dis [-h] [-C] [-O] [infile] The following options are accepted: @@ -94,6 +99,10 @@ The following options are accepted: Show inline caches. +.. cmdoption:: -O, --show-offsets + + Show offsets of instructions. + If :file:`infile` is specified, its disassembled code will be written to stdout. Otherwise, disassembly is performed on compiled source code recieved from stdin. @@ -107,7 +116,7 @@ The bytecode analysis API allows pieces of Python code to be wrapped in a code. .. class:: Bytecode(x, *, first_line=None, current_offset=None,\ - show_caches=False, adaptive=False) + show_caches=False, adaptive=False, show_offsets=False) Analyse the bytecode corresponding to a function, generator, asynchronous generator, coroutine, method, string of source code, or a code object (as @@ -132,6 +141,9 @@ code. If *adaptive* is ``True``, :meth:`.dis` will display specialized bytecode that may be different from the original bytecode. + If *show_offsets* is ``True``, :meth:`.dis` will include instruction + offsets in the output. + .. classmethod:: from_traceback(tb, *, show_caches=False) Construct a :class:`Bytecode` instance from the given traceback, setting @@ -254,7 +266,8 @@ operation is being performed, so the intermediate analysis object isn't useful: Added the *show_caches* and *adaptive* parameters. -.. function:: distb(tb=None, *, file=None, show_caches=False, adaptive=False) +.. function:: distb(tb=None, *, file=None, show_caches=False, adaptive=False, + show_offset=False) Disassemble the top-of-stack function of a traceback, using the last traceback if none was passed. The instruction causing the exception is @@ -269,9 +282,12 @@ operation is being performed, so the intermediate analysis object isn't useful: .. versionchanged:: 3.11 Added the *show_caches* and *adaptive* parameters. + .. versionchanged:: 3.13 + Added the *show_offsets* parameter. .. function:: disassemble(code, lasti=-1, *, file=None, show_caches=False, adaptive=False) - disco(code, lasti=-1, *, file=None, show_caches=False, adaptive=False) + disco(code, lasti=-1, *, file=None, show_caches=False, adaptive=False, + show_offsets=False) Disassemble a code object, indicating the last instruction if *lasti* was provided. The output is divided in the following columns: @@ -296,6 +312,8 @@ operation is being performed, so the intermediate analysis object isn't useful: .. versionchanged:: 3.11 Added the *show_caches* and *adaptive* parameters. + .. versionchanged:: 3.13 + Added the *show_offsets* parameter. .. function:: get_instructions(x, *, first_line=None, show_caches=False, adaptive=False) @@ -328,8 +346,9 @@ operation is being performed, so the intermediate analysis object isn't useful: Line numbers can be decreasing. Before, they were always increasing. .. versionchanged:: 3.10 - The :pep:`626` ``co_lines`` method is used instead of the ``co_firstlineno`` - and ``co_lnotab`` attributes of the code object. + The :pep:`626` ``co_lines`` method is used instead of the + :attr:`~codeobject.co_firstlineno` and :attr:`~codeobject.co_lnotab` + attributes of the code object. .. versionchanged:: 3.13 Line numbers can be ``None`` for bytecode that does not map to source lines. @@ -847,8 +866,8 @@ iterations of the loop. .. opcode:: RERAISE Re-raises the exception currently on top of the stack. If oparg is non-zero, - pops an additional value from the stack which is used to set ``f_lasti`` - of the current frame. + pops an additional value from the stack which is used to set + :attr:`~frame.f_lasti` of the current frame. .. versionadded:: 3.9 @@ -965,13 +984,13 @@ iterations of the loop. .. opcode:: STORE_NAME (namei) Implements ``name = STACK.pop()``. *namei* is the index of *name* in the attribute - :attr:`!co_names` of the :ref:`code object `. + :attr:`~codeobject.co_names` of the :ref:`code object `. The compiler tries to use :opcode:`STORE_FAST` or :opcode:`STORE_GLOBAL` if possible. .. opcode:: DELETE_NAME (namei) - Implements ``del name``, where *namei* is the index into :attr:`!co_names` + Implements ``del name``, where *namei* is the index into :attr:`~codeobject.co_names` attribute of the :ref:`code object `. @@ -1011,7 +1030,7 @@ iterations of the loop. value = STACK.pop() obj.name = value - where *namei* is the index of name in :attr:`!co_names` of the + where *namei* is the index of name in :attr:`~codeobject.co_names` of the :ref:`code object `. .. opcode:: DELETE_ATTR (namei) @@ -1021,7 +1040,7 @@ iterations of the loop. obj = STACK.pop() del obj.name - where *namei* is the index of name into :attr:`!co_names` of the + where *namei* is the index of name into :attr:`~codeobject.co_names` of the :ref:`code object `. @@ -1384,7 +1403,7 @@ iterations of the loop. Pushes a reference to the object the cell contains on the stack. .. versionchanged:: 3.11 - ``i`` is no longer offset by the length of ``co_varnames``. + ``i`` is no longer offset by the length of :attr:`~codeobject.co_varnames`. .. opcode:: LOAD_FROM_DICT_OR_DEREF (i) @@ -1406,7 +1425,7 @@ iterations of the loop. storage. .. versionchanged:: 3.11 - ``i`` is no longer offset by the length of ``co_varnames``. + ``i`` is no longer offset by the length of :attr:`~codeobject.co_varnames`. .. opcode:: DELETE_DEREF (i) @@ -1417,7 +1436,7 @@ iterations of the loop. .. versionadded:: 3.2 .. versionchanged:: 3.11 - ``i`` is no longer offset by the length of ``co_varnames``. + ``i`` is no longer offset by the length of :attr:`~codeobject.co_varnames`. .. opcode:: COPY_FREE_VARS (n) diff --git a/Doc/library/doctest.rst b/Doc/library/doctest.rst index ad013944ce3ca3..fa1b850c531346 100644 --- a/Doc/library/doctest.rst +++ b/Doc/library/doctest.rst @@ -143,13 +143,13 @@ Simple Usage: Checking Examples in Docstrings --------------------------------------------- The simplest way to start using doctest (but not necessarily the way you'll -continue to do it) is to end each module :mod:`M` with:: +continue to do it) is to end each module :mod:`!M` with:: if __name__ == "__main__": import doctest doctest.testmod() -:mod:`doctest` then examines docstrings in module :mod:`M`. +:mod:`!doctest` then examines docstrings in module :mod:`!M`. Running the module as a script causes the examples in the docstrings to get executed and verified:: @@ -403,10 +403,10 @@ What's the Execution Context? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ By default, each time :mod:`doctest` finds a docstring to test, it uses a -*shallow copy* of :mod:`M`'s globals, so that running tests doesn't change the -module's real globals, and so that one test in :mod:`M` can't leave behind +*shallow copy* of :mod:`!M`'s globals, so that running tests doesn't change the +module's real globals, and so that one test in :mod:`!M` can't leave behind crumbs that accidentally allow another test to work. This means examples can -freely use any names defined at top-level in :mod:`M`, and names defined earlier +freely use any names defined at top-level in :mod:`!M`, and names defined earlier in the docstring being run. Examples cannot see names defined in other docstrings. @@ -958,7 +958,8 @@ and :ref:`doctest-simple-testfile`. Optional argument *exclude_empty* defaults to false. If true, objects for which no doctests are found are excluded from consideration. The default is a backward - compatibility hack, so that code still using :meth:`doctest.master.summarize` in + compatibility hack, so that code still using + :meth:`doctest.master.summarize ` in conjunction with :func:`testmod` continues to get output for objects with no tests. The *exclude_empty* argument to the newer :class:`DocTestFinder` constructor defaults to true. @@ -997,7 +998,7 @@ As your collection of doctest'ed modules grows, you'll want a way to run all their doctests systematically. :mod:`doctest` provides two functions that can be used to create :mod:`unittest` test suites from modules and text files containing doctests. To integrate with :mod:`unittest` test discovery, include -a :func:`load_tests` function in your test module:: +a :ref:`load_tests ` function in your test module:: import unittest import doctest @@ -1111,19 +1112,24 @@ from text files and modules with doctests: :func:`DocTestSuite` returns an empty :class:`unittest.TestSuite` if *module* contains no docstrings instead of raising :exc:`ValueError`. +.. exception:: failureException + + When doctests which have been converted to unit tests by :func:`DocFileSuite` + or :func:`DocTestSuite` fail, this exception is raised showing the name of + the file containing the test and a (sometimes approximate) line number. Under the covers, :func:`DocTestSuite` creates a :class:`unittest.TestSuite` out -of :class:`doctest.DocTestCase` instances, and :class:`DocTestCase` is a -subclass of :class:`unittest.TestCase`. :class:`DocTestCase` isn't documented +of :class:`!doctest.DocTestCase` instances, and :class:`!DocTestCase` is a +subclass of :class:`unittest.TestCase`. :class:`!DocTestCase` isn't documented here (it's an internal detail), but studying its code can answer questions about the exact details of :mod:`unittest` integration. Similarly, :func:`DocFileSuite` creates a :class:`unittest.TestSuite` out of -:class:`doctest.DocFileCase` instances, and :class:`DocFileCase` is a subclass -of :class:`DocTestCase`. +:class:`!doctest.DocFileCase` instances, and :class:`!DocFileCase` is a subclass +of :class:`!DocTestCase`. So both ways of creating a :class:`unittest.TestSuite` run instances of -:class:`DocTestCase`. This is important for a subtle reason: when you run +:class:`!DocTestCase`. This is important for a subtle reason: when you run :mod:`doctest` functions yourself, you can control the :mod:`doctest` options in use directly, by passing option flags to :mod:`doctest` functions. However, if you're writing a :mod:`unittest` framework, :mod:`unittest` ultimately controls @@ -1144,14 +1150,14 @@ reporting flags specific to :mod:`unittest` support, via this function: section :ref:`doctest-options`. Only "reporting flags" can be used. This is a module-global setting, and affects all future doctests run by module - :mod:`unittest`: the :meth:`runTest` method of :class:`DocTestCase` looks at - the option flags specified for the test case when the :class:`DocTestCase` + :mod:`unittest`: the :meth:`!runTest` method of :class:`!DocTestCase` looks at + the option flags specified for the test case when the :class:`!DocTestCase` instance was constructed. If no reporting flags were specified (which is the - typical and expected case), :mod:`doctest`'s :mod:`unittest` reporting flags are + typical and expected case), :mod:`!doctest`'s :mod:`unittest` reporting flags are :ref:`bitwise ORed ` into the option flags, and the option flags so augmented are passed to the :class:`DocTestRunner` instance created to run the doctest. If any reporting flags were specified when the - :class:`DocTestCase` instance was constructed, :mod:`doctest`'s + :class:`!DocTestCase` instance was constructed, :mod:`!doctest`'s :mod:`unittest` reporting flags are ignored. The value of the :mod:`unittest` reporting flags in effect before the function @@ -1321,7 +1327,8 @@ Example Objects A dictionary mapping from option flags to ``True`` or ``False``, which is used to override default options for this example. Any option flags not contained in this dictionary are left at their default value (as specified by the - :class:`DocTestRunner`'s :attr:`optionflags`). By default, no options are set. + :class:`DocTestRunner`'s :ref:`optionflags `). + By default, no options are set. .. _doctest-doctestfinder: @@ -1560,7 +1567,7 @@ DocTestRunner objects The output of each example is checked using the :class:`DocTestRunner`'s output checker, and the results are formatted by the - :meth:`DocTestRunner.report_\*` methods. + :meth:`!DocTestRunner.report_\*` methods. .. method:: summarize(verbose=None) @@ -1735,12 +1742,12 @@ code under the debugger: module) of the object with the doctests of interest. The result is a string, containing the object's docstring converted to a Python script, as described for :func:`script_from_examples` above. For example, if module :file:`a.py` - contains a top-level function :func:`f`, then :: + contains a top-level function :func:`!f`, then :: import a, doctest print(doctest.testsource(a, "a.f")) - prints a script version of function :func:`f`'s docstring, with doctests + prints a script version of function :func:`!f`'s docstring, with doctests converted to code, and the rest placed in comments. diff --git a/Doc/library/email.errors.rst b/Doc/library/email.errors.rst index 194a98696f437d..56aea6598b8615 100644 --- a/Doc/library/email.errors.rst +++ b/Doc/library/email.errors.rst @@ -58,6 +58,15 @@ The following exception classes are defined in the :mod:`email.errors` module: :class:`~email.mime.nonmultipart.MIMENonMultipart` (e.g. :class:`~email.mime.image.MIMEImage`). +.. exception:: MessageDefect() + + This is the base class for all defects found when parsing email messages. + It is derived from :exc:`ValueError`. + +.. exception:: HeaderDefect() + + This is the base class for all defects found when parsing email headers. + It is derived from :exc:`MessageDefect`. Here is the list of the defects that the :class:`~email.parser.FeedParser` can find while parsing messages. Note that the defects are added to the message diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 2d5ae361c3f1e3..20222bfb3611ab 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -235,6 +235,10 @@ Data Types >>> len(Color) 3 + .. attribute:: EnumType.__members__ + + Returns a mapping of every enum name to its member, including aliases + .. method:: EnumType.__reversed__(cls) Returns each member in *cls* in reverse definition order:: @@ -242,9 +246,19 @@ Data Types >>> list(reversed(Color)) [, , ] + .. method:: EnumType._add_alias_ + + Adds a new name as an alias to an existing member. Raises a + :exc:`NameError` if the name is already assigned to a different member. + + .. method:: EnumType._add_value_alias_ + + Adds a new value as an alias to an existing member. Raises a + :exc:`ValueError` if the value is already linked with a different member. + .. versionadded:: 3.11 - Before 3.11 ``enum`` used ``EnumMeta`` type, which is kept as an alias. + Before 3.11 ``EnumType`` was called ``EnumMeta``, which is still available as an alias. .. class:: Enum @@ -323,7 +337,7 @@ Data Types >>> PowersOfThree.SECOND.value 9 - .. method:: Enum.__init_subclass__(cls, **kwds) + .. method:: Enum.__init_subclass__(cls, \**kwds) A *classmethod* that is used to further configure subsequent subclasses. By default, does nothing. @@ -549,7 +563,7 @@ Data Types .. method:: __invert__(self): - Returns all the flags in *type(self)* that are not in self:: + Returns all the flags in *type(self)* that are not in *self*:: >>> ~white @@ -769,37 +783,41 @@ Supported ``__dunder__`` names :attr:`~EnumType.__members__` is a read-only ordered mapping of ``member_name``:``member`` items. It is only available on the class. -:meth:`~object.__new__`, if specified, must create and return the enum members; it is -also a very good idea to set the member's :attr:`!_value_` appropriately. Once -all the members are created it is no longer used. +:meth:`~object.__new__`, if specified, must create and return the enum members; +it is also a very good idea to set the member's :attr:`!_value_` appropriately. +Once all the members are created it is no longer used. Supported ``_sunder_`` names """""""""""""""""""""""""""" -- ``_name_`` -- name of the member -- ``_value_`` -- value of the member; can be set / modified in ``__new__`` - -- ``_missing_`` -- a lookup function used when a value is not found; may be - overridden -- ``_ignore_`` -- a list of names, either as a :class:`list` or a :class:`str`, - that will not be transformed into members, and will be removed from the final - class -- ``_order_`` -- used in Python 2/3 code to ensure member order is consistent - (class attribute, removed during class creation) -- ``_generate_next_value_`` -- used to get an appropriate value for an enum - member; may be overridden +- :meth:`~EnumType._add_alias_` -- adds a new name as an alias to an existing + member. +- :meth:`~EnumType._add_value_alias_` -- adds a new value as an alias to an + existing member. +- :attr:`~Enum._name_` -- name of the member +- :attr:`~Enum._value_` -- value of the member; can be set in ``__new__`` +- :meth:`~Enum._missing_` -- a lookup function used when a value is not found; + may be overridden +- :attr:`~Enum._ignore_` -- a list of names, either as a :class:`list` or a + :class:`str`, that will not be transformed into members, and will be removed + from the final class +- :attr:`~Enum._order_` -- used in Python 2/3 code to ensure member order is + consistent (class attribute, removed during class creation) +- :meth:`~Enum._generate_next_value_` -- used to get an appropriate value for + an enum member; may be overridden .. note:: - For standard :class:`Enum` classes the next value chosen is the last value seen - incremented by one. + For standard :class:`Enum` classes the next value chosen is the highest + value seen incremented by one. For :class:`Flag` classes the next value chosen will be the next highest - power-of-two, regardless of the last value seen. + power-of-two. .. versionadded:: 3.6 ``_missing_``, ``_order_``, ``_generate_next_value_`` .. versionadded:: 3.7 ``_ignore_`` +.. versionadded:: 3.13 ``_add_alias_``, ``_add_value_alias_`` --------------- diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index cd85df8723a76b..b67215b8b3a362 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -429,9 +429,11 @@ The following exceptions are the exceptions that are usually raised. :meth:`~iterator.__next__` method to signal that there are no further items produced by the iterator. - The exception object has a single attribute :attr:`value`, which is - given as an argument when constructing the exception, and defaults - to :const:`None`. + .. attribute:: StopIteration.value + + The exception object has a single attribute :attr:`!value`, which is + given as an argument when constructing the exception, and defaults + to :const:`None`. When a :term:`generator` or :term:`coroutine` function returns, a new :exc:`StopIteration` instance is diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index a72f779f69714a..c731b6fd333275 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -57,7 +57,8 @@ are always available. They are listed here in alphabetical order. .. function:: abs(x) Return the absolute value of a number. The argument may be an - integer, a floating point number, or an object implementing :meth:`__abs__`. + integer, a floating point number, or an object implementing + :meth:`~object.__abs__`. If the argument is a complex number, its magnitude is returned. @@ -235,7 +236,7 @@ are always available. They are listed here in alphabetical order. :const:`False` if not. If this returns ``True``, it is still possible that a call fails, but if it is ``False``, calling *object* will never succeed. Note that classes are callable (calling a class returns a new instance); - instances are callable if their class has a :meth:`__call__` method. + instances are callable if their class has a :meth:`~object.__call__` method. .. versionadded:: 3.2 This function was first removed in Python 3.0 and then brought back @@ -432,15 +433,18 @@ are always available. They are listed here in alphabetical order. Without arguments, return the list of names in the current local scope. With an argument, attempt to return a list of valid attributes for that object. - If the object has a method named :meth:`__dir__`, this method will be called and + If the object has a method named :meth:`~object.__dir__`, + this method will be called and must return the list of attributes. This allows objects that implement a custom - :func:`__getattr__` or :func:`__getattribute__` function to customize the way + :func:`~object.__getattr__` or :func:`~object.__getattribute__` function + to customize the way :func:`dir` reports their attributes. - If the object does not provide :meth:`__dir__`, the function tries its best to - gather information from the object's :attr:`~object.__dict__` attribute, if defined, and + If the object does not provide :meth:`~object.__dir__`, + the function tries its best to gather information from the object's + :attr:`~object.__dict__` attribute, if defined, and from its type object. The resulting list is not necessarily complete and may - be inaccurate when the object has a custom :func:`__getattr__`. + be inaccurate when the object has a custom :func:`~object.__getattr__`. The default :func:`dir` mechanism behaves differently with different types of objects, as it attempts to produce the most relevant, rather than complete, @@ -664,7 +668,7 @@ are always available. They are listed here in alphabetical order. sign: "+" | "-" infinity: "Infinity" | "inf" nan: "nan" - digitpart: `digit` (["_"] `digit`)* + digitpart: `!digit` (["_"] `!digit`)* number: [`digitpart`] "." `digitpart` | `digitpart` ["."] exponent: ("e" | "E") ["+" | "-"] `digitpart` floatnumber: number [`exponent`] @@ -727,8 +731,8 @@ are always available. They are listed here in alphabetical order. A call to ``format(value, format_spec)`` is translated to ``type(value).__format__(value, format_spec)`` which bypasses the instance - dictionary when searching for the value's :meth:`__format__` method. A - :exc:`TypeError` exception is raised if the method search reaches + dictionary when searching for the value's :meth:`~object.__format__` method. + A :exc:`TypeError` exception is raised if the method search reaches :mod:`object` and the *format_spec* is non-empty, or if either the *format_spec* or the return value are not strings. @@ -792,9 +796,9 @@ are always available. They are listed here in alphabetical order. .. note:: - For objects with custom :meth:`__hash__` methods, note that :func:`hash` + For objects with custom :meth:`~object.__hash__` methods, + note that :func:`hash` truncates the return value based on the bit width of the host machine. - See :meth:`__hash__ ` for details. .. function:: help() help(request) @@ -982,7 +986,8 @@ are always available. They are listed here in alphabetical order. Return an :term:`iterator` object. The first argument is interpreted very differently depending on the presence of the second argument. Without a second argument, *object* must be a collection object which supports the - :term:`iterable` protocol (the :meth:`__iter__` method), or it must support + :term:`iterable` protocol (the :meth:`~object.__iter__` method), + or it must support the sequence protocol (the :meth:`~object.__getitem__` method with integer arguments starting at ``0``). If it does not support either of those protocols, :exc:`TypeError` is raised. If the second argument, *sentinel*, is given, @@ -1221,7 +1226,7 @@ are always available. They are listed here in alphabetical order. *buffering* is an optional integer used to set the buffering policy. Pass 0 to switch buffering off (only allowed in binary mode), 1 to select line - buffering (only usable in text mode), and an integer > 1 to indicate the size + buffering (only usable when writing in text mode), and an integer > 1 to indicate the size in bytes of a fixed-size chunk buffer. Note that specifying a buffer size this way applies for binary buffered I/O, but ``TextIOWrapper`` (i.e., files opened with ``mode='r+'``) would have another buffering. To disable buffering in @@ -1500,38 +1505,44 @@ are always available. They are listed here in alphabetical order. """Get the current voltage.""" return self._voltage - The ``@property`` decorator turns the :meth:`voltage` method into a "getter" + The ``@property`` decorator turns the :meth:`!voltage` method into a "getter" for a read-only attribute with the same name, and it sets the docstring for *voltage* to "Get the current voltage." - A property object has :attr:`~property.getter`, :attr:`~property.setter`, - and :attr:`~property.deleter` methods usable as decorators that create a - copy of the property with the corresponding accessor function set to the - decorated function. This is best explained with an example:: + .. decorator:: property.getter + .. decorator:: property.setter + .. decorator:: property.deleter - class C: - def __init__(self): - self._x = None + A property object has ``getter``, ``setter``, + and ``deleter`` methods usable as decorators that create a + copy of the property with the corresponding accessor function set to the + decorated function. This is best explained with an example: - @property - def x(self): - """I'm the 'x' property.""" - return self._x + .. testcode:: - @x.setter - def x(self, value): - self._x = value + class C: + def __init__(self): + self._x = None - @x.deleter - def x(self): - del self._x + @property + def x(self): + """I'm the 'x' property.""" + return self._x - This code is exactly equivalent to the first example. Be sure to give the - additional functions the same name as the original property (``x`` in this - case.) + @x.setter + def x(self, value): + self._x = value - The returned property object also has the attributes ``fget``, ``fset``, and - ``fdel`` corresponding to the constructor arguments. + @x.deleter + def x(self): + del self._x + + This code is exactly equivalent to the first example. Be sure to give the + additional functions the same name as the original property (``x`` in this + case.) + + The returned property object also has the attributes ``fget``, ``fset``, and + ``fdel`` corresponding to the constructor arguments. .. versionchanged:: 3.5 The docstrings of property objects are now writeable. @@ -1554,7 +1565,8 @@ are always available. They are listed here in alphabetical order. representation is a string enclosed in angle brackets that contains the name of the type of the object together with additional information often including the name and address of the object. A class can control what this - function returns for its instances by defining a :meth:`__repr__` method. + function returns for its instances + by defining a :meth:`~object.__repr__` method. If :func:`sys.displayhook` is not accessible, this function will raise :exc:`RuntimeError`. @@ -1562,9 +1574,9 @@ are always available. They are listed here in alphabetical order. .. function:: reversed(seq) Return a reverse :term:`iterator`. *seq* must be an object which has - a :meth:`__reversed__` method or supports the sequence protocol (the - :meth:`__len__` method and the :meth:`~object.__getitem__` method with integer - arguments starting at ``0``). + a :meth:`~object.__reversed__` method or supports the sequence protocol (the + :meth:`~object.__len__` method and the :meth:`~object.__getitem__` method + with integer arguments starting at ``0``). .. function:: round(number, ndigits=None) @@ -1635,13 +1647,21 @@ are always available. They are listed here in alphabetical order. Return a :term:`slice` object representing the set of indices specified by ``range(start, stop, step)``. The *start* and *step* arguments default to - ``None``. Slice objects have read-only data attributes :attr:`~slice.start`, - :attr:`~slice.stop`, and :attr:`~slice.step` which merely return the argument - values (or their default). They have no other explicit functionality; - however, they are used by NumPy and other third-party packages. + ``None``. + + .. attribute:: slice.start + .. attribute:: slice.stop + .. attribute:: slice.step + + Slice objects have read-only data attributes :attr:`!start`, + :attr:`!stop`, and :attr:`!step` which merely return the argument + values (or their default). They have no other explicit functionality; + however, they are used by NumPy and other third-party packages. + Slice objects are also generated when extended indexing syntax is used. For example: ``a[start:stop:step]`` or ``a[start:stop, i]``. See - :func:`itertools.islice` for an alternate version that returns an iterator. + :func:`itertools.islice` for an alternate version that returns an + :term:`iterator`. .. versionchanged:: 3.12 Slice objects are now :term:`hashable` (provided :attr:`~slice.start`, @@ -1808,7 +1828,8 @@ are always available. They are listed here in alphabetical order. Note that :func:`super` is implemented as part of the binding process for explicit dotted attribute lookups such as ``super().__getitem__(name)``. - It does so by implementing its own :meth:`__getattribute__` method for searching + It does so by implementing its own :meth:`~object.__getattribute__` method + for searching classes in a predictable order that supports cooperative multiple inheritance. Accordingly, :func:`super` is undefined for implicit lookups using statements or operators such as ``super()[name]``. diff --git a/Doc/library/gc.rst b/Doc/library/gc.rst index 331c071cda7692..82277aa52aee01 100644 --- a/Doc/library/gc.rst +++ b/Doc/library/gc.rst @@ -42,8 +42,8 @@ The :mod:`gc` module provides the following functions: With no arguments, run a full collection. The optional argument *generation* may be an integer specifying which generation to collect (from 0 to 2). A - :exc:`ValueError` is raised if the generation number is invalid. The number of - unreachable objects found is returned. + :exc:`ValueError` is raised if the generation number is invalid. The sum of + collected objects and uncollectable objects is returned. The free lists maintained for a number of built-in types are cleared whenever a full collection or collection of the highest generation (2) diff --git a/Doc/library/getpass.rst b/Doc/library/getpass.rst index 5c79daf0f47d8e..54c84d45a59856 100644 --- a/Doc/library/getpass.rst +++ b/Doc/library/getpass.rst @@ -46,7 +46,10 @@ The :mod:`getpass` module provides two functions: :envvar:`USER`, :envvar:`!LNAME` and :envvar:`USERNAME`, in order, and returns the value of the first one which is set to a non-empty string. If none are set, the login name from the password database is returned on - systems which support the :mod:`pwd` module, otherwise, an exception is - raised. + systems which support the :mod:`pwd` module, otherwise, an :exc:`OSError` + is raised. In general, this function should be preferred over :func:`os.getlogin()`. + + .. versionchanged:: 3.13 + Previously, various exceptions beyond just :exc:`OSError` were raised. diff --git a/Doc/library/gettext.rst b/Doc/library/gettext.rst index dc6cf5533fccbe..41beac3e0c7396 100644 --- a/Doc/library/gettext.rst +++ b/Doc/library/gettext.rst @@ -257,7 +257,7 @@ are the methods of :class:`!NullTranslations`: .. method:: info() - Return the "protected" :attr:`_info` variable, a dictionary containing + Return a dictionary containing the metadata found in the message catalog file. @@ -296,9 +296,9 @@ are the methods of :class:`!NullTranslations`: The :class:`GNUTranslations` class ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The :mod:`gettext` module provides one additional class derived from +The :mod:`!gettext` module provides one additional class derived from :class:`NullTranslations`: :class:`GNUTranslations`. This class overrides -:meth:`_parse` to enable reading GNU :program:`gettext` format :file:`.mo` files +:meth:`!_parse` to enable reading GNU :program:`gettext` format :file:`.mo` files in both big-endian and little-endian format. :class:`GNUTranslations` parses optional metadata out of the translation @@ -306,7 +306,7 @@ catalog. It is convention with GNU :program:`gettext` to include metadata as the translation for the empty string. This metadata is in :rfc:`822`\ -style ``key: value`` pairs, and should contain the ``Project-Id-Version`` key. If the key ``Content-Type`` is found, then the ``charset`` property is used to -initialize the "protected" :attr:`_charset` instance variable, defaulting to +initialize the "protected" :attr:`!_charset` instance variable, defaulting to ``None`` if not found. If the charset encoding is specified, then all message ids and message strings read from the catalog are converted to Unicode using this encoding, else ASCII is assumed. @@ -315,7 +315,7 @@ Since message ids are read as Unicode strings too, all ``*gettext()`` methods will assume message ids as Unicode strings, not byte strings. The entire set of key/value pairs are placed into a dictionary and set as the -"protected" :attr:`_info` instance variable. +"protected" :attr:`!_info` instance variable. If the :file:`.mo` file's magic number is invalid, the major version number is unexpected, or if other problems occur while reading the file, instantiating a @@ -636,9 +636,9 @@ implementations, and valuable experience to the creation of this module: .. rubric:: Footnotes -.. [#] The default locale directory is system dependent; for example, on RedHat Linux +.. [#] The default locale directory is system dependent; for example, on Red Hat Linux it is :file:`/usr/share/locale`, but on Solaris it is :file:`/usr/lib/locale`. - The :mod:`gettext` module does not try to support these system dependent + The :mod:`!gettext` module does not try to support these system dependent defaults; instead its default is :file:`{sys.base_prefix}/share/locale` (see :data:`sys.base_prefix`). For this reason, it is always best to call :func:`bindtextdomain` with an explicit absolute path at the start of your diff --git a/Doc/library/gzip.rst b/Doc/library/gzip.rst index f931d0e399c9f2..50cde09fa10a9d 100644 --- a/Doc/library/gzip.rst +++ b/Doc/library/gzip.rst @@ -105,7 +105,7 @@ The module defines the following items: should only be provided in compression mode. If omitted or ``None``, the current time is used. See the :attr:`mtime` attribute for more details. - Calling a :class:`GzipFile` object's :meth:`close` method does not close + Calling a :class:`GzipFile` object's :meth:`!close` method does not close *fileobj*, since you might wish to append more material after the compressed data. This also allows you to pass an :class:`io.BytesIO` object opened for writing as *fileobj*, and retrieve the resulting memory buffer using the diff --git a/Doc/library/importlib.resources.rst b/Doc/library/importlib.resources.rst index 3de97e80311a17..a5adf0b8546dbf 100644 --- a/Doc/library/importlib.resources.rst +++ b/Doc/library/importlib.resources.rst @@ -50,7 +50,7 @@ for example, a package and its resources can be imported from a zip file using ``get_resource_reader(fullname)`` method as specified by :class:`importlib.resources.abc.ResourceReader`. -.. data:: Anchor +.. class:: Anchor Represents an anchor for resources, either a :class:`module object ` or a module name as a string. Defined as @@ -63,7 +63,7 @@ for example, a package and its resources can be imported from a zip file using (think files). A Traversable may contain other containers (think subdirectories). - *anchor* is an optional :data:`Anchor`. If the anchor is a + *anchor* is an optional :class:`Anchor`. If the anchor is a package, resources are resolved from that package. If a module, resources are resolved adjacent to that module (in the same package or the package root). If the anchor is omitted, the caller's module @@ -72,10 +72,10 @@ for example, a package and its resources can be imported from a zip file using .. versionadded:: 3.9 .. versionchanged:: 3.12 - "package" parameter was renamed to "anchor". "anchor" can now + *package* parameter was renamed to *anchor*. *anchor* can now be a non-package module and if omitted will default to the caller's - module. "package" is still accepted for compatibility but will raise - a DeprecationWarning. Consider passing the anchor positionally or + module. *package* is still accepted for compatibility but will raise + a :exc:`DeprecationWarning`. Consider passing the anchor positionally or using ``importlib_resources >= 5.10`` for a compatible interface on older Pythons. @@ -96,4 +96,4 @@ for example, a package and its resources can be imported from a zip file using .. versionadded:: 3.9 .. versionchanged:: 3.12 - Added support for ``traversable`` representing a directory. + Added support for *traversable* representing a directory. diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index fc954724bb72fe..2402bc5cd3ee2c 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1145,7 +1145,7 @@ find and load modules. .. versionadded:: 3.4 -.. class:: NamespaceLoader(name, path, path_finder): +.. class:: NamespaceLoader(name, path, path_finder) A concrete implementation of :class:`importlib.abc.InspectLoader` for namespace packages. This is an alias for a private class and is only made diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index b463c0b6d0e402..8381e508139fbd 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -1,6 +1,11 @@ :mod:`inspect` --- Inspect live objects ======================================= +.. testsetup:: * + + import inspect + from inspect import * + .. module:: inspect :synopsis: Extract information and source code from live objects. @@ -268,7 +273,7 @@ attributes (see :ref:`import-mod-attrs` for module attributes): :func:`getmembers` will only return class attributes defined in the metaclass when the argument is a class and those attributes have been - listed in the metaclass' custom :meth:`__dir__`. + listed in the metaclass' custom :meth:`~object.__dir__`. .. function:: getmembers_static(object[, predicate]) @@ -387,7 +392,11 @@ attributes (see :ref:`import-mod-attrs` for module attributes): Return ``True`` if the object can be used in :keyword:`await` expression. Can also be used to distinguish generator-based coroutines from regular - generators:: + generators: + + .. testcode:: + + import types def gen(): yield @@ -404,13 +413,15 @@ attributes (see :ref:`import-mod-attrs` for module attributes): .. function:: isasyncgenfunction(object) Return ``True`` if the object is an :term:`asynchronous generator` function, - for example:: + for example: + + .. doctest:: - >>> async def agen(): - ... yield 1 - ... - >>> inspect.isasyncgenfunction(agen) - True + >>> async def agen(): + ... yield 1 + ... + >>> inspect.isasyncgenfunction(agen) + True .. versionadded:: 3.6 @@ -476,12 +487,13 @@ attributes (see :ref:`import-mod-attrs` for module attributes): has a :meth:`~object.__get__` method but not a :meth:`~object.__set__` method, but beyond that the set of attributes varies. A :attr:`~definition.__name__` attribute is usually - sensible, and :attr:`__doc__` often is. + sensible, and :attr:`!__doc__` often is. Methods implemented via descriptors that also pass one of the other tests return ``False`` from the :func:`ismethoddescriptor` test, simply because the other tests promise more -- you can, e.g., count on having the - :attr:`__func__` attribute (etc) when an object passes :func:`ismethod`. + :attr:`~method.__func__` attribute (etc) when an object passes + :func:`ismethod`. .. function:: isdatadescriptor(object) @@ -492,7 +504,7 @@ attributes (see :ref:`import-mod-attrs` for module attributes): Examples are properties (defined in Python), getsets, and members. The latter two are defined in C and there are more specific tests available for those types, which is robust across Python implementations. Typically, data - descriptors will also have :attr:`~definition.__name__` and :attr:`__doc__` attributes + descriptors will also have :attr:`~definition.__name__` and :attr:`!__doc__` attributes (properties, getsets, and members have both of these attributes), but this is not guaranteed. @@ -614,13 +626,16 @@ Introspecting callables with the Signature object .. versionadded:: 3.3 -The Signature object represents the call signature of a callable object and its -return annotation. To retrieve a Signature object, use the :func:`signature` +The :class:`Signature` object represents the call signature of a callable object +and its return annotation. To retrieve a :class:`!Signature` object, +use the :func:`!signature` function. .. function:: signature(callable, *, follow_wrapped=True, globals=None, locals=None, eval_str=False) - Return a :class:`Signature` object for the given ``callable``:: + Return a :class:`Signature` object for the given *callable*: + + .. doctest:: >>> from inspect import signature >>> def foo(a, *, b:int, **kwargs): @@ -629,10 +644,10 @@ function. >>> sig = signature(foo) >>> str(sig) - '(a, *, b:int, **kwargs)' + '(a, *, b: int, **kwargs)' >>> str(sig.parameters['b']) - 'b:int' + 'b: int' >>> sig.parameters['b'].annotation @@ -646,29 +661,30 @@ function. For objects defined in modules using stringized annotations (``from __future__ import annotations``), :func:`signature` will attempt to automatically un-stringize the annotations using - :func:`inspect.get_annotations()`. The - ``global``, ``locals``, and ``eval_str`` parameters are passed - into :func:`inspect.get_annotations()` when resolving the - annotations; see the documentation for :func:`inspect.get_annotations()` + :func:`get_annotations`. The + *globals*, *locals*, and *eval_str* parameters are passed + into :func:`get_annotations` when resolving the + annotations; see the documentation for :func:`get_annotations` for instructions on how to use these parameters. Raises :exc:`ValueError` if no signature can be provided, and :exc:`TypeError` if that type of object is not supported. Also, - if the annotations are stringized, and ``eval_str`` is not false, - the ``eval()`` call(s) to un-stringize the annotations could - potentially raise any kind of exception. + if the annotations are stringized, and *eval_str* is not false, + the ``eval()`` call(s) to un-stringize the annotations in :func:`get_annotations` + could potentially raise any kind of exception. A slash(/) in the signature of a function denotes that the parameters prior to it are positional-only. For more info, see :ref:`the FAQ entry on positional-only parameters `. - .. versionadded:: 3.5 - ``follow_wrapped`` parameter. Pass ``False`` to get a signature of - ``callable`` specifically (``callable.__wrapped__`` will not be used to + .. versionchanged:: 3.5 + The *follow_wrapped* parameter was added. + Pass ``False`` to get a signature of + *callable* specifically (``callable.__wrapped__`` will not be used to unwrap decorated callables.) - .. versionadded:: 3.10 - ``globals``, ``locals``, and ``eval_str`` parameters. + .. versionchanged:: 3.10 + The *globals*, *locals*, and *eval_str* parameters were added. .. note:: @@ -679,7 +695,8 @@ function. .. class:: Signature(parameters=None, *, return_annotation=Signature.empty) - A Signature object represents the call signature of a function and its return + A :class:`!Signature` object represents the call signature of a function + and its return annotation. For each parameter accepted by the function it stores a :class:`Parameter` object in its :attr:`parameters` collection. @@ -689,14 +706,14 @@ function. positional-only first, then positional-or-keyword, and that parameters with defaults follow parameters without defaults. - The optional *return_annotation* argument, can be an arbitrary Python object, - is the "return" annotation of the callable. + The optional *return_annotation* argument can be an arbitrary Python object. + It represents the "return" annotation of the callable. - Signature objects are *immutable*. Use :meth:`Signature.replace` or + :class:`!Signature` objects are *immutable*. Use :meth:`Signature.replace` or :func:`copy.replace` to make a modified copy. .. versionchanged:: 3.5 - Signature objects are picklable and :term:`hashable`. + :class:`!Signature` objects are now picklable and :term:`hashable`. .. attribute:: Signature.empty @@ -733,13 +750,15 @@ function. .. method:: Signature.replace(*[, parameters][, return_annotation]) - Create a new Signature instance based on the instance :meth:`replace` was invoked - on. It is possible to pass different ``parameters`` and/or - ``return_annotation`` to override the corresponding properties of the base - signature. To remove return_annotation from the copied Signature, pass in + Create a new :class:`Signature` instance based on the instance + :meth:`replace` was invoked on. + It is possible to pass different *parameters* and/or + *return_annotation* to override the corresponding properties of the base + signature. To remove ``return_annotation`` from the copied + :class:`!Signature`, pass in :attr:`Signature.empty`. - :: + .. doctest:: >>> def test(a, b): ... pass @@ -749,38 +768,50 @@ function. >>> str(new_sig) "(a, b) -> 'new return anno'" - Signature objects are also supported by generic function + :class:`Signature` objects are also supported by the generic function :func:`copy.replace`. - .. classmethod:: Signature.from_callable(obj, *, follow_wrapped=True, globalns=None, localns=None) + .. method:: format(*, max_width=None) + + Create a string representation of the :class:`Signature` object. + + If *max_width* is passed, the method will attempt to fit + the signature into lines of at most *max_width* characters. + If the signature is longer than *max_width*, + all parameters will be on separate lines. + + .. versionadded:: 3.13 + + .. classmethod:: Signature.from_callable(obj, *, follow_wrapped=True, globals=None, locals=None, eval_str=False) Return a :class:`Signature` (or its subclass) object for a given callable - ``obj``. Pass ``follow_wrapped=False`` to get a signature of ``obj`` - without unwrapping its ``__wrapped__`` chain. ``globalns`` and - ``localns`` will be used as the namespaces when resolving annotations. + *obj*. + + This method simplifies subclassing of :class:`Signature`: - This method simplifies subclassing of :class:`Signature`:: + .. testcode:: - class MySignature(Signature): - pass - sig = MySignature.from_callable(min) - assert isinstance(sig, MySignature) + class MySignature(Signature): + pass + sig = MySignature.from_callable(sum) + assert isinstance(sig, MySignature) Its behavior is otherwise identical to that of :func:`signature`. .. versionadded:: 3.5 - .. versionadded:: 3.10 - ``globalns`` and ``localns`` parameters. + .. versionchanged:: 3.10 + The *globals*, *locals*, and *eval_str* parameters were added. .. class:: Parameter(name, kind, *, default=Parameter.empty, annotation=Parameter.empty) - Parameter objects are *immutable*. Instead of modifying a Parameter object, + :class:`!Parameter` objects are *immutable*. + Instead of modifying a :class:`!Parameter` object, you can use :meth:`Parameter.replace` or :func:`copy.replace` to create a modified copy. .. versionchanged:: 3.5 - Parameter objects are picklable and :term:`hashable`. + Parameter objects are now picklable and :term:`hashable`. .. attribute:: Parameter.empty @@ -799,7 +830,7 @@ function. expressions. .. versionchanged:: 3.6 - These parameter names are exposed by this module as names like + These parameter names are now exposed by this module as names like ``implicit0``. .. attribute:: Parameter.default @@ -849,7 +880,9 @@ function. | | definition. | +------------------------+----------------------------------------------+ - Example: print all keyword-only arguments without default values:: + Example: print all keyword-only arguments without default values: + + .. doctest:: >>> def foo(a, b, *, c, d=10): ... pass @@ -863,11 +896,13 @@ function. .. attribute:: Parameter.kind.description - Describes a enum value of Parameter.kind. + Describes a enum value of :attr:`Parameter.kind`. .. versionadded:: 3.8 - Example: print all descriptions of arguments:: + Example: print all descriptions of arguments: + + .. doctest:: >>> def foo(a, b, *, c, d=10): ... pass @@ -882,12 +917,12 @@ function. .. method:: Parameter.replace(*[, name][, kind][, default][, annotation]) - Create a new Parameter instance based on the instance replaced was invoked - on. To override a :class:`Parameter` attribute, pass the corresponding + Create a new :class:`Parameter` instance based on the instance replaced was invoked + on. To override a :class:`!Parameter` attribute, pass the corresponding argument. To remove a default value or/and an annotation from a - Parameter, pass :attr:`Parameter.empty`. + :class:`!Parameter`, pass :attr:`Parameter.empty`. - :: + .. doctest:: >>> from inspect import Parameter >>> param = Parameter('foo', Parameter.KEYWORD_ONLY, default=42) @@ -898,12 +933,13 @@ function. 'foo=42' >>> str(param.replace(default=Parameter.empty, annotation='spam')) - "foo:'spam'" + "foo: 'spam'" - Parameter objects are also supported by generic function :func:`copy.replace`. + :class:`Parameter` objects are also supported by the generic function + :func:`copy.replace`. .. versionchanged:: 3.4 - In Python 3.3 Parameter objects were allowed to have ``name`` set + In Python 3.3 :class:`Parameter` objects were allowed to have ``name`` set to ``None`` if their ``kind`` was set to ``POSITIONAL_ONLY``. This is no longer permitted. @@ -956,18 +992,20 @@ function. For variable-keyword arguments (``**kwargs``) the default is an empty dict. - :: + .. doctest:: - >>> def foo(a, b='ham', *args): pass - >>> ba = inspect.signature(foo).bind('spam') - >>> ba.apply_defaults() - >>> ba.arguments - {'a': 'spam', 'b': 'ham', 'args': ()} + >>> def foo(a, b='ham', *args): pass + >>> ba = inspect.signature(foo).bind('spam') + >>> ba.apply_defaults() + >>> ba.arguments + {'a': 'spam', 'b': 'ham', 'args': ()} .. versionadded:: 3.5 The :attr:`args` and :attr:`kwargs` properties can be used to invoke - functions:: + functions: + + .. testcode:: def test(a, *, b): ... @@ -1086,20 +1124,22 @@ Classes and functions ``**`` arguments, if any) to their values from *args* and *kwds*. In case of invoking *func* incorrectly, i.e. whenever ``func(*args, **kwds)`` would raise an exception because of incompatible signature, an exception of the same type - and the same or similar message is raised. For example:: - - >>> from inspect import getcallargs - >>> def f(a, b=1, *pos, **named): - ... pass - ... - >>> getcallargs(f, 1, 2, 3) == {'a': 1, 'named': {}, 'b': 2, 'pos': (3,)} - True - >>> getcallargs(f, a=2, x=4) == {'a': 2, 'named': {'x': 4}, 'b': 1, 'pos': ()} - True - >>> getcallargs(f) - Traceback (most recent call last): - ... - TypeError: f() missing 1 required positional argument: 'a' + and the same or similar message is raised. For example: + + .. doctest:: + + >>> from inspect import getcallargs + >>> def f(a, b=1, *pos, **named): + ... pass + ... + >>> getcallargs(f, 1, 2, 3) == {'a': 1, 'named': {}, 'b': 2, 'pos': (3,)} + True + >>> getcallargs(f, a=2, x=4) == {'a': 2, 'named': {'x': 4}, 'b': 1, 'pos': ()} + True + >>> getcallargs(f) + Traceback (most recent call last): + ... + TypeError: f() missing 1 required positional argument: 'a' .. versionadded:: 3.2 @@ -1401,7 +1441,8 @@ Fetching attributes statically Both :func:`getattr` and :func:`hasattr` can trigger code execution when fetching or checking for the existence of attributes. Descriptors, like -properties, will be invoked and :meth:`__getattr__` and :meth:`__getattribute__` +properties, will be invoked and :meth:`~object.__getattr__` and +:meth:`~object.__getattribute__` may be called. For cases where you want passive introspection, like documentation tools, this @@ -1411,7 +1452,8 @@ but avoids executing code when it fetches attributes. .. function:: getattr_static(obj, attr, default=None) Retrieve attributes without triggering dynamic lookup via the - descriptor protocol, :meth:`__getattr__` or :meth:`__getattribute__`. + descriptor protocol, :meth:`~object.__getattr__` + or :meth:`~object.__getattribute__`. Note: this function may not be able to retrieve all attributes that getattr can fetch (like dynamically created attributes) @@ -1554,8 +1596,8 @@ updated as expected: Code Objects Bit Flags ---------------------- -Python code objects have a ``co_flags`` attribute, which is a bitmap of -the following flags: +Python code objects have a :attr:`~codeobject.co_flags` attribute, +which is a bitmap of the following flags: .. data:: CO_OPTIMIZED @@ -1563,8 +1605,8 @@ the following flags: .. data:: CO_NEWLOCALS - If set, a new dict will be created for the frame's ``f_locals`` when - the code object is executed. + If set, a new dict will be created for the frame's :attr:`~frame.f_locals` + when the code object is executed. .. data:: CO_VARARGS diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index f97e7f720ae4e4..8a4254cf15ebe2 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -798,10 +798,10 @@ which incur interpreter overhead. "Return first n items of the iterable as a list" return list(islice(iterable, n)) - def prepend(value, iterator): - "Prepend a single value in front of an iterator" + def prepend(value, iterable): + "Prepend a single value in front of an iterable" # prepend(1, [2, 3, 4]) --> 1 2 3 4 - return chain([value], iterator) + return chain([value], iterable) def tabulate(function, start=0): "Return function(0), function(1), ..." @@ -916,9 +916,9 @@ which incur interpreter overhead. args = [iter(iterable)] * n if incomplete == 'fill': return zip_longest(*args, fillvalue=fillvalue) - if incomplete == 'strict': + elif incomplete == 'strict': return zip(*args, strict=True) - if incomplete == 'ignore': + elif incomplete == 'ignore': return zip(*args) else: raise ValueError('Expected fill, strict, or ignore') diff --git a/Doc/library/multiprocessing.shared_memory.rst b/Doc/library/multiprocessing.shared_memory.rst index f453e6403d932d..671130d9b29fc0 100644 --- a/Doc/library/multiprocessing.shared_memory.rst +++ b/Doc/library/multiprocessing.shared_memory.rst @@ -36,7 +36,7 @@ or other communications requiring the serialization/deserialization and copying of data. -.. class:: SharedMemory(name=None, create=False, size=0) +.. class:: SharedMemory(name=None, create=False, size=0, *, track=True) Creates a new shared memory block or attaches to an existing shared memory block. Each shared memory block is assigned a unique name. @@ -64,26 +64,45 @@ copying of data. memory block may be larger or equal to the size requested. When attaching to an existing shared memory block, the ``size`` parameter is ignored. + *track*, when enabled, registers the shared memory block with a resource + tracker process on platforms where the OS does not do this automatically. + The resource tracker ensures proper cleanup of the shared memory even + if all other processes with access to the memory exit without doing so. + Python processes created from a common ancestor using :mod:`multiprocessing` + facilities share a single resource tracker process, and the lifetime of + shared memory segments is handled automatically among these processes. + Python processes created in any other way will receive their own + resource tracker when accessing shared memory with *track* enabled. + This will cause the shared memory to be deleted by the resource tracker + of the first process that terminates. + To avoid this issue, users of :mod:`subprocess` or standalone Python + processes should set *track* to ``False`` when there is already another + process in place that does the bookkeeping. + *track* is ignored on Windows, which has its own tracking and + automatically deletes shared memory when all handles to it have been closed. + + .. versionchanged:: 3.13 Added *track* parameter. + .. method:: close() - Closes access to the shared memory from this instance. In order to - ensure proper cleanup of resources, all instances should call - ``close()`` once the instance is no longer needed. Note that calling - ``close()`` does not cause the shared memory block itself to be - destroyed. + Closes the file descriptor/handle to the shared memory from this + instance. :meth:`close()` should be called once access to the shared + memory block from this instance is no longer needed. Depending + on operating system, the underlying memory may or may not be freed + even if all handles to it have been closed. To ensure proper cleanup, + use the :meth:`unlink()` method. .. method:: unlink() - Requests that the underlying shared memory block be destroyed. In - order to ensure proper cleanup of resources, ``unlink()`` should be - called once (and only once) across all processes which have need - for the shared memory block. After requesting its destruction, a - shared memory block may or may not be immediately destroyed and - this behavior may differ across platforms. Attempts to access data - inside the shared memory block after ``unlink()`` has been called may - result in memory access errors. Note: the last process relinquishing - its hold on a shared memory block may call ``unlink()`` and - :meth:`close()` in either order. + Deletes the underlying shared memory block. This should be called only + once per shared memory block regardless of the number of handles to it, + even in other processes. + :meth:`unlink()` and :meth:`close()` can be called in any order, but + trying to access data inside a shared memory block after :meth:`unlink()` + may result in memory access errors, depending on platform. + + This method has no effect on Windows, where the only way to delete a + shared memory block is to close all handles. .. attribute:: buf diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 8ee89a003a339a..43200e269f56f4 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -306,7 +306,7 @@ Pure paths provide the following methods and properties: .. attribute:: PurePath.pathmod The implementation of the :mod:`os.path` module used for low-level path - operations: either ``posixpath`` or ``ntpath``. + operations: either :mod:`posixpath` or :mod:`ntpath`. .. versionadded:: 3.13 @@ -1017,15 +1017,21 @@ call fails (for example because the path doesn't exist). future Python release, patterns with this ending will match both files and directories. Add a trailing slash to match only directories. -.. method:: Path.group() +.. method:: Path.group(*, follow_symlinks=True) - Return the name of the group owning the file. :exc:`KeyError` is raised + Return the name of the group owning the file. :exc:`KeyError` is raised if the file's gid isn't found in the system database. + This method normally follows symlinks; to get the group of the symlink, add + the argument ``follow_symlinks=False``. + .. versionchanged:: 3.13 Raises :exc:`UnsupportedOperation` if the :mod:`grp` module is not available. In previous versions, :exc:`NotImplementedError` was raised. + .. versionchanged:: 3.13 + The *follow_symlinks* parameter was added. + .. method:: Path.is_dir(*, follow_symlinks=True) @@ -1291,15 +1297,21 @@ call fails (for example because the path doesn't exist). '#!/usr/bin/env python3\n' -.. method:: Path.owner() +.. method:: Path.owner(*, follow_symlinks=True) - Return the name of the user owning the file. :exc:`KeyError` is raised + Return the name of the user owning the file. :exc:`KeyError` is raised if the file's uid isn't found in the system database. + This method normally follows symlinks; to get the owner of the symlink, add + the argument ``follow_symlinks=False``. + .. versionchanged:: 3.13 Raises :exc:`UnsupportedOperation` if the :mod:`pwd` module is not available. In previous versions, :exc:`NotImplementedError` was raised. + .. versionchanged:: 3.13 + The *follow_symlinks* parameter was added. + .. method:: Path.read_bytes() @@ -1314,7 +1326,7 @@ call fails (for example because the path doesn't exist). .. versionadded:: 3.5 -.. method:: Path.read_text(encoding=None, errors=None) +.. method:: Path.read_text(encoding=None, errors=None, newline=None) Return the decoded contents of the pointed-to file as a string:: @@ -1329,6 +1341,8 @@ call fails (for example because the path doesn't exist). .. versionadded:: 3.5 + .. versionchanged:: 3.13 + The *newline* parameter was added. .. method:: Path.readlink() diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index bbc6aacc62aafa..2495dcf50bb17f 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -570,10 +570,27 @@ can be overridden by the local file. Start an interactive interpreter (using the :mod:`code` module) whose global namespace contains all the (global and local) names found in the current - scope. + scope. Use ``exit()`` or ``quit()`` to exit the interpreter and return to + the debugger. + + .. note:: + + Because interact creates a new global namespace with the current global + and local namespace for execution, assignment to variables will not + affect the original namespaces. + However, modification to the mutable objects will be reflected in the + original namespaces. .. versionadded:: 3.2 + .. versionadded:: 3.13 + ``exit()`` and ``quit()`` can be used to exit :pdbcmd:`interact` + command. + + .. versionchanged:: 3.13 + :pdbcmd:`interact` directs its output to the debugger's + output channel rather than :data:`sys.stderr`. + .. _debugger-aliases: .. pdbcommand:: alias [name [command]] diff --git a/Doc/library/profile.rst b/Doc/library/profile.rst index 4c60a1e0d781b0..cc059b66fcb84b 100644 --- a/Doc/library/profile.rst +++ b/Doc/library/profile.rst @@ -82,8 +82,8 @@ the following:: The first line indicates that 214 calls were monitored. Of those calls, 207 were :dfn:`primitive`, meaning that the call was not induced via recursion. The -next line: ``Ordered by: cumulative time``, indicates that the text string in the -far right column was used to sort the output. The column headings include: +next line: ``Ordered by: cumulative time`` indicates the output is sorted +by the ``cumtime`` values. The column headings include: ncalls for the number of calls. diff --git a/Doc/library/readline.rst b/Doc/library/readline.rst index 8fb0eca8df74d8..2e0f45ced30b9c 100644 --- a/Doc/library/readline.rst +++ b/Doc/library/readline.rst @@ -27,16 +27,15 @@ Readline library in general. .. note:: The underlying Readline library API may be implemented by - the ``libedit`` library instead of GNU readline. + the ``editline`` (``libedit``) library instead of GNU readline. On macOS the :mod:`readline` module detects which library is being used at run time. - The configuration file for ``libedit`` is different from that + The configuration file for ``editline`` is different from that of GNU readline. If you programmatically load configuration strings - you can check for the text "libedit" in :const:`readline.__doc__` - to differentiate between GNU readline and libedit. + you can use :data:`backend` to determine which library is being used. - If you use *editline*/``libedit`` readline emulation on macOS, the + If you use ``editline``/``libedit`` readline emulation on macOS, the initialization file located in your home directory is named ``.editrc``. For example, the following content in ``~/.editrc`` will turn ON *vi* keybindings and TAB completion:: @@ -44,6 +43,12 @@ Readline library in general. python:bind -v python:bind ^I rl_complete +.. data:: backend + + The name of the underlying Readline library being used, either + ``"readline"`` or ``"editline"``. + + .. versionadded:: 3.13 Init file --------- diff --git a/Doc/library/reprlib.rst b/Doc/library/reprlib.rst index 5ebb0a7780c37b..678a11c6f45490 100644 --- a/Doc/library/reprlib.rst +++ b/Doc/library/reprlib.rst @@ -10,7 +10,7 @@ -------------- -The :mod:`reprlib` module provides a means for producing object representations +The :mod:`!reprlib` module provides a means for producing object representations with limits on the size of the resulting strings. This is used in the Python debugger and may be useful in other contexts as well. @@ -58,29 +58,31 @@ This module provides a class, an instance, and a function: limits on most sizes. In addition to size-limiting tools, the module also provides a decorator for -detecting recursive calls to :meth:`__repr__` and substituting a placeholder -string instead. +detecting recursive calls to :meth:`~object.__repr__` and substituting a +placeholder string instead. .. index:: single: ...; placeholder .. decorator:: recursive_repr(fillvalue="...") - Decorator for :meth:`__repr__` methods to detect recursive calls within the + Decorator for :meth:`~object.__repr__` methods to detect recursive calls within the same thread. If a recursive call is made, the *fillvalue* is returned, - otherwise, the usual :meth:`__repr__` call is made. For example: - - >>> from reprlib import recursive_repr - >>> class MyList(list): - ... @recursive_repr() - ... def __repr__(self): - ... return '<' + '|'.join(map(repr, self)) + '>' - ... - >>> m = MyList('abc') - >>> m.append(m) - >>> m.append('x') - >>> print(m) - <'a'|'b'|'c'|...|'x'> + otherwise, the usual :meth:`!__repr__` call is made. For example: + + .. doctest:: + + >>> from reprlib import recursive_repr + >>> class MyList(list): + ... @recursive_repr() + ... def __repr__(self): + ... return '<' + '|'.join(map(repr, self)) + '>' + ... + >>> m = MyList('abc') + >>> m.append(m) + >>> m.append('x') + >>> print(m) + <'a'|'b'|'c'|...|'x'> .. versionadded:: 3.2 @@ -148,10 +150,10 @@ which format specific object types. with no line breaks or indentation, like the standard :func:`repr`. For example: - .. code-block:: pycon + .. doctest:: indent >>> example = [ - 1, 'spam', {'a': 2, 'b': 'spam eggs', 'c': {3: 4.5, 6: []}}, 'ham'] + ... 1, 'spam', {'a': 2, 'b': 'spam eggs', 'c': {3: 4.5, 6: []}}, 'ham'] >>> import reprlib >>> aRepr = reprlib.Repr() >>> print(aRepr.repr(example)) @@ -160,7 +162,7 @@ which format specific object types. If :attr:`~Repr.indent` is set to a string, each recursion level is placed on its own line, indented by that string: - .. code-block:: pycon + .. doctest:: indent >>> aRepr.indent = '-->' >>> print(aRepr.repr(example)) @@ -181,7 +183,7 @@ which format specific object types. Setting :attr:`~Repr.indent` to a positive integer value behaves as if it was set to a string with that number of spaces: - .. code-block:: pycon + .. doctest:: indent >>> aRepr.indent = 4 >>> print(aRepr.repr(example)) @@ -234,7 +236,9 @@ Subclassing Repr Objects The use of dynamic dispatching by :meth:`Repr.repr1` allows subclasses of :class:`Repr` to add support for additional built-in object types or to modify the handling of types already supported. This example shows how special support -for file objects could be added:: +for file objects could be added: + +.. testcode:: import reprlib import sys @@ -248,3 +252,7 @@ for file objects could be added:: aRepr = MyRepr() print(aRepr.repr(sys.stdin)) # prints '' + +.. testoutput:: + + diff --git a/Doc/library/shelve.rst b/Doc/library/shelve.rst index 219219af6fd87f..88802d717d7383 100644 --- a/Doc/library/shelve.rst +++ b/Doc/library/shelve.rst @@ -149,13 +149,14 @@ Restrictions .. class:: BsdDbShelf(dict, protocol=None, writeback=False, keyencoding='utf-8') - A subclass of :class:`Shelf` which exposes :meth:`first`, :meth:`!next`, - :meth:`previous`, :meth:`last` and :meth:`set_location` which are available - in the third-party :mod:`bsddb` module from `pybsddb + A subclass of :class:`Shelf` which exposes :meth:`!first`, :meth:`!next`, + :meth:`!previous`, :meth:`!last` and :meth:`!set_location` methods. + These are available + in the third-party :mod:`!bsddb` module from `pybsddb `_ but not in other database modules. The *dict* object passed to the constructor must support those methods. This is generally accomplished by calling one of - :func:`bsddb.hashopen`, :func:`bsddb.btopen` or :func:`bsddb.rnopen`. The + :func:`!bsddb.hashopen`, :func:`!bsddb.btopen` or :func:`!bsddb.rnopen`. The optional *protocol*, *writeback*, and *keyencoding* parameters have the same interpretation as for the :class:`Shelf` class. diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index d1949d698f5614..f61ef8b0ecc7ba 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -343,6 +343,12 @@ Directory and files operations .. versionchanged:: 3.12 Added the *onexc* parameter, deprecated *onerror*. + .. versionchanged:: 3.13 + :func:`!rmtree` now ignores :exc:`FileNotFoundError` exceptions for all + but the top-level path. + Exceptions other than :exc:`OSError` and subclasses of :exc:`!OSError` + are now always propagated to the caller. + .. attribute:: rmtree.avoids_symlink_attacks Indicates whether the current platform and implementation provides a diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 9ff1aa3984e828..e0a75304ef1606 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -23,7 +23,7 @@ all modern Unix systems, Windows, MacOS, and probably additional platforms. The Python interface is a straightforward transliteration of the Unix system call and library interface for sockets to Python's object-oriented style: the -:func:`.socket` function returns a :dfn:`socket object` whose methods implement +:func:`~socket.socket` function returns a :dfn:`socket object` whose methods implement the various socket system calls. Parameter types are somewhat higher-level than in the C interface: as with :meth:`read` and :meth:`write` operations on Python files, buffer allocation on receive operations is automatic, and buffer length @@ -185,7 +185,7 @@ created. Socket addresses are represented as follows: .. versionadded:: 3.7 - :const:`AF_PACKET` is a low-level interface directly to network devices. - The packets are represented by the tuple + The addresses are represented by the tuple ``(ifname, proto[, pkttype[, hatype[, addr]]])`` where: - *ifname* - String specifying the device name. @@ -193,7 +193,6 @@ created. Socket addresses are represented as follows: May be :data:`ETH_P_ALL` to capture all protocols, one of the :ref:`ETHERTYPE_* constants ` or any other Ethernet protocol number. - Value must be in network-byte-order. - *pkttype* - Optional integer specifying the packet type: - ``PACKET_HOST`` (the default) - Packet addressed to the local host. @@ -348,7 +347,7 @@ Constants AF_INET6 These constants represent the address (and protocol) families, used for the - first argument to :func:`.socket`. If the :const:`AF_UNIX` constant is not + first argument to :func:`~socket.socket`. If the :const:`AF_UNIX` constant is not defined then this protocol is unsupported. More constants may be available depending on the system. @@ -365,7 +364,7 @@ Constants SOCK_SEQPACKET These constants represent the socket types, used for the second argument to - :func:`.socket`. More constants may be available depending on the system. + :func:`~socket.socket`. More constants may be available depending on the system. (Only :const:`SOCK_STREAM` and :const:`SOCK_DGRAM` appear to be generally useful.) @@ -404,7 +403,7 @@ Constants Many constants of these forms, documented in the Unix documentation on sockets and/or the IP protocol, are also defined in the socket module. They are - generally used in arguments to the :meth:`setsockopt` and :meth:`getsockopt` + generally used in arguments to the :meth:`~socket.setsockopt` and :meth:`~socket.getsockopt` methods of socket objects. In most cases, only those symbols that are defined in the Unix header files are defined; for a few symbols, default values are provided. @@ -770,7 +769,7 @@ The following functions all create :ref:`socket objects `. Build a pair of connected socket objects using the given address family, socket type, and protocol number. Address family, socket type, and protocol number are - as for the :func:`.socket` function above. The default family is :const:`AF_UNIX` + as for the :func:`~socket.socket` function above. The default family is :const:`AF_UNIX` if defined on the platform; otherwise, the default is :const:`AF_INET`. The newly created sockets are :ref:`non-inheritable `. @@ -866,7 +865,7 @@ The following functions all create :ref:`socket objects `. Duplicate the file descriptor *fd* (an integer as returned by a file object's :meth:`~io.IOBase.fileno` method) and build a socket object from the result. Address - family, socket type and protocol number are as for the :func:`.socket` function + family, socket type and protocol number are as for the :func:`~socket.socket` function above. The file descriptor should refer to a socket, but this is not checked --- subsequent operations on the object may fail if the file descriptor is invalid. This function is rarely needed, but can be used to get or set socket options on @@ -931,7 +930,7 @@ The :mod:`socket` module also offers various network-related services: ``(family, type, proto, canonname, sockaddr)`` In these tuples, *family*, *type*, *proto* are all integers and are - meant to be passed to the :func:`.socket` function. *canonname* will be + meant to be passed to the :func:`~socket.socket` function. *canonname* will be a string representing the canonical name of the *host* if :const:`AI_CANONNAME` is part of the *flags* argument; else *canonname* will be empty. *sockaddr* is a tuple describing a socket address, whose @@ -1047,7 +1046,7 @@ The :mod:`socket` module also offers various network-related services: .. function:: getprotobyname(protocolname) Translate an internet protocol name (for example, ``'icmp'``) to a constant - suitable for passing as the (optional) third argument to the :func:`.socket` + suitable for passing as the (optional) third argument to the :func:`~socket.socket` function. This is usually only needed for sockets opened in "raw" mode (:const:`SOCK_RAW`); for the normal socket modes, the correct protocol is chosen automatically if the protocol is omitted or zero. @@ -1331,7 +1330,7 @@ The :mod:`socket` module also offers various network-related services: Send the list of file descriptors *fds* over an :const:`AF_UNIX` socket *sock*. The *fds* parameter is a sequence of file descriptors. - Consult :meth:`sendmsg` for the documentation of these parameters. + Consult :meth:`~socket.sendmsg` for the documentation of these parameters. .. availability:: Unix, Windows, not Emscripten, not WASI. @@ -1345,7 +1344,7 @@ The :mod:`socket` module also offers various network-related services: Receive up to *maxfds* file descriptors from an :const:`AF_UNIX` socket *sock*. Return ``(msg, list(fds), flags, addr)``. - Consult :meth:`recvmsg` for the documentation of these parameters. + Consult :meth:`~socket.recvmsg` for the documentation of these parameters. .. availability:: Unix, Windows, not Emscripten, not WASI. @@ -2064,10 +2063,10 @@ Example Here are four minimal example programs using the TCP/IP protocol: a server that echoes all data that it receives back (servicing only one client), and a client -using it. Note that a server must perform the sequence :func:`.socket`, +using it. Note that a server must perform the sequence :func:`~socket.socket`, :meth:`~socket.bind`, :meth:`~socket.listen`, :meth:`~socket.accept` (possibly repeating the :meth:`~socket.accept` to service more than one client), while a -client only needs the sequence :func:`.socket`, :meth:`~socket.connect`. Also +client only needs the sequence :func:`~socket.socket`, :meth:`~socket.connect`. Also note that the server does not :meth:`~socket.sendall`/:meth:`~socket.recv` on the socket it is listening on but on the new socket returned by :meth:`~socket.accept`. diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index a5b3474f4bd39a..6dbb34a84a4c40 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1502,7 +1502,7 @@ Cursor objects .. method:: execute(sql, parameters=(), /) - Execute SQL a single SQL statement, + Execute a single SQL statement, optionally binding Python values using :ref:`placeholders `. diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 21b38ae62fe02f..0db233e2dde33c 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -908,6 +908,12 @@ Constants .. versionadded:: 3.7 +.. data:: HAS_PSK + + Whether the OpenSSL library has built-in support for TLS-PSK. + + .. versionadded:: 3.13 + .. data:: CHANNEL_BINDING_TYPES List of supported TLS channel binding types. Strings in this list @@ -2006,6 +2012,100 @@ to speed up repeated connections from the same clients. >>> ssl.create_default_context().verify_mode # doctest: +SKIP +.. method:: SSLContext.set_psk_client_callback(callback) + + Enables TLS-PSK (pre-shared key) authentication on a client-side connection. + + In general, certificate based authentication should be preferred over this method. + + The parameter ``callback`` is a callable object with the signature: + ``def callback(hint: str | None) -> tuple[str | None, bytes]``. + The ``hint`` parameter is an optional identity hint sent by the server. + The return value is a tuple in the form (client-identity, psk). + Client-identity is an optional string which may be used by the server to + select a corresponding PSK for the client. The string must be less than or + equal to ``256`` octets when UTF-8 encoded. PSK is a + :term:`bytes-like object` representing the pre-shared key. Return a zero + length PSK to reject the connection. + + Setting ``callback`` to :const:`None` removes any existing callback. + + .. note:: + When using TLS 1.3: + + - the ``hint`` parameter is always :const:`None`. + - client-identity must be a non-empty string. + + Example usage:: + + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + context.maximum_version = ssl.TLSVersion.TLSv1_2 + context.set_ciphers('PSK') + + # A simple lambda: + psk = bytes.fromhex('c0ffee') + context.set_psk_client_callback(lambda hint: (None, psk)) + + # A table using the hint from the server: + psk_table = { 'ServerId_1': bytes.fromhex('c0ffee'), + 'ServerId_2': bytes.fromhex('facade') + } + def callback(hint): + return 'ClientId_1', psk_table.get(hint, b'') + context.set_psk_client_callback(callback) + + This method will raise :exc:`NotImplementedError` if :data:`HAS_PSK` is + ``False``. + + .. versionadded:: 3.13 + +.. method:: SSLContext.set_psk_server_callback(callback, identity_hint=None) + + Enables TLS-PSK (pre-shared key) authentication on a server-side connection. + + In general, certificate based authentication should be preferred over this method. + + The parameter ``callback`` is a callable object with the signature: + ``def callback(identity: str | None) -> bytes``. + The ``identity`` parameter is an optional identity sent by the client which can + be used to select a corresponding PSK. + The return value is a :term:`bytes-like object` representing the pre-shared key. + Return a zero length PSK to reject the connection. + + Setting ``callback`` to :const:`None` removes any existing callback. + + The parameter ``identity_hint`` is an optional identity hint string sent to + the client. The string must be less than or equal to ``256`` octets when + UTF-8 encoded. + + .. note:: + When using TLS 1.3 the ``identity_hint`` parameter is not sent to the client. + + Example usage:: + + context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + context.maximum_version = ssl.TLSVersion.TLSv1_2 + context.set_ciphers('PSK') + + # A simple lambda: + psk = bytes.fromhex('c0ffee') + context.set_psk_server_callback(lambda identity: psk) + + # A table using the identity of the client: + psk_table = { 'ClientId_1': bytes.fromhex('c0ffee'), + 'ClientId_2': bytes.fromhex('facade') + } + def callback(identity): + return psk_table.get(identity, b'') + context.set_psk_server_callback(callback, 'ServerId_1') + + This method will raise :exc:`NotImplementedError` if :data:`HAS_PSK` is + ``False``. + + .. versionadded:: 3.13 + .. index:: single: certificates .. index:: single: X509 certificate diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index f204b287b565eb..1265b5b12e492d 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -44,7 +44,8 @@ Any object can be tested for truth value, for use in an :keyword:`if` or .. index:: single: true By default, an object is considered true unless its class defines either a -:meth:`~object.__bool__` method that returns ``False`` or a :meth:`__len__` method that +:meth:`~object.__bool__` method that returns ``False`` or a +:meth:`~object.__len__` method that returns zero, when called with the object. [1]_ Here are most of the built-in objects considered false: @@ -197,7 +198,7 @@ exception. Two more operations with the same syntactic priority, :keyword:`in` and :keyword:`not in`, are supported by types that are :term:`iterable` or -implement the :meth:`__contains__` method. +implement the :meth:`~object.__contains__` method. .. _typesnumeric: @@ -717,7 +718,7 @@ that's defined for any rational number, and hence applies to all instances of :class:`int` and :class:`fractions.Fraction`, and all finite instances of :class:`float` and :class:`decimal.Decimal`. Essentially, this function is given by reduction modulo ``P`` for a fixed prime ``P``. The value of ``P`` is -made available to Python as the :attr:`modulus` attribute of +made available to Python as the :attr:`~sys.hash_info.modulus` attribute of :data:`sys.hash_info`. .. impl-detail:: @@ -906,9 +907,9 @@ Generator Types --------------- Python's :term:`generator`\s provide a convenient way to implement the iterator -protocol. If a container object's :meth:`__iter__` method is implemented as a +protocol. If a container object's :meth:`~iterator.__iter__` method is implemented as a generator, it will automatically return an iterator object (technically, a -generator object) supplying the :meth:`__iter__` and :meth:`~generator.__next__` +generator object) supplying the :meth:`!__iter__` and :meth:`~generator.__next__` methods. More information about generators can be found in :ref:`the documentation for the yield expression `. @@ -3672,7 +3673,7 @@ The conversion types are: +------------+-----------------------------------------------------+-------+ | ``'b'`` | Bytes (any object that follows the | \(5) | | | :ref:`buffer protocol ` or has | | -| | :meth:`__bytes__`). | | +| | :meth:`~object.__bytes__`). | | +------------+-----------------------------------------------------+-------+ | ``'s'`` | ``'s'`` is an alias for ``'b'`` and should only | \(6) | | | be used for Python2/3 code bases. | | @@ -4410,7 +4411,8 @@ The constructors for both classes work the same: :meth:`symmetric_difference_update` methods will accept any iterable as an argument. - Note, the *elem* argument to the :meth:`__contains__`, :meth:`remove`, and + Note, the *elem* argument to the :meth:`~object.__contains__`, + :meth:`remove`, and :meth:`discard` methods may be a set. To support searching for an equivalent frozenset, a temporary one is created from *elem*. @@ -5236,9 +5238,11 @@ instantiated from the type:: TypeError: cannot create 'types.UnionType' instances .. note:: - The :meth:`__or__` method for type objects was added to support the syntax - ``X | Y``. If a metaclass implements :meth:`__or__`, the Union may - override it:: + The :meth:`!__or__` method for type objects was added to support the syntax + ``X | Y``. If a metaclass implements :meth:`!__or__`, the Union may + override it: + + .. doctest:: >>> class M(type): ... def __or__(self, other): @@ -5250,7 +5254,7 @@ instantiated from the type:: >>> C | int 'Hello' >>> int | C - int | __main__.C + int | C .. seealso:: @@ -5324,25 +5328,30 @@ Methods .. index:: pair: object; method Methods are functions that are called using the attribute notation. There are -two flavors: built-in methods (such as :meth:`append` on lists) and class -instance methods. Built-in methods are described with the types that support -them. +two flavors: :ref:`built-in methods ` (such as :meth:`append` +on lists) and :ref:`class instance method `. +Built-in methods are described with the types that support them. If you access a method (a function defined in a class namespace) through an instance, you get a special object: a :dfn:`bound method` (also called -:dfn:`instance method`) object. When called, it will add the ``self`` argument +:ref:`instance method `) object. When called, it will add +the ``self`` argument to the argument list. Bound methods have two special read-only attributes: -``m.__self__`` is the object on which the method operates, and ``m.__func__`` is +:attr:`m.__self__ ` is the object on which the method +operates, and :attr:`m.__func__ ` is the function implementing the method. Calling ``m(arg-1, arg-2, ..., arg-n)`` is completely equivalent to calling ``m.__func__(m.__self__, arg-1, arg-2, ..., arg-n)``. -Like function objects, bound method objects support getting arbitrary +Like :ref:`function objects `, bound method objects support +getting arbitrary attributes. However, since method attributes are actually stored on the -underlying function object (``meth.__func__``), setting method attributes on +underlying function object (:attr:`method.__func__`), setting method attributes on bound methods is disallowed. Attempting to set an attribute on a method results in an :exc:`AttributeError` being raised. In order to set a method -attribute, you need to explicitly set it on the underlying function object:: +attribute, you need to explicitly set it on the underlying function object: + +.. doctest:: >>> class C: ... def method(self): @@ -5357,7 +5366,7 @@ attribute, you need to explicitly set it on the underlying function object:: >>> c.method.whoami 'my name is method' -See :ref:`types` for more information. +See :ref:`instance-methods` for more information. .. index:: object; code, code object diff --git a/Doc/library/sys.monitoring.rst b/Doc/library/sys.monitoring.rst index f2fe3d76248c2c..762581b7eda7f1 100644 --- a/Doc/library/sys.monitoring.rst +++ b/Doc/library/sys.monitoring.rst @@ -54,6 +54,13 @@ Registering and using tools Should be called once a tool no longer requires *tool_id*. +.. note:: + + :func:`free_tool_id` will not disable global or local events associated + with *tool_id*, nor will it unregister any callback functions. This + function is only intended to be used to notify the VM that the + particular *tool_id* is no longer in use. + .. function:: get_tool(tool_id: int, /) -> str | None Returns the name of the tool if *tool_id* is in use, diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index bf9aaca2a696de..aaf79205d44282 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -1205,6 +1205,18 @@ always available. .. versionadded:: 3.12 +.. function:: _is_interned(string) + + Return :const:`True` if the given string is "interned", :const:`False` + otherwise. + + .. versionadded:: 3.13 + + .. impl-detail:: + + It is not guaranteed to exist in all implementations of Python. + + .. data:: last_type last_value last_traceback @@ -1588,7 +1600,8 @@ always available. :file:`Objects/lnotab_notes.txt` for a detailed explanation of how this works. Per-line events may be disabled for a frame by setting - :attr:`!f_trace_lines` to :const:`False` on that :ref:`frame `. + :attr:`~frame.f_trace_lines` to :const:`False` on that + :ref:`frame `. ``'return'`` A function (or other code block) is about to return. The local trace @@ -1606,7 +1619,7 @@ always available. opcode details). The local trace function is called; *arg* is ``None``; the return value specifies the new local trace function. Per-opcode events are not emitted by default: they must be explicitly - requested by setting :attr:`!f_trace_opcodes` to :const:`True` on the + requested by setting :attr:`~frame.f_trace_opcodes` to :const:`True` on the :ref:`frame `. Note that as an exception is propagated down the chain of callers, an @@ -1636,8 +1649,8 @@ always available. .. versionchanged:: 3.7 - ``'opcode'`` event type added; :attr:`!f_trace_lines` and - :attr:`!f_trace_opcodes` attributes added to frames + ``'opcode'`` event type added; :attr:`~frame.f_trace_lines` and + :attr:`~frame.f_trace_opcodes` attributes added to frames .. function:: set_asyncgen_hooks(firstiter, finalizer) diff --git a/Doc/library/test.rst b/Doc/library/test.rst index 7a8d38685b984c..6cbdc39b3c024d 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -159,6 +159,9 @@ guidelines to be followed: Running tests using the command-line interface ---------------------------------------------- +.. module:: test.regrtest + :synopsis: Drives the regression test suite. + The :mod:`test` package can be run as a script to drive Python's regression test suite, thanks to the :option:`-m` option: :program:`python -m test`. Under the hood, it uses :mod:`test.regrtest`; the call :program:`python -m diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index 5fab26db67633c..e084d8554c7c09 100644 --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -58,8 +58,8 @@ details that are unchanged. * `Modern Tkinter for Busy Python Developers `_ By Mark Roseman. (ISBN 978-1999149567) - * `Python and Tkinter Programming `_ - By Alan Moore. (ISBN 978-1788835886) + * `Python GUI programming with Tkinter `_ + By Alan D. Moore. (ISBN 978-1788835886) * `Programming Python `_ By Mark Lutz; has excellent coverage of Tkinter. (ISBN 978-0596158101) @@ -232,6 +232,9 @@ The modules that provide Tk support include: Additional modules: +.. module:: _tkinter + :synopsis: A binary module that contains the low-level interface to Tcl/Tk. + :mod:`_tkinter` A binary module that contains the low-level interface to Tcl/Tk. It is automatically imported by the main :mod:`tkinter` module, diff --git a/Doc/library/tkinter.ttk.rst b/Doc/library/tkinter.ttk.rst index 5fab1454944989..6e01ec7b291255 100644 --- a/Doc/library/tkinter.ttk.rst +++ b/Doc/library/tkinter.ttk.rst @@ -1391,7 +1391,8 @@ option. If you don't know the class name of a widget, use the method .. method:: element_create(elementname, etype, *args, **kw) Create a new element in the current theme, of the given *etype* which is - expected to be either "image" or "from". + expected to be either "image", "from" or "vsapi". + The latter is only available in Tk 8.6 on Windows. If "image" is used, *args* should contain the default image name followed by statespec/value pairs (this is the imagespec), and *kw* may have the @@ -1439,6 +1440,63 @@ option. If you don't know the class name of a widget, use the method style = ttk.Style(root) style.element_create('plain.background', 'from', 'default') + If "vsapi" is used as the value of *etype*, :meth:`element_create` + will create a new element in the current theme whose visual appearance + is drawn using the Microsoft Visual Styles API which is responsible + for the themed styles on Windows XP and Vista. + *args* is expected to contain the Visual Styles class and part as + given in the Microsoft documentation followed by an optional sequence + of tuples of ttk states and the corresponding Visual Styles API state + value. + *kw* may have the following options: + + padding=padding + Specify the element's interior padding. + *padding* is a list of up to four integers specifying the left, + top, right and bottom padding quantities respectively. + If fewer than four elements are specified, bottom defaults to top, + right defaults to left, and top defaults to left. + In other words, a list of three numbers specify the left, vertical, + and right padding; a list of two numbers specify the horizontal + and the vertical padding; a single number specifies the same + padding all the way around the widget. + This option may not be mixed with any other options. + + margins=padding + Specifies the elements exterior padding. + *padding* is a list of up to four integers specifying the left, top, + right and bottom padding quantities respectively. + This option may not be mixed with any other options. + + width=width + Specifies the width for the element. + If this option is set then the Visual Styles API will not be queried + for the recommended size or the part. + If this option is set then *height* should also be set. + The *width* and *height* options cannot be mixed with the *padding* + or *margins* options. + + height=height + Specifies the height of the element. + See the comments for *width*. + + Example:: + + style = ttk.Style(root) + style.element_create('pin', 'vsapi', 'EXPLORERBAR', 3, [ + ('pressed', '!selected', 3), + ('active', '!selected', 2), + ('pressed', 'selected', 6), + ('active', 'selected', 5), + ('selected', 4), + ('', 1)]) + style.layout('Explorer.Pin', + [('Explorer.Pin.pin', {'sticky': 'news'})]) + pin = ttk.Checkbutton(style='Explorer.Pin') + pin.pack(expand=True, fill='both') + + .. versionchanged:: 3.13 + Added support of the "vsapi" element factory. .. method:: element_names() diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst index 408da7fc5f0645..2d5ea19b2cb892 100644 --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -287,6 +287,14 @@ capture data for later printing in a lightweight fashion. The class of the original traceback. + .. deprecated:: 3.13 + + .. attribute:: exc_type_str + + String display of the class of the original exception. + + .. versionadded:: 3.13 + .. attribute:: filename For syntax errors - the file name where the error occurred. @@ -515,27 +523,32 @@ The output for the example would look similar to this: *** print_tb: File "", line 10, in lumberjack() + ~~~~~~~~~~^^ *** print_exception: Traceback (most recent call last): File "", line 10, in lumberjack() + ~~~~~~~~~~^^ File "", line 4, in lumberjack bright_side_of_life() + ~~~~~~~~~~~~~~~~~~~^^ IndexError: tuple index out of range *** print_exc: Traceback (most recent call last): File "", line 10, in lumberjack() + ~~~~~~~~~~^^ File "", line 4, in lumberjack bright_side_of_life() + ~~~~~~~~~~~~~~~~~~~^^ IndexError: tuple index out of range *** format_exc, first and last line: Traceback (most recent call last): IndexError: tuple index out of range *** format_exception: ['Traceback (most recent call last):\n', - ' File "", line 10, in \n lumberjack()\n', - ' File "", line 4, in lumberjack\n bright_side_of_life()\n', + ' File "", line 10, in \n lumberjack()\n ~~~~~~~~~~^^\n', + ' File "", line 4, in lumberjack\n bright_side_of_life()\n ~~~~~~~~~~~~~~~~~~~^^\n', ' File "", line 7, in bright_side_of_life\n return tuple()[0]\n ~~~~~~~^^^\n', 'IndexError: tuple index out of range\n'] *** extract_tb: @@ -543,8 +556,8 @@ The output for the example would look similar to this: , line 4 in lumberjack>, , line 7 in bright_side_of_life>] *** format_tb: - [' File "", line 10, in \n lumberjack()\n', - ' File "", line 4, in lumberjack\n bright_side_of_life()\n', + [' File "", line 10, in \n lumberjack()\n ~~~~~~~~~~^^\n', + ' File "", line 4, in lumberjack\n bright_side_of_life()\n ~~~~~~~~~~~~~~~~~~~^^\n', ' File "", line 7, in bright_side_of_life\n return tuple()[0]\n ~~~~~~~^^^\n'] *** tb_lineno: 10 diff --git a/Doc/library/types.rst b/Doc/library/types.rst index 54c3907dec98cc..22766462822af9 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -388,7 +388,8 @@ Standard names are defined for the following types: .. data:: GetSetDescriptorType The type of objects defined in extension modules with ``PyGetSetDef``, such - as ``FrameType.f_locals`` or ``array.array.typecode``. This type is used as + as :attr:`FrameType.f_locals ` or ``array.array.typecode``. + This type is used as descriptor for object attributes; it has the same purpose as the :class:`property` type, but for classes defined in extension modules. diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index c90c554591e748..02b72cb9f6b8aa 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -2326,6 +2326,8 @@ Loading and running tests test names. +.. _load_tests-protocol: + load_tests Protocol ################### diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst index 884de08eab1b16..a9c469707e8227 100644 --- a/Doc/library/warnings.rst +++ b/Doc/library/warnings.rst @@ -522,6 +522,56 @@ Available Functions and calls to :func:`simplefilter`. +.. decorator:: deprecated(msg, *, category=DeprecationWarning, stacklevel=1) + + Decorator to indicate that a class, function or overload is deprecated. + + When this decorator is applied to an object, + deprecation warnings may be emitted at runtime when the object is used. + :term:`static type checkers ` + will also generate a diagnostic on usage of the deprecated object. + + Usage:: + + from warnings import deprecated + from typing import overload + + @deprecated("Use B instead") + class A: + pass + + @deprecated("Use g instead") + def f(): + pass + + @overload + @deprecated("int support is deprecated") + def g(x: int) -> int: ... + @overload + def g(x: str) -> int: ... + + The warning specified by *category* will be emitted at runtime + on use of deprecated objects. For functions, that happens on calls; + for classes, on instantiation and on creation of subclasses. + If the *category* is ``None``, no warning is emitted at runtime. + The *stacklevel* determines where the + warning is emitted. If it is ``1`` (the default), the warning + is emitted at the direct caller of the deprecated object; if it + is higher, it is emitted further up the stack. + Static type checker behavior is not affected by the *category* + and *stacklevel* arguments. + + The deprecation message passed to the decorator is saved in the + ``__deprecated__`` attribute on the decorated object. + If applied to an overload, the decorator + must be after the :func:`@overload ` decorator + for the attribute to exist on the overload as returned by + :func:`typing.get_overloads`. + + .. versionadded:: 3.13 + See :pep:`702`. + + Available Context Managers -------------------------- diff --git a/Doc/library/xml.rst b/Doc/library/xml.rst index 1e49b6568dfc28..909022ea4ba6a4 100644 --- a/Doc/library/xml.rst +++ b/Doc/library/xml.rst @@ -73,7 +73,7 @@ decompression bomb Safe Safe Safe 1. Expat 2.4.1 and newer is not vulnerable to the "billion laughs" and "quadratic blowup" vulnerabilities. Items still listed as vulnerable due to potential reliance on system-provided libraries. Check - :const:`pyexpat.EXPAT_VERSION`. + :const:`!pyexpat.EXPAT_VERSION`. 2. :mod:`xml.etree.ElementTree` doesn't expand external entities and raises a :exc:`~xml.etree.ElementTree.ParseError` when an entity occurs. 3. :mod:`xml.dom.minidom` doesn't expand external entities and simply returns diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index f7d3d2d0bbec23..27d379a8b70f31 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -88,7 +88,7 @@ Some objects contain references to "external" resources such as open files or windows. It is understood that these resources are freed when the object is garbage-collected, but since garbage collection is not guaranteed to happen, such objects also provide an explicit way to release the external resource, -usually a :meth:`close` method. Programs are strongly recommended to explicitly +usually a :meth:`!close` method. Programs are strongly recommended to explicitly close such objects. The ':keyword:`try`...\ :keyword:`finally`' statement and the ':keyword:`with`' statement provide convenient ways to do this. @@ -519,6 +519,8 @@ These are the types to which the function call operation (see section :ref:`calls`) can be applied: +.. _user-defined-funcs: + User-defined functions ^^^^^^^^^^^^^^^^^^^^^^ @@ -634,6 +636,8 @@ code object; see the description of internal types below. The module. +.. _instance-methods: + Instance methods ^^^^^^^^^^^^^^^^ @@ -652,43 +656,64 @@ callable object (normally a user-defined function). single: __name__ (method attribute) single: __module__ (method attribute) -Special read-only attributes: :attr:`__self__` is the class instance object, -:attr:`__func__` is the function object; :attr:`__doc__` is the method's -documentation (same as ``__func__.__doc__``); :attr:`~definition.__name__` is the -method name (same as ``__func__.__name__``); :attr:`__module__` is the -name of the module the method was defined in, or ``None`` if unavailable. +Special read-only attributes: + +.. list-table:: + + * - .. attribute:: method.__self__ + - Refers to the class instance object to which the method is + :ref:`bound ` + + * - .. attribute:: method.__func__ + - Refers to the original function object + + * - .. attribute:: method.__doc__ + - The method's documentation (same as :attr:`!method.__func__.__doc__`). + A :class:`string ` if the original function had a docstring, else + ``None``. + + * - .. attribute:: method.__name__ + - The name of the method (same as :attr:`!method.__func__.__name__`) + + * - .. attribute:: method.__module__ + - The name of the module the method was defined in, or ``None`` if + unavailable. Methods also support accessing (but not setting) the arbitrary function -attributes on the underlying function object. +attributes on the underlying :ref:`function object `. User-defined method objects may be created when getting an attribute of a class (perhaps via an instance of that class), if that attribute is a -user-defined function object or a class method object. +user-defined :ref:`function object ` or a +:class:`classmethod` object. + +.. _method-binding: When an instance method object is created by retrieving a user-defined -function object from a class via one of its instances, its -:attr:`__self__` attribute is the instance, and the method object is said -to be bound. The new method's :attr:`__func__` attribute is the original -function object. - -When an instance method object is created by retrieving a class method -object from a class or instance, its :attr:`__self__` attribute is the -class itself, and its :attr:`__func__` attribute is the function object +:ref:`function object ` from a class via one of its +instances, its :attr:`~method.__self__` attribute is the instance, and the +method object is said to be *bound*. The new method's :attr:`~method.__func__` +attribute is the original function object. + +When an instance method object is created by retrieving a :class:`classmethod` +object from a class or instance, its :attr:`~method.__self__` attribute is the +class itself, and its :attr:`~method.__func__` attribute is the function object underlying the class method. When an instance method object is called, the underlying function -(:attr:`__func__`) is called, inserting the class instance -(:attr:`__self__`) in front of the argument list. For instance, when -:class:`C` is a class which contains a definition for a function -:meth:`f`, and ``x`` is an instance of :class:`C`, calling ``x.f(1)`` is +(:attr:`~method.__func__`) is called, inserting the class instance +(:attr:`~method.__self__`) in front of the argument list. For instance, when +:class:`!C` is a class which contains a definition for a function +:meth:`!f`, and ``x`` is an instance of :class:`!C`, calling ``x.f(1)`` is equivalent to calling ``C.f(x, 1)``. -When an instance method object is derived from a class method object, the -"class instance" stored in :attr:`__self__` will actually be the class +When an instance method object is derived from a :class:`classmethod` object, the +"class instance" stored in :attr:`~method.__self__` will actually be the class itself, so that calling either ``x.f(1)`` or ``C.f(1)`` is equivalent to calling ``f(C,1)`` where ``f`` is the underlying function. -Note that the transformation from function object to instance method +Note that the transformation from :ref:`function object ` +to instance method object happens each time the attribute is retrieved from the instance. In some cases, a fruitful optimization is to assign the attribute to a local variable and call that local variable. Also notice that this @@ -772,6 +797,8 @@ set to ``None`` (but see the next item); :attr:`__module__` is the name of the module the function was defined in or ``None`` if unavailable. +.. _builtin-methods: + Built-in methods ^^^^^^^^^^^^^^^^ @@ -783,8 +810,9 @@ Built-in methods This is really a different disguise of a built-in function, this time containing an object passed to the C function as an implicit extra argument. An example of a built-in method is ``alist.append()``, assuming *alist* is a list object. In -this case, the special read-only attribute :attr:`__self__` is set to the object -denoted by *alist*. +this case, the special read-only attribute :attr:`!__self__` is set to the object +denoted by *alist*. (The attribute has the same semantics as it does with +:attr:`other instance methods `.) Classes @@ -793,7 +821,7 @@ Classes Classes are callable. These objects normally act as factories for new instances of themselves, but variations are possible for class types that override :meth:`~object.__new__`. The arguments of the call are passed to -:meth:`__new__` and, in the typical case, to :meth:`~object.__init__` to +:meth:`!__new__` and, in the typical case, to :meth:`~object.__init__` to initialize the new instance. @@ -897,10 +925,11 @@ https://www.python.org/download/releases/2.3/mro/. pair: object; dictionary pair: class; attribute -When a class attribute reference (for class :class:`C`, say) would yield a +When a class attribute reference (for class :class:`!C`, say) would yield a class method object, it is transformed into an instance method object whose -:attr:`__self__` attribute is :class:`C`. When it would yield a static -method object, it is transformed into the object wrapped by the static method +:attr:`~method.__self__` attribute is :class:`!C`. +When it would yield a :class:`staticmethod` object, +it is transformed into the object wrapped by the static method object. See section :ref:`descriptors` for another way in which attributes retrieved from a class may differ from those actually contained in its :attr:`~object.__dict__`. @@ -968,7 +997,7 @@ in which attribute references are searched. When an attribute is not found there, and the instance's class has an attribute by that name, the search continues with the class attributes. If a class attribute is found that is a user-defined function object, it is transformed into an instance method -object whose :attr:`__self__` attribute is the instance. Static method and +object whose :attr:`~method.__self__` attribute is the instance. Static method and class method objects are also transformed; see above under "Classes". See section :ref:`descriptors` for another way in which attributes of a class retrieved via its instances may differ from the objects actually stored in @@ -1075,57 +1104,111 @@ indirectly) to mutable objects. single: co_freevars (code object attribute) single: co_qualname (code object attribute) -Special read-only attributes: :attr:`co_name` gives the function name; -:attr:`co_qualname` gives the fully qualified function name; -:attr:`co_argcount` is the total number of positional arguments -(including positional-only arguments and arguments with default values); -:attr:`co_posonlyargcount` is the number of positional-only arguments -(including arguments with default values); :attr:`co_kwonlyargcount` is -the number of keyword-only arguments (including arguments with default -values); :attr:`co_nlocals` is the number of local variables used by the -function (including arguments); :attr:`co_varnames` is a tuple containing -the names of the local variables (starting with the argument names); -:attr:`co_cellvars` is a tuple containing the names of local variables -that are referenced by nested functions; :attr:`co_freevars` is a tuple -containing the names of free variables; :attr:`co_code` is a string -representing the sequence of bytecode instructions; :attr:`co_consts` is -a tuple containing the literals used by the bytecode; :attr:`co_names` is -a tuple containing the names used by the bytecode; :attr:`co_filename` is -the filename from which the code was compiled; :attr:`co_firstlineno` is -the first line number of the function; :attr:`co_lnotab` is a string -encoding the mapping from bytecode offsets to line numbers (for details -see the source code of the interpreter, is deprecated since 3.12 -and may be removed in 3.14); :attr:`co_stacksize` is the -required stack size; :attr:`co_flags` is an integer encoding a number -of flags for the interpreter. +Special read-only attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. list-table:: + + * - .. attribute:: codeobject.co_name + - The function name + + * - .. attribute:: codeobject.co_qualname + - The fully qualified function name + + * - .. attribute:: codeobject.co_argcount + - The total number of positional :term:`parameters ` + (including positional-only parameters and parameters with default values) + that the function has + + * - .. attribute:: codeobject.co_posonlyargcount + - The number of positional-only :term:`parameters ` + (including arguments with default values) that the function has + + * - .. attribute:: codeobject.co_kwonlyargcount + - The number of keyword-only :term:`parameters ` + (including arguments with default values) that the function has + + * - .. attribute:: codeobject.co_nlocals + - The number of :ref:`local variables ` used by the function + (including parameters) + + * - .. attribute:: codeobject.co_varnames + - A :class:`tuple` containing the names of the local variables in the + function (starting with the parameter names) + + * - .. attribute:: codeobject.co_cellvars + - A :class:`tuple` containing the names of :ref:`local variables ` + that are referenced by nested functions inside the function + + * - .. attribute:: codeobject.co_freevars + - A :class:`tuple` containing the names of free variables in the function + + * - .. attribute:: codeobject.co_code + - A string representing the sequence of :term:`bytecode` instructions in + the function + + * - .. attribute:: codeobject.co_consts + - A :class:`tuple` containing the literals used by the :term:`bytecode` in + the function + + * - .. attribute:: codeobject.co_names + - A :class:`tuple` containing the names used by the :term:`bytecode` in + the function + + * - .. attribute:: codeobject.co_filename + - The name of the file from which the code was compiled + + * - .. attribute:: codeobject.co_firstlineno + - The line number of the first line of the function + + * - .. attribute:: codeobject.co_lnotab + - A string encoding the mapping from :term:`bytecode` offsets to line + numbers. For details, see the source code of the interpreter. + + .. deprecated:: 3.12 + This attribute of code objects is deprecated, and may be removed in + Python 3.14. + + * - .. attribute:: codeobject.co_stacksize + - The required stack size of the code object + + * - .. attribute:: codeobject.co_flags + - An :class:`integer ` encoding a number of flags for the + interpreter. .. index:: pair: object; generator -The following flag bits are defined for :attr:`co_flags`: bit ``0x04`` is set if +The following flag bits are defined for :attr:`~codeobject.co_flags`: +bit ``0x04`` is set if the function uses the ``*arguments`` syntax to accept an arbitrary number of positional arguments; bit ``0x08`` is set if the function uses the ``**keywords`` syntax to accept arbitrary keyword arguments; bit ``0x20`` is set -if the function is a generator. +if the function is a generator. See :ref:`inspect-module-co-flags` for details +on the semantics of each flags that might be present. Future feature declarations (``from __future__ import division``) also use bits -in :attr:`co_flags` to indicate whether a code object was compiled with a +in :attr:`~codeobject.co_flags` to indicate whether a code object was compiled with a particular feature enabled: bit ``0x2000`` is set if the function was compiled with future division enabled; bits ``0x10`` and ``0x1000`` were used in earlier versions of Python. -Other bits in :attr:`co_flags` are reserved for internal use. +Other bits in :attr:`~codeobject.co_flags` are reserved for internal use. .. index:: single: documentation string -If a code object represents a function, the first item in :attr:`co_consts` is +If a code object represents a function, the first item in +:attr:`~codeobject.co_consts` is the documentation string of the function, or ``None`` if undefined. +The :meth:`!co_positions` method +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + .. method:: codeobject.co_positions() - Returns an iterable over the source code positions of each bytecode + Returns an iterable over the source code positions of each :term:`bytecode` instruction in the code object. - The iterator returns tuples containing the ``(start_line, end_line, + The iterator returns :class:`tuple`\s containing the ``(start_line, end_line, start_column, end_column)``. The *i-th* tuple corresponds to the position of the source code that compiled to the *i-th* instruction. Column information is 0-indexed utf-8 byte offsets on the given source @@ -1172,16 +1255,36 @@ Frame objects represent execution frames. They may occur in traceback objects single: f_lasti (frame attribute) single: f_builtins (frame attribute) -Special read-only attributes: :attr:`f_back` is to the previous stack frame -(towards the caller), or ``None`` if this is the bottom stack frame; -:attr:`f_code` is the code object being executed in this frame; :attr:`f_locals` -is the dictionary used to look up local variables; :attr:`f_globals` is used for -global variables; :attr:`f_builtins` is used for built-in (intrinsic) names; -:attr:`f_lasti` gives the precise instruction (this is an index into the -bytecode string of the code object). +Special read-only attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. list-table:: -Accessing ``f_code`` raises an :ref:`auditing event ` -``object.__getattr__`` with arguments ``obj`` and ``"f_code"``. + * - .. attribute:: frame.f_back + - Points to the previous stack frame (towards the caller), + or ``None`` if this is the bottom stack frame + + * - .. attribute:: frame.f_code + - The :ref:`code object ` being executed in this frame. + Accessing this attribute raises an :ref:`auditing event ` + ``object.__getattr__`` with arguments ``obj`` and ``"f_code"``. + + * - .. attribute:: frame.f_locals + - The dictionary used by the frame to look up + :ref:`local variables ` + + * - .. attribute:: frame.f_globals + - The dictionary used by the frame to look up + :ref:`global variables ` + + * - .. attribute:: frame.f_builtins + - The dictionary used by the frame to look up + :ref:`built-in (intrinsic) names ` + + * - .. attribute:: frame.f_lasti + - The "precise instruction" of the frame object + (this is an index into the :term:`bytecode` string of the + :ref:`code object `) .. index:: single: f_trace (frame attribute) @@ -1189,30 +1292,44 @@ Accessing ``f_code`` raises an :ref:`auditing event ` single: f_trace_opcodes (frame attribute) single: f_lineno (frame attribute) -Special writable attributes: :attr:`f_trace`, if not ``None``, is a function -called for various events during code execution (this is used by the debugger). -Normally an event is triggered for each new source line - this can be -disabled by setting :attr:`f_trace_lines` to :const:`False`. +Special writable attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Implementations *may* allow per-opcode events to be requested by setting -:attr:`f_trace_opcodes` to :const:`True`. Note that this may lead to -undefined interpreter behaviour if exceptions raised by the trace -function escape to the function being traced. +.. list-table:: -:attr:`f_lineno` is the current line number of the frame --- writing to this -from within a trace function jumps to the given line (only for the bottom-most -frame). A debugger can implement a Jump command (aka Set Next Statement) -by writing to f_lineno. + * - .. attribute:: frame.f_trace + - If not ``None``, this is a function called for various events during + code execution (this is used by debuggers). Normally an event is + triggered for each new source line (see :attr:`~frame.f_trace_lines`). + + * - .. attribute:: frame.f_trace_lines + - Set this attribute to :const:`False` to disable triggering a tracing + event for each source line. + + * - .. attribute:: frame.f_trace_opcodes + - Set this attribute to :const:`True` to allow per-opcode events to be + requested. Note that this may lead to + undefined interpreter behaviour if exceptions raised by the trace + function escape to the function being traced. + + * - .. attribute:: frame.f_lineno + - The current line number of the frame -- writing to this + from within a trace function jumps to the given line (only for the bottom-most + frame). A debugger can implement a Jump command (aka Set Next Statement) + by writing to this attribute. + +Frame object methods +~~~~~~~~~~~~~~~~~~~~ Frame objects support one method: .. method:: frame.clear() - This method clears all references to local variables held by the - frame. Also, if the frame belonged to a generator, the generator + This method clears all references to :ref:`local variables ` held by the + frame. Also, if the frame belonged to a :term:`generator`, the generator is finalized. This helps break reference cycles involving frame - objects (for example when catching an exception and storing its - traceback for later use). + objects (for example when catching an :ref:`exception ` + and storing its :ref:`traceback ` for later use). :exc:`RuntimeError` is raised if the frame is currently executing or suspended. @@ -1901,13 +2018,17 @@ class' :attr:`~object.__dict__`. Called to delete the attribute on an instance *instance* of the owner class. +Instances of descriptors may also have the :attr:`!__objclass__` attribute +present: + +.. attribute:: object.__objclass__ -The attribute :attr:`__objclass__` is interpreted by the :mod:`inspect` module -as specifying the class where this object was defined (setting this -appropriately can assist in runtime introspection of dynamic class attributes). -For callables, it may indicate that an instance of the given type (or a -subclass) is expected or required as the first positional argument (for example, -CPython sets this attribute for unbound methods that are implemented in C). + The attribute :attr:`!__objclass__` is interpreted by the :mod:`inspect` module + as specifying the class where this object was defined (setting this + appropriately can assist in runtime introspection of dynamic class attributes). + For callables, it may indicate that an instance of the given type (or a + subclass) is expected or required as the first positional argument (for example, + CPython sets this attribute for unbound methods that are implemented in C). .. _descriptor-invocation: @@ -1988,13 +2109,14 @@ For instance bindings, the precedence of descriptor invocation depends on which descriptor methods are defined. A descriptor can define any combination of :meth:`~object.__get__`, :meth:`~object.__set__` and :meth:`~object.__delete__`. If it does not -define :meth:`__get__`, then accessing the attribute will return the descriptor +define :meth:`!__get__`, then accessing the attribute will return the descriptor object itself unless there is a value in the object's instance dictionary. If -the descriptor defines :meth:`__set__` and/or :meth:`__delete__`, it is a data +the descriptor defines :meth:`!__set__` and/or :meth:`!__delete__`, it is a data descriptor; if it defines neither, it is a non-data descriptor. Normally, data -descriptors define both :meth:`__get__` and :meth:`__set__`, while non-data -descriptors have just the :meth:`__get__` method. Data descriptors with -:meth:`__get__` and :meth:`__set__` (and/or :meth:`__delete__`) defined always override a redefinition in an +descriptors define both :meth:`!__get__` and :meth:`!__set__`, while non-data +descriptors have just the :meth:`!__get__` method. Data descriptors with +:meth:`!__get__` and :meth:`!__set__` (and/or :meth:`!__delete__`) defined +always override a redefinition in an instance dictionary. In contrast, non-data descriptors can be overridden by instances. @@ -2571,16 +2693,17 @@ either to emulate a sequence or to emulate a mapping; the difference is that for a sequence, the allowable keys should be the integers *k* for which ``0 <= k < N`` where *N* is the length of the sequence, or :class:`slice` objects, which define a range of items. It is also recommended that mappings provide the methods -:meth:`keys`, :meth:`values`, :meth:`items`, :meth:`get`, :meth:`clear`, -:meth:`setdefault`, :meth:`pop`, :meth:`popitem`, :meth:`!copy`, and -:meth:`update` behaving similar to those for Python's standard :class:`dictionary ` +:meth:`!keys`, :meth:`!values`, :meth:`!items`, :meth:`!get`, :meth:`!clear`, +:meth:`!setdefault`, :meth:`!pop`, :meth:`!popitem`, :meth:`!copy`, and +:meth:`!update` behaving similar to those for Python's standard :class:`dictionary ` objects. The :mod:`collections.abc` module provides a :class:`~collections.abc.MutableMapping` :term:`abstract base class` to help create those methods from a base set of -:meth:`~object.__getitem__`, :meth:`~object.__setitem__`, :meth:`~object.__delitem__`, and :meth:`keys`. -Mutable sequences should provide methods :meth:`append`, :meth:`count`, -:meth:`index`, :meth:`extend`, :meth:`insert`, :meth:`pop`, :meth:`remove`, -:meth:`reverse` and :meth:`sort`, like Python standard :class:`list` +:meth:`~object.__getitem__`, :meth:`~object.__setitem__`, +:meth:`~object.__delitem__`, and :meth:`!keys`. +Mutable sequences should provide methods :meth:`!append`, :meth:`!count`, +:meth:`!index`, :meth:`!extend`, :meth:`!insert`, :meth:`!pop`, :meth:`!remove`, +:meth:`!reverse` and :meth:`!sort`, like Python standard :class:`list` objects. Finally, sequence types should implement addition (meaning concatenation) and multiplication (meaning repetition) by defining the methods @@ -2593,7 +2716,7 @@ operator; for mappings, ``in`` should search the mapping's keys; for sequences, it should search through the values. It is further recommended that both mappings and sequences implement the :meth:`~object.__iter__` method to allow efficient iteration -through the container; for mappings, :meth:`__iter__` should iterate +through the container; for mappings, :meth:`!__iter__` should iterate through the object's keys; for sequences, it should iterate through the values. .. method:: object.__len__(self) @@ -3172,7 +3295,7 @@ generators, coroutines do not directly support iteration. to the :meth:`~generator.send` method of the iterator that caused the coroutine to suspend. The result (return value, :exc:`StopIteration`, or other exception) is the same as when - iterating over the :meth:`__await__` return value, described above. + iterating over the :meth:`!__await__` return value, described above. .. method:: coroutine.throw(value) coroutine.throw(type[, value[, traceback]]) diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 14c2afa15ad7fb..3f6d5bfafee9d1 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -823,12 +823,18 @@ An attribute reference is a primary followed by a period and a name: The primary must evaluate to an object of a type that supports attribute references, which most objects do. This object is then asked to produce the -attribute whose name is the identifier. This production can be customized by -overriding the :meth:`__getattr__` method. If this attribute is not available, -the exception :exc:`AttributeError` is raised. Otherwise, the type and value of -the object produced is determined by the object. Multiple evaluations of the -same attribute reference may yield different objects. - +attribute whose name is the identifier. The type and value produced is +determined by the object. Multiple evaluations of the same attribute +reference may yield different objects. + +This production can be customized by overriding the +:meth:`~object.__getattribute__` method or the :meth:`~object.__getattr__` +method. The :meth:`!__getattribute__` method is called first and either +returns a value or raises :exc:`AttributeError` if the attribute is not +available. + +If an :exc:`AttributeError` is raised and the object has a :meth:`!__getattr__` +method, that method is called as a fallback. .. _subscriptions: diff --git a/Doc/requirements.txt b/Doc/requirements.txt index ce87be2d392824..04334fd5a464d4 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -13,6 +13,7 @@ blurb sphinx-autobuild sphinxext-opengraph==0.7.5 +sphinx-notfound-page==1.0.0 # The theme used by the documentation is stored separately, so we need # to install that as well. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 105e00db8e368a..5ef68cc089d436 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -14,25 +14,21 @@ Doc/c-api/memory.rst Doc/c-api/memoryview.rst Doc/c-api/module.rst Doc/c-api/object.rst -Doc/c-api/set.rst Doc/c-api/stable.rst Doc/c-api/structures.rst Doc/c-api/sys.rst Doc/c-api/type.rst Doc/c-api/typeobj.rst Doc/extending/extending.rst -Doc/extending/newtypes.rst Doc/glossary.rst Doc/howto/descriptor.rst Doc/howto/enum.rst Doc/howto/isolating-extensions.rst Doc/howto/logging.rst Doc/howto/urllib2.rst -Doc/library/abc.rst Doc/library/ast.rst Doc/library/asyncio-extending.rst Doc/library/asyncio-policy.rst -Doc/library/asyncio-stream.rst Doc/library/asyncio-subprocess.rst Doc/library/asyncio-task.rst Doc/library/bdb.rst @@ -43,16 +39,13 @@ Doc/library/collections.abc.rst Doc/library/collections.rst Doc/library/concurrent.futures.rst Doc/library/configparser.rst -Doc/library/contextlib.rst Doc/library/csv.rst Doc/library/datetime.rst Doc/library/dbm.rst Doc/library/decimal.rst -Doc/library/doctest.rst Doc/library/email.charset.rst Doc/library/email.compat32-message.rst Doc/library/email.errors.rst -Doc/library/email.headerregistry.rst Doc/library/email.mime.rst Doc/library/email.parser.rst Doc/library/email.policy.rst @@ -61,15 +54,11 @@ Doc/library/exceptions.rst Doc/library/faulthandler.rst Doc/library/fcntl.rst Doc/library/ftplib.rst -Doc/library/functions.rst Doc/library/functools.rst -Doc/library/gettext.rst -Doc/library/gzip.rst Doc/library/http.client.rst Doc/library/http.cookiejar.rst Doc/library/http.cookies.rst Doc/library/http.server.rst -Doc/library/importlib.resources.rst Doc/library/importlib.rst Doc/library/inspect.rst Doc/library/locale.rst @@ -82,7 +71,6 @@ Doc/library/multiprocessing.rst Doc/library/multiprocessing.shared_memory.rst Doc/library/numbers.rst Doc/library/optparse.rst -Doc/library/os.path.rst Doc/library/os.rst Doc/library/pickle.rst Doc/library/pickletools.rst @@ -94,11 +82,9 @@ Doc/library/pydoc.rst Doc/library/pyexpat.rst Doc/library/random.rst Doc/library/readline.rst -Doc/library/reprlib.rst Doc/library/resource.rst Doc/library/rlcompleter.rst Doc/library/select.rst -Doc/library/shelve.rst Doc/library/signal.rst Doc/library/smtplib.rst Doc/library/socket.rst @@ -123,7 +109,6 @@ Doc/library/wsgiref.rst Doc/library/xml.dom.minidom.rst Doc/library/xml.dom.pulldom.rst Doc/library/xml.dom.rst -Doc/library/xml.rst Doc/library/xml.sax.handler.rst Doc/library/xml.sax.reader.rst Doc/library/xml.sax.rst diff --git a/Doc/tutorial/classes.rst b/Doc/tutorial/classes.rst index 7b92e1a51b6e67..3bf138ca225ee5 100644 --- a/Doc/tutorial/classes.rst +++ b/Doc/tutorial/classes.rst @@ -769,8 +769,10 @@ data from a string buffer instead, and pass it as an argument. or arithmetic operators, and assigning such a "pseudo-file" to sys.stdin will not cause the interpreter to read further input from it.) -Instance method objects have attributes, too: ``m.__self__`` is the instance -object with the method :meth:`!m`, and ``m.__func__`` is the function object +:ref:`Instance method objects ` have attributes, too: +:attr:`m.__self__ ` is the instance +object with the method :meth:`!m`, and :attr:`m.__func__ ` is +the :ref:`function object ` corresponding to the method. diff --git a/Doc/tutorial/errors.rst b/Doc/tutorial/errors.rst index 1ec59767e9ce12..4058ebe8efdb42 100644 --- a/Doc/tutorial/errors.rst +++ b/Doc/tutorial/errors.rst @@ -108,8 +108,7 @@ The :keyword:`try` statement works as follows. * If an exception occurs which does not match the exception named in the *except clause*, it is passed on to outer :keyword:`try` statements; if no handler is - found, it is an *unhandled exception* and execution stops with a message as - shown above. + found, it is an *unhandled exception* and execution stops with an error message. A :keyword:`try` statement may have more than one *except clause*, to specify handlers for different exceptions. At most one handler will be executed. diff --git a/Doc/tutorial/floatingpoint.rst b/Doc/tutorial/floatingpoint.rst index 30f3dfb6b238b4..0795e2fef98830 100644 --- a/Doc/tutorial/floatingpoint.rst +++ b/Doc/tutorial/floatingpoint.rst @@ -150,7 +150,7 @@ section. See `Examples of Floating Point Problems `_ for a pleasant summary of how binary floating-point works and the kinds of problems commonly encountered in practice. Also see -`The Perils of Floating Point `_ +`The Perils of Floating Point `_ for a more complete account of other common surprises. As that says near the end, "there are no easy answers." Still, don't be unduly diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 39c8d114f1e2c5..56235bf4c28c7c 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -612,6 +612,27 @@ Miscellaneous options .. versionadded:: 3.13 The ``-X presite`` option. +Controlling Color +~~~~~~~~~~~~~~~~~ + +The Python interpreter is configured by default to use colors to highlight +output in certain situations such as when displaying tracebacks. This +behavior can be controlled by setting different environment variables. + +Setting the environment variable ``TERM`` to ``dumb`` will disable color. + +If the environment variable ``FORCE_COLOR`` is set, then color will be +enabled regardless of the value of TERM. This is useful on CI systems which +aren’t terminals but can none-the-less display ANSI escape sequences. + +If the environment variable ``NO_COLOR`` is set, Python will disable all color +in the output. This takes precedence over ``FORCE_COLOR``. + +All these environment variables are used also by other tools to control color +output. To control the color output only in the Python interpreter, the +:envvar:`PYTHON_COLORS` environment variable can be used. This variable takes +precedence over ``NO_COLOR``, which in turn takes precedence over +``FORCE_COLOR``. Options you shouldn't use ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1110,6 +1131,12 @@ conflict. .. versionadded:: 3.13 +.. envvar:: PYTHON_COLORS + + If this variable is set to ``1``, the interpreter will colorize various kinds + of output. Setting it to ``0`` deactivates this behavior. + + .. versionadded:: 3.13 Debug-mode variables ~~~~~~~~~~~~~~~~~~~~ diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index b51546e072a353..cb7eda42fe3fad 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -287,10 +287,15 @@ General Options .. versionadded:: 3.11 +.. _free-threading-build: + .. option:: --disable-gil Enables **experimental** support for running Python without the - :term:`global interpreter lock` (GIL). + :term:`global interpreter lock` (GIL): free threading build. + + Defines the ``Py_GIL_DISABLED`` macro and adds ``"t"`` to + :data:`sys.abiflags`. See :pep:`703` "Making the Global Interpreter Lock Optional in CPython". diff --git a/Doc/whatsnew/2.0.rst b/Doc/whatsnew/2.0.rst index c2b0ae8c76302a..6d6e51006d5bd8 100644 --- a/Doc/whatsnew/2.0.rst +++ b/Doc/whatsnew/2.0.rst @@ -671,9 +671,9 @@ errors. If you absolutely must use 2.0 but can't fix your code, you can edit ``NO_STRICT_LIST_APPEND`` to preserve the old behaviour; this isn't recommended. Some of the functions in the :mod:`socket` module are still forgiving in this -way. For example, :func:`socket.connect( ('hostname', 25) )` is the correct -form, passing a tuple representing an IP address, but :func:`socket.connect( -'hostname', 25 )` also works. :func:`socket.connect_ex` and :func:`socket.bind` +way. For example, ``socket.connect( ('hostname', 25) )`` is the correct +form, passing a tuple representing an IP address, but ``socket.connect('hostname', 25)`` +also works. :meth:`socket.connect_ex ` and :meth:`socket.bind ` are similarly easy-going. 2.0alpha1 tightened these functions up, but because the documentation actually used the erroneous multiple argument form, many people wrote code which would break with the stricter checking. GvR backed out diff --git a/Doc/whatsnew/2.3.rst b/Doc/whatsnew/2.3.rst index af332b28a28231..c989e6d3fa5787 100644 --- a/Doc/whatsnew/2.3.rst +++ b/Doc/whatsnew/2.3.rst @@ -1998,13 +1998,13 @@ Some of the more notable changes are: It would be difficult to detect any resulting difference from Python code, apart from a slight speed up when Python is run without :option:`-O`. - C extensions that access the :attr:`f_lineno` field of frame objects should + C extensions that access the :attr:`~frame.f_lineno` field of frame objects should instead call ``PyCode_Addr2Line(f->f_code, f->f_lasti)``. This will have the added effect of making the code work as desired under "python -O" in earlier versions of Python. A nifty new feature is that trace functions can now assign to the - :attr:`f_lineno` attribute of frame objects, changing the line that will be + :attr:`~frame.f_lineno` attribute of frame objects, changing the line that will be executed next. A ``jump`` command has been added to the :mod:`pdb` debugger taking advantage of this new feature. (Implemented by Richie Hindle.) diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index 016de153f3dd6a..e8c1709c42abac 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -1677,8 +1677,9 @@ Some smaller changes made to the core Python language are: (:issue:`1591665`) * Instance method objects have new attributes for the object and function - comprising the method; the new synonym for :attr:`im_self` is - :attr:`__self__`, and :attr:`im_func` is also available as :attr:`__func__`. + comprising the method; the new synonym for :attr:`!im_self` is + :attr:`~method.__self__`, and :attr:`!im_func` is also available as + :attr:`~method.__func__`. The old names are still supported in Python 2.6, but are gone in 3.0. * An obscure change: when you use the :func:`locals` function inside a diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index 61934ab1a2df56..cf6d26859bb6a2 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -858,9 +858,11 @@ Some smaller changes made to the core Python language are: .. XXX bytearray doesn't seem to be documented -* When using ``@classmethod`` and ``@staticmethod`` to wrap +* When using :class:`@classmethod ` and + :class:`@staticmethod ` to wrap methods as class or static methods, the wrapper object now - exposes the wrapped function as their :attr:`__func__` attribute. + exposes the wrapped function as their :attr:`~method.__func__` + attribute. (Contributed by Amaury Forgeot d'Arc, after a suggestion by George Sakkis; :issue:`5982`.) @@ -2383,8 +2385,8 @@ Port-Specific Changes: Mac OS X Port-Specific Changes: FreeBSD ----------------------------------- -* FreeBSD 7.1's :const:`SO_SETFIB` constant, used with - :func:`~socket.getsockopt`/:func:`~socket.setsockopt` to select an +* FreeBSD 7.1's :const:`SO_SETFIB` constant, used with the :func:`~socket.socket` methods + :func:`~socket.socket.getsockopt`/:func:`~socket.socket.setsockopt` to select an alternate routing table, is now available in the :mod:`socket` module. (Added by Kyle VanderBeek; :issue:`8235`.) @@ -2404,7 +2406,7 @@ Other Changes and Fixes :issue:`5464`.) * When importing a module from a :file:`.pyc` or :file:`.pyo` file - with an existing :file:`.py` counterpart, the :attr:`co_filename` + with an existing :file:`.py` counterpart, the :attr:`~codeobject.co_filename` attributes of the resulting code objects are overwritten when the original filename is obsolete. This can happen if the file has been renamed, moved, or is accessed through different paths. (Patch by diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index df821d68eb8d9f..2da90b7ed55744 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -399,9 +399,11 @@ PEP 626: Precise line numbers for debugging and other tools PEP 626 brings more precise and reliable line numbers for debugging, profiling and coverage tools. Tracing events, with the correct line number, are generated for all lines of code executed and only for lines of code that are executed. -The ``f_lineno`` attribute of frame objects will always contain the expected line number. +The :attr:`~frame.f_lineno` attribute of frame objects will always contain the +expected line number. -The ``co_lnotab`` attribute of code objects is deprecated and will be removed in 3.12. +The :attr:`~codeobject.co_lnotab` attribute of code objects is deprecated and +will be removed in 3.12. Code that needs to convert from offset to line number should use the new ``co_lines()`` method instead. PEP 634: Structural Pattern Matching @@ -1959,11 +1961,11 @@ Changes in the C API source_buf = PyBytes_AsString(source_bytes_object); code = Py_CompileString(source_buf, filename, Py_file_input); - * For ``FrameObject`` objects, the ``f_lasti`` member now represents a wordcode + * For ``FrameObject`` objects, the :attr:`~frame.f_lasti` member now represents a wordcode offset instead of a simple offset into the bytecode string. This means that this number needs to be multiplied by 2 to be used with APIs that expect a byte offset instead (like :c:func:`PyCode_Addr2Line` for example). Notice as well that the - ``f_lasti`` member of ``FrameObject`` objects is not considered stable: please + :attr:`!f_lasti` member of ``FrameObject`` objects is not considered stable: please use :c:func:`PyFrame_GetLineNumber` instead. CPython bytecode changes diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 48a0e621baad02..8db133b90a7a4b 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -2458,11 +2458,12 @@ Porting to Python 3.11 * ``f_valuestack``: removed. The Python frame object is now created lazily. A side effect is that the - ``f_back`` member must not be accessed directly, since its value is now also + :attr:`~frame.f_back` member must not be accessed directly, + since its value is now also computed lazily. The :c:func:`PyFrame_GetBack` function must be called instead. - Debuggers that accessed the ``f_locals`` directly *must* call + Debuggers that accessed the :attr:`~frame.f_locals` directly *must* call :c:func:`PyFrame_GetLocals` instead. They no longer need to call :c:func:`PyFrame_FastToLocalsWithError` or :c:func:`PyFrame_LocalsToFast`, in fact they should not call those functions. The necessary updating of the diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 8b7a043d068e5c..8551b35438e2c3 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -838,8 +838,7 @@ shutil * :func:`shutil.rmtree` now accepts a new argument *onexc* which is an error handler like *onerror* but which expects an exception instance - rather than a *(typ, val, tb)* triplet. *onerror* is deprecated and - will be removed in Python 3.14. + rather than a *(typ, val, tb)* triplet. *onerror* is deprecated. (Contributed by Irit Katriel in :gh:`102828`.) * :func:`shutil.which` now consults the *PATHEXT* environment variable to @@ -1261,8 +1260,8 @@ Deprecated :mod:`concurrent.futures` the fix is to use a different :mod:`multiprocessing` start method such as ``"spawn"`` or ``"forkserver"``. -* :mod:`shutil`: The *onerror* argument of :func:`shutil.rmtree` is deprecated and will be removed - in Python 3.14. Use *onexc* instead. (Contributed by Irit Katriel in :gh:`102828`.) +* :mod:`shutil`: The *onerror* argument of :func:`shutil.rmtree` is deprecated; + use *onexc* instead. (Contributed by Irit Katriel in :gh:`102828`.) * :mod:`sqlite3`: @@ -1324,7 +1323,8 @@ Deprecated ``int``, convert to int explicitly: ``~int(x)``. (Contributed by Tim Hoffmann in :gh:`103487`.) -* Accessing ``co_lnotab`` on code objects was deprecated in Python 3.10 via :pep:`626`, +* Accessing :attr:`~codeobject.co_lnotab` on code objects was deprecated in + Python 3.10 via :pep:`626`, but it only got a proper :exc:`DeprecationWarning` in 3.12, therefore it will be removed in 3.14. (Contributed by Nikita Sobolev in :gh:`101866`.) @@ -1431,7 +1431,7 @@ and will be removed in Python 3.14. * The ``__package__`` and ``__cached__`` attributes on module objects. -* The ``co_lnotab`` attribute of code objects. +* The :attr:`~codeobject.co_lnotab` attribute of code objects. Pending Removal in Python 3.15 ------------------------------ @@ -1640,7 +1640,10 @@ locale use :func:`locale.format_string` instead. (Contributed by Victor Stinner in :gh:`94226`.) -* ``smtpd``: The module has been removed according to the schedule in :pep:`594`, +smtpd +----- + +* The ``smtpd`` module has been removed according to the schedule in :pep:`594`, having been deprecated in Python 3.4.7 and 3.5.4. Use aiosmtpd_ PyPI module or any other :mod:`asyncio`-based server instead. @@ -1773,6 +1776,14 @@ Others (*ssl_context* in :mod:`imaplib`) instead. (Contributed by Victor Stinner in :gh:`94172`.) +* Remove ``Jython`` compatibility hacks from several stdlib modules and tests. + (Contributed by Nikita Sobolev in :gh:`99482`.) + +* Remove ``_use_broken_old_ctypes_structure_semantics_`` flag + from :mod:`ctypes` module. + (Contributed by Nikita Sobolev in :gh:`99285`.) + + .. _whatsnew312-porting-to-python312: Porting to Python 3.12 @@ -1884,6 +1895,15 @@ Changes in the Python API * Mixing tabs and spaces as indentation in the same file is not supported anymore and will raise a :exc:`TabError`. +* The :mod:`threading` module now expects the :mod:`!_thread` module to have + an ``_is_main_interpreter`` attribute. It is a function with no + arguments that returns ``True`` if the current interpreter is the + main interpreter. + + Any library or application that provides a custom ``_thread`` module + should provide ``_is_main_interpreter()``. + (See :gh:`112826`.) + Build Changes ============= @@ -2424,10 +2444,3 @@ Removed * Remove the ``PyUnicode_InternImmortal()`` function macro. (Contributed by Victor Stinner in :gh:`85858`.) - -* Remove ``Jython`` compatibility hacks from several stdlib modules and tests. - (Contributed by Nikita Sobolev in :gh:`99482`.) - -* Remove ``_use_broken_old_ctypes_structure_semantics_`` flag - from :mod:`ctypes` module. - (Contributed by Nikita Sobolev in :gh:`99285`.) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 4d05bce34ef847..4401deb0768c11 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -85,7 +85,13 @@ Important deprecations, removals or restrictions: New Features ============ +Improved Error Messages +----------------------- +* The interpreter now colorizes error messages when displaying tracebacks by default. + This feature can be controlled via the new :envvar:`PYTHON_COLORS` environment + variable as well as the canonical ``NO_COLOR`` and ``FORCE_COLOR`` environment + variables. (Contributed by Pablo Galindo Salgado in :gh:`112730`.) Other Language Changes ====================== @@ -168,6 +174,15 @@ copy any user classes which define the :meth:`!__replace__` method. (Contributed by Serhiy Storchaka in :gh:`108751`.) +dis +--- + +* Change the output of :mod:`dis` module functions to show logical + labels for jump targets and exception handlers, rather than offsets. + The offsets can be added with the new ``-O`` command line option or + the ``show_offsets`` parameter. + (Contributed by Irit Katriel in :gh:`112137`.) + dbm --- @@ -261,9 +276,11 @@ pathlib (Contributed by Barney Gale in :gh:`73435`.) * Add *follow_symlinks* keyword-only argument to :meth:`pathlib.Path.glob`, - :meth:`~pathlib.Path.rglob`, :meth:`~pathlib.Path.is_file`, and - :meth:`~pathlib.Path.is_dir`. - (Contributed by Barney Gale in :gh:`77609` and :gh:`105793`.) + :meth:`~pathlib.Path.rglob`, :meth:`~pathlib.Path.is_file`, + :meth:`~pathlib.Path.is_dir`, :meth:`~pathlib.Path.owner`, + :meth:`~pathlib.Path.group`. + (Contributed by Barney Gale in :gh:`77609` and :gh:`105793`, and + Kamil Turek in :gh:`107962`). pdb --- @@ -276,6 +293,11 @@ pdb identified and executed. (Contributed by Tian Gao in :gh:`108464`.) +* ``sys.path[0]`` will no longer be replaced by the directory of the script + being debugged when ``sys.flags.safe_path`` is set (via the :option:`-P` + command line option or :envvar:`PYTHONSAFEPATH` environment variable). + (Contributed by Tian Gao and Christian Walther in :gh:`111762`.) + sqlite3 ------- @@ -283,6 +305,13 @@ sqlite3 object is not :meth:`closed ` explicitly. (Contributed by Erlend E. Aasland in :gh:`105539`.) +sys +--- + +* Add the :func:`sys._is_interned` function to test if the string was interned. + This function is not guaranteed to exist in all implementations of Python. + (Contributed by Serhiy Storchaka in :gh:`78573`.) + tkinter ------- @@ -292,6 +321,11 @@ tkinter :meth:`!tk_busy_current`, and :meth:`!tk_busy_status`. (Contributed by Miguel, klappnase and Serhiy Storchaka in :gh:`72684`.) +* Add support of the "vsapi" element type in + the :meth:`~tkinter.ttk.Style.element_create` method of + :class:`tkinter.ttk.Style`. + (Contributed by Serhiy Storchaka in :gh:`68166`.) + traceback --------- @@ -299,6 +333,12 @@ traceback to format the nested exceptions of a :exc:`BaseExceptionGroup` instance, recursively. (Contributed by Irit Katriel in :gh:`105292`.) +* Add the field *exc_type_str* to :class:`~traceback.TracebackException`, which + holds a string display of the *exc_type*. Deprecate the field *exc_type* + which holds the type object itself. Add parameter *save_exc_type* (default + ``True``) to indicate whether ``exc_type`` should be saved. + (Contributed by Irit Katriel in :gh:`112332`.) + typing ------ @@ -307,6 +347,12 @@ typing check whether a class is a :class:`typing.Protocol`. (Contributed by Jelle Zijlstra in :gh:`104873`.) +unicodedata +----------- + +* The Unicode database has been updated to version 15.1.0. (Contributed by + James Gerity in :gh:`109559`.) + venv ---- @@ -317,6 +363,15 @@ venv (using ``--without-scm-ignore-files``). (Contributed by Brett Cannon in :gh:`108125`.) +warnings +-------- + +* The new :func:`warnings.deprecated` decorator provides a way to communicate + deprecations to :term:`static type checkers ` and + to warn on usage of deprecated classes and functions. A runtime deprecation + warning may also be emitted when a decorated function or class is used at runtime. + See :pep:`702`. (Contributed by Jelle Zijlstra in :gh:`104003`.) + Optimizations ============= @@ -352,6 +407,11 @@ Deprecated security and functionality bugs. This includes removal of the ``--cgi`` flag to the ``python -m http.server`` command line in 3.15. +* :mod:`traceback`: + + * The field *exc_type* of :class:`traceback.TracebackException` is + deprecated. Use *exc_type_str* instead. + * :mod:`typing`: * Creating a :class:`typing.NamedTuple` class using keyword arguments to denote @@ -518,7 +578,8 @@ Pending Removal in Python 3.14 * date and datetime adapter, date and timestamp converter: see the :mod:`sqlite3` documentation for suggested replacement recipes. -* :class:`types.CodeType`: Accessing ``co_lnotab`` was deprecated in :pep:`626` +* :class:`types.CodeType`: Accessing :attr:`~codeobject.co_lnotab` was + deprecated in :pep:`626` since 3.10 and was planned to be removed in 3.12, but it only got a proper :exc:`DeprecationWarning` in 3.12. May be removed in 3.14. @@ -681,7 +742,7 @@ although there is currently no date scheduled for their removal. * :mod:`!sre_compile`, :mod:`!sre_constants` and :mod:`!sre_parse` modules. -* ``types.CodeType.co_lnotab``: use the ``co_lines`` attribute instead. +* :attr:`~codeobject.co_lnotab`: use the ``co_lines`` attribute instead. * :class:`typing.Text` (:gh:`92332`). @@ -1017,6 +1078,20 @@ Changes in the Python API recomended in the documentation. (Contributed by Serhiy Storchaka in :gh:`106672`.) +* An :exc:`OSError` is now raised by :func:`getpass.getuser` for any failure to + retrieve a username, instead of :exc:`ImportError` on non-Unix platforms or + :exc:`KeyError` on Unix platforms where the password database is empty. + +* The :mod:`threading` module now expects the :mod:`!_thread` module to have + an ``_is_main_interpreter`` attribute. It is a function with no + arguments that returns ``True`` if the current interpreter is the + main interpreter. + + Any library or application that provides a custom ``_thread`` module + must provide ``_is_main_interpreter()``, just like the module's + other "private" attributes. + (See :gh:`112826`.) + Build Changes ============= @@ -1055,6 +1130,16 @@ New Features APIs accepting the format codes always use ``Py_ssize_t`` for ``#`` formats. (Contributed by Inada Naoki in :gh:`104922`.) +* The *keywords* parameter of :c:func:`PyArg_ParseTupleAndKeywords` and + :c:func:`PyArg_VaParseTupleAndKeywords` has now type :c:expr:`char * const *` + in C and :c:expr:`const char * const *` in C++, instead of :c:expr:`char **`. + It makes these functions compatible with arguments of type + :c:expr:`const char * const *`, :c:expr:`const char **` or + :c:expr:`char * const *` in C++ and :c:expr:`char * const *` in C + without an explicit type cast. + This can be overridden with the :c:macro:`PY_CXX_CONST` macro. + (Contributed by Serhiy Storchaka in :gh:`65210`.) + * Add :c:func:`PyImport_AddModuleRef`: similar to :c:func:`PyImport_AddModule`, but return a :term:`strong reference` instead of a :term:`borrowed reference`. @@ -1181,6 +1266,9 @@ New Features :exc:`KeyError` if the key missing. (Contributed by Stefan Behnel and Victor Stinner in :gh:`111262`.) +* Add :c:func:`Py_HashPointer` function to hash a pointer. + (Contributed by Victor Stinner in :gh:`111545`.) + Porting to Python 3.13 ---------------------- diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index c15d8be651fd17..5a3cea0ec87cb2 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -2160,14 +2160,15 @@ Changes in the Python API * :c:func:`PyErr_SetImportError` now sets :exc:`TypeError` when its **msg** argument is not set. Previously only ``NULL`` was returned. -* The format of the ``co_lnotab`` attribute of code objects changed to support +* The format of the :attr:`~codeobject.co_lnotab` attribute of code objects + changed to support a negative line number delta. By default, Python does not emit bytecode with - a negative line number delta. Functions using ``frame.f_lineno``, + a negative line number delta. Functions using :attr:`frame.f_lineno`, ``PyFrame_GetLineNumber()`` or ``PyCode_Addr2Line()`` are not affected. - Functions directly decoding ``co_lnotab`` should be updated to use a signed + Functions directly decoding :attr:`!co_lnotab` should be updated to use a signed 8-bit integer type for the line number delta, but this is only required to support applications using a negative line number delta. See - ``Objects/lnotab_notes.txt`` for the ``co_lnotab`` format and how to decode + ``Objects/lnotab_notes.txt`` for the :attr:`!co_lnotab` format and how to decode it, and see the :pep:`511` for the rationale. * The functions in the :mod:`compileall` module now return booleans instead diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index a7d5c3db6ddcb2..7a74f9c1685c31 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -1891,7 +1891,7 @@ Other CPython Implementation Changes * Trace hooks may now opt out of receiving the ``line`` and opt into receiving the ``opcode`` events from the interpreter by setting the corresponding new - ``f_trace_lines`` and ``f_trace_opcodes`` attributes on the + :attr:`~frame.f_trace_lines` and :attr:`~frame.f_trace_opcodes` attributes on the frame being traced. (Contributed by Nick Coghlan in :issue:`31344`.) * Fixed some consistency problems with namespace package module attributes. @@ -2144,9 +2144,9 @@ The following features and APIs have been removed from Python 3.7: * Removed support of the *exclude* argument in :meth:`tarfile.TarFile.add`. It was deprecated in Python 2.7 and 3.2. Use the *filter* argument instead. -* The ``splitunc()`` function in the :mod:`ntpath` module was deprecated in - Python 3.1, and has now been removed. Use the :func:`~os.path.splitdrive` - function instead. +* The :func:`!ntpath.splitunc` function was deprecated in + Python 3.1, and has now been removed. Use :func:`~os.path.splitdrive` + instead. * :func:`collections.namedtuple` no longer supports the *verbose* parameter or ``_source`` attribute which showed the generated source code for the diff --git a/Grammar/python.gram b/Grammar/python.gram index 55930b0f357ca0..c9269b058bc7f6 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -124,6 +124,7 @@ simple_stmt[stmt_ty] (memo): | &'nonlocal' nonlocal_stmt compound_stmt[stmt_ty]: + | invalid_compound_stmt | &('def' | '@' | 'async') function_def | &'if' if_stmt | &('class' | '@') class_def @@ -1298,6 +1299,10 @@ invalid_import_from_targets: | import_from_as_names ',' NEWLINE { RAISE_SYNTAX_ERROR("trailing comma not allowed without surrounding parentheses") } +invalid_compound_stmt: + | a='elif' named_expression ':' { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "'elif' must match an if-statement here") } + | a='else' ':' { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "'else' must match a valid statement here") } + invalid_with_stmt: | ['async'] 'with' ','.(expression ['as' star_target])+ NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") } | ['async'] 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") } diff --git a/Include/boolobject.h b/Include/boolobject.h index 976fa35201d035..19aef5b1b87c6a 100644 --- a/Include/boolobject.h +++ b/Include/boolobject.h @@ -7,7 +7,7 @@ extern "C" { #endif -PyAPI_DATA(PyTypeObject) PyBool_Type; +// PyBool_Type is declared by object.h #define PyBool_Check(x) Py_IS_TYPE((x), &PyBool_Type) diff --git a/Include/cpython/pyhash.h b/Include/cpython/pyhash.h index 6f7113daa5fe4d..396c208e1b106a 100644 --- a/Include/cpython/pyhash.h +++ b/Include/cpython/pyhash.h @@ -21,7 +21,9 @@ /* Helpers for hash functions */ PyAPI_FUNC(Py_hash_t) _Py_HashDouble(PyObject *, double); -PyAPI_FUNC(Py_hash_t) _Py_HashPointer(const void*); + +// Kept for backward compatibility +#define _Py_HashPointer Py_HashPointer /* hash function definition */ @@ -33,3 +35,5 @@ typedef struct { } PyHash_FuncDef; PyAPI_FUNC(PyHash_FuncDef*) PyHash_GetFuncDef(void); + +PyAPI_FUNC(Py_hash_t) Py_HashPointer(const void *ptr); diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 34e23830f27e77..56172d231c44f4 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -151,7 +151,7 @@ struct _ts { /* Tagged pointer to top-most critical section, or zero if there is no * active critical section. Critical sections are only used in - * `--disable-gil` builds (i.e., when Py_NOGIL is defined to 1). In the + * `--disable-gil` builds (i.e., when Py_GIL_DISABLED is defined to 1). In the * default build, this field is always zero. */ uintptr_t critical_section; diff --git a/Include/cpython/sysmodule.h b/Include/cpython/sysmodule.h index 9fd7cc0cb43931..a3ac07f538a94f 100644 --- a/Include/cpython/sysmodule.h +++ b/Include/cpython/sysmodule.h @@ -4,10 +4,6 @@ typedef int(*Py_AuditHookFunction)(const char *, PyObject *, void *); -PyAPI_FUNC(int) PySys_Audit( - const char *event, - const char *format, - ...); PyAPI_FUNC(int) PySys_AddAuditHook(Py_AuditHookFunction, void*); typedef struct { diff --git a/Include/internal/pycore_atexit.h b/Include/internal/pycore_atexit.h index 3966df70e2616f..4dcda8f517c787 100644 --- a/Include/internal/pycore_atexit.h +++ b/Include/internal/pycore_atexit.h @@ -1,5 +1,8 @@ #ifndef Py_INTERNAL_ATEXIT_H #define Py_INTERNAL_ATEXIT_H + +#include "pycore_lock.h" // PyMutex + #ifdef __cplusplus extern "C" { #endif @@ -15,7 +18,7 @@ extern "C" { typedef void (*atexit_callbackfunc)(void); struct _atexit_runtime_state { - PyThread_type_lock mutex; + PyMutex mutex; #define NEXITFUNCS 32 atexit_callbackfunc callbacks[NEXITFUNCS]; int ncallbacks; diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index c372b7224fb047..64fb4034669e19 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -41,8 +41,7 @@ PyAPI_FUNC(int) _PyEval_MakePendingCalls(PyThreadState *); #endif extern void _Py_FinishPendingCalls(PyThreadState *tstate); -extern void _PyEval_InitState(PyInterpreterState *, PyThread_type_lock); -extern void _PyEval_FiniState(struct _ceval_state *ceval); +extern void _PyEval_InitState(PyInterpreterState *); extern void _PyEval_SignalReceived(PyInterpreterState *interp); // bitwise flags: @@ -101,6 +100,7 @@ extern int _PyPerfTrampoline_SetCallbacks(_PyPerf_Callbacks *); extern void _PyPerfTrampoline_GetCallbacks(_PyPerf_Callbacks *); extern int _PyPerfTrampoline_Init(int activate); extern int _PyPerfTrampoline_Fini(void); +extern void _PyPerfTrampoline_FreeArenas(void); extern int _PyIsPerfTrampolineActive(void); extern PyStatus _PyPerfTrampoline_AfterFork_Child(void); #ifdef PY_HAVE_PERF_TRAMPOLINE diff --git a/Include/internal/pycore_ceval_state.h b/Include/internal/pycore_ceval_state.h index 072bbcda0c3c82..28738980eb49be 100644 --- a/Include/internal/pycore_ceval_state.h +++ b/Include/internal/pycore_ceval_state.h @@ -8,6 +8,7 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +#include "pycore_lock.h" // PyMutex #include "pycore_gil.h" // struct _gil_runtime_state @@ -15,7 +16,7 @@ typedef int (*_Py_pending_call_func)(void *); struct _pending_calls { int busy; - PyThread_type_lock lock; + PyMutex mutex; /* Request for running pending calls. */ int32_t calls_to_do; #define NPENDINGCALLS 32 diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index eaf84a9c94fc9b..73df6c3568ffe0 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -394,27 +394,29 @@ write_varint(uint8_t *ptr, unsigned int val) val >>= 6; written++; } - *ptr = val; + *ptr = (uint8_t)val; return written; } static inline int write_signed_varint(uint8_t *ptr, int val) { + unsigned int uval; if (val < 0) { - val = ((-val)<<1) | 1; + // (unsigned int)(-val) has an undefined behavior for INT_MIN + uval = ((0 - (unsigned int)val) << 1) | 1; } else { - val = val << 1; + uval = (unsigned int)val << 1; } - return write_varint(ptr, val); + return write_varint(ptr, uval); } static inline int write_location_entry_start(uint8_t *ptr, int code, int length) { assert((code & 15) == code); - *ptr = 128 | (code << 3) | (length - 1); + *ptr = 128 | (uint8_t)(code << 3) | (uint8_t)(length - 1); return 1; } @@ -454,9 +456,9 @@ write_location_entry_start(uint8_t *ptr, int code, int length) static inline uint16_t -adaptive_counter_bits(int value, int backoff) { - return (value << ADAPTIVE_BACKOFF_BITS) | - (backoff & ((1< MAX_BACKOFF_VALUE) { backoff = MAX_BACKOFF_VALUE; } - unsigned int value = (1 << backoff) - 1; + uint16_t value = (uint16_t)(1 << backoff) - 1; return adaptive_counter_bits(value, backoff); } diff --git a/Include/internal/pycore_critical_section.h b/Include/internal/pycore_critical_section.h index 73c2e243f20bcc..bf2bbfffc38bd0 100644 --- a/Include/internal/pycore_critical_section.h +++ b/Include/internal/pycore_critical_section.h @@ -86,7 +86,7 @@ extern "C" { #define _Py_CRITICAL_SECTION_TWO_MUTEXES 0x2 #define _Py_CRITICAL_SECTION_MASK 0x3 -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED # define Py_BEGIN_CRITICAL_SECTION(op) \ { \ _PyCriticalSection _cs; \ @@ -104,13 +104,13 @@ extern "C" { # define Py_END_CRITICAL_SECTION2() \ _PyCriticalSection2_End(&_cs2); \ } -#else /* !Py_NOGIL */ +#else /* !Py_GIL_DISABLED */ // The critical section APIs are no-ops with the GIL. # define Py_BEGIN_CRITICAL_SECTION(op) # define Py_END_CRITICAL_SECTION() # define Py_BEGIN_CRITICAL_SECTION2(a, b) # define Py_END_CRITICAL_SECTION2() -#endif /* !Py_NOGIL */ +#endif /* !Py_GIL_DISABLED */ typedef struct { // Tagged pointer to an outer active critical section (or 0). diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index ee9ff0090c2484..2e6d09a49f95d3 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -8,6 +8,7 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +#include "pycore_lock.h" // PyMutex #include "pycore_pyerrors.h" @@ -128,7 +129,7 @@ struct _xidregitem { struct _xidregistry { int global; /* builtin types or heap types */ int initialized; - PyThread_type_lock mutex; + PyMutex mutex; struct _xidregitem *head; }; @@ -170,9 +171,14 @@ extern void _PyXI_Fini(PyInterpreterState *interp); // of the exception in the calling interpreter. typedef struct _excinfo { - const char *type; + struct _excinfo_type { + PyTypeObject *builtin; + const char *name; + const char *qualname; + const char *module; + } type; const char *msg; -} _Py_excinfo; +} _PyXI_excinfo; typedef enum error_code { @@ -193,13 +199,13 @@ typedef struct _sharedexception { // The kind of error to propagate. _PyXI_errcode code; // The exception information to propagate, if applicable. - // This is populated only for _PyXI_ERR_UNCAUGHT_EXCEPTION. - _Py_excinfo uncaught; -} _PyXI_exception_info; + // This is populated only for some error codes, + // but always for _PyXI_ERR_UNCAUGHT_EXCEPTION. + _PyXI_excinfo uncaught; +} _PyXI_error; + +PyAPI_FUNC(PyObject *) _PyXI_ApplyError(_PyXI_error *err); -PyAPI_FUNC(void) _PyXI_ApplyExceptionInfo( - _PyXI_exception_info *info, - PyObject *exctype); typedef struct xi_session _PyXI_session; typedef struct _sharedns _PyXI_namespace; @@ -251,13 +257,13 @@ struct xi_session { // This is set if the interpreter is entered and raised an exception // that needs to be handled in some special way during exit. - _PyXI_errcode *exc_override; + _PyXI_errcode *error_override; // This is set if exit captured an exception to propagate. - _PyXI_exception_info *exc; + _PyXI_error *error; // -- pre-allocated memory -- - _PyXI_exception_info _exc; - _PyXI_errcode _exc_override; + _PyXI_error _error; + _PyXI_errcode _error_override; }; PyAPI_FUNC(int) _PyXI_Enter( @@ -266,9 +272,7 @@ PyAPI_FUNC(int) _PyXI_Enter( PyObject *nsupdates); PyAPI_FUNC(void) _PyXI_Exit(_PyXI_session *session); -PyAPI_FUNC(void) _PyXI_ApplyCapturedException( - _PyXI_session *session, - PyObject *excwrapper); +PyAPI_FUNC(PyObject *) _PyXI_ApplyCapturedException(_PyXI_session *session); PyAPI_FUNC(int) _PyXI_HasCapturedException(_PyXI_session *session); diff --git a/Include/internal/pycore_dtoa.h b/Include/internal/pycore_dtoa.h index ac62a4d300720a..c5cfdf4ce8f823 100644 --- a/Include/internal/pycore_dtoa.h +++ b/Include/internal/pycore_dtoa.h @@ -35,6 +35,9 @@ struct _dtoa_state { /* The size of the Bigint freelist */ #define Bigint_Kmax 7 +/* The size of the cached powers of 5 array */ +#define Bigint_Pow5size 8 + #ifndef PRIVATE_MEM #define PRIVATE_MEM 2304 #endif @@ -42,9 +45,10 @@ struct _dtoa_state { ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) struct _dtoa_state { - /* p5s is a linked list of powers of 5 of the form 5**(2**i), i >= 2 */ + // p5s is an array of powers of 5 of the form: + // 5**(2**(i+2)) for 0 <= i < Bigint_Pow5size + struct Bigint *p5s[Bigint_Pow5size]; // XXX This should be freed during runtime fini. - struct Bigint *p5s; struct Bigint *freelist[Bigint_Kmax+1]; double preallocated[Bigint_PREALLOC_SIZE]; double *preallocated_next; @@ -57,9 +61,6 @@ struct _dtoa_state { #endif // !Py_USING_MEMORY_DEBUGGER -/* These functions are used by modules compiled as C extension like math: - they must be exported. */ - extern double _Py_dg_strtod(const char *str, char **ptr); extern char* _Py_dg_dtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve); @@ -67,6 +68,11 @@ extern void _Py_dg_freedtoa(char *s); #endif // _PY_SHORT_FLOAT_REPR == 1 + +extern PyStatus _PyDtoa_Init(PyInterpreterState *interp); +extern void _PyDtoa_Fini(PyInterpreterState *interp); + + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_fileutils.h b/Include/internal/pycore_fileutils.h index 2f89da2c6ecd91..5c55282fa39e6f 100644 --- a/Include/internal/pycore_fileutils.h +++ b/Include/internal/pycore_fileutils.h @@ -320,6 +320,10 @@ PyAPI_FUNC(char*) _Py_UniversalNewlineFgetsWithSize(char *, int, FILE*, PyObject extern int _PyFile_Flush(PyObject *); +#ifndef MS_WINDOWS +extern int _Py_GetTicksPerSecond(long *ticks_per_second); +#endif + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 0808076f44de31..89ec8cbbbcd649 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -826,6 +826,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(call)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(call_exception_handler)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(call_soon)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(callback)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cancel)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(capath)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(category)); @@ -971,6 +972,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hook)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(id)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ident)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(identity_hint)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ignore)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(imag)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(importlib)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 8d22a9ba261010..62c3ee3ae2a0bd 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -315,6 +315,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(call) STRUCT_FOR_ID(call_exception_handler) STRUCT_FOR_ID(call_soon) + STRUCT_FOR_ID(callback) STRUCT_FOR_ID(cancel) STRUCT_FOR_ID(capath) STRUCT_FOR_ID(category) @@ -460,6 +461,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(hook) STRUCT_FOR_ID(id) STRUCT_FOR_ID(ident) + STRUCT_FOR_ID(identity_hint) STRUCT_FOR_ID(ignore) STRUCT_FOR_ID(imag) STRUCT_FOR_ID(importlib) diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index 117e46bb86285d..c84f87a831bf38 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -9,6 +9,7 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +#include "pycore_lock.h" // PyMutex #include "pycore_hashtable.h" // _Py_hashtable_t #include "pycore_time.h" // _PyTime_t @@ -47,7 +48,7 @@ struct _import_runtime_state { Py_ssize_t last_module_index; struct { /* A lock to guard the cache. */ - PyThread_type_lock mutex; + PyMutex mutex; /* The actual cache of (filename, name, PyModuleDef) for modules. Only legacy (single-phase init) extension modules are added and only if they support multiple initialization (m_size >- 0) diff --git a/Include/internal/pycore_importdl.h b/Include/internal/pycore_importdl.h index dee64241c763f3..c8583582b358ac 100644 --- a/Include/internal/pycore_importdl.h +++ b/Include/internal/pycore_importdl.h @@ -31,7 +31,7 @@ typedef FARPROC dl_funcptr; # define PYD_DEBUG_SUFFIX "" #endif -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED # define PYD_THREADING_TAG "t" #else # define PYD_THREADING_TAG "" diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 498db8becf114c..2a683196eeced3 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -29,6 +29,7 @@ extern "C" { #include "pycore_list.h" // struct _Py_list_state #include "pycore_object_state.h" // struct _py_object_state #include "pycore_obmalloc.h" // struct _obmalloc_state +#include "pycore_tstate.h" // _PyThreadStateImpl #include "pycore_tuple.h" // struct _Py_tuple_state #include "pycore_typeobject.h" // struct types_state #include "pycore_unicodeobject.h" // struct _Py_unicode_state @@ -210,8 +211,8 @@ struct _is { struct _Py_interp_cached_objects cached_objects; struct _Py_interp_static_objects static_objects; - /* the initial PyInterpreterState.threads.head */ - PyThreadState _initial_thread; + /* the initial PyInterpreterState.threads.head */ + _PyThreadStateImpl _initial_thread; Py_ssize_t _interactive_src_count; }; diff --git a/Include/internal/pycore_lock.h b/Include/internal/pycore_lock.h index 25c3cf5377b778..03ad1c9fd584b5 100644 --- a/Include/internal/pycore_lock.h +++ b/Include/internal/pycore_lock.h @@ -33,11 +33,11 @@ extern "C" { // ... // PyMutex_Unlock(&m); -// NOTE: In Py_NOGIL builds, `struct _PyMutex` is defined in Include/object.h. -// The Py_NOGIL builds need the definition in Include/object.h for the +// NOTE: In Py_GIL_DISABLED builds, `struct _PyMutex` is defined in Include/object.h. +// The Py_GIL_DISABLED builds need the definition in Include/object.h for the // `ob_mutex` field in PyObject. For the default (non-free-threaded) build, // we define the struct here to avoid exposing it in the public API. -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED struct _PyMutex { uint8_t v; }; #endif @@ -92,6 +92,13 @@ PyMutex_IsLocked(PyMutex *m) return (_Py_atomic_load_uint8(&m->v) & _Py_LOCKED) != 0; } +// Re-initializes the mutex after a fork to the unlocked state. +static inline void +_PyMutex_at_fork_reinit(PyMutex *m) +{ + memset(m, 0, sizeof(*m)); +} + typedef enum _PyLockFlags { // Do not detach/release the GIL when waiting on the lock. _Py_LOCK_DONT_DETACH = 0, @@ -108,6 +115,16 @@ typedef enum _PyLockFlags { extern PyLockStatus _PyMutex_LockTimed(PyMutex *m, _PyTime_t timeout_ns, _PyLockFlags flags); +// Lock a mutex with aditional options. See _PyLockFlags for details. +static inline void +PyMutex_LockFlags(PyMutex *m, _PyLockFlags flags) +{ + uint8_t expected = _Py_UNLOCKED; + if (!_Py_atomic_compare_exchange_uint8(&m->v, &expected, _Py_LOCKED)) { + _PyMutex_LockTimed(m, -1, flags); + } +} + // Unlock a mutex, returns 0 if the mutex is not locked (used for improved // error messages). extern int _PyMutex_TryUnlock(PyMutex *m); diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 206d8a5d4cc5e1..f413b8451e5ab4 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -54,7 +54,7 @@ PyAPI_FUNC(int) _PyObject_IsFreed(PyObject *); Furthermore, we can't use designated initializers in Extensions since these are not supported pre-C++20. Thus, keeping an internal copy here is the most backwards compatible solution */ -#if defined(Py_NOGIL) +#if defined(Py_GIL_DISABLED) #define _PyObject_HEAD_INIT(type) \ { \ .ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL, \ @@ -103,7 +103,7 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n) #ifdef Py_REF_DEBUG _Py_AddRefTotal(_PyInterpreterState_GET(), n); #endif -#if !defined(Py_NOGIL) +#if !defined(Py_GIL_DISABLED) op->ob_refcnt += n; #else if (_Py_IsOwnedByCurrentThread(op)) { @@ -128,7 +128,7 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n) static inline void _Py_SetImmortal(PyObject *op) { if (op) { -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED op->ob_tid = _Py_UNOWNED_TID; op->ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL; op->ob_ref_shared = 0; @@ -145,7 +145,7 @@ static inline void _Py_SetMortal(PyObject *op, Py_ssize_t refcnt) { if (op) { assert(_Py_IsImmortal(op)); -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED op->ob_tid = _Py_UNOWNED_TID; op->ob_ref_local = 0; op->ob_ref_shared = _Py_REF_SHARED(refcnt, _Py_REF_MERGED); @@ -169,7 +169,7 @@ static inline void _Py_ClearImmortal(PyObject *op) op = NULL; \ } while (0) -#if !defined(Py_NOGIL) +#if !defined(Py_GIL_DISABLED) static inline void _Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct) { @@ -210,7 +210,7 @@ _Py_DECREF_NO_DEALLOC(PyObject *op) } #else -// TODO: implement Py_DECREF specializations for Py_NOGIL build +// TODO: implement Py_DECREF specializations for Py_GIL_DISABLED build static inline void _Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct) { @@ -238,7 +238,7 @@ _Py_REF_IS_QUEUED(Py_ssize_t ob_ref_shared) // 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); -#endif // !defined(Py_NOGIL) +#endif // !defined(Py_GIL_DISABLED) #ifdef Py_REF_DEBUG # undef _Py_DEC_REFTOTAL diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 4d98b23df5d927..774c0f99379ed6 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -81,45 +81,46 @@ #define _IS_NONE 353 #define _SPECIALIZE_FOR_ITER 354 #define _FOR_ITER 355 -#define _ITER_CHECK_LIST 356 -#define _ITER_JUMP_LIST 357 -#define _GUARD_NOT_EXHAUSTED_LIST 358 -#define _ITER_NEXT_LIST 359 -#define _ITER_CHECK_TUPLE 360 -#define _ITER_JUMP_TUPLE 361 -#define _GUARD_NOT_EXHAUSTED_TUPLE 362 -#define _ITER_NEXT_TUPLE 363 -#define _ITER_CHECK_RANGE 364 -#define _ITER_JUMP_RANGE 365 -#define _GUARD_NOT_EXHAUSTED_RANGE 366 -#define _ITER_NEXT_RANGE 367 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 368 -#define _GUARD_KEYS_VERSION 369 -#define _LOAD_ATTR_METHOD_WITH_VALUES 370 -#define _LOAD_ATTR_METHOD_NO_DICT 371 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 372 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 373 -#define _CHECK_ATTR_METHOD_LAZY_DICT 374 -#define _LOAD_ATTR_METHOD_LAZY_DICT 375 -#define _SPECIALIZE_CALL 376 -#define _CALL 377 -#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 378 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 379 -#define _CHECK_PEP_523 380 -#define _CHECK_FUNCTION_EXACT_ARGS 381 -#define _CHECK_STACK_SPACE 382 -#define _INIT_CALL_PY_EXACT_ARGS 383 -#define _PUSH_FRAME 384 -#define _SPECIALIZE_BINARY_OP 385 -#define _BINARY_OP 386 -#define _GUARD_IS_TRUE_POP 387 -#define _GUARD_IS_FALSE_POP 388 -#define _GUARD_IS_NONE_POP 389 -#define _GUARD_IS_NOT_NONE_POP 390 -#define _JUMP_TO_TOP 391 -#define _SAVE_RETURN_OFFSET 392 -#define _INSERT 393 -#define _CHECK_VALIDITY 394 +#define _FOR_ITER_TIER_TWO 356 +#define _ITER_CHECK_LIST 357 +#define _ITER_JUMP_LIST 358 +#define _GUARD_NOT_EXHAUSTED_LIST 359 +#define _ITER_NEXT_LIST 360 +#define _ITER_CHECK_TUPLE 361 +#define _ITER_JUMP_TUPLE 362 +#define _GUARD_NOT_EXHAUSTED_TUPLE 363 +#define _ITER_NEXT_TUPLE 364 +#define _ITER_CHECK_RANGE 365 +#define _ITER_JUMP_RANGE 366 +#define _GUARD_NOT_EXHAUSTED_RANGE 367 +#define _ITER_NEXT_RANGE 368 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 369 +#define _GUARD_KEYS_VERSION 370 +#define _LOAD_ATTR_METHOD_WITH_VALUES 371 +#define _LOAD_ATTR_METHOD_NO_DICT 372 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 373 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 374 +#define _CHECK_ATTR_METHOD_LAZY_DICT 375 +#define _LOAD_ATTR_METHOD_LAZY_DICT 376 +#define _SPECIALIZE_CALL 377 +#define _CALL 378 +#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 379 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 380 +#define _CHECK_PEP_523 381 +#define _CHECK_FUNCTION_EXACT_ARGS 382 +#define _CHECK_STACK_SPACE 383 +#define _INIT_CALL_PY_EXACT_ARGS 384 +#define _PUSH_FRAME 385 +#define _SPECIALIZE_BINARY_OP 386 +#define _BINARY_OP 387 +#define _GUARD_IS_TRUE_POP 388 +#define _GUARD_IS_FALSE_POP 389 +#define _GUARD_IS_NONE_POP 390 +#define _GUARD_IS_NOT_NONE_POP 391 +#define _JUMP_TO_TOP 392 +#define _SAVE_RETURN_OFFSET 393 +#define _INSERT 394 +#define _CHECK_VALIDITY 395 extern int _PyOpcode_num_popped(int opcode, int oparg, bool jump); #ifdef NEED_OPCODE_METADATA @@ -543,6 +544,8 @@ int _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 1; case _FOR_ITER: return 1; + case _FOR_ITER_TIER_TWO: + return 1; case FOR_ITER: return 1; case INSTRUMENTED_FOR_ITER: @@ -1181,6 +1184,8 @@ int _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return 1; case _FOR_ITER: return 2; + case _FOR_ITER_TIER_TWO: + return 2; case FOR_ITER: return 2; case INSTRUMENTED_FOR_ITER: @@ -1280,11 +1285,11 @@ int _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case _INIT_CALL_PY_EXACT_ARGS: return 1; case _PUSH_FRAME: - return 1; + return 0; case CALL_BOUND_METHOD_EXACT_ARGS: - return 1; + return 0; case CALL_PY_EXACT_ARGS: - return 1; + return 0; case CALL_PY_WITH_DEFAULTS: return 1; case CALL_TYPE_1: @@ -1656,7 +1661,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = { [IMPORT_FROM] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [JUMP_FORWARD] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [JUMP_BACKWARD] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [JUMP] = { true, 0, HAS_ARG_FLAG | HAS_JUMP_FLAG }, + [JUMP] = { true, 0, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [JUMP_NO_INTERRUPT] = { true, 0, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [ENTER_EXECUTOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG }, [_POP_JUMP_IF_FALSE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, @@ -1676,6 +1681,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = { [GET_YIELD_FROM_ITER] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [_SPECIALIZE_FOR_ITER] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_ESCAPES_FLAG }, [_FOR_ITER] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, + [_FOR_ITER_TIER_TWO] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [FOR_ITER] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [INSTRUMENTED_FOR_ITER] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [_ITER_CHECK_LIST] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG }, @@ -1697,9 +1703,9 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = { [BEFORE_ASYNC_WITH] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [BEFORE_WITH] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [WITH_EXCEPT_START] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [SETUP_FINALLY] = { true, 0, 0 }, - [SETUP_CLEANUP] = { true, 0, 0 }, - [SETUP_WITH] = { true, 0, 0 }, + [SETUP_FINALLY] = { true, 0, HAS_ARG_FLAG }, + [SETUP_CLEANUP] = { true, 0, HAS_ARG_FLAG }, + [SETUP_WITH] = { true, 0, HAS_ARG_FLAG }, [POP_BLOCK] = { true, 0, 0 }, [PUSH_EXC_INFO] = { true, INSTR_FMT_IX, 0 }, [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG }, @@ -1906,6 +1912,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN [MATCH_KEYS] = { .nuops = 1, .uops = { { MATCH_KEYS, 0, 0 } } }, [GET_ITER] = { .nuops = 1, .uops = { { GET_ITER, 0, 0 } } }, [GET_YIELD_FROM_ITER] = { .nuops = 1, .uops = { { GET_YIELD_FROM_ITER, 0, 0 } } }, + [FOR_ITER] = { .nuops = 1, .uops = { { _FOR_ITER, 0, 0 } } }, [FOR_ITER_LIST] = { .nuops = 3, .uops = { { _ITER_CHECK_LIST, 0, 0 }, { _ITER_JUMP_LIST, 0, 0 }, { _ITER_NEXT_LIST, 0, 0 } } }, [FOR_ITER_TUPLE] = { .nuops = 3, .uops = { { _ITER_CHECK_TUPLE, 0, 0 }, { _ITER_JUMP_TUPLE, 0, 0 }, { _ITER_NEXT_TUPLE, 0, 0 } } }, [FOR_ITER_RANGE] = { .nuops = 3, .uops = { { _ITER_CHECK_RANGE, 0, 0 }, { _ITER_JUMP_RANGE, 0, 0 }, { _ITER_NEXT_RANGE, 0, 0 } } }, @@ -2005,6 +2012,7 @@ const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE] = { [_IS_NONE] = "_IS_NONE", [_SPECIALIZE_FOR_ITER] = "_SPECIALIZE_FOR_ITER", [_FOR_ITER] = "_FOR_ITER", + [_FOR_ITER_TIER_TWO] = "_FOR_ITER_TIER_TWO", [_ITER_CHECK_LIST] = "_ITER_CHECK_LIST", [_ITER_JUMP_LIST] = "_ITER_JUMP_LIST", [_GUARD_NOT_EXHAUSTED_LIST] = "_GUARD_NOT_EXHAUSTED_LIST", diff --git a/Include/internal/pycore_pyhash.h b/Include/internal/pycore_pyhash.h index c3b72d90de3a69..0ce08900e96f0b 100644 --- a/Include/internal/pycore_pyhash.h +++ b/Include/internal/pycore_pyhash.h @@ -5,8 +5,20 @@ # error "this header requires Py_BUILD_CORE define" #endif -// Similar to _Py_HashPointer(), but don't replace -1 with -2 -extern Py_hash_t _Py_HashPointerRaw(const void*); +// Similar to Py_HashPointer(), but don't replace -1 with -2. +static inline Py_hash_t +_Py_HashPointerRaw(const void *ptr) +{ + uintptr_t x = (uintptr_t)ptr; + Py_BUILD_ASSERT(sizeof(x) == sizeof(ptr)); + + // Bottom 3 or 4 bits are likely to be 0; rotate x by 4 to the right + // to avoid excessive hash collisions for dicts and sets. + x = (x >> 4) | (x << (8 * sizeof(uintptr_t) - 4)); + + Py_BUILD_ASSERT(sizeof(x) == sizeof(Py_hash_t)); + return (Py_hash_t)x; +} // Export for '_datetime' shared extension PyAPI_FUNC(Py_hash_t) _Py_HashBytes(const void*, Py_ssize_t); diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index 61e0150e89009c..daf7cb77dcc63a 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -40,7 +40,6 @@ extern void _PySys_FiniTypes(PyInterpreterState *interp); extern int _PyBuiltins_AddExceptions(PyObject * bltinmod); extern PyStatus _Py_HashRandomization_Init(const PyConfig *); -extern PyStatus _PyTime_Init(void); extern PyStatus _PyGC_Init(PyInterpreterState *interp); extern PyStatus _PyAtExit_Init(PyInterpreterState *interp); extern int _Py_Deepfreeze_Init(void); diff --git a/Include/internal/pycore_pymem.h b/Include/internal/pycore_pymem.h index 6b5113714dbeb2..8631ca34a5e616 100644 --- a/Include/internal/pycore_pymem.h +++ b/Include/internal/pycore_pymem.h @@ -1,5 +1,8 @@ #ifndef Py_INTERNAL_PYMEM_H #define Py_INTERNAL_PYMEM_H + +#include "pycore_lock.h" // PyMutex + #ifdef __cplusplus extern "C" { #endif @@ -30,7 +33,7 @@ typedef struct { } debug_alloc_api_t; struct _pymem_allocators { - PyThread_type_lock mutex; + PyMutex mutex; struct { PyMemAllocatorEx raw; PyMemAllocatorEx mem; diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 7fa952e371d7b4..c031a38cd6bfa3 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -220,9 +220,9 @@ PyAPI_FUNC(int) _PyState_AddModule( extern int _PyOS_InterruptOccurred(PyThreadState *tstate); #define HEAD_LOCK(runtime) \ - PyThread_acquire_lock((runtime)->interpreters.mutex, WAIT_LOCK) + PyMutex_LockFlags(&(runtime)->interpreters.mutex, _Py_LOCK_DONT_DETACH) #define HEAD_UNLOCK(runtime) \ - PyThread_release_lock((runtime)->interpreters.mutex) + PyMutex_Unlock(&(runtime)->interpreters.mutex) // Get the configuration of the current interpreter. // The caller must hold the GIL. diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index e6efe8b646e86f..e3348296ea61b7 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -21,7 +21,6 @@ extern "C" { #include "pycore_pymem.h" // struct _pymem_allocators #include "pycore_pythread.h" // struct _pythread_runtime_state #include "pycore_signal.h" // struct _signals_runtime_state -#include "pycore_time.h" // struct _time_runtime_state #include "pycore_tracemalloc.h" // struct _tracemalloc_runtime_state #include "pycore_typeobject.h" // struct _types_runtime_state #include "pycore_unicodeobject.h" // struct _Py_unicode_runtime_state @@ -174,7 +173,7 @@ typedef struct pyruntimestate { unsigned long _finalizing_id; struct pyinterpreters { - PyThread_type_lock mutex; + PyMutex mutex; /* The linked list of interpreters, newest first. */ PyInterpreterState *head; /* The runtime's initial interpreter, which has a special role @@ -205,7 +204,6 @@ typedef struct pyruntimestate { struct _pymem_allocators allocators; struct _obmalloc_global_state obmalloc; struct pyhash_runtime_state pyhash_state; - struct _time_runtime_state time; struct _pythread_runtime_state threads; struct _signals_runtime_state signals; @@ -236,7 +234,7 @@ typedef struct pyruntimestate { Py_OpenCodeHookFunction open_code_hook; void *open_code_userdata; struct { - PyThread_type_lock mutex; + PyMutex mutex; _Py_AuditHookEntry *head; } audit_hooks; diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index fa5d8114abf0d7..d324a94278839c 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -186,7 +186,12 @@ extern PyTypeObject _PyExc_MemoryError; }, \ }, \ }, \ - ._initial_thread = _PyThreadState_INIT, \ + ._initial_thread = _PyThreadStateImpl_INIT, \ + } + +#define _PyThreadStateImpl_INIT \ + { \ + .base = _PyThreadState_INIT, \ } #define _PyThreadState_INIT \ diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index d41a7478db663f..1defa39f816e78 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -824,6 +824,7 @@ extern "C" { INIT_ID(call), \ INIT_ID(call_exception_handler), \ INIT_ID(call_soon), \ + INIT_ID(callback), \ INIT_ID(cancel), \ INIT_ID(capath), \ INIT_ID(category), \ @@ -969,6 +970,7 @@ extern "C" { INIT_ID(hook), \ INIT_ID(id), \ INIT_ID(ident), \ + INIT_ID(identity_hint), \ INIT_ID(ignore), \ INIT_ID(imag), \ INIT_ID(importlib), \ diff --git a/Include/internal/pycore_time.h b/Include/internal/pycore_time.h index 46713f91d190ff..dabbd7b41556cd 100644 --- a/Include/internal/pycore_time.h +++ b/Include/internal/pycore_time.h @@ -52,16 +52,6 @@ extern "C" { #endif -struct _time_runtime_state { -#ifdef HAVE_TIMES - int ticks_per_second_initialized; - long ticks_per_second; -#else - int _not_used; -#endif -}; - - #ifdef __clang__ struct timeval; #endif @@ -263,13 +253,6 @@ PyAPI_FUNC(void) _PyTime_AsTimespec_clamp(_PyTime_t t, struct timespec *ts); // Compute t1 + t2. Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow. extern _PyTime_t _PyTime_Add(_PyTime_t t1, _PyTime_t t2); -// Compute ticks * mul / div. -// Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow. -// The caller must ensure that ((div - 1) * mul) cannot overflow. -extern _PyTime_t _PyTime_MulDiv(_PyTime_t ticks, - _PyTime_t mul, - _PyTime_t div); - // Structure used by time.get_clock_info() typedef struct { const char *implementation; @@ -365,6 +348,32 @@ PyAPI_FUNC(_PyTime_t) _PyDeadline_Init(_PyTime_t timeout); PyAPI_FUNC(_PyTime_t) _PyDeadline_Get(_PyTime_t deadline); +// --- _PyTimeFraction ------------------------------------------------------- + +typedef struct { + _PyTime_t numer; + _PyTime_t denom; +} _PyTimeFraction; + +// Set a fraction. +// Return 0 on success. +// Return -1 if the fraction is invalid. +extern int _PyTimeFraction_Set( + _PyTimeFraction *frac, + _PyTime_t numer, + _PyTime_t denom); + +// Compute ticks * frac.numer / frac.denom. +// Clamp to [_PyTime_MIN; _PyTime_MAX] on overflow. +extern _PyTime_t _PyTimeFraction_Mul( + _PyTime_t ticks, + const _PyTimeFraction *frac); + +// Compute a clock resolution: frac.numer / frac.denom / 1e9. +extern double _PyTimeFraction_Resolution( + const _PyTimeFraction *frac); + + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h new file mode 100644 index 00000000000000..17f3e865641773 --- /dev/null +++ b/Include/internal/pycore_tstate.h @@ -0,0 +1,26 @@ +#ifndef Py_INTERNAL_TSTATE_H +#define Py_INTERNAL_TSTATE_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + + +// Every PyThreadState is actually allocated as a _PyThreadStateImpl. The +// PyThreadState fields are exposed as part of the C API, although most fields +// are intended to be private. The _PyThreadStateImpl fields not exposed. +typedef struct _PyThreadStateImpl { + // semi-public fields are in PyThreadState. + PyThreadState base; + + // TODO: add private fields here +} _PyThreadStateImpl; + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_TSTATE_H */ diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 27c6c8731cb3f9..f983de56049631 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -135,6 +135,8 @@ extern PyObject* _Py_type_getattro_impl(PyTypeObject *type, PyObject *name, int *suppress_missing_attribute); extern PyObject* _Py_type_getattro(PyTypeObject *type, PyObject *name); +extern PyObject* _Py_BaseObject_RichCompare(PyObject* self, PyObject* other, int op); + extern PyObject* _Py_slot_tp_getattro(PyObject *self, PyObject *name); extern PyObject* _Py_slot_tp_getattr_hook(PyObject *self, PyObject *name); @@ -143,6 +145,11 @@ extern PyTypeObject _PyBufferWrapper_Type; extern PyObject* _PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *meth_found); + +// This is exported for the _testinternalcapi module. +PyAPI_FUNC(PyObject *) _PyType_GetModuleName(PyTypeObject *); + + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_ucnhash.h b/Include/internal/pycore_ucnhash.h index 187dd68e7347ff..1561dfbb3150d3 100644 --- a/Include/internal/pycore_ucnhash.h +++ b/Include/internal/pycore_ucnhash.h @@ -28,6 +28,8 @@ typedef struct { } _PyUnicode_Name_CAPI; +extern _PyUnicode_Name_CAPI* _PyUnicode_GetNameCAPI(void); + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_unicodeobject.h b/Include/internal/pycore_unicodeobject.h index a0d00af92e0f5d..7ee540154b23d8 100644 --- a/Include/internal/pycore_unicodeobject.h +++ b/Include/internal/pycore_unicodeobject.h @@ -8,6 +8,7 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +#include "pycore_lock.h" // PyMutex #include "pycore_fileutils.h" // _Py_error_handler #include "pycore_identifier.h" // _Py_Identifier #include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI @@ -277,7 +278,7 @@ extern PyTypeObject _PyUnicodeASCIIIter_Type; /* --- Other API ---------------------------------------------------------- */ struct _Py_unicode_runtime_ids { - PyThread_type_lock lock; + PyMutex mutex; // next_index value must be preserved when Py_Initialize()/Py_Finalize() // is called multiple times: see _PyUnicode_FromId() implementation. Py_ssize_t next_index; diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 0c02e902b308e3..be9baa3eebecfc 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -786,6 +786,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(call_soon); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(callback); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(cancel); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); @@ -1221,6 +1224,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(ident); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(identity_hint); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(ignore); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); diff --git a/Include/internal/pycore_uops.h b/Include/internal/pycore_uops.h index 8ab9aaf4108079..ea8f90bf8c1d8f 100644 --- a/Include/internal/pycore_uops.h +++ b/Include/internal/pycore_uops.h @@ -10,7 +10,7 @@ extern "C" { #include "pycore_frame.h" // _PyInterpreterFrame -#define _Py_UOP_MAX_TRACE_LENGTH 256 +#define _Py_UOP_MAX_TRACE_LENGTH 512 typedef struct { uint16_t opcode; @@ -24,7 +24,7 @@ typedef struct { _PyUOpInstruction trace[1]; } _PyUOpExecutorObject; -_PyInterpreterFrame *_PyUopExecute( +_PyInterpreterFrame *_PyUOpExecute( _PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject **stack_pointer); diff --git a/Include/internal/pycore_weakref.h b/Include/internal/pycore_weakref.h index 51b2bb6b11ede9..eacbe14c903289 100644 --- a/Include/internal/pycore_weakref.h +++ b/Include/internal/pycore_weakref.h @@ -8,6 +8,8 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION() + static inline PyObject* _PyWeakref_GET_REF(PyObject *ref_obj) { assert(PyWeakref_Check(ref_obj)); PyWeakReference *ref = _Py_CAST(PyWeakReference*, ref_obj); @@ -35,15 +37,20 @@ static inline PyObject* _PyWeakref_GET_REF(PyObject *ref_obj) { static inline int _PyWeakref_IS_DEAD(PyObject *ref_obj) { assert(PyWeakref_Check(ref_obj)); + int is_dead; + Py_BEGIN_CRITICAL_SECTION(ref_obj); PyWeakReference *ref = _Py_CAST(PyWeakReference*, ref_obj); PyObject *obj = ref->wr_object; if (obj == Py_None) { // clear_weakref() was called - return 1; + is_dead = 1; } - - // See _PyWeakref_GET_REF() for the rationale of this test - return (Py_REFCNT(obj) == 0); + else { + // See _PyWeakref_GET_REF() for the rationale of this test + is_dead = (Py_REFCNT(obj) == 0); + } + Py_END_CRITICAL_SECTION(); + return is_dead; } extern Py_ssize_t _PyWeakref_GetWeakrefCount(PyWeakReference *head); diff --git a/Include/longobject.h b/Include/longobject.h index 7393254cd24a9b..51005efff636fa 100644 --- a/Include/longobject.h +++ b/Include/longobject.h @@ -7,7 +7,7 @@ extern "C" { /* Long (arbitrary precision) integer object interface */ -PyAPI_DATA(PyTypeObject) PyLong_Type; +// PyLong_Type is declared by object.h #define PyLong_Check(op) \ PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_LONG_SUBCLASS) diff --git a/Include/modsupport.h b/Include/modsupport.h index 450cc99c404c17..ea4c0fce9f4562 100644 --- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -9,10 +9,10 @@ extern "C" { PyAPI_FUNC(int) PyArg_Parse(PyObject *, const char *, ...); PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...); PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *, - const char *, char **, ...); + const char *, PY_CXX_CONST char * const *, ...); PyAPI_FUNC(int) PyArg_VaParse(PyObject *, const char *, va_list); PyAPI_FUNC(int) PyArg_VaParseTupleAndKeywords(PyObject *, PyObject *, - const char *, char **, va_list); + const char *, PY_CXX_CONST char * const *, va_list); PyAPI_FUNC(int) PyArg_ValidateKeywordArguments(PyObject *); PyAPI_FUNC(int) PyArg_UnpackTuple(PyObject *, const char *, Py_ssize_t, Py_ssize_t, ...); diff --git a/Include/object.h b/Include/object.h index 061b5093fb1a35..85abd30b5ad7d6 100644 --- a/Include/object.h +++ b/Include/object.h @@ -88,7 +88,7 @@ having all the lower 32 bits set, which will avoid the reference count to go beyond the refcount limit. Immortality checks for reference count decreases will be done by checking the bit sign flag in the lower 32 bits. */ -#define _Py_IMMORTAL_REFCNT UINT_MAX +#define _Py_IMMORTAL_REFCNT _Py_CAST(Py_ssize_t, UINT_MAX) #else /* @@ -103,12 +103,12 @@ immortality, but the execution would still be correct. Reference count increases and decreases will first go through an immortality check by comparing the reference count field to the immortality reference count. */ -#define _Py_IMMORTAL_REFCNT (UINT_MAX >> 2) +#define _Py_IMMORTAL_REFCNT _Py_CAST(Py_ssize_t, UINT_MAX >> 2) #endif -// Py_NOGIL builds indicate immortal objects using `ob_ref_local`, which is +// Py_GIL_DISABLED builds indicate immortal objects using `ob_ref_local`, which is // always 32-bits. -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED #define _Py_IMMORTAL_REFCNT_LOCAL UINT32_MAX #endif @@ -117,7 +117,7 @@ check by comparing the reference count field to the immortality reference count. // Make all internal uses of PyObject_HEAD_INIT immortal while preserving the // C-API expectation that the refcnt will be set to 1. -#if defined(Py_NOGIL) +#if defined(Py_GIL_DISABLED) #define PyObject_HEAD_INIT(type) \ { \ 0, \ @@ -162,7 +162,7 @@ check by comparing the reference count field to the immortality reference count. * by hand. Similarly every pointer to a variable-size Python object can, * in addition, be cast to PyVarObject*. */ -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED struct _object { #if (defined(__GNUC__) || defined(__clang__)) \ && !(defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L) @@ -238,7 +238,7 @@ typedef struct { PyAPI_FUNC(int) Py_Is(PyObject *x, PyObject *y); #define Py_Is(x, y) ((x) == (y)) -#if defined(Py_NOGIL) && !defined(Py_LIMITED_API) +#if defined(Py_GIL_DISABLED) && !defined(Py_LIMITED_API) static inline uintptr_t _Py_ThreadId(void) { @@ -261,6 +261,24 @@ _Py_ThreadId(void) __asm__ ("mrs %0, tpidrro_el0" : "=r" (tid)); #elif defined(__aarch64__) __asm__ ("mrs %0, tpidr_el0" : "=r" (tid)); +#elif defined(__powerpc64__) + #if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer) + tid = (uintptr_t)__builtin_thread_pointer(); + #else + // r13 is reserved for use as system thread ID by the Power 64-bit ABI. + register uintptr_t tp __asm__ ("r13"); + __asm__("" : "=r" (tp)); + tid = tp; + #endif +#elif defined(__powerpc__) + #if defined(__clang__) && _Py__has_builtin(__builtin_thread_pointer) + tid = (uintptr_t)__builtin_thread_pointer(); + #else + // r2 is reserved for use as system thread ID by the Power 32-bit ABI. + register uintptr_t tp __asm__ ("r2"); + __asm__ ("" : "=r" (tp)); + tid = tp; + #endif #else # error "define _Py_ThreadId for this platform" #endif @@ -275,7 +293,7 @@ _Py_IsOwnedByCurrentThread(PyObject *ob) #endif static inline Py_ssize_t Py_REFCNT(PyObject *ob) { -#if !defined(Py_NOGIL) +#if !defined(Py_GIL_DISABLED) return ob->ob_refcnt; #else uint32_t local = _Py_atomic_load_uint32_relaxed(&ob->ob_ref_local); @@ -316,12 +334,12 @@ static inline Py_ssize_t Py_SIZE(PyObject *ob) { static inline Py_ALWAYS_INLINE int _Py_IsImmortal(PyObject *op) { -#if defined(Py_NOGIL) - return op->ob_ref_local == _Py_IMMORTAL_REFCNT_LOCAL; +#if defined(Py_GIL_DISABLED) + return (op->ob_ref_local == _Py_IMMORTAL_REFCNT_LOCAL); #elif SIZEOF_VOID_P > 4 - return _Py_CAST(PY_INT32_T, op->ob_refcnt) < 0; + return (_Py_CAST(PY_INT32_T, op->ob_refcnt) < 0); #else - return op->ob_refcnt == _Py_IMMORTAL_REFCNT; + return (op->ob_refcnt == _Py_IMMORTAL_REFCNT); #endif } #define _Py_IsImmortal(op) _Py_IsImmortal(_PyObject_CAST(op)) @@ -350,15 +368,23 @@ static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { if (_Py_IsImmortal(ob)) { return; } -#ifndef Py_NOGIL + +#ifndef Py_GIL_DISABLED ob->ob_refcnt = refcnt; #else if (_Py_IsOwnedByCurrentThread(ob)) { - // Set local refcount to desired refcount and shared refcount to zero, - // but preserve the shared refcount flags. - assert(refcnt < UINT32_MAX); - ob->ob_ref_local = _Py_STATIC_CAST(uint32_t, refcnt); - ob->ob_ref_shared &= _Py_REF_SHARED_FLAG_MASK; + if ((size_t)refcnt > (size_t)UINT32_MAX) { + // On overflow, make the object immortal + ob->ob_tid = _Py_UNOWNED_TID; + ob->ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL; + ob->ob_ref_shared = 0; + } + else { + // Set local refcount to desired refcount and shared refcount + // to zero, but preserve the shared refcount flags. + ob->ob_ref_local = _Py_STATIC_CAST(uint32_t, refcnt); + ob->ob_ref_shared &= _Py_REF_SHARED_FLAG_MASK; + } } else { // Set local refcount to zero and shared refcount to desired refcount. @@ -367,7 +393,7 @@ static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { ob->ob_ref_local = 0; ob->ob_ref_shared = _Py_REF_SHARED(refcnt, _Py_REF_MERGED); } -#endif // Py_NOGIL +#endif // Py_GIL_DISABLED #endif // Py_LIMITED_API+0 < 0x030d0000 } #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000 @@ -746,10 +772,11 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op) #else // Non-limited C API and limited C API for Python 3.9 and older access // directly PyObject.ob_refcnt. -#if defined(Py_NOGIL) +#if defined(Py_GIL_DISABLED) uint32_t local = _Py_atomic_load_uint32_relaxed(&op->ob_ref_local); uint32_t new_local = local + 1; if (new_local == 0) { + // local is equal to _Py_IMMORTAL_REFCNT: do nothing return; } if (_Py_IsOwnedByCurrentThread(op)) { @@ -763,6 +790,8 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op) PY_UINT32_T cur_refcnt = op->ob_refcnt_split[PY_BIG_ENDIAN]; PY_UINT32_T new_refcnt = cur_refcnt + 1; if (new_refcnt == 0) { + // cur_refcnt is equal to _Py_IMMORTAL_REFCNT: the object is immortal, + // do nothing return; } op->ob_refcnt_split[PY_BIG_ENDIAN] = new_refcnt; @@ -784,7 +813,7 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op) #endif -#if !defined(Py_LIMITED_API) && defined(Py_NOGIL) +#if !defined(Py_LIMITED_API) && defined(Py_GIL_DISABLED) // Implements Py_DECREF on objects not owned by the current thread. PyAPI_FUNC(void) _Py_DecRefShared(PyObject *); PyAPI_FUNC(void) _Py_DecRefSharedDebug(PyObject *, const char *, int); @@ -810,7 +839,7 @@ static inline void Py_DECREF(PyObject *op) { } #define Py_DECREF(op) Py_DECREF(_PyObject_CAST(op)) -#elif defined(Py_NOGIL) && defined(Py_REF_DEBUG) +#elif defined(Py_GIL_DISABLED) && defined(Py_REF_DEBUG) static inline void Py_DECREF(const char *filename, int lineno, PyObject *op) { uint32_t local = _Py_atomic_load_uint32_relaxed(&op->ob_ref_local); @@ -835,7 +864,7 @@ static inline void Py_DECREF(const char *filename, int lineno, PyObject *op) } #define Py_DECREF(op) Py_DECREF(__FILE__, __LINE__, _PyObject_CAST(op)) -#elif defined(Py_NOGIL) +#elif defined(Py_GIL_DISABLED) static inline void Py_DECREF(PyObject *op) { uint32_t local = _Py_atomic_load_uint32_relaxed(&op->ob_ref_local); diff --git a/Include/objimpl.h b/Include/objimpl.h index 967e2776767756..ff5fa7a8c1d3d8 100644 --- a/Include/objimpl.h +++ b/Include/objimpl.h @@ -35,7 +35,7 @@ Functions and macros for modules that implement new object types. fields, this also fills in the ob_size field. - PyObject_Free(op) releases the memory allocated for an object. It does not - run a destructor -- it only frees the memory. PyObject_Free is identical. + run a destructor -- it only frees the memory. - PyObject_Init(op, typeobj) and PyObject_InitVar(op, typeobj, n) don't allocate memory. Instead of a 'type' parameter, they take a pointer to a diff --git a/Include/opcode_ids.h b/Include/opcode_ids.h index ba25bd459c1bcd..47f809e345f61c 100644 --- a/Include/opcode_ids.h +++ b/Include/opcode_ids.h @@ -1,6 +1,6 @@ -// This file is generated by Tools/cases_generator/generate_cases.py +// This file is generated by Tools/cases_generator/opcode_id_generator.py // from: -// Python/bytecodes.c +// ['./Python/bytecodes.c'] // Do not edit! #ifndef Py_OPCODE_IDS_H @@ -55,7 +55,6 @@ extern "C" { #define UNARY_NEGATIVE 42 #define UNARY_NOT 43 #define WITH_EXCEPT_START 44 -#define HAVE_ARGUMENT 45 #define BINARY_OP 45 #define BUILD_CONST_KEY_MAP 46 #define BUILD_LIST 47 @@ -200,7 +199,6 @@ extern "C" { #define UNPACK_SEQUENCE_LIST 216 #define UNPACK_SEQUENCE_TUPLE 217 #define UNPACK_SEQUENCE_TWO_TUPLE 218 -#define MIN_INSTRUMENTED_OPCODE 236 #define INSTRUMENTED_RESUME 236 #define INSTRUMENTED_END_FOR 237 #define INSTRUMENTED_END_SEND 238 @@ -233,6 +231,9 @@ extern "C" { #define SETUP_WITH 266 #define STORE_FAST_MAYBE_NULL 267 +#define HAVE_ARGUMENT 45 +#define MIN_INSTRUMENTED_OPCODE 236 + #ifdef __cplusplus } #endif diff --git a/Include/patchlevel.h b/Include/patchlevel.h index c2ffa3d5a75c06..fad79ecfda7b28 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -20,10 +20,10 @@ #define PY_MINOR_VERSION 13 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA -#define PY_RELEASE_SERIAL 1 +#define PY_RELEASE_SERIAL 2 /* Version as a string */ -#define PY_VERSION "3.13.0a1+" +#define PY_VERSION "3.13.0a2+" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. diff --git a/Include/pymacconfig.h b/Include/pymacconfig.h index 806e41955efd7f..615abe103ca038 100644 --- a/Include/pymacconfig.h +++ b/Include/pymacconfig.h @@ -7,7 +7,9 @@ #define PY_MACCONFIG_H #ifdef __APPLE__ +#undef ALIGNOF_MAX_ALIGN_T #undef SIZEOF_LONG +#undef SIZEOF_LONG_DOUBLE #undef SIZEOF_PTHREAD_T #undef SIZEOF_SIZE_T #undef SIZEOF_TIME_T @@ -20,6 +22,7 @@ #undef DOUBLE_IS_BIG_ENDIAN_IEEE754 #undef DOUBLE_IS_LITTLE_ENDIAN_IEEE754 #undef HAVE_GCC_ASM_FOR_X87 +#undef HAVE_GCC_ASM_FOR_X64 #undef VA_LIST_IS_ARRAY #if defined(__LP64__) && defined(__x86_64__) @@ -74,8 +77,14 @@ # define DOUBLE_IS_LITTLE_ENDIAN_IEEE754 #endif -#ifdef __i386__ +#if defined(__i386__) || defined(__x86_64__) # define HAVE_GCC_ASM_FOR_X87 +# define ALIGNOF_MAX_ALIGN_T 16 +# define HAVE_GCC_ASM_FOR_X64 1 +# define SIZEOF_LONG_DOUBLE 16 +#else +# define ALIGNOF_MAX_ALIGN_T 8 +# define SIZEOF_LONG_DOUBLE 8 #endif #endif // __APPLE__ diff --git a/Include/pyport.h b/Include/pyport.h index abb526d503fddd..328471085f959d 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -586,6 +586,14 @@ extern "C" { # define ALIGNOF_MAX_ALIGN_T _Alignof(long double) #endif +#ifndef PY_CXX_CONST +# ifdef __cplusplus +# define PY_CXX_CONST const +# else +# define PY_CXX_CONST +# endif +#endif + #if defined(__sgi) && !defined(_SGI_MP_SOURCE) # define _SGI_MP_SOURCE #endif diff --git a/Lib/cmd.py b/Lib/cmd.py index e933b8dbc1470a..2e358d6cd5a02d 100644 --- a/Lib/cmd.py +++ b/Lib/cmd.py @@ -108,7 +108,15 @@ def cmdloop(self, intro=None): import readline self.old_completer = readline.get_completer() readline.set_completer(self.complete) - readline.parse_and_bind(self.completekey+": complete") + if readline.backend == "editline": + if self.completekey == 'tab': + # libedit uses "^I" instead of "tab" + command_string = "bind ^I rl_complete" + else: + command_string = f"bind {self.completekey} rl_complete" + else: + command_string = f"{self.completekey}: complete" + readline.parse_and_bind(command_string) except ImportError: pass try: diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index a461550ea40da7..2e527dfd810c43 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -457,7 +457,7 @@ def _make(cls, iterable): def _replace(self, /, **kwds): result = self._make(_map(kwds.pop, field_names, self)) if kwds: - raise ValueError(f'Got unexpected field names: {list(kwds)!r}') + raise TypeError(f'Got unexpected field names: {list(kwds)!r}') return result _replace.__doc__ = (f'Return a new {typename} object replacing specified ' diff --git a/Lib/dis.py b/Lib/dis.py index 965a0268635ebc..8d3885d2526b70 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -30,6 +30,7 @@ SET_FUNCTION_ATTRIBUTE = opmap['SET_FUNCTION_ATTRIBUTE'] FUNCTION_ATTR_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure') +ENTER_EXECUTOR = opmap['ENTER_EXECUTOR'] LOAD_CONST = opmap['LOAD_CONST'] RETURN_CONST = opmap['RETURN_CONST'] LOAD_GLOBAL = opmap['LOAD_GLOBAL'] @@ -72,7 +73,8 @@ def _try_compile(source, name): pass return compile(source, name, 'exec') -def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False): +def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False, + show_offsets=False): """Disassemble classes, methods, functions, and other compiled objects. With no argument, disassemble the last traceback. @@ -82,7 +84,8 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False): in a special attribute. """ if x is None: - distb(file=file, show_caches=show_caches, adaptive=adaptive) + distb(file=file, show_caches=show_caches, adaptive=adaptive, + show_offsets=show_offsets) return # Extract functions from methods. if hasattr(x, '__func__'): @@ -103,21 +106,21 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False): if isinstance(x1, _have_code): print("Disassembly of %s:" % name, file=file) try: - dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive) + dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets) except TypeError as msg: print("Sorry:", msg, file=file) print(file=file) elif hasattr(x, 'co_code'): # Code object - _disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive) + _disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets) elif isinstance(x, (bytes, bytearray)): # Raw bytecode - _disassemble_bytes(x, file=file, show_caches=show_caches) + _disassemble_bytes(x, file=file, show_caches=show_caches, show_offsets=show_offsets) elif isinstance(x, str): # Source code - _disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive) + _disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets) else: raise TypeError("don't know how to disassemble %s objects" % type(x).__name__) -def distb(tb=None, *, file=None, show_caches=False, adaptive=False): +def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets=False): """Disassemble a traceback (default: last traceback).""" if tb is None: try: @@ -128,7 +131,7 @@ def distb(tb=None, *, file=None, show_caches=False, adaptive=False): except AttributeError: raise RuntimeError("no last traceback to disassemble") from None while tb.tb_next: tb = tb.tb_next - disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file, show_caches=show_caches, adaptive=adaptive) + disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets) # The inspect module interrogates this dictionary to build its # list of CO_* constants. It is also used by pretty_flags to @@ -263,10 +266,10 @@ def show_code(co, *, file=None): 'start_offset', 'starts_line', 'line_number', - 'is_jump_target', + 'label', 'positions' ], - defaults=[None] + defaults=[None, None] ) _Instruction.opname.__doc__ = "Human readable name for operation" @@ -281,12 +284,15 @@ def show_code(co, *, file=None): ) _Instruction.starts_line.__doc__ = "True if this opcode starts a source line, otherwise False" _Instruction.line_number.__doc__ = "source line number associated with this opcode (if any), otherwise None" -_Instruction.is_jump_target.__doc__ = "True if other code jumps to here, otherwise False" +_Instruction.label.__doc__ = "A label (int > 0) if this instruction is a jump target, otherwise None" _Instruction.positions.__doc__ = "dis.Positions object holding the span of source code covered by this instruction" -_ExceptionTableEntry = collections.namedtuple("_ExceptionTableEntry", +_ExceptionTableEntryBase = collections.namedtuple("_ExceptionTableEntryBase", "start end target depth lasti") +class _ExceptionTableEntry(_ExceptionTableEntryBase): + pass + _OPNAME_WIDTH = 20 _OPARG_WIDTH = 5 @@ -325,13 +331,14 @@ class Instruction(_Instruction): otherwise equal to Instruction.offset starts_line - True if this opcode starts a source line, otherwise False line_number - source line number associated with this opcode (if any), otherwise None - is_jump_target - True if other code jumps to here, otherwise False + label - A label if this instruction is a jump target, otherwise None positions - Optional dis.Positions object holding the span of source code covered by this instruction """ @staticmethod - def _get_argval_argrepr(op, arg, offset, co_consts, names, varname_from_oparg): + def _get_argval_argrepr(op, arg, offset, co_consts, names, varname_from_oparg, + labels_map): get_name = None if names is None else names.__getitem__ argval = None argrepr = '' @@ -361,13 +368,15 @@ def _get_argval_argrepr(op, arg, offset, co_consts, names, varname_from_oparg): argval, argrepr = _get_name_info(arg, get_name) elif deop in hasjabs: argval = arg*2 - argrepr = "to " + repr(argval) + argrepr = f"to L{labels_map[argval]}" elif deop in hasjrel: signed_arg = -arg if _is_backward_jump(deop) else arg argval = offset + 2 + signed_arg*2 caches = _get_cache_size(_all_opname[deop]) argval += 2 * caches - argrepr = "to " + repr(argval) + if deop == ENTER_EXECUTOR: + argval += 2 + argrepr = f"to L{labels_map[argval]}" elif deop in (LOAD_FAST_LOAD_FAST, STORE_FAST_LOAD_FAST, STORE_FAST_STORE_FAST): arg1 = arg >> 4 arg2 = arg & 15 @@ -399,14 +408,20 @@ def _get_argval_argrepr(op, arg, offset, co_consts, names, varname_from_oparg): @classmethod def _create(cls, op, arg, offset, start_offset, starts_line, line_number, - is_jump_target, positions, - co_consts=None, varname_from_oparg=None, names=None): + positions, + co_consts=None, varname_from_oparg=None, names=None, + labels_map=None, exceptions_map=None): + argval, argrepr = cls._get_argval_argrepr( op, arg, offset, - co_consts, names, varname_from_oparg) - return Instruction(_all_opname[op], op, arg, argval, argrepr, - offset, start_offset, starts_line, line_number, - is_jump_target, positions) + co_consts, names, varname_from_oparg, labels_map) + label = labels_map.get(offset, None) + instr = Instruction(_all_opname[op], op, arg, argval, argrepr, + offset, start_offset, starts_line, line_number, + label, positions) + instr.label_width = 4 + len(str(len(labels_map))) + instr.exc_handler = exceptions_map.get(offset, None) + return instr @property def oparg(self): @@ -447,12 +462,19 @@ def jump_target(self): """ return _get_jump_target(self.opcode, self.arg, self.offset) - def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=4): + @property + def is_jump_target(self): + """True if other code jumps to here, otherwise False""" + return self.label is not None + + def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=0, + label_width=0): """Format instruction details for inclusion in disassembly output. *lineno_width* sets the width of the line number field (0 omits it) *mark_as_current* inserts a '-->' marker arrow as part of the line *offset_width* sets the width of the instruction offset field + *label_width* sets the width of the label field """ fields = [] # Column: Source code line number @@ -460,21 +482,24 @@ def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=4): if self.starts_line: lineno_fmt = "%%%dd" if self.line_number is not None else "%%%ds" lineno_fmt = lineno_fmt % lineno_width - fields.append(lineno_fmt % self.line_number) + lineno = self.line_number if self.line_number is not None else '--' + fields.append(lineno_fmt % lineno) else: fields.append(' ' * lineno_width) + # Column: Label + if self.label is not None: + lbl = f"L{self.label}:" + fields.append(f"{lbl:>{label_width}}") + else: + fields.append(' ' * label_width) + # Column: Instruction offset from start of code sequence + if offset_width > 0: + fields.append(f"{repr(self.offset):>{offset_width}} ") # Column: Current instruction indicator if mark_as_current: fields.append('-->') else: fields.append(' ') - # Column: Jump target marker - if self.is_jump_target: - fields.append('>>') - else: - fields.append(' ') - # Column: Instruction offset from start of code sequence - fields.append(repr(self.offset).rjust(offset_width)) # Column: Opcode name fields.append(self.opname.ljust(_OPNAME_WIDTH)) # Column: Opcode argument @@ -584,7 +609,9 @@ def _parse_exception_table(code): return entries def _is_backward_jump(op): - return 'JUMP_BACKWARD' in opname[op] + return opname[op] in ('JUMP_BACKWARD', + 'JUMP_BACKWARD_NO_INTERRUPT', + 'ENTER_EXECUTOR') def _get_instructions_bytes(code, varname_from_oparg=None, names=None, co_consts=None, @@ -605,10 +632,30 @@ def _get_instructions_bytes(code, varname_from_oparg=None, original_code = original_code or code co_positions = co_positions or iter(()) get_name = None if names is None else names.__getitem__ - labels = set(findlabels(original_code)) - for start, end, target, _, _ in exception_entries: - for i in range(start, end): + + def make_labels_map(original_code, exception_entries): + jump_targets = set(findlabels(original_code)) + labels = set(jump_targets) + for start, end, target, _, _ in exception_entries: + labels.add(start) + labels.add(end) labels.add(target) + labels = sorted(labels) + labels_map = {offset: i+1 for (i, offset) in enumerate(sorted(labels))} + for e in exception_entries: + e.start_label = labels_map[e.start] + e.end_label = labels_map[e.end] + e.target_label = labels_map[e.target] + return labels_map + + labels_map = make_labels_map(original_code, exception_entries) + label_width = 4 + len(str(len(labels_map))) + + exceptions_map = {} + for start, end, target, _, _ in exception_entries: + exceptions_map[start] = labels_map[target] + exceptions_map[end] = -1 + starts_line = False local_line_number = None line_number = None @@ -621,14 +668,14 @@ def _get_instructions_bytes(code, varname_from_oparg=None, line_number = local_line_number + line_offset else: line_number = None - is_jump_target = offset in labels positions = Positions(*next(co_positions, ())) deop = _deoptop(op) op = code[offset] yield Instruction._create(op, arg, offset, start_offset, starts_line, line_number, - is_jump_target, positions, co_consts=co_consts, - varname_from_oparg=varname_from_oparg, names=names) + positions, co_consts=co_consts, + varname_from_oparg=varname_from_oparg, names=names, + labels_map=labels_map, exceptions_map=exceptions_map) caches = _get_cache_size(_all_opname[deop]) if not caches: @@ -649,11 +696,12 @@ def _get_instructions_bytes(code, varname_from_oparg=None, else: argrepr = "" yield Instruction( - "CACHE", CACHE, 0, None, argrepr, offset, offset, False, None, False, + "CACHE", CACHE, 0, None, argrepr, offset, offset, False, None, None, Positions(*next(co_positions, ())) ) -def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False): +def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False, + show_offsets=False): """Disassemble a code object.""" linestarts = dict(findlinestarts(co)) exception_entries = _parse_exception_table(co) @@ -662,10 +710,10 @@ def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False): co.co_names, co.co_consts, linestarts, file=file, exception_entries=exception_entries, co_positions=co.co_positions(), show_caches=show_caches, - original_code=co.co_code) + original_code=co.co_code, show_offsets=show_offsets) -def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False): - disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive) +def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False, show_offsets=False): + disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets) if depth is None or depth > 0: if depth is not None: depth = depth - 1 @@ -674,13 +722,15 @@ def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adap print(file=file) print("Disassembly of %r:" % (x,), file=file) _disassemble_recursive( - x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive + x, file=file, depth=depth, show_caches=show_caches, + adaptive=adaptive, show_offsets=show_offsets ) def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None, names=None, co_consts=None, linestarts=None, *, file=None, line_offset=0, exception_entries=(), - co_positions=None, show_caches=False, original_code=None): + co_positions=None, show_caches=False, original_code=None, + show_offsets=False): # Omit the line number column entirely if we have no line number info if bool(linestarts): linestarts_ints = [line for line in linestarts.values() if line is not None] @@ -699,11 +749,16 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None, lineno_width = len(str(None)) else: lineno_width = 0 - maxoffset = len(code) - 2 - if maxoffset >= 10000: - offset_width = len(str(maxoffset)) + if show_offsets: + maxoffset = len(code) - 2 + if maxoffset >= 10000: + offset_width = len(str(maxoffset)) + else: + offset_width = 4 else: - offset_width = 4 + offset_width = 0 + + label_width = -1 for instr in _get_instructions_bytes(code, varname_from_oparg, names, co_consts, linestarts, line_offset=line_offset, @@ -722,14 +777,18 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None, # Each CACHE takes 2 bytes is_current_instr = instr.offset <= lasti \ <= instr.offset + 2 * _get_cache_size(_all_opname[_deoptop(instr.opcode)]) - print(instr._disassemble(lineno_width, is_current_instr, offset_width), + label_width = getattr(instr, 'label_width', label_width) + assert label_width >= 0 + print(instr._disassemble(lineno_width, is_current_instr, offset_width, label_width), file=file) if exception_entries: print("ExceptionTable:", file=file) for entry in exception_entries: lasti = " lasti" if entry.lasti else "" - end = entry.end-2 - print(f" {entry.start} to {end} -> {entry.target} [{entry.depth}]{lasti}", file=file) + start = entry.start_label + end = entry.end_label + target = entry.target_label + print(f" L{start} to L{end} -> L{target} [{entry.depth}]{lasti}", file=file) def _disassemble_str(source, **kwargs): """Compile the source string, then disassemble the code object.""" @@ -850,7 +909,7 @@ class Bytecode: Iterating over this yields the bytecode operations as Instruction instances. """ - def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False): + def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False, show_offsets=False): self.codeobj = co = _get_code_object(x) if first_line is None: self.first_line = co.co_firstlineno @@ -864,6 +923,7 @@ def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False self.exception_entries = _parse_exception_table(co) self.show_caches = show_caches self.adaptive = adaptive + self.show_offsets = show_offsets def __iter__(self): co = self.codeobj @@ -912,7 +972,8 @@ def dis(self): exception_entries=self.exception_entries, co_positions=co.co_positions(), show_caches=self.show_caches, - original_code=co.co_code) + original_code=co.co_code, + show_offsets=self.show_offsets) return output.getvalue() @@ -922,12 +983,14 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument('-C', '--show-caches', action='store_true', help='show inline caches') + parser.add_argument('-O', '--show-offsets', action='store_true', + help='show instruction offsets') parser.add_argument('infile', type=argparse.FileType('rb'), nargs='?', default='-') args = parser.parse_args() with args.infile as infile: source = infile.read() code = compile(source, args.infile.name, "exec") - dis(code, show_caches=args.show_caches) + dis(code, show_caches=args.show_caches, show_offsets=args.show_offsets) if __name__ == "__main__": main() diff --git a/Lib/doctest.py b/Lib/doctest.py index 2f14aa08334895..d109b6c9e37343 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -591,9 +591,11 @@ def __hash__(self): def __lt__(self, other): if not isinstance(other, DocTest): return NotImplemented - return ((self.name, self.filename, self.lineno, id(self)) + self_lno = self.lineno if self.lineno is not None else -1 + other_lno = other.lineno if other.lineno is not None else -1 + return ((self.name, self.filename, self_lno, id(self)) < - (other.name, other.filename, other.lineno, id(other))) + (other.name, other.filename, other_lno, id(other))) ###################################################################### ## 3. DocTestParser diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 1fb1d505cfd0c5..21e4ad99a39628 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -10,7 +10,7 @@ __all__ = ["version", "bootstrap"] _PACKAGE_NAMES = ('pip',) -_PIP_VERSION = "23.2.1" +_PIP_VERSION = "23.3.1" _PROJECTS = [ ("pip", _PIP_VERSION, "py3"), ] diff --git a/Lib/ensurepip/_bundled/pip-23.2.1-py3-none-any.whl b/Lib/ensurepip/_bundled/pip-23.3.1-py3-none-any.whl similarity index 80% rename from Lib/ensurepip/_bundled/pip-23.2.1-py3-none-any.whl rename to Lib/ensurepip/_bundled/pip-23.3.1-py3-none-any.whl index ba28ef02e265f0..a4faf716b663e6 100644 Binary files a/Lib/ensurepip/_bundled/pip-23.2.1-py3-none-any.whl and b/Lib/ensurepip/_bundled/pip-23.3.1-py3-none-any.whl differ diff --git a/Lib/enum.py b/Lib/enum.py index 4e76e96d2c916e..a8a50a58380375 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -4,7 +4,7 @@ __all__ = [ - 'EnumType', 'EnumMeta', + 'EnumType', 'EnumMeta', 'EnumDict', 'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag', 'ReprEnum', 'auto', 'unique', 'property', 'verify', 'member', 'nonmember', 'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP', @@ -313,45 +313,8 @@ def __set_name__(self, enum_class, member_name): ): # no other instances found, record this member in _member_names_ enum_class._member_names_.append(member_name) - # if necessary, get redirect in place and then add it to _member_map_ - found_descriptor = None - descriptor_type = None - class_type = None - for base in enum_class.__mro__[1:]: - attr = base.__dict__.get(member_name) - if attr is not None: - if isinstance(attr, (property, DynamicClassAttribute)): - found_descriptor = attr - class_type = base - descriptor_type = 'enum' - break - elif _is_descriptor(attr): - found_descriptor = attr - descriptor_type = descriptor_type or 'desc' - class_type = class_type or base - continue - else: - descriptor_type = 'attr' - class_type = base - if found_descriptor: - redirect = property() - redirect.member = enum_member - redirect.__set_name__(enum_class, member_name) - if descriptor_type in ('enum','desc'): - # earlier descriptor found; copy fget, fset, fdel to this one. - redirect.fget = getattr(found_descriptor, 'fget', None) - redirect._get = getattr(found_descriptor, '__get__', None) - redirect.fset = getattr(found_descriptor, 'fset', None) - redirect._set = getattr(found_descriptor, '__set__', None) - redirect.fdel = getattr(found_descriptor, 'fdel', None) - redirect._del = getattr(found_descriptor, '__delete__', None) - redirect._attr_type = descriptor_type - redirect._cls_type = class_type - setattr(enum_class, member_name, redirect) - else: - setattr(enum_class, member_name, enum_member) - # now add to _member_map_ (even aliases) - enum_class._member_map_[member_name] = enum_member + + enum_class._add_member_(member_name, enum_member) try: # This may fail if value is not hashable. We can't add the value # to the map, and by-value lookups for this value will be @@ -360,9 +323,10 @@ def __set_name__(self, enum_class, member_name): except TypeError: # keep track of the value in a list so containment checks are quick enum_class._unhashable_values_.append(value) + enum_class._unhashable_values_map_.setdefault(member_name, []).append(value) -class _EnumDict(dict): +class EnumDict(dict): """ Track enum member order and ensure member names are not reused. @@ -371,7 +335,7 @@ class _EnumDict(dict): """ def __init__(self): super().__init__() - self._member_names = {} # use a dict to keep insertion order + self._member_names = {} # use a dict -- faster look-up than a list, and keeps insertion order since 3.7 self._last_values = [] self._ignore = [] self._auto_called = False @@ -393,6 +357,7 @@ def __setitem__(self, key, value): '_order_', '_generate_next_value_', '_numeric_repr_', '_missing_', '_ignore_', '_iter_member_', '_iter_member_by_value_', '_iter_member_by_def_', + '_add_alias_', '_add_value_alias_', ): raise ValueError( '_sunder_ names, such as %r, are reserved for future Enum use' @@ -468,6 +433,10 @@ def __setitem__(self, key, value): self._last_values.append(value) super().__setitem__(key, value) + @property + def member_names(self): + return list(self._member_names) + def update(self, members, **more_members): try: for name in members.keys(): @@ -478,6 +447,8 @@ def update(self, members, **more_members): for name, value in more_members.items(): self[name] = value +_EnumDict = EnumDict # keep private name for backwards compatibility + class EnumType(type): """ @@ -489,7 +460,7 @@ def __prepare__(metacls, cls, bases, **kwds): # check that previous enum members do not exist metacls._check_for_existing_members_(cls, bases) # create the namespace dict - enum_dict = _EnumDict() + enum_dict = EnumDict() enum_dict._cls_name = cls # inherit previous flags and _generate_next_value_ function member_type, first_enum = metacls._get_mixins_(cls, bases) @@ -552,6 +523,7 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k classdict['_member_map_'] = {} classdict['_value2member_map_'] = {} classdict['_unhashable_values_'] = [] + classdict['_unhashable_values_map_'] = {} classdict['_member_type_'] = member_type # now set the __repr__ for the value classdict['_value_repr_'] = metacls._find_data_repr_(cls, bases) @@ -568,12 +540,16 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k try: exc = None enum_class = super().__new__(metacls, cls, bases, classdict, **kwds) - except RuntimeError as e: - # any exceptions raised by member.__new__ will get converted to a - # RuntimeError, so get that original exception back and raise it instead - exc = e.__cause__ or e + except Exception as e: + # since 3.12 the line "Error calling __set_name__ on '_proto_member' instance ..." + # is tacked on to the error instead of raising a RuntimeError + # recreate the exception to discard + exc = type(e)(str(e)) + exc.__cause__ = e.__cause__ + exc.__context__ = e.__context__ + tb = e.__traceback__ if exc is not None: - raise exc + raise exc.with_traceback(tb) # # update classdict with any changes made by __init_subclass__ classdict.update(enum_class.__dict__) @@ -750,7 +726,10 @@ def __contains__(cls, value): """ if isinstance(value, cls): return True - return value in cls._value2member_map_ or value in cls._unhashable_values_ + try: + return value in cls._value2member_map_ + except TypeError: + return value in cls._unhashable_values_ def __delattr__(cls, attr): # nicer error message when someone tries to delete an attribute @@ -1046,7 +1025,57 @@ def _find_new_(mcls, classdict, member_type, first_enum): else: use_args = True return __new__, save_new, use_args -EnumMeta = EnumType + + def _add_member_(cls, name, member): + # _value_ structures are not updated + if name in cls._member_map_: + if cls._member_map_[name] is not member: + raise NameError('%r is already bound: %r' % (name, cls._member_map_[name])) + return + # + # if necessary, get redirect in place and then add it to _member_map_ + found_descriptor = None + descriptor_type = None + class_type = None + for base in cls.__mro__[1:]: + attr = base.__dict__.get(name) + if attr is not None: + if isinstance(attr, (property, DynamicClassAttribute)): + found_descriptor = attr + class_type = base + descriptor_type = 'enum' + break + elif _is_descriptor(attr): + found_descriptor = attr + descriptor_type = descriptor_type or 'desc' + class_type = class_type or base + continue + else: + descriptor_type = 'attr' + class_type = base + if found_descriptor: + redirect = property() + redirect.member = member + redirect.__set_name__(cls, name) + if descriptor_type in ('enum', 'desc'): + # earlier descriptor found; copy fget, fset, fdel to this one. + redirect.fget = getattr(found_descriptor, 'fget', None) + redirect._get = getattr(found_descriptor, '__get__', None) + redirect.fset = getattr(found_descriptor, 'fset', None) + redirect._set = getattr(found_descriptor, '__set__', None) + redirect.fdel = getattr(found_descriptor, 'fdel', None) + redirect._del = getattr(found_descriptor, '__delete__', None) + redirect._attr_type = descriptor_type + redirect._cls_type = class_type + setattr(cls, name, redirect) + else: + setattr(cls, name, member) + # now add to _member_map_ (even aliases) + cls._member_map_[name] = member + # + cls._member_map_[name] = member + +EnumMeta = EnumType # keep EnumMeta name for backwards compatibility class Enum(metaclass=EnumType): @@ -1112,9 +1141,9 @@ def __new__(cls, value): pass except TypeError: # not there, now do long search -- O(n) behavior - for member in cls._member_map_.values(): - if member._value_ == value: - return member + for name, values in cls._unhashable_values_map_.items(): + if value in values: + return cls[name] # still not found -- verify that members exist, in-case somebody got here mistakenly # (such as via super when trying to override __new__) if not cls._member_map_: @@ -1155,6 +1184,33 @@ def __new__(cls, value): def __init__(self, *args, **kwds): pass + def _add_alias_(self, name): + self.__class__._add_member_(name, self) + + def _add_value_alias_(self, value): + cls = self.__class__ + try: + if value in cls._value2member_map_: + if cls._value2member_map_[value] is not self: + raise ValueError('%r is already bound: %r' % (value, cls._value2member_map_[value])) + return + except TypeError: + # unhashable value, do long search + for m in cls._member_map_.values(): + if m._value_ == value: + if m is not self: + raise ValueError('%r is already bound: %r' % (value, cls._value2member_map_[value])) + return + try: + # This may fail if value is not hashable. We can't add the value + # to the map, and by-value lookups for this value will be + # linear. + cls._value2member_map_.setdefault(value, self) + except TypeError: + # keep track of the value in a list so containment checks are quick + cls._unhashable_values_.append(value) + cls._unhashable_values_map_.setdefault(self.name, []).append(value) + @staticmethod def _generate_next_value_(name, start, count, last_values): """ @@ -1667,7 +1723,8 @@ def convert_class(cls): body['_member_names_'] = member_names = [] body['_member_map_'] = member_map = {} body['_value2member_map_'] = value2member_map = {} - body['_unhashable_values_'] = [] + body['_unhashable_values_'] = unhashable_values = [] + body['_unhashable_values_map_'] = {} body['_member_type_'] = member_type = etype._member_type_ body['_value_repr_'] = etype._value_repr_ if issubclass(etype, Flag): @@ -1714,14 +1771,9 @@ def convert_class(cls): for name, value in attrs.items(): if isinstance(value, auto) and auto.value is _auto_null: value = gnv(name, 1, len(member_names), gnv_last_values) - if value in value2member_map: + if value in value2member_map or value in unhashable_values: # an alias to an existing member - member = value2member_map[value] - redirect = property() - redirect.member = member - redirect.__set_name__(enum_class, name) - setattr(enum_class, name, redirect) - member_map[name] = member + enum_class(value)._add_alias_(name) else: # create the member if use_args: @@ -1736,12 +1788,12 @@ def convert_class(cls): member._name_ = name member.__objclass__ = enum_class member.__init__(value) - redirect = property() - redirect.member = member - redirect.__set_name__(enum_class, name) - setattr(enum_class, name, redirect) - member_map[name] = member member._sort_order_ = len(member_names) + if name not in ('name', 'value'): + setattr(enum_class, name, member) + member_map[name] = member + else: + enum_class._add_member_(name, member) value2member_map[value] = member if _is_single_bit(value): # not a multi-bit alias, record in _member_names_ and _flag_mask_ @@ -1764,14 +1816,13 @@ def convert_class(cls): if value.value is _auto_null: value.value = gnv(name, 1, len(member_names), gnv_last_values) value = value.value - if value in value2member_map: + try: + contained = value in value2member_map + except TypeError: + contained = value in unhashable_values + if contained: # an alias to an existing member - member = value2member_map[value] - redirect = property() - redirect.member = member - redirect.__set_name__(enum_class, name) - setattr(enum_class, name, redirect) - member_map[name] = member + enum_class(value)._add_alias_(name) else: # create the member if use_args: @@ -1787,14 +1838,22 @@ def convert_class(cls): member.__objclass__ = enum_class member.__init__(value) member._sort_order_ = len(member_names) - redirect = property() - redirect.member = member - redirect.__set_name__(enum_class, name) - setattr(enum_class, name, redirect) - member_map[name] = member - value2member_map[value] = member + if name not in ('name', 'value'): + setattr(enum_class, name, member) + member_map[name] = member + else: + enum_class._add_member_(name, member) member_names.append(name) gnv_last_values.append(value) + try: + # This may fail if value is not hashable. We can't add the value + # to the map, and by-value lookups for this value will be + # linear. + enum_class._value2member_map_.setdefault(value, member) + except TypeError: + # keep track of the value in a list so containment checks are quick + enum_class._unhashable_values_.append(value) + enum_class._unhashable_values_map_.setdefault(name, []).append(value) if '__new__' in body: enum_class.__new_member__ = enum_class.__new__ enum_class.__new__ = Enum.__new__ diff --git a/Lib/getpass.py b/Lib/getpass.py index 8b42c0a536b4c4..bd0097ced94c5e 100644 --- a/Lib/getpass.py +++ b/Lib/getpass.py @@ -156,7 +156,11 @@ def getuser(): First try various environment variables, then the password database. This works on Windows as long as USERNAME is set. + Any failure to find a username raises OSError. + .. versionchanged:: 3.13 + Previously, various exceptions beyond just :exc:`OSError` + were raised. """ for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'): @@ -164,9 +168,12 @@ def getuser(): if user: return user - # If this fails, the exception will "explain" why - import pwd - return pwd.getpwuid(os.getuid())[0] + try: + import pwd + return pwd.getpwuid(os.getuid())[0] + except (ImportError, KeyError) as e: + raise OSError('No username set in the environment') from e + # Bind the name getpass to the appropriate function try: diff --git a/Lib/http/client.py b/Lib/http/client.py index b35b1d6368aae7..7bb5d824bb6da4 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -172,6 +172,13 @@ def _encode(data, name='data'): "if you want to send it encoded in UTF-8." % (name.title(), data[err.start:err.end], name)) from None +def _strip_ipv6_iface(enc_name: bytes) -> bytes: + """Remove interface scope from IPv6 address.""" + enc_name, percent, _ = enc_name.partition(b"%") + if percent: + assert enc_name.startswith(b'['), enc_name + enc_name += b']' + return enc_name class HTTPMessage(email.message.Message): # XXX The only usage of this method is in @@ -1194,7 +1201,7 @@ def putrequest(self, method, url, skip_host=False, netloc_enc = netloc.encode("ascii") except UnicodeEncodeError: netloc_enc = netloc.encode("idna") - self.putheader('Host', netloc_enc) + self.putheader('Host', _strip_ipv6_iface(netloc_enc)) else: if self._tunnel_host: host = self._tunnel_host @@ -1211,8 +1218,9 @@ def putrequest(self, method, url, skip_host=False, # As per RFC 273, IPv6 address should be wrapped with [] # when used as Host header - if host.find(':') >= 0: + if ":" in host: host_enc = b'[' + host_enc + b']' + host_enc = _strip_ipv6_iface(host_enc) if port == self.default_port: self.putheader('Host', host_enc) diff --git a/Lib/idlelib/browser.py b/Lib/idlelib/browser.py index 4fe64dced60aca..8b9060e57072ea 100644 --- a/Lib/idlelib/browser.py +++ b/Lib/idlelib/browser.py @@ -250,9 +250,11 @@ def closure(): class Nested_in_closure: pass ModuleBrowser(parent, file, _htest=True) + if __name__ == "__main__": if len(sys.argv) == 1: # If pass file on command line, unittest fails. from unittest import main main('idlelib.idle_test.test_browser', verbosity=2, exit=False) + from idlelib.idle_test.htest import run run(_module_browser) diff --git a/Lib/idlelib/calltip_w.py b/Lib/idlelib/calltip_w.py index 278546064adde2..9386376058c791 100644 --- a/Lib/idlelib/calltip_w.py +++ b/Lib/idlelib/calltip_w.py @@ -193,6 +193,7 @@ def calltip_hide(event): text.focus_set() + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_calltip_w', verbosity=2, exit=False) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 898efeb4dd1550..92992fd9cce9cd 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -906,6 +906,7 @@ def dumpCfg(cfg): dumpCfg(idleConf.userCfg) print('\nlines = ', line, ', crc = ', crc, sep='') + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_config', verbosity=2, exit=False) diff --git a/Lib/idlelib/config_key.py b/Lib/idlelib/config_key.py index bb07231cd590b6..e5f67e8d4069ee 100644 --- a/Lib/idlelib/config_key.py +++ b/Lib/idlelib/config_key.py @@ -351,4 +351,4 @@ def cancel(self, event=None): main('idlelib.idle_test.test_config_key', verbosity=2, exit=False) from idlelib.idle_test.htest import run - run(GetKeysDialog) + run(GetKeysWindow) diff --git a/Lib/idlelib/debugger.py b/Lib/idlelib/debugger.py index a92bb98d908d46..f487b4c4b16a60 100644 --- a/Lib/idlelib/debugger.py +++ b/Lib/idlelib/debugger.py @@ -1,3 +1,20 @@ +"""Debug user code with a GUI interface to a subclass of bdb.Bdb. + +The Idb idb and Debugger gui instances each need a reference to each +other or to an rpc proxy for each other. + +If IDLE is started with '-n', so that user code and idb both run in the +IDLE process, Debugger is called without an idb. Debugger.__init__ +calls Idb with its incomplete self. Idb.__init__ stores gui and gui +then stores idb. + +If IDLE is started normally, so that user code executes in a separate +process, debugger_r.start_remote_debugger is called, executing in the +IDLE process. It calls 'start the debugger' in the remote process, +which calls Idb with a gui proxy. Then Debugger is called in the IDLE +for more. +""" + import bdb import os @@ -10,66 +27,95 @@ class Idb(bdb.Bdb): + "Supply user_line and user_exception functions for Bdb." def __init__(self, gui): - self.gui = gui # An instance of Debugger or proxy of remote. - bdb.Bdb.__init__(self) + self.gui = gui # An instance of Debugger or proxy thereof. + super().__init__() def user_line(self, frame): - if self.in_rpc_code(frame): + """Handle a user stopping or breaking at a line. + + Convert frame to a string and send it to gui. + """ + if _in_rpc_code(frame): self.set_step() return - message = self.__frame2message(frame) + message = _frame2message(frame) try: self.gui.interaction(message, frame) except TclError: # When closing debugger window with [x] in 3.x pass - def user_exception(self, frame, info): - if self.in_rpc_code(frame): + def user_exception(self, frame, exc_info): + """Handle an the occurrence of an exception.""" + if _in_rpc_code(frame): self.set_step() return - message = self.__frame2message(frame) - self.gui.interaction(message, frame, info) - - def in_rpc_code(self, frame): - if frame.f_code.co_filename.count('rpc.py'): - return True - else: - prev_frame = frame.f_back - prev_name = prev_frame.f_code.co_filename - if 'idlelib' in prev_name and 'debugger' in prev_name: - # catch both idlelib/debugger.py and idlelib/debugger_r.py - # on both Posix and Windows - return False - return self.in_rpc_code(prev_frame) - - def __frame2message(self, frame): - code = frame.f_code - filename = code.co_filename - lineno = frame.f_lineno - basename = os.path.basename(filename) - message = f"{basename}:{lineno}" - if code.co_name != "?": - message = f"{message}: {code.co_name}()" - return message + message = _frame2message(frame) + self.gui.interaction(message, frame, exc_info) + +def _in_rpc_code(frame): + "Determine if debugger is within RPC code." + if frame.f_code.co_filename.count('rpc.py'): + return True # Skip this frame. + else: + prev_frame = frame.f_back + if prev_frame is None: + return False + prev_name = prev_frame.f_code.co_filename + if 'idlelib' in prev_name and 'debugger' in prev_name: + # catch both idlelib/debugger.py and idlelib/debugger_r.py + # on both Posix and Windows + return False + return _in_rpc_code(prev_frame) + +def _frame2message(frame): + """Return a message string for frame.""" + code = frame.f_code + filename = code.co_filename + lineno = frame.f_lineno + basename = os.path.basename(filename) + message = f"{basename}:{lineno}" + if code.co_name != "?": + message = f"{message}: {code.co_name}()" + return message class Debugger: - - vstack = vsource = vlocals = vglobals = None + """The debugger interface. + + This class handles the drawing of the debugger window and + the interactions with the underlying debugger session. + """ + vstack = None + vsource = None + vlocals = None + vglobals = None + stackviewer = None + localsviewer = None + globalsviewer = None def __init__(self, pyshell, idb=None): + """Instantiate and draw a debugger window. + + :param pyshell: An instance of the PyShell Window + :type pyshell: :class:`idlelib.pyshell.PyShell` + + :param idb: An instance of the IDLE debugger (optional) + :type idb: :class:`idlelib.debugger.Idb` + """ if idb is None: idb = Idb(self) self.pyshell = pyshell self.idb = idb # If passed, a proxy of remote instance. self.frame = None self.make_gui() - self.interacting = 0 + self.interacting = False self.nesting_level = 0 def run(self, *args): + """Run the debugger.""" # Deal with the scenario where we've already got a program running # in the debugger and we want to start another. If that is the case, # our second 'run' was invoked from an event dispatched not from @@ -104,12 +150,13 @@ def run(self, *args): self.root.after(100, lambda: self.run(*args)) return try: - self.interacting = 1 + self.interacting = True return self.idb.run(*args) finally: - self.interacting = 0 + self.interacting = False def close(self, event=None): + """Close the debugger and window.""" try: self.quit() except Exception: @@ -127,6 +174,7 @@ def close(self, event=None): self.top.destroy() def make_gui(self): + """Draw the debugger gui on the screen.""" pyshell = self.pyshell self.flist = pyshell.flist self.root = root = pyshell.root @@ -135,11 +183,11 @@ def make_gui(self): self.top.wm_iconname("Debug") top.wm_protocol("WM_DELETE_WINDOW", self.close) self.top.bind("", self.close) - # + self.bframe = bframe = Frame(top) self.bframe.pack(anchor="w") self.buttons = bl = [] - # + self.bcont = b = Button(bframe, text="Go", command=self.cont) bl.append(b) self.bstep = b = Button(bframe, text="Step", command=self.step) @@ -150,14 +198,14 @@ def make_gui(self): bl.append(b) self.bret = b = Button(bframe, text="Quit", command=self.quit) bl.append(b) - # + for b in bl: b.configure(state="disabled") b.pack(side="left") - # + self.cframe = cframe = Frame(bframe) self.cframe.pack(side="left") - # + if not self.vstack: self.__class__.vstack = BooleanVar(top) self.vstack.set(1) @@ -180,20 +228,20 @@ def make_gui(self): self.bglobals = Checkbutton(cframe, text="Globals", command=self.show_globals, variable=self.vglobals) self.bglobals.grid(row=1, column=1) - # + self.status = Label(top, anchor="w") self.status.pack(anchor="w") self.error = Label(top, anchor="w") self.error.pack(anchor="w", fill="x") self.errorbg = self.error.cget("background") - # + self.fstack = Frame(top, height=1) self.fstack.pack(expand=1, fill="both") self.flocals = Frame(top) self.flocals.pack(expand=1, fill="both") self.fglobals = Frame(top, height=1) self.fglobals.pack(expand=1, fill="both") - # + if self.vstack.get(): self.show_stack() if self.vlocals.get(): @@ -204,7 +252,7 @@ def make_gui(self): def interaction(self, message, frame, info=None): self.frame = frame self.status.configure(text=message) - # + if info: type, value, tb = info try: @@ -223,28 +271,28 @@ def interaction(self, message, frame, info=None): tb = None bg = self.errorbg self.error.configure(text=m1, background=bg) - # + sv = self.stackviewer if sv: stack, i = self.idb.get_stack(self.frame, tb) sv.load_stack(stack, i) - # + self.show_variables(1) - # + if self.vsource.get(): self.sync_source_line() - # + for b in self.buttons: b.configure(state="normal") - # + self.top.wakeup() # Nested main loop: Tkinter's main loop is not reentrant, so use # Tcl's vwait facility, which reenters the event loop until an - # event handler sets the variable we're waiting on + # event handler sets the variable we're waiting on. self.nesting_level += 1 self.root.tk.call('vwait', '::idledebugwait') self.nesting_level -= 1 - # + for b in self.buttons: b.configure(state="disabled") self.status.configure(text="") @@ -288,8 +336,6 @@ def quit(self): def abort_loop(self): self.root.tk.call('set', '::idledebugwait', '1') - stackviewer = None - def show_stack(self): if not self.stackviewer and self.vstack.get(): self.stackviewer = sv = StackViewer(self.fstack, self.flist, self) @@ -311,9 +357,6 @@ def show_frame(self, stackitem): self.frame = stackitem[0] # lineno is stackitem[1] self.show_variables() - localsviewer = None - globalsviewer = None - def show_locals(self): lv = self.localsviewer if self.vlocals.get(): @@ -354,26 +397,32 @@ def show_variables(self, force=0): if gv: gv.load_dict(gdict, force, self.pyshell.interp.rpcclt) - def set_breakpoint_here(self, filename, lineno): + def set_breakpoint(self, filename, lineno): + """Set a filename-lineno breakpoint in the debugger. + + Called from self.load_breakpoints and EW.setbreakpoint + """ self.idb.set_break(filename, lineno) - def clear_breakpoint_here(self, filename, lineno): + def clear_breakpoint(self, filename, lineno): self.idb.clear_break(filename, lineno) def clear_file_breaks(self, filename): self.idb.clear_all_file_breaks(filename) def load_breakpoints(self): - "Load PyShellEditorWindow breakpoints into subprocess debugger" + """Load PyShellEditorWindow breakpoints into subprocess debugger.""" for editwin in self.pyshell.flist.inversedict: filename = editwin.io.filename try: for lineno in editwin.breakpoints: - self.set_breakpoint_here(filename, lineno) + self.set_breakpoint(filename, lineno) except AttributeError: continue + class StackViewer(ScrolledList): + "Code stack viewer for debugger GUI." def __init__(self, master, flist, gui): if macosx.isAquaTk(): @@ -414,12 +463,12 @@ def load_stack(self, stack, index=None): self.select(index) def popup_event(self, event): - "override base method" + "Override base method." if self.stack: return ScrolledList.popup_event(self, event) def fill_menu(self): - "override base method" + "Override base method." menu = self.menu menu.add_command(label="Go to source line", command=self.goto_source_line) @@ -427,12 +476,12 @@ def fill_menu(self): command=self.show_stack_frame) def on_select(self, index): - "override base method" + "Override base method." if 0 <= index < len(self.stack): self.gui.show_frame(self.stack[index]) def on_double(self, index): - "override base method" + "Override base method." self.show_source(index) def goto_source_line(self): @@ -457,6 +506,7 @@ def show_source(self, index): class NamespaceViewer: + "Global/local namespace viewer for debugger GUI." def __init__(self, master, title, dict=None): width = 0 @@ -544,6 +594,7 @@ def load_dict(self, dict, force=0, rpc_client=None): def close(self): self.frame.destroy() + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_debugger', verbosity=2, exit=False) diff --git a/Lib/idlelib/debugobj.py b/Lib/idlelib/debugobj.py index 032b686f379378..156377f8ed26ac 100644 --- a/Lib/idlelib/debugobj.py +++ b/Lib/idlelib/debugobj.py @@ -120,7 +120,7 @@ def make_objecttreeitem(labeltext, object, setfunction=None): return c(labeltext, object, setfunction) -def _object_browser(parent): # htest # +def _debug_object_browser(parent): # htest # import sys from tkinter import Toplevel top = Toplevel(parent) @@ -135,9 +135,10 @@ def _object_browser(parent): # htest # node = TreeNode(sc.canvas, None, item) node.update() + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_debugobj', verbosity=2, exit=False) from idlelib.idle_test.htest import run - run(_object_browser) + run(_debug_object_browser) diff --git a/Lib/idlelib/delegator.py b/Lib/idlelib/delegator.py index 55c95da8532f47..93ae8bbd43ff44 100644 --- a/Lib/idlelib/delegator.py +++ b/Lib/idlelib/delegator.py @@ -28,6 +28,7 @@ def setdelegate(self, delegate): self.resetcache() self.delegate = delegate + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_delegator', verbosity=2) diff --git a/Lib/idlelib/dynoption.py b/Lib/idlelib/dynoption.py index d5dfc3eda13f60..b8937f7106ca75 100644 --- a/Lib/idlelib/dynoption.py +++ b/Lib/idlelib/dynoption.py @@ -29,6 +29,7 @@ def SetMenu(self,valueList,value=None): if value: self.variable.set(value) + def _dyn_option_menu(parent): # htest # from tkinter import Toplevel # + StringVar, Button @@ -49,6 +50,7 @@ def update(): button = Button(top, text="Change option set", command=update) button.pack() + if __name__ == '__main__': # Only module without unittests because of intention to replace. from idlelib.idle_test.htest import run diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 69b27d0683a104..6ad383f460c7ee 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -1748,6 +1748,7 @@ def _editor_window(parent): # htest # # Does not stop error, neither does following # edit.text.bind("<>", edit.close_event) + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_editor', verbosity=2, exit=False) diff --git a/Lib/idlelib/filelist.py b/Lib/idlelib/filelist.py index f87781d2570fe0..e27e5d32a0ff63 100644 --- a/Lib/idlelib/filelist.py +++ b/Lib/idlelib/filelist.py @@ -124,6 +124,7 @@ def _test(): # TODO check and convert to htest if flist.inversedict: root.mainloop() + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_filelist', verbosity=2) diff --git a/Lib/idlelib/grep.py b/Lib/idlelib/grep.py index 12513594b76f8f..ef14349960bfa2 100644 --- a/Lib/idlelib/grep.py +++ b/Lib/idlelib/grep.py @@ -204,15 +204,17 @@ def _grep_dialog(parent): # htest # frame.pack() text = Text(frame, height=5) text.pack() + text.insert('1.0', 'import grep') def show_grep_dialog(): - text.tag_add(SEL, "1.0", END) + text.tag_add(SEL, "1.0", '1.end') grep(text, flist=flist) - text.tag_remove(SEL, "1.0", END) + text.tag_remove(SEL, "1.0", '1.end') button = Button(frame, text="Show GrepDialog", command=show_grep_dialog) button.pack() + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_grep', verbosity=2, exit=False) diff --git a/Lib/idlelib/help.py b/Lib/idlelib/help.py index cc027b9cef4f5b..3cc7e36e35555b 100644 --- a/Lib/idlelib/help.py +++ b/Lib/idlelib/help.py @@ -278,7 +278,8 @@ def copy_strip(): out.write(line.rstrip() + b'\n') print(f'{src} copied to {dst}') -def show_idlehelp(parent): + +def _helpwindow(parent): "Create HelpWindow; called from Idle Help event handler." filename = join(abspath(dirname(__file__)), 'help.html') if not isfile(filename): @@ -286,9 +287,10 @@ def show_idlehelp(parent): return HelpWindow(parent, filename, 'IDLE Help (%s)' % python_version()) + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_help', verbosity=2, exit=False) from idlelib.idle_test.htest import run - run(show_idlehelp) + run(_helpwindow) diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index d297f8aa0094ee..997f85ff5a78b2 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -1,38 +1,36 @@ -'''Run human tests of Idle's window, dialog, and popup widgets. - -run(*tests) -Create a master Tk window. Within that, run each callable in tests -after finding the matching test spec in this file. If tests is empty, -run an htest for each spec dict in this file after finding the matching -callable in the module named in the spec. Close the window to skip or -end the test. - -In a tested module, let X be a global name bound to a callable (class -or function) whose .__name__ attribute is also X (the usual situation). -The first parameter of X must be 'parent'. When called, the parent -argument will be the root window. X must create a child Toplevel -window (or subclass thereof). The Toplevel may be a test widget or -dialog, in which case the callable is the corresponding class. Or the -Toplevel may contain the widget to be tested or set up a context in -which a test widget is invoked. In this latter case, the callable is a -wrapper function that sets up the Toplevel and other objects. Wrapper -function names, such as _editor_window', should start with '_'. +"""Run human tests of Idle's window, dialog, and popup widgets. + +run(*tests) Create a master Tk() htest window. Within that, run each +callable in tests after finding the matching test spec in this file. If +tests is empty, run an htest for each spec dict in this file after +finding the matching callable in the module named in the spec. Close +the master window to end testing. + +In a tested module, let X be a global name bound to a callable (class or +function) whose .__name__ attribute is also X (the usual situation). The +first parameter of X must be 'parent' or 'master'. When called, the +first argument will be the root window. X must create a child +Toplevel(parent/master) (or subclass thereof). The Toplevel may be a +test widget or dialog, in which case the callable is the corresponding +class. Or the Toplevel may contain the widget to be tested or set up a +context in which a test widget is invoked. In this latter case, the +callable is a wrapper function that sets up the Toplevel and other +objects. Wrapper function names, such as _editor_window', should start +with '_' and be lowercase. End the module with if __name__ == '__main__': - + from idlelib.idle_test.htest import run - run(X) + run(callable) # There could be multiple comma-separated callables. -To have wrapper functions and test invocation code ignored by coveragepy -reports, put '# htest #' on the def statement header line. - -def _wrapper(parent): # htest # - -Also make sure that the 'if __name__' line matches the above. Then have -make sure that .coveragerc includes the following. +To have wrapper functions ignored by coverage reports, tag the def +header like so: "def _wrapper(parent): # htest #". Use the same tag +for htest lines in widget code. Make sure that the 'if __name__' line +matches the above. Then have make sure that .coveragerc includes the +following: [report] exclude_lines = @@ -46,7 +44,7 @@ def _wrapper(parent): # htest # following template, with X.__name__ prepended to '_spec'. When all tests are run, the prefix is use to get X. -_spec = { +callable_spec = { 'file': '', 'kwds': {'title': ''}, 'msg': "" @@ -54,16 +52,16 @@ def _wrapper(parent): # htest # file (no .py): run() imports file.py. kwds: augmented with {'parent':root} and passed to X as **kwds. -title: an example kwd; some widgets need this, delete if not. +title: an example kwd; some widgets need this, delete line if not. msg: master window hints about testing the widget. -Modules and classes not being tested at the moment: -pyshell.PyShellEditorWindow -debugger.Debugger -autocomplete_w.AutoCompleteWindow -outwin.OutputWindow (indirectly being tested with grep test) -''' +TODO test these modules and classes: + autocomplete_w.AutoCompleteWindow + debugger.Debugger + outwin.OutputWindow (indirectly being tested with grep test) + pyshell.PyShellEditorWindow +""" import idlelib.pyshell # Set Windows DPI awareness before Tk(). from importlib import import_module @@ -91,15 +89,6 @@ def _wrapper(parent): # htest # "Force-open-calltip does not work here.\n" } -_module_browser_spec = { - 'file': 'browser', - 'kwds': {}, - 'msg': "Inspect names of module, class(with superclass if " - "applicable), methods and functions.\nToggle nested items.\n" - "Double clicking on items prints a traceback for an exception " - "that is ignored." - } - _color_delegator_spec = { 'file': 'colorizer', 'kwds': {}, @@ -109,16 +98,6 @@ def _wrapper(parent): # htest # "The default color scheme is in idlelib/config-highlight.def" } -CustomRun_spec = { - 'file': 'query', - 'kwds': {'title': 'Customize query.py Run', - '_htest': True}, - 'msg': "Enter with or [Run]. Print valid entry to Shell\n" - "Arguments are parsed into a list\n" - "Mode is currently restart True or False\n" - "Close dialog with valid entry, , [Cancel], [X]" - } - ConfigDialog_spec = { 'file': 'configdialog', 'kwds': {'title': 'ConfigDialogTest', @@ -135,6 +114,24 @@ def _wrapper(parent): # htest # "changes made have persisted." } +CustomRun_spec = { + 'file': 'query', + 'kwds': {'title': 'Customize query.py Run', + '_htest': True}, + 'msg': "Enter with or [OK]. Print valid entry to Shell\n" + "Arguments are parsed into a list\n" + "Mode is currently restart True or False\n" + "Close dialog with valid entry, , [Cancel], [X]" + } + +_debug_object_browser_spec = { + 'file': 'debugobj', + 'kwds': {}, + 'msg': "Double click on items up to the lowest level.\n" + "Attributes of the objects and related information " + "will be displayed side-by-side at each level." + } + # TODO Improve message _dyn_option_menu_spec = { 'file': 'dynoption', @@ -152,7 +149,7 @@ def _wrapper(parent): # htest # "Best to close editor first." } -GetKeysDialog_spec = { +GetKeysWindow_spec = { 'file': 'config_key', 'kwds': {'title': 'Test keybindings', 'action': 'find-again', @@ -173,8 +170,8 @@ def _wrapper(parent): # htest # 'msg': "Click the 'Show GrepDialog' button.\n" "Test the various 'Find-in-files' functions.\n" "The results should be displayed in a new '*Output*' window.\n" - "'Right-click'->'Go to file/line' anywhere in the search results " - "should open that file \nin a new EditorWindow." + "'Right-click'->'Go to file/line' in the search results\n " + "should open that file in a new EditorWindow." } HelpSource_spec = { @@ -190,7 +187,14 @@ def _wrapper(parent): # htest # "Any url ('www...', 'http...') is accepted.\n" "Test Browse with and without path, as cannot unittest.\n" "[Ok] or prints valid entry to shell\n" - "[Cancel] or prints None to shell" + ", [Cancel], or [X] prints None to shell" + } + +_helpwindow_spec = { + 'file': 'help', + 'kwds': {}, + 'msg': "If the help text displays, this works.\n" + "Text is selectable. Window is scrollable." } _io_binding_spec = { @@ -206,56 +210,36 @@ def _wrapper(parent): # htest # "Check that changes were saved by opening the file elsewhere." } -_linenumbers_drag_scrolling_spec = { - 'file': 'sidebar', +_multi_call_spec = { + 'file': 'multicall', 'kwds': {}, - 'msg': textwrap.dedent("""\ - 1. Click on the line numbers and drag down below the edge of the - window, moving the mouse a bit and then leaving it there for a while. - The text and line numbers should gradually scroll down, with the - selection updated continuously. - - 2. With the lines still selected, click on a line number above the - selected lines. Only the line whose number was clicked should be - selected. - - 3. Repeat step #1, dragging to above the window. The text and line - numbers should gradually scroll up, with the selection updated - continuously. - - 4. Repeat step #2, clicking a line number below the selection."""), + 'msg': "The following should trigger a print to console or IDLE Shell.\n" + "Entering and leaving the text area, key entry, ,\n" + ", , , \n" + ", and focusing elsewhere." } -_multi_call_spec = { - 'file': 'multicall', +_module_browser_spec = { + 'file': 'browser', 'kwds': {}, - 'msg': "The following actions should trigger a print to console or IDLE" - " Shell.\nEntering and leaving the text area, key entry, " - ",\n, , " - ", \n, and " - "focusing out of the window\nare sequences to be tested." + 'msg': textwrap.dedent(""" + "Inspect names of module, class(with superclass if applicable), + "methods and functions. Toggle nested items. Double clicking + "on items prints a traceback for an exception that is ignored.""") } _multistatus_bar_spec = { 'file': 'statusbar', 'kwds': {}, 'msg': "Ensure presence of multi-status bar below text area.\n" - "Click 'Update Status' to change the multi-status text" - } - -_object_browser_spec = { - 'file': 'debugobj', - 'kwds': {}, - 'msg': "Double click on items up to the lowest level.\n" - "Attributes of the objects and related information " - "will be displayed side-by-side at each level." + "Click 'Update Status' to change the status text" } -_path_browser_spec = { +PathBrowser_spec = { 'file': 'pathbrowser', - 'kwds': {}, + 'kwds': {'_htest': True}, 'msg': "Test for correct display of all paths in sys.path.\n" - "Toggle nested items up to the lowest level.\n" + "Toggle nested items out to the lowest level.\n" "Double clicking on an item prints a traceback\n" "for an exception that is ignored." } @@ -291,6 +275,15 @@ def _wrapper(parent): # htest # "Click [Close] or [X] to close the 'Replace Dialog'." } +_scrolled_list_spec = { + 'file': 'scrolledlist', + 'kwds': {}, + 'msg': "You should see a scrollable list of items\n" + "Selecting (clicking) or double clicking an item " + "prints the name to the console or Idle shell.\n" + "Right clicking an item will display a popup." + } + _search_dialog_spec = { 'file': 'search', 'kwds': {}, @@ -306,28 +299,31 @@ def _wrapper(parent): # htest # "Its only action is to close." } -_scrolled_list_spec = { - 'file': 'scrolledlist', +_sidebar_number_scrolling_spec = { + 'file': 'sidebar', 'kwds': {}, - 'msg': "You should see a scrollable list of items\n" - "Selecting (clicking) or double clicking an item " - "prints the name to the console or Idle shell.\n" - "Right clicking an item will display a popup." - } + 'msg': textwrap.dedent("""\ + 1. Click on the line numbers and drag down below the edge of the + window, moving the mouse a bit and then leaving it there for a + while. The text and line numbers should gradually scroll down, + with the selection updated continuously. -show_idlehelp_spec = { - 'file': 'help', - 'kwds': {}, - 'msg': "If the help text displays, this works.\n" - "Text is selectable. Window is scrollable." + 2. With the lines still selected, click on a line number above + or below the selected lines. Only the line whose number was + clicked should be selected. + + 3. Repeat step #1, dragging to above the window. The text and + line numbers should gradually scroll up, with the selection + updated continuously. + + 4. Repeat step #2, clicking a line number below the selection."""), } -_stack_viewer_spec = { +_stackbrowser_spec = { 'file': 'stackviewer', 'kwds': {}, 'msg': "A stacktrace for a NameError exception.\n" - "Expand 'idlelib ...' and ''.\n" - "Check that exc_value, exc_tb, and exc_type are correct.\n" + "Should have NameError and 1 traceback line." } _tooltip_spec = { @@ -370,11 +366,12 @@ def _wrapper(parent): # htest # } def run(*tests): + "Run callables in tests." root = tk.Tk() root.title('IDLE htest') root.resizable(0, 0) - # a scrollable Label like constant width text widget. + # A scrollable Label-like constant width text widget. frameLabel = tk.Frame(root, padx=10) frameLabel.pack() text = tk.Text(frameLabel, wrap='word') @@ -384,45 +381,44 @@ def run(*tests): scrollbar.pack(side='right', fill='y', expand=False) text.pack(side='left', fill='both', expand=True) - test_list = [] # List of tuples of the form (spec, callable widget) + test_list = [] # Make list of (spec, callable) tuples. if tests: for test in tests: test_spec = globals()[test.__name__ + '_spec'] test_spec['name'] = test.__name__ test_list.append((test_spec, test)) else: - for k, d in globals().items(): - if k.endswith('_spec'): - test_name = k[:-5] - test_spec = d + for key, dic in globals().items(): + if key.endswith('_spec'): + test_name = key[:-5] + test_spec = dic test_spec['name'] = test_name mod = import_module('idlelib.' + test_spec['file']) test = getattr(mod, test_name) test_list.append((test_spec, test)) + test_list.reverse() # So can pop in proper order in next_test. test_name = tk.StringVar(root) callable_object = None test_kwds = None def next_test(): - nonlocal test_name, callable_object, test_kwds if len(test_list) == 1: next_button.pack_forget() test_spec, callable_object = test_list.pop() test_kwds = test_spec['kwds'] - test_kwds['parent'] = root test_name.set('Test ' + test_spec['name']) - text.configure(state='normal') # enable text editing - text.delete('1.0','end') - text.insert("1.0",test_spec['msg']) - text.configure(state='disabled') # preserve read-only property + text['state'] = 'normal' # Enable text replacement. + text.delete('1.0', 'end') + text.insert("1.0", test_spec['msg']) + text['state'] = 'disabled' # Restore read-only property. def run_test(_=None): - widget = callable_object(**test_kwds) + widget = callable_object(root, **test_kwds) try: - print(widget.result) + print(widget.result) # Only true for query classes(?). except AttributeError: pass @@ -441,5 +437,6 @@ def close(_=None): next_test() root.mainloop() + if __name__ == '__main__': run() diff --git a/Lib/idlelib/idle_test/test_debugger.py b/Lib/idlelib/idle_test/test_debugger.py index 35efb3411c73b5..d1c9638dd5d711 100644 --- a/Lib/idlelib/idle_test/test_debugger.py +++ b/Lib/idlelib/idle_test/test_debugger.py @@ -1,16 +1,286 @@ -"Test debugger, coverage 19%" +"""Test debugger, coverage 66% + +Try to make tests pass with draft bdbx, which may replace bdb in 3.13+. +""" from idlelib import debugger -import unittest -from test.support import requires -requires('gui') +from collections import namedtuple +from textwrap import dedent from tkinter import Tk +from test.support import requires +import unittest +from unittest import mock +from unittest.mock import Mock, patch + +"""A test python script for the debug tests.""" +TEST_CODE = dedent(""" + i = 1 + i += 2 + if i == 3: + print(i) + """) + + +class MockFrame: + "Minimal mock frame." + + def __init__(self, code, lineno): + self.f_code = code + self.f_lineno = lineno + + +class IdbTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.gui = Mock() + cls.idb = debugger.Idb(cls.gui) + + # Create test and code objects to simulate a debug session. + code_obj = compile(TEST_CODE, 'idlelib/file.py', mode='exec') + frame1 = MockFrame(code_obj, 1) + frame1.f_back = None + frame2 = MockFrame(code_obj, 2) + frame2.f_back = frame1 + cls.frame = frame2 + cls.msg = 'file.py:2: ()' + + def test_init(self): + self.assertIs(self.idb.gui, self.gui) + # Won't test super call since two Bdbs are very different. + + def test_user_line(self): + # Test that .user_line() creates a string message for a frame. + self.gui.interaction = Mock() + self.idb.user_line(self.frame) + self.gui.interaction.assert_called_once_with(self.msg, self.frame) + + def test_user_exception(self): + # Test that .user_exception() creates a string message for a frame. + exc_info = (type(ValueError), ValueError(), None) + self.gui.interaction = Mock() + self.idb.user_exception(self.frame, exc_info) + self.gui.interaction.assert_called_once_with( + self.msg, self.frame, exc_info) + + +class FunctionTest(unittest.TestCase): + # Test module functions together. + + def test_functions(self): + rpc_obj = compile(TEST_CODE,'rpc.py', mode='exec') + rpc_frame = MockFrame(rpc_obj, 2) + rpc_frame.f_back = rpc_frame + self.assertTrue(debugger._in_rpc_code(rpc_frame)) + self.assertEqual(debugger._frame2message(rpc_frame), + 'rpc.py:2: ()') + + code_obj = compile(TEST_CODE, 'idlelib/debugger.py', mode='exec') + code_frame = MockFrame(code_obj, 1) + code_frame.f_back = None + self.assertFalse(debugger._in_rpc_code(code_frame)) + self.assertEqual(debugger._frame2message(code_frame), + 'debugger.py:1: ()') + + code_frame.f_back = code_frame + self.assertFalse(debugger._in_rpc_code(code_frame)) + code_frame.f_back = rpc_frame + self.assertTrue(debugger._in_rpc_code(code_frame)) + + +class DebuggerTest(unittest.TestCase): + "Tests for Debugger that do not need a real root." + + @classmethod + def setUpClass(cls): + cls.pyshell = Mock() + cls.pyshell.root = Mock() + cls.idb = Mock() + with patch.object(debugger.Debugger, 'make_gui'): + cls.debugger = debugger.Debugger(cls.pyshell, cls.idb) + cls.debugger.root = Mock() + + def test_cont(self): + self.debugger.cont() + self.idb.set_continue.assert_called_once() + + def test_step(self): + self.debugger.step() + self.idb.set_step.assert_called_once() + + def test_quit(self): + self.debugger.quit() + self.idb.set_quit.assert_called_once() + + def test_next(self): + with patch.object(self.debugger, 'frame') as frame: + self.debugger.next() + self.idb.set_next.assert_called_once_with(frame) + + def test_ret(self): + with patch.object(self.debugger, 'frame') as frame: + self.debugger.ret() + self.idb.set_return.assert_called_once_with(frame) + + def test_clear_breakpoint(self): + self.debugger.clear_breakpoint('test.py', 4) + self.idb.clear_break.assert_called_once_with('test.py', 4) + + def test_clear_file_breaks(self): + self.debugger.clear_file_breaks('test.py') + self.idb.clear_all_file_breaks.assert_called_once_with('test.py') + + def test_set_load_breakpoints(self): + # Test the .load_breakpoints() method calls idb. + FileIO = namedtuple('FileIO', 'filename') + + class MockEditWindow(object): + def __init__(self, fn, breakpoints): + self.io = FileIO(fn) + self.breakpoints = breakpoints + + self.pyshell.flist = Mock() + self.pyshell.flist.inversedict = ( + MockEditWindow('test1.py', [4, 4]), + MockEditWindow('test2.py', [13, 44, 45]), + ) + self.debugger.set_breakpoint('test0.py', 1) + self.idb.set_break.assert_called_once_with('test0.py', 1) + self.debugger.load_breakpoints() # Call set_breakpoint 5 times. + self.idb.set_break.assert_has_calls( + [mock.call('test0.py', 1), + mock.call('test1.py', 4), + mock.call('test1.py', 4), + mock.call('test2.py', 13), + mock.call('test2.py', 44), + mock.call('test2.py', 45)]) + + def test_sync_source_line(self): + # Test that .sync_source_line() will set the flist.gotofileline with fixed frame. + test_code = compile(TEST_CODE, 'test_sync.py', 'exec') + test_frame = MockFrame(test_code, 1) + self.debugger.frame = test_frame + + self.debugger.flist = Mock() + with patch('idlelib.debugger.os.path.exists', return_value=True): + self.debugger.sync_source_line() + self.debugger.flist.gotofileline.assert_called_once_with('test_sync.py', 1) + + +class DebuggerGuiTest(unittest.TestCase): + """Tests for debugger.Debugger that need tk root. + + close needs debugger.top set in make_gui. + """ + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = root = Tk() + root.withdraw() + cls.pyshell = Mock() + cls.pyshell.root = root + cls.idb = Mock() +# stack tests fail with debugger here. +## cls.debugger = debugger.Debugger(cls.pyshell, cls.idb) +## cls.debugger.root = root +## # real root needed for real make_gui +## # run, interacting, abort_loop + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + del cls.root + + def setUp(self): + self.debugger = debugger.Debugger(self.pyshell, self.idb) + self.debugger.root = self.root + # real root needed for real make_gui + # run, interacting, abort_loop + + def test_run_debugger(self): + self.debugger.run(1, 'two') + self.idb.run.assert_called_once_with(1, 'two') + self.assertEqual(self.debugger.interacting, 0) + + def test_close(self): + # Test closing the window in an idle state. + self.debugger.close() + self.pyshell.close_debugger.assert_called_once() + + def test_show_stack(self): + self.debugger.show_stack() + self.assertEqual(self.debugger.stackviewer.gui, self.debugger) + + def test_show_stack_with_frame(self): + test_frame = MockFrame(None, None) + self.debugger.frame = test_frame + + # Reset the stackviewer to force it to be recreated. + self.debugger.stackviewer = None + self.idb.get_stack.return_value = ([], 0) + self.debugger.show_stack() + + # Check that the newly created stackviewer has the test gui as a field. + self.assertEqual(self.debugger.stackviewer.gui, self.debugger) + self.idb.get_stack.assert_called_once_with(test_frame, None) + + +class StackViewerTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.root.withdraw() + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + del cls.root + + def setUp(self): + self.code = compile(TEST_CODE, 'test_stackviewer.py', 'exec') + self.stack = [ + (MockFrame(self.code, 1), 1), + (MockFrame(self.code, 2), 2) + ] + # Create a stackviewer and load the test stack. + self.sv = debugger.StackViewer(self.root, None, None) + self.sv.load_stack(self.stack) + + def test_init(self): + # Test creation of StackViewer. + gui = None + flist = None + master_window = self.root + sv = debugger.StackViewer(master_window, flist, gui) + self.assertTrue(hasattr(sv, 'stack')) + + def test_load_stack(self): + # Test the .load_stack() method against a fixed test stack. + # Check the test stack is assigned and the list contains the repr of them. + self.assertEqual(self.sv.stack, self.stack) + self.assertTrue('?.(), line 1:' in self.sv.get(0)) + self.assertEqual(self.sv.get(1), '?.(), line 2: ') + + def test_show_source(self): + # Test the .show_source() method against a fixed test stack. + # Patch out the file list to monitor it + self.sv.flist = Mock() + # Patch out isfile to pretend file exists. + with patch('idlelib.debugger.os.path.isfile', return_value=True) as isfile: + self.sv.show_source(1) + isfile.assert_called_once_with('test_stackviewer.py') + self.sv.flist.open.assert_called_once_with('test_stackviewer.py') + class NameSpaceTest(unittest.TestCase): @classmethod def setUpClass(cls): + requires('gui') cls.root = Tk() cls.root.withdraw() @@ -23,7 +293,5 @@ def test_init(self): debugger.NamespaceViewer(self.root, 'Test') -# Other classes are Idb, Debugger, and StackViewer. - if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/iomenu.py b/Lib/idlelib/iomenu.py index af8159c2b33f51..667623ec71ac98 100644 --- a/Lib/idlelib/iomenu.py +++ b/Lib/idlelib/iomenu.py @@ -393,13 +393,15 @@ def updaterecentfileslist(self,filename): if self.editwin.flist: self.editwin.update_recent_files_list(filename) + def _io_binding(parent): # htest # from tkinter import Toplevel, Text - root = Toplevel(parent) - root.title("Test IOBinding") + top = Toplevel(parent) + top.title("Test IOBinding") x, y = map(int, parent.geometry().split('+')[1:]) - root.geometry("+%d+%d" % (x, y + 175)) + top.geometry("+%d+%d" % (x, y + 175)) + class MyEditWin: def __init__(self, text): self.text = text @@ -423,12 +425,13 @@ def saveas(self, event): def savecopy(self, event): self.text.event_generate("<>") - text = Text(root) + text = Text(top) text.pack() text.focus_set() editwin = MyEditWin(text) IOBinding(editwin) + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_iomenu', verbosity=2, exit=False) diff --git a/Lib/idlelib/multicall.py b/Lib/idlelib/multicall.py index 0200f445cc9340..41f81813113062 100644 --- a/Lib/idlelib/multicall.py +++ b/Lib/idlelib/multicall.py @@ -421,6 +421,8 @@ def _multi_call(parent): # htest # top.geometry("+%d+%d" % (x, y + 175)) text = MultiCallCreator(tkinter.Text)(top) text.pack() + text.focus_set() + def bindseq(seq, n=[0]): def handler(event): print(seq) @@ -440,6 +442,7 @@ def handler(event): bindseq("") bindseq("") + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_mainmenu', verbosity=2, exit=False) diff --git a/Lib/idlelib/outwin.py b/Lib/idlelib/outwin.py index 610031e26f1dff..5ed3f35a7af655 100644 --- a/Lib/idlelib/outwin.py +++ b/Lib/idlelib/outwin.py @@ -182,6 +182,7 @@ def setup(self): text.tag_raise('sel') self.write = self.owin.write + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_outwin', verbosity=2, exit=False) diff --git a/Lib/idlelib/pathbrowser.py b/Lib/idlelib/pathbrowser.py index 6de242d0000bed..48a77875ba5801 100644 --- a/Lib/idlelib/pathbrowser.py +++ b/Lib/idlelib/pathbrowser.py @@ -99,13 +99,9 @@ def listmodules(self, allnames): return sorted -def _path_browser(parent): # htest # - PathBrowser(parent, _htest=True) - parent.mainloop() - if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_pathbrowser', verbosity=2, exit=False) from idlelib.idle_test.htest import run - run(_path_browser) + run(PathBrowser) diff --git a/Lib/idlelib/percolator.py b/Lib/idlelib/percolator.py index 1fe34d29f54eb2..aa73427c4915c8 100644 --- a/Lib/idlelib/percolator.py +++ b/Lib/idlelib/percolator.py @@ -86,11 +86,11 @@ def delete(self, *args): print(self.name, ": delete", args) self.delegate.delete(*args) - box = tk.Toplevel(parent) - box.title("Test Percolator") + top = tk.Toplevel(parent) + top.title("Test Percolator") x, y = map(int, parent.geometry().split('+')[1:]) - box.geometry("+%d+%d" % (x, y + 175)) - text = tk.Text(box) + top.geometry("+%d+%d" % (x, y + 175)) + text = tk.Text(top) p = Percolator(text) pin = p.insertfilter pout = p.removefilter @@ -103,13 +103,15 @@ def toggle2(): (pin if var2.get() else pout)(t2) text.pack() + text.focus_set() var1 = tk.IntVar(parent) - cb1 = tk.Checkbutton(box, text="Tracer1", command=toggle1, variable=var1) + cb1 = tk.Checkbutton(top, text="Tracer1", command=toggle1, variable=var1) cb1.pack() var2 = tk.IntVar(parent) - cb2 = tk.Checkbutton(box, text="Tracer2", command=toggle2, variable=var2) + cb2 = tk.Checkbutton(top, text="Tracer2", command=toggle2, variable=var2) cb2.pack() + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_percolator', verbosity=2, exit=False) diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 7a2707935b60c9..1524fccd5d20f8 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -133,8 +133,8 @@ class PyShellEditorWindow(EditorWindow): def __init__(self, *args): self.breakpoints = [] EditorWindow.__init__(self, *args) - self.text.bind("<>", self.set_breakpoint_here) - self.text.bind("<>", self.clear_breakpoint_here) + self.text.bind("<>", self.set_breakpoint_event) + self.text.bind("<>", self.clear_breakpoint_event) self.text.bind("<>", self.flist.open_shell) #TODO: don't read/write this from/to .idlerc when testing @@ -155,8 +155,8 @@ def filename_changed_hook(old_hook=self.io.filename_change_hook, ("Copy", "<>", "rmenu_check_copy"), ("Paste", "<>", "rmenu_check_paste"), (None, None, None), - ("Set Breakpoint", "<>", None), - ("Clear Breakpoint", "<>", None) + ("Set Breakpoint", "<>", None), + ("Clear Breakpoint", "<>", None) ] def color_breakpoint_text(self, color=True): @@ -181,11 +181,11 @@ def set_breakpoint(self, lineno): self.breakpoints.append(lineno) try: # update the subprocess debugger debug = self.flist.pyshell.interp.debugger - debug.set_breakpoint_here(filename, lineno) + debug.set_breakpoint(filename, lineno) except: # but debugger may not be active right now.... pass - def set_breakpoint_here(self, event=None): + def set_breakpoint_event(self, event=None): text = self.text filename = self.io.filename if not filename: @@ -194,7 +194,7 @@ def set_breakpoint_here(self, event=None): lineno = int(float(text.index("insert"))) self.set_breakpoint(lineno) - def clear_breakpoint_here(self, event=None): + def clear_breakpoint_event(self, event=None): text = self.text filename = self.io.filename if not filename: @@ -209,7 +209,7 @@ def clear_breakpoint_here(self, event=None): "insert lineend +1char") try: debug = self.flist.pyshell.interp.debugger - debug.clear_breakpoint_here(filename, lineno) + debug.clear_breakpoint(filename, lineno) except: pass @@ -1694,6 +1694,7 @@ def main(): root.destroy() capture_warnings(False) + if __name__ == "__main__": main() diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index df02f2123ab02f..57230e2aaca66d 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -368,7 +368,7 @@ def create_extra(self): sticky='we') def cli_args_ok(self): - "Validity check and parsing for command line arguments." + "Return command line arg list or None if error." cli_string = self.entry.get().strip() try: cli_args = shlex.split(cli_string, posix=True) diff --git a/Lib/idlelib/redirector.py b/Lib/idlelib/redirector.py index 4928340e98df68..08728956abd900 100644 --- a/Lib/idlelib/redirector.py +++ b/Lib/idlelib/redirector.py @@ -164,6 +164,7 @@ def my_insert(*args): original_insert(*args) original_insert = redir.register("insert", my_insert) + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_redirector', verbosity=2, exit=False) diff --git a/Lib/idlelib/replace.py b/Lib/idlelib/replace.py index ca83173877ad1d..a29ca591427491 100644 --- a/Lib/idlelib/replace.py +++ b/Lib/idlelib/replace.py @@ -299,6 +299,7 @@ def show_replace(): button = Button(frame, text="Replace", command=show_replace) button.pack() + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_replace', verbosity=2, exit=False) diff --git a/Lib/idlelib/scrolledlist.py b/Lib/idlelib/scrolledlist.py index 71fd18ab19ec8a..4fb418db326255 100644 --- a/Lib/idlelib/scrolledlist.py +++ b/Lib/idlelib/scrolledlist.py @@ -132,6 +132,7 @@ def _scrolled_list(parent): # htest # top = Toplevel(parent) x, y = map(int, parent.geometry().split('+')[1:]) top.geometry("+%d+%d" % (x+200, y + 175)) + class MyScrolledList(ScrolledList): def fill_menu(self): self.menu.add_command(label="right click") def on_select(self, index): print("select", self.get(index)) @@ -141,9 +142,10 @@ def on_double(self, index): print("double", self.get(index)) for i in range(30): scrolled_list.append("Item %02d" % i) + if __name__ == '__main__': from unittest import main - main('idlelib.idle_test.test_scrolledlist', verbosity=2,) + main('idlelib.idle_test.test_scrolledlist', verbosity=2, exit=False) from idlelib.idle_test.htest import run run(_scrolled_list) diff --git a/Lib/idlelib/search.py b/Lib/idlelib/search.py index b35f3b59c3d2e8..935a4832257fa4 100644 --- a/Lib/idlelib/search.py +++ b/Lib/idlelib/search.py @@ -156,6 +156,7 @@ def show_find(): button = Button(frame, text="Search (selection ignored)", command=show_find) button.pack() + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_search', verbosity=2, exit=False) diff --git a/Lib/idlelib/sidebar.py b/Lib/idlelib/sidebar.py index 166c04342907f9..ff77b568a786e0 100644 --- a/Lib/idlelib/sidebar.py +++ b/Lib/idlelib/sidebar.py @@ -513,16 +513,16 @@ def update_colors(self): self.change_callback() -def _linenumbers_drag_scrolling(parent): # htest # +def _sidebar_number_scrolling(parent): # htest # from idlelib.idle_test.test_sidebar import Dummy_editwin - toplevel = tk.Toplevel(parent) - text_frame = tk.Frame(toplevel) + top = tk.Toplevel(parent) + text_frame = tk.Frame(top) text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) text_frame.rowconfigure(1, weight=1) text_frame.columnconfigure(1, weight=1) - font = idleConf.GetFont(toplevel, 'main', 'EditorWindow') + font = idleConf.GetFont(top, 'main', 'EditorWindow') text = tk.Text(text_frame, width=80, height=24, wrap=tk.NONE, font=font) text.grid(row=1, column=1, sticky=tk.NSEW) @@ -540,4 +540,4 @@ def _linenumbers_drag_scrolling(parent): # htest # main('idlelib.idle_test.test_sidebar', verbosity=2, exit=False) from idlelib.idle_test.htest import run - run(_linenumbers_drag_scrolling) + run(_sidebar_number_scrolling) diff --git a/Lib/idlelib/stackviewer.py b/Lib/idlelib/stackviewer.py index 4858cc682a4f45..977c56ef15f2ae 100644 --- a/Lib/idlelib/stackviewer.py +++ b/Lib/idlelib/stackviewer.py @@ -1,3 +1,5 @@ +# Rename to stackbrowser or possibly consolidate with browser. + import linecache import os @@ -111,7 +113,7 @@ def setfunction(value, key=key, object=self.object): return sublist -def _stack_viewer(parent): # htest # +def _stackbrowser(parent): # htest # from idlelib.pyshell import PyShellFileList top = tk.Toplevel(parent) top.title("Test StackViewer") @@ -129,4 +131,4 @@ def _stack_viewer(parent): # htest # main('idlelib.idle_test.test_stackviewer', verbosity=2, exit=False) from idlelib.idle_test.htest import run - run(_stack_viewer) + run(_stackbrowser) diff --git a/Lib/idlelib/statusbar.py b/Lib/idlelib/statusbar.py index 755fafb0ac6438..8445d4cc8dfdb9 100644 --- a/Lib/idlelib/statusbar.py +++ b/Lib/idlelib/statusbar.py @@ -26,6 +26,7 @@ def _multistatus_bar(parent): # htest # x, y = map(int, parent.geometry().split('+')[1:]) top.geometry("+%d+%d" %(x, y + 175)) top.title("Test multistatus bar") + frame = Frame(top) text = Text(frame, height=5, width=40) text.pack() @@ -42,6 +43,7 @@ def change(): button.pack(side='bottom') frame.pack() + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_statusbar', verbosity=2, exit=False) diff --git a/Lib/idlelib/tree.py b/Lib/idlelib/tree.py index 5f30f0f6092bfa..9c2eb47b24aec9 100644 --- a/Lib/idlelib/tree.py +++ b/Lib/idlelib/tree.py @@ -492,6 +492,7 @@ def _tree_widget(parent): # htest # node = TreeNode(sc.canvas, None, item) node.expand() + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_tree', verbosity=2, exit=False) diff --git a/Lib/idlelib/undo.py b/Lib/idlelib/undo.py index 5f10c0f05c1acb..f52446d5fcdcf8 100644 --- a/Lib/idlelib/undo.py +++ b/Lib/idlelib/undo.py @@ -339,25 +339,26 @@ def bump_depth(self, incr=1): def _undo_delegator(parent): # htest # from tkinter import Toplevel, Text, Button from idlelib.percolator import Percolator - undowin = Toplevel(parent) - undowin.title("Test UndoDelegator") + top = Toplevel(parent) + top.title("Test UndoDelegator") x, y = map(int, parent.geometry().split('+')[1:]) - undowin.geometry("+%d+%d" % (x, y + 175)) + top.geometry("+%d+%d" % (x, y + 175)) - text = Text(undowin, height=10) + text = Text(top, height=10) text.pack() text.focus_set() p = Percolator(text) d = UndoDelegator() p.insertfilter(d) - undo = Button(undowin, text="Undo", command=lambda:d.undo_event(None)) + undo = Button(top, text="Undo", command=lambda:d.undo_event(None)) undo.pack(side='left') - redo = Button(undowin, text="Redo", command=lambda:d.redo_event(None)) + redo = Button(top, text="Redo", command=lambda:d.redo_event(None)) redo.pack(side='left') - dump = Button(undowin, text="Dump", command=lambda:d.dump_event(None)) + dump = Button(top, text="Dump", command=lambda:d.dump_event(None)) dump.pack(side='left') + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_undo', verbosity=2, exit=False) diff --git a/Lib/idlelib/util.py b/Lib/idlelib/util.py index ede670a4db5536..5ac69a7b94cb56 100644 --- a/Lib/idlelib/util.py +++ b/Lib/idlelib/util.py @@ -16,6 +16,7 @@ # .pyw is for Windows; .pyi is for stub files. py_extensions = ('.py', '.pyw', '.pyi') # Order needed for open/save dialogs. + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_util', verbosity=2) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index ec2e56f6ea9ca1..d942045f3de666 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -824,10 +824,16 @@ def _module_repr_from_spec(spec): """Return the repr to use for the module.""" name = '?' if spec.name is None else spec.name if spec.origin is None: - if spec.loader is None: + loader = spec.loader + if loader is None: return f'' + elif ( + _bootstrap_external is not None + and isinstance(loader, _bootstrap_external.NamespaceLoader) + ): + return f'' else: - return f'' + return f'' else: if spec.has_location: return f'' diff --git a/Lib/inspect.py b/Lib/inspect.py index aaa22bef896602..f0b72662a9a0b2 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -3316,6 +3316,16 @@ def __repr__(self): return '<{} {}>'.format(self.__class__.__name__, self) def __str__(self): + return self.format() + + def format(self, *, max_width=None): + """Create a string representation of the Signature object. + + If *max_width* integer is passed, + signature will try to fit into the *max_width*. + If signature is longer than *max_width*, + all parameters will be on separate lines. + """ result = [] render_pos_only_separator = False render_kw_only_separator = True @@ -3353,6 +3363,8 @@ def __str__(self): result.append('/') rendered = '({})'.format(', '.join(result)) + if max_width is not None and len(rendered) > max_width: + rendered = '(\n {}\n)'.format(',\n '.join(result)) if self.return_annotation is not _empty: anno = formatannotation(self.return_annotation) diff --git a/Lib/multiprocessing/shared_memory.py b/Lib/multiprocessing/shared_memory.py index 9a1e5aa17b87a2..67e70fdc27cf31 100644 --- a/Lib/multiprocessing/shared_memory.py +++ b/Lib/multiprocessing/shared_memory.py @@ -71,8 +71,9 @@ class SharedMemory: _flags = os.O_RDWR _mode = 0o600 _prepend_leading_slash = True if _USE_POSIX else False + _track = True - def __init__(self, name=None, create=False, size=0): + def __init__(self, name=None, create=False, size=0, *, track=True): if not size >= 0: raise ValueError("'size' must be a positive integer") if create: @@ -82,6 +83,7 @@ def __init__(self, name=None, create=False, size=0): if name is None and not self._flags & os.O_EXCL: raise ValueError("'name' can only be None if create=True") + self._track = track if _USE_POSIX: # POSIX Shared Memory @@ -116,8 +118,8 @@ def __init__(self, name=None, create=False, size=0): except OSError: self.unlink() raise - - resource_tracker.register(self._name, "shared_memory") + if self._track: + resource_tracker.register(self._name, "shared_memory") else: @@ -236,12 +238,20 @@ def close(self): def unlink(self): """Requests that the underlying shared memory block be destroyed. - In order to ensure proper cleanup of resources, unlink should be - called once (and only once) across all processes which have access - to the shared memory block.""" + Unlink should be called once (and only once) across all handles + which have access to the shared memory block, even if these + handles belong to different processes. Closing and unlinking may + happen in any order, but trying to access data inside a shared + memory block after unlinking may result in memory errors, + depending on platform. + + This method has no effect on Windows, where the only way to + delete a shared memory block is to close all handles.""" + if _USE_POSIX and self._name: _posixshmem.shm_unlink(self._name) - resource_tracker.unregister(self._name, "shared_memory") + if self._track: + resource_tracker.unregister(self._name, "shared_memory") _encoding = "utf8" diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 51b2f38036286b..87d1f6b58ec52e 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -5,18 +5,16 @@ operating systems. """ -import contextlib import functools -import glob import io import ntpath import os import posixpath -import re import sys import warnings from _collections_abc import Sequence from errno import ENOENT, ENOTDIR, EBADF, ELOOP, EINVAL +from itertools import chain from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO try: @@ -75,17 +73,23 @@ def _is_case_sensitive(pathmod): # Globbing helpers # +re = glob = None + @functools.lru_cache(maxsize=256) def _compile_pattern(pat, sep, case_sensitive): """Compile given glob pattern to a re.Pattern object (observing case sensitivity).""" + global re, glob + if re is None: + import re, glob + flags = re.NOFLAG if case_sensitive else re.IGNORECASE regex = glob.translate(pat, recursive=True, include_hidden=True, seps=sep) # The string representation of an empty path is a single dot ('.'). Empty # paths shouldn't match wildcards, so we consume it with an atomic group. regex = r'(\.\Z)?+' + regex - return re.compile(regex, flags).match + return re.compile(regex, flags=flags).match def _select_children(parent_paths, dir_only, follow_symlinks, match): @@ -387,13 +391,14 @@ def stem(self): def with_name(self, name): """Return a new path with the file name changed.""" - if not self.name: - raise ValueError("%r has an empty name" % (self,)) m = self.pathmod if not name or m.sep in name or (m.altsep and m.altsep in name) or name == '.': - raise ValueError("Invalid name %r" % (name)) - return self._from_parsed_parts(self.drive, self.root, - self._tail[:-1] + [name]) + raise ValueError(f"Invalid name {name!r}") + tail = self._tail.copy() + if not tail: + raise ValueError(f"{self!r} has an empty name") + tail[-1] = name + return self._from_parsed_parts(self.drive, self.root, tail) def with_stem(self, stem): """Return a new path with the stem changed.""" @@ -404,21 +409,12 @@ def with_suffix(self, suffix): has no suffix, add given suffix. If the given suffix is an empty string, remove the suffix from the path. """ - m = self.pathmod - if m.sep in suffix or m.altsep and m.altsep in suffix: - raise ValueError("Invalid suffix %r" % (suffix,)) - if suffix and not suffix.startswith('.') or suffix == '.': - raise ValueError("Invalid suffix %r" % (suffix)) - name = self.name - if not name: - raise ValueError("%r has an empty name" % (self,)) - old_suffix = self.suffix - if not old_suffix: - name = name + suffix + if not suffix: + return self.with_name(self.stem) + elif suffix.startswith('.') and len(suffix) > 1: + return self.with_name(self.stem + suffix) else: - name = name[:-len(old_suffix)] + suffix - return self._from_parsed_parts(self.drive, self.root, - self._tail[:-1] + [name]) + raise ValueError(f"Invalid suffix {suffix!r}") def relative_to(self, other, /, *_deprecated, walk_up=False): """Return the relative path to another path identified by the passed @@ -437,7 +433,7 @@ def relative_to(self, other, /, *_deprecated, walk_up=False): other = self.with_segments(other, *_deprecated) elif not isinstance(other, _PurePathBase): other = self.with_segments(other) - for step, path in enumerate([other] + list(other.parents)): + for step, path in enumerate(chain([other], other.parents)): if path == self or path in self.parents: break elif not walk_up: @@ -963,12 +959,12 @@ def read_bytes(self): with self.open(mode='rb') as f: return f.read() - def read_text(self, encoding=None, errors=None): + def read_text(self, encoding=None, errors=None, newline=None): """ Open the file in text mode, read it, and close the file. """ encoding = io.text_encoding(encoding) - with self.open(mode='r', encoding=encoding, errors=errors) as f: + with self.open(mode='r', encoding=encoding, errors=errors, newline=newline) as f: return f.read() def write_bytes(self, data): @@ -1002,7 +998,8 @@ def iterdir(self): def _scandir(self): # Emulate os.scandir(), which returns an object that can be used as a # context manager. This method is called by walk() and glob(). - return contextlib.nullcontext(self.iterdir()) + from contextlib import nullcontext + return nullcontext(self.iterdir()) def _make_child_relpath(self, name): path_str = str(self) @@ -1042,7 +1039,7 @@ def _glob(self, pattern, case_sensitive, follow_symlinks): elif not path_pattern._tail: raise ValueError("Unacceptable pattern: {!r}".format(pattern)) - pattern_parts = list(path_pattern._tail) + pattern_parts = path_pattern._tail.copy() if pattern[-1] in (self.pathmod.sep, self.pathmod.altsep): # GH-65238: pathlib doesn't preserve trailing slash. Add it back. pattern_parts.append('') @@ -1335,13 +1332,13 @@ def rmdir(self): """ self._unsupported("rmdir") - def owner(self): + def owner(self, *, follow_symlinks=True): """ Return the login name of the file owner. """ self._unsupported("owner") - def group(self): + def group(self, *, follow_symlinks=True): """ Return the group name of the file gid. """ @@ -1429,21 +1426,29 @@ def absolute(self): """ if self.is_absolute(): return self - elif self.drive: + if self.root: + drive = os.path.splitroot(os.getcwd())[0] + return self._from_parsed_parts(drive, self.root, self._tail) + if self.drive: # There is a CWD on each drive-letter drive. cwd = os.path.abspath(self.drive) else: cwd = os.getcwd() + if not self._tail: # Fast path for "empty" paths, e.g. Path("."), Path("") or Path(). # We pass only one argument to with_segments() to avoid the cost # of joining, and we exploit the fact that getcwd() returns a # fully-normalized string by storing it in _str. This is used to # implement Path.cwd(). - if not self.root and not self._tail: - result = self.with_segments(cwd) - result._str = cwd - return result - return self.with_segments(cwd, self) + result = self.with_segments(cwd) + result._str = cwd + return result + drive, root, rel = os.path.splitroot(cwd) + if not rel: + return self._from_parsed_parts(drive, root, self._tail) + tail = rel.split(self.pathmod.sep) + tail.extend(self._tail) + return self._from_parsed_parts(drive, root, tail) def resolve(self, strict=False): """ @@ -1454,18 +1459,20 @@ def resolve(self, strict=False): return self.with_segments(os.path.realpath(self, strict=strict)) if pwd: - def owner(self): + def owner(self, *, follow_symlinks=True): """ Return the login name of the file owner. """ - return pwd.getpwuid(self.stat().st_uid).pw_name + uid = self.stat(follow_symlinks=follow_symlinks).st_uid + return pwd.getpwuid(uid).pw_name if grp: - def group(self): + def group(self, *, follow_symlinks=True): """ Return the group name of the file gid. """ - return grp.getgrgid(self.stat().st_gid).gr_name + gid = self.stat(follow_symlinks=follow_symlinks).st_gid + return grp.getgrgid(gid).gr_name if hasattr(os, "readlink"): def readlink(self): diff --git a/Lib/pdb.py b/Lib/pdb.py index ed78d749a47fa8..83b7fefec63636 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -142,8 +142,10 @@ def check(self): print('Error:', self.orig, 'is a directory') sys.exit(1) - # Replace pdb's dir with script's dir in front of module search path. - sys.path[0] = os.path.dirname(self) + # If safe_path(-P) is not set, sys.path[0] is the directory + # of pdb, and we should replace it with the directory of the script + if not sys.flags.safe_path: + sys.path[0] = os.path.dirname(self) @property def filename(self): @@ -205,6 +207,15 @@ def namespace(self): ) +class _PdbInteractiveConsole(code.InteractiveConsole): + def __init__(self, ns, message): + self._message = message + super().__init__(locals=ns, local_exit=True) + + def write(self, data): + self._message(data, end='') + + # Interaction prompt line will separate file and call info from code # text using value of line_prefix string. A newline and arrow may # be to your liking. You can set it once pdb is imported using the @@ -670,8 +681,8 @@ def handle_command_def(self, line): # interface abstraction functions - def message(self, msg): - print(msg, file=self.stdout) + def message(self, msg, end='\n'): + print(msg, end=end, file=self.stdout) def error(self, msg): print('***', msg, file=self.stdout) @@ -1784,7 +1795,9 @@ def do_interact(self, arg): contains all the (global and local) names found in the current scope. """ ns = {**self.curframe.f_globals, **self.curframe_locals} - code.interact("*interactive*", local=ns, local_exit=True) + console = _PdbInteractiveConsole(ns, message=self.message) + console.interact(banner="*pdb interact start*", + exitmsg="*exit from pdb interact command*") def do_alias(self, arg): """alias [name [command]] diff --git a/Lib/platform.py b/Lib/platform.py index 7bb222088d5061..75aa55510858fd 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -118,6 +118,10 @@ import sys import functools import itertools +try: + import _wmi +except ImportError: + _wmi = None ### Globals & Constants @@ -312,24 +316,26 @@ def _syscmd_ver(system='', release='', version='', version = _norm_version(version) return system, release, version -try: - import _wmi -except ImportError: - def _wmi_query(*keys): + +def _wmi_query(table, *keys): + global _wmi + if not _wmi: raise OSError("not supported") -else: - def _wmi_query(table, *keys): - table = { - "OS": "Win32_OperatingSystem", - "CPU": "Win32_Processor", - }[table] + table = { + "OS": "Win32_OperatingSystem", + "CPU": "Win32_Processor", + }[table] + try: data = _wmi.exec_query("SELECT {} FROM {}".format( ",".join(keys), table, )).split("\0") - split_data = (i.partition("=") for i in data) - dict_data = {i[0]: i[2] for i in split_data} - return (dict_data[k] for k in keys) + except OSError: + _wmi = None + raise OSError("not supported") + split_data = (i.partition("=") for i in data) + dict_data = {i[0]: i[2] for i in split_data} + return (dict_data[k] for k in keys) _WIN32_CLIENT_RELEASES = [ diff --git a/Lib/pydoc.py b/Lib/pydoc.py index be41592cc64bad..83c74a75cd1c00 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -201,7 +201,10 @@ def _getargspec(object): try: signature = inspect.signature(object) if signature: - return str(signature) + name = getattr(object, '__name__', '') + # function are always single-line and should not be formatted + max_width = (80 - len(name)) if name != '' else None + return signature.format(max_width=max_width) except (ValueError, TypeError): argspec = getattr(object, '__text_signature__', None) if argspec: diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index 87b0a2d669e5a3..7c1bdc4dff2ec4 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Fri Oct 13 10:51:21 2023 +# Autogenerated by Sphinx on Wed Nov 22 11:44:32 2023 # as part of the release process. topics = {'assert': 'The "assert" statement\n' '**********************\n' @@ -5574,7 +5574,7 @@ ' Create an alias called *name* that executes *command*. The\n' ' *command* must *not* be enclosed in quotes. Replaceable ' 'parameters\n' - ' can be indicated by "%1", "%2", and so on, while "%*" is ' + ' can be indicated by "%1", "%2", … and "%9", while "%*" is ' 'replaced\n' ' by all the parameters. If *command* is omitted, the current ' 'alias\n' @@ -14650,10 +14650,16 @@ ' objects (for example when catching an exception and storing its\n' ' traceback for later use).\n' '\n' - ' "RuntimeError" is raised if the frame is currently executing.\n' + ' "RuntimeError" is raised if the frame is currently executing or\n' + ' suspended.\n' '\n' ' New in version 3.4.\n' '\n' + ' Changed in version 3.13: Attempting to clear a suspended frame\n' + ' raises "RuntimeError" (as has always been the case for ' + 'executing\n' + ' frames).\n' + '\n' '\n' 'Traceback objects\n' '-----------------\n' @@ -15216,21 +15222,23 @@ '\n' 'Keys views are set-like since their entries are unique and ' '*hashable*.\n' - 'If all values are hashable, so that "(key, value)" pairs are ' - 'unique\n' - 'and hashable, then the items view is also set-like. (Values ' - 'views are\n' - 'not treated as set-like since the entries are generally not ' - 'unique.)\n' - 'For set-like views, all of the operations defined for the ' - 'abstract\n' - 'base class "collections.abc.Set" are available (for example, ' - '"==",\n' - '"<", or "^"). While using set operators, set-like views ' - 'accept any\n' - 'iterable as the other operand, unlike sets which only accept ' - 'sets as\n' - 'the input.\n' + 'Items views also have set-like operations since the (key, ' + 'value) pairs\n' + 'are unique and the keys are hashable. If all values in an ' + 'items view\n' + 'are hashable as well, then the items view can interoperate ' + 'with other\n' + 'sets. (Values views are not treated as set-like since the ' + 'entries are\n' + 'generally not unique.) For set-like views, all of the ' + 'operations\n' + 'defined for the abstract base class "collections.abc.Set" ' + 'are\n' + 'available (for example, "==", "<", or "^"). While using ' + 'set\n' + 'operators, set-like views accept any iterable as the other ' + 'operand,\n' + 'unlike sets which only accept sets as the input.\n' '\n' 'An example of dictionary view usage:\n' '\n' diff --git a/Lib/shutil.py b/Lib/shutil.py index 0fed0117a63234..dc3aac3e07f910 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -590,23 +590,21 @@ def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, dirs_exist_ok=dirs_exist_ok) if hasattr(os.stat_result, 'st_file_attributes'): - def _rmtree_islink(path): - try: - st = os.lstat(path) - return (stat.S_ISLNK(st.st_mode) or - (st.st_file_attributes & stat.FILE_ATTRIBUTE_REPARSE_POINT - and st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT)) - except OSError: - return False + def _rmtree_islink(st): + return (stat.S_ISLNK(st.st_mode) or + (st.st_file_attributes & stat.FILE_ATTRIBUTE_REPARSE_POINT + and st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT)) else: - def _rmtree_islink(path): - return os.path.islink(path) + def _rmtree_islink(st): + return stat.S_ISLNK(st.st_mode) # version vulnerable to race conditions def _rmtree_unsafe(path, onexc): try: with os.scandir(path) as scandir_it: entries = list(scandir_it) + except FileNotFoundError: + return except OSError as err: onexc(os.scandir, path, err) entries = [] @@ -614,6 +612,8 @@ def _rmtree_unsafe(path, onexc): fullname = entry.path try: is_dir = entry.is_dir(follow_symlinks=False) + except FileNotFoundError: + continue except OSError: is_dir = False @@ -624,6 +624,8 @@ def _rmtree_unsafe(path, onexc): # a directory with a symlink after the call to # os.scandir or entry.is_dir above. raise OSError("Cannot call rmtree on a symbolic link") + except FileNotFoundError: + continue except OSError as err: onexc(os.path.islink, fullname, err) continue @@ -631,10 +633,14 @@ def _rmtree_unsafe(path, onexc): else: try: os.unlink(fullname) + except FileNotFoundError: + continue except OSError as err: onexc(os.unlink, fullname, err) try: os.rmdir(path) + except FileNotFoundError: + pass except OSError as err: onexc(os.rmdir, path, err) @@ -643,6 +649,8 @@ def _rmtree_safe_fd(topfd, path, onexc): try: with os.scandir(topfd) as scandir_it: entries = list(scandir_it) + except FileNotFoundError: + return except OSError as err: err.filename = path onexc(os.scandir, path, err) @@ -651,6 +659,8 @@ def _rmtree_safe_fd(topfd, path, onexc): fullname = os.path.join(path, entry.name) try: is_dir = entry.is_dir(follow_symlinks=False) + except FileNotFoundError: + continue except OSError: is_dir = False else: @@ -658,6 +668,8 @@ def _rmtree_safe_fd(topfd, path, onexc): try: orig_st = entry.stat(follow_symlinks=False) is_dir = stat.S_ISDIR(orig_st.st_mode) + except FileNotFoundError: + continue except OSError as err: onexc(os.lstat, fullname, err) continue @@ -665,6 +677,8 @@ def _rmtree_safe_fd(topfd, path, onexc): try: dirfd = os.open(entry.name, os.O_RDONLY, dir_fd=topfd) dirfd_closed = False + except FileNotFoundError: + continue except OSError as err: onexc(os.open, fullname, err) else: @@ -673,8 +687,15 @@ def _rmtree_safe_fd(topfd, path, onexc): _rmtree_safe_fd(dirfd, fullname, onexc) try: os.close(dirfd) + except OSError as err: + # close() should not be retried after an error. dirfd_closed = True + onexc(os.close, fullname, err) + dirfd_closed = True + try: os.rmdir(entry.name, dir_fd=topfd) + except FileNotFoundError: + continue except OSError as err: onexc(os.rmdir, fullname, err) else: @@ -688,10 +709,15 @@ def _rmtree_safe_fd(topfd, path, onexc): onexc(os.path.islink, fullname, err) finally: if not dirfd_closed: - os.close(dirfd) + try: + os.close(dirfd) + except OSError as err: + onexc(os.close, fullname, err) else: try: os.unlink(entry.name, dir_fd=topfd) + except FileNotFoundError: + continue except OSError as err: onexc(os.unlink, fullname, err) @@ -721,11 +747,6 @@ def rmtree(path, ignore_errors=False, onerror=None, *, onexc=None, dir_fd=None): If both onerror and onexc are set, onerror is ignored and onexc is used. """ - if onerror is not None: - import warnings - warnings.warn("onerror argument is deprecated, use onexc instead", - DeprecationWarning, stacklevel=2) - sys.audit("shutil.rmtree", path, dir_fd) if ignore_errors: def onexc(*args): @@ -755,13 +776,13 @@ def onexc(*args): # lstat()/open()/fstat() trick. try: orig_st = os.lstat(path, dir_fd=dir_fd) - except Exception as err: + except OSError as err: onexc(os.lstat, path, err) return try: fd = os.open(path, os.O_RDONLY, dir_fd=dir_fd) fd_closed = False - except Exception as err: + except OSError as err: onexc(os.open, path, err) return try: @@ -769,7 +790,12 @@ def onexc(*args): _rmtree_safe_fd(fd, path, onexc) try: os.close(fd) + except OSError as err: + # close() should not be retried after an error. fd_closed = True + onexc(os.close, path, err) + fd_closed = True + try: os.rmdir(path, dir_fd=dir_fd) except OSError as err: onexc(os.rmdir, path, err) @@ -781,12 +807,20 @@ def onexc(*args): onexc(os.path.islink, path, err) finally: if not fd_closed: - os.close(fd) + try: + os.close(fd) + except OSError as err: + onexc(os.close, path, err) else: if dir_fd is not None: raise NotImplementedError("dir_fd unavailable on this platform") try: - if _rmtree_islink(path): + st = os.lstat(path) + except OSError as err: + onexc(os.lstat, path, err) + return + try: + if _rmtree_islink(st): # symlinks to directories are forbidden, see bug #1669 raise OSError("Cannot call rmtree on a symbolic link") except OSError as err: diff --git a/Lib/site.py b/Lib/site.py index 672fa7b000ad02..2517b7e5f1d22a 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -444,8 +444,7 @@ def register_readline(): # Reading the initialization (config) file may not be enough to set a # completion key, so we set one first and then read the file. - readline_doc = getattr(readline, '__doc__', '') - if readline_doc is not None and 'libedit' in readline_doc: + if readline.backend == 'editline': readline.parse_and_bind('bind ^I rl_complete') else: readline.parse_and_bind('tab: complete') diff --git a/Lib/ssl.py b/Lib/ssl.py index 62e55857141dfc..d01484964b6895 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -116,7 +116,7 @@ from _ssl import ( HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_SSLv2, HAS_SSLv3, HAS_TLSv1, - HAS_TLSv1_1, HAS_TLSv1_2, HAS_TLSv1_3 + HAS_TLSv1_1, HAS_TLSv1_2, HAS_TLSv1_3, HAS_PSK ) from _ssl import _DEFAULT_CIPHERS, _OPENSSL_API_VERSION @@ -1270,10 +1270,14 @@ def recv(self, buflen=1024, flags=0): def recv_into(self, buffer, nbytes=None, flags=0): self._checkClosed() - if buffer and (nbytes is None): - nbytes = len(buffer) - elif nbytes is None: - nbytes = 1024 + if nbytes is None: + if buffer is not None: + with memoryview(buffer) as view: + nbytes = view.nbytes + if not nbytes: + nbytes = 1024 + else: + nbytes = 1024 if self._sslobj is not None: if flags != 0: raise ValueError( diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 6df5dd551ea67e..d6edd1a9807d1b 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -74,8 +74,8 @@ else: _mswindows = True -# wasm32-emscripten and wasm32-wasi do not support processes -_can_fork_exec = sys.platform not in {"emscripten", "wasi"} +# some platforms do not support subprocesses +_can_fork_exec = sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos"} if _mswindows: import _winapi @@ -103,18 +103,22 @@ if _can_fork_exec: from _posixsubprocess import fork_exec as _fork_exec # used in methods that are called by __del__ - _waitpid = os.waitpid - _waitstatus_to_exitcode = os.waitstatus_to_exitcode - _WIFSTOPPED = os.WIFSTOPPED - _WSTOPSIG = os.WSTOPSIG - _WNOHANG = os.WNOHANG + class _del_safe: + waitpid = os.waitpid + waitstatus_to_exitcode = os.waitstatus_to_exitcode + WIFSTOPPED = os.WIFSTOPPED + WSTOPSIG = os.WSTOPSIG + WNOHANG = os.WNOHANG + ECHILD = errno.ECHILD else: - _fork_exec = None - _waitpid = None - _waitstatus_to_exitcode = None - _WIFSTOPPED = None - _WSTOPSIG = None - _WNOHANG = None + class _del_safe: + waitpid = None + waitstatus_to_exitcode = None + WIFSTOPPED = None + WSTOPSIG = None + WNOHANG = None + ECHILD = errno.ECHILD + import select import selectors @@ -1951,20 +1955,16 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, raise child_exception_type(err_msg) - def _handle_exitstatus(self, sts, - _waitstatus_to_exitcode=_waitstatus_to_exitcode, - _WIFSTOPPED=_WIFSTOPPED, - _WSTOPSIG=_WSTOPSIG): + def _handle_exitstatus(self, sts, _del_safe=_del_safe): """All callers to this function MUST hold self._waitpid_lock.""" # This method is called (indirectly) by __del__, so it cannot # refer to anything outside of its local scope. - if _WIFSTOPPED(sts): - self.returncode = -_WSTOPSIG(sts) + if _del_safe.WIFSTOPPED(sts): + self.returncode = -_del_safe.WSTOPSIG(sts) else: - self.returncode = _waitstatus_to_exitcode(sts) + self.returncode = _del_safe.waitstatus_to_exitcode(sts) - def _internal_poll(self, _deadstate=None, _waitpid=_waitpid, - _WNOHANG=_WNOHANG, _ECHILD=errno.ECHILD): + def _internal_poll(self, _deadstate=None, _del_safe=_del_safe): """Check if child process has terminated. Returns returncode attribute. @@ -1980,13 +1980,13 @@ def _internal_poll(self, _deadstate=None, _waitpid=_waitpid, try: if self.returncode is not None: return self.returncode # Another thread waited. - pid, sts = _waitpid(self.pid, _WNOHANG) + pid, sts = _del_safe.waitpid(self.pid, _del_safe.WNOHANG) if pid == self.pid: self._handle_exitstatus(sts) except OSError as e: if _deadstate is not None: self.returncode = _deadstate - elif e.errno == _ECHILD: + elif e.errno == _del_safe.ECHILD: # This happens if SIGCLD is set to be ignored or # waiting for child processes has otherwise been # disabled for our process. This child is dead, we diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index 68d30c0f9e618f..2a7fa45be079de 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -348,7 +348,7 @@ def _init_non_posix(vars): vars['BINLIBDEST'] = get_path('platstdlib') vars['INCLUDEPY'] = get_path('include') - # Add EXT_SUFFIX, SOABI, and Py_NOGIL + # Add EXT_SUFFIX, SOABI, and Py_GIL_DISABLED vars.update(_sysconfig.config_vars()) vars['LIBDIR'] = _safe_realpath(os.path.join(get_config_var('installed_base'), 'libs')) diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 2b4f4313247128..cbfc172a789b25 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -269,6 +269,22 @@ def _mkstemp_inner(dir, pre, suf, flags, output_type): raise FileExistsError(_errno.EEXIST, "No usable temporary file name found") +def _dont_follow_symlinks(func, path, *args): + # Pass follow_symlinks=False, unless not supported on this platform. + if func in _os.supports_follow_symlinks: + func(path, *args, follow_symlinks=False) + elif _os.name == 'nt' or not _os.path.islink(path): + func(path, *args) + +def _resetperms(path): + try: + chflags = _os.chflags + except AttributeError: + pass + else: + _dont_follow_symlinks(chflags, path, 0) + _dont_follow_symlinks(_os.chmod, path, 0o700) + # User visible interfaces. @@ -872,26 +888,37 @@ def __init__(self, suffix=None, prefix=None, dir=None, ignore_errors=self._ignore_cleanup_errors, delete=self._delete) @classmethod - def _rmtree(cls, name, ignore_errors=False): + def _rmtree(cls, name, ignore_errors=False, repeated=False): def onexc(func, path, exc): if isinstance(exc, PermissionError): - def resetperms(path): - try: - _os.chflags(path, 0) - except AttributeError: - pass - _os.chmod(path, 0o700) + if repeated and path == name: + if ignore_errors: + return + raise try: if path != name: - resetperms(_os.path.dirname(path)) - resetperms(path) + _resetperms(_os.path.dirname(path)) + _resetperms(path) try: _os.unlink(path) - # PermissionError is raised on FreeBSD for directories - except (IsADirectoryError, PermissionError): + except IsADirectoryError: cls._rmtree(path, ignore_errors=ignore_errors) + except PermissionError: + # The PermissionError handler was originally added for + # FreeBSD in directories, but it seems that it is raised + # on Windows too. + # bpo-43153: Calling _rmtree again may + # raise NotADirectoryError and mask the PermissionError. + # So we must re-raise the current PermissionError if + # path is not a directory. + if not _os.path.isdir(path) or _os.path.isjunction(path): + if ignore_errors: + return + raise + cls._rmtree(path, ignore_errors=ignore_errors, + repeated=(path == name)) except FileNotFoundError: pass elif isinstance(exc, FileNotFoundError): diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index ec003d8dc4314d..a94eb6c0ae4b8e 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -4455,6 +4455,59 @@ def test_shared_memory_cleaned_after_process_termination(self): "resource_tracker: There appear to be 1 leaked " "shared_memory objects to clean up at shutdown", err) + @unittest.skipIf(os.name != "posix", "resource_tracker is posix only") + def test_shared_memory_untracking(self): + # gh-82300: When a separate Python process accesses shared memory + # with track=False, it must not cause the memory to be deleted + # when terminating. + cmd = '''if 1: + import sys + from multiprocessing.shared_memory import SharedMemory + mem = SharedMemory(create=False, name=sys.argv[1], track=False) + mem.close() + ''' + mem = shared_memory.SharedMemory(create=True, size=10) + # The resource tracker shares pipes with the subprocess, and so + # err existing means that the tracker process has terminated now. + try: + rc, out, err = script_helper.assert_python_ok("-c", cmd, mem.name) + self.assertNotIn(b"resource_tracker", err) + self.assertEqual(rc, 0) + mem2 = shared_memory.SharedMemory(create=False, name=mem.name) + mem2.close() + finally: + try: + mem.unlink() + except OSError: + pass + mem.close() + + @unittest.skipIf(os.name != "posix", "resource_tracker is posix only") + def test_shared_memory_tracking(self): + # gh-82300: When a separate Python process accesses shared memory + # with track=True, it must cause the memory to be deleted when + # terminating. + cmd = '''if 1: + import sys + from multiprocessing.shared_memory import SharedMemory + mem = SharedMemory(create=False, name=sys.argv[1], track=True) + mem.close() + ''' + mem = shared_memory.SharedMemory(create=True, size=10) + try: + rc, out, err = script_helper.assert_python_ok("-c", cmd, mem.name) + self.assertEqual(rc, 0) + self.assertIn( + b"resource_tracker: There appear to be 1 leaked " + b"shared_memory objects to clean up at shutdown", err) + finally: + try: + mem.unlink() + except OSError: + pass + resource_tracker.unregister(mem._name, "shared_memory") + mem.close() + # # Test to verify that `Finalize` works. # diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index da730c5b2495c8..ee4a4228fd28be 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -4951,6 +4951,27 @@ static PyObject * Test_meth_coexist_impl(TestObj *self) /*[clinic end generated code: output=808a293d0cd27439 input=2a1d75b5e6fec6dd]*/ +/*[clinic input] +@getter +Test.property +[clinic start generated code]*/ + +#define TEST_PROPERTY_GETTERDEF \ + {"property", (getter)Test_property_get, NULL, NULL}, + +static PyObject * +Test_property_get_impl(TestObj *self); + +static PyObject * +Test_property_get(TestObj *self, void *Py_UNUSED(context)) +{ + return Test_property_get_impl(self); +} + +static PyObject * +Test_property_get_impl(TestObj *self) +/*[clinic end generated code: output=892b6fb351ff85fd input=2d92b3449fbc7d2b]*/ + /*[clinic input] output push @@ -5542,3 +5563,173 @@ test_critical_section_meth_o(PyObject *module, PyObject *arg) static PyObject * test_critical_section_meth_o_impl(PyObject *module, PyObject *a) /*[clinic end generated code: output=7a9d7420802d1202 input=376533f51eceb6c3]*/ + +/*[clinic input] +@critical_section a +test_critical_section_object + a: object(subclass_of="&PyUnicode_Type") + / +test_critical_section_object +[clinic start generated code]*/ + +PyDoc_STRVAR(test_critical_section_object__doc__, +"test_critical_section_object($module, a, /)\n" +"--\n" +"\n" +"test_critical_section_object"); + +#define TEST_CRITICAL_SECTION_OBJECT_METHODDEF \ + {"test_critical_section_object", (PyCFunction)test_critical_section_object, METH_O, test_critical_section_object__doc__}, + +static PyObject * +test_critical_section_object_impl(PyObject *module, PyObject *a); + +static PyObject * +test_critical_section_object(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *a; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("test_critical_section_object", "argument", "str", arg); + goto exit; + } + a = arg; + Py_BEGIN_CRITICAL_SECTION(a); + return_value = test_critical_section_object_impl(module, a); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +static PyObject * +test_critical_section_object_impl(PyObject *module, PyObject *a) +/*[clinic end generated code: output=ec06df92232b0fb5 input=6f67f91b523c875f]*/ + +PyDoc_STRVAR(test_critical_section_object__doc__, +"test_critical_section_object($module, a, /)\n" +"--\n" +"\n" +"test_critical_section_object"); + +#define TEST_CRITICAL_SECTION_OBJECT_METHODDEF \ + {"test_critical_section_object", (PyCFunction)test_critical_section_object, METH_O, test_critical_section_object__doc__}, + +static PyObject * +test_critical_section_object_impl(PyObject *module, PyObject *a); + +static PyObject * +test_critical_section_object(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *a; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("test_critical_section_object", "argument", "str", arg); + goto exit; + } + a = arg; + Py_BEGIN_CRITICAL_SECTION(a); + return_value = test_critical_section_object_impl(module, a); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +/*[clinic input] +@critical_section a b +test_critical_section_object2 + a: object(subclass_of="&PyUnicode_Type") + b: object(subclass_of="&PyUnicode_Type") + / +test_critical_section_object2 +[clinic start generated code]*/ + +PyDoc_STRVAR(test_critical_section_object2__doc__, +"test_critical_section_object2($module, a, b, /)\n" +"--\n" +"\n" +"test_critical_section_object2"); + +#define TEST_CRITICAL_SECTION_OBJECT2_METHODDEF \ + {"test_critical_section_object2", _PyCFunction_CAST(test_critical_section_object2), METH_FASTCALL, test_critical_section_object2__doc__}, + +static PyObject * +test_critical_section_object2_impl(PyObject *module, PyObject *a, + PyObject *b); + +static PyObject * +test_critical_section_object2(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + + if (!_PyArg_CheckPositional("test_critical_section_object2", nargs, 2, 2)) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("test_critical_section_object2", "argument 1", "str", args[0]); + goto exit; + } + a = args[0]; + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("test_critical_section_object2", "argument 2", "str", args[1]); + goto exit; + } + b = args[1]; + Py_BEGIN_CRITICAL_SECTION2(a, b); + return_value = test_critical_section_object2_impl(module, a, b); + Py_END_CRITICAL_SECTION2(); + +exit: + return return_value; +} + +static PyObject * +test_critical_section_object2_impl(PyObject *module, PyObject *a, + PyObject *b) +/*[clinic end generated code: output=d73a1657c18df17a input=638824e41419a466]*/ + +PyDoc_STRVAR(test_critical_section_object2__doc__, +"test_critical_section_object2($module, a, b, /)\n" +"--\n" +"\n" +"test_critical_section_object2"); + +#define TEST_CRITICAL_SECTION_OBJECT2_METHODDEF \ + {"test_critical_section_object2", _PyCFunction_CAST(test_critical_section_object2), METH_FASTCALL, test_critical_section_object2__doc__}, + +static PyObject * +test_critical_section_object2_impl(PyObject *module, PyObject *a, + PyObject *b); + +static PyObject * +test_critical_section_object2(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + + if (!_PyArg_CheckPositional("test_critical_section_object2", nargs, 2, 2)) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("test_critical_section_object2", "argument 1", "str", args[0]); + goto exit; + } + a = args[0]; + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("test_critical_section_object2", "argument 2", "str", args[1]); + goto exit; + } + b = args[1]; + Py_BEGIN_CRITICAL_SECTION2(a, b); + return_value = test_critical_section_object2_impl(module, a, b); + Py_END_CRITICAL_SECTION2(); + +exit: + return return_value; +} diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index a5f02d6335f58f..0053bce4292f64 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -3,7 +3,7 @@ import shlex import sys from test.support import os_helper, Py_DEBUG -from .utils import ALL_RESOURCES, RESOURCE_NAMES +from .utils import ALL_RESOURCES, RESOURCE_NAMES, TestFilter USAGE = """\ @@ -161,7 +161,7 @@ def __init__(self, **kwargs) -> None: self.forever = False self.header = False self.failfast = False - self.match_tests = [] + self.match_tests: TestFilter = [] self.pgo = False self.pgo_extended = False self.worker_json = None diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 86428945a6def2..7ca1b1cb65ae40 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -295,7 +295,9 @@ def run_test( namespace = dict(locals()) tracer.runctx(cmd, globals=globals(), locals=namespace) result = namespace['result'] - result.covered_lines = list(tracer.counts) + # Mypy doesn't know about this attribute yet, + # but it will do soon: https://github.com/python/typeshed/pull/11091 + result.covered_lines = list(tracer.counts) # type: ignore[attr-defined] else: result = run_single_test(test_name, runtests) @@ -309,7 +311,7 @@ def run_tests_sequentially(self, runtests) -> None: else: tracer = None - save_modules = sys.modules.keys() + save_modules = set(sys.modules) jobs = runtests.get_jobs() if jobs is not None: @@ -333,10 +335,18 @@ def run_tests_sequentially(self, runtests) -> None: result = self.run_test(test_name, runtests, tracer) - # Unload the newly imported modules (best effort finalization) - for module in sys.modules.keys(): - if module not in save_modules and module.startswith("test."): - support.unload(module) + # Unload the newly imported test modules (best effort finalization) + new_modules = [module for module in sys.modules + if module not in save_modules and + module.startswith(("test.", "test_"))] + for module in new_modules: + sys.modules.pop(module, None) + # Remove the attribute of the parent module. + parent, _, name = module.rpartition('.') + try: + delattr(sys.modules[parent], name) + except (KeyError, AttributeError): + pass if result.must_stop(self.fail_fast, self.fail_env_changed): break @@ -371,7 +381,8 @@ def finalize_tests(self, coverage: trace.CoverageResults | None) -> None: os.unlink(self.next_single_filename) if coverage is not None: - coverage.write_results(show_missing=True, summary=True, + # uses a new-in-Python 3.13 keyword argument that mypy doesn't know about yet: + coverage.write_results(show_missing=True, summary=True, # type: ignore[call-arg] coverdir=self.coverage_dir, ignore_missing_files=True) @@ -420,7 +431,6 @@ def create_run_tests(self, tests: TestTuple): python_cmd=self.python_cmd, randomize=self.randomize, random_seed=self.random_seed, - json_file=None, ) def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int: @@ -432,7 +442,10 @@ def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int: if self.num_workers < 0: # Use all CPUs + 2 extra worker processes for tests # that like to sleep - self.num_workers = (os.process_cpu_count() or 1) + 2 + # + # os.process.cpu_count() is new in Python 3.13; + # mypy doesn't know about it yet + self.num_workers = (os.process_cpu_count() or 1) + 2 # type: ignore[attr-defined] # For a partial run, we do not need to clutter the output. if (self.want_header diff --git a/Lib/test/libregrtest/mypy.ini b/Lib/test/libregrtest/mypy.ini index fefc347728a701..22c7c7a9acef14 100644 --- a/Lib/test/libregrtest/mypy.ini +++ b/Lib/test/libregrtest/mypy.ini @@ -5,7 +5,7 @@ [mypy] files = Lib/test/libregrtest explicit_package_bases = True -python_version = 3.11 +python_version = 3.12 platform = linux pretty = True @@ -25,7 +25,7 @@ warn_return_any = False disable_error_code = return # Enable --strict-optional for these ASAP: -[mypy-Lib.test.libregrtest.main.*,Lib.test.libregrtest.run_workers.*,Lib.test.libregrtest.worker.*,Lib.test.libregrtest.single.*,Lib.test.libregrtest.results.*,Lib.test.libregrtest.utils.*] +[mypy-Lib.test.libregrtest.main.*,Lib.test.libregrtest.run_workers.*] strict_optional = False # Various internal modules that typeshed deliberately doesn't have stubs for: diff --git a/Lib/test/libregrtest/refleak.py b/Lib/test/libregrtest/refleak.py index ada1a65b867ee6..5836a8421cb42d 100644 --- a/Lib/test/libregrtest/refleak.py +++ b/Lib/test/libregrtest/refleak.py @@ -52,7 +52,8 @@ def runtest_refleak(test_name, test_func, except ImportError: zdc = None # Run unmodified on platforms without zipimport support else: - zdc = zipimport._zip_directory_cache.copy() + # private attribute that mypy doesn't know about: + zdc = zipimport._zip_directory_cache.copy() # type: ignore[attr-defined] abcs = {} for abc in [getattr(collections.abc, a) for a in collections.abc.__all__]: if not isabstract(abc): diff --git a/Lib/test/libregrtest/results.py b/Lib/test/libregrtest/results.py index 71aaef3ae9ae61..a41ea8aba028c3 100644 --- a/Lib/test/libregrtest/results.py +++ b/Lib/test/libregrtest/results.py @@ -34,7 +34,7 @@ def __init__(self): self.test_times: list[tuple[float, TestName]] = [] self.stats = TestStats() # used by --junit-xml - self.testsuite_xml: list[str] = [] + self.testsuite_xml: list = [] # used by -T with -j self.covered_lines: set[Location] = set() @@ -117,6 +117,8 @@ def accumulate_result(self, result: TestResult, runtests: RunTests): self.worker_bug = True if result.has_meaningful_duration() and not rerun: + if result.duration is None: + raise ValueError("result.duration is None") self.test_times.append((result.duration, test_name)) if result.stats is not None: self.stats.accumulate(result.stats) diff --git a/Lib/test/libregrtest/run_workers.py b/Lib/test/libregrtest/run_workers.py index 99c2cf34d206d0..18a0342f0611cf 100644 --- a/Lib/test/libregrtest/run_workers.py +++ b/Lib/test/libregrtest/run_workers.py @@ -10,7 +10,7 @@ import threading import time import traceback -from typing import Literal, TextIO +from typing import Any, Literal, TextIO from test import support from test.support import os_helper, MS_WINDOWS @@ -18,7 +18,7 @@ from .logger import Logger from .result import TestResult, State from .results import TestResults -from .runtests import RunTests, JsonFile, JsonFileType +from .runtests import RunTests, WorkerRunTests, JsonFile, JsonFileType from .single import PROGRESS_MIN_TIME from .utils import ( StrPath, TestName, @@ -162,7 +162,7 @@ def stop(self) -> None: self._stopped = True self._kill() - def _run_process(self, runtests: RunTests, output_fd: int, + def _run_process(self, runtests: WorkerRunTests, output_fd: int, tmp_dir: StrPath | None = None) -> int | None: popen = create_worker_process(runtests, output_fd, tmp_dir) self._popen = popen @@ -243,34 +243,34 @@ def create_json_file(self, stack: contextlib.ExitStack) -> tuple[JsonFile, TextI json_fd = json_tmpfile.fileno() if MS_WINDOWS: - json_handle = msvcrt.get_osfhandle(json_fd) + # The msvcrt module is only available on Windows; + # we run mypy with `--platform=linux` in CI + json_handle: int = msvcrt.get_osfhandle(json_fd) # type: ignore[attr-defined] json_file = JsonFile(json_handle, JsonFileType.WINDOWS_HANDLE) else: json_file = JsonFile(json_fd, JsonFileType.UNIX_FD) return (json_file, json_tmpfile) - def create_worker_runtests(self, test_name: TestName, json_file: JsonFile) -> RunTests: - """Create the worker RunTests.""" - + def create_worker_runtests(self, test_name: TestName, json_file: JsonFile) -> WorkerRunTests: tests = (test_name,) if self.runtests.rerun: match_tests = self.runtests.get_match_tests(test_name) else: match_tests = None - kwargs = {} + kwargs: dict[str, Any] = {} if match_tests: kwargs['match_tests'] = [(test, True) for test in match_tests] if self.runtests.output_on_failure: kwargs['verbose'] = True kwargs['output_on_failure'] = False - return self.runtests.copy( + return self.runtests.create_worker_runtests( tests=tests, json_file=json_file, **kwargs) - def run_tmp_files(self, worker_runtests: RunTests, + def run_tmp_files(self, worker_runtests: WorkerRunTests, stdout_fd: int) -> tuple[int | None, list[StrPath]]: # gh-93353: Check for leaked temporary files in the parent process, # since the deletion of temporary files can happen late during @@ -345,6 +345,7 @@ def _runtest(self, test_name: TestName) -> MultiprocessResult: json_file, json_tmpfile = self.create_json_file(stack) worker_runtests = self.create_worker_runtests(test_name, json_file) + retcode: str | int | None retcode, tmp_files = self.run_tmp_files(worker_runtests, stdout_file.fileno()) diff --git a/Lib/test/libregrtest/runtests.py b/Lib/test/libregrtest/runtests.py index ac47c07f8d4341..edd72276320e41 100644 --- a/Lib/test/libregrtest/runtests.py +++ b/Lib/test/libregrtest/runtests.py @@ -33,7 +33,8 @@ def configure_subprocess(self, popen_kwargs: dict) -> None: popen_kwargs['pass_fds'] = [self.file] case JsonFileType.WINDOWS_HANDLE: # Windows handle - startupinfo = subprocess.STARTUPINFO() + # We run mypy with `--platform=linux` so it complains about this: + startupinfo = subprocess.STARTUPINFO() # type: ignore[attr-defined] startupinfo.lpAttributeList = {"handle_list": [self.file]} popen_kwargs['startupinfo'] = startupinfo @@ -92,13 +93,17 @@ class RunTests: python_cmd: tuple[str, ...] | None randomize: bool random_seed: int | str - json_file: JsonFile | None - def copy(self, **override): + def copy(self, **override) -> 'RunTests': state = dataclasses.asdict(self) state.update(override) return RunTests(**state) + def create_worker_runtests(self, **override): + state = dataclasses.asdict(self) + state.update(override) + return WorkerRunTests(**state) + def get_match_tests(self, test_name) -> FilterTuple | None: if self.match_tests_dict is not None: return self.match_tests_dict.get(test_name, None) @@ -119,13 +124,6 @@ def iter_tests(self): else: yield from self.tests - def as_json(self) -> StrJSON: - return json.dumps(self, cls=_EncodeRunTests) - - @staticmethod - def from_json(worker_json: StrJSON) -> 'RunTests': - return json.loads(worker_json, object_hook=_decode_runtests) - def json_file_use_stdout(self) -> bool: # Use STDOUT in two cases: # @@ -140,9 +138,21 @@ def json_file_use_stdout(self) -> bool: ) +@dataclasses.dataclass(slots=True, frozen=True) +class WorkerRunTests(RunTests): + json_file: JsonFile + + def as_json(self) -> StrJSON: + return json.dumps(self, cls=_EncodeRunTests) + + @staticmethod + def from_json(worker_json: StrJSON) -> 'WorkerRunTests': + return json.loads(worker_json, object_hook=_decode_runtests) + + class _EncodeRunTests(json.JSONEncoder): def default(self, o: Any) -> dict[str, Any]: - if isinstance(o, RunTests): + if isinstance(o, WorkerRunTests): result = dataclasses.asdict(o) result["__runtests__"] = True return result @@ -157,6 +167,6 @@ def _decode_runtests(data: dict[str, Any]) -> RunTests | dict[str, Any]: data['hunt_refleak'] = HuntRefleak(**data['hunt_refleak']) if data['json_file']: data['json_file'] = JsonFile(**data['json_file']) - return RunTests(**data) + return WorkerRunTests(**data) else: return data diff --git a/Lib/test/libregrtest/setup.py b/Lib/test/libregrtest/setup.py index 97edba9f87d7f9..9e9741493e9a5b 100644 --- a/Lib/test/libregrtest/setup.py +++ b/Lib/test/libregrtest/setup.py @@ -124,7 +124,8 @@ def setup_tests(runtests: RunTests): support.LONG_TIMEOUT = min(support.LONG_TIMEOUT, timeout) if runtests.hunt_refleak: - unittest.BaseTestSuite._cleanup = False + # private attribute that mypy doesn't know about: + unittest.BaseTestSuite._cleanup = False # type: ignore[attr-defined] if runtests.gc_threshold is not None: gc.set_threshold(runtests.gc_threshold) diff --git a/Lib/test/libregrtest/single.py b/Lib/test/libregrtest/single.py index 5c7bc7d40fb394..235029d8620ff5 100644 --- a/Lib/test/libregrtest/single.py +++ b/Lib/test/libregrtest/single.py @@ -122,10 +122,6 @@ def _load_run_test(result: TestResult, runtests: RunTests) -> None: # Load the test module and run the tests. test_name = result.test_name module_name = abs_module_name(test_name, runtests.test_dir) - - # Remove the module from sys.module to reload it if it was already imported - sys.modules.pop(module_name, None) - test_mod = importlib.import_module(module_name) if hasattr(test_mod, "test_main"): @@ -237,11 +233,11 @@ def _runtest(result: TestResult, runtests: RunTests) -> None: output_on_failure = runtests.output_on_failure timeout = runtests.timeout - use_timeout = ( - timeout is not None and threading_helper.can_start_thread - ) - if use_timeout: + if timeout is not None and threading_helper.can_start_thread: + use_timeout = True faulthandler.dump_traceback_later(timeout, exit=True) + else: + use_timeout = False try: setup_tests(runtests) diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index bd4dce3400f0a9..26481e71221ade 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -12,7 +12,7 @@ import sysconfig import tempfile import textwrap -from collections.abc import Callable +from collections.abc import Callable, Iterable from test import support from test.support import os_helper @@ -290,8 +290,8 @@ def get_build_info(): build = [] # --disable-gil - if sysconfig.get_config_var('Py_NOGIL'): - build.append("nogil") + if sysconfig.get_config_var('Py_GIL_DISABLED'): + build.append("free_threading") if hasattr(sys, 'gettotalrefcount'): # --with-pydebug @@ -377,10 +377,19 @@ def get_temp_dir(tmp_dir: StrPath | None = None) -> StrPath: # Python out of the source tree, especially when the # source tree is read only. tmp_dir = sysconfig.get_config_var('srcdir') + if not tmp_dir: + raise RuntimeError( + "Could not determine the correct value for tmp_dir" + ) tmp_dir = os.path.join(tmp_dir, 'build') else: # WASI platform tmp_dir = sysconfig.get_config_var('projectbase') + if not tmp_dir: + raise RuntimeError( + "sysconfig.get_config_var('projectbase') " + f"unexpectedly returned {tmp_dir!r} on WASI" + ) tmp_dir = os.path.join(tmp_dir, 'build') # When get_temp_dir() is called in a worker process, @@ -547,7 +556,7 @@ def is_cross_compiled(): return ('_PYTHON_HOST_PLATFORM' in os.environ) -def format_resources(use_resources: tuple[str, ...]): +def format_resources(use_resources: Iterable[str]): use_resources = set(use_resources) all_resources = set(ALL_RESOURCES) @@ -580,9 +589,10 @@ def display_header(use_resources: tuple[str, ...], print("== Python build:", ' '.join(get_build_info())) print("== cwd:", os.getcwd()) - cpu_count = os.cpu_count() + cpu_count: object = os.cpu_count() if cpu_count: - process_cpu_count = os.process_cpu_count() + # The function is new in Python 3.13; mypy doesn't know about it yet: + process_cpu_count = os.process_cpu_count() # type: ignore[attr-defined] if process_cpu_count and process_cpu_count != cpu_count: cpu_count = f"{process_cpu_count} (process) / {cpu_count} (system)" print("== CPU count:", cpu_count) diff --git a/Lib/test/libregrtest/worker.py b/Lib/test/libregrtest/worker.py index b3bb0b7f34a060..7a6d33d4499943 100644 --- a/Lib/test/libregrtest/worker.py +++ b/Lib/test/libregrtest/worker.py @@ -7,7 +7,7 @@ from test.support import os_helper, Py_DEBUG from .setup import setup_process, setup_test_dir -from .runtests import RunTests, JsonFile, JsonFileType +from .runtests import WorkerRunTests, JsonFile, JsonFileType from .single import run_single_test from .utils import ( StrPath, StrJSON, TestFilter, @@ -17,7 +17,7 @@ USE_PROCESS_GROUP = (hasattr(os, "setsid") and hasattr(os, "killpg")) -def create_worker_process(runtests: RunTests, output_fd: int, +def create_worker_process(runtests: WorkerRunTests, output_fd: int, tmp_dir: StrPath | None = None) -> subprocess.Popen: python_cmd = runtests.python_cmd worker_json = runtests.as_json() @@ -73,7 +73,7 @@ def create_worker_process(runtests: RunTests, output_fd: int, def worker_process(worker_json: StrJSON) -> NoReturn: - runtests = RunTests.from_json(worker_json) + runtests = WorkerRunTests.from_json(worker_json) test_name = runtests.tests[0] match_tests: TestFilter = runtests.match_tests json_file: JsonFile = runtests.json_file diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index ddb180ef5ef825..fd446c8145850c 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -3514,6 +3514,84 @@ def __init__(self): pass self.assertRaises(pickle.PicklingError, BadPickler().dump, 0) self.assertRaises(pickle.UnpicklingError, BadUnpickler().load) + def test_unpickler_bad_file(self): + # bpo-38384: Crash in _pickle if the read attribute raises an error. + def raises_oserror(self, *args, **kwargs): + raise OSError + @property + def bad_property(self): + 1/0 + + # File without read and readline + class F: + pass + self.assertRaises((AttributeError, TypeError), self.Unpickler, F()) + + # File without read + class F: + readline = raises_oserror + self.assertRaises((AttributeError, TypeError), self.Unpickler, F()) + + # File without readline + class F: + read = raises_oserror + self.assertRaises((AttributeError, TypeError), self.Unpickler, F()) + + # File with bad read + class F: + read = bad_property + readline = raises_oserror + self.assertRaises(ZeroDivisionError, self.Unpickler, F()) + + # File with bad readline + class F: + readline = bad_property + read = raises_oserror + self.assertRaises(ZeroDivisionError, self.Unpickler, F()) + + # File with bad readline, no read + class F: + readline = bad_property + self.assertRaises(ZeroDivisionError, self.Unpickler, F()) + + # File with bad read, no readline + class F: + read = bad_property + self.assertRaises((AttributeError, ZeroDivisionError), self.Unpickler, F()) + + # File with bad peek + class F: + peek = bad_property + read = raises_oserror + readline = raises_oserror + try: + self.Unpickler(F()) + except ZeroDivisionError: + pass + + # File with bad readinto + class F: + readinto = bad_property + read = raises_oserror + readline = raises_oserror + try: + self.Unpickler(F()) + except ZeroDivisionError: + pass + + def test_pickler_bad_file(self): + # File without write + class F: + pass + self.assertRaises(TypeError, self.Pickler, F()) + + # File with bad write + class F: + @property + def write(self): + 1/0 + self.assertRaises(ZeroDivisionError, self.Pickler, F()) + def check_dumps_loads_oob_buffers(self, dumps, loads): # No need to do the full gamut of tests here, just enough to # check that dumps() and loads() redirect their arguments diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 3a91a7dbc65021..49e41ca6cdaf98 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -517,7 +517,7 @@ def collect_sysconfig(info_add): 'PY_STDMODULE_CFLAGS', 'Py_DEBUG', 'Py_ENABLE_SHARED', - 'Py_NOGIL', + 'Py_GIL_DISABLED', 'SHELL', 'SOABI', 'abs_builddir', diff --git a/Lib/test/regrtestdata/import_from_tests/test_regrtest_a.py b/Lib/test/regrtestdata/import_from_tests/test_regrtest_a.py new file mode 100644 index 00000000000000..9c3d0c7cf4bfaa --- /dev/null +++ b/Lib/test/regrtestdata/import_from_tests/test_regrtest_a.py @@ -0,0 +1,11 @@ +import sys +import unittest +import test_regrtest_b.util + +class Test(unittest.TestCase): + def test(self): + test_regrtest_b.util # does not fail + self.assertIn('test_regrtest_a', sys.modules) + self.assertIs(sys.modules['test_regrtest_b'], test_regrtest_b) + self.assertIs(sys.modules['test_regrtest_b.util'], test_regrtest_b.util) + self.assertNotIn('test_regrtest_c', sys.modules) diff --git a/Lib/test/regrtestdata/import_from_tests/test_regrtest_b/__init__.py b/Lib/test/regrtestdata/import_from_tests/test_regrtest_b/__init__.py new file mode 100644 index 00000000000000..3dfba253455ad2 --- /dev/null +++ b/Lib/test/regrtestdata/import_from_tests/test_regrtest_b/__init__.py @@ -0,0 +1,9 @@ +import sys +import unittest + +class Test(unittest.TestCase): + def test(self): + self.assertNotIn('test_regrtest_a', sys.modules) + self.assertIn('test_regrtest_b', sys.modules) + self.assertNotIn('test_regrtest_b.util', sys.modules) + self.assertNotIn('test_regrtest_c', sys.modules) diff --git a/Lib/test/regrtestdata/import_from_tests/test_regrtest_b/util.py b/Lib/test/regrtestdata/import_from_tests/test_regrtest_b/util.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/Lib/test/regrtestdata/import_from_tests/test_regrtest_c.py b/Lib/test/regrtestdata/import_from_tests/test_regrtest_c.py new file mode 100644 index 00000000000000..de80769118d709 --- /dev/null +++ b/Lib/test/regrtestdata/import_from_tests/test_regrtest_c.py @@ -0,0 +1,11 @@ +import sys +import unittest +import test_regrtest_b.util + +class Test(unittest.TestCase): + def test(self): + test_regrtest_b.util # does not fail + self.assertNotIn('test_regrtest_a', sys.modules) + self.assertIs(sys.modules['test_regrtest_b'], test_regrtest_b) + self.assertIs(sys.modules['test_regrtest_b.util'], test_regrtest_b.util) + self.assertIn('test_regrtest_c', sys.modules) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index bb9f998db4622b..c22d73c231b46e 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -796,7 +796,8 @@ def check_cflags_pgo(): return any(option in cflags_nodist for option in pgo_options) -if sysconfig.get_config_var('Py_NOGIL'): +Py_GIL_DISABLED = bool(sysconfig.get_config_var('Py_GIL_DISABLED')) +if Py_GIL_DISABLED: _header = 'PHBBInP' else: _header = 'nP' @@ -2380,7 +2381,8 @@ def _get_c_recursion_limit(): import _testcapi return _testcapi.Py_C_RECURSION_LIMIT except (ImportError, AttributeError): - return 1500 # (from Include/cpython/pystate.h) + # Originally taken from Include/cpython/pystate.h . + return 1500 # The default C recursion limit. Py_C_RECURSION_LIMIT = _get_c_recursion_limit() diff --git a/Lib/test/support/interpreters.py b/Lib/test/support/interpreters.py index ab9342b767dfae..089fe7ef56df78 100644 --- a/Lib/test/support/interpreters.py +++ b/Lib/test/support/interpreters.py @@ -14,6 +14,7 @@ __all__ = [ 'Interpreter', 'get_current', 'get_main', 'create', 'list_all', + 'RunFailedError', 'SendChannel', 'RecvChannel', 'create_channel', 'list_all_channels', 'is_shareable', 'ChannelError', 'ChannelNotFoundError', @@ -21,6 +22,19 @@ ] +class RunFailedError(RuntimeError): + + def __init__(self, excinfo): + msg = excinfo.formatted + if not msg: + if excinfo.type and snapshot.msg: + msg = f'{snapshot.type.__name__}: {snapshot.msg}' + else: + msg = snapshot.type.__name__ or snapshot.msg + super().__init__(msg) + self.snapshot = excinfo + + def create(*, isolated=True): """Return a new (idle) Python interpreter.""" id = _interpreters.create(isolated=isolated) @@ -110,7 +124,9 @@ def run(self, src_str, /, channels=None): that time, the previous interpreter is allowed to run in other threads. """ - _interpreters.exec(self._id, src_str, channels) + excinfo = _interpreters.exec(self._id, src_str, channels) + if excinfo is not None: + raise RunFailedError(excinfo) def create_channel(): diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py index 46ae53aa11a91f..7a67d87fb9e846 100644 --- a/Lib/test/support/os_helper.py +++ b/Lib/test/support/os_helper.py @@ -592,10 +592,17 @@ def fd_count(): """Count the number of open file descriptors. """ if sys.platform.startswith(('linux', 'freebsd', 'emscripten')): + fd_path = "/proc/self/fd" + elif sys.platform == "darwin": + fd_path = "/dev/fd" + else: + fd_path = None + + if fd_path is not None: try: - names = os.listdir("/proc/self/fd") + names = os.listdir(fd_path) # Subtract one because listdir() internally opens a file - # descriptor to list the content of the /proc/self/fd/ directory. + # descriptor to list the content of the directory. return len(names) - 1 except FileNotFoundError: pass diff --git a/Lib/test/test__xxinterpchannels.py b/Lib/test/test__xxinterpchannels.py index 1c1ef3fac9d65f..2b75e2f1916c82 100644 --- a/Lib/test/test__xxinterpchannels.py +++ b/Lib/test/test__xxinterpchannels.py @@ -1017,16 +1017,16 @@ def test_close_multiple_users(self): _channels.recv({cid}) """)) channels.close(cid) - with self.assertRaises(interpreters.RunFailedError) as cm: - interpreters.run_string(id1, dedent(f""" + + excsnap = interpreters.run_string(id1, dedent(f""" _channels.send({cid}, b'spam') """)) - self.assertIn('ChannelClosedError', str(cm.exception)) - with self.assertRaises(interpreters.RunFailedError) as cm: - interpreters.run_string(id2, dedent(f""" + self.assertEqual(excsnap.type.__name__, 'ChannelClosedError') + + excsnap = interpreters.run_string(id2, dedent(f""" _channels.send({cid}, b'spam') """)) - self.assertIn('ChannelClosedError', str(cm.exception)) + self.assertEqual(excsnap.type.__name__, 'ChannelClosedError') def test_close_multiple_times(self): cid = channels.create() diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index 97314ddbb55ec8..64a9db95e5eaf5 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -10,6 +10,7 @@ import _testinternalcapi from test import support from test.support import import_helper +from test.support import os_helper from test.support import script_helper @@ -759,43 +760,6 @@ def test_bytes_for_script(self): with self.assertRaises(TypeError): interpreters.run_string(self.id, b'print("spam")') - @contextlib.contextmanager - def assert_run_failed(self, exctype, msg=None): - with self.assertRaises(interpreters.RunFailedError) as caught: - yield - if msg is None: - self.assertEqual(str(caught.exception).split(':')[0], - exctype.__name__) - else: - self.assertEqual(str(caught.exception), - "{}: {}".format(exctype.__name__, msg)) - - def test_invalid_syntax(self): - with self.assert_run_failed(SyntaxError): - # missing close paren - interpreters.run_string(self.id, 'print("spam"') - - def test_failure(self): - with self.assert_run_failed(Exception, 'spam'): - interpreters.run_string(self.id, 'raise Exception("spam")') - - def test_SystemExit(self): - with self.assert_run_failed(SystemExit, '42'): - interpreters.run_string(self.id, 'raise SystemExit(42)') - - def test_sys_exit(self): - with self.assert_run_failed(SystemExit): - interpreters.run_string(self.id, dedent(""" - import sys - sys.exit() - """)) - - with self.assert_run_failed(SystemExit, '42'): - interpreters.run_string(self.id, dedent(""" - import sys - sys.exit(42) - """)) - def test_with_shared(self): r, w = os.pipe() @@ -959,6 +923,163 @@ def f(): self.assertEqual(retcode, 0) +class RunFailedTests(TestBase): + + def setUp(self): + super().setUp() + self.id = interpreters.create() + + def add_module(self, modname, text): + import tempfile + tempdir = tempfile.mkdtemp() + self.addCleanup(lambda: os_helper.rmtree(tempdir)) + interpreters.run_string(self.id, dedent(f""" + import sys + sys.path.insert(0, {tempdir!r}) + """)) + return script_helper.make_script(tempdir, modname, text) + + def run_script(self, text, *, fails=False): + r, w = os.pipe() + try: + script = dedent(f""" + import os, sys + os.write({w}, b'0') + + # This raises an exception: + {{}} + + # Nothing from here down should ever run. + os.write({w}, b'1') + class NeverError(Exception): pass + raise NeverError # never raised + """).format(dedent(text)) + if fails: + err = interpreters.run_string(self.id, script) + self.assertIsNot(err, None) + return err + else: + err = interpreters.run_string(self.id, script) + self.assertIs(err, None) + return None + except: + raise # re-raise + else: + msg = os.read(r, 100) + self.assertEqual(msg, b'0') + finally: + os.close(r) + os.close(w) + + def _assert_run_failed(self, exctype, msg, script): + if isinstance(exctype, str): + exctype_name = exctype + exctype = None + else: + exctype_name = exctype.__name__ + + # Run the script. + excinfo = self.run_script(script, fails=True) + + # Check the wrapper exception. + self.assertEqual(excinfo.type.__name__, exctype_name) + if msg is None: + self.assertEqual(excinfo.formatted.split(':')[0], + exctype_name) + else: + self.assertEqual(excinfo.formatted, + '{}: {}'.format(exctype_name, msg)) + + return excinfo + + def assert_run_failed(self, exctype, script): + self._assert_run_failed(exctype, None, script) + + def assert_run_failed_msg(self, exctype, msg, script): + self._assert_run_failed(exctype, msg, script) + + def test_exit(self): + with self.subTest('sys.exit(0)'): + # XXX Should an unhandled SystemExit(0) be handled as not-an-error? + self.assert_run_failed(SystemExit, """ + sys.exit(0) + """) + + with self.subTest('sys.exit()'): + self.assert_run_failed(SystemExit, """ + import sys + sys.exit() + """) + + with self.subTest('sys.exit(42)'): + self.assert_run_failed_msg(SystemExit, '42', """ + import sys + sys.exit(42) + """) + + with self.subTest('SystemExit'): + self.assert_run_failed_msg(SystemExit, '42', """ + raise SystemExit(42) + """) + + # XXX Also check os._exit() (via a subprocess)? + + def test_plain_exception(self): + self.assert_run_failed_msg(Exception, 'spam', """ + raise Exception("spam") + """) + + def test_invalid_syntax(self): + script = dedent(""" + x = 1 + 2 + y = 2 + 4 + z = 4 + 8 + + # missing close paren + print("spam" + + if x + y + z < 20: + ... + """) + + with self.subTest('script'): + self.assert_run_failed(SyntaxError, script) + + with self.subTest('module'): + modname = 'spam_spam_spam' + filename = self.add_module(modname, script) + self.assert_run_failed(SyntaxError, f""" + import {modname} + """) + + def test_NameError(self): + self.assert_run_failed(NameError, """ + res = spam + eggs + """) + # XXX check preserved suggestions + + def test_AttributeError(self): + self.assert_run_failed(AttributeError, """ + object().spam + """) + # XXX check preserved suggestions + + def test_ExceptionGroup(self): + self.assert_run_failed(ExceptionGroup, """ + raise ExceptionGroup('exceptions', [ + Exception('spam'), + ImportError('eggs'), + ]) + """) + + def test_user_defined_exception(self): + self.assert_run_failed_msg('MyError', 'spam', """ + class MyError(Exception): + pass + raise MyError('spam') + """) + + class RunFuncTests(TestBase): def setUp(self): diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index ccb7dbf667c320..b408cd1f7da205 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -1122,13 +1122,10 @@ async def inner(httpd): self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) with test_utils.run_test_server() as httpd: - try: + with self.assertRaises(RuntimeError): + # This exception is caused by `self.loop.stop()` as expected. self.loop.run_until_complete(inner(httpd)) - # This exception is caused by `self.loop.stop()` as expected. - except RuntimeError: - pass - finally: - gc.collect() + gc.collect() self.assertEqual(messages, []) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index b7966f8f03875b..5e66d58fd2cb18 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -627,6 +627,11 @@ def __dir__(self): # test that object has a __dir__() self.assertEqual(sorted([].__dir__()), dir([])) + def test___ne__(self): + self.assertFalse(None.__ne__(None)) + self.assertTrue(None.__ne__(0)) + self.assertTrue(None.__ne__("abc")) + def test_divmod(self): self.assertEqual(divmod(12, 7), (1, 5)) self.assertEqual(divmod(-12, 7), (-2, 2)) @@ -2039,6 +2044,23 @@ def test_bytearray_extend_error(self): bad_iter = map(int, "X") self.assertRaises(ValueError, array.extend, bad_iter) + def test_bytearray_join_with_misbehaving_iterator(self): + # Issue #112625 + array = bytearray(b',') + def iterator(): + array.clear() + yield b'A' + yield b'B' + self.assertRaises(BufferError, array.join, iterator()) + + def test_bytearray_join_with_custom_iterator(self): + # Issue #112625 + array = bytearray(b',') + def iterator(): + yield b'A' + yield b'B' + self.assertEqual(bytearray(b'A,B'), array.join(iterator())) + def test_construct_singletons(self): for const in None, Ellipsis, NotImplemented: tp = type(const) @@ -2252,7 +2274,10 @@ def _run_child(self, child, terminal_input): return lines - def check_input_tty(self, prompt, terminal_input, stdio_encoding=None): + def check_input_tty(self, prompt, terminal_input, stdio_encoding=None, *, + expected=None, + stdin_errors='surrogateescape', + stdout_errors='replace'): if not sys.stdin.isatty() or not sys.stdout.isatty(): self.skipTest("stdin and stdout must be ttys") def child(wpipe): @@ -2260,22 +2285,26 @@ def child(wpipe): if stdio_encoding: sys.stdin = io.TextIOWrapper(sys.stdin.detach(), encoding=stdio_encoding, - errors='surrogateescape') + errors=stdin_errors) sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding=stdio_encoding, - errors='replace') + errors=stdout_errors) print("tty =", sys.stdin.isatty() and sys.stdout.isatty(), file=wpipe) - print(ascii(input(prompt)), file=wpipe) + try: + print(ascii(input(prompt)), file=wpipe) + except BaseException as e: + print(ascii(f'{e.__class__.__name__}: {e!s}'), file=wpipe) lines = self.run_child(child, terminal_input + b"\r\n") # Check we did exercise the GNU readline path self.assertIn(lines[0], {'tty = True', 'tty = False'}) if lines[0] != 'tty = True': self.skipTest("standard IO in should have been a tty") input_result = eval(lines[1]) # ascii() -> eval() roundtrip - if stdio_encoding: - expected = terminal_input.decode(stdio_encoding, 'surrogateescape') - else: - expected = terminal_input.decode(sys.stdin.encoding) # what else? + if expected is None: + if stdio_encoding: + expected = terminal_input.decode(stdio_encoding, 'surrogateescape') + else: + expected = terminal_input.decode(sys.stdin.encoding) # what else? self.assertEqual(input_result, expected) def test_input_tty(self): @@ -2296,13 +2325,32 @@ def skip_if_readline(self): def test_input_tty_non_ascii(self): self.skip_if_readline() # Check stdin/stdout encoding is used when invoking PyOS_Readline() - self.check_input_tty("prompté", b"quux\xe9", "utf-8") + self.check_input_tty("prompté", b"quux\xc3\xa9", "utf-8") def test_input_tty_non_ascii_unicode_errors(self): self.skip_if_readline() # Check stdin/stdout error handler is used when invoking PyOS_Readline() self.check_input_tty("prompté", b"quux\xe9", "ascii") + def test_input_tty_null_in_prompt(self): + self.check_input_tty("prompt\0", b"", + expected='ValueError: input: prompt string cannot contain ' + 'null characters') + + def test_input_tty_nonencodable_prompt(self): + self.skip_if_readline() + self.check_input_tty("prompté", b"quux", "ascii", stdout_errors='strict', + expected="UnicodeEncodeError: 'ascii' codec can't encode " + "character '\\xe9' in position 6: ordinal not in " + "range(128)") + + def test_input_tty_nondecodable_input(self): + self.skip_if_readline() + self.check_input_tty("prompt", b"quux\xe9", "ascii", stdin_errors='strict', + expected="UnicodeDecodeError: 'ascii' codec can't decode " + "byte 0xe9 in position 4: ordinal not in " + "range(128)") + def test_input_no_stdout_fileno(self): # Issue #24402: If stdin is the original terminal but stdout.fileno() # fails, do not use the original stdout file descriptor diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index afd506f07520d8..a3804a945f2e3a 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -46,6 +46,10 @@ def __index__(self): class BaseBytesTest: + def assertTypedEqual(self, actual, expected): + self.assertIs(type(actual), type(expected)) + self.assertEqual(actual, expected) + def test_basics(self): b = self.type2test() self.assertEqual(type(b), self.type2test) @@ -1023,36 +1027,63 @@ def test_buffer_is_readonly(self): self.assertRaises(TypeError, f.readinto, b"") def test_custom(self): - class A: - def __bytes__(self): - return b'abc' - self.assertEqual(bytes(A()), b'abc') - class A: pass - self.assertRaises(TypeError, bytes, A()) - class A: - def __bytes__(self): - return None - self.assertRaises(TypeError, bytes, A()) - class A: + self.assertEqual(bytes(BytesSubclass(b'abc')), b'abc') + self.assertEqual(BytesSubclass(OtherBytesSubclass(b'abc')), + BytesSubclass(b'abc')) + self.assertEqual(bytes(WithBytes(b'abc')), b'abc') + self.assertEqual(BytesSubclass(WithBytes(b'abc')), BytesSubclass(b'abc')) + + class NoBytes: pass + self.assertRaises(TypeError, bytes, NoBytes()) + self.assertRaises(TypeError, bytes, WithBytes('abc')) + self.assertRaises(TypeError, bytes, WithBytes(None)) + class IndexWithBytes: def __bytes__(self): return b'a' def __index__(self): return 42 - self.assertEqual(bytes(A()), b'a') + self.assertEqual(bytes(IndexWithBytes()), b'a') # Issue #25766 - class A(str): + class StrWithBytes(str): + def __new__(cls, value): + self = str.__new__(cls, '\u20ac') + self.value = value + return self def __bytes__(self): - return b'abc' - self.assertEqual(bytes(A('\u20ac')), b'abc') - self.assertEqual(bytes(A('\u20ac'), 'iso8859-15'), b'\xa4') + return self.value + self.assertEqual(bytes(StrWithBytes(b'abc')), b'abc') + self.assertEqual(bytes(StrWithBytes(b'abc'), 'iso8859-15'), b'\xa4') + self.assertEqual(bytes(StrWithBytes(BytesSubclass(b'abc'))), b'abc') + self.assertEqual(BytesSubclass(StrWithBytes(b'abc')), BytesSubclass(b'abc')) + self.assertEqual(BytesSubclass(StrWithBytes(b'abc'), 'iso8859-15'), + BytesSubclass(b'\xa4')) + self.assertEqual(BytesSubclass(StrWithBytes(BytesSubclass(b'abc'))), + BytesSubclass(b'abc')) + self.assertEqual(BytesSubclass(StrWithBytes(OtherBytesSubclass(b'abc'))), + BytesSubclass(b'abc')) # Issue #24731 - class A: + self.assertTypedEqual(bytes(WithBytes(BytesSubclass(b'abc'))), BytesSubclass(b'abc')) + self.assertTypedEqual(BytesSubclass(WithBytes(BytesSubclass(b'abc'))), + BytesSubclass(b'abc')) + self.assertTypedEqual(BytesSubclass(WithBytes(OtherBytesSubclass(b'abc'))), + BytesSubclass(b'abc')) + + class BytesWithBytes(bytes): + def __new__(cls, value): + self = bytes.__new__(cls, b'\xa4') + self.value = value + return self def __bytes__(self): - return OtherBytesSubclass(b'abc') - self.assertEqual(bytes(A()), b'abc') - self.assertIs(type(bytes(A())), OtherBytesSubclass) - self.assertEqual(BytesSubclass(A()), b'abc') - self.assertIs(type(BytesSubclass(A())), BytesSubclass) + return self.value + self.assertTypedEqual(bytes(BytesWithBytes(b'abc')), b'abc') + self.assertTypedEqual(BytesSubclass(BytesWithBytes(b'abc')), + BytesSubclass(b'abc')) + self.assertTypedEqual(bytes(BytesWithBytes(BytesSubclass(b'abc'))), + BytesSubclass(b'abc')) + self.assertTypedEqual(BytesSubclass(BytesWithBytes(BytesSubclass(b'abc'))), + BytesSubclass(b'abc')) + self.assertTypedEqual(BytesSubclass(BytesWithBytes(OtherBytesSubclass(b'abc'))), + BytesSubclass(b'abc')) # Test PyBytes_FromFormat() def test_from_format(self): @@ -2069,6 +2100,12 @@ class BytesSubclass(bytes): class OtherBytesSubclass(bytes): pass +class WithBytes: + def __init__(self, value): + self.value = value + def __bytes__(self): + return self.value + class ByteArraySubclassTest(SubclassTest, unittest.TestCase): basetype = bytearray type2test = ByteArraySubclass diff --git a/Lib/test/test_capi/test_abstract.py b/Lib/test/test_capi/test_abstract.py index 26152c3049848c..97ed939928c360 100644 --- a/Lib/test/test_capi/test_abstract.py +++ b/Lib/test/test_capi/test_abstract.py @@ -8,6 +8,30 @@ NULL = None +class StrSubclass(str): + pass + +class BytesSubclass(bytes): + pass + +class WithStr: + def __init__(self, value): + self.value = value + def __str__(self): + return self.value + +class WithRepr: + def __init__(self, value): + self.value = value + def __repr__(self): + return self.value + +class WithBytes: + def __init__(self, value): + self.value = value + def __bytes__(self): + return self.value + class TestObject: @property def evil(self): @@ -44,6 +68,68 @@ def gen(): class CAPITest(unittest.TestCase): + def assertTypedEqual(self, actual, expected): + self.assertIs(type(actual), type(expected)) + self.assertEqual(actual, expected) + + def test_object_str(self): + # Test PyObject_Str() + object_str = _testcapi.object_str + self.assertTypedEqual(object_str(''), '') + self.assertTypedEqual(object_str('abc'), 'abc') + self.assertTypedEqual(object_str('\U0001f40d'), '\U0001f40d') + self.assertTypedEqual(object_str(StrSubclass('abc')), 'abc') + self.assertTypedEqual(object_str(WithStr('abc')), 'abc') + self.assertTypedEqual(object_str(WithStr(StrSubclass('abc'))), StrSubclass('abc')) + self.assertTypedEqual(object_str(WithRepr('')), '') + self.assertTypedEqual(object_str(WithRepr(StrSubclass(''))), StrSubclass('')) + self.assertTypedEqual(object_str(NULL), '') + + def test_object_repr(self): + # Test PyObject_Repr() + object_repr = _testcapi.object_repr + self.assertTypedEqual(object_repr(''), "''") + self.assertTypedEqual(object_repr('abc'), "'abc'") + self.assertTypedEqual(object_repr('\U0001f40d'), "'\U0001f40d'") + self.assertTypedEqual(object_repr(StrSubclass('abc')), "'abc'") + self.assertTypedEqual(object_repr(WithRepr('')), '') + self.assertTypedEqual(object_repr(WithRepr(StrSubclass(''))), StrSubclass('')) + self.assertTypedEqual(object_repr(WithRepr('<\U0001f40d>')), '<\U0001f40d>') + self.assertTypedEqual(object_repr(WithRepr(StrSubclass('<\U0001f40d>'))), StrSubclass('<\U0001f40d>')) + self.assertTypedEqual(object_repr(NULL), '') + + def test_object_ascii(self): + # Test PyObject_ASCII() + object_ascii = _testcapi.object_ascii + self.assertTypedEqual(object_ascii(''), "''") + self.assertTypedEqual(object_ascii('abc'), "'abc'") + self.assertTypedEqual(object_ascii('\U0001f40d'), r"'\U0001f40d'") + self.assertTypedEqual(object_ascii(StrSubclass('abc')), "'abc'") + self.assertTypedEqual(object_ascii(WithRepr('')), '') + self.assertTypedEqual(object_ascii(WithRepr(StrSubclass(''))), StrSubclass('')) + self.assertTypedEqual(object_ascii(WithRepr('<\U0001f40d>')), r'<\U0001f40d>') + self.assertTypedEqual(object_ascii(WithRepr(StrSubclass('<\U0001f40d>'))), r'<\U0001f40d>') + self.assertTypedEqual(object_ascii(NULL), '') + + def test_object_bytes(self): + # Test PyObject_Bytes() + object_bytes = _testcapi.object_bytes + self.assertTypedEqual(object_bytes(b''), b'') + self.assertTypedEqual(object_bytes(b'abc'), b'abc') + self.assertTypedEqual(object_bytes(BytesSubclass(b'abc')), b'abc') + self.assertTypedEqual(object_bytes(WithBytes(b'abc')), b'abc') + self.assertTypedEqual(object_bytes(WithBytes(BytesSubclass(b'abc'))), BytesSubclass(b'abc')) + self.assertTypedEqual(object_bytes(bytearray(b'abc')), b'abc') + self.assertTypedEqual(object_bytes(memoryview(b'abc')), b'abc') + self.assertTypedEqual(object_bytes([97, 98, 99]), b'abc') + self.assertTypedEqual(object_bytes((97, 98, 99)), b'abc') + self.assertTypedEqual(object_bytes(iter([97, 98, 99])), b'abc') + self.assertRaises(TypeError, object_bytes, WithBytes(bytearray(b'abc'))) + self.assertRaises(TypeError, object_bytes, WithBytes([97, 98, 99])) + self.assertRaises(TypeError, object_bytes, 3) + self.assertRaises(TypeError, object_bytes, 'abc') + self.assertRaises(TypeError, object_bytes, object()) + self.assertTypedEqual(object_bytes(NULL), b'') def test_object_getattr(self): xgetattr = _testcapi.object_getattr diff --git a/Lib/test/test_capi/test_complex.py b/Lib/test/test_capi/test_complex.py index 9f51efb091dea5..d6fc1f077c40aa 100644 --- a/Lib/test/test_capi/test_complex.py +++ b/Lib/test/test_capi/test_complex.py @@ -1,3 +1,5 @@ +from math import isnan +import errno import unittest import warnings @@ -10,6 +12,10 @@ _testcapi = import_helper.import_module('_testcapi') NULL = None +INF = float("inf") +NAN = float("nan") +DBL_MAX = _testcapi.DBL_MAX + class BadComplex3: def __complex__(self): @@ -141,6 +147,87 @@ def test_asccomplex(self): # CRASHES asccomplex(NULL) + def test_py_c_sum(self): + # Test _Py_c_sum() + _py_c_sum = _testcapi._py_c_sum + + self.assertEqual(_py_c_sum(1, 1j), (1+1j, 0)) + + def test_py_c_diff(self): + # Test _Py_c_diff() + _py_c_diff = _testcapi._py_c_diff + + self.assertEqual(_py_c_diff(1, 1j), (1-1j, 0)) + + def test_py_c_neg(self): + # Test _Py_c_neg() + _py_c_neg = _testcapi._py_c_neg + + self.assertEqual(_py_c_neg(1+1j), -1-1j) + + def test_py_c_prod(self): + # Test _Py_c_prod() + _py_c_prod = _testcapi._py_c_prod + + self.assertEqual(_py_c_prod(2, 1j), (2j, 0)) + + def test_py_c_quot(self): + # Test _Py_c_quot() + _py_c_quot = _testcapi._py_c_quot + + self.assertEqual(_py_c_quot(1, 1j), (-1j, 0)) + self.assertEqual(_py_c_quot(1, -1j), (1j, 0)) + self.assertEqual(_py_c_quot(1j, 2), (0.5j, 0)) + self.assertEqual(_py_c_quot(1j, -2), (-0.5j, 0)) + self.assertEqual(_py_c_quot(1, 2j), (-0.5j, 0)) + + z, e = _py_c_quot(NAN, 1j) + self.assertTrue(isnan(z.real)) + self.assertTrue(isnan(z.imag)) + self.assertEqual(e, 0) + + z, e = _py_c_quot(1j, NAN) + self.assertTrue(isnan(z.real)) + self.assertTrue(isnan(z.imag)) + self.assertEqual(e, 0) + + self.assertEqual(_py_c_quot(1, 0j)[1], errno.EDOM) + + def test_py_c_pow(self): + # Test _Py_c_pow() + _py_c_pow = _testcapi._py_c_pow + + self.assertEqual(_py_c_pow(1j, 0j), (1+0j, 0)) + self.assertEqual(_py_c_pow(1, 1j), (1+0j, 0)) + self.assertEqual(_py_c_pow(0j, 1), (0j, 0)) + self.assertAlmostEqual(_py_c_pow(1j, 2)[0], -1.0+0j) + + r, e = _py_c_pow(1+1j, -1) + self.assertAlmostEqual(r, 0.5-0.5j) + self.assertEqual(e, 0) + + self.assertEqual(_py_c_pow(0j, -1)[1], errno.EDOM) + self.assertEqual(_py_c_pow(0j, 1j)[1], errno.EDOM) + self.assertEqual(_py_c_pow(*[DBL_MAX+1j]*2)[0], complex(*[INF]*2)) + + + def test_py_c_abs(self): + # Test _Py_c_abs() + _py_c_abs = _testcapi._py_c_abs + + self.assertEqual(_py_c_abs(-1), (1.0, 0)) + self.assertEqual(_py_c_abs(1j), (1.0, 0)) + + self.assertEqual(_py_c_abs(complex('+inf+1j')), (INF, 0)) + self.assertEqual(_py_c_abs(complex('-inf+1j')), (INF, 0)) + self.assertEqual(_py_c_abs(complex('1.25+infj')), (INF, 0)) + self.assertEqual(_py_c_abs(complex('1.25-infj')), (INF, 0)) + + self.assertTrue(isnan(_py_c_abs(complex('1.25+nanj'))[0])) + self.assertTrue(isnan(_py_c_abs(complex('nan-1j'))[0])) + + self.assertEqual(_py_c_abs(complex(*[DBL_MAX]*2))[1], errno.ERANGE) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_capi/test_getargs.py b/Lib/test/test_capi/test_getargs.py index c964b1efd577ba..9b6aef27625ad0 100644 --- a/Lib/test/test_capi/test_getargs.py +++ b/Lib/test/test_capi/test_getargs.py @@ -1314,6 +1314,34 @@ def test_nonascii_keywords(self): f"'{name2}' is an invalid keyword argument"): parse((), {name2: 1, name3: 2}, '|OO', [name, name3]) + def test_nested_tuple(self): + parse = _testcapi.parse_tuple_and_keywords + + self.assertEqual(parse(((1, 2, 3),), {}, '(OOO)', ['a']), (1, 2, 3)) + self.assertEqual(parse((1, (2, 3), 4), {}, 'O(OO)O', ['a', 'b', 'c']), + (1, 2, 3, 4)) + parse(((1, 2, 3),), {}, '(iii)', ['a']) + + with self.assertRaisesRegex(TypeError, + "argument 1 must be sequence of length 2, not 3"): + parse(((1, 2, 3),), {}, '(ii)', ['a']) + with self.assertRaisesRegex(TypeError, + "argument 1 must be sequence of length 2, not 1"): + parse(((1,),), {}, '(ii)', ['a']) + with self.assertRaisesRegex(TypeError, + "argument 1 must be 2-item sequence, not int"): + parse((1,), {}, '(ii)', ['a']) + with self.assertRaisesRegex(TypeError, + "argument 1 must be 2-item sequence, not bytes"): + parse((b'ab',), {}, '(ii)', ['a']) + + for f in 'es', 'et', 'es#', 'et#': + with self.assertRaises(LookupError): # empty encoding "" + parse((('a',),), {}, '(' + f + ')', ['a']) + with self.assertRaisesRegex(TypeError, + "argument 1 must be sequence of length 1, not 0"): + parse(((),), {}, '(' + f + ')', ['a']) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_capi/test_hash.py b/Lib/test/test_capi/test_hash.py index 59dec15bc21445..8436da7c32df10 100644 --- a/Lib/test/test_capi/test_hash.py +++ b/Lib/test/test_capi/test_hash.py @@ -4,7 +4,8 @@ _testcapi = import_helper.import_module('_testcapi') -SIZEOF_PY_HASH_T = _testcapi.SIZEOF_VOID_P +SIZEOF_VOID_P = _testcapi.SIZEOF_VOID_P +SIZEOF_PY_HASH_T = SIZEOF_VOID_P class CAPITest(unittest.TestCase): @@ -31,3 +32,48 @@ def test_hash_getfuncdef(self): self.assertEqual(func_def.name, hash_info.algorithm) self.assertEqual(func_def.hash_bits, hash_info.hash_bits) self.assertEqual(func_def.seed_bits, hash_info.seed_bits) + + def test_hash_pointer(self): + # Test Py_HashPointer() + hash_pointer = _testcapi.hash_pointer + + UHASH_T_MASK = ((2 ** (8 * SIZEOF_PY_HASH_T)) - 1) + HASH_T_MAX = (2 ** (8 * SIZEOF_PY_HASH_T - 1) - 1) + + def python_hash_pointer(x): + # Py_HashPointer() rotates the pointer bits by 4 bits to the right + x = (x >> 4) | ((x & 15) << (8 * SIZEOF_VOID_P - 4)) + + # Convert unsigned uintptr_t (Py_uhash_t) to signed Py_hash_t + if HASH_T_MAX < x: + x = (~x) + 1 + x &= UHASH_T_MASK + x = (~x) + 1 + return x + + if SIZEOF_VOID_P == 8: + values = ( + 0xABCDEF1234567890, + 0x1234567890ABCDEF, + 0xFEE4ABEDD1CECA5E, + ) + else: + values = ( + 0x12345678, + 0x1234ABCD, + 0xDEADCAFE, + ) + + for value in values: + expected = python_hash_pointer(value) + with self.subTest(value=value): + self.assertEqual(hash_pointer(value), expected, + f"hash_pointer({value:x}) = " + f"{hash_pointer(value):x} != {expected:x}") + + # Py_HashPointer(NULL) returns 0 + self.assertEqual(hash_pointer(0), 0) + + # Py_HashPointer((void*)(uintptr_t)-1) doesn't return -1 but -2 + VOID_P_MAX = -1 & (2 ** (8 * SIZEOF_VOID_P) - 1) + self.assertEqual(hash_pointer(VOID_P_MAX), -2) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index fe5c36c0c0dec9..3d86ae37190475 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -1098,6 +1098,21 @@ class Data(_testcapi.ObjExtraData): del d.extra self.assertIsNone(d.extra) + def test_get_type_module_name(self): + from collections import OrderedDict + ht = _testcapi.get_heaptype_for_name() + for cls, expected in { + int: 'builtins', + OrderedDict: 'collections', + ht: '_testcapi', + }.items(): + with self.subTest(repr(cls)): + modname = _testinternalcapi.get_type_module_name(cls) + self.assertEqual(modname, expected) + + ht.__module__ = 'test_module' + modname = _testinternalcapi.get_type_module_name(ht) + self.assertEqual(modname, 'test_module') @requires_limited_api class TestHeapTypeRelative(unittest.TestCase): @@ -2808,6 +2823,91 @@ def testfunc(n): uops = {opname for opname, _, _ in ex} self.assertIn("_GUARD_IS_FALSE_POP", uops) + def test_for_iter_tier_two(self): + class MyIter: + def __init__(self, n): + self.n = n + def __iter__(self): + return self + def __next__(self): + self.n -= 1 + if self.n < 0: + raise StopIteration + return self.n + + def testfunc(n, m): + x = 0 + for i in range(m): + for j in MyIter(n): + x += 1000*i + j + return x + + opt = _testinternalcapi.get_uop_optimizer() + with temporary_optimizer(opt): + x = testfunc(10, 10) + + self.assertEqual(x, sum(range(10)) * 10010) + + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = {opname for opname, _, _ in ex} + self.assertIn("_FOR_ITER_TIER_TWO", uops) + + +@unittest.skipUnless(support.Py_GIL_DISABLED, 'need Py_GIL_DISABLED') +class TestPyThreadId(unittest.TestCase): + def test_py_thread_id(self): + # gh-112535: Test _Py_ThreadId(): make sure that thread identifiers + # in a few threads are unique + py_thread_id = _testinternalcapi.py_thread_id + short_sleep = 0.010 + + class GetThreadId(threading.Thread): + def __init__(self): + super().__init__() + self.get_lock = threading.Lock() + self.get_lock.acquire() + self.started_lock = threading.Event() + self.py_tid = None + + def run(self): + self.started_lock.set() + self.get_lock.acquire() + self.py_tid = py_thread_id() + time.sleep(short_sleep) + self.py_tid2 = py_thread_id() + + nthread = 5 + threads = [GetThreadId() for _ in range(nthread)] + + # first make run sure that all threads are running + for thread in threads: + thread.start() + for thread in threads: + thread.started_lock.wait() + + # call _Py_ThreadId() in the main thread + py_thread_ids = [py_thread_id()] + + # now call _Py_ThreadId() in each thread + for thread in threads: + thread.get_lock.release() + + # call _Py_ThreadId() in each thread and wait until threads complete + for thread in threads: + thread.join() + py_thread_ids.append(thread.py_tid) + # _PyThread_Id() should not change for a given thread. + # For example, it should remain the same after a short sleep. + self.assertEqual(thread.py_tid2, thread.py_tid) + + # make sure that all _Py_ThreadId() are unique + for tid in py_thread_ids: + self.assertIsInstance(tid, int) + self.assertGreater(tid, 0) + self.assertEqual(len(set(py_thread_ids)), len(py_thread_ids), + py_thread_ids) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index da957fcebaa296..f53e9481083106 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -638,7 +638,7 @@ class C "void *" "" C.__init__ = C.meth [clinic start generated code]*/ """ - err = "'__init__' must be a normal method, not a class or static method" + err = "'__init__' must be a normal method; got 'FunctionKind.CLASS_METHOD'!" self.expect_failure(block, err, lineno=8) def test_validate_cloned_new(self): @@ -2180,14 +2180,22 @@ class Foo "" "" self.expect_failure(block, err, lineno=2) def test_init_must_be_a_normal_method(self): - err = "'__init__' must be a normal method, not a class or static method!" - block = """ - module foo - class Foo "" "" - @classmethod - Foo.__init__ - """ - self.expect_failure(block, err, lineno=3) + err_template = "'__init__' must be a normal method; got 'FunctionKind.{}'!" + annotations = { + "@classmethod": "CLASS_METHOD", + "@staticmethod": "STATIC_METHOD", + "@getter": "GETTER", + } + for annotation, invalid_kind in annotations.items(): + with self.subTest(annotation=annotation, invalid_kind=invalid_kind): + block = f""" + module foo + class Foo "" "" + {annotation} + Foo.__init__ + """ + expected_error = err_template.format(invalid_kind) + self.expect_failure(block, expected_error, lineno=3) def test_duplicate_coexist(self): err = "Called @coexist twice" diff --git a/Lib/test/test_cmd.py b/Lib/test/test_cmd.py index 951336fa08542d..46ec82b704963d 100644 --- a/Lib/test/test_cmd.py +++ b/Lib/test/test_cmd.py @@ -9,7 +9,10 @@ import doctest import unittest import io +import textwrap from test import support +from test.support.import_helper import import_module +from test.support.pty_helper import run_pty class samplecmdclass(cmd.Cmd): """ @@ -259,6 +262,33 @@ class CmdPrintExceptionClass(cmd.Cmd): def default(self, line): print(sys.exc_info()[:2]) + +@support.requires_subprocess() +class CmdTestReadline(unittest.TestCase): + def setUpClass(): + # Ensure that the readline module is loaded + # If this fails, the test is skipped because SkipTest will be raised + readline = import_module('readline') + + def test_basic_completion(self): + script = textwrap.dedent(""" + import cmd + class simplecmd(cmd.Cmd): + def do_tab_completion_test(self, args): + print('tab completion success') + return True + + simplecmd().cmdloop() + """) + + # 't' and complete 'ab_completion_test' to 'tab_completion_test' + input = b"t\t\n" + + output = run_pty(script, input) + + self.assertIn(b'ab_completion_test', output) + self.assertIn(b'tab completion success', output) + def load_tests(loader, tests, pattern): tests.addTest(doctest.DocTestSuite()) return tests diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index bb8b352518ef3e..7e6f811e17cfa2 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -488,12 +488,8 @@ def test_instance(self): self.assertEqual(p._replace(x=1), (1, 22)) # test _replace method self.assertEqual(p._asdict(), dict(x=11, y=22)) # test _asdict method - try: + with self.assertRaises(TypeError): p._replace(x=1, error=2) - except ValueError: - pass - else: - self._fail('Did not detect an incorrect fieldname') # verify that field string can have commas Point = namedtuple('Point', 'x, y') diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 9180cca62b28b8..b057121f285dc7 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -109,6 +109,8 @@ def test_truediv(self): complex(random(), random())) self.assertAlmostEqual(complex.__truediv__(2+0j, 1+1j), 1-1j) + self.assertRaises(TypeError, operator.truediv, 1j, None) + self.assertRaises(TypeError, operator.truediv, None, 1j) for denom_real, denom_imag in [(0, NAN), (NAN, 0), (NAN, NAN)]: z = complex(0, 0) / complex(denom_real, denom_imag) @@ -140,6 +142,7 @@ def test_floordiv_zero_division(self): def test_richcompare(self): self.assertIs(complex.__eq__(1+1j, 1<<10000), False) self.assertIs(complex.__lt__(1+1j, None), NotImplemented) + self.assertIs(complex.__eq__(1+1j, None), NotImplemented) self.assertIs(complex.__eq__(1+1j, 1+1j), True) self.assertIs(complex.__eq__(1+1j, 2+2j), False) self.assertIs(complex.__ne__(1+1j, 1+1j), False) @@ -162,6 +165,7 @@ def test_richcompare(self): self.assertIs(operator.eq(1+1j, 2+2j), False) self.assertIs(operator.ne(1+1j, 1+1j), False) self.assertIs(operator.ne(1+1j, 2+2j), True) + self.assertIs(operator.eq(1+1j, 2.0), False) def test_richcompare_boundaries(self): def check(n, deltas, is_equal, imag = 0.0): @@ -180,6 +184,27 @@ def check(n, deltas, is_equal, imag = 0.0): check(2 ** pow, range(1, 101), lambda delta: False, float(i)) check(2 ** 53, range(-100, 0), lambda delta: True) + def test_add(self): + self.assertEqual(1j + int(+1), complex(+1, 1)) + self.assertEqual(1j + int(-1), complex(-1, 1)) + self.assertRaises(OverflowError, operator.add, 1j, 10**1000) + self.assertRaises(TypeError, operator.add, 1j, None) + self.assertRaises(TypeError, operator.add, None, 1j) + + def test_sub(self): + self.assertEqual(1j - int(+1), complex(-1, 1)) + self.assertEqual(1j - int(-1), complex(1, 1)) + self.assertRaises(OverflowError, operator.sub, 1j, 10**1000) + self.assertRaises(TypeError, operator.sub, 1j, None) + self.assertRaises(TypeError, operator.sub, None, 1j) + + def test_mul(self): + self.assertEqual(1j * int(20), complex(0, 20)) + self.assertEqual(1j * int(-1), complex(0, -1)) + self.assertRaises(OverflowError, operator.mul, 1j, 10**1000) + self.assertRaises(TypeError, operator.mul, 1j, None) + self.assertRaises(TypeError, operator.mul, None, 1j) + def test_mod(self): # % is no longer supported on complex numbers with self.assertRaises(TypeError): @@ -212,11 +237,18 @@ def test_divmod_zero_division(self): def test_pow(self): self.assertAlmostEqual(pow(1+1j, 0+0j), 1.0) self.assertAlmostEqual(pow(0+0j, 2+0j), 0.0) + self.assertEqual(pow(0+0j, 2000+0j), 0.0) + self.assertEqual(pow(0, 0+0j), 1.0) + self.assertEqual(pow(-1, 0+0j), 1.0) self.assertRaises(ZeroDivisionError, pow, 0+0j, 1j) + self.assertRaises(ZeroDivisionError, pow, 0+0j, -1000) self.assertAlmostEqual(pow(1j, -1), 1/1j) self.assertAlmostEqual(pow(1j, 200), 1) self.assertRaises(ValueError, pow, 1+1j, 1+1j, 1+1j) self.assertRaises(OverflowError, pow, 1e200+1j, 1e200+1j) + self.assertRaises(TypeError, pow, 1j, None) + self.assertRaises(TypeError, pow, None, 1j) + self.assertAlmostEqual(pow(1j, 0.5), 0.7071067811865476+0.7071067811865475j) a = 3.33+4.43j self.assertEqual(a ** 0j, 1) @@ -301,6 +333,7 @@ def test_boolcontext(self): for i in range(100): self.assertTrue(complex(random() + 1e-6, random() + 1e-6)) self.assertTrue(not complex(0.0, 0.0)) + self.assertTrue(1j) def test_conjugate(self): self.assertClose(complex(5.3, 9.8).conjugate(), 5.3-9.8j) @@ -314,6 +347,8 @@ def __complex__(self): return self.value self.assertRaises(TypeError, complex, {}) self.assertRaises(TypeError, complex, NS(1.5)) self.assertRaises(TypeError, complex, NS(1)) + self.assertRaises(TypeError, complex, object()) + self.assertRaises(TypeError, complex, NS(4.25+0.5j), object()) self.assertAlmostEqual(complex("1+10j"), 1+10j) self.assertAlmostEqual(complex(10), 10+0j) @@ -359,6 +394,8 @@ def __complex__(self): return self.value self.assertAlmostEqual(complex('1e-500'), 0.0 + 0.0j) self.assertAlmostEqual(complex('-1e-500j'), 0.0 - 0.0j) self.assertAlmostEqual(complex('-1e-500+1e-500j'), -0.0 + 0.0j) + self.assertEqual(complex('1-1j'), 1.0 - 1j) + self.assertEqual(complex('1J'), 1j) class complex2(complex): pass self.assertAlmostEqual(complex(complex2(1+1j)), 1+1j) @@ -553,6 +590,8 @@ def test_hash(self): x /= 3.0 # now check against floating point self.assertEqual(hash(x), hash(complex(x, 0.))) + self.assertNotEqual(hash(2000005 - 1j), -1) + def test_abs(self): nums = [complex(x/3., y/7.) for x in range(-9,9) for y in range(-9,9)] for num in nums: @@ -602,6 +641,14 @@ def test(v, expected, test_fn=self.assertEqual): test(complex(-0., 0.), "(-0+0j)") test(complex(-0., -0.), "(-0-0j)") + def test_pos(self): + class ComplexSubclass(complex): + pass + + self.assertEqual(+(1+6j), 1+6j) + self.assertEqual(+ComplexSubclass(1, 6), 1+6j) + self.assertIs(type(+ComplexSubclass(1, 6)), complex) + def test_neg(self): self.assertEqual(-(1+6j), -1-6j) diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py index 60735ba89a80ee..89102373759ca0 100644 --- a/Lib/test/test_copy.py +++ b/Lib/test/test_copy.py @@ -952,7 +952,7 @@ class PointFromClass(NamedTuple): self.assertEqual(copy.replace(p, x=1), (1, 22)) self.assertEqual(copy.replace(p, y=2), (11, 2)) self.assertEqual(copy.replace(p, x=1, y=2), (1, 2)) - with self.assertRaisesRegex(ValueError, 'unexpected field name'): + with self.assertRaisesRegex(TypeError, 'unexpected field name'): copy.replace(p, x=1, error=2) def test_dataclass(self): diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 47145782c0f04f..25c981d1511bc1 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -2216,6 +2216,14 @@ async def f(): gen.cr_frame.clear() gen.close() + def test_cr_frame_after_close(self): + async def f(): + pass + gen = f() + self.assertIsNotNone(gen.cr_frame) + gen.close() + self.assertIsNone(gen.cr_frame) + def test_stack_in_coroutine_throw(self): # Regression test for https://github.com/python/cpython/issues/93592 async def a(): diff --git a/Lib/test/test_cppext/__init__.py b/Lib/test/test_cppext/__init__.py index 4d9ee3cb2228ae..c6039bd17b0662 100644 --- a/Lib/test/test_cppext/__init__.py +++ b/Lib/test/test_cppext/__init__.py @@ -2,7 +2,6 @@ # compatible with C++ and does not emit C++ compiler warnings. import os.path import shutil -import sys import unittest import subprocess import sysconfig @@ -15,7 +14,7 @@ # gh-110119: pip does not currently support 't' in the ABI flag use by # --disable-gil builds. Once it does, we can remove this skip. -@unittest.skipIf(sysconfig.get_config_var('Py_NOGIL') == 1, +@unittest.skipIf(support.Py_GIL_DISABLED, 'test does not work with --disable-gil') @support.requires_subprocess() class TestCPPExt(unittest.TestCase): diff --git a/Lib/test/test_ctypes/test_structures.py b/Lib/test/test_ctypes/test_structures.py index f05ee5e491a41e..57ae9240b3c165 100644 --- a/Lib/test/test_ctypes/test_structures.py +++ b/Lib/test/test_ctypes/test_structures.py @@ -1,8 +1,9 @@ import _ctypes_test +import platform import struct import sys import unittest -from ctypes import (CDLL, Structure, Union, POINTER, sizeof, byref, alignment, +from ctypes import (CDLL, Array, Structure, Union, POINTER, sizeof, byref, alignment, c_void_p, c_char, c_wchar, c_byte, c_ubyte, c_uint8, c_uint16, c_uint32, c_short, c_ushort, c_int, c_uint, @@ -494,12 +495,14 @@ class Test3B(Test3A): ('more_data', c_float * 2), ] + # Load the shared library + dll = CDLL(_ctypes_test.__file__) + s = Test2() expected = 0 for i in range(16): s.data[i] = i expected += i - dll = CDLL(_ctypes_test.__file__) func = dll._testfunc_array_in_struct1 func.restype = c_int func.argtypes = (Test2,) @@ -540,6 +543,130 @@ class Test3B(Test3A): self.assertAlmostEqual(s.more_data[0], -3.0, places=6) self.assertAlmostEqual(s.more_data[1], -2.0, places=6) + @unittest.skipIf( + 'ppc64le' in platform.uname().machine, + "gh-110190: currently fails on ppc64le", + ) + def test_array_in_struct_registers(self): + dll = CDLL(_ctypes_test.__file__) + + class Test3C1(Structure): + _fields_ = [ + ("data", c_double * 4) + ] + + class DataType4(Array): + _type_ = c_double + _length_ = 4 + + class Test3C2(Structure): + _fields_ = [ + ("data", DataType4) + ] + + class Test3C3(Structure): + _fields_ = [ + ("x", c_double), + ("y", c_double), + ("z", c_double), + ("t", c_double) + ] + + class Test3D1(Structure): + _fields_ = [ + ("data", c_double * 5) + ] + + class DataType5(Array): + _type_ = c_double + _length_ = 5 + + class Test3D2(Structure): + _fields_ = [ + ("data", DataType5) + ] + + class Test3D3(Structure): + _fields_ = [ + ("x", c_double), + ("y", c_double), + ("z", c_double), + ("t", c_double), + ("u", c_double) + ] + + # Tests for struct Test3C + expected = (1.0, 2.0, 3.0, 4.0) + func = dll._testfunc_array_in_struct_set_defaults_3C + func.restype = Test3C1 + result = func() + # check the default values have been set properly + self.assertEqual( + (result.data[0], + result.data[1], + result.data[2], + result.data[3]), + expected + ) + + func = dll._testfunc_array_in_struct_set_defaults_3C + func.restype = Test3C2 + result = func() + # check the default values have been set properly + self.assertEqual( + (result.data[0], + result.data[1], + result.data[2], + result.data[3]), + expected + ) + + func = dll._testfunc_array_in_struct_set_defaults_3C + func.restype = Test3C3 + result = func() + # check the default values have been set properly + self.assertEqual((result.x, result.y, result.z, result.t), expected) + + # Tests for struct Test3D + expected = (1.0, 2.0, 3.0, 4.0, 5.0) + func = dll._testfunc_array_in_struct_set_defaults_3D + func.restype = Test3D1 + result = func() + # check the default values have been set properly + self.assertEqual( + (result.data[0], + result.data[1], + result.data[2], + result.data[3], + result.data[4]), + expected + ) + + func = dll._testfunc_array_in_struct_set_defaults_3D + func.restype = Test3D2 + result = func() + # check the default values have been set properly + self.assertEqual( + (result.data[0], + result.data[1], + result.data[2], + result.data[3], + result.data[4]), + expected + ) + + func = dll._testfunc_array_in_struct_set_defaults_3D + func.restype = Test3D3 + result = func() + # check the default values have been set properly + self.assertEqual( + (result.x, + result.y, + result.z, + result.t, + result.u), + expected) + def test_38368(self): class U(Union): _fields_ = [ diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 43debdcf6e1607..349790ecd7d075 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -42,45 +42,45 @@ def cm(cls, x): cls.x = x == 1 dis_c_instance_method = """\ -%3d RESUME 0 - -%3d LOAD_FAST 1 (x) - LOAD_CONST 1 (1) - COMPARE_OP 72 (==) - LOAD_FAST 0 (self) - STORE_ATTR 0 (x) - RETURN_CONST 0 (None) +%3d RESUME 0 + +%3d LOAD_FAST 1 (x) + LOAD_CONST 1 (1) + COMPARE_OP 72 (==) + LOAD_FAST 0 (self) + STORE_ATTR 0 (x) + RETURN_CONST 0 (None) """ % (_C.__init__.__code__.co_firstlineno, _C.__init__.__code__.co_firstlineno + 1,) dis_c_instance_method_bytes = """\ - RESUME 0 - LOAD_FAST 1 - LOAD_CONST 1 - COMPARE_OP 72 (==) - LOAD_FAST 0 - STORE_ATTR 0 - RETURN_CONST 0 + RESUME 0 + LOAD_FAST 1 + LOAD_CONST 1 + COMPARE_OP 72 (==) + LOAD_FAST 0 + STORE_ATTR 0 + RETURN_CONST 0 """ dis_c_class_method = """\ -%3d RESUME 0 - -%3d LOAD_FAST 1 (x) - LOAD_CONST 1 (1) - COMPARE_OP 72 (==) - LOAD_FAST 0 (cls) - STORE_ATTR 0 (x) - RETURN_CONST 0 (None) +%3d RESUME 0 + +%3d LOAD_FAST 1 (x) + LOAD_CONST 1 (1) + COMPARE_OP 72 (==) + LOAD_FAST 0 (cls) + STORE_ATTR 0 (x) + RETURN_CONST 0 (None) """ % (_C.cm.__code__.co_firstlineno, _C.cm.__code__.co_firstlineno + 2,) dis_c_static_method = """\ -%3d RESUME 0 +%3d RESUME 0 -%3d LOAD_FAST 0 (x) - LOAD_CONST 1 (1) - COMPARE_OP 72 (==) - STORE_FAST 0 (x) - RETURN_CONST 0 (None) +%3d LOAD_FAST 0 (x) + LOAD_CONST 1 (1) + COMPARE_OP 72 (==) + STORE_FAST 0 (x) + RETURN_CONST 0 (None) """ % (_C.sm.__code__.co_firstlineno, _C.sm.__code__.co_firstlineno + 2,) # Class disassembling info has an extra newline at end. @@ -100,51 +100,63 @@ def _f(a): return 1 dis_f = """\ -%3d RESUME 0 +%3d RESUME 0 -%3d LOAD_GLOBAL 1 (print + NULL) - LOAD_FAST 0 (a) - CALL 1 - POP_TOP +%3d LOAD_GLOBAL 1 (print + NULL) + LOAD_FAST 0 (a) + CALL 1 + POP_TOP -%3d RETURN_CONST 1 (1) +%3d RETURN_CONST 1 (1) +""" % (_f.__code__.co_firstlineno, + _f.__code__.co_firstlineno + 1, + _f.__code__.co_firstlineno + 2) + +dis_f_with_offsets = """\ +%3d 0 RESUME 0 + +%3d 2 LOAD_GLOBAL 1 (print + NULL) + 12 LOAD_FAST 0 (a) + 14 CALL 1 + 22 POP_TOP + +%3d 24 RETURN_CONST 1 (1) """ % (_f.__code__.co_firstlineno, _f.__code__.co_firstlineno + 1, _f.__code__.co_firstlineno + 2) dis_f_co_code = """\ - RESUME 0 - LOAD_GLOBAL 1 - LOAD_FAST 0 - CALL 1 - POP_TOP - RETURN_CONST 1 + RESUME 0 + LOAD_GLOBAL 1 + LOAD_FAST 0 + CALL 1 + POP_TOP + RETURN_CONST 1 """ - def bug708901(): for res in range(1, 10): pass dis_bug708901 = """\ -%3d RESUME 0 +%3d RESUME 0 -%3d LOAD_GLOBAL 1 (range + NULL) - LOAD_CONST 1 (1) +%3d LOAD_GLOBAL 1 (range + NULL) + LOAD_CONST 1 (1) -%3d LOAD_CONST 2 (10) +%3d LOAD_CONST 2 (10) -%3d CALL 2 - GET_ITER - >> FOR_ITER 3 (to 36) - STORE_FAST 0 (res) +%3d CALL 2 + GET_ITER + L1: FOR_ITER 3 (to L2) + STORE_FAST 0 (res) -%3d JUMP_BACKWARD 5 (to 26) +%3d JUMP_BACKWARD 5 (to L1) -%3d >> END_FOR - RETURN_CONST 0 (None) +%3d L2: END_FOR + RETURN_CONST 0 (None) """ % (bug708901.__code__.co_firstlineno, bug708901.__code__.co_firstlineno + 1, bug708901.__code__.co_firstlineno + 2, @@ -159,20 +171,20 @@ def bug1333982(x=[]): pass dis_bug1333982 = """\ -%3d RESUME 0 +%3d RESUME 0 -%3d LOAD_ASSERTION_ERROR - LOAD_CONST 1 ( at 0x..., file "%s", line %d>) - MAKE_FUNCTION - LOAD_FAST 0 (x) - GET_ITER - CALL 0 +%3d LOAD_ASSERTION_ERROR + LOAD_CONST 1 ( at 0x..., file "%s", line %d>) + MAKE_FUNCTION + LOAD_FAST 0 (x) + GET_ITER + CALL 0 -%3d LOAD_CONST 2 (1) +%3d LOAD_CONST 2 (1) -%3d BINARY_OP 0 (+) - CALL 0 - RAISE_VARARGS 1 +%3d BINARY_OP 0 (+) + CALL 0 + RAISE_VARARGS 1 """ % (bug1333982.__code__.co_firstlineno, bug1333982.__code__.co_firstlineno + 1, __file__, @@ -190,8 +202,8 @@ def bug42562(): dis_bug42562 = """\ - RESUME 0 - RETURN_CONST 0 (None) + RESUME 0 + RETURN_CONST 0 (None) """ # Extended arg followed by NOP @@ -204,11 +216,11 @@ def bug42562(): ]) dis_bug_45757 = """\ - EXTENDED_ARG 1 - NOP - EXTENDED_ARG 1 - LOAD_CONST 297 - RETURN_VALUE + EXTENDED_ARG 1 + NOP + EXTENDED_ARG 1 + LOAD_CONST 297 + RETURN_VALUE """ # [255, 255, 255, 252] is -4 in a 4 byte signed integer @@ -221,10 +233,10 @@ def bug42562(): dis_bug46724 = """\ - >> EXTENDED_ARG 255 - EXTENDED_ARG 65535 - EXTENDED_ARG 16777215 - JUMP_FORWARD -4 (to 0) + L1: EXTENDED_ARG 255 + EXTENDED_ARG 65535 + EXTENDED_ARG 16777215 + JUMP_FORWARD -4 (to L1) """ def func_w_kwargs(a, b, **c): @@ -234,96 +246,96 @@ def wrap_func_w_kwargs(): func_w_kwargs(1, 2, c=5) dis_kw_names = """\ -%3d RESUME 0 - -%3d LOAD_GLOBAL 1 (func_w_kwargs + NULL) - LOAD_CONST 1 (1) - LOAD_CONST 2 (2) - LOAD_CONST 3 (5) - LOAD_CONST 4 (('c',)) - CALL_KW 3 - POP_TOP - RETURN_CONST 0 (None) +%3d RESUME 0 + +%3d LOAD_GLOBAL 1 (func_w_kwargs + NULL) + LOAD_CONST 1 (1) + LOAD_CONST 2 (2) + LOAD_CONST 3 (5) + LOAD_CONST 4 (('c',)) + CALL_KW 3 + POP_TOP + RETURN_CONST 0 (None) """ % (wrap_func_w_kwargs.__code__.co_firstlineno, wrap_func_w_kwargs.__code__.co_firstlineno + 1) dis_intrinsic_1_2 = """\ - 0 RESUME 0 - - 1 LOAD_CONST 0 (0) - LOAD_CONST 1 (('*',)) - IMPORT_NAME 0 (math) - CALL_INTRINSIC_1 2 (INTRINSIC_IMPORT_STAR) - POP_TOP - RETURN_CONST 2 (None) + 0 RESUME 0 + + 1 LOAD_CONST 0 (0) + LOAD_CONST 1 (('*',)) + IMPORT_NAME 0 (math) + CALL_INTRINSIC_1 2 (INTRINSIC_IMPORT_STAR) + POP_TOP + RETURN_CONST 2 (None) """ dis_intrinsic_1_5 = """\ - 0 RESUME 0 + 0 RESUME 0 - 1 LOAD_NAME 0 (a) - CALL_INTRINSIC_1 5 (INTRINSIC_UNARY_POSITIVE) - RETURN_VALUE + 1 LOAD_NAME 0 (a) + CALL_INTRINSIC_1 5 (INTRINSIC_UNARY_POSITIVE) + RETURN_VALUE """ dis_intrinsic_1_6 = """\ - 0 RESUME 0 + 0 RESUME 0 - 1 BUILD_LIST 0 - LOAD_NAME 0 (a) - LIST_EXTEND 1 - CALL_INTRINSIC_1 6 (INTRINSIC_LIST_TO_TUPLE) - RETURN_VALUE + 1 BUILD_LIST 0 + LOAD_NAME 0 (a) + LIST_EXTEND 1 + CALL_INTRINSIC_1 6 (INTRINSIC_LIST_TO_TUPLE) + RETURN_VALUE """ _BIG_LINENO_FORMAT = """\ - 1 RESUME 0 + 1 RESUME 0 -%3d LOAD_GLOBAL 0 (spam) - POP_TOP - RETURN_CONST 0 (None) +%3d LOAD_GLOBAL 0 (spam) + POP_TOP + RETURN_CONST 0 (None) """ _BIG_LINENO_FORMAT2 = """\ - 1 RESUME 0 + 1 RESUME 0 -%4d LOAD_GLOBAL 0 (spam) - POP_TOP - RETURN_CONST 0 (None) +%4d LOAD_GLOBAL 0 (spam) + POP_TOP + RETURN_CONST 0 (None) """ dis_module_expected_results = """\ Disassembly of f: - 4 RESUME 0 - RETURN_CONST 0 (None) + 4 RESUME 0 + RETURN_CONST 0 (None) Disassembly of g: - 5 RESUME 0 - RETURN_CONST 0 (None) + 5 RESUME 0 + RETURN_CONST 0 (None) """ expr_str = "x + 1" dis_expr_str = """\ - 0 RESUME 0 + 0 RESUME 0 - 1 LOAD_NAME 0 (x) - LOAD_CONST 0 (1) - BINARY_OP 0 (+) - RETURN_VALUE + 1 LOAD_NAME 0 (x) + LOAD_CONST 0 (1) + BINARY_OP 0 (+) + RETURN_VALUE """ simple_stmt_str = "x = x + 1" dis_simple_stmt_str = """\ - 0 RESUME 0 + 0 RESUME 0 - 1 LOAD_NAME 0 (x) - LOAD_CONST 0 (1) - BINARY_OP 0 (+) - STORE_NAME 0 (x) - RETURN_CONST 1 (None) + 1 LOAD_NAME 0 (x) + LOAD_CONST 0 (1) + BINARY_OP 0 (+) + STORE_NAME 0 (x) + RETURN_CONST 1 (None) """ annot_stmt_str = """\ @@ -335,34 +347,34 @@ def wrap_func_w_kwargs(): # leading newline is for a reason (tests lineno) dis_annot_stmt_str = """\ - 0 RESUME 0 - - 2 SETUP_ANNOTATIONS - LOAD_CONST 0 (1) - STORE_NAME 0 (x) - LOAD_NAME 1 (int) - LOAD_NAME 2 (__annotations__) - LOAD_CONST 1 ('x') - STORE_SUBSCR - - 3 LOAD_NAME 3 (fun) - PUSH_NULL - LOAD_CONST 0 (1) - CALL 1 - LOAD_NAME 2 (__annotations__) - LOAD_CONST 2 ('y') - STORE_SUBSCR - - 4 LOAD_CONST 0 (1) - LOAD_NAME 4 (lst) - LOAD_NAME 3 (fun) - PUSH_NULL - LOAD_CONST 3 (0) - CALL 1 - STORE_SUBSCR - LOAD_NAME 1 (int) - POP_TOP - RETURN_CONST 4 (None) + 0 RESUME 0 + + 2 SETUP_ANNOTATIONS + LOAD_CONST 0 (1) + STORE_NAME 0 (x) + LOAD_NAME 1 (int) + LOAD_NAME 2 (__annotations__) + LOAD_CONST 1 ('x') + STORE_SUBSCR + + 3 LOAD_NAME 3 (fun) + PUSH_NULL + LOAD_CONST 0 (1) + CALL 1 + LOAD_NAME 2 (__annotations__) + LOAD_CONST 2 ('y') + STORE_SUBSCR + + 4 LOAD_CONST 0 (1) + LOAD_NAME 4 (lst) + LOAD_NAME 3 (fun) + PUSH_NULL + LOAD_CONST 3 (0) + CALL 1 + STORE_SUBSCR + LOAD_NAME 1 (int) + POP_TOP + RETURN_CONST 4 (None) """ compound_stmt_str = """\ @@ -372,64 +384,67 @@ def wrap_func_w_kwargs(): # Trailing newline has been deliberately omitted dis_compound_stmt_str = """\ - 0 RESUME 0 + 0 RESUME 0 - 1 LOAD_CONST 0 (0) - STORE_NAME 0 (x) + 1 LOAD_CONST 0 (0) + STORE_NAME 0 (x) - 2 NOP + 2 NOP - 3 >> LOAD_NAME 0 (x) - LOAD_CONST 1 (1) - BINARY_OP 13 (+=) - STORE_NAME 0 (x) + 3 L1: LOAD_NAME 0 (x) + LOAD_CONST 1 (1) + BINARY_OP 13 (+=) + STORE_NAME 0 (x) - 2 JUMP_BACKWARD 7 (to 8) + 2 JUMP_BACKWARD 7 (to L1) """ dis_traceback = """\ -%4d RESUME 0 +%4d RESUME 0 -%4d NOP +%4d NOP -%4d LOAD_CONST 1 (1) - LOAD_CONST 2 (0) - --> BINARY_OP 11 (/) - POP_TOP +%4d L1: LOAD_CONST 1 (1) + LOAD_CONST 2 (0) + --> BINARY_OP 11 (/) + POP_TOP -%4d LOAD_FAST_CHECK 1 (tb) - RETURN_VALUE +%4d L2: LOAD_FAST_CHECK 1 (tb) + RETURN_VALUE -None >> PUSH_EXC_INFO + -- L3: PUSH_EXC_INFO -%4d LOAD_GLOBAL 0 (Exception) - CHECK_EXC_MATCH - POP_JUMP_IF_FALSE 23 (to 82) - STORE_FAST 0 (e) +%4d LOAD_GLOBAL 0 (Exception) + CHECK_EXC_MATCH + POP_JUMP_IF_FALSE 23 (to L7) + STORE_FAST 0 (e) -%4d LOAD_FAST 0 (e) - LOAD_ATTR 2 (__traceback__) - STORE_FAST 1 (tb) - POP_EXCEPT - LOAD_CONST 0 (None) - STORE_FAST 0 (e) - DELETE_FAST 0 (e) +%4d L4: LOAD_FAST 0 (e) + LOAD_ATTR 2 (__traceback__) + STORE_FAST 1 (tb) + L5: POP_EXCEPT + LOAD_CONST 0 (None) + STORE_FAST 0 (e) + DELETE_FAST 0 (e) -%4d LOAD_FAST 1 (tb) - RETURN_VALUE +%4d LOAD_FAST 1 (tb) + RETURN_VALUE -None >> LOAD_CONST 0 (None) - STORE_FAST 0 (e) - DELETE_FAST 0 (e) - RERAISE 1 + -- L6: LOAD_CONST 0 (None) + STORE_FAST 0 (e) + DELETE_FAST 0 (e) + RERAISE 1 -%4d >> RERAISE 0 +%4d L7: RERAISE 0 -None >> COPY 3 - POP_EXCEPT - RERAISE 1 + -- L8: COPY 3 + POP_EXCEPT + RERAISE 1 ExceptionTable: -4 rows + L1 to L2 -> L3 [0] + L3 to L4 -> L8 [1] lasti + L4 to L5 -> L6 [1] lasti + L6 to L8 -> L8 [1] lasti """ % (TRACEBACK_CODE.co_firstlineno, TRACEBACK_CODE.co_firstlineno + 1, TRACEBACK_CODE.co_firstlineno + 2, @@ -443,25 +458,25 @@ def _fstring(a, b, c, d): return f'{a} {b:4} {c!r} {d!r:4}' dis_fstring = """\ -%3d RESUME 0 - -%3d LOAD_FAST 0 (a) - FORMAT_SIMPLE - LOAD_CONST 1 (' ') - LOAD_FAST 1 (b) - LOAD_CONST 2 ('4') - FORMAT_WITH_SPEC - LOAD_CONST 1 (' ') - LOAD_FAST 2 (c) - CONVERT_VALUE 2 (repr) - FORMAT_SIMPLE - LOAD_CONST 1 (' ') - LOAD_FAST 3 (d) - CONVERT_VALUE 2 (repr) - LOAD_CONST 2 ('4') - FORMAT_WITH_SPEC - BUILD_STRING 7 - RETURN_VALUE +%3d RESUME 0 + +%3d LOAD_FAST 0 (a) + FORMAT_SIMPLE + LOAD_CONST 1 (' ') + LOAD_FAST 1 (b) + LOAD_CONST 2 ('4') + FORMAT_WITH_SPEC + LOAD_CONST 1 (' ') + LOAD_FAST 2 (c) + CONVERT_VALUE 2 (repr) + FORMAT_SIMPLE + LOAD_CONST 1 (' ') + LOAD_FAST 3 (d) + CONVERT_VALUE 2 (repr) + LOAD_CONST 2 ('4') + FORMAT_WITH_SPEC + BUILD_STRING 7 + RETURN_VALUE """ % (_fstring.__code__.co_firstlineno, _fstring.__code__.co_firstlineno + 1) def _with(c): @@ -470,44 +485,45 @@ def _with(c): y = 2 dis_with = """\ -%4d RESUME 0 - -%4d LOAD_FAST 0 (c) - BEFORE_WITH - POP_TOP - -%4d LOAD_CONST 1 (1) - STORE_FAST 1 (x) - -%4d LOAD_CONST 0 (None) - LOAD_CONST 0 (None) - LOAD_CONST 0 (None) - CALL 2 - POP_TOP - -%4d LOAD_CONST 2 (2) - STORE_FAST 2 (y) - RETURN_CONST 0 (None) - -%4d >> PUSH_EXC_INFO - WITH_EXCEPT_START - TO_BOOL - POP_JUMP_IF_TRUE 1 (to 52) - RERAISE 2 - >> POP_TOP - POP_EXCEPT - POP_TOP - POP_TOP - -%4d LOAD_CONST 2 (2) - STORE_FAST 2 (y) - RETURN_CONST 0 (None) - -None >> COPY 3 - POP_EXCEPT - RERAISE 1 +%4d RESUME 0 + +%4d LOAD_FAST 0 (c) + BEFORE_WITH + L1: POP_TOP + +%4d LOAD_CONST 1 (1) + STORE_FAST 1 (x) + +%4d L2: LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + CALL 2 + POP_TOP + +%4d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + RETURN_CONST 0 (None) + +%4d L3: PUSH_EXC_INFO + WITH_EXCEPT_START + TO_BOOL + POP_JUMP_IF_TRUE 1 (to L4) + RERAISE 2 + L4: POP_TOP + L5: POP_EXCEPT + POP_TOP + POP_TOP + +%4d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + RETURN_CONST 0 (None) + + -- L6: COPY 3 + POP_EXCEPT + RERAISE 1 ExceptionTable: -2 rows + L1 to L2 -> L3 [1] lasti + L3 to L5 -> L6 [3] lasti """ % (_with.__code__.co_firstlineno, _with.__code__.co_firstlineno + 1, _with.__code__.co_firstlineno + 2, @@ -523,78 +539,89 @@ async def _asyncwith(c): y = 2 dis_asyncwith = """\ -%4d RETURN_GENERATOR - POP_TOP - RESUME 0 - -%4d LOAD_FAST 0 (c) - BEFORE_ASYNC_WITH - GET_AWAITABLE 1 - LOAD_CONST 0 (None) - >> SEND 3 (to 24) - YIELD_VALUE 1 - RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 5 (to 14) - >> END_SEND - POP_TOP - -%4d LOAD_CONST 1 (1) - STORE_FAST 1 (x) - -%4d LOAD_CONST 0 (None) - LOAD_CONST 0 (None) - LOAD_CONST 0 (None) - CALL 2 - GET_AWAITABLE 2 - LOAD_CONST 0 (None) - >> SEND 3 (to 60) - YIELD_VALUE 1 - RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 5 (to 50) - >> END_SEND - POP_TOP - -%4d LOAD_CONST 2 (2) - STORE_FAST 2 (y) - RETURN_CONST 0 (None) - -%4d >> CLEANUP_THROW - -None JUMP_BACKWARD 26 (to 24) - -%4d >> CLEANUP_THROW - -None JUMP_BACKWARD 11 (to 60) - -%4d >> PUSH_EXC_INFO - WITH_EXCEPT_START - GET_AWAITABLE 2 - LOAD_CONST 0 (None) - >> SEND 4 (to 102) - YIELD_VALUE 1 - RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 5 (to 90) - >> CLEANUP_THROW - >> END_SEND - TO_BOOL - POP_JUMP_IF_TRUE 1 (to 118) - RERAISE 2 - >> POP_TOP - POP_EXCEPT - POP_TOP - POP_TOP - -%4d LOAD_CONST 2 (2) - STORE_FAST 2 (y) - RETURN_CONST 0 (None) - -None >> COPY 3 - POP_EXCEPT - RERAISE 1 - >> CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR) - RERAISE 1 +%4d RETURN_GENERATOR + POP_TOP + L1: RESUME 0 + +%4d LOAD_FAST 0 (c) + BEFORE_ASYNC_WITH + GET_AWAITABLE 1 + LOAD_CONST 0 (None) + L2: SEND 3 (to L5) + L3: YIELD_VALUE 1 + L4: RESUME 3 + JUMP_BACKWARD_NO_INTERRUPT 5 (to L2) + L5: END_SEND + L6: POP_TOP + +%4d LOAD_CONST 1 (1) + STORE_FAST 1 (x) + +%4d L7: LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + LOAD_CONST 0 (None) + CALL 2 + GET_AWAITABLE 2 + LOAD_CONST 0 (None) + L8: SEND 3 (to L11) + L9: YIELD_VALUE 1 + L10: RESUME 3 + JUMP_BACKWARD_NO_INTERRUPT 5 (to L8) + L11: END_SEND + POP_TOP + +%4d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + RETURN_CONST 0 (None) + +%4d L12: CLEANUP_THROW + + -- L13: JUMP_BACKWARD 26 (to L5) + +%4d L14: CLEANUP_THROW + + -- L15: JUMP_BACKWARD 11 (to L11) + +%4d L16: PUSH_EXC_INFO + WITH_EXCEPT_START + GET_AWAITABLE 2 + LOAD_CONST 0 (None) + L17: SEND 4 (to L21) + L18: YIELD_VALUE 1 + L19: RESUME 3 + JUMP_BACKWARD_NO_INTERRUPT 5 (to L17) + L20: CLEANUP_THROW + L21: END_SEND + TO_BOOL + POP_JUMP_IF_TRUE 1 (to L22) + RERAISE 2 + L22: POP_TOP + L23: POP_EXCEPT + POP_TOP + POP_TOP + +%4d LOAD_CONST 2 (2) + STORE_FAST 2 (y) + RETURN_CONST 0 (None) + + -- L24: COPY 3 + POP_EXCEPT + RERAISE 1 + L25: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR) + RERAISE 1 ExceptionTable: -12 rows + L1 to L3 -> L25 [0] lasti + L3 to L4 -> L12 [3] + L4 to L6 -> L25 [0] lasti + L6 to L7 -> L16 [1] lasti + L7 to L9 -> L25 [0] lasti + L9 to L10 -> L14 [2] + L10 to L13 -> L25 [0] lasti + L14 to L15 -> L25 [0] lasti + L16 to L18 -> L24 [3] lasti + L18 to L19 -> L20 [6] + L19 to L23 -> L24 [3] lasti + L23 to L25 -> L25 [0] lasti """ % (_asyncwith.__code__.co_firstlineno, _asyncwith.__code__.co_firstlineno + 1, _asyncwith.__code__.co_firstlineno + 2, @@ -620,31 +647,32 @@ def _tryfinallyconst(b): b() dis_tryfinally = """\ -%4d RESUME 0 +%4d RESUME 0 -%4d NOP +%4d NOP -%4d LOAD_FAST 0 (a) +%4d L1: LOAD_FAST 0 (a) -%4d LOAD_FAST 1 (b) - PUSH_NULL - CALL 0 - POP_TOP - RETURN_VALUE +%4d L2: LOAD_FAST 1 (b) + PUSH_NULL + CALL 0 + POP_TOP + RETURN_VALUE -None >> PUSH_EXC_INFO + -- L3: PUSH_EXC_INFO -%4d LOAD_FAST 1 (b) - PUSH_NULL - CALL 0 - POP_TOP - RERAISE 0 +%4d LOAD_FAST 1 (b) + PUSH_NULL + CALL 0 + POP_TOP + RERAISE 0 -None >> COPY 3 - POP_EXCEPT - RERAISE 1 + -- L4: COPY 3 + POP_EXCEPT + RERAISE 1 ExceptionTable: -2 rows + L1 to L2 -> L3 [0] + L3 to L4 -> L4 [1] lasti """ % (_tryfinally.__code__.co_firstlineno, _tryfinally.__code__.co_firstlineno + 1, _tryfinally.__code__.co_firstlineno + 2, @@ -653,31 +681,31 @@ def _tryfinallyconst(b): ) dis_tryfinallyconst = """\ -%4d RESUME 0 +%4d RESUME 0 -%4d NOP +%4d NOP -%4d NOP +%4d NOP -%4d LOAD_FAST 0 (b) - PUSH_NULL - CALL 0 - POP_TOP - RETURN_CONST 1 (1) +%4d LOAD_FAST 0 (b) + PUSH_NULL + CALL 0 + POP_TOP + RETURN_CONST 1 (1) -None PUSH_EXC_INFO + -- L1: PUSH_EXC_INFO -%4d LOAD_FAST 0 (b) - PUSH_NULL - CALL 0 - POP_TOP - RERAISE 0 +%4d LOAD_FAST 0 (b) + PUSH_NULL + CALL 0 + POP_TOP + RERAISE 0 -None >> COPY 3 - POP_EXCEPT - RERAISE 1 + -- L2: COPY 3 + POP_EXCEPT + RERAISE 1 ExceptionTable: -1 row + L1 to L2 -> L2 [1] lasti """ % (_tryfinallyconst.__code__.co_firstlineno, _tryfinallyconst.__code__.co_firstlineno + 1, _tryfinallyconst.__code__.co_firstlineno + 2, @@ -702,19 +730,19 @@ def foo(x): return foo dis_nested_0 = """\ -None MAKE_CELL 0 (y) + -- MAKE_CELL 0 (y) -%4d RESUME 0 +%4d RESUME 0 -%4d LOAD_FAST 0 (y) - BUILD_TUPLE 1 - LOAD_CONST 1 () - MAKE_FUNCTION - SET_FUNCTION_ATTRIBUTE 8 (closure) - STORE_FAST 1 (foo) +%4d LOAD_FAST 0 (y) + BUILD_TUPLE 1 + LOAD_CONST 1 () + MAKE_FUNCTION + SET_FUNCTION_ATTRIBUTE 8 (closure) + STORE_FAST 1 (foo) -%4d LOAD_FAST 1 (foo) - RETURN_VALUE +%4d LOAD_FAST 1 (foo) + RETURN_VALUE """ % (_h.__code__.co_firstlineno, _h.__code__.co_firstlineno + 1, __file__, @@ -724,22 +752,22 @@ def foo(x): dis_nested_1 = """%s Disassembly of : -None COPY_FREE_VARS 1 - MAKE_CELL 0 (x) - -%4d RESUME 0 - -%4d LOAD_GLOBAL 1 (list + NULL) - LOAD_FAST 0 (x) - BUILD_TUPLE 1 - LOAD_CONST 1 ( at 0x..., file "%s", line %d>) - MAKE_FUNCTION - SET_FUNCTION_ATTRIBUTE 8 (closure) - LOAD_DEREF 1 (y) - GET_ITER - CALL 0 - CALL 1 - RETURN_VALUE + -- COPY_FREE_VARS 1 + MAKE_CELL 0 (x) + +%4d RESUME 0 + +%4d LOAD_GLOBAL 1 (list + NULL) + LOAD_FAST 0 (x) + BUILD_TUPLE 1 + LOAD_CONST 1 ( at 0x..., file "%s", line %d>) + MAKE_FUNCTION + SET_FUNCTION_ATTRIBUTE 8 (closure) + LOAD_DEREF 1 (y) + GET_ITER + CALL 0 + CALL 1 + RETURN_VALUE """ % (dis_nested_0, __file__, _h.__code__.co_firstlineno + 1, @@ -751,28 +779,28 @@ def foo(x): dis_nested_2 = """%s Disassembly of at 0x..., file "%s", line %d>: -None COPY_FREE_VARS 1 - -%4d RETURN_GENERATOR - POP_TOP - RESUME 0 - LOAD_FAST 0 (.0) - >> FOR_ITER 10 (to 34) - STORE_FAST 1 (z) - LOAD_DEREF 2 (x) - LOAD_FAST 1 (z) - BINARY_OP 0 (+) - YIELD_VALUE 0 - RESUME 5 - POP_TOP - JUMP_BACKWARD 12 (to 10) - >> END_FOR - RETURN_CONST 0 (None) - -None >> CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR) - RERAISE 1 + -- COPY_FREE_VARS 1 + +%4d RETURN_GENERATOR + POP_TOP + L1: RESUME 0 + LOAD_FAST 0 (.0) + L2: FOR_ITER 10 (to L3) + STORE_FAST 1 (z) + LOAD_DEREF 2 (x) + LOAD_FAST 1 (z) + BINARY_OP 0 (+) + YIELD_VALUE 0 + RESUME 5 + POP_TOP + JUMP_BACKWARD 12 (to L2) + L3: END_FOR + RETURN_CONST 0 (None) + + -- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR) + RERAISE 1 ExceptionTable: -1 row + L1 to L4 -> L4 [0] lasti """ % (dis_nested_1, __file__, _h.__code__.co_firstlineno + 3, @@ -784,14 +812,14 @@ def load_test(x, y=0): return a, b dis_load_test_quickened_code = """\ -%3d 0 RESUME_CHECK 0 +%3d RESUME_CHECK 0 -%3d 2 LOAD_FAST_LOAD_FAST 1 (x, y) - 4 STORE_FAST_STORE_FAST 50 (b, a) +%3d LOAD_FAST_LOAD_FAST 1 (x, y) + STORE_FAST_STORE_FAST 50 (b, a) -%3d 6 LOAD_FAST_LOAD_FAST 35 (a, b) - 8 BUILD_TUPLE 2 - 10 RETURN_VALUE +%3d LOAD_FAST_LOAD_FAST 35 (a, b) + BUILD_TUPLE 2 + RETURN_VALUE """ % (load_test.__code__.co_firstlineno, load_test.__code__.co_firstlineno + 1, load_test.__code__.co_firstlineno + 2) @@ -801,25 +829,25 @@ def loop_test(): load_test(i) dis_loop_test_quickened_code = """\ -%3d RESUME_CHECK 0 - -%3d BUILD_LIST 0 - LOAD_CONST 1 ((1, 2, 3)) - LIST_EXTEND 1 - LOAD_CONST 2 (3) - BINARY_OP 5 (*) - GET_ITER - >> FOR_ITER_LIST 14 (to 48) - STORE_FAST 0 (i) - -%3d LOAD_GLOBAL_MODULE 1 (load_test + NULL) - LOAD_FAST 0 (i) - CALL_PY_WITH_DEFAULTS 1 - POP_TOP - JUMP_BACKWARD 16 (to 16) - -%3d >> END_FOR - RETURN_CONST 0 (None) +%3d RESUME_CHECK 0 + +%3d BUILD_LIST 0 + LOAD_CONST 1 ((1, 2, 3)) + LIST_EXTEND 1 + LOAD_CONST 2 (3) + BINARY_OP 5 (*) + GET_ITER + L1: FOR_ITER_LIST 14 (to L2) + STORE_FAST 0 (i) + +%3d LOAD_GLOBAL_MODULE 1 (load_test + NULL) + LOAD_FAST 0 (i) + CALL_PY_WITH_DEFAULTS 1 + POP_TOP + JUMP_BACKWARD 16 (to L1) + +%3d L2: END_FOR + RETURN_CONST 0 (None) """ % (loop_test.__code__.co_firstlineno, loop_test.__code__.co_firstlineno + 1, loop_test.__code__.co_firstlineno + 2, @@ -829,14 +857,14 @@ def extended_arg_quick(): *_, _ = ... dis_extended_arg_quick_code = """\ -%3d 0 RESUME 0 - -%3d 2 LOAD_CONST 1 (Ellipsis) - 4 EXTENDED_ARG 1 - 6 UNPACK_EX 256 - 8 POP_TOP - 10 STORE_FAST 0 (_) - 12 RETURN_CONST 0 (None) +%3d RESUME 0 + +%3d LOAD_CONST 1 (Ellipsis) + EXTENDED_ARG 1 + UNPACK_EX 256 + POP_TOP + STORE_FAST 0 (_) + RETURN_CONST 0 (None) """% (extended_arg_quick.__code__.co_firstlineno, extended_arg_quick.__code__.co_firstlineno + 1,) @@ -848,74 +876,19 @@ class DisTestBase(unittest.TestCase): def strip_addresses(self, text): return re.sub(r'\b0x[0-9A-Fa-f]+\b', '0x...', text) - def find_offset_column(self, lines): - for line in lines: - if line and not line.startswith("Disassembly"): - break - else: - return 0, 0 - offset = 5 - while (line[offset] == " "): - offset += 1 - if (line[offset] == ">"): - offset += 2 - while (line[offset] == " "): - offset += 1 - end = offset - while line[end] in "0123456789": - end += 1 - return end-5, end - - def assert_offsets_increasing(self, text, delta): - expected_offset = 0 - lines = text.splitlines() - start, end = self.find_offset_column(lines) - for line in lines: - if not line: - continue - if line.startswith("Disassembly"): - expected_offset = 0 - continue - if line.startswith("Exception"): - break - offset = int(line[start:end]) - self.assertGreaterEqual(offset, expected_offset, line) - expected_offset = offset + delta - def assert_exception_table_increasing(self, lines): prev_start, prev_end = -1, -1 count = 0 for line in lines: - m = re.match(r' (\d+) to (\d+) -> \d+ \[\d+\]', line) + m = re.match(r' L(\d+) to L(\d+) -> L\d+ \[\d+\]', line) start, end = [int(g) for g in m.groups()] self.assertGreaterEqual(end, start) - self.assertGreater(start, prev_end) + self.assertGreaterEqual(start, prev_end) prev_start, prev_end = start, end count += 1 return count - def strip_offsets(self, text): - lines = text.splitlines(True) - start, end = self.find_offset_column(lines) - res = [] - lines = iter(lines) - for line in lines: - if line.startswith("Exception"): - res.append(line) - break - if not line or line.startswith("Disassembly"): - res.append(line) - else: - res.append(line[:start] + line[end:]) - num_rows = self.assert_exception_table_increasing(lines) - if num_rows: - res.append(f"{num_rows} row{'s' if num_rows > 1 else ''}\n") - return "".join(res) - - def do_disassembly_compare(self, got, expected, with_offsets=False): - if not with_offsets: - self.assert_offsets_increasing(got, 2) - got = self.strip_offsets(got) + def do_disassembly_compare(self, got, expected): if got != expected: got = self.strip_addresses(got) self.assertEqual(got, expected) @@ -938,17 +911,16 @@ def get_disassembly(self, func, lasti=-1, wrapper=True, **kwargs): def get_disassemble_as_string(self, func, lasti=-1): return self.get_disassembly(func, lasti, False) - def do_disassembly_test(self, func, expected, with_offsets=False): + def do_disassembly_test(self, func, expected, **kwargs): self.maxDiff = None - got = self.get_disassembly(func, depth=0) - self.do_disassembly_compare(got, expected, with_offsets) + got = self.get_disassembly(func, depth=0, **kwargs) + self.do_disassembly_compare(got, expected) # Add checks for dis.disco if hasattr(func, '__code__'): got_disco = io.StringIO() with contextlib.redirect_stdout(got_disco): - dis.disco(func.__code__) - self.do_disassembly_compare(got_disco.getvalue(), expected, - with_offsets) + dis.disco(func.__code__, **kwargs) + self.do_disassembly_compare(got_disco.getvalue(), expected) def test_opmap(self): self.assertEqual(dis.opmap["CACHE"], 0) @@ -976,6 +948,9 @@ def test_widths(self): def test_dis(self): self.do_disassembly_test(_f, dis_f) + def test_dis_with_offsets(self): + self.do_disassembly_test(_f, dis_f_with_offsets, show_offsets=True) + def test_bug_708901(self): self.do_disassembly_test(bug708901, dis_bug708901) @@ -1036,41 +1011,6 @@ def func(count): from test import dis_module self.do_disassembly_test(dis_module, dis_module_expected_results) - def test_big_offsets(self): - self.maxDiff = None - def func(count): - namespace = {} - func = "def foo(x):\n " + ";".join(["x = x + 1"] * count) + "\n return x" - exec(func, namespace) - return namespace['foo'] - - def expected(count, w): - s = ['''\ - 1 %*d RESUME 0 - - 2 %*d LOAD_FAST 0 (x) - %*d LOAD_CONST 1 (1) - %*d BINARY_OP 0 (+) -''' % (w, 0, w, 2, w, 4, w, 6)] - s += ['''\ - %*d STORE_FAST_LOAD_FAST 0 (x, x) - %*d LOAD_CONST 1 (1) - %*d BINARY_OP 0 (+) -''' % (w, 8*i + 10, w, 8*i + 12, w, 8*i + 14) - for i in range(count-1)] - s += ['''\ - %*d STORE_FAST 0 (x) - - 3 %*d LOAD_FAST 0 (x) - %*d RETURN_VALUE -''' % (w, 8*count + 2, w, 8*count + 4, w, 8*count + 6)] - return ''.join(s) - - for i in range(1, 5): - self.do_disassembly_test(func(i), expected(i, 4), True) - self.do_disassembly_test(func(1200), expected(1200, 4), True) - self.do_disassembly_test(func(1300), expected(1300, 5), True) - def test_disassemble_str(self): self.do_disassembly_test(expr_str, dis_expr_str) self.do_disassembly_test(simple_stmt_str, dis_simple_stmt_str) @@ -1151,7 +1091,7 @@ def test_dis_traceback(self): sys.last_exc = e tb_dis = self.get_disassemble_as_string(tb.tb_frame.f_code, tb.tb_lasti) - self.do_disassembly_test(None, tb_dis, True) + self.do_disassembly_test(None, tb_dis) def test_dis_object(self): self.assertRaises(TypeError, dis.dis, object()) @@ -1160,7 +1100,6 @@ def test_disassemble_recursive(self): def check(expected, **kwargs): dis = self.get_disassembly(_h, **kwargs) dis = self.strip_addresses(dis) - dis = self.strip_offsets(dis) self.assertEqual(dis, expected) check(dis_nested_0, depth=0) @@ -1187,73 +1126,73 @@ def code_quicken(f, times=ADAPTIVE_WARMUP_DELAY): def test_super_instructions(self): self.code_quicken(lambda: load_test(0, 0)) got = self.get_disassembly(load_test, adaptive=True) - self.do_disassembly_compare(got, dis_load_test_quickened_code, True) + self.do_disassembly_compare(got, dis_load_test_quickened_code) @cpython_only @requires_specialization def test_binary_specialize(self): binary_op_quicken = """\ - 0 0 RESUME_CHECK 0 + 0 RESUME_CHECK 0 - 1 2 LOAD_NAME 0 (a) - 4 LOAD_NAME 1 (b) - 6 %s - 10 RETURN_VALUE + 1 LOAD_NAME 0 (a) + LOAD_NAME 1 (b) + %s + RETURN_VALUE """ co_int = compile('a + b', "", "eval") self.code_quicken(lambda: exec(co_int, {}, {'a': 1, 'b': 2})) got = self.get_disassembly(co_int, adaptive=True) - self.do_disassembly_compare(got, binary_op_quicken % "BINARY_OP_ADD_INT 0 (+)", True) + self.do_disassembly_compare(got, binary_op_quicken % "BINARY_OP_ADD_INT 0 (+)") co_unicode = compile('a + b', "", "eval") self.code_quicken(lambda: exec(co_unicode, {}, {'a': 'a', 'b': 'b'})) got = self.get_disassembly(co_unicode, adaptive=True) - self.do_disassembly_compare(got, binary_op_quicken % "BINARY_OP_ADD_UNICODE 0 (+)", True) + self.do_disassembly_compare(got, binary_op_quicken % "BINARY_OP_ADD_UNICODE 0 (+)") binary_subscr_quicken = """\ - 0 0 RESUME_CHECK 0 + 0 RESUME_CHECK 0 - 1 2 LOAD_NAME 0 (a) - 4 LOAD_CONST 0 (0) - 6 %s - 10 RETURN_VALUE + 1 LOAD_NAME 0 (a) + LOAD_CONST 0 (0) + %s + RETURN_VALUE """ co_list = compile('a[0]', "", "eval") self.code_quicken(lambda: exec(co_list, {}, {'a': [0]})) got = self.get_disassembly(co_list, adaptive=True) - self.do_disassembly_compare(got, binary_subscr_quicken % "BINARY_SUBSCR_LIST_INT", True) + self.do_disassembly_compare(got, binary_subscr_quicken % "BINARY_SUBSCR_LIST_INT") co_dict = compile('a[0]', "", "eval") self.code_quicken(lambda: exec(co_dict, {}, {'a': {0: '1'}})) got = self.get_disassembly(co_dict, adaptive=True) - self.do_disassembly_compare(got, binary_subscr_quicken % "BINARY_SUBSCR_DICT", True) + self.do_disassembly_compare(got, binary_subscr_quicken % "BINARY_SUBSCR_DICT") @cpython_only @requires_specialization def test_load_attr_specialize(self): load_attr_quicken = """\ - 0 0 RESUME_CHECK 0 + 0 RESUME_CHECK 0 - 1 2 LOAD_CONST 0 ('a') - 4 LOAD_ATTR_SLOT 0 (__class__) - 24 RETURN_VALUE + 1 LOAD_CONST 0 ('a') + LOAD_ATTR_SLOT 0 (__class__) + RETURN_VALUE """ co = compile("'a'.__class__", "", "eval") self.code_quicken(lambda: exec(co, {}, {})) got = self.get_disassembly(co, adaptive=True) - self.do_disassembly_compare(got, load_attr_quicken, True) + self.do_disassembly_compare(got, load_attr_quicken) @cpython_only @requires_specialization def test_call_specialize(self): call_quicken = """\ - 0 RESUME_CHECK 0 + 0 RESUME_CHECK 0 - 1 LOAD_NAME 0 (str) - PUSH_NULL - LOAD_CONST 0 (1) - CALL_STR_1 1 - RETURN_VALUE + 1 LOAD_NAME 0 (str) + PUSH_NULL + LOAD_CONST 0 (1) + CALL_STR_1 1 + RETURN_VALUE """ co = compile("str(1)", "", "eval") self.code_quicken(lambda: exec(co, {}, {})) @@ -1276,7 +1215,7 @@ def test_loop_quicken(self): @cpython_only def test_extended_arg_quick(self): got = self.get_disassembly(extended_arg_quick) - self.do_disassembly_compare(got, dis_extended_arg_quick_code, True) + self.do_disassembly_compare(got, dis_extended_arg_quick_code) def get_cached_values(self, quickened, adaptive): def f(): @@ -1328,14 +1267,16 @@ def f(): op_offset = inst.offset - 2 cache_offset = inst.offset break + else: + opname = inst.opname else: self.fail("Can't find a CACHE entry in the function provided to do the test") assem_op = self.get_disassembly(f.__code__, lasti=op_offset, wrapper=False) assem_cache = self.get_disassembly(f.__code__, lasti=cache_offset, wrapper=False) - # Make sure --> exists and points to the correct offset - self.assertRegex(assem_op, fr"-->\s+{op_offset}") + # Make sure --> exists and points to the correct op + self.assertRegex(assem_op, fr"--> {opname}") # Make sure when lasti points to cache, it shows the same disassembly self.assertEqual(assem_op, assem_cache) @@ -1637,197 +1578,197 @@ def _prepare_test_cases(): Instruction = dis.Instruction expected_opinfo_outer = [ - Instruction(opname='MAKE_CELL', opcode=94, arg=0, argval='a', argrepr='a', offset=0, start_offset=0, starts_line=True, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='MAKE_CELL', opcode=94, arg=1, argval='b', argrepr='b', offset=2, start_offset=2, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=4, start_offset=4, starts_line=True, line_number=1, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=5, argval=(3, 4), argrepr='(3, 4)', offset=6, start_offset=6, starts_line=True, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='a', argrepr='a', offset=8, start_offset=8, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=1, argval='b', argrepr='b', offset=10, start_offset=10, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='BUILD_TUPLE', opcode=52, arg=2, argval=2, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=1, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='MAKE_FUNCTION', opcode=26, arg=None, argval=None, argrepr='', offset=16, start_offset=16, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=8, argval=8, argrepr='closure', offset=18, start_offset=18, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=1, argval=1, argrepr='defaults', offset=20, start_offset=20, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='STORE_FAST', opcode=110, arg=2, argval='f', argrepr='f', offset=22, start_offset=22, starts_line=False, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='print', argrepr='print + NULL', offset=24, start_offset=24, starts_line=True, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=0, argval='a', argrepr='a', offset=34, start_offset=34, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=1, argval='b', argrepr='b', offset=36, start_offset=36, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=3, argval=1, argrepr='1', offset=40, start_offset=40, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='BUILD_LIST', opcode=47, arg=0, argval=0, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='BUILD_MAP', opcode=48, arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=4, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=7, argval=7, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=2, argval='f', argrepr='f', offset=58, start_offset=58, starts_line=True, line_number=8, is_jump_target=False, positions=None), - Instruction(opname='RETURN_VALUE', opcode=36, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=8, is_jump_target=False, positions=None), + Instruction(opname='MAKE_CELL', opcode=94, arg=0, argval='a', argrepr='a', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None), + Instruction(opname='MAKE_CELL', opcode=94, arg=1, argval='b', argrepr='b', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=4, start_offset=4, starts_line=True, line_number=1, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=5, argval=(3, 4), argrepr='(3, 4)', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='a', argrepr='a', offset=8, start_offset=8, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=1, argval='b', argrepr='b', offset=10, start_offset=10, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='BUILD_TUPLE', opcode=52, arg=2, argval=2, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=1, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='MAKE_FUNCTION', opcode=26, arg=None, argval=None, argrepr='', offset=16, start_offset=16, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=8, argval=8, argrepr='closure', offset=18, start_offset=18, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=1, argval=1, argrepr='defaults', offset=20, start_offset=20, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='STORE_FAST', opcode=110, arg=2, argval='f', argrepr='f', offset=22, start_offset=22, starts_line=False, line_number=2, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='print', argrepr='print + NULL', offset=24, start_offset=24, starts_line=True, line_number=7, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=0, argval='a', argrepr='a', offset=34, start_offset=34, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=1, argval='b', argrepr='b', offset=36, start_offset=36, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=3, argval=1, argrepr='1', offset=40, start_offset=40, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='BUILD_LIST', opcode=47, arg=0, argval=0, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='BUILD_MAP', opcode=48, arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=4, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=7, argval=7, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=2, argval='f', argrepr='f', offset=58, start_offset=58, starts_line=True, line_number=8, label=None, positions=None), + Instruction(opname='RETURN_VALUE', opcode=36, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=8, label=None, positions=None), ] expected_opinfo_f = [ - Instruction(opname='COPY_FREE_VARS', opcode=62, arg=2, argval=2, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='MAKE_CELL', opcode=94, arg=0, argval='c', argrepr='c', offset=2, start_offset=2, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='MAKE_CELL', opcode=94, arg=1, argval='d', argrepr='d', offset=4, start_offset=4, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=6, start_offset=6, starts_line=True, line_number=2, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval=(5, 6), argrepr='(5, 6)', offset=8, start_offset=8, starts_line=True, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=3, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=4, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='c', argrepr='c', offset=14, start_offset=14, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=1, argval='d', argrepr='d', offset=16, start_offset=16, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='BUILD_TUPLE', opcode=52, arg=4, argval=4, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=1, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, start_offset=20, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='MAKE_FUNCTION', opcode=26, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=8, argval=8, argrepr='closure', offset=24, start_offset=24, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=1, argval=1, argrepr='defaults', offset=26, start_offset=26, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='STORE_FAST', opcode=110, arg=2, argval='inner', argrepr='inner', offset=28, start_offset=28, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=3, argval='a', argrepr='a', offset=40, start_offset=40, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=4, argval='b', argrepr='b', offset=42, start_offset=42, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=0, argval='c', argrepr='c', offset=44, start_offset=44, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=1, argval='d', argrepr='d', offset=46, start_offset=46, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=4, argval=4, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=2, argval='inner', argrepr='inner', offset=58, start_offset=58, starts_line=True, line_number=6, is_jump_target=False, positions=None), - Instruction(opname='RETURN_VALUE', opcode=36, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=6, is_jump_target=False, positions=None), + Instruction(opname='COPY_FREE_VARS', opcode=62, arg=2, argval=2, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None), + Instruction(opname='MAKE_CELL', opcode=94, arg=0, argval='c', argrepr='c', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='MAKE_CELL', opcode=94, arg=1, argval='d', argrepr='d', offset=4, start_offset=4, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval=(5, 6), argrepr='(5, 6)', offset=8, start_offset=8, starts_line=True, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=3, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=4, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='c', argrepr='c', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=1, argval='d', argrepr='d', offset=16, start_offset=16, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='BUILD_TUPLE', opcode=52, arg=4, argval=4, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=1, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, start_offset=20, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='MAKE_FUNCTION', opcode=26, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=8, argval=8, argrepr='closure', offset=24, start_offset=24, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=106, arg=1, argval=1, argrepr='defaults', offset=26, start_offset=26, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='STORE_FAST', opcode=110, arg=2, argval='inner', argrepr='inner', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=5, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=3, argval='a', argrepr='a', offset=40, start_offset=40, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=4, argval='b', argrepr='b', offset=42, start_offset=42, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=0, argval='c', argrepr='c', offset=44, start_offset=44, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=1, argval='d', argrepr='d', offset=46, start_offset=46, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=4, argval=4, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=2, argval='inner', argrepr='inner', offset=58, start_offset=58, starts_line=True, line_number=6, label=None, positions=None), + Instruction(opname='RETURN_VALUE', opcode=36, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=6, label=None, positions=None), ] expected_opinfo_inner = [ - Instruction(opname='COPY_FREE_VARS', opcode=62, arg=4, argval=4, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=2, start_offset=2, starts_line=True, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='print', argrepr='print + NULL', offset=4, start_offset=4, starts_line=True, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=2, argval='a', argrepr='a', offset=14, start_offset=14, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=3, argval='b', argrepr='b', offset=16, start_offset=16, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=4, argval='c', argrepr='c', offset=18, start_offset=18, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='LOAD_DEREF', opcode=84, arg=5, argval='d', argrepr='d', offset=20, start_offset=20, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST_LOAD_FAST', opcode=88, arg=1, argval=('e', 'f'), argrepr='e, f', offset=22, start_offset=22, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=6, argval=6, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=32, start_offset=32, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='RETURN_CONST', opcode=103, arg=0, argval=None, argrepr='None', offset=34, start_offset=34, starts_line=False, line_number=4, is_jump_target=False, positions=None), + Instruction(opname='COPY_FREE_VARS', opcode=62, arg=4, argval=4, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None), + Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='print', argrepr='print + NULL', offset=4, start_offset=4, starts_line=True, line_number=4, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=2, argval='a', argrepr='a', offset=14, start_offset=14, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=3, argval='b', argrepr='b', offset=16, start_offset=16, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=4, argval='c', argrepr='c', offset=18, start_offset=18, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='LOAD_DEREF', opcode=84, arg=5, argval='d', argrepr='d', offset=20, start_offset=20, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='LOAD_FAST_LOAD_FAST', opcode=88, arg=1, argval=('e', 'f'), argrepr='e, f', offset=22, start_offset=22, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=6, argval=6, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=32, start_offset=32, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='RETURN_CONST', opcode=103, arg=0, argval=None, argrepr='None', offset=34, start_offset=34, starts_line=False, line_number=4, label=None, positions=None), ] expected_opinfo_jumpy = [ - Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=1, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='range', argrepr='range + NULL', offset=2, start_offset=2, starts_line=True, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=1, argval=10, argrepr='10', offset=12, start_offset=12, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='GET_ITER', opcode=19, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='FOR_ITER', opcode=72, arg=30, argval=88, argrepr='to 88', offset=24, start_offset=24, starts_line=False, line_number=3, is_jump_target=True, positions=None), - Instruction(opname='STORE_FAST', opcode=110, arg=0, argval='i', argrepr='i', offset=28, start_offset=28, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=40, start_offset=40, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=50, start_offset=50, starts_line=False, line_number=4, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=52, start_offset=52, starts_line=True, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval=4, argrepr='4', offset=54, start_offset=54, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=58, arg=18, argval='<', argrepr='bool(<)', offset=56, start_offset=56, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=2, argval=68, argrepr='to 68', offset=60, start_offset=60, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=77, arg=22, argval=24, argrepr='to 24', offset=64, start_offset=64, starts_line=True, line_number=6, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=68, start_offset=68, starts_line=True, line_number=7, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=3, argval=6, argrepr='6', offset=70, start_offset=70, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=58, arg=148, argval='>', argrepr='bool(>)', offset=72, start_offset=72, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=2, argval=84, argrepr='to 84', offset=76, start_offset=76, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=77, arg=30, argval=24, argrepr='to 24', offset=80, start_offset=80, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=84, start_offset=84, starts_line=True, line_number=8, is_jump_target=True, positions=None), - Instruction(opname='JUMP_FORWARD', opcode=79, arg=12, argval=112, argrepr='to 112', offset=86, start_offset=86, starts_line=False, line_number=8, is_jump_target=False, positions=None), - Instruction(opname='END_FOR', opcode=11, arg=None, argval=None, argrepr='', offset=88, start_offset=88, starts_line=True, line_number=3, is_jump_target=True, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=90, start_offset=90, starts_line=True, line_number=10, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=100, start_offset=100, starts_line=False, line_number=10, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=102, start_offset=102, starts_line=False, line_number=10, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=110, start_offset=110, starts_line=False, line_number=10, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST_CHECK', opcode=87, arg=0, argval='i', argrepr='i', offset=112, start_offset=112, starts_line=True, line_number=11, is_jump_target=True, positions=None), - Instruction(opname='TO_BOOL', opcode=40, arg=None, argval=None, argrepr='', offset=114, start_offset=114, starts_line=False, line_number=11, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=40, argval=206, argrepr='to 206', offset=122, start_offset=122, starts_line=False, line_number=11, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=126, start_offset=126, starts_line=True, line_number=12, is_jump_target=True, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=136, start_offset=136, starts_line=False, line_number=12, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=138, start_offset=138, starts_line=False, line_number=12, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=146, start_offset=146, starts_line=False, line_number=12, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=148, start_offset=148, starts_line=True, line_number=13, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=5, argval=1, argrepr='1', offset=150, start_offset=150, starts_line=False, line_number=13, is_jump_target=False, positions=None), - Instruction(opname='BINARY_OP', opcode=45, arg=23, argval=23, argrepr='-=', offset=152, start_offset=152, starts_line=False, line_number=13, is_jump_target=False, positions=None), - Instruction(opname='STORE_FAST', opcode=110, arg=0, argval='i', argrepr='i', offset=156, start_offset=156, starts_line=False, line_number=13, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=158, start_offset=158, starts_line=True, line_number=14, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=3, argval=6, argrepr='6', offset=160, start_offset=160, starts_line=False, line_number=14, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=58, arg=148, argval='>', argrepr='bool(>)', offset=162, start_offset=162, starts_line=False, line_number=14, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=2, argval=174, argrepr='to 174', offset=166, start_offset=166, starts_line=False, line_number=14, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=77, arg=31, argval=112, argrepr='to 112', offset=170, start_offset=170, starts_line=True, line_number=15, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=174, start_offset=174, starts_line=True, line_number=16, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval=4, argrepr='4', offset=176, start_offset=176, starts_line=False, line_number=16, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=58, arg=18, argval='<', argrepr='bool(<)', offset=178, start_offset=178, starts_line=False, line_number=16, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=1, argval=188, argrepr='to 188', offset=182, start_offset=182, starts_line=False, line_number=16, is_jump_target=False, positions=None), - Instruction(opname='JUMP_FORWARD', opcode=79, arg=20, argval=228, argrepr='to 228', offset=186, start_offset=186, starts_line=True, line_number=17, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=188, start_offset=188, starts_line=True, line_number=11, is_jump_target=True, positions=None), - Instruction(opname='TO_BOOL', opcode=40, arg=None, argval=None, argrepr='', offset=190, start_offset=190, starts_line=False, line_number=11, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=2, argval=206, argrepr='to 206', offset=198, start_offset=198, starts_line=False, line_number=11, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=77, arg=40, argval=126, argrepr='to 126', offset=202, start_offset=202, starts_line=False, line_number=11, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=206, start_offset=206, starts_line=True, line_number=19, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=216, start_offset=216, starts_line=False, line_number=19, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=218, start_offset=218, starts_line=False, line_number=19, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=226, start_offset=226, starts_line=False, line_number=19, is_jump_target=False, positions=None), - Instruction(opname='NOP', opcode=30, arg=None, argval=None, argrepr='', offset=228, start_offset=228, starts_line=True, line_number=20, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=5, argval=1, argrepr='1', offset=230, start_offset=230, starts_line=True, line_number=21, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=7, argval=0, argrepr='0', offset=232, start_offset=232, starts_line=False, line_number=21, is_jump_target=False, positions=None), - Instruction(opname='BINARY_OP', opcode=45, arg=11, argval=11, argrepr='/', offset=234, start_offset=234, starts_line=False, line_number=21, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=238, start_offset=238, starts_line=False, line_number=21, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=240, start_offset=240, starts_line=True, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='BEFORE_WITH', opcode=2, arg=None, argval=None, argrepr='', offset=242, start_offset=242, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='STORE_FAST', opcode=110, arg=1, argval='dodgy', argrepr='dodgy', offset=244, start_offset=244, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=246, start_offset=246, starts_line=True, line_number=26, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=8, argval='Never reach this', argrepr="'Never reach this'", offset=256, start_offset=256, starts_line=False, line_number=26, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=258, start_offset=258, starts_line=False, line_number=26, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=266, start_offset=266, starts_line=False, line_number=26, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=0, argval=None, argrepr='None', offset=268, start_offset=268, starts_line=True, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=0, argval=None, argrepr='None', offset=270, start_offset=270, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=0, argval=None, argrepr='None', offset=272, start_offset=272, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=2, argval=2, argrepr='', offset=274, start_offset=274, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=282, start_offset=282, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=284, start_offset=284, starts_line=True, line_number=28, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=294, start_offset=294, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=296, start_offset=296, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=304, start_offset=304, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='RETURN_CONST', opcode=103, arg=0, argval=None, argrepr='None', offset=306, start_offset=306, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=33, arg=None, argval=None, argrepr='', offset=308, start_offset=308, starts_line=True, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='WITH_EXCEPT_START', opcode=44, arg=None, argval=None, argrepr='', offset=310, start_offset=310, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='TO_BOOL', opcode=40, arg=None, argval=None, argrepr='', offset=312, start_offset=312, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=1, argval=326, argrepr='to 326', offset=320, start_offset=320, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=102, arg=2, argval=2, argrepr='', offset=324, start_offset=324, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=326, start_offset=326, starts_line=False, line_number=25, is_jump_target=True, positions=None), - Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=328, start_offset=328, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=330, start_offset=330, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=332, start_offset=332, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=77, arg=27, argval=284, argrepr='to 284', offset=334, start_offset=334, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='COPY', opcode=61, arg=3, argval=3, argrepr='', offset=338, start_offset=338, starts_line=True, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=340, start_offset=340, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=342, start_offset=342, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=33, arg=None, argval=None, argrepr='', offset=344, start_offset=344, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=346, start_offset=346, starts_line=True, line_number=22, is_jump_target=False, positions=None), - Instruction(opname='CHECK_EXC_MATCH', opcode=7, arg=None, argval=None, argrepr='', offset=356, start_offset=356, starts_line=False, line_number=22, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=15, argval=392, argrepr='to 392', offset=358, start_offset=358, starts_line=False, line_number=22, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=22, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=364, start_offset=364, starts_line=True, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=374, start_offset=374, starts_line=False, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=376, start_offset=376, starts_line=False, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=384, start_offset=384, starts_line=False, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=386, start_offset=386, starts_line=False, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=77, arg=54, argval=284, argrepr='to 284', offset=388, start_offset=388, starts_line=False, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=392, start_offset=392, starts_line=True, line_number=22, is_jump_target=True, positions=None), - Instruction(opname='COPY', opcode=61, arg=3, argval=3, argrepr='', offset=394, start_offset=394, starts_line=True, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=396, start_offset=396, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=398, start_offset=398, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=33, arg=None, argval=None, argrepr='', offset=400, start_offset=400, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=402, start_offset=402, starts_line=True, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=83, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=412, start_offset=412, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=414, start_offset=414, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=422, start_offset=422, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=424, start_offset=424, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='COPY', opcode=61, arg=3, argval=3, argrepr='', offset=426, start_offset=426, starts_line=True, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=428, start_offset=428, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=430, start_offset=430, starts_line=False, line_number=None, is_jump_target=False, positions=None), + Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=1, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=1, argval='range', argrepr='range + NULL', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=1, argval=10, argrepr='10', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='GET_ITER', opcode=19, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='FOR_ITER', opcode=72, arg=30, argval=88, argrepr='to L4', offset=24, start_offset=24, starts_line=False, line_number=3, label=1, positions=None), + Instruction(opname='STORE_FAST', opcode=110, arg=0, argval='i', argrepr='i', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=4, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=40, start_offset=40, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=50, start_offset=50, starts_line=False, line_number=4, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=52, start_offset=52, starts_line=True, line_number=5, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval=4, argrepr='4', offset=54, start_offset=54, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='COMPARE_OP', opcode=58, arg=18, argval='<', argrepr='bool(<)', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=2, argval=68, argrepr='to L2', offset=60, start_offset=60, starts_line=False, line_number=5, label=None, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=77, arg=22, argval=24, argrepr='to L1', offset=64, start_offset=64, starts_line=True, line_number=6, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=68, start_offset=68, starts_line=True, line_number=7, label=2, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=3, argval=6, argrepr='6', offset=70, start_offset=70, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='COMPARE_OP', opcode=58, arg=148, argval='>', argrepr='bool(>)', offset=72, start_offset=72, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=2, argval=84, argrepr='to L3', offset=76, start_offset=76, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=77, arg=30, argval=24, argrepr='to L1', offset=80, start_offset=80, starts_line=False, line_number=7, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=84, start_offset=84, starts_line=True, line_number=8, label=3, positions=None), + Instruction(opname='JUMP_FORWARD', opcode=79, arg=12, argval=112, argrepr='to L5', offset=86, start_offset=86, starts_line=False, line_number=8, label=None, positions=None), + Instruction(opname='END_FOR', opcode=11, arg=None, argval=None, argrepr='', offset=88, start_offset=88, starts_line=True, line_number=3, label=4, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=90, start_offset=90, starts_line=True, line_number=10, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=100, start_offset=100, starts_line=False, line_number=10, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=102, start_offset=102, starts_line=False, line_number=10, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=110, start_offset=110, starts_line=False, line_number=10, label=None, positions=None), + Instruction(opname='LOAD_FAST_CHECK', opcode=87, arg=0, argval='i', argrepr='i', offset=112, start_offset=112, starts_line=True, line_number=11, label=5, positions=None), + Instruction(opname='TO_BOOL', opcode=40, arg=None, argval=None, argrepr='', offset=114, start_offset=114, starts_line=False, line_number=11, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=40, argval=206, argrepr='to L9', offset=122, start_offset=122, starts_line=False, line_number=11, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=126, start_offset=126, starts_line=True, line_number=12, label=6, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=136, start_offset=136, starts_line=False, line_number=12, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=138, start_offset=138, starts_line=False, line_number=12, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=146, start_offset=146, starts_line=False, line_number=12, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=148, start_offset=148, starts_line=True, line_number=13, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=5, argval=1, argrepr='1', offset=150, start_offset=150, starts_line=False, line_number=13, label=None, positions=None), + Instruction(opname='BINARY_OP', opcode=45, arg=23, argval=23, argrepr='-=', offset=152, start_offset=152, starts_line=False, line_number=13, label=None, positions=None), + Instruction(opname='STORE_FAST', opcode=110, arg=0, argval='i', argrepr='i', offset=156, start_offset=156, starts_line=False, line_number=13, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=158, start_offset=158, starts_line=True, line_number=14, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=3, argval=6, argrepr='6', offset=160, start_offset=160, starts_line=False, line_number=14, label=None, positions=None), + Instruction(opname='COMPARE_OP', opcode=58, arg=148, argval='>', argrepr='bool(>)', offset=162, start_offset=162, starts_line=False, line_number=14, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=2, argval=174, argrepr='to L7', offset=166, start_offset=166, starts_line=False, line_number=14, label=None, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=77, arg=31, argval=112, argrepr='to L5', offset=170, start_offset=170, starts_line=True, line_number=15, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=174, start_offset=174, starts_line=True, line_number=16, label=7, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=2, argval=4, argrepr='4', offset=176, start_offset=176, starts_line=False, line_number=16, label=None, positions=None), + Instruction(opname='COMPARE_OP', opcode=58, arg=18, argval='<', argrepr='bool(<)', offset=178, start_offset=178, starts_line=False, line_number=16, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=1, argval=188, argrepr='to L8', offset=182, start_offset=182, starts_line=False, line_number=16, label=None, positions=None), + Instruction(opname='JUMP_FORWARD', opcode=79, arg=20, argval=228, argrepr='to L10', offset=186, start_offset=186, starts_line=True, line_number=17, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=188, start_offset=188, starts_line=True, line_number=11, label=8, positions=None), + Instruction(opname='TO_BOOL', opcode=40, arg=None, argval=None, argrepr='', offset=190, start_offset=190, starts_line=False, line_number=11, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=2, argval=206, argrepr='to L9', offset=198, start_offset=198, starts_line=False, line_number=11, label=None, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=77, arg=40, argval=126, argrepr='to L6', offset=202, start_offset=202, starts_line=False, line_number=11, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=206, start_offset=206, starts_line=True, line_number=19, label=9, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=216, start_offset=216, starts_line=False, line_number=19, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=218, start_offset=218, starts_line=False, line_number=19, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=226, start_offset=226, starts_line=False, line_number=19, label=None, positions=None), + Instruction(opname='NOP', opcode=30, arg=None, argval=None, argrepr='', offset=228, start_offset=228, starts_line=True, line_number=20, label=10, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=5, argval=1, argrepr='1', offset=230, start_offset=230, starts_line=True, line_number=21, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=7, argval=0, argrepr='0', offset=232, start_offset=232, starts_line=False, line_number=21, label=None, positions=None), + Instruction(opname='BINARY_OP', opcode=45, arg=11, argval=11, argrepr='/', offset=234, start_offset=234, starts_line=False, line_number=21, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=238, start_offset=238, starts_line=False, line_number=21, label=None, positions=None), + Instruction(opname='LOAD_FAST', opcode=85, arg=0, argval='i', argrepr='i', offset=240, start_offset=240, starts_line=True, line_number=25, label=None, positions=None), + Instruction(opname='BEFORE_WITH', opcode=2, arg=None, argval=None, argrepr='', offset=242, start_offset=242, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='STORE_FAST', opcode=110, arg=1, argval='dodgy', argrepr='dodgy', offset=244, start_offset=244, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=246, start_offset=246, starts_line=True, line_number=26, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=8, argval='Never reach this', argrepr="'Never reach this'", offset=256, start_offset=256, starts_line=False, line_number=26, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=258, start_offset=258, starts_line=False, line_number=26, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=266, start_offset=266, starts_line=False, line_number=26, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=0, argval=None, argrepr='None', offset=268, start_offset=268, starts_line=True, line_number=25, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=0, argval=None, argrepr='None', offset=270, start_offset=270, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=0, argval=None, argrepr='None', offset=272, start_offset=272, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=2, argval=2, argrepr='', offset=274, start_offset=274, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=282, start_offset=282, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=284, start_offset=284, starts_line=True, line_number=28, label=11, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=294, start_offset=294, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=296, start_offset=296, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=304, start_offset=304, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='RETURN_CONST', opcode=103, arg=0, argval=None, argrepr='None', offset=306, start_offset=306, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=33, arg=None, argval=None, argrepr='', offset=308, start_offset=308, starts_line=True, line_number=25, label=None, positions=None), + Instruction(opname='WITH_EXCEPT_START', opcode=44, arg=None, argval=None, argrepr='', offset=310, start_offset=310, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='TO_BOOL', opcode=40, arg=None, argval=None, argrepr='', offset=312, start_offset=312, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=1, argval=326, argrepr='to L12', offset=320, start_offset=320, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='RERAISE', opcode=102, arg=2, argval=2, argrepr='', offset=324, start_offset=324, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=326, start_offset=326, starts_line=False, line_number=25, label=12, positions=None), + Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=328, start_offset=328, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=330, start_offset=330, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=332, start_offset=332, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=77, arg=27, argval=284, argrepr='to L11', offset=334, start_offset=334, starts_line=False, line_number=25, label=None, positions=None), + Instruction(opname='COPY', opcode=61, arg=3, argval=3, argrepr='', offset=338, start_offset=338, starts_line=True, line_number=None, label=None, positions=None), + Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=340, start_offset=340, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=342, start_offset=342, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=33, arg=None, argval=None, argrepr='', offset=344, start_offset=344, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=346, start_offset=346, starts_line=True, line_number=22, label=None, positions=None), + Instruction(opname='CHECK_EXC_MATCH', opcode=7, arg=None, argval=None, argrepr='', offset=356, start_offset=356, starts_line=False, line_number=22, label=None, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=15, argval=392, argrepr='to L13', offset=358, start_offset=358, starts_line=False, line_number=22, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=22, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=364, start_offset=364, starts_line=True, line_number=23, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=374, start_offset=374, starts_line=False, line_number=23, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=376, start_offset=376, starts_line=False, line_number=23, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=384, start_offset=384, starts_line=False, line_number=23, label=None, positions=None), + Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=386, start_offset=386, starts_line=False, line_number=23, label=None, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=77, arg=54, argval=284, argrepr='to L11', offset=388, start_offset=388, starts_line=False, line_number=23, label=None, positions=None), + Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=392, start_offset=392, starts_line=True, line_number=22, label=13, positions=None), + Instruction(opname='COPY', opcode=61, arg=3, argval=3, argrepr='', offset=394, start_offset=394, starts_line=True, line_number=None, label=None, positions=None), + Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=396, start_offset=396, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=398, start_offset=398, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=33, arg=None, argval=None, argrepr='', offset=400, start_offset=400, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=91, arg=3, argval='print', argrepr='print + NULL', offset=402, start_offset=402, starts_line=True, line_number=28, label=None, positions=None), + Instruction(opname='LOAD_CONST', opcode=83, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=412, start_offset=412, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='CALL', opcode=53, arg=1, argval=1, argrepr='', offset=414, start_offset=414, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=422, start_offset=422, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=424, start_offset=424, starts_line=False, line_number=28, label=None, positions=None), + Instruction(opname='COPY', opcode=61, arg=3, argval=3, argrepr='', offset=426, start_offset=426, starts_line=True, line_number=None, label=None, positions=None), + Instruction(opname='POP_EXCEPT', opcode=31, arg=None, argval=None, argrepr='', offset=428, start_offset=428, starts_line=False, line_number=None, label=None, positions=None), + Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=430, start_offset=430, starts_line=False, line_number=None, label=None, positions=None), ] # One last piece of inspect fodder to check the default line number handling def simple(): pass expected_opinfo_simple = [ - Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=simple.__code__.co_firstlineno, is_jump_target=False, positions=None), - Instruction(opname='RETURN_CONST', opcode=103, arg=0, argval=None, argrepr='None', offset=2, start_offset=2, starts_line=False, line_number=simple.__code__.co_firstlineno, is_jump_target=False), + Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=simple.__code__.co_firstlineno, label=None, positions=None), + Instruction(opname='RETURN_CONST', opcode=103, arg=0, argval=None, argrepr='None', offset=2, start_offset=2, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None), ] @@ -1951,15 +1892,27 @@ def roots(a, b, c): def test_oparg_alias(self): instruction = Instruction(opname="NOP", opcode=dis.opmap["NOP"], arg=None, argval=None, - argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, is_jump_target=False, + argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, label=None, positions=None) self.assertEqual(instruction.arg, instruction.oparg) + def test_show_caches_with_label(self): + def f(x, y, z): + if x: + res = y + else: + res = z + return res + + output = io.StringIO() + dis.dis(f.__code__, file=output, show_caches=True) + self.assertIn("L1:", output.getvalue()) + def test_baseopname_and_baseopcode(self): # Standard instructions for name, code in dis.opmap.items(): instruction = Instruction(opname=name, opcode=code, arg=None, argval=None, argrepr='', offset=0, - start_offset=0, starts_line=True, line_number=1, is_jump_target=False, positions=None) + start_offset=0, starts_line=True, line_number=1, label=None, positions=None) baseopname = instruction.baseopname baseopcode = instruction.baseopcode self.assertIsNotNone(baseopname) @@ -1970,7 +1923,7 @@ def test_baseopname_and_baseopcode(self): # Specialized instructions for name in opcode._specialized_opmap: instruction = Instruction(opname=name, opcode=dis._all_opmap[name], arg=None, argval=None, argrepr='', - offset=0, start_offset=0, starts_line=True, line_number=1, is_jump_target=False, positions=None) + offset=0, start_offset=0, starts_line=True, line_number=1, label=None, positions=None) baseopname = instruction.baseopname baseopcode = instruction.baseopcode self.assertIn(name, opcode._specializations[baseopname]) @@ -1979,30 +1932,32 @@ def test_baseopname_and_baseopcode(self): def test_jump_target(self): # Non-jump instructions should return None instruction = Instruction(opname="NOP", opcode=dis.opmap["NOP"], arg=None, argval=None, - argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, is_jump_target=False, + argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, label=None, positions=None) self.assertIsNone(instruction.jump_target) delta = 100 instruction = Instruction(opname="JUMP_FORWARD", opcode=dis.opmap["JUMP_FORWARD"], arg=delta, argval=delta, - argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, is_jump_target=False, + argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, label=None, positions=None) self.assertEqual(10 + 2 + 100*2, instruction.jump_target) # Test negative deltas instruction = Instruction(opname="JUMP_BACKWARD", opcode=dis.opmap["JUMP_BACKWARD"], arg=delta, argval=delta, - argrepr='', offset=200, start_offset=200, starts_line=True, line_number=1, is_jump_target=False, + argrepr='', offset=200, start_offset=200, starts_line=True, line_number=1, label=None, positions=None) self.assertEqual(200 + 2 - 100*2 + 2*1, instruction.jump_target) # Make sure cache entries are handled instruction = Instruction(opname="SEND", opcode=dis.opmap["SEND"], arg=delta, argval=delta, - argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, is_jump_target=False, + argrepr='', offset=10, start_offset=10, starts_line=True, line_number=1, label=None, positions=None) self.assertEqual(10 + 2 + 1*2 + 100*2, instruction.jump_target) def test_argval_argrepr(self): - f = dis.Instruction._get_argval_argrepr + def f(*args): + return dis.Instruction._get_argval_argrepr( + *args, labels_map={24: 1}) offset = 42 co_consts = (0, 1, 2, 3) @@ -2012,7 +1967,7 @@ def test_argval_argrepr(self): self.assertEqual(f(opcode.opmap["POP_TOP"], None, *args), (None, '')) self.assertEqual(f(opcode.opmap["LOAD_CONST"], 1, *args), (1, '1')) self.assertEqual(f(opcode.opmap["LOAD_GLOBAL"], 2, *args), ('a', 'a')) - self.assertEqual(f(opcode.opmap["JUMP_BACKWARD"], 11, *args), (24, 'to 24')) + self.assertEqual(f(opcode.opmap["JUMP_BACKWARD"], 11, *args), (24, 'to L1')) self.assertEqual(f(opcode.opmap["COMPARE_OP"], 3, *args), ('<', '<')) self.assertEqual(f(opcode.opmap["SET_FUNCTION_ATTRIBUTE"], 2, *args), (2, 'kwdefaults')) self.assertEqual(f(opcode.opmap["BINARY_OP"], 3, *args), (3, '<<')) @@ -2149,7 +2104,7 @@ def test_from_traceback_dis(self): self.maxDiff = None tb = get_tb() b = dis.Bytecode.from_traceback(tb) - self.assertEqual(self.strip_offsets(b.dis()), dis_traceback) + self.assertEqual(b.dis(), dis_traceback) @requires_debug_ranges() def test_bytecode_co_positions(self): diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index cb4e2157bb228b..36328f8086c7ad 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -413,6 +413,23 @@ def test_DocTest(): r""" False >>> test != other_test True + >>> test < other_test + False + >>> other_test < test + True + +Test comparison with lineno None on one side + + >>> no_lineno = parser.get_doctest(docstring, globs, 'some_test', + ... 'some_test', None) + >>> test.lineno is None + False + >>> no_lineno.lineno is None + True + >>> test < no_lineno + False + >>> no_lineno < test + True Compare `DocTestCase`: @@ -2905,6 +2922,9 @@ def test_unicode(): """ Traceback (most recent call last): File ... exec(compile(example.source, filename, "single", + ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + compileflags, True), test.globs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "", line 1, in raise Exception('clé') Exception: clé diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index c602913ca69277..f99d4ca204b5a7 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -514,6 +514,7 @@ def test_contains_tf(self): self.assertFalse('first' in MainEnum) val = MainEnum.dupe self.assertIn(val, MainEnum) + self.assertNotIn(float('nan'), MainEnum) # class OtherEnum(Enum): one = auto() @@ -3268,6 +3269,65 @@ def __new__(cls, value): member._value_ = Base(value) return member + def test_extra_member_creation(self): + class IDEnumMeta(EnumMeta): + def __new__(metacls, cls, bases, classdict, **kwds): + # add new entries to classdict + for name in classdict.member_names: + classdict[f'{name}_DESC'] = f'-{classdict[name]}' + return super().__new__(metacls, cls, bases, classdict, **kwds) + class IDEnum(StrEnum, metaclass=IDEnumMeta): + pass + class MyEnum(IDEnum): + ID = 'id' + NAME = 'name' + self.assertEqual(list(MyEnum), [MyEnum.ID, MyEnum.NAME, MyEnum.ID_DESC, MyEnum.NAME_DESC]) + + def test_add_alias(self): + class mixin: + @property + def ORG(self): + return 'huh' + class Color(mixin, Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + Color.RED._add_alias_('ROJO') + self.assertIs(Color.RED, Color['ROJO']) + self.assertIs(Color.RED, Color.ROJO) + Color.BLUE._add_alias_('ORG') + self.assertIs(Color.BLUE, Color['ORG']) + self.assertIs(Color.BLUE, Color.ORG) + self.assertEqual(Color.RED.ORG, 'huh') + self.assertEqual(Color.GREEN.ORG, 'huh') + self.assertEqual(Color.BLUE.ORG, 'huh') + self.assertEqual(Color.ORG.ORG, 'huh') + + def test_add_value_alias_after_creation(self): + class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + Color.RED._add_value_alias_(5) + self.assertIs(Color.RED, Color(5)) + + def test_add_value_alias_during_creation(self): + class Types(Enum): + Unknown = 0, + Source = 1, 'src' + NetList = 2, 'nl' + def __new__(cls, int_value, *value_aliases): + member = object.__new__(cls) + member._value_ = int_value + for alias in value_aliases: + member._add_value_alias_(alias) + return member + self.assertIs(Types(0), Types.Unknown) + self.assertIs(Types(1), Types.Source) + self.assertIs(Types('src'), Types.Source) + self.assertIs(Types(2), Types.NetList) + self.assertIs(Types('nl'), Types.NetList) + class TestOrder(unittest.TestCase): "test usage of the `_order_` attribute" @@ -4941,12 +5001,14 @@ class CheckedColor(Enum): @bltns.property def zeroth(self): return 'zeroed %s' % self.name - self.assertTrue(_test_simple_enum(CheckedColor, SimpleColor) is None) + _test_simple_enum(CheckedColor, SimpleColor) SimpleColor.MAGENTA._value_ = 9 self.assertRaisesRegex( TypeError, "enum mismatch", _test_simple_enum, CheckedColor, SimpleColor, ) + # + # class CheckedMissing(IntFlag, boundary=KEEP): SIXTY_FOUR = 64 ONE_TWENTY_EIGHT = 128 @@ -4963,8 +5025,28 @@ class Missing: ALL = 2048 + 128 + 64 + 12 M = Missing self.assertEqual(list(CheckedMissing), [M.SIXTY_FOUR, M.ONE_TWENTY_EIGHT, M.TWENTY_FORTY_EIGHT]) - # _test_simple_enum(CheckedMissing, Missing) + # + # + class CheckedUnhashable(Enum): + ONE = dict() + TWO = set() + name = 'python' + self.assertIn(dict(), CheckedUnhashable) + self.assertIn('python', CheckedUnhashable) + self.assertEqual(CheckedUnhashable.name.value, 'python') + self.assertEqual(CheckedUnhashable.name.name, 'name') + # + @_simple_enum() + class Unhashable: + ONE = dict() + TWO = set() + name = 'python' + self.assertIn(dict(), Unhashable) + self.assertIn('python', Unhashable) + self.assertEqual(Unhashable.name.value, 'python') + self.assertEqual(Unhashable.name.name, 'name') + _test_simple_enum(Unhashable, Unhashable) class MiscTestCase(unittest.TestCase): diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 8ccf08703e5389..c57488e44aecc6 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2080,6 +2080,7 @@ def test_multiline_not_highlighted(self): """, [ ' 1 < 2 and', + ' 3 > 4', 'AssertionError', ], ), @@ -2087,7 +2088,7 @@ def test_multiline_not_highlighted(self): for source, expected in cases: with self.subTest(source): result = self.write_source(source) - self.assertEqual(result[-2:], expected) + self.assertEqual(result[-len(expected):], expected) class SyntaxErrorTests(unittest.TestCase): diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index dd8c2dd628ee13..27c7f70cef32e3 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -8,6 +8,7 @@ # Unicode identifiers in tests is allowed by PEP 3131. import ast +import dis import os import re import types @@ -1627,6 +1628,9 @@ def __repr__(self): self.assertEqual(f'X{x = }Y', 'Xx = '+repr(x)+'Y') self.assertEqual(f"sadsd {1 + 1 = :{1 + 1:1d}f}", "sadsd 1 + 1 = 2.000000") + self.assertEqual(f"{1+2 = # my comment + }", '1+2 = \n 3') + # These next lines contains tabs. Backslash escapes don't # work in f-strings. # patchcheck doesn't like these tabs. So the only way to test @@ -1735,5 +1739,14 @@ def test_syntax_warning_infinite_recursion_in_file(self): self.assertIn(rb'\1', stdout) self.assertEqual(len(stderr.strip().splitlines()), 2) + def test_fstring_without_formatting_bytecode(self): + # f-string without any formatting should emit the same bytecode + # as a normal string. See gh-99606. + def get_code(s): + return [(i.opname, i.oparg) for i in dis.get_instructions(s)] + + for s in ["", "some string"]: + self.assertEqual(get_code(f"'{s}'"), get_code(f"f'{s}'")) + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 585be642f0427d..de96a8764594ba 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -405,19 +405,6 @@ def test_macro_instruction(self): family(OP, INLINE_CACHE_ENTRIES_OP) = { OP3 }; """ output = """ - TARGET(OP1) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(OP1); - PyObject *right; - PyObject *left; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - uint16_t counter = read_u16(&this_instr[1].cache); - op1(left, right); - DISPATCH(); - } - TARGET(OP) { frame->instr_ptr = next_instr; next_instr += 6; @@ -447,6 +434,19 @@ def test_macro_instruction(self): DISPATCH(); } + TARGET(OP1) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(OP1); + PyObject *right; + PyObject *left; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + uint16_t counter = read_u16(&this_instr[1].cache); + op1(left, right); + DISPATCH(); + } + TARGET(OP3) { frame->instr_ptr = next_instr; next_instr += 6; @@ -466,6 +466,44 @@ def test_macro_instruction(self): """ self.run_cases_test(input, output) + def test_pseudo_instruction_no_flags(self): + input = """ + pseudo(OP) = { + OP1, + }; + + inst(OP1, (--)) { + } + """ + output = """ + TARGET(OP1) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(OP1); + DISPATCH(); + } + """ + self.run_cases_test(input, output) + + def test_pseudo_instruction_with_flags(self): + input = """ + pseudo(OP, (HAS_ARG, HAS_JUMP)) = { + OP1, + }; + + inst(OP1, (--)) { + } + """ + output = """ + TARGET(OP1) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(OP1); + DISPATCH(); + } + """ + self.run_cases_test(input, output) + def test_array_input(self): input = """ inst(OP, (below, values[oparg*2], above --)) { diff --git a/Lib/test/test_getpass.py b/Lib/test/test_getpass.py index 98ecec94336e32..80dda2caaa3331 100644 --- a/Lib/test/test_getpass.py +++ b/Lib/test/test_getpass.py @@ -26,7 +26,7 @@ def test_username_priorities_of_env_values(self, environ): environ.get.return_value = None try: getpass.getuser() - except ImportError: # in case there's no pwd module + except OSError: # in case there's no pwd module pass except KeyError: # current user has no pwd entry @@ -47,7 +47,7 @@ def test_username_falls_back_to_pwd(self, environ): getpass.getuser()) getpw.assert_called_once_with(42) else: - self.assertRaises(ImportError, getpass.getuser) + self.assertRaises(OSError, getpass.getuser) class GetpassRawinputTest(unittest.TestCase): diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 5d5832b62b2f94..caa4c76a913a01 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -283,6 +283,22 @@ def test_ipv6host_header(self): conn.request('GET', '/foo') self.assertTrue(sock.data.startswith(expected)) + expected = b'GET /foo HTTP/1.1\r\nHost: [fe80::]\r\n' \ + b'Accept-Encoding: identity\r\n\r\n' + conn = client.HTTPConnection('[fe80::%2]') + sock = FakeSocket('') + conn.sock = sock + conn.request('GET', '/foo') + self.assertTrue(sock.data.startswith(expected)) + + expected = b'GET /foo HTTP/1.1\r\nHost: [fe80::]:81\r\n' \ + b'Accept-Encoding: identity\r\n\r\n' + conn = client.HTTPConnection('[fe80::%2]:81') + sock = FakeSocket('') + conn.sock = sock + conn.request('GET', '/foo') + self.assertTrue(sock.data.startswith(expected)) + def test_malformed_headers_coped_with(self): # Issue 19996 body = "HTTP/1.1 200 OK\r\nFirst: val\r\n: nval\r\nSecond: val\r\n\r\n" diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index aa465c70dfbcd0..1ecac4f37fe1c1 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -1968,10 +1968,12 @@ def test_disallowed_reimport(self): print(_testsinglephase) ''') interpid = _interpreters.create() - with self.assertRaises(_interpreters.RunFailedError): - _interpreters.run_string(interpid, script) - with self.assertRaises(_interpreters.RunFailedError): - _interpreters.run_string(interpid, script) + + excsnap = _interpreters.run_string(interpid, script) + self.assertIsNot(excsnap, None) + + excsnap = _interpreters.run_string(interpid, script) + self.assertIsNot(excsnap, None) class TestSinglePhaseSnapshot(ModuleSnapshot): diff --git a/Lib/test/test_importlib/test_namespace_pkgs.py b/Lib/test/test_importlib/test_namespace_pkgs.py index 9b3bef02c66820..072e198795d394 100644 --- a/Lib/test/test_importlib/test_namespace_pkgs.py +++ b/Lib/test/test_importlib/test_namespace_pkgs.py @@ -80,7 +80,7 @@ def test_cant_import_other(self): def test_simple_repr(self): import foo.one - assert repr(foo).startswith(" 42: pass self.assertEqual(str(inspect.signature(foo)), '(a: int = 1, *, b, c=None, **kwargs) -> 42') + self.assertEqual(str(inspect.signature(foo)), + inspect.signature(foo).format()) def foo(a:int=1, *args, b, c=None, **kwargs) -> 42: pass self.assertEqual(str(inspect.signature(foo)), '(a: int = 1, *args, b, c=None, **kwargs) -> 42') + self.assertEqual(str(inspect.signature(foo)), + inspect.signature(foo).format()) def foo(): pass self.assertEqual(str(inspect.signature(foo)), '()') + self.assertEqual(str(inspect.signature(foo)), + inspect.signature(foo).format()) def foo(a: list[str]) -> tuple[str, float]: pass self.assertEqual(str(inspect.signature(foo)), '(a: list[str]) -> tuple[str, float]') + self.assertEqual(str(inspect.signature(foo)), + inspect.signature(foo).format()) from typing import Tuple def foo(a: list[str]) -> Tuple[str, float]: pass self.assertEqual(str(inspect.signature(foo)), '(a: list[str]) -> Tuple[str, float]') + self.assertEqual(str(inspect.signature(foo)), + inspect.signature(foo).format()) def test_signature_str_positional_only(self): P = inspect.Parameter @@ -3818,19 +3836,85 @@ def test(a_po, /, *, b, **kwargs): self.assertEqual(str(inspect.signature(test)), '(a_po, /, *, b, **kwargs)') + self.assertEqual(str(inspect.signature(test)), + inspect.signature(test).format()) + + test = S(parameters=[P('foo', P.POSITIONAL_ONLY)]) + self.assertEqual(str(test), '(foo, /)') + self.assertEqual(str(test), test.format()) + + test = S(parameters=[P('foo', P.POSITIONAL_ONLY), + P('bar', P.VAR_KEYWORD)]) + self.assertEqual(str(test), '(foo, /, **bar)') + self.assertEqual(str(test), test.format()) + + test = S(parameters=[P('foo', P.POSITIONAL_ONLY), + P('bar', P.VAR_POSITIONAL)]) + self.assertEqual(str(test), '(foo, /, *bar)') + self.assertEqual(str(test), test.format()) + + def test_signature_format(self): + from typing import Annotated, Literal + + def func(x: Annotated[int, 'meta'], y: Literal['a', 'b'], z: 'LiteralString'): + pass - self.assertEqual(str(S(parameters=[P('foo', P.POSITIONAL_ONLY)])), - '(foo, /)') + expected_singleline = "(x: Annotated[int, 'meta'], y: Literal['a', 'b'], z: 'LiteralString')" + expected_multiline = """( + x: Annotated[int, 'meta'], + y: Literal['a', 'b'], + z: 'LiteralString' +)""" + self.assertEqual( + inspect.signature(func).format(), + expected_singleline, + ) + self.assertEqual( + inspect.signature(func).format(max_width=None), + expected_singleline, + ) + self.assertEqual( + inspect.signature(func).format(max_width=len(expected_singleline)), + expected_singleline, + ) + self.assertEqual( + inspect.signature(func).format(max_width=len(expected_singleline) - 1), + expected_multiline, + ) + self.assertEqual( + inspect.signature(func).format(max_width=0), + expected_multiline, + ) + self.assertEqual( + inspect.signature(func).format(max_width=-1), + expected_multiline, + ) - self.assertEqual(str(S(parameters=[ - P('foo', P.POSITIONAL_ONLY), - P('bar', P.VAR_KEYWORD)])), - '(foo, /, **bar)') + def test_signature_format_all_arg_types(self): + from typing import Annotated, Literal + + def func( + x: Annotated[int, 'meta'], + /, + y: Literal['a', 'b'], + *, + z: 'LiteralString', + **kwargs: object, + ) -> None: + pass - self.assertEqual(str(S(parameters=[ - P('foo', P.POSITIONAL_ONLY), - P('bar', P.VAR_POSITIONAL)])), - '(foo, /, *bar)') + expected_multiline = """( + x: Annotated[int, 'meta'], + /, + y: Literal['a', 'b'], + *, + z: 'LiteralString', + **kwargs: object +) -> None""" + self.assertEqual( + inspect.signature(func).format(max_width=-1), + expected_multiline, + ) def test_signature_replace_parameters(self): def test(a, b) -> 42: diff --git a/Lib/test/test_interpreters.py b/Lib/test/test_interpreters.py index 7c030bcf0321cd..5663706c0ccfb7 100644 --- a/Lib/test/test_interpreters.py +++ b/Lib/test/test_interpreters.py @@ -478,6 +478,11 @@ def test_success(self): self.assertEqual(out, 'it worked!') + def test_failure(self): + interp = interpreters.create() + with self.assertRaises(interpreters.RunFailedError): + interp.run('raise Exception') + def test_in_thread(self): interp = interpreters.create() script, file = _captured_script('print("it worked!", end="")') diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index 512745e45350d1..705e880d98685e 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -1152,6 +1152,78 @@ def test_pairwise(self): with self.assertRaises(TypeError): pairwise(None) # non-iterable argument + def test_pairwise_reenter(self): + def check(reenter_at, expected): + class I: + count = 0 + def __iter__(self): + return self + def __next__(self): + self.count +=1 + if self.count in reenter_at: + return next(it) + return [self.count] # new object + + it = pairwise(I()) + for item in expected: + self.assertEqual(next(it), item) + + check({1}, [ + (([2], [3]), [4]), + ([4], [5]), + ]) + check({2}, [ + ([1], ([1], [3])), + (([1], [3]), [4]), + ([4], [5]), + ]) + check({3}, [ + ([1], [2]), + ([2], ([2], [4])), + (([2], [4]), [5]), + ([5], [6]), + ]) + check({1, 2}, [ + ((([3], [4]), [5]), [6]), + ([6], [7]), + ]) + check({1, 3}, [ + (([2], ([2], [4])), [5]), + ([5], [6]), + ]) + check({1, 4}, [ + (([2], [3]), (([2], [3]), [5])), + ((([2], [3]), [5]), [6]), + ([6], [7]), + ]) + check({2, 3}, [ + ([1], ([1], ([1], [4]))), + (([1], ([1], [4])), [5]), + ([5], [6]), + ]) + + def test_pairwise_reenter2(self): + def check(maxcount, expected): + class I: + count = 0 + def __iter__(self): + return self + def __next__(self): + if self.count >= maxcount: + raise StopIteration + self.count +=1 + if self.count == 1: + return next(it, None) + return [self.count] # new object + + it = pairwise(I()) + self.assertEqual(list(it), expected) + + check(1, []) + check(2, []) + check(3, []) + check(4, [(([2], [3]), [4])]) + def test_product(self): for args, result in [ ([], [()]), # zero iterables diff --git a/Lib/test/test_module/__init__.py b/Lib/test/test_module/__init__.py index db2133a9e8d17b..d49c44df4d839d 100644 --- a/Lib/test/test_module/__init__.py +++ b/Lib/test/test_module/__init__.py @@ -1,4 +1,5 @@ # Test the module type +import importlib.machinery import unittest import weakref from test.support import gc_collect @@ -264,6 +265,35 @@ def test_module_repr_source(self): self.assertEqual(r[-len(ends_with):], ends_with, '{!r} does not end with {!r}'.format(r, ends_with)) + def test_module_repr_with_namespace_package(self): + m = ModuleType('foo') + loader = importlib.machinery.NamespaceLoader('foo', ['bar'], 'baz') + spec = importlib.machinery.ModuleSpec('foo', loader) + m.__loader__ = loader + m.__spec__ = spec + self.assertEqual(repr(m), "") + + def test_module_repr_with_namespace_package_and_custom_loader(self): + m = ModuleType('foo') + loader = BareLoader() + spec = importlib.machinery.ModuleSpec('foo', loader) + m.__loader__ = loader + m.__spec__ = spec + expected_repr_pattern = r"\)>" + self.assertRegex(repr(m), expected_repr_pattern) + self.assertNotIn('from', repr(m)) + + def test_module_repr_with_fake_namespace_package(self): + m = ModuleType('foo') + loader = BareLoader() + loader._path = ['spam'] + spec = importlib.machinery.ModuleSpec('foo', loader) + m.__loader__ = loader + m.__spec__ = spec + expected_repr_pattern = r"\)>" + self.assertRegex(repr(m), expected_repr_pattern) + self.assertNotIn('from', repr(m)) + def test_module_finalization_at_shutdown(self): # Module globals and builtins should still be available during shutdown rc, out, err = assert_python_ok("-c", "from test.test_module import final_a") diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 3e710d1c6dabe4..bf990ed36fbcae 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -256,6 +256,7 @@ def test_join(self): tester('ntpath.join("a", "b", "c")', 'a\\b\\c') tester('ntpath.join("a\\", "b", "c")', 'a\\b\\c') tester('ntpath.join("a", "b\\", "c")', 'a\\b\\c') + tester('ntpath.join("a", "b", "c\\")', 'a\\b\\c\\') tester('ntpath.join("a", "b", "\\c")', '\\c') tester('ntpath.join("d:\\", "\\pleep")', 'd:\\pleep') tester('ntpath.join("d:\\", "a", "b")', 'd:\\a\\b') @@ -313,6 +314,16 @@ def test_join(self): tester("ntpath.join('\\\\computer\\', 'share')", '\\\\computer\\share') tester("ntpath.join('\\\\computer\\share\\', 'a')", '\\\\computer\\share\\a') tester("ntpath.join('\\\\computer\\share\\a\\', 'b')", '\\\\computer\\share\\a\\b') + # Second part is anchored, so that the first part is ignored. + tester("ntpath.join('a', 'Z:b', 'c')", 'Z:b\\c') + tester("ntpath.join('a', 'Z:\\b', 'c')", 'Z:\\b\\c') + tester("ntpath.join('a', '\\\\b\\c', 'd')", '\\\\b\\c\\d') + # Second part has a root but not drive. + tester("ntpath.join('a', '\\b', 'c')", '\\b\\c') + tester("ntpath.join('Z:/a', '/b', 'c')", 'Z:\\b\\c') + tester("ntpath.join('//?/Z:/a', '/b', 'c')", '\\\\?\\Z:\\b\\c') + tester("ntpath.join('D:a', './c:b')", 'D:a\\.\\c:b') + tester("ntpath.join('D:/a', './c:b')", 'D:\\a\\.\\c:b') def test_normpath(self): tester("ntpath.normpath('A//////././//.//B')", r'A\B') diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index d6719559a248f2..d35516a5c8fd9b 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -41,6 +41,9 @@ def test_is_notimplemented(self): only_posix = unittest.skipIf(os.name == 'nt', 'test requires a POSIX-compatible system') +root_in_posix = False +if hasattr(os, 'geteuid'): + root_in_posix = (os.geteuid() == 0) # # Tests for the pure classes. @@ -178,45 +181,30 @@ def with_segments(self, *pathsegments): for parent in p.parents: self.assertEqual(42, parent.session_id) - def _get_drive_root_parts(self, parts): - path = self.cls(*parts) - return path.drive, path.root, path.parts - - def _check_drive_root_parts(self, arg, *expected): + def _check_parse_path(self, raw_path, *expected): sep = self.pathmod.sep - actual = self._get_drive_root_parts([x.replace('/', sep) for x in arg]) + actual = self.cls._parse_path(raw_path.replace('/', sep)) self.assertEqual(actual, expected) if altsep := self.pathmod.altsep: - actual = self._get_drive_root_parts([x.replace('/', altsep) for x in arg]) + actual = self.cls._parse_path(raw_path.replace('/', altsep)) self.assertEqual(actual, expected) - def test_drive_root_parts_common(self): - check = self._check_drive_root_parts + def test_parse_path_common(self): + check = self._check_parse_path sep = self.pathmod.sep - # Unanchored parts. - check((), '', '', ()) - check(('a',), '', '', ('a',)) - check(('a/',), '', '', ('a',)) - check(('a', 'b'), '', '', ('a', 'b')) - # Expansion. - check(('a/b',), '', '', ('a', 'b')) - check(('a/b/',), '', '', ('a', 'b')) - check(('a', 'b/c', 'd'), '', '', ('a', 'b', 'c', 'd')) - # Collapsing and stripping excess slashes. - check(('a', 'b//c', 'd'), '', '', ('a', 'b', 'c', 'd')) - check(('a', 'b/c/', 'd'), '', '', ('a', 'b', 'c', 'd')) - # Eliminating standalone dots. - check(('.',), '', '', ()) - check(('.', '.', 'b'), '', '', ('b',)) - check(('a', '.', 'b'), '', '', ('a', 'b')) - check(('a', '.', '.'), '', '', ('a',)) - # The first part is anchored. - check(('/a/b',), '', sep, (sep, 'a', 'b')) - check(('/a', 'b'), '', sep, (sep, 'a', 'b')) - check(('/a/', 'b'), '', sep, (sep, 'a', 'b')) - # Ignoring parts before an anchored part. - check(('a', '/b', 'c'), '', sep, (sep, 'b', 'c')) - check(('a', '/b', '/c'), '', sep, (sep, 'c')) + check('', '', '', []) + check('a', '', '', ['a']) + check('a/', '', '', ['a']) + check('a/b', '', '', ['a', 'b']) + check('a/b/', '', '', ['a', 'b']) + check('a/b/c/d', '', '', ['a', 'b', 'c', 'd']) + check('a/b//c/d', '', '', ['a', 'b', 'c', 'd']) + check('a/b/c/d', '', '', ['a', 'b', 'c', 'd']) + check('.', '', '', []) + check('././b', '', '', ['b']) + check('a/./b', '', '', ['a', 'b']) + check('a/./.', '', '', ['a']) + check('/a/b', '', sep, ['a', 'b']) def test_join_common(self): P = self.cls @@ -592,8 +580,6 @@ def test_with_suffix_common(self): self.assertRaises(ValueError, P('a/b').with_suffix, '.c/.d') self.assertRaises(ValueError, P('a/b').with_suffix, './.d') self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.') - self.assertRaises(ValueError, P('a/b').with_suffix, - (self.pathmod.sep, 'd')) def test_relative_to_common(self): P = self.cls @@ -831,17 +817,17 @@ def test_repr_roundtrips(self): class PurePosixPathTest(PurePathTest): cls = pathlib.PurePosixPath - def test_drive_root_parts(self): - check = self._check_drive_root_parts + def test_parse_path(self): + check = self._check_parse_path # Collapsing of excess leading slashes, except for the double-slash # special case. - check(('//a', 'b'), '', '//', ('//', 'a', 'b')) - check(('///a', 'b'), '', '/', ('/', 'a', 'b')) - check(('////a', 'b'), '', '/', ('/', 'a', 'b')) + check('//a/b', '', '//', ['a', 'b']) + check('///a/b', '', '/', ['a', 'b']) + check('////a/b', '', '/', ['a', 'b']) # Paths which look like NT paths aren't treated specially. - check(('c:a',), '', '', ('c:a',)) - check(('c:\\a',), '', '', ('c:\\a',)) - check(('\\a',), '', '', ('\\a',)) + check('c:a', '', '', ['c:a',]) + check('c:\\a', '', '', ['c:\\a',]) + check('\\a', '', '', ['\\a',]) def test_root(self): P = self.cls @@ -939,67 +925,53 @@ class PureWindowsPathTest(PurePathTest): ], }) - def test_drive_root_parts(self): - check = self._check_drive_root_parts + def test_parse_path(self): + check = self._check_parse_path # First part is anchored. - check(('c:',), 'c:', '', ('c:',)) - check(('c:/',), 'c:', '\\', ('c:\\',)) - check(('/',), '', '\\', ('\\',)) - check(('c:a',), 'c:', '', ('c:', 'a')) - check(('c:/a',), 'c:', '\\', ('c:\\', 'a')) - check(('/a',), '', '\\', ('\\', 'a')) - # UNC paths. - check(('//',), '\\\\', '', ('\\\\',)) - check(('//a',), '\\\\a', '', ('\\\\a',)) - check(('//a/',), '\\\\a\\', '', ('\\\\a\\',)) - check(('//a/b',), '\\\\a\\b', '\\', ('\\\\a\\b\\',)) - check(('//a/b/',), '\\\\a\\b', '\\', ('\\\\a\\b\\',)) - check(('//a/b/c',), '\\\\a\\b', '\\', ('\\\\a\\b\\', 'c')) - # Second part is anchored, so that the first part is ignored. - check(('a', 'Z:b', 'c'), 'Z:', '', ('Z:', 'b', 'c')) - check(('a', 'Z:/b', 'c'), 'Z:', '\\', ('Z:\\', 'b', 'c')) + check('c:', 'c:', '', []) + check('c:/', 'c:', '\\', []) + check('/', '', '\\', []) + check('c:a', 'c:', '', ['a']) + check('c:/a', 'c:', '\\', ['a']) + check('/a', '', '\\', ['a']) # UNC paths. - check(('a', '//b/c', 'd'), '\\\\b\\c', '\\', ('\\\\b\\c\\', 'd')) + check('//', '\\\\', '', []) + check('//a', '\\\\a', '', []) + check('//a/', '\\\\a\\', '', []) + check('//a/b', '\\\\a\\b', '\\', []) + check('//a/b/', '\\\\a\\b', '\\', []) + check('//a/b/c', '\\\\a\\b', '\\', ['c']) # Collapsing and stripping excess slashes. - check(('a', 'Z://b//c/', 'd/'), 'Z:', '\\', ('Z:\\', 'b', 'c', 'd')) + check('Z://b//c/d/', 'Z:', '\\', ['b', 'c', 'd']) # UNC paths. - check(('a', '//b/c//', 'd'), '\\\\b\\c', '\\', ('\\\\b\\c\\', 'd')) + check('//b/c//d', '\\\\b\\c', '\\', ['d']) # Extended paths. - check(('//./c:',), '\\\\.\\c:', '', ('\\\\.\\c:',)) - check(('//?/c:/',), '\\\\?\\c:', '\\', ('\\\\?\\c:\\',)) - check(('//?/c:/a',), '\\\\?\\c:', '\\', ('\\\\?\\c:\\', 'a')) - check(('//?/c:/a', '/b'), '\\\\?\\c:', '\\', ('\\\\?\\c:\\', 'b')) + check('//./c:', '\\\\.\\c:', '', []) + check('//?/c:/', '\\\\?\\c:', '\\', []) + check('//?/c:/a', '\\\\?\\c:', '\\', ['a']) # Extended UNC paths (format is "\\?\UNC\server\share"). - check(('//?',), '\\\\?', '', ('\\\\?',)) - check(('//?/',), '\\\\?\\', '', ('\\\\?\\',)) - check(('//?/UNC',), '\\\\?\\UNC', '', ('\\\\?\\UNC',)) - check(('//?/UNC/',), '\\\\?\\UNC\\', '', ('\\\\?\\UNC\\',)) - check(('//?/UNC/b',), '\\\\?\\UNC\\b', '', ('\\\\?\\UNC\\b',)) - check(('//?/UNC/b/',), '\\\\?\\UNC\\b\\', '', ('\\\\?\\UNC\\b\\',)) - check(('//?/UNC/b/c',), '\\\\?\\UNC\\b\\c', '\\', ('\\\\?\\UNC\\b\\c\\',)) - check(('//?/UNC/b/c/',), '\\\\?\\UNC\\b\\c', '\\', ('\\\\?\\UNC\\b\\c\\',)) - check(('//?/UNC/b/c/d',), '\\\\?\\UNC\\b\\c', '\\', ('\\\\?\\UNC\\b\\c\\', 'd')) + check('//?', '\\\\?', '', []) + check('//?/', '\\\\?\\', '', []) + check('//?/UNC', '\\\\?\\UNC', '', []) + check('//?/UNC/', '\\\\?\\UNC\\', '', []) + check('//?/UNC/b', '\\\\?\\UNC\\b', '', []) + check('//?/UNC/b/', '\\\\?\\UNC\\b\\', '', []) + check('//?/UNC/b/c', '\\\\?\\UNC\\b\\c', '\\', []) + check('//?/UNC/b/c/', '\\\\?\\UNC\\b\\c', '\\', []) + check('//?/UNC/b/c/d', '\\\\?\\UNC\\b\\c', '\\', ['d']) # UNC device paths - check(('//./BootPartition/',), '\\\\.\\BootPartition', '\\', ('\\\\.\\BootPartition\\',)) - check(('//?/BootPartition/',), '\\\\?\\BootPartition', '\\', ('\\\\?\\BootPartition\\',)) - check(('//./PhysicalDrive0',), '\\\\.\\PhysicalDrive0', '', ('\\\\.\\PhysicalDrive0',)) - check(('//?/Volume{}/',), '\\\\?\\Volume{}', '\\', ('\\\\?\\Volume{}\\',)) - check(('//./nul',), '\\\\.\\nul', '', ('\\\\.\\nul',)) - # Second part has a root but not drive. - check(('a', '/b', 'c'), '', '\\', ('\\', 'b', 'c')) - check(('Z:/a', '/b', 'c'), 'Z:', '\\', ('Z:\\', 'b', 'c')) - check(('//?/Z:/a', '/b', 'c'), '\\\\?\\Z:', '\\', ('\\\\?\\Z:\\', 'b', 'c')) - # Joining with the same drive => the first path is appended to if - # the second path is relative. - check(('c:/a/b', 'c:x/y'), 'c:', '\\', ('c:\\', 'a', 'b', 'x', 'y')) - check(('c:/a/b', 'c:/x/y'), 'c:', '\\', ('c:\\', 'x', 'y')) + check('//./BootPartition/', '\\\\.\\BootPartition', '\\', []) + check('//?/BootPartition/', '\\\\?\\BootPartition', '\\', []) + check('//./PhysicalDrive0', '\\\\.\\PhysicalDrive0', '', []) + check('//?/Volume{}/', '\\\\?\\Volume{}', '\\', []) + check('//./nul', '\\\\.\\nul', '', []) # Paths to files with NTFS alternate data streams - check(('./c:s',), '', '', ('c:s',)) - check(('cc:s',), '', '', ('cc:s',)) - check(('C:c:s',), 'C:', '', ('C:', 'c:s')) - check(('C:/c:s',), 'C:', '\\', ('C:\\', 'c:s')) - check(('D:a', './c:b'), 'D:', '', ('D:', 'a', 'c:b')) - check(('D:/a', './c:b'), 'D:', '\\', ('D:\\', 'a', 'c:b')) + check('./c:s', '', '', ['c:s']) + check('cc:s', '', '', ['cc:s']) + check('C:c:s', 'C:', '', ['c:s']) + check('C:/c:s', 'C:', '\\', ['c:s']) + check('D:a/c:b', 'D:', '', ['a', 'c:b']) + check('D:/a/c:b', 'D:', '\\', ['a', 'c:b']) def test_str(self): p = self.cls('a/b/c') @@ -1926,6 +1898,21 @@ def test_read_write_text(self): self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes') self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), 'äbcdefg') + def test_read_text_with_newlines(self): + p = self.cls(BASE) + # Check that `\n` character change nothing + (p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq') + self.assertEqual((p / 'fileA').read_text(newline='\n'), + 'abcde\r\nfghlk\n\rmnopq') + # Check that `\r` character replaces `\n` + (p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq') + self.assertEqual((p / 'fileA').read_text(newline='\r'), + 'abcde\r\nfghlk\n\rmnopq') + # Check that `\r\n` character replaces `\n` + (p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq') + self.assertEqual((p / 'fileA').read_text(newline='\r\n'), + 'abcde\r\nfghlk\n\rmnopq') + def test_write_text_with_newlines(self): p = self.cls(BASE) # Check that `\n` character change nothing @@ -2003,9 +1990,9 @@ def _check(glob, expected): _check(p.glob("brokenLink"), ['brokenLink']) if not self.can_symlink: - _check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE"]) + _check(p.glob("*/"), ["dirA/", "dirB/", "dirC/", "dirE/"]) else: - _check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE", "linkB"]) + _check(p.glob("*/"), ["dirA/", "dirB/", "dirC/", "dirE/", "linkB/"]) def test_glob_empty_pattern(self): p = self.cls() @@ -2038,17 +2025,17 @@ def _check(path, glob, expected): _check(p, "*A", ["dirA", "fileA", "linkA"]) _check(p, "*B/*", ["dirB/fileB", "dirB/linkD", "linkB/fileB", "linkB/linkD"]) _check(p, "*/fileB", ["dirB/fileB", "linkB/fileB"]) - _check(p, "*/", ["dirA", "dirB", "dirC", "dirE", "linkB"]) + _check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirE/", "linkB/"]) _check(p, "dir*/*/..", ["dirC/dirD/..", "dirA/linkC/.."]) - _check(p, "dir*/**/", ["dirA", "dirA/linkC", "dirA/linkC/linkD", "dirB", "dirB/linkD", - "dirC", "dirC/dirD", "dirE"]) + _check(p, "dir*/**/", ["dirA/", "dirA/linkC/", "dirA/linkC/linkD/", "dirB/", "dirB/linkD/", + "dirC/", "dirC/dirD/", "dirE/"]) _check(p, "dir*/**/..", ["dirA/..", "dirA/linkC/..", "dirB/..", "dirC/..", "dirC/dirD/..", "dirE/.."]) - _check(p, "dir*/*/**/", ["dirA/linkC", "dirA/linkC/linkD", "dirB/linkD", "dirC/dirD"]) + _check(p, "dir*/*/**/", ["dirA/linkC/", "dirA/linkC/linkD/", "dirB/linkD/", "dirC/dirD/"]) _check(p, "dir*/*/**/..", ["dirA/linkC/..", "dirC/dirD/.."]) _check(p, "dir*/**/fileC", ["dirC/fileC"]) - _check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD"]) - _check(p, "*/dirD/**/", ["dirC/dirD"]) + _check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD/"]) + _check(p, "*/dirD/**/", ["dirC/dirD/"]) def test_glob_no_follow_symlinks_common(self): if not self.can_symlink: @@ -2063,15 +2050,15 @@ def _check(path, glob, expected): _check(p, "*A", ["dirA", "fileA", "linkA"]) _check(p, "*B/*", ["dirB/fileB", "dirB/linkD"]) _check(p, "*/fileB", ["dirB/fileB"]) - _check(p, "*/", ["dirA", "dirB", "dirC", "dirE"]) + _check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirE/"]) _check(p, "dir*/*/..", ["dirC/dirD/.."]) - _check(p, "dir*/**/", ["dirA", "dirB", "dirC", "dirC/dirD", "dirE"]) + _check(p, "dir*/**/", ["dirA/", "dirB/", "dirC/", "dirC/dirD/", "dirE/"]) _check(p, "dir*/**/..", ["dirA/..", "dirB/..", "dirC/..", "dirC/dirD/..", "dirE/.."]) - _check(p, "dir*/*/**/", ["dirC/dirD"]) + _check(p, "dir*/*/**/", ["dirC/dirD/"]) _check(p, "dir*/*/**/..", ["dirC/dirD/.."]) _check(p, "dir*/**/fileC", ["dirC/fileC"]) - _check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD"]) - _check(p, "*/dirD/**/", ["dirC/dirD"]) + _check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD/"]) + _check(p, "*/dirD/**/", ["dirC/dirD/"]) def test_rglob_common(self): def _check(glob, expected): @@ -2093,25 +2080,25 @@ def _check(glob, expected): "dirC/fileC", "dirC/dirD/fileD"]) if not self.can_symlink: _check(p.rglob("*/"), [ - "dirA", "dirB", "dirC", "dirC/dirD", "dirE", + "dirA/", "dirB/", "dirC/", "dirC/dirD/", "dirE/", ]) else: _check(p.rglob("*/"), [ - "dirA", "dirA/linkC", "dirB", "dirB/linkD", "dirC", - "dirC/dirD", "dirE", "linkB", + "dirA/", "dirA/linkC/", "dirB/", "dirB/linkD/", "dirC/", + "dirC/dirD/", "dirE/", "linkB/", ]) - _check(p.rglob(""), ["", "dirA", "dirB", "dirC", "dirE", "dirC/dirD"]) + _check(p.rglob(""), ["./", "dirA/", "dirB/", "dirC/", "dirE/", "dirC/dirD/"]) p = P(BASE, "dirC") _check(p.rglob("*"), ["dirC/fileC", "dirC/novel.txt", "dirC/dirD", "dirC/dirD/fileD"]) _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"]) _check(p.rglob("**/file*"), ["dirC/fileC", "dirC/dirD/fileD"]) - _check(p.rglob("dir*/**/"), ["dirC/dirD"]) + _check(p.rglob("dir*/**/"), ["dirC/dirD/"]) _check(p.rglob("*/*"), ["dirC/dirD/fileD"]) - _check(p.rglob("*/"), ["dirC/dirD"]) - _check(p.rglob(""), ["dirC", "dirC/dirD"]) - _check(p.rglob("**/"), ["dirC", "dirC/dirD"]) + _check(p.rglob("*/"), ["dirC/dirD/"]) + _check(p.rglob(""), ["dirC/", "dirC/dirD/"]) + _check(p.rglob("**/"), ["dirC/", "dirC/dirD/"]) # gh-91616, a re module regression _check(p.rglob("*.txt"), ["dirC/novel.txt"]) _check(p.rglob("*.*"), ["dirC/novel.txt"]) @@ -2130,18 +2117,18 @@ def _check(path, glob, expected): _check(p, "*/fileB", ["dirB/fileB", "dirA/linkC/fileB", "linkB/fileB"]) _check(p, "file*", ["fileA", "dirA/linkC/fileB", "dirB/fileB", "dirC/fileC", "dirC/dirD/fileD", "linkB/fileB"]) - _check(p, "*/", ["dirA", "dirA/linkC", "dirA/linkC/linkD", "dirB", "dirB/linkD", - "dirC", "dirC/dirD", "dirE", "linkB", "linkB/linkD"]) - _check(p, "", ["", "dirA", "dirA/linkC", "dirA/linkC/linkD", "dirB", "dirB/linkD", - "dirC", "dirE", "dirC/dirD", "linkB", "linkB/linkD"]) + _check(p, "*/", ["dirA/", "dirA/linkC/", "dirA/linkC/linkD/", "dirB/", "dirB/linkD/", + "dirC/", "dirC/dirD/", "dirE/", "linkB/", "linkB/linkD/"]) + _check(p, "", ["./", "dirA/", "dirA/linkC/", "dirA/linkC/linkD/", "dirB/", "dirB/linkD/", + "dirC/", "dirE/", "dirC/dirD/", "linkB/", "linkB/linkD/"]) p = P(BASE, "dirC") _check(p, "*", ["dirC/fileC", "dirC/novel.txt", "dirC/dirD", "dirC/dirD/fileD"]) _check(p, "file*", ["dirC/fileC", "dirC/dirD/fileD"]) _check(p, "*/*", ["dirC/dirD/fileD"]) - _check(p, "*/", ["dirC/dirD"]) - _check(p, "", ["dirC", "dirC/dirD"]) + _check(p, "*/", ["dirC/dirD/"]) + _check(p, "", ["dirC/", "dirC/dirD/"]) # gh-91616, a re module regression _check(p, "*.txt", ["dirC/novel.txt"]) _check(p, "*.*", ["dirC/novel.txt"]) @@ -2158,16 +2145,16 @@ def _check(path, glob, expected): _check(p, "*/fileA", []) _check(p, "*/fileB", ["dirB/fileB"]) _check(p, "file*", ["fileA", "dirB/fileB", "dirC/fileC", "dirC/dirD/fileD", ]) - _check(p, "*/", ["dirA", "dirB", "dirC", "dirC/dirD", "dirE"]) - _check(p, "", ["", "dirA", "dirB", "dirC", "dirE", "dirC/dirD"]) + _check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirC/dirD/", "dirE/"]) + _check(p, "", ["./", "dirA/", "dirB/", "dirC/", "dirE/", "dirC/dirD/"]) p = P(BASE, "dirC") _check(p, "*", ["dirC/fileC", "dirC/novel.txt", "dirC/dirD", "dirC/dirD/fileD"]) _check(p, "file*", ["dirC/fileC", "dirC/dirD/fileD"]) _check(p, "*/*", ["dirC/dirD/fileD"]) - _check(p, "*/", ["dirC/dirD"]) - _check(p, "", ["dirC", "dirC/dirD"]) + _check(p, "*/", ["dirC/dirD/"]) + _check(p, "", ["dirC/", "dirC/dirD/"]) # gh-91616, a re module regression _check(p, "*.txt", ["dirC/novel.txt"]) _check(p, "*.*", ["dirC/novel.txt"]) @@ -3010,27 +2997,75 @@ def test_chmod_follow_symlinks_true(self): # XXX also need a test for lchmod. - @unittest.skipUnless(pwd, "the pwd module is needed for this test") - def test_owner(self): - p = self.cls(BASE) / 'fileA' - uid = p.stat().st_uid + def _get_pw_name_or_skip_test(self, uid): try: - name = pwd.getpwuid(uid).pw_name + return pwd.getpwuid(uid).pw_name except KeyError: self.skipTest( "user %d doesn't have an entry in the system database" % uid) - self.assertEqual(name, p.owner()) - @unittest.skipUnless(grp, "the grp module is needed for this test") - def test_group(self): + @unittest.skipUnless(pwd, "the pwd module is needed for this test") + def test_owner(self): p = self.cls(BASE) / 'fileA' - gid = p.stat().st_gid + expected_uid = p.stat().st_uid + expected_name = self._get_pw_name_or_skip_test(expected_uid) + + self.assertEqual(expected_name, p.owner()) + + @unittest.skipUnless(pwd, "the pwd module is needed for this test") + @unittest.skipUnless(root_in_posix, "test needs root privilege") + def test_owner_no_follow_symlinks(self): + all_users = [u.pw_uid for u in pwd.getpwall()] + if len(all_users) < 2: + self.skipTest("test needs more than one user") + + target = self.cls(BASE) / 'fileA' + link = self.cls(BASE) / 'linkA' + + uid_1, uid_2 = all_users[:2] + os.chown(target, uid_1, -1) + os.chown(link, uid_2, -1, follow_symlinks=False) + + expected_uid = link.stat(follow_symlinks=False).st_uid + expected_name = self._get_pw_name_or_skip_test(expected_uid) + + self.assertEqual(expected_uid, uid_2) + self.assertEqual(expected_name, link.owner(follow_symlinks=False)) + + def _get_gr_name_or_skip_test(self, gid): try: - name = grp.getgrgid(gid).gr_name + return grp.getgrgid(gid).gr_name except KeyError: self.skipTest( "group %d doesn't have an entry in the system database" % gid) - self.assertEqual(name, p.group()) + + @unittest.skipUnless(grp, "the grp module is needed for this test") + def test_group(self): + p = self.cls(BASE) / 'fileA' + expected_gid = p.stat().st_gid + expected_name = self._get_gr_name_or_skip_test(expected_gid) + + self.assertEqual(expected_name, p.group()) + + @unittest.skipUnless(grp, "the grp module is needed for this test") + @unittest.skipUnless(root_in_posix, "test needs root privilege") + def test_group_no_follow_symlinks(self): + all_groups = [g.gr_gid for g in grp.getgrall()] + if len(all_groups) < 2: + self.skipTest("test needs more than one group") + + target = self.cls(BASE) / 'fileA' + link = self.cls(BASE) / 'linkA' + + gid_1, gid_2 = all_groups[:2] + os.chown(target, -1, gid_1) + os.chown(link, -1, gid_2, follow_symlinks=False) + + expected_gid = link.stat(follow_symlinks=False).st_gid + expected_name = self._get_pw_name_or_skip_test(expected_gid) + + self.assertEqual(expected_gid, gid_2) + self.assertEqual(expected_name, link.group(follow_symlinks=False)) def test_unlink(self): p = self.cls(BASE) / 'fileA' @@ -3677,7 +3712,7 @@ def test_glob(self): P = self.cls p = P(BASE) self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") }) - self.assertEqual(set(p.glob("*a\\")), { P(BASE, "dirA") }) + self.assertEqual(set(p.glob("*a\\")), { P(BASE, "dirA/") }) self.assertEqual(set(p.glob("F*a")), { P(BASE, "fileA") }) self.assertEqual(set(map(str, p.glob("FILEa"))), {f"{p}\\fileA"}) self.assertEqual(set(map(str, p.glob("F*a"))), {f"{p}\\fileA"}) @@ -3686,7 +3721,7 @@ def test_rglob(self): P = self.cls p = P(BASE, "dirC") self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") }) - self.assertEqual(set(p.rglob("*\\")), { P(BASE, "dirC/dirD") }) + self.assertEqual(set(p.rglob("*\\")), { P(BASE, "dirC/dirD/") }) self.assertEqual(set(map(str, p.rglob("FILEd"))), {f"{p}\\dirD\\fileD"}) def test_expanduser(self): diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 67a4053a2ac8bc..d53fe3c611bc35 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -778,6 +778,59 @@ def test_pdb_where_command(): (Pdb) continue """ +def test_pdb_interact_command(): + """Test interact command + + >>> g = 0 + >>> dict_g = {} + + >>> def test_function(): + ... x = 1 + ... lst_local = [] + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + + >>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE + ... 'interact', + ... 'x', + ... 'g', + ... 'x = 2', + ... 'g = 3', + ... 'dict_g["a"] = True', + ... 'lst_local.append(x)', + ... 'exit()', + ... 'p x', + ... 'p g', + ... 'p dict_g', + ... 'p lst_local', + ... 'continue', + ... ]): + ... test_function() + --Return-- + > (4)test_function()->None + -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + (Pdb) interact + *pdb interact start* + ... x + 1 + ... g + 0 + ... x = 2 + ... g = 3 + ... dict_g["a"] = True + ... lst_local.append(x) + ... exit() + *exit from pdb interact command* + (Pdb) p x + 1 + (Pdb) p g + 0 + (Pdb) p dict_g + {'a': True} + (Pdb) p lst_local + [2] + (Pdb) continue + """ + def test_convenience_variables(): """Test convenience variables @@ -2520,15 +2573,21 @@ def tearDown(self): @unittest.skipIf(sys.flags.safe_path, 'PYTHONSAFEPATH changes default sys.path') - def _run_pdb(self, pdb_args, commands, expected_returncode=0): + def _run_pdb(self, pdb_args, commands, + expected_returncode=0, + extra_env=None): self.addCleanup(os_helper.rmtree, '__pycache__') cmd = [sys.executable, '-m', 'pdb'] + pdb_args + if extra_env is not None: + env = os.environ | extra_env + else: + env = os.environ with subprocess.Popen( cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, - env = {**os.environ, 'PYTHONIOENCODING': 'utf-8'} + env = {**env, 'PYTHONIOENCODING': 'utf-8'} ) as proc: stdout, stderr = proc.communicate(str.encode(commands)) stdout = stdout and bytes.decode(stdout) @@ -2540,13 +2599,15 @@ def _run_pdb(self, pdb_args, commands, expected_returncode=0): ) return stdout, stderr - def run_pdb_script(self, script, commands, expected_returncode=0): + def run_pdb_script(self, script, commands, + expected_returncode=0, + extra_env=None): """Run 'script' lines with pdb and the pdb 'commands'.""" filename = 'main.py' with open(filename, 'w') as f: f.write(textwrap.dedent(script)) self.addCleanup(os_helper.unlink, filename) - return self._run_pdb([filename], commands, expected_returncode) + return self._run_pdb([filename], commands, expected_returncode, extra_env) def run_pdb_module(self, script, commands): """Runs the script code as part of a module""" @@ -3131,6 +3192,23 @@ def test_issue42384_symlink(self): self.assertEqual(stdout.split('\n')[2].rstrip('\r'), expected) + def test_safe_path(self): + """ With safe_path set, pdb should not mangle sys.path[0]""" + + script = textwrap.dedent(""" + import sys + import random + print('sys.path[0] is', sys.path[0]) + """) + commands = 'c\n' + + + with os_helper.temp_cwd() as cwd: + stdout, _ = self.run_pdb_script(script, commands, extra_env={'PYTHONSAFEPATH': '1'}) + + unexpected = f'sys.path[0] is {os.path.realpath(cwd)}' + self.assertNotIn(unexpected, stdout) + def test_issue42383(self): with os_helper.temp_cwd() as cwd: with open('foo.py', 'w') as f: @@ -3268,7 +3346,7 @@ def setUpClass(): # Ensure that the readline module is loaded # If this fails, the test is skipped because SkipTest will be raised readline = import_module('readline') - if readline.__doc__ and "libedit" in readline.__doc__: + if readline.backend == "editline": raise unittest.SkipTest("libedit readline is not supported for pdb") def test_basic_completion(self): diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 9be4640f970aef..86ce1b1d41ba61 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -47,18 +47,26 @@ def tearDown(self): safe_rmdir(os_helper.TESTFN + suffix) def test_join(self): - self.assertEqual(posixpath.join("/foo", "bar", "/bar", "baz"), - "/bar/baz") - self.assertEqual(posixpath.join("/foo", "bar", "baz"), "/foo/bar/baz") - self.assertEqual(posixpath.join("/foo/", "bar/", "baz/"), - "/foo/bar/baz/") - - self.assertEqual(posixpath.join(b"/foo", b"bar", b"/bar", b"baz"), - b"/bar/baz") - self.assertEqual(posixpath.join(b"/foo", b"bar", b"baz"), - b"/foo/bar/baz") - self.assertEqual(posixpath.join(b"/foo/", b"bar/", b"baz/"), - b"/foo/bar/baz/") + fn = posixpath.join + self.assertEqual(fn("/foo", "bar", "/bar", "baz"), "/bar/baz") + self.assertEqual(fn("/foo", "bar", "baz"), "/foo/bar/baz") + self.assertEqual(fn("/foo/", "bar/", "baz/"), "/foo/bar/baz/") + + self.assertEqual(fn(b"/foo", b"bar", b"/bar", b"baz"), b"/bar/baz") + self.assertEqual(fn(b"/foo", b"bar", b"baz"), b"/foo/bar/baz") + self.assertEqual(fn(b"/foo/", b"bar/", b"baz/"), b"/foo/bar/baz/") + + self.assertEqual(fn("a", "b"), "a/b") + self.assertEqual(fn("a", "b/"), "a/b/") + self.assertEqual(fn("a/", "b"), "a/b") + self.assertEqual(fn("a/", "b/"), "a/b/") + self.assertEqual(fn("a", "b/c", "d"), "a/b/c/d") + self.assertEqual(fn("a", "b//c", "d"), "a/b//c/d") + self.assertEqual(fn("a", "b/c/", "d"), "a/b/c/d") + self.assertEqual(fn("/a", "b"), "/a/b") + self.assertEqual(fn("/a/", "b"), "/a/b") + self.assertEqual(fn("a", "/b", "c"), "/b/c") + self.assertEqual(fn("a", "/b", "/c"), "/c") def test_split(self): self.assertEqual(posixpath.split("/foo/bar"), ("/foo", "bar")) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 4d1766fb16ce9f..4e6fed1ab969ac 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -9,7 +9,6 @@ import random import re import test.support -import test.test_set import types import unittest @@ -623,9 +622,6 @@ def test_set_reprs(self): self.assertEqual(pprint.pformat(frozenset3(range(7)), width=20), 'frozenset3({0, 1, 2, 3, 4, 5, 6})') - @unittest.expectedFailure - #See http://bugs.python.org/issue13907 - @test.support.cpython_only def test_set_of_sets_reprs(self): # This test creates a complex arrangement of frozensets and # compares the pretty-printed repr against a string hard-coded in @@ -636,204 +632,106 @@ def test_set_of_sets_reprs(self): # partial ordering (subset relationships), the output of the # list.sort() method is undefined for lists of sets." # - # In a nutshell, the test assumes frozenset({0}) will always - # sort before frozenset({1}), but: - # # >>> frozenset({0}) < frozenset({1}) # False # >>> frozenset({1}) < frozenset({0}) # False # - # Consequently, this test is fragile and - # implementation-dependent. Small changes to Python's sort - # algorithm cause the test to fail when it should pass. - # XXX Or changes to the dictionary implementation... - - cube_repr_tgt = """\ -{frozenset(): frozenset({frozenset({2}), frozenset({0}), frozenset({1})}), - frozenset({0}): frozenset({frozenset(), - frozenset({0, 2}), - frozenset({0, 1})}), - frozenset({1}): frozenset({frozenset(), - frozenset({1, 2}), - frozenset({0, 1})}), - frozenset({2}): frozenset({frozenset(), - frozenset({1, 2}), - frozenset({0, 2})}), - frozenset({1, 2}): frozenset({frozenset({2}), - frozenset({1}), - frozenset({0, 1, 2})}), - frozenset({0, 2}): frozenset({frozenset({2}), - frozenset({0}), - frozenset({0, 1, 2})}), - frozenset({0, 1}): frozenset({frozenset({0}), - frozenset({1}), - frozenset({0, 1, 2})}), - frozenset({0, 1, 2}): frozenset({frozenset({1, 2}), - frozenset({0, 2}), - frozenset({0, 1})})}""" - cube = test.test_set.cube(3) - self.assertEqual(pprint.pformat(cube), cube_repr_tgt) - cubo_repr_tgt = """\ -{frozenset({frozenset({0, 2}), frozenset({0})}): frozenset({frozenset({frozenset({0, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0}), - frozenset({0, - 1})}), - frozenset({frozenset(), - frozenset({0})}), - frozenset({frozenset({2}), - frozenset({0, - 2})})}), - frozenset({frozenset({0, 1}), frozenset({1})}): frozenset({frozenset({frozenset({0, - 1}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0}), - frozenset({0, - 1})}), - frozenset({frozenset({1}), - frozenset({1, - 2})}), - frozenset({frozenset(), - frozenset({1})})}), - frozenset({frozenset({1, 2}), frozenset({1})}): frozenset({frozenset({frozenset({1, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({2}), - frozenset({1, - 2})}), - frozenset({frozenset(), - frozenset({1})}), - frozenset({frozenset({1}), - frozenset({0, - 1})})}), - frozenset({frozenset({1, 2}), frozenset({2})}): frozenset({frozenset({frozenset({1, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({1}), - frozenset({1, - 2})}), - frozenset({frozenset({2}), - frozenset({0, - 2})}), - frozenset({frozenset(), - frozenset({2})})}), - frozenset({frozenset(), frozenset({0})}): frozenset({frozenset({frozenset({0}), - frozenset({0, - 1})}), - frozenset({frozenset({0}), - frozenset({0, - 2})}), - frozenset({frozenset(), - frozenset({1})}), - frozenset({frozenset(), - frozenset({2})})}), - frozenset({frozenset(), frozenset({1})}): frozenset({frozenset({frozenset(), - frozenset({0})}), - frozenset({frozenset({1}), - frozenset({1, - 2})}), - frozenset({frozenset(), - frozenset({2})}), - frozenset({frozenset({1}), - frozenset({0, - 1})})}), - frozenset({frozenset({2}), frozenset()}): frozenset({frozenset({frozenset({2}), - frozenset({1, - 2})}), - frozenset({frozenset(), - frozenset({0})}), - frozenset({frozenset(), - frozenset({1})}), - frozenset({frozenset({2}), - frozenset({0, - 2})})}), - frozenset({frozenset({0, 1, 2}), frozenset({0, 1})}): frozenset({frozenset({frozenset({1, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0}), - frozenset({0, - 1})}), - frozenset({frozenset({1}), - frozenset({0, - 1})})}), - frozenset({frozenset({0}), frozenset({0, 1})}): frozenset({frozenset({frozenset(), - frozenset({0})}), - frozenset({frozenset({0, - 1}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0}), - frozenset({0, - 2})}), - frozenset({frozenset({1}), - frozenset({0, - 1})})}), - frozenset({frozenset({2}), frozenset({0, 2})}): frozenset({frozenset({frozenset({0, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({2}), - frozenset({1, - 2})}), - frozenset({frozenset({0}), - frozenset({0, - 2})}), - frozenset({frozenset(), - frozenset({2})})}), - frozenset({frozenset({0, 1, 2}), frozenset({0, 2})}): frozenset({frozenset({frozenset({1, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0, - 1}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0}), - frozenset({0, - 2})}), - frozenset({frozenset({2}), - frozenset({0, - 2})})}), - frozenset({frozenset({1, 2}), frozenset({0, 1, 2})}): frozenset({frozenset({frozenset({0, - 2}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({0, - 1}), - frozenset({0, - 1, - 2})}), - frozenset({frozenset({2}), - frozenset({1, - 2})}), - frozenset({frozenset({1}), - frozenset({1, - 2})})})}""" - - cubo = test.test_set.linegraph(cube) - self.assertEqual(pprint.pformat(cubo), cubo_repr_tgt) + # In this test we list all possible invariants of the result + # for unordered frozensets. + # + # This test has a long history, see: + # - https://github.com/python/cpython/commit/969fe57baa0eb80332990f9cda936a33e13fabef + # - https://github.com/python/cpython/issues/58115 + # - https://github.com/python/cpython/issues/111147 + + import textwrap + + # Single-line, always ordered: + fs0 = frozenset() + fs1 = frozenset(('abc', 'xyz')) + data = frozenset((fs0, fs1)) + self.assertEqual(pprint.pformat(data), + 'frozenset({%r, %r})' % (fs0, fs1)) + self.assertEqual(pprint.pformat(data), repr(data)) + + fs2 = frozenset(('one', 'two')) + data = {fs2: frozenset((fs0, fs1))} + self.assertEqual(pprint.pformat(data), + "{%r: frozenset({%r, %r})}" % (fs2, fs0, fs1)) + self.assertEqual(pprint.pformat(data), repr(data)) + + # Single-line, unordered: + fs1 = frozenset(("xyz", "qwerty")) + fs2 = frozenset(("abcd", "spam")) + fs = frozenset((fs1, fs2)) + self.assertEqual(pprint.pformat(fs), repr(fs)) + + # Multiline, unordered: + def check(res, invariants): + self.assertIn(res, [textwrap.dedent(i).strip() for i in invariants]) + + # Inner-most frozensets are singleline, result is multiline, unordered: + fs1 = frozenset(('regular string', 'other string')) + fs2 = frozenset(('third string', 'one more string')) + check( + pprint.pformat(frozenset((fs1, fs2))), + [ + """ + frozenset({%r, + %r}) + """ % (fs1, fs2), + """ + frozenset({%r, + %r}) + """ % (fs2, fs1), + ], + ) + + # Everything is multiline, unordered: + check( + pprint.pformat( + frozenset(( + frozenset(( + "xyz very-very long string", + "qwerty is also absurdly long", + )), + frozenset(( + "abcd is even longer that before", + "spam is not so long", + )), + )), + ), + [ + """ + frozenset({frozenset({'abcd is even longer that before', + 'spam is not so long'}), + frozenset({'qwerty is also absurdly long', + 'xyz very-very long string'})}) + """, + + """ + frozenset({frozenset({'abcd is even longer that before', + 'spam is not so long'}), + frozenset({'xyz very-very long string', + 'qwerty is also absurdly long'})}) + """, + + """ + frozenset({frozenset({'qwerty is also absurdly long', + 'xyz very-very long string'}), + frozenset({'abcd is even longer that before', + 'spam is not so long'})}) + """, + + """ + frozenset({frozenset({'qwerty is also absurdly long', + 'xyz very-very long string'}), + frozenset({'spam is not so long', + 'abcd is even longer that before'})}) + """, + ], + ) def test_depth(self): nested_tuple = (1, (2, (3, (4, (5, 6))))) diff --git a/Lib/test/test_pstats.py b/Lib/test/test_pstats.py index acc2fa5385d923..d5a5a9738c2498 100644 --- a/Lib/test/test_pstats.py +++ b/Lib/test/test_pstats.py @@ -5,7 +5,9 @@ from pstats import SortKey from enum import StrEnum, _test_simple_enum +import os import pstats +import tempfile import cProfile class AddCallersTestCase(unittest.TestCase): @@ -36,6 +38,33 @@ def test_add(self): stats = pstats.Stats(stream=stream) stats.add(self.stats, self.stats) + def test_dump_and_load_works_correctly(self): + temp_storage_new = tempfile.NamedTemporaryFile(delete=False) + try: + self.stats.dump_stats(filename=temp_storage_new.name) + tmp_stats = pstats.Stats(temp_storage_new.name) + self.assertEqual(self.stats.stats, tmp_stats.stats) + finally: + temp_storage_new.close() + os.remove(temp_storage_new.name) + + def test_load_equivalent_to_init(self): + stats = pstats.Stats() + self.temp_storage = tempfile.NamedTemporaryFile(delete=False) + try: + cProfile.run('import os', filename=self.temp_storage.name) + stats.load_stats(self.temp_storage.name) + created = pstats.Stats(self.temp_storage.name) + self.assertEqual(stats.stats, created.stats) + finally: + self.temp_storage.close() + os.remove(self.temp_storage.name) + + def test_loading_wrong_types(self): + stats = pstats.Stats() + with self.assertRaises(TypeError): + stats.load_stats(42) + def test_sort_stats_int(self): valid_args = {-1: 'stdname', 0: 'calls', diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 70c5ebd694ca88..eb50510e12b7b6 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -43,8 +43,8 @@ class nonascii: if test.support.HAVE_DOCSTRINGS: expected_data_docstrings = ( - 'dictionary for instance variables (if defined)', - 'list of weak references to the object (if defined)', + 'dictionary for instance variables', + 'list of weak references to the object', ) * 2 else: expected_data_docstrings = ('', '', '', '') @@ -108,10 +108,10 @@ class C(builtins.object) | Data descriptors defined here: | | __dict__ - | dictionary for instance variables (if defined) + | dictionary for instance variables | | __weakref__ - | list of weak references to the object (if defined) + | list of weak references to the object FUNCTIONS doc_func() @@ -169,16 +169,16 @@ class A(builtins.object) Data descriptors defined here: __dict__ - dictionary for instance variables (if defined) + dictionary for instance variables __weakref__ - list of weak references to the object (if defined) + list of weak references to the object class B(builtins.object) Data descriptors defined here: __dict__ - dictionary for instance variables (if defined) + dictionary for instance variables __weakref__ - list of weak references to the object (if defined) + list of weak references to the object Data and other attributes defined here: NO_MEANING = 'eggs' __annotations__ = {'NO_MEANING': } @@ -195,9 +195,9 @@ class C(builtins.object) __class_getitem__(item) from builtins.type Data descriptors defined here: __dict__ - dictionary for instance variables (if defined) + dictionary for instance variables __weakref__ - list of weak references to the object (if defined) + list of weak references to the object Functions doc_func() @@ -829,10 +829,10 @@ class B(A) | Data descriptors inherited from A: | | __dict__ - | dictionary for instance variables (if defined) + | dictionary for instance variables | | __weakref__ - | list of weak references to the object (if defined) + | list of weak references to the object ''' % __name__) doc = pydoc.render_doc(B, renderer=pydoc.HTMLDoc()) @@ -861,15 +861,104 @@ class B(A) Data descriptors inherited from A: __dict__ - dictionary for instance variables (if defined) + dictionary for instance variables __weakref__ - list of weak references to the object (if defined) + list of weak references to the object """ as_text = html2text(doc) expected_lines = [line.strip() for line in expected_text.split("\n") if line] for expected_line in expected_lines: self.assertIn(expected_line, as_text) + def test_long_signatures(self): + from collections.abc import Callable + from typing import Literal, Annotated + + class A: + def __init__(self, + arg1: Callable[[int, int, int], str], + arg2: Literal['some value', 'other value'], + arg3: Annotated[int, 'some docs about this type'], + ) -> None: + ... + + doc = pydoc.render_doc(A) + # clean up the extra text formatting that pydoc performs + doc = re.sub('\b.', '', doc) + self.assertEqual(doc, '''Python Library Documentation: class A in module %s + +class A(builtins.object) + | A( + | arg1: collections.abc.Callable[[int, int, int], str], + | arg2: Literal['some value', 'other value'], + | arg3: Annotated[int, 'some docs about this type'] + | ) -> None + | + | Methods defined here: + | + | __init__( + | self, + | arg1: collections.abc.Callable[[int, int, int], str], + | arg2: Literal['some value', 'other value'], + | arg3: Annotated[int, 'some docs about this type'] + | ) -> None + | + | ---------------------------------------------------------------------- + | Data descriptors defined here: + | + | __dict__ + | dictionary for instance variables + | + | __weakref__ + | list of weak references to the object +''' % __name__) + + def func( + arg1: Callable[[Annotated[int, 'Some doc']], str], + arg2: Literal[1, 2, 3, 4, 5, 6, 7, 8], + ) -> Annotated[int, 'Some other']: + ... + + doc = pydoc.render_doc(func) + # clean up the extra text formatting that pydoc performs + doc = re.sub('\b.', '', doc) + self.assertEqual(doc, '''Python Library Documentation: function func in module %s + +func( + arg1: collections.abc.Callable[[typing.Annotated[int, 'Some doc']], str], + arg2: Literal[1, 2, 3, 4, 5, 6, 7, 8] +) -> Annotated[int, 'Some other'] +''' % __name__) + + def function_with_really_long_name_so_annotations_can_be_rather_small( + arg1: int, + arg2: str, + ): + ... + + doc = pydoc.render_doc(function_with_really_long_name_so_annotations_can_be_rather_small) + # clean up the extra text formatting that pydoc performs + doc = re.sub('\b.', '', doc) + self.assertEqual(doc, '''Python Library Documentation: function function_with_really_long_name_so_annotations_can_be_rather_small in module %s + +function_with_really_long_name_so_annotations_can_be_rather_small( + arg1: int, + arg2: str +) +''' % __name__) + + does_not_have_name = lambda \ + very_long_parameter_name_that_should_not_fit_into_a_single_line, \ + second_very_long_parameter_name: ... + + doc = pydoc.render_doc(does_not_have_name) + # clean up the extra text formatting that pydoc performs + doc = re.sub('\b.', '', doc) + self.assertEqual(doc, '''Python Library Documentation: function in module %s + + lambda very_long_parameter_name_that_should_not_fit_into_a_single_line, second_very_long_parameter_name +''' % __name__) + def test__future__imports(self): # __future__ features are excluded from module help, # except when it's the __future__ module itself diff --git a/Lib/test/test_raise.py b/Lib/test/test_raise.py index 5936d7535edd5f..6d26a61bee4292 100644 --- a/Lib/test/test_raise.py +++ b/Lib/test/test_raise.py @@ -185,6 +185,20 @@ def test_class_cause(self): else: self.fail("No exception raised") + def test_class_cause_nonexception_result(self): + class ConstructsNone(BaseException): + @classmethod + def __new__(*args, **kwargs): + return None + try: + raise IndexError from ConstructsNone + except TypeError as e: + self.assertIn("should have returned an instance of BaseException", str(e)) + except IndexError: + self.fail("Wrong kind of exception raised") + else: + self.fail("No exception raised") + def test_instance_cause(self): cause = KeyError() try: diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 50bea7be6d54c7..b1e4ef4197d130 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -1081,6 +1081,7 @@ def test_binomialvariate(self): B(n=1, p=-0.5) # Negative p with self.assertRaises(ValueError): B(n=1, p=1.5) # p > 1.0 + self.assertEqual(B(0, 0.5), 0) # n == 0 self.assertEqual(B(10, 0.0), 0) # p == 0.0 self.assertEqual(B(10, 1.0), 10) # p == 1.0 self.assertTrue(B(1, 0.3) in {0, 1}) # n == 1 fast path @@ -1088,6 +1089,9 @@ def test_binomialvariate(self): self.assertTrue(B(1, 0.0) in {0}) # n == 1 fast path self.assertTrue(B(1, 1.0) in {1}) # n == 1 fast path + # BG method very small p + self.assertEqual(B(5, 1e-18), 0) + # BG method p <= 0.5 and n*p=1.25 self.assertTrue(B(5, 0.25) in set(range(6))) diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py index 835280f2281cde..5e0e6f8dfac651 100644 --- a/Lib/test/test_readline.py +++ b/Lib/test/test_readline.py @@ -5,6 +5,7 @@ import os import sys import tempfile +import textwrap import unittest from test.support import verbose from test.support.import_helper import import_module @@ -18,7 +19,7 @@ if hasattr(readline, "_READLINE_LIBRARY_VERSION"): is_editline = ("EditLine wrapper" in readline._READLINE_LIBRARY_VERSION) else: - is_editline = (readline.__doc__ and "libedit" in readline.__doc__) + is_editline = readline.backend == "editline" def setUpModule(): @@ -144,6 +145,9 @@ def test_init(self): TERM='xterm-256color') self.assertEqual(stdout, b'') + def test_backend(self): + self.assertIn(readline.backend, ("readline", "editline")) + auto_history_script = """\ import readline readline.set_auto_history({}) @@ -163,6 +167,25 @@ def test_auto_history_disabled(self): # end, so don't expect it in the output. self.assertIn(b"History length: 0", output) + def test_set_complete_delims(self): + script = textwrap.dedent(""" + import readline + def complete(text, state): + if state == 0 and text == "$": + return "$complete" + return None + if readline.backend == "editline": + readline.parse_and_bind(r'bind "\\t" rl_complete') + else: + readline.parse_and_bind(r'"\\t": complete') + readline.set_completer_delims(" \\t\\n") + readline.set_completer(complete) + print(input()) + """) + + output = run_pty(script, input=b"$\t\n") + self.assertIn(b"$complete", output) + def test_nonascii(self): loc = locale.setlocale(locale.LC_CTYPE, None) if loc in ('C', 'POSIX'): @@ -178,7 +201,7 @@ def test_nonascii(self): script = r"""import readline -is_editline = readline.__doc__ and "libedit" in readline.__doc__ +is_editline = readline.backend == "editline" inserted = "[\xEFnserted]" macro = "|t\xEB[after]" set_pre_input_hook = getattr(readline, "set_pre_input_hook", None) diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index d7b9f801092498..e828941f6c779d 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -2031,6 +2031,25 @@ def test_dev_mode(self): self.check_executed_tests(output, tests, stats=len(tests), parallel=True) + def test_unload_tests(self): + # Test that unloading test modules does not break tests + # that import from other tests. + # The test execution order matters for this test. + # Both test_regrtest_a and test_regrtest_c which are executed before + # and after test_regrtest_b import a submodule from the test_regrtest_b + # package and use it in testing. test_regrtest_b itself does not import + # that submodule. + # Previously test_regrtest_c failed because test_regrtest_b.util in + # sys.modules was left after test_regrtest_a (making the import + # statement no-op), but new test_regrtest_b without the util attribute + # was imported for test_regrtest_b. + testdir = os.path.join(os.path.dirname(__file__), + 'regrtestdata', 'import_from_tests') + tests = [f'test_regrtest_{name}' for name in ('a', 'b', 'c')] + args = ['-Wd', '-E', '-bb', '-m', 'test', '--testdir=%s' % testdir, *tests] + output = self.run_python(args) + self.check_executed_tests(output, tests, stats=3) + def check_add_python_opts(self, option): # --fast-ci and --slow-ci add "-u -W default -bb -E" options to Python code = textwrap.dedent(r""" diff --git a/Lib/test/test_repl.py b/Lib/test/test_repl.py index 7533376e015e73..a28d1595f44533 100644 --- a/Lib/test/test_repl.py +++ b/Lib/test/test_repl.py @@ -161,10 +161,11 @@ def foo(x): output = kill_python(p) self.assertEqual(p.returncode, 0) - traceback_lines = output.splitlines()[-7:-1] + traceback_lines = output.splitlines()[-8:-1] expected_lines = [ ' File "", line 1, in ', ' foo(0)', + ' ~~~^^^', ' File "", line 2, in foo', ' 1 / x', ' ~~^~~', diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index d231e66b7b889f..d7061b2f9d8724 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -208,8 +208,7 @@ def test_rmtree_fails_on_symlink_onerror(self): errors = [] def onerror(*args): errors.append(args) - with self.assertWarns(DeprecationWarning): - shutil.rmtree(link, onerror=onerror) + shutil.rmtree(link, onerror=onerror) self.assertEqual(len(errors), 1) self.assertIs(errors[0][0], os.path.islink) self.assertEqual(errors[0][1], link) @@ -270,8 +269,7 @@ def test_rmtree_fails_on_junctions_onerror(self): errors = [] def onerror(*args): errors.append(args) - with self.assertWarns(DeprecationWarning): - shutil.rmtree(link, onerror=onerror) + shutil.rmtree(link, onerror=onerror) self.assertEqual(len(errors), 1) self.assertIs(errors[0][0], os.path.islink) self.assertEqual(errors[0][1], link) @@ -319,7 +317,7 @@ def test_rmtree_works_on_junctions(self): self.assertTrue(os.path.exists(dir3)) self.assertTrue(os.path.exists(file1)) - def test_rmtree_errors_onerror(self): + def test_rmtree_errors(self): # filename is guaranteed not to exist filename = tempfile.mktemp(dir=self.mkdtemp()) self.assertRaises(FileNotFoundError, shutil.rmtree, filename) @@ -328,8 +326,8 @@ def test_rmtree_errors_onerror(self): # existing file tmpdir = self.mkdtemp() - write_file((tmpdir, "tstfile"), "") filename = os.path.join(tmpdir, "tstfile") + write_file(filename, "") with self.assertRaises(NotADirectoryError) as cm: shutil.rmtree(filename) self.assertEqual(cm.exception.filename, filename) @@ -337,11 +335,23 @@ def test_rmtree_errors_onerror(self): # test that ignore_errors option is honored shutil.rmtree(filename, ignore_errors=True) self.assertTrue(os.path.exists(filename)) + + self.assertRaises(TypeError, shutil.rmtree, None) + self.assertRaises(TypeError, shutil.rmtree, None, ignore_errors=True) + exc = TypeError if shutil.rmtree.avoids_symlink_attacks else NotImplementedError + with self.assertRaises(exc): + shutil.rmtree(filename, dir_fd='invalid') + with self.assertRaises(exc): + shutil.rmtree(filename, dir_fd='invalid', ignore_errors=True) + + def test_rmtree_errors_onerror(self): + tmpdir = self.mkdtemp() + filename = os.path.join(tmpdir, "tstfile") + write_file(filename, "") errors = [] def onerror(*args): errors.append(args) - with self.assertWarns(DeprecationWarning): - shutil.rmtree(filename, onerror=onerror) + shutil.rmtree(filename, onerror=onerror) self.assertEqual(len(errors), 2) self.assertIs(errors[0][0], os.scandir) self.assertEqual(errors[0][1], filename) @@ -353,23 +363,9 @@ def onerror(*args): self.assertEqual(errors[1][2][1].filename, filename) def test_rmtree_errors_onexc(self): - # filename is guaranteed not to exist - filename = tempfile.mktemp(dir=self.mkdtemp()) - self.assertRaises(FileNotFoundError, shutil.rmtree, filename) - # test that ignore_errors option is honored - shutil.rmtree(filename, ignore_errors=True) - - # existing file tmpdir = self.mkdtemp() - write_file((tmpdir, "tstfile"), "") filename = os.path.join(tmpdir, "tstfile") - with self.assertRaises(NotADirectoryError) as cm: - shutil.rmtree(filename) - self.assertEqual(cm.exception.filename, filename) - self.assertTrue(os.path.exists(filename)) - # test that ignore_errors option is honored - shutil.rmtree(filename, ignore_errors=True) - self.assertTrue(os.path.exists(filename)) + write_file(filename, "") errors = [] def onexc(*args): errors.append(args) @@ -410,8 +406,7 @@ def test_on_error(self): self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode) self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode) - with self.assertWarns(DeprecationWarning): - shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror) + shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror) # Test whether onerror has actually been called. self.assertEqual(self.errorState, 3, "Expected call to onerror function did not happen.") @@ -537,8 +532,7 @@ def onexc(*args): self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode) self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode) - with self.assertWarns(DeprecationWarning): - shutil.rmtree(TESTFN, onerror=onerror, onexc=onexc) + shutil.rmtree(TESTFN, onerror=onerror, onexc=onexc) self.assertTrue(onexc_called) self.assertFalse(onerror_called) @@ -582,6 +576,41 @@ def _raiser(*args, **kwargs): self.assertFalse(shutil._use_fd_functions) self.assertFalse(shutil.rmtree.avoids_symlink_attacks) + @unittest.skipUnless(shutil._use_fd_functions, "requires safe rmtree") + def test_rmtree_fails_on_close(self): + # Test that the error handler is called for failed os.close() and that + # os.close() is only called once for a file descriptor. + tmp = self.mkdtemp() + dir1 = os.path.join(tmp, 'dir1') + os.mkdir(dir1) + dir2 = os.path.join(dir1, 'dir2') + os.mkdir(dir2) + def close(fd): + orig_close(fd) + nonlocal close_count + close_count += 1 + raise OSError + + close_count = 0 + with support.swap_attr(os, 'close', close) as orig_close: + with self.assertRaises(OSError): + shutil.rmtree(dir1) + self.assertTrue(os.path.isdir(dir2)) + self.assertEqual(close_count, 2) + + close_count = 0 + errors = [] + def onexc(*args): + errors.append(args) + with support.swap_attr(os, 'close', close) as orig_close: + shutil.rmtree(dir1, onexc=onexc) + self.assertEqual(len(errors), 2) + self.assertIs(errors[0][0], close) + self.assertEqual(errors[0][1], dir2) + self.assertIs(errors[1][0], close) + self.assertEqual(errors[1][1], dir1) + self.assertEqual(close_count, 2) + @unittest.skipUnless(shutil._use_fd_functions, "dir_fd is not supported") def test_rmtree_with_dir_fd(self): tmp_dir = self.mkdtemp() @@ -638,6 +667,63 @@ def test_rmtree_on_junction(self): finally: shutil.rmtree(TESTFN, ignore_errors=True) + @unittest.skipIf(sys.platform[:6] == 'cygwin', + "This test can't be run on Cygwin (issue #1071513).") + @os_helper.skip_if_dac_override + @os_helper.skip_unless_working_chmod + def test_rmtree_deleted_race_condition(self): + # bpo-37260 + # + # Test that a file or a directory deleted after it is enumerated + # by scandir() but before unlink() or rmdr() is called doesn't + # generate any errors. + def _onexc(fn, path, exc): + assert fn in (os.rmdir, os.unlink) + if not isinstance(exc, PermissionError): + raise + # Make the parent and the children writeable. + for p, mode in zip(paths, old_modes): + os.chmod(p, mode) + # Remove other dirs except one. + keep = next(p for p in dirs if p != path) + for p in dirs: + if p != keep: + os.rmdir(p) + # Remove other files except one. + keep = next(p for p in files if p != path) + for p in files: + if p != keep: + os.unlink(p) + + os.mkdir(TESTFN) + paths = [TESTFN] + [os.path.join(TESTFN, f'child{i}') + for i in range(6)] + dirs = paths[1::2] + files = paths[2::2] + for path in dirs: + os.mkdir(path) + for path in files: + write_file(path, '') + + old_modes = [os.stat(path).st_mode for path in paths] + + # Make the parent and the children non-writeable. + new_mode = stat.S_IREAD|stat.S_IEXEC + for path in reversed(paths): + os.chmod(path, new_mode) + + try: + shutil.rmtree(TESTFN, onexc=_onexc) + except: + # Test failed, so cleanup artifacts. + for path, mode in zip(paths, old_modes): + try: + os.chmod(path, mode) + except OSError: + pass + shutil.rmtree(TESTFN) + raise + class TestCopyTree(BaseTest, unittest.TestCase): diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index f2ae28c38dd72d..acb7e9d4c6074d 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -1318,6 +1318,7 @@ def handler(signum, frame): # Python handler self.assertEqual(len(sigs), N, "Some signals were lost") + @unittest.skipIf(sys.platform == "darwin", "crashes due to system bug (FB13453490)") @unittest.skipUnless(hasattr(signal, "SIGUSR1"), "test needs SIGUSR1") @threading_helper.requires_working_threading() diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 86701caf05399e..4eb5af99d6674c 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1082,7 +1082,20 @@ def testInterfaceNameIndex(self): 'socket.if_indextoname() not available.') def testInvalidInterfaceIndexToName(self): self.assertRaises(OSError, socket.if_indextoname, 0) + self.assertRaises(OverflowError, socket.if_indextoname, -1) + self.assertRaises(OverflowError, socket.if_indextoname, 2**1000) self.assertRaises(TypeError, socket.if_indextoname, '_DEADBEEF') + if hasattr(socket, 'if_nameindex'): + indices = dict(socket.if_nameindex()) + for index in indices: + index2 = index + 2**32 + if index2 not in indices: + with self.assertRaises((OverflowError, OSError)): + socket.if_indextoname(index2) + for index in 2**32-1, 2**64-1: + if index not in indices: + with self.assertRaises((OverflowError, OSError)): + socket.if_indextoname(index) @unittest.skipUnless(hasattr(socket, 'if_nametoindex'), 'socket.if_nametoindex() not available.') diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index d8ae7b75e18150..3fdfa2960503b8 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -10,6 +10,7 @@ from test.support import threading_helper from test.support import warnings_helper from test.support import asyncore +import array import re import socket import select @@ -3517,6 +3518,27 @@ def test_recv_zero(self): self.assertEqual(s.recv(0), b"") self.assertEqual(s.recv_into(bytearray()), 0) + def test_recv_into_buffer_protocol_len(self): + server = ThreadedEchoServer(CERTFILE) + self.enterContext(server) + s = socket.create_connection((HOST, server.port)) + self.addCleanup(s.close) + s = test_wrap_socket(s, suppress_ragged_eofs=False) + self.addCleanup(s.close) + + s.send(b"data") + buf = array.array('I', [0, 0]) + self.assertEqual(s.recv_into(buf), 4) + self.assertEqual(bytes(buf)[:4], b"data") + + class B(bytearray): + def __len__(self): + 1/0 + s.send(b"data") + buf = B(6) + self.assertEqual(s.recv_into(buf), 4) + self.assertEqual(bytes(buf), b"data\0\0") + def test_nonblocking_send(self): server = ThreadedEchoServer(CERTFILE, certreqs=ssl.CERT_NONE, @@ -4236,6 +4258,107 @@ def test_session_handling(self): self.assertEqual(str(e.exception), 'Session refers to a different SSLContext.') + @requires_tls_version('TLSv1_2') + @unittest.skipUnless(ssl.HAS_PSK, 'TLS-PSK disabled on this OpenSSL build') + def test_psk(self): + psk = bytes.fromhex('deadbeef') + + client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + client_context.check_hostname = False + client_context.verify_mode = ssl.CERT_NONE + client_context.maximum_version = ssl.TLSVersion.TLSv1_2 + client_context.set_ciphers('PSK') + client_context.set_psk_client_callback(lambda hint: (None, psk)) + + server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_context.maximum_version = ssl.TLSVersion.TLSv1_2 + server_context.set_ciphers('PSK') + server_context.set_psk_server_callback(lambda identity: psk) + + # correct PSK should connect + server = ThreadedEchoServer(context=server_context) + with server: + with client_context.wrap_socket(socket.socket()) as s: + s.connect((HOST, server.port)) + + # incorrect PSK should fail + incorrect_psk = bytes.fromhex('cafebabe') + client_context.set_psk_client_callback(lambda hint: (None, incorrect_psk)) + server = ThreadedEchoServer(context=server_context) + with server: + with client_context.wrap_socket(socket.socket()) as s: + with self.assertRaises(ssl.SSLError): + s.connect((HOST, server.port)) + + # identity_hint and client_identity should be sent to the other side + identity_hint = 'identity-hint' + client_identity = 'client-identity' + + def client_callback(hint): + self.assertEqual(hint, identity_hint) + return client_identity, psk + + def server_callback(identity): + self.assertEqual(identity, client_identity) + return psk + + client_context.set_psk_client_callback(client_callback) + server_context.set_psk_server_callback(server_callback, identity_hint) + server = ThreadedEchoServer(context=server_context) + with server: + with client_context.wrap_socket(socket.socket()) as s: + s.connect((HOST, server.port)) + + # adding client callback to server or vice versa raises an exception + with self.assertRaisesRegex(ssl.SSLError, 'Cannot add PSK server callback'): + client_context.set_psk_server_callback(server_callback, identity_hint) + with self.assertRaisesRegex(ssl.SSLError, 'Cannot add PSK client callback'): + server_context.set_psk_client_callback(client_callback) + + # test with UTF-8 identities + identity_hint = '身份暗示' # Translation: "Identity hint" + client_identity = '客户身份' # Translation: "Customer identity" + + client_context.set_psk_client_callback(client_callback) + server_context.set_psk_server_callback(server_callback, identity_hint) + server = ThreadedEchoServer(context=server_context) + with server: + with client_context.wrap_socket(socket.socket()) as s: + s.connect((HOST, server.port)) + + @requires_tls_version('TLSv1_3') + @unittest.skipUnless(ssl.HAS_PSK, 'TLS-PSK disabled on this OpenSSL build') + def test_psk_tls1_3(self): + psk = bytes.fromhex('deadbeef') + identity_hint = 'identity-hint' + client_identity = 'client-identity' + + def client_callback(hint): + # identity_hint is not sent to the client in TLS 1.3 + self.assertIsNone(hint) + return client_identity, psk + + def server_callback(identity): + self.assertEqual(identity, client_identity) + return psk + + client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + client_context.check_hostname = False + client_context.verify_mode = ssl.CERT_NONE + client_context.minimum_version = ssl.TLSVersion.TLSv1_3 + client_context.set_ciphers('PSK') + client_context.set_psk_client_callback(client_callback) + + server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_context.minimum_version = ssl.TLSVersion.TLSv1_3 + server_context.set_ciphers('PSK') + server_context.set_psk_server_callback(server_callback, identity_hint) + + server = ThreadedEchoServer(context=server_context) + with server: + with client_context.wrap_socket(socket.socket()) as s: + s.connect((HOST, server.port)) + @unittest.skipUnless(has_tls_version('TLSv1_3'), "Test needs TLS 1.3") class TestPostHandshakeAuth(unittest.TestCase): diff --git a/Lib/test/test_str.py b/Lib/test/test_str.py index 814ef111c5bec8..b4927113db44e3 100644 --- a/Lib/test/test_str.py +++ b/Lib/test/test_str.py @@ -55,6 +55,21 @@ def duplicate_string(text): class StrSubclass(str): pass +class OtherStrSubclass(str): + pass + +class WithStr: + def __init__(self, value): + self.value = value + def __str__(self): + return self.value + +class WithRepr: + def __init__(self, value): + self.value = value + def __repr__(self): + return self.value + class StrTest(string_tests.StringLikeTest, string_tests.MixinStrUnicodeTest, unittest.TestCase): @@ -83,6 +98,10 @@ def __repr__(self): self.assertEqual(realresult, result) self.assertTrue(object is not realresult) + def assertTypedEqual(self, actual, expected): + self.assertIs(type(actual), type(expected)) + self.assertEqual(actual, expected) + def test_literals(self): self.assertEqual('\xff', '\u00ff') self.assertEqual('\uffff', '\U0000ffff') @@ -127,10 +146,13 @@ def test_ascii(self): self.assertEqual(ascii("\U00010000" * 39 + "\uffff" * 4096), ascii("\U00010000" * 39 + "\uffff" * 4096)) - class WrongRepr: - def __repr__(self): - return b'byte-repr' - self.assertRaises(TypeError, ascii, WrongRepr()) + self.assertTypedEqual(ascii('\U0001f40d'), r"'\U0001f40d'") + self.assertTypedEqual(ascii(StrSubclass('abc')), "'abc'") + self.assertTypedEqual(ascii(WithRepr('')), '') + self.assertTypedEqual(ascii(WithRepr(StrSubclass(''))), StrSubclass('')) + self.assertTypedEqual(ascii(WithRepr('<\U0001f40d>')), r'<\U0001f40d>') + self.assertTypedEqual(ascii(WithRepr(StrSubclass('<\U0001f40d>'))), r'<\U0001f40d>') + self.assertRaises(TypeError, ascii, WithRepr(b'byte-repr')) def test_repr(self): # Test basic sanity of repr() @@ -168,10 +190,13 @@ def test_repr(self): self.assertEqual(repr("\U00010000" * 39 + "\uffff" * 4096), repr("\U00010000" * 39 + "\uffff" * 4096)) - class WrongRepr: - def __repr__(self): - return b'byte-repr' - self.assertRaises(TypeError, repr, WrongRepr()) + self.assertTypedEqual(repr('\U0001f40d'), "'\U0001f40d'") + self.assertTypedEqual(repr(StrSubclass('abc')), "'abc'") + self.assertTypedEqual(repr(WithRepr('')), '') + self.assertTypedEqual(repr(WithRepr(StrSubclass(''))), StrSubclass('')) + self.assertTypedEqual(repr(WithRepr('<\U0001f40d>')), '<\U0001f40d>') + self.assertTypedEqual(repr(WithRepr(StrSubclass('<\U0001f40d>'))), StrSubclass('<\U0001f40d>')) + self.assertRaises(TypeError, repr, WithRepr(b'byte-repr')) def test_iterators(self): # Make sure unicode objects have an __iter__ method @@ -2367,28 +2392,37 @@ def test_ucs4(self): def test_conversion(self): # Make sure __str__() works properly - class ObjectToStr: - def __str__(self): - return "foo" - - class StrSubclassToStr(str): - def __str__(self): - return "foo" - - class StrSubclassToStrSubclass(str): - def __new__(cls, content=""): - return str.__new__(cls, 2*content) - def __str__(self): + class StrWithStr(str): + def __new__(cls, value): + self = str.__new__(cls, "") + self.value = value return self + def __str__(self): + return self.value - self.assertEqual(str(ObjectToStr()), "foo") - self.assertEqual(str(StrSubclassToStr("bar")), "foo") - s = str(StrSubclassToStrSubclass("foo")) - self.assertEqual(s, "foofoo") - self.assertIs(type(s), StrSubclassToStrSubclass) - s = StrSubclass(StrSubclassToStrSubclass("foo")) - self.assertEqual(s, "foofoo") - self.assertIs(type(s), StrSubclass) + self.assertTypedEqual(str(WithStr('abc')), 'abc') + self.assertTypedEqual(str(WithStr(StrSubclass('abc'))), StrSubclass('abc')) + self.assertTypedEqual(StrSubclass(WithStr('abc')), StrSubclass('abc')) + self.assertTypedEqual(StrSubclass(WithStr(StrSubclass('abc'))), + StrSubclass('abc')) + self.assertTypedEqual(StrSubclass(WithStr(OtherStrSubclass('abc'))), + StrSubclass('abc')) + + self.assertTypedEqual(str(StrWithStr('abc')), 'abc') + self.assertTypedEqual(str(StrWithStr(StrSubclass('abc'))), StrSubclass('abc')) + self.assertTypedEqual(StrSubclass(StrWithStr('abc')), StrSubclass('abc')) + self.assertTypedEqual(StrSubclass(StrWithStr(StrSubclass('abc'))), + StrSubclass('abc')) + self.assertTypedEqual(StrSubclass(StrWithStr(OtherStrSubclass('abc'))), + StrSubclass('abc')) + + self.assertTypedEqual(str(WithRepr('')), '') + self.assertTypedEqual(str(WithRepr(StrSubclass(''))), StrSubclass('')) + self.assertTypedEqual(StrSubclass(WithRepr('')), StrSubclass('')) + self.assertTypedEqual(StrSubclass(WithRepr(StrSubclass(''))), + StrSubclass('')) + self.assertTypedEqual(StrSubclass(WithRepr(OtherStrSubclass(''))), + StrSubclass('')) def test_unicode_repr(self): class s1: diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index c76649cdcd9cce..15f6ee06ffe19b 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -700,20 +700,6 @@ def test__struct_types_immutable(self): with self.assertRaises(TypeError): cls.x = 1 - @support.cpython_only - def test__struct_Struct__new__initialized(self): - # See https://github.com/python/cpython/issues/78724 - - s = struct.Struct.__new__(struct.Struct, "b") - s.unpack_from(b"abcd") - - @support.cpython_only - def test__struct_Struct_subclassing(self): - class Bob(struct.Struct): - pass - - s = Bob("b") - s.unpack_from(b"abcd") def test_issue35714(self): # Embedded null characters should not be allowed in format strings. @@ -774,6 +760,15 @@ def test_error_propagation(fmt_str): test_error_propagation('N') test_error_propagation('n') + def test_struct_subclass_instantiation(self): + # Regression test for https://github.com/python/cpython/issues/112358 + class MyStruct(struct.Struct): + def __init__(self): + super().__init__('>h') + + my_struct = MyStruct() + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + def test_repr(self): s = struct.Struct('=i2H') self.assertEqual(repr(s), f'Struct({s.format!r})') diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index fe1a3675fced65..319bc0d2638563 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -2066,8 +2066,14 @@ def test_group_error(self): def test_extra_groups(self): gid = os.getegid() group_list = [65534 if gid != 65534 else 65533] + self._test_extra_groups_impl(gid=gid, group_list=group_list) + + @unittest.skipUnless(hasattr(os, 'setgroups'), 'no setgroups() on platform') + def test_extra_groups_empty_list(self): + self._test_extra_groups_impl(gid=os.getegid(), group_list=[]) + + def _test_extra_groups_impl(self, *, gid, group_list): name_group = _get_test_grp_name() - perm_error = False if grp is not None: group_list.append(name_group) @@ -2077,11 +2083,8 @@ def test_extra_groups(self): [sys.executable, "-c", "import os, sys, json; json.dump(os.getgroups(), sys.stdout)"], extra_groups=group_list) - except OSError as ex: - if ex.errno != errno.EPERM: - raise - perm_error = True - + except PermissionError: + self.skipTest("setgroup() EPERM; this test may require root.") else: parent_groups = os.getgroups() child_groups = json.loads(output) @@ -2092,12 +2095,15 @@ def test_extra_groups(self): else: desired_gids = group_list - if perm_error: - self.assertEqual(set(child_groups), set(parent_groups)) - else: - self.assertEqual(set(desired_gids), set(child_groups)) + self.assertEqual(set(desired_gids), set(child_groups)) - # make sure we bomb on negative values + if grp is None: + with self.assertRaises(ValueError): + subprocess.check_call(ZERO_RETURN_CMD, + extra_groups=[name_group]) + + # No skip necessary, this test won't make it to a setgroup() call. + def test_extra_groups_invalid_gid_t_values(self): with self.assertRaises(ValueError): subprocess.check_call(ZERO_RETURN_CMD, extra_groups=[-1]) @@ -2106,16 +2112,6 @@ def test_extra_groups(self): cwd=os.curdir, env=os.environ, extra_groups=[2**64]) - if grp is None: - with self.assertRaises(ValueError): - subprocess.check_call(ZERO_RETURN_CMD, - extra_groups=[name_group]) - - @unittest.skipIf(hasattr(os, 'setgroups'), 'setgroups() available on platform') - def test_extra_groups_error(self): - with self.assertRaises(ValueError): - subprocess.check_call(ZERO_RETURN_CMD, extra_groups=[]) - @unittest.skipIf(mswindows or not hasattr(os, 'umask'), 'POSIX umask() is not available.') def test_umask(self): diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 7ebf9ca1707acd..99433df73387d0 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -1752,6 +1752,28 @@ Traceback (most recent call last): SyntaxError: positional patterns follow keyword patterns +Non-matching 'elif'/'else' statements: + + >>> if a == b: + ... ... + ... elif a == c: + Traceback (most recent call last): + SyntaxError: 'elif' must match an if-statement here + + >>> if x == y: + ... ... + ... else: + Traceback (most recent call last): + SyntaxError: 'else' must match a valid statement here + + >>> elif m == n: + Traceback (most recent call last): + SyntaxError: 'elif' must match an if-statement here + + >>> else: + Traceback (most recent call last): + SyntaxError: 'else' must match a valid statement here + Uses of the star operator which should fail: A[:*b] @@ -2006,8 +2028,8 @@ def _check_error(self, code, errtext, lineno=None, offset=None, end_lineno=None, end_offset=None): """Check that compiling code raises SyntaxError with errtext. - errtest is a regular expression that must be present in the - test of the exception raised. If subclass is specified it + errtext is a regular expression that must be present in the + test of the exception raised. If subclass is specified, it is the expected subclass of SyntaxError (e.g. IndentationError). """ try: @@ -2031,6 +2053,22 @@ def _check_error(self, code, errtext, else: self.fail("compile() did not raise SyntaxError") + def _check_noerror(self, code, + errtext="compile() raised unexpected SyntaxError", + filename="", mode="exec", subclass=None): + """Check that compiling code does not raise a SyntaxError. + + errtext is the message passed to self.fail if there is + a SyntaxError. If the subclass parameter is specified, + it is the subclass of SyntaxError (e.g. IndentationError) + that the raised error is checked against. + """ + try: + compile(code, filename, mode) + except SyntaxError as err: + if (not subclass) or isinstance(err, subclass): + self.fail(errtext) + def test_expression_with_assignment(self): self._check_error( "print(end1 + end2 = ' ')", @@ -2296,6 +2334,10 @@ def test_error_parenthesis(self): """ self._check_error(code, "parenthesis '\\)' does not match opening parenthesis '\\['") + # Examples with dencodings + s = b'# coding=latin\n(aaaaaaaaaaaaaaaaa\naaaaaaaaaaa\xb5' + self._check_error(s, "'\(' was never closed") + def test_error_string_literal(self): self._check_error("'blech", r"unterminated string literal \(.*\)$") @@ -2311,6 +2353,7 @@ def test_error_string_literal(self): def test_invisible_characters(self): self._check_error('print\x17("Hello")', "invalid non-printable character") + self._check_error(b"with(0,,):\n\x01", "invalid non-printable character") def test_match_call_does_not_raise_syntax_error(self): code = """ @@ -2372,6 +2415,25 @@ def test_syntax_error_on_deeply_nested_blocks(self): """ self._check_error(source, "too many statically nested blocks") + def test_syntax_error_non_matching_elif_else_statements(self): + # Check bpo-45759: 'elif' statements that doesn't match an + # if-statement or 'else' statements that doesn't match any + # valid else-able statement (e.g. 'while') + self._check_error( + "elif m == n:\n ...", + "'elif' must match an if-statement here") + self._check_error( + "else:\n ...", + "'else' must match a valid statement here") + self._check_noerror("if a == b:\n ...\nelif a == c:\n ...") + self._check_noerror("if x == y:\n ...\nelse:\n ...") + self._check_error( + "else = 123", + "invalid syntax") + self._check_error( + "elif 55 = 123", + "cannot assign to literal here") + @support.cpython_only def test_error_on_parser_stack_overflow(self): source = "-" * 100000 + "4" diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index b111962abdcdc3..db5ba16c4d9739 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -691,11 +691,23 @@ def test_43581(self): self.assertEqual(sys.__stdout__.encoding, sys.__stderr__.encoding) def test_intern(self): + has_is_interned = (test.support.check_impl_detail(cpython=True) + or hasattr(sys, '_is_interned')) self.assertRaises(TypeError, sys.intern) + self.assertRaises(TypeError, sys.intern, b'abc') + if has_is_interned: + self.assertRaises(TypeError, sys._is_interned) + self.assertRaises(TypeError, sys._is_interned, b'abc') s = "never interned before" + str(random.randrange(0, 10**9)) self.assertTrue(sys.intern(s) is s) + if has_is_interned: + self.assertIs(sys._is_interned(s), True) s2 = s.swapcase().swapcase() + if has_is_interned: + self.assertIs(sys._is_interned(s2), False) self.assertTrue(sys.intern(s2) is s) + if has_is_interned: + self.assertIs(sys._is_interned(s2), False) # Subclasses of string can't be interned, because they # provide too much opportunity for insane things to happen. @@ -707,6 +719,8 @@ def __hash__(self): return 123 self.assertRaises(TypeError, sys.intern, S("abc")) + if has_is_interned: + self.assertIs(sys._is_interned(S("abc")), False) @requires_subinterpreters def test_subinterp_intern_dynamically_allocated(self): @@ -1115,8 +1129,10 @@ def check(tracebacklimit, expected): b'Traceback (most recent call last):', b' File "", line 8, in ', b' f2()', + b' ~~^^', b' File "", line 6, in f2', b' f1()', + b' ~~^^', b' File "", line 4, in f1', b' 1 / 0', b' ~~^~~', @@ -1124,8 +1140,8 @@ def check(tracebacklimit, expected): ] check(10, traceback) check(3, traceback) - check(2, traceback[:1] + traceback[3:]) - check(1, traceback[:1] + traceback[5:]) + check(2, traceback[:1] + traceback[4:]) + check(1, traceback[:1] + traceback[7:]) check(0, [traceback[-1]]) check(-1, [traceback[-1]]) check(1<<1000, traceback) @@ -1208,9 +1224,7 @@ def test_pystats(self): @test.support.cpython_only @unittest.skipUnless(hasattr(sys, 'abiflags'), 'need sys.abiflags') def test_disable_gil_abi(self): - abi_threaded = 't' in sys.abiflags - py_nogil = (sysconfig.get_config_var('Py_NOGIL') == 1) - self.assertEqual(py_nogil, abi_threaded) + self.assertEqual('t' in sys.abiflags, support.Py_GIL_DISABLED) @test.support.cpython_only diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index 1673507e2f7c91..b64b6a4f2baeb5 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -1641,6 +1641,28 @@ def test_explicit_cleanup_ignore_errors(self): temp_path.exists(), f"TemporaryDirectory {temp_path!s} exists after cleanup") + @unittest.skipUnless(os.name == "nt", "Only on Windows.") + def test_explicit_cleanup_correct_error(self): + with tempfile.TemporaryDirectory() as working_dir: + temp_dir = self.do_create(dir=working_dir) + with open(os.path.join(temp_dir.name, "example.txt"), 'wb'): + # Previously raised NotADirectoryError on some OSes + # (e.g. Windows). See bpo-43153. + with self.assertRaises(PermissionError): + temp_dir.cleanup() + + @unittest.skipUnless(os.name == "nt", "Only on Windows.") + def test_cleanup_with_used_directory(self): + with tempfile.TemporaryDirectory() as working_dir: + temp_dir = self.do_create(dir=working_dir) + subdir = os.path.join(temp_dir.name, "subdir") + os.mkdir(subdir) + with os_helper.change_cwd(subdir): + # Previously raised RecursionError on some OSes + # (e.g. Windows). See bpo-35144. + with self.assertRaises(PermissionError): + temp_dir.cleanup() + @os_helper.skip_unless_symlink def test_cleanup_with_symlink_to_a_directory(self): # cleanup() should not follow symlinks to directories (issue #12464) @@ -1662,6 +1684,103 @@ def test_cleanup_with_symlink_to_a_directory(self): "were deleted") d2.cleanup() + @os_helper.skip_unless_symlink + def test_cleanup_with_symlink_modes(self): + # cleanup() should not follow symlinks when fixing mode bits (#91133) + with self.do_create(recurse=0) as d2: + file1 = os.path.join(d2, 'file1') + open(file1, 'wb').close() + dir1 = os.path.join(d2, 'dir1') + os.mkdir(dir1) + for mode in range(8): + mode <<= 6 + with self.subTest(mode=format(mode, '03o')): + def test(target, target_is_directory): + d1 = self.do_create(recurse=0) + symlink = os.path.join(d1.name, 'symlink') + os.symlink(target, symlink, + target_is_directory=target_is_directory) + try: + os.chmod(symlink, mode, follow_symlinks=False) + except NotImplementedError: + pass + try: + os.chmod(symlink, mode) + except FileNotFoundError: + pass + os.chmod(d1.name, mode) + d1.cleanup() + self.assertFalse(os.path.exists(d1.name)) + + with self.subTest('nonexisting file'): + test('nonexisting', target_is_directory=False) + with self.subTest('nonexisting dir'): + test('nonexisting', target_is_directory=True) + + with self.subTest('existing file'): + os.chmod(file1, mode) + old_mode = os.stat(file1).st_mode + test(file1, target_is_directory=False) + new_mode = os.stat(file1).st_mode + self.assertEqual(new_mode, old_mode, + '%03o != %03o' % (new_mode, old_mode)) + + with self.subTest('existing dir'): + os.chmod(dir1, mode) + old_mode = os.stat(dir1).st_mode + test(dir1, target_is_directory=True) + new_mode = os.stat(dir1).st_mode + self.assertEqual(new_mode, old_mode, + '%03o != %03o' % (new_mode, old_mode)) + + @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags') + @os_helper.skip_unless_symlink + def test_cleanup_with_symlink_flags(self): + # cleanup() should not follow symlinks when fixing flags (#91133) + flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK + self.check_flags(flags) + + with self.do_create(recurse=0) as d2: + file1 = os.path.join(d2, 'file1') + open(file1, 'wb').close() + dir1 = os.path.join(d2, 'dir1') + os.mkdir(dir1) + def test(target, target_is_directory): + d1 = self.do_create(recurse=0) + symlink = os.path.join(d1.name, 'symlink') + os.symlink(target, symlink, + target_is_directory=target_is_directory) + try: + os.chflags(symlink, flags, follow_symlinks=False) + except NotImplementedError: + pass + try: + os.chflags(symlink, flags) + except FileNotFoundError: + pass + os.chflags(d1.name, flags) + d1.cleanup() + self.assertFalse(os.path.exists(d1.name)) + + with self.subTest('nonexisting file'): + test('nonexisting', target_is_directory=False) + with self.subTest('nonexisting dir'): + test('nonexisting', target_is_directory=True) + + with self.subTest('existing file'): + os.chflags(file1, flags) + old_flags = os.stat(file1).st_flags + test(file1, target_is_directory=False) + new_flags = os.stat(file1).st_flags + self.assertEqual(new_flags, old_flags) + + with self.subTest('existing dir'): + os.chflags(dir1, flags) + old_flags = os.stat(dir1).st_flags + test(dir1, target_is_directory=True) + new_flags = os.stat(dir1).st_flags + self.assertEqual(new_flags, old_flags) + @support.cpython_only def test_del_on_collection(self): # A TemporaryDirectory is deleted when garbage collected @@ -1834,10 +1953,7 @@ def test_modes(self): d.cleanup() self.assertFalse(os.path.exists(d.name)) - @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags') - def test_flags(self): - flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK - + def check_flags(self, flags): # skip the test if these flags are not supported (ex: FreeBSD 13) filename = os_helper.TESTFN try: @@ -1846,13 +1962,18 @@ def test_flags(self): os.chflags(filename, flags) except OSError as exc: # "OSError: [Errno 45] Operation not supported" - self.skipTest(f"chflags() doesn't support " - f"UF_IMMUTABLE|UF_NOUNLINK: {exc}") + self.skipTest(f"chflags() doesn't support flags " + f"{flags:#b}: {exc}") else: os.chflags(filename, 0) finally: os_helper.unlink(filename) + @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags') + def test_flags(self): + flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK + self.check_flags(flags) + d = self.do_create(recurse=3, dirs=2, files=2) with d: # Change files and directories flags recursively. diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py index ca99caaf88b80d..6639eaaa59936a 100644 --- a/Lib/test/test_tkinter/test_misc.py +++ b/Lib/test/test_tkinter/test_misc.py @@ -479,26 +479,46 @@ def test2(e): pass def test_unbind2(self): f = self.frame + f.wait_visibility() + f.focus_force() + f.update_idletasks() event = '' self.assertEqual(f.bind(), ()) self.assertEqual(f.bind(event), '') - def test1(e): pass - def test2(e): pass + def test1(e): events.append('a') + def test2(e): events.append('b') + def test3(e): events.append('c') funcid = f.bind(event, test1) funcid2 = f.bind(event, test2, add=True) + funcid3 = f.bind(event, test3, add=True) + events = [] + f.event_generate(event) + self.assertEqual(events, ['a', 'b', 'c']) - f.unbind(event, funcid) + f.unbind(event, funcid2) script = f.bind(event) - self.assertNotIn(funcid, script) - self.assertCommandNotExist(funcid) - self.assertCommandExist(funcid2) + self.assertNotIn(funcid2, script) + self.assertIn(funcid, script) + self.assertIn(funcid3, script) + self.assertEqual(f.bind(), (event,)) + self.assertCommandNotExist(funcid2) + self.assertCommandExist(funcid) + self.assertCommandExist(funcid3) + events = [] + f.event_generate(event) + self.assertEqual(events, ['a', 'c']) - f.unbind(event, funcid2) + f.unbind(event, funcid) + f.unbind(event, funcid3) self.assertEqual(f.bind(event), '') self.assertEqual(f.bind(), ()) self.assertCommandNotExist(funcid) self.assertCommandNotExist(funcid2) + self.assertCommandNotExist(funcid3) + events = [] + f.event_generate(event) + self.assertEqual(events, []) # non-idempotent self.assertRaises(tkinter.TclError, f.unbind, event, funcid2) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index b43dca6f640b9a..a6708119b81191 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -8,6 +8,7 @@ import inspect import builtins import unittest +import unittest.mock import re import tempfile import random @@ -24,6 +25,7 @@ import json import textwrap import traceback +import contextlib from functools import partial from pathlib import Path @@ -41,6 +43,14 @@ class TracebackCases(unittest.TestCase): # For now, a very minimal set of tests. I want to be sure that # formatting of SyntaxErrors works based on changes for 2.1. + def setUp(self): + super().setUp() + self.colorize = traceback._COLORIZE + traceback._COLORIZE = False + + def tearDown(self): + super().tearDown() + traceback._COLORIZE = self.colorize def get_exception_format(self, func, exc): try: @@ -521,7 +531,7 @@ def test_signatures(self): self.assertEqual( str(inspect.signature(traceback.print_exception)), ('(exc, /, value=, tb=, ' - 'limit=None, file=None, chain=True)')) + 'limit=None, file=None, chain=True, **kwargs)')) self.assertEqual( str(inspect.signature(traceback.format_exception)), @@ -578,6 +588,7 @@ def f(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+1}, in f\n' ' if True: raise ValueError("basic caret tests")\n' ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' @@ -596,6 +607,7 @@ def f_with_unicode(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+1}, in f_with_unicode\n' ' if True: raise ValueError("Ĥellö Wörld")\n' ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' @@ -613,6 +625,7 @@ def foo(a: THIS_DOES_NOT_EXIST ) -> int: 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+1}, in f_with_type\n' ' def foo(a: THIS_DOES_NOT_EXIST ) -> int:\n' ' ^^^^^^^^^^^^^^^^^^^\n' @@ -633,9 +646,14 @@ def f_with_multiline(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+1}, in f_with_multiline\n' ' if True: raise ValueError(\n' - ' ^^^^^^^^^^^^^^^^^' + ' ^^^^^^^^^^^^^^^^^\n' + ' "error over multiple lines"\n' + ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' + ' )\n' + ' ^' ) result_lines = self.get_exception(f_with_multiline) self.assertEqual(result_lines, expected_f.splitlines()) @@ -664,9 +682,10 @@ def f_with_multiline(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_multiline\n' ' return compile(code, "?", "exec")\n' - ' ^^^^^^^^^^^^^^^^^^^^^^^^^^\n' + ' ~~~~~~~^^^^^^^^^^^^^^^^^^^\n' ' File "?", line 7\n' ' foo(a, z\n' ' ^' @@ -689,9 +708,12 @@ def f_with_multiline(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_multiline\n' ' 2 + 1 /\n' - ' ^^^' + ' ~~^\n' + ' 0\n' + ' ~' ) result_lines = self.get_exception(f_with_multiline) self.assertEqual(result_lines, expected_f.splitlines()) @@ -706,6 +728,7 @@ def f_with_binary_operator(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n' ' return 10 + divisor / 0 + 30\n' ' ~~~~~~~~^~~\n' @@ -723,6 +746,7 @@ def f_with_binary_operator(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n' ' return 10 + áóí / 0 + 30\n' ' ~~~~^~~\n' @@ -740,6 +764,7 @@ def f_with_binary_operator(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n' ' return 10 + divisor // 0 + 30\n' ' ~~~~~~~~^^~~\n' @@ -751,16 +776,102 @@ def test_caret_for_binary_operators_with_spaces_and_parenthesis(self): def f_with_binary_operator(): a = 1 b = "" - return ( a ) + b + return ( a ) +b lineno_f = f_with_binary_operator.__code__.co_firstlineno expected_error = ( 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n' - ' return ( a ) + b\n' - ' ~~~~~~~~~~^~~\n' + ' return ( a ) +b\n' + ' ~~~~~~~~~~^~\n' + ) + result_lines = self.get_exception(f_with_binary_operator) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_caret_for_binary_operators_multiline(self): + def f_with_binary_operator(): + b = 1 + c = "" + a = b \ + +\ + c # test + return a + + lineno_f = f_with_binary_operator.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n' + ' a = b \\\n' + ' ~~~~~~\n' + ' +\\\n' + ' ^~\n' + ' c # test\n' + ' ~\n' + ) + result_lines = self.get_exception(f_with_binary_operator) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_caret_for_binary_operators_multiline_two_char(self): + def f_with_binary_operator(): + b = 1 + c = "" + a = ( + (b # test + + ) \ + # + + << (c # test + \ + ) # test + ) + return a + + lineno_f = f_with_binary_operator.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+4}, in f_with_binary_operator\n' + ' (b # test +\n' + ' ~~~~~~~~~~~~\n' + ' ) \\\n' + ' ~~~~\n' + ' # +\n' + ' ~~~\n' + ' << (c # test\n' + ' ^^~~~~~~~~~~~\n' + ' \\\n' + ' ~\n' + ' ) # test\n' + ' ~\n' + ) + result_lines = self.get_exception(f_with_binary_operator) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_caret_for_binary_operators_multiline_with_unicode(self): + def f_with_binary_operator(): + b = 1 + a = ("ááá" + + "áá") + b + return a + + lineno_f = f_with_binary_operator.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n' + ' a = ("ááá" +\n' + ' ~~~~~~~~\n' + ' "áá") + b\n' + ' ~~~~~~^~~\n' ) result_lines = self.get_exception(f_with_binary_operator) self.assertEqual(result_lines, expected_error.splitlines()) @@ -775,6 +886,7 @@ def f_with_subscript(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_subscript\n' " return some_dict['x']['y']['z']\n" ' ~~~~~~~~~~~~~~~~~~~^^^^^\n' @@ -792,6 +904,7 @@ def f_with_subscript(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_subscript\n' " return some_dict['ó']['á']['í']['beta']\n" ' ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^\n' @@ -810,6 +923,7 @@ def f_with_binary_operator(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n' ' return b [ a ] + c\n' ' ~~~~~~^^^^^^^^^\n' @@ -817,6 +931,226 @@ def f_with_binary_operator(): result_lines = self.get_exception(f_with_binary_operator) self.assertEqual(result_lines, expected_error.splitlines()) + def test_caret_for_subscript_multiline(self): + def f_with_subscript(): + bbbbb = {} + ccc = 1 + ddd = 2 + b = bbbbb \ + [ ccc # test + + + ddd \ + + ] # test + return b + + lineno_f = f_with_subscript.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+4}, in f_with_subscript\n' + ' b = bbbbb \\\n' + ' ~~~~~~~\n' + ' [ ccc # test\n' + ' ^^^^^^^^^^^^^\n' + ' \n' + ' \n' + ' + ddd \\\n' + ' ^^^^^^^^\n' + ' \n' + ' \n' + ' ] # test\n' + ' ^\n' + ) + result_lines = self.get_exception(f_with_subscript) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_caret_for_call(self): + def f_with_call(): + def f1(a): + def f2(b): + raise RuntimeError("fail") + return f2 + return f1("x")("y") + + lineno_f = f_with_call.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+5}, in f_with_call\n' + ' return f1("x")("y")\n' + ' ~~~~~~~^^^^^\n' + f' File "{__file__}", line {lineno_f+3}, in f2\n' + ' raise RuntimeError("fail")\n' + ) + result_lines = self.get_exception(f_with_call) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_caret_for_call_unicode(self): + def f_with_call(): + def f1(a): + def f2(b): + raise RuntimeError("fail") + return f2 + return f1("ó")("á") + + lineno_f = f_with_call.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+5}, in f_with_call\n' + ' return f1("ó")("á")\n' + ' ~~~~~~~^^^^^\n' + f' File "{__file__}", line {lineno_f+3}, in f2\n' + ' raise RuntimeError("fail")\n' + ) + result_lines = self.get_exception(f_with_call) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_caret_for_call_with_spaces_and_parenthesis(self): + def f_with_binary_operator(): + def f(a): + raise RuntimeError("fail") + return f ( "x" ) + 2 + + lineno_f = f_with_binary_operator.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n' + ' return f ( "x" ) + 2\n' + ' ~~~~~~^^^^^^^^^^^\n' + f' File "{__file__}", line {lineno_f+2}, in f\n' + ' raise RuntimeError("fail")\n' + ) + result_lines = self.get_exception(f_with_binary_operator) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_caret_for_call_multiline(self): + def f_with_call(): + class C: + def y(self, a): + def f(b): + raise RuntimeError("fail") + return f + def g(x): + return C() + a = (g(1).y)( + 2 + )(3)(4) + return a + + lineno_f = f_with_call.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+8}, in f_with_call\n' + ' a = (g(1).y)(\n' + ' ~~~~~~~~~\n' + ' 2\n' + ' ~\n' + ' )(3)(4)\n' + ' ~^^^\n' + f' File "{__file__}", line {lineno_f+4}, in f\n' + ' raise RuntimeError("fail")\n' + ) + result_lines = self.get_exception(f_with_call) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_many_lines(self): + def f(): + x = 1 + if True: x += ( + "a" + + "a" + ) # test + + lineno_f = f.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+2}, in f\n' + ' if True: x += (\n' + ' ^^^^^^\n' + ' ...<2 lines>...\n' + ' ) # test\n' + ' ^\n' + ) + result_lines = self.get_exception(f) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_many_lines_no_caret(self): + def f(): + x = 1 + x += ( + "a" + + "a" + ) + + lineno_f = f.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+2}, in f\n' + ' x += (\n' + ' ...<2 lines>...\n' + ' )\n' + ) + result_lines = self.get_exception(f) + self.assertEqual(result_lines, expected_error.splitlines()) + + def test_many_lines_binary_op(self): + def f_with_binary_operator(): + b = 1 + c = "a" + a = ( + b + + b + ) + ( + c + + c + + c + ) + return a + + lineno_f = f_with_binary_operator.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ~~~~~~~~^^\n' + f' File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n' + ' a = (\n' + ' ~\n' + ' b +\n' + ' ~~~\n' + ' b\n' + ' ~\n' + ' ) + (\n' + ' ~~^~~\n' + ' c +\n' + ' ~~~\n' + ' ...<2 lines>...\n' + ' )\n' + ' ~\n' + ) + result_lines = self.get_exception(f_with_binary_operator) + self.assertEqual(result_lines, expected_error.splitlines()) + def test_traceback_specialization_with_syntax_error(self): bytecode = compile("1 / 0 / 1 / 2\n", TESTFN, "exec") @@ -833,6 +1167,7 @@ def test_traceback_specialization_with_syntax_error(self): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{TESTFN}", line {lineno_f}, in \n' " 1 $ 0 / 1 / 2\n" ' ^^^^^\n' @@ -855,6 +1190,7 @@ def test_traceback_very_long_line(self): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{TESTFN}", line {lineno_f}, in \n' f' {source}\n' f' {" "*len("if True: ") + "^"*256}\n' @@ -872,6 +1208,7 @@ def f_with_subscript(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_f+2}, in f_with_subscript\n' " some_dict['x']['y']['z']\n" ' ~~~~~~~~~~~~~~~~~~~^^^^^\n' @@ -891,6 +1228,7 @@ def exc(): f' + Exception Group Traceback (most recent call last):\n' f' | File "{__file__}", line {self.callable_line}, in get_exception\n' f' | callable()\n' + f' | ~~~~~~~~^^\n' f' | File "{__file__}", line {exc.__code__.co_firstlineno + 1}, in exc\n' f' | if True: raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])\n' f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' @@ -956,6 +1294,7 @@ def g(): pass 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_applydescs + 1}, in applydecs\n' ' @dec_error\n' ' ^^^^^^^^^\n' @@ -974,6 +1313,7 @@ class A: pass 'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' ' callable()\n' + ' ~~~~~~~~^^\n' f' File "{__file__}", line {lineno_applydescs_class + 1}, in applydecs_class\n' ' @dec_error\n' ' ^^^^^^^^^\n' @@ -992,6 +1332,7 @@ def f(): "Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", " callable()", + " ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f", " .method", " ^^^^^^", @@ -1008,6 +1349,7 @@ def f(): "Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", " callable()", + " ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f", " method", ] @@ -1023,6 +1365,7 @@ def f(): "Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", " callable()", + " ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f", " . method", " ^^^^^^", @@ -1038,6 +1381,7 @@ def f(): "Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", " callable()", + " ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f", " width", ] @@ -1054,6 +1398,7 @@ def f(): "Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", " callable()", + " ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f", " raise ValueError(width)", ] @@ -1072,9 +1417,12 @@ def f(): "Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", " callable()", + " ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 4}, in f", f" print(1, www(", - f" ^^^^^^^", + f" ~~~~~~^", + f" th))", + f" ^^^^^", ] self.assertEqual(actual, expected) @@ -1089,6 +1437,7 @@ def f(): f"Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", f" callable()", + f" ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 3}, in f", f" return 说明说明 / şçöğıĤellö", f" ~~~~~~~~~^~~~~~~~~~~~", @@ -1105,6 +1454,7 @@ def f(): f"Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", f" callable()", + f" ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f", f' return "✨🐍" + func_说明说明("📗🚛",', f" ^^^^^^^^^^^^^", @@ -1127,6 +1477,7 @@ def f(): f"Traceback (most recent call last):", f" File \"{__file__}\", line {self.callable_line}, in get_exception", f" callable()", + f" ~~~~~~~~^^", f" File \"{__file__}\", line {f.__code__.co_firstlineno + 8}, in f", f' return my_dct["✨🚛✨"]["说明"]["🐍"]["说明"]["🐍🐍"]', f" ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^", @@ -1141,6 +1492,7 @@ def f(): expected = ['Traceback (most recent call last):', f' File "{__file__}", line {self.callable_line}, in get_exception', ' callable()', + ' ~~~~~~~~^^', f' File "{__file__}", line {f.__code__.co_firstlineno + 1}, in f', ' raise MemoryError()'] self.assertEqual(actual, expected) @@ -1187,6 +1539,14 @@ class TracebackFormatMixin: def some_exception(self): raise KeyError('blah') + def _filter_debug_ranges(self, expected): + return [line for line in expected if not set(line.strip()) <= set("^~")] + + def _maybe_filter_debug_ranges(self, expected): + if not self.DEBUG_RANGES: + return self._filter_debug_ranges(expected) + return expected + @cpython_only def check_traceback_format(self, cleanup_func=None): from _testcapi import traceback_print @@ -1199,6 +1559,11 @@ def check_traceback_format(self, cleanup_func=None): cleanup_func(tb.tb_next) traceback_fmt = 'Traceback (most recent call last):\n' + \ ''.join(traceback.format_tb(tb)) + # clear caret lines from traceback_fmt since internal API does + # not emit them + traceback_fmt = "\n".join( + self._filter_debug_ranges(traceback_fmt.splitlines()) + ) + "\n" file_ = StringIO() traceback_print(tb, file_) python_fmt = file_.getvalue() @@ -1291,12 +1656,16 @@ def f(): 'Traceback (most recent call last):\n' f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n' ' f()\n' + ' ~^^\n' f' File "{__file__}", line {lineno_f+1}, in f\n' ' f()\n' + ' ~^^\n' f' File "{__file__}", line {lineno_f+1}, in f\n' ' f()\n' + ' ~^^\n' f' File "{__file__}", line {lineno_f+1}, in f\n' ' f()\n' + ' ~^^\n' # XXX: The following line changes depending on whether the tests # are run through the interactive interpreter or with -m # It also varies depending on the platform (stack size) @@ -1305,7 +1674,7 @@ def f(): 'RecursionError: maximum recursion depth exceeded\n' ) - expected = result_f.splitlines() + expected = self._maybe_filter_debug_ranges(result_f.splitlines()) actual = stderr_f.getvalue().splitlines() # Check the output text matches expectations @@ -1337,13 +1706,13 @@ def g(count=10): result_g = ( f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' ' [Previous line repeated 7 more times]\n' f' File "{__file__}", line {lineno_g+3}, in g\n' ' raise ValueError\n' @@ -1353,11 +1722,10 @@ def g(count=10): 'Traceback (most recent call last):\n' f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n' ' g()\n' + ' ~^^\n' ) - expected = (tb_line + result_g).splitlines() + expected = self._maybe_filter_debug_ranges((tb_line + result_g).splitlines()) actual = stderr_g.getvalue().splitlines() - if not self.DEBUG_RANGES: - expected = [line for line in expected if not set(line.strip()) == {"^"}] self.assertEqual(actual, expected) # Check 2 different repetitive sections @@ -1379,23 +1747,23 @@ def h(count=10): 'Traceback (most recent call last):\n' f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n' ' h()\n' + ' ~^^\n' f' File "{__file__}", line {lineno_h+2}, in h\n' ' return h(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_h+2}, in h\n' ' return h(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_h+2}, in h\n' ' return h(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' ' [Previous line repeated 7 more times]\n' f' File "{__file__}", line {lineno_h+3}, in h\n' ' g()\n' + ' ~^^\n' ) - expected = (result_h + result_g).splitlines() + expected = self._maybe_filter_debug_ranges((result_h + result_g).splitlines()) actual = stderr_h.getvalue().splitlines() - if not self.DEBUG_RANGES: - expected = [line for line in expected if not set(line.strip()) == {"^"}] self.assertEqual(actual, expected) # Check the boundary conditions. First, test just below the cutoff. @@ -1409,26 +1777,25 @@ def h(count=10): result_g = ( f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+3}, in g\n' ' raise ValueError\n' 'ValueError\n' ) tb_line = ( 'Traceback (most recent call last):\n' - f' File "{__file__}", line {lineno_g+81}, in _check_recursive_traceback_display\n' + f' File "{__file__}", line {lineno_g+80}, in _check_recursive_traceback_display\n' ' g(traceback._RECURSIVE_CUTOFF)\n' + ' ~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' ) - expected = (tb_line + result_g).splitlines() + expected = self._maybe_filter_debug_ranges((tb_line + result_g).splitlines()) actual = stderr_g.getvalue().splitlines() - if not self.DEBUG_RANGES: - expected = [line for line in expected if not set(line.strip()) == {"^"}] self.assertEqual(actual, expected) # Second, test just above the cutoff. @@ -1442,13 +1809,13 @@ def h(count=10): result_g = ( f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - ' ^^^^^^^^^^\n' + ' ~^^^^^^^^^\n' ' [Previous line repeated 1 more time]\n' f' File "{__file__}", line {lineno_g+3}, in g\n' ' raise ValueError\n' @@ -1456,13 +1823,12 @@ def h(count=10): ) tb_line = ( 'Traceback (most recent call last):\n' - f' File "{__file__}", line {lineno_g+114}, in _check_recursive_traceback_display\n' + f' File "{__file__}", line {lineno_g+112}, in _check_recursive_traceback_display\n' ' g(traceback._RECURSIVE_CUTOFF + 1)\n' + ' ~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' ) - expected = (tb_line + result_g).splitlines() + expected = self._maybe_filter_debug_ranges((tb_line + result_g).splitlines()) actual = stderr_g.getvalue().splitlines() - if not self.DEBUG_RANGES: - expected = [line for line in expected if not set(line.strip()) == {"^"}] self.assertEqual(actual, expected) @requires_debug_ranges() @@ -1942,6 +2308,7 @@ def exc(): f' + Exception Group Traceback (most recent call last):\n' f' | File "{__file__}", line {self.callable_line}, in get_exception\n' f' | exception_or_callable()\n' + f' | ~~~~~~~~~~~~~~~~~~~~~^^\n' f' | File "{__file__}", line {exc.__code__.co_firstlineno + 1}, in exc\n' f' | raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])\n' f' | ExceptionGroup: eg (2 sub-exceptions)\n' @@ -1977,6 +2344,7 @@ def exc(): f' + Exception Group Traceback (most recent call last):\n' f' | File "{__file__}", line {self.callable_line}, in get_exception\n' f' | exception_or_callable()\n' + f' | ~~~~~~~~~~~~~~~~~~~~~^^\n' f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n' f' | raise EG("eg2", [ValueError(3), TypeError(4)]) from e\n' f' | ExceptionGroup: eg2 (2 sub-exceptions)\n' @@ -2028,6 +2396,7 @@ def exc(): f'Traceback (most recent call last):\n' f' File "{__file__}", line {self.callable_line}, in get_exception\n' f' exception_or_callable()\n' + f' ~~~~~~~~~~~~~~~~~~~~~^^\n' f' File "{__file__}", line {exc.__code__.co_firstlineno + 8}, in exc\n' f' raise ImportError(5)\n' f'ImportError: 5\n') @@ -2074,6 +2443,7 @@ def exc(): f' + Exception Group Traceback (most recent call last):\n' f' | File "{__file__}", line {self.callable_line}, in get_exception\n' f' | exception_or_callable()\n' + f' | ~~~~~~~~~~~~~~~~~~~~~^^\n' f' | File "{__file__}", line {exc.__code__.co_firstlineno + 11}, in exc\n' f' | raise EG("top", [VE(5)])\n' f' | ExceptionGroup: top (1 sub-exception)\n' @@ -2233,6 +2603,7 @@ def exc(): expected = (f' + Exception Group Traceback (most recent call last):\n' f' | File "{__file__}", line {self.callable_line}, in get_exception\n' f' | exception_or_callable()\n' + f' | ~~~~~~~~~~~~~~~~~~~~~^^\n' f' | File "{__file__}", line {exc.__code__.co_firstlineno + 9}, in exc\n' f' | raise ExceptionGroup("nested", excs)\n' f' | ExceptionGroup: nested (2 sub-exceptions)\n' @@ -2284,6 +2655,7 @@ def exc(): expected = (f' + Exception Group Traceback (most recent call last):\n' f' | File "{__file__}", line {self.callable_line}, in get_exception\n' f' | exception_or_callable()\n' + f' | ~~~~~~~~~~~~~~~~~~~~~^^\n' f' | File "{__file__}", line {exc.__code__.co_firstlineno + 10}, in exc\n' f' | raise ExceptionGroup("nested", excs)\n' f' | ExceptionGroup: nested (2 sub-exceptions)\n' @@ -2552,7 +2924,7 @@ def test_basics(self): def test_lazy_lines(self): linecache.clearcache() f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False) - self.assertEqual(None, f._line) + self.assertEqual(None, f._lines) linecache.lazycache("f", globals()) self.assertEqual( '"""Test cases for traceback module"""', @@ -2669,7 +3041,7 @@ def some_inner(k, v): def test_custom_format_frame(self): class CustomStackSummary(traceback.StackSummary): - def format_frame_summary(self, frame_summary): + def format_frame_summary(self, frame_summary, colorize=False): return f'{frame_summary.filename}:{frame_summary.lineno}' def some_inner(): @@ -2694,7 +3066,7 @@ def g(): tb = g() class Skip_G(traceback.StackSummary): - def format_frame_summary(self, frame_summary): + def format_frame_summary(self, frame_summary, colorize=False): if frame_summary.name == 'g': return None return super().format_frame_summary(frame_summary) @@ -2714,10 +3086,9 @@ def __repr__(self) -> str: raise Exception("Unrepresentable") class TestTracebackException(unittest.TestCase): - - def test_smoke(self): + def do_test_smoke(self, exc, expected_type_str): try: - 1/0 + raise exc except Exception as e: exc_obj = e exc = traceback.TracebackException.from_exception(e) @@ -2727,9 +3098,23 @@ def test_smoke(self): self.assertEqual(None, exc.__context__) self.assertEqual(False, exc.__suppress_context__) self.assertEqual(expected_stack, exc.stack) - self.assertEqual(type(exc_obj), exc.exc_type) + with self.assertWarns(DeprecationWarning): + self.assertEqual(type(exc_obj), exc.exc_type) + self.assertEqual(expected_type_str, exc.exc_type_str) self.assertEqual(str(exc_obj), str(exc)) + def test_smoke_builtin(self): + self.do_test_smoke(ValueError(42), 'ValueError') + + def test_smoke_user_exception(self): + class MyException(Exception): + pass + + self.do_test_smoke( + MyException('bad things happened'), + ('test.test_traceback.TestTracebackException.' + 'test_smoke_user_exception..MyException')) + def test_from_exception(self): # Check all the parameters are accepted. def foo(): @@ -2750,7 +3135,9 @@ def foo(): self.assertEqual(None, exc.__context__) self.assertEqual(False, exc.__suppress_context__) self.assertEqual(expected_stack, exc.stack) - self.assertEqual(type(exc_obj), exc.exc_type) + with self.assertWarns(DeprecationWarning): + self.assertEqual(type(exc_obj), exc.exc_type) + self.assertEqual(type(exc_obj).__name__, exc.exc_type_str) self.assertEqual(str(exc_obj), str(exc)) def test_cause(self): @@ -2772,7 +3159,9 @@ def test_cause(self): self.assertEqual(exc_context, exc.__context__) self.assertEqual(True, exc.__suppress_context__) self.assertEqual(expected_stack, exc.stack) - self.assertEqual(type(exc_obj), exc.exc_type) + with self.assertWarns(DeprecationWarning): + self.assertEqual(type(exc_obj), exc.exc_type) + self.assertEqual(type(exc_obj).__name__, exc.exc_type_str) self.assertEqual(str(exc_obj), str(exc)) def test_context(self): @@ -2792,7 +3181,9 @@ def test_context(self): self.assertEqual(exc_context, exc.__context__) self.assertEqual(False, exc.__suppress_context__) self.assertEqual(expected_stack, exc.stack) - self.assertEqual(type(exc_obj), exc.exc_type) + with self.assertWarns(DeprecationWarning): + self.assertEqual(type(exc_obj), exc.exc_type) + self.assertEqual(type(exc_obj).__name__, exc.exc_type_str) self.assertEqual(str(exc_obj), str(exc)) def test_long_context_chain(self): @@ -2837,7 +3228,9 @@ def test_compact_with_cause(self): self.assertEqual(None, exc.__context__) self.assertEqual(True, exc.__suppress_context__) self.assertEqual(expected_stack, exc.stack) - self.assertEqual(type(exc_obj), exc.exc_type) + with self.assertWarns(DeprecationWarning): + self.assertEqual(type(exc_obj), exc.exc_type) + self.assertEqual(type(exc_obj).__name__, exc.exc_type_str) self.assertEqual(str(exc_obj), str(exc)) def test_compact_no_cause(self): @@ -2857,9 +3250,22 @@ def test_compact_no_cause(self): self.assertEqual(exc_context, exc.__context__) self.assertEqual(False, exc.__suppress_context__) self.assertEqual(expected_stack, exc.stack) - self.assertEqual(type(exc_obj), exc.exc_type) + with self.assertWarns(DeprecationWarning): + self.assertEqual(type(exc_obj), exc.exc_type) + self.assertEqual(type(exc_obj).__name__, exc.exc_type_str) self.assertEqual(str(exc_obj), str(exc)) + def test_no_save_exc_type(self): + try: + 1/0 + except Exception as e: + exc = e + + te = traceback.TracebackException.from_exception( + exc, save_exc_type=False) + with self.assertWarns(DeprecationWarning): + self.assertIsNone(te.exc_type) + def test_no_refs_to_exception_and_traceback_objects(self): try: 1/0 @@ -3108,6 +3514,7 @@ def test_exception_group_format(self): f' | Traceback (most recent call last):', f' | File "{__file__}", line {lno_g+9}, in _get_exception_group', f' | f()', + f' | ~^^', f' | File "{__file__}", line {lno_f+1}, in f', f' | 1/0', f' | ~^~', @@ -3116,6 +3523,7 @@ def test_exception_group_format(self): f' | Traceback (most recent call last):', f' | File "{__file__}", line {lno_g+13}, in _get_exception_group', f' | g(42)', + f' | ~^^^^', f' | File "{__file__}", line {lno_g+1}, in g', f' | raise ValueError(v)', f' | ValueError: 42', @@ -3124,6 +3532,7 @@ def test_exception_group_format(self): f' | Traceback (most recent call last):', f' | File "{__file__}", line {lno_g+20}, in _get_exception_group', f' | g(24)', + f' | ~^^^^', f' | File "{__file__}", line {lno_g+1}, in g', f' | raise ValueError(v)', f' | ValueError: 24', @@ -3845,6 +4254,115 @@ def test_levenshtein_distance_short_circuit(self): res3 = traceback._levenshtein_distance(a, b, threshold) self.assertGreater(res3, threshold, msg=(a, b, threshold)) +class TestColorizedTraceback(unittest.TestCase): + def test_colorized_traceback(self): + def foo(*args): + x = {'a':{'b': None}} + y = x['a']['b']['c'] + + def baz(*args): + return foo(1,2,3,4) + + def bar(): + return baz(1, + 2,3 + ,4) + try: + bar() + except Exception as e: + exc = traceback.TracebackException.from_exception( + e, capture_locals=True + ) + lines = "".join(exc.format(colorize=True)) + red = traceback._ANSIColors.RED + boldr = traceback._ANSIColors.BOLD_RED + reset = traceback._ANSIColors.RESET + self.assertIn("y = " + red + "x['a']['b']" + reset + boldr + "['c']" + reset, lines) + self.assertIn("return " + red + "foo" + reset + boldr + "(1,2,3,4)" + reset, lines) + self.assertIn("return " + red + "baz" + reset + boldr + "(1," + reset, lines) + self.assertIn(boldr + "2,3" + reset, lines) + self.assertIn(boldr + ",4)" + reset, lines) + self.assertIn(red + "bar" + reset + boldr + "()" + reset, lines) + + def test_colorized_syntax_error(self): + try: + compile("a $ b", "", "exec") + except SyntaxError as e: + exc = traceback.TracebackException.from_exception( + e, capture_locals=True + ) + actual = "".join(exc.format(colorize=True)) + red = traceback._ANSIColors.RED + magenta = traceback._ANSIColors.MAGENTA + boldm = traceback._ANSIColors.BOLD_MAGENTA + boldr = traceback._ANSIColors.BOLD_RED + reset = traceback._ANSIColors.RESET + expected = "".join([ + f' File {magenta}""{reset}, line {magenta}1{reset}\n', + f' a {boldr}${reset} b\n', + f' {boldr}^{reset}\n', + f'{boldm}SyntaxError{reset}: {magenta}invalid syntax{reset}\n'] + ) + self.assertIn(expected, actual) + + def test_colorized_traceback_is_the_default(self): + def foo(): + 1/0 + + from _testcapi import exception_print + try: + foo() + self.fail("No exception thrown.") + except Exception as e: + with captured_output("stderr") as tbstderr: + with unittest.mock.patch('traceback._can_colorize', return_value=True): + exception_print(e) + actual = tbstderr.getvalue().splitlines() + + red = traceback._ANSIColors.RED + boldr = traceback._ANSIColors.BOLD_RED + magenta = traceback._ANSIColors.MAGENTA + boldm = traceback._ANSIColors.BOLD_MAGENTA + reset = traceback._ANSIColors.RESET + lno_foo = foo.__code__.co_firstlineno + expected = ['Traceback (most recent call last):', + f' File {magenta}"{__file__}"{reset}, ' + f'line {magenta}{lno_foo+5}{reset}, in {magenta}test_colorized_traceback_is_the_default{reset}', + f' {red}foo{reset+boldr}(){reset}', + f' {red}~~~{reset+boldr}^^{reset}', + f' File {magenta}"{__file__}"{reset}, ' + f'line {magenta}{lno_foo+1}{reset}, in {magenta}foo{reset}', + f' {red}1{reset+boldr}/{reset+red}0{reset}', + f' {red}~{reset+boldr}^{reset+red}~{reset}', + f'{boldm}ZeroDivisionError{reset}: {magenta}division by zero{reset}'] + self.assertEqual(actual, expected) + + def test_colorized_detection_checks_for_environment_variables(self): + if sys.platform == "win32": + virtual_patching = unittest.mock.patch("nt._supports_virtual_terminal", return_value=True) + else: + virtual_patching = contextlib.nullcontext() + with virtual_patching: + with unittest.mock.patch("os.isatty") as isatty_mock: + isatty_mock.return_value = True + with unittest.mock.patch("os.environ", {'TERM': 'dumb'}): + self.assertEqual(traceback._can_colorize(), False) + with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '1'}): + self.assertEqual(traceback._can_colorize(), True) + with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '0'}): + self.assertEqual(traceback._can_colorize(), False) + with unittest.mock.patch("os.environ", {'NO_COLOR': '1'}): + self.assertEqual(traceback._can_colorize(), False) + with unittest.mock.patch("os.environ", {'NO_COLOR': '1', "PYTHON_COLORS": '1'}): + self.assertEqual(traceback._can_colorize(), True) + with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1'}): + self.assertEqual(traceback._can_colorize(), True) + with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1', 'NO_COLOR': '1'}): + self.assertEqual(traceback._can_colorize(), False) + with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1', "PYTHON_COLORS": '0'}): + self.assertEqual(traceback._can_colorize(), False) + isatty_mock.return_value = False + self.assertEqual(traceback._can_colorize(), False) if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_ttk/test_style.py b/Lib/test/test_ttk/test_style.py index 52c4b7a0beabd0..9a04a95dc40d65 100644 --- a/Lib/test/test_ttk/test_style.py +++ b/Lib/test/test_ttk/test_style.py @@ -258,6 +258,55 @@ def test_element_create_image_errors(self): with self.assertRaisesRegex(TclError, 'bad option'): style.element_create('block2', 'image', image, spam=1) + def test_element_create_vsapi_1(self): + style = self.style + if 'xpnative' not in style.theme_names(): + self.skipTest("requires 'xpnative' theme") + style.element_create('smallclose', 'vsapi', 'WINDOW', 19, [ + ('disabled', 4), + ('pressed', 3), + ('active', 2), + ('', 1)]) + style.layout('CloseButton', + [('CloseButton.smallclose', {'sticky': 'news'})]) + b = ttk.Button(self.root, style='CloseButton') + b.pack(expand=True, fill='both') + self.assertEqual(b.winfo_reqwidth(), 13) + self.assertEqual(b.winfo_reqheight(), 13) + + def test_element_create_vsapi_2(self): + style = self.style + if 'xpnative' not in style.theme_names(): + self.skipTest("requires 'xpnative' theme") + style.element_create('pin', 'vsapi', 'EXPLORERBAR', 3, [ + ('pressed', '!selected', 3), + ('active', '!selected', 2), + ('pressed', 'selected', 6), + ('active', 'selected', 5), + ('selected', 4), + ('', 1)]) + style.layout('Explorer.Pin', + [('Explorer.Pin.pin', {'sticky': 'news'})]) + pin = ttk.Checkbutton(self.root, style='Explorer.Pin') + pin.pack(expand=True, fill='both') + self.assertEqual(pin.winfo_reqwidth(), 16) + self.assertEqual(pin.winfo_reqheight(), 16) + + def test_element_create_vsapi_3(self): + style = self.style + if 'xpnative' not in style.theme_names(): + self.skipTest("requires 'xpnative' theme") + style.element_create('headerclose', 'vsapi', 'EXPLORERBAR', 2, [ + ('pressed', 3), + ('active', 2), + ('', 1)]) + style.layout('Explorer.CloseButton', + [('Explorer.CloseButton.headerclose', {'sticky': 'news'})]) + b = ttk.Button(self.root, style='Explorer.CloseButton') + b.pack(expand=True, fill='both') + self.assertEqual(b.winfo_reqwidth(), 16) + self.assertEqual(b.winfo_reqheight(), 16) + def test_theme_create(self): style = self.style curr_theme = style.theme_use() @@ -358,6 +407,39 @@ def test_theme_create_image(self): style.theme_use(curr_theme) + def test_theme_create_vsapi(self): + style = self.style + if 'xpnative' not in style.theme_names(): + self.skipTest("requires 'xpnative' theme") + curr_theme = style.theme_use() + new_theme = 'testtheme5' + style.theme_create(new_theme, settings={ + 'pin' : { + 'element create': ['vsapi', 'EXPLORERBAR', 3, [ + ('pressed', '!selected', 3), + ('active', '!selected', 2), + ('pressed', 'selected', 6), + ('active', 'selected', 5), + ('selected', 4), + ('', 1)]], + }, + 'Explorer.Pin' : { + 'layout': [('Explorer.Pin.pin', {'sticky': 'news'})], + }, + }) + + style.theme_use(new_theme) + self.assertIn('pin', style.element_names()) + self.assertEqual(style.layout('Explorer.Pin'), + [('Explorer.Pin.pin', {'sticky': 'nswe'})]) + + pin = ttk.Checkbutton(self.root, style='Explorer.Pin') + pin.pack(expand=True, fill='both') + self.assertEqual(pin.winfo_reqwidth(), 16) + self.assertEqual(pin.winfo_reqheight(), 16) + + style.theme_use(curr_theme) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_ttk_textonly.py b/Lib/test/test_ttk_textonly.py index 96dc179a69ecac..e6525c4d6c6982 100644 --- a/Lib/test/test_ttk_textonly.py +++ b/Lib/test/test_ttk_textonly.py @@ -179,7 +179,7 @@ def test_format_elemcreate(self): # don't format returned values as a tcl script # minimum acceptable for image type self.assertEqual(ttk._format_elemcreate('image', False, 'test'), - ("test ", ())) + ("test", ())) # specifying a state spec self.assertEqual(ttk._format_elemcreate('image', False, 'test', ('', 'a')), ("test {} a", ())) @@ -203,17 +203,19 @@ def test_format_elemcreate(self): # don't format returned values as a tcl script # minimum acceptable for vsapi self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b'), - ("a b ", ())) + ('a', 'b', ('', 1), ())) # now with a state spec with multiple states self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b', - ('a', 'b', 'c')), ("a b {a b} c", ())) + [('a', 'b', 'c')]), ('a', 'b', ('a b', 'c'), ())) # state spec and option self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b', - ('a', 'b'), opt='x'), ("a b a b", ("-opt", "x"))) + [('a', 'b')], opt='x'), ('a', 'b', ('a', 'b'), ("-opt", "x"))) # format returned values as a tcl script # state spec with a multivalue and an option self.assertEqual(ttk._format_elemcreate('vsapi', True, 'a', 'b', - ('a', 'b', [1, 2]), opt='x'), ("{a b {a b} {1 2}}", "-opt x")) + opt='x'), ("a b {{} 1}", "-opt x")) + self.assertEqual(ttk._format_elemcreate('vsapi', True, 'a', 'b', + [('a', 'b', [1, 2])], opt='x'), ("a b {{a b} {1 2}}", "-opt x")) # Testing type = from # from type expects at least a type name @@ -222,9 +224,9 @@ def test_format_elemcreate(self): self.assertEqual(ttk._format_elemcreate('from', False, 'a'), ('a', ())) self.assertEqual(ttk._format_elemcreate('from', False, 'a', 'b'), - ('a', ('b', ))) + ('a', ('b',))) self.assertEqual(ttk._format_elemcreate('from', True, 'a', 'b'), - ('{a}', 'b')) + ('a', 'b')) def test_format_layoutlist(self): @@ -326,6 +328,22 @@ def test_script_from_settings(self): "ttk::style element create thing image {name {state1 state2} val} " "-opt {3 2m}") + vsapi = {'pin': {'element create': + ['vsapi', 'EXPLORERBAR', 3, [ + ('pressed', '!selected', 3), + ('active', '!selected', 2), + ('pressed', 'selected', 6), + ('active', 'selected', 5), + ('selected', 4), + ('', 1)]]}} + self.assertEqual(ttk._script_from_settings(vsapi), + "ttk::style element create pin vsapi EXPLORERBAR 3 {" + "{pressed !selected} 3 " + "{active !selected} 2 " + "{pressed selected} 6 " + "{active selected} 5 " + "selected 4 " + "{} 1} ") def test_tclobj_to_py(self): self.assertEqual( diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 2b5f34b4b92e0c..2d10c39840ddf3 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -3533,13 +3533,26 @@ def __subclasshook__(cls, other): def test_issubclass_fails_correctly(self): @runtime_checkable - class P(Protocol): + class NonCallableMembers(Protocol): x = 1 + class NotRuntimeCheckable(Protocol): + def callable_member(self) -> int: ... + + @runtime_checkable + class RuntimeCheckable(Protocol): + def callable_member(self) -> int: ... + class C: pass - with self.assertRaisesRegex(TypeError, r"issubclass\(\) arg 1 must be a class"): - issubclass(C(), P) + # These three all exercise different code paths, + # but should result in the same error message: + for protocol in NonCallableMembers, NotRuntimeCheckable, RuntimeCheckable: + with self.subTest(proto_name=protocol.__name__): + with self.assertRaisesRegex( + TypeError, r"issubclass\(\) arg 1 must be a class" + ): + issubclass(C(), protocol) def test_defining_generic_protocols(self): T = TypeVar('T') @@ -4091,6 +4104,22 @@ def method(self) -> None: ... self.assertIsInstance(Foo(), ProtocolWithMixedMembers) self.assertNotIsInstance(42, ProtocolWithMixedMembers) + def test_protocol_issubclass_error_message(self): + class Vec2D(Protocol): + x: float + y: float + + def square_norm(self) -> float: + return self.x ** 2 + self.y ** 2 + + self.assertEqual(Vec2D.__protocol_attrs__, {'x', 'y', 'square_norm'}) + expected_error_message = ( + "Protocols with non-method members don't support issubclass()." + " Non-method members: 'x', 'y'." + ) + with self.assertRaisesRegex(TypeError, re.escape(expected_error_message)): + issubclass(int, Vec2D) + class GenericTests(BaseTestCase): @@ -7519,6 +7548,83 @@ class GenericNamedTuple(NamedTuple, Generic[T]): self.assertEqual(CallNamedTuple.__orig_bases__, (NamedTuple,)) + def test_setname_called_on_values_in_class_dictionary(self): + class Vanilla: + def __set_name__(self, owner, name): + self.name = name + + class Foo(NamedTuple): + attr = Vanilla() + + foo = Foo() + self.assertEqual(len(foo), 0) + self.assertNotIn('attr', Foo._fields) + self.assertIsInstance(foo.attr, Vanilla) + self.assertEqual(foo.attr.name, "attr") + + class Bar(NamedTuple): + attr: Vanilla = Vanilla() + + bar = Bar() + self.assertEqual(len(bar), 1) + self.assertIn('attr', Bar._fields) + self.assertIsInstance(bar.attr, Vanilla) + self.assertEqual(bar.attr.name, "attr") + + def test_setname_raises_the_same_as_on_other_classes(self): + class CustomException(BaseException): pass + + class Annoying: + def __set_name__(self, owner, name): + raise CustomException + + annoying = Annoying() + + with self.assertRaises(CustomException) as cm: + class NormalClass: + attr = annoying + normal_exception = cm.exception + + with self.assertRaises(CustomException) as cm: + class NamedTupleClass(NamedTuple): + attr = annoying + namedtuple_exception = cm.exception + + self.assertIs(type(namedtuple_exception), CustomException) + self.assertIs(type(namedtuple_exception), type(normal_exception)) + + self.assertEqual(len(namedtuple_exception.__notes__), 1) + self.assertEqual( + len(namedtuple_exception.__notes__), len(normal_exception.__notes__) + ) + + expected_note = ( + "Error calling __set_name__ on 'Annoying' instance " + "'attr' in 'NamedTupleClass'" + ) + self.assertEqual(namedtuple_exception.__notes__[0], expected_note) + self.assertEqual( + namedtuple_exception.__notes__[0], + normal_exception.__notes__[0].replace("NormalClass", "NamedTupleClass") + ) + + def test_strange_errors_when_accessing_set_name_itself(self): + class CustomException(Exception): pass + + class Meta(type): + def __getattribute__(self, attr): + if attr == "__set_name__": + raise CustomException + return object.__getattribute__(self, attr) + + class VeryAnnoying(metaclass=Meta): pass + + very_annoying = VeryAnnoying() + + with self.assertRaises(CustomException): + class Foo(NamedTuple): + attr = very_annoying + class TypedDictTests(BaseTestCase): def test_basics_functional_syntax(self): @@ -7676,6 +7782,46 @@ class Cat(Animal): 'voice': str, }) + def test_keys_inheritance_with_same_name(self): + class NotTotal(TypedDict, total=False): + a: int + + class Total(NotTotal): + a: int + + self.assertEqual(NotTotal.__required_keys__, frozenset()) + self.assertEqual(NotTotal.__optional_keys__, frozenset(['a'])) + self.assertEqual(Total.__required_keys__, frozenset(['a'])) + self.assertEqual(Total.__optional_keys__, frozenset()) + + class Base(TypedDict): + a: NotRequired[int] + b: Required[int] + + class Child(Base): + a: Required[int] + b: NotRequired[int] + + self.assertEqual(Base.__required_keys__, frozenset(['b'])) + self.assertEqual(Base.__optional_keys__, frozenset(['a'])) + self.assertEqual(Child.__required_keys__, frozenset(['a'])) + self.assertEqual(Child.__optional_keys__, frozenset(['b'])) + + def test_multiple_inheritance_with_same_key(self): + class Base1(TypedDict): + a: NotRequired[int] + + class Base2(TypedDict): + a: Required[str] + + class Child(Base1, Base2): + pass + + # Last base wins + self.assertEqual(Child.__annotations__, {'a': Required[str]}) + self.assertEqual(Child.__required_keys__, frozenset(['a'])) + self.assertEqual(Child.__optional_keys__, frozenset()) + def test_required_notrequired_keys(self): self.assertEqual(NontotalMovie.__required_keys__, frozenset({"title"})) @@ -8542,6 +8688,40 @@ class X(Annotated[int, (1, 10)]): ... self.assertEqual(X.__mro__, (X, int, object), "Annotated should be transparent.") + def test_annotated_cached_with_types(self): + class A(str): ... + class B(str): ... + + field_a1 = Annotated[str, A("X")] + field_a2 = Annotated[str, B("X")] + a1_metadata = field_a1.__metadata__[0] + a2_metadata = field_a2.__metadata__[0] + + self.assertIs(type(a1_metadata), A) + self.assertEqual(a1_metadata, A("X")) + self.assertIs(type(a2_metadata), B) + self.assertEqual(a2_metadata, B("X")) + self.assertIsNot(type(a1_metadata), type(a2_metadata)) + + field_b1 = Annotated[str, A("Y")] + field_b2 = Annotated[str, B("Y")] + b1_metadata = field_b1.__metadata__[0] + b2_metadata = field_b2.__metadata__[0] + + self.assertIs(type(b1_metadata), A) + self.assertEqual(b1_metadata, A("Y")) + self.assertIs(type(b2_metadata), B) + self.assertEqual(b2_metadata, B("Y")) + self.assertIsNot(type(b1_metadata), type(b2_metadata)) + + field_c1 = Annotated[int, 1] + field_c2 = Annotated[int, 1.0] + field_c3 = Annotated[int, True] + + self.assertIs(type(field_c1.__metadata__[0]), int) + self.assertIs(type(field_c2.__metadata__[0]), float) + self.assertIs(type(field_c3.__metadata__[0]), bool) + class TypeAliasTests(BaseTestCase): def test_canonical_usage_with_variable_annotation(self): diff --git a/Lib/test/test_userdict.py b/Lib/test/test_userdict.py index 483910aaa4620e..9a03f2d04ce970 100644 --- a/Lib/test/test_userdict.py +++ b/Lib/test/test_userdict.py @@ -1,6 +1,6 @@ # Check every path through every method of UserDict -from test import mapping_tests +from test import mapping_tests, support import unittest import collections @@ -213,6 +213,11 @@ class G(collections.UserDict): else: self.fail("g[42] didn't raise KeyError") + # Decorate existing test with recursion limit, because + # the test is for C structure, but `UserDict` is a Python structure. + test_repr_deep = support.infinite_recursion()( + mapping_tests.TestHashMappingProtocol.test_repr_deep, + ) if __name__ == "__main__": diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index 2c523230e7e97f..50b0f3fff04c57 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -5,6 +5,8 @@ import re import sys import textwrap +import types +from typing import overload, get_overloads import unittest from test import support from test.support import import_helper @@ -16,6 +18,7 @@ from test.test_warnings.data import stacklevel as warning_tests import warnings as original_warnings +from warnings import deprecated py_warnings = import_helper.import_fresh_module('warnings', @@ -90,7 +93,7 @@ def test_module_all_attribute(self): self.assertTrue(hasattr(self.module, '__all__')) target_api = ["warn", "warn_explicit", "showwarning", "formatwarning", "filterwarnings", "simplefilter", - "resetwarnings", "catch_warnings"] + "resetwarnings", "catch_warnings", "deprecated"] self.assertSetEqual(set(self.module.__all__), set(target_api)) @@ -372,6 +375,28 @@ def test_append_duplicate(self): "appended duplicate changed order of filters" ) + def test_argument_validation(self): + with self.assertRaises(ValueError): + self.module.filterwarnings(action='foo') + with self.assertRaises(TypeError): + self.module.filterwarnings('ignore', message=0) + with self.assertRaises(TypeError): + self.module.filterwarnings('ignore', category=0) + with self.assertRaises(TypeError): + self.module.filterwarnings('ignore', category=int) + with self.assertRaises(TypeError): + self.module.filterwarnings('ignore', module=0) + with self.assertRaises(TypeError): + self.module.filterwarnings('ignore', lineno=int) + with self.assertRaises(ValueError): + self.module.filterwarnings('ignore', lineno=-1) + with self.assertRaises(ValueError): + self.module.simplefilter(action='foo') + with self.assertRaises(TypeError): + self.module.simplefilter('ignore', lineno=int) + with self.assertRaises(ValueError): + self.module.simplefilter('ignore', lineno=-1) + def test_catchwarnings_with_simplefilter_ignore(self): with original_warnings.catch_warnings(module=self.module): self.module.resetwarnings() @@ -1235,8 +1260,8 @@ def test_conflicting_envvar_and_command_line(self): b" File \"\", line 1, in ", b' import sys, warnings; sys.stdout.write(str(sys.warnoptions)); warnings.w' b"arn('Message', DeprecationWarning)", - b' ^^^^^^^^^^' - b'^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^', + b' ~~~~~~~~~~' + b'~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^', b"DeprecationWarning: Message"]) def test_default_filter_configuration(self): @@ -1377,6 +1402,283 @@ def test_late_resource_warning(self): self.assertTrue(err.startswith(expected), ascii(err)) +class DeprecatedTests(unittest.TestCase): + def test_dunder_deprecated(self): + @deprecated("A will go away soon") + class A: + pass + + self.assertEqual(A.__deprecated__, "A will go away soon") + self.assertIsInstance(A, type) + + @deprecated("b will go away soon") + def b(): + pass + + self.assertEqual(b.__deprecated__, "b will go away soon") + self.assertIsInstance(b, types.FunctionType) + + @overload + @deprecated("no more ints") + def h(x: int) -> int: ... + @overload + def h(x: str) -> str: ... + def h(x): + return x + + overloads = get_overloads(h) + self.assertEqual(len(overloads), 2) + self.assertEqual(overloads[0].__deprecated__, "no more ints") + + def test_class(self): + @deprecated("A will go away soon") + class A: + pass + + with self.assertWarnsRegex(DeprecationWarning, "A will go away soon"): + A() + with self.assertWarnsRegex(DeprecationWarning, "A will go away soon"): + with self.assertRaises(TypeError): + A(42) + + def test_class_with_init(self): + @deprecated("HasInit will go away soon") + class HasInit: + def __init__(self, x): + self.x = x + + with self.assertWarnsRegex(DeprecationWarning, "HasInit will go away soon"): + instance = HasInit(42) + self.assertEqual(instance.x, 42) + + def test_class_with_new(self): + has_new_called = False + + @deprecated("HasNew will go away soon") + class HasNew: + def __new__(cls, x): + nonlocal has_new_called + has_new_called = True + return super().__new__(cls) + + def __init__(self, x) -> None: + self.x = x + + with self.assertWarnsRegex(DeprecationWarning, "HasNew will go away soon"): + instance = HasNew(42) + self.assertEqual(instance.x, 42) + self.assertTrue(has_new_called) + + def test_class_with_inherited_new(self): + new_base_called = False + + class NewBase: + def __new__(cls, x): + nonlocal new_base_called + new_base_called = True + return super().__new__(cls) + + def __init__(self, x) -> None: + self.x = x + + @deprecated("HasInheritedNew will go away soon") + class HasInheritedNew(NewBase): + pass + + with self.assertWarnsRegex(DeprecationWarning, "HasInheritedNew will go away soon"): + instance = HasInheritedNew(42) + self.assertEqual(instance.x, 42) + self.assertTrue(new_base_called) + + def test_class_with_new_but_no_init(self): + new_called = False + + @deprecated("HasNewNoInit will go away soon") + class HasNewNoInit: + def __new__(cls, x): + nonlocal new_called + new_called = True + obj = super().__new__(cls) + obj.x = x + return obj + + with self.assertWarnsRegex(DeprecationWarning, "HasNewNoInit will go away soon"): + instance = HasNewNoInit(42) + self.assertEqual(instance.x, 42) + self.assertTrue(new_called) + + def test_mixin_class(self): + @deprecated("Mixin will go away soon") + class Mixin: + pass + + class Base: + def __init__(self, a) -> None: + self.a = a + + with self.assertWarnsRegex(DeprecationWarning, "Mixin will go away soon"): + class Child(Base, Mixin): + pass + + instance = Child(42) + self.assertEqual(instance.a, 42) + + def test_existing_init_subclass(self): + @deprecated("C will go away soon") + class C: + def __init_subclass__(cls) -> None: + cls.inited = True + + with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"): + C() + + with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"): + class D(C): + pass + + self.assertTrue(D.inited) + self.assertIsInstance(D(), D) # no deprecation + + def test_existing_init_subclass_in_base(self): + class Base: + def __init_subclass__(cls, x) -> None: + cls.inited = x + + @deprecated("C will go away soon") + class C(Base, x=42): + pass + + self.assertEqual(C.inited, 42) + + with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"): + C() + + with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"): + class D(C, x=3): + pass + + self.assertEqual(D.inited, 3) + + def test_init_subclass_has_correct_cls(self): + init_subclass_saw = None + + @deprecated("Base will go away soon") + class Base: + def __init_subclass__(cls) -> None: + nonlocal init_subclass_saw + init_subclass_saw = cls + + self.assertIsNone(init_subclass_saw) + + with self.assertWarnsRegex(DeprecationWarning, "Base will go away soon"): + class C(Base): + pass + + self.assertIs(init_subclass_saw, C) + + def test_init_subclass_with_explicit_classmethod(self): + init_subclass_saw = None + + @deprecated("Base will go away soon") + class Base: + @classmethod + def __init_subclass__(cls) -> None: + nonlocal init_subclass_saw + init_subclass_saw = cls + + self.assertIsNone(init_subclass_saw) + + with self.assertWarnsRegex(DeprecationWarning, "Base will go away soon"): + class C(Base): + pass + + self.assertIs(init_subclass_saw, C) + + def test_function(self): + @deprecated("b will go away soon") + def b(): + pass + + with self.assertWarnsRegex(DeprecationWarning, "b will go away soon"): + b() + + def test_method(self): + class Capybara: + @deprecated("x will go away soon") + def x(self): + pass + + instance = Capybara() + with self.assertWarnsRegex(DeprecationWarning, "x will go away soon"): + instance.x() + + def test_property(self): + class Capybara: + @property + @deprecated("x will go away soon") + def x(self): + pass + + @property + def no_more_setting(self): + return 42 + + @no_more_setting.setter + @deprecated("no more setting") + def no_more_setting(self, value): + pass + + instance = Capybara() + with self.assertWarnsRegex(DeprecationWarning, "x will go away soon"): + instance.x + + with py_warnings.catch_warnings(): + py_warnings.simplefilter("error") + self.assertEqual(instance.no_more_setting, 42) + + with self.assertWarnsRegex(DeprecationWarning, "no more setting"): + instance.no_more_setting = 42 + + def test_category(self): + @deprecated("c will go away soon", category=RuntimeWarning) + def c(): + pass + + with self.assertWarnsRegex(RuntimeWarning, "c will go away soon"): + c() + + def test_turn_off_warnings(self): + @deprecated("d will go away soon", category=None) + def d(): + pass + + with py_warnings.catch_warnings(): + py_warnings.simplefilter("error") + d() + + def test_only_strings_allowed(self): + with self.assertRaisesRegex( + TypeError, + "Expected an object of type str for 'message', not 'type'" + ): + @deprecated + class Foo: ... + + with self.assertRaisesRegex( + TypeError, + "Expected an object of type str for 'message', not 'function'" + ): + @deprecated + def foo(): ... + + def test_no_retained_references_to_wrapper_instance(self): + @deprecated('depr') + def d(): pass + + self.assertFalse(any( + isinstance(cell.cell_contents, deprecated) for cell in d.__closure__ + )) + def setUpModule(): py_warnings.onceregistry.clear() c_warnings.onceregistry.clear() diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index 1dc8b91a453f92..ef02c64f886f8a 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -18,6 +18,21 @@ hasattr(zlib.decompressobj(), "copy"), 'requires Decompress.copy()') + +def _zlib_runtime_version_tuple(zlib_version=zlib.ZLIB_RUNTIME_VERSION): + # Register "1.2.3" as "1.2.3.0" + # or "1.2.0-linux","1.2.0.f","1.2.0.f-linux" + v = zlib_version.split('-', 1)[0].split('.') + if len(v) < 4: + v.append('0') + elif not v[-1].isnumeric(): + v[-1] = '0' + return tuple(map(int, v)) + + +ZLIB_RUNTIME_VERSION_TUPLE = _zlib_runtime_version_tuple() + + # bpo-46623: On s390x, when a hardware accelerator is used, using different # ways to compress data with zlib can produce different compressed data. # Simplified test_pair() code: @@ -473,9 +488,8 @@ def test_flushes(self): sync_opt = ['Z_NO_FLUSH', 'Z_SYNC_FLUSH', 'Z_FULL_FLUSH', 'Z_PARTIAL_FLUSH'] - ver = tuple(int(v) for v in zlib.ZLIB_RUNTIME_VERSION.split('.')) # Z_BLOCK has a known failure prior to 1.2.5.3 - if ver >= (1, 2, 5, 3): + if ZLIB_RUNTIME_VERSION_TUPLE >= (1, 2, 5, 3): sync_opt.append('Z_BLOCK') sync_opt = [getattr(zlib, opt) for opt in sync_opt @@ -793,16 +807,7 @@ def test_large_unconsumed_tail(self, size): def test_wbits(self): # wbits=0 only supported since zlib v1.2.3.5 - # Register "1.2.3" as "1.2.3.0" - # or "1.2.0-linux","1.2.0.f","1.2.0.f-linux" - v = zlib.ZLIB_RUNTIME_VERSION.split('-', 1)[0].split('.') - if len(v) < 4: - v.append('0') - elif not v[-1].isnumeric(): - v[-1] = '0' - - v = tuple(map(int, v)) - supports_wbits_0 = v >= (1, 2, 3, 5) + supports_wbits_0 = ZLIB_RUNTIME_VERSION_TUPLE >= (1, 2, 3, 5) co = zlib.compressobj(level=1, wbits=15) zlib15 = co.compress(HAMLET_SCENE) + co.flush() diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 0df7f9d889413c..124882420c255c 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -1527,10 +1527,24 @@ def bind(self, sequence=None, func=None, add=None): return self._bind(('bind', self._w), sequence, func, add) def unbind(self, sequence, funcid=None): - """Unbind for this widget for event SEQUENCE the - function identified with FUNCID.""" - self.tk.call('bind', self._w, sequence, '') - if funcid: + """Unbind for this widget the event SEQUENCE. + + If FUNCID is given, only unbind the function identified with FUNCID + and also delete the corresponding Tcl command. + + Otherwise destroy the current binding for SEQUENCE, leaving SEQUENCE + unbound. + """ + if funcid is None: + self.tk.call('bind', self._w, sequence, '') + else: + lines = self.tk.call('bind', self._w, sequence).split('\n') + prefix = f'if {{"[{funcid} ' + keep = '\n'.join(line for line in lines + if not line.startswith(prefix)) + if not keep.strip(): + keep = '' + self.tk.call('bind', self._w, sequence, keep) self.deletecommand(funcid) def bind_all(self, sequence=None, func=None, add=None): diff --git a/Lib/tkinter/ttk.py b/Lib/tkinter/ttk.py index efeabb7a92c627..5ca938a670831a 100644 --- a/Lib/tkinter/ttk.py +++ b/Lib/tkinter/ttk.py @@ -95,40 +95,47 @@ def _format_mapdict(mapdict, script=False): def _format_elemcreate(etype, script=False, *args, **kw): """Formats args and kw according to the given element factory etype.""" - spec = None + specs = () opts = () - if etype in ("image", "vsapi"): - if etype == "image": # define an element based on an image - # first arg should be the default image name - iname = args[0] - # next args, if any, are statespec/value pairs which is almost - # a mapdict, but we just need the value - imagespec = _join(_mapdict_values(args[1:])) - spec = "%s %s" % (iname, imagespec) - + if etype == "image": # define an element based on an image + # first arg should be the default image name + iname = args[0] + # next args, if any, are statespec/value pairs which is almost + # a mapdict, but we just need the value + imagespec = (iname, *_mapdict_values(args[1:])) + if script: + specs = (imagespec,) else: - # define an element whose visual appearance is drawn using the - # Microsoft Visual Styles API which is responsible for the - # themed styles on Windows XP and Vista. - # Availability: Tk 8.6, Windows XP and Vista. - class_name, part_id = args[:2] - statemap = _join(_mapdict_values(args[2:])) - spec = "%s %s %s" % (class_name, part_id, statemap) + specs = (_join(imagespec),) + opts = _format_optdict(kw, script) + if etype == "vsapi": + # define an element whose visual appearance is drawn using the + # Microsoft Visual Styles API which is responsible for the + # themed styles on Windows XP and Vista. + # Availability: Tk 8.6, Windows XP and Vista. + if len(args) < 3: + class_name, part_id = args + statemap = (((), 1),) + else: + class_name, part_id, statemap = args + specs = (class_name, part_id, tuple(_mapdict_values(statemap))) opts = _format_optdict(kw, script) elif etype == "from": # clone an element # it expects a themename and optionally an element to clone from, # otherwise it will clone {} (empty element) - spec = args[0] # theme name + specs = (args[0],) # theme name if len(args) > 1: # elementfrom specified opts = (_format_optvalue(args[1], script),) if script: - spec = '{%s}' % spec + specs = _join(specs) opts = ' '.join(opts) + return specs, opts + else: + return *specs, opts - return spec, opts def _format_layoutlist(layout, indent=0, indent_size=2): """Formats a layout list so we can pass the result to ttk::style @@ -214,10 +221,10 @@ def _script_from_settings(settings): elemargs = eopts[1:argc] elemkw = eopts[argc] if argc < len(eopts) and eopts[argc] else {} - spec, opts = _format_elemcreate(etype, True, *elemargs, **elemkw) + specs, eopts = _format_elemcreate(etype, True, *elemargs, **elemkw) script.append("ttk::style element create %s %s %s %s" % ( - name, etype, spec, opts)) + name, etype, specs, eopts)) return '\n'.join(script) @@ -434,9 +441,9 @@ def layout(self, style, layoutspec=None): def element_create(self, elementname, etype, *args, **kw): """Create a new element in the current theme of given etype.""" - spec, opts = _format_elemcreate(etype, False, *args, **kw) + *specs, opts = _format_elemcreate(etype, False, *args, **kw) self.tk.call(self._name, "element", "create", elementname, etype, - spec, *opts) + *specs, *opts) def element_names(self): diff --git a/Lib/traceback.py b/Lib/traceback.py index b25a7291f6be51..1cf008c7e9da97 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -1,10 +1,13 @@ """Extract, format and print information about Python stack traces.""" +import os +import io import collections.abc import itertools import linecache import sys import textwrap +import warnings from contextlib import suppress __all__ = ['extract_stack', 'extract_tb', 'format_exception', @@ -18,6 +21,8 @@ # Formatting and printing lists of traceback lines. # +_COLORIZE = True + def print_list(extracted_list, file=None): """Print the list of tuples as returned by extract_tb() or extract_stack() as a formatted stack trace to the given file.""" @@ -109,7 +114,7 @@ def _parse_value_tb(exc, value, tb): def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \ - file=None, chain=True): + file=None, chain=True, **kwargs): """Print exception up to 'limit' stack trace entries from 'tb' to 'file'. This differs from print_tb() in the following ways: (1) if @@ -120,17 +125,44 @@ def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \ occurred with a caret on the next line indicating the approximate position of the error. """ + colorize = kwargs.get("colorize", False) value, tb = _parse_value_tb(exc, value, tb) te = TracebackException(type(value), value, tb, limit=limit, compact=True) - te.print(file=file, chain=chain) + te.print(file=file, chain=chain, colorize=colorize) BUILTIN_EXCEPTION_LIMIT = object() +def _can_colorize(): + if sys.platform == "win32": + try: + import nt + if not nt._supports_virtual_terminal(): + return False + except (ImportError, AttributeError): + return False + + if os.environ.get("PYTHON_COLORS") == "0": + return False + if os.environ.get("PYTHON_COLORS") == "1": + return True + if "NO_COLOR" in os.environ: + return False + if not _COLORIZE: + return False + if "FORCE_COLOR" in os.environ: + return True + if os.environ.get("TERM") == "dumb": + return False + try: + return os.isatty(sys.stderr.fileno()) + except io.UnsupportedOperation: + return sys.stderr.isatty() def _print_exception_bltin(exc, /): file = sys.stderr if sys.stderr is not None else sys.__stderr__ - return print_exception(exc, limit=BUILTIN_EXCEPTION_LIMIT, file=file) + colorize = _can_colorize() + return print_exception(exc, limit=BUILTIN_EXCEPTION_LIMIT, file=file, colorize=colorize) def format_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \ @@ -171,13 +203,19 @@ def format_exception_only(exc, /, value=_sentinel, *, show_group=False): # -- not official API but folk probably use these two functions. -def _format_final_exc_line(etype, value, *, insert_final_newline=True): +def _format_final_exc_line(etype, value, *, insert_final_newline=True, colorize=False): valuestr = _safe_string(value, 'exception') end_char = "\n" if insert_final_newline else "" - if value is None or not valuestr: - line = f"{etype}{end_char}" + if colorize: + if value is None or not valuestr: + line = f"{_ANSIColors.BOLD_MAGENTA}{etype}{_ANSIColors.RESET}{end_char}" + else: + line = f"{_ANSIColors.BOLD_MAGENTA}{etype}{_ANSIColors.RESET}: {_ANSIColors.MAGENTA}{valuestr}{_ANSIColors.RESET}{end_char}" else: - line = f"{etype}: {valuestr}{end_char}" + if value is None or not valuestr: + line = f"{etype}{end_char}" + else: + line = f"{etype}: {valuestr}{end_char}" return line def _safe_string(value, what, func=str): @@ -273,7 +311,7 @@ class FrameSummary: """ __slots__ = ('filename', 'lineno', 'end_lineno', 'colno', 'end_colno', - 'name', '_line', 'locals') + 'name', '_lines', '_lines_dedented', 'locals') def __init__(self, filename, lineno, name, *, lookup_line=True, locals=None, line=None, @@ -289,15 +327,16 @@ def __init__(self, filename, lineno, name, *, lookup_line=True, """ self.filename = filename self.lineno = lineno + self.end_lineno = lineno if end_lineno is None else end_lineno + self.colno = colno + self.end_colno = end_colno self.name = name - self._line = line + self._lines = line + self._lines_dedented = None if lookup_line: self.line self.locals = {k: _safe_string(v, 'local', func=repr) for k, v in locals.items()} if locals else None - self.end_lineno = end_lineno - self.colno = colno - self.end_colno = end_colno def __eq__(self, other): if isinstance(other, FrameSummary): @@ -322,19 +361,39 @@ def __repr__(self): def __len__(self): return 4 + def _set_lines(self): + if ( + self._lines is None + and self.lineno is not None + and self.end_lineno is not None + ): + lines = [] + for lineno in range(self.lineno, self.end_lineno + 1): + # treat errors (empty string) and empty lines (newline) as the same + lines.append(linecache.getline(self.filename, lineno).rstrip()) + self._lines = "\n".join(lines) + "\n" + @property - def _original_line(self): + def _original_lines(self): # Returns the line as-is from the source, without modifying whitespace. - self.line - return self._line + self._set_lines() + return self._lines + + @property + def _dedented_lines(self): + # Returns _original_lines, but dedented + self._set_lines() + if self._lines_dedented is None and self._lines is not None: + self._lines_dedented = textwrap.dedent(self._lines) + return self._lines_dedented @property def line(self): - if self._line is None: - if self.lineno is None: - return None - self._line = linecache.getline(self.filename, self.lineno) - return self._line.strip() + self._set_lines() + if self._lines is None: + return None + # return only the first line, stripped + return self._lines.partition("\n")[0].strip() def walk_stack(f): @@ -384,6 +443,14 @@ def _get_code_position(code, instruction_index): _RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c. +class _ANSIColors: + RED = '\x1b[31m' + BOLD_RED = '\x1b[1;31m' + MAGENTA = '\x1b[35m' + BOLD_MAGENTA = '\x1b[1;35m' + GREY = '\x1b[90m' + RESET = '\x1b[0m' + class StackSummary(list): """A list of FrameSummary objects, representing a stack of frames.""" @@ -474,75 +541,193 @@ def from_list(klass, a_list): result.append(FrameSummary(filename, lineno, name, line=line)) return result - def format_frame_summary(self, frame_summary): + def format_frame_summary(self, frame_summary, **kwargs): """Format the lines for a single FrameSummary. Returns a string representing one frame involved in the stack. This gets called for every frame to be printed in the stack summary. """ + colorize = kwargs.get("colorize", False) row = [] filename = frame_summary.filename if frame_summary.filename.startswith("-"): filename = "" - row.append(' File "{}", line {}, in {}\n'.format( - filename, frame_summary.lineno, frame_summary.name)) - if frame_summary.line: - stripped_line = frame_summary.line.strip() - row.append(' {}\n'.format(stripped_line)) - - line = frame_summary._original_line - orig_line_len = len(line) - frame_line_len = len(frame_summary.line.lstrip()) - stripped_characters = orig_line_len - frame_line_len + if colorize: + row.append(' File {}"{}"{}, line {}{}{}, in {}{}{}\n'.format( + _ANSIColors.MAGENTA, + filename, + _ANSIColors.RESET, + _ANSIColors.MAGENTA, + frame_summary.lineno, + _ANSIColors.RESET, + _ANSIColors.MAGENTA, + frame_summary.name, + _ANSIColors.RESET, + ) + ) + else: + row.append(' File "{}", line {}, in {}\n'.format( + filename, frame_summary.lineno, frame_summary.name)) + if frame_summary._dedented_lines and frame_summary._dedented_lines.strip(): if ( - frame_summary.colno is not None - and frame_summary.end_colno is not None + frame_summary.colno is None or + frame_summary.end_colno is None ): - start_offset = _byte_offset_to_character_offset( - line, frame_summary.colno) - end_offset = _byte_offset_to_character_offset( - line, frame_summary.end_colno) - code_segment = line[start_offset:end_offset] + # only output first line if column information is missing + row.append(textwrap.indent(frame_summary.line, ' ') + "\n") + else: + # get first and last line + all_lines_original = frame_summary._original_lines.splitlines() + first_line = all_lines_original[0] + # assume all_lines_original has enough lines (since we constructed it) + last_line = all_lines_original[frame_summary.end_lineno - frame_summary.lineno] + + # character index of the start/end of the instruction + start_offset = _byte_offset_to_character_offset(first_line, frame_summary.colno) + end_offset = _byte_offset_to_character_offset(last_line, frame_summary.end_colno) + + all_lines = frame_summary._dedented_lines.splitlines()[ + :frame_summary.end_lineno - frame_summary.lineno + 1 + ] + + # adjust start/end offset based on dedent + dedent_characters = len(first_line) - len(all_lines[0]) + start_offset = max(0, start_offset - dedent_characters) + end_offset = max(0, end_offset - dedent_characters) + # When showing this on a terminal, some of the non-ASCII characters + # might be rendered as double-width characters, so we need to take + # that into account when calculating the length of the line. + dp_start_offset = _display_width(all_lines[0], offset=start_offset) + dp_end_offset = _display_width(all_lines[-1], offset=end_offset) + + # get exact code segment corresponding to the instruction + segment = "\n".join(all_lines) + segment = segment[start_offset:len(segment) - (len(all_lines[-1]) - end_offset)] + + # attempt to parse for anchors anchors = None - if frame_summary.lineno == frame_summary.end_lineno: - with suppress(Exception): - anchors = _extract_caret_anchors_from_line_segment(code_segment) - else: - # Don't count the newline since the anchors only need to - # go up until the last character of the line. - end_offset = len(line.rstrip()) - - # show indicators if primary char doesn't span the frame line - if end_offset - start_offset < len(stripped_line) or ( - anchors and anchors.right_start_offset - anchors.left_end_offset > 0): - # When showing this on a terminal, some of the non-ASCII characters - # might be rendered as double-width characters, so we need to take - # that into account when calculating the length of the line. - dp_start_offset = _display_width(line, start_offset) + 1 - dp_end_offset = _display_width(line, end_offset) + 1 - - row.append(' ') - row.append(' ' * (dp_start_offset - stripped_characters)) - - if anchors: - dp_left_end_offset = _display_width(code_segment, anchors.left_end_offset) - dp_right_start_offset = _display_width(code_segment, anchors.right_start_offset) - row.append(anchors.primary_char * dp_left_end_offset) - row.append(anchors.secondary_char * (dp_right_start_offset - dp_left_end_offset)) - row.append(anchors.primary_char * (dp_end_offset - dp_start_offset - dp_right_start_offset)) - else: - row.append('^' * (dp_end_offset - dp_start_offset)) + with suppress(Exception): + anchors = _extract_caret_anchors_from_line_segment(segment) + + # only use carets if there are anchors or the carets do not span all lines + show_carets = False + if anchors or all_lines[0][:start_offset].lstrip() or all_lines[-1][end_offset:].rstrip(): + show_carets = True + + result = [] + + # only display first line, last line, and lines around anchor start/end + significant_lines = {0, len(all_lines) - 1} + + anchors_left_end_offset = 0 + anchors_right_start_offset = 0 + primary_char = "^" + secondary_char = "^" + if anchors: + anchors_left_end_offset = anchors.left_end_offset + anchors_right_start_offset = anchors.right_start_offset + # computed anchor positions do not take start_offset into account, + # so account for it here + if anchors.left_end_lineno == 0: + anchors_left_end_offset += start_offset + if anchors.right_start_lineno == 0: + anchors_right_start_offset += start_offset + + # account for display width + anchors_left_end_offset = _display_width( + all_lines[anchors.left_end_lineno], offset=anchors_left_end_offset + ) + anchors_right_start_offset = _display_width( + all_lines[anchors.right_start_lineno], offset=anchors_right_start_offset + ) - row.append('\n') + primary_char = anchors.primary_char + secondary_char = anchors.secondary_char + significant_lines.update( + range(anchors.left_end_lineno - 1, anchors.left_end_lineno + 2) + ) + significant_lines.update( + range(anchors.right_start_lineno - 1, anchors.right_start_lineno + 2) + ) + # remove bad line numbers + significant_lines.discard(-1) + significant_lines.discard(len(all_lines)) + + def output_line(lineno): + """output all_lines[lineno] along with carets""" + result.append(all_lines[lineno] + "\n") + if not show_carets: + return + num_spaces = len(all_lines[lineno]) - len(all_lines[lineno].lstrip()) + carets = [] + num_carets = dp_end_offset if lineno == len(all_lines) - 1 else _display_width(all_lines[lineno]) + # compute caret character for each position + for col in range(num_carets): + if col < num_spaces or (lineno == 0 and col < dp_start_offset): + # before first non-ws char of the line, or before start of instruction + carets.append(' ') + elif anchors and ( + lineno > anchors.left_end_lineno or + (lineno == anchors.left_end_lineno and col >= anchors_left_end_offset) + ) and ( + lineno < anchors.right_start_lineno or + (lineno == anchors.right_start_lineno and col < anchors_right_start_offset) + ): + # within anchors + carets.append(secondary_char) + else: + carets.append(primary_char) + if colorize: + # Replace the previous line with a red version of it only in the parts covered + # by the carets. + line = result[-1] + colorized_line_parts = [] + colorized_carets_parts = [] + + for color, group in itertools.groupby(itertools.zip_longest(line, carets, fillvalue=""), key=lambda x: x[1]): + caret_group = list(group) + if color == "^": + colorized_line_parts.append(_ANSIColors.BOLD_RED + "".join(char for char, _ in caret_group) + _ANSIColors.RESET) + colorized_carets_parts.append(_ANSIColors.BOLD_RED + "".join(caret for _, caret in caret_group) + _ANSIColors.RESET) + elif color == "~": + colorized_line_parts.append(_ANSIColors.RED + "".join(char for char, _ in caret_group) + _ANSIColors.RESET) + colorized_carets_parts.append(_ANSIColors.RED + "".join(caret for _, caret in caret_group) + _ANSIColors.RESET) + else: + colorized_line_parts.append("".join(char for char, _ in caret_group)) + colorized_carets_parts.append("".join(caret for _, caret in caret_group)) + + colorized_line = "".join(colorized_line_parts) + colorized_carets = "".join(colorized_carets_parts) + result[-1] = colorized_line + result.append(colorized_carets + "\n") + else: + result.append("".join(carets) + "\n") + + # display significant lines + sig_lines_list = sorted(significant_lines) + for i, lineno in enumerate(sig_lines_list): + if i: + linediff = lineno - sig_lines_list[i - 1] + if linediff == 2: + # 1 line in between - just output it + output_line(lineno - 1) + elif linediff > 2: + # > 1 line in between - abbreviate + result.append(f"...<{linediff - 1} lines>...\n") + output_line(lineno) + + row.append( + textwrap.indent(textwrap.dedent("".join(result)), ' ', lambda line: True) + ) if frame_summary.locals: for name, value in sorted(frame_summary.locals.items()): row.append(' {name} = {value}\n'.format(name=name, value=value)) return ''.join(row) - def format(self): + def format(self, **kwargs): """Format the stack ready for printing. Returns a list of strings ready for printing. Each string in the @@ -554,13 +739,14 @@ def format(self): repetitions are shown, followed by a summary line stating the exact number of further repetitions. """ + colorize = kwargs.get("colorize", False) result = [] last_file = None last_line = None last_name = None count = 0 for frame_summary in self: - formatted_frame = self.format_frame_summary(frame_summary) + formatted_frame = self.format_frame_summary(frame_summary, colorize=colorize) if formatted_frame is None: continue if (last_file is None or last_file != frame_summary.filename or @@ -598,7 +784,9 @@ def _byte_offset_to_character_offset(str, offset): _Anchors = collections.namedtuple( "_Anchors", [ + "left_end_lineno", "left_end_offset", + "right_start_lineno", "right_start_offset", "primary_char", "secondary_char", @@ -607,59 +795,161 @@ def _byte_offset_to_character_offset(str, offset): ) def _extract_caret_anchors_from_line_segment(segment): + """ + Given source code `segment` corresponding to a FrameSummary, determine: + - for binary ops, the location of the binary op + - for indexing and function calls, the location of the brackets. + `segment` is expected to be a valid Python expression. + """ import ast try: - tree = ast.parse(segment) + # Without parentheses, `segment` is parsed as a statement. + # Binary ops, subscripts, and calls are expressions, so + # we can wrap them with parentheses to parse them as + # (possibly multi-line) expressions. + # e.g. if we try to highlight the addition in + # x = ( + # a + + # b + # ) + # then we would ast.parse + # a + + # b + # which is not a valid statement because of the newline. + # Adding brackets makes it a valid expression. + # ( + # a + + # b + # ) + # Line locations will be different than the original, + # which is taken into account later on. + tree = ast.parse(f"(\n{segment}\n)") except SyntaxError: return None if len(tree.body) != 1: return None - normalize = lambda offset: _byte_offset_to_character_offset(segment, offset) + lines = segment.splitlines() + + def normalize(lineno, offset): + """Get character index given byte offset""" + return _byte_offset_to_character_offset(lines[lineno], offset) + + def next_valid_char(lineno, col): + """Gets the next valid character index in `lines`, if + the current location is not valid. Handles empty lines. + """ + while lineno < len(lines) and col >= len(lines[lineno]): + col = 0 + lineno += 1 + assert lineno < len(lines) and col < len(lines[lineno]) + return lineno, col + + def increment(lineno, col): + """Get the next valid character index in `lines`.""" + col += 1 + lineno, col = next_valid_char(lineno, col) + return lineno, col + + def nextline(lineno, col): + """Get the next valid character at least on the next line""" + col = 0 + lineno += 1 + lineno, col = next_valid_char(lineno, col) + return lineno, col + + def increment_until(lineno, col, stop): + """Get the next valid non-"\\#" character that satisfies the `stop` predicate""" + while True: + ch = lines[lineno][col] + if ch in "\\#": + lineno, col = nextline(lineno, col) + elif not stop(ch): + lineno, col = increment(lineno, col) + else: + break + return lineno, col + + def setup_positions(expr, force_valid=True): + """Get the lineno/col position of the end of `expr`. If `force_valid` is True, + forces the position to be a valid character (e.g. if the position is beyond the + end of the line, move to the next line) + """ + # -2 since end_lineno is 1-indexed and because we added an extra + # bracket + newline to `segment` when calling ast.parse + lineno = expr.end_lineno - 2 + col = normalize(lineno, expr.end_col_offset) + return next_valid_char(lineno, col) if force_valid else (lineno, col) + statement = tree.body[0] match statement: case ast.Expr(expr): match expr: case ast.BinOp(): - operator_start = normalize(expr.left.end_col_offset) - operator_end = normalize(expr.right.col_offset) - operator_str = segment[operator_start:operator_end] - operator_offset = len(operator_str) - len(operator_str.lstrip()) + # ast gives these locations for BinOp subexpressions + # ( left_expr ) + ( right_expr ) + # left^^^^^ right^^^^^ + lineno, col = setup_positions(expr.left) + + # First operator character is the first non-space/')' character + lineno, col = increment_until(lineno, col, lambda x: not x.isspace() and x != ')') - left_anchor = expr.left.end_col_offset + operator_offset - right_anchor = left_anchor + 1 + # binary op is 1 or 2 characters long, on the same line, + # before the right subexpression + right_col = col + 1 if ( - operator_offset + 1 < len(operator_str) - and not operator_str[operator_offset + 1].isspace() + right_col < len(lines[lineno]) + and ( + # operator char should not be in the right subexpression + expr.right.lineno - 2 > lineno or + right_col < normalize(expr.right.lineno - 2, expr.right.col_offset) + ) + and not (ch := lines[lineno][right_col]).isspace() + and ch not in "\\#" ): - right_anchor += 1 + right_col += 1 - while left_anchor < len(segment) and ((ch := segment[left_anchor]).isspace() or ch in ")#"): - left_anchor += 1 - right_anchor += 1 - return _Anchors(normalize(left_anchor), normalize(right_anchor)) + # right_col can be invalid since it is exclusive + return _Anchors(lineno, col, lineno, right_col) case ast.Subscript(): - left_anchor = normalize(expr.value.end_col_offset) - right_anchor = normalize(expr.slice.end_col_offset + 1) - while left_anchor < len(segment) and ((ch := segment[left_anchor]).isspace() or ch != "["): - left_anchor += 1 - while right_anchor < len(segment) and ((ch := segment[right_anchor]).isspace() or ch != "]"): - right_anchor += 1 - if right_anchor < len(segment): - right_anchor += 1 - return _Anchors(left_anchor, right_anchor) + # ast gives these locations for value and slice subexpressions + # ( value_expr ) [ slice_expr ] + # value^^^^^ slice^^^^^ + # subscript^^^^^^^^^^^^^^^^^^^^ + + # find left bracket + left_lineno, left_col = setup_positions(expr.value) + left_lineno, left_col = increment_until(left_lineno, left_col, lambda x: x == '[') + # find right bracket (final character of expression) + right_lineno, right_col = setup_positions(expr, force_valid=False) + return _Anchors(left_lineno, left_col, right_lineno, right_col) + case ast.Call(): + # ast gives these locations for function call expressions + # ( func_expr ) (args, kwargs) + # func^^^^^ + # call^^^^^^^^^^^^^^^^^^^^^^^^ + + # find left bracket + left_lineno, left_col = setup_positions(expr.func) + left_lineno, left_col = increment_until(left_lineno, left_col, lambda x: x == '(') + # find right bracket (final character of expression) + right_lineno, right_col = setup_positions(expr, force_valid=False) + return _Anchors(left_lineno, left_col, right_lineno, right_col) return None _WIDE_CHAR_SPECIFIERS = "WF" -def _display_width(line, offset): +def _display_width(line, offset=None): """Calculate the extra amount of width space the given source code segment might take if it were to be displayed on a fixed width output device. Supports wide unicode characters and emojis.""" + if offset is None: + offset = len(line) + # Fast track for ASCII-only strings if line.isascii(): return offset @@ -719,7 +1009,8 @@ class TracebackException: - :attr:`__suppress_context__` The *__suppress_context__* value from the original exception. - :attr:`stack` A `StackSummary` representing the traceback. - - :attr:`exc_type` The class of the original traceback. + - :attr:`exc_type` (deprecated) The class of the original traceback. + - :attr:`exc_type_str` String display of exc_type - :attr:`filename` For syntax errors - the filename where the error occurred. - :attr:`lineno` For syntax errors - the linenumber where the error @@ -737,7 +1028,7 @@ class TracebackException: def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, lookup_lines=True, capture_locals=False, compact=False, - max_group_width=15, max_group_depth=10, _seen=None): + max_group_width=15, max_group_depth=10, save_exc_type=True, _seen=None): # NB: we need to accept exc_traceback, exc_value, exc_traceback to # permit backwards compat with the existing API, otherwise we # need stub thunk objects just to glue it together. @@ -754,12 +1045,23 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, _walk_tb_with_full_positions(exc_traceback), limit=limit, lookup_lines=lookup_lines, capture_locals=capture_locals) - self.exc_type = exc_type + + self._exc_type = exc_type if save_exc_type else None + # Capture now to permit freeing resources: only complication is in the # unofficial API _format_final_exc_line self._str = _safe_string(exc_value, 'exception') self.__notes__ = getattr(exc_value, '__notes__', None) + self._is_syntax_error = False + self._have_exc_type = exc_type is not None + if exc_type is not None: + self.exc_type_qualname = exc_type.__qualname__ + self.exc_type_module = exc_type.__module__ + else: + self.exc_type_qualname = None + self.exc_type_module = None + if exc_type and issubclass(exc_type, SyntaxError): # Handle SyntaxError's specially self.filename = exc_value.filename @@ -771,6 +1073,7 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, self.offset = exc_value.offset self.end_offset = exc_value.end_offset self.msg = exc_value.msg + self._is_syntax_error = True elif exc_type and issubclass(exc_type, ImportError) and \ getattr(exc_value, "name_from", None) is not None: wrong_name = getattr(exc_value, "name_from", None) @@ -869,6 +1172,24 @@ def from_exception(cls, exc, *args, **kwargs): """Create a TracebackException from an exception.""" return cls(type(exc), exc, exc.__traceback__, *args, **kwargs) + @property + def exc_type(self): + warnings.warn('Deprecated in 3.13. Use exc_type_str instead.', + DeprecationWarning, stacklevel=2) + return self._exc_type + + @property + def exc_type_str(self): + if not self._have_exc_type: + return None + stype = self.exc_type_qualname + smod = self.exc_type_module + if smod not in ("__main__", "builtins"): + if not isinstance(smod, str): + smod = "" + stype = smod + '.' + stype + return stype + def _load_lines(self): """Private API. force all lines in the stack to be loaded.""" for frame in self.stack: @@ -882,7 +1203,7 @@ def __eq__(self, other): def __str__(self): return self._str - def format_exception_only(self, *, show_group=False, _depth=0): + def format_exception_only(self, *, show_group=False, _depth=0, **kwargs): """Format the exception part of the traceback. The return value is a generator of strings, each ending in a newline. @@ -899,33 +1220,28 @@ def format_exception_only(self, *, show_group=False, _depth=0): :exc:`BaseExceptionGroup`, the nested exceptions are included as well, recursively, with indentation relative to their nesting depth. """ + colorize = kwargs.get("colorize", False) indent = 3 * _depth * ' ' - if self.exc_type is None: - yield indent + _format_final_exc_line(None, self._str) + if not self._have_exc_type: + yield indent + _format_final_exc_line(None, self._str, colorize=colorize) return - stype = self.exc_type.__qualname__ - smod = self.exc_type.__module__ - if smod not in ("__main__", "builtins"): - if not isinstance(smod, str): - smod = "" - stype = smod + '.' + stype - - if not issubclass(self.exc_type, SyntaxError): + stype = self.exc_type_str + if not self._is_syntax_error: if _depth > 0: # Nested exceptions needs correct handling of multiline messages. formatted = _format_final_exc_line( - stype, self._str, insert_final_newline=False, + stype, self._str, insert_final_newline=False, colorize=colorize ).split('\n') yield from [ indent + l + '\n' for l in formatted ] else: - yield _format_final_exc_line(stype, self._str) + yield _format_final_exc_line(stype, self._str, colorize=colorize) else: - yield from [indent + l for l in self._format_syntax_error(stype)] + yield from [indent + l for l in self._format_syntax_error(stype, colorize=colorize)] if ( isinstance(self.__notes__, collections.abc.Sequence) @@ -939,15 +1255,26 @@ def format_exception_only(self, *, show_group=False, _depth=0): if self.exceptions and show_group: for ex in self.exceptions: - yield from ex.format_exception_only(show_group=show_group, _depth=_depth+1) + yield from ex.format_exception_only(show_group=show_group, _depth=_depth+1, colorize=colorize) - def _format_syntax_error(self, stype): + def _format_syntax_error(self, stype, **kwargs): """Format SyntaxError exceptions (internal helper).""" # Show exactly where the problem was found. + colorize = kwargs.get("colorize", False) filename_suffix = '' if self.lineno is not None: - yield ' File "{}", line {}\n'.format( - self.filename or "", self.lineno) + if colorize: + yield ' File {}"{}"{}, line {}{}{}\n'.format( + _ANSIColors.MAGENTA, + self.filename or "", + _ANSIColors.RESET, + _ANSIColors.MAGENTA, + self.lineno, + _ANSIColors.RESET, + ) + else: + yield ' File "{}", line {}\n'.format( + self.filename or "", self.lineno) elif self.filename is not None: filename_suffix = ' ({})'.format(self.filename) @@ -959,9 +1286,9 @@ def _format_syntax_error(self, stype): rtext = text.rstrip('\n') ltext = rtext.lstrip(' \n\f') spaces = len(rtext) - len(ltext) - yield ' {}\n'.format(ltext) - - if self.offset is not None: + if self.offset is None: + yield ' {}\n'.format(ltext) + else: offset = self.offset end_offset = self.end_offset if self.end_offset not in {None, 0} else offset if self.text and offset > len(self.text): @@ -974,14 +1301,43 @@ def _format_syntax_error(self, stype): # Convert 1-based column offset to 0-based index into stripped text colno = offset - 1 - spaces end_colno = end_offset - 1 - spaces + caretspace = ' ' if colno >= 0: # non-space whitespace (likes tabs) must be kept for alignment caretspace = ((c if c.isspace() else ' ') for c in ltext[:colno]) - yield ' {}{}'.format("".join(caretspace), ('^' * (end_colno - colno) + "\n")) + start_color = end_color = "" + if colorize: + # colorize from colno to end_colno + ltext = ( + ltext[:colno] + + _ANSIColors.BOLD_RED + ltext[colno:end_colno] + _ANSIColors.RESET + + ltext[end_colno:] + ) + start_color = _ANSIColors.BOLD_RED + end_color = _ANSIColors.RESET + yield ' {}\n'.format(ltext) + yield ' {}{}{}{}\n'.format( + "".join(caretspace), + start_color, + ('^' * (end_colno - colno)), + end_color, + ) + else: + yield ' {}\n'.format(ltext) msg = self.msg or "" - yield "{}: {}{}\n".format(stype, msg, filename_suffix) + if colorize: + yield "{}{}{}: {}{}{}{}\n".format( + _ANSIColors.BOLD_MAGENTA, + stype, + _ANSIColors.RESET, + _ANSIColors.MAGENTA, + msg, + _ANSIColors.RESET, + filename_suffix) + else: + yield "{}: {}{}\n".format(stype, msg, filename_suffix) - def format(self, *, chain=True, _ctx=None): + def format(self, *, chain=True, _ctx=None, **kwargs): """Format the exception. If chain is not *True*, *__cause__* and *__context__* will not be formatted. @@ -993,7 +1349,7 @@ def format(self, *, chain=True, _ctx=None): The message indicating which exception occurred is always the last string in the output. """ - + colorize = kwargs.get("colorize", False) if _ctx is None: _ctx = _ExceptionPrintContext() @@ -1023,8 +1379,8 @@ def format(self, *, chain=True, _ctx=None): if exc.exceptions is None: if exc.stack: yield from _ctx.emit('Traceback (most recent call last):\n') - yield from _ctx.emit(exc.stack.format()) - yield from _ctx.emit(exc.format_exception_only()) + yield from _ctx.emit(exc.stack.format(colorize=colorize)) + yield from _ctx.emit(exc.format_exception_only(colorize=colorize)) elif _ctx.exception_group_depth > self.max_group_depth: # exception group, but depth exceeds limit yield from _ctx.emit( @@ -1039,9 +1395,9 @@ def format(self, *, chain=True, _ctx=None): yield from _ctx.emit( 'Exception Group Traceback (most recent call last):\n', margin_char = '+' if is_toplevel else None) - yield from _ctx.emit(exc.stack.format()) + yield from _ctx.emit(exc.stack.format(colorize=colorize)) - yield from _ctx.emit(exc.format_exception_only()) + yield from _ctx.emit(exc.format_exception_only(colorize=colorize)) num_excs = len(exc.exceptions) if num_excs <= self.max_group_width: n = num_excs @@ -1082,11 +1438,12 @@ def format(self, *, chain=True, _ctx=None): _ctx.exception_group_depth = 0 - def print(self, *, file=None, chain=True): + def print(self, *, file=None, chain=True, **kwargs): """Print the result of self.format(chain=chain) to 'file'.""" + colorize = kwargs.get("colorize", False) if file is None: file = sys.stderr - for line in self.format(chain=chain): + for line in self.format(chain=chain, colorize=colorize): print(line, file=file, end="") diff --git a/Lib/typing.py b/Lib/typing.py index a96c7083eb785e..61b88a560e9dc5 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -490,7 +490,7 @@ def __getitem__(self, parameters): return self._getitem(self, parameters) -class _LiteralSpecialForm(_SpecialForm, _root=True): +class _TypedCacheSpecialForm(_SpecialForm, _root=True): def __getitem__(self, parameters): if not isinstance(parameters, tuple): parameters = (parameters,) @@ -723,7 +723,7 @@ def Optional(self, parameters): arg = _type_check(parameters, f"{self} requires a single type.") return Union[arg, type(None)] -@_LiteralSpecialForm +@_TypedCacheSpecialForm @_tp_cache(typed=True) def Literal(self, *parameters): """Special typing form to define literal types (a.k.a. value types). @@ -1782,6 +1782,31 @@ def _pickle_pskwargs(pskwargs): del _pickle_psargs, _pickle_pskwargs +# Preload these once, as globals, as a micro-optimisation. +# This makes a significant difference to the time it takes +# to do `isinstance()`/`issubclass()` checks +# against runtime-checkable protocols with only one callable member. +_abc_instancecheck = ABCMeta.__instancecheck__ +_abc_subclasscheck = ABCMeta.__subclasscheck__ + + +def _type_check_issubclass_arg_1(arg): + """Raise TypeError if `arg` is not an instance of `type` + in `issubclass(arg, )`. + + In most cases, this is verified by type.__subclasscheck__. + Checking it again unnecessarily would slow down issubclass() checks, + so, we don't perform this check unless we absolutely have to. + + For various error paths, however, + we want to ensure that *this* error message is shown to the user + where relevant, rather than a typing.py-specific error message. + """ + if not isinstance(arg, type): + # Same error message as for issubclass(1, int). + raise TypeError('issubclass() arg 1 must be a class') + + class _ProtocolMeta(ABCMeta): # This metaclass is somewhat unfortunate, # but is necessary for several reasons... @@ -1821,22 +1846,26 @@ def __subclasscheck__(cls, other): getattr(cls, '_is_protocol', False) and not _allow_reckless_class_checks() ): - if not isinstance(other, type): - # Same error message as for issubclass(1, int). - raise TypeError('issubclass() arg 1 must be a class') if ( not cls.__callable_proto_members_only__ and cls.__dict__.get("__subclasshook__") is _proto_hook ): + _type_check_issubclass_arg_1(other) + non_method_attrs = sorted( + attr for attr in cls.__protocol_attrs__ + if not callable(getattr(cls, attr, None)) + ) raise TypeError( - "Protocols with non-method members don't support issubclass()" + "Protocols with non-method members don't support issubclass()." + f" Non-method members: {str(non_method_attrs)[1:-1]}." ) if not getattr(cls, '_is_runtime_protocol', False): + _type_check_issubclass_arg_1(other) raise TypeError( "Instance and class checks can only be used with " "@runtime_checkable protocols" ) - return super().__subclasscheck__(other) + return _abc_subclasscheck(cls, other) def __instancecheck__(cls, instance): # We need this method for situations where attributes are @@ -1845,7 +1874,7 @@ def __instancecheck__(cls, instance): return type.__instancecheck__(cls, instance) if not getattr(cls, "_is_protocol", False): # i.e., it's a concrete subclass of a protocol - return super().__instancecheck__(instance) + return _abc_instancecheck(cls, instance) if ( not getattr(cls, '_is_runtime_protocol', False) and @@ -1854,7 +1883,7 @@ def __instancecheck__(cls, instance): raise TypeError("Instance and class checks can only be used with" " @runtime_checkable protocols") - if super().__instancecheck__(instance): + if _abc_instancecheck(cls, instance): return True getattr_static = _lazy_load_getattr_static() @@ -2000,8 +2029,9 @@ def __mro_entries__(self, bases): return (self.__origin__,) -@_SpecialForm -def Annotated(self, params): +@_TypedCacheSpecialForm +@_tp_cache(typed=True) +def Annotated(self, *params): """Add context-specific metadata to a type. Example: Annotated[int, runtime_check.Unsigned] indicates to the @@ -2048,7 +2078,7 @@ def Annotated(self, params): where T1, T2 etc. are TypeVars, which would be invalid, because only one type should be passed to Annotated. """ - if not isinstance(params, tuple) or len(params) < 2: + if len(params) < 2: raise TypeError("Annotated[...] should be used " "with at least two arguments (a type and an " "annotation).") @@ -2738,11 +2768,26 @@ def __new__(cls, typename, bases, ns): class_getitem = _generic_class_getitem nm_tpl.__class_getitem__ = classmethod(class_getitem) # update from user namespace without overriding special namedtuple attributes - for key in ns: + for key, val in ns.items(): if key in _prohibited: raise AttributeError("Cannot overwrite NamedTuple attribute " + key) - elif key not in _special and key not in nm_tpl._fields: - setattr(nm_tpl, key, ns[key]) + elif key not in _special: + if key not in nm_tpl._fields: + setattr(nm_tpl, key, val) + try: + set_name = type(val).__set_name__ + except AttributeError: + pass + else: + try: + set_name(val, nm_tpl, key) + except BaseException as e: + e.add_note( + f"Error calling __set_name__ on {type(val).__name__!r} " + f"instance {key!r} in {typename!r}" + ) + raise + if Generic in bases: nm_tpl.__init_subclass__() return nm_tpl @@ -2864,8 +2909,14 @@ def __new__(cls, name, bases, ns, total=True): for base in bases: annotations.update(base.__dict__.get('__annotations__', {})) - required_keys.update(base.__dict__.get('__required_keys__', ())) - optional_keys.update(base.__dict__.get('__optional_keys__', ())) + + base_required = base.__dict__.get('__required_keys__', set()) + required_keys |= base_required + optional_keys -= base_required + + base_optional = base.__dict__.get('__optional_keys__', set()) + required_keys -= base_optional + optional_keys |= base_optional annotations.update(own_annotations) for annotation_key, annotation_type in own_annotations.items(): @@ -2877,14 +2928,23 @@ def __new__(cls, name, bases, ns, total=True): annotation_origin = get_origin(annotation_type) if annotation_origin is Required: - required_keys.add(annotation_key) + is_required = True elif annotation_origin is NotRequired: - optional_keys.add(annotation_key) - elif total: + is_required = False + else: + is_required = total + + if is_required: required_keys.add(annotation_key) + optional_keys.discard(annotation_key) else: optional_keys.add(annotation_key) + required_keys.discard(annotation_key) + assert required_keys.isdisjoint(optional_keys), ( + f"Required keys overlap with optional keys in {name}:" + f" {required_keys=}, {optional_keys=}" + ) tp_dict.__annotations__ = annotations tp_dict.__required_keys__ = frozenset(required_keys) tp_dict.__optional_keys__ = frozenset(optional_keys) diff --git a/Lib/venv/scripts/common/activate b/Lib/venv/scripts/common/activate index 458740a35b0d20..6fdf423a1c516a 100644 --- a/Lib/venv/scripts/common/activate +++ b/Lib/venv/scripts/common/activate @@ -14,12 +14,9 @@ deactivate () { unset _OLD_VIRTUAL_PYTHONHOME fi - # This should detect bash and zsh, which have a hash command that must - # be called to get it to forget past commands. Without forgetting + # Call hash to forget past commands. Without forgetting # past commands the $PATH changes we made may not be respected - if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then - hash -r 2> /dev/null - fi + hash -r 2> /dev/null if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then PS1="${_OLD_VIRTUAL_PS1:-}" @@ -39,7 +36,7 @@ deactivate () { deactivate nondestructive # on Windows, a path can contain colons and backslashes and has to be converted: -if [ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ] ; then +if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then # transform D:\path\to\venv to /d/path/to/venv on MSYS # and to /cygdrive/d/path/to/venv on Cygwin export VIRTUAL_ENV=$(cygpath "__VENV_DIR__") @@ -69,9 +66,6 @@ if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then export PS1 fi -# This should detect bash and zsh, which have a hash command that must -# be called to get it to forget past commands. Without forgetting +# Call hash to forget past commands. Without forgetting # past commands the $PATH changes we made may not be respected -if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then - hash -r 2> /dev/null -fi +hash -r 2> /dev/null diff --git a/Lib/warnings.py b/Lib/warnings.py index 32e58072b9cc33..b8ff078569d2ce 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -5,7 +5,7 @@ __all__ = ["warn", "warn_explicit", "showwarning", "formatwarning", "filterwarnings", "simplefilter", - "resetwarnings", "catch_warnings"] + "resetwarnings", "catch_warnings", "deprecated"] def showwarning(message, category, filename, lineno, file=None, line=None): """Hook to write a warning to a file; replace if you like.""" @@ -139,14 +139,18 @@ def filterwarnings(action, message="", category=Warning, module="", lineno=0, 'lineno' -- an integer line number, 0 matches all warnings 'append' -- if true, append to the list of filters """ - assert action in ("error", "ignore", "always", "default", "module", - "once"), "invalid action: %r" % (action,) - assert isinstance(message, str), "message must be a string" - assert isinstance(category, type), "category must be a class" - assert issubclass(category, Warning), "category must be a Warning subclass" - assert isinstance(module, str), "module must be a string" - assert isinstance(lineno, int) and lineno >= 0, \ - "lineno must be an int >= 0" + if action not in {"error", "ignore", "always", "default", "module", "once"}: + raise ValueError(f"invalid action: {action!r}") + if not isinstance(message, str): + raise TypeError("message must be a string") + if not isinstance(category, type) or not issubclass(category, Warning): + raise TypeError("category must be a Warning subclass") + if not isinstance(module, str): + raise TypeError("module must be a string") + if not isinstance(lineno, int): + raise TypeError("lineno must be an int") + if lineno < 0: + raise ValueError("lineno must be an int >= 0") if message or module: import re @@ -172,10 +176,12 @@ def simplefilter(action, category=Warning, lineno=0, append=False): 'lineno' -- an integer line number, 0 matches all warnings 'append' -- if true, append to the list of filters """ - assert action in ("error", "ignore", "always", "default", "module", - "once"), "invalid action: %r" % (action,) - assert isinstance(lineno, int) and lineno >= 0, \ - "lineno must be an int >= 0" + if action not in {"error", "ignore", "always", "default", "module", "once"}: + raise ValueError(f"invalid action: {action!r}") + if not isinstance(lineno, int): + raise TypeError("lineno must be an int") + if lineno < 0: + raise ValueError("lineno must be an int >= 0") _add_filter(action, None, category, None, lineno, append=append) def _add_filter(*item, append): @@ -508,6 +514,135 @@ def __exit__(self, *exc_info): self._module._showwarnmsg_impl = self._showwarnmsg_impl +class deprecated: + """Indicate that a class, function or overload is deprecated. + + When this decorator is applied to an object, the type checker + will generate a diagnostic on usage of the deprecated object. + + Usage: + + @deprecated("Use B instead") + class A: + pass + + @deprecated("Use g instead") + def f(): + pass + + @overload + @deprecated("int support is deprecated") + def g(x: int) -> int: ... + @overload + def g(x: str) -> int: ... + + The warning specified by *category* will be emitted at runtime + on use of deprecated objects. For functions, that happens on calls; + for classes, on instantiation and on creation of subclasses. + If the *category* is ``None``, no warning is emitted at runtime. + The *stacklevel* determines where the + warning is emitted. If it is ``1`` (the default), the warning + is emitted at the direct caller of the deprecated object; if it + is higher, it is emitted further up the stack. + Static type checker behavior is not affected by the *category* + and *stacklevel* arguments. + + The deprecation message passed to the decorator is saved in the + ``__deprecated__`` attribute on the decorated object. + If applied to an overload, the decorator + must be after the ``@overload`` decorator for the attribute to + exist on the overload as returned by ``get_overloads()``. + + See PEP 702 for details. + + """ + def __init__( + self, + message: str, + /, + *, + category: type[Warning] | None = DeprecationWarning, + stacklevel: int = 1, + ) -> None: + if not isinstance(message, str): + raise TypeError( + f"Expected an object of type str for 'message', not {type(message).__name__!r}" + ) + self.message = message + self.category = category + self.stacklevel = stacklevel + + def __call__(self, arg, /): + # Make sure the inner functions created below don't + # retain a reference to self. + msg = self.message + category = self.category + stacklevel = self.stacklevel + if category is None: + arg.__deprecated__ = msg + return arg + elif isinstance(arg, type): + import functools + from types import MethodType + + original_new = arg.__new__ + + @functools.wraps(original_new) + def __new__(cls, *args, **kwargs): + if cls is arg: + warn(msg, category=category, stacklevel=stacklevel + 1) + if original_new is not object.__new__: + return original_new(cls, *args, **kwargs) + # Mirrors a similar check in object.__new__. + elif cls.__init__ is object.__init__ and (args or kwargs): + raise TypeError(f"{cls.__name__}() takes no arguments") + else: + return original_new(cls) + + arg.__new__ = staticmethod(__new__) + + original_init_subclass = arg.__init_subclass__ + # We need slightly different behavior if __init_subclass__ + # is a bound method (likely if it was implemented in Python) + if isinstance(original_init_subclass, MethodType): + original_init_subclass = original_init_subclass.__func__ + + @functools.wraps(original_init_subclass) + def __init_subclass__(*args, **kwargs): + warn(msg, category=category, stacklevel=stacklevel + 1) + return original_init_subclass(*args, **kwargs) + + arg.__init_subclass__ = classmethod(__init_subclass__) + # Or otherwise, which likely means it's a builtin such as + # object's implementation of __init_subclass__. + else: + @functools.wraps(original_init_subclass) + def __init_subclass__(*args, **kwargs): + warn(msg, category=category, stacklevel=stacklevel + 1) + return original_init_subclass(*args, **kwargs) + + arg.__init_subclass__ = __init_subclass__ + + arg.__deprecated__ = __new__.__deprecated__ = msg + __init_subclass__.__deprecated__ = msg + return arg + elif callable(arg): + import functools + + @functools.wraps(arg) + def wrapper(*args, **kwargs): + warn(msg, category=category, stacklevel=stacklevel + 1) + return arg(*args, **kwargs) + + arg.__deprecated__ = wrapper.__deprecated__ = msg + return wrapper + else: + raise TypeError( + "@deprecated decorator with non-None category must be applied to " + f"a class or callable, not {arg!r}" + ) + + _DEPRECATED_MSG = "{name!r} is deprecated and slated for removal in Python {remove}" def _deprecated(name, message=_DEPRECATED_MSG, *, remove, _version=sys.version_info): diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py index 2b28a079dbaa95..fe629ed1cf2fc5 100644 --- a/Lib/zipfile/__init__.py +++ b/Lib/zipfile/__init__.py @@ -2227,12 +2227,79 @@ def _compile(file, optimize=-1): return (fname, archivename) +def main(args=None): + import argparse + + description = 'A simple command-line interface for zipfile module.' + parser = argparse.ArgumentParser(description=description) + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-l', '--list', metavar='', + help='Show listing of a zipfile') + group.add_argument('-e', '--extract', nargs=2, + metavar=('', ''), + help='Extract zipfile into target dir') + group.add_argument('-c', '--create', nargs='+', + metavar=('', ''), + help='Create zipfile from sources') + group.add_argument('-t', '--test', metavar='', + help='Test if a zipfile is valid') + parser.add_argument('--metadata-encoding', metavar='', + help='Specify encoding of member names for -l, -e and -t') + args = parser.parse_args(args) + + encoding = args.metadata_encoding + + if args.test is not None: + src = args.test + with ZipFile(src, 'r', metadata_encoding=encoding) as zf: + badfile = zf.testzip() + if badfile: + print("The following enclosed file is corrupted: {!r}".format(badfile)) + print("Done testing") + + elif args.list is not None: + src = args.list + with ZipFile(src, 'r', metadata_encoding=encoding) as zf: + zf.printdir() + + elif args.extract is not None: + src, curdir = args.extract + with ZipFile(src, 'r', metadata_encoding=encoding) as zf: + zf.extractall(curdir) + + elif args.create is not None: + if encoding: + print("Non-conforming encodings not supported with -c.", + file=sys.stderr) + sys.exit(1) + + zip_name = args.create.pop(0) + files = args.create + + def addToZip(zf, path, zippath): + if os.path.isfile(path): + zf.write(path, zippath, ZIP_DEFLATED) + elif os.path.isdir(path): + if zippath: + zf.write(path, zippath) + for nm in sorted(os.listdir(path)): + addToZip(zf, + os.path.join(path, nm), os.path.join(zippath, nm)) + # else: ignore + + with ZipFile(zip_name, 'w') as zf: + for path in files: + zippath = os.path.basename(path) + if not zippath: + zippath = os.path.basename(os.path.dirname(path)) + if zippath in ('', os.curdir, os.pardir): + zippath = '' + addToZip(zf, path, zippath) + + from ._path import ( # noqa: E402 Path, # used privately for tests CompleteDirs, # noqa: F401 ) - -# used privately for tests -from .__main__ import main # noqa: F401, E402 diff --git a/Lib/zipfile/__main__.py b/Lib/zipfile/__main__.py index a9e5fb1b8d72c4..868d99efc3c4a3 100644 --- a/Lib/zipfile/__main__.py +++ b/Lib/zipfile/__main__.py @@ -1,77 +1,4 @@ -import sys -import os -from . import ZipFile, ZIP_DEFLATED - - -def main(args=None): - import argparse - - description = 'A simple command-line interface for zipfile module.' - parser = argparse.ArgumentParser(description=description) - group = parser.add_mutually_exclusive_group(required=True) - group.add_argument('-l', '--list', metavar='', - help='Show listing of a zipfile') - group.add_argument('-e', '--extract', nargs=2, - metavar=('', ''), - help='Extract zipfile into target dir') - group.add_argument('-c', '--create', nargs='+', - metavar=('', ''), - help='Create zipfile from sources') - group.add_argument('-t', '--test', metavar='', - help='Test if a zipfile is valid') - parser.add_argument('--metadata-encoding', metavar='', - help='Specify encoding of member names for -l, -e and -t') - args = parser.parse_args(args) - - encoding = args.metadata_encoding - - if args.test is not None: - src = args.test - with ZipFile(src, 'r', metadata_encoding=encoding) as zf: - badfile = zf.testzip() - if badfile: - print("The following enclosed file is corrupted: {!r}".format(badfile)) - print("Done testing") - - elif args.list is not None: - src = args.list - with ZipFile(src, 'r', metadata_encoding=encoding) as zf: - zf.printdir() - - elif args.extract is not None: - src, curdir = args.extract - with ZipFile(src, 'r', metadata_encoding=encoding) as zf: - zf.extractall(curdir) - - elif args.create is not None: - if encoding: - print("Non-conforming encodings not supported with -c.", - file=sys.stderr) - sys.exit(1) - - zip_name = args.create.pop(0) - files = args.create - - def addToZip(zf, path, zippath): - if os.path.isfile(path): - zf.write(path, zippath, ZIP_DEFLATED) - elif os.path.isdir(path): - if zippath: - zf.write(path, zippath) - for nm in sorted(os.listdir(path)): - addToZip(zf, - os.path.join(path, nm), os.path.join(zippath, nm)) - # else: ignore - - with ZipFile(zip_name, 'w') as zf: - for path in files: - zippath = os.path.basename(path) - if not zippath: - zippath = os.path.basename(os.path.dirname(path)) - if zippath in ('', os.curdir, os.pardir): - zippath = '' - addToZip(zf, path, zippath) - +from . import main if __name__ == "__main__": main() diff --git a/Lib/zipimport.py b/Lib/zipimport.py index 5b9f614f02f7af..823a82ee830465 100644 --- a/Lib/zipimport.py +++ b/Lib/zipimport.py @@ -352,7 +352,7 @@ def _read_directory(archive): with fp: # GH-87235: On macOS all file descriptors for /dev/fd/N share the same - # file offset, reset the file offset after scanning the zipfile diretory + # file offset, reset the file offset after scanning the zipfile directory # to not cause problems when some runs 'python3 /dev/fd/9 9mainPtr); + TkDeleteAllImages(winPtr->mainPtr); + TkFontPkgFree(winPtr->mainPtr); + TkFocusFree(winPtr->mainPtr); + TkStylePkgFree(winPtr->mainPtr); ++ Ttk_TkDestroyedHandler(winPtr->mainPtr->interp); + + /* + * When embedding Tk into other applications, make sure that all + * destroy events reach the server. Otherwise the embedding + * application may also attempt to destroy the windows, resulting + +Index: generic/ttk/ttkTheme.c +================================================================== +--- a/generic/ttk/ttkTheme.c.orig ++++ b/generic/ttk/ttkTheme.c +@@ -415,17 +415,10 @@ + StylePackageData *pkgPtr = (StylePackageData *)clientData; + Tcl_HashSearch search; + Tcl_HashEntry *entryPtr; + Cleanup *cleanup; + +- /* +- * Cancel any pending ThemeChanged calls: +- */ +- if (pkgPtr->themeChangePending) { +- Tcl_CancelIdleCall(ThemeChangedProc, pkgPtr); +- } +- + /* + * Free themes. + */ + entryPtr = Tcl_FirstHashEntry(&pkgPtr->themeTable, &search); + while (entryPtr != NULL) { +@@ -528,10 +521,29 @@ + if (!pkgPtr->themeChangePending) { + Tcl_DoWhenIdle(ThemeChangedProc, pkgPtr); + pkgPtr->themeChangePending = 1; + } + } ++ ++/* Ttk_TkDestroyedHandler -- ++ * See bug [310c74ecf440]: idle calls to ThemeChangedProc() ++ * need to be canceled when Tk is destroyed, since the interp ++ * may still be active afterward; canceling them from ++ * Ttk_StylePkgFree() would be too late. ++ */ ++void Ttk_TkDestroyedHandler( ++ Tcl_Interp* interp) ++{ ++ StylePackageData* pkgPtr = GetStylePackageData(interp); ++ ++ /* ++ * Cancel any pending ThemeChanged calls: ++ */ ++ if (pkgPtr->themeChangePending) { ++ Tcl_CancelIdleCall(ThemeChangedProc, pkgPtr); ++ } ++} + + /* + * Ttk_CreateTheme -- + * Create a new theme and register it in the global theme table. + * + diff --git a/Mac/BuildScript/backport_gh92603_fix.patch b/Mac/BuildScript/backport_gh92603_fix.patch new file mode 100644 index 00000000000000..9a37b029650340 --- /dev/null +++ b/Mac/BuildScript/backport_gh92603_fix.patch @@ -0,0 +1,82 @@ +Accepted upstream for release in Tk 8.6.14: +https://core.tcl-lang.org/tk/info/cf3830280b + +--- tk8.6.13/macosx/tkMacOSXWindowEvent.c.orig ++++ tk8.6.13-patched/macosx/tkMacOSXWindowEvent.c +@@ -239,8 +239,8 @@ extern NSString *NSWindowDidOrderOffScreenNotification; + if (winPtr) { + TKContentView *view = [window contentView]; + +-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 +- if (@available(macOS 10.15, *)) { ++#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 ++ if (@available(macOS 10.14, *)) { + [view viewDidChangeEffectiveAppearance]; + } + #endif +@@ -1237,29 +1237,8 @@ static const char *const accentNames[] = { + } else if (effectiveAppearanceName == NSAppearanceNameDarkAqua) { + TkSendVirtualEvent(tkwin, "DarkAqua", NULL); + } +- if ([NSApp macOSVersion] < 101500) { +- +- /* +- * Mojave cannot handle the KVO shenanigans that we need for the +- * highlight and accent color notifications. +- */ +- +- return; +- } + if (!defaultColor) { + defaultColor = [NSApp macOSVersion] < 110000 ? "Blue" : "Multicolor"; +- preferences = [[NSUserDefaults standardUserDefaults] retain]; +- +- /* +- * AppKit calls this method when the user changes the Accent Color +- * but not when the user changes the Highlight Color. So we register +- * to receive KVO notifications for Highlight Color as well. +- */ +- +- [preferences addObserver:self +- forKeyPath:@"AppleHighlightColor" +- options:NSKeyValueObservingOptionNew +- context:NULL]; + } + NSString *accent = [preferences stringForKey:@"AppleAccentColor"]; + NSArray *words = [[preferences stringForKey:@"AppleHighlightColor"] +--- tk8.6.13/macosx/tkMacOSXWm.c.orig ++++ tk8.6.13-patched/macosx/tkMacOSXWm.c +@@ -1289,6 +1289,11 @@ TkWmDeadWindow( + [NSApp _setMainWindow:nil]; + } + [deadNSWindow close]; ++#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 ++ NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; ++ [preferences removeObserver:deadNSWindow.contentView ++ forKeyPath:@"AppleHighlightColor"]; ++#endif + [deadNSWindow release]; + + #if DEBUG_ZOMBIES > 1 +@@ -6763,6 +6768,21 @@ TkMacOSXMakeRealWindowExist( + } + TKContentView *contentView = [[TKContentView alloc] + initWithFrame:NSZeroRect]; ++#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 ++ NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; ++ ++ /* ++ * AppKit calls the viewDidChangeEffectiveAppearance method when the ++ * user changes the Accent Color but not when the user changes the ++ * Highlight Color. So we register to receive KVO notifications for ++ * Highlight Color as well. ++ */ ++ ++ [preferences addObserver:contentView ++ forKeyPath:@"AppleHighlightColor" ++ options:NSKeyValueObservingOptionNew ++ context:NULL]; ++#endif + [window setContentView:contentView]; + [contentView release]; + [window setDelegate:NSApp]; diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index a07d38aedcb5d6..938c895c784f33 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -261,14 +261,14 @@ def library_recipes(): tcl_checksum='81656d3367af032e0ae6157eff134f89' tk_checksum='5e0faecba458ee1386078fb228d008ba' - tk_patches = ['tk868_on_10_8_10_9.patch'] + tk_patches = ['backport_gh71383_fix.patch', 'tk868_on_10_8_10_9.patch', 'backport_gh110950_fix.patch'] else: tcl_tk_ver='8.6.13' tcl_checksum='43a1fae7412f61ff11de2cfd05d28cfc3a73762f354a417c62370a54e2caf066' tk_checksum='2e65fa069a23365440a3c56c556b8673b5e32a283800d8d9b257e3f584ce0675' - tk_patches = [ ] + tk_patches = ['backport_gh92603_fix.patch', 'backport_gh71383_fix.patch', 'backport_gh110950_fix.patch'] base_url = "https://prdownloads.sourceforge.net/tcl/{what}{version}-src.tar.gz" @@ -1147,7 +1147,9 @@ def buildPython(): # will find them during its extension import sanity checks. print("Running configure...") + print(" NOTE: --with-mimalloc=no pending resolution of weak linking issues") runCommand("%s -C --enable-framework --enable-universalsdk=/ " + "--with-mimalloc=no " "--with-universal-archs=%s " "%s " "%s " @@ -1490,7 +1492,7 @@ def packageFromRecipe(targetDir, recipe): IFPkgFlagRelocatable=False, IFPkgFlagRestartAction="NoRestart", IFPkgFlagRootVolumeOnly=True, - IFPkgFlagUpdateInstalledLangauges=False, + IFPkgFlagUpdateInstalledLanguages=False, ) writePlist(pl, os.path.join(packageContents, 'Info.plist')) diff --git a/Makefile.pre.in b/Makefile.pre.in index 3d766425abba34..6ca11f080dcc3f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1359,7 +1359,7 @@ regen-unicodedata: regen-all: regen-cases regen-typeslots \ regen-token regen-ast regen-keyword regen-sre regen-frozen \ regen-pegen-metaparser regen-pegen regen-test-frozenmain \ - regen-test-levenshtein regen-global-objects + regen-test-levenshtein regen-global-objects regen-sbom @echo @echo "Note: make regen-stdlib-module-names, make regen-limited-abi, " @echo "make regen-configure and make regen-unicodedata should be run manually" @@ -1587,14 +1587,16 @@ regen-cases: $(PYTHON_FOR_REGEN) \ $(srcdir)/Tools/cases_generator/generate_cases.py \ $(CASESFLAG) \ - -o $(srcdir)/Python/generated_cases.c.h.new \ - -n $(srcdir)/Include/opcode_ids.h.new \ -t $(srcdir)/Python/opcode_targets.h.new \ -m $(srcdir)/Include/internal/pycore_opcode_metadata.h.new \ -e $(srcdir)/Python/executor_cases.c.h.new \ -p $(srcdir)/Lib/_opcode_metadata.py.new \ -a $(srcdir)/Python/abstract_interp_cases.c.h.new \ $(srcdir)/Python/bytecodes.c + $(PYTHON_FOR_REGEN) \ + $(srcdir)/Tools/cases_generator/opcode_id_generator.py -o $(srcdir)/Include/opcode_ids.h.new $(srcdir)/Python/bytecodes.c + $(PYTHON_FOR_REGEN) \ + $(srcdir)/Tools/cases_generator/tier1_generator.py -o $(srcdir)/Python/generated_cases.c.h.new $(srcdir)/Python/bytecodes.c $(UPDATE_FILE) $(srcdir)/Python/generated_cases.c.h $(srcdir)/Python/generated_cases.c.h.new $(UPDATE_FILE) $(srcdir)/Include/opcode_ids.h $(srcdir)/Include/opcode_ids.h.new $(UPDATE_FILE) $(srcdir)/Python/opcode_targets.h $(srcdir)/Python/opcode_targets.h.new @@ -1873,6 +1875,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_token.h \ $(srcdir)/Include/internal/pycore_traceback.h \ $(srcdir)/Include/internal/pycore_tracemalloc.h \ + $(srcdir)/Include/internal/pycore_tstate.h \ $(srcdir)/Include/internal/pycore_tuple.h \ $(srcdir)/Include/internal/pycore_typeobject.h \ $(srcdir)/Include/internal/pycore_typevarobject.h \ @@ -2194,6 +2197,9 @@ TESTSUBDIRS= idlelib/idle_test \ test/leakers \ test/libregrtest \ test/mathdata \ + test/regrtestdata \ + test/regrtestdata/import_from_tests \ + test/regrtestdata/import_from_tests/test_regrtest_b \ test/subprocessdata \ test/support \ test/support/_hypothesis_stubs \ @@ -2647,6 +2653,10 @@ autoconf: regen-configure: $(srcdir)/Tools/build/regen-configure.sh +.PHONY: regen-sbom +regen-sbom: + $(PYTHON_FOR_REGEN) $(srcdir)/Tools/build/generate_sbom.py + # Create a tags file for vi tags:: ctags -w $(srcdir)/Include/*.h $(srcdir)/Include/cpython/*.h $(srcdir)/Include/internal/*.h @@ -2732,6 +2742,7 @@ clobber: clean -rm -rf build platform -rm -rf $(PYTHONFRAMEWORKDIR) -rm -f python-config.py python-config + -rm -rf cross-build # Make things extra clean, before making a distribution: # remove all generated files, even Makefile[.pre] diff --git a/Misc/ACKS b/Misc/ACKS index 6d3a4e3fdb8fe7..12335c911ae42a 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -788,6 +788,7 @@ Thomas Holmes Craig Holmquist Philip Homburg Naofumi Honda +Constantin Hong Weipeng Hong Jeffrey Honig Rob Hooft @@ -1459,6 +1460,7 @@ Paul Prescod Donovan Preston Eric Price Paul Price +Matt Prodani Iuliia Proskurnia Dorian Pula Jyrki Pulliainen @@ -1482,6 +1484,7 @@ Ajith Ramachandran Dhushyanth Ramasamy Ashwin Ramaswami Jeff Ramnani +Grant Ramsay Bayard Randel Varpu Rantala Brodie Rao diff --git a/Misc/NEWS.d/3.13.0a2.rst b/Misc/NEWS.d/3.13.0a2.rst new file mode 100644 index 00000000000000..c1b1be523325e8 --- /dev/null +++ b/Misc/NEWS.d/3.13.0a2.rst @@ -0,0 +1,1622 @@ +.. date: 2023-11-20-14-13-02 +.. gh-issue: 112243 +.. nonce: FKdQnr +.. release date: 2023-11-22 +.. section: Core and Builtins + +Don't include comments in f-string debug expressions. Patch by Pablo Galindo + +.. + +.. date: 2023-11-20-10-40-40 +.. gh-issue: 112287 +.. nonce: 15gWAK +.. section: Core and Builtins + +Slightly optimize the Tier 2 (uop) interpreter by only loading ``oparg`` and +``operand`` when needed. Also double the trace size limit again, to 512 this +time. + +.. + +.. date: 2023-11-19-15-57-23 +.. gh-issue: 112266 +.. nonce: BSJMbR +.. section: Core and Builtins + +Change docstrings of :attr:`~object.__dict__` and +:attr:`~object.__weakref__`. + +.. + +.. date: 2023-11-17-16-49-32 +.. gh-issue: 111807 +.. nonce: QvjP9_ +.. section: Core and Builtins + +Lower the max parser stack depth to 1000 under WASI debug builds. + +.. + +.. date: 2023-11-15-20-20-51 +.. gh-issue: 111798 +.. nonce: cs-3t3 +.. section: Core and Builtins + +When Python is built in debug mode, set the C recursion limit to 500 instead +of 1500. A debug build is likely built with low optimization level which +implies higher stack memory usage than a release build. Patch by Victor +Stinner. + +.. + +.. date: 2023-11-15-16-14-10 +.. gh-issue: 106529 +.. nonce: Y48ax9 +.. section: Core and Builtins + +Enable translating unspecialized ``FOR_ITER`` to Tier 2. + +.. + +.. date: 2023-11-14-22-12-11 +.. gh-issue: 111916 +.. nonce: ZGCayL +.. section: Core and Builtins + +Make hashlib related modules thread-safe without the GIL + +.. + +.. date: 2023-11-07-12-59-02 +.. gh-issue: 81137 +.. nonce: qFpJCY +.. section: Core and Builtins + +Deprecate assignment to a function's ``__code__`` field when the new code +object is of a mismatched type (e.g., from a generator to a plain function). + +.. + +.. date: 2023-11-06-16-44-09 +.. gh-issue: 79932 +.. nonce: 2qv7uD +.. section: Core and Builtins + +Raise exception if :meth:`frame.clear` is called on a suspended frame. + +.. + +.. date: 2023-11-05-20-59-10 +.. gh-issue: 81925 +.. nonce: wKHLSS +.. section: Core and Builtins + +Implement native thread ids for GNU KFreeBSD. + +.. + +.. date: 2023-11-05-06-40-35 +.. gh-issue: 111843 +.. nonce: c045cB +.. section: Core and Builtins + +Use exponential backoff to reduce the number of failed tier 2 optimization +attempts by over 99%. + +.. + +.. date: 2023-11-04-13-36-51 +.. gh-issue: 110829 +.. nonce: Pa0CJI +.. section: Core and Builtins + +Joining a thread now ensures the underlying OS thread has exited. This is +required for safer fork() in multi-threaded processes. + +.. + +.. date: 2023-11-03-22-48-29 +.. gh-issue: 109369 +.. nonce: ELYaxJ +.. section: Core and Builtins + +Make sure that tier 2 traces are de-optimized if the code is instrumented + +.. + +.. date: 2023-11-03-19-25-38 +.. gh-issue: 111772 +.. nonce: aRQvOn +.. section: Core and Builtins + +Specialize slot loads and stores for _Py_T_OBJECT as well as Py_T_OBJECT_EX + +.. + +.. date: 2023-11-03-01-23-48 +.. gh-issue: 111666 +.. nonce: l8Q8G5 +.. section: Core and Builtins + +Speed up :meth:`BaseExceptionGroup.derive`, +:meth:`BaseExceptionGroup.subgroup`, and :meth:`BaseExceptionGroup.split` by +changing how they parse passed arguments. + +.. + +.. date: 2023-11-03-01-04-55 +.. gh-issue: 111654 +.. nonce: scUhDO +.. section: Core and Builtins + +Fix runtime crash when some error happens in opcode +``LOAD_FROM_DICT_OR_DEREF``. + +.. + +.. date: 2023-11-02-15-00-57 +.. gh-issue: 111623 +.. nonce: BZxYc8 +.. section: Core and Builtins + +Add support for sharing tuples between interpreters using the +cross-interpreter API. Patch by Anthony Shaw. + +.. + +.. date: 2023-11-02-14-49-19 +.. gh-issue: 111354 +.. nonce: gIS3f- +.. section: Core and Builtins + +The oparg of :opcode:`YIELD_VALUE` is now ``1`` if the instruction is part +of a yield-from or await, and ``0`` otherwise. + +The SUSPENDED frame state is now split into ``SUSPENDED`` and +``SUSPENDED_YIELD_FROM``. This simplifies the code in ``_PyGen_yf``. + +.. + +.. date: 2023-10-31-21-33-35 +.. gh-issue: 111520 +.. nonce: vw-rxJ +.. section: Core and Builtins + +Merge the Tier 1 (bytecode) and Tier 2 (micro-ops) interpreters together, +moving the Tier 2 interpreter loop and switch into +``_PyEval_EvalFrameDefault()`` in ``Python/ceval.c``. The +``Python/executor.c`` file is gone. Also the ``TIER_ONE`` and ``TIER_TWO`` +macros are now handled by the code generator. + +**Beware!** This changes the environment variables to enable micro-ops and +their debugging to ``PYTHON_UOPS`` and ``PYTHON_LLTRACE``. + +.. + +.. date: 2023-10-31-14-25-21 +.. gh-issue: 109181 +.. nonce: 11h6Mc +.. section: Core and Builtins + +Speed up :obj:`Traceback` object creation by lazily compute the line number. +Patch by Pablo Galindo + +.. + +.. date: 2023-10-29-20-11-21 +.. gh-issue: 111420 +.. nonce: IUT-GK +.. section: Core and Builtins + +Allow type comments in parenthesized ``with`` statements + +.. + +.. date: 2023-10-29-12-33-33 +.. gh-issue: 111438 +.. nonce: bHTLLl +.. section: Core and Builtins + +Add support for sharing floats between interpreters using the +cross-interpreter API. Patch by Anthony Shaw. + +.. + +.. date: 2023-10-29-11-35-21 +.. gh-issue: 111435 +.. nonce: ageUWQ +.. section: Core and Builtins + +Add support for sharing of True and False between interpreters using the +cross-interpreter API. Patch by Anthony Shaw. + +.. + +.. date: 2023-10-27-19-38-33 +.. gh-issue: 102388 +.. nonce: vd5YUZ +.. section: Core and Builtins + +Fix a bug where ``iso2022_jp_3`` and ``iso2022_jp_2004`` codecs read out of +bounds + +.. + +.. date: 2023-10-27-12-17-49 +.. gh-issue: 111366 +.. nonce: _TSknV +.. section: Core and Builtins + +Fix an issue in the :mod:`codeop` that was causing :exc:`SyntaxError` +exceptions raised in the presence of invalid syntax to not contain precise +error messages. Patch by Pablo Galindo + +.. + +.. date: 2023-10-27-11-51-40 +.. gh-issue: 111380 +.. nonce: vgSbir +.. section: Core and Builtins + +Fix a bug that was causing :exc:`SyntaxWarning` to appear twice when parsing +if invalid syntax is encountered later. Patch by Pablo galindo + +.. + +.. date: 2023-10-27-11-22-09 +.. gh-issue: 111374 +.. nonce: e9lrPZ +.. section: Core and Builtins + +Added a new environment variable :envvar:`PYTHON_FROZEN_MODULES`. It +determines whether or not frozen modules are ignored by the import +machinery, equivalent of the :option:`-X frozen_modules <-X>` command-line +option. + +.. + +.. date: 2023-10-26-18-45-20 +.. gh-issue: 111354 +.. nonce: GrT-Wf +.. section: Core and Builtins + +Remove ``oparg`` from :opcode:`YIELD_VALUE`. Change ``oparg`` of +:opcode:`RESUME` to include information about the except-depth. These +changes make it possible to simplify the code in generator close. + +.. + +.. date: 2023-10-23-22-11-09 +.. gh-issue: 94438 +.. nonce: y2pITu +.. section: Core and Builtins + +Fix a regression that prevented jumping across ``is None`` and ``is not +None`` when debugging. Patch by Savannah Ostrowski. + +.. + +.. date: 2023-10-23-15-44-47 +.. gh-issue: 67224 +.. nonce: S4D6CR +.. section: Core and Builtins + +Show source lines in tracebacks when using the ``-c`` option when running +Python. Patch by Pablo Galindo + +.. + +.. date: 2023-10-20-23-14-06 +.. gh-issue: 111123 +.. nonce: jjVc3M +.. section: Core and Builtins + +Fix a bug where a :keyword:`global` declaration in an :keyword:`except` +block is rejected when the global is used in the :keyword:`else` block. + +.. + +.. date: 2023-10-17-11-03-45 +.. gh-issue: 110938 +.. nonce: X3sbMb +.. section: Core and Builtins + +Fix error messages for indented blocks with functions and classes with +generic type parameters. Patch by Pablo Galindo + +.. + +.. date: 2023-10-16-15-51-37 +.. gh-issue: 109214 +.. nonce: -RGTFH +.. section: Core and Builtins + +Remove unnecessary instruction pointer updates before returning from frames. + +.. + +.. date: 2023-10-16-12-12-48 +.. gh-issue: 110912 +.. nonce: uEJGi_ +.. section: Core and Builtins + +Correctly display the traceback for :exc:`MemoryError` exceptions using the +:mod:`traceback` module. Patch by Pablo Galindo + +.. + +.. date: 2023-10-15-22-18-45 +.. gh-issue: 109894 +.. nonce: UAmo06 +.. section: Core and Builtins + +Fixed crash due to improperly initialized static :exc:`MemoryError` in +subinterpreter. + +.. + +.. date: 2023-10-15-20-45-35 +.. gh-issue: 110892 +.. nonce: oA6eVY +.. section: Core and Builtins + +Return ``NULL`` for ``PyTrace_RETURN`` events caused by an exception + +.. + +.. date: 2023-10-14-12-19-34 +.. gh-issue: 110864 +.. nonce: -baPDE +.. section: Core and Builtins + +Fix argument parsing by ``_PyArg_UnpackKeywordsWithVararg`` for functions +defining pos-or-keyword, vararg, and kw-only parameters. + +.. + +.. date: 2023-10-13-16-55-55 +.. gh-issue: 109094 +.. nonce: ziL4cJ +.. section: Core and Builtins + +Replace ``prev_instr`` on the interpreter frame by ``instr_ptr`` which +points to the beginning of the instruction that is currently executing (or +will execute once the frame resumes). + +.. + +.. date: 2023-10-13-09-21-29 +.. gh-issue: 110805 +.. nonce: vhU7A7 +.. section: Core and Builtins + +Allow the repl to show source code and complete tracebacks. Patch by Pablo +Galindo + +.. + +.. date: 2023-10-12-17-15-23 +.. gh-issue: 110722 +.. nonce: sjMwQe +.. section: Core and Builtins + +Add :envvar:`PYTHON_PRESITE=package.module` to import a module early in the +interpreter lifecycle before ``site.py`` is executed. Python needs to be +:ref:`built in debug mode ` for this option to exist. + +.. + +.. date: 2023-10-12-12-09-01 +.. gh-issue: 110481 +.. nonce: 3Er3it +.. section: Core and Builtins + +Implement biased reference counting in ``--disable-gil`` builds. + +.. + +.. date: 2023-10-09-19-54-33 +.. gh-issue: 110543 +.. nonce: 1wrxO8 +.. section: Core and Builtins + +Fix regression in Python 3.12 where :meth:`types.CodeType.replace` would +produce a broken code object if called on a module or class code object that +contains a comprehension. Patch by Jelle Zijlstra. + +.. + +.. date: 2023-09-30-17-30-11 +.. gh-issue: 89519 +.. nonce: hz2pZf +.. section: Core and Builtins + +Removed chained :class:`classmethod` descriptors (introduced in +:issue:`19072`). This can no longer be used to wrap other descriptors such +as :class:`property`. The core design of this feature was flawed and caused +a number of downstream problems. To "pass-through" a :class:`classmethod`, +consider using the :attr:`!__wrapped__` attribute that was added in Python +3.10. + +.. + +.. date: 2023-09-15-23-39-43 +.. gh-issue: 103615 +.. nonce: WZavly +.. section: Core and Builtins + +Use local events for opcode tracing + +.. + +.. bpo: 46657 +.. date: 2023-09-06-12-36-11 +.. nonce: xea1T_ +.. section: Core and Builtins + +Add mimalloc memory allocator support. + +.. + +.. date: 2023-08-31-11-42-16 +.. gh-issue: 106718 +.. nonce: _-57DA +.. section: Core and Builtins + +When PyConfig.stdlib_dir is explicitly set, it's now respected and won't be +overridden by PyConfig.home. + +.. + +.. date: 2023-07-20-11-41-16 +.. gh-issue: 106905 +.. nonce: AyZpuB +.. section: Core and Builtins + +Fix incorrect SystemError about AST constructor recursion depth mismatch. + +.. + +.. date: 2022-12-27-02-51-45 +.. gh-issue: 100445 +.. nonce: C8f6ph +.. section: Core and Builtins + +Improve error message for unterminated strings with escapes. + +.. + +.. bpo: 45759 +.. date: 2021-11-10-10-40-05 +.. nonce: WJoB3D +.. section: Core and Builtins + +Improved error messages for ``elif``/``else`` statements not matching any +valid statements. Patch by Jeremiah Vivian. + +.. + +.. date: 2023-11-14-18-43-55 +.. gh-issue: 111942 +.. nonce: x1pnrj +.. section: Library + +Fix SystemError in the TextIOWrapper constructor with non-encodable "errors" +argument in non-debug mode. + +.. + +.. date: 2023-11-14-16-31-59 +.. gh-issue: 111995 +.. nonce: OoX8JJ +.. section: Library + +Added the ``NI_IDN`` constant to the :mod:`socket` module when present in C +at build time for use with :func:`socket.getnameinfo`. + +.. + +.. date: 2023-11-11-16-42-48 +.. gh-issue: 109538 +.. nonce: cMG5ux +.. section: Library + +Issue warning message instead of having :class:`RuntimeError` be displayed +when event loop has already been closed at :meth:`StreamWriter.__del__`. + +.. + +.. date: 2023-11-10-22-08-28 +.. gh-issue: 111942 +.. nonce: MDFm6v +.. section: Library + +Fix crashes in :meth:`io.TextIOWrapper.reconfigure` when pass invalid +arguments, e.g. non-string encoding. + +.. + +.. date: 2023-11-09-12-57-43 +.. gh-issue: 111460 +.. nonce: TQaz9I +.. section: Library + +:mod:`curses`: restore wide character support (including +:func:`curses.unget_wch` and :meth:`~curses.window.get_wch`) on macOS, which +was unavailable due to a regression in Python 3.12. + +.. + +.. date: 2023-11-09-10-45-56 +.. gh-issue: 103791 +.. nonce: sdfkja +.. section: Library + +:class:`contextlib.suppress` now supports suppressing exceptions raised as +part of a :exc:`BaseExceptionGroup`, in addition to the recent support for +:exc:`ExceptionGroup`. + +.. + +.. date: 2023-11-08-23-32-03 +.. gh-issue: 111835 +.. nonce: ufFiuW +.. section: Library + +The :class:`mmap.mmap` class now has an :meth:`~mmap.mmap.seekable` method +that can be used where it requires a file-like object with seekable and the +:meth:`~mmap.mmap.seek` method return the new absolute position. Patch by +Donghee Na. + +.. + +.. date: 2023-11-08-15-58-57 +.. gh-issue: 111804 +.. nonce: uAXTOL +.. section: Library + +Remove posix.fallocate() under WASI as the underlying posix_fallocate() is +not available in WASI preview2. + +.. + +.. date: 2023-11-08-11-50-49 +.. gh-issue: 111841 +.. nonce: iSqdQf +.. section: Library + +Fix truncating arguments on an embedded null character in :meth:`os.putenv` +and :meth:`os.unsetenv` on Windows. + +.. + +.. date: 2023-11-08-07-42-53 +.. gh-issue: 111768 +.. nonce: g-WpnV +.. section: Library + +:func:`wsgiref.util.is_hop_by_hop` is now exposed correctly in ``__all__``. + +.. + +.. date: 2023-11-04-21-12-27 +.. gh-issue: 80731 +.. nonce: Wq51xg +.. section: Library + +Avoid executing the default function in :class:`cmd.Cmd` in an except block + +.. + +.. date: 2023-11-04-10-24-25 +.. gh-issue: 111541 +.. nonce: x0RBI1 +.. section: Library + +Fix :mod:`doctest` for :exc:`SyntaxError` not-builtin subclasses. + +.. + +.. date: 2023-11-04-01-20-23 +.. gh-issue: 111719 +.. nonce: fUiKBD +.. section: Library + +Add extra argument validation for ``alias`` command in :mod:`pdb` + +.. + +.. date: 2023-11-02-12-15-46 +.. gh-issue: 111482 +.. nonce: FWqZIU +.. section: Library + +:mod:`time`: Make :func:`time.clock_gettime()` and +:func:`time.clock_gettime_ns()` functions up to 2x faster by faster calling +convention. Patch by Victor Stinner. + +.. + +.. date: 2023-11-01-14-03-24 +.. gh-issue: 110894 +.. nonce: 7-wZxC +.. section: Library + +Call loop exception handler for exceptions in ``client_connected_cb`` of +:func:`asyncio.start_server` so that applications can handle it. Patch by +Kumar Aditya. + +.. + +.. date: 2023-10-31-07-46-56 +.. gh-issue: 111531 +.. nonce: 6zUV_G +.. section: Library + +Fix reference leaks in ``bind_class()`` and ``bind_all()`` methods of +:mod:`tkinter` widgets. + +.. + +.. date: 2023-10-30-14-47-23 +.. gh-issue: 111246 +.. nonce: QJ_ehs +.. section: Library + +:meth:`asyncio.loop.create_unix_server` will now automatically remove the +Unix socket when the server is closed. + +.. + +.. date: 2023-10-30-08-50-46 +.. gh-issue: 111356 +.. nonce: Bc8LvA +.. section: Library + +Added :func:`io.text_encoding()`, :data:`io.DEFAULT_BUFFER_SIZE`, and +:class:`io.IncrementalNewlineDecoder` to ``io.__all__``. + +.. + +.. date: 2023-10-29-03-46-27 +.. gh-issue: 66425 +.. nonce: FWTdDo +.. section: Library + +Remove the code to set the REMOTE_HOST header from wsgiref module, as it is +unreachable. This header is used for performance reasons, which is not +necessary in the wsgiref module. + +.. + +.. date: 2023-10-28-22-11-11 +.. gh-issue: 111429 +.. nonce: mJGxuQ +.. section: Library + +Speed up :meth:`pathlib.PurePath.relative_to` and +:meth:`~pathlib.PurePath.is_relative_to`. + +.. + +.. date: 2023-10-28-04-21-17 +.. gh-issue: 111342 +.. nonce: m8Ln1k +.. section: Library + +Fixed typo in :func:`math.sumprod`. + +.. + +.. date: 2023-10-27-12-46-56 +.. gh-issue: 68166 +.. nonce: 0EbWW4 +.. section: Library + +Remove mention of not supported "vsapi" element type in +:meth:`tkinter.ttk.Style.element_create`. Add tests for ``element_create()`` +and other ``ttk.Style`` methods. Add examples for ``element_create()`` in +the documentation. + +.. + +.. date: 2023-10-27-09-56-20 +.. gh-issue: 111388 +.. nonce: SlmDbC +.. section: Library + +Add ``show_group`` parameter to :func:`traceback.format_exception_only`, +which allows to format :exc:`ExceptionGroup` instances. + +.. + +.. date: 2023-10-25-11-54-00 +.. gh-issue: 79033 +.. nonce: 5ePgFl +.. section: Library + +Another attempt at fixing :func:`asyncio.Server.wait_closed()`. It now +blocks until both conditions are true: the server is closed, *and* there are +no more active connections. (This means that in some cases where in 3.12.0 +this function would *incorrectly* have returned immediately, it will now +block; in particular, when there are no active connections but the server +hasn't been closed yet.) + +.. + +.. date: 2023-10-25-11-13-35 +.. gh-issue: 111259 +.. nonce: z7ndeA +.. section: Library + +Optimize recursive wildcards in :mod:`pathlib`. + +.. + +.. date: 2023-10-25-08-42-05 +.. gh-issue: 111295 +.. nonce: H2K4lf +.. section: Library + +Fix :mod:`time` not checking for errors when initializing. + +.. + +.. date: 2023-10-24-12-20-46 +.. gh-issue: 111253 +.. nonce: HFywSK +.. section: Library + +Add error checking during :mod:`!_socket` module init. + +.. + +.. date: 2023-10-24-12-09-46 +.. gh-issue: 111251 +.. nonce: urFYtn +.. section: Library + +Fix :mod:`_blake2` not checking for errors when initializing. + +.. + +.. date: 2023-10-23-23-14-54 +.. gh-issue: 111233 +.. nonce: sCdCC0 +.. section: Library + +Fix :mod:`select` not checking for errors when initializing. + +.. + +.. date: 2023-10-23-22-40-47 +.. gh-issue: 111230 +.. nonce: k3Jm84 +.. section: Library + +Fix :mod:`ssl` not checking for errors when initializing. + +.. + +.. date: 2023-10-23-13-53-58 +.. gh-issue: 111174 +.. nonce: Oohmzd +.. section: Library + +Fix crash in :meth:`io.BytesIO.getbuffer` called repeatedly for empty +BytesIO. + +.. + +.. date: 2023-10-22-21-28-05 +.. gh-issue: 111187 +.. nonce: _W11Ab +.. section: Library + +Postpone removal version for locale.getdefaultlocale() to Python 3.15. + +.. + +.. date: 2023-10-21-13-57-06 +.. gh-issue: 111159 +.. nonce: GoHp7s +.. section: Library + +Fix :mod:`doctest` output comparison for exceptions with notes. + +.. + +.. date: 2023-10-20-15-29-10 +.. gh-issue: 110910 +.. nonce: u2oPwX +.. section: Library + +Fix invalid state handling in :class:`asyncio.TaskGroup` and +:class:`asyncio.Timeout`. They now raise proper RuntimeError if they are +improperly used and are left in consistent state after this. + +.. + +.. date: 2023-10-19-22-46-34 +.. gh-issue: 111092 +.. nonce: hgut12 +.. section: Library + +Make turtledemo run without default root enabled. + +.. + +.. date: 2023-10-16-18-41-51 +.. gh-issue: 110944 +.. nonce: CmUKXo +.. section: Library + +Support alias and convenience vars for :mod:`pdb` completion + +.. + +.. date: 2023-10-15-08-08-26 +.. gh-issue: 110745 +.. nonce: mxEkh0 +.. section: Library + +Added *newline* parameter to :meth:`pathlib.Path.read_text`. Patch by Junya +Okabe. + +.. + +.. date: 2023-10-14-21-33-57 +.. gh-issue: 84583 +.. nonce: -Cmn4_ +.. section: Library + +Make :mod:`pdb` enter post-mortem mode even for :exc:`SyntaxError` + +.. + +.. date: 2023-10-14-20-15-53 +.. gh-issue: 80675 +.. nonce: _M-cQC +.. section: Library + +Set ``f_trace_lines = True`` on all frames upon :func:`pdb.set_trace()` + +.. + +.. date: 2023-10-13-06-47-20 +.. gh-issue: 110771 +.. nonce: opwdlc +.. section: Library + +Expose the setup and cleanup portions of ``asyncio.run_forever()`` as the +standalone methods ``asyncio.run_forever_setup()`` and +``asyncio.run_forever_cleanup()``. This allows for tighter integration with +GUI event loops. + +.. + +.. date: 2023-10-12-15-16-44 +.. gh-issue: 110774 +.. nonce: AdCb5A +.. section: Library + +Support setting the :class:`asyncio.Runner` loop_factory kwarg in +:class:`unittest.IsolatedAsyncioTestCase` + +.. + +.. date: 2023-10-10-17-56-41 +.. gh-issue: 110392 +.. nonce: 6g6CnP +.. section: Library + +Fix :func:`tty.setraw` and :func:`tty.setcbreak`: previously they returned +partially modified list of the original tty attributes. +:func:`tty.cfmakeraw` and :func:`tty.cfmakecbreak` now make a copy of the +list of special characters before modifying it. + +.. + +.. date: 2023-10-09-23-59-04 +.. gh-issue: 59013 +.. nonce: qPbS-G +.. section: Library + +Make line number of function breakpoint more precise in :mod:`pdb` + +.. + +.. date: 2023-10-08-18-38-09 +.. gh-issue: 88434 +.. nonce: 2Q_IkG +.. section: Library + +Emit deprecation warning for non-integer numbers in :mod:`gettext` functions +and methods that consider plural forms even if the translation was not +found. + +.. + +.. date: 2023-10-08-14-17-06 +.. gh-issue: 110395 +.. nonce: _tdCsV +.. section: Library + +Ensure that :func:`select.kqueue` objects correctly appear as closed in +forked children, to prevent operations on an invalid file descriptor. + +.. + +.. date: 2023-10-02-05-23-27 +.. gh-issue: 110196 +.. nonce: djwt0z +.. section: Library + +Add ``__reduce__`` method to :class:`IPv6Address` in order to keep +``scope_id`` + +.. + +.. date: 2023-09-25-20-05-41 +.. gh-issue: 109747 +.. nonce: _cRJH8 +.. section: Library + +Improve errors for unsupported look-behind patterns. Now re.error is raised +instead of OverflowError or RuntimeError for too large width of look-behind +pattern. + +.. + +.. date: 2023-09-15-12-30-21 +.. gh-issue: 109466 +.. nonce: 6ah-aw +.. section: Library + +Add the :attr:`ipaddress.IPv4Address.ipv6_mapped` property, which retuns the +IPv4-mapped IPv6 address. + +.. + +.. date: 2023-09-08-12-10-10 +.. gh-issue: 85098 +.. nonce: DfQbeJ +.. section: Library + +Implement the CLI of the :mod:`symtable` module and improve the repr of +:class:`~symtable.Symbol`. + +.. + +.. date: 2023-09-02-16-07-23 +.. gh-issue: 108791 +.. nonce: fBcAqh +.. section: Library + +Improved error handling in :mod:`pdb` command line interface, making it +produce more concise error messages. + +.. + +.. date: 2023-08-30-19-10-35 +.. gh-issue: 105931 +.. nonce: Lpwve8 +.. section: Library + +Change :mod:`compileall` to only strip the stripdir prefix from the full +path recorded in the compiled ``.pyc`` file, when the prefix matches the +start of the full path in its entirety. When the prefix does not match, no +stripping is performed and a warning to this effect is displayed. + +Previously all path components of the stripdir prefix that matched the full +path were removed, while those that did not match were left alone (including +ones interspersed between matching components). + +.. + +.. date: 2023-07-29-19-00-39 +.. gh-issue: 107431 +.. nonce: 1GzJ2p +.. section: Library + +Make the ``DictProxy`` and ``ListProxy`` types in +:mod:`multiprocessing.managers` :ref:`Generic Alias +Types` for ``[]`` use in typing contexts. + +.. + +.. date: 2023-07-13-00-24-52 +.. gh-issue: 72904 +.. nonce: Yn5-j0 +.. section: Library + +Add :func:`glob.translate`. This function converts a pathname with +shell-style wildcards to a regular expression. + +.. + +.. date: 2023-05-30-02-01-14 +.. gh-issue: 90026 +.. nonce: FyCXw8 +.. section: Library + +Define ``USE_XATTRS`` on Cygwin so that XATTR-related functions in the +:mod:`os` module become available. + +.. + +.. date: 2023-04-26-16-37-00 +.. gh-issue: 90890 +.. nonce: fIag4w +.. section: Library + +New methods :meth:`mailbox.Maildir.get_info`, +:meth:`mailbox.Maildir.set_info`, :meth:`mailbox.Maildir.get_flags`, +:meth:`mailbox.Maildir.set_flags`, :meth:`mailbox.Maildir.add_flag`, +:meth:`mailbox.Maildir.remove_flag`. These methods speed up accessing a +message's info and/or flags and are useful when it is not necessary to +access the message's contents, as when iterating over a Maildir to find +messages with specific flags. + +.. + +.. date: 2023-04-15-14-45-21 +.. gh-issue: 102956 +.. nonce: Z6qeUy +.. section: Library + +Fix returning of empty byte strings after seek in zipfile module + +.. + +.. date: 2023-03-22-02-01-30 +.. gh-issue: 102895 +.. nonce: HiEqaZ +.. section: Library + +Added a parameter ``local_exit`` for :func:`code.interact` to prevent +``exit()`` and ``quit`` from closing ``sys.stdin`` and raise ``SystemExit``. + +.. + +.. date: 2022-10-14-21-11-10 +.. gh-issue: 97928 +.. nonce: Pdxh1G +.. section: Library + +Change the behavior of :meth:`tkinter.Text.count`. It now always returns an +integer if one or less counting options are specified. Previously it could +return a single count as a 1-tuple, an integer (only if option ``"update"`` +was specified) or ``None`` if no items found. The result is now the same if +``wantobjects`` is set to ``0``. + +.. + +.. date: 2022-10-05-15-01-36 +.. gh-issue: 96954 +.. nonce: ezwkrU +.. section: Library + +Switch the storage of the unicode codepoint names to use a different +data-structure, a `directed acyclic word graph +`_. +This makes the unicodedata shared library about 440 KiB smaller. Contributed +by Carl Friedrich Bolz-Tereick using code from the PyPy project. + +.. + +.. date: 2022-05-28-20-55-07 +.. gh-issue: 73561 +.. nonce: YRmAvy +.. section: Library + +Omit the interface scope from an IPv6 address when used as Host header by +:mod:`http.client`. + +.. + +.. date: 2022-05-06-15-49-57 +.. gh-issue: 86826 +.. nonce: rf006W +.. section: Library + +:mod:`zipinfo` now supports the full range of values in the TZ string +determined by RFC 8536 and detects all invalid formats. Both Python and C +implementations now raise exceptions of the same type on invalid data. + +.. + +.. date: 2023-11-17-15-20-41 +.. gh-issue: 111808 +.. nonce: jtIayt +.. section: Tests + +Make the default value of ``test.support.infinite_recursion()`` to be +conditional based on whether optimizations were used when compiling the +interpreter. This helps with platforms like WASI whose stack size is greatly +restricted in debug builds. + +.. + +.. date: 2023-11-03-18-59-13 +.. gh-issue: 110722 +.. nonce: jvT1pb +.. section: Tests + +Gathering line coverage of standard libraries within the regression test +suite is now precise, as well as much faster. Patch by Łukasz Langa. + +.. + +.. date: 2023-10-31-22-09-25 +.. gh-issue: 110367 +.. nonce: UhQi44 +.. section: Tests + +Make regrtest ``--verbose3`` option compatible with ``--huntrleaks -jN`` +options. The ``./python -m test -j1 -R 3:3 --verbose3`` command now works as +expected. Patch by Victor Stinner. + +.. + +.. date: 2023-10-21-19-27-36 +.. gh-issue: 111165 +.. nonce: FU6mUk +.. section: Tests + +Remove no longer used functions ``run_unittest()`` and ``run_doctest()`` +from the :mod:`test.support` module. + +.. + +.. date: 2023-10-21-00-10-36 +.. gh-issue: 110932 +.. nonce: jktjJU +.. section: Tests + +Fix regrtest if the ``SOURCE_DATE_EPOCH`` environment variable is defined: +use the variable value as the random seed. Patch by Victor Stinner. + +.. + +.. date: 2023-10-17-17-54-36 +.. gh-issue: 110995 +.. nonce: Fx8KRD +.. section: Tests + +test_gdb: Fix detection of gdb built without Python scripting support. Patch +by Victor Stinner. + +.. + +.. date: 2023-10-16-13-47-24 +.. gh-issue: 110918 +.. nonce: aFgZK3 +.. section: Tests + +Test case matching patterns specified by options ``--match``, ``--ignore``, +``--matchfile`` and ``--ignorefile`` are now tested in the order of +specification, and the last match determines whether the test case be run or +ignored. + +.. + +.. date: 2023-09-15-15-00-14 +.. gh-issue: 108747 +.. nonce: ql0owS +.. section: Tests + +Add unit test for ``usercustomize`` and ``sitecustomize`` hooks from +:class:`site`. + +.. + +.. date: 2023-11-15-16-56-20 +.. gh-issue: 96954 +.. nonce: 6FYvKn +.. section: Build + +Make ``make regen-unicodedata`` work for out-of-tree builds of CPython. + +.. + +.. date: 2023-11-15-13-40-29 +.. gh-issue: 112088 +.. nonce: UJQxxh +.. section: Build + +Add ``Tools/build/regen-configure.sh`` script to regenerate the +``configure`` with an Ubuntu container image. The +``quay.io/tiran/cpython_autoconf:271`` container image +(`tiran/cpython_autoconf `_) is +no longer used. Patch by Victor Stinner. + +.. + +.. date: 2023-10-20-15-29-31 +.. gh-issue: 111046 +.. nonce: 2DxQl8 +.. section: Build + +For wasi-threads, memory is now exported to fix compatibility issues with +some wasm runtimes. + +.. + +.. date: 2023-10-17-03-10-40 +.. gh-issue: 110828 +.. nonce: 31vQ9B +.. section: Build + +AIX 32bit needs ``-latomic`` to build the :mod:`!_testcapi` extension +module. + +.. + +.. date: 2023-10-17-01-56-11 +.. gh-issue: 85283 +.. nonce: V156T2 +.. section: Build + +The ``errno``, ``md5``, ``resource``, ``winsound``, ``_ctypes_test``, +``_multiprocessing.posixshmem``, ``_scproxy``, ``_stat``, +``_testimportmultiple`` and ``_uuid`` C extensions are now built with the +:ref:`limited C API `. Patch by Victor Stinner. + +.. + +.. date: 2023-11-13-22-35-27 +.. gh-issue: 111856 +.. nonce: vEtA5z +.. section: Windows + +Fixes :func:`~os.fstat` on file systems that do not support file ID +requests. This includes FAT32 and exFAT. + +.. + +.. date: 2023-10-25-05-01-28 +.. gh-issue: 111293 +.. nonce: FSsLT6 +.. section: Windows + +Fix :data:`os.DirEntry.inode` dropping higher 64 bits of a file id on some +filesystems on Windows. + +.. + +.. date: 2023-10-19-21-46-18 +.. gh-issue: 110913 +.. nonce: CWlPfg +.. section: Windows + +WindowsConsoleIO now correctly chunks large buffers without splitting up +UTF-8 sequences. + +.. + +.. date: 2023-10-31-22-13-05 +.. gh-issue: 59703 +.. nonce: SML6Ag +.. section: macOS + +For macOS framework builds, in ``getpath.c`` use the system ``dladdr`` +function to find the path to the shared library rather than depending on +deprecated macOS APIs. + +.. + +.. date: 2023-10-18-17-26-36 +.. gh-issue: 110950 +.. nonce: sonoma +.. section: macOS + +Update macOS installer to include an upstream Tcl/Tk fix for the ``Secure +coding is not enabled for restorable state!`` warning encountered in Tkinter +on macOS 14 Sonoma. + +.. + +.. date: 2023-10-18-01-40-36 +.. gh-issue: 111015 +.. nonce: NaLI2L +.. section: macOS + +Ensure that IDLE.app and Python Launcher.app are installed with appropriate +permissions on macOS builds. + +.. + +.. date: 2023-09-02-08-49-57 +.. gh-issue: 71383 +.. nonce: Ttkchg +.. section: macOS + +Update macOS installer to include an upstream Tcl/Tk fix for the +``ttk::ThemeChanged`` error encountered in Tkinter. + +.. + +.. date: 2023-08-30-16-33-57 +.. gh-issue: 92603 +.. nonce: ATkKVO +.. section: macOS + +Update macOS installer to include a fix accepted by upstream Tcl/Tk for a +crash encountered after the first :meth:`tkinter.Tk` instance is destroyed. + +.. + +.. bpo: 35668 +.. date: 2019-01-07-06-18-25 +.. nonce: JimxP5 +.. section: IDLE + +Add docstrings to the IDLE debugger module. Fix two bugs: initialize +Idb.botframe (should be in Bdb); in Idb.in_rpc_code, check whether +prev_frame is None before trying to use it. Greatly expand test_debugger. + +.. + +.. date: 2023-11-09-13-04-29 +.. gh-issue: 111903 +.. nonce: 7Prryr +.. section: Tools/Demos + +Argument Clinic now supports the ``@critical_section`` directive that +instructs Argument Clinic to generate a critical section around the function +call, which locks the ``self`` object in ``--disable-gil`` builds. Patch by +Sam Gross. + +.. + +.. date: 2023-11-15-18-36-21 +.. gh-issue: 112026 +.. nonce: _Yybr5 +.. section: C API + +Add again the private ``_PyThreadState_UncheckedGet()`` function as an alias +to the new public :c:func:`PyThreadState_GetUnchecked` function. Patch by +Victor Stinner. + +.. + +.. date: 2023-11-15-17-10-09 +.. gh-issue: 112026 +.. nonce: ts9yyn +.. section: C API + +Restore the removed ``_PyDict_GetItemStringWithError()`` function. It is +used by numpy. Patch by Victor Stinner. + +.. + +.. date: 2023-11-15-16-07-57 +.. gh-issue: 112026 +.. nonce: bnr8dd +.. section: C API + +Restore removed private C API functions, macros and structures which have no +simple replacement for now: + +* _PyDict_GetItem_KnownHash() +* _PyDict_NewPresized() +* _PyHASH_BITS +* _PyHASH_IMAG +* _PyHASH_INF +* _PyHASH_MODULUS +* _PyHASH_MULTIPLIER +* _PyLong_Copy() +* _PyLong_FromDigits() +* _PyLong_New() +* _PyLong_Sign() +* _PyObject_CallMethodId() +* _PyObject_CallMethodNoArgs() +* _PyObject_CallMethodOneArg() +* _PyObject_CallOneArg() +* _PyObject_EXTRA_INIT +* _PyObject_FastCallDict() +* _PyObject_GetAttrId() +* _PyObject_Vectorcall() +* _PyObject_VectorcallMethod() +* _PyStack_AsDict() +* _PyThread_CurrentFrames() +* _PyUnicodeWriter structure +* _PyUnicodeWriter_Dealloc() +* _PyUnicodeWriter_Finish() +* _PyUnicodeWriter_Init() +* _PyUnicodeWriter_Prepare() +* _PyUnicodeWriter_PrepareKind() +* _PyUnicodeWriter_WriteASCIIString() +* _PyUnicodeWriter_WriteChar() +* _PyUnicodeWriter_WriteLatin1String() +* _PyUnicodeWriter_WriteStr() +* _PyUnicodeWriter_WriteSubstring() +* _PyUnicode_AsString() +* _PyUnicode_FromId() +* _PyVectorcall_Function() +* _Py_IDENTIFIER() +* _Py_c_abs() +* _Py_c_diff() +* _Py_c_neg() +* _Py_c_pow() +* _Py_c_prod() +* _Py_c_quot() +* _Py_c_sum() +* _Py_static_string() +* _Py_static_string_init() + +Patch by Victor Stinner. + +.. + +.. date: 2023-11-13-17-57-11 +.. gh-issue: 112026 +.. nonce: WJLJcI +.. section: C API + +Add again ```` and ```` includes in ``Python.h``, but +don't include them in the limited C API version 3.13 and newer. Patch by +Victor Stinner. + +.. + +.. date: 2023-11-10-10-24-28 +.. gh-issue: 111956 +.. nonce: ImE6Cx +.. section: C API + +Add internal-only one-time initialization API: ``_PyOnceFlag`` and +``_PyOnceFlag_CallOnce``. + +.. + +.. date: 2023-11-10-10-21-38 +.. gh-issue: 111262 +.. nonce: 2utB5m +.. section: C API + +Add :c:func:`PyDict_Pop` and :c:func:`PyDict_PopString` functions: remove a +key from a dictionary and optionally return the removed value. This is +similar to :meth:`dict.pop`, but without the default value and not raising +:exc:`KeyError` if the key missing. Patch by Stefan Behnel and Victor +Stinner. + +.. + +.. date: 2023-11-08-20-28-03 +.. gh-issue: 111863 +.. nonce: RPeFAX +.. section: C API + +Rename ``Py_NOGIL`` to ``Py_GIL_DISABLED``. Patch by Hugo van Kemenade. + +.. + +.. date: 2023-11-08-18-37-19 +.. gh-issue: 111138 +.. nonce: 3Ypq8h +.. section: C API + +Add :c:func:`PyList_Extend` and :c:func:`PyList_Clear` functions: similar to +Python ``list.extend()`` and ``list.clear()`` methods. Patch by Victor +Stinner. + +.. + +.. date: 2023-10-31-18-22-03 +.. gh-issue: 108765 +.. nonce: _beYv8 +.. section: C API + +On Windows, ``Python.h`` no longer includes the ```` standard +header file. If needed, it should now be included explicitly. Patch by +Victor Stinner. + +.. + +.. date: 2023-10-31-14-58-17 +.. gh-issue: 111569 +.. nonce: _V8iu4 +.. section: C API + +Implement "Python Critical Sections" from :pep:`703`. These are macros to +help replace the GIL with per-object locks in the ``--disable-gil`` build of +CPython. The macros are no-ops in the default build. + +.. + +.. date: 2023-10-30-18-13-01 +.. gh-issue: 111506 +.. nonce: EUdO22 +.. section: C API + +In the limited C API version 3.13, :c:func:`Py_SET_REFCNT` function is now +implemented as an opaque function call. Patch by Victor Stinner. + +.. + +.. date: 2023-10-19-22-39-24 +.. gh-issue: 108082 +.. nonce: uJytvc +.. section: C API + +Add :c:func:`PyErr_FormatUnraisable` function. + +.. + +.. date: 2023-10-17-10-21-59 +.. gh-issue: 110964 +.. nonce: OxqEjd +.. section: C API + +Move the undocumented private _PyArg functions and _PyArg_Parser structure +to internal C API (``pycore_modsupport.h``). Patch by Victor Stinner. + +.. + +.. date: 2023-10-13-14-18-06 +.. gh-issue: 110815 +.. nonce: tEFLVl +.. section: C API + +Support non-ASCII keyword names in :c:func:`PyArg_ParseTupleAndKeywords`. + +.. + +.. date: 2023-10-02-23-08-53 +.. gh-issue: 109587 +.. nonce: UqqnDY +.. section: C API + +Introduced :c:func:`PyUnstable_PerfTrampoline_CompileCode`, +:c:func:`PyUnstable_PerfTrampoline_SetPersistAfterFork` and +:c:func:`PyUnstable_CopyPerfMapFile`. These functions allow extension +modules to initialize trampolines eagerly, after the application is "warmed +up". This makes it possible to have perf-trampolines running in an +always-enabled fashion. + +.. + +.. date: 2023-08-28-17-40-51 +.. gh-issue: 85283 +.. nonce: raFNiD +.. section: C API + +Add the :c:func:`PySys_Audit` function to the limited C API. Patch by Victor +Stinner. + +.. + +.. date: 2023-08-28-17-34-10 +.. gh-issue: 85283 +.. nonce: f1zXcc +.. section: C API + +Add :c:func:`PyMem_RawMalloc`, :c:func:`PyMem_RawCalloc`, +:c:func:`PyMem_RawRealloc` and :c:func:`PyMem_RawFree` to the limited C API. +Patch by Victor Stinner. + +.. + +.. date: 2023-07-12-12-14-52 +.. gh-issue: 106672 +.. nonce: fkRjmi +.. section: C API + +Functions :c:func:`PyDict_GetItem`, :c:func:`PyDict_GetItemString`, +:c:func:`PyMapping_HasKey`, :c:func:`PyMapping_HasKeyString`, +:c:func:`PyObject_HasAttr`, :c:func:`PyObject_HasAttrString`, and +:c:func:`PySys_GetObject`, which clear all errors occurred during calling +the function, report now them using :func:`sys.unraisablehook`. + +.. + +.. date: 2023-06-08-21-12-44 +.. gh-issue: 67565 +.. nonce: UkK3x- +.. section: C API + +Remove redundant C-contiguity check in :file:`getargs.c`, :mod:`binascii`, +:mod:`ssl` and Argument Clinic. Patched by Stefan Krah and Furkan Onder diff --git a/Misc/NEWS.d/next/Build/2023-10-17-01-56-11.gh-issue-85283.V156T2.rst b/Misc/NEWS.d/next/Build/2023-10-17-01-56-11.gh-issue-85283.V156T2.rst deleted file mode 100644 index 399054040f2ec7..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-10-17-01-56-11.gh-issue-85283.V156T2.rst +++ /dev/null @@ -1,5 +0,0 @@ -The ``errno``, ``md5``, ``resource``, ``winsound``, ``_ctypes_test``, -``_multiprocessing.posixshmem``, ``_scproxy``, ``_stat``, -``_testimportmultiple`` and ``_uuid`` C extensions are now built with the -:ref:`limited C API `. -Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Build/2023-10-17-03-10-40.gh-issue-110828.31vQ9B.rst b/Misc/NEWS.d/next/Build/2023-10-17-03-10-40.gh-issue-110828.31vQ9B.rst deleted file mode 100644 index 13647fee058021..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-10-17-03-10-40.gh-issue-110828.31vQ9B.rst +++ /dev/null @@ -1 +0,0 @@ -AIX 32bit needs ``-latomic`` to build the :mod:`!_testcapi` extension module. diff --git a/Misc/NEWS.d/next/Build/2023-10-20-15-29-31.gh-issue-111046.2DxQl8.rst b/Misc/NEWS.d/next/Build/2023-10-20-15-29-31.gh-issue-111046.2DxQl8.rst deleted file mode 100644 index 446b8b612862f9..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-10-20-15-29-31.gh-issue-111046.2DxQl8.rst +++ /dev/null @@ -1 +0,0 @@ -For wasi-threads, memory is now exported to fix compatibility issues with some wasm runtimes. diff --git a/Misc/NEWS.d/next/Build/2023-11-15-13-40-29.gh-issue-112088.UJQxxh.rst b/Misc/NEWS.d/next/Build/2023-11-15-13-40-29.gh-issue-112088.UJQxxh.rst deleted file mode 100644 index b176d06ec8d749..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-11-15-13-40-29.gh-issue-112088.UJQxxh.rst +++ /dev/null @@ -1,5 +0,0 @@ -Add ``Tools/build/regen-configure.sh`` script to regenerate the ``configure`` -with an Ubuntu container image. The ``quay.io/tiran/cpython_autoconf:271`` -container image (`tiran/cpython_autoconf -`_) is no longer used. Patch by -Victor Stinner. diff --git a/Misc/NEWS.d/next/Build/2023-11-15-16-56-20.gh-issue-96954.6FYvKn.rst b/Misc/NEWS.d/next/Build/2023-11-15-16-56-20.gh-issue-96954.6FYvKn.rst deleted file mode 100644 index e9a30e5f4bbaa7..00000000000000 --- a/Misc/NEWS.d/next/Build/2023-11-15-16-56-20.gh-issue-96954.6FYvKn.rst +++ /dev/null @@ -1 +0,0 @@ -Make ``make regen-unicodedata`` work for out-of-tree builds of CPython. diff --git a/Misc/NEWS.d/next/Build/2023-11-27-13-55-47.gh-issue-103065.o72OiA.rst b/Misc/NEWS.d/next/Build/2023-11-27-13-55-47.gh-issue-103065.o72OiA.rst new file mode 100644 index 00000000000000..e2240b7c656a2f --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-11-27-13-55-47.gh-issue-103065.o72OiA.rst @@ -0,0 +1 @@ +Introduce ``Tools/wasm/wasi.py`` to simplify doing a WASI build. diff --git a/Misc/NEWS.d/next/C API/2023-06-08-21-12-44.gh-issue-67565.UkK3x-.rst b/Misc/NEWS.d/next/C API/2023-06-08-21-12-44.gh-issue-67565.UkK3x-.rst deleted file mode 100644 index 0e50976b0e1ddf..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-06-08-21-12-44.gh-issue-67565.UkK3x-.rst +++ /dev/null @@ -1 +0,0 @@ -Remove redundant C-contiguity check in :file:`getargs.c`, :mod:`binascii`, :mod:`ssl` and Argument Clinic. Patched by Stefan Krah and Furkan Onder diff --git a/Misc/NEWS.d/next/C API/2023-06-21-11-53-09.gh-issue-65210.PhFRBJ.rst b/Misc/NEWS.d/next/C API/2023-06-21-11-53-09.gh-issue-65210.PhFRBJ.rst new file mode 100644 index 00000000000000..a15646f4dad127 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-06-21-11-53-09.gh-issue-65210.PhFRBJ.rst @@ -0,0 +1,3 @@ +Change the declaration of the *keywords* parameter of +:c:func:`PyArg_ParseTupleAndKeywords` and +:c:func:`PyArg_VaParseTupleAndKeywords` for better compatibility with C++. diff --git a/Misc/NEWS.d/next/C API/2023-07-12-12-14-52.gh-issue-106672.fkRjmi.rst b/Misc/NEWS.d/next/C API/2023-07-12-12-14-52.gh-issue-106672.fkRjmi.rst deleted file mode 100644 index 420f43175e595a..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-07-12-12-14-52.gh-issue-106672.fkRjmi.rst +++ /dev/null @@ -1,5 +0,0 @@ -Functions :c:func:`PyDict_GetItem`, :c:func:`PyDict_GetItemString`, -:c:func:`PyMapping_HasKey`, :c:func:`PyMapping_HasKeyString`, -:c:func:`PyObject_HasAttr`, :c:func:`PyObject_HasAttrString`, and -:c:func:`PySys_GetObject`, which clear all errors occurred during calling -the function, report now them using :func:`sys.unraisablehook`. diff --git a/Misc/NEWS.d/next/C API/2023-08-28-17-34-10.gh-issue-85283.f1zXcc.rst b/Misc/NEWS.d/next/C API/2023-08-28-17-34-10.gh-issue-85283.f1zXcc.rst deleted file mode 100644 index 1c25fdb6ec8d2f..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-08-28-17-34-10.gh-issue-85283.f1zXcc.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add :c:func:`PyMem_RawMalloc`, :c:func:`PyMem_RawCalloc`, -:c:func:`PyMem_RawRealloc` and :c:func:`PyMem_RawFree` to the limited C API. -Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-08-28-17-40-51.gh-issue-85283.raFNiD.rst b/Misc/NEWS.d/next/C API/2023-08-28-17-40-51.gh-issue-85283.raFNiD.rst deleted file mode 100644 index 45ffda3d23dd22..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-08-28-17-40-51.gh-issue-85283.raFNiD.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add the :c:func:`PySys_Audit` function to the limited C API. Patch by Victor -Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-10-02-23-08-53.gh-issue-109587.UqqnDY.rst b/Misc/NEWS.d/next/C API/2023-10-02-23-08-53.gh-issue-109587.UqqnDY.rst deleted file mode 100644 index c6fa24f024c20c..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-10-02-23-08-53.gh-issue-109587.UqqnDY.rst +++ /dev/null @@ -1,2 +0,0 @@ -Introduced :c:func:`PyUnstable_PerfTrampoline_CompileCode`, :c:func:`PyUnstable_PerfTrampoline_SetPersistAfterFork` and -:c:func:`PyUnstable_CopyPerfMapFile`. These functions allow extension modules to initialize trampolines eagerly, after the application is "warmed up". This makes it possible to have perf-trampolines running in an always-enabled fashion. diff --git a/Misc/NEWS.d/next/C API/2023-10-13-14-18-06.gh-issue-110815.tEFLVl.rst b/Misc/NEWS.d/next/C API/2023-10-13-14-18-06.gh-issue-110815.tEFLVl.rst deleted file mode 100644 index 216d2d211644a8..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-10-13-14-18-06.gh-issue-110815.tEFLVl.rst +++ /dev/null @@ -1 +0,0 @@ -Support non-ASCII keyword names in :c:func:`PyArg_ParseTupleAndKeywords`. diff --git a/Misc/NEWS.d/next/C API/2023-10-17-10-21-59.gh-issue-110964.OxqEjd.rst b/Misc/NEWS.d/next/C API/2023-10-17-10-21-59.gh-issue-110964.OxqEjd.rst deleted file mode 100644 index e45a25d8661ca4..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-10-17-10-21-59.gh-issue-110964.OxqEjd.rst +++ /dev/null @@ -1,2 +0,0 @@ -Move the undocumented private _PyArg functions and _PyArg_Parser structure -to internal C API (``pycore_modsupport.h``). Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-10-19-22-39-24.gh-issue-108082.uJytvc.rst b/Misc/NEWS.d/next/C API/2023-10-19-22-39-24.gh-issue-108082.uJytvc.rst deleted file mode 100644 index b99a829e3f2a52..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-10-19-22-39-24.gh-issue-108082.uJytvc.rst +++ /dev/null @@ -1 +0,0 @@ -Add :c:func:`PyErr_FormatUnraisable` function. diff --git a/Misc/NEWS.d/next/C API/2023-10-30-18-13-01.gh-issue-111506.EUdO22.rst b/Misc/NEWS.d/next/C API/2023-10-30-18-13-01.gh-issue-111506.EUdO22.rst deleted file mode 100644 index f4d71fd008241a..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-10-30-18-13-01.gh-issue-111506.EUdO22.rst +++ /dev/null @@ -1,2 +0,0 @@ -In the limited C API version 3.13, :c:func:`Py_SET_REFCNT` function is now -implemented as an opaque function call. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-10-31-14-58-17.gh-issue-111569._V8iu4.rst b/Misc/NEWS.d/next/C API/2023-10-31-14-58-17.gh-issue-111569._V8iu4.rst deleted file mode 100644 index c2bd3ae36e6439..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-10-31-14-58-17.gh-issue-111569._V8iu4.rst +++ /dev/null @@ -1,3 +0,0 @@ -Implement "Python Critical Sections" from :pep:`703`. These are macros to -help replace the GIL with per-object locks in the ``--disable-gil`` build of -CPython. The macros are no-ops in the default build. diff --git a/Misc/NEWS.d/next/C API/2023-10-31-18-22-03.gh-issue-108765._beYv8.rst b/Misc/NEWS.d/next/C API/2023-10-31-18-22-03.gh-issue-108765._beYv8.rst deleted file mode 100644 index 14af79eaea9a03..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-10-31-18-22-03.gh-issue-108765._beYv8.rst +++ /dev/null @@ -1,3 +0,0 @@ -On Windows, ``Python.h`` no longer includes the ```` standard -header file. If needed, it should now be included explicitly. Patch by -Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-11-08-18-37-19.gh-issue-111138.3Ypq8h.rst b/Misc/NEWS.d/next/C API/2023-11-08-18-37-19.gh-issue-111138.3Ypq8h.rst deleted file mode 100644 index 15c3b9b3a6b9ad..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-11-08-18-37-19.gh-issue-111138.3Ypq8h.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :c:func:`PyList_Extend` and :c:func:`PyList_Clear` functions: similar to -Python ``list.extend()`` and ``list.clear()`` methods. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-11-10-10-21-38.gh-issue-111262.2utB5m.rst b/Misc/NEWS.d/next/C API/2023-11-10-10-21-38.gh-issue-111262.2utB5m.rst deleted file mode 100644 index d432b7ebd3a565..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-11-10-10-21-38.gh-issue-111262.2utB5m.rst +++ /dev/null @@ -1,4 +0,0 @@ -Add :c:func:`PyDict_Pop` and :c:func:`PyDict_PopString` functions: remove a key -from a dictionary and optionally return the removed value. This is similar to -:meth:`dict.pop`, but without the default value and not raising :exc:`KeyError` -if the key missing. Patch by Stefan Behnel and Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-11-10-10-24-28.gh-issue-111956.ImE6Cx.rst b/Misc/NEWS.d/next/C API/2023-11-10-10-24-28.gh-issue-111956.ImE6Cx.rst deleted file mode 100644 index 30ee07aa2f1f9b..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-11-10-10-24-28.gh-issue-111956.ImE6Cx.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add internal-only one-time initialization API: ``_PyOnceFlag`` and -``_PyOnceFlag_CallOnce``. diff --git a/Misc/NEWS.d/next/C API/2023-11-13-17-57-11.gh-issue-112026.WJLJcI.rst b/Misc/NEWS.d/next/C API/2023-11-13-17-57-11.gh-issue-112026.WJLJcI.rst deleted file mode 100644 index deb82ff7af7d54..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-11-13-17-57-11.gh-issue-112026.WJLJcI.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add again ```` and ```` includes in ``Python.h``, but -don't include them in the limited C API version 3.13 and newer. Patch by -Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-11-15-01-26-59.gh-issue-111545.iAoFtA.rst b/Misc/NEWS.d/next/C API/2023-11-15-01-26-59.gh-issue-111545.iAoFtA.rst new file mode 100644 index 00000000000000..7bde2498acf999 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-11-15-01-26-59.gh-issue-111545.iAoFtA.rst @@ -0,0 +1,2 @@ +Add :c:func:`Py_HashPointer` function to hash a pointer. Patch by Victor +Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-11-15-16-07-57.gh-issue-112026.bnr8dd.rst b/Misc/NEWS.d/next/C API/2023-11-15-16-07-57.gh-issue-112026.bnr8dd.rst deleted file mode 100644 index 5dbbde88fccd2c..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-11-15-16-07-57.gh-issue-112026.bnr8dd.rst +++ /dev/null @@ -1,51 +0,0 @@ -Restore removed private C API functions, macros and structures which have no -simple replacement for now: - -* _PyDict_GetItem_KnownHash() -* _PyDict_NewPresized() -* _PyHASH_BITS -* _PyHASH_IMAG -* _PyHASH_INF -* _PyHASH_MODULUS -* _PyHASH_MULTIPLIER -* _PyLong_Copy() -* _PyLong_FromDigits() -* _PyLong_New() -* _PyLong_Sign() -* _PyObject_CallMethodId() -* _PyObject_CallMethodNoArgs() -* _PyObject_CallMethodOneArg() -* _PyObject_CallOneArg() -* _PyObject_EXTRA_INIT -* _PyObject_FastCallDict() -* _PyObject_GetAttrId() -* _PyObject_Vectorcall() -* _PyObject_VectorcallMethod() -* _PyStack_AsDict() -* _PyThread_CurrentFrames() -* _PyUnicodeWriter structure -* _PyUnicodeWriter_Dealloc() -* _PyUnicodeWriter_Finish() -* _PyUnicodeWriter_Init() -* _PyUnicodeWriter_Prepare() -* _PyUnicodeWriter_PrepareKind() -* _PyUnicodeWriter_WriteASCIIString() -* _PyUnicodeWriter_WriteChar() -* _PyUnicodeWriter_WriteLatin1String() -* _PyUnicodeWriter_WriteStr() -* _PyUnicodeWriter_WriteSubstring() -* _PyUnicode_AsString() -* _PyUnicode_FromId() -* _PyVectorcall_Function() -* _Py_IDENTIFIER() -* _Py_c_abs() -* _Py_c_diff() -* _Py_c_neg() -* _Py_c_pow() -* _Py_c_prod() -* _Py_c_quot() -* _Py_c_sum() -* _Py_static_string() -* _Py_static_string_init() - -Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-11-15-17-10-09.gh-issue-112026.ts9yyn.rst b/Misc/NEWS.d/next/C API/2023-11-15-17-10-09.gh-issue-112026.ts9yyn.rst deleted file mode 100644 index 7d54f6b3626828..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-11-15-17-10-09.gh-issue-112026.ts9yyn.rst +++ /dev/null @@ -1,2 +0,0 @@ -Restore the removed ``_PyDict_GetItemStringWithError()`` function. It is -used by numpy. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-11-15-18-36-21.gh-issue-112026._Yybr5.rst b/Misc/NEWS.d/next/C API/2023-11-15-18-36-21.gh-issue-112026._Yybr5.rst deleted file mode 100644 index 0c39c78a78d1e0..00000000000000 --- a/Misc/NEWS.d/next/C API/2023-11-15-18-36-21.gh-issue-112026._Yybr5.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add again the private ``_PyThreadState_UncheckedGet()`` function as an alias -to the new public :c:func:`PyThreadState_GetUnchecked` function. Patch by -Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2023-11-27-09-44-16.gh-issue-112438.GdNZiI.rst b/Misc/NEWS.d/next/C API/2023-11-27-09-44-16.gh-issue-112438.GdNZiI.rst new file mode 100644 index 00000000000000..113119efd6aebb --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-11-27-09-44-16.gh-issue-112438.GdNZiI.rst @@ -0,0 +1,2 @@ +Fix support of format units "es", "et", "es#", and "et#" in nested tuples in +:c:func:`PyArg_ParseTuple`-like functions. diff --git a/Misc/NEWS.d/next/C API/2023-12-02-02-08-11.gh-issue-106560.THvuji.rst b/Misc/NEWS.d/next/C API/2023-12-02-02-08-11.gh-issue-106560.THvuji.rst new file mode 100644 index 00000000000000..59b461ec47ad64 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-12-02-02-08-11.gh-issue-106560.THvuji.rst @@ -0,0 +1,2 @@ +Fix redundant declarations in the public C API. Declare PyBool_Type, +PyLong_Type and PySys_Audit() only once. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-08-13-13-25-15.bpo-34392.9kIlMF.rst b/Misc/NEWS.d/next/Core and Builtins/2018-08-13-13-25-15.bpo-34392.9kIlMF.rst new file mode 100644 index 00000000000000..bc4fd1ad1f5c7c --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-08-13-13-25-15.bpo-34392.9kIlMF.rst @@ -0,0 +1 @@ +Added :func:`sys._is_interned`. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-27-02-51-45.gh-issue-100445.C8f6ph.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-27-02-51-45.gh-issue-100445.C8f6ph.rst deleted file mode 100644 index 72f38849df9b82..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-27-02-51-45.gh-issue-100445.C8f6ph.rst +++ /dev/null @@ -1 +0,0 @@ -Improve error message for unterminated strings with escapes. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-20-11-41-16.gh-issue-106905.AyZpuB.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-20-11-41-16.gh-issue-106905.AyZpuB.rst deleted file mode 100644 index 73b75833a71284..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-07-20-11-41-16.gh-issue-106905.AyZpuB.rst +++ /dev/null @@ -1 +0,0 @@ -Fix incorrect SystemError about AST constructor recursion depth mismatch. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-08-31-11-42-16.gh-issue-106718._-57DA.rst b/Misc/NEWS.d/next/Core and Builtins/2023-08-31-11-42-16.gh-issue-106718._-57DA.rst deleted file mode 100644 index 4c564bba4122c9..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-08-31-11-42-16.gh-issue-106718._-57DA.rst +++ /dev/null @@ -1,2 +0,0 @@ -When PyConfig.stdlib_dir is explicitly set, it's now respected and won't be -overridden by PyConfig.home. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-06-12-36-11.bpo-46657.xea1T_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-06-12-36-11.bpo-46657.xea1T_.rst deleted file mode 100644 index 193b02a3b911f5..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-09-06-12-36-11.bpo-46657.xea1T_.rst +++ /dev/null @@ -1 +0,0 @@ -Add mimalloc memory allocator support. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-15-23-39-43.gh-issue-103615.WZavly.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-15-23-39-43.gh-issue-103615.WZavly.rst deleted file mode 100644 index 2a0e10b84ccf32..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-09-15-23-39-43.gh-issue-103615.WZavly.rst +++ /dev/null @@ -1 +0,0 @@ -Use local events for opcode tracing diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-09-30-17-30-11.gh-issue-89519.hz2pZf.rst b/Misc/NEWS.d/next/Core and Builtins/2023-09-30-17-30-11.gh-issue-89519.hz2pZf.rst deleted file mode 100644 index fd9d0edf6666c7..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-09-30-17-30-11.gh-issue-89519.hz2pZf.rst +++ /dev/null @@ -1,6 +0,0 @@ -Removed chained :class:`classmethod` descriptors (introduced in -:issue:`19072`). This can no longer be used to wrap other descriptors such -as :class:`property`. The core design of this feature was flawed and caused -a number of downstream problems. To "pass-through" a :class:`classmethod`, -consider using the :attr:`!__wrapped__` attribute that was added in Python -3.10. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-09-19-54-33.gh-issue-110543.1wrxO8.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-09-19-54-33.gh-issue-110543.1wrxO8.rst deleted file mode 100644 index 5f9571566da18d..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-09-19-54-33.gh-issue-110543.1wrxO8.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix regression in Python 3.12 where :meth:`types.CodeType.replace` would -produce a broken code object if called on a module or class code object that -contains a comprehension. Patch by Jelle Zijlstra. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-12-12-09-01.gh-issue-110481.3Er3it.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-12-12-09-01.gh-issue-110481.3Er3it.rst deleted file mode 100644 index 984548aced3df8..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-12-12-09-01.gh-issue-110481.3Er3it.rst +++ /dev/null @@ -1 +0,0 @@ -Implement biased reference counting in ``--disable-gil`` builds. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-12-17-15-23.gh-issue-110722.sjMwQe.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-12-17-15-23.gh-issue-110722.sjMwQe.rst deleted file mode 100644 index 79b941ef6879f8..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-12-17-15-23.gh-issue-110722.sjMwQe.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add :envvar:`PYTHON_PRESITE=package.module` to import a module early in the -interpreter lifecycle before ``site.py`` is executed. Python needs to be -:ref:`built in debug mode ` for this option to exist. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-13-09-21-29.gh-issue-110805.vhU7A7.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-13-09-21-29.gh-issue-110805.vhU7A7.rst deleted file mode 100644 index be90bb3564fd54..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-13-09-21-29.gh-issue-110805.vhU7A7.rst +++ /dev/null @@ -1,2 +0,0 @@ -Allow the repl to show source code and complete tracebacks. Patch by Pablo -Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-13-16-55-55.gh-issue-109094.ziL4cJ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-13-16-55-55.gh-issue-109094.ziL4cJ.rst deleted file mode 100644 index 332afb61e349c9..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-13-16-55-55.gh-issue-109094.ziL4cJ.rst +++ /dev/null @@ -1,3 +0,0 @@ -Replace ``prev_instr`` on the interpreter frame by ``instr_ptr`` which -points to the beginning of the instruction that is currently executing (or -will execute once the frame resumes). diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-14-12-19-34.gh-issue-110864.-baPDE.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-14-12-19-34.gh-issue-110864.-baPDE.rst deleted file mode 100644 index 3d79a7124bd2f2..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-14-12-19-34.gh-issue-110864.-baPDE.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix argument parsing by ``_PyArg_UnpackKeywordsWithVararg`` for functions -defining pos-or-keyword, vararg, and kw-only parameters. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-15-20-45-35.gh-issue-110892.oA6eVY.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-15-20-45-35.gh-issue-110892.oA6eVY.rst deleted file mode 100644 index 4422273d51757c..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-15-20-45-35.gh-issue-110892.oA6eVY.rst +++ /dev/null @@ -1 +0,0 @@ -Return ``NULL`` for ``PyTrace_RETURN`` events caused by an exception diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-15-22-18-45.gh-issue-109894.UAmo06.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-15-22-18-45.gh-issue-109894.UAmo06.rst deleted file mode 100644 index 214853660c5a57..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-15-22-18-45.gh-issue-109894.UAmo06.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed crash due to improperly initialized static :exc:`MemoryError` in subinterpreter. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-16-12-12-48.gh-issue-110912.uEJGi_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-16-12-12-48.gh-issue-110912.uEJGi_.rst deleted file mode 100644 index d70d45ebb931ea..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-16-12-12-48.gh-issue-110912.uEJGi_.rst +++ /dev/null @@ -1,2 +0,0 @@ -Correctly display the traceback for :exc:`MemoryError` exceptions using the -:mod:`traceback` module. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-16-15-51-37.gh-issue-109214.-RGTFH.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-16-15-51-37.gh-issue-109214.-RGTFH.rst deleted file mode 100644 index c24f18cee71fa0..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-16-15-51-37.gh-issue-109214.-RGTFH.rst +++ /dev/null @@ -1 +0,0 @@ -Remove unnecessary instruction pointer updates before returning from frames. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-17-11-03-45.gh-issue-110938.X3sbMb.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-17-11-03-45.gh-issue-110938.X3sbMb.rst deleted file mode 100644 index a2f9319e69de9b..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-17-11-03-45.gh-issue-110938.X3sbMb.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix error messages for indented blocks with functions and classes with -generic type parameters. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-20-23-14-06.gh-issue-111123.jjVc3M.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-20-23-14-06.gh-issue-111123.jjVc3M.rst deleted file mode 100644 index f2cebe287db3ee..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-20-23-14-06.gh-issue-111123.jjVc3M.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug where a :keyword:`global` declaration in an :keyword:`except` block -is rejected when the global is used in the :keyword:`else` block. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-23-15-44-47.gh-issue-67224.S4D6CR.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-23-15-44-47.gh-issue-67224.S4D6CR.rst deleted file mode 100644 index b0474f38cff6f6..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-23-15-44-47.gh-issue-67224.S4D6CR.rst +++ /dev/null @@ -1,2 +0,0 @@ -Show source lines in tracebacks when using the ``-c`` option when running -Python. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-23-22-11-09.gh-issue-94438.y2pITu.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-23-22-11-09.gh-issue-94438.y2pITu.rst deleted file mode 100644 index b6e147a48a8cd8..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-23-22-11-09.gh-issue-94438.y2pITu.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a regression that prevented jumping across ``is None`` and ``is not None`` when debugging. Patch by Savannah Ostrowski. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-26-18-45-20.gh-issue-111354.GrT-Wf.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-26-18-45-20.gh-issue-111354.GrT-Wf.rst deleted file mode 100644 index 96798701066df9..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-26-18-45-20.gh-issue-111354.GrT-Wf.rst +++ /dev/null @@ -1,3 +0,0 @@ -Remove ``oparg`` from :opcode:`YIELD_VALUE`. Change ``oparg`` of -:opcode:`RESUME` to include information about the except-depth. -These changes make it possible to simplify the code in generator close. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-22-09.gh-issue-111374.e9lrPZ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-22-09.gh-issue-111374.e9lrPZ.rst deleted file mode 100644 index 6f18339112466d..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-22-09.gh-issue-111374.e9lrPZ.rst +++ /dev/null @@ -1,3 +0,0 @@ -Added a new environment variable :envvar:`PYTHON_FROZEN_MODULES`. It -determines whether or not frozen modules are ignored by the import machinery, -equivalent of the :option:`-X frozen_modules <-X>` command-line option. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-51-40.gh-issue-111380.vgSbir.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-51-40.gh-issue-111380.vgSbir.rst deleted file mode 100644 index 4ce6398dbfe3b1..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-11-51-40.gh-issue-111380.vgSbir.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug that was causing :exc:`SyntaxWarning` to appear twice when parsing -if invalid syntax is encountered later. Patch by Pablo galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-12-17-49.gh-issue-111366._TSknV.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-27-12-17-49.gh-issue-111366._TSknV.rst deleted file mode 100644 index 7e76ce916ea714..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-12-17-49.gh-issue-111366._TSknV.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix an issue in the :mod:`codeop` that was causing :exc:`SyntaxError` -exceptions raised in the presence of invalid syntax to not contain precise -error messages. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-19-38-33.gh-issue-102388.vd5YUZ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-27-19-38-33.gh-issue-102388.vd5YUZ.rst deleted file mode 100644 index 268a3d310f2b49..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-19-38-33.gh-issue-102388.vd5YUZ.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a bug where ``iso2022_jp_3`` and ``iso2022_jp_2004`` codecs read out of bounds diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-11-35-21.gh-issue-111435.ageUWQ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-29-11-35-21.gh-issue-111435.ageUWQ.rst deleted file mode 100644 index 034615581b6789..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-11-35-21.gh-issue-111435.ageUWQ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add support for sharing of True and False between interpreters using the cross-interpreter -API. Patch by Anthony Shaw. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-12-33-33.gh-issue-111438.bHTLLl.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-29-12-33-33.gh-issue-111438.bHTLLl.rst deleted file mode 100644 index 009ba11ae16683..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-12-33-33.gh-issue-111438.bHTLLl.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add support for sharing floats between interpreters using the cross-interpreter -API. Patch by Anthony Shaw. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-20-11-21.gh-issue-111420.IUT-GK.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-29-20-11-21.gh-issue-111420.IUT-GK.rst deleted file mode 100644 index 6646ecf920eeb5..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-29-20-11-21.gh-issue-111420.IUT-GK.rst +++ /dev/null @@ -1 +0,0 @@ -Allow type comments in parenthesized ``with`` statements diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-31-14-25-21.gh-issue-109181.11h6Mc.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-31-14-25-21.gh-issue-109181.11h6Mc.rst deleted file mode 100644 index 61a15b471cfb27..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-31-14-25-21.gh-issue-109181.11h6Mc.rst +++ /dev/null @@ -1,2 +0,0 @@ -Speed up :obj:`Traceback` object creation by lazily compute the line number. -Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst deleted file mode 100644 index 67ca9c6671f99b..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst +++ /dev/null @@ -1,9 +0,0 @@ -Merge the Tier 1 (bytecode) and Tier 2 (micro-ops) interpreters together, -moving the Tier 2 interpreter loop and switch into -``_PyEval_EvalFrameDefault()`` in ``Python/ceval.c``. -The ``Python/executor.c`` file is gone. -Also the ``TIER_ONE`` and ``TIER_TWO`` macros are now handled -by the code generator. - -**Beware!** This changes the environment variables to enable micro-ops and -their debugging to ``PYTHON_UOPS`` and ``PYTHON_LLTRACE``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-02-14-49-19.gh-issue-111354.gIS3f-.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-02-14-49-19.gh-issue-111354.gIS3f-.rst deleted file mode 100644 index 21d45340489caa..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-02-14-49-19.gh-issue-111354.gIS3f-.rst +++ /dev/null @@ -1,5 +0,0 @@ -The oparg of :opcode:`YIELD_VALUE` is now ``1`` if the instruction is part -of a yield-from or await, and ``0`` otherwise. - -The SUSPENDED frame state is now split into ``SUSPENDED`` and -``SUSPENDED_YIELD_FROM``. This simplifies the code in ``_PyGen_yf``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-02-15-00-57.gh-issue-111623.BZxYc8.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-02-15-00-57.gh-issue-111623.BZxYc8.rst deleted file mode 100644 index 3a75d5e36841b4..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-02-15-00-57.gh-issue-111623.BZxYc8.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add support for sharing tuples between interpreters using the cross-interpreter -API. Patch by Anthony Shaw. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-01-04-55.gh-issue-111654.scUhDO.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-03-01-04-55.gh-issue-111654.scUhDO.rst deleted file mode 100644 index e9a896e660916f..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-01-04-55.gh-issue-111654.scUhDO.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix runtime crash when some error happens in opcode -``LOAD_FROM_DICT_OR_DEREF``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-01-23-48.gh-issue-111666.l8Q8G5.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-03-01-23-48.gh-issue-111666.l8Q8G5.rst deleted file mode 100644 index 1d742a7c918599..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-01-23-48.gh-issue-111666.l8Q8G5.rst +++ /dev/null @@ -1,3 +0,0 @@ -Speed up :meth:`BaseExceptionGroup.derive`, -:meth:`BaseExceptionGroup.subgroup`, and :meth:`BaseExceptionGroup.split` by -changing how they parse passed arguments. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-19-25-38.gh-issue-111772.aRQvOn.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-03-19-25-38.gh-issue-111772.aRQvOn.rst deleted file mode 100644 index 79ae5ab4c8ff3b..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-19-25-38.gh-issue-111772.aRQvOn.rst +++ /dev/null @@ -1 +0,0 @@ -Specialize slot loads and stores for _Py_T_OBJECT as well as Py_T_OBJECT_EX diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-22-48-29.gh-issue-109369.ELYaxJ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-03-22-48-29.gh-issue-109369.ELYaxJ.rst deleted file mode 100644 index 1a73afca756f9e..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-03-22-48-29.gh-issue-109369.ELYaxJ.rst +++ /dev/null @@ -1 +0,0 @@ -Make sure that tier 2 traces are de-optimized if the code is instrumented diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-04-13-36-51.gh-issue-110829.Pa0CJI.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-04-13-36-51.gh-issue-110829.Pa0CJI.rst deleted file mode 100644 index f4fa61db369ece..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-04-13-36-51.gh-issue-110829.Pa0CJI.rst +++ /dev/null @@ -1 +0,0 @@ -Joining a thread now ensures the underlying OS thread has exited. This is required for safer fork() in multi-threaded processes. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-05-06-40-35.gh-issue-111843.c045cB.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-05-06-40-35.gh-issue-111843.c045cB.rst deleted file mode 100644 index 280f8f9bf0b8c0..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-05-06-40-35.gh-issue-111843.c045cB.rst +++ /dev/null @@ -1,2 +0,0 @@ -Use exponential backoff to reduce the number of failed tier 2 optimization -attempts by over 99%. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-05-20-59-10.gh-issue-81925.wKHLSS.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-05-20-59-10.gh-issue-81925.wKHLSS.rst deleted file mode 100644 index 9caa5cf6abe1ff..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-05-20-59-10.gh-issue-81925.wKHLSS.rst +++ /dev/null @@ -1 +0,0 @@ -Implement native thread ids for GNU KFreeBSD. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-06-16-44-09.gh-issue-79932.2qv7uD.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-06-16-44-09.gh-issue-79932.2qv7uD.rst deleted file mode 100644 index 543dbe4413027a..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-06-16-44-09.gh-issue-79932.2qv7uD.rst +++ /dev/null @@ -1 +0,0 @@ -Raise exception if :meth:`frame.clear` is called on a suspended frame. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-07-12-59-02.gh-issue-81137.qFpJCY.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-07-12-59-02.gh-issue-81137.qFpJCY.rst deleted file mode 100644 index 5ca1dda63cb222..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-07-12-59-02.gh-issue-81137.qFpJCY.rst +++ /dev/null @@ -1,2 +0,0 @@ -Deprecate assignment to a function's ``__code__`` field when the new code -object is of a mismatched type (e.g., from a generator to a plain function). diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-14-22-12-11.gh-issue-111916.ZGCayL.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-14-22-12-11.gh-issue-111916.ZGCayL.rst deleted file mode 100644 index b3814828e476b9..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-14-22-12-11.gh-issue-111916.ZGCayL.rst +++ /dev/null @@ -1 +0,0 @@ -Make hashlib related modules thread-safe without the GIL diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-15-20-20-51.gh-issue-111798.cs-3t3.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-15-20-20-51.gh-issue-111798.cs-3t3.rst deleted file mode 100644 index 24bb4ec096c2d1..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-11-15-20-20-51.gh-issue-111798.cs-3t3.rst +++ /dev/null @@ -1,4 +0,0 @@ -When Python is built in debug mode, set the C recursion limit to 500 instead -of 1500. A debug build is likely built with low optimization level which -implies higher stack memory usage than a release build. Patch by Victor -Stinner. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-24-14-10-57.gh-issue-112367.9z1IDp.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-24-14-10-57.gh-issue-112367.9z1IDp.rst new file mode 100644 index 00000000000000..991e45ad47fabe --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-24-14-10-57.gh-issue-112367.9z1IDp.rst @@ -0,0 +1,2 @@ +Avoid undefined behaviour when using the perf trampolines by not freeing the +code arenas until shutdown. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-25-20-36-38.gh-issue-99606.fDY5hK.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-25-20-36-38.gh-issue-99606.fDY5hK.rst new file mode 100644 index 00000000000000..adc0e3a6bbc89a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-25-20-36-38.gh-issue-99606.fDY5hK.rst @@ -0,0 +1,2 @@ +Make code generated for an empty f-string identical to the code of an empty +normal string. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-25-22-39-44.gh-issue-112387.AbBq5W.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-25-22-39-44.gh-issue-112387.AbBq5W.rst new file mode 100644 index 00000000000000..adac11bf4c90a1 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-25-22-39-44.gh-issue-112387.AbBq5W.rst @@ -0,0 +1,2 @@ +Fix error positions for decoded strings with backwards tokenize errors. +Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-25-22-58-49.gh-issue-112388.MU3cIM.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-25-22-58-49.gh-issue-112388.MU3cIM.rst new file mode 100644 index 00000000000000..1c82be2febda4f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-25-22-58-49.gh-issue-112388.MU3cIM.rst @@ -0,0 +1,2 @@ +Fix an error that was causing the parser to try to overwrite tokenizer +errors. Patch by pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-26-21-30-11.gh-issue-111058.q4DqDY.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-26-21-30-11.gh-issue-111058.q4DqDY.rst new file mode 100644 index 00000000000000..de5661f911aa82 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-26-21-30-11.gh-issue-111058.q4DqDY.rst @@ -0,0 +1,3 @@ +Change coro.cr_frame/gen.gi_frame to return ``None`` after the coroutine/generator has been closed. +This fixes a bug where :func:`~inspect.getcoroutinestate` and :func:`~inspect.getgeneratorstate` +return the wrong state for a closed coroutine/generator. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-27-18-55-30.gh-issue-112217.SwFLMj.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-27-18-55-30.gh-issue-112217.SwFLMj.rst new file mode 100644 index 00000000000000..d4efbab6b2d128 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-27-18-55-30.gh-issue-112217.SwFLMj.rst @@ -0,0 +1 @@ +Add check for the type of ``__cause__`` returned from calling the type ``T`` in ``raise from T``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-01-19-02-21.gh-issue-105967.Puq5Cn.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-01-19-02-21.gh-issue-105967.Puq5Cn.rst new file mode 100644 index 00000000000000..c69511218e3e16 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-01-19-02-21.gh-issue-105967.Puq5Cn.rst @@ -0,0 +1,4 @@ +Workaround a bug in Apple's macOS platform zlib library where +:func:`zlib.crc32` and :func:`binascii.crc32` could produce incorrect results +on multi-gigabyte inputs. Including when using :mod:`zipfile` on zips +containing large data. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-03-15-29-53.gh-issue-112660.gldBvh.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-03-15-29-53.gh-issue-112660.gldBvh.rst new file mode 100644 index 00000000000000..ea9052b3e35c48 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-03-15-29-53.gh-issue-112660.gldBvh.rst @@ -0,0 +1,2 @@ +Do not clear unexpected errors during formatting error messages for +ImportError and AttributeError for modules. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-03-19-34-51.gh-issue-112625.QWTlwS.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-03-19-34-51.gh-issue-112625.QWTlwS.rst new file mode 100644 index 00000000000000..4970e10f3f4dcb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-03-19-34-51.gh-issue-112625.QWTlwS.rst @@ -0,0 +1 @@ +Fixes a bug where a bytearray object could be cleared while iterating over an argument in the ``bytearray.join()`` method that could result in reading memory after it was freed. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-04-23-09-07.gh-issue-112730.BXHlFa.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-04-23-09-07.gh-issue-112730.BXHlFa.rst new file mode 100644 index 00000000000000..51758dd5f4c318 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-04-23-09-07.gh-issue-112730.BXHlFa.rst @@ -0,0 +1 @@ +Use color to highlight error locations in tracebacks. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-07-12-00-04.gh-issue-74616.kgTGVb.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-07-12-00-04.gh-issue-74616.kgTGVb.rst new file mode 100644 index 00000000000000..5c345be9de6d0b --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-07-12-00-04.gh-issue-74616.kgTGVb.rst @@ -0,0 +1,2 @@ +:func:`input` now raises a ValueError when output on the terminal if the +prompt contains embedded null characters instead of silently truncating it. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-07-13-19-55.gh-issue-112125.4ADN7i.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-07-13-19-55.gh-issue-112125.4ADN7i.rst new file mode 100644 index 00000000000000..52cd45029fb8c7 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-07-13-19-55.gh-issue-112125.4ADN7i.rst @@ -0,0 +1 @@ +Fix None.__ne__(None) returning NotImplemented instead of False diff --git a/Misc/NEWS.d/next/Documentation/2023-11-30-02-33-59.gh-issue-111699._O5G_y.rst b/Misc/NEWS.d/next/Documentation/2023-11-30-02-33-59.gh-issue-111699._O5G_y.rst new file mode 100644 index 00000000000000..2d31345e6c2044 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-11-30-02-33-59.gh-issue-111699._O5G_y.rst @@ -0,0 +1 @@ +Relocate ``smtpd`` deprecation notice to its own section rather than under ``locale`` in What's New in Python 3.12 document diff --git a/Misc/NEWS.d/next/Library/2019-06-14-22-37-32.bpo-37260.oecdIf.rst b/Misc/NEWS.d/next/Library/2019-06-14-22-37-32.bpo-37260.oecdIf.rst new file mode 100644 index 00000000000000..a5f2c5e8e18919 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-14-22-37-32.bpo-37260.oecdIf.rst @@ -0,0 +1,2 @@ +Fixed a race condition in :func:`shutil.rmtree` in which directory entries removed by another process or thread while ``shutil.rmtree()`` is running can cause it to raise FileNotFoundError. Patch by Jeffrey Kintscher. + diff --git a/Misc/NEWS.d/next/Library/2020-03-09-15-08-29.bpo-39912.xPOBBY.rst b/Misc/NEWS.d/next/Library/2020-03-09-15-08-29.bpo-39912.xPOBBY.rst new file mode 100644 index 00000000000000..fb8579725a2d7d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-03-09-15-08-29.bpo-39912.xPOBBY.rst @@ -0,0 +1,3 @@ +:func:`warnings.filterwarnings()` and :func:`warnings.simplefilter()` now raise +appropriate exceptions instead of ``AssertionError``. Patch contributed by +Rémi Lapeyre. diff --git a/Misc/NEWS.d/next/Library/2020-05-21-23-32-46.bpo-40262.z4fQv1.rst b/Misc/NEWS.d/next/Library/2020-05-21-23-32-46.bpo-40262.z4fQv1.rst new file mode 100644 index 00000000000000..c017a1c8df09d8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-21-23-32-46.bpo-40262.z4fQv1.rst @@ -0,0 +1,2 @@ +The :meth:`ssl.SSLSocket.recv_into` method no longer requires the *buffer* +argument to implement ``__len__`` and supports buffers with arbitrary item size. diff --git a/Misc/NEWS.d/next/Library/2020-07-28-20-48-05.bpo-41422.iMwnMu.rst b/Misc/NEWS.d/next/Library/2020-07-28-20-48-05.bpo-41422.iMwnMu.rst new file mode 100644 index 00000000000000..8bde68f8f2afc8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-07-28-20-48-05.bpo-41422.iMwnMu.rst @@ -0,0 +1,2 @@ +Fixed memory leaks of :class:`pickle.Pickler` and :class:`pickle.Unpickler` involving cyclic references via the +internal memo mapping. diff --git a/Misc/NEWS.d/next/Library/2020-12-14-09-31-13.bpo-35332.s22wAx.rst b/Misc/NEWS.d/next/Library/2020-12-14-09-31-13.bpo-35332.s22wAx.rst new file mode 100644 index 00000000000000..80564b99a079c6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-12-14-09-31-13.bpo-35332.s22wAx.rst @@ -0,0 +1,3 @@ +The :func:`shutil.rmtree` function now ignores errors when calling +:func:`os.close` when *ignore_errors* is ``True``, and +:func:`os.close` no longer retried after error. diff --git a/Misc/NEWS.d/next/Library/2021-11-23-22-22-49.bpo-32731.kNOASr.rst b/Misc/NEWS.d/next/Library/2021-11-23-22-22-49.bpo-32731.kNOASr.rst new file mode 100644 index 00000000000000..92f3b870c11131 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-11-23-22-22-49.bpo-32731.kNOASr.rst @@ -0,0 +1,3 @@ +:func:`getpass.getuser` now raises :exc:`OSError` for all failures rather +than :exc:`ImportError` on systems lacking the :mod:`pwd` module or +:exc:`KeyError` if the password database is empty. diff --git a/Misc/NEWS.d/next/Library/2021-12-06-22-10-53.bpo-43153.J7mjSy.rst b/Misc/NEWS.d/next/Library/2021-12-06-22-10-53.bpo-43153.J7mjSy.rst new file mode 100644 index 00000000000000..7800e0a4869adf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-12-06-22-10-53.bpo-43153.J7mjSy.rst @@ -0,0 +1,4 @@ +On Windows, ``tempfile.TemporaryDirectory`` previously masked a +``PermissionError`` with ``NotADirectoryError`` during directory cleanup. It +now correctly raises ``PermissionError`` if errors are not ignored. Patch by +Andrei Kulakov and Ken Jin. diff --git a/Misc/NEWS.d/next/Library/2022-05-06-15-49-57.gh-issue-86826.rf006W.rst b/Misc/NEWS.d/next/Library/2022-05-06-15-49-57.gh-issue-86826.rf006W.rst deleted file mode 100644 index 02cd75eec4be9e..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-05-06-15-49-57.gh-issue-86826.rf006W.rst +++ /dev/null @@ -1,4 +0,0 @@ -:mod:`zipinfo` now supports the full range of values in the TZ string -determined by RFC 8536 and detects all invalid formats. -Both Python and C implementations now raise exceptions of the same -type on invalid data. diff --git a/Misc/NEWS.d/next/Library/2022-10-05-15-01-36.gh-issue-96954.ezwkrU.rst b/Misc/NEWS.d/next/Library/2022-10-05-15-01-36.gh-issue-96954.ezwkrU.rst deleted file mode 100644 index f4886bd3cc27f5..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-10-05-15-01-36.gh-issue-96954.ezwkrU.rst +++ /dev/null @@ -1,5 +0,0 @@ -Switch the storage of the unicode codepoint names to use a different -data-structure, a `directed acyclic word graph -`_. -This makes the unicodedata shared library about 440 KiB smaller. Contributed by -Carl Friedrich Bolz-Tereick using code from the PyPy project. diff --git a/Misc/NEWS.d/next/Library/2022-10-14-21-11-10.gh-issue-97928.Pdxh1G.rst b/Misc/NEWS.d/next/Library/2022-10-14-21-11-10.gh-issue-97928.Pdxh1G.rst deleted file mode 100644 index 4acf396f840d61..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-10-14-21-11-10.gh-issue-97928.Pdxh1G.rst +++ /dev/null @@ -1,6 +0,0 @@ -Change the behavior of :meth:`tkinter.Text.count`. It now always returns an -integer if one or less counting options are specified. Previously it could -return a single count as a 1-tuple, an integer (only if option ``"update"`` -was specified) or ``None`` if no items found. The result is now the same if -``wantobjects`` is set to ``0``. - diff --git a/Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst b/Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst new file mode 100644 index 00000000000000..7991048fc48e03 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst @@ -0,0 +1,2 @@ +Fix a bug in :class:`tempfile.TemporaryDirectory` cleanup, which now no longer +dereferences symlinks when working around file system permission errors. diff --git a/Misc/NEWS.d/next/Library/2023-03-22-02-01-30.gh-issue-102895.HiEqaZ.rst b/Misc/NEWS.d/next/Library/2023-03-22-02-01-30.gh-issue-102895.HiEqaZ.rst deleted file mode 100644 index 20a1a5baccd24b..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-03-22-02-01-30.gh-issue-102895.HiEqaZ.rst +++ /dev/null @@ -1 +0,0 @@ -Added a parameter ``local_exit`` for :func:`code.interact` to prevent ``exit()`` and ``quit`` from closing ``sys.stdin`` and raise ``SystemExit``. diff --git a/Misc/NEWS.d/next/Library/2023-04-15-14-45-21.gh-issue-102956.Z6qeUy.rst b/Misc/NEWS.d/next/Library/2023-04-15-14-45-21.gh-issue-102956.Z6qeUy.rst deleted file mode 100644 index 1a4bb9bc0dc46a..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-04-15-14-45-21.gh-issue-102956.Z6qeUy.rst +++ /dev/null @@ -1 +0,0 @@ -Fix returning of empty byte strings after seek in zipfile module diff --git a/Misc/NEWS.d/next/Library/2023-04-26-16-37-00.gh-issue-90890.fIag4w.rst b/Misc/NEWS.d/next/Library/2023-04-26-16-37-00.gh-issue-90890.fIag4w.rst deleted file mode 100644 index ee2e69eb27980f..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-04-26-16-37-00.gh-issue-90890.fIag4w.rst +++ /dev/null @@ -1,7 +0,0 @@ -New methods :meth:`mailbox.Maildir.get_info`, -:meth:`mailbox.Maildir.set_info`, :meth:`mailbox.Maildir.get_flags`, -:meth:`mailbox.Maildir.set_flags`, :meth:`mailbox.Maildir.add_flag`, -:meth:`mailbox.Maildir.remove_flag`. These methods speed up accessing a -message's info and/or flags and are useful when it is not necessary to -access the message's contents, as when iterating over a Maildir to find -messages with specific flags. diff --git a/Misc/NEWS.d/next/Library/2023-04-29-20-49-13.gh-issue-104003.-8Ruk2.rst b/Misc/NEWS.d/next/Library/2023-04-29-20-49-13.gh-issue-104003.-8Ruk2.rst new file mode 100644 index 00000000000000..82d61ca8b8bc97 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-04-29-20-49-13.gh-issue-104003.-8Ruk2.rst @@ -0,0 +1,3 @@ +Add :func:`warnings.deprecated`, a decorator to mark deprecated functions to +static type checkers and to warn on usage of deprecated classes and functions. +See :pep:`702`. Patch by Jelle Zijlstra. diff --git a/Misc/NEWS.d/next/Library/2023-05-30-02-01-14.gh-issue-90026.FyCXw8.rst b/Misc/NEWS.d/next/Library/2023-05-30-02-01-14.gh-issue-90026.FyCXw8.rst deleted file mode 100644 index 3e32c9836f0cff..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-05-30-02-01-14.gh-issue-90026.FyCXw8.rst +++ /dev/null @@ -1 +0,0 @@ -Define ``USE_XATTRS`` on Cygwin so that XATTR-related functions in the :mod:`os` module become available. diff --git a/Misc/NEWS.d/next/Library/2023-07-13-00-24-52.gh-issue-72904.Yn5-j0.rst b/Misc/NEWS.d/next/Library/2023-07-13-00-24-52.gh-issue-72904.Yn5-j0.rst deleted file mode 100644 index edc8ab07bb06b3..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-13-00-24-52.gh-issue-72904.Yn5-j0.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :func:`glob.translate`. This function converts a pathname with shell-style -wildcards to a regular expression. diff --git a/Misc/NEWS.d/next/Library/2023-07-29-19-00-39.gh-issue-107431.1GzJ2p.rst b/Misc/NEWS.d/next/Library/2023-07-29-19-00-39.gh-issue-107431.1GzJ2p.rst deleted file mode 100644 index fb5bd8cb7cad47..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-29-19-00-39.gh-issue-107431.1GzJ2p.rst +++ /dev/null @@ -1,2 +0,0 @@ -Make the ``DictProxy`` and ``ListProxy`` types in :mod:`multiprocessing.managers` -:ref:`Generic Alias Types` for ``[]`` use in typing contexts. diff --git a/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst b/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst new file mode 100644 index 00000000000000..f582ad5df39e84 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst @@ -0,0 +1 @@ +Support tab completion in :mod:`cmd` for ``editline``. diff --git a/Misc/NEWS.d/next/Library/2023-08-14-21-10-52.gh-issue-103363.u64_QI.rst b/Misc/NEWS.d/next/Library/2023-08-14-21-10-52.gh-issue-103363.u64_QI.rst new file mode 100644 index 00000000000000..d4a27d624eb5e6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-14-21-10-52.gh-issue-103363.u64_QI.rst @@ -0,0 +1,2 @@ +Add *follow_symlinks* keyword-only argument to :meth:`pathlib.Path.owner` +and :meth:`~pathlib.Path.group`, defaulting to ``True``. diff --git a/Misc/NEWS.d/next/Library/2023-08-30-19-10-35.gh-issue-105931.Lpwve8.rst b/Misc/NEWS.d/next/Library/2023-08-30-19-10-35.gh-issue-105931.Lpwve8.rst deleted file mode 100644 index 4e769ec4c47561..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-30-19-10-35.gh-issue-105931.Lpwve8.rst +++ /dev/null @@ -1,8 +0,0 @@ -Change :mod:`compileall` to only strip the stripdir prefix from the full path -recorded in the compiled ``.pyc`` file, when the prefix matches the start of -the full path in its entirety. When the prefix does not match, no stripping is -performed and a warning to this effect is displayed. - -Previously all path components of the stripdir prefix that matched the full -path were removed, while those that did not match were left alone (including -ones interspersed between matching components). diff --git a/Misc/NEWS.d/next/Library/2023-09-02-16-07-23.gh-issue-108791.fBcAqh.rst b/Misc/NEWS.d/next/Library/2023-09-02-16-07-23.gh-issue-108791.fBcAqh.rst deleted file mode 100644 index 84a2cd589e10d5..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-02-16-07-23.gh-issue-108791.fBcAqh.rst +++ /dev/null @@ -1 +0,0 @@ -Improved error handling in :mod:`pdb` command line interface, making it produce more concise error messages. diff --git a/Misc/NEWS.d/next/Library/2023-09-08-12-10-10.gh-issue-85098.DfQbeJ.rst b/Misc/NEWS.d/next/Library/2023-09-08-12-10-10.gh-issue-85098.DfQbeJ.rst deleted file mode 100644 index cf0e782237b2c0..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-08-12-10-10.gh-issue-85098.DfQbeJ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Implement the CLI of the :mod:`symtable` module and improve the repr of -:class:`~symtable.Symbol`. diff --git a/Misc/NEWS.d/next/Library/2023-09-15-12-30-21.gh-issue-109466.6ah-aw.rst b/Misc/NEWS.d/next/Library/2023-09-15-12-30-21.gh-issue-109466.6ah-aw.rst deleted file mode 100644 index e8e5be320ea805..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-15-12-30-21.gh-issue-109466.6ah-aw.rst +++ /dev/null @@ -1 +0,0 @@ -Add the :attr:`ipaddress.IPv4Address.ipv6_mapped` property, which retuns the IPv4-mapped IPv6 address. diff --git a/Misc/NEWS.d/next/Library/2023-09-23-14-40-51.gh-issue-109786.UX3pKv.rst b/Misc/NEWS.d/next/Library/2023-09-23-14-40-51.gh-issue-109786.UX3pKv.rst new file mode 100644 index 00000000000000..07222fa339d703 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-23-14-40-51.gh-issue-109786.UX3pKv.rst @@ -0,0 +1,2 @@ +Fix possible reference leaks and crash when re-enter the ``__next__()`` method of +:class:`itertools.pairwise`. diff --git a/Misc/NEWS.d/next/Library/2023-09-25-20-05-41.gh-issue-109747._cRJH8.rst b/Misc/NEWS.d/next/Library/2023-09-25-20-05-41.gh-issue-109747._cRJH8.rst deleted file mode 100644 index b64ba627897a1a..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-09-25-20-05-41.gh-issue-109747._cRJH8.rst +++ /dev/null @@ -1,3 +0,0 @@ -Improve errors for unsupported look-behind patterns. Now re.error is raised -instead of OverflowError or RuntimeError for too large width of look-behind -pattern. diff --git a/Misc/NEWS.d/next/Library/2023-10-02-05-23-27.gh-issue-110196.djwt0z.rst b/Misc/NEWS.d/next/Library/2023-10-02-05-23-27.gh-issue-110196.djwt0z.rst deleted file mode 100644 index 341f3380fffd60..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-02-05-23-27.gh-issue-110196.djwt0z.rst +++ /dev/null @@ -1 +0,0 @@ -Add ``__reduce__`` method to :class:`IPv6Address` in order to keep ``scope_id`` diff --git a/Misc/NEWS.d/next/Library/2023-10-08-14-17-06.gh-issue-110395._tdCsV.rst b/Misc/NEWS.d/next/Library/2023-10-08-14-17-06.gh-issue-110395._tdCsV.rst deleted file mode 100644 index eb9bcf1f337fb3..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-08-14-17-06.gh-issue-110395._tdCsV.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensure that :func:`select.kqueue` objects correctly appear as closed in -forked children, to prevent operations on an invalid file descriptor. diff --git a/Misc/NEWS.d/next/Library/2023-10-08-18-38-09.gh-issue-88434.2Q_IkG.rst b/Misc/NEWS.d/next/Library/2023-10-08-18-38-09.gh-issue-88434.2Q_IkG.rst deleted file mode 100644 index 9722be251bfcad..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-08-18-38-09.gh-issue-88434.2Q_IkG.rst +++ /dev/null @@ -1,3 +0,0 @@ -Emit deprecation warning for non-integer numbers in :mod:`gettext` functions -and methods that consider plural forms even if the translation was not -found. diff --git a/Misc/NEWS.d/next/Library/2023-10-09-23-59-04.gh-issue-59013.qPbS-G.rst b/Misc/NEWS.d/next/Library/2023-10-09-23-59-04.gh-issue-59013.qPbS-G.rst deleted file mode 100644 index 57915f5fb4368d..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-09-23-59-04.gh-issue-59013.qPbS-G.rst +++ /dev/null @@ -1 +0,0 @@ -Make line number of function breakpoint more precise in :mod:`pdb` diff --git a/Misc/NEWS.d/next/Library/2023-10-10-17-56-41.gh-issue-110392.6g6CnP.rst b/Misc/NEWS.d/next/Library/2023-10-10-17-56-41.gh-issue-110392.6g6CnP.rst deleted file mode 100644 index 47e4e8ee1f058d..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-10-17-56-41.gh-issue-110392.6g6CnP.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix :func:`tty.setraw` and :func:`tty.setcbreak`: previously they returned -partially modified list of the original tty attributes. -:func:`tty.cfmakeraw` and :func:`tty.cfmakecbreak` now make a copy of the -list of special characters before modifying it. diff --git a/Misc/NEWS.d/next/Library/2023-10-12-15-16-44.gh-issue-110774.AdCb5A.rst b/Misc/NEWS.d/next/Library/2023-10-12-15-16-44.gh-issue-110774.AdCb5A.rst deleted file mode 100644 index a5a9fed11864d6..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-12-15-16-44.gh-issue-110774.AdCb5A.rst +++ /dev/null @@ -1 +0,0 @@ -Support setting the :class:`asyncio.Runner` loop_factory kwarg in :class:`unittest.IsolatedAsyncioTestCase` diff --git a/Misc/NEWS.d/next/Library/2023-10-12-18-19-47.gh-issue-82300.P8-O38.rst b/Misc/NEWS.d/next/Library/2023-10-12-18-19-47.gh-issue-82300.P8-O38.rst new file mode 100644 index 00000000000000..d7e6b225489b99 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-12-18-19-47.gh-issue-82300.P8-O38.rst @@ -0,0 +1 @@ +Add ``track`` parameter to :class:`multiprocessing.shared_memory.SharedMemory` that allows using shared memory blocks without having to register with the POSIX resource tracker that automatically releases them upon process exit. diff --git a/Misc/NEWS.d/next/Library/2023-10-13-06-47-20.gh-issue-110771.opwdlc.rst b/Misc/NEWS.d/next/Library/2023-10-13-06-47-20.gh-issue-110771.opwdlc.rst deleted file mode 100644 index a22f8a0d5e56c3..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-13-06-47-20.gh-issue-110771.opwdlc.rst +++ /dev/null @@ -1 +0,0 @@ -Expose the setup and cleanup portions of ``asyncio.run_forever()`` as the standalone methods ``asyncio.run_forever_setup()`` and ``asyncio.run_forever_cleanup()``. This allows for tighter integration with GUI event loops. diff --git a/Misc/NEWS.d/next/Library/2023-10-14-20-15-53.gh-issue-80675._M-cQC.rst b/Misc/NEWS.d/next/Library/2023-10-14-20-15-53.gh-issue-80675._M-cQC.rst deleted file mode 100644 index a4bc679be905d6..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-14-20-15-53.gh-issue-80675._M-cQC.rst +++ /dev/null @@ -1 +0,0 @@ -Set ``f_trace_lines = True`` on all frames upon :func:`pdb.set_trace()` diff --git a/Misc/NEWS.d/next/Library/2023-10-14-21-33-57.gh-issue-84583.-Cmn4_.rst b/Misc/NEWS.d/next/Library/2023-10-14-21-33-57.gh-issue-84583.-Cmn4_.rst deleted file mode 100644 index aa86da461a0b6d..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-14-21-33-57.gh-issue-84583.-Cmn4_.rst +++ /dev/null @@ -1 +0,0 @@ -Make :mod:`pdb` enter post-mortem mode even for :exc:`SyntaxError` diff --git a/Misc/NEWS.d/next/Library/2023-10-16-18-41-51.gh-issue-110944.CmUKXo.rst b/Misc/NEWS.d/next/Library/2023-10-16-18-41-51.gh-issue-110944.CmUKXo.rst deleted file mode 100644 index ec9ca5a11f1ac8..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-16-18-41-51.gh-issue-110944.CmUKXo.rst +++ /dev/null @@ -1 +0,0 @@ -Support alias and convenience vars for :mod:`pdb` completion diff --git a/Misc/NEWS.d/next/Library/2023-10-19-22-46-34.gh-issue-111092.hgut12.rst b/Misc/NEWS.d/next/Library/2023-10-19-22-46-34.gh-issue-111092.hgut12.rst deleted file mode 100644 index 487bd177d27e31..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-19-22-46-34.gh-issue-111092.hgut12.rst +++ /dev/null @@ -1 +0,0 @@ -Make turtledemo run without default root enabled. diff --git a/Misc/NEWS.d/next/Library/2023-10-20-15-29-10.gh-issue-110910.u2oPwX.rst b/Misc/NEWS.d/next/Library/2023-10-20-15-29-10.gh-issue-110910.u2oPwX.rst deleted file mode 100644 index c750447e9fe4a5..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-20-15-29-10.gh-issue-110910.u2oPwX.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix invalid state handling in :class:`asyncio.TaskGroup` and -:class:`asyncio.Timeout`. They now raise proper RuntimeError if they are -improperly used and are left in consistent state after this. diff --git a/Misc/NEWS.d/next/Library/2023-10-21-13-57-06.gh-issue-111159.GoHp7s.rst b/Misc/NEWS.d/next/Library/2023-10-21-13-57-06.gh-issue-111159.GoHp7s.rst deleted file mode 100644 index bdec4f4443d80b..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-21-13-57-06.gh-issue-111159.GoHp7s.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :mod:`doctest` output comparison for exceptions with notes. diff --git a/Misc/NEWS.d/next/Library/2023-10-22-21-28-05.gh-issue-111187._W11Ab.rst b/Misc/NEWS.d/next/Library/2023-10-22-21-28-05.gh-issue-111187._W11Ab.rst deleted file mode 100644 index dc2424370bb96c..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-22-21-28-05.gh-issue-111187._W11Ab.rst +++ /dev/null @@ -1 +0,0 @@ -Postpone removal version for locale.getdefaultlocale() to Python 3.15. diff --git a/Misc/NEWS.d/next/Library/2023-10-23-03-49-34.gh-issue-102980.aXBd54.rst b/Misc/NEWS.d/next/Library/2023-10-23-03-49-34.gh-issue-102980.aXBd54.rst new file mode 100644 index 00000000000000..d4bae4790d6fa4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-23-03-49-34.gh-issue-102980.aXBd54.rst @@ -0,0 +1 @@ +Redirect the output of ``interact`` command of :mod:`pdb` to the same channel as the debugger. Add tests and improve docs. diff --git a/Misc/NEWS.d/next/Library/2023-10-23-13-53-58.gh-issue-111174.Oohmzd.rst b/Misc/NEWS.d/next/Library/2023-10-23-13-53-58.gh-issue-111174.Oohmzd.rst deleted file mode 100644 index 95c315404d0ee6..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-23-13-53-58.gh-issue-111174.Oohmzd.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix crash in :meth:`io.BytesIO.getbuffer` called repeatedly for empty -BytesIO. diff --git a/Misc/NEWS.d/next/Library/2023-10-23-22-40-47.gh-issue-111230.k3Jm84.rst b/Misc/NEWS.d/next/Library/2023-10-23-22-40-47.gh-issue-111230.k3Jm84.rst deleted file mode 100644 index 61d035853db070..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-23-22-40-47.gh-issue-111230.k3Jm84.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :mod:`ssl` not checking for errors when initializing. diff --git a/Misc/NEWS.d/next/Library/2023-10-23-23-14-54.gh-issue-111233.sCdCC0.rst b/Misc/NEWS.d/next/Library/2023-10-23-23-14-54.gh-issue-111233.sCdCC0.rst deleted file mode 100644 index 86d622a74095ab..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-23-23-14-54.gh-issue-111233.sCdCC0.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :mod:`select` not checking for errors when initializing. diff --git a/Misc/NEWS.d/next/Library/2023-10-24-12-09-46.gh-issue-111251.urFYtn.rst b/Misc/NEWS.d/next/Library/2023-10-24-12-09-46.gh-issue-111251.urFYtn.rst deleted file mode 100644 index 3a87cb25da5cb4..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-24-12-09-46.gh-issue-111251.urFYtn.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :mod:`_blake2` not checking for errors when initializing. diff --git a/Misc/NEWS.d/next/Library/2023-10-24-12-20-46.gh-issue-111253.HFywSK.rst b/Misc/NEWS.d/next/Library/2023-10-24-12-20-46.gh-issue-111253.HFywSK.rst deleted file mode 100644 index e21a42605aeaf6..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-24-12-20-46.gh-issue-111253.HFywSK.rst +++ /dev/null @@ -1 +0,0 @@ -Add error checking during :mod:`!_socket` module init. diff --git a/Misc/NEWS.d/next/Library/2023-10-25-08-42-05.gh-issue-111295.H2K4lf.rst b/Misc/NEWS.d/next/Library/2023-10-25-08-42-05.gh-issue-111295.H2K4lf.rst deleted file mode 100644 index 28b85ec3eadab7..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-25-08-42-05.gh-issue-111295.H2K4lf.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :mod:`time` not checking for errors when initializing. diff --git a/Misc/NEWS.d/next/Library/2023-10-25-11-13-35.gh-issue-111259.z7ndeA.rst b/Misc/NEWS.d/next/Library/2023-10-25-11-13-35.gh-issue-111259.z7ndeA.rst deleted file mode 100644 index 4b597f51a955a7..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-25-11-13-35.gh-issue-111259.z7ndeA.rst +++ /dev/null @@ -1 +0,0 @@ -Optimize recursive wildcards in :mod:`pathlib`. diff --git a/Misc/NEWS.d/next/Library/2023-10-25-11-54-00.gh-issue-79033.5ePgFl.rst b/Misc/NEWS.d/next/Library/2023-10-25-11-54-00.gh-issue-79033.5ePgFl.rst deleted file mode 100644 index f131bf590870ad..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-25-11-54-00.gh-issue-79033.5ePgFl.rst +++ /dev/null @@ -1,6 +0,0 @@ -Another attempt at fixing :func:`asyncio.Server.wait_closed()`. It now -blocks until both conditions are true: the server is closed, *and* there -are no more active connections. (This means that in some cases where in -3.12.0 this function would *incorrectly* have returned immediately, -it will now block; in particular, when there are no active connections -but the server hasn't been closed yet.) diff --git a/Misc/NEWS.d/next/Library/2023-10-25-16-37-13.gh-issue-75666.BpsWut.rst b/Misc/NEWS.d/next/Library/2023-10-25-16-37-13.gh-issue-75666.BpsWut.rst new file mode 100644 index 00000000000000..d774cc4f7c687f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-25-16-37-13.gh-issue-75666.BpsWut.rst @@ -0,0 +1,6 @@ +Fix the behavior of :mod:`tkinter` widget's ``unbind()`` method with two +arguments. Previously, ``widget.unbind(sequence, funcid)`` destroyed the +current binding for *sequence*, leaving *sequence* unbound, and deleted the +*funcid* command. Now it removes only *funcid* from the binding for +*sequence*, keeping other commands, and deletes the *funcid* command. It +leaves *sequence* unbound only if *funcid* was the last bound command. diff --git a/Misc/NEWS.d/next/Library/2023-10-27-09-56-20.gh-issue-111388.SlmDbC.rst b/Misc/NEWS.d/next/Library/2023-10-27-09-56-20.gh-issue-111388.SlmDbC.rst deleted file mode 100644 index 353196439a9cff..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-27-09-56-20.gh-issue-111388.SlmDbC.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add ``show_group`` parameter to :func:`traceback.format_exception_only`, -which allows to format :exc:`ExceptionGroup` instances. diff --git a/Misc/NEWS.d/next/Library/2023-10-27-12-46-56.gh-issue-68166.0EbWW4.rst b/Misc/NEWS.d/next/Library/2023-10-27-12-46-56.gh-issue-68166.0EbWW4.rst deleted file mode 100644 index 757a7004cc1dc0..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-27-12-46-56.gh-issue-68166.0EbWW4.rst +++ /dev/null @@ -1,4 +0,0 @@ -Remove mention of not supported "vsapi" element type in -:meth:`tkinter.ttk.Style.element_create`. Add tests for ``element_create()`` -and other ``ttk.Style`` methods. Add examples for ``element_create()`` in -the documentation. diff --git a/Misc/NEWS.d/next/Library/2023-10-28-04-21-17.gh-issue-111342.m8Ln1k.rst b/Misc/NEWS.d/next/Library/2023-10-28-04-21-17.gh-issue-111342.m8Ln1k.rst deleted file mode 100644 index 57707fd4acf1b2..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-28-04-21-17.gh-issue-111342.m8Ln1k.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed typo in :func:`math.sumprod`. diff --git a/Misc/NEWS.d/next/Library/2023-10-28-22-11-11.gh-issue-111429.mJGxuQ.rst b/Misc/NEWS.d/next/Library/2023-10-28-22-11-11.gh-issue-111429.mJGxuQ.rst deleted file mode 100644 index c8bc4c5295a106..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-28-22-11-11.gh-issue-111429.mJGxuQ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Speed up :meth:`pathlib.PurePath.relative_to` and -:meth:`~pathlib.PurePath.is_relative_to`. diff --git a/Misc/NEWS.d/next/Library/2023-10-29-03-46-27.gh-issue-66425.FWTdDo.rst b/Misc/NEWS.d/next/Library/2023-10-29-03-46-27.gh-issue-66425.FWTdDo.rst deleted file mode 100644 index e7ede35891fb33..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-29-03-46-27.gh-issue-66425.FWTdDo.rst +++ /dev/null @@ -1,3 +0,0 @@ -Remove the code to set the REMOTE_HOST header from wsgiref module, -as it is unreachable. This header is used for performance reasons, -which is not necessary in the wsgiref module. diff --git a/Misc/NEWS.d/next/Library/2023-10-30-08-50-46.gh-issue-111356.Bc8LvA.rst b/Misc/NEWS.d/next/Library/2023-10-30-08-50-46.gh-issue-111356.Bc8LvA.rst deleted file mode 100644 index a821b52b982175..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-30-08-50-46.gh-issue-111356.Bc8LvA.rst +++ /dev/null @@ -1 +0,0 @@ -Added :func:`io.text_encoding()`, :data:`io.DEFAULT_BUFFER_SIZE`, and :class:`io.IncrementalNewlineDecoder` to ``io.__all__``. diff --git a/Misc/NEWS.d/next/Library/2023-10-30-14-47-23.gh-issue-111246.QJ_ehs.rst b/Misc/NEWS.d/next/Library/2023-10-30-14-47-23.gh-issue-111246.QJ_ehs.rst deleted file mode 100644 index a9630de5cc6947..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-30-14-47-23.gh-issue-111246.QJ_ehs.rst +++ /dev/null @@ -1,2 +0,0 @@ -:meth:`asyncio.loop.create_unix_server` will now automatically remove the -Unix socket when the server is closed. diff --git a/Misc/NEWS.d/next/Library/2023-10-31-07-46-56.gh-issue-111531.6zUV_G.rst b/Misc/NEWS.d/next/Library/2023-10-31-07-46-56.gh-issue-111531.6zUV_G.rst deleted file mode 100644 index b722f0414184b1..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-10-31-07-46-56.gh-issue-111531.6zUV_G.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix reference leaks in ``bind_class()`` and ``bind_all()`` methods of -:mod:`tkinter` widgets. diff --git a/Misc/NEWS.d/next/Library/2023-11-01-14-03-24.gh-issue-110894.7-wZxC.rst b/Misc/NEWS.d/next/Library/2023-11-01-14-03-24.gh-issue-110894.7-wZxC.rst deleted file mode 100644 index c59fe6b9119eca..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-01-14-03-24.gh-issue-110894.7-wZxC.rst +++ /dev/null @@ -1 +0,0 @@ -Call loop exception handler for exceptions in ``client_connected_cb`` of :func:`asyncio.start_server` so that applications can handle it. Patch by Kumar Aditya. diff --git a/Misc/NEWS.d/next/Library/2023-11-02-12-15-46.gh-issue-111482.FWqZIU.rst b/Misc/NEWS.d/next/Library/2023-11-02-12-15-46.gh-issue-111482.FWqZIU.rst deleted file mode 100644 index d73e45ccf09b48..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-02-12-15-46.gh-issue-111482.FWqZIU.rst +++ /dev/null @@ -1,3 +0,0 @@ -:mod:`time`: Make :func:`time.clock_gettime()` and -:func:`time.clock_gettime_ns()` functions up to 2x faster by faster calling -convention. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2023-11-04-01-20-23.gh-issue-111719.fUiKBD.rst b/Misc/NEWS.d/next/Library/2023-11-04-01-20-23.gh-issue-111719.fUiKBD.rst deleted file mode 100644 index 1d3b948644d232..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-04-01-20-23.gh-issue-111719.fUiKBD.rst +++ /dev/null @@ -1 +0,0 @@ -Add extra argument validation for ``alias`` command in :mod:`pdb` diff --git a/Misc/NEWS.d/next/Library/2023-11-04-10-24-25.gh-issue-111541.x0RBI1.rst b/Misc/NEWS.d/next/Library/2023-11-04-10-24-25.gh-issue-111541.x0RBI1.rst deleted file mode 100644 index 719b63dad36fb7..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-04-10-24-25.gh-issue-111541.x0RBI1.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :mod:`doctest` for :exc:`SyntaxError` not-builtin subclasses. diff --git a/Misc/NEWS.d/next/Library/2023-11-04-21-12-27.gh-issue-80731.Wq51xg.rst b/Misc/NEWS.d/next/Library/2023-11-04-21-12-27.gh-issue-80731.Wq51xg.rst deleted file mode 100644 index 5f957a39b2e34b..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-04-21-12-27.gh-issue-80731.Wq51xg.rst +++ /dev/null @@ -1 +0,0 @@ -Avoid executing the default function in :class:`cmd.Cmd` in an except block diff --git a/Misc/NEWS.d/next/Library/2023-11-05-20-09-27.gh-issue-99367.HLaWKo.rst b/Misc/NEWS.d/next/Library/2023-11-05-20-09-27.gh-issue-99367.HLaWKo.rst new file mode 100644 index 00000000000000..0920da221e423f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-05-20-09-27.gh-issue-99367.HLaWKo.rst @@ -0,0 +1 @@ +Do not mangle ``sys.path[0]`` in :mod:`pdb` if safe_path is set diff --git a/Misc/NEWS.d/next/Library/2023-11-08-07-42-53.gh-issue-111768.g-WpnV.rst b/Misc/NEWS.d/next/Library/2023-11-08-07-42-53.gh-issue-111768.g-WpnV.rst deleted file mode 100644 index 501cfa3920a0bf..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-08-07-42-53.gh-issue-111768.g-WpnV.rst +++ /dev/null @@ -1 +0,0 @@ -:func:`wsgiref.util.is_hop_by_hop` is now exposed correctly in ``__all__``. diff --git a/Misc/NEWS.d/next/Library/2023-11-08-11-50-49.gh-issue-111841.iSqdQf.rst b/Misc/NEWS.d/next/Library/2023-11-08-11-50-49.gh-issue-111841.iSqdQf.rst deleted file mode 100644 index cd1780988aeac7..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-08-11-50-49.gh-issue-111841.iSqdQf.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix truncating arguments on an embedded null character in :meth:`os.putenv` -and :meth:`os.unsetenv` on Windows. diff --git a/Misc/NEWS.d/next/Library/2023-11-08-15-58-57.gh-issue-111804.uAXTOL.rst b/Misc/NEWS.d/next/Library/2023-11-08-15-58-57.gh-issue-111804.uAXTOL.rst deleted file mode 100644 index 2696f2f492a8b0..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-08-15-58-57.gh-issue-111804.uAXTOL.rst +++ /dev/null @@ -1,2 +0,0 @@ -Remove posix.fallocate() under WASI as the underlying posix_fallocate() is -not available in WASI preview2. diff --git a/Misc/NEWS.d/next/Library/2023-11-08-16-11-04.gh-issue-110275.Bm6GwR.rst b/Misc/NEWS.d/next/Library/2023-11-08-16-11-04.gh-issue-110275.Bm6GwR.rst new file mode 100644 index 00000000000000..194dd5cb623f0f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-08-16-11-04.gh-issue-110275.Bm6GwR.rst @@ -0,0 +1,2 @@ +Named tuple's methods ``_replace()`` and ``__replace__()`` now raise +TypeError instead of ValueError for invalid keyword arguments. diff --git a/Misc/NEWS.d/next/Library/2023-11-08-18-53-07.gh-issue-68166.1iTh4Y.rst b/Misc/NEWS.d/next/Library/2023-11-08-18-53-07.gh-issue-68166.1iTh4Y.rst new file mode 100644 index 00000000000000..30379b8fa1afaf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-08-18-53-07.gh-issue-68166.1iTh4Y.rst @@ -0,0 +1,2 @@ +Add support of the "vsapi" element type in +:meth:`tkinter.ttk.Style.element_create`. diff --git a/Misc/NEWS.d/next/Library/2023-11-08-23-32-03.gh-issue-111835.ufFiuW.rst b/Misc/NEWS.d/next/Library/2023-11-08-23-32-03.gh-issue-111835.ufFiuW.rst deleted file mode 100644 index 5d06c22f477bab..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-08-23-32-03.gh-issue-111835.ufFiuW.rst +++ /dev/null @@ -1,4 +0,0 @@ -The :class:`mmap.mmap` class now has an :meth:`~mmap.mmap.seekable` method -that can be used where it requires a file-like object with seekable and -the :meth:`~mmap.mmap.seek` method return the new absolute position. -Patch by Donghee Na. diff --git a/Misc/NEWS.d/next/Library/2023-11-09-10-45-56.gh-issue-103791.sdfkja.rst b/Misc/NEWS.d/next/Library/2023-11-09-10-45-56.gh-issue-103791.sdfkja.rst deleted file mode 100644 index 5bfdd75ccc93e8..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-09-10-45-56.gh-issue-103791.sdfkja.rst +++ /dev/null @@ -1,3 +0,0 @@ -:class:`contextlib.suppress` now supports suppressing exceptions raised as -part of a :exc:`BaseExceptionGroup`, in addition to the recent support for -:exc:`ExceptionGroup`. diff --git a/Misc/NEWS.d/next/Library/2023-11-09-11-07-34.gh-issue-111874.dzYc3j.rst b/Misc/NEWS.d/next/Library/2023-11-09-11-07-34.gh-issue-111874.dzYc3j.rst new file mode 100644 index 00000000000000..50408202a7a5a1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-09-11-07-34.gh-issue-111874.dzYc3j.rst @@ -0,0 +1,4 @@ +When creating a :class:`typing.NamedTuple` class, ensure +:func:`~object.__set_name__` is called on all objects that define +``__set_name__`` and exist in the values of the ``NamedTuple`` class's class +dictionary. Patch by Alex Waygood. diff --git a/Misc/NEWS.d/next/Library/2023-11-09-12-57-43.gh-issue-111460.TQaz9I.rst b/Misc/NEWS.d/next/Library/2023-11-09-12-57-43.gh-issue-111460.TQaz9I.rst deleted file mode 100644 index 956c536a291a9c..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-09-12-57-43.gh-issue-111460.TQaz9I.rst +++ /dev/null @@ -1,3 +0,0 @@ -:mod:`curses`: restore wide character support (including -:func:`curses.unget_wch` and :meth:`~curses.window.get_wch`) on macOS, which -was unavailable due to a regression in Python 3.12. diff --git a/Misc/NEWS.d/next/Library/2023-11-10-22-08-28.gh-issue-111942.MDFm6v.rst b/Misc/NEWS.d/next/Library/2023-11-10-22-08-28.gh-issue-111942.MDFm6v.rst deleted file mode 100644 index 4fc505c8f257a6..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-10-22-08-28.gh-issue-111942.MDFm6v.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix crashes in :meth:`io.TextIOWrapper.reconfigure` when pass invalid -arguments, e.g. non-string encoding. diff --git a/Misc/NEWS.d/next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst b/Misc/NEWS.d/next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst deleted file mode 100644 index d1ee4c054a3f19..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst +++ /dev/null @@ -1 +0,0 @@ -Issue warning message instead of having :class:`RuntimeError` be displayed when event loop has already been closed at :meth:`StreamWriter.__del__`. diff --git a/Misc/NEWS.d/next/Library/2023-11-14-16-31-59.gh-issue-111995.OoX8JJ.rst b/Misc/NEWS.d/next/Library/2023-11-14-16-31-59.gh-issue-111995.OoX8JJ.rst deleted file mode 100644 index 773f620c97b608..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-14-16-31-59.gh-issue-111995.OoX8JJ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added the ``NI_IDN`` constant to the :mod:`socket` module when present in C -at build time for use with :func:`socket.getnameinfo`. diff --git a/Misc/NEWS.d/next/Library/2023-11-14-18-43-55.gh-issue-111942.x1pnrj.rst b/Misc/NEWS.d/next/Library/2023-11-14-18-43-55.gh-issue-111942.x1pnrj.rst deleted file mode 100644 index ca58a6fa5d6ae1..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-14-18-43-55.gh-issue-111942.x1pnrj.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix SystemError in the TextIOWrapper constructor with non-encodable "errors" -argument in non-debug mode. diff --git a/Misc/NEWS.d/next/Library/2023-11-15-01-36-04.gh-issue-106922.qslOVH.rst b/Misc/NEWS.d/next/Library/2023-11-15-01-36-04.gh-issue-106922.qslOVH.rst new file mode 100644 index 00000000000000..b68e75ab87cd0b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-15-01-36-04.gh-issue-106922.qslOVH.rst @@ -0,0 +1 @@ +Display multiple lines with ``traceback`` when errors span multiple lines. diff --git a/Misc/NEWS.d/next/Library/2023-11-15-04-53-37.gh-issue-112105.I3RcVN.rst b/Misc/NEWS.d/next/Library/2023-11-15-04-53-37.gh-issue-112105.I3RcVN.rst new file mode 100644 index 00000000000000..4243dcb190434f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-15-04-53-37.gh-issue-112105.I3RcVN.rst @@ -0,0 +1 @@ +Make :func:`readline.set_completer_delims` work with libedit diff --git a/Misc/NEWS.d/next/Library/2023-11-16-10-42-15.gh-issue-112139.WpHosf.rst b/Misc/NEWS.d/next/Library/2023-11-16-10-42-15.gh-issue-112139.WpHosf.rst new file mode 100644 index 00000000000000..090dc8847d9556 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-16-10-42-15.gh-issue-112139.WpHosf.rst @@ -0,0 +1,3 @@ +Add :meth:`Signature.format` to format signatures to string with extra options. +And use it in :mod:`pydoc` to render more readable signatures that have new +lines between parameters. diff --git a/Misc/NEWS.d/next/Library/2023-11-16-17-18-09.gh-issue-112137.QvjGjN.rst b/Misc/NEWS.d/next/Library/2023-11-16-17-18-09.gh-issue-112137.QvjGjN.rst new file mode 100644 index 00000000000000..6b61d051966846 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-16-17-18-09.gh-issue-112137.QvjGjN.rst @@ -0,0 +1 @@ +Change :mod:`dis` output to display logical labels for jump targets instead of offsets. diff --git a/Misc/NEWS.d/next/Library/2023-11-21-02-58-14.gh-issue-77621.MYv5XS.rst b/Misc/NEWS.d/next/Library/2023-11-21-02-58-14.gh-issue-77621.MYv5XS.rst new file mode 100644 index 00000000000000..f3e6efc389afca --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-21-02-58-14.gh-issue-77621.MYv5XS.rst @@ -0,0 +1,2 @@ +Slightly improve the import time of the :mod:`pathlib` module by deferring +some imports. Patch by Barney Gale. diff --git a/Misc/NEWS.d/next/Library/2023-11-22-19-43-54.gh-issue-112292.5nDU87.rst b/Misc/NEWS.d/next/Library/2023-11-22-19-43-54.gh-issue-112292.5nDU87.rst new file mode 100644 index 00000000000000..8345e33791cde0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-22-19-43-54.gh-issue-112292.5nDU87.rst @@ -0,0 +1,2 @@ +Fix a crash in :mod:`readline` when imported from a sub interpreter. Patch +by Anthony Shaw diff --git a/Misc/NEWS.d/next/Library/2023-11-22-23-08-47.gh-issue-81620.mfZ2Wf.rst b/Misc/NEWS.d/next/Library/2023-11-22-23-08-47.gh-issue-81620.mfZ2Wf.rst new file mode 100644 index 00000000000000..ff35806e4d5ed6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-22-23-08-47.gh-issue-81620.mfZ2Wf.rst @@ -0,0 +1 @@ +Add extra tests for :func:`random.binomialvariate` diff --git a/Misc/NEWS.d/next/Library/2023-11-23-10-41-21.gh-issue-112332.rhTBaa.rst b/Misc/NEWS.d/next/Library/2023-11-23-10-41-21.gh-issue-112332.rhTBaa.rst new file mode 100644 index 00000000000000..bd686ad052e5b2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-23-10-41-21.gh-issue-112332.rhTBaa.rst @@ -0,0 +1,2 @@ +Deprecate the ``exc_type`` field of :class:`traceback.TracebackException`. +Add ``exc_type_str`` to replace it. diff --git a/Misc/NEWS.d/next/Library/2023-11-23-12-37-22.gh-issue-112137.kM46Q6.rst b/Misc/NEWS.d/next/Library/2023-11-23-12-37-22.gh-issue-112137.kM46Q6.rst new file mode 100644 index 00000000000000..1b2e41ae96ff09 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-23-12-37-22.gh-issue-112137.kM46Q6.rst @@ -0,0 +1 @@ +Change :mod:`dis` output to display no-lineno as "--" instead of "None". diff --git a/Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst b/Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst new file mode 100644 index 00000000000000..b2b9894e6bef3a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-23-17-25-27.gh-issue-112345.FFApHx.rst @@ -0,0 +1,3 @@ +Improve error message when trying to call :func:`issubclass` against a +:class:`typing.Protocol` that has non-method members. +Patch by Randolf Scholz. diff --git a/Misc/NEWS.d/next/Library/2023-11-24-09-27-01.gh-issue-112361.kYtnHW.rst b/Misc/NEWS.d/next/Library/2023-11-24-09-27-01.gh-issue-112361.kYtnHW.rst new file mode 100644 index 00000000000000..5a83f93f9fbec8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-24-09-27-01.gh-issue-112361.kYtnHW.rst @@ -0,0 +1,2 @@ +Speed up a small handful of :mod:`pathlib` methods by removing some +temporary objects. diff --git a/Misc/NEWS.d/next/Library/2023-11-24-21-00-24.gh-issue-94722.GMIQIn.rst b/Misc/NEWS.d/next/Library/2023-11-24-21-00-24.gh-issue-94722.GMIQIn.rst new file mode 100644 index 00000000000000..41bd57f46ed82a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-24-21-00-24.gh-issue-94722.GMIQIn.rst @@ -0,0 +1,2 @@ +Fix bug where comparison between instances of :class:`~doctest.DocTest` fails if +one of them has ``None`` as its lineno. diff --git a/Misc/NEWS.d/next/Library/2023-11-25-20-29-28.gh-issue-112405.cOtzxC.rst b/Misc/NEWS.d/next/Library/2023-11-25-20-29-28.gh-issue-112405.cOtzxC.rst new file mode 100644 index 00000000000000..f6f1bee2a0c38f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-25-20-29-28.gh-issue-112405.cOtzxC.rst @@ -0,0 +1 @@ +Optimize :meth:`pathlib.PurePath.relative_to`. Patch by Alex Waygood. diff --git a/Misc/NEWS.d/next/Library/2023-11-26-13-26-56.gh-issue-112358.smhaeZ.rst b/Misc/NEWS.d/next/Library/2023-11-26-13-26-56.gh-issue-112358.smhaeZ.rst new file mode 100644 index 00000000000000..e473ded46a1309 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-26-13-26-56.gh-issue-112358.smhaeZ.rst @@ -0,0 +1,2 @@ +Revert change to :class:`struct.Struct` initialization that broke some cases +of subclassing. diff --git a/Misc/NEWS.d/next/Library/2023-11-26-13-44-19.gh-issue-112414.kx2E7S.rst b/Misc/NEWS.d/next/Library/2023-11-26-13-44-19.gh-issue-112414.kx2E7S.rst new file mode 100644 index 00000000000000..058e5a33227e5a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-26-13-44-19.gh-issue-112414.kx2E7S.rst @@ -0,0 +1,3 @@ +Fix regression in Python 3.12 where calling :func:`repr` on a module that +had been imported using a custom :term:`loader` could fail with +:exc:`AttributeError`. Patch by Alex Waygood. diff --git a/Misc/NEWS.d/next/Library/2023-11-27-12-41-23.gh-issue-63284.q2Qi9q.rst b/Misc/NEWS.d/next/Library/2023-11-27-12-41-23.gh-issue-63284.q2Qi9q.rst new file mode 100644 index 00000000000000..abb57dccd5a91a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-27-12-41-23.gh-issue-63284.q2Qi9q.rst @@ -0,0 +1 @@ +Added support for TLS-PSK (pre-shared key) mode to the :mod:`ssl` module. diff --git a/Misc/NEWS.d/next/Library/2023-11-28-20-01-33.gh-issue-112509.QtoKed.rst b/Misc/NEWS.d/next/Library/2023-11-28-20-01-33.gh-issue-112509.QtoKed.rst new file mode 100644 index 00000000000000..a16d67e7776bcb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-28-20-01-33.gh-issue-112509.QtoKed.rst @@ -0,0 +1,3 @@ +Fix edge cases that could cause a key to be present in both the +``__required_keys__`` and ``__optional_keys__`` attributes of a +:class:`typing.TypedDict`. Patch by Jelle Zijlstra. diff --git a/Misc/NEWS.d/next/Library/2023-11-28-20-47-39.gh-issue-112328.Z2AxEY.rst b/Misc/NEWS.d/next/Library/2023-11-28-20-47-39.gh-issue-112328.Z2AxEY.rst new file mode 100644 index 00000000000000..6e6902486b7bc9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-28-20-47-39.gh-issue-112328.Z2AxEY.rst @@ -0,0 +1,2 @@ +[Enum] Make ``EnumDict``, ``EnumDict.member_names``, +``EnumType._add_alias_`` and ``EnumType._add_value_alias_`` public. diff --git a/Misc/NEWS.d/next/Library/2023-11-29-02-26-32.gh-issue-112510.j-zXGc.rst b/Misc/NEWS.d/next/Library/2023-11-29-02-26-32.gh-issue-112510.j-zXGc.rst new file mode 100644 index 00000000000000..02de6fa80c1b3e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-29-02-26-32.gh-issue-112510.j-zXGc.rst @@ -0,0 +1 @@ +Add :data:`readline.backend` for the backend readline uses (``editline`` or ``readline``) diff --git a/Misc/NEWS.d/next/Library/2023-11-29-10-51-41.gh-issue-112516.rFKUKN.rst b/Misc/NEWS.d/next/Library/2023-11-29-10-51-41.gh-issue-112516.rFKUKN.rst new file mode 100644 index 00000000000000..530cf992dcd77a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-29-10-51-41.gh-issue-112516.rFKUKN.rst @@ -0,0 +1 @@ +Update the bundled copy of pip to version 23.3.1. diff --git a/Misc/NEWS.d/next/Library/2023-12-01-08-28-09.gh-issue-112578.bfNbfi.rst b/Misc/NEWS.d/next/Library/2023-12-01-08-28-09.gh-issue-112578.bfNbfi.rst new file mode 100644 index 00000000000000..1de5b1fe26ce6d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-01-08-28-09.gh-issue-112578.bfNbfi.rst @@ -0,0 +1 @@ +Fix a spurious :exc:`RuntimeWarning` when executing the :mod:`zipfile` module. diff --git a/Misc/NEWS.d/next/Library/2023-12-01-16-09-59.gh-issue-81194.FFad1c.rst b/Misc/NEWS.d/next/Library/2023-12-01-16-09-59.gh-issue-81194.FFad1c.rst new file mode 100644 index 00000000000000..feb7a8643b97f6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-01-16-09-59.gh-issue-81194.FFad1c.rst @@ -0,0 +1,3 @@ +Fix a crash in :func:`socket.if_indextoname` with specific value (UINT_MAX). +Fix an integer overflow in :func:`socket.if_indextoname` on 64-bit +non-Windows platforms. diff --git a/Misc/NEWS.d/next/Library/2023-12-01-18-05-09.gh-issue-110190.5bf-c9.rst b/Misc/NEWS.d/next/Library/2023-12-01-18-05-09.gh-issue-110190.5bf-c9.rst new file mode 100644 index 00000000000000..730b9d49119805 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-01-18-05-09.gh-issue-110190.5bf-c9.rst @@ -0,0 +1 @@ +Fix ctypes structs with array on Arm platform by setting ``MAX_STRUCT_SIZE`` to 32 in stgdict. Patch by Diego Russo. diff --git a/Misc/NEWS.d/next/Library/2023-12-01-21-05-46.gh-issue-112334.DmNXKh.rst b/Misc/NEWS.d/next/Library/2023-12-01-21-05-46.gh-issue-112334.DmNXKh.rst new file mode 100644 index 00000000000000..3a53a8bf84230f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-01-21-05-46.gh-issue-112334.DmNXKh.rst @@ -0,0 +1,11 @@ +Fixed a performance regression in 3.12's :mod:`subprocess` on Linux where it +would no longer use the fast-path ``vfork()`` system call when it could have +due to a logic bug, instead falling back to the safe but slower ``fork()``. + +Also fixed a second 3.12.0 potential security bug. If a value of +``extra_groups=[]`` was passed to :mod:`subprocess.Popen` or related APIs, +the underlying ``setgroups(0, NULL)`` system call to clear the groups list +would not be made in the child process prior to ``exec()``. + +This was identified via code inspection in the process of fixing the first +bug. diff --git a/Misc/NEWS.d/next/Library/2023-12-02-12-55-17.gh-issue-112618.7_FT8-.rst b/Misc/NEWS.d/next/Library/2023-12-02-12-55-17.gh-issue-112618.7_FT8-.rst new file mode 100644 index 00000000000000..c732de15609c96 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-02-12-55-17.gh-issue-112618.7_FT8-.rst @@ -0,0 +1,2 @@ +Fix a caching bug relating to :data:`typing.Annotated`. +``Annotated[str, True]`` is no longer identical to ``Annotated[str, 1]``. diff --git a/Misc/NEWS.d/next/Library/2023-12-03-12-41-48.gh-issue-112645.blMsKf.rst b/Misc/NEWS.d/next/Library/2023-12-03-12-41-48.gh-issue-112645.blMsKf.rst new file mode 100644 index 00000000000000..4e8f6ebdb882e0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-03-12-41-48.gh-issue-112645.blMsKf.rst @@ -0,0 +1 @@ +Remove deprecation error on passing ``onerror`` to :func:`shutil.rmtree`. diff --git a/Misc/NEWS.d/next/Library/2023-12-04-14-05-24.gh-issue-74690.eODKRm.rst b/Misc/NEWS.d/next/Library/2023-12-04-14-05-24.gh-issue-74690.eODKRm.rst new file mode 100644 index 00000000000000..36d793f787302e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-04-14-05-24.gh-issue-74690.eODKRm.rst @@ -0,0 +1,5 @@ +Speedup :func:`isinstance` checks by roughly 20% for +:func:`runtime-checkable protocols ` +that only have one callable member. +Speedup :func:`issubclass` checks for these protocols by roughly 10%. +Patch by Alex Waygood. diff --git a/Misc/NEWS.d/next/Library/2023-12-04-16-45-11.gh-issue-74690.pQYP5U.rst b/Misc/NEWS.d/next/Library/2023-12-04-16-45-11.gh-issue-74690.pQYP5U.rst new file mode 100644 index 00000000000000..8102f02e941c29 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-04-16-45-11.gh-issue-74690.pQYP5U.rst @@ -0,0 +1,2 @@ +Speedup :func:`issubclass` checks against simple :func:`runtime-checkable +protocols ` by around 6%. Patch by Alex Waygood. diff --git a/Misc/NEWS.d/next/Library/2023-12-04-21-30-34.gh-issue-112727.jpgNRB.rst b/Misc/NEWS.d/next/Library/2023-12-04-21-30-34.gh-issue-112727.jpgNRB.rst new file mode 100644 index 00000000000000..bbe7aae5732d9a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-04-21-30-34.gh-issue-112727.jpgNRB.rst @@ -0,0 +1 @@ +Speed up :meth:`pathlib.Path.absolute`. Patch by Barney Gale. diff --git a/Misc/NEWS.d/next/Library/2023-12-05-01-19-28.gh-issue-112736.rdHDrU.rst b/Misc/NEWS.d/next/Library/2023-12-05-01-19-28.gh-issue-112736.rdHDrU.rst new file mode 100644 index 00000000000000..6c09e622923af8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-05-01-19-28.gh-issue-112736.rdHDrU.rst @@ -0,0 +1 @@ +The use of del-safe symbols in ``subprocess`` was refactored to allow for use in cross-platform build environments. diff --git a/Misc/NEWS.d/next/Library/2023-12-05-16-20-40.gh-issue-94692.-e5C3c.rst b/Misc/NEWS.d/next/Library/2023-12-05-16-20-40.gh-issue-94692.-e5C3c.rst new file mode 100644 index 00000000000000..c67ba6c9ececdb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-05-16-20-40.gh-issue-94692.-e5C3c.rst @@ -0,0 +1,4 @@ +:func:`shutil.rmtree` now only catches OSError exceptions. Previously a +symlink attack resistant version of ``shutil.rmtree()`` could ignore or pass +to the error handler arbitrary exception when invalid arguments were +provided. diff --git a/Misc/NEWS.d/next/Library/2023-12-05-18-57-53.gh-issue-79325.P2vMVK.rst b/Misc/NEWS.d/next/Library/2023-12-05-18-57-53.gh-issue-79325.P2vMVK.rst new file mode 100644 index 00000000000000..f3c32d27b5fe66 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-05-18-57-53.gh-issue-79325.P2vMVK.rst @@ -0,0 +1,2 @@ +Fix an infinite recursion error in :func:`tempfile.TemporaryDirectory` +cleanup on Windows. diff --git a/Misc/NEWS.d/next/Library/2023-12-06-14-06-14.gh-issue-51944.-5qq_L.rst b/Misc/NEWS.d/next/Library/2023-12-06-14-06-14.gh-issue-51944.-5qq_L.rst new file mode 100644 index 00000000000000..821eefa7cffcd5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-06-14-06-14.gh-issue-51944.-5qq_L.rst @@ -0,0 +1,6 @@ +Add the following constants to the :mod:`termios` module. These values are +present in macOS system headers: ``ALTWERASE``, ``B14400``, ``B28800``, +``B7200``, ``B76800``, ``CCAR_OFLOW``, ``CCTS_OFLOW``, ``CDSR_OFLOW``, +``CDTR_IFLOW``, ``CIGNORE``, ``CRTS_IFLOW``, ``EXTPROC``, ``IUTF8``, +``MDMBUF``, ``NL2``, ``NL3``, ``NOKERNINFO``, ``ONOEOT``, ``OXTABS``, +``VDSUSP``, ``VSTATUS``. diff --git a/Misc/NEWS.d/next/Security/2023-12-06-14-06-59.gh-issue-112302.3bl20f.rst b/Misc/NEWS.d/next/Security/2023-12-06-14-06-59.gh-issue-112302.3bl20f.rst new file mode 100644 index 00000000000000..65e4dc3762d3c0 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2023-12-06-14-06-59.gh-issue-112302.3bl20f.rst @@ -0,0 +1,2 @@ +Created a Software Bill-of-Materials document and tooling for tracking +dependencies. diff --git a/Misc/NEWS.d/next/Tests/2023-09-05-20-46-35.gh-issue-108927.TpwWav.rst b/Misc/NEWS.d/next/Tests/2023-09-05-20-46-35.gh-issue-108927.TpwWav.rst new file mode 100644 index 00000000000000..b1a78370afedb2 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-05-20-46-35.gh-issue-108927.TpwWav.rst @@ -0,0 +1,4 @@ +Fixed order dependence in running tests in the same process +when a test that has submodules (e.g. test_importlib) follows a test that +imports its submodule (e.g. test_importlib.util) and precedes a test +(e.g. test_unittest or test_compileall) that uses that submodule. diff --git a/Misc/NEWS.d/next/Tests/2023-09-15-15-00-14.gh-issue-108747.ql0owS.rst b/Misc/NEWS.d/next/Tests/2023-09-15-15-00-14.gh-issue-108747.ql0owS.rst deleted file mode 100644 index ba1fe97871929c..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-09-15-15-00-14.gh-issue-108747.ql0owS.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add unit test for ``usercustomize`` and ``sitecustomize`` hooks from -:class:`site`. diff --git a/Misc/NEWS.d/next/Tests/2023-10-16-13-47-24.gh-issue-110918.aFgZK3.rst b/Misc/NEWS.d/next/Tests/2023-10-16-13-47-24.gh-issue-110918.aFgZK3.rst deleted file mode 100644 index 7cb79c0cbf29f1..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-10-16-13-47-24.gh-issue-110918.aFgZK3.rst +++ /dev/null @@ -1,4 +0,0 @@ -Test case matching patterns specified by options ``--match``, ``--ignore``, -``--matchfile`` and ``--ignorefile`` are now tested in the order of -specification, and the last match determines whether the test case be run or -ignored. diff --git a/Misc/NEWS.d/next/Tests/2023-10-17-17-54-36.gh-issue-110995.Fx8KRD.rst b/Misc/NEWS.d/next/Tests/2023-10-17-17-54-36.gh-issue-110995.Fx8KRD.rst deleted file mode 100644 index db29eaf234b731..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-10-17-17-54-36.gh-issue-110995.Fx8KRD.rst +++ /dev/null @@ -1,2 +0,0 @@ -test_gdb: Fix detection of gdb built without Python scripting support. Patch -by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-10-21-00-10-36.gh-issue-110932.jktjJU.rst b/Misc/NEWS.d/next/Tests/2023-10-21-00-10-36.gh-issue-110932.jktjJU.rst deleted file mode 100644 index 45bb0774a9abe3..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-10-21-00-10-36.gh-issue-110932.jktjJU.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix regrtest if the ``SOURCE_DATE_EPOCH`` environment variable is defined: -use the variable value as the random seed. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-10-21-19-27-36.gh-issue-111165.FU6mUk.rst b/Misc/NEWS.d/next/Tests/2023-10-21-19-27-36.gh-issue-111165.FU6mUk.rst deleted file mode 100644 index 11f302d943c29a..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-10-21-19-27-36.gh-issue-111165.FU6mUk.rst +++ /dev/null @@ -1,2 +0,0 @@ -Remove no longer used functions ``run_unittest()`` and ``run_doctest()`` -from the :mod:`test.support` module. diff --git a/Misc/NEWS.d/next/Tests/2023-10-31-22-09-25.gh-issue-110367.UhQi44.rst b/Misc/NEWS.d/next/Tests/2023-10-31-22-09-25.gh-issue-110367.UhQi44.rst deleted file mode 100644 index 0254288d3626cc..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-10-31-22-09-25.gh-issue-110367.UhQi44.rst +++ /dev/null @@ -1,3 +0,0 @@ -Make regrtest ``--verbose3`` option compatible with ``--huntrleaks -jN`` -options. The ``./python -m test -j1 -R 3:3 --verbose3`` command now works as -expected. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2023-11-03-18-59-13.gh-issue-110722.jvT1pb.rst b/Misc/NEWS.d/next/Tests/2023-11-03-18-59-13.gh-issue-110722.jvT1pb.rst deleted file mode 100644 index ad1ac536a092ea..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-11-03-18-59-13.gh-issue-110722.jvT1pb.rst +++ /dev/null @@ -1,2 +0,0 @@ -Gathering line coverage of standard libraries within the regression test -suite is now precise, as well as much faster. Patch by Łukasz Langa. diff --git a/Misc/NEWS.d/next/Tests/2023-11-17-15-20-41.gh-issue-111808.jtIayt.rst b/Misc/NEWS.d/next/Tests/2023-11-17-15-20-41.gh-issue-111808.jtIayt.rst deleted file mode 100644 index 36151d4b0adf03..00000000000000 --- a/Misc/NEWS.d/next/Tests/2023-11-17-15-20-41.gh-issue-111808.jtIayt.rst +++ /dev/null @@ -1,4 +0,0 @@ -Make the default value of ``test.support.infinite_recursion()`` to be -conditional based on whether optimizations were used when compiling the -interpreter. This helps with platforms like WASI whose stack size is greatly -restricted in debug builds. diff --git a/Misc/NEWS.d/next/Tests/2023-12-05-19-50-03.gh-issue-112769.kdLJmS.rst b/Misc/NEWS.d/next/Tests/2023-12-05-19-50-03.gh-issue-112769.kdLJmS.rst new file mode 100644 index 00000000000000..1bbbb26fc322fa --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-12-05-19-50-03.gh-issue-112769.kdLJmS.rst @@ -0,0 +1,3 @@ +The tests now correctly compare zlib version when +:const:`zlib.ZLIB_RUNTIME_VERSION` contains non-integer suffixes. For +example zlib-ng defines the version as ``1.3.0.zlib-ng``. diff --git a/Misc/NEWS.d/next/Tools-Demos/2023-11-09-13-04-29.gh-issue-111903.7Prryr.rst b/Misc/NEWS.d/next/Tools-Demos/2023-11-09-13-04-29.gh-issue-111903.7Prryr.rst deleted file mode 100644 index 41601f7511797a..00000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2023-11-09-13-04-29.gh-issue-111903.7Prryr.rst +++ /dev/null @@ -1,4 +0,0 @@ -Argument Clinic now supports the ``@critical_section`` directive that -instructs Argument Clinic to generate a critical section around the function -call, which locks the ``self`` object in ``--disable-gil`` builds. Patch by -Sam Gross. diff --git a/Misc/NEWS.d/next/Windows/2023-10-19-21-46-18.gh-issue-110913.CWlPfg.rst b/Misc/NEWS.d/next/Windows/2023-10-19-21-46-18.gh-issue-110913.CWlPfg.rst deleted file mode 100644 index d4c1b56d98ef0e..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-10-19-21-46-18.gh-issue-110913.CWlPfg.rst +++ /dev/null @@ -1 +0,0 @@ -WindowsConsoleIO now correctly chunks large buffers without splitting up UTF-8 sequences. diff --git a/Misc/NEWS.d/next/Windows/2023-10-25-05-01-28.gh-issue-111293.FSsLT6.rst b/Misc/NEWS.d/next/Windows/2023-10-25-05-01-28.gh-issue-111293.FSsLT6.rst deleted file mode 100644 index 4c6b255bc44c7b..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-10-25-05-01-28.gh-issue-111293.FSsLT6.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :data:`os.DirEntry.inode` dropping higher 64 bits of a file id on some filesystems on Windows. diff --git a/Misc/NEWS.d/next/Windows/2023-11-13-22-35-27.gh-issue-111856.vEtA5z.rst b/Misc/NEWS.d/next/Windows/2023-11-13-22-35-27.gh-issue-111856.vEtA5z.rst deleted file mode 100644 index b1388df8e4c479..00000000000000 --- a/Misc/NEWS.d/next/Windows/2023-11-13-22-35-27.gh-issue-111856.vEtA5z.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixes :func:`~os.fstat` on file systems that do not support file ID -requests. This includes FAT32 and exFAT. diff --git a/Misc/NEWS.d/next/Windows/2023-12-03-19-22-37.gh-issue-112278.FiloCE.rst b/Misc/NEWS.d/next/Windows/2023-12-03-19-22-37.gh-issue-112278.FiloCE.rst new file mode 100644 index 00000000000000..0350d105d97375 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-12-03-19-22-37.gh-issue-112278.FiloCE.rst @@ -0,0 +1,2 @@ +Reduce the time cost for some functions in :mod:`platform` on Windows if +current user has no permission to the WMI. diff --git a/Misc/NEWS.d/next/macOS/2023-10-18-01-40-36.gh-issue-111015.NaLI2L.rst b/Misc/NEWS.d/next/macOS/2023-10-18-01-40-36.gh-issue-111015.NaLI2L.rst deleted file mode 100644 index 4c6eea136554c8..00000000000000 --- a/Misc/NEWS.d/next/macOS/2023-10-18-01-40-36.gh-issue-111015.NaLI2L.rst +++ /dev/null @@ -1 +0,0 @@ -Ensure that IDLE.app and Python Launcher.app are installed with appropriate permissions on macOS builds. diff --git a/Misc/NEWS.d/next/macOS/2023-12-06-12-11-13.gh-issue-109981.mOHg10.rst b/Misc/NEWS.d/next/macOS/2023-12-06-12-11-13.gh-issue-109981.mOHg10.rst new file mode 100644 index 00000000000000..f86ab2c37ee6ec --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-12-06-12-11-13.gh-issue-109981.mOHg10.rst @@ -0,0 +1,3 @@ +Use ``/dev/fd`` on macOS to determine the number of open files in +``test.support.os_helper.fd_count`` to avoid a crash with "guarded" file +descriptors when probing for open files. diff --git a/Misc/NEWS.d/next/macOS/2023-12-07-14-19-46.gh-issue-110820.DIxb_F.rst b/Misc/NEWS.d/next/macOS/2023-12-07-14-19-46.gh-issue-110820.DIxb_F.rst new file mode 100644 index 00000000000000..0badace7928745 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-12-07-14-19-46.gh-issue-110820.DIxb_F.rst @@ -0,0 +1,3 @@ +Make sure the preprocessor definitions for ``ALIGNOF_MAX_ALIGN_T``, +``SIZEOF_LONG_DOUBLE`` and ``HAVE_GCC_ASM_FOR_X64`` are correct for +Universal 2 builds on macOS. diff --git a/Misc/NEWS.d/next/macOS/2023-12-07-15-53-16.gh-issue-110017.UMYzMR.rst b/Misc/NEWS.d/next/macOS/2023-12-07-15-53-16.gh-issue-110017.UMYzMR.rst new file mode 100644 index 00000000000000..eab1746f1ae3f7 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-12-07-15-53-16.gh-issue-110017.UMYzMR.rst @@ -0,0 +1,2 @@ +Disable a signal handling stress test on macOS due to a bug in macOS +(FB13453490). diff --git a/Misc/sbom.spdx.json b/Misc/sbom.spdx.json new file mode 100644 index 00000000000000..09355640db888e --- /dev/null +++ b/Misc/sbom.spdx.json @@ -0,0 +1,2294 @@ +{ + "SPDXID": "SPDXRef-DOCUMENT", + "files": [ + { + "SPDXID": "SPDXRef-FILE-Modules-expat-COPYING", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "39e6f567a10e36b2e77727e98e60bbcb3eb3af0b" + }, + { + "algorithm": "SHA256", + "checksumValue": "122f2c27000472a201d337b9b31f7eb2b52d091b02857061a8880371612d9534" + } + ], + "fileName": "Modules/expat/COPYING" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-ascii.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "b0235fa3cf845a7d68e8e66dd344d5e32e8951b5" + }, + { + "algorithm": "SHA256", + "checksumValue": "42f8b392c70366743eacbc60ce021389ccaa333598dd49eef6ee5c93698ca205" + } + ], + "fileName": "Modules/expat/ascii.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-asciitab.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "cbb53d16ca1f35ee9c9e296116efd222ae611ed9" + }, + { + "algorithm": "SHA256", + "checksumValue": "1cc0ae749019fc0e488cd1cf245f6beaa6d4f7c55a1fc797e5aa40a408bc266b" + } + ], + "fileName": "Modules/expat/asciitab.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-expat.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "ab7bb32514d170592dfb3f76e41bbdc075a4e7e0" + }, + { + "algorithm": "SHA256", + "checksumValue": "f521acdad222644365b0e81a33bcd6939a98c91b225c47582cc84bd73d96febc" + } + ], + "fileName": "Modules/expat/expat.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-expat-config.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "73627287302ee3e84347c4fe21f37a9cb828bc3b" + }, + { + "algorithm": "SHA256", + "checksumValue": "f17e59f9d95eeb05694c02508aa284d332616c22cbe2e6a802d8a0710310eaab" + } + ], + "fileName": "Modules/expat/expat_config.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-expat-external.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "b70ce53fdc25ae482681ae2f6623c3c8edc9c1b7" + }, + { + "algorithm": "SHA256", + "checksumValue": "86afb425ec9999eb4f1ec9ab2fb41c58c4aa5cb9bf934b8c94264670fc5a961d" + } + ], + "fileName": "Modules/expat/expat_external.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-iasciitab.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "1b0e9014c0baa4c6254d2b5e6a67c70148309c34" + }, + { + "algorithm": "SHA256", + "checksumValue": "ad8b01e9f323cc4208bcd22241df383d7e8641fe3c8b3415aa513de82531f89f" + } + ], + "fileName": "Modules/expat/iasciitab.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-internal.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "2790d37e7de2f13dccc4f4fb352cbdf9ed6abaa2" + }, + { + "algorithm": "SHA256", + "checksumValue": "d2efe5a1018449968a689f444cca432e3d5875aba6ad08ee18ca235d64f41bb9" + } + ], + "fileName": "Modules/expat/internal.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-latin1tab.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "d335ecca380e331a0ea7dc33838a4decd93ec1e4" + }, + { + "algorithm": "SHA256", + "checksumValue": "eab66226da100372e01e42e1cbcd8ac2bbbb5c1b5f95d735289cc85c7a8fc2ba" + } + ], + "fileName": "Modules/expat/latin1tab.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-nametab.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "cf2bc9626c945826602ba9170786e9a2a44645e4" + }, + { + "algorithm": "SHA256", + "checksumValue": "67dcf415d37a4b692a6a8bb46f990c02d83f2ef3d01a65cd61c8594a084246f2" + } + ], + "fileName": "Modules/expat/nametab.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-pyexpatns.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "baa44fe4581895d42e8d5e83d8ce6a69b1c34dbe" + }, + { + "algorithm": "SHA256", + "checksumValue": "33a7b9ac8bf4571e23272cdf644c6f9808bd44c66b149e3c41ab3870d1888609" + } + ], + "fileName": "Modules/expat/pyexpatns.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-siphash.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "2b984f806f10fbfbf72d8d1b7ba2992413c15299" + }, + { + "algorithm": "SHA256", + "checksumValue": "fbce56cd680e690043bbf572188cc2d0a25dbfc0d47ac8cb98eb3de768d4e694" + } + ], + "fileName": "Modules/expat/siphash.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-utf8tab.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "b77c8fcfb551553c81d6fbd94c798c8aa04ad021" + }, + { + "algorithm": "SHA256", + "checksumValue": "8cd26bd461d334d5e1caedb3af4518d401749f2fc66d56208542b29085159c18" + } + ], + "fileName": "Modules/expat/utf8tab.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-winconfig.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "e774ae6ee9391aa6ffb8f775fb74e48f4b428959" + }, + { + "algorithm": "SHA256", + "checksumValue": "3c71cea9a6174718542331971a35db317902b2433be9d8dd1cb24239b635c0cc" + } + ], + "fileName": "Modules/expat/winconfig.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmlparse.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "b580e827e16baa6b035586ffcd4d90301e5a353f" + }, + { + "algorithm": "SHA256", + "checksumValue": "483518bbd69338eefc706cd7fc0b6039df2d3e347f64097989059ed6d2385a1e" + } + ], + "fileName": "Modules/expat/xmlparse.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmlrole.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "5ef21312af73deb2428be3fe97a65244608e76de" + }, + { + "algorithm": "SHA256", + "checksumValue": "6fcf8c72ac0112c1b98bd2039c632a66b4c3dc516ce7c1f981390951121ef3c0" + } + ], + "fileName": "Modules/expat/xmlrole.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmlrole.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "c1a4ea6356643d0820edb9c024c20ad2aaf562dc" + }, + { + "algorithm": "SHA256", + "checksumValue": "2b5d674be6ef20c7e3f69295176d75e68c5616e4dfce0a186fdd5e2ed8315f7a" + } + ], + "fileName": "Modules/expat/xmlrole.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmltok.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "e6d66ae9fd61d7950c62c5d87693c30a707e8577" + }, + { + "algorithm": "SHA256", + "checksumValue": "1110f651bdccfa765ad3d6f3857a35887ab35fc0fe7f3f3488fde2b238b482e3" + } + ], + "fileName": "Modules/expat/xmltok.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmltok.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "9c2a544875fd08ba9c2397296c97263518a410aa" + }, + { + "algorithm": "SHA256", + "checksumValue": "4299a03828b98bfe47ec6809f6e279252954a9a911dc7e0f19551bd74e3af971" + } + ], + "fileName": "Modules/expat/xmltok.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmltok-impl.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "aa96882de8e3d1d3083124b595aa911efe44e5ad" + }, + { + "algorithm": "SHA256", + "checksumValue": "0fbcba7931707c60301305dab78d2298d96447d0a5513926d8b18135228c0818" + } + ], + "fileName": "Modules/expat/xmltok_impl.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmltok-impl.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "788332fe8040bed71172cddedb69abd848cc62f7" + }, + { + "algorithm": "SHA256", + "checksumValue": "f05ad4fe5e98429a7349ff04f57192cac58c324601f2a2e5e697ab0bc05d36d5" + } + ], + "fileName": "Modules/expat/xmltok_impl.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-expat-xmltok-ns.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "2d82d0a1201f78d478b30d108ff8fc27ee3e2672" + }, + { + "algorithm": "SHA256", + "checksumValue": "6ce6d03193279078d55280150fe91e7370370b504a6c123a79182f28341f3e90" + } + ], + "fileName": "Modules/expat/xmltok_ns.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-MD5.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "f77449b2b4eb99f1da0938633cc558baf9c444fb" + }, + { + "algorithm": "SHA256", + "checksumValue": "0f252967debca5b35362ca53951ea16ca8bb97a19a1d24f6695f44d50010859e" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_MD5.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-MD5.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "c24e6779a91c840f3d65d24abbce225b608b676e" + }, + { + "algorithm": "SHA256", + "checksumValue": "9cd062e782801013e3cacaba583e44e1b5e682e217d20208d5323354d42011f1" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_MD5.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA1.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "560f6ff541b5eff480ea047b147f4212bb0db7ed" + }, + { + "algorithm": "SHA256", + "checksumValue": "0ade3ab264e912d7b4e5cdcf773db8c63e4440540d295922d74b06bcfc74c77a" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_SHA1.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA1.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "853b77d45379146faaeac5fe899b28db386ad13c" + }, + { + "algorithm": "SHA256", + "checksumValue": "b13eb14f91582703819235ea7c8f807bb93e4f1e6b695499dc1d86021dc39e72" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_SHA1.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA2.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "667120b6100c946cdaa442f1173c723339923071" + }, + { + "algorithm": "SHA256", + "checksumValue": "b189459b863341a3a9c5c78c0208b6554a2f2ac26e0748fbd4432a91db21fae6" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_SHA2.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA2.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "81db38b0b920e63ec33c7109d1144c35cf091da0" + }, + { + "algorithm": "SHA256", + "checksumValue": "631c9ba19c1c2c835bb63d3f2f22b8d76fb535edfed3c254ff2a52f12af3fe61" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_SHA2.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA3.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "9c832b98a2f2a68202d2da016fb718965d7b7602" + }, + { + "algorithm": "SHA256", + "checksumValue": "38d350d1184238966cfa821a59ae00343f362182b6c2fbea7f2651763d757fb7" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_SHA3.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA3.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "ecc766fb6f7ee85e902b593b61b41e5a728fca34" + }, + { + "algorithm": "SHA256", + "checksumValue": "bae290a94366a2460f51e8468144baaade91d9048db111e10d2e2ffddc3f98cf" + } + ], + "fileName": "Modules/_hacl/Hacl_Hash_SHA3.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-Hacl-Streaming-Types.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "ab7b4d9465a2765a07f8d5bccace7182b28ed1b8" + }, + { + "algorithm": "SHA256", + "checksumValue": "26913613f3b4f8ffff0a3e211a5ebc849159094e5e11de0a31fcb95b6105b74c" + } + ], + "fileName": "Modules/_hacl/Hacl_Streaming_Types.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-include-krml-FStar-UInt128-Verified.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "2ea61d6a236147462045f65c20311819d74db80c" + }, + { + "algorithm": "SHA256", + "checksumValue": "2c22b4d49ba06d6a3053cdc66405bd5ae953a28fcfed1ab164e8f5e0f6e2fb8b" + } + ], + "fileName": "Modules/_hacl/include/krml/FStar_UInt128_Verified.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-include-krml-FStar-UInt-8-16-32-64.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "1a647d841180ac8ca667afa968c353425e81ad0d" + }, + { + "algorithm": "SHA256", + "checksumValue": "e5d1c5854833bec7ea02e227ec35bd7b49c5fb9e0f339efa0dd83e1595f722d4" + } + ], + "fileName": "Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-include-krml-fstar-uint128-struct-endianness.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "1987119a563a8fdc5966286e274f716dbcea77ee" + }, + { + "algorithm": "SHA256", + "checksumValue": "fe57e1bc5ce3224d106e36cb8829b5399c63a68a70b0ccd0c91d82a4565c8869" + } + ], + "fileName": "Modules/_hacl/include/krml/fstar_uint128_struct_endianness.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-include-krml-internal-target.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "903c9eb76b01f3a95c04c3bc841c2fb71dea5403" + }, + { + "algorithm": "SHA256", + "checksumValue": "08ec602c7f90a1540389c0cfc95769fa7fec251e7ca143ef83c0b9f7afcf89a7" + } + ], + "fileName": "Modules/_hacl/include/krml/internal/target.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-include-krml-lowstar-endianness.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "964e09bd99ff2366afd6193b59863fc925e7fb05" + }, + { + "algorithm": "SHA256", + "checksumValue": "3734c7942bec9a434e16df069fa45bdcb84b130f14417bc5f7bfe8546272d9f5" + } + ], + "fileName": "Modules/_hacl/include/krml/lowstar_endianness.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-include-krml-types.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "df8e0ed74a5970d09d3cc4c6e7c6c7a4c4e5015c" + }, + { + "algorithm": "SHA256", + "checksumValue": "de7444c345caa4c47902c4380500356a3ee7e199d2aab84fd8c4960410154f3d" + } + ], + "fileName": "Modules/_hacl/include/krml/types.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-MD5.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "5dd4ee3c835a0d176a6e9fecbe9752fd1474ff41" + }, + { + "algorithm": "SHA256", + "checksumValue": "d82ef594cba44203576d67b047240316bb3c542912ebb7034afa1e07888cec56" + } + ], + "fileName": "Modules/_hacl/internal/Hacl_Hash_MD5.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-SHA1.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "515b3082eb7c30597773e1c63ec46688f6da3634" + }, + { + "algorithm": "SHA256", + "checksumValue": "10aacf847006b8e0dfb64d5c327443f954db6718b4aec712fb3268230df6a752" + } + ], + "fileName": "Modules/_hacl/internal/Hacl_Hash_SHA1.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-SHA2.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "a044ec12b70ba97b67e9a312827d6270452a20ca" + }, + { + "algorithm": "SHA256", + "checksumValue": "a1426b54fa7273ba5b50817c25b2b26fc85c4d1befb14092cd27dc4c99439463" + } + ], + "fileName": "Modules/_hacl/internal/Hacl_Hash_SHA2.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-SHA3.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "cfb7b520c39a73cb84c541d370455f92b998781f" + }, + { + "algorithm": "SHA256", + "checksumValue": "fd41997f9e96b3c9a3337b1b51fab965a1e21b0c16f353d156f1a1fa00709fbf" + } + ], + "fileName": "Modules/_hacl/internal/Hacl_Hash_SHA3.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-hacl-python-hacl-namespaces.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "f5c7b3ed911af6c8d582e8b3714b0c36195dc994" + }, + { + "algorithm": "SHA256", + "checksumValue": "07de72398b12957e014e97b9ac197bceef12d6d6505c2bfe8b23ee17b94ec5fa" + } + ], + "fileName": "Modules/_hacl/python_hacl_namespaces.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2-config.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "ff5e3ae2360adf7279a9c54d12a1d32e16a1f223" + }, + { + "algorithm": "SHA256", + "checksumValue": "1eb919e885244e43cdf7b2104ad30dc9271513478c0026f6bfb4bad6e2f0ab42" + } + ], + "fileName": "Modules/_blake2/impl/blake2-config.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2-impl.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "28b947b43bdc680b9f4335712bb2a5f2d5d32623" + }, + { + "algorithm": "SHA256", + "checksumValue": "4277092643b289f1d36d32cf0fd2efc30ead8bdd99342e5da3b3609dd8ea7d86" + } + ], + "fileName": "Modules/_blake2/impl/blake2-impl.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "caa3da7953109d0d2961e3b686d2d285c484b901" + }, + { + "algorithm": "SHA256", + "checksumValue": "2f6c9d0ecf70be474f2853b52394993625a32960e0a64eae147ef97a3a5c1460" + } + ], + "fileName": "Modules/_blake2/impl/blake2.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2b-load-sse2.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "029a98f87a178936d9e5211c7798b3e0fc622f94" + }, + { + "algorithm": "SHA256", + "checksumValue": "b392a6e7b43813a05609e994db5fc3552c5912bd482efc781daa0778eb56ab4e" + } + ], + "fileName": "Modules/_blake2/impl/blake2b-load-sse2.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2b-load-sse41.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "fb466dd72344170d09e311e5ea12de99ce071357" + }, + { + "algorithm": "SHA256", + "checksumValue": "cc3072c92164142bf2f9dda4e6c08db61be68ec15a95442415e861090d08f6a2" + } + ], + "fileName": "Modules/_blake2/impl/blake2b-load-sse41.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2b-ref.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "4c0d79128cf891a95b1f668031d55c0c6d2e0270" + }, + { + "algorithm": "SHA256", + "checksumValue": "07b257d44e9cc2d95d4911629c92138feafd16d63fef0a5fa7b38914dfd82349" + } + ], + "fileName": "Modules/_blake2/impl/blake2b-ref.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2b-round.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "4c7418e2026417c9c6736fcd305a31f23e05a661" + }, + { + "algorithm": "SHA256", + "checksumValue": "fa34a60c2d198a0585033f43fd4003f4ba279c9ebcabdf5d6650def0e6d1e914" + } + ], + "fileName": "Modules/_blake2/impl/blake2b-round.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2b.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "6fa074693aa7305018dfa8db48010a8ef1050ad4" + }, + { + "algorithm": "SHA256", + "checksumValue": "c8c6dd861ac193d4a0e836242ff44900f83423f86d2c2940c8c4c1e41fbd5812" + } + ], + "fileName": "Modules/_blake2/impl/blake2b.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2s-load-sse2.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "ad3f79b6cbe3fd812722114a0d5d08064e69e4d0" + }, + { + "algorithm": "SHA256", + "checksumValue": "57f1ac6c09f4a50d95811529062220eab4f29cec3805bc6081dec00426c6df62" + } + ], + "fileName": "Modules/_blake2/impl/blake2s-load-sse2.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2s-load-sse41.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "51c32d79f419f3d2eb9875cd9a7f5c0d7892f8a8" + }, + { + "algorithm": "SHA256", + "checksumValue": "ecc9e09adcbe098629eafd305596bed8d7004be1d83f326995def42bbde93b23" + } + ], + "fileName": "Modules/_blake2/impl/blake2s-load-sse41.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2s-load-xop.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "2749a7ba0104b765d4f56f13faf70b6eb89cf203" + }, + { + "algorithm": "SHA256", + "checksumValue": "8bc95595cec4c50f5d70f2b330d3798de07cc784e8890791b3328890e602d5c5" + } + ], + "fileName": "Modules/_blake2/impl/blake2s-load-xop.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2s-ref.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "883fcfe85f9063819f21b1100296d1f9eb55bac1" + }, + { + "algorithm": "SHA256", + "checksumValue": "9715c00d0f11587a139b07fa26678e6d26e44d3d4910b96158d158da2b022bfb" + } + ], + "fileName": "Modules/_blake2/impl/blake2s-ref.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2s-round.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "5d9f69adda40ed163b287b9ed4cedb35b88f2daa" + }, + { + "algorithm": "SHA256", + "checksumValue": "65d90111c89c43bb18a9e1d1a4fdbd9f85bebd1ff00129335b85995d0f30ee8b" + } + ], + "fileName": "Modules/_blake2/impl/blake2s-round.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-blake2-impl-blake2s.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "d2691353fa54ac6ffcd7c0a294984dc9d7968ef7" + }, + { + "algorithm": "SHA256", + "checksumValue": "cfd7948c9fd50e9f9c62f8a93b20a254d1d510a862d1092af4f187b7c1a859a3" + } + ], + "fileName": "Modules/_blake2/impl/blake2s.c" + }, + { + "SPDXID": "SPDXRef-FILE-Lib-ctypes-macholib-init-.py", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0fbc026a9771d9675e7094790b5b945334d3cb53" + }, + { + "algorithm": "SHA256", + "checksumValue": "1e77c01eec8f167ed10b754f153c0c743c8e5196ae9c81dffc08f129ab56dbfd" + } + ], + "fileName": "Lib/ctypes/macholib/__init__.py" + }, + { + "SPDXID": "SPDXRef-FILE-Lib-ctypes-macholib-dyld.py", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "4a78ebd73ce4167c722689781a15fe0b4578e967" + }, + { + "algorithm": "SHA256", + "checksumValue": "eb8e7b17f1533bc3e86e23e8695f7a5e4b7a99ef1b1575d10af54f389161b655" + } + ], + "fileName": "Lib/ctypes/macholib/dyld.py" + }, + { + "SPDXID": "SPDXRef-FILE-Lib-ctypes-macholib-dylib.py", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "f339420cc01bd01f8d0da19b6102f099075e8bcd" + }, + { + "algorithm": "SHA256", + "checksumValue": "f19ee056b18165cc6735efab0b4ca3508be9405b9646c38113316c15e8278a6f" + } + ], + "fileName": "Lib/ctypes/macholib/dylib.py" + }, + { + "SPDXID": "SPDXRef-FILE-Lib-ctypes-macholib-framework.py", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0b219f58467d7f193fa1de0c1b118485840d855b" + }, + { + "algorithm": "SHA256", + "checksumValue": "302439e40d9cbdd61b8b7cffd0b7e1278a6811b635044ee366a36e0d991f62da" + } + ], + "fileName": "Lib/ctypes/macholib/framework.py" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-README.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "bda6e0bd6121f7069b420bdc0bc7c49414d948d1" + }, + { + "algorithm": "SHA256", + "checksumValue": "89926cd0fe6cfb33a2b5b7416c101e9b5d42b0d639d348e0871acf6ffc8258a3" + } + ], + "fileName": "Modules/_decimal/libmpdec/README.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-basearith.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "33757ce2ec0c93c1b5e03c45a495563a00e498ae" + }, + { + "algorithm": "SHA256", + "checksumValue": "ad498362c31a5b99ab19fce320ac540cf14c5c4ec09478f0ad3858da1428113d" + } + ], + "fileName": "Modules/_decimal/libmpdec/basearith.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-basearith.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "bf03919412c068e6969e7ac48850f91bfcd3b2b1" + }, + { + "algorithm": "SHA256", + "checksumValue": "2eaac88a71b9bcf3144396c12dcfeced573e0e550a0050d75b9ed3903248596d" + } + ], + "fileName": "Modules/_decimal/libmpdec/basearith.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-bench.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "c925b7f26754ae182aaa461d51802e8b6a2bb5e9" + }, + { + "algorithm": "SHA256", + "checksumValue": "007e38542ec8d9d8805fe243b5390d79211b9360e2797a20079e833e68ad9e45" + } + ], + "fileName": "Modules/_decimal/libmpdec/bench.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-bench-full.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "cb22686269685a53a17afdea9ed984714e399d9d" + }, + { + "algorithm": "SHA256", + "checksumValue": "1b9e892d4b268deea835ec8906f20a1e5d25e037b2e698edcd34315613f3608c" + } + ], + "fileName": "Modules/_decimal/libmpdec/bench_full.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-bits.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "fc91c2450cdf1e785d1347411662294c3945eb27" + }, + { + "algorithm": "SHA256", + "checksumValue": "ce7741e58ea761a24250c0bfa10058cec8c4fd220dca70a41de3927a2e4f5376" + } + ], + "fileName": "Modules/_decimal/libmpdec/bits.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-constants.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "7187c18916b0a546ec19b4fc4bec43d0d9fb5fc2" + }, + { + "algorithm": "SHA256", + "checksumValue": "cd430b8657cf8a616916e02f9bd5ca044d5fc19e69333f5d427e1fdb90b0864b" + } + ], + "fileName": "Modules/_decimal/libmpdec/constants.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-constants.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "af9cbd016fb0ef0b30ced49c0aa4ce2ca3c20125" + }, + { + "algorithm": "SHA256", + "checksumValue": "19dc46df04abb7ee08e9a403f87c8aac8d4a077efcce314c597f8b73e22884f2" + } + ], + "fileName": "Modules/_decimal/libmpdec/constants.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-context.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "666162870230bebd3f2383020d908806fd03909e" + }, + { + "algorithm": "SHA256", + "checksumValue": "9a265d366f31894aad78bca7fcdc1457bc4a3aa3887ca231b7d78e41f79541c0" + } + ], + "fileName": "Modules/_decimal/libmpdec/context.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-convolute.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0545547a8b37b922fbe2574fbad8fc3bf16f1d33" + }, + { + "algorithm": "SHA256", + "checksumValue": "66fe27b9bb37039cad5be32b105ed509e5aefa15c1957a9058af8ee23cddc97a" + } + ], + "fileName": "Modules/_decimal/libmpdec/convolute.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-convolute.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "05ff0936c5bb08f40d460f5843004a1cc0751d9b" + }, + { + "algorithm": "SHA256", + "checksumValue": "c00d17450c2b8e1d7f1eb8a084f7e6a68f257a453f8701600e860bf357c531d7" + } + ], + "fileName": "Modules/_decimal/libmpdec/convolute.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-crt.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "fe8176849bc99a306332ba25caa4e91bfa3c6f7d" + }, + { + "algorithm": "SHA256", + "checksumValue": "1f4e65c44864c3e911a6e91f33adec76765293e90553459e3ebce35a58898dba" + } + ], + "fileName": "Modules/_decimal/libmpdec/crt.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-crt.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "1930b9e0910014b3479aec4e940f02118d9e4a08" + }, + { + "algorithm": "SHA256", + "checksumValue": "7d31f1d0dd73b62964dab0f7a1724473bf87f1f95d8febf0b40c15430ae9a47c" + } + ], + "fileName": "Modules/_decimal/libmpdec/crt.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-difradix2.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "415c51e7d7f517b6366bec2a809610d0d38ada14" + }, + { + "algorithm": "SHA256", + "checksumValue": "0a9fef8a374f55277e9f6000b7277bb037b9763c32b156c29950422b057498bd" + } + ], + "fileName": "Modules/_decimal/libmpdec/difradix2.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-difradix2.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "d8a998c3bee4c3d9059ba7bf9ae6a8b64649c2ba" + }, + { + "algorithm": "SHA256", + "checksumValue": "5c6766496224de657400995b58b64db3e7084004bf00daebdd7e08d0c5995243" + } + ], + "fileName": "Modules/_decimal/libmpdec/difradix2.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-README.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "158f6ad18edf348efa4fdd7cf61114c77c1d22e9" + }, + { + "algorithm": "SHA256", + "checksumValue": "7b0da2758097a2688f06b3c7ca46b2ebc8329addbd28bb4f5fe95626cc81f8a9" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/README.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-compare.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "ef80ba26847287fb351ab0df0a78b5f08ba0b5b7" + }, + { + "algorithm": "SHA256", + "checksumValue": "452666ee4eb10a8cf0a926cb3bcf5e95b5c361fa129dbdfe27b654e6d640417e" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/compare.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-div.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "6ca3a369b3d1e140fdc93c4fdbedb724f7daf969" + }, + { + "algorithm": "SHA256", + "checksumValue": "6d369f5a24d0bb1e7cb6a4f8b0e97a273260e7668c8a540a8fcc92e039f7af2e" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/div.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-divmod.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "3872a28b4f77e07e1760256067ea338a8dd183f8" + }, + { + "algorithm": "SHA256", + "checksumValue": "5db54bae75ac3d7fa12f1bb0f7ce1bf797df86a81030e8c3ce44d3b1f9b958b7" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/divmod.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-multiply.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "25dbc94fd4ee5dec21061d2d40dd5d0f88849cb1" + }, + { + "algorithm": "SHA256", + "checksumValue": "22ed39b18fa740a27aacfd29a7bb40066be24500ba49b9b1f24e2af1e039fcd9" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/multiply.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-pow.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "13d3b7657dc2dc5000fea428f57963d520792ef7" + }, + { + "algorithm": "SHA256", + "checksumValue": "cd8c037649b3d4d6897c9acd2f92f3f9d5390433061d5e48623a5d526a3f4f9c" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/pow.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-powmod.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "1f7e6c3d3e38df52bbcec0f5a180a8f328679618" + }, + { + "algorithm": "SHA256", + "checksumValue": "e29614b43abf1856b656a84d6b67c22cc5dc7af8cbae8ddc7acf17022220ee12" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/powmod.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-shift.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0bd9ce89c7987d1109eb7b0c8f1f9a1298e1422e" + }, + { + "algorithm": "SHA256", + "checksumValue": "203f2dbf11d115580cb3c7c524ac6ccca2a7b31d89545db1b6263381b5de2b6a" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/shift.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-sqrt.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "b401ba0814e17c9164c0df26e01cc0a355382f46" + }, + { + "algorithm": "SHA256", + "checksumValue": "f3dc2ce321833bbd4b3d1d9ea6fa2e0bcc1bfe1e39abb8d55be53e46c33949db" + } + ], + "fileName": "Modules/_decimal/libmpdec/examples/sqrt.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-fnt.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "060615ddef089a5a8f879a57e4968d920972a0e2" + }, + { + "algorithm": "SHA256", + "checksumValue": "a9f923524d53a9445769f27405375ec3d95fa804bb11db5ee249ae047f11cfce" + } + ], + "fileName": "Modules/_decimal/libmpdec/fnt.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-fnt.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "b205043ebeaf065b16505a299342a992654f19b0" + }, + { + "algorithm": "SHA256", + "checksumValue": "3b03e69adf78fde68c8f87d33595d557237581d33fc067e1039eed9e9f2cc44c" + } + ], + "fileName": "Modules/_decimal/libmpdec/fnt.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-fourstep.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "702c27599b43280c94906235d7e1a74193ba701b" + }, + { + "algorithm": "SHA256", + "checksumValue": "cf2e69b946ec14b087e523c0ff606553070d13c23e851fb0ba1df51a728017e6" + } + ], + "fileName": "Modules/_decimal/libmpdec/fourstep.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-fourstep.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "ee5291c265ef1f5ae373bc243a4d96975eb3e7b5" + }, + { + "algorithm": "SHA256", + "checksumValue": "dbaced03b52d0f880c377b86c943bcb36f24d557c99a5e9732df3ad5debb5917" + } + ], + "fileName": "Modules/_decimal/libmpdec/fourstep.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-io.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "12402bcf7f0161adb83f78163f41cc10a5e5de5f" + }, + { + "algorithm": "SHA256", + "checksumValue": "cba044c76b6bc3ae6cfa49df1121cad7552140157b9e61e11cbb6580cc5d74cf" + } + ], + "fileName": "Modules/_decimal/libmpdec/io.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-io.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "28c653cd40b1ce46575e41f5dbfda5f6dd0db4d1" + }, + { + "algorithm": "SHA256", + "checksumValue": "259eab89fe27914e0e39e61199094a357ac60d86b2aab613c909040ff64a4a0c" + } + ], + "fileName": "Modules/_decimal/libmpdec/io.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-REFERENCES.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "218d1d7bedb335cd2c31eae89a15873c3139e13f" + }, + { + "algorithm": "SHA256", + "checksumValue": "a57e8bed93ded481ef264166aec2c49d1a7f3252f29a873ee41fff053cfd9c20" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/REFERENCES.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-bignum.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "f67eab2431336cf6eeafb30cdafd7e54c251def3" + }, + { + "algorithm": "SHA256", + "checksumValue": "dc34aa122c208ce79e3fc6baee8628094ffaf6a662862dd5647836241f6ebd79" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/bignum.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-fnt.py", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "a58cfbcd8ea57d66ddfd11fb5a170138c8bbfb3a" + }, + { + "algorithm": "SHA256", + "checksumValue": "122de20eebf87274af2d02072251a94500e7df2d5ef29e81aeabeda991c079e3" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/fnt.py" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-matrix-transform.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "9a947f6b660150cbd457c4458da2956a36c5824d" + }, + { + "algorithm": "SHA256", + "checksumValue": "592659e7192e3a939b797f5bc7455455834a285f5d8b643ccd780b5114914f73" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/matrix-transform.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-mulmod-64.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "69fe9afb8353b5a2b57917469c51c64ac518169d" + }, + { + "algorithm": "SHA256", + "checksumValue": "229a80ca940c594a32e3345412370cbc097043fe59c66a6153cbcf01e7837266" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/mulmod-64.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-mulmod-ppro.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "720d468a1f51098036c7a0c869810fff22ed9b79" + }, + { + "algorithm": "SHA256", + "checksumValue": "f3549fc73f697a087267c7b042e30a409e191cbba69a2c0902685e507fbae9f7" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/mulmod-ppro.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-six-step.txt", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "6815ec3a39baebebe7b3f51d45d10c180a659f17" + }, + { + "algorithm": "SHA256", + "checksumValue": "bf15f73910a173c98fca9db56122b6cc71983668fa8b934c46ca21a57398ec54" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/six-step.txt" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-umodarith.lisp", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "c91ac4438e661ce78f86e981257546e5adff39ae" + }, + { + "algorithm": "SHA256", + "checksumValue": "783a1b4b9b7143677b0c3d30ffaf28aa0cb01956409031fa38ed8011970bdee0" + } + ], + "fileName": "Modules/_decimal/libmpdec/literature/umodarith.lisp" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-mpalloc.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "7e8dfb4b7a801b48c501969b001153203b14679e" + }, + { + "algorithm": "SHA256", + "checksumValue": "5ba2f4c80302e71fb216aa247c858e0bf6c8cfabffe7980ac17d4d023c0fef2b" + } + ], + "fileName": "Modules/_decimal/libmpdec/mpalloc.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-mpalloc.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "bccb6a6ae76fd7f6c8a9102a78958bcad7862950" + }, + { + "algorithm": "SHA256", + "checksumValue": "f7412521de38afb837fcabc2b1d48b971b86bfaa55f3f40d58ff9e46e92debd3" + } + ], + "fileName": "Modules/_decimal/libmpdec/mpalloc.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-mpdecimal.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "f4539afb1ace58c52d18ffd0cc7704f53ca55182" + }, + { + "algorithm": "SHA256", + "checksumValue": "4f89b8095e408a18deff79cfb605299e615bae747898eb105d8936064f7fb626" + } + ], + "fileName": "Modules/_decimal/libmpdec/mpdecimal.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-mpdecimal.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "4b80e25ac49b7e1ea0d1e84967ee32a3d111fc4c" + }, + { + "algorithm": "SHA256", + "checksumValue": "ea0b9c6b296c13aed6ecaa50b463e39a9c1bdc059b84f50507fd8247b2e660f9" + } + ], + "fileName": "Modules/_decimal/libmpdec/mpdecimal.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-mpsignal.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "5c7305a6db0fddf64c6d97e29d3b0c402e3d5d6e" + }, + { + "algorithm": "SHA256", + "checksumValue": "653171cf2549719478417db7e9800fa0f9d99c02dec6da6876329ccf2c07b93f" + } + ], + "fileName": "Modules/_decimal/libmpdec/mpsignal.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-numbertheory.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "d736b874c43777ca021dde5289ea718893f39219" + }, + { + "algorithm": "SHA256", + "checksumValue": "bdbf2e246f341a3ba3f6f9d8759e7cb222eb9b15f9ed1e7c9f6a59cbb9f8bc91" + } + ], + "fileName": "Modules/_decimal/libmpdec/numbertheory.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-numbertheory.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "d341508d8c6dd4c4cbd8b99afc8029945f9bbe0d" + }, + { + "algorithm": "SHA256", + "checksumValue": "2f7d5b40af508fa6ac86f5d62101fa3bf683c63b24aa87c9548e3fdd13abc57b" + } + ], + "fileName": "Modules/_decimal/libmpdec/numbertheory.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-sixstep.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "cbd05d68bb3940d0d7d0818b14cc03b090a4dd74" + }, + { + "algorithm": "SHA256", + "checksumValue": "7602aaf98ec9525bc4b3cab9631615e1be2efd9af894002ef4e3f5ec63924fcf" + } + ], + "fileName": "Modules/_decimal/libmpdec/sixstep.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-sixstep.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "4c059463ec4b4522562dab24760fc64c172c9eee" + }, + { + "algorithm": "SHA256", + "checksumValue": "a191366348b3d3dd49b9090ec5c77dbd77bb3a523c01ff32adafa137e5097ce7" + } + ], + "fileName": "Modules/_decimal/libmpdec/sixstep.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-transpose.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "cc5593ac9fdb60480cc23fc9d6f27d85670bd35f" + }, + { + "algorithm": "SHA256", + "checksumValue": "2d12fcae512143a9376c8a0d4c1ba3008e420e024497a7e7ec64c6bec23fcddc" + } + ], + "fileName": "Modules/_decimal/libmpdec/transpose.c" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-transpose.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "2f616425756b6cbdf7d189744870b98b613455bd" + }, + { + "algorithm": "SHA256", + "checksumValue": "fafeb2b901b2b41bf0df00be7d99b84df1a78e3cc1e582e09cbfc3b6d44d4abe" + } + ], + "fileName": "Modules/_decimal/libmpdec/transpose.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-typearith.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "b1e9341e173cc8e219ad4aa45fad36d92cce10d3" + }, + { + "algorithm": "SHA256", + "checksumValue": "25e0a0703b51744277834e6b2398d7b7d2c17f92bf30f8b6f949e0486ae2b346" + } + ], + "fileName": "Modules/_decimal/libmpdec/typearith.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-umodarith.h", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "46f6483fce136cd3cc2f7516ee119a487d86333e" + }, + { + "algorithm": "SHA256", + "checksumValue": "bfe1ddb2ca92906456b80745adcbe02c83cadac3ef69caa21bc09b7292cc152b" + } + ], + "fileName": "Modules/_decimal/libmpdec/umodarith.h" + }, + { + "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-vcdiv64.asm", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "d0cc1052fcba08b773d935b0ae2dc6b80d0f2f68" + }, + { + "algorithm": "SHA256", + "checksumValue": "aacc3e47ea8f41e8840c6c67f64ec96d54696a16889903098fa1aab56949a00f" + } + ], + "fileName": "Modules/_decimal/libmpdec/vcdiv64.asm" + }, + { + "SPDXID": "SPDXRef-FILE-Lib-ensurepip-bundled-pip-23.3.1-py3-none-any.whl", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "4b2baddc0673f73017e531648a9ee27e47925e7a" + }, + { + "algorithm": "SHA256", + "checksumValue": "55eb67bb6171d37447e82213be585b75fe2b12b359e993773aca4de9247a052b" + } + ], + "fileName": "Lib/ensurepip/_bundled/pip-23.3.1-py3-none-any.whl" + } + ], + "packages": [ + { + "SPDXID": "SPDXRef-PACKAGE-expat", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "6b902ab103843592be5e99504f846ec109c1abb692e85347587f237a4ffa1033" + } + ], + "downloadLocation": "https://github.com/libexpat/libexpat/releases/download/R_2_5_0/expat-2.5.0.tar.gz", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceLocator": "cpe:2.3:a:libexpat_project:libexpat:2.5.0:*:*:*:*:*:*:*", + "referenceType": "cpe23Type" + } + ], + "licenseConcluded": "MIT", + "name": "expat", + "originator": "Organization: Expat development team", + "primaryPackagePurpose": "SOURCE", + "versionInfo": "2.5.0" + }, + { + "SPDXID": "SPDXRef-PACKAGE-hacl-star", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "c23ac158b238c368389dc86bfc315263e5c0e57785da74144aea2cab9a3d51a2" + } + ], + "downloadLocation": "https://github.com/hacl-star/hacl-star/archive/521af282fdf6d60227335120f18ae9309a4b8e8c.zip", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceLocator": "cpe:2.3:a:hacl-star:hacl-star:521af282fdf6d60227335120f18ae9309a4b8e8c:*:*:*:*:*:*:*", + "referenceType": "cpe23Type" + } + ], + "licenseConcluded": "Apache-2.0", + "name": "hacl-star", + "originator": "Organization: HACL* Developers", + "primaryPackagePurpose": "SOURCE", + "versionInfo": "521af282fdf6d60227335120f18ae9309a4b8e8c" + }, + { + "SPDXID": "SPDXRef-PACKAGE-libb2", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "53626fddce753c454a3fea581cbbc7fe9bbcf0bc70416d48fdbbf5d87ef6c72e" + } + ], + "downloadLocation": "https://github.com/BLAKE2/libb2/releases/download/v0.98.1/libb2-0.98.1.tar.gz", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceLocator": "cpe:2.3:a:blake2:libb2:0.98.1:*:*:*:*:*:*:*", + "referenceType": "cpe23Type" + } + ], + "licenseConcluded": "CC0-1.0", + "name": "libb2", + "originator": "Organization: BLAKE2 - fast secure hashing", + "primaryPackagePurpose": "SOURCE", + "versionInfo": "0.98.1" + }, + { + "SPDXID": "SPDXRef-PACKAGE-macholib", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "c76f268f5054024e962f2515a0e522baf85313064f6740d80375afc850787a38" + } + ], + "downloadLocation": "https://files.pythonhosted.org/packages/ec/57/f0a712efc3ed982cf4038a3cee172057303b9be914c32c93b2fbec27f785/macholib-1.0.tar.gz", + "externalRefs": [ + { + "referenceCategory": "PACKAGE_MANAGER", + "referenceLocator": "pkg:pypi/macholib@1.0", + "referenceType": "purl" + } + ], + "licenseConcluded": "MIT", + "name": "macholib", + "originator": "Person: Ronald Oussoren (ronaldoussoren@mac.com)", + "primaryPackagePurpose": "SOURCE", + "versionInfo": "1.0" + }, + { + "SPDXID": "SPDXRef-PACKAGE-mpdecimal", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "9f9cd4c041f99b5c49ffb7b59d9f12d95b683d88585608aa56a6307667b2b21f" + } + ], + "downloadLocation": "https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-2.5.1.tar.gz", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceLocator": "cpe:2.3:a:bytereef:mpdecimal:2.5.1:*:*:*:*:*:*:*", + "referenceType": "cpe23Type" + } + ], + "licenseConcluded": "BSD-2-Clause", + "name": "mpdecimal", + "originator": "Organization: bytereef.org", + "primaryPackagePurpose": "SOURCE", + "versionInfo": "2.5.1" + }, + { + "SPDXID": "SPDXRef-PACKAGE-pip", + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "7ccf472345f20d35bdc9d1841ff5f313260c2c33fe417f48c30ac46cccabf5be" + } + ], + "downloadLocation": "https://files.pythonhosted.org/packages/50/c2/e06851e8cc28dcad7c155f4753da8833ac06a5c704c109313b8d5a62968a/pip-23.2.1-py3-none-any.whl", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceLocator": "cpe:2.3:a:pypa:pip:23.2.1:*:*:*:*:*:*:*", + "referenceType": "cpe23Type" + }, + { + "referenceCategory": "PACKAGE_MANAGER", + "referenceLocator": "pkg:pypi/pip@23.2.1", + "referenceType": "purl" + } + ], + "licenseConcluded": "MIT", + "name": "pip", + "originator": "Organization: Python Packaging Authority", + "primaryPackagePurpose": "SOURCE", + "versionInfo": "23.2.1" + } + ], + "relationships": [ + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-COPYING", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-ascii.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-asciitab.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-expat.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-expat-config.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-expat-external.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-iasciitab.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-internal.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-latin1tab.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-nametab.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-pyexpatns.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-siphash.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-utf8tab.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-winconfig.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmlparse.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmlrole.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmlrole.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmltok.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmltok.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmltok-impl.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmltok-impl.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-expat-xmltok-ns.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-expat" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-MD5.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-MD5.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA1.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA1.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA2.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA2.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA3.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Hash-SHA3.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-Hacl-Streaming-Types.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-include-krml-FStar-UInt128-Verified.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-include-krml-FStar-UInt-8-16-32-64.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-include-krml-fstar-uint128-struct-endianness.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-include-krml-internal-target.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-include-krml-lowstar-endianness.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-include-krml-types.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-MD5.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-SHA1.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-SHA2.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-internal-Hacl-Hash-SHA3.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-hacl-python-hacl-namespaces.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-hacl-star" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2-config.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2-impl.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2b-load-sse2.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2b-load-sse41.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2b-ref.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2b-round.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2b.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2s-load-sse2.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2s-load-sse41.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2s-load-xop.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2s-ref.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2s-round.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-blake2-impl-blake2s.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-libb2" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Lib-ctypes-macholib-init-.py", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-macholib" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Lib-ctypes-macholib-dyld.py", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-macholib" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Lib-ctypes-macholib-dylib.py", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-macholib" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Lib-ctypes-macholib-framework.py", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-macholib" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-README.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-basearith.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-basearith.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-bench.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-bench-full.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-bits.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-constants.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-constants.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-context.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-convolute.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-convolute.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-crt.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-crt.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-difradix2.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-difradix2.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-README.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-compare.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-div.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-divmod.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-multiply.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-pow.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-powmod.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-shift.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-sqrt.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-fnt.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-fnt.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-fourstep.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-fourstep.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-io.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-io.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-REFERENCES.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-bignum.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-fnt.py", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-matrix-transform.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-mulmod-64.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-mulmod-ppro.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-six-step.txt", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-umodarith.lisp", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-mpalloc.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-mpalloc.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-mpdecimal.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-mpdecimal.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-mpsignal.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-numbertheory.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-numbertheory.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-sixstep.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-sixstep.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-transpose.c", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-transpose.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-typearith.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-umodarith.h", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-vcdiv64.asm", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" + }, + { + "relatedSpdxElement": "SPDXRef-FILE-Lib-ensurepip-bundled-pip-23.3.1-py3-none-any.whl", + "relationshipType": "CONTAINS", + "spdxElementId": "SPDXRef-PACKAGE-pip" + } + ], + "spdxVersion": "SPDX-2.3" +} \ No newline at end of file diff --git a/Modules/_csv.c b/Modules/_csv.c index 714fbef08d22c9..ae6b6457ffad9a 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -160,15 +160,9 @@ static PyObject * get_dialect_from_registry(PyObject *name_obj, _csvstate *module_state) { PyObject *dialect_obj; - - dialect_obj = PyDict_GetItemWithError(module_state->dialects, name_obj); - if (dialect_obj == NULL) { - if (!PyErr_Occurred()) - PyErr_Format(module_state->error_obj, "unknown dialect"); + if (PyDict_GetItemRef(module_state->dialects, name_obj, &dialect_obj) == 0) { + PyErr_SetString(module_state->error_obj, "unknown dialect"); } - else - Py_INCREF(dialect_obj); - return dialect_obj; } diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c index 5869d8f952d184..fc9fc131f6249a 100644 --- a/Modules/_ctypes/_ctypes_test.c +++ b/Modules/_ctypes/_ctypes_test.c @@ -1,8 +1,8 @@ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED #define Py_LIMITED_API 0x030c0000 #endif @@ -150,6 +150,42 @@ _testfunc_array_in_struct2a(Test3B in) return result; } +/* + * See gh-110190. structs containing arrays of up to four floating point types + * (max 32 bytes) are passed in registers on Arm. + */ + +typedef struct { + double data[4]; +} Test3C; + +EXPORT(Test3C) +_testfunc_array_in_struct_set_defaults_3C(void) +{ + Test3C s; + s.data[0] = 1.0; + s.data[1] = 2.0; + s.data[2] = 3.0; + s.data[3] = 4.0; + return s; +} + +typedef struct { + double data[5]; +} Test3D; + +EXPORT(Test3D) +_testfunc_array_in_struct_set_defaults_3D(void) +{ + Test3D s; + s.data[0] = 1.0; + s.data[1] = 2.0; + s.data[2] = 3.0; + s.data[3] = 4.0; + s.data[4] = 5.0; + return s; +} + typedef union { long a_long; struct { diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 6fbcf77a115371..04dd9bae32cd5e 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -697,29 +697,43 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct stgdict->align = total_align; stgdict->length = len; /* ADD ffi_ofs? */ -#define MAX_STRUCT_SIZE 16 +/* + * On Arm platforms, structs with at most 4 elements of any floating point + * type values can be passed through registers. If the type is double the + * maximum size of the struct is 32 bytes. + * By Arm platforms it is meant both 32 and 64-bit. +*/ +#if defined(__aarch64__) || defined(__arm__) +# define MAX_STRUCT_SIZE 32 +#else +# define MAX_STRUCT_SIZE 16 +#endif if (arrays_seen && (size <= MAX_STRUCT_SIZE)) { /* - * See bpo-22273. Arrays are normally treated as pointers, which is - * fine when an array name is being passed as parameter, but not when - * passing structures by value that contain arrays. On 64-bit Linux, - * small structures passed by value are passed in registers, and in - * order to do this, libffi needs to know the true type of the array - * members of structs. Treating them as pointers breaks things. + * See bpo-22273 and gh-110190. Arrays are normally treated as + * pointers, which is fine when an array name is being passed as + * parameter, but not when passing structures by value that contain + * arrays. + * On x86_64 Linux and Arm platforms, small structures passed by + * value are passed in registers, and in order to do this, libffi needs + * to know the true type of the array members of structs. Treating them + * as pointers breaks things. * - * By small structures, we mean ones that are 16 bytes or less. In that - * case, there can't be more than 16 elements after unrolling arrays, - * as we (will) disallow bitfields. So we can collect the true ffi_type - * values in a fixed-size local array on the stack and, if any arrays - * were seen, replace the ffi_type_pointer.elements with a more - * accurate set, to allow libffi to marshal them into registers - * correctly. It means one more loop over the fields, but if we got - * here, the structure is small, so there aren't too many of those. + * By small structures, we mean ones that are 16 bytes or less on + * x86-64 and 32 bytes or less on Arm. In that case, there can't be + * more than 16 or 32 elements after unrolling arrays, as we (will) + * disallow bitfields. So we can collect the true ffi_type values in + * a fixed-size local array on the stack and, if any arrays were seen, + * replace the ffi_type_pointer.elements with a more accurate set, + * to allow libffi to marshal them into registers correctly. + * It means one more loop over the fields, but if we got here, + * the structure is small, so there aren't too many of those. * - * Although the passing in registers is specific to 64-bit Linux, the - * array-in-struct vs. pointer problem is general. But we restrict the - * type transformation to small structs nonetheless. + * Although the passing in registers is specific to x86_64 Linux + * and Arm platforms, the array-in-struct vs. pointer problem is + * general. But we restrict the type transformation to small structs + * nonetheless. * * Note that although a union may be small in terms of memory usage, it * could contain many overlapping declarations of arrays, e.g. @@ -745,6 +759,9 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct * struct { uint_32 e1; uint_32 e2; ... uint_32 e_4; } f6; * } * + * The same principle applies for a struct 32 bytes in size like in + * the case of Arm platforms. + * * So the struct/union needs setting up as follows: all non-array * elements copied across as is, and all array elements replaced with * an equivalent struct which has as many fields as the array has diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 2592c4d7f75631..9ab847165dc097 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -1,6 +1,5 @@ #include "Python.h" #include "pycore_call.h" // _PyObject_CallNoArgs() -#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION #include "pycore_dict.h" // _PyDict_Pop_KnownHash() #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_moduleobject.h" // _PyModule_GetState() diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 169b2b3d105669..679626863c385c 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -8,12 +8,11 @@ */ #include "Python.h" -#include "pycore_bytesobject.h" // _PyBytes_Join() -#include "pycore_call.h" // _PyObject_CallNoArgs() -#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION() -#include "pycore_object.h" // _PyObject_GC_UNTRACK() -#include "pycore_pyerrors.h" // _Py_FatalErrorFormat() -#include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() +#include "pycore_bytesobject.h" // _PyBytes_Join() +#include "pycore_call.h" // _PyObject_CallNoArgs() +#include "pycore_object.h" // _PyObject_GC_UNTRACK() +#include "pycore_pyerrors.h" // _Py_FatalErrorFormat() +#include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() #include "_iomodule.h" @@ -83,6 +82,7 @@ _bufferediobase_readinto_generic(PyObject *self, Py_buffer *buffer, char readint } /*[clinic input] +@critical_section _io._BufferedIOBase.readinto buffer: Py_buffer(accept={rwbuffer}) / @@ -90,12 +90,13 @@ _io._BufferedIOBase.readinto static PyObject * _io__BufferedIOBase_readinto_impl(PyObject *self, Py_buffer *buffer) -/*[clinic end generated code: output=8c8cda6684af8038 input=00a6b9a38f29830a]*/ +/*[clinic end generated code: output=8c8cda6684af8038 input=5273d20db7f56e1a]*/ { return _bufferediobase_readinto_generic(self, buffer, 0); } /*[clinic input] +@critical_section _io._BufferedIOBase.readinto1 buffer: Py_buffer(accept={rwbuffer}) / @@ -103,7 +104,7 @@ _io._BufferedIOBase.readinto1 static PyObject * _io__BufferedIOBase_readinto1_impl(PyObject *self, Py_buffer *buffer) -/*[clinic end generated code: output=358623e4fd2b69d3 input=ebad75b4aadfb9be]*/ +/*[clinic end generated code: output=358623e4fd2b69d3 input=d6eb723dedcee654]*/ { return _bufferediobase_readinto_generic(self, buffer, 1); } @@ -432,12 +433,13 @@ buffered_dealloc(buffered *self) } /*[clinic input] +@critical_section _io._Buffered.__sizeof__ [clinic start generated code]*/ static PyObject * _io__Buffered___sizeof___impl(buffered *self) -/*[clinic end generated code: output=0231ef7f5053134e input=753c782d808d34df]*/ +/*[clinic end generated code: output=0231ef7f5053134e input=07a32d578073ea64]*/ { size_t res = _PyObject_SIZE(Py_TYPE(self)); if (self->buffer) { @@ -489,12 +491,13 @@ _io__Buffered__dealloc_warn(buffered *self, PyObject *source) /* Flush and close */ /*[clinic input] +@critical_section _io._Buffered.flush as _io__Buffered_simple_flush [clinic start generated code]*/ static PyObject * _io__Buffered_simple_flush_impl(buffered *self) -/*[clinic end generated code: output=29ebb3820db1bdfd input=f33ef045e7250767]*/ +/*[clinic end generated code: output=29ebb3820db1bdfd input=5248cb84a65f80bd]*/ { CHECK_INITIALIZED(self) return PyObject_CallMethodNoArgs(self->raw, &_Py_ID(flush)); @@ -514,8 +517,15 @@ buffered_closed(buffered *self) return closed; } +/*[clinic input] +@critical_section +@getter +_io._Buffered.closed +[clinic start generated code]*/ + static PyObject * -buffered_closed_get(buffered *self, void *context) +_io__Buffered_closed_get_impl(buffered *self) +/*[clinic end generated code: output=f08ce57290703a1a input=18eddefdfe4a3d2f]*/ { CHECK_INITIALIZED(self) return PyObject_GetAttr(self->raw, &_Py_ID(closed)); @@ -585,12 +595,13 @@ _io__Buffered_close_impl(buffered *self) } /*[clinic input] +@critical_section _io._Buffered.detach [clinic start generated code]*/ static PyObject * _io__Buffered_detach_impl(buffered *self) -/*[clinic end generated code: output=dd0fc057b8b779f7 input=482762a345cc9f44]*/ +/*[clinic end generated code: output=dd0fc057b8b779f7 input=d4ef1828a678be37]*/ { PyObject *raw; CHECK_INITIALIZED(self) @@ -607,50 +618,68 @@ _io__Buffered_detach_impl(buffered *self) /* Inquiries */ /*[clinic input] +@critical_section _io._Buffered.seekable [clinic start generated code]*/ static PyObject * _io__Buffered_seekable_impl(buffered *self) -/*[clinic end generated code: output=90172abb5ceb6e8f input=7d35764f5fb5262b]*/ +/*[clinic end generated code: output=90172abb5ceb6e8f input=e3a4fc1d297b2fd3]*/ { CHECK_INITIALIZED(self) return PyObject_CallMethodNoArgs(self->raw, &_Py_ID(seekable)); } /*[clinic input] +@critical_section _io._Buffered.readable [clinic start generated code]*/ static PyObject * _io__Buffered_readable_impl(buffered *self) -/*[clinic end generated code: output=92afa07661ecb698 input=640619addb513b8b]*/ +/*[clinic end generated code: output=92afa07661ecb698 input=abe54107d59bca9a]*/ { CHECK_INITIALIZED(self) return PyObject_CallMethodNoArgs(self->raw, &_Py_ID(readable)); } /*[clinic input] +@critical_section _io._Buffered.writable [clinic start generated code]*/ static PyObject * _io__Buffered_writable_impl(buffered *self) -/*[clinic end generated code: output=4e3eee8d6f9d8552 input=b35ea396b2201554]*/ +/*[clinic end generated code: output=4e3eee8d6f9d8552 input=45eb76bf6a10e6f7]*/ { CHECK_INITIALIZED(self) return PyObject_CallMethodNoArgs(self->raw, &_Py_ID(writable)); } + +/*[clinic input] +@critical_section +@getter +_io._Buffered.name +[clinic start generated code]*/ + static PyObject * -buffered_name_get(buffered *self, void *context) +_io__Buffered_name_get_impl(buffered *self) +/*[clinic end generated code: output=d2adf384051d3d10 input=6b84a0e6126f545e]*/ { CHECK_INITIALIZED(self) return PyObject_GetAttr(self->raw, &_Py_ID(name)); } +/*[clinic input] +@critical_section +@getter +_io._Buffered.mode +[clinic start generated code]*/ + static PyObject * -buffered_mode_get(buffered *self, void *context) +_io__Buffered_mode_get_impl(buffered *self) +/*[clinic end generated code: output=0feb205748892fa4 input=0762d5e28542fd8c]*/ { CHECK_INITIALIZED(self) return PyObject_GetAttr(self->raw, &_Py_ID(mode)); @@ -659,24 +688,26 @@ buffered_mode_get(buffered *self, void *context) /* Lower-level APIs */ /*[clinic input] +@critical_section _io._Buffered.fileno [clinic start generated code]*/ static PyObject * _io__Buffered_fileno_impl(buffered *self) -/*[clinic end generated code: output=b717648d58a95ee3 input=768ea30b3f6314a7]*/ +/*[clinic end generated code: output=b717648d58a95ee3 input=1c4fead777bae20a]*/ { CHECK_INITIALIZED(self) return PyObject_CallMethodNoArgs(self->raw, &_Py_ID(fileno)); } /*[clinic input] +@critical_section _io._Buffered.isatty [clinic start generated code]*/ static PyObject * _io__Buffered_isatty_impl(buffered *self) -/*[clinic end generated code: output=c20e55caae67baea input=9ea007b11559bee4]*/ +/*[clinic end generated code: output=c20e55caae67baea input=e53d182d7e490e3a]*/ { CHECK_INITIALIZED(self) return PyObject_CallMethodNoArgs(self->raw, &_Py_ID(isatty)); @@ -882,12 +913,13 @@ buffered_flush_and_rewind_unlocked(buffered *self) } /*[clinic input] +@critical_section _io._Buffered.flush [clinic start generated code]*/ static PyObject * _io__Buffered_flush_impl(buffered *self) -/*[clinic end generated code: output=da2674ef1ce71f3a input=fda63444697c6bf4]*/ +/*[clinic end generated code: output=da2674ef1ce71f3a input=6b30de9f083419c2]*/ { PyObject *res; @@ -903,6 +935,7 @@ _io__Buffered_flush_impl(buffered *self) } /*[clinic input] +@critical_section _io._Buffered.peek size: Py_ssize_t = 0 / @@ -911,7 +944,7 @@ _io._Buffered.peek static PyObject * _io__Buffered_peek_impl(buffered *self, Py_ssize_t size) -/*[clinic end generated code: output=ba7a097ca230102b input=37ffb97d06ff4adb]*/ +/*[clinic end generated code: output=ba7a097ca230102b input=56733376f926d982]*/ { PyObject *res = NULL; @@ -935,6 +968,7 @@ _io__Buffered_peek_impl(buffered *self, Py_ssize_t size) } /*[clinic input] +@critical_section _io._Buffered.read size as n: Py_ssize_t(accept={int, NoneType}) = -1 / @@ -942,7 +976,7 @@ _io._Buffered.read static PyObject * _io__Buffered_read_impl(buffered *self, Py_ssize_t n) -/*[clinic end generated code: output=f41c78bb15b9bbe9 input=7df81e82e08a68a2]*/ +/*[clinic end generated code: output=f41c78bb15b9bbe9 input=bdb4b0425b295472]*/ { PyObject *res; @@ -976,6 +1010,7 @@ _io__Buffered_read_impl(buffered *self, Py_ssize_t n) } /*[clinic input] +@critical_section _io._Buffered.read1 size as n: Py_ssize_t = -1 / @@ -983,7 +1018,7 @@ _io._Buffered.read1 static PyObject * _io__Buffered_read1_impl(buffered *self, Py_ssize_t n) -/*[clinic end generated code: output=bcc4fb4e54d103a3 input=7d22de9630b61774]*/ +/*[clinic end generated code: output=bcc4fb4e54d103a3 input=3d0ad241aa52b36c]*/ { Py_ssize_t have, r; PyObject *res = NULL; @@ -1112,6 +1147,7 @@ _buffered_readinto_generic(buffered *self, Py_buffer *buffer, char readinto1) } /*[clinic input] +@critical_section _io._Buffered.readinto buffer: Py_buffer(accept={rwbuffer}) / @@ -1119,12 +1155,13 @@ _io._Buffered.readinto static PyObject * _io__Buffered_readinto_impl(buffered *self, Py_buffer *buffer) -/*[clinic end generated code: output=bcb376580b1d8170 input=ed6b98b7a20a3008]*/ +/*[clinic end generated code: output=bcb376580b1d8170 input=777c33e7adaa2bcd]*/ { return _buffered_readinto_generic(self, buffer, 0); } /*[clinic input] +@critical_section _io._Buffered.readinto1 buffer: Py_buffer(accept={rwbuffer}) / @@ -1132,7 +1169,7 @@ _io._Buffered.readinto1 static PyObject * _io__Buffered_readinto1_impl(buffered *self, Py_buffer *buffer) -/*[clinic end generated code: output=6e5c6ac5868205d6 input=4455c5d55fdf1687]*/ +/*[clinic end generated code: output=6e5c6ac5868205d6 input=ef03cc5fc92a6895]*/ { return _buffered_readinto_generic(self, buffer, 1); } @@ -1247,6 +1284,7 @@ _buffered_readline(buffered *self, Py_ssize_t limit) } /*[clinic input] +@critical_section _io._Buffered.readline size: Py_ssize_t(accept={int, NoneType}) = -1 / @@ -1254,7 +1292,7 @@ _io._Buffered.readline static PyObject * _io__Buffered_readline_impl(buffered *self, Py_ssize_t size) -/*[clinic end generated code: output=24dd2aa6e33be83c input=673b6240e315ef8a]*/ +/*[clinic end generated code: output=24dd2aa6e33be83c input=e81ca5abd4280776]*/ { CHECK_INITIALIZED(self) return _buffered_readline(self, size); @@ -1262,12 +1300,13 @@ _io__Buffered_readline_impl(buffered *self, Py_ssize_t size) /*[clinic input] +@critical_section _io._Buffered.tell [clinic start generated code]*/ static PyObject * _io__Buffered_tell_impl(buffered *self) -/*[clinic end generated code: output=386972ae84716c1e input=ad61e04a6b349573]*/ +/*[clinic end generated code: output=386972ae84716c1e input=ab12e67d8abcb42f]*/ { Py_off_t pos; @@ -1281,6 +1320,7 @@ _io__Buffered_tell_impl(buffered *self) } /*[clinic input] +@critical_section _io._Buffered.seek target as targetobj: object whence: int = 0 @@ -1289,7 +1329,7 @@ _io._Buffered.seek static PyObject * _io__Buffered_seek_impl(buffered *self, PyObject *targetobj, int whence) -/*[clinic end generated code: output=7ae0e8dc46efdefb input=a9c4920bfcba6163]*/ +/*[clinic end generated code: output=7ae0e8dc46efdefb input=b5a12be70e0ad07b]*/ { Py_off_t target, n; PyObject *res = NULL; @@ -1378,6 +1418,7 @@ _io__Buffered_seek_impl(buffered *self, PyObject *targetobj, int whence) } /*[clinic input] +@critical_section _io._Buffered.truncate cls: defining_class pos: object = None @@ -1386,7 +1427,7 @@ _io._Buffered.truncate static PyObject * _io__Buffered_truncate_impl(buffered *self, PyTypeObject *cls, PyObject *pos) -/*[clinic end generated code: output=fe3882fbffe79f1a input=f5b737d97d76303f]*/ +/*[clinic end generated code: output=fe3882fbffe79f1a input=e3cbf794575bd794]*/ { PyObject *res = NULL; @@ -2000,6 +2041,7 @@ _bufferedwriter_flush_unlocked(buffered *self) } /*[clinic input] +@critical_section _io.BufferedWriter.write buffer: Py_buffer / @@ -2007,7 +2049,7 @@ _io.BufferedWriter.write static PyObject * _io_BufferedWriter_write_impl(buffered *self, Py_buffer *buffer) -/*[clinic end generated code: output=7f8d1365759bfc6b input=dd87dd85fc7f8850]*/ +/*[clinic end generated code: output=7f8d1365759bfc6b input=6a9c041de0c337be]*/ { PyObject *res = NULL; Py_ssize_t written, avail, remaining; @@ -2484,9 +2526,9 @@ static PyMemberDef bufferedreader_members[] = { }; static PyGetSetDef bufferedreader_getset[] = { - {"closed", (getter)buffered_closed_get, NULL, NULL}, - {"name", (getter)buffered_name_get, NULL, NULL}, - {"mode", (getter)buffered_mode_get, NULL, NULL}, + _IO__BUFFERED_CLOSED_GETTERDEF + _IO__BUFFERED_NAME_GETTERDEF + _IO__BUFFERED_MODE_GETTERDEF {NULL} }; @@ -2544,9 +2586,9 @@ static PyMemberDef bufferedwriter_members[] = { }; static PyGetSetDef bufferedwriter_getset[] = { - {"closed", (getter)buffered_closed_get, NULL, NULL}, - {"name", (getter)buffered_name_get, NULL, NULL}, - {"mode", (getter)buffered_mode_get, NULL, NULL}, + _IO__BUFFERED_CLOSED_GETTERDEF + _IO__BUFFERED_NAME_GETTERDEF + _IO__BUFFERED_MODE_GETTERDEF {NULL} }; @@ -2662,9 +2704,9 @@ static PyMemberDef bufferedrandom_members[] = { }; static PyGetSetDef bufferedrandom_getset[] = { - {"closed", (getter)buffered_closed_get, NULL, NULL}, - {"name", (getter)buffered_name_get, NULL, NULL}, - {"mode", (getter)buffered_mode_get, NULL, NULL}, + _IO__BUFFERED_CLOSED_GETTERDEF + _IO__BUFFERED_NAME_GETTERDEF + _IO__BUFFERED_MODE_GETTERDEF {NULL} }; diff --git a/Modules/_io/clinic/bufferedio.c.h b/Modules/_io/clinic/bufferedio.c.h index 3fe03506b6cfa5..69d28ad00c2ad5 100644 --- a/Modules/_io/clinic/bufferedio.c.h +++ b/Modules/_io/clinic/bufferedio.c.h @@ -7,6 +7,7 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_BadArgument() PyDoc_STRVAR(_io__BufferedIOBase_readinto__doc__, @@ -30,7 +31,9 @@ _io__BufferedIOBase_readinto(PyObject *self, PyObject *arg) _PyArg_BadArgument("readinto", "argument", "read-write bytes-like object", arg); goto exit; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__BufferedIOBase_readinto_impl(self, &buffer); + Py_END_CRITICAL_SECTION(); exit: /* Cleanup for buffer */ @@ -62,7 +65,9 @@ _io__BufferedIOBase_readinto1(PyObject *self, PyObject *arg) _PyArg_BadArgument("readinto1", "argument", "read-write bytes-like object", arg); goto exit; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__BufferedIOBase_readinto1_impl(self, &buffer); + Py_END_CRITICAL_SECTION(); exit: /* Cleanup for buffer */ @@ -282,7 +287,13 @@ _io__Buffered___sizeof___impl(buffered *self); static PyObject * _io__Buffered___sizeof__(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered___sizeof___impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered___sizeof___impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered__dealloc_warn__doc__, @@ -307,7 +318,31 @@ _io__Buffered_simple_flush_impl(buffered *self); static PyObject * _io__Buffered_simple_flush(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_simple_flush_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_simple_flush_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#define _IO__BUFFERED_CLOSED_GETTERDEF \ + {"closed", (getter)_io__Buffered_closed_get, NULL, NULL}, + +static PyObject * +_io__Buffered_closed_get_impl(buffered *self); + +static PyObject * +_io__Buffered_closed_get(buffered *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_closed_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_close__doc__, @@ -347,7 +382,13 @@ _io__Buffered_detach_impl(buffered *self); static PyObject * _io__Buffered_detach(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_detach_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_detach_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_seekable__doc__, @@ -364,7 +405,13 @@ _io__Buffered_seekable_impl(buffered *self); static PyObject * _io__Buffered_seekable(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_seekable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_seekable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_readable__doc__, @@ -381,7 +428,13 @@ _io__Buffered_readable_impl(buffered *self); static PyObject * _io__Buffered_readable(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_readable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_readable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_writable__doc__, @@ -398,7 +451,49 @@ _io__Buffered_writable_impl(buffered *self); static PyObject * _io__Buffered_writable(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_writable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_writable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#define _IO__BUFFERED_NAME_GETTERDEF \ + {"name", (getter)_io__Buffered_name_get, NULL, NULL}, + +static PyObject * +_io__Buffered_name_get_impl(buffered *self); + +static PyObject * +_io__Buffered_name_get(buffered *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_name_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#define _IO__BUFFERED_MODE_GETTERDEF \ + {"mode", (getter)_io__Buffered_mode_get, NULL, NULL}, + +static PyObject * +_io__Buffered_mode_get_impl(buffered *self); + +static PyObject * +_io__Buffered_mode_get(buffered *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_mode_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_fileno__doc__, @@ -415,7 +510,13 @@ _io__Buffered_fileno_impl(buffered *self); static PyObject * _io__Buffered_fileno(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_fileno_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_fileno_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_isatty__doc__, @@ -432,7 +533,13 @@ _io__Buffered_isatty_impl(buffered *self); static PyObject * _io__Buffered_isatty(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_isatty_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_isatty_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_flush__doc__, @@ -449,7 +556,13 @@ _io__Buffered_flush_impl(buffered *self); static PyObject * _io__Buffered_flush(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_flush_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_flush_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_peek__doc__, @@ -488,7 +601,9 @@ _io__Buffered_peek(buffered *self, PyObject *const *args, Py_ssize_t nargs) size = ival; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_peek_impl(self, size); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -521,7 +636,9 @@ _io__Buffered_read(buffered *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_read_impl(self, n); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -563,7 +680,9 @@ _io__Buffered_read1(buffered *self, PyObject *const *args, Py_ssize_t nargs) n = ival; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_read1_impl(self, n); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -590,7 +709,9 @@ _io__Buffered_readinto(buffered *self, PyObject *arg) _PyArg_BadArgument("readinto", "argument", "read-write bytes-like object", arg); goto exit; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_readinto_impl(self, &buffer); + Py_END_CRITICAL_SECTION(); exit: /* Cleanup for buffer */ @@ -622,7 +743,9 @@ _io__Buffered_readinto1(buffered *self, PyObject *arg) _PyArg_BadArgument("readinto1", "argument", "read-write bytes-like object", arg); goto exit; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_readinto1_impl(self, &buffer); + Py_END_CRITICAL_SECTION(); exit: /* Cleanup for buffer */ @@ -660,7 +783,9 @@ _io__Buffered_readline(buffered *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_readline_impl(self, size); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -680,7 +805,13 @@ _io__Buffered_tell_impl(buffered *self); static PyObject * _io__Buffered_tell(buffered *self, PyObject *Py_UNUSED(ignored)) { - return _io__Buffered_tell_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io__Buffered_tell_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io__Buffered_seek__doc__, @@ -713,7 +844,9 @@ _io__Buffered_seek(buffered *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_seek_impl(self, targetobj, whence); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -759,7 +892,9 @@ _io__Buffered_truncate(buffered *self, PyTypeObject *cls, PyObject *const *args, } pos = args[0]; skip_optional_posonly: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io__Buffered_truncate_impl(self, cls, pos); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -935,7 +1070,9 @@ _io_BufferedWriter_write(buffered *self, PyObject *arg) if (PyObject_GetBuffer(arg, &buffer, PyBUF_SIMPLE) != 0) { goto exit; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_BufferedWriter_write_impl(self, &buffer); + Py_END_CRITICAL_SECTION(); exit: /* Cleanup for buffer */ @@ -1081,4 +1218,4 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=b23847480eba3d9b input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f21ed03255032b43 input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/stringio.c.h b/Modules/_io/clinic/stringio.c.h index 571ec5117147d5..ed505ae67589a8 100644 --- a/Modules/_io/clinic/stringio.c.h +++ b/Modules/_io/clinic/stringio.c.h @@ -7,6 +7,7 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_abstract.h" // _Py_convert_optional_to_ssize_t() +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_CheckPositional() PyDoc_STRVAR(_io_StringIO_getvalue__doc__, @@ -24,7 +25,13 @@ _io_StringIO_getvalue_impl(stringio *self); static PyObject * _io_StringIO_getvalue(stringio *self, PyObject *Py_UNUSED(ignored)) { - return _io_StringIO_getvalue_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_getvalue_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_StringIO_tell__doc__, @@ -42,7 +49,13 @@ _io_StringIO_tell_impl(stringio *self); static PyObject * _io_StringIO_tell(stringio *self, PyObject *Py_UNUSED(ignored)) { - return _io_StringIO_tell_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_tell_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_StringIO_read__doc__, @@ -76,7 +89,9 @@ _io_StringIO_read(stringio *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_StringIO_read_impl(self, size); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -112,7 +127,9 @@ _io_StringIO_readline(stringio *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_StringIO_readline_impl(self, size); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -150,7 +167,9 @@ _io_StringIO_truncate(stringio *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_StringIO_truncate_impl(self, size); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -204,7 +223,9 @@ _io_StringIO_seek(stringio *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_StringIO_seek_impl(self, pos, whence); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -222,6 +243,21 @@ PyDoc_STRVAR(_io_StringIO_write__doc__, #define _IO_STRINGIO_WRITE_METHODDEF \ {"write", (PyCFunction)_io_StringIO_write, METH_O, _io_StringIO_write__doc__}, +static PyObject * +_io_StringIO_write_impl(stringio *self, PyObject *obj); + +static PyObject * +_io_StringIO_write(stringio *self, PyObject *obj) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_write_impl(self, obj); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(_io_StringIO_close__doc__, "close($self, /)\n" "--\n" @@ -242,7 +278,13 @@ _io_StringIO_close_impl(stringio *self); static PyObject * _io_StringIO_close(stringio *self, PyObject *Py_UNUSED(ignored)) { - return _io_StringIO_close_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_close_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_StringIO___init____doc__, @@ -330,7 +372,13 @@ _io_StringIO_readable_impl(stringio *self); static PyObject * _io_StringIO_readable(stringio *self, PyObject *Py_UNUSED(ignored)) { - return _io_StringIO_readable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_readable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_StringIO_writable__doc__, @@ -348,7 +396,13 @@ _io_StringIO_writable_impl(stringio *self); static PyObject * _io_StringIO_writable(stringio *self, PyObject *Py_UNUSED(ignored)) { - return _io_StringIO_writable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_writable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_StringIO_seekable__doc__, @@ -366,6 +420,112 @@ _io_StringIO_seekable_impl(stringio *self); static PyObject * _io_StringIO_seekable(stringio *self, PyObject *Py_UNUSED(ignored)) { - return _io_StringIO_seekable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_seekable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_io_StringIO___getstate____doc__, +"__getstate__($self, /)\n" +"--\n" +"\n"); + +#define _IO_STRINGIO___GETSTATE___METHODDEF \ + {"__getstate__", (PyCFunction)_io_StringIO___getstate__, METH_NOARGS, _io_StringIO___getstate____doc__}, + +static PyObject * +_io_StringIO___getstate___impl(stringio *self); + +static PyObject * +_io_StringIO___getstate__(stringio *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO___getstate___impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_io_StringIO___setstate____doc__, +"__setstate__($self, state, /)\n" +"--\n" +"\n"); + +#define _IO_STRINGIO___SETSTATE___METHODDEF \ + {"__setstate__", (PyCFunction)_io_StringIO___setstate__, METH_O, _io_StringIO___setstate____doc__}, + +static PyObject * +_io_StringIO___setstate___impl(stringio *self, PyObject *state); + +static PyObject * +_io_StringIO___setstate__(stringio *self, PyObject *state) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO___setstate___impl(self, state); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#define _IO_STRINGIO_CLOSED_GETTERDEF \ + {"closed", (getter)_io_StringIO_closed_get, NULL, NULL}, + +static PyObject * +_io_StringIO_closed_get_impl(stringio *self); + +static PyObject * +_io_StringIO_closed_get(stringio *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_closed_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#define _IO_STRINGIO_LINE_BUFFERING_GETTERDEF \ + {"line_buffering", (getter)_io_StringIO_line_buffering_get, NULL, NULL}, + +static PyObject * +_io_StringIO_line_buffering_get_impl(stringio *self); + +static PyObject * +_io_StringIO_line_buffering_get(stringio *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_line_buffering_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#define _IO_STRINGIO_NEWLINES_GETTERDEF \ + {"newlines", (getter)_io_StringIO_newlines_get, NULL, NULL}, + +static PyObject * +_io_StringIO_newlines_get_impl(stringio *self); + +static PyObject * +_io_StringIO_newlines_get(stringio *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_StringIO_newlines_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } -/*[clinic end generated code: output=f56aa7f8a271acf6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3a92e8b6c322f61b input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/textio.c.h b/Modules/_io/clinic/textio.c.h index b24a1669f9b344..675e0ed2eab75e 100644 --- a/Modules/_io/clinic/textio.c.h +++ b/Modules/_io/clinic/textio.c.h @@ -7,6 +7,7 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_abstract.h" // _Py_convert_optional_to_ssize_t() +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(_io__TextIOBase_detach__doc__, @@ -630,7 +631,9 @@ _io_TextIOWrapper_reconfigure(textio *self, PyObject *const *args, Py_ssize_t na } write_through_obj = args[4]; skip_optional_kwonly: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_TextIOWrapper_reconfigure_impl(self, encoding, errors, newline_obj, line_buffering_obj, write_through_obj); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -650,7 +653,13 @@ _io_TextIOWrapper_detach_impl(textio *self); static PyObject * _io_TextIOWrapper_detach(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_detach_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_detach_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_write__doc__, @@ -675,7 +684,9 @@ _io_TextIOWrapper_write(textio *self, PyObject *arg) goto exit; } text = arg; + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_TextIOWrapper_write_impl(self, text); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -708,7 +719,9 @@ _io_TextIOWrapper_read(textio *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_TextIOWrapper_read_impl(self, n); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -750,7 +763,9 @@ _io_TextIOWrapper_readline(textio *self, PyObject *const *args, Py_ssize_t nargs size = ival; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_TextIOWrapper_readline_impl(self, size); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -804,7 +819,9 @@ _io_TextIOWrapper_seek(textio *self, PyObject *const *args, Py_ssize_t nargs) goto exit; } skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_TextIOWrapper_seek_impl(self, cookieObj, whence); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -828,7 +845,13 @@ _io_TextIOWrapper_tell_impl(textio *self); static PyObject * _io_TextIOWrapper_tell(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_tell_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_tell_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_truncate__doc__, @@ -856,7 +879,9 @@ _io_TextIOWrapper_truncate(textio *self, PyObject *const *args, Py_ssize_t nargs } pos = args[0]; skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_TextIOWrapper_truncate_impl(self, pos); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -876,7 +901,13 @@ _io_TextIOWrapper_fileno_impl(textio *self); static PyObject * _io_TextIOWrapper_fileno(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_fileno_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_fileno_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_seekable__doc__, @@ -893,7 +924,13 @@ _io_TextIOWrapper_seekable_impl(textio *self); static PyObject * _io_TextIOWrapper_seekable(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_seekable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_seekable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_readable__doc__, @@ -910,7 +947,13 @@ _io_TextIOWrapper_readable_impl(textio *self); static PyObject * _io_TextIOWrapper_readable(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_readable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_readable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_writable__doc__, @@ -927,7 +970,13 @@ _io_TextIOWrapper_writable_impl(textio *self); static PyObject * _io_TextIOWrapper_writable(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_writable_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_writable_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_isatty__doc__, @@ -944,7 +993,13 @@ _io_TextIOWrapper_isatty_impl(textio *self); static PyObject * _io_TextIOWrapper_isatty(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_isatty_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_isatty_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_flush__doc__, @@ -961,7 +1016,13 @@ _io_TextIOWrapper_flush_impl(textio *self); static PyObject * _io_TextIOWrapper_flush(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_flush_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_flush_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper_close__doc__, @@ -978,6 +1039,12 @@ _io_TextIOWrapper_close_impl(textio *self); static PyObject * _io_TextIOWrapper_close(textio *self, PyObject *Py_UNUSED(ignored)) { - return _io_TextIOWrapper_close_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_TextIOWrapper_close_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } -/*[clinic end generated code: output=e58ce89b7354e77a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8781a91be6d99e2c input=a9049054013a1b77]*/ diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index 1856b07108bab6..74dcee23730306 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -45,6 +45,10 @@ typedef struct { _PyIO_State *module_state; } stringio; +#define clinic_state() (find_io_state_by_def(Py_TYPE(self))) +#include "clinic/stringio.c.h" +#undef clinic_state + static int _io_StringIO___init__(PyObject *self, PyObject *args, PyObject *kwargs); #define CHECK_INITIALIZED(self) \ @@ -263,6 +267,7 @@ write_str(stringio *self, PyObject *obj) } /*[clinic input] +@critical_section _io.StringIO.getvalue Retrieve the entire contents of the object. @@ -270,7 +275,7 @@ Retrieve the entire contents of the object. static PyObject * _io_StringIO_getvalue_impl(stringio *self) -/*[clinic end generated code: output=27b6a7bfeaebce01 input=d23cb81d6791cf88]*/ +/*[clinic end generated code: output=27b6a7bfeaebce01 input=fb5dee06b8d467f3]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -281,6 +286,7 @@ _io_StringIO_getvalue_impl(stringio *self) } /*[clinic input] +@critical_section _io.StringIO.tell Tell the current file position. @@ -288,7 +294,7 @@ Tell the current file position. static PyObject * _io_StringIO_tell_impl(stringio *self) -/*[clinic end generated code: output=2e87ac67b116c77b input=ec866ebaff02f405]*/ +/*[clinic end generated code: output=2e87ac67b116c77b input=98a08f3e2dae3550]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -296,6 +302,7 @@ _io_StringIO_tell_impl(stringio *self) } /*[clinic input] +@critical_section _io.StringIO.read size: Py_ssize_t(accept={int, NoneType}) = -1 / @@ -308,7 +315,7 @@ is reached. Return an empty string at EOF. static PyObject * _io_StringIO_read_impl(stringio *self, Py_ssize_t size) -/*[clinic end generated code: output=ae8cf6002f71626c input=0921093383dfb92d]*/ +/*[clinic end generated code: output=ae8cf6002f71626c input=9fbef45d8aece8e7]*/ { Py_ssize_t n; Py_UCS4 *output; @@ -368,6 +375,7 @@ _stringio_readline(stringio *self, Py_ssize_t limit) } /*[clinic input] +@critical_section _io.StringIO.readline size: Py_ssize_t(accept={int, NoneType}) = -1 / @@ -379,7 +387,7 @@ Returns an empty string if EOF is hit immediately. static PyObject * _io_StringIO_readline_impl(stringio *self, Py_ssize_t size) -/*[clinic end generated code: output=cabd6452f1b7e85d input=a5bd70bf682aa276]*/ +/*[clinic end generated code: output=cabd6452f1b7e85d input=4d14b8495dea1d98]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -427,6 +435,7 @@ stringio_iternext(stringio *self) } /*[clinic input] +@critical_section _io.StringIO.truncate pos as size: Py_ssize_t(accept={int, NoneType}, c_default="self->pos") = None / @@ -440,7 +449,7 @@ Returns the new absolute position. static PyObject * _io_StringIO_truncate_impl(stringio *self, Py_ssize_t size) -/*[clinic end generated code: output=eb3aef8e06701365 input=5505cff90ca48b96]*/ +/*[clinic end generated code: output=eb3aef8e06701365 input=461b872dce238452]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -462,6 +471,7 @@ _io_StringIO_truncate_impl(stringio *self, Py_ssize_t size) } /*[clinic input] +@critical_section _io.StringIO.seek pos: Py_ssize_t whence: int = 0 @@ -478,7 +488,7 @@ Returns the new absolute position. static PyObject * _io_StringIO_seek_impl(stringio *self, Py_ssize_t pos, int whence) -/*[clinic end generated code: output=e9e0ac9a8ae71c25 input=e3855b24e7cae06a]*/ +/*[clinic end generated code: output=e9e0ac9a8ae71c25 input=c75ced09343a00d7]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -515,6 +525,7 @@ _io_StringIO_seek_impl(stringio *self, Py_ssize_t pos, int whence) } /*[clinic input] +@critical_section _io.StringIO.write s as obj: object / @@ -526,8 +537,8 @@ the length of the string. [clinic start generated code]*/ static PyObject * -_io_StringIO_write(stringio *self, PyObject *obj) -/*[clinic end generated code: output=0deaba91a15b94da input=cf96f3b16586e669]*/ +_io_StringIO_write_impl(stringio *self, PyObject *obj) +/*[clinic end generated code: output=d53b1d841d7db288 input=1561272c0da4651f]*/ { Py_ssize_t size; @@ -547,6 +558,7 @@ _io_StringIO_write(stringio *self, PyObject *obj) } /*[clinic input] +@critical_section _io.StringIO.close Close the IO object. @@ -559,7 +571,7 @@ This method has no effect if the file is already closed. static PyObject * _io_StringIO_close_impl(stringio *self) -/*[clinic end generated code: output=04399355cbe518f1 input=cbc10b45f35d6d46]*/ +/*[clinic end generated code: output=04399355cbe518f1 input=305d19aa29cc40b9]*/ { self->closed = 1; /* Free up some memory */ @@ -756,6 +768,7 @@ _io_StringIO___init___impl(stringio *self, PyObject *value, /* Properties and pseudo-properties */ /*[clinic input] +@critical_section _io.StringIO.readable Returns True if the IO object can be read. @@ -763,7 +776,7 @@ Returns True if the IO object can be read. static PyObject * _io_StringIO_readable_impl(stringio *self) -/*[clinic end generated code: output=b19d44dd8b1ceb99 input=39ce068b224c21ad]*/ +/*[clinic end generated code: output=b19d44dd8b1ceb99 input=6cd2ffd65a8e8763]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -771,6 +784,7 @@ _io_StringIO_readable_impl(stringio *self) } /*[clinic input] +@critical_section _io.StringIO.writable Returns True if the IO object can be written. @@ -778,7 +792,7 @@ Returns True if the IO object can be written. static PyObject * _io_StringIO_writable_impl(stringio *self) -/*[clinic end generated code: output=13e4dd77187074ca input=7a691353aac38835]*/ +/*[clinic end generated code: output=13e4dd77187074ca input=1b3c63dbaa761c69]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -786,6 +800,7 @@ _io_StringIO_writable_impl(stringio *self) } /*[clinic input] +@critical_section _io.StringIO.seekable Returns True if the IO object can be seeked. @@ -793,7 +808,7 @@ Returns True if the IO object can be seeked. static PyObject * _io_StringIO_seekable_impl(stringio *self) -/*[clinic end generated code: output=4d20b4641c756879 input=4c606d05b32952e6]*/ +/*[clinic end generated code: output=4d20b4641c756879 input=a820fad2cf085fc3]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -812,8 +827,15 @@ _io_StringIO_seekable_impl(stringio *self) supported. */ +/*[clinic input] +@critical_section +_io.StringIO.__getstate__ + +[clinic start generated code]*/ + static PyObject * -stringio_getstate(stringio *self, PyObject *Py_UNUSED(ignored)) +_io_StringIO___getstate___impl(stringio *self) +/*[clinic end generated code: output=780be4a996410199 input=76f27255ef83bb92]*/ { PyObject *initvalue = _io_StringIO_getvalue_impl(self); PyObject *dict; @@ -839,8 +861,17 @@ stringio_getstate(stringio *self, PyObject *Py_UNUSED(ignored)) return state; } +/*[clinic input] +@critical_section +_io.StringIO.__setstate__ + + state: object + / +[clinic start generated code]*/ + static PyObject * -stringio_setstate(stringio *self, PyObject *state) +_io_StringIO___setstate___impl(stringio *self, PyObject *state) +/*[clinic end generated code: output=cb3962bc6d5c5609 input=8a27784b11b82e47]*/ { PyObject *initarg; PyObject *position_obj; @@ -939,36 +970,53 @@ stringio_setstate(stringio *self, PyObject *state) Py_RETURN_NONE; } +/*[clinic input] +@critical_section +@getter +_io.StringIO.closed +[clinic start generated code]*/ static PyObject * -stringio_closed(stringio *self, void *context) +_io_StringIO_closed_get_impl(stringio *self) +/*[clinic end generated code: output=531ddca7954331d6 input=178d2ef24395fd49]*/ { CHECK_INITIALIZED(self); return PyBool_FromLong(self->closed); } +/*[clinic input] +@critical_section +@getter +_io.StringIO.line_buffering +[clinic start generated code]*/ + static PyObject * -stringio_line_buffering(stringio *self, void *context) +_io_StringIO_line_buffering_get_impl(stringio *self) +/*[clinic end generated code: output=360710e0112966ae input=6a7634e7f890745e]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); Py_RETURN_FALSE; } +/*[clinic input] +@critical_section +@getter +_io.StringIO.newlines +[clinic start generated code]*/ + static PyObject * -stringio_newlines(stringio *self, void *context) +_io_StringIO_newlines_get_impl(stringio *self) +/*[clinic end generated code: output=35d7c0b66d7e0160 input=092a14586718244b]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); - if (self->decoder == NULL) + if (self->decoder == NULL) { Py_RETURN_NONE; + } return PyObject_GetAttr(self->decoder, &_Py_ID(newlines)); } -#define clinic_state() (find_io_state_by_def(Py_TYPE(self))) -#include "clinic/stringio.c.h" -#undef clinic_state - static struct PyMethodDef stringio_methods[] = { _IO_STRINGIO_CLOSE_METHODDEF _IO_STRINGIO_GETVALUE_METHODDEF @@ -983,21 +1031,21 @@ static struct PyMethodDef stringio_methods[] = { _IO_STRINGIO_READABLE_METHODDEF _IO_STRINGIO_WRITABLE_METHODDEF - {"__getstate__", (PyCFunction)stringio_getstate, METH_NOARGS}, - {"__setstate__", (PyCFunction)stringio_setstate, METH_O}, + _IO_STRINGIO___GETSTATE___METHODDEF + _IO_STRINGIO___SETSTATE___METHODDEF {NULL, NULL} /* sentinel */ }; static PyGetSetDef stringio_getset[] = { - {"closed", (getter)stringio_closed, NULL, NULL}, - {"newlines", (getter)stringio_newlines, NULL, NULL}, + _IO_STRINGIO_CLOSED_GETTERDEF + _IO_STRINGIO_NEWLINES_GETTERDEF /* (following comments straight off of the original Python wrapper:) XXX Cruft to support the TextIOWrapper API. This would only be meaningful if StringIO supported the buffer attribute. Hopefully, a better solution, than adding these pseudo-attributes, will be found. */ - {"line_buffering", (getter)stringio_line_buffering, NULL, NULL}, + _IO_STRINGIO_LINE_BUFFERING_GETTERDEF {NULL} }; diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 8d19198502fe12..545f467b7f0257 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -7,14 +7,14 @@ */ #include "Python.h" -#include "pycore_call.h" // _PyObject_CallMethod() -#include "pycore_codecs.h" // _PyCodecInfo_GetIncrementalDecoder() -#include "pycore_fileutils.h" // _Py_GetLocaleEncoding() -#include "pycore_interp.h" // PyInterpreterState.fs_codec -#include "pycore_long.h" // _PyLong_GetZero() -#include "pycore_object.h" // _PyObject_GC_UNTRACK() -#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() -#include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_call.h" // _PyObject_CallMethod() +#include "pycore_codecs.h" // _PyCodecInfo_GetIncrementalDecoder() +#include "pycore_fileutils.h" // _Py_GetLocaleEncoding() +#include "pycore_interp.h" // PyInterpreterState.fs_codec +#include "pycore_long.h" // _PyLong_GetZero() +#include "pycore_object.h" // _PyObject_GC_UNTRACK() +#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() +#include "pycore_pystate.h" // _PyInterpreterState_GET() #include "_iomodule.h" @@ -1327,6 +1327,7 @@ textiowrapper_change_encoding(textio *self, PyObject *encoding, } /*[clinic input] +@critical_section _io.TextIOWrapper.reconfigure * encoding: object = None @@ -1346,7 +1347,7 @@ _io_TextIOWrapper_reconfigure_impl(textio *self, PyObject *encoding, PyObject *errors, PyObject *newline_obj, PyObject *line_buffering_obj, PyObject *write_through_obj) -/*[clinic end generated code: output=52b812ff4b3d4b0f input=671e82136e0f5822]*/ +/*[clinic end generated code: output=52b812ff4b3d4b0f input=dc3bd35ebda702a7]*/ { int line_buffering; int write_through; @@ -1531,12 +1532,13 @@ textiowrapper_closed_get(textio *self, void *context); /*[clinic input] +@critical_section _io.TextIOWrapper.detach [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_detach_impl(textio *self) -/*[clinic end generated code: output=7ba3715cd032d5f2 input=e5a71fbda9e1d9f9]*/ +/*[clinic end generated code: output=7ba3715cd032d5f2 input=c908a3b4ef203b0f]*/ { PyObject *buffer; CHECK_ATTACHED(self); @@ -1622,6 +1624,7 @@ _textiowrapper_writeflush(textio *self) } /*[clinic input] +@critical_section _io.TextIOWrapper.write text: unicode / @@ -1629,7 +1632,7 @@ _io.TextIOWrapper.write static PyObject * _io_TextIOWrapper_write_impl(textio *self, PyObject *text) -/*[clinic end generated code: output=d2deb0d50771fcec input=fdf19153584a0e44]*/ +/*[clinic end generated code: output=d2deb0d50771fcec input=73ec95c5c4a3489c]*/ { PyObject *ret; PyObject *b; @@ -1933,6 +1936,7 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) } /*[clinic input] +@critical_section _io.TextIOWrapper.read size as n: Py_ssize_t(accept={int, NoneType}) = -1 / @@ -1940,7 +1944,7 @@ _io.TextIOWrapper.read static PyObject * _io_TextIOWrapper_read_impl(textio *self, Py_ssize_t n) -/*[clinic end generated code: output=7e651ce6cc6a25a6 input=123eecbfe214aeb8]*/ +/*[clinic end generated code: output=7e651ce6cc6a25a6 input=67d14c5661121377]*/ { PyObject *result = NULL, *chunks = NULL; @@ -2308,6 +2312,7 @@ _textiowrapper_readline(textio *self, Py_ssize_t limit) } /*[clinic input] +@critical_section _io.TextIOWrapper.readline size: Py_ssize_t = -1 / @@ -2315,7 +2320,7 @@ _io.TextIOWrapper.readline static PyObject * _io_TextIOWrapper_readline_impl(textio *self, Py_ssize_t size) -/*[clinic end generated code: output=344afa98804e8b25 input=56c7172483b36db6]*/ +/*[clinic end generated code: output=344afa98804e8b25 input=b65bab871dc3ddba]*/ { CHECK_ATTACHED(self); return _textiowrapper_readline(self, size); @@ -2454,6 +2459,7 @@ _textiowrapper_encoder_setstate(textio *self, cookie_type *cookie) } /*[clinic input] +@critical_section _io.TextIOWrapper.seek cookie as cookieObj: object Zero or an opaque number returned by tell(). @@ -2478,7 +2484,7 @@ and may raise exceptions. static PyObject * _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) -/*[clinic end generated code: output=0a15679764e2d04d input=0f68adcb02cf2823]*/ +/*[clinic end generated code: output=0a15679764e2d04d input=4bea78698be23d7e]*/ { PyObject *posobj; cookie_type cookie; @@ -2665,6 +2671,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) } /*[clinic input] +@critical_section _io.TextIOWrapper.tell Return the stream position as an opaque number. @@ -2675,7 +2682,7 @@ previous stream position. static PyObject * _io_TextIOWrapper_tell_impl(textio *self) -/*[clinic end generated code: output=4f168c08bf34ad5f input=0852d627d76fb520]*/ +/*[clinic end generated code: output=4f168c08bf34ad5f input=415d6b4e4f8e6e8c]*/ { PyObject *res; PyObject *posobj = NULL; @@ -2901,6 +2908,7 @@ _io_TextIOWrapper_tell_impl(textio *self) } /*[clinic input] +@critical_section _io.TextIOWrapper.truncate pos: object = None / @@ -2908,7 +2916,7 @@ _io.TextIOWrapper.truncate static PyObject * _io_TextIOWrapper_truncate_impl(textio *self, PyObject *pos) -/*[clinic end generated code: output=90ec2afb9bb7745f input=56ec8baa65aea377]*/ +/*[clinic end generated code: output=90ec2afb9bb7745f input=8bddb320834c93ee]*/ { CHECK_ATTACHED(self) @@ -2988,72 +2996,78 @@ textiowrapper_repr(textio *self) /* Inquiries */ /*[clinic input] +@critical_section _io.TextIOWrapper.fileno [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_fileno_impl(textio *self) -/*[clinic end generated code: output=21490a4c3da13e6c input=c488ca83d0069f9b]*/ +/*[clinic end generated code: output=21490a4c3da13e6c input=515e1196aceb97ab]*/ { CHECK_ATTACHED(self); return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(fileno)); } /*[clinic input] +@critical_section _io.TextIOWrapper.seekable [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_seekable_impl(textio *self) -/*[clinic end generated code: output=ab223dbbcffc0f00 input=8b005ca06e1fca13]*/ +/*[clinic end generated code: output=ab223dbbcffc0f00 input=71c4c092736c549b]*/ { CHECK_ATTACHED(self); return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(seekable)); } /*[clinic input] +@critical_section _io.TextIOWrapper.readable [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_readable_impl(textio *self) -/*[clinic end generated code: output=72ff7ba289a8a91b input=0704ea7e01b0d3eb]*/ +/*[clinic end generated code: output=72ff7ba289a8a91b input=80438d1f01b0a89b]*/ { CHECK_ATTACHED(self); return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(readable)); } /*[clinic input] +@critical_section _io.TextIOWrapper.writable [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_writable_impl(textio *self) -/*[clinic end generated code: output=a728c71790d03200 input=c41740bc9d8636e8]*/ +/*[clinic end generated code: output=a728c71790d03200 input=9d6c22befb0c340a]*/ { CHECK_ATTACHED(self); return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(writable)); } /*[clinic input] +@critical_section _io.TextIOWrapper.isatty [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_isatty_impl(textio *self) -/*[clinic end generated code: output=12be1a35bace882e input=fb68d9f2c99bbfff]*/ +/*[clinic end generated code: output=12be1a35bace882e input=7f83ff04d4d1733d]*/ { CHECK_ATTACHED(self); return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(isatty)); } /*[clinic input] +@critical_section _io.TextIOWrapper.flush [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_flush_impl(textio *self) -/*[clinic end generated code: output=59de9165f9c2e4d2 input=928c60590694ab85]*/ +/*[clinic end generated code: output=59de9165f9c2e4d2 input=3ac3bf521bfed59d]*/ { CHECK_ATTACHED(self); CHECK_CLOSED(self); @@ -3064,12 +3078,13 @@ _io_TextIOWrapper_flush_impl(textio *self) } /*[clinic input] +@critical_section _io.TextIOWrapper.close [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_close_impl(textio *self) -/*[clinic end generated code: output=056ccf8b4876e4f4 input=9c2114315eae1948]*/ +/*[clinic end generated code: output=056ccf8b4876e4f4 input=8e12d7079d5ac5c1]*/ { PyObject *res; int r; @@ -3150,21 +3165,41 @@ textiowrapper_iternext(textio *self) } static PyObject * -textiowrapper_name_get(textio *self, void *context) +textiowrapper_name_get_impl(textio *self, void *context) { CHECK_ATTACHED(self); return PyObject_GetAttr(self->buffer, &_Py_ID(name)); } static PyObject * -textiowrapper_closed_get(textio *self, void *context) +textiowrapper_name_get(textio *self, void *context) +{ + PyObject *result = NULL; + Py_BEGIN_CRITICAL_SECTION(self); + result = textiowrapper_name_get_impl(self, context); + Py_END_CRITICAL_SECTION(); + return result; +} + +static PyObject * +textiowrapper_closed_get_impl(textio *self, void *context) { CHECK_ATTACHED(self); return PyObject_GetAttr(self->buffer, &_Py_ID(closed)); } static PyObject * -textiowrapper_newlines_get(textio *self, void *context) +textiowrapper_closed_get(textio *self, void *context) +{ + PyObject *result = NULL; + Py_BEGIN_CRITICAL_SECTION(self); + result = textiowrapper_closed_get_impl(self, context); + Py_END_CRITICAL_SECTION(); + return result; +} + +static PyObject * +textiowrapper_newlines_get_impl(textio *self, void *context) { PyObject *res; CHECK_ATTACHED(self); @@ -3177,21 +3212,51 @@ textiowrapper_newlines_get(textio *self, void *context) } static PyObject * -textiowrapper_errors_get(textio *self, void *context) +textiowrapper_newlines_get(textio *self, void *context) +{ + PyObject *result = NULL; + Py_BEGIN_CRITICAL_SECTION(self); + result = textiowrapper_newlines_get_impl(self, context); + Py_END_CRITICAL_SECTION(); + return result; +} + +static PyObject * +textiowrapper_errors_get_impl(textio *self, void *context) { CHECK_INITIALIZED(self); return Py_NewRef(self->errors); } static PyObject * -textiowrapper_chunk_size_get(textio *self, void *context) +textiowrapper_errors_get(textio *self, void *context) +{ + PyObject *result = NULL; + Py_BEGIN_CRITICAL_SECTION(self); + result = textiowrapper_errors_get_impl(self, context); + Py_END_CRITICAL_SECTION(); + return result; +} + +static PyObject * +textiowrapper_chunk_size_get_impl(textio *self, void *context) { CHECK_ATTACHED(self); return PyLong_FromSsize_t(self->chunk_size); } +static PyObject * +textiowrapper_chunk_size_get(textio *self, void *context) +{ + PyObject *result = NULL; + Py_BEGIN_CRITICAL_SECTION(self); + result = textiowrapper_chunk_size_get_impl(self, context); + Py_END_CRITICAL_SECTION(); + return result; +} + static int -textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context) +textiowrapper_chunk_size_set_impl(textio *self, PyObject *arg, void *context) { Py_ssize_t n; CHECK_ATTACHED_INT(self); @@ -3211,6 +3276,16 @@ textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context) return 0; } +static int +textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context) +{ + int result = 0; + Py_BEGIN_CRITICAL_SECTION(self); + result = textiowrapper_chunk_size_set_impl(self, arg, context); + Py_END_CRITICAL_SECTION(); + return result; +} + static PyMethodDef incrementalnewlinedecoder_methods[] = { _IO_INCREMENTALNEWLINEDECODER_DECODE_METHODDEF _IO_INCREMENTALNEWLINEDECODER_GETSTATE_METHODDEF diff --git a/Modules/_multiprocessing/posixshmem.c b/Modules/_multiprocessing/posixshmem.c index cd08a9fedc0578..425ce10075c156 100644 --- a/Modules/_multiprocessing/posixshmem.c +++ b/Modules/_multiprocessing/posixshmem.c @@ -2,9 +2,9 @@ posixshmem - A Python extension that provides shm_open() and shm_unlink() */ -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED #define Py_LIMITED_API 0x030c0000 #endif diff --git a/Modules/_pickle.c b/Modules/_pickle.c index a3cf34699ba509..227e5378e42285 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -4707,6 +4707,14 @@ Pickler_traverse(PicklerObject *self, visitproc visit, void *arg) Py_VISIT(self->fast_memo); Py_VISIT(self->reducer_override); Py_VISIT(self->buffer_callback); + PyMemoTable *memo = self->memo; + if (memo && memo->mt_table) { + Py_ssize_t i = memo->mt_allocated; + while (--i >= 0) { + Py_VISIT(memo->mt_table[i].me_key); + } + } + return 0; } @@ -7175,6 +7183,13 @@ Unpickler_traverse(UnpicklerObject *self, visitproc visit, void *arg) Py_VISIT(self->stack); Py_VISIT(self->pers_func); Py_VISIT(self->buffers); + PyObject **memo = self->memo; + if (memo) { + Py_ssize_t i = self->memo_size; + while (--i >= 0) { + Py_VISIT(memo[i]); + } + } return 0; } diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 2898eedc3e3a8f..d0dd8f064e0395 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -767,8 +767,10 @@ child_exec(char *const exec_array[], #endif #ifdef HAVE_SETGROUPS - if (extra_group_size > 0) + if (extra_group_size >= 0) { + assert((extra_group_size == 0) == (extra_groups == NULL)); POSIX_CALL(setgroups(extra_group_size, extra_groups)); + } #endif /* HAVE_SETGROUPS */ #ifdef HAVE_SETREGID @@ -1022,7 +1024,6 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args, pid_t pid = -1; int need_to_reenable_gc = 0; char *const *argv = NULL, *const *envp = NULL; - Py_ssize_t extra_group_size = 0; int need_after_fork = 0; int saved_errno = 0; int *c_fds_to_keep = NULL; @@ -1103,6 +1104,13 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args, cwd = PyBytes_AsString(cwd_obj2); } + // Special initial value meaning that subprocess API was called with + // extra_groups=None leading to _posixsubprocess.fork_exec(gids=None). + // We use this to differentiate between code desiring a setgroups(0, NULL) + // call vs no call at all. The fast vfork() code path could be used when + // there is no setgroups call. + Py_ssize_t extra_group_size = -2; + if (extra_groups_packed != Py_None) { #ifdef HAVE_SETGROUPS if (!PyList_Check(extra_groups_packed)) { diff --git a/Modules/_randommodule.c b/Modules/_randommodule.c index 514bec16a347f4..4403e1d132c057 100644 --- a/Modules/_randommodule.c +++ b/Modules/_randommodule.c @@ -175,6 +175,7 @@ genrand_uint32(RandomObject *self) */ /*[clinic input] +@critical_section _random.Random.random self: self(type="RandomObject *") @@ -184,7 +185,7 @@ random() -> x in the interval [0, 1). static PyObject * _random_Random_random_impl(RandomObject *self) -/*[clinic end generated code: output=117ff99ee53d755c input=afb2a59cbbb00349]*/ +/*[clinic end generated code: output=117ff99ee53d755c input=26492e52d26e8b7b]*/ { uint32_t a=genrand_uint32(self)>>5, b=genrand_uint32(self)>>6; return PyFloat_FromDouble((a*67108864.0+b)*(1.0/9007199254740992.0)); @@ -368,6 +369,7 @@ random_seed(RandomObject *self, PyObject *arg) } /*[clinic input] +@critical_section _random.Random.seed self: self(type="RandomObject *") @@ -382,7 +384,7 @@ of the current time and the process identifier. static PyObject * _random_Random_seed_impl(RandomObject *self, PyObject *n) -/*[clinic end generated code: output=0fad1e16ba883681 input=78d6ef0d52532a54]*/ +/*[clinic end generated code: output=0fad1e16ba883681 input=46d01d2ba938c7b1]*/ { if (random_seed(self, n) < 0) { return NULL; @@ -391,6 +393,7 @@ _random_Random_seed_impl(RandomObject *self, PyObject *n) } /*[clinic input] +@critical_section _random.Random.getstate self: self(type="RandomObject *") @@ -400,7 +403,7 @@ getstate() -> tuple containing the current state. static PyObject * _random_Random_getstate_impl(RandomObject *self) -/*[clinic end generated code: output=bf6cef0c092c7180 input=b937a487928c0e89]*/ +/*[clinic end generated code: output=bf6cef0c092c7180 input=b6621f31eb639694]*/ { PyObject *state; PyObject *element; @@ -428,6 +431,7 @@ _random_Random_getstate_impl(RandomObject *self) /*[clinic input] +@critical_section _random.Random.setstate self: self(type="RandomObject *") @@ -438,8 +442,8 @@ setstate(state) -> None. Restores generator state. [clinic start generated code]*/ static PyObject * -_random_Random_setstate(RandomObject *self, PyObject *state) -/*[clinic end generated code: output=fd1c3cd0037b6681 input=b3b4efbb1bc66af8]*/ +_random_Random_setstate_impl(RandomObject *self, PyObject *state) +/*[clinic end generated code: output=babfc2c2eac6b027 input=358e898ec07469b7]*/ { int i; unsigned long element; @@ -479,7 +483,7 @@ _random_Random_setstate(RandomObject *self, PyObject *state) } /*[clinic input] - +@critical_section _random.Random.getrandbits self: self(type="RandomObject *") @@ -491,7 +495,7 @@ getrandbits(k) -> x. Generates an int with k random bits. static PyObject * _random_Random_getrandbits_impl(RandomObject *self, int k) -/*[clinic end generated code: output=b402f82a2158887f input=8c0e6396dd176fc0]*/ +/*[clinic end generated code: output=b402f82a2158887f input=87603cd60f79f730]*/ { int i, words; uint32_t r; diff --git a/Modules/_scproxy.c b/Modules/_scproxy.c index 9f7a65c91e2d33..7920d2c2b8739d 100644 --- a/Modules/_scproxy.c +++ b/Modules/_scproxy.c @@ -4,10 +4,10 @@ */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED #define Py_LIMITED_API 0x030c0000 #endif diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 7bc30cb3529d47..90b600f4b776a3 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -301,6 +301,10 @@ typedef struct { BIO *keylog_bio; /* Cached module state, also used in SSLSocket and SSLSession code. */ _sslmodulestate *state; +#ifndef OPENSSL_NO_PSK + PyObject *psk_client_callback; + PyObject *psk_server_callback; +#endif } PySSLContext; typedef struct { @@ -3123,6 +3127,10 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version) self->alpn_protocols = NULL; self->set_sni_cb = NULL; self->state = get_ssl_state(module); +#ifndef OPENSSL_NO_PSK + self->psk_client_callback = NULL; + self->psk_server_callback = NULL; +#endif /* Don't check host name by default */ if (proto_version == PY_SSL_VERSION_TLS_CLIENT) { @@ -3235,6 +3243,10 @@ context_clear(PySSLContext *self) Py_CLEAR(self->set_sni_cb); Py_CLEAR(self->msg_cb); Py_CLEAR(self->keylog_filename); +#ifndef OPENSSL_NO_PSK + Py_CLEAR(self->psk_client_callback); + Py_CLEAR(self->psk_server_callback); +#endif if (self->keylog_bio != NULL) { PySSL_BEGIN_ALLOW_THREADS BIO_free_all(self->keylog_bio); @@ -4662,6 +4674,238 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form) return NULL; } +#ifndef OPENSSL_NO_PSK +static unsigned int psk_client_callback(SSL *s, + const char *hint, + char *identity, + unsigned int max_identity_len, + unsigned char *psk, + unsigned int max_psk_len) +{ + PyGILState_STATE gstate = PyGILState_Ensure(); + PyObject *callback = NULL; + + PySSLSocket *ssl = SSL_get_app_data(s); + if (ssl == NULL || ssl->ctx == NULL) { + goto error; + } + callback = ssl->ctx->psk_client_callback; + if (callback == NULL) { + goto error; + } + + PyObject *hint_str = (hint != NULL && hint[0] != '\0') ? + PyUnicode_DecodeUTF8(hint, strlen(hint), "strict") : + Py_NewRef(Py_None); + if (hint_str == NULL) { + /* The remote side has sent an invalid UTF-8 string + * (breaking the standard), drop the connection without + * raising a decode exception. */ + PyErr_Clear(); + goto error; + } + PyObject *result = PyObject_CallFunctionObjArgs(callback, hint_str, NULL); + Py_DECREF(hint_str); + + if (result == NULL) { + goto error; + } + + const char *psk_; + const char *identity_; + Py_ssize_t psk_len_; + Py_ssize_t identity_len_ = 0; + if (!PyArg_ParseTuple(result, "z#y#", &identity_, &identity_len_, &psk_, &psk_len_)) { + Py_DECREF(result); + goto error; + } + + if (identity_len_ + 1 > max_identity_len || psk_len_ > max_psk_len) { + Py_DECREF(result); + goto error; + } + memcpy(psk, psk_, psk_len_); + if (identity_ != NULL) { + memcpy(identity, identity_, identity_len_); + } + identity[identity_len_] = 0; + + Py_DECREF(result); + + PyGILState_Release(gstate); + return (unsigned int)psk_len_; + +error: + if (PyErr_Occurred()) { + PyErr_WriteUnraisable(callback); + } + PyGILState_Release(gstate); + return 0; +} +#endif + +/*[clinic input] +_ssl._SSLContext.set_psk_client_callback + callback: object + +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLContext_set_psk_client_callback_impl(PySSLContext *self, + PyObject *callback) +/*[clinic end generated code: output=0aba86f6ed75119e input=7627bae0e5ee7635]*/ +{ +#ifndef OPENSSL_NO_PSK + if (self->protocol == PY_SSL_VERSION_TLS_SERVER) { + _setSSLError(get_state_ctx(self), + "Cannot add PSK client callback to a " + "PROTOCOL_TLS_SERVER context", 0, __FILE__, __LINE__); + return NULL; + } + + SSL_psk_client_cb_func ssl_callback; + if (callback == Py_None) { + callback = NULL; + // Delete the existing callback + ssl_callback = NULL; + } else { + if (!PyCallable_Check(callback)) { + PyErr_SetString(PyExc_TypeError, "callback must be callable"); + return NULL; + } + ssl_callback = psk_client_callback; + } + + Py_XDECREF(self->psk_client_callback); + Py_XINCREF(callback); + + self->psk_client_callback = callback; + SSL_CTX_set_psk_client_callback(self->ctx, ssl_callback); + + Py_RETURN_NONE; +#else + PyErr_SetString(PyExc_NotImplementedError, + "TLS-PSK is not supported by your OpenSSL version."); + return NULL; +#endif +} + +#ifndef OPENSSL_NO_PSK +static unsigned int psk_server_callback(SSL *s, + const char *identity, + unsigned char *psk, + unsigned int max_psk_len) +{ + PyGILState_STATE gstate = PyGILState_Ensure(); + PyObject *callback = NULL; + + PySSLSocket *ssl = SSL_get_app_data(s); + if (ssl == NULL || ssl->ctx == NULL) { + goto error; + } + callback = ssl->ctx->psk_server_callback; + if (callback == NULL) { + goto error; + } + + PyObject *identity_str = (identity != NULL && identity[0] != '\0') ? + PyUnicode_DecodeUTF8(identity, strlen(identity), "strict") : + Py_NewRef(Py_None); + if (identity_str == NULL) { + /* The remote side has sent an invalid UTF-8 string + * (breaking the standard), drop the connection without + * raising a decode exception. */ + PyErr_Clear(); + goto error; + } + PyObject *result = PyObject_CallFunctionObjArgs(callback, identity_str, NULL); + Py_DECREF(identity_str); + + if (result == NULL) { + goto error; + } + + char *psk_; + Py_ssize_t psk_len_; + if (PyBytes_AsStringAndSize(result, &psk_, &psk_len_) < 0) { + Py_DECREF(result); + goto error; + } + + if (psk_len_ > max_psk_len) { + Py_DECREF(result); + goto error; + } + memcpy(psk, psk_, psk_len_); + + Py_DECREF(result); + + PyGILState_Release(gstate); + return (unsigned int)psk_len_; + +error: + if (PyErr_Occurred()) { + PyErr_WriteUnraisable(callback); + } + PyGILState_Release(gstate); + return 0; +} +#endif + +/*[clinic input] +_ssl._SSLContext.set_psk_server_callback + callback: object + identity_hint: str(accept={str, NoneType}) = None + +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLContext_set_psk_server_callback_impl(PySSLContext *self, + PyObject *callback, + const char *identity_hint) +/*[clinic end generated code: output=1f4d6a4e09a92b03 input=65d4b6022aa85ea3]*/ +{ +#ifndef OPENSSL_NO_PSK + if (self->protocol == PY_SSL_VERSION_TLS_CLIENT) { + _setSSLError(get_state_ctx(self), + "Cannot add PSK server callback to a " + "PROTOCOL_TLS_CLIENT context", 0, __FILE__, __LINE__); + return NULL; + } + + SSL_psk_server_cb_func ssl_callback; + if (callback == Py_None) { + callback = NULL; + // Delete the existing callback and hint + ssl_callback = NULL; + identity_hint = NULL; + } else { + if (!PyCallable_Check(callback)) { + PyErr_SetString(PyExc_TypeError, "callback must be callable"); + return NULL; + } + ssl_callback = psk_server_callback; + } + + if (SSL_CTX_use_psk_identity_hint(self->ctx, identity_hint) != 1) { + PyErr_SetString(PyExc_ValueError, "failed to set identity hint"); + return NULL; + } + + Py_XDECREF(self->psk_server_callback); + Py_XINCREF(callback); + + self->psk_server_callback = callback; + SSL_CTX_set_psk_server_callback(self->ctx, ssl_callback); + + Py_RETURN_NONE; +#else + PyErr_SetString(PyExc_NotImplementedError, + "TLS-PSK is not supported by your OpenSSL version."); + return NULL; +#endif +} + static PyGetSetDef context_getsetlist[] = { {"check_hostname", (getter) get_check_hostname, @@ -4716,6 +4960,8 @@ static struct PyMethodDef context_methods[] = { _SSL__SSLCONTEXT_CERT_STORE_STATS_METHODDEF _SSL__SSLCONTEXT_GET_CA_CERTS_METHODDEF _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF + _SSL__SSLCONTEXT_SET_PSK_CLIENT_CALLBACK_METHODDEF + _SSL__SSLCONTEXT_SET_PSK_SERVER_CALLBACK_METHODDEF {NULL, NULL} /* sentinel */ }; @@ -6019,6 +6265,12 @@ sslmodule_init_constants(PyObject *m) addbool(m, "HAS_TLSv1_3", 0); #endif +#ifdef OPENSSL_NO_PSK + addbool(m, "HAS_PSK", 0); +#else + addbool(m, "HAS_PSK", 1); +#endif + #undef addbool #undef ADD_INT_CONST diff --git a/Modules/_stat.c b/Modules/_stat.c index 9e278d6dcd1589..1ef1e97f4b7dca 100644 --- a/Modules/_stat.c +++ b/Modules/_stat.c @@ -12,10 +12,10 @@ */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.13 for PyModule_Add() on Windows #define Py_LIMITED_API 0x030d0000 #endif diff --git a/Modules/_struct.c b/Modules/_struct.c index 0116b03ea95115..bd16fa89f18945 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -1553,9 +1553,28 @@ prepare_s(PyStructObject *self) return -1; } +static PyObject * +s_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *self; + + assert(type != NULL); + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + assert(alloc_func != NULL); + + self = alloc_func(type, 0); + if (self != NULL) { + PyStructObject *s = (PyStructObject*)self; + s->s_format = Py_NewRef(Py_None); + s->s_codes = NULL; + s->s_size = -1; + s->s_len = -1; + } + return self; +} + /*[clinic input] -@classmethod -Struct.__new__ +Struct.__init__ format: object @@ -1567,24 +1586,16 @@ the format string. See help(struct) for more on format strings. [clinic start generated code]*/ -static PyObject * -Struct_impl(PyTypeObject *type, PyObject *format) -/*[clinic end generated code: output=49468b044e334308 input=8b91868eb1df0e28]*/ +static int +Struct___init___impl(PyStructObject *self, PyObject *format) +/*[clinic end generated code: output=b8e80862444e92d0 input=192a4575a3dde802]*/ { - allocfunc alloc = PyType_GetSlot(type, Py_tp_alloc); - assert(alloc != NULL); - PyStructObject *self = (PyStructObject *)alloc(type, 0); - - if (self == NULL) { - return NULL; - } + int ret = 0; if (PyUnicode_Check(format)) { format = PyUnicode_AsASCIIString(format); - if (format == NULL) { - Py_DECREF(self); - return NULL; - } + if (format == NULL) + return -1; } else { Py_INCREF(format); @@ -1592,24 +1603,19 @@ Struct_impl(PyTypeObject *type, PyObject *format) if (!PyBytes_Check(format)) { Py_DECREF(format); - Py_DECREF(self); PyErr_Format(PyExc_TypeError, "Struct() argument 1 must be a str or bytes object, " "not %.200s", _PyType_Name(Py_TYPE(format))); - return NULL; + return -1; } - self->s_format = format; + Py_SETREF(self->s_format, format); - if (prepare_s(self) < 0) { - Py_DECREF(self); - return NULL; - } - return (PyObject *)self; + ret = prepare_s(self); + return ret; } - static int s_clear(PyStructObject *s) { @@ -2219,8 +2225,9 @@ static PyType_Slot PyStructType_slots[] = { {Py_tp_methods, s_methods}, {Py_tp_members, s_members}, {Py_tp_getset, s_getsetlist}, - {Py_tp_new, Struct}, + {Py_tp_init, Struct___init__}, {Py_tp_alloc, PyType_GenericAlloc}, + {Py_tp_new, s_new}, {Py_tp_free, PyObject_GC_Del}, {0, 0}, }; @@ -2250,14 +2257,13 @@ cache_struct_converter(PyObject *module, PyObject *fmt, PyStructObject **ptr) return 1; } - s_object = PyDict_GetItemWithError(state->cache, fmt); + if (PyDict_GetItemRef(state->cache, fmt, &s_object) < 0) { + return 0; + } if (s_object != NULL) { - *ptr = (PyStructObject *)Py_NewRef(s_object); + *ptr = (PyStructObject *)s_object; return Py_CLEANUP_SUPPORTED; } - else if (PyErr_Occurred()) { - return 0; - } s_object = PyObject_CallOneArg(state->PyStructType, fmt); if (s_object != NULL) { diff --git a/Modules/_sysconfig.c b/Modules/_sysconfig.c index 6f1cc16b58467d..c76b9e6b3ebafa 100644 --- a/Modules/_sysconfig.c +++ b/Modules/_sysconfig.c @@ -57,12 +57,12 @@ _sysconfig_config_vars_impl(PyObject *module) } #endif -#ifdef Py_NOGIL - PyObject *py_nogil = _PyLong_GetOne(); +#ifdef Py_GIL_DISABLED + PyObject *py_gil_disabled = _PyLong_GetOne(); #else - PyObject *py_nogil = _PyLong_GetZero(); + PyObject *py_gil_disabled = _PyLong_GetZero(); #endif - if (PyDict_SetItemString(config, "Py_NOGIL", py_nogil) < 0) { + if (PyDict_SetItemString(config, "Py_GIL_DISABLED", py_gil_disabled) < 0) { Py_DECREF(config); return NULL; } diff --git a/Modules/_testcapi/abstract.c b/Modules/_testcapi/abstract.c index 4a9144e66f0fcd..a8ba009eb6a54b 100644 --- a/Modules/_testcapi/abstract.c +++ b/Modules/_testcapi/abstract.c @@ -2,6 +2,34 @@ #include "util.h" +static PyObject * +object_repr(PyObject *self, PyObject *arg) +{ + NULLABLE(arg); + return PyObject_Repr(arg); +} + +static PyObject * +object_ascii(PyObject *self, PyObject *arg) +{ + NULLABLE(arg); + return PyObject_ASCII(arg); +} + +static PyObject * +object_str(PyObject *self, PyObject *arg) +{ + NULLABLE(arg); + return PyObject_Str(arg); +} + +static PyObject * +object_bytes(PyObject *self, PyObject *arg) +{ + NULLABLE(arg); + return PyObject_Bytes(arg); +} + static PyObject * object_getattr(PyObject *self, PyObject *args) { @@ -616,6 +644,11 @@ sequence_tuple(PyObject *self, PyObject *obj) static PyMethodDef test_methods[] = { + {"object_repr", object_repr, METH_O}, + {"object_ascii", object_ascii, METH_O}, + {"object_str", object_str, METH_O}, + {"object_bytes", object_bytes, METH_O}, + {"object_getattr", object_getattr, METH_VARARGS}, {"object_getattrstring", object_getattrstring, METH_VARARGS}, {"object_getoptionalattr", object_getoptionalattr, METH_VARARGS}, diff --git a/Modules/_testcapi/complex.c b/Modules/_testcapi/complex.c index 400f4054c613ee..4a70217eb90d62 100644 --- a/Modules/_testcapi/complex.c +++ b/Modules/_testcapi/complex.c @@ -85,6 +85,58 @@ complex_asccomplex(PyObject *Py_UNUSED(module), PyObject *obj) return PyComplex_FromCComplex(complex); } +static PyObject* +_py_c_neg(PyObject *Py_UNUSED(module), PyObject *num) +{ + Py_complex complex; + + complex = PyComplex_AsCComplex(num); + if (complex.real == -1. && PyErr_Occurred()) { + return NULL; + } + + return PyComplex_FromCComplex(_Py_c_neg(complex)); +} + +#define _PY_C_FUNC2(suffix) \ + static PyObject * \ + _py_c_##suffix(PyObject *Py_UNUSED(module), PyObject *args) \ + { \ + Py_complex num, exp, res; \ + \ + if (!PyArg_ParseTuple(args, "DD", &num, &exp)) { \ + return NULL; \ + } \ + \ + errno = 0; \ + res = _Py_c_##suffix(num, exp); \ + return Py_BuildValue("Di", &res, errno); \ + }; + +_PY_C_FUNC2(sum) +_PY_C_FUNC2(diff) +_PY_C_FUNC2(prod) +_PY_C_FUNC2(quot) +_PY_C_FUNC2(pow) + +static PyObject* +_py_c_abs(PyObject *Py_UNUSED(module), PyObject* obj) +{ + Py_complex complex; + double res; + + NULLABLE(obj); + complex = PyComplex_AsCComplex(obj); + + if (complex.real == -1. && PyErr_Occurred()) { + return NULL; + } + + errno = 0; + res = _Py_c_abs(complex); + return Py_BuildValue("di", res, errno); +} + static PyMethodDef test_methods[] = { {"complex_check", complex_check, METH_O}, @@ -94,6 +146,13 @@ static PyMethodDef test_methods[] = { {"complex_realasdouble", complex_realasdouble, METH_O}, {"complex_imagasdouble", complex_imagasdouble, METH_O}, {"complex_asccomplex", complex_asccomplex, METH_O}, + {"_py_c_sum", _py_c_sum, METH_VARARGS}, + {"_py_c_diff", _py_c_diff, METH_VARARGS}, + {"_py_c_neg", _py_c_neg, METH_O}, + {"_py_c_prod", _py_c_prod, METH_VARARGS}, + {"_py_c_quot", _py_c_quot, METH_VARARGS}, + {"_py_c_pow", _py_c_pow, METH_VARARGS}, + {"_py_c_abs", _py_c_abs, METH_O}, {NULL}, }; diff --git a/Modules/_testcapi/getargs.c b/Modules/_testcapi/getargs.c index e4cd15503fd11f..33e8af7d7bbb39 100644 --- a/Modules/_testcapi/getargs.c +++ b/Modules/_testcapi/getargs.c @@ -71,18 +71,22 @@ parse_tuple_and_keywords(PyObject *self, PyObject *args) if (result) { int objects_only = 1; + int count = 0; for (const char *f = sub_format; *f; f++) { - if (Py_ISALNUM(*f) && strchr("OSUY", *f) == NULL) { - objects_only = 0; - break; + if (Py_ISALNUM(*f)) { + if (strchr("OSUY", *f) == NULL) { + objects_only = 0; + break; + } + count++; } } if (objects_only) { - return_value = PyTuple_New(size); + return_value = PyTuple_New(count); if (return_value == NULL) { goto exit; } - for (Py_ssize_t i = 0; i < size; i++) { + for (Py_ssize_t i = 0; i < count; i++) { PyObject *arg = *(PyObject **)(buffers + i); if (arg == NULL) { arg = Py_None; diff --git a/Modules/_testcapi/hash.c b/Modules/_testcapi/hash.c index d0b8127020c5c1..aee76787dcddb3 100644 --- a/Modules/_testcapi/hash.c +++ b/Modules/_testcapi/hash.c @@ -44,8 +44,24 @@ hash_getfuncdef(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) return result; } + +static PyObject * +hash_pointer(PyObject *Py_UNUSED(module), PyObject *arg) +{ + void *ptr = PyLong_AsVoidPtr(arg); + if (ptr == NULL && PyErr_Occurred()) { + return NULL; + } + + Py_hash_t hash = Py_HashPointer(ptr); + Py_BUILD_ASSERT(sizeof(long long) >= sizeof(hash)); + return PyLong_FromLongLong(hash); +} + + static PyMethodDef test_methods[] = { {"hash_getfuncdef", hash_getfuncdef, METH_NOARGS}, + {"hash_pointer", hash_pointer, METH_O}, {NULL}, }; diff --git a/Modules/_testcapi/heaptype_relative.c b/Modules/_testcapi/heaptype_relative.c index b58d26cddc723a..52286f05f7154c 100644 --- a/Modules/_testcapi/heaptype_relative.c +++ b/Modules/_testcapi/heaptype_relative.c @@ -1,8 +1,8 @@ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED #define Py_LIMITED_API 0x030c0000 // 3.12 #endif diff --git a/Modules/_testcapi/vectorcall_limited.c b/Modules/_testcapi/vectorcall_limited.c index 857cb30b3da1ca..0a650f1b351d2d 100644 --- a/Modules/_testcapi/vectorcall_limited.c +++ b/Modules/_testcapi/vectorcall_limited.c @@ -1,10 +1,10 @@ /* Test Vectorcall in the limited API */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED #define Py_LIMITED_API 0x030c0000 // 3.12 #endif diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 999bd866f14814..9fdd67093338e4 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -569,6 +569,12 @@ static PyType_Spec HeapTypeNameType_Spec = { .slots = HeapTypeNameType_slots, }; +static PyObject * +get_heaptype_for_name(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return PyType_FromSpec(&HeapTypeNameType_Spec); +} + static PyObject * test_get_type_name(PyObject *self, PyObject *Py_UNUSED(ignored)) { @@ -3210,6 +3216,7 @@ static PyMethodDef TestMethods[] = { {"py_buildvalue_ints", py_buildvalue_ints, METH_VARARGS}, {"test_buildvalue_N", test_buildvalue_N, METH_NOARGS}, {"test_get_statictype_slots", test_get_statictype_slots, METH_NOARGS}, + {"get_heaptype_for_name", get_heaptype_for_name, METH_NOARGS}, {"test_get_type_name", test_get_type_name, METH_NOARGS}, {"test_get_type_qualname", test_get_type_qualname, METH_NOARGS}, {"test_get_type_dict", test_get_type_dict, METH_NOARGS}, diff --git a/Modules/_testclinic_limited.c b/Modules/_testclinic_limited.c index c5d07e1370dac8..61bc84134458da 100644 --- a/Modules/_testclinic_limited.c +++ b/Modules/_testclinic_limited.c @@ -5,10 +5,10 @@ #undef Py_BUILD_CORE_BUILTIN #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // For now, only limited C API 3.13 is supported #define Py_LIMITED_API 0x030d0000 #endif diff --git a/Modules/_testimportmultiple.c b/Modules/_testimportmultiple.c index 99d48b6617f49c..245e81b2dce7f8 100644 --- a/Modules/_testimportmultiple.c +++ b/Modules/_testimportmultiple.c @@ -5,10 +5,10 @@ */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED #define Py_LIMITED_API 0x03020000 #endif diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 4fc9e853c0cd91..ba7653f2d9c7aa 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -27,6 +27,7 @@ #include "pycore_pathconfig.h" // _PyPathConfig_ClearGlobal() #include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_typeobject.h" // _PyType_GetModuleName() #include "interpreteridobject.h" // PyInterpreterID_LookUp() @@ -1616,6 +1617,25 @@ perf_trampoline_set_persist_after_fork(PyObject *self, PyObject *args) } +static PyObject * +get_type_module_name(PyObject *self, PyObject *type) +{ + assert(PyType_Check(type)); + return _PyType_GetModuleName((PyTypeObject *)type); +} + + +#ifdef Py_GIL_DISABLED +static PyObject * +get_py_thread_id(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + uintptr_t tid = _Py_ThreadId(); + Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(tid)); + return PyLong_FromUnsignedLongLong(tid); +} +#endif + + static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, @@ -1678,6 +1698,10 @@ static PyMethodDef module_functions[] = { {"get_crossinterp_data", get_crossinterp_data, METH_VARARGS}, {"restore_crossinterp_data", restore_crossinterp_data, METH_VARARGS}, _TESTINTERNALCAPI_TEST_LONG_NUMBITS_METHODDEF + {"get_type_module_name", get_type_module_name, METH_O}, +#ifdef Py_GIL_DISABLED + {"py_thread_id", get_py_thread_id, METH_NOARGS}, +#endif {NULL, NULL} /* sentinel */ }; diff --git a/Modules/_testinternalcapi/test_critical_sections.c b/Modules/_testinternalcapi/test_critical_sections.c index 9392096a16d1bf..1f7e311558b27c 100644 --- a/Modules/_testinternalcapi/test_critical_sections.c +++ b/Modules/_testinternalcapi/test_critical_sections.c @@ -6,7 +6,7 @@ #include "pycore_critical_section.h" -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED #define assert_nogil assert #define assert_gil(x) #else @@ -25,7 +25,7 @@ test_critical_sections(PyObject *self, PyObject *Py_UNUSED(args)) assert(d2 != NULL); // Beginning a critical section should lock the associated object and - // push the critical section onto the thread's stack (in Py_NOGIL builds). + // push the critical section onto the thread's stack (in Py_GIL_DISABLED builds). Py_BEGIN_CRITICAL_SECTION(d1); assert_nogil(PyMutex_IsLocked(&d1->ob_mutex)); assert_nogil(_PyCriticalSection_IsActive(PyThreadState_GET()->critical_section)); diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index c608789b5fbd5c..afcf646e3bc19e 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1115,12 +1115,10 @@ local_getattro(localobject *self, PyObject *name) } /* Optimization: just look in dict ourselves */ - PyObject *value = PyDict_GetItemWithError(ldict, name); - if (value != NULL) { - return Py_NewRef(value); - } - if (PyErr_Occurred()) { - return NULL; + PyObject *value; + if (PyDict_GetItemRef(ldict, name, &value) != 0) { + // found or error + return value; } /* Fall back on generic to get __class__ and __dict__ */ diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index f9a18644945c65..64e752c305aae1 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -1202,7 +1202,7 @@ typedef struct Tkapp_CallEvent { Tcl_Condition *done; } Tkapp_CallEvent; -void +static void Tkapp_CallDeallocArgs(Tcl_Obj** objv, Tcl_Obj** objStore, int objc) { int i; diff --git a/Modules/_uuidmodule.c b/Modules/_uuidmodule.c index 1d716051df4ade..d8b211c632eef1 100644 --- a/Modules/_uuidmodule.c +++ b/Modules/_uuidmodule.c @@ -4,10 +4,10 @@ */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED #define Py_LIMITED_API 0x030c0000 #endif diff --git a/Modules/_weakref.c b/Modules/_weakref.c index 90c7afcc248ff3..7225dbc9ce4a1b 100644 --- a/Modules/_weakref.c +++ b/Modules/_weakref.c @@ -1,5 +1,4 @@ #include "Python.h" -#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION() #include "pycore_dict.h" // _PyDict_DelItemIf() #include "pycore_object.h" // _PyObject_GET_WEAKREFS_LISTPTR() #include "pycore_weakref.h" // _PyWeakref_IS_DEAD() @@ -15,7 +14,7 @@ module _weakref #include "clinic/_weakref.c.h" /*[clinic input] - +@critical_section object _weakref.getweakrefcount -> Py_ssize_t object: object @@ -26,17 +25,13 @@ Return the number of weak references to 'object'. static Py_ssize_t _weakref_getweakrefcount_impl(PyObject *module, PyObject *object) -/*[clinic end generated code: output=301806d59558ff3e input=cedb69711b6a2507]*/ +/*[clinic end generated code: output=301806d59558ff3e input=6535a580f1d0ebdc]*/ { - PyWeakReference **list; - - if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(object))) + if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(object))) { return 0; - Py_ssize_t count; - Py_BEGIN_CRITICAL_SECTION(object); - list = GET_WEAKREFS_LISTPTR(object); - count = _PyWeakref_GetWeakrefCount(*list); - Py_END_CRITICAL_SECTION(); + } + PyWeakReference **list = GET_WEAKREFS_LISTPTR(object); + Py_ssize_t count = _PyWeakref_GetWeakrefCount(*list); return count; } @@ -48,11 +43,7 @@ is_dead_weakref(PyObject *value) PyErr_SetString(PyExc_TypeError, "not a weakref"); return -1; } - int is_dead; - Py_BEGIN_CRITICAL_SECTION(value); - is_dead = _PyWeakref_IS_DEAD(value); - Py_END_CRITICAL_SECTION(); - return is_dead; + return _PyWeakref_IS_DEAD(value); } /*[clinic input] @@ -86,6 +77,7 @@ _weakref__remove_dead_weakref_impl(PyObject *module, PyObject *dct, /*[clinic input] +@critical_section object _weakref.getweakrefs object: object / @@ -94,21 +86,19 @@ Return a list of all weak reference objects pointing to 'object'. [clinic start generated code]*/ static PyObject * -_weakref_getweakrefs(PyObject *module, PyObject *object) -/*[clinic end generated code: output=25c7731d8e011824 input=00c6d0e5d3206693]*/ +_weakref_getweakrefs_impl(PyObject *module, PyObject *object) +/*[clinic end generated code: output=5ec268989fb8f035 input=3dea95b8f5b31bbb]*/ { if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(object))) { return PyList_New(0); } - PyObject *result; - Py_BEGIN_CRITICAL_SECTION(object); PyWeakReference **list = GET_WEAKREFS_LISTPTR(object); Py_ssize_t count = _PyWeakref_GetWeakrefCount(*list); - result = PyList_New(count); + PyObject *result = PyList_New(count); if (result == NULL) { - goto exit; + return NULL; } PyWeakReference *current = *list; @@ -116,8 +106,6 @@ _weakref_getweakrefs(PyObject *module, PyObject *object) PyList_SET_ITEM(result, i, Py_NewRef(current)); current = current->wr_next; } -exit: - Py_END_CRITICAL_SECTION(); return result; } diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 001fa887847cbd..02c2abed27ddfa 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -28,31 +28,11 @@ _get_current_interp(void) return PyInterpreterState_Get(); } -static PyObject * -add_new_exception(PyObject *mod, const char *name, PyObject *base) -{ - assert(!PyObject_HasAttrStringWithError(mod, name)); - PyObject *exctype = PyErr_NewException(name, base, NULL); - if (exctype == NULL) { - return NULL; - } - int res = PyModule_AddType(mod, (PyTypeObject *)exctype); - if (res < 0) { - Py_DECREF(exctype); - return NULL; - } - return exctype; -} - -#define ADD_NEW_EXCEPTION(MOD, NAME, BASE) \ - add_new_exception(MOD, MODULE_NAME "." Py_STRINGIFY(NAME), BASE) - /* module state *************************************************************/ typedef struct { - /* exceptions */ - PyObject *RunFailedError; + int _notused; } module_state; static inline module_state * @@ -67,18 +47,12 @@ get_module_state(PyObject *mod) static int traverse_module_state(module_state *state, visitproc visit, void *arg) { - /* exceptions */ - Py_VISIT(state->RunFailedError); - return 0; } static int clear_module_state(module_state *state) { - /* exceptions */ - Py_CLEAR(state->RunFailedError); - return 0; } @@ -177,30 +151,6 @@ get_code_str(PyObject *arg, Py_ssize_t *len_p, PyObject **bytes_p, int *flags_p) /* interpreter-specific code ************************************************/ -static int -exceptions_init(PyObject *mod) -{ - module_state *state = get_module_state(mod); - if (state == NULL) { - return -1; - } - -#define ADD(NAME, BASE) \ - do { \ - assert(state->NAME == NULL); \ - state->NAME = ADD_NEW_EXCEPTION(mod, NAME, BASE); \ - if (state->NAME == NULL) { \ - return -1; \ - } \ - } while (0) - - // An uncaught exception came out of interp_run_string(). - ADD(RunFailedError, PyExc_RuntimeError); -#undef ADD - - return 0; -} - static int _run_script(PyObject *ns, const char *codestr, Py_ssize_t codestrlen, int flags) { @@ -229,7 +179,7 @@ static int _run_in_interpreter(PyInterpreterState *interp, const char *codestr, Py_ssize_t codestrlen, PyObject *shareables, int flags, - PyObject *excwrapper) + PyObject **p_excinfo) { assert(!PyErr_Occurred()); _PyXI_session session = {0}; @@ -237,7 +187,10 @@ _run_in_interpreter(PyInterpreterState *interp, // Prep and switch interpreters. if (_PyXI_Enter(&session, interp, shareables) < 0) { assert(!PyErr_Occurred()); - _PyXI_ApplyExceptionInfo(session.exc, excwrapper); + PyObject *excinfo = _PyXI_ApplyError(session.error); + if (excinfo != NULL) { + *p_excinfo = excinfo; + } assert(PyErr_Occurred()); return -1; } @@ -251,7 +204,10 @@ _run_in_interpreter(PyInterpreterState *interp, // Propagate any exception out to the caller. assert(!PyErr_Occurred()); if (res < 0) { - _PyXI_ApplyCapturedException(&session, excwrapper); + PyObject *excinfo = _PyXI_ApplyCapturedException(&session); + if (excinfo != NULL) { + *p_excinfo = excinfo; + } } else { assert(!_PyXI_HasCapturedException(&session)); @@ -521,7 +477,8 @@ convert_code_arg(PyObject *arg, const char *fname, const char *displayname, static int _interp_exec(PyObject *self, - PyObject *id_arg, PyObject *code_arg, PyObject *shared_arg) + PyObject *id_arg, PyObject *code_arg, PyObject *shared_arg, + PyObject **p_excinfo) { // Look up the interpreter. PyInterpreterState *interp = PyInterpreterID_LookUp(id_arg); @@ -540,10 +497,8 @@ _interp_exec(PyObject *self, } // Run the code in the interpreter. - module_state *state = get_module_state(self); - assert(state != NULL); int res = _run_in_interpreter(interp, codestr, codestrlen, - shared_arg, flags, state->RunFailedError); + shared_arg, flags, p_excinfo); Py_XDECREF(bytes_obj); if (res < 0) { return -1; @@ -577,10 +532,12 @@ interp_exec(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } - int res = _interp_exec(self, id, code, shared); + PyObject *excinfo = NULL; + int res = _interp_exec(self, id, code, shared, &excinfo); Py_DECREF(code); if (res < 0) { - return NULL; + assert((excinfo == NULL) != (PyErr_Occurred() == NULL)); + return excinfo; } Py_RETURN_NONE; } @@ -620,10 +577,12 @@ interp_run_string(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } - int res = _interp_exec(self, id, script, shared); + PyObject *excinfo = NULL; + int res = _interp_exec(self, id, script, shared, &excinfo); Py_DECREF(script); if (res < 0) { - return NULL; + assert((excinfo == NULL) != (PyErr_Occurred() == NULL)); + return excinfo; } Py_RETURN_NONE; } @@ -654,10 +613,12 @@ interp_run_func(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } - int res = _interp_exec(self, id, (PyObject *)code, shared); + PyObject *excinfo = NULL; + int res = _interp_exec(self, id, (PyObject *)code, shared, &excinfo); Py_DECREF(code); if (res < 0) { - return NULL; + assert((excinfo == NULL) != (PyErr_Occurred() == NULL)); + return excinfo; } Py_RETURN_NONE; } @@ -759,11 +720,6 @@ The 'interpreters' module provides a more convenient interface."); static int module_exec(PyObject *mod) { - /* Add exception types */ - if (exceptions_init(mod) != 0) { - goto error; - } - // PyInterpreterID if (PyModule_AddType(mod, &PyInterpreterID_Type) < 0) { goto error; diff --git a/Modules/binascii.c b/Modules/binascii.c index 17970aa5e9456c..86493241a1fb7e 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -770,12 +770,20 @@ binascii_crc32_impl(PyObject *module, Py_buffer *data, unsigned int crc) Py_BEGIN_ALLOW_THREADS /* Avoid truncation of length for very large buffers. crc32() takes - length as an unsigned int, which may be narrower than Py_ssize_t. */ - while ((size_t)len > UINT_MAX) { - crc = crc32(crc, buf, UINT_MAX); - buf += (size_t) UINT_MAX; - len -= (size_t) UINT_MAX; + length as an unsigned int, which may be narrower than Py_ssize_t. + We further limit size due to bugs in Apple's macOS zlib. + See https://github.com/python/cpython/issues/105967 + */ +#define ZLIB_CRC_CHUNK_SIZE 0x40000000 +#if ZLIB_CRC_CHUNK_SIZE > INT_MAX +# error "unsupported less than 32-bit platform?" +#endif + while ((size_t)len > ZLIB_CRC_CHUNK_SIZE) { + crc = crc32(crc, buf, ZLIB_CRC_CHUNK_SIZE); + buf += (size_t) ZLIB_CRC_CHUNK_SIZE; + len -= (size_t) ZLIB_CRC_CHUNK_SIZE; } +#undef ZLIB_CRC_CHUNK_SIZE crc = crc32(crc, buf, (unsigned int)len); Py_END_ALLOW_THREADS } else { diff --git a/Modules/clinic/_functoolsmodule.c.h b/Modules/clinic/_functoolsmodule.c.h index 11fe0439c08a5e..e98984dc4d3a09 100644 --- a/Modules/clinic/_functoolsmodule.c.h +++ b/Modules/clinic/_functoolsmodule.c.h @@ -6,6 +6,7 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(_functools_cmp_to_key__doc__, @@ -113,4 +114,4 @@ _functools__lru_cache_wrapper_cache_clear(PyObject *self, PyObject *Py_UNUSED(ig return return_value; } -/*[clinic end generated code: output=5e3207fa0d28cdb1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=755265bb6d5ea751 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_randommodule.c.h b/Modules/clinic/_randommodule.c.h index 757e49e23cacfb..6193acac67e7ac 100644 --- a/Modules/clinic/_randommodule.c.h +++ b/Modules/clinic/_randommodule.c.h @@ -2,6 +2,7 @@ preserve [clinic start generated code]*/ +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_CheckPositional() PyDoc_STRVAR(_random_Random_random__doc__, @@ -19,7 +20,13 @@ _random_Random_random_impl(RandomObject *self); static PyObject * _random_Random_random(RandomObject *self, PyObject *Py_UNUSED(ignored)) { - return _random_Random_random_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _random_Random_random_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_random_Random_seed__doc__, @@ -51,7 +58,9 @@ _random_Random_seed(RandomObject *self, PyObject *const *args, Py_ssize_t nargs) } n = args[0]; skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _random_Random_seed_impl(self, n); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -72,7 +81,13 @@ _random_Random_getstate_impl(RandomObject *self); static PyObject * _random_Random_getstate(RandomObject *self, PyObject *Py_UNUSED(ignored)) { - return _random_Random_getstate_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _random_Random_getstate_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_random_Random_setstate__doc__, @@ -84,6 +99,21 @@ PyDoc_STRVAR(_random_Random_setstate__doc__, #define _RANDOM_RANDOM_SETSTATE_METHODDEF \ {"setstate", (PyCFunction)_random_Random_setstate, METH_O, _random_Random_setstate__doc__}, +static PyObject * +_random_Random_setstate_impl(RandomObject *self, PyObject *state); + +static PyObject * +_random_Random_setstate(RandomObject *self, PyObject *state) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _random_Random_setstate_impl(self, state); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(_random_Random_getrandbits__doc__, "getrandbits($self, k, /)\n" "--\n" @@ -106,9 +136,11 @@ _random_Random_getrandbits(RandomObject *self, PyObject *arg) if (k == -1 && PyErr_Occurred()) { goto exit; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = _random_Random_getrandbits_impl(self, k); + Py_END_CRITICAL_SECTION(); exit: return return_value; } -/*[clinic end generated code: output=5c800a28c2d7b9d1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=bf49ece1d341b1b6 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_ssl.c.h b/Modules/clinic/_ssl.c.h index 88401b0490a1bb..19c0f619b92f45 100644 --- a/Modules/clinic/_ssl.c.h +++ b/Modules/clinic/_ssl.c.h @@ -1014,6 +1014,141 @@ _ssl__SSLContext_get_ca_certs(PySSLContext *self, PyObject *const *args, Py_ssiz return return_value; } +PyDoc_STRVAR(_ssl__SSLContext_set_psk_client_callback__doc__, +"set_psk_client_callback($self, /, callback)\n" +"--\n" +"\n"); + +#define _SSL__SSLCONTEXT_SET_PSK_CLIENT_CALLBACK_METHODDEF \ + {"set_psk_client_callback", _PyCFunction_CAST(_ssl__SSLContext_set_psk_client_callback), METH_FASTCALL|METH_KEYWORDS, _ssl__SSLContext_set_psk_client_callback__doc__}, + +static PyObject * +_ssl__SSLContext_set_psk_client_callback_impl(PySSLContext *self, + PyObject *callback); + +static PyObject * +_ssl__SSLContext_set_psk_client_callback(PySSLContext *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(callback), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"callback", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "set_psk_client_callback", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *callback; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + callback = args[0]; + return_value = _ssl__SSLContext_set_psk_client_callback_impl(self, callback); + +exit: + return return_value; +} + +PyDoc_STRVAR(_ssl__SSLContext_set_psk_server_callback__doc__, +"set_psk_server_callback($self, /, callback, identity_hint=None)\n" +"--\n" +"\n"); + +#define _SSL__SSLCONTEXT_SET_PSK_SERVER_CALLBACK_METHODDEF \ + {"set_psk_server_callback", _PyCFunction_CAST(_ssl__SSLContext_set_psk_server_callback), METH_FASTCALL|METH_KEYWORDS, _ssl__SSLContext_set_psk_server_callback__doc__}, + +static PyObject * +_ssl__SSLContext_set_psk_server_callback_impl(PySSLContext *self, + PyObject *callback, + const char *identity_hint); + +static PyObject * +_ssl__SSLContext_set_psk_server_callback(PySSLContext *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(callback), &_Py_ID(identity_hint), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"callback", "identity_hint", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "set_psk_server_callback", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *callback; + const char *identity_hint = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + if (!args) { + goto exit; + } + callback = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[1] == Py_None) { + identity_hint = NULL; + } + else if (PyUnicode_Check(args[1])) { + Py_ssize_t identity_hint_length; + identity_hint = PyUnicode_AsUTF8AndSize(args[1], &identity_hint_length); + if (identity_hint == NULL) { + goto exit; + } + if (strlen(identity_hint) != (size_t)identity_hint_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } + } + else { + _PyArg_BadArgument("set_psk_server_callback", "argument 'identity_hint'", "str or None", args[1]); + goto exit; + } +skip_optional_pos: + return_value = _ssl__SSLContext_set_psk_server_callback_impl(self, callback, identity_hint); + +exit: + return return_value; +} + static PyObject * _ssl_MemoryBIO_impl(PyTypeObject *type); @@ -1527,4 +1662,4 @@ _ssl_enum_crls(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje #ifndef _SSL_ENUM_CRLS_METHODDEF #define _SSL_ENUM_CRLS_METHODDEF #endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */ -/*[clinic end generated code: output=aa6b0a898b6077fe input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6342ea0062ab16c7 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_struct.c.h b/Modules/clinic/_struct.c.h index e5118fbdb3b9d3..1a07532bdd75ad 100644 --- a/Modules/clinic/_struct.c.h +++ b/Modules/clinic/_struct.c.h @@ -9,7 +9,7 @@ preserve #include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() -PyDoc_STRVAR(Struct__doc__, +PyDoc_STRVAR(Struct___init____doc__, "Struct(format)\n" "--\n" "\n" @@ -20,13 +20,13 @@ PyDoc_STRVAR(Struct__doc__, "\n" "See help(struct) for more on format strings."); -static PyObject * -Struct_impl(PyTypeObject *type, PyObject *format); +static int +Struct___init___impl(PyStructObject *self, PyObject *format); -static PyObject * -Struct(PyTypeObject *type, PyObject *args, PyObject *kwargs) +static int +Struct___init__(PyObject *self, PyObject *args, PyObject *kwargs) { - PyObject *return_value = NULL; + int return_value = -1; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) #define NUM_KEYWORDS 1 @@ -62,7 +62,7 @@ Struct(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto exit; } format = fastargs[0]; - return_value = Struct_impl(type, format); + return_value = Struct___init___impl((PyStructObject *)self, format); exit: return return_value; @@ -436,4 +436,4 @@ iter_unpack(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } -/*[clinic end generated code: output=6a20e87f9b298b14 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=67bd299e5d72fee0 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_weakref.c.h b/Modules/clinic/_weakref.c.h index 8d7bc5dc936610..550b6c4d71a015 100644 --- a/Modules/clinic/_weakref.c.h +++ b/Modules/clinic/_weakref.c.h @@ -2,6 +2,7 @@ preserve [clinic start generated code]*/ +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_CheckPositional() PyDoc_STRVAR(_weakref_getweakrefcount__doc__, @@ -22,7 +23,9 @@ _weakref_getweakrefcount(PyObject *module, PyObject *object) PyObject *return_value = NULL; Py_ssize_t _return_value; + Py_BEGIN_CRITICAL_SECTION(object); _return_value = _weakref_getweakrefcount_impl(module, object); + Py_END_CRITICAL_SECTION(); if ((_return_value == -1) && PyErr_Occurred()) { goto exit; } @@ -76,6 +79,21 @@ PyDoc_STRVAR(_weakref_getweakrefs__doc__, #define _WEAKREF_GETWEAKREFS_METHODDEF \ {"getweakrefs", (PyCFunction)_weakref_getweakrefs, METH_O, _weakref_getweakrefs__doc__}, +static PyObject * +_weakref_getweakrefs_impl(PyObject *module, PyObject *object); + +static PyObject * +_weakref_getweakrefs(PyObject *module, PyObject *object) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(object); + return_value = _weakref_getweakrefs_impl(module, object); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(_weakref_proxy__doc__, "proxy($module, object, callback=None, /)\n" "--\n" @@ -112,4 +130,4 @@ _weakref_proxy(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=60f59adc1dc9eab8 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d5d30707212a9870 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 9c54935bafa617..9d6cd337f4a2f4 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -5997,8 +5997,6 @@ os_symlink(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * #endif /* defined(HAVE_SYMLINK) */ -#if defined(HAVE_TIMES) - PyDoc_STRVAR(os_times__doc__, "times($module, /)\n" "--\n" @@ -6021,8 +6019,6 @@ os_times(PyObject *module, PyObject *Py_UNUSED(ignored)) return os_times_impl(module); } -#endif /* defined(HAVE_TIMES) */ - #if defined(HAVE_TIMERFD_CREATE) PyDoc_STRVAR(os_timerfd_create__doc__, @@ -11760,6 +11756,28 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #endif /* (defined(WIFEXITED) || defined(MS_WINDOWS)) */ +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(os__supports_virtual_terminal__doc__, +"_supports_virtual_terminal($module, /)\n" +"--\n" +"\n" +"Checks if virtual terminal is supported in windows"); + +#define OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF \ + {"_supports_virtual_terminal", (PyCFunction)os__supports_virtual_terminal, METH_NOARGS, os__supports_virtual_terminal__doc__}, + +static PyObject * +os__supports_virtual_terminal_impl(PyObject *module); + +static PyObject * +os__supports_virtual_terminal(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return os__supports_virtual_terminal_impl(module); +} + +#endif /* defined(MS_WINDOWS) */ + #ifndef OS_TTYNAME_METHODDEF #define OS_TTYNAME_METHODDEF #endif /* !defined(OS_TTYNAME_METHODDEF) */ @@ -12116,10 +12134,6 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #define OS_SYMLINK_METHODDEF #endif /* !defined(OS_SYMLINK_METHODDEF) */ -#ifndef OS_TIMES_METHODDEF - #define OS_TIMES_METHODDEF -#endif /* !defined(OS_TIMES_METHODDEF) */ - #ifndef OS_TIMERFD_CREATE_METHODDEF #define OS_TIMERFD_CREATE_METHODDEF #endif /* !defined(OS_TIMERFD_CREATE_METHODDEF) */ @@ -12403,4 +12417,8 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF #endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */ -/*[clinic end generated code: output=0f216bf44ea358f9 input=a9049054013a1b77]*/ + +#ifndef OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF + #define OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF +#endif /* !defined(OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF) */ +/*[clinic end generated code: output=ff0ec3371de19904 input=a9049054013a1b77]*/ diff --git a/Modules/errnomodule.c b/Modules/errnomodule.c index 1e2c55a1a26b4b..8287edbfb47f6c 100644 --- a/Modules/errnomodule.c +++ b/Modules/errnomodule.c @@ -1,10 +1,10 @@ /* Errno module */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED #define Py_LIMITED_API 0x030c0000 #endif diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 568e02a4210a2b..8233fc56159b60 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -491,15 +491,16 @@ subtract_refs(PyGC_Head *containers) PyObject *op = FROM_GC(gc); traverse = Py_TYPE(op)->tp_traverse; (void) traverse(op, - (visitproc)visit_decref, + visit_decref, op); } } /* A traversal callback for move_unreachable. */ static int -visit_reachable(PyObject *op, PyGC_Head *reachable) +visit_reachable(PyObject *op, void *arg) { + PyGC_Head *reachable = arg; OBJECT_STAT_INC(object_visits); if (!_PyObject_IS_GC(op)) { return 0; @@ -603,7 +604,7 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable) // NOTE: visit_reachable may change gc->_gc_next when // young->_gc_prev == gc. Don't do gc = GC_NEXT(gc) before! (void) traverse(op, - (visitproc)visit_reachable, + visit_reachable, (void *)young); // relink gc_prev to prev element. _PyGCHead_SET_PREV(gc, prev); @@ -726,8 +727,9 @@ clear_unreachable_mask(PyGC_Head *unreachable) /* A traversal callback for move_legacy_finalizer_reachable. */ static int -visit_move(PyObject *op, PyGC_Head *tolist) +visit_move(PyObject *op, void *arg) { + PyGC_Head *tolist = arg; OBJECT_STAT_INC(object_visits); if (_PyObject_IS_GC(op)) { PyGC_Head *gc = AS_GC(op); @@ -751,7 +753,7 @@ move_legacy_finalizer_reachable(PyGC_Head *finalizers) /* Note that the finalizers list may grow during this. */ traverse = Py_TYPE(FROM_GC(gc))->tp_traverse; (void) traverse(FROM_GC(gc), - (visitproc)visit_move, + visit_move, (void *)finalizers); } } @@ -1684,8 +1686,9 @@ gc_get_count_impl(PyObject *module) } static int -referrersvisit(PyObject* obj, PyObject *objs) +referrersvisit(PyObject* obj, void *arg) { + PyObject *objs = arg; Py_ssize_t i; for (i = 0; i < PyTuple_GET_SIZE(objs); i++) if (PyTuple_GET_ITEM(objs, i) == obj) @@ -1704,7 +1707,7 @@ gc_referrers_for(PyObject *objs, PyGC_Head *list, PyObject *resultlist) traverse = Py_TYPE(obj)->tp_traverse; if (obj == objs || obj == resultlist) continue; - if (traverse(obj, (visitproc)referrersvisit, objs)) { + if (traverse(obj, referrersvisit, objs)) { if (PyList_Append(resultlist, obj) < 0) return 0; /* error */ } @@ -1740,8 +1743,9 @@ gc_get_referrers(PyObject *self, PyObject *args) /* Append obj to list; return true if error (out of memory), false if OK. */ static int -referentsvisit(PyObject *obj, PyObject *list) +referentsvisit(PyObject *obj, void *arg) { + PyObject *list = arg; return PyList_Append(list, obj) < 0; } @@ -1770,7 +1774,7 @@ gc_get_referents(PyObject *self, PyObject *args) traverse = Py_TYPE(obj)->tp_traverse; if (! traverse) continue; - if (traverse(obj, (visitproc)referentsvisit, result)) { + if (traverse(obj, referentsvisit, result)) { Py_DECREF(result); return NULL; } diff --git a/Modules/getpath.c b/Modules/getpath.c index 50ba090ee548ca..6c1078b8914522 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -17,6 +17,7 @@ #endif #ifdef __APPLE__ +# include # include #endif @@ -768,16 +769,11 @@ library_to_dict(PyObject *dict, const char *key) which is in the framework, not relative to the executable, which may be outside of the framework. Except when we're in the build directory... */ - NSSymbol symbol = NSLookupAndBindSymbol("_Py_Initialize"); - if (symbol != NULL) { - NSModule pythonModule = NSModuleForSymbol(symbol); - if (pythonModule != NULL) { - /* Use dylib functions to find out where the framework was loaded from */ - const char *path = NSLibraryNameForModule(pythonModule); - if (path) { - strncpy(modPath, path, MAXPATHLEN); - modPathInitialized = 1; - } + Dl_info pythonInfo; + if (dladdr(&Py_Initialize, &pythonInfo)) { + if (pythonInfo.dli_fname) { + strncpy(modPath, pythonInfo.dli_fname, MAXPATHLEN); + modPathInitialized = 1; } } } diff --git a/Modules/hashlib.h b/Modules/hashlib.h index 25b2aaadfb2868..7105e68af7b806 100644 --- a/Modules/hashlib.h +++ b/Modules/hashlib.h @@ -58,7 +58,7 @@ PyMutex_Unlock(&(obj)->mutex); \ } -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED #define HASHLIB_INIT_MUTEX(obj) \ do { \ (obj)->mutex = (PyMutex){0}; \ diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 4ed34aa5bde827..ab99fa4d873bf5 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -330,21 +330,30 @@ pairwise_next(pairwiseobject *po) return NULL; } if (old == NULL) { - po->old = old = (*Py_TYPE(it)->tp_iternext)(it); + old = (*Py_TYPE(it)->tp_iternext)(it); + Py_XSETREF(po->old, old); if (old == NULL) { Py_CLEAR(po->it); return NULL; } + it = po->it; + if (it == NULL) { + Py_CLEAR(po->old); + return NULL; + } } + Py_INCREF(old); new = (*Py_TYPE(it)->tp_iternext)(it); if (new == NULL) { Py_CLEAR(po->it); Py_CLEAR(po->old); + Py_DECREF(old); return NULL; } /* Future optimization: Reuse the result tuple as we do in enumerate() */ result = PyTuple_Pack(2, old, new); - Py_SETREF(po->old, new); + Py_XSETREF(po->old, new); + Py_DECREF(old); return result; } diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index d99b5335b6989a..ddbb4cd43babfc 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1030,6 +1030,10 @@ typedef struct { PyObject *struct_rusage; #endif PyObject *st_mode; +#ifndef MS_WINDOWS + // times() clock frequency in hertz; used by os.times() + long ticks_per_second; +#endif } _posixstate; @@ -9986,8 +9990,6 @@ os_symlink_impl(PyObject *module, path_t *src, path_t *dst, #endif /* HAVE_SYMLINK */ - - static PyStructSequence_Field times_result_fields[] = { {"user", "user time"}, {"system", "system time"}, @@ -10013,12 +10015,6 @@ static PyStructSequence_Desc times_result_desc = { 5 }; -#ifdef MS_WINDOWS -#define HAVE_TIMES /* mandatory, for the method table */ -#endif - -#ifdef HAVE_TIMES - static PyObject * build_times_result(PyObject *module, double user, double system, double children_user, double children_system, @@ -10064,8 +10060,8 @@ All fields are floating point numbers. static PyObject * os_times_impl(PyObject *module) /*[clinic end generated code: output=35f640503557d32a input=2bf9df3d6ab2e48b]*/ -#ifdef MS_WINDOWS { +#ifdef MS_WINDOWS FILETIME create, exit, kernel, user; HANDLE hProc; hProc = GetCurrentProcess(); @@ -10083,28 +10079,26 @@ os_times_impl(PyObject *module) (double)0, (double)0, (double)0); -} #else /* MS_WINDOWS */ -{ - struct tms t; - clock_t c; + _posixstate *state = get_posix_state(module); + long ticks_per_second = state->ticks_per_second; + + struct tms process; + clock_t elapsed; errno = 0; - c = times(&t); - if (c == (clock_t) -1) { + elapsed = times(&process); + if (elapsed == (clock_t) -1) { return posix_error(); } - assert(_PyRuntime.time.ticks_per_second_initialized); -#define ticks_per_second _PyRuntime.time.ticks_per_second + return build_times_result(module, - (double)t.tms_utime / ticks_per_second, - (double)t.tms_stime / ticks_per_second, - (double)t.tms_cutime / ticks_per_second, - (double)t.tms_cstime / ticks_per_second, - (double)c / ticks_per_second); -#undef ticks_per_second -} + (double)process.tms_utime / ticks_per_second, + (double)process.tms_stime / ticks_per_second, + (double)process.tms_cutime / ticks_per_second, + (double)process.tms_cstime / ticks_per_second, + (double)elapsed / ticks_per_second); #endif /* MS_WINDOWS */ -#endif /* HAVE_TIMES */ +} #if defined(HAVE_TIMERFD_CREATE) @@ -16079,6 +16073,26 @@ os_waitstatus_to_exitcode_impl(PyObject *module, PyObject *status_obj) } #endif +#if defined(MS_WINDOWS) +/*[clinic input] +os._supports_virtual_terminal + +Checks if virtual terminal is supported in windows +[clinic start generated code]*/ + +static PyObject * +os__supports_virtual_terminal_impl(PyObject *module) +/*[clinic end generated code: output=bd0556a6d9d99fe6 input=0752c98e5d321542]*/ +{ + DWORD mode = 0; + HANDLE handle = GetStdHandle(STD_ERROR_HANDLE); + if (!GetConsoleMode(handle, &mode)) { + Py_RETURN_FALSE; + } + return PyBool_FromLong(mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING); +} +#endif + static PyMethodDef posix_methods[] = { @@ -16283,6 +16297,8 @@ static PyMethodDef posix_methods[] = { OS__PATH_ISFILE_METHODDEF OS__PATH_ISLINK_METHODDEF OS__PATH_EXISTS_METHODDEF + + OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF {NULL, NULL} /* Sentinel */ }; @@ -17279,6 +17295,15 @@ posixmodule_exec(PyObject *m) Py_DECREF(unicode); } +#ifndef MS_WINDOWS + if (_Py_GetTicksPerSecond(&state->ticks_per_second) < 0) { + PyErr_SetString(PyExc_RuntimeError, + "cannot read ticks_per_second"); + return -1; + } + assert(state->ticks_per_second >= 1); +#endif + return PyModule_Add(m, "_have_functions", list); } diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index 21579a80dd7f70..9d95309dbb7aa6 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -240,19 +240,12 @@ string_intern(xmlparseobject *self, const char* str) return result; if (!self->intern) return result; - value = PyDict_GetItemWithError(self->intern, result); - if (!value) { - if (!PyErr_Occurred() && - PyDict_SetItem(self->intern, result, result) == 0) - { - return result; - } - else { - Py_DECREF(result); - return NULL; - } + if (PyDict_GetItemRef(self->intern, result, &value) == 0 && + PyDict_SetItem(self->intern, result, result) == 0) + { + return result; } - Py_INCREF(value); + assert((value != NULL) == !PyErr_Occurred()); Py_DECREF(result); return value; } diff --git a/Modules/readline.c b/Modules/readline.c index fde552d124bc77..e29051c37f8827 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -147,8 +147,19 @@ readline_free(void *m) static PyModuleDef readlinemodule; -#define readlinestate_global ((readlinestate *)PyModule_GetState(PyState_FindModule(&readlinemodule))) - +static inline readlinestate* +get_hook_module_state(void) +{ + PyObject *mod = PyState_FindModule(&readlinemodule); + if (mod == NULL){ + PyErr_Clear(); + return NULL; + } + Py_INCREF(mod); + readlinestate *state = get_readline_state(mod); + Py_DECREF(mod); + return state; +} /* Convert to/from multibyte C strings */ @@ -438,14 +449,15 @@ readline_set_completion_display_matches_hook_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=516e5cb8db75a328 input=4f0bfd5ab0179a26]*/ { + readlinestate *state = get_readline_state(module); PyObject *result = set_hook("completion_display_matches_hook", - &readlinestate_global->completion_display_matches_hook, + &state->completion_display_matches_hook, function); #ifdef HAVE_RL_COMPLETION_DISPLAY_MATCHES_HOOK /* We cannot set this hook globally, since it replaces the default completion display. */ rl_completion_display_matches_hook = - readlinestate_global->completion_display_matches_hook ? + state->completion_display_matches_hook ? #if defined(HAVE_RL_COMPDISP_FUNC_T) (rl_compdisp_func_t *)on_completion_display_matches_hook : 0; #else @@ -472,7 +484,8 @@ static PyObject * readline_set_startup_hook_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=02cd0e0c4fa082ad input=7783b4334b26d16d]*/ { - return set_hook("startup_hook", &readlinestate_global->startup_hook, + readlinestate *state = get_readline_state(module); + return set_hook("startup_hook", &state->startup_hook, function); } @@ -497,7 +510,8 @@ static PyObject * readline_set_pre_input_hook_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=fe1a96505096f464 input=4f3eaeaf7ce1fdbe]*/ { - return set_hook("pre_input_hook", &readlinestate_global->pre_input_hook, + readlinestate *state = get_readline_state(module); + return set_hook("pre_input_hook", &state->pre_input_hook, function); } #endif @@ -530,7 +544,8 @@ static PyObject * readline_get_begidx_impl(PyObject *module) /*[clinic end generated code: output=362616ee8ed1b2b1 input=e083b81c8eb4bac3]*/ { - return Py_NewRef(readlinestate_global->begidx); + readlinestate *state = get_readline_state(module); + return Py_NewRef(state->begidx); } /* Get the ending index for the scope of the tab-completion */ @@ -545,7 +560,8 @@ static PyObject * readline_get_endidx_impl(PyObject *module) /*[clinic end generated code: output=7f763350b12d7517 input=d4c7e34a625fd770]*/ { - return Py_NewRef(readlinestate_global->endidx); + readlinestate *state = get_readline_state(module); + return Py_NewRef(state->endidx); } /* Set the tab-completion word-delimiters that readline uses */ @@ -576,6 +592,13 @@ readline_set_completer_delims(PyObject *module, PyObject *string) if (break_chars) { free(completer_word_break_characters); completer_word_break_characters = break_chars; +#ifdef WITH_EDITLINE + rl_basic_word_break_characters = break_chars; +#else + if (using_libedit_emulation) { + rl_basic_word_break_characters = break_chars; + } +#endif rl_completer_word_break_characters = break_chars; Py_RETURN_NONE; } @@ -772,7 +795,8 @@ static PyObject * readline_set_completer_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=171a2a60f81d3204 input=51e81e13118eb877]*/ { - return set_hook("completer", &readlinestate_global->completer, function); + readlinestate *state = get_readline_state(module); + return set_hook("completer", &state->completer, function); } /*[clinic input] @@ -785,10 +809,11 @@ static PyObject * readline_get_completer_impl(PyObject *module) /*[clinic end generated code: output=6e6bbd8226d14475 input=6457522e56d70d13]*/ { - if (readlinestate_global->completer == NULL) { + readlinestate *state = get_readline_state(module); + if (state->completer == NULL) { Py_RETURN_NONE; } - return Py_NewRef(readlinestate_global->completer); + return Py_NewRef(state->completer); } /* Private function to get current length of history. XXX It may be @@ -1018,15 +1043,18 @@ on_hook(PyObject *func) static int #if defined(_RL_FUNCTION_TYPEDEF) on_startup_hook(void) -#elif defined(WITH_APPLE_EDITLINE) -on_startup_hook(const char *Py_UNUSED(text), int Py_UNUSED(state)) #else -on_startup_hook(void) +on_startup_hook(const char *Py_UNUSED(text), int Py_UNUSED(state)) #endif { int r; PyGILState_STATE gilstate = PyGILState_Ensure(); - r = on_hook(readlinestate_global->startup_hook); + readlinestate *state = get_hook_module_state(); + if (state == NULL) { + PyGILState_Release(gilstate); + return -1; + } + r = on_hook(state->startup_hook); PyGILState_Release(gilstate); return r; } @@ -1035,15 +1063,18 @@ on_startup_hook(void) static int #if defined(_RL_FUNCTION_TYPEDEF) on_pre_input_hook(void) -#elif defined(WITH_APPLE_EDITLINE) -on_pre_input_hook(const char *Py_UNUSED(text), int Py_UNUSED(state)) #else -on_pre_input_hook(void) +on_pre_input_hook(const char *Py_UNUSED(text), int Py_UNUSED(state)) #endif { int r; PyGILState_STATE gilstate = PyGILState_Ensure(); - r = on_hook(readlinestate_global->pre_input_hook); + readlinestate *state = get_hook_module_state(); + if (state == NULL) { + PyGILState_Release(gilstate); + return -1; + } + r = on_hook(state->pre_input_hook); PyGILState_Release(gilstate); return r; } @@ -1060,6 +1091,11 @@ on_completion_display_matches_hook(char **matches, int i; PyObject *sub, *m=NULL, *s=NULL, *r=NULL; PyGILState_STATE gilstate = PyGILState_Ensure(); + readlinestate *state = get_hook_module_state(); + if (state == NULL) { + PyGILState_Release(gilstate); + return; + } m = PyList_New(num_matches); if (m == NULL) goto error; @@ -1070,7 +1106,7 @@ on_completion_display_matches_hook(char **matches, PyList_SET_ITEM(m, i, s); } sub = decode(matches[0]); - r = PyObject_CallFunction(readlinestate_global->completion_display_matches_hook, + r = PyObject_CallFunction(state->completion_display_matches_hook, "NNi", sub, m, max_length); m=NULL; @@ -1118,12 +1154,17 @@ static char * on_completion(const char *text, int state) { char *result = NULL; - if (readlinestate_global->completer != NULL) { + PyGILState_STATE gilstate = PyGILState_Ensure(); + readlinestate *module_state = get_hook_module_state(); + if (module_state == NULL) { + PyGILState_Release(gilstate); + return NULL; + } + if (module_state->completer != NULL) { PyObject *r = NULL, *t; - PyGILState_STATE gilstate = PyGILState_Ensure(); rl_attempted_completion_over = 1; t = decode(text); - r = PyObject_CallFunction(readlinestate_global->completer, "Ni", t, state); + r = PyObject_CallFunction(module_state->completer, "Ni", t, state); if (r == NULL) goto error; if (r == Py_None) { @@ -1145,6 +1186,7 @@ on_completion(const char *text, int state) PyGILState_Release(gilstate); return result; } + PyGILState_Release(gilstate); return result; } @@ -1160,6 +1202,7 @@ flex_complete(const char *text, int start, int end) size_t start_size, end_size; wchar_t *s; PyGILState_STATE gilstate = PyGILState_Ensure(); + readlinestate *state = get_hook_module_state(); #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER rl_completion_append_character ='\0'; #endif @@ -1187,10 +1230,12 @@ flex_complete(const char *text, int start, int end) end = start + (int)end_size; done: - Py_XDECREF(readlinestate_global->begidx); - Py_XDECREF(readlinestate_global->endidx); - readlinestate_global->begidx = PyLong_FromLong((long) start); - readlinestate_global->endidx = PyLong_FromLong((long) end); + if (state) { + Py_XDECREF(state->begidx); + Py_XDECREF(state->endidx); + state->begidx = PyLong_FromLong((long) start); + state->endidx = PyLong_FromLong((long) end); + } result = completion_matches((char *)text, *on_completion); PyGILState_Release(gilstate); return result; @@ -1267,6 +1312,15 @@ setup_readline(readlinestate *mod_state) completer_word_break_characters = strdup(" \t\n`~!@#$%^&*()-=+[{]}\\|;:'\",<>/?"); /* All nonalphanums except '.' */ +#ifdef WITH_EDITLINE + // libedit uses rl_basic_word_break_characters instead of + // rl_completer_word_break_characters as complete delimiter + rl_basic_word_break_characters = completer_word_break_characters; +#else + if (using_libedit_emulation) { + rl_basic_word_break_characters = completer_word_break_characters; + } +#endif rl_completer_word_break_characters = completer_word_break_characters; mod_state->begidx = PyLong_FromLong(0L); @@ -1480,6 +1534,7 @@ static struct PyModuleDef readlinemodule = { PyMODINIT_FUNC PyInit_readline(void) { + const char *backend = "readline"; PyObject *m; readlinestate *mod_state; @@ -1487,8 +1542,10 @@ PyInit_readline(void) using_libedit_emulation = 1; } - if (using_libedit_emulation) + if (using_libedit_emulation) { readlinemodule.m_doc = doc_module_le; + backend = "editline"; + } m = PyModule_Create(&readlinemodule); @@ -1510,13 +1567,22 @@ PyInit_readline(void) goto error; } + if (PyModule_AddStringConstant(m, "backend", backend) < 0) { + goto error; + } + mod_state = (readlinestate *) PyModule_GetState(m); + if (mod_state == NULL){ + goto error; + } PyOS_ReadlineFunctionPointer = call_readline; if (setup_readline(mod_state) < 0) { PyErr_NoMemory(); goto error; } - + if (PyErr_Occurred()){ + goto error; + } return m; error: diff --git a/Modules/resource.c b/Modules/resource.c index 87c44607df1c4c..a4b8f648c329e3 100644 --- a/Modules/resource.c +++ b/Modules/resource.c @@ -1,8 +1,8 @@ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.13 for PySys_Audit() #define Py_LIMITED_API 0x030d0000 #endif diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 9ac2001c0132d3..0a0e0e78656f76 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -7071,17 +7071,23 @@ _socket_socket_if_nametoindex_impl(PySocketSockObject *self, PyObject *oname) static PyObject * socket_if_indextoname(PyObject *self, PyObject *arg) { + unsigned long index_long = PyLong_AsUnsignedLong(arg); + if (index_long == (unsigned long) -1 && PyErr_Occurred()) { + return NULL; + } + #ifdef MS_WINDOWS - NET_IFINDEX index; + NET_IFINDEX index = (NET_IFINDEX)index_long; #else - unsigned long index; + unsigned int index = (unsigned int)index_long; #endif - char name[IF_NAMESIZE + 1]; - index = PyLong_AsUnsignedLong(arg); - if (index == (unsigned long) -1) + if ((unsigned long)index != index_long) { + PyErr_SetString(PyExc_OverflowError, "index is too large"); return NULL; + } + char name[IF_NAMESIZE + 1]; if (if_indextoname(index, name) == NULL) { PyErr_SetFromErrno(PyExc_OSError); return NULL; diff --git a/Modules/termios.c b/Modules/termios.c index 9fc2673ce0e788..1d97a3a2757966 100644 --- a/Modules/termios.c +++ b/Modules/termios.c @@ -702,6 +702,9 @@ static struct constant { #ifdef IMAXBEL {"IMAXBEL", IMAXBEL}, #endif +#ifdef IUTF8 + {"IUTF8", IUTF8}, +#endif /* struct termios.c_oflag constants */ {"OPOST", OPOST}, @@ -726,6 +729,12 @@ static struct constant { #ifdef OFDEL {"OFDEL", OFDEL}, #endif +#ifdef OXTABS + {"OXTABS", OXTABS}, +#endif +#ifdef ONOEOT + {"ONOEOT", ONOEOT}, +#endif #ifdef NLDLY {"NLDLY", NLDLY}, #endif @@ -752,6 +761,12 @@ static struct constant { #ifdef NL1 {"NL1", NL1}, #endif +#ifdef NL2 + {"NL2", NL2}, +#endif +#ifdef NL3 + {"NL3", NL3}, +#endif #ifdef CR0 {"CR0", CR0}, #endif @@ -799,6 +814,9 @@ static struct constant { #endif /* struct termios.c_cflag constants */ +#ifdef CIGNORE + {"CIGNORE", CIGNORE}, +#endif {"CSIZE", CSIZE}, {"CSTOPB", CSTOPB}, {"CREAD", CREAD}, @@ -813,6 +831,22 @@ static struct constant { {"CRTSCTS", (long)CRTSCTS}, #endif +#ifdef CRTS_IFLOW + {"CRTS_IFLOW", CRTS_IFLOW}, +#endif +#ifdef CDTR_IFLOW + {"CDTR_IFLOW", CDTR_IFLOW}, +#endif +#ifdef CDSR_OFLOW + {"CDSR_OFLOW", CDSR_OFLOW}, +#endif +#ifdef CCAR_OFLOW + {"CCAR_OFLOW", CCAR_OFLOW}, +#endif +#ifdef MDMBUF + {"MDMBUF", MDMBUF}, +#endif + /* struct termios.c_cflag-related values (character size) */ {"CS5", CS5}, {"CS6", CS6}, @@ -820,6 +854,9 @@ static struct constant { {"CS8", CS8}, /* struct termios.c_lflag constants */ +#ifdef ALTWERASE + {"ALTWERASE", ALTWERASE}, +#endif {"ISIG", ISIG}, {"ICANON", ICANON}, #ifdef XCASE @@ -840,6 +877,9 @@ static struct constant { #endif #ifdef FLUSHO {"FLUSHO", FLUSHO}, +#endif +#ifdef NOKERNINFO + {"NOKERNINFO", NOKERNINFO}, #endif {"NOFLSH", NOFLSH}, {"TOSTOP", TOSTOP}, @@ -847,6 +887,9 @@ static struct constant { {"PENDIN", PENDIN}, #endif {"IEXTEN", IEXTEN}, +#ifdef EXTPROC + {"EXTPROC", EXTPROC}, +#endif /* indexes into the control chars array returned by tcgetattr() */ {"VINTR", VINTR}, @@ -855,6 +898,9 @@ static struct constant { {"VKILL", VKILL}, {"VEOF", VEOF}, {"VTIME", VTIME}, +#ifdef VSTATUS + {"VSTATUS", VSTATUS}, +#endif {"VMIN", VMIN}, #ifdef VSWTC /* The #defines above ensure that if either is defined, both are, @@ -865,6 +911,9 @@ static struct constant { {"VSTART", VSTART}, {"VSTOP", VSTOP}, {"VSUSP", VSUSP}, +#ifdef VDSUSP + {"VDSUSP", VREPRINT}, +#endif {"VEOL", VEOL}, #ifdef VREPRINT {"VREPRINT", VREPRINT}, @@ -883,6 +932,18 @@ static struct constant { #endif +#ifdef B7200 + {"B7200", B7200}, +#endif +#ifdef B14400 + {"B14400", B14400}, +#endif +#ifdef B28800 + {"B28800", B28800}, +#endif +#ifdef B76800 + {"B76800", B76800}, +#endif #ifdef B460800 {"B460800", B460800}, #endif diff --git a/Modules/timemodule.c b/Modules/timemodule.c index bc3901e0d7a621..b3fe175d9b184a 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -69,62 +69,20 @@ module time /*[clinic end generated code: output=da39a3ee5e6b4b0d input=a668a08771581f36]*/ -#if defined(HAVE_TIMES) || defined(HAVE_CLOCK) -static int -check_ticks_per_second(long tps, const char *context) -{ - /* Effectively, check that _PyTime_MulDiv(t, SEC_TO_NS, ticks_per_second) - cannot overflow. */ - if (tps >= 0 && (_PyTime_t)tps > _PyTime_MAX / SEC_TO_NS) { - PyErr_Format(PyExc_OverflowError, "%s is too large", context); - return -1; - } - return 0; -} -#endif /* HAVE_TIMES || HAVE_CLOCK */ - -#ifdef HAVE_TIMES - -# define ticks_per_second _PyRuntime.time.ticks_per_second - -static void -ensure_ticks_per_second(void) -{ - if (_PyRuntime.time.ticks_per_second_initialized) { - return; - } - _PyRuntime.time.ticks_per_second_initialized = 1; -# if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK) - ticks_per_second = sysconf(_SC_CLK_TCK); - if (ticks_per_second < 1) { - ticks_per_second = -1; - } -# elif defined(HZ) - ticks_per_second = HZ; -# else - ticks_per_second = 60; /* magic fallback value; may be bogus */ -# endif -} - -#endif /* HAVE_TIMES */ - - -PyStatus -_PyTime_Init(void) -{ -#ifdef HAVE_TIMES - ensure_ticks_per_second(); -#endif - return PyStatus_Ok(); -} - - /* Forward declarations */ static int pysleep(_PyTime_t timeout); typedef struct { PyTypeObject *struct_time_type; +#ifdef HAVE_TIMES + // times() clock frequency in hertz + _PyTimeFraction times_base; +#endif +#ifdef HAVE_CLOCK + // clock() frequency in hertz + _PyTimeFraction clock_base; +#endif } time_module_state; static inline time_module_state* @@ -184,7 +142,7 @@ PyDoc_STRVAR(time_ns_doc, \n\ Return the current time in nanoseconds since the Epoch."); -#if defined(HAVE_CLOCK) +#ifdef HAVE_CLOCK #ifndef CLOCKS_PER_SEC # ifdef CLK_TCK @@ -195,15 +153,13 @@ Return the current time in nanoseconds since the Epoch."); #endif static int -_PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) +py_clock(time_module_state *state, _PyTime_t *tp, _Py_clock_info_t *info) { - if (check_ticks_per_second(CLOCKS_PER_SEC, "CLOCKS_PER_SEC") < 0) { - return -1; - } + _PyTimeFraction *base = &state->clock_base; if (info) { info->implementation = "clock()"; - info->resolution = 1.0 / (double)CLOCKS_PER_SEC; + info->resolution = _PyTimeFraction_Resolution(base); info->monotonic = 1; info->adjustable = 0; } @@ -215,7 +171,7 @@ _PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) "or its value cannot be represented"); return -1; } - _PyTime_t ns = _PyTime_MulDiv(ticks, SEC_TO_NS, (_PyTime_t)CLOCKS_PER_SEC); + _PyTime_t ns = _PyTimeFraction_Mul(ticks, base); *tp = _PyTime_FromNanoseconds(ns); return 0; } @@ -1277,8 +1233,38 @@ PyDoc_STRVAR(perf_counter_ns_doc, \n\ Performance counter for benchmarking as nanoseconds."); + +#ifdef HAVE_TIMES static int -_PyTime_GetProcessTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) +process_time_times(time_module_state *state, _PyTime_t *tp, + _Py_clock_info_t *info) +{ + _PyTimeFraction *base = &state->times_base; + + struct tms process; + if (times(&process) == (clock_t)-1) { + return 0; + } + + if (info) { + info->implementation = "times()"; + info->resolution = _PyTimeFraction_Resolution(base); + info->monotonic = 1; + info->adjustable = 0; + } + + _PyTime_t ns; + ns = _PyTimeFraction_Mul(process.tms_utime, base); + ns += _PyTimeFraction_Mul(process.tms_stime, base); + *tp = _PyTime_FromNanoseconds(ns); + return 1; +} +#endif + + +static int +py_process_time(time_module_state *state, _PyTime_t *tp, + _Py_clock_info_t *info) { #if defined(MS_WINDOWS) HANDLE process; @@ -1381,41 +1367,27 @@ _PyTime_GetProcessTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) /* times() */ #ifdef HAVE_TIMES - struct tms t; - - if (times(&t) != (clock_t)-1) { - assert(_PyRuntime.time.ticks_per_second_initialized); - if (check_ticks_per_second(ticks_per_second, "_SC_CLK_TCK") < 0) { - return -1; - } - if (ticks_per_second != -1) { - if (info) { - info->implementation = "times()"; - info->monotonic = 1; - info->adjustable = 0; - info->resolution = 1.0 / (double)ticks_per_second; - } - - _PyTime_t ns; - ns = _PyTime_MulDiv(t.tms_utime, SEC_TO_NS, ticks_per_second); - ns += _PyTime_MulDiv(t.tms_stime, SEC_TO_NS, ticks_per_second); - *tp = _PyTime_FromNanoseconds(ns); - return 0; - } + int res = process_time_times(state, tp, info); + if (res < 0) { + return -1; } + if (res == 1) { + return 0; + } + // times() failed, ignore failure #endif - /* clock */ - /* Currently, Python 3 requires clock() to build: see issue #22624 */ - return _PyTime_GetClockWithInfo(tp, info); + /* clock(). Python 3 requires clock() to build (see gh-66814) */ + return py_clock(state, tp, info); #endif } static PyObject * -time_process_time(PyObject *self, PyObject *unused) +time_process_time(PyObject *module, PyObject *unused) { + time_module_state *state = get_time_state(module); _PyTime_t t; - if (_PyTime_GetProcessTimeWithInfo(&t, NULL) < 0) { + if (py_process_time(state, &t, NULL) < 0) { return NULL; } return _PyFloat_FromPyTime(t); @@ -1427,10 +1399,11 @@ PyDoc_STRVAR(process_time_doc, Process time for profiling: sum of the kernel and user-space CPU time."); static PyObject * -time_process_time_ns(PyObject *self, PyObject *unused) +time_process_time_ns(PyObject *module, PyObject *unused) { + time_module_state *state = get_time_state(module); _PyTime_t t; - if (_PyTime_GetProcessTimeWithInfo(&t, NULL) < 0) { + if (py_process_time(state, &t, NULL) < 0) { return NULL; } return _PyTime_AsNanosecondsObject(t); @@ -1617,7 +1590,7 @@ sum of the kernel and user-space CPU time."); static PyObject * -time_get_clock_info(PyObject *self, PyObject *args) +time_get_clock_info(PyObject *module, PyObject *args) { char *name; _Py_clock_info_t info; @@ -1656,7 +1629,8 @@ time_get_clock_info(PyObject *self, PyObject *args) } } else if (strcmp(name, "process_time") == 0) { - if (_PyTime_GetProcessTimeWithInfo(&t, &info) < 0) { + time_module_state *state = get_time_state(module); + if (py_process_time(state, &t, &info) < 0) { return NULL; } } @@ -2116,6 +2090,28 @@ time_exec(PyObject *module) } #endif +#ifdef HAVE_TIMES + long ticks_per_second; + if (_Py_GetTicksPerSecond(&ticks_per_second) < 0) { + PyErr_SetString(PyExc_RuntimeError, + "cannot read ticks_per_second"); + return -1; + } + if (_PyTimeFraction_Set(&state->times_base, SEC_TO_NS, + ticks_per_second) < 0) { + PyErr_Format(PyExc_OverflowError, "ticks_per_second is too large"); + return -1; + } +#endif + +#ifdef HAVE_CLOCK + if (_PyTimeFraction_Set(&state->clock_base, SEC_TO_NS, + CLOCKS_PER_SEC) < 0) { + PyErr_Format(PyExc_OverflowError, "CLOCKS_PER_SEC is too large"); + return -1; + } +#endif + return 0; } diff --git a/Modules/xxlimited.c b/Modules/xxlimited.c index 21d7e9ba67ad78..19f61216255cfa 100644 --- a/Modules/xxlimited.c +++ b/Modules/xxlimited.c @@ -63,10 +63,10 @@ */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED #define Py_LIMITED_API 0x030c0000 #endif diff --git a/Modules/xxlimited_35.c b/Modules/xxlimited_35.c index 04673ea68cfee5..867820a6cb93fa 100644 --- a/Modules/xxlimited_35.c +++ b/Modules/xxlimited_35.c @@ -6,10 +6,10 @@ */ #ifndef _MSC_VER -#include "pyconfig.h" // Py_NOGIL +#include "pyconfig.h" // Py_GIL_DISABLED #endif -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED #define Py_LIMITED_API 0x03050000 #endif diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c index 9b76afa0e56f76..fe9a6d8d4150ab 100644 --- a/Modules/zlibmodule.c +++ b/Modules/zlibmodule.c @@ -1896,12 +1896,20 @@ zlib_crc32_impl(PyObject *module, Py_buffer *data, unsigned int value) Py_BEGIN_ALLOW_THREADS /* Avoid truncation of length for very large buffers. crc32() takes - length as an unsigned int, which may be narrower than Py_ssize_t. */ - while ((size_t)len > UINT_MAX) { - value = crc32(value, buf, UINT_MAX); - buf += (size_t) UINT_MAX; - len -= (size_t) UINT_MAX; + length as an unsigned int, which may be narrower than Py_ssize_t. + We further limit size due to bugs in Apple's macOS zlib. + See https://github.com/python/cpython/issues/105967. + */ +#define ZLIB_CRC_CHUNK_SIZE 0x40000000 +#if ZLIB_CRC_CHUNK_SIZE > INT_MAX +# error "unsupported less than 32-bit platform?" +#endif + while ((size_t)len > ZLIB_CRC_CHUNK_SIZE) { + value = crc32(value, buf, ZLIB_CRC_CHUNK_SIZE); + buf += (size_t) ZLIB_CRC_CHUNK_SIZE; + len -= (size_t) ZLIB_CRC_CHUNK_SIZE; } +#undef ZLIB_CRC_CHUNK_SIZE value = crc32(value, buf, (unsigned int)len); Py_END_ALLOW_THREADS } else { diff --git a/Objects/abstract.c b/Objects/abstract.c index 43842fbdd6aedd..1ec5c5b8c3dc2f 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -1180,29 +1180,10 @@ PyNumber_Multiply(PyObject *v, PyObject *w) return result; } -PyObject * -PyNumber_MatrixMultiply(PyObject *v, PyObject *w) -{ - return binary_op(v, w, NB_SLOT(nb_matrix_multiply), "@"); -} - -PyObject * -PyNumber_FloorDivide(PyObject *v, PyObject *w) -{ - return binary_op(v, w, NB_SLOT(nb_floor_divide), "//"); -} - -PyObject * -PyNumber_TrueDivide(PyObject *v, PyObject *w) -{ - return binary_op(v, w, NB_SLOT(nb_true_divide), "/"); -} - -PyObject * -PyNumber_Remainder(PyObject *v, PyObject *w) -{ - return binary_op(v, w, NB_SLOT(nb_remainder), "%"); -} +BINARY_FUNC(PyNumber_MatrixMultiply, nb_matrix_multiply, "@") +BINARY_FUNC(PyNumber_FloorDivide, nb_floor_divide, "//") +BINARY_FUNC(PyNumber_TrueDivide, nb_true_divide, "/") +BINARY_FUNC(PyNumber_Remainder, nb_remainder, "%") PyObject * PyNumber_Power(PyObject *v, PyObject *w, PyObject *z) @@ -1379,73 +1360,27 @@ _PyNumber_InPlacePowerNoMod(PyObject *lhs, PyObject *rhs) /* Unary operators and functions */ -PyObject * -PyNumber_Negative(PyObject *o) -{ - if (o == NULL) { - return null_error(); - } - - PyNumberMethods *m = Py_TYPE(o)->tp_as_number; - if (m && m->nb_negative) { - PyObject *res = (*m->nb_negative)(o); - assert(_Py_CheckSlotResult(o, "__neg__", res != NULL)); - return res; - } - - return type_error("bad operand type for unary -: '%.200s'", o); -} - -PyObject * -PyNumber_Positive(PyObject *o) -{ - if (o == NULL) { - return null_error(); - } - - PyNumberMethods *m = Py_TYPE(o)->tp_as_number; - if (m && m->nb_positive) { - PyObject *res = (*m->nb_positive)(o); - assert(_Py_CheckSlotResult(o, "__pos__", res != NULL)); - return res; - } - - return type_error("bad operand type for unary +: '%.200s'", o); -} - -PyObject * -PyNumber_Invert(PyObject *o) -{ - if (o == NULL) { - return null_error(); - } - - PyNumberMethods *m = Py_TYPE(o)->tp_as_number; - if (m && m->nb_invert) { - PyObject *res = (*m->nb_invert)(o); - assert(_Py_CheckSlotResult(o, "__invert__", res != NULL)); - return res; - } - - return type_error("bad operand type for unary ~: '%.200s'", o); -} - -PyObject * -PyNumber_Absolute(PyObject *o) -{ - if (o == NULL) { - return null_error(); - } - - PyNumberMethods *m = Py_TYPE(o)->tp_as_number; - if (m && m->nb_absolute) { - PyObject *res = m->nb_absolute(o); - assert(_Py_CheckSlotResult(o, "__abs__", res != NULL)); - return res; - } - - return type_error("bad operand type for abs(): '%.200s'", o); -} +#define UNARY_FUNC(func, op, meth_name, descr) \ + PyObject * \ + func(PyObject *o) { \ + if (o == NULL) { \ + return null_error(); \ + } \ + \ + PyNumberMethods *m = Py_TYPE(o)->tp_as_number; \ + if (m && m->op) { \ + PyObject *res = (*m->op)(o); \ + assert(_Py_CheckSlotResult(o, #meth_name, res != NULL)); \ + return res; \ + } \ + \ + return type_error("bad operand type for "descr": '%.200s'", o); \ + } + +UNARY_FUNC(PyNumber_Negative, nb_negative, __neg__, "unary -") +UNARY_FUNC(PyNumber_Positive, nb_positive, __pow__, "unary +") +UNARY_FUNC(PyNumber_Invert, nb_invert, __invert__, "unary ~") +UNARY_FUNC(PyNumber_Absolute, nb_absolute, __abs__, "abs()") int diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 67073190cc889d..659de7d3dd5a99 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -2007,7 +2007,10 @@ static PyObject * bytearray_join(PyByteArrayObject *self, PyObject *iterable_of_bytes) /*[clinic end generated code: output=a8516370bf68ae08 input=aba6b1f9b30fcb8e]*/ { - return stringlib_bytes_join((PyObject*)self, iterable_of_bytes); + self->ob_exports++; // this protects `self` from being cleared/resized if `iterable_of_bytes` is a custom iterator + PyObject* ret = stringlib_bytes_join((PyObject*)self, iterable_of_bytes); + self->ob_exports--; // unexport `self` + return ret; } /*[clinic input] diff --git a/Objects/genobject.c b/Objects/genobject.c index f98aa357cd2ce1..9614713883741c 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -732,7 +732,7 @@ _gen_getframe(PyGenObject *gen, const char *const name) if (PySys_Audit("object.__getattr__", "Os", gen, name) < 0) { return NULL; } - if (gen->gi_frame_state == FRAME_CLEARED) { + if (FRAME_STATE_FINISHED(gen->gi_frame_state)) { Py_RETURN_NONE; } return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject((_PyInterpreterFrame *)gen->gi_iframe)); diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index bba77ce8ab7e7b..e2741fef6debd3 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -749,27 +749,20 @@ module_repr(PyModuleObject *m) } /* Check if the "_initializing" attribute of the module spec is set to true. - Clear the exception and return 0 if spec is NULL. */ int _PyModuleSpec_IsInitializing(PyObject *spec) { - if (spec != NULL) { - PyObject *value; - int ok = PyObject_GetOptionalAttr(spec, &_Py_ID(_initializing), &value); - if (ok == 0) { - return 0; - } - if (value != NULL) { - int initializing = PyObject_IsTrue(value); - Py_DECREF(value); - if (initializing >= 0) { - return initializing; - } - } + if (spec == NULL) { + return 0; } - PyErr_Clear(); - return 0; + PyObject *value; + int rc = PyObject_GetOptionalAttr(spec, &_Py_ID(_initializing), &value); + if (rc > 0) { + rc = PyObject_IsTrue(value); + Py_DECREF(value); + } + return rc; } /* Check if the submodule name is in the "_uninitialized_submodules" attribute @@ -782,17 +775,13 @@ _PyModuleSpec_IsUninitializedSubmodule(PyObject *spec, PyObject *name) return 0; } - PyObject *value = PyObject_GetAttr(spec, &_Py_ID(_uninitialized_submodules)); - if (value == NULL) { - return 0; + PyObject *value; + int rc = PyObject_GetOptionalAttr(spec, &_Py_ID(_uninitialized_submodules), &value); + if (rc > 0) { + rc = PySequence_Contains(value, name); + Py_DECREF(value); } - - int is_uninitialized = PySequence_Contains(value, name); - Py_DECREF(value); - if (is_uninitialized == -1) { - return 0; - } - return is_uninitialized; + return rc; } PyObject* @@ -840,23 +829,27 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) return NULL; } if (suppress != 1) { - if (_PyModuleSpec_IsInitializing(spec)) { + int rc = _PyModuleSpec_IsInitializing(spec); + if (rc > 0) { PyErr_Format(PyExc_AttributeError, "partially initialized " "module '%U' has no attribute '%U' " "(most likely due to a circular import)", mod_name, name); } - else if (_PyModuleSpec_IsUninitializedSubmodule(spec, name)) { - PyErr_Format(PyExc_AttributeError, + else if (rc == 0) { + rc = _PyModuleSpec_IsUninitializedSubmodule(spec, name); + if (rc > 0) { + PyErr_Format(PyExc_AttributeError, "cannot access submodule '%U' of module '%U' " "(most likely due to a circular import)", name, mod_name); - } - else { - PyErr_Format(PyExc_AttributeError, + } + else if (rc == 0) { + PyErr_Format(PyExc_AttributeError, "module '%U' has no attribute '%U'", mod_name, name); + } } } Py_XDECREF(spec); diff --git a/Objects/object.c b/Objects/object.c index 1f5b2b4f79e5fa..cdb7a08a7828fb 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -296,7 +296,7 @@ _Py_DecRef(PyObject *o) Py_DECREF(o); } -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED # ifdef Py_REF_DEBUG static inline int is_shared_refcnt_dead(Py_ssize_t shared) @@ -414,7 +414,7 @@ _Py_ExplicitMergeRefcount(PyObject *op, Py_ssize_t extra) _Py_atomic_store_uintptr_relaxed(&op->ob_tid, 0); return refcnt; } -#endif /* Py_NOGIL */ +#endif /* Py_GIL_DISABLED */ /**************************************/ @@ -2026,7 +2026,7 @@ PyTypeObject _PyNone_Type = { 0, /*tp_doc */ 0, /*tp_traverse */ 0, /*tp_clear */ - 0, /*tp_richcompare */ + _Py_BaseObject_RichCompare, /*tp_richcompare */ 0, /*tp_weaklistoffset */ 0, /*tp_iter */ 0, /*tp_iternext */ @@ -2359,7 +2359,7 @@ new_reference(PyObject *op) _PyTraceMalloc_NewReference(op); } // Skip the immortal object check in Py_SET_REFCNT; always set refcnt to 1 -#if !defined(Py_NOGIL) +#if !defined(Py_GIL_DISABLED) op->ob_refcnt = 1; #else op->ob_tid = _Py_ThreadId(); diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index 2761c774209786..b737c030957564 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -329,13 +329,9 @@ int _PyMem_SetDefaultAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *old_alloc) { - if (ALLOCATORS_MUTEX == NULL) { - /* The runtime must be initializing. */ - return set_default_allocator_unlocked(domain, pydebug, old_alloc); - } - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); int res = set_default_allocator_unlocked(domain, pydebug, old_alloc); - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); return res; } @@ -467,9 +463,9 @@ set_up_allocators_unlocked(PyMemAllocatorName allocator) int _PyMem_SetupAllocators(PyMemAllocatorName allocator) { - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); int res = set_up_allocators_unlocked(allocator); - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); return res; } @@ -554,9 +550,9 @@ get_current_allocator_name_unlocked(void) const char* _PyMem_GetCurrentAllocatorName(void) { - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); const char *name = get_current_allocator_name_unlocked(); - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); return name; } @@ -653,14 +649,9 @@ set_up_debug_hooks_unlocked(void) void PyMem_SetupDebugHooks(void) { - if (ALLOCATORS_MUTEX == NULL) { - /* The runtime must not be completely initialized yet. */ - set_up_debug_hooks_unlocked(); - return; - } - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); set_up_debug_hooks_unlocked(); - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); } static void @@ -696,53 +687,33 @@ set_allocator_unlocked(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator) void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator) { - if (ALLOCATORS_MUTEX == NULL) { - /* The runtime must not be completely initialized yet. */ - get_allocator_unlocked(domain, allocator); - return; - } - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); get_allocator_unlocked(domain, allocator); - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); } void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator) { - if (ALLOCATORS_MUTEX == NULL) { - /* The runtime must not be completely initialized yet. */ - set_allocator_unlocked(domain, allocator); - return; - } - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); set_allocator_unlocked(domain, allocator); - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); } void PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator) { - if (ALLOCATORS_MUTEX == NULL) { - /* The runtime must not be completely initialized yet. */ - *allocator = _PyObject_Arena; - return; - } - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); *allocator = _PyObject_Arena; - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); } void PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator) { - if (ALLOCATORS_MUTEX == NULL) { - /* The runtime must not be completely initialized yet. */ - _PyObject_Arena = *allocator; - return; - } - PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK); + PyMutex_Lock(&ALLOCATORS_MUTEX); _PyObject_Arena = *allocator; - PyThread_release_lock(ALLOCATORS_MUTEX); + PyMutex_Unlock(&ALLOCATORS_MUTEX); } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 4464b5af8cd15b..08f5f47d586729 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3006,21 +3006,21 @@ subtype_getweakref(PyObject *obj, void *context) static PyGetSetDef subtype_getsets_full[] = { {"__dict__", subtype_dict, subtype_setdict, - PyDoc_STR("dictionary for instance variables (if defined)")}, + PyDoc_STR("dictionary for instance variables")}, {"__weakref__", subtype_getweakref, NULL, - PyDoc_STR("list of weak references to the object (if defined)")}, + PyDoc_STR("list of weak references to the object")}, {0} }; static PyGetSetDef subtype_getsets_dict_only[] = { {"__dict__", subtype_dict, subtype_setdict, - PyDoc_STR("dictionary for instance variables (if defined)")}, + PyDoc_STR("dictionary for instance variables")}, {0} }; static PyGetSetDef subtype_getsets_weakref_only[] = { {"__weakref__", subtype_getweakref, NULL, - PyDoc_STR("list of weak references to the object (if defined)")}, + PyDoc_STR("list of weak references to the object")}, {0} }; @@ -4540,6 +4540,12 @@ PyType_GetQualName(PyTypeObject *type) return type_qualname(type, NULL); } +PyObject * +_PyType_GetModuleName(PyTypeObject *type) +{ + return type_module(type, NULL); +} + void * PyType_GetSlot(PyTypeObject *type, int slot) { @@ -5619,6 +5625,12 @@ object_richcompare(PyObject *self, PyObject *other, int op) return res; } +PyObject* +_Py_BaseObject_RichCompare(PyObject* self, PyObject* other, int op) +{ + return object_richcompare(self, other, op); +} + static PyObject * object_get_class(PyObject *self, void *closure) { diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index cffc06297a9aee..836e14fd5d5dea 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -1904,7 +1904,7 @@ _PyUnicode_FromId(_Py_Identifier *id) if (index < 0) { struct _Py_unicode_runtime_ids *rt_ids = &interp->runtime->unicode_state.ids; - PyThread_acquire_lock(rt_ids->lock, WAIT_LOCK); + PyMutex_Lock(&rt_ids->mutex); // Check again to detect concurrent access. Another thread can have // initialized the index while this thread waited for the lock. index = _Py_atomic_load_ssize(&id->index); @@ -1914,7 +1914,7 @@ _PyUnicode_FromId(_Py_Identifier *id) rt_ids->next_index++; _Py_atomic_store_ssize(&id->index, index); } - PyThread_release_lock(rt_ids->lock); + PyMutex_Unlock(&rt_ids->mutex); } assert(index >= 0); @@ -5869,6 +5869,23 @@ PyUnicode_AsUTF16String(PyObject *unicode) return _PyUnicode_EncodeUTF16(unicode, NULL, 0); } +_PyUnicode_Name_CAPI * +_PyUnicode_GetNameCAPI(void) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyUnicode_Name_CAPI *ucnhash_capi; + + ucnhash_capi = _Py_atomic_load_ptr(&interp->unicode.ucnhash_capi); + if (ucnhash_capi == NULL) { + ucnhash_capi = (_PyUnicode_Name_CAPI *)PyCapsule_Import( + PyUnicodeData_CAPSULE_NAME, 1); + + // It's fine if we overwite the value here. It's always the same value. + _Py_atomic_store_ptr(&interp->unicode.ucnhash_capi, ucnhash_capi); + } + return ucnhash_capi; +} + /* --- Unicode Escape Codec ----------------------------------------------- */ PyObject * @@ -5884,7 +5901,6 @@ _PyUnicode_DecodeUnicodeEscapeInternal(const char *s, PyObject *errorHandler = NULL; PyObject *exc = NULL; _PyUnicode_Name_CAPI *ucnhash_capi; - PyInterpreterState *interp = _PyInterpreterState_GET(); // so we can remember if we've seen an invalid escape char or not *first_invalid_escape = NULL; @@ -6032,19 +6048,13 @@ _PyUnicode_DecodeUnicodeEscapeInternal(const char *s, /* \N{name} */ case 'N': - ucnhash_capi = interp->unicode.ucnhash_capi; + ucnhash_capi = _PyUnicode_GetNameCAPI(); if (ucnhash_capi == NULL) { - /* load the unicode data module */ - ucnhash_capi = (_PyUnicode_Name_CAPI *)PyCapsule_Import( - PyUnicodeData_CAPSULE_NAME, 1); - if (ucnhash_capi == NULL) { - PyErr_SetString( + PyErr_SetString( PyExc_UnicodeError, "\\N escapes not supported (can't load unicodedata module)" - ); - goto onError; - } - interp->unicode.ucnhash_capi = ucnhash_capi; + ); + goto onError; } message = "malformed \\N character escape"; diff --git a/PC/_wmimodule.cpp b/PC/_wmimodule.cpp index fdf09ec6ec6f63..215350acfb0d8e 100644 --- a/PC/_wmimodule.cpp +++ b/PC/_wmimodule.cpp @@ -44,6 +44,7 @@ struct _query_data { LPCWSTR query; HANDLE writePipe; HANDLE readPipe; + HANDLE connectEvent; }; @@ -86,6 +87,9 @@ _query_thread(LPVOID param) NULL, NULL, 0, NULL, 0, 0, &services ); } + if (!SetEvent(data->connectEvent)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + } if (SUCCEEDED(hr)) { hr = CoSetProxyBlanket( services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, @@ -231,7 +235,8 @@ _wmi_exec_query_impl(PyObject *module, PyObject *query) Py_BEGIN_ALLOW_THREADS - if (!CreatePipe(&data.readPipe, &data.writePipe, NULL, 0)) { + data.connectEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!data.connectEvent || !CreatePipe(&data.readPipe, &data.writePipe, NULL, 0)) { err = GetLastError(); } else { hThread = CreateThread(NULL, 0, _query_thread, (LPVOID*)&data, 0, NULL); @@ -243,6 +248,21 @@ _wmi_exec_query_impl(PyObject *module, PyObject *query) } } + // gh-112278: If current user doesn't have permission to query the WMI, the + // function IWbemLocator::ConnectServer will hang for 5 seconds, and there + // is no way to specify the timeout. So we use an Event object to simulate + // a timeout. + switch (WaitForSingleObject(data.connectEvent, 100)) { + case WAIT_OBJECT_0: + break; + case WAIT_TIMEOUT: + err = WAIT_TIMEOUT; + break; + default: + err = GetLastError(); + break; + } + while (!err) { if (ReadFile( data.readPipe, @@ -265,7 +285,7 @@ _wmi_exec_query_impl(PyObject *module, PyObject *query) } // Allow the thread some time to clean up - switch (WaitForSingleObject(hThread, 1000)) { + switch (WaitForSingleObject(hThread, 100)) { case WAIT_OBJECT_0: // Thread ended cleanly if (!GetExitCodeThread(hThread, (LPDWORD)&err)) { @@ -286,6 +306,7 @@ _wmi_exec_query_impl(PyObject *module, PyObject *query) } CloseHandle(hThread); + CloseHandle(data.connectEvent); hThread = NULL; Py_END_ALLOW_THREADS diff --git a/PC/launcher2.c b/PC/launcher2.c index 116091f01227b8..2a8f8a101fc8a6 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -438,7 +438,7 @@ typedef struct { bool list; // if true, only list detected runtimes with paths without launching bool listPaths; - // if true, display help message before contiuning + // if true, display help message before continuing bool help; // if set, limits search to registry keys with the specified Company // This is intended for debugging and testing only diff --git a/PC/winsound.c b/PC/winsound.c index d1ff991c934bae..b0e416cfec4699 100644 --- a/PC/winsound.c +++ b/PC/winsound.c @@ -35,7 +35,7 @@ winsound.PlaySound(None, 0) */ -#ifndef Py_NOGIL +#ifndef Py_GIL_DISABLED // Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED #define Py_LIMITED_API 0x030c0000 #endif diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props index bb3555bd123089..0acc7045c39a26 100644 --- a/PCbuild/pyproject.props +++ b/PCbuild/pyproject.props @@ -40,7 +40,7 @@ $(PySourcePath)Include;$(PySourcePath)Include\internal;$(PySourcePath)Include\internal\mimalloc;$(PySourcePath)PC;$(IntDir);%(AdditionalIncludeDirectories) WIN32;$(_Py3NamePreprocessorDefinition);$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PydPreprocessorDefinition)%(PreprocessorDefinitions) - Py_NOGIL=1;%(PreprocessorDefinitions) + Py_GIL_DISABLED=1;%(PreprocessorDefinitions) _Py_USING_PGO=1;%(PreprocessorDefinitions) MaxSpeed diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index be1b98dba02fc5..278f1f5622543c 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -285,6 +285,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 4f0da8f35998b7..c9b34c64fbf75f 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -780,6 +780,9 @@ Include\internal + + Include\internal + Include\internal diff --git a/Parser/lexer/lexer.c b/Parser/lexer/lexer.c index 2ba24a2c2405f2..ea4bdf7ce4a24c 100644 --- a/Parser/lexer/lexer.c +++ b/Parser/lexer/lexer.c @@ -116,13 +116,56 @@ set_fstring_expr(struct tok_state* tok, struct token *token, char c) { if (!tok_mode->f_string_debug || token->metadata) { return 0; } + PyObject *res = NULL; - PyObject *res = PyUnicode_DecodeUTF8( - tok_mode->last_expr_buffer, - tok_mode->last_expr_size - tok_mode->last_expr_end, - NULL - ); - if (!res) { + // Check if there is a # character in the expression + int hash_detected = 0; + for (Py_ssize_t i = 0; i < tok_mode->last_expr_size - tok_mode->last_expr_end; i++) { + if (tok_mode->last_expr_buffer[i] == '#') { + hash_detected = 1; + break; + } + } + + if (hash_detected) { + Py_ssize_t input_length = tok_mode->last_expr_size - tok_mode->last_expr_end; + char *result = (char *)PyObject_Malloc((input_length + 1) * sizeof(char)); + if (!result) { + return -1; + } + + Py_ssize_t i = 0; + Py_ssize_t j = 0; + + for (i = 0, j = 0; i < input_length; i++) { + if (tok_mode->last_expr_buffer[i] == '#') { + // Skip characters until newline or end of string + while (tok_mode->last_expr_buffer[i] != '\0' && i < input_length) { + if (tok_mode->last_expr_buffer[i] == '\n') { + result[j++] = tok_mode->last_expr_buffer[i]; + break; + } + i++; + } + } else { + result[j++] = tok_mode->last_expr_buffer[i]; + } + } + + result[j] = '\0'; // Null-terminate the result string + res = PyUnicode_DecodeUTF8(result, j, NULL); + PyObject_Free(result); + } else { + res = PyUnicode_DecodeUTF8( + tok_mode->last_expr_buffer, + tok_mode->last_expr_size - tok_mode->last_expr_end, + NULL + ); + + } + + + if (!res) { return -1; } token->metadata = res; diff --git a/Parser/parser.c b/Parser/parser.c index ca8e9d0e23661c..9006efb09a59fa 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -8,7 +8,11 @@ #endif #ifdef __wasi__ -# define MAXSTACK 4000 +# ifdef Py_DEBUG +# define MAXSTACK 1000 +# else +# define MAXSTACK 4000 +# endif #else # define MAXSTACK 6000 #endif @@ -17,18 +21,18 @@ static KeywordToken *reserved_keywords[] = { (KeywordToken[]) {{NULL, -1}}, (KeywordToken[]) {{NULL, -1}}, (KeywordToken[]) { - {"if", 656}, - {"as", 654}, - {"in", 667}, + {"if", 658}, + {"as", 656}, + {"in", 669}, {"or", 581}, {"is", 589}, {NULL, -1}, }, (KeywordToken[]) { {"del", 613}, - {"def", 669}, - {"for", 666}, - {"try", 638}, + {"def", 671}, + {"for", 668}, + {"try", 640}, {"and", 582}, {"not", 588}, {NULL, -1}, @@ -36,9 +40,9 @@ static KeywordToken *reserved_keywords[] = { (KeywordToken[]) { {"from", 618}, {"pass", 504}, - {"with", 629}, - {"elif", 658}, - {"else", 659}, + {"with", 631}, + {"elif", 660}, + {"else", 661}, {"None", 611}, {"True", 610}, {NULL, -1}, @@ -47,9 +51,9 @@ static KeywordToken *reserved_keywords[] = { {"raise", 525}, {"yield", 580}, {"break", 508}, - {"async", 668}, - {"class", 671}, - {"while", 661}, + {"async", 670}, + {"class", 673}, + {"while", 663}, {"False", 612}, {"await", 590}, {NULL, -1}, @@ -59,12 +63,12 @@ static KeywordToken *reserved_keywords[] = { {"import", 617}, {"assert", 529}, {"global", 526}, - {"except", 651}, + {"except", 653}, {"lambda", 609}, {NULL, -1}, }, (KeywordToken[]) { - {"finally", 647}, + {"finally", 649}, {NULL, -1}, }, (KeywordToken[]) { @@ -300,311 +304,312 @@ static char *soft_keywords[] = { #define invalid_group_type 1217 #define invalid_import_type 1218 #define invalid_import_from_targets_type 1219 -#define invalid_with_stmt_type 1220 -#define invalid_with_stmt_indent_type 1221 -#define invalid_try_stmt_type 1222 -#define invalid_except_stmt_type 1223 -#define invalid_finally_stmt_type 1224 -#define invalid_except_stmt_indent_type 1225 -#define invalid_except_star_stmt_indent_type 1226 -#define invalid_match_stmt_type 1227 -#define invalid_case_block_type 1228 -#define invalid_as_pattern_type 1229 -#define invalid_class_pattern_type 1230 -#define invalid_class_argument_pattern_type 1231 -#define invalid_if_stmt_type 1232 -#define invalid_elif_stmt_type 1233 -#define invalid_else_stmt_type 1234 -#define invalid_while_stmt_type 1235 -#define invalid_for_stmt_type 1236 -#define invalid_def_raw_type 1237 -#define invalid_class_def_raw_type 1238 -#define invalid_double_starred_kvpairs_type 1239 -#define invalid_kvpair_type 1240 -#define invalid_starred_expression_type 1241 -#define invalid_replacement_field_type 1242 -#define invalid_conversion_character_type 1243 -#define _loop0_1_type 1244 -#define _loop0_2_type 1245 -#define _loop1_3_type 1246 -#define _loop0_5_type 1247 -#define _gather_4_type 1248 -#define _tmp_6_type 1249 -#define _tmp_7_type 1250 -#define _tmp_8_type 1251 -#define _tmp_9_type 1252 -#define _tmp_10_type 1253 -#define _tmp_11_type 1254 -#define _tmp_12_type 1255 -#define _tmp_13_type 1256 -#define _loop1_14_type 1257 -#define _tmp_15_type 1258 -#define _tmp_16_type 1259 -#define _tmp_17_type 1260 -#define _loop0_19_type 1261 -#define _gather_18_type 1262 -#define _loop0_21_type 1263 -#define _gather_20_type 1264 -#define _tmp_22_type 1265 -#define _tmp_23_type 1266 -#define _loop0_24_type 1267 -#define _loop1_25_type 1268 -#define _loop0_27_type 1269 -#define _gather_26_type 1270 -#define _tmp_28_type 1271 -#define _loop0_30_type 1272 -#define _gather_29_type 1273 -#define _tmp_31_type 1274 -#define _loop1_32_type 1275 -#define _tmp_33_type 1276 -#define _tmp_34_type 1277 -#define _tmp_35_type 1278 -#define _loop0_36_type 1279 -#define _loop0_37_type 1280 -#define _loop0_38_type 1281 -#define _loop1_39_type 1282 -#define _loop0_40_type 1283 -#define _loop1_41_type 1284 -#define _loop1_42_type 1285 -#define _loop1_43_type 1286 -#define _loop0_44_type 1287 -#define _loop1_45_type 1288 -#define _loop0_46_type 1289 -#define _loop1_47_type 1290 -#define _loop0_48_type 1291 -#define _loop0_49_type 1292 -#define _loop1_50_type 1293 -#define _loop0_52_type 1294 -#define _gather_51_type 1295 -#define _loop0_54_type 1296 -#define _gather_53_type 1297 -#define _loop0_56_type 1298 -#define _gather_55_type 1299 -#define _loop0_58_type 1300 -#define _gather_57_type 1301 -#define _tmp_59_type 1302 -#define _loop1_60_type 1303 -#define _loop1_61_type 1304 -#define _tmp_62_type 1305 -#define _tmp_63_type 1306 -#define _loop1_64_type 1307 -#define _loop0_66_type 1308 -#define _gather_65_type 1309 -#define _tmp_67_type 1310 -#define _tmp_68_type 1311 -#define _tmp_69_type 1312 -#define _tmp_70_type 1313 -#define _loop0_72_type 1314 -#define _gather_71_type 1315 -#define _loop0_74_type 1316 -#define _gather_73_type 1317 -#define _tmp_75_type 1318 -#define _loop0_77_type 1319 -#define _gather_76_type 1320 -#define _loop0_79_type 1321 -#define _gather_78_type 1322 -#define _loop0_81_type 1323 -#define _gather_80_type 1324 -#define _loop1_82_type 1325 -#define _loop1_83_type 1326 -#define _loop0_85_type 1327 -#define _gather_84_type 1328 -#define _loop1_86_type 1329 -#define _loop1_87_type 1330 -#define _loop1_88_type 1331 -#define _tmp_89_type 1332 -#define _loop0_91_type 1333 -#define _gather_90_type 1334 -#define _tmp_92_type 1335 -#define _tmp_93_type 1336 -#define _tmp_94_type 1337 -#define _tmp_95_type 1338 -#define _tmp_96_type 1339 -#define _tmp_97_type 1340 -#define _loop0_98_type 1341 -#define _loop0_99_type 1342 -#define _loop0_100_type 1343 -#define _loop1_101_type 1344 -#define _loop0_102_type 1345 -#define _loop1_103_type 1346 -#define _loop1_104_type 1347 -#define _loop1_105_type 1348 -#define _loop0_106_type 1349 -#define _loop1_107_type 1350 -#define _loop0_108_type 1351 -#define _loop1_109_type 1352 -#define _loop0_110_type 1353 -#define _loop1_111_type 1354 -#define _tmp_112_type 1355 -#define _loop0_113_type 1356 -#define _loop0_114_type 1357 -#define _loop1_115_type 1358 -#define _tmp_116_type 1359 -#define _loop0_118_type 1360 -#define _gather_117_type 1361 -#define _loop1_119_type 1362 -#define _loop0_120_type 1363 -#define _loop0_121_type 1364 -#define _tmp_122_type 1365 -#define _loop0_124_type 1366 -#define _gather_123_type 1367 -#define _tmp_125_type 1368 -#define _loop0_127_type 1369 -#define _gather_126_type 1370 -#define _loop0_129_type 1371 -#define _gather_128_type 1372 -#define _loop0_131_type 1373 -#define _gather_130_type 1374 -#define _loop0_133_type 1375 -#define _gather_132_type 1376 -#define _loop0_134_type 1377 -#define _loop0_136_type 1378 -#define _gather_135_type 1379 -#define _loop1_137_type 1380 -#define _tmp_138_type 1381 -#define _loop0_140_type 1382 -#define _gather_139_type 1383 -#define _loop0_142_type 1384 -#define _gather_141_type 1385 -#define _loop0_144_type 1386 -#define _gather_143_type 1387 -#define _loop0_146_type 1388 -#define _gather_145_type 1389 -#define _loop0_148_type 1390 -#define _gather_147_type 1391 -#define _tmp_149_type 1392 -#define _tmp_150_type 1393 -#define _tmp_151_type 1394 -#define _tmp_152_type 1395 -#define _tmp_153_type 1396 -#define _tmp_154_type 1397 -#define _tmp_155_type 1398 -#define _tmp_156_type 1399 -#define _tmp_157_type 1400 -#define _tmp_158_type 1401 -#define _tmp_159_type 1402 -#define _tmp_160_type 1403 -#define _loop0_161_type 1404 -#define _loop0_162_type 1405 -#define _loop0_163_type 1406 -#define _tmp_164_type 1407 -#define _tmp_165_type 1408 -#define _tmp_166_type 1409 -#define _tmp_167_type 1410 -#define _tmp_168_type 1411 -#define _loop0_169_type 1412 -#define _loop0_170_type 1413 -#define _loop0_171_type 1414 -#define _loop1_172_type 1415 -#define _tmp_173_type 1416 -#define _loop0_174_type 1417 -#define _tmp_175_type 1418 -#define _loop0_176_type 1419 -#define _loop1_177_type 1420 -#define _tmp_178_type 1421 -#define _tmp_179_type 1422 -#define _tmp_180_type 1423 -#define _loop0_181_type 1424 -#define _tmp_182_type 1425 -#define _tmp_183_type 1426 -#define _loop1_184_type 1427 -#define _tmp_185_type 1428 -#define _loop0_186_type 1429 -#define _loop0_187_type 1430 -#define _loop0_188_type 1431 -#define _loop0_190_type 1432 -#define _gather_189_type 1433 -#define _tmp_191_type 1434 -#define _loop0_192_type 1435 -#define _tmp_193_type 1436 -#define _loop0_194_type 1437 -#define _loop1_195_type 1438 -#define _loop1_196_type 1439 -#define _tmp_197_type 1440 -#define _tmp_198_type 1441 -#define _loop0_199_type 1442 -#define _tmp_200_type 1443 -#define _tmp_201_type 1444 -#define _tmp_202_type 1445 -#define _loop0_204_type 1446 -#define _gather_203_type 1447 -#define _loop0_206_type 1448 -#define _gather_205_type 1449 -#define _loop0_208_type 1450 -#define _gather_207_type 1451 -#define _loop0_210_type 1452 -#define _gather_209_type 1453 -#define _loop0_212_type 1454 -#define _gather_211_type 1455 -#define _tmp_213_type 1456 -#define _loop0_214_type 1457 -#define _loop1_215_type 1458 -#define _tmp_216_type 1459 -#define _loop0_217_type 1460 -#define _loop1_218_type 1461 -#define _tmp_219_type 1462 -#define _tmp_220_type 1463 -#define _tmp_221_type 1464 -#define _tmp_222_type 1465 -#define _tmp_223_type 1466 -#define _tmp_224_type 1467 -#define _tmp_225_type 1468 -#define _tmp_226_type 1469 -#define _tmp_227_type 1470 -#define _tmp_228_type 1471 -#define _loop0_230_type 1472 -#define _gather_229_type 1473 -#define _tmp_231_type 1474 -#define _tmp_232_type 1475 -#define _tmp_233_type 1476 -#define _tmp_234_type 1477 -#define _tmp_235_type 1478 -#define _tmp_236_type 1479 -#define _tmp_237_type 1480 -#define _tmp_238_type 1481 -#define _tmp_239_type 1482 -#define _tmp_240_type 1483 -#define _tmp_241_type 1484 -#define _tmp_242_type 1485 -#define _tmp_243_type 1486 -#define _loop0_244_type 1487 -#define _tmp_245_type 1488 -#define _tmp_246_type 1489 -#define _tmp_247_type 1490 -#define _tmp_248_type 1491 -#define _tmp_249_type 1492 -#define _tmp_250_type 1493 -#define _tmp_251_type 1494 -#define _tmp_252_type 1495 -#define _tmp_253_type 1496 -#define _tmp_254_type 1497 -#define _tmp_255_type 1498 -#define _tmp_256_type 1499 -#define _tmp_257_type 1500 -#define _tmp_258_type 1501 -#define _tmp_259_type 1502 -#define _tmp_260_type 1503 -#define _tmp_261_type 1504 -#define _tmp_262_type 1505 -#define _tmp_263_type 1506 -#define _tmp_264_type 1507 -#define _tmp_265_type 1508 -#define _tmp_266_type 1509 -#define _tmp_267_type 1510 -#define _tmp_268_type 1511 -#define _tmp_269_type 1512 -#define _tmp_270_type 1513 -#define _tmp_271_type 1514 -#define _tmp_272_type 1515 -#define _tmp_273_type 1516 -#define _loop0_275_type 1517 -#define _gather_274_type 1518 -#define _tmp_276_type 1519 -#define _tmp_277_type 1520 -#define _tmp_278_type 1521 -#define _tmp_279_type 1522 -#define _tmp_280_type 1523 -#define _tmp_281_type 1524 +#define invalid_compound_stmt_type 1220 +#define invalid_with_stmt_type 1221 +#define invalid_with_stmt_indent_type 1222 +#define invalid_try_stmt_type 1223 +#define invalid_except_stmt_type 1224 +#define invalid_finally_stmt_type 1225 +#define invalid_except_stmt_indent_type 1226 +#define invalid_except_star_stmt_indent_type 1227 +#define invalid_match_stmt_type 1228 +#define invalid_case_block_type 1229 +#define invalid_as_pattern_type 1230 +#define invalid_class_pattern_type 1231 +#define invalid_class_argument_pattern_type 1232 +#define invalid_if_stmt_type 1233 +#define invalid_elif_stmt_type 1234 +#define invalid_else_stmt_type 1235 +#define invalid_while_stmt_type 1236 +#define invalid_for_stmt_type 1237 +#define invalid_def_raw_type 1238 +#define invalid_class_def_raw_type 1239 +#define invalid_double_starred_kvpairs_type 1240 +#define invalid_kvpair_type 1241 +#define invalid_starred_expression_type 1242 +#define invalid_replacement_field_type 1243 +#define invalid_conversion_character_type 1244 +#define _loop0_1_type 1245 +#define _loop0_2_type 1246 +#define _loop1_3_type 1247 +#define _loop0_5_type 1248 +#define _gather_4_type 1249 +#define _tmp_6_type 1250 +#define _tmp_7_type 1251 +#define _tmp_8_type 1252 +#define _tmp_9_type 1253 +#define _tmp_10_type 1254 +#define _tmp_11_type 1255 +#define _tmp_12_type 1256 +#define _tmp_13_type 1257 +#define _loop1_14_type 1258 +#define _tmp_15_type 1259 +#define _tmp_16_type 1260 +#define _tmp_17_type 1261 +#define _loop0_19_type 1262 +#define _gather_18_type 1263 +#define _loop0_21_type 1264 +#define _gather_20_type 1265 +#define _tmp_22_type 1266 +#define _tmp_23_type 1267 +#define _loop0_24_type 1268 +#define _loop1_25_type 1269 +#define _loop0_27_type 1270 +#define _gather_26_type 1271 +#define _tmp_28_type 1272 +#define _loop0_30_type 1273 +#define _gather_29_type 1274 +#define _tmp_31_type 1275 +#define _loop1_32_type 1276 +#define _tmp_33_type 1277 +#define _tmp_34_type 1278 +#define _tmp_35_type 1279 +#define _loop0_36_type 1280 +#define _loop0_37_type 1281 +#define _loop0_38_type 1282 +#define _loop1_39_type 1283 +#define _loop0_40_type 1284 +#define _loop1_41_type 1285 +#define _loop1_42_type 1286 +#define _loop1_43_type 1287 +#define _loop0_44_type 1288 +#define _loop1_45_type 1289 +#define _loop0_46_type 1290 +#define _loop1_47_type 1291 +#define _loop0_48_type 1292 +#define _loop0_49_type 1293 +#define _loop1_50_type 1294 +#define _loop0_52_type 1295 +#define _gather_51_type 1296 +#define _loop0_54_type 1297 +#define _gather_53_type 1298 +#define _loop0_56_type 1299 +#define _gather_55_type 1300 +#define _loop0_58_type 1301 +#define _gather_57_type 1302 +#define _tmp_59_type 1303 +#define _loop1_60_type 1304 +#define _loop1_61_type 1305 +#define _tmp_62_type 1306 +#define _tmp_63_type 1307 +#define _loop1_64_type 1308 +#define _loop0_66_type 1309 +#define _gather_65_type 1310 +#define _tmp_67_type 1311 +#define _tmp_68_type 1312 +#define _tmp_69_type 1313 +#define _tmp_70_type 1314 +#define _loop0_72_type 1315 +#define _gather_71_type 1316 +#define _loop0_74_type 1317 +#define _gather_73_type 1318 +#define _tmp_75_type 1319 +#define _loop0_77_type 1320 +#define _gather_76_type 1321 +#define _loop0_79_type 1322 +#define _gather_78_type 1323 +#define _loop0_81_type 1324 +#define _gather_80_type 1325 +#define _loop1_82_type 1326 +#define _loop1_83_type 1327 +#define _loop0_85_type 1328 +#define _gather_84_type 1329 +#define _loop1_86_type 1330 +#define _loop1_87_type 1331 +#define _loop1_88_type 1332 +#define _tmp_89_type 1333 +#define _loop0_91_type 1334 +#define _gather_90_type 1335 +#define _tmp_92_type 1336 +#define _tmp_93_type 1337 +#define _tmp_94_type 1338 +#define _tmp_95_type 1339 +#define _tmp_96_type 1340 +#define _tmp_97_type 1341 +#define _loop0_98_type 1342 +#define _loop0_99_type 1343 +#define _loop0_100_type 1344 +#define _loop1_101_type 1345 +#define _loop0_102_type 1346 +#define _loop1_103_type 1347 +#define _loop1_104_type 1348 +#define _loop1_105_type 1349 +#define _loop0_106_type 1350 +#define _loop1_107_type 1351 +#define _loop0_108_type 1352 +#define _loop1_109_type 1353 +#define _loop0_110_type 1354 +#define _loop1_111_type 1355 +#define _tmp_112_type 1356 +#define _loop0_113_type 1357 +#define _loop0_114_type 1358 +#define _loop1_115_type 1359 +#define _tmp_116_type 1360 +#define _loop0_118_type 1361 +#define _gather_117_type 1362 +#define _loop1_119_type 1363 +#define _loop0_120_type 1364 +#define _loop0_121_type 1365 +#define _tmp_122_type 1366 +#define _loop0_124_type 1367 +#define _gather_123_type 1368 +#define _tmp_125_type 1369 +#define _loop0_127_type 1370 +#define _gather_126_type 1371 +#define _loop0_129_type 1372 +#define _gather_128_type 1373 +#define _loop0_131_type 1374 +#define _gather_130_type 1375 +#define _loop0_133_type 1376 +#define _gather_132_type 1377 +#define _loop0_134_type 1378 +#define _loop0_136_type 1379 +#define _gather_135_type 1380 +#define _loop1_137_type 1381 +#define _tmp_138_type 1382 +#define _loop0_140_type 1383 +#define _gather_139_type 1384 +#define _loop0_142_type 1385 +#define _gather_141_type 1386 +#define _loop0_144_type 1387 +#define _gather_143_type 1388 +#define _loop0_146_type 1389 +#define _gather_145_type 1390 +#define _loop0_148_type 1391 +#define _gather_147_type 1392 +#define _tmp_149_type 1393 +#define _tmp_150_type 1394 +#define _tmp_151_type 1395 +#define _tmp_152_type 1396 +#define _tmp_153_type 1397 +#define _tmp_154_type 1398 +#define _tmp_155_type 1399 +#define _tmp_156_type 1400 +#define _tmp_157_type 1401 +#define _tmp_158_type 1402 +#define _tmp_159_type 1403 +#define _tmp_160_type 1404 +#define _loop0_161_type 1405 +#define _loop0_162_type 1406 +#define _loop0_163_type 1407 +#define _tmp_164_type 1408 +#define _tmp_165_type 1409 +#define _tmp_166_type 1410 +#define _tmp_167_type 1411 +#define _tmp_168_type 1412 +#define _loop0_169_type 1413 +#define _loop0_170_type 1414 +#define _loop0_171_type 1415 +#define _loop1_172_type 1416 +#define _tmp_173_type 1417 +#define _loop0_174_type 1418 +#define _tmp_175_type 1419 +#define _loop0_176_type 1420 +#define _loop1_177_type 1421 +#define _tmp_178_type 1422 +#define _tmp_179_type 1423 +#define _tmp_180_type 1424 +#define _loop0_181_type 1425 +#define _tmp_182_type 1426 +#define _tmp_183_type 1427 +#define _loop1_184_type 1428 +#define _tmp_185_type 1429 +#define _loop0_186_type 1430 +#define _loop0_187_type 1431 +#define _loop0_188_type 1432 +#define _loop0_190_type 1433 +#define _gather_189_type 1434 +#define _tmp_191_type 1435 +#define _loop0_192_type 1436 +#define _tmp_193_type 1437 +#define _loop0_194_type 1438 +#define _loop1_195_type 1439 +#define _loop1_196_type 1440 +#define _tmp_197_type 1441 +#define _tmp_198_type 1442 +#define _loop0_199_type 1443 +#define _tmp_200_type 1444 +#define _tmp_201_type 1445 +#define _tmp_202_type 1446 +#define _loop0_204_type 1447 +#define _gather_203_type 1448 +#define _loop0_206_type 1449 +#define _gather_205_type 1450 +#define _loop0_208_type 1451 +#define _gather_207_type 1452 +#define _loop0_210_type 1453 +#define _gather_209_type 1454 +#define _loop0_212_type 1455 +#define _gather_211_type 1456 +#define _tmp_213_type 1457 +#define _loop0_214_type 1458 +#define _loop1_215_type 1459 +#define _tmp_216_type 1460 +#define _loop0_217_type 1461 +#define _loop1_218_type 1462 +#define _tmp_219_type 1463 +#define _tmp_220_type 1464 +#define _tmp_221_type 1465 +#define _tmp_222_type 1466 +#define _tmp_223_type 1467 +#define _tmp_224_type 1468 +#define _tmp_225_type 1469 +#define _tmp_226_type 1470 +#define _tmp_227_type 1471 +#define _tmp_228_type 1472 +#define _loop0_230_type 1473 +#define _gather_229_type 1474 +#define _tmp_231_type 1475 +#define _tmp_232_type 1476 +#define _tmp_233_type 1477 +#define _tmp_234_type 1478 +#define _tmp_235_type 1479 +#define _tmp_236_type 1480 +#define _tmp_237_type 1481 +#define _tmp_238_type 1482 +#define _tmp_239_type 1483 +#define _tmp_240_type 1484 +#define _tmp_241_type 1485 +#define _tmp_242_type 1486 +#define _tmp_243_type 1487 +#define _loop0_244_type 1488 +#define _tmp_245_type 1489 +#define _tmp_246_type 1490 +#define _tmp_247_type 1491 +#define _tmp_248_type 1492 +#define _tmp_249_type 1493 +#define _tmp_250_type 1494 +#define _tmp_251_type 1495 +#define _tmp_252_type 1496 +#define _tmp_253_type 1497 +#define _tmp_254_type 1498 +#define _tmp_255_type 1499 +#define _tmp_256_type 1500 +#define _tmp_257_type 1501 +#define _tmp_258_type 1502 +#define _tmp_259_type 1503 +#define _tmp_260_type 1504 +#define _tmp_261_type 1505 +#define _tmp_262_type 1506 +#define _tmp_263_type 1507 +#define _tmp_264_type 1508 +#define _tmp_265_type 1509 +#define _tmp_266_type 1510 +#define _tmp_267_type 1511 +#define _tmp_268_type 1512 +#define _tmp_269_type 1513 +#define _tmp_270_type 1514 +#define _tmp_271_type 1515 +#define _tmp_272_type 1516 +#define _tmp_273_type 1517 +#define _loop0_275_type 1518 +#define _gather_274_type 1519 +#define _tmp_276_type 1520 +#define _tmp_277_type 1521 +#define _tmp_278_type 1522 +#define _tmp_279_type 1523 +#define _tmp_280_type 1524 +#define _tmp_281_type 1525 static mod_ty file_rule(Parser *p); static mod_ty interactive_rule(Parser *p); @@ -826,6 +831,7 @@ static void *invalid_for_target_rule(Parser *p); static void *invalid_group_rule(Parser *p); static void *invalid_import_rule(Parser *p); static void *invalid_import_from_targets_rule(Parser *p); +static void *invalid_compound_stmt_rule(Parser *p); static void *invalid_with_stmt_rule(Parser *p); static void *invalid_with_stmt_indent_rule(Parser *p); static void *invalid_try_stmt_rule(Parser *p); @@ -2040,6 +2046,7 @@ simple_stmt_rule(Parser *p) } // compound_stmt: +// | invalid_compound_stmt // | &('def' | '@' | 'async') function_def // | &'if' if_stmt // | &('class' | '@') class_def @@ -2060,6 +2067,25 @@ compound_stmt_rule(Parser *p) } stmt_ty _res = NULL; int _mark = p->mark; + if (p->call_invalid_rules) { // invalid_compound_stmt + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_compound_stmt")); + void *invalid_compound_stmt_var; + if ( + (invalid_compound_stmt_var = invalid_compound_stmt_rule(p)) // invalid_compound_stmt + ) + { + D(fprintf(stderr, "%*c+ compound_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_compound_stmt")); + _res = invalid_compound_stmt_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s compound_stmt[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_compound_stmt")); + } { // &('def' | '@' | 'async') function_def if (p->error_indicator) { p->level--; @@ -2089,7 +2115,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'if' if_stmt")); stmt_ty if_stmt_var; if ( - _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 656) // token='if' + _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 658) // token='if' && (if_stmt_var = if_stmt_rule(p)) // if_stmt ) @@ -2173,7 +2199,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'try' try_stmt")); stmt_ty try_stmt_var; if ( - _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 638) // token='try' + _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 640) // token='try' && (try_stmt_var = try_stmt_rule(p)) // try_stmt ) @@ -2194,7 +2220,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'while' while_stmt")); stmt_ty while_stmt_var; if ( - _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 661) // token='while' + _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 663) // token='while' && (while_stmt_var = while_stmt_rule(p)) // while_stmt ) @@ -4334,7 +4360,7 @@ class_def_raw_rule(Parser *p) asdl_stmt_seq* c; void *t; if ( - (_keyword = _PyPegen_expect_token(p, 671)) // token='class' + (_keyword = _PyPegen_expect_token(p, 673)) // token='class' && (a = _PyPegen_name_token(p)) // NAME && @@ -4501,7 +4527,7 @@ function_def_raw_rule(Parser *p) void *t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 669)) // token='def' + (_keyword = _PyPegen_expect_token(p, 671)) // token='def' && (n = _PyPegen_name_token(p)) // NAME && @@ -4562,9 +4588,9 @@ function_def_raw_rule(Parser *p) void *t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 669)) // token='def' + (_keyword_1 = _PyPegen_expect_token(p, 671)) // token='def' && (n = _PyPegen_name_token(p)) // NAME && @@ -5902,7 +5928,7 @@ if_stmt_rule(Parser *p) asdl_stmt_seq* b; stmt_ty c; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (a = named_expression_rule(p)) // named_expression && @@ -5947,7 +5973,7 @@ if_stmt_rule(Parser *p) asdl_stmt_seq* b; void *c; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (a = named_expression_rule(p)) // named_expression && @@ -6042,7 +6068,7 @@ elif_stmt_rule(Parser *p) asdl_stmt_seq* b; stmt_ty c; if ( - (_keyword = _PyPegen_expect_token(p, 658)) // token='elif' + (_keyword = _PyPegen_expect_token(p, 660)) // token='elif' && (a = named_expression_rule(p)) // named_expression && @@ -6087,7 +6113,7 @@ elif_stmt_rule(Parser *p) asdl_stmt_seq* b; void *c; if ( - (_keyword = _PyPegen_expect_token(p, 658)) // token='elif' + (_keyword = _PyPegen_expect_token(p, 660)) // token='elif' && (a = named_expression_rule(p)) // named_expression && @@ -6168,7 +6194,7 @@ else_block_rule(Parser *p) Token * _literal; asdl_stmt_seq* b; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='else' + (_keyword = _PyPegen_expect_token(p, 661)) // token='else' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -6247,7 +6273,7 @@ while_stmt_rule(Parser *p) asdl_stmt_seq* b; void *c; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='while' + (_keyword = _PyPegen_expect_token(p, 663)) // token='while' && (a = named_expression_rule(p)) // named_expression && @@ -6347,11 +6373,11 @@ for_stmt_rule(Parser *p) expr_ty t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 666)) // token='for' + (_keyword = _PyPegen_expect_token(p, 668)) // token='for' && (t = star_targets_rule(p)) // star_targets && - (_keyword_1 = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 669)) // token='in' && (_cut_var = 1) && @@ -6409,13 +6435,13 @@ for_stmt_rule(Parser *p) expr_ty t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 666)) // token='for' + (_keyword_1 = _PyPegen_expect_token(p, 668)) // token='for' && (t = star_targets_rule(p)) // star_targets && - (_keyword_2 = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword_2 = _PyPegen_expect_token(p, 669)) // token='in' && (_cut_var = 1) && @@ -6544,7 +6570,7 @@ with_stmt_rule(Parser *p) asdl_stmt_seq* b; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 629)) // token='with' + (_keyword = _PyPegen_expect_token(p, 631)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && @@ -6595,7 +6621,7 @@ with_stmt_rule(Parser *p) asdl_stmt_seq* b; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 629)) // token='with' + (_keyword = _PyPegen_expect_token(p, 631)) // token='with' && (a = (asdl_withitem_seq*)_gather_53_rule(p)) // ','.with_item+ && @@ -6644,9 +6670,9 @@ with_stmt_rule(Parser *p) asdl_withitem_seq* a; asdl_stmt_seq* b; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 629)) // token='with' + (_keyword_1 = _PyPegen_expect_token(p, 631)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && @@ -6696,9 +6722,9 @@ with_stmt_rule(Parser *p) asdl_stmt_seq* b; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 629)) // token='with' + (_keyword_1 = _PyPegen_expect_token(p, 631)) // token='with' && (a = (asdl_withitem_seq*)_gather_57_rule(p)) // ','.with_item+ && @@ -6784,7 +6810,7 @@ with_item_rule(Parser *p) if ( (e = expression_rule(p)) // expression && - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (t = star_target_rule(p)) // star_target && @@ -6909,7 +6935,7 @@ try_stmt_rule(Parser *p) asdl_stmt_seq* b; asdl_stmt_seq* f; if ( - (_keyword = _PyPegen_expect_token(p, 638)) // token='try' + (_keyword = _PyPegen_expect_token(p, 640)) // token='try' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -6953,7 +6979,7 @@ try_stmt_rule(Parser *p) asdl_excepthandler_seq* ex; void *f; if ( - (_keyword = _PyPegen_expect_token(p, 638)) // token='try' + (_keyword = _PyPegen_expect_token(p, 640)) // token='try' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -7001,7 +7027,7 @@ try_stmt_rule(Parser *p) asdl_excepthandler_seq* ex; void *f; if ( - (_keyword = _PyPegen_expect_token(p, 638)) // token='try' + (_keyword = _PyPegen_expect_token(p, 640)) // token='try' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -7099,7 +7125,7 @@ except_block_rule(Parser *p) expr_ty e; void *t; if ( - (_keyword = _PyPegen_expect_token(p, 651)) // token='except' + (_keyword = _PyPegen_expect_token(p, 653)) // token='except' && (e = expression_rule(p)) // expression && @@ -7142,7 +7168,7 @@ except_block_rule(Parser *p) Token * _literal; asdl_stmt_seq* b; if ( - (_keyword = _PyPegen_expect_token(p, 651)) // token='except' + (_keyword = _PyPegen_expect_token(p, 653)) // token='except' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -7253,7 +7279,7 @@ except_star_block_rule(Parser *p) expr_ty e; void *t; if ( - (_keyword = _PyPegen_expect_token(p, 651)) // token='except' + (_keyword = _PyPegen_expect_token(p, 653)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && @@ -7355,7 +7381,7 @@ finally_block_rule(Parser *p) Token * _literal; asdl_stmt_seq* a; if ( - (_keyword = _PyPegen_expect_token(p, 647)) // token='finally' + (_keyword = _PyPegen_expect_token(p, 649)) // token='finally' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -7663,7 +7689,7 @@ guard_rule(Parser *p) Token * _keyword; expr_ty guard; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (guard = named_expression_rule(p)) // named_expression ) @@ -7858,7 +7884,7 @@ as_pattern_rule(Parser *p) if ( (pattern = or_pattern_rule(p)) // or_pattern && - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (target = pattern_capture_target_rule(p)) // pattern_capture_target ) @@ -11081,11 +11107,11 @@ expression_rule(Parser *p) if ( (a = disjunction_rule(p)) // disjunction && - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (b = disjunction_rule(p)) // disjunction && - (_keyword_1 = _PyPegen_expect_token(p, 659)) // token='else' + (_keyword_1 = _PyPegen_expect_token(p, 661)) // token='else' && (c = expression_rule(p)) // expression ) @@ -12623,7 +12649,7 @@ notin_bitwise_or_rule(Parser *p) if ( (_keyword = _PyPegen_expect_token(p, 588)) // token='not' && - (_keyword_1 = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 669)) // token='in' && (a = bitwise_or_rule(p)) // bitwise_or ) @@ -12669,7 +12695,7 @@ in_bitwise_or_rule(Parser *p) Token * _keyword; expr_ty a; if ( - (_keyword = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword = _PyPegen_expect_token(p, 669)) // token='in' && (a = bitwise_or_rule(p)) // bitwise_or ) @@ -16849,13 +16875,13 @@ for_if_clause_rule(Parser *p) expr_ty b; asdl_expr_seq* c; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 666)) // token='for' + (_keyword_1 = _PyPegen_expect_token(p, 668)) // token='for' && (a = star_targets_rule(p)) // star_targets && - (_keyword_2 = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword_2 = _PyPegen_expect_token(p, 669)) // token='in' && (_cut_var = 1) && @@ -16894,11 +16920,11 @@ for_if_clause_rule(Parser *p) expr_ty b; asdl_expr_seq* c; if ( - (_keyword = _PyPegen_expect_token(p, 666)) // token='for' + (_keyword = _PyPegen_expect_token(p, 668)) // token='for' && (a = star_targets_rule(p)) // star_targets && - (_keyword_1 = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 669)) // token='in' && (_cut_var = 1) && @@ -20158,11 +20184,11 @@ expression_without_invalid_rule(Parser *p) if ( (a = disjunction_rule(p)) // disjunction && - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (b = disjunction_rule(p)) // disjunction && - (_keyword_1 = _PyPegen_expect_token(p, 659)) // token='else' + (_keyword_1 = _PyPegen_expect_token(p, 661)) // token='else' && (c = expression_rule(p)) // expression ) @@ -20343,7 +20369,7 @@ invalid_expression_rule(Parser *p) if ( (a = disjunction_rule(p)) // disjunction && - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (b = disjunction_rule(p)) // disjunction && @@ -22281,7 +22307,7 @@ invalid_with_item_rule(Parser *p) if ( (expression_var = expression_rule(p)) // expression && - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (a = expression_rule(p)) // expression && @@ -22331,9 +22357,9 @@ invalid_for_target_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings expr_ty a; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 666)) // token='for' + (_keyword = _PyPegen_expect_token(p, 668)) // token='for' && (a = star_expressions_rule(p)) // star_expressions ) @@ -22540,6 +22566,82 @@ invalid_import_from_targets_rule(Parser *p) return _res; } +// invalid_compound_stmt: 'elif' named_expression ':' | 'else' ':' +static void * +invalid_compound_stmt_rule(Parser *p) +{ + if (p->level++ == MAXSTACK) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // 'elif' named_expression ':' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'elif' named_expression ':'")); + Token * _literal; + Token * a; + expr_ty named_expression_var; + if ( + (a = _PyPegen_expect_token(p, 660)) // token='elif' + && + (named_expression_var = named_expression_rule(p)) // named_expression + && + (_literal = _PyPegen_expect_token(p, 11)) // token=':' + ) + { + D(fprintf(stderr, "%*c+ invalid_compound_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'elif' named_expression ':'")); + _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( a , "'elif' must match an if-statement here" ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_compound_stmt[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'elif' named_expression ':'")); + } + { // 'else' ':' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'else' ':'")); + Token * _literal; + Token * a; + if ( + (a = _PyPegen_expect_token(p, 661)) // token='else' + && + (_literal = _PyPegen_expect_token(p, 11)) // token=':' + ) + { + D(fprintf(stderr, "%*c+ invalid_compound_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else' ':'")); + _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( a , "'else' must match a valid statement here" ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_compound_stmt[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'else' ':'")); + } + _res = NULL; + done: + p->level--; + return _res; +} + // invalid_with_stmt: // | 'async'? 'with' ','.(expression ['as' star_target])+ NEWLINE // | 'async'? 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' NEWLINE @@ -22567,9 +22669,9 @@ invalid_with_stmt_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 629)) // token='with' + (_keyword = _PyPegen_expect_token(p, 631)) // token='with' && (_gather_205_var = _gather_205_rule(p)) // ','.(expression ['as' star_target])+ && @@ -22605,9 +22707,9 @@ invalid_with_stmt_rule(Parser *p) UNUSED(_opt_var_1); // Silence compiler warnings Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 629)) // token='with' + (_keyword = _PyPegen_expect_token(p, 631)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && @@ -22667,9 +22769,9 @@ invalid_with_stmt_indent_rule(Parser *p) Token * a; Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 629)) // token='with' + (a = _PyPegen_expect_token(p, 631)) // token='with' && (_gather_209_var = _gather_209_rule(p)) // ','.(expression ['as' star_target])+ && @@ -22710,9 +22812,9 @@ invalid_with_stmt_indent_rule(Parser *p) Token * a; Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 629)) // token='with' + (a = _PyPegen_expect_token(p, 631)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && @@ -22775,7 +22877,7 @@ invalid_try_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 638)) // token='try' + (a = _PyPegen_expect_token(p, 640)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -22807,7 +22909,7 @@ invalid_try_stmt_rule(Parser *p) Token * _literal; asdl_stmt_seq* block_var; if ( - (_keyword = _PyPegen_expect_token(p, 638)) // token='try' + (_keyword = _PyPegen_expect_token(p, 640)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -22846,7 +22948,7 @@ invalid_try_stmt_rule(Parser *p) Token * b; expr_ty expression_var; if ( - (_keyword = _PyPegen_expect_token(p, 638)) // token='try' + (_keyword = _PyPegen_expect_token(p, 640)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -22854,7 +22956,7 @@ invalid_try_stmt_rule(Parser *p) && (_loop1_215_var = _loop1_215_rule(p)) // except_block+ && - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (b = _PyPegen_expect_token(p, 16)) // token='*' && @@ -22893,7 +22995,7 @@ invalid_try_stmt_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings Token * a; if ( - (_keyword = _PyPegen_expect_token(p, 638)) // token='try' + (_keyword = _PyPegen_expect_token(p, 640)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -22901,7 +23003,7 @@ invalid_try_stmt_rule(Parser *p) && (_loop1_218_var = _loop1_218_rule(p)) // except_star_block+ && - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (_opt_var = _tmp_219_rule(p), !p->error_indicator) // [expression ['as' NAME]] && @@ -22960,7 +23062,7 @@ invalid_except_stmt_rule(Parser *p) expr_ty a; expr_ty expressions_var; if ( - (_keyword = _PyPegen_expect_token(p, 651)) // token='except' + (_keyword = _PyPegen_expect_token(p, 653)) // token='except' && (_opt_var = _PyPegen_expect_token(p, 16), !p->error_indicator) // '*'? && @@ -23002,7 +23104,7 @@ invalid_except_stmt_rule(Parser *p) expr_ty expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (_opt_var = _PyPegen_expect_token(p, 16), !p->error_indicator) // '*'? && @@ -23035,7 +23137,7 @@ invalid_except_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) @@ -23063,7 +23165,7 @@ invalid_except_stmt_rule(Parser *p) void *_tmp_222_var; Token * a; if ( - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && @@ -23112,7 +23214,7 @@ invalid_finally_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 647)) // token='finally' + (a = _PyPegen_expect_token(p, 649)) // token='finally' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23168,7 +23270,7 @@ invalid_except_stmt_indent_rule(Parser *p) expr_ty expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (expression_var = expression_rule(p)) // expression && @@ -23204,7 +23306,7 @@ invalid_except_stmt_indent_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23260,7 +23362,7 @@ invalid_except_star_stmt_indent_rule(Parser *p) expr_ty expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 651)) // token='except' + (a = _PyPegen_expect_token(p, 653)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && @@ -23499,7 +23601,7 @@ invalid_as_pattern_rule(Parser *p) if ( (or_pattern_var = or_pattern_rule(p)) // or_pattern && - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (a = _PyPegen_expect_soft_keyword(p, "_")) // soft_keyword='"_"' ) @@ -23529,7 +23631,7 @@ invalid_as_pattern_rule(Parser *p) if ( (or_pattern_var = or_pattern_rule(p)) // or_pattern && - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && _PyPegen_lookahead_with_name(0, _PyPegen_name_token, p) && @@ -23683,7 +23785,7 @@ invalid_if_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -23714,7 +23816,7 @@ invalid_if_stmt_rule(Parser *p) expr_ty a_1; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 656)) // token='if' + (a = _PyPegen_expect_token(p, 658)) // token='if' && (a_1 = named_expression_rule(p)) // named_expression && @@ -23769,7 +23871,7 @@ invalid_elif_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 658)) // token='elif' + (_keyword = _PyPegen_expect_token(p, 660)) // token='elif' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -23800,7 +23902,7 @@ invalid_elif_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 658)) // token='elif' + (a = _PyPegen_expect_token(p, 660)) // token='elif' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -23853,7 +23955,7 @@ invalid_else_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 659)) // token='else' + (a = _PyPegen_expect_token(p, 661)) // token='else' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -23906,7 +24008,7 @@ invalid_while_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='while' + (_keyword = _PyPegen_expect_token(p, 663)) // token='while' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -23937,7 +24039,7 @@ invalid_while_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 661)) // token='while' + (a = _PyPegen_expect_token(p, 663)) // token='while' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -23996,13 +24098,13 @@ invalid_for_stmt_rule(Parser *p) expr_ty star_expressions_var; expr_ty star_targets_var; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 666)) // token='for' + (_keyword = _PyPegen_expect_token(p, 668)) // token='for' && (star_targets_var = star_targets_rule(p)) // star_targets && - (_keyword_1 = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 669)) // token='in' && (star_expressions_var = star_expressions_rule(p)) // star_expressions && @@ -24037,13 +24139,13 @@ invalid_for_stmt_rule(Parser *p) expr_ty star_expressions_var; expr_ty star_targets_var; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 666)) // token='for' + (a = _PyPegen_expect_token(p, 668)) // token='for' && (star_targets_var = star_targets_rule(p)) // star_targets && - (_keyword = _PyPegen_expect_token(p, 667)) // token='in' + (_keyword = _PyPegen_expect_token(p, 669)) // token='in' && (star_expressions_var = star_expressions_rule(p)) // star_expressions && @@ -24108,9 +24210,9 @@ invalid_def_raw_rule(Parser *p) expr_ty name_var; Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 668), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 670), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 669)) // token='def' + (a = _PyPegen_expect_token(p, 671)) // token='def' && (name_var = _PyPegen_name_token(p)) // NAME && @@ -24179,7 +24281,7 @@ invalid_class_def_raw_rule(Parser *p) expr_ty name_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 671)) // token='class' + (_keyword = _PyPegen_expect_token(p, 673)) // token='class' && (name_var = _PyPegen_name_token(p)) // NAME && @@ -24218,7 +24320,7 @@ invalid_class_def_raw_rule(Parser *p) expr_ty name_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 671)) // token='class' + (a = _PyPegen_expect_token(p, 673)) // token='class' && (name_var = _PyPegen_name_token(p)) // NAME && @@ -25366,7 +25468,7 @@ _tmp_7_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_7[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'def'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 669)) // token='def' + (_keyword = _PyPegen_expect_token(p, 671)) // token='def' ) { D(fprintf(stderr, "%*c+ _tmp_7[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'def'")); @@ -25404,7 +25506,7 @@ _tmp_7_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_7[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' ) { D(fprintf(stderr, "%*c+ _tmp_7[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'")); @@ -25442,7 +25544,7 @@ _tmp_8_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_8[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'class'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 671)) // token='class' + (_keyword = _PyPegen_expect_token(p, 673)) // token='class' ) { D(fprintf(stderr, "%*c+ _tmp_8[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'class'")); @@ -25499,7 +25601,7 @@ _tmp_9_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_9[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'with'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 629)) // token='with' + (_keyword = _PyPegen_expect_token(p, 631)) // token='with' ) { D(fprintf(stderr, "%*c+ _tmp_9[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'with'")); @@ -25518,7 +25620,7 @@ _tmp_9_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_9[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' ) { D(fprintf(stderr, "%*c+ _tmp_9[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'")); @@ -25556,7 +25658,7 @@ _tmp_10_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_10[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'for'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 666)) // token='for' + (_keyword = _PyPegen_expect_token(p, 668)) // token='for' ) { D(fprintf(stderr, "%*c+ _tmp_10[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'for'")); @@ -25575,7 +25677,7 @@ _tmp_10_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_10[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 668)) // token='async' + (_keyword = _PyPegen_expect_token(p, 670)) // token='async' ) { D(fprintf(stderr, "%*c+ _tmp_10[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'")); @@ -26599,7 +26701,7 @@ _tmp_28_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (z = _PyPegen_name_token(p)) // NAME ) @@ -26762,7 +26864,7 @@ _tmp_31_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (z = _PyPegen_name_token(p)) // NAME ) @@ -28749,7 +28851,7 @@ _tmp_62_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (z = _PyPegen_name_token(p)) // NAME ) @@ -28795,7 +28897,7 @@ _tmp_63_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (z = _PyPegen_name_token(p)) // NAME ) @@ -34577,7 +34679,7 @@ _tmp_157_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'else'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 659)) // token='else' + (_keyword = _PyPegen_expect_token(p, 661)) // token='else' ) { D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else'")); @@ -38170,7 +38272,7 @@ _tmp_213_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_213[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'except'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 651)) // token='except' + (_keyword = _PyPegen_expect_token(p, 653)) // token='except' ) { D(fprintf(stderr, "%*c+ _tmp_213[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except'")); @@ -38189,7 +38291,7 @@ _tmp_213_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_213[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'finally'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 647)) // token='finally' + (_keyword = _PyPegen_expect_token(p, 649)) // token='finally' ) { D(fprintf(stderr, "%*c+ _tmp_213[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'finally'")); @@ -38367,7 +38469,7 @@ _tmp_216_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -38589,7 +38691,7 @@ _tmp_220_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -38630,7 +38732,7 @@ _tmp_221_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -38728,7 +38830,7 @@ _tmp_223_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -38769,7 +38871,7 @@ _tmp_224_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -40594,7 +40696,7 @@ _tmp_258_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (z = disjunction_rule(p)) // disjunction ) @@ -40640,7 +40742,7 @@ _tmp_259_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 656)) // token='if' + (_keyword = _PyPegen_expect_token(p, 658)) // token='if' && (z = disjunction_rule(p)) // disjunction ) @@ -41246,7 +41348,7 @@ _tmp_272_rule(Parser *p) Token * _keyword; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME ) @@ -41464,7 +41566,7 @@ _tmp_276_rule(Parser *p) Token * _keyword; expr_ty star_target_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (star_target_var = star_target_rule(p)) // star_target ) @@ -41505,7 +41607,7 @@ _tmp_277_rule(Parser *p) Token * _keyword; expr_ty star_target_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (star_target_var = star_target_rule(p)) // star_target ) @@ -41546,7 +41648,7 @@ _tmp_278_rule(Parser *p) Token * _keyword; expr_ty star_target_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (star_target_var = star_target_rule(p)) // star_target ) @@ -41587,7 +41689,7 @@ _tmp_279_rule(Parser *p) Token * _keyword; expr_ty star_target_var; if ( - (_keyword = _PyPegen_expect_token(p, 654)) // token='as' + (_keyword = _PyPegen_expect_token(p, 656)) // token='as' && (star_target_var = star_target_rule(p)) // star_target ) diff --git a/Parser/pegen_errors.c b/Parser/pegen_errors.c index e2bc3b91c80718..8a02aab1f4e504 100644 --- a/Parser/pegen_errors.c +++ b/Parser/pegen_errors.c @@ -219,6 +219,10 @@ _PyPegen_tokenize_full_source_to_check_for_errors(Parser *p) { void * _PyPegen_raise_error(Parser *p, PyObject *errtype, int use_mark, const char *errmsg, ...) { + // Bail out if we already have an error set. + if (p->error_indicator && PyErr_Occurred()) { + return NULL; + } if (p->fill == 0) { va_list va; va_start(va, errmsg); @@ -278,6 +282,10 @@ get_error_line_from_tokenizer_buffers(Parser *p, Py_ssize_t lineno) Py_ssize_t relative_lineno = p->starting_lineno ? lineno - p->starting_lineno + 1 : lineno; const char* buf_end = p->tok->fp_interactive ? p->tok->interactive_src_end : p->tok->inp; + if (buf_end < cur_line) { + buf_end = cur_line + strlen(cur_line); + } + for (int i = 0; i < relative_lineno - 1; i++) { char *new_line = strchr(cur_line, '\n'); // The assert is here for debug builds but the conditional that @@ -394,7 +402,7 @@ _PyPegen_raise_error_known_location(Parser *p, PyObject *errtype, void _Pypegen_set_syntax_error(Parser* p, Token* last_token) { - // Existing sintax error + // Existing syntax error if (PyErr_Occurred()) { // Prioritize tokenizer errors to custom syntax errors raised // on the second phase only if the errors come from the parser. diff --git a/Python/_warnings.c b/Python/_warnings.c index 4b7fb888247145..d4765032824e56 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -425,15 +425,15 @@ already_warned(PyInterpreterState *interp, PyObject *registry, PyObject *key, Py_DECREF(version_obj); } else { - already_warned = PyDict_GetItemWithError(registry, key); + if (PyDict_GetItemRef(registry, key, &already_warned) < 0) { + return -1; + } if (already_warned != NULL) { int rc = PyObject_IsTrue(already_warned); + Py_DECREF(already_warned); if (rc != 0) return rc; } - else if (PyErr_Occurred()) { - return -1; - } } /* This warning wasn't found in the registry, set it. */ diff --git a/Python/abstract_interp_cases.c.h b/Python/abstract_interp_cases.c.h index a2f6aa8def8f69..96ac0aabd1b59f 100644 --- a/Python/abstract_interp_cases.c.h +++ b/Python/abstract_interp_cases.c.h @@ -624,6 +624,12 @@ break; } + case _FOR_ITER_TIER_TWO: { + STACK_GROW(1); + PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(-1)), true); + break; + } + case _ITER_CHECK_LIST: { break; } @@ -768,7 +774,8 @@ } case _PUSH_FRAME: { - PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(-1)), true); + STACK_SHRINK(1); + PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(0)), true); break; } diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index ff07c498263cd3..960bca01990c83 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -5,7 +5,6 @@ #include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_ceval.h" // _PyEval_Vector() #include "pycore_compile.h" // _PyAST_Compile() -#include "pycore_dict.h" // _PyDict_GetItemWithError() #include "pycore_long.h" // _PyLong_CompactValue #include "pycore_modsupport.h" // _PyArg_NoKwnames() #include "pycore_object.h" // _Py_AddToAllObjects() @@ -141,18 +140,16 @@ builtin___build_class__(PyObject *self, PyObject *const *args, Py_ssize_t nargs, goto error; } - meta = _PyDict_GetItemWithError(mkw, &_Py_ID(metaclass)); + if (PyDict_GetItemRef(mkw, &_Py_ID(metaclass), &meta) < 0) { + goto error; + } if (meta != NULL) { - Py_INCREF(meta); if (PyDict_DelItem(mkw, &_Py_ID(metaclass)) < 0) { goto error; } /* metaclass is explicitly given, check if it's indeed a class */ isclass = PyType_Check(meta); } - else if (PyErr_Occurred()) { - goto error; - } } if (meta == NULL) { /* if there are no bases, use type: */ @@ -2265,6 +2262,11 @@ builtin_input_impl(PyObject *module, PyObject *prompt) goto _readline_errors; assert(PyBytes_Check(po)); promptstr = PyBytes_AS_STRING(po); + if ((Py_ssize_t)strlen(promptstr) != PyBytes_GET_SIZE(po)) { + PyErr_SetString(PyExc_ValueError, + "input: prompt string cannot contain null characters"); + goto _readline_errors; + } } else { po = NULL; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index da98630f53943a..bcad8dcf0e7dab 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -800,11 +800,11 @@ dummy_func( // We also push it onto the stack on exit, but that's a // different frame, and it's accounted for by _PUSH_FRAME. op(_POP_FRAME, (retval --)) { - assert(EMPTY()); #if TIER_ONE assert(frame != &entry_frame); #endif STORE_SP(); + assert(EMPTY()); _Py_LeaveRecursiveCallPy(tstate); // GH-99729: We need to unlink the frame *before* clearing it: _PyInterpreterFrame *dying = frame; @@ -1165,7 +1165,6 @@ dummy_func( } } - inst(STORE_NAME, (v -- )) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); PyObject *ns = LOCALS(); @@ -2357,7 +2356,7 @@ dummy_func( JUMPBY(1-original_oparg); frame->instr_ptr = next_instr; Py_INCREF(executor); - if (executor->execute == _PyUopExecute) { + if (executor->execute == _PyUOpExecute) { current_executor = (_PyUOpExecutorObject *)executor; GOTO_TIER_TWO(); } @@ -2369,7 +2368,7 @@ dummy_func( goto enter_tier_one; } - replaced op(_POP_JUMP_IF_FALSE, (unused/1, cond -- )) { + replaced op(_POP_JUMP_IF_FALSE, (unused/1, cond -- )) { assert(PyBool_Check(cond)); int flag = Py_IsFalse(cond); #if ENABLE_SPECIALIZATION @@ -2513,7 +2512,7 @@ dummy_func( #endif /* ENABLE_SPECIALIZATION */ } - op(_FOR_ITER, (iter -- iter, next)) { + replaced op(_FOR_ITER, (iter -- iter, next)) { /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ next = (*Py_TYPE(iter)->tp_iternext)(iter); if (next == NULL) { @@ -2536,6 +2535,25 @@ dummy_func( // Common case: no jump, leave it to the code generator } + op(_FOR_ITER_TIER_TWO, (iter -- iter, next)) { + /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ + next = (*Py_TYPE(iter)->tp_iternext)(iter); + if (next == NULL) { + if (_PyErr_Occurred(tstate)) { + if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { + GOTO_ERROR(error); + } + _PyErr_Clear(tstate); + } + /* iterator ended normally */ + Py_DECREF(iter); + STACK_SHRINK(1); + /* The translator sets the deopt target just past END_FOR */ + DEOPT_IF(true); + } + // Common case: no jump, leave it to the code generator + } + macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _FOR_ITER; inst(INSTRUMENTED_FOR_ITER, (unused/1 -- )) { @@ -2812,15 +2830,15 @@ dummy_func( ERROR_IF(res == NULL, error); } - pseudo(SETUP_FINALLY) = { + pseudo(SETUP_FINALLY, (HAS_ARG)) = { NOP, }; - pseudo(SETUP_CLEANUP) = { + pseudo(SETUP_CLEANUP, (HAS_ARG)) = { NOP, }; - pseudo(SETUP_WITH) = { + pseudo(SETUP_WITH, (HAS_ARG)) = { NOP, }; @@ -3111,7 +3129,7 @@ dummy_func( // The 'unused' output effect represents the return value // (which will be pushed when the frame returns). // It is needed so CALL_PY_EXACT_ARGS matches its family. - op(_PUSH_FRAME, (new_frame: _PyInterpreterFrame* -- unused)) { + op(_PUSH_FRAME, (new_frame: _PyInterpreterFrame* -- unused if (0))) { // Write it out explicitly because it's subtly different. // Eventually this should be the only occurrence of this code. assert(tstate->interp->eval_frame == NULL); diff --git a/Python/ceval.c b/Python/ceval.c index ae515780440ecb..f8fa50eb46c75e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -46,13 +46,13 @@ # error "ceval.c must be build with Py_BUILD_CORE define for best performance" #endif -#if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_NOGIL) +#if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_GIL_DISABLED) // GH-89279: The MSVC compiler does not inline these static inline functions // in PGO build in _PyEval_EvalFrameDefault(), because this function is over // the limit of PGO, and that limit cannot be configured. // Define them as macros to make sure that they are always inlined by the // preprocessor. -// TODO: implement Py_DECREF macro for Py_NOGIL +// TODO: implement Py_DECREF macro for Py_GIL_DISABLED #undef Py_DECREF #define Py_DECREF(arg) \ @@ -647,7 +647,7 @@ static const _Py_CODEUNIT _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS[] = { extern const struct _PyCode_DEF(8) _Py_InitCleanup; -extern const char *_PyUopName(int index); +extern const char *_PyUOpName(int index); /* Disable unused label warnings. They are handy for debugging, even if computed gotos aren't used. */ @@ -689,7 +689,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #ifdef Py_STATS int lastopcode = 0; #endif - int opcode; /* Current opcode */ + uint8_t opcode; /* Current opcode */ int oparg; /* Current opcode argument, if any */ #ifdef LLTRACE int lltrace = 0; @@ -776,9 +776,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int /* Start instructions */ #if !USE_COMPUTED_GOTOS dispatch_opcode: - // Cast to an 8-bit value to improve the code generated by MSVC - // (in combination with the EXTRA_CASES macro). - switch ((uint8_t)opcode) + switch (opcode) #endif { @@ -822,7 +820,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #if USE_COMPUTED_GOTOS _unknown_opcode: #else - EXTRA_CASES // From pycore_opcode.h, a 'case' for each unused opcode + EXTRA_CASES // From pycore_opcode_metadata.h, a 'case' for each unused opcode #endif /* Tell C compilers not to hold the opcode variable in the loop. next_instr points the current instruction without TARGET(). */ @@ -994,39 +992,38 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int OPT_STAT_INC(traces_executed); _PyUOpInstruction *next_uop = current_executor->trace; - uint64_t operand; + uint16_t uopcode; #ifdef Py_STATS uint64_t trace_uop_execution_counter = 0; #endif for (;;) { - opcode = next_uop->opcode; - oparg = next_uop->oparg; - operand = next_uop->operand; + uopcode = next_uop->opcode; DPRINTF(3, "%4d: uop %s, oparg %d, operand %" PRIu64 ", target %d, stack_level %d\n", (int)(next_uop - current_executor->trace), - _PyUopName(opcode), - oparg, - operand, + _PyUOpName(uopcode), + next_uop->oparg, + next_uop->operand, next_uop->target, (int)(stack_pointer - _PyFrame_Stackbase(frame))); next_uop++; OPT_STAT_INC(uops_executed); - UOP_STAT_INC(opcode, execution_count); + UOP_STAT_INC(uopcode, execution_count); #ifdef Py_STATS trace_uop_execution_counter++; #endif - switch (opcode) { + switch (uopcode) { #include "executor_cases.c.h" default: #ifdef Py_DEBUG { - fprintf(stderr, "Unknown uop %d, oparg %d, operand %" PRIu64 "\n", - opcode, oparg, operand); + fprintf(stderr, "Unknown uop %d, oparg %d, operand %" PRIu64 " @ %d\n", + opcode, next_uop[-1].oparg, next_uop[-1].operand, + (int)(next_uop - current_executor->trace - 1)); Py_FatalError("Unknown uop"); } #else @@ -1054,8 +1051,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int pop_1_error_tier_two: STACK_SHRINK(1); error_tier_two: - DPRINTF(2, "Error: [Uop %d (%s), oparg %d, operand %" PRIu64 ", target %d @ %d]\n", - opcode, _PyUopName(opcode), oparg, operand, next_uop[-1].target, + DPRINTF(2, "Error: [UOp %d (%s), oparg %d, operand %" PRIu64 ", target %d @ %d]\n", + uopcode, _PyUOpName(uopcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target, (int)(next_uop - current_executor->trace - 1)); OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); frame->return_offset = 0; // Don't leave this random @@ -1067,14 +1064,14 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int deoptimize: // On DEOPT_IF we just repeat the last instruction. // This presumes nothing was popped from the stack (nor pushed). - DPRINTF(2, "DEOPT: [Uop %d (%s), oparg %d, operand %" PRIu64 ", target %d @ %d]\n", - opcode, _PyUopName(opcode), oparg, operand, next_uop[-1].target, + DPRINTF(2, "DEOPT: [UOp %d (%s), oparg %d, operand %" PRIu64 ", target %d @ %d]\n", + uopcode, _PyUOpName(uopcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target, (int)(next_uop - current_executor->trace - 1)); OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); - UOP_STAT_INC(opcode, miss); + UOP_STAT_INC(uopcode, miss); frame->return_offset = 0; // Dispatch to frame->instr_ptr _PyFrame_SetStackPointer(frame, stack_pointer); - frame->instr_ptr = next_uop[-1].target + _PyCode_CODE((PyCodeObject *)frame->f_executable); + frame->instr_ptr = next_uop[-1].target + _PyCode_CODE(_PyFrame_GetCode(frame)); Py_DECREF(current_executor); // Fall through // Jump here from ENTER_EXECUTOR @@ -1085,7 +1082,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int // Jump here from _EXIT_TRACE exit_trace: _PyFrame_SetStackPointer(frame, stack_pointer); - frame->instr_ptr = next_uop[-1].target + _PyCode_CODE((PyCodeObject *)frame->f_executable); + frame->instr_ptr = next_uop[-1].target + _PyCode_CODE(_PyFrame_GetCode(frame)); Py_DECREF(current_executor); OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); goto enter_tier_one; @@ -1923,6 +1920,13 @@ do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause) fixed_cause = _PyObject_CallNoArgs(cause); if (fixed_cause == NULL) goto raise_error; + if (!PyExceptionInstance_Check(fixed_cause)) { + _PyErr_Format(tstate, PyExc_TypeError, + "calling %R should have returned an instance of " + "BaseException, not %R", + cause, Py_TYPE(fixed_cause)); + goto raise_error; + } Py_DECREF(cause); } else if (PyExceptionInstance_Check(cause)) { @@ -2610,11 +2614,10 @@ import_from(PyThreadState *tstate, PyObject *v, PyObject *name) /* Issue #17636: in case this failed because of a circular relative import, try to fallback on reading the module directly from sys.modules. */ - pkgname = PyObject_GetAttr(v, &_Py_ID(__name__)); - if (pkgname == NULL) { - goto error; + if (PyObject_GetOptionalAttr(v, &_Py_ID(__name__), &pkgname) < 0) { + return NULL; } - if (!PyUnicode_Check(pkgname)) { + if (pkgname == NULL || !PyUnicode_Check(pkgname)) { Py_CLEAR(pkgname); goto error; } @@ -2631,42 +2634,59 @@ import_from(PyThreadState *tstate, PyObject *v, PyObject *name) Py_DECREF(pkgname); return x; error: - pkgpath = PyModule_GetFilenameObject(v); if (pkgname == NULL) { pkgname_or_unknown = PyUnicode_FromString(""); if (pkgname_or_unknown == NULL) { - Py_XDECREF(pkgpath); return NULL; } } else { pkgname_or_unknown = pkgname; } + pkgpath = NULL; + if (PyModule_Check(v)) { + pkgpath = PyModule_GetFilenameObject(v); + if (pkgpath == NULL) { + if (!PyErr_ExceptionMatches(PyExc_SystemError)) { + Py_DECREF(pkgname_or_unknown); + return NULL; + } + // module filename missing + _PyErr_Clear(tstate); + } + } if (pkgpath == NULL || !PyUnicode_Check(pkgpath)) { - _PyErr_Clear(tstate); + Py_CLEAR(pkgpath); errmsg = PyUnicode_FromFormat( "cannot import name %R from %R (unknown location)", name, pkgname_or_unknown ); - /* NULL checks for errmsg and pkgname done by PyErr_SetImportError. */ - _PyErr_SetImportErrorWithNameFrom(errmsg, pkgname, NULL, name); } else { - PyObject *spec = PyObject_GetAttr(v, &_Py_ID(__spec__)); + PyObject *spec; + int rc = PyObject_GetOptionalAttr(v, &_Py_ID(__spec__), &spec); + if (rc > 0) { + rc = _PyModuleSpec_IsInitializing(spec); + Py_DECREF(spec); + } + if (rc < 0) { + Py_DECREF(pkgname_or_unknown); + Py_DECREF(pkgpath); + return NULL; + } const char *fmt = - _PyModuleSpec_IsInitializing(spec) ? + rc ? "cannot import name %R from partially initialized module %R " "(most likely due to a circular import) (%S)" : "cannot import name %R from %R (%S)"; - Py_XDECREF(spec); errmsg = PyUnicode_FromFormat(fmt, name, pkgname_or_unknown, pkgpath); - /* NULL checks for errmsg and pkgname done by PyErr_SetImportError. */ - _PyErr_SetImportErrorWithNameFrom(errmsg, pkgname, pkgpath, name); } + /* NULL checks for errmsg and pkgname done by PyErr_SetImportError. */ + _PyErr_SetImportErrorWithNameFrom(errmsg, pkgname, pkgpath, name); Py_XDECREF(errmsg); - Py_XDECREF(pkgname_or_unknown); + Py_DECREF(pkgname_or_unknown); Py_XDECREF(pkgpath); return NULL; } diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index 92c4b2fee9f863..636e4db898f2d9 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -589,9 +589,7 @@ _PyEval_ReInitThreads(PyThreadState *tstate) take_gil(tstate); struct _pending_calls *pending = &tstate->interp->ceval.pending; - if (_PyThread_at_fork_reinit(&pending->lock) < 0) { - return _PyStatus_ERR("Can't reinitialize pending calls lock"); - } + _PyMutex_at_fork_reinit(&pending->mutex); /* Destroy all threads except the current one */ _PyThreadState_DeleteExcept(tstate); @@ -720,13 +718,10 @@ _PyEval_AddPendingCall(PyInterpreterState *interp, assert(_Py_IsMainInterpreter(interp)); pending = &_PyRuntime.ceval.pending_mainthread; } - /* Ensure that _PyEval_InitState() was called - and that _PyEval_FiniState() is not called yet. */ - assert(pending->lock != NULL); - PyThread_acquire_lock(pending->lock, WAIT_LOCK); + PyMutex_Lock(&pending->mutex); int result = _push_pending_call(pending, func, arg, flags); - PyThread_release_lock(pending->lock); + PyMutex_Unlock(&pending->mutex); /* signal main loop */ SIGNAL_PENDING_CALLS(interp); @@ -768,9 +763,9 @@ _make_pending_calls(struct _pending_calls *pending) int flags = 0; /* pop one item off the queue while holding the lock */ - PyThread_acquire_lock(pending->lock, WAIT_LOCK); + PyMutex_Lock(&pending->mutex); _pop_pending_call(pending, &func, &arg, &flags); - PyThread_release_lock(pending->lock); + PyMutex_Unlock(&pending->mutex); /* having released the lock, perform the callback */ if (func == NULL) { @@ -795,7 +790,7 @@ make_pending_calls(PyInterpreterState *interp) /* Only one thread (per interpreter) may run the pending calls at once. In the same way, we don't do recursive pending calls. */ - PyThread_acquire_lock(pending->lock, WAIT_LOCK); + PyMutex_Lock(&pending->mutex); if (pending->busy) { /* A pending call was added after another thread was already handling the pending calls (and had already "unsignaled"). @@ -807,11 +802,11 @@ make_pending_calls(PyInterpreterState *interp) care of any remaining pending calls. Until then, though, all the interpreter's threads will be tripping the eval breaker every time it's checked. */ - PyThread_release_lock(pending->lock); + PyMutex_Unlock(&pending->mutex); return 0; } pending->busy = 1; - PyThread_release_lock(pending->lock); + PyMutex_Unlock(&pending->mutex); /* unsignal before starting to call callbacks, so that any callback added in-between re-signals */ @@ -892,23 +887,9 @@ Py_MakePendingCalls(void) } void -_PyEval_InitState(PyInterpreterState *interp, PyThread_type_lock pending_lock) +_PyEval_InitState(PyInterpreterState *interp) { _gil_initialize(&interp->_gil); - - struct _pending_calls *pending = &interp->ceval.pending; - assert(pending->lock == NULL); - pending->lock = pending_lock; -} - -void -_PyEval_FiniState(struct _ceval_state *ceval) -{ - struct _pending_calls *pending = &ceval->pending; - if (pending->lock != NULL) { - PyThread_free_lock(pending->lock); - pending->lock = NULL; - } } diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 546adbe5f438d1..b0cb7c8926338c 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -397,3 +397,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); #define GOTO_TIER_TWO() goto enter_tier_two; #define GOTO_TIER_ONE() goto exit_trace; + +#define CURRENT_OPARG() (next_uop[-1].oparg) + +#define CURRENT_OPERAND() (next_uop[-1].operand) diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 98717ecc875b8b..93b8385a5b4097 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -289,6 +289,40 @@ sys_intern(PyObject *module, PyObject *arg) return return_value; } +PyDoc_STRVAR(sys__is_interned__doc__, +"_is_interned($module, string, /)\n" +"--\n" +"\n" +"Return True if the given string is \"interned\"."); + +#define SYS__IS_INTERNED_METHODDEF \ + {"_is_interned", (PyCFunction)sys__is_interned, METH_O, sys__is_interned__doc__}, + +static int +sys__is_interned_impl(PyObject *module, PyObject *string); + +static PyObject * +sys__is_interned(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *string; + int _return_value; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("_is_interned", "argument", "str", arg); + goto exit; + } + string = arg; + _return_value = sys__is_interned_impl(module, string); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); + +exit: + return return_value; +} + PyDoc_STRVAR(sys__settraceallthreads__doc__, "_settraceallthreads($module, arg, /)\n" "--\n" @@ -1452,4 +1486,4 @@ sys__get_cpu_count_config(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=f36d45c829250775 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3dc3b2cb0ce38ebb input=a9049054013a1b77]*/ diff --git a/Python/codecs.c b/Python/codecs.c index b79bf555f2f22a..d8fe7b22063a80 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -146,15 +146,14 @@ PyObject *_PyCodec_Lookup(const char *encoding) PyUnicode_InternInPlace(&v); /* First, try to lookup the name in the registry dictionary */ - PyObject *result = PyDict_GetItemWithError(interp->codec_search_cache, v); + PyObject *result; + if (PyDict_GetItemRef(interp->codec_search_cache, v, &result) < 0) { + goto onError; + } if (result != NULL) { - Py_INCREF(result); Py_DECREF(v); return result; } - else if (PyErr_Occurred()) { - goto onError; - } /* Next, scan the search functions in order of registration */ const Py_ssize_t len = PyList_Size(interp->codec_search_path); @@ -932,8 +931,6 @@ PyObject *PyCodec_BackslashReplaceErrors(PyObject *exc) return Py_BuildValue("(Nn)", res, end); } -static _PyUnicode_Name_CAPI *ucnhash_capi = NULL; - PyObject *PyCodec_NameReplaceErrors(PyObject *exc) { if (PyObject_TypeCheck(exc, (PyTypeObject *)PyExc_UnicodeEncodeError)) { @@ -954,13 +951,9 @@ PyObject *PyCodec_NameReplaceErrors(PyObject *exc) return NULL; if (!(object = PyUnicodeEncodeError_GetObject(exc))) return NULL; - if (!ucnhash_capi) { - /* load the unicode data module */ - ucnhash_capi = (_PyUnicode_Name_CAPI *)PyCapsule_Import( - PyUnicodeData_CAPSULE_NAME, 1); - if (!ucnhash_capi) { - return NULL; - } + _PyUnicode_Name_CAPI *ucnhash_capi = _PyUnicode_GetNameCAPI(); + if (ucnhash_capi == NULL) { + return NULL; } for (i = start, ressize = 0; i < end; ++i) { /* object is guaranteed to be "ready" */ diff --git a/Python/compile.c b/Python/compile.c index 8b1eef79a79eae..8b9e2f02048f11 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -5042,8 +5042,12 @@ compiler_joined_str(struct compiler *c, expr_ty e) } else { VISIT_SEQ(c, expr, e->v.JoinedStr.values); - if (asdl_seq_LEN(e->v.JoinedStr.values) != 1) { - ADDOP_I(c, loc, BUILD_STRING, asdl_seq_LEN(e->v.JoinedStr.values)); + if (value_count > 1) { + ADDOP_I(c, loc, BUILD_STRING, value_count); + } + else if (value_count == 0) { + _Py_DECLARE_STR(empty, ""); + ADDOP_LOAD_CONST_NEW(c, loc, Py_NewRef(&_Py_STR(empty))); } } return SUCCESS; diff --git a/Python/crossinterp.c b/Python/crossinterp.c index a908f9ae340ee9..f74fee38648266 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -5,8 +5,10 @@ #include "pycore_ceval.h" // _Py_simple_func #include "pycore_crossinterp.h" // struct _xid #include "pycore_initconfig.h" // _PyStatus_OK() +#include "pycore_namespace.h" //_PyNamespace_New() #include "pycore_pyerrors.h" // _PyErr_Clear() #include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_typeobject.h" // _PyType_GetModuleName() #include "pycore_weakref.h" // _PyWeakref_GET_REF() @@ -454,16 +456,17 @@ _xidregistry_clear(struct _xidregistry *xidregistry) static void _xidregistry_lock(struct _xidregistry *registry) { - if (registry->mutex != NULL) { - PyThread_acquire_lock(registry->mutex, WAIT_LOCK); + if (registry->global) { + PyMutex_Lock(®istry->mutex); } + // else: Within an interpreter we rely on the GIL instead of a separate lock. } static void _xidregistry_unlock(struct _xidregistry *registry) { - if (registry->mutex != NULL) { - PyThread_release_lock(registry->mutex); + if (registry->global) { + PyMutex_Unlock(®istry->mutex); } } @@ -564,6 +567,8 @@ _lookup_getdata_from_registry(PyInterpreterState *interp, PyObject *obj) /* cross-interpreter data for builtin types */ +// bytes + struct _shared_bytes_data { char *bytes; Py_ssize_t len; @@ -595,6 +600,8 @@ _bytes_shared(PyThreadState *tstate, PyObject *obj, return 0; } +// str + struct _shared_str_data { int kind; const void *buffer; @@ -626,6 +633,8 @@ _str_shared(PyThreadState *tstate, PyObject *obj, return 0; } +// int + static PyObject * _new_long_object(_PyCrossInterpreterData *data) { @@ -653,6 +662,8 @@ _long_shared(PyThreadState *tstate, PyObject *obj, return 0; } +// float + static PyObject * _new_float_object(_PyCrossInterpreterData *data) { @@ -676,6 +687,8 @@ _float_shared(PyThreadState *tstate, PyObject *obj, return 0; } +// None + static PyObject * _new_none_object(_PyCrossInterpreterData *data) { @@ -693,6 +706,8 @@ _none_shared(PyThreadState *tstate, PyObject *obj, return 0; } +// bool + static PyObject * _new_bool_object(_PyCrossInterpreterData *data) { @@ -713,6 +728,8 @@ _bool_shared(PyThreadState *tstate, PyObject *obj, return 0; } +// tuple + struct _shared_tuple_data { Py_ssize_t len; _PyCrossInterpreterData **data; @@ -806,6 +823,8 @@ _tuple_shared(PyThreadState *tstate, PyObject *obj, return -1; } +// registration + static void _register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry) { @@ -856,19 +875,10 @@ _xidregistry_init(struct _xidregistry *registry) registry->initialized = 1; if (registry->global) { - // We manage the mutex lifecycle in pystate.c. - assert(registry->mutex != NULL); - // Registering the builtins is cheap so we don't bother doing it lazily. assert(registry->head == NULL); _register_builtins_for_crossinterpreter_data(registry); } - else { - // Within an interpreter we rely on the GIL instead of a separate lock. - assert(registry->mutex == NULL); - - // There's nothing else to initialize. - } } static void @@ -880,17 +890,6 @@ _xidregistry_fini(struct _xidregistry *registry) registry->initialized = 0; _xidregistry_clear(registry); - - if (registry->global) { - // We manage the mutex lifecycle in pystate.c. - assert(registry->mutex != NULL); - } - else { - // There's nothing else to finalize. - - // Within an interpreter we rely on the GIL instead of a separate lock. - assert(registry->mutex == NULL); - } } @@ -898,17 +897,6 @@ _xidregistry_fini(struct _xidregistry *registry) /* convenience utilities */ /*************************/ -static const char * -_copy_raw_string(const char *str) -{ - char *copied = PyMem_RawMalloc(strlen(str)+1); - if (copied == NULL) { - return NULL; - } - strcpy(copied, str); - return copied; -} - static const char * _copy_string_obj_raw(PyObject *strobj) { @@ -944,115 +932,309 @@ _release_xid_data(_PyCrossInterpreterData *data, int rawfree) } +/***********************/ /* exception snapshots */ +/***********************/ static int -_exc_type_name_as_utf8(PyObject *exc, const char **p_typename) +_excinfo_init_type(struct _excinfo_type *info, PyObject *exc) { - // XXX Use PyObject_GetAttrString(Py_TYPE(exc), '__name__')? - PyObject *nameobj = PyUnicode_FromString(Py_TYPE(exc)->tp_name); - if (nameobj == NULL) { - assert(PyErr_Occurred()); - *p_typename = "unable to format exception type name"; - return -1; + /* Note that this copies directly rather than into an intermediate + struct and does not clear on error. If we need that then we + should have a separate function to wrap this one + and do all that there. */ + PyObject *strobj = NULL; + + PyTypeObject *type = Py_TYPE(exc); + if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { + assert(_Py_IsImmortal((PyObject *)type)); + info->builtin = type; } - const char *name = PyUnicode_AsUTF8(nameobj); - if (name == NULL) { - assert(PyErr_Occurred()); - Py_DECREF(nameobj); - *p_typename = "unable to encode exception type name"; + else { + // Only builtin types are preserved. + info->builtin = NULL; + } + + // __name__ + strobj = PyType_GetName(type); + if (strobj == NULL) { return -1; } - name = _copy_raw_string(name); - Py_DECREF(nameobj); - if (name == NULL) { - *p_typename = "out of memory copying exception type name"; + info->name = _copy_string_obj_raw(strobj); + Py_DECREF(strobj); + if (info->name == NULL) { return -1; } - *p_typename = name; - return 0; -} -static int -_exc_msg_as_utf8(PyObject *exc, const char **p_msg) -{ - PyObject *msgobj = PyObject_Str(exc); - if (msgobj == NULL) { - assert(PyErr_Occurred()); - *p_msg = "unable to format exception message"; + // __qualname__ + strobj = PyType_GetQualName(type); + if (strobj == NULL) { return -1; } - const char *msg = PyUnicode_AsUTF8(msgobj); - if (msg == NULL) { - assert(PyErr_Occurred()); - Py_DECREF(msgobj); - *p_msg = "unable to encode exception message"; + info->qualname = _copy_string_obj_raw(strobj); + Py_DECREF(strobj); + if (info->name == NULL) { return -1; } - msg = _copy_raw_string(msg); - Py_DECREF(msgobj); - if (msg == NULL) { - assert(PyErr_ExceptionMatches(PyExc_MemoryError)); - *p_msg = "out of memory copying exception message"; + + // __module__ + strobj = _PyType_GetModuleName(type); + if (strobj == NULL) { + return -1; + } + info->module = _copy_string_obj_raw(strobj); + Py_DECREF(strobj); + if (info->name == NULL) { return -1; } - *p_msg = msg; + return 0; } static void -_Py_excinfo_Clear(_Py_excinfo *info) +_excinfo_clear_type(struct _excinfo_type *info) { - if (info->type != NULL) { - PyMem_RawFree((void *)info->type); + if (info->builtin != NULL) { + assert(info->builtin->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN); + assert(_Py_IsImmortal((PyObject *)info->builtin)); + } + if (info->name != NULL) { + PyMem_RawFree((void *)info->name); + } + if (info->qualname != NULL) { + PyMem_RawFree((void *)info->qualname); + } + if (info->module != NULL) { + PyMem_RawFree((void *)info->module); + } + *info = (struct _excinfo_type){NULL}; +} + +static void +_excinfo_normalize_type(struct _excinfo_type *info, + const char **p_module, const char **p_qualname) +{ + if (info->name == NULL) { + assert(info->builtin == NULL); + assert(info->qualname == NULL); + assert(info->module == NULL); + // This is inspired by TracebackException.format_exception_only(). + *p_module = NULL; + *p_qualname = NULL; + return; + } + + const char *module = info->module; + const char *qualname = info->qualname; + if (qualname == NULL) { + qualname = info->name; + } + assert(module != NULL); + if (strcmp(module, "builtins") == 0) { + module = NULL; + } + else if (strcmp(module, "__main__") == 0) { + module = NULL; } + *p_qualname = qualname; + *p_module = module; +} + +static void +_PyXI_excinfo_Clear(_PyXI_excinfo *info) +{ + _excinfo_clear_type(&info->type); if (info->msg != NULL) { PyMem_RawFree((void *)info->msg); } - *info = (_Py_excinfo){ NULL }; + *info = (_PyXI_excinfo){{NULL}}; +} + +static PyObject * +_PyXI_excinfo_format(_PyXI_excinfo *info) +{ + const char *module, *qualname; + _excinfo_normalize_type(&info->type, &module, &qualname); + if (qualname != NULL) { + if (module != NULL) { + if (info->msg != NULL) { + return PyUnicode_FromFormat("%s.%s: %s", + module, qualname, info->msg); + } + else { + return PyUnicode_FromFormat("%s.%s", module, qualname); + } + } + else { + if (info->msg != NULL) { + return PyUnicode_FromFormat("%s: %s", qualname, info->msg); + } + else { + return PyUnicode_FromString(qualname); + } + } + } + else if (info->msg != NULL) { + return PyUnicode_FromString(info->msg); + } + else { + Py_RETURN_NONE; + } } static const char * -_Py_excinfo_InitFromException(_Py_excinfo *info, PyObject *exc) +_PyXI_excinfo_InitFromException(_PyXI_excinfo *info, PyObject *exc) { assert(exc != NULL); - // Extract the exception type name. - const char *typename = NULL; - if (_exc_type_name_as_utf8(exc, &typename) < 0) { - assert(typename != NULL); - return typename; + if (PyErr_GivenExceptionMatches(exc, PyExc_MemoryError)) { + _PyXI_excinfo_Clear(info); + return NULL; + } + const char *failure = NULL; + + if (_excinfo_init_type(&info->type, exc) < 0) { + failure = "error while initializing exception type snapshot"; + goto error; } // Extract the exception message. - const char *msg = NULL; - if (_exc_msg_as_utf8(exc, &msg) < 0) { - assert(msg != NULL); - return msg; + PyObject *msgobj = PyObject_Str(exc); + if (msgobj == NULL) { + failure = "error while formatting exception"; + goto error; + } + info->msg = _copy_string_obj_raw(msgobj); + Py_DECREF(msgobj); + if (info->msg == NULL) { + failure = "error while copying exception message"; + goto error; } - info->type = typename; - info->msg = msg; return NULL; + +error: + assert(failure != NULL); + _PyXI_excinfo_Clear(info); + return failure; } static void -_Py_excinfo_Apply(_Py_excinfo *info, PyObject *exctype) +_PyXI_excinfo_Apply(_PyXI_excinfo *info, PyObject *exctype) +{ + PyObject *formatted = _PyXI_excinfo_format(info); + PyErr_SetObject(exctype, formatted); + Py_DECREF(formatted); +} + +static PyObject * +_PyXI_excinfo_TypeAsObject(_PyXI_excinfo *info) { - if (info->type != NULL) { - if (info->msg != NULL) { - PyErr_Format(exctype, "%s: %s", info->type, info->msg); + PyObject *ns = _PyNamespace_New(NULL); + if (ns == NULL) { + return NULL; + } + int empty = 1; + + if (info->type.name != NULL) { + PyObject *name = PyUnicode_FromString(info->type.name); + if (name == NULL) { + goto error; } - else { - PyErr_SetString(exctype, info->type); + int res = PyObject_SetAttrString(ns, "__name__", name); + Py_DECREF(name); + if (res < 0) { + goto error; } + empty = 0; } - else if (info->msg != NULL) { - PyErr_SetString(exctype, info->msg); + + if (info->type.qualname != NULL) { + PyObject *qualname = PyUnicode_FromString(info->type.qualname); + if (qualname == NULL) { + goto error; + } + int res = PyObject_SetAttrString(ns, "__qualname__", qualname); + Py_DECREF(qualname); + if (res < 0) { + goto error; + } + empty = 0; } - else { - PyErr_SetNone(exctype); + + if (info->type.module != NULL) { + PyObject *module = PyUnicode_FromString(info->type.module); + if (module == NULL) { + goto error; + } + int res = PyObject_SetAttrString(ns, "__module__", module); + Py_DECREF(module); + if (res < 0) { + goto error; + } + empty = 0; + } + + if (empty) { + Py_CLEAR(ns); + } + + return ns; + +error: + Py_DECREF(ns); + return NULL; +} + +static PyObject * +_PyXI_excinfo_AsObject(_PyXI_excinfo *info) +{ + PyObject *ns = _PyNamespace_New(NULL); + if (ns == NULL) { + return NULL; + } + int res; + + PyObject *type = _PyXI_excinfo_TypeAsObject(info); + if (type == NULL) { + if (PyErr_Occurred()) { + goto error; + } + type = Py_NewRef(Py_None); + } + res = PyObject_SetAttrString(ns, "type", type); + Py_DECREF(type); + if (res < 0) { + goto error; + } + + PyObject *msg = info->msg != NULL + ? PyUnicode_FromString(info->msg) + : Py_NewRef(Py_None); + if (msg == NULL) { + goto error; + } + res = PyObject_SetAttrString(ns, "msg", msg); + Py_DECREF(msg); + if (res < 0) { + goto error; + } + + PyObject *formatted = _PyXI_excinfo_format(info); + if (formatted == NULL) { + goto error; } + res = PyObject_SetAttrString(ns, "formatted", formatted); + Py_DECREF(formatted); + if (res < 0) { + goto error; + } + + return ns; + +error: + Py_DECREF(ns); + return NULL; } @@ -1111,72 +1293,69 @@ _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp) /* shared exceptions */ static const char * -_PyXI_InitExceptionInfo(_PyXI_exception_info *info, - PyObject *excobj, _PyXI_errcode code) +_PyXI_InitError(_PyXI_error *error, PyObject *excobj, _PyXI_errcode code) { - if (info->interp == NULL) { - info->interp = PyInterpreterState_Get(); + if (error->interp == NULL) { + error->interp = PyInterpreterState_Get(); } const char *failure = NULL; if (code == _PyXI_ERR_UNCAUGHT_EXCEPTION) { // There is an unhandled exception we need to propagate. - failure = _Py_excinfo_InitFromException(&info->uncaught, excobj); + failure = _PyXI_excinfo_InitFromException(&error->uncaught, excobj); if (failure != NULL) { - // We failed to initialize info->uncaught. + // We failed to initialize error->uncaught. // XXX Print the excobj/traceback? Emit a warning? // XXX Print the current exception/traceback? if (PyErr_ExceptionMatches(PyExc_MemoryError)) { - info->code = _PyXI_ERR_NO_MEMORY; + error->code = _PyXI_ERR_NO_MEMORY; } else { - info->code = _PyXI_ERR_OTHER; + error->code = _PyXI_ERR_OTHER; } PyErr_Clear(); } else { - info->code = code; + error->code = code; } - assert(info->code != _PyXI_ERR_NO_ERROR); + assert(error->code != _PyXI_ERR_NO_ERROR); } else { // There is an error code we need to propagate. assert(excobj == NULL); assert(code != _PyXI_ERR_NO_ERROR); - info->code = code; - _Py_excinfo_Clear(&info->uncaught); + error->code = code; + _PyXI_excinfo_Clear(&error->uncaught); } return failure; } -void -_PyXI_ApplyExceptionInfo(_PyXI_exception_info *info, PyObject *exctype) +PyObject * +_PyXI_ApplyError(_PyXI_error *error) { - if (exctype == NULL) { - exctype = PyExc_RuntimeError; - } - if (info->code == _PyXI_ERR_UNCAUGHT_EXCEPTION) { + if (error->code == _PyXI_ERR_UNCAUGHT_EXCEPTION) { // Raise an exception that proxies the propagated exception. - _Py_excinfo_Apply(&info->uncaught, exctype); + return _PyXI_excinfo_AsObject(&error->uncaught); } - else if (info->code == _PyXI_ERR_NOT_SHAREABLE) { + else if (error->code == _PyXI_ERR_NOT_SHAREABLE) { // Propagate the exception directly. - _set_xid_lookup_failure(info->interp, NULL, info->uncaught.msg); + _set_xid_lookup_failure(error->interp, NULL, error->uncaught.msg); } else { // Raise an exception corresponding to the code. - assert(info->code != _PyXI_ERR_NO_ERROR); - (void)_PyXI_ApplyErrorCode(info->code, info->interp); - if (info->uncaught.type != NULL || info->uncaught.msg != NULL) { + assert(error->code != _PyXI_ERR_NO_ERROR); + (void)_PyXI_ApplyErrorCode(error->code, error->interp); + if (error->uncaught.type.name != NULL || error->uncaught.msg != NULL) { // __context__ will be set to a proxy of the propagated exception. PyObject *exc = PyErr_GetRaisedException(); - _Py_excinfo_Apply(&info->uncaught, exctype); + _PyXI_excinfo_Apply(&error->uncaught, PyExc_RuntimeError); PyObject *exc2 = PyErr_GetRaisedException(); PyException_SetContext(exc, exc2); PyErr_SetRaisedException(exc); } } assert(PyErr_Occurred()); + return NULL; } /* shared namespaces */ @@ -1603,7 +1782,7 @@ _PyXI_NamespaceFromDict(PyObject *nsobj, _PyXI_session *session) error: assert(PyErr_Occurred() - || (session != NULL && session->exc_override != NULL)); + || (session != NULL && session->error_override != NULL)); _sharedns_free(ns); return NULL; } @@ -1637,9 +1816,9 @@ _enter_session(_PyXI_session *session, PyInterpreterState *interp) assert(!session->running); assert(session->main_ns == NULL); // Set elsewhere and cleared in _capture_current_exception(). - assert(session->exc_override == NULL); + assert(session->error_override == NULL); // Set elsewhere and cleared in _PyXI_ApplyCapturedException(). - assert(session->exc == NULL); + assert(session->error == NULL); // Switch to interpreter. PyThreadState *tstate = PyThreadState_Get(); @@ -1708,23 +1887,23 @@ _propagate_not_shareable_error(_PyXI_session *session) PyInterpreterState *interp = _PyInterpreterState_GET(); if (PyErr_ExceptionMatches(_get_not_shareable_error_type(interp))) { // We want to propagate the exception directly. - session->_exc_override = _PyXI_ERR_NOT_SHAREABLE; - session->exc_override = &session->_exc_override; + session->_error_override = _PyXI_ERR_NOT_SHAREABLE; + session->error_override = &session->_error_override; } } static void _capture_current_exception(_PyXI_session *session) { - assert(session->exc == NULL); + assert(session->error == NULL); if (!PyErr_Occurred()) { - assert(session->exc_override == NULL); + assert(session->error_override == NULL); return; } // Handle the exception override. - _PyXI_errcode *override = session->exc_override; - session->exc_override = NULL; + _PyXI_errcode *override = session->error_override; + session->error_override = NULL; _PyXI_errcode errcode = override != NULL ? *override : _PyXI_ERR_UNCAUGHT_EXCEPTION; @@ -1747,19 +1926,18 @@ _capture_current_exception(_PyXI_session *session) } // Capture the exception. - _PyXI_exception_info *exc = &session->_exc; - *exc = (_PyXI_exception_info){ + _PyXI_error *err = &session->_error; + *err = (_PyXI_error){ .interp = session->init_tstate->interp, }; const char *failure; if (excval == NULL) { - failure = _PyXI_InitExceptionInfo(exc, NULL, errcode); + failure = _PyXI_InitError(err, NULL, errcode); } else { - failure = _PyXI_InitExceptionInfo(exc, excval, - _PyXI_ERR_UNCAUGHT_EXCEPTION); + failure = _PyXI_InitError(err, excval, _PyXI_ERR_UNCAUGHT_EXCEPTION); if (failure == NULL && override != NULL) { - exc->code = errcode; + err->code = errcode; } } @@ -1769,7 +1947,7 @@ _capture_current_exception(_PyXI_session *session) fprintf(stderr, "RunFailedError: script raised an uncaught exception (%s)", failure); - exc = NULL; + err = NULL; } // a temporary hack (famous last words) @@ -1786,23 +1964,24 @@ _capture_current_exception(_PyXI_session *session) // Finished! assert(!PyErr_Occurred()); - session->exc = exc; + session->error = err; } -void -_PyXI_ApplyCapturedException(_PyXI_session *session, PyObject *excwrapper) +PyObject * +_PyXI_ApplyCapturedException(_PyXI_session *session) { assert(!PyErr_Occurred()); - assert(session->exc != NULL); - _PyXI_ApplyExceptionInfo(session->exc, excwrapper); - assert(PyErr_Occurred()); - session->exc = NULL; + assert(session->error != NULL); + PyObject *res = _PyXI_ApplyError(session->error); + assert((res == NULL) != (PyErr_Occurred() == NULL)); + session->error = NULL; + return res; } int _PyXI_HasCapturedException(_PyXI_session *session) { - return session->exc != NULL; + return session->error != NULL; } int @@ -1814,7 +1993,7 @@ _PyXI_Enter(_PyXI_session *session, if (nsupdates != NULL) { sharedns = _PyXI_NamespaceFromDict(nsupdates, NULL); if (sharedns == NULL && PyErr_Occurred()) { - assert(session->exc == NULL); + assert(session->error == NULL); return -1; } } @@ -1864,7 +2043,7 @@ _PyXI_Enter(_PyXI_session *session, assert(PyErr_Occurred()); // We want to propagate all exceptions here directly (best effort). assert(errcode != _PyXI_ERR_UNCAUGHT_EXCEPTION); - session->exc_override = &errcode; + session->error_override = &errcode; _capture_current_exception(session); _exit_session(session); if (sharedns != NULL) { diff --git a/Python/dtoa.c b/Python/dtoa.c index 5dfc0e179cbc34..6e3162f80bdae1 100644 --- a/Python/dtoa.c +++ b/Python/dtoa.c @@ -309,7 +309,7 @@ BCinfo { // struct Bigint is defined in pycore_dtoa.h. typedef struct Bigint Bigint; -#ifndef Py_USING_MEMORY_DEBUGGER +#if !defined(Py_GIL_DISABLED) && !defined(Py_USING_MEMORY_DEBUGGER) /* Memory management: memory is allocated from, and returned to, Kmax+1 pools of memory, where pool k (0 <= k <= Kmax) is for Bigints b with b->maxwds == @@ -428,7 +428,7 @@ Bfree(Bigint *v) } } -#endif /* Py_USING_MEMORY_DEBUGGER */ +#endif /* !defined(Py_GIL_DISABLED) && !defined(Py_USING_MEMORY_DEBUGGER) */ #define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ y->wds*sizeof(Long) + 2*sizeof(int)) @@ -673,10 +673,17 @@ mult(Bigint *a, Bigint *b) static Bigint * pow5mult(Bigint *b, int k) { - Bigint *b1, *p5, *p51; + Bigint *b1, *p5, **p5s; int i; static const int p05[3] = { 5, 25, 125 }; + // For double-to-string conversion, the maximum value of k is limited by + // DBL_MAX_10_EXP (308), the maximum decimal base-10 exponent for binary64. + // For string-to-double conversion, the extreme case is constrained by our + // hardcoded exponent limit before we underflow of -512, adjusted by + // STRTOD_DIGLIM-DBL_DIG-1, giving a maximum of k=535. + assert(0 <= k && k < 1024); + if ((i = k & 3)) { b = multadd(b, p05[i-1], 0); if (b == NULL) @@ -686,18 +693,11 @@ pow5mult(Bigint *b, int k) if (!(k >>= 2)) return b; PyInterpreterState *interp = _PyInterpreterState_GET(); - p5 = interp->dtoa.p5s; - if (!p5) { - /* first time */ - p5 = i2b(625); - if (p5 == NULL) { - Bfree(b); - return NULL; - } - interp->dtoa.p5s = p5; - p5->next = 0; - } + p5s = interp->dtoa.p5s; for(;;) { + assert(p5s != interp->dtoa.p5s + Bigint_Pow5size); + p5 = *p5s; + p5s++; if (k & 1) { b1 = mult(b, p5); Bfree(b); @@ -707,17 +707,6 @@ pow5mult(Bigint *b, int k) } if (!(k >>= 1)) break; - p51 = p5->next; - if (!p51) { - p51 = mult(p5,p5); - if (p51 == NULL) { - Bfree(b); - return NULL; - } - p51->next = 0; - p5->next = p51; - } - p5 = p51; } return b; } @@ -2811,3 +2800,42 @@ _Py_dg_dtoa(double dd, int mode, int ndigits, } #endif // _PY_SHORT_FLOAT_REPR == 1 + +PyStatus +_PyDtoa_Init(PyInterpreterState *interp) +{ +#if _PY_SHORT_FLOAT_REPR == 1 && !defined(Py_USING_MEMORY_DEBUGGER) + Bigint **p5s = interp->dtoa.p5s; + + // 5**4 = 625 + Bigint *p5 = i2b(625); + if (p5 == NULL) { + return PyStatus_NoMemory(); + } + p5s[0] = p5; + + // compute 5**8, 5**16, 5**32, ..., 5**512 + for (Py_ssize_t i = 1; i < Bigint_Pow5size; i++) { + p5 = mult(p5, p5); + if (p5 == NULL) { + return PyStatus_NoMemory(); + } + p5s[i] = p5; + } + +#endif + return PyStatus_Ok(); +} + +void +_PyDtoa_Fini(PyInterpreterState *interp) +{ +#if _PY_SHORT_FLOAT_REPR == 1 && !defined(Py_USING_MEMORY_DEBUGGER) + Bigint **p5s = interp->dtoa.p5s; + for (Py_ssize_t i = 0; i < Bigint_Pow5size; i++) { + Bigint *p5 = p5s[i]; + p5s[i] = NULL; + Bfree(p5); + } +#endif +} diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 4e29fb9f0fa93d..974e3f28a411b8 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -25,6 +25,7 @@ } case LOAD_FAST_CHECK: { + oparg = CURRENT_OPARG(); PyObject *value; value = GETLOCAL(oparg); if (value == NULL) goto unbound_local_error_tier_two; @@ -35,6 +36,7 @@ } case LOAD_FAST: { + oparg = CURRENT_OPARG(); PyObject *value; value = GETLOCAL(oparg); assert(value != NULL); @@ -45,6 +47,7 @@ } case LOAD_FAST_AND_CLEAR: { + oparg = CURRENT_OPARG(); PyObject *value; value = GETLOCAL(oparg); // do not use SETLOCAL here, it decrefs the old value @@ -55,6 +58,7 @@ } case LOAD_CONST: { + oparg = CURRENT_OPARG(); PyObject *value; value = GETITEM(FRAME_CO_CONSTS, oparg); Py_INCREF(value); @@ -64,6 +68,7 @@ } case STORE_FAST: { + oparg = CURRENT_OPARG(); PyObject *value; value = stack_pointer[-1]; SETLOCAL(oparg, value); @@ -204,7 +209,7 @@ PyObject *value; PyObject *res; value = stack_pointer[-1]; - uint32_t version = (uint32_t)operand; + uint32_t version = (uint32_t)CURRENT_OPERAND(); // This one is a bit weird, because we expect *some* failures: assert(version); DEOPT_IF(Py_TYPE(value)->tp_version_tag != version, TO_BOOL); @@ -525,6 +530,7 @@ } case LIST_APPEND: { + oparg = CURRENT_OPARG(); PyObject *v; PyObject *list; v = stack_pointer[-1]; @@ -535,6 +541,7 @@ } case SET_ADD: { + oparg = CURRENT_OPARG(); PyObject *v; PyObject *set; v = stack_pointer[-1]; @@ -621,6 +628,7 @@ } case CALL_INTRINSIC_1: { + oparg = CURRENT_OPARG(); PyObject *value; PyObject *res; value = stack_pointer[-1]; @@ -633,6 +641,7 @@ } case CALL_INTRINSIC_2: { + oparg = CURRENT_OPARG(); PyObject *value1; PyObject *value2; PyObject *res; @@ -652,11 +661,11 @@ PyObject *retval; retval = stack_pointer[-1]; STACK_SHRINK(1); - assert(EMPTY()); #if TIER_ONE assert(frame != &entry_frame); #endif STORE_SP(); + assert(EMPTY()); _Py_LeaveRecursiveCallPy(tstate); // GH-99729: We need to unlink the frame *before* clearing it: _PyInterpreterFrame *dying = frame; @@ -764,6 +773,7 @@ } case GET_AWAITABLE: { + oparg = CURRENT_OPARG(); PyObject *iterable; PyObject *iter; iterable = stack_pointer[-1]; @@ -825,6 +835,7 @@ } case STORE_NAME: { + oparg = CURRENT_OPARG(); PyObject *v; v = stack_pointer[-1]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); @@ -847,6 +858,7 @@ } case DELETE_NAME: { + oparg = CURRENT_OPARG(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); PyObject *ns = LOCALS(); int err; @@ -867,6 +879,7 @@ } case _UNPACK_SEQUENCE: { + oparg = CURRENT_OPARG(); PyObject *seq; seq = stack_pointer[-1]; PyObject **top = stack_pointer + oparg - 1; @@ -879,6 +892,7 @@ } case UNPACK_SEQUENCE_TWO_TUPLE: { + oparg = CURRENT_OPARG(); PyObject *seq; PyObject **values; seq = stack_pointer[-1]; @@ -896,6 +910,7 @@ } case UNPACK_SEQUENCE_TUPLE: { + oparg = CURRENT_OPARG(); PyObject *seq; PyObject **values; seq = stack_pointer[-1]; @@ -914,6 +929,7 @@ } case UNPACK_SEQUENCE_LIST: { + oparg = CURRENT_OPARG(); PyObject *seq; PyObject **values; seq = stack_pointer[-1]; @@ -932,6 +948,7 @@ } case UNPACK_EX: { + oparg = CURRENT_OPARG(); PyObject *seq; seq = stack_pointer[-1]; int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8); @@ -944,6 +961,7 @@ } case _STORE_ATTR: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *v; owner = stack_pointer[-1]; @@ -958,6 +976,7 @@ } case DELETE_ATTR: { + oparg = CURRENT_OPARG(); PyObject *owner; owner = stack_pointer[-1]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); @@ -969,6 +988,7 @@ } case STORE_GLOBAL: { + oparg = CURRENT_OPARG(); PyObject *v; v = stack_pointer[-1]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); @@ -980,6 +1000,7 @@ } case DELETE_GLOBAL: { + oparg = CURRENT_OPARG(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); int err; err = PyDict_DelItem(GLOBALS(), name); @@ -1009,6 +1030,7 @@ } case LOAD_FROM_DICT_OR_GLOBALS: { + oparg = CURRENT_OPARG(); PyObject *mod_or_class_dict; PyObject *v; mod_or_class_dict = stack_pointer[-1]; @@ -1038,6 +1060,7 @@ } case LOAD_NAME: { + oparg = CURRENT_OPARG(); PyObject *v; PyObject *mod_or_class_dict = LOCALS(); if (mod_or_class_dict == NULL) { @@ -1071,6 +1094,7 @@ } case _LOAD_GLOBAL: { + oparg = CURRENT_OPARG(); PyObject *res; PyObject *null = NULL; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); @@ -1115,7 +1139,7 @@ } case _GUARD_GLOBALS_VERSION: { - uint16_t version = (uint16_t)operand; + uint16_t version = (uint16_t)CURRENT_OPERAND(); PyDictObject *dict = (PyDictObject *)GLOBALS(); DEOPT_IF(!PyDict_CheckExact(dict), _GUARD_GLOBALS_VERSION); DEOPT_IF(dict->ma_keys->dk_version != version, _GUARD_GLOBALS_VERSION); @@ -1124,7 +1148,7 @@ } case _GUARD_BUILTINS_VERSION: { - uint16_t version = (uint16_t)operand; + uint16_t version = (uint16_t)CURRENT_OPERAND(); PyDictObject *dict = (PyDictObject *)BUILTINS(); DEOPT_IF(!PyDict_CheckExact(dict), _GUARD_BUILTINS_VERSION); DEOPT_IF(dict->ma_keys->dk_version != version, _GUARD_BUILTINS_VERSION); @@ -1133,9 +1157,10 @@ } case _LOAD_GLOBAL_MODULE: { + oparg = CURRENT_OPARG(); PyObject *res; PyObject *null = NULL; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)CURRENT_OPERAND(); PyDictObject *dict = (PyDictObject *)GLOBALS(); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys); res = entries[index].me_value; @@ -1151,9 +1176,10 @@ } case _LOAD_GLOBAL_BUILTINS: { + oparg = CURRENT_OPARG(); PyObject *res; PyObject *null = NULL; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)CURRENT_OPERAND(); PyDictObject *bdict = (PyDictObject *)BUILTINS(); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(bdict->ma_keys); res = entries[index].me_value; @@ -1169,6 +1195,7 @@ } case DELETE_FAST: { + oparg = CURRENT_OPARG(); PyObject *v = GETLOCAL(oparg); if (v == NULL) goto unbound_local_error_tier_two; SETLOCAL(oparg, NULL); @@ -1176,6 +1203,7 @@ } case MAKE_CELL: { + oparg = CURRENT_OPARG(); // "initial" is probably NULL but not if it's an arg (or set // via PyFrame_LocalsToFast() before MAKE_CELL has run). PyObject *initial = GETLOCAL(oparg); @@ -1188,6 +1216,7 @@ } case DELETE_DEREF: { + oparg = CURRENT_OPARG(); PyObject *cell = GETLOCAL(oparg); PyObject *oldobj = PyCell_GET(cell); // Can't use ERROR_IF here. @@ -1202,6 +1231,7 @@ } case LOAD_FROM_DICT_OR_DEREF: { + oparg = CURRENT_OPARG(); PyObject *class_dict; PyObject *value; class_dict = stack_pointer[-1]; @@ -1227,6 +1257,7 @@ } case LOAD_DEREF: { + oparg = CURRENT_OPARG(); PyObject *value; PyObject *cell = GETLOCAL(oparg); value = PyCell_GET(cell); @@ -1241,6 +1272,7 @@ } case STORE_DEREF: { + oparg = CURRENT_OPARG(); PyObject *v; v = stack_pointer[-1]; PyObject *cell = GETLOCAL(oparg); @@ -1252,6 +1284,7 @@ } case COPY_FREE_VARS: { + oparg = CURRENT_OPARG(); /* Copy closure variables to free variables */ PyCodeObject *co = _PyFrame_GetCode(frame); assert(PyFunction_Check(frame->f_funcobj)); @@ -1266,6 +1299,7 @@ } case BUILD_STRING: { + oparg = CURRENT_OPARG(); PyObject **pieces; PyObject *str; pieces = stack_pointer - oparg; @@ -1281,6 +1315,7 @@ } case BUILD_TUPLE: { + oparg = CURRENT_OPARG(); PyObject **values; PyObject *tup; values = stack_pointer - oparg; @@ -1293,6 +1328,7 @@ } case BUILD_LIST: { + oparg = CURRENT_OPARG(); PyObject **values; PyObject *list; values = stack_pointer - oparg; @@ -1305,6 +1341,7 @@ } case LIST_EXTEND: { + oparg = CURRENT_OPARG(); PyObject *iterable; PyObject *list; iterable = stack_pointer[-1]; @@ -1329,6 +1366,7 @@ } case SET_UPDATE: { + oparg = CURRENT_OPARG(); PyObject *iterable; PyObject *set; iterable = stack_pointer[-1]; @@ -1341,6 +1379,7 @@ } case BUILD_SET: { + oparg = CURRENT_OPARG(); PyObject **values; PyObject *set; values = stack_pointer - oparg; @@ -1365,6 +1404,7 @@ } case BUILD_MAP: { + oparg = CURRENT_OPARG(); PyObject **values; PyObject *map; values = stack_pointer - oparg*2; @@ -1407,6 +1447,7 @@ } case BUILD_CONST_KEY_MAP: { + oparg = CURRENT_OPARG(); PyObject *keys; PyObject **values; PyObject *map; @@ -1432,6 +1473,7 @@ } case DICT_UPDATE: { + oparg = CURRENT_OPARG(); PyObject *update; PyObject *dict; update = stack_pointer[-1]; @@ -1451,6 +1493,7 @@ } case DICT_MERGE: { + oparg = CURRENT_OPARG(); PyObject *update; PyObject *dict; PyObject *callable; @@ -1468,6 +1511,7 @@ } case MAP_ADD: { + oparg = CURRENT_OPARG(); PyObject *value; PyObject *key; PyObject *dict; @@ -1483,6 +1527,7 @@ } case LOAD_SUPER_ATTR_ATTR: { + oparg = CURRENT_OPARG(); PyObject *self; PyObject *class; PyObject *global_super; @@ -1506,6 +1551,7 @@ } case LOAD_SUPER_ATTR_METHOD: { + oparg = CURRENT_OPARG(); PyObject *self; PyObject *class; PyObject *global_super; @@ -1542,6 +1588,7 @@ } case _LOAD_ATTR: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *self_or_null = NULL; @@ -1585,7 +1632,7 @@ case _GUARD_TYPE_VERSION: { PyObject *owner; owner = stack_pointer[-1]; - uint32_t type_version = (uint32_t)operand; + uint32_t type_version = (uint32_t)CURRENT_OPERAND(); PyTypeObject *tp = Py_TYPE(owner); assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, _GUARD_TYPE_VERSION); @@ -1603,11 +1650,12 @@ } case _LOAD_ATTR_INSTANCE_VALUE: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *null = NULL; owner = stack_pointer[-1]; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)CURRENT_OPERAND(); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); attr = _PyDictOrValues_GetValues(dorv)->values[index]; DEOPT_IF(attr == NULL, _LOAD_ATTR_INSTANCE_VALUE); @@ -1624,7 +1672,7 @@ case _CHECK_ATTR_MODULE: { PyObject *owner; owner = stack_pointer[-1]; - uint32_t type_version = (uint32_t)operand; + uint32_t type_version = (uint32_t)CURRENT_OPERAND(); DEOPT_IF(!PyModule_CheckExact(owner), _CHECK_ATTR_MODULE); PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; assert(dict != NULL); @@ -1633,11 +1681,12 @@ } case _LOAD_ATTR_MODULE: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *null = NULL; owner = stack_pointer[-1]; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)CURRENT_OPERAND(); PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); assert(index < dict->ma_keys->dk_nentries); @@ -1667,11 +1716,12 @@ } case _LOAD_ATTR_WITH_HINT: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *null = NULL; owner = stack_pointer[-1]; - uint16_t hint = (uint16_t)operand; + uint16_t hint = (uint16_t)CURRENT_OPERAND(); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, _LOAD_ATTR_WITH_HINT); @@ -1698,11 +1748,12 @@ } case _LOAD_ATTR_SLOT: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *null = NULL; owner = stack_pointer[-1]; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)CURRENT_OPERAND(); char *addr = (char *)owner + index; attr = *(PyObject **)addr; DEOPT_IF(attr == NULL, _LOAD_ATTR_SLOT); @@ -1719,7 +1770,7 @@ case _CHECK_ATTR_CLASS: { PyObject *owner; owner = stack_pointer[-1]; - uint32_t type_version = (uint32_t)operand; + uint32_t type_version = (uint32_t)CURRENT_OPERAND(); DEOPT_IF(!PyType_Check(owner), _CHECK_ATTR_CLASS); assert(type_version != 0); DEOPT_IF(((PyTypeObject *)owner)->tp_version_tag != type_version, _CHECK_ATTR_CLASS); @@ -1727,11 +1778,12 @@ } case _LOAD_ATTR_CLASS: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *null = NULL; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)CURRENT_OPERAND(); STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); attr = Py_NewRef(descr); @@ -1757,7 +1809,7 @@ PyObject *value; owner = stack_pointer[-1]; value = stack_pointer[-2]; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)CURRENT_OPERAND(); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); STAT_INC(STORE_ATTR, hit); PyDictValues *values = _PyDictOrValues_GetValues(dorv); @@ -1779,7 +1831,7 @@ PyObject *value; owner = stack_pointer[-1]; value = stack_pointer[-2]; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)CURRENT_OPERAND(); char *addr = (char *)owner + index; STAT_INC(STORE_ATTR, hit); PyObject *old_value = *(PyObject **)addr; @@ -1791,6 +1843,7 @@ } case _COMPARE_OP: { + oparg = CURRENT_OPARG(); PyObject *right; PyObject *left; PyObject *res; @@ -1813,6 +1866,7 @@ } case COMPARE_OP_FLOAT: { + oparg = CURRENT_OPARG(); PyObject *right; PyObject *left; PyObject *res; @@ -1835,6 +1889,7 @@ } case COMPARE_OP_INT: { + oparg = CURRENT_OPARG(); PyObject *right; PyObject *left; PyObject *res; @@ -1861,6 +1916,7 @@ } case COMPARE_OP_STR: { + oparg = CURRENT_OPARG(); PyObject *right; PyObject *left; PyObject *res; @@ -1884,6 +1940,7 @@ } case IS_OP: { + oparg = CURRENT_OPARG(); PyObject *right; PyObject *left; PyObject *b; @@ -1899,6 +1956,7 @@ } case CONTAINS_OP: { + oparg = CURRENT_OPARG(); PyObject *right; PyObject *left; PyObject *b; @@ -1995,6 +2053,7 @@ } case MATCH_CLASS: { + oparg = CURRENT_OPARG(); PyObject *names; PyObject *type; PyObject *subject; @@ -2101,6 +2160,31 @@ break; } + case _FOR_ITER_TIER_TWO: { + PyObject *iter; + PyObject *next; + iter = stack_pointer[-1]; + /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ + next = (*Py_TYPE(iter)->tp_iternext)(iter); + if (next == NULL) { + if (_PyErr_Occurred(tstate)) { + if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { + GOTO_ERROR(error); + } + _PyErr_Clear(tstate); + } + /* iterator ended normally */ + Py_DECREF(iter); + STACK_SHRINK(1); + /* The translator sets the deopt target just past END_FOR */ + DEOPT_IF(true, _FOR_ITER_TIER_TWO); + } + // Common case: no jump, leave it to the code generator + STACK_GROW(1); + stack_pointer[-1] = next; + break; + } + case _ITER_CHECK_LIST: { PyObject *iter; iter = stack_pointer[-1]; @@ -2353,7 +2437,7 @@ case _GUARD_KEYS_VERSION: { PyObject *owner; owner = stack_pointer[-1]; - uint32_t keys_version = (uint32_t)operand; + uint32_t keys_version = (uint32_t)CURRENT_OPERAND(); PyTypeObject *owner_cls = Py_TYPE(owner); PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version, _GUARD_KEYS_VERSION); @@ -2361,11 +2445,12 @@ } case _LOAD_ATTR_METHOD_WITH_VALUES: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *self; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)CURRENT_OPERAND(); assert(oparg & 1); /* Cached method object */ STAT_INC(LOAD_ATTR, hit); @@ -2380,11 +2465,12 @@ } case _LOAD_ATTR_METHOD_NO_DICT: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *self; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)CURRENT_OPERAND(); assert(oparg & 1); assert(Py_TYPE(owner)->tp_dictoffset == 0); STAT_INC(LOAD_ATTR, hit); @@ -2399,10 +2485,11 @@ } case _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)CURRENT_OPERAND(); assert((oparg & 1) == 0); STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); @@ -2413,10 +2500,11 @@ } case _LOAD_ATTR_NONDESCRIPTOR_NO_DICT: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)CURRENT_OPERAND(); assert((oparg & 1) == 0); assert(Py_TYPE(owner)->tp_dictoffset == 0); STAT_INC(LOAD_ATTR, hit); @@ -2439,11 +2527,12 @@ } case _LOAD_ATTR_METHOD_LAZY_DICT: { + oparg = CURRENT_OPARG(); PyObject *owner; PyObject *attr; PyObject *self; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)CURRENT_OPERAND(); assert(oparg & 1); STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); @@ -2457,6 +2546,7 @@ } case _CHECK_CALL_BOUND_METHOD_EXACT_ARGS: { + oparg = CURRENT_OPARG(); PyObject *null; PyObject *callable; null = stack_pointer[-1 - oparg]; @@ -2467,6 +2557,7 @@ } case _INIT_CALL_BOUND_METHOD_EXACT_ARGS: { + oparg = CURRENT_OPARG(); PyObject *callable; PyObject *func; PyObject *self; @@ -2488,11 +2579,12 @@ } case _CHECK_FUNCTION_EXACT_ARGS: { + oparg = CURRENT_OPARG(); PyObject *self_or_null; PyObject *callable; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; - uint32_t func_version = (uint32_t)operand; + uint32_t func_version = (uint32_t)CURRENT_OPERAND(); DEOPT_IF(!PyFunction_Check(callable), _CHECK_FUNCTION_EXACT_ARGS); PyFunctionObject *func = (PyFunctionObject *)callable; DEOPT_IF(func->func_version != func_version, _CHECK_FUNCTION_EXACT_ARGS); @@ -2502,6 +2594,7 @@ } case _CHECK_STACK_SPACE: { + oparg = CURRENT_OPARG(); PyObject *callable; callable = stack_pointer[-2 - oparg]; PyFunctionObject *func = (PyFunctionObject *)callable; @@ -2512,6 +2605,7 @@ } case _INIT_CALL_PY_EXACT_ARGS: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2560,6 +2654,7 @@ } case CALL_TYPE_1: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *null; PyObject *callable; @@ -2582,6 +2677,7 @@ } case CALL_STR_1: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *null; PyObject *callable; @@ -2606,6 +2702,7 @@ } case CALL_TUPLE_1: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *null; PyObject *callable; @@ -2644,6 +2741,7 @@ } case CALL_BUILTIN_CLASS: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2675,6 +2773,7 @@ } case CALL_BUILTIN_O: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2714,6 +2813,7 @@ } case CALL_BUILTIN_FAST: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2757,6 +2857,7 @@ } case CALL_BUILTIN_FAST_WITH_KEYWORDS: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2794,6 +2895,7 @@ } case CALL_LEN: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2829,6 +2931,7 @@ } case CALL_ISINSTANCE: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2866,6 +2969,7 @@ } case CALL_METHOD_DESCRIPTOR_O: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2908,6 +3012,7 @@ } case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2948,6 +3053,7 @@ } case CALL_METHOD_DESCRIPTOR_NOARGS: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -2989,6 +3095,7 @@ } case CALL_METHOD_DESCRIPTOR_FAST: { + oparg = CURRENT_OPARG(); PyObject **args; PyObject *self_or_null; PyObject *callable; @@ -3048,6 +3155,7 @@ } case SET_FUNCTION_ATTRIBUTE: { + oparg = CURRENT_OPARG(); PyObject *func; PyObject *attr; func = stack_pointer[-1]; @@ -3082,6 +3190,7 @@ } case BUILD_SLICE: { + oparg = CURRENT_OPARG(); PyObject *step = NULL; PyObject *stop; PyObject *start; @@ -3101,6 +3210,7 @@ } case CONVERT_VALUE: { + oparg = CURRENT_OPARG(); PyObject *value; PyObject *result; value = stack_pointer[-1]; @@ -3148,6 +3258,7 @@ } case COPY: { + oparg = CURRENT_OPARG(); PyObject *bottom; PyObject *top; bottom = stack_pointer[-1 - (oparg-1)]; @@ -3159,6 +3270,7 @@ } case _BINARY_OP: { + oparg = CURRENT_OPARG(); PyObject *rhs; PyObject *lhs; PyObject *res; @@ -3175,6 +3287,7 @@ } case SWAP: { + oparg = CURRENT_OPARG(); PyObject *top; PyObject *bottom; top = stack_pointer[-1]; @@ -3227,6 +3340,7 @@ } case _SET_IP: { + oparg = CURRENT_OPARG(); TIER_TWO_ONLY // TODO: Put the code pointer in `operand` to avoid indirection via `frame` frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame)) + oparg; @@ -3234,6 +3348,7 @@ } case _SAVE_RETURN_OFFSET: { + oparg = CURRENT_OPARG(); #if TIER_ONE frame->return_offset = (uint16_t)(next_instr - this_instr); #endif @@ -3250,6 +3365,7 @@ } case _INSERT: { + oparg = CURRENT_OPARG(); PyObject *top; top = stack_pointer[-1]; // Inserts TOS at position specified by oparg; diff --git a/Python/fileutils.c b/Python/fileutils.c index 649b188b5167d0..9d12bc89c95436 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -2943,3 +2943,27 @@ _Py_closerange(int first, int last) #endif /* USE_FDWALK */ _Py_END_SUPPRESS_IPH } + + +#ifndef MS_WINDOWS +// Ticks per second used by clock() and times() functions. +// See os.times() and time.process_time() implementations. +int +_Py_GetTicksPerSecond(long *ticks_per_second) +{ +#if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK) + long value = sysconf(_SC_CLK_TCK); + if (value < 1) { + return -1; + } + *ticks_per_second = value; +#elif defined(HZ) + assert(HZ >= 1); + *ticks_per_second = HZ; +#else + // Magic fallback value; may be bogus + *ticks_per_second = 60; +#endif + return 0; +} +#endif diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 87401e14f97f02..fe632082d5a66c 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -97,6 +97,7 @@ static const jump_target_label NO_LABEL = {-1}; static inline int is_block_push(cfg_instr *i) { + assert(OPCODE_HAS_ARG(i->i_opcode) || !IS_BLOCK_PUSH_OPCODE(i->i_opcode)); return IS_BLOCK_PUSH_OPCODE(i->i_opcode); } @@ -2239,7 +2240,6 @@ convert_pseudo_ops(basicblock *entryblock) for (int i = 0; i < b->b_iused; i++) { cfg_instr *instr = &b->b_instr[i]; if (is_block_push(instr) || instr->i_opcode == POP_BLOCK) { - assert(SAME_OPCODE_METADATA(instr->i_opcode, NOP)); INSTR_SET_OP0(instr, NOP); } else if (instr->i_opcode == LOAD_CLOSURE) { diff --git a/Python/frozen.c b/Python/frozen.c index 0fb38a11902f35..77f51a7f750965 100644 --- a/Python/frozen.c +++ b/Python/frozen.c @@ -80,7 +80,6 @@ extern PyObject *_Py_get__sitebuiltins_toplevel(void); extern PyObject *_Py_get_genericpath_toplevel(void); extern PyObject *_Py_get_ntpath_toplevel(void); extern PyObject *_Py_get_posixpath_toplevel(void); -extern PyObject *_Py_get_posixpath_toplevel(void); extern PyObject *_Py_get_os_toplevel(void); extern PyObject *_Py_get_site_toplevel(void); extern PyObject *_Py_get_stat_toplevel(void); @@ -88,13 +87,8 @@ extern PyObject *_Py_get_importlib_util_toplevel(void); extern PyObject *_Py_get_importlib_machinery_toplevel(void); extern PyObject *_Py_get_runpy_toplevel(void); extern PyObject *_Py_get___hello___toplevel(void); -extern PyObject *_Py_get___hello___toplevel(void); -extern PyObject *_Py_get___hello___toplevel(void); -extern PyObject *_Py_get___hello___toplevel(void); -extern PyObject *_Py_get___phello___toplevel(void); extern PyObject *_Py_get___phello___toplevel(void); extern PyObject *_Py_get___phello___ham_toplevel(void); -extern PyObject *_Py_get___phello___ham_toplevel(void); extern PyObject *_Py_get___phello___ham_eggs_toplevel(void); extern PyObject *_Py_get___phello___spam_toplevel(void); extern PyObject *_Py_get_frozen_only_toplevel(void); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 3b4cc7562da081..24243ecfb5b8df 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1,6 +1,6 @@ -// This file is generated by Tools/cases_generator/generate_cases.py +// This file is generated by Tools/cases_generator/tier1_generator.py // from: -// Python/bytecodes.c +// ['./Python/bytecodes.c'] // Do not edit! #ifdef TIER_TWO @@ -8,1068 +8,1229 @@ #endif #define TIER_ONE 1 - TARGET(NOP) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(NOP); - DISPATCH(); - } - TARGET(RESUME) { + TARGET(BEFORE_ASYNC_WITH) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(RESUME); - PREDICTED(RESUME); - _Py_CODEUNIT *this_instr = next_instr - 1; - static_assert(0 == 0, "incorrect cache size"); - TIER_ONE_ONLY - assert(frame == tstate->current_frame); - uintptr_t global_version = - _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker) & - ~_PY_EVAL_EVENTS_MASK; - uintptr_t code_version = _PyFrame_GetCode(frame)->_co_instrumentation_version; - assert((code_version & 255) == 0); - if (code_version != global_version) { - int err = _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); - if (err) goto error; - next_instr = this_instr; + INSTRUCTION_STATS(BEFORE_ASYNC_WITH); + PyObject *mgr; + PyObject *exit; + PyObject *res; + mgr = stack_pointer[-1]; + PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__aenter__)); + if (enter == NULL) { + if (!_PyErr_Occurred(tstate)) { + _PyErr_Format(tstate, PyExc_TypeError, + "'%.200s' object does not support the " + "asynchronous context manager protocol", + Py_TYPE(mgr)->tp_name); + } + GOTO_ERROR(error); } - else { - if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { - CHECK_EVAL_BREAKER(); + exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__)); + if (exit == NULL) { + if (!_PyErr_Occurred(tstate)) { + _PyErr_Format(tstate, PyExc_TypeError, + "'%.200s' object does not support the " + "asynchronous context manager protocol " + "(missed __aexit__ method)", + Py_TYPE(mgr)->tp_name); } - this_instr->op.code = RESUME_CHECK; + Py_DECREF(enter); + GOTO_ERROR(error); + } + Py_DECREF(mgr); + res = _PyObject_CallNoArgsTstate(tstate, enter); + Py_DECREF(enter); + if (res == NULL) { + Py_DECREF(exit); + if (true) goto pop_1_error; } + stack_pointer[-1] = exit; + stack_pointer[0] = res; + stack_pointer += 1; DISPATCH(); } - TARGET(RESUME_CHECK) { + TARGET(BEFORE_WITH) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(RESUME_CHECK); -#if defined(__EMSCRIPTEN__) - DEOPT_IF(_Py_emscripten_signal_clock == 0, RESUME); - _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; -#endif - uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker); - uintptr_t version = _PyFrame_GetCode(frame)->_co_instrumentation_version; - assert((version & _PY_EVAL_EVENTS_MASK) == 0); - DEOPT_IF(eval_breaker != version, RESUME); - DISPATCH(); - } - - TARGET(INSTRUMENTED_RESUME) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_RESUME); - uintptr_t global_version = _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker) & ~_PY_EVAL_EVENTS_MASK; - uintptr_t code_version = _PyFrame_GetCode(frame)->_co_instrumentation_version; - if (code_version != global_version) { - if (_Py_Instrument(_PyFrame_GetCode(frame), tstate->interp)) { - GOTO_ERROR(error); + INSTRUCTION_STATS(BEFORE_WITH); + PyObject *mgr; + PyObject *exit; + PyObject *res; + mgr = stack_pointer[-1]; + /* pop the context manager, push its __exit__ and the + * value returned from calling its __enter__ + */ + PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__enter__)); + if (enter == NULL) { + if (!_PyErr_Occurred(tstate)) { + _PyErr_Format(tstate, PyExc_TypeError, + "'%.200s' object does not support the " + "context manager protocol", + Py_TYPE(mgr)->tp_name); } - next_instr = this_instr; + GOTO_ERROR(error); } - else { - if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { - CHECK_EVAL_BREAKER(); + exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__)); + if (exit == NULL) { + if (!_PyErr_Occurred(tstate)) { + _PyErr_Format(tstate, PyExc_TypeError, + "'%.200s' object does not support the " + "context manager protocol " + "(missed __exit__ method)", + Py_TYPE(mgr)->tp_name); } - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_call_instrumentation( - tstate, oparg > 0, frame, this_instr); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) goto error; - if (frame->instr_ptr != this_instr) { - /* Instrumentation has jumped */ + Py_DECREF(enter); + GOTO_ERROR(error); + } + Py_DECREF(mgr); + res = _PyObject_CallNoArgsTstate(tstate, enter); + Py_DECREF(enter); + if (res == NULL) { + Py_DECREF(exit); + if (true) goto pop_1_error; + } + stack_pointer[-1] = exit; + stack_pointer[0] = res; + stack_pointer += 1; + DISPATCH(); + } + + TARGET(BINARY_OP) { + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP); + PREDICTED(BINARY_OP); + _Py_CODEUNIT *this_instr = next_instr - 2; + PyObject *rhs; + PyObject *lhs; + PyObject *res; + // _SPECIALIZE_BINARY_OP + rhs = stack_pointer[-1]; + lhs = stack_pointer[-2]; + { + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { next_instr = this_instr; - DISPATCH(); + _Py_Specialize_BinaryOp(lhs, rhs, next_instr, oparg, LOCALS_ARRAY); + DISPATCH_SAME_OPARG(); } + STAT_INC(BINARY_OP, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ + assert(NB_ADD <= oparg); + assert(oparg <= NB_INPLACE_XOR); + } + // _BINARY_OP + { + assert(_PyEval_BinaryOps[oparg]); + res = _PyEval_BinaryOps[oparg](lhs, rhs); + Py_DECREF(lhs); + Py_DECREF(rhs); + if (res == NULL) goto pop_2_error; } + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(LOAD_FAST_CHECK) { + TARGET(BINARY_OP_ADD_FLOAT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(LOAD_FAST_CHECK); - PyObject *value; - value = GETLOCAL(oparg); - if (value == NULL) goto unbound_local_error; - Py_INCREF(value); - STACK_GROW(1); - stack_pointer[-1] = value; + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_ADD_FLOAT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + PyObject *right; + PyObject *left; + PyObject *res; + // _GUARD_BOTH_FLOAT + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_ADD_FLOAT + { + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left)->ob_fval + + ((PyFloatObject *)right)->ob_fval; + DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); + } + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(LOAD_FAST) { + TARGET(BINARY_OP_ADD_INT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(LOAD_FAST); - PyObject *value; - value = GETLOCAL(oparg); - assert(value != NULL); - Py_INCREF(value); - STACK_GROW(1); - stack_pointer[-1] = value; + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_ADD_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + PyObject *right; + PyObject *left; + PyObject *res; + // _GUARD_BOTH_INT + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_ADD_INT + { + STAT_INC(BINARY_OP, hit); + res = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right); + _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); + _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); + if (res == NULL) goto pop_2_error; + } + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(LOAD_FAST_AND_CLEAR) { + TARGET(BINARY_OP_ADD_UNICODE) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(LOAD_FAST_AND_CLEAR); - PyObject *value; - value = GETLOCAL(oparg); - // do not use SETLOCAL here, it decrefs the old value - GETLOCAL(oparg) = NULL; - STACK_GROW(1); - stack_pointer[-1] = value; + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_ADD_UNICODE); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + PyObject *right; + PyObject *left; + PyObject *res; + // _GUARD_BOTH_UNICODE + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyUnicode_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_ADD_UNICODE + { + STAT_INC(BINARY_OP, hit); + res = PyUnicode_Concat(left, right); + _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); + _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); + if (res == NULL) goto pop_2_error; + } + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(LOAD_FAST_LOAD_FAST) { + TARGET(BINARY_OP_INPLACE_ADD_UNICODE) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(LOAD_FAST_LOAD_FAST); - PyObject *value1; - PyObject *value2; - uint32_t oparg1 = oparg >> 4; - uint32_t oparg2 = oparg & 15; - value1 = GETLOCAL(oparg1); - value2 = GETLOCAL(oparg2); - Py_INCREF(value1); - Py_INCREF(value2); - STACK_GROW(2); - stack_pointer[-2] = value1; - stack_pointer[-1] = value2; + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_INPLACE_ADD_UNICODE); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + PyObject *right; + PyObject *left; + // _GUARD_BOTH_UNICODE + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyUnicode_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_INPLACE_ADD_UNICODE + { + assert(next_instr->op.code == STORE_FAST); + PyObject **target_local = &GETLOCAL(next_instr->op.arg); + DEOPT_IF(*target_local != left, BINARY_OP); + STAT_INC(BINARY_OP, hit); + /* Handle `left = left + right` or `left += right` for str. + * + * When possible, extend `left` in place rather than + * allocating a new PyUnicodeObject. This attempts to avoid + * quadratic behavior when one neglects to use str.join(). + * + * If `left` has only two references remaining (one from + * the stack, one in the locals), DECREFing `left` leaves + * only the locals reference, so PyUnicode_Append knows + * that the string is safe to mutate. + */ + assert(Py_REFCNT(left) >= 2); + _Py_DECREF_NO_DEALLOC(left); + PyUnicode_Append(target_local, right); + _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); + if (*target_local == NULL) goto pop_2_error; + // The STORE_FAST is already done. + assert(next_instr->op.code == STORE_FAST); + SKIP_OVER(1); + } + stack_pointer += -2; DISPATCH(); } - TARGET(LOAD_CONST) { + TARGET(BINARY_OP_MULTIPLY_FLOAT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(LOAD_CONST); - PyObject *value; - value = GETITEM(FRAME_CO_CONSTS, oparg); - Py_INCREF(value); - STACK_GROW(1); - stack_pointer[-1] = value; + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_MULTIPLY_FLOAT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + PyObject *right; + PyObject *left; + PyObject *res; + // _GUARD_BOTH_FLOAT + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_MULTIPLY_FLOAT + { + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left)->ob_fval * + ((PyFloatObject *)right)->ob_fval; + DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); + } + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(STORE_FAST) { + TARGET(BINARY_OP_MULTIPLY_INT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(STORE_FAST); - PyObject *value; - value = stack_pointer[-1]; - SETLOCAL(oparg, value); - STACK_SHRINK(1); + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_MULTIPLY_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + PyObject *right; + PyObject *left; + PyObject *res; + // _GUARD_BOTH_INT + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_MULTIPLY_INT + { + STAT_INC(BINARY_OP, hit); + res = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right); + _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); + _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); + if (res == NULL) goto pop_2_error; + } + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(STORE_FAST_LOAD_FAST) { + TARGET(BINARY_OP_SUBTRACT_FLOAT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(STORE_FAST_LOAD_FAST); - PyObject *value1; - PyObject *value2; - value1 = stack_pointer[-1]; - uint32_t oparg1 = oparg >> 4; - uint32_t oparg2 = oparg & 15; - SETLOCAL(oparg1, value1); - value2 = GETLOCAL(oparg2); - Py_INCREF(value2); - stack_pointer[-1] = value2; + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_SUBTRACT_FLOAT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + PyObject *right; + PyObject *left; + PyObject *res; + // _GUARD_BOTH_FLOAT + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_SUBTRACT_FLOAT + { + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left)->ob_fval - + ((PyFloatObject *)right)->ob_fval; + DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); + } + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(STORE_FAST_STORE_FAST) { + TARGET(BINARY_OP_SUBTRACT_INT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(STORE_FAST_STORE_FAST); - PyObject *value1; - PyObject *value2; - value1 = stack_pointer[-1]; - value2 = stack_pointer[-2]; - uint32_t oparg1 = oparg >> 4; - uint32_t oparg2 = oparg & 15; - SETLOCAL(oparg1, value1); - SETLOCAL(oparg2, value2); - STACK_SHRINK(2); + next_instr += 2; + INSTRUCTION_STATS(BINARY_OP_SUBTRACT_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + PyObject *right; + PyObject *left; + PyObject *res; + // _GUARD_BOTH_INT + right = stack_pointer[-1]; + left = stack_pointer[-2]; + { + DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); + DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); + } + // _BINARY_OP_SUBTRACT_INT + { + STAT_INC(BINARY_OP, hit); + res = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right); + _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); + _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); + if (res == NULL) goto pop_2_error; + } + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(POP_TOP) { + TARGET(BINARY_SLICE) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(POP_TOP); - PyObject *value; - value = stack_pointer[-1]; - Py_DECREF(value); - STACK_SHRINK(1); + INSTRUCTION_STATS(BINARY_SLICE); + PyObject *stop; + PyObject *start; + PyObject *container; + PyObject *res; + stop = stack_pointer[-1]; + start = stack_pointer[-2]; + container = stack_pointer[-3]; + PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop); + // Can't use ERROR_IF() here, because we haven't + // DECREF'ed container yet, and we still own slice. + if (slice == NULL) { + res = NULL; + } + else { + res = PyObject_GetItem(container, slice); + Py_DECREF(slice); + } + Py_DECREF(container); + if (res == NULL) goto pop_3_error; + stack_pointer[-3] = res; + stack_pointer += -2; DISPATCH(); } - TARGET(PUSH_NULL) { + TARGET(BINARY_SUBSCR) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(PUSH_NULL); + next_instr += 2; + INSTRUCTION_STATS(BINARY_SUBSCR); + PREDICTED(BINARY_SUBSCR); + _Py_CODEUNIT *this_instr = next_instr - 2; + PyObject *sub; + PyObject *container; PyObject *res; - res = NULL; - STACK_GROW(1); - stack_pointer[-1] = res; + // _SPECIALIZE_BINARY_SUBSCR + sub = stack_pointer[-1]; + container = stack_pointer[-2]; + { + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + next_instr = this_instr; + _Py_Specialize_BinarySubscr(container, sub, next_instr); + DISPATCH_SAME_OPARG(); + } + STAT_INC(BINARY_SUBSCR, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ + } + // _BINARY_SUBSCR + { + res = PyObject_GetItem(container, sub); + Py_DECREF(container); + Py_DECREF(sub); + if (res == NULL) goto pop_2_error; + } + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(END_FOR) { + TARGET(BINARY_SUBSCR_DICT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(END_FOR); - PyObject *value; - // POP_TOP - value = stack_pointer[-1]; - { - Py_DECREF(value); - } - // POP_TOP - value = stack_pointer[-2]; - { - Py_DECREF(value); + next_instr += 2; + INSTRUCTION_STATS(BINARY_SUBSCR_DICT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); + PyObject *sub; + PyObject *dict; + PyObject *res; + sub = stack_pointer[-1]; + dict = stack_pointer[-2]; + DEOPT_IF(!PyDict_CheckExact(dict), BINARY_SUBSCR); + STAT_INC(BINARY_SUBSCR, hit); + int rc = PyDict_GetItemRef(dict, sub, &res); + if (rc == 0) { + _PyErr_SetKeyError(sub); } - STACK_SHRINK(2); + Py_DECREF(dict); + Py_DECREF(sub); + if (rc <= 0) goto pop_2_error; + // not found or error + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(INSTRUMENTED_END_FOR) { + TARGET(BINARY_SUBSCR_GETITEM) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_END_FOR); - PyObject *value; - PyObject *receiver; - value = stack_pointer[-1]; - receiver = stack_pointer[-2]; - TIER_ONE_ONLY - /* Need to create a fake StopIteration error here, - * to conform to PEP 380 */ - if (PyGen_Check(receiver)) { - PyErr_SetObject(PyExc_StopIteration, value); - if (monitor_stop_iteration(tstate, frame, this_instr)) { - GOTO_ERROR(error); - } - PyErr_SetRaisedException(NULL); - } - Py_DECREF(receiver); - Py_DECREF(value); + next_instr += 2; + INSTRUCTION_STATS(BINARY_SUBSCR_GETITEM); + static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); + PyObject *sub; + PyObject *container; + sub = stack_pointer[-1]; + container = stack_pointer[-2]; + DEOPT_IF(tstate->interp->eval_frame, BINARY_SUBSCR); + PyTypeObject *tp = Py_TYPE(container); + DEOPT_IF(!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE), BINARY_SUBSCR); + PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; + PyObject *cached = ht->_spec_cache.getitem; + DEOPT_IF(cached == NULL, BINARY_SUBSCR); + assert(PyFunction_Check(cached)); + PyFunctionObject *getitem = (PyFunctionObject *)cached; + uint32_t cached_version = ht->_spec_cache.getitem_version; + DEOPT_IF(getitem->func_version != cached_version, BINARY_SUBSCR); + PyCodeObject *code = (PyCodeObject *)getitem->func_code; + assert(code->co_argcount == 2); + DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), BINARY_SUBSCR); + STAT_INC(BINARY_SUBSCR, hit); + Py_INCREF(getitem); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, getitem, 2); STACK_SHRINK(2); - DISPATCH(); + new_frame->localsplus[0] = container; + new_frame->localsplus[1] = sub; + frame->return_offset = (uint16_t)(next_instr - this_instr); + DISPATCH_INLINED(new_frame); } - TARGET(END_SEND) { + TARGET(BINARY_SUBSCR_LIST_INT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(END_SEND); - PyObject *value; - PyObject *receiver; - value = stack_pointer[-1]; - receiver = stack_pointer[-2]; - Py_DECREF(receiver); - STACK_SHRINK(1); - stack_pointer[-1] = value; + next_instr += 2; + INSTRUCTION_STATS(BINARY_SUBSCR_LIST_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); + PyObject *sub; + PyObject *list; + PyObject *res; + sub = stack_pointer[-1]; + list = stack_pointer[-2]; + DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); + DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR); + // Deopt unless 0 <= sub < PyList_Size(list) + DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR); + STAT_INC(BINARY_SUBSCR, hit); + res = PyList_GET_ITEM(list, index); + assert(res != NULL); + Py_INCREF(res); + _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); + Py_DECREF(list); + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(INSTRUMENTED_END_SEND) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_END_SEND); - PyObject *value; - PyObject *receiver; - value = stack_pointer[-1]; - receiver = stack_pointer[-2]; - TIER_ONE_ONLY - if (PyGen_Check(receiver) || PyCoro_CheckExact(receiver)) { - PyErr_SetObject(PyExc_StopIteration, value); - if (monitor_stop_iteration(tstate, frame, this_instr)) { - GOTO_ERROR(error); - } - PyErr_SetRaisedException(NULL); - } - Py_DECREF(receiver); - STACK_SHRINK(1); - stack_pointer[-1] = value; + TARGET(BINARY_SUBSCR_STR_INT) { + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(BINARY_SUBSCR_STR_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); + PyObject *sub; + PyObject *str; + PyObject *res; + sub = stack_pointer[-1]; + str = stack_pointer[-2]; + DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); + DEOPT_IF(!PyUnicode_CheckExact(str), BINARY_SUBSCR); + DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + DEOPT_IF(PyUnicode_GET_LENGTH(str) <= index, BINARY_SUBSCR); + // Specialize for reading an ASCII character from any string: + Py_UCS4 c = PyUnicode_READ_CHAR(str, index); + DEOPT_IF(Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c, BINARY_SUBSCR); + STAT_INC(BINARY_SUBSCR, hit); + res = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; + _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); + Py_DECREF(str); + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(UNARY_NEGATIVE) { + TARGET(BINARY_SUBSCR_TUPLE_INT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(UNARY_NEGATIVE); - PyObject *value; + next_instr += 2; + INSTRUCTION_STATS(BINARY_SUBSCR_TUPLE_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); + PyObject *sub; + PyObject *tuple; PyObject *res; - value = stack_pointer[-1]; - res = PyNumber_Negative(value); - Py_DECREF(value); - if (res == NULL) goto pop_1_error; - stack_pointer[-1] = res; + sub = stack_pointer[-1]; + tuple = stack_pointer[-2]; + DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); + DEOPT_IF(!PyTuple_CheckExact(tuple), BINARY_SUBSCR); + // Deopt unless 0 <= sub < PyTuple_Size(list) + DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR); + STAT_INC(BINARY_SUBSCR, hit); + res = PyTuple_GET_ITEM(tuple, index); + assert(res != NULL); + Py_INCREF(res); + _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); + Py_DECREF(tuple); + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(UNARY_NOT) { + TARGET(BUILD_CONST_KEY_MAP) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(UNARY_NOT); - PyObject *value; - PyObject *res; - value = stack_pointer[-1]; - assert(PyBool_Check(value)); - res = Py_IsFalse(value) ? Py_True : Py_False; - stack_pointer[-1] = res; + INSTRUCTION_STATS(BUILD_CONST_KEY_MAP); + PyObject *keys; + PyObject **values; + PyObject *map; + keys = stack_pointer[-1]; + values = &stack_pointer[-1 - oparg]; + if (!PyTuple_CheckExact(keys) || + PyTuple_GET_SIZE(keys) != (Py_ssize_t)oparg) { + _PyErr_SetString(tstate, PyExc_SystemError, + "bad BUILD_CONST_KEY_MAP keys argument"); + GOTO_ERROR(error); // Pop the keys and values. + } + map = _PyDict_FromItems( + &PyTuple_GET_ITEM(keys, 0), 1, + values, 1, oparg); + for (int _i = oparg; --_i >= 0;) { + Py_DECREF(values[_i]); + } + Py_DECREF(keys); + if (map == NULL) { stack_pointer += -1 - oparg; goto error; } + stack_pointer[-1 - oparg] = map; + stack_pointer += -oparg; DISPATCH(); } - TARGET(TO_BOOL) { + TARGET(BUILD_LIST) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(TO_BOOL); - PREDICTED(TO_BOOL); - _Py_CODEUNIT *this_instr = next_instr - 4; - static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); - PyObject *value; - PyObject *res; - // _SPECIALIZE_TO_BOOL - value = stack_pointer[-1]; - { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - next_instr = this_instr; - _Py_Specialize_ToBool(value, next_instr); - DISPATCH_SAME_OPARG(); - } - STAT_INC(TO_BOOL, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ - } - // _TO_BOOL - { - int err = PyObject_IsTrue(value); - Py_DECREF(value); - if (err < 0) goto pop_1_error; - res = err ? Py_True : Py_False; - } - stack_pointer[-1] = res; + next_instr += 1; + INSTRUCTION_STATS(BUILD_LIST); + PyObject **values; + PyObject *list; + values = &stack_pointer[-oparg]; + list = _PyList_FromArraySteal(values, oparg); + if (list == NULL) { stack_pointer += -oparg; goto error; } + stack_pointer[-oparg] = list; + stack_pointer += 1 - oparg; DISPATCH(); } - TARGET(TO_BOOL_BOOL) { + TARGET(BUILD_MAP) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(TO_BOOL_BOOL); - PyObject *value; - value = stack_pointer[-1]; - DEOPT_IF(!PyBool_Check(value), TO_BOOL); - STAT_INC(TO_BOOL, hit); + next_instr += 1; + INSTRUCTION_STATS(BUILD_MAP); + PyObject **values; + PyObject *map; + values = &stack_pointer[-oparg*2]; + map = _PyDict_FromItems( + values, 2, + values+1, 2, + oparg); + for (int _i = oparg*2; --_i >= 0;) { + Py_DECREF(values[_i]); + } + if (map == NULL) { stack_pointer += -oparg*2; goto error; } + stack_pointer[-oparg*2] = map; + stack_pointer += 1 - oparg*2; DISPATCH(); } - TARGET(TO_BOOL_INT) { + TARGET(BUILD_SET) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(TO_BOOL_INT); - PyObject *value; - PyObject *res; - value = stack_pointer[-1]; - DEOPT_IF(!PyLong_CheckExact(value), TO_BOOL); - STAT_INC(TO_BOOL, hit); - if (_PyLong_IsZero((PyLongObject *)value)) { - assert(_Py_IsImmortal(value)); - res = Py_False; + next_instr += 1; + INSTRUCTION_STATS(BUILD_SET); + PyObject **values; + PyObject *set; + values = &stack_pointer[-oparg]; + set = PySet_New(NULL); + if (set == NULL) + GOTO_ERROR(error); + int err = 0; + for (int i = 0; i < oparg; i++) { + PyObject *item = values[i]; + if (err == 0) + err = PySet_Add(set, item); + Py_DECREF(item); } - else { - Py_DECREF(value); - res = Py_True; + if (err != 0) { + Py_DECREF(set); + if (true) { stack_pointer += -oparg; goto error; } } - stack_pointer[-1] = res; + stack_pointer[-oparg] = set; + stack_pointer += 1 - oparg; DISPATCH(); } - TARGET(TO_BOOL_LIST) { + TARGET(BUILD_SLICE) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(TO_BOOL_LIST); - PyObject *value; - PyObject *res; - value = stack_pointer[-1]; - DEOPT_IF(!PyList_CheckExact(value), TO_BOOL); - STAT_INC(TO_BOOL, hit); - res = Py_SIZE(value) ? Py_True : Py_False; - Py_DECREF(value); - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(TO_BOOL_NONE) { - frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(TO_BOOL_NONE); - PyObject *value; - PyObject *res; - value = stack_pointer[-1]; - // This one is a bit weird, because we expect *some* failures: - DEOPT_IF(!Py_IsNone(value), TO_BOOL); - STAT_INC(TO_BOOL, hit); - res = Py_False; - stack_pointer[-1] = res; + next_instr += 1; + INSTRUCTION_STATS(BUILD_SLICE); + PyObject *step = NULL; + PyObject *stop; + PyObject *start; + PyObject *slice; + if (oparg == 3) { step = stack_pointer[-(((oparg == 3) ? 1 : 0))]; } + stop = stack_pointer[-1 - (((oparg == 3) ? 1 : 0))]; + start = stack_pointer[-2 - (((oparg == 3) ? 1 : 0))]; + slice = PySlice_New(start, stop, step); + Py_DECREF(start); + Py_DECREF(stop); + Py_XDECREF(step); + if (slice == NULL) { stack_pointer += -2 - (((oparg == 3) ? 1 : 0)); goto error; } + stack_pointer[-2 - (((oparg == 3) ? 1 : 0))] = slice; + stack_pointer += -1 - (((oparg == 3) ? 1 : 0)); DISPATCH(); } - TARGET(TO_BOOL_STR) { + TARGET(BUILD_STRING) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(TO_BOOL_STR); - PyObject *value; - PyObject *res; - value = stack_pointer[-1]; - DEOPT_IF(!PyUnicode_CheckExact(value), TO_BOOL); - STAT_INC(TO_BOOL, hit); - if (value == &_Py_STR(empty)) { - assert(_Py_IsImmortal(value)); - res = Py_False; - } - else { - assert(Py_SIZE(value)); - Py_DECREF(value); - res = Py_True; + next_instr += 1; + INSTRUCTION_STATS(BUILD_STRING); + PyObject **pieces; + PyObject *str; + pieces = &stack_pointer[-oparg]; + str = _PyUnicode_JoinArray(&_Py_STR(empty), pieces, oparg); + for (int _i = oparg; --_i >= 0;) { + Py_DECREF(pieces[_i]); } - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(TO_BOOL_ALWAYS_TRUE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(TO_BOOL_ALWAYS_TRUE); - PyObject *value; - PyObject *res; - value = stack_pointer[-1]; - uint32_t version = read_u32(&this_instr[2].cache); - // This one is a bit weird, because we expect *some* failures: - assert(version); - DEOPT_IF(Py_TYPE(value)->tp_version_tag != version, TO_BOOL); - STAT_INC(TO_BOOL, hit); - Py_DECREF(value); - res = Py_True; - stack_pointer[-1] = res; + if (str == NULL) { stack_pointer += -oparg; goto error; } + stack_pointer[-oparg] = str; + stack_pointer += 1 - oparg; DISPATCH(); } - TARGET(UNARY_INVERT) { + TARGET(BUILD_TUPLE) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(UNARY_INVERT); - PyObject *value; - PyObject *res; - value = stack_pointer[-1]; - res = PyNumber_Invert(value); - Py_DECREF(value); - if (res == NULL) goto pop_1_error; - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(BINARY_OP_MULTIPLY_INT) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_MULTIPLY_INT); - PyObject *right; - PyObject *left; - PyObject *res; - // _GUARD_BOTH_INT - right = stack_pointer[-1]; - left = stack_pointer[-2]; - { - DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); - } - // _BINARY_OP_MULTIPLY_INT - { - STAT_INC(BINARY_OP, hit); - res = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right); - _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); - _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - if (res == NULL) goto pop_2_error; - } - STACK_SHRINK(1); - stack_pointer[-1] = res; + INSTRUCTION_STATS(BUILD_TUPLE); + PyObject **values; + PyObject *tup; + values = &stack_pointer[-oparg]; + tup = _PyTuple_FromArraySteal(values, oparg); + if (tup == NULL) { stack_pointer += -oparg; goto error; } + stack_pointer[-oparg] = tup; + stack_pointer += 1 - oparg; DISPATCH(); } - TARGET(BINARY_OP_ADD_INT) { + TARGET(CACHE) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_ADD_INT); - PyObject *right; - PyObject *left; - PyObject *res; - // _GUARD_BOTH_INT - right = stack_pointer[-1]; - left = stack_pointer[-2]; - { - DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); - } - // _BINARY_OP_ADD_INT - { - STAT_INC(BINARY_OP, hit); - res = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right); - _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); - _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - if (res == NULL) goto pop_2_error; - } - STACK_SHRINK(1); - stack_pointer[-1] = res; - DISPATCH(); + next_instr += 1; + INSTRUCTION_STATS(CACHE); + assert(0 && "Executing a cache."); + Py_UNREACHABLE(); } - TARGET(BINARY_OP_SUBTRACT_INT) { + TARGET(CALL) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_SUBTRACT_INT); - PyObject *right; - PyObject *left; + next_instr += 4; + INSTRUCTION_STATS(CALL); + PREDICTED(CALL); + _Py_CODEUNIT *this_instr = next_instr - 4; + PyObject **args; + PyObject *self_or_null; + PyObject *callable; PyObject *res; - // _GUARD_BOTH_INT - right = stack_pointer[-1]; - left = stack_pointer[-2]; + // _SPECIALIZE_CALL + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; { - DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + next_instr = this_instr; + _Py_Specialize_Call(callable, next_instr, oparg + (self_or_null != NULL)); + DISPATCH_SAME_OPARG(); + } + STAT_INC(CALL, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ } - // _BINARY_OP_SUBTRACT_INT + // _CALL { - STAT_INC(BINARY_OP, hit); - res = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right); - _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); - _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - if (res == NULL) goto pop_2_error; + // oparg counts all of the args, but *not* self: + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + else if (Py_TYPE(callable) == &PyMethod_Type) { + args--; + total_args++; + PyObject *self = ((PyMethodObject *)callable)->im_self; + args[0] = Py_NewRef(self); + PyObject *method = ((PyMethodObject *)callable)->im_func; + args[-1] = Py_NewRef(method); + Py_DECREF(callable); + callable = method; + } + // Check if the call can be inlined or not + if (Py_TYPE(callable) == &PyFunction_Type && + tstate->interp->eval_frame == NULL && + ((PyFunctionObject *)callable)->vectorcall == _PyFunction_Vectorcall) + { + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( + tstate, (PyFunctionObject *)callable, locals, + args, total_args, NULL + ); + // Manipulate stack directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); + // The frame has stolen all the arguments from the stack, + // so there is no need to clean them up. + if (new_frame == NULL) { + GOTO_ERROR(error); + } + frame->return_offset = (uint16_t)(next_instr - this_instr); + DISPATCH_INLINED(new_frame); + } + /* Callable is not a normal Python function */ + res = PyObject_Vectorcall( + callable, args, + total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, + NULL); + if (opcode == INSTRUMENTED_CALL) { + PyObject *arg = total_args == 0 ? + &_PyInstrumentation_MISSING : args[0]; + if (res == NULL) { + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, callable, arg); + } + else { + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, callable, arg); + if (err < 0) { + Py_CLEAR(res); + } + } + } + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(callable); + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } } - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(BINARY_OP_MULTIPLY_FLOAT) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_MULTIPLY_FLOAT); - PyObject *right; - PyObject *left; - PyObject *res; - // _GUARD_BOTH_FLOAT - right = stack_pointer[-1]; - left = stack_pointer[-2]; - { - DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP); + TARGET(CALL_ALLOC_AND_ENTER_INIT) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_ALLOC_AND_ENTER_INIT); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *null; + PyObject *callable; + args = &stack_pointer[-oparg]; + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + /* This instruction does the following: + * 1. Creates the object (by calling ``object.__new__``) + * 2. Pushes a shim frame to the frame stack (to cleanup after ``__init__``) + * 3. Pushes the frame for ``__init__`` to the frame stack + * */ + _PyCallCache *cache = (_PyCallCache *)&this_instr[1]; + DEOPT_IF(null != NULL, CALL); + DEOPT_IF(!PyType_Check(callable), CALL); + PyTypeObject *tp = (PyTypeObject *)callable; + DEOPT_IF(tp->tp_version_tag != read_u32(cache->func_version), CALL); + PyHeapTypeObject *cls = (PyHeapTypeObject *)callable; + PyFunctionObject *init = (PyFunctionObject *)cls->_spec_cache.init; + PyCodeObject *code = (PyCodeObject *)init->func_code; + DEOPT_IF(code->co_argcount != oparg+1, CALL); + DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize), CALL); + STAT_INC(CALL, hit); + PyObject *self = _PyType_NewManagedObject(tp); + if (self == NULL) { + GOTO_ERROR(error); } - // _BINARY_OP_MULTIPLY_FLOAT - { - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left)->ob_fval * - ((PyFloatObject *)right)->ob_fval; - DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); + Py_DECREF(tp); + _PyInterpreterFrame *shim = _PyFrame_PushTrampolineUnchecked( + tstate, (PyCodeObject *)&_Py_InitCleanup, 1); + assert(_PyCode_CODE((PyCodeObject *)shim->f_executable)[0].op.code == EXIT_INIT_CHECK); + /* Push self onto stack of shim */ + Py_INCREF(self); + shim->localsplus[0] = self; + Py_INCREF(init); + _PyInterpreterFrame *init_frame = _PyFrame_PushUnchecked(tstate, init, oparg+1); + /* Copy self followed by args to __init__ frame */ + init_frame->localsplus[0] = self; + for (int i = 0; i < oparg; i++) { + init_frame->localsplus[i+1] = args[i]; } - STACK_SHRINK(1); - stack_pointer[-1] = res; - DISPATCH(); + frame->return_offset = (uint16_t)(next_instr - this_instr); + STACK_SHRINK(oparg+2); + _PyFrame_SetStackPointer(frame, stack_pointer); + /* Link frames */ + init_frame->previous = shim; + shim->previous = frame; + frame = tstate->current_frame = init_frame; + CALL_STAT_INC(inlined_py_calls); + /* Account for pushing the extra frame. + * We don't check recursion depth here, + * as it will be checked after start_frame */ + tstate->py_recursion_remaining--; + goto start_frame; } - TARGET(BINARY_OP_ADD_FLOAT) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_ADD_FLOAT); - PyObject *right; - PyObject *left; - PyObject *res; - // _GUARD_BOTH_FLOAT - right = stack_pointer[-1]; - left = stack_pointer[-2]; + TARGET(CALL_BOUND_METHOD_EXACT_ARGS) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_BOUND_METHOD_EXACT_ARGS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject *null; + PyObject *callable; + PyObject *func; + PyObject *self; + PyObject *self_or_null; + PyObject **args; + _PyInterpreterFrame *new_frame; + /* Skip 1 cache entry */ + // _CHECK_PEP_523 { - DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP); + DEOPT_IF(tstate->interp->eval_frame, CALL); } - // _BINARY_OP_ADD_FLOAT + // _CHECK_CALL_BOUND_METHOD_EXACT_ARGS + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; { - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left)->ob_fval + - ((PyFloatObject *)right)->ob_fval; - DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); + DEOPT_IF(null != NULL, CALL); + DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type, CALL); } - STACK_SHRINK(1); - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(BINARY_OP_SUBTRACT_FLOAT) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_SUBTRACT_FLOAT); - PyObject *right; - PyObject *left; - PyObject *res; - // _GUARD_BOTH_FLOAT - right = stack_pointer[-1]; - left = stack_pointer[-2]; + // _INIT_CALL_BOUND_METHOD_EXACT_ARGS { - DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP); + STAT_INC(CALL, hit); + self = Py_NewRef(((PyMethodObject *)callable)->im_self); + stack_pointer[-1 - oparg] = self; // Patch stack as it is used by _INIT_CALL_PY_EXACT_ARGS + func = Py_NewRef(((PyMethodObject *)callable)->im_func); + stack_pointer[-2 - oparg] = func; // This is used by CALL, upon deoptimization + Py_DECREF(callable); } - // _BINARY_OP_SUBTRACT_FLOAT + // _CHECK_FUNCTION_EXACT_ARGS + self_or_null = self; + callable = func; { - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left)->ob_fval - - ((PyFloatObject *)right)->ob_fval; - DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); + uint32_t func_version = read_u32(&this_instr[2].cache); + DEOPT_IF(!PyFunction_Check(callable), CALL); + PyFunctionObject *func = (PyFunctionObject *)callable; + DEOPT_IF(func->func_version != func_version, CALL); + PyCodeObject *code = (PyCodeObject *)func->func_code; + DEOPT_IF(code->co_argcount != oparg + (self_or_null != NULL), CALL); } - STACK_SHRINK(1); - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(BINARY_OP_ADD_UNICODE) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_ADD_UNICODE); - PyObject *right; - PyObject *left; - PyObject *res; - // _GUARD_BOTH_UNICODE - right = stack_pointer[-1]; - left = stack_pointer[-2]; + // _CHECK_STACK_SPACE { - DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyUnicode_CheckExact(right), BINARY_OP); + PyFunctionObject *func = (PyFunctionObject *)callable; + PyCodeObject *code = (PyCodeObject *)func->func_code; + DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); + DEOPT_IF(tstate->py_recursion_remaining <= 1, CALL); } - // _BINARY_OP_ADD_UNICODE + // _INIT_CALL_PY_EXACT_ARGS + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; { - STAT_INC(BINARY_OP, hit); - res = PyUnicode_Concat(left, right); - _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); - _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); - if (res == NULL) goto pop_2_error; + int argcount = oparg; + if (self_or_null != NULL) { + args--; + argcount++; + } + STAT_INC(CALL, hit); + PyFunctionObject *func = (PyFunctionObject *)callable; + new_frame = _PyFrame_PushUnchecked(tstate, func, argcount); + for (int i = 0; i < argcount; i++) { + new_frame->localsplus[i] = args[i]; + } } - STACK_SHRINK(1); - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(BINARY_OP_INPLACE_ADD_UNICODE) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_OP_INPLACE_ADD_UNICODE); - PyObject *right; - PyObject *left; - // _GUARD_BOTH_UNICODE - right = stack_pointer[-1]; - left = stack_pointer[-2]; + // _SAVE_RETURN_OFFSET { - DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); - DEOPT_IF(!PyUnicode_CheckExact(right), BINARY_OP); + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif } - // _BINARY_OP_INPLACE_ADD_UNICODE + // _PUSH_FRAME { - assert(next_instr->op.code == STORE_FAST); - PyObject **target_local = &GETLOCAL(next_instr->op.arg); - DEOPT_IF(*target_local != left, BINARY_OP); - STAT_INC(BINARY_OP, hit); - /* Handle `left = left + right` or `left += right` for str. - * - * When possible, extend `left` in place rather than - * allocating a new PyUnicodeObject. This attempts to avoid - * quadratic behavior when one neglects to use str.join(). - * - * If `left` has only two references remaining (one from - * the stack, one in the locals), DECREFing `left` leaves - * only the locals reference, so PyUnicode_Append knows - * that the string is safe to mutate. - */ - assert(Py_REFCNT(left) >= 2); - _Py_DECREF_NO_DEALLOC(left); - PyUnicode_Append(target_local, right); - _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); - if (*target_local == NULL) goto pop_2_error; - // The STORE_FAST is already done. - assert(next_instr->op.code == STORE_FAST); - SKIP_OVER(1); + // Write it out explicitly because it's subtly different. + // Eventually this should be the only occurrence of this code. + assert(tstate->interp->eval_frame == NULL); + stack_pointer += -2 - oparg; + _PyFrame_SetStackPointer(frame, stack_pointer); + new_frame->previous = frame; + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = new_frame; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + #if LLTRACE && TIER_ONE + lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); + if (lltrace < 0) { + goto exit_unwind; + } + #endif } - STACK_SHRINK(2); + stack_pointer += (((0) ? 1 : 0)); DISPATCH(); } - TARGET(BINARY_SUBSCR) { + TARGET(CALL_BUILTIN_CLASS) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR); - PREDICTED(BINARY_SUBSCR); - _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); - PyObject *sub; - PyObject *container; + next_instr += 4; + INSTRUCTION_STATS(CALL_BUILTIN_CLASS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; PyObject *res; - // _SPECIALIZE_BINARY_SUBSCR - sub = stack_pointer[-1]; - container = stack_pointer[-2]; - { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - next_instr = this_instr; - _Py_Specialize_BinarySubscr(container, sub, next_instr); - DISPATCH_SAME_OPARG(); - } - STAT_INC(BINARY_SUBSCR, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; } - // _BINARY_SUBSCR - { - res = PyObject_GetItem(container, sub); - Py_DECREF(container); - Py_DECREF(sub); - if (res == NULL) goto pop_2_error; + DEOPT_IF(!PyType_Check(callable), CALL); + PyTypeObject *tp = (PyTypeObject *)callable; + DEOPT_IF(tp->tp_vectorcall == NULL, CALL); + STAT_INC(CALL, hit); + res = tp->tp_vectorcall((PyObject *)tp, args, total_args, NULL); + /* Free the arguments. */ + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); } - STACK_SHRINK(1); - stack_pointer[-1] = res; + Py_DECREF(tp); + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(BINARY_SLICE) { + TARGET(CALL_BUILTIN_FAST) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(BINARY_SLICE); - PyObject *stop; - PyObject *start; - PyObject *container; + next_instr += 4; + INSTRUCTION_STATS(CALL_BUILTIN_FAST); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; PyObject *res; - stop = stack_pointer[-1]; - start = stack_pointer[-2]; - container = stack_pointer[-3]; - PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop); - // Can't use ERROR_IF() here, because we haven't - // DECREF'ed container yet, and we still own slice. - if (slice == NULL) { - res = NULL; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + /* Builtin METH_FASTCALL functions, without keywords */ + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; } - else { - res = PyObject_GetItem(container, slice); - Py_DECREF(slice); + DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); + DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL, CALL); + STAT_INC(CALL, hit); + PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); + /* res = func(self, args, nargs) */ + res = ((_PyCFunctionFast)(void(*)(void))cfunc)( + PyCFunction_GET_SELF(callable), + args, + total_args); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + /* Free the arguments. */ + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); } - Py_DECREF(container); - if (res == NULL) goto pop_3_error; - STACK_SHRINK(2); - stack_pointer[-1] = res; + Py_DECREF(callable); + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + /* Not deopting because this doesn't mean our optimization was + wrong. `res` can be NULL for valid reasons. Eg. getattr(x, + 'invalid'). In those cases an exception is set, so we must + handle it. + */ + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(STORE_SLICE) { + TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(STORE_SLICE); - PyObject *stop; - PyObject *start; - PyObject *container; - PyObject *v; - stop = stack_pointer[-1]; - start = stack_pointer[-2]; - container = stack_pointer[-3]; - v = stack_pointer[-4]; - PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop); - int err; - if (slice == NULL) { - err = 1; + next_instr += 4; + INSTRUCTION_STATS(CALL_BUILTIN_FAST_WITH_KEYWORDS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + PyObject *res; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; } - else { - err = PyObject_SetItem(container, slice, v); - Py_DECREF(slice); + DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); + DEOPT_IF(PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS), CALL); + STAT_INC(CALL, hit); + /* res = func(self, args, nargs, kwnames) */ + _PyCFunctionFastWithKeywords cfunc = + (_PyCFunctionFastWithKeywords)(void(*)(void)) + PyCFunction_GET_FUNCTION(callable); + res = cfunc(PyCFunction_GET_SELF(callable), args, total_args, NULL); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + /* Free the arguments. */ + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); } - Py_DECREF(v); - Py_DECREF(container); - if (err) goto pop_4_error; - STACK_SHRINK(4); - DISPATCH(); - } - - TARGET(BINARY_SUBSCR_LIST_INT) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR_LIST_INT); - PyObject *sub; - PyObject *list; - PyObject *res; - sub = stack_pointer[-1]; - list = stack_pointer[-2]; - DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); - DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR); - - // Deopt unless 0 <= sub < PyList_Size(list) - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR); - STAT_INC(BINARY_SUBSCR, hit); - res = PyList_GET_ITEM(list, index); - assert(res != NULL); - Py_INCREF(res); - _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); - Py_DECREF(list); - STACK_SHRINK(1); - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(BINARY_SUBSCR_STR_INT) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR_STR_INT); - PyObject *sub; - PyObject *str; - PyObject *res; - sub = stack_pointer[-1]; - str = stack_pointer[-2]; - DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); - DEOPT_IF(!PyUnicode_CheckExact(str), BINARY_SUBSCR); - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - DEOPT_IF(PyUnicode_GET_LENGTH(str) <= index, BINARY_SUBSCR); - // Specialize for reading an ASCII character from any string: - Py_UCS4 c = PyUnicode_READ_CHAR(str, index); - DEOPT_IF(Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c, BINARY_SUBSCR); - STAT_INC(BINARY_SUBSCR, hit); - res = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; - _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); - Py_DECREF(str); - STACK_SHRINK(1); - stack_pointer[-1] = res; + Py_DECREF(callable); + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(BINARY_SUBSCR_TUPLE_INT) { + TARGET(CALL_BUILTIN_O) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR_TUPLE_INT); - PyObject *sub; - PyObject *tuple; + next_instr += 4; + INSTRUCTION_STATS(CALL_BUILTIN_O); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; PyObject *res; - sub = stack_pointer[-1]; - tuple = stack_pointer[-2]; - DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); - DEOPT_IF(!PyTuple_CheckExact(tuple), BINARY_SUBSCR); - - // Deopt unless 0 <= sub < PyTuple_Size(list) - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR); - STAT_INC(BINARY_SUBSCR, hit); - res = PyTuple_GET_ITEM(tuple, index); - assert(res != NULL); - Py_INCREF(res); - _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); - Py_DECREF(tuple); - STACK_SHRINK(1); - stack_pointer[-1] = res; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + /* Builtin METH_O functions */ + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + DEOPT_IF(total_args != 1, CALL); + DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); + DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_O, CALL); + STAT_INC(CALL, hit); + PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); + // This is slower but CPython promises to check all non-vectorcall + // function calls. + if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { + GOTO_ERROR(error); + } + PyObject *arg = args[0]; + res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(arg); + Py_DECREF(callable); + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(BINARY_SUBSCR_DICT) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR_DICT); - PyObject *sub; - PyObject *dict; - PyObject *res; - sub = stack_pointer[-1]; - dict = stack_pointer[-2]; - DEOPT_IF(!PyDict_CheckExact(dict), BINARY_SUBSCR); - STAT_INC(BINARY_SUBSCR, hit); - int rc = PyDict_GetItemRef(dict, sub, &res); - if (rc == 0) { - _PyErr_SetKeyError(sub); - } - Py_DECREF(dict); - Py_DECREF(sub); - if (rc <= 0) goto pop_2_error; - STACK_SHRINK(1); - stack_pointer[-1] = res; - DISPATCH(); - } - - TARGET(BINARY_SUBSCR_GETITEM) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR_GETITEM); - PyObject *sub; - PyObject *container; - sub = stack_pointer[-1]; - container = stack_pointer[-2]; - DEOPT_IF(tstate->interp->eval_frame, BINARY_SUBSCR); - PyTypeObject *tp = Py_TYPE(container); - DEOPT_IF(!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE), BINARY_SUBSCR); - PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; - PyObject *cached = ht->_spec_cache.getitem; - DEOPT_IF(cached == NULL, BINARY_SUBSCR); - assert(PyFunction_Check(cached)); - PyFunctionObject *getitem = (PyFunctionObject *)cached; - uint32_t cached_version = ht->_spec_cache.getitem_version; - DEOPT_IF(getitem->func_version != cached_version, BINARY_SUBSCR); - PyCodeObject *code = (PyCodeObject *)getitem->func_code; - assert(code->co_argcount == 2); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), BINARY_SUBSCR); - STAT_INC(BINARY_SUBSCR, hit); - Py_INCREF(getitem); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, getitem, 2); - STACK_SHRINK(2); - new_frame->localsplus[0] = container; - new_frame->localsplus[1] = sub; - frame->return_offset = (uint16_t)(next_instr - this_instr); - DISPATCH_INLINED(new_frame); - } - - TARGET(LIST_APPEND) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(LIST_APPEND); - PyObject *v; - PyObject *list; - v = stack_pointer[-1]; - list = stack_pointer[-2 - (oparg-1)]; - if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0) goto pop_1_error; - STACK_SHRINK(1); - DISPATCH(); - } - - TARGET(SET_ADD) { + TARGET(CALL_FUNCTION_EX) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(SET_ADD); - PyObject *v; - PyObject *set; - v = stack_pointer[-1]; - set = stack_pointer[-2 - (oparg-1)]; - int err = PySet_Add(set, v); - Py_DECREF(v); - if (err) goto pop_1_error; - STACK_SHRINK(1); - DISPATCH(); - } - - TARGET(STORE_SUBSCR) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(STORE_SUBSCR); - PREDICTED(STORE_SUBSCR); - _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_STORE_SUBSCR == 1, "incorrect cache size"); - PyObject *sub; - PyObject *container; - PyObject *v; - // _SPECIALIZE_STORE_SUBSCR - sub = stack_pointer[-1]; - container = stack_pointer[-2]; - { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - next_instr = this_instr; - _Py_Specialize_StoreSubscr(container, sub, next_instr); - DISPATCH_SAME_OPARG(); + INSTRUCTION_STATS(CALL_FUNCTION_EX); + PREDICTED(CALL_FUNCTION_EX); + _Py_CODEUNIT *this_instr = next_instr - 1; + PyObject *kwargs = NULL; + PyObject *callargs; + PyObject *func; + PyObject *result; + if (oparg & 1) { kwargs = stack_pointer[-((oparg & 1))]; } + callargs = stack_pointer[-1 - ((oparg & 1))]; + func = stack_pointer[-3 - ((oparg & 1))]; + // DICT_MERGE is called before this opcode if there are kwargs. + // It converts all dict subtypes in kwargs into regular dicts. + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + if (!PyTuple_CheckExact(callargs)) { + if (check_args_iterable(tstate, func, callargs) < 0) { + GOTO_ERROR(error); } - STAT_INC(STORE_SUBSCR, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ + PyObject *tuple = PySequence_Tuple(callargs); + if (tuple == NULL) { + GOTO_ERROR(error); + } + Py_SETREF(callargs, tuple); } - // _STORE_SUBSCR - v = stack_pointer[-3]; - { - /* container[sub] = v */ - int err = PyObject_SetItem(container, sub, v); - Py_DECREF(v); - Py_DECREF(container); - Py_DECREF(sub); - if (err) goto pop_3_error; + assert(PyTuple_CheckExact(callargs)); + EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_FUNCTION_EX, func); + if (opcode == INSTRUMENTED_CALL_FUNCTION_EX && + !PyFunction_Check(func) && !PyMethod_Check(func) + ) { + PyObject *arg = PyTuple_GET_SIZE(callargs) > 0 ? + PyTuple_GET_ITEM(callargs, 0) : Py_None; + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, func, arg); + if (err) GOTO_ERROR(error); + result = PyObject_Call(func, callargs, kwargs); + if (result == NULL) { + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, func, arg); + } + else { + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, func, arg); + if (err < 0) { + Py_CLEAR(result); + } + } } - STACK_SHRINK(3); - DISPATCH(); - } - - TARGET(STORE_SUBSCR_LIST_INT) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(STORE_SUBSCR_LIST_INT); - PyObject *sub; - PyObject *list; - PyObject *value; - sub = stack_pointer[-1]; - list = stack_pointer[-2]; - value = stack_pointer[-3]; - DEOPT_IF(!PyLong_CheckExact(sub), STORE_SUBSCR); - DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR); - - // Ensure nonnegative, zero-or-one-digit ints. - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), STORE_SUBSCR); - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - // Ensure index < len(list) - DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR); - STAT_INC(STORE_SUBSCR, hit); - - PyObject *old_value = PyList_GET_ITEM(list, index); - PyList_SET_ITEM(list, index, value); - assert(old_value != NULL); - Py_DECREF(old_value); - _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); - Py_DECREF(list); - STACK_SHRINK(3); - DISPATCH(); - } - - TARGET(STORE_SUBSCR_DICT) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(STORE_SUBSCR_DICT); - PyObject *sub; - PyObject *dict; - PyObject *value; - sub = stack_pointer[-1]; - dict = stack_pointer[-2]; - value = stack_pointer[-3]; - DEOPT_IF(!PyDict_CheckExact(dict), STORE_SUBSCR); - STAT_INC(STORE_SUBSCR, hit); - int err = _PyDict_SetItem_Take2((PyDictObject *)dict, sub, value); - Py_DECREF(dict); - if (err) goto pop_3_error; - STACK_SHRINK(3); - DISPATCH(); - } - - TARGET(DELETE_SUBSCR) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(DELETE_SUBSCR); - PyObject *sub; - PyObject *container; - sub = stack_pointer[-1]; - container = stack_pointer[-2]; - /* del container[sub] */ - int err = PyObject_DelItem(container, sub); - Py_DECREF(container); - Py_DECREF(sub); - if (err) goto pop_2_error; - STACK_SHRINK(2); + else { + if (Py_TYPE(func) == &PyFunction_Type && + tstate->interp->eval_frame == NULL && + ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { + assert(PyTuple_CheckExact(callargs)); + Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); + int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex(tstate, + (PyFunctionObject *)func, locals, + nargs, callargs, kwargs); + // Need to manually shrink the stack since we exit with DISPATCH_INLINED. + STACK_SHRINK(oparg + 3); + if (new_frame == NULL) { + GOTO_ERROR(error); + } + assert(next_instr - this_instr == 1); + frame->return_offset = 1; + DISPATCH_INLINED(new_frame); + } + result = PyObject_Call(func, callargs, kwargs); + } + Py_DECREF(func); + Py_DECREF(callargs); + Py_XDECREF(kwargs); + assert(PEEK(2 + (oparg & 1)) == NULL); + if (result == NULL) { stack_pointer += -3 - ((oparg & 1)); goto error; } + stack_pointer[-3 - ((oparg & 1))] = result; + stack_pointer += -2 - ((oparg & 1)); + CHECK_EVAL_BREAKER(); DISPATCH(); } @@ -1102,1068 +1263,875 @@ Py_DECREF(value2); Py_DECREF(value1); if (res == NULL) goto pop_2_error; - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(RAISE_VARARGS) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(RAISE_VARARGS); + TARGET(CALL_ISINSTANCE) { + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_ISINSTANCE); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); PyObject **args; - args = stack_pointer - oparg; - TIER_ONE_ONLY - PyObject *cause = NULL, *exc = NULL; - switch (oparg) { - case 2: - cause = args[1]; - /* fall through */ - case 1: - exc = args[0]; - /* fall through */ - case 0: - if (do_raise(tstate, exc, cause)) { - assert(oparg == 0); - monitor_reraise(tstate, frame, this_instr); - goto exception_unwind; - } - break; - default: - _PyErr_SetString(tstate, PyExc_SystemError, - "bad RAISE_VARARGS oparg"); - break; + PyObject *self_or_null; + PyObject *callable; + PyObject *res; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + /* isinstance(o, o2) */ + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; } - if (true) { STACK_SHRINK(oparg); goto error; } - } - - TARGET(INTERPRETER_EXIT) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INTERPRETER_EXIT); - PyObject *retval; - retval = stack_pointer[-1]; - assert(frame == &entry_frame); - assert(_PyFrame_IsIncomplete(frame)); - /* Restore previous frame and return. */ - tstate->current_frame = frame->previous; - assert(!_PyErr_Occurred(tstate)); - tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS; - return retval; - } - - TARGET(RETURN_VALUE) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(RETURN_VALUE); - PyObject *retval; - retval = stack_pointer[-1]; - STACK_SHRINK(1); - assert(EMPTY()); - #if TIER_ONE - assert(frame != &entry_frame); - #endif - STORE_SP(); - _Py_LeaveRecursiveCallPy(tstate); - // GH-99729: We need to unlink the frame *before* clearing it: - _PyInterpreterFrame *dying = frame; - frame = tstate->current_frame = dying->previous; - _PyEval_FrameClearAndPop(tstate, dying); - _PyFrame_StackPush(frame, retval); - LOAD_SP(); - LOAD_IP(frame->return_offset); -#if LLTRACE && TIER_ONE - lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); - if (lltrace < 0) { - goto exit_unwind; + DEOPT_IF(total_args != 2, CALL); + PyInterpreterState *interp = tstate->interp; + DEOPT_IF(callable != interp->callable_cache.isinstance, CALL); + STAT_INC(CALL, hit); + PyObject *cls = args[1]; + PyObject *inst = args[0]; + int retval = PyObject_IsInstance(inst, cls); + if (retval < 0) { + GOTO_ERROR(error); } -#endif + res = PyBool_FromLong(retval); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(inst); + Py_DECREF(cls); + Py_DECREF(callable); + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; DISPATCH(); } - TARGET(INSTRUMENTED_RETURN_VALUE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_RETURN_VALUE); - PyObject *retval; - retval = stack_pointer[-1]; - int err = _Py_call_instrumentation_arg( - tstate, PY_MONITORING_EVENT_PY_RETURN, - frame, this_instr, retval); - if (err) GOTO_ERROR(error); - STACK_SHRINK(1); - assert(EMPTY()); - _PyFrame_SetStackPointer(frame, stack_pointer); - _Py_LeaveRecursiveCallPy(tstate); - assert(frame != &entry_frame); - // GH-99729: We need to unlink the frame *before* clearing it: - _PyInterpreterFrame *dying = frame; - frame = tstate->current_frame = dying->previous; - _PyEval_FrameClearAndPop(tstate, dying); - _PyFrame_StackPush(frame, retval); - LOAD_IP(frame->return_offset); - goto resume_frame; - } - - TARGET(RETURN_CONST) { + TARGET(CALL_KW) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(RETURN_CONST); - PyObject *value; - PyObject *retval; - // LOAD_CONST - { - value = GETITEM(FRAME_CO_CONSTS, oparg); - Py_INCREF(value); + INSTRUCTION_STATS(CALL_KW); + PREDICTED(CALL_KW); + _Py_CODEUNIT *this_instr = next_instr - 1; + PyObject *kwnames; + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + PyObject *res; + kwnames = stack_pointer[-1]; + args = &stack_pointer[-1 - oparg]; + self_or_null = stack_pointer[-2 - oparg]; + callable = stack_pointer[-3 - oparg]; + // oparg counts all of the args, but *not* self: + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; } - // _POP_FRAME - retval = value; + if (self_or_null == NULL && Py_TYPE(callable) == &PyMethod_Type) { + args--; + total_args++; + PyObject *self = ((PyMethodObject *)callable)->im_self; + args[0] = Py_NewRef(self); + PyObject *method = ((PyMethodObject *)callable)->im_func; + args[-1] = Py_NewRef(method); + Py_DECREF(callable); + callable = method; + } + int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames); + // Check if the call can be inlined or not + if (Py_TYPE(callable) == &PyFunction_Type && + tstate->interp->eval_frame == NULL && + ((PyFunctionObject *)callable)->vectorcall == _PyFunction_Vectorcall) { - assert(EMPTY()); - #if TIER_ONE - assert(frame != &entry_frame); - #endif - STORE_SP(); - _Py_LeaveRecursiveCallPy(tstate); - // GH-99729: We need to unlink the frame *before* clearing it: - _PyInterpreterFrame *dying = frame; - frame = tstate->current_frame = dying->previous; - _PyEval_FrameClearAndPop(tstate, dying); - _PyFrame_StackPush(frame, retval); - LOAD_SP(); - LOAD_IP(frame->return_offset); - #if LLTRACE && TIER_ONE - lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); - if (lltrace < 0) { - goto exit_unwind; + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( + tstate, (PyFunctionObject *)callable, locals, + args, positional_args, kwnames + ); + Py_DECREF(kwnames); + // Manipulate stack directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 3); + // The frame has stolen all the arguments from the stack, + // so there is no need to clean them up. + if (new_frame == NULL) { + GOTO_ERROR(error); + } + assert(next_instr - this_instr == 1); + frame->return_offset = 1; + DISPATCH_INLINED(new_frame); + } + /* Callable is not a normal Python function */ + res = PyObject_Vectorcall( + callable, args, + positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, + kwnames); + if (opcode == INSTRUMENTED_CALL_KW) { + PyObject *arg = total_args == 0 ? + &_PyInstrumentation_MISSING : args[0]; + if (res == NULL) { + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, callable, arg); + } + else { + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, callable, arg); + if (err < 0) { + Py_CLEAR(res); + } } - #endif } + Py_DECREF(kwnames); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(callable); + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); + } + if (res == NULL) { stack_pointer += -3 - oparg; goto error; } + stack_pointer[-3 - oparg] = res; + stack_pointer += -2 - oparg; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(INSTRUMENTED_RETURN_CONST) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_RETURN_CONST); - PyObject *retval = GETITEM(FRAME_CO_CONSTS, oparg); - int err = _Py_call_instrumentation_arg( - tstate, PY_MONITORING_EVENT_PY_RETURN, - frame, this_instr, retval); - if (err) GOTO_ERROR(error); - Py_INCREF(retval); - assert(EMPTY()); - _PyFrame_SetStackPointer(frame, stack_pointer); - _Py_LeaveRecursiveCallPy(tstate); - assert(frame != &entry_frame); - // GH-99729: We need to unlink the frame *before* clearing it: - _PyInterpreterFrame *dying = frame; - frame = tstate->current_frame = dying->previous; - _PyEval_FrameClearAndPop(tstate, dying); - _PyFrame_StackPush(frame, retval); - LOAD_IP(frame->return_offset); - goto resume_frame; + TARGET(CALL_LEN) { + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_LEN); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + PyObject *res; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + /* len(o) */ + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + DEOPT_IF(total_args != 1, CALL); + PyInterpreterState *interp = tstate->interp; + DEOPT_IF(callable != interp->callable_cache.len, CALL); + STAT_INC(CALL, hit); + PyObject *arg = args[0]; + Py_ssize_t len_i = PyObject_Length(arg); + if (len_i < 0) { + GOTO_ERROR(error); + } + res = PyLong_FromSsize_t(len_i); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(callable); + Py_DECREF(arg); + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + DISPATCH(); } - TARGET(GET_AITER) { + TARGET(CALL_LIST_APPEND) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(GET_AITER); - PyObject *obj; - PyObject *iter; - obj = stack_pointer[-1]; - unaryfunc getter = NULL; - PyTypeObject *type = Py_TYPE(obj); - - if (type->tp_as_async != NULL) { - getter = type->tp_as_async->am_aiter; + next_instr += 4; + INSTRUCTION_STATS(CALL_LIST_APPEND); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *self; + PyObject *callable; + args = &stack_pointer[-oparg]; + self = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + assert(oparg == 1); + PyInterpreterState *interp = tstate->interp; + DEOPT_IF(callable != interp->callable_cache.list_append, CALL); + assert(self != NULL); + DEOPT_IF(!PyList_Check(self), CALL); + STAT_INC(CALL, hit); + if (_PyList_AppendTakeRef((PyListObject *)self, args[0]) < 0) { + goto pop_1_error; // Since arg is DECREF'ed already } + Py_DECREF(self); + Py_DECREF(callable); + STACK_SHRINK(3); + // Skip POP_TOP + assert(next_instr->op.code == POP_TOP); + SKIP_OVER(1); + DISPATCH(); + } - if (getter == NULL) { - _PyErr_Format(tstate, PyExc_TypeError, - "'async for' requires an object with " - "__aiter__ method, got %.100s", - type->tp_name); - Py_DECREF(obj); - if (true) goto pop_1_error; + TARGET(CALL_METHOD_DESCRIPTOR_FAST) { + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_FAST); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + PyObject *res; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; } - - iter = (*getter)(obj); - Py_DECREF(obj); - if (iter == NULL) goto pop_1_error; - - if (Py_TYPE(iter)->tp_as_async == NULL || - Py_TYPE(iter)->tp_as_async->am_anext == NULL) { - - _PyErr_Format(tstate, PyExc_TypeError, - "'async for' received an object from __aiter__ " - "that does not implement __anext__: %.100s", - Py_TYPE(iter)->tp_name); - Py_DECREF(iter); - if (true) goto pop_1_error; + PyMethodDescrObject *method = (PyMethodDescrObject *)callable; + /* Builtin METH_FASTCALL methods, without keywords */ + DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); + PyMethodDef *meth = method->d_method; + DEOPT_IF(meth->ml_flags != METH_FASTCALL, CALL); + PyObject *self = args[0]; + DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); + STAT_INC(CALL, hit); + _PyCFunctionFast cfunc = + (_PyCFunctionFast)(void(*)(void))meth->ml_meth; + int nargs = total_args - 1; + res = cfunc(self, args + 1, nargs); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + /* Clear the stack of the arguments. */ + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); } - stack_pointer[-1] = iter; + Py_DECREF(callable); + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(GET_ANEXT) { + TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(GET_ANEXT); - PyObject *aiter; - PyObject *awaitable; - aiter = stack_pointer[-1]; - unaryfunc getter = NULL; - PyObject *next_iter = NULL; - PyTypeObject *type = Py_TYPE(aiter); - - if (PyAsyncGen_CheckExact(aiter)) { - awaitable = type->tp_as_async->am_anext(aiter); - if (awaitable == NULL) { - GOTO_ERROR(error); - } - } else { - if (type->tp_as_async != NULL){ - getter = type->tp_as_async->am_anext; - } - - if (getter != NULL) { - next_iter = (*getter)(aiter); - if (next_iter == NULL) { - GOTO_ERROR(error); - } - } - else { - _PyErr_Format(tstate, PyExc_TypeError, - "'async for' requires an iterator with " - "__anext__ method, got %.100s", - type->tp_name); - GOTO_ERROR(error); - } - - awaitable = _PyCoro_GetAwaitableIter(next_iter); - if (awaitable == NULL) { - _PyErr_FormatFromCause( - PyExc_TypeError, - "'async for' received an invalid object " - "from __anext__: %.100s", - Py_TYPE(next_iter)->tp_name); - - Py_DECREF(next_iter); - GOTO_ERROR(error); - } else { - Py_DECREF(next_iter); - } + next_instr += 4; + INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + PyObject *res; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; + } + PyMethodDescrObject *method = (PyMethodDescrObject *)callable; + DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); + PyMethodDef *meth = method->d_method; + DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), CALL); + PyTypeObject *d_type = method->d_common.d_type; + PyObject *self = args[0]; + DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL); + STAT_INC(CALL, hit); + int nargs = total_args - 1; + _PyCFunctionFastWithKeywords cfunc = + (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; + res = cfunc(self, args + 1, nargs, NULL); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + /* Free the arguments. */ + for (int i = 0; i < total_args; i++) { + Py_DECREF(args[i]); } - STACK_GROW(1); - stack_pointer[-1] = awaitable; + Py_DECREF(callable); + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(GET_AWAITABLE) { + TARGET(CALL_METHOD_DESCRIPTOR_NOARGS) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(GET_AWAITABLE); - PyObject *iterable; - PyObject *iter; - iterable = stack_pointer[-1]; - iter = _PyCoro_GetAwaitableIter(iterable); - - if (iter == NULL) { - _PyEval_FormatAwaitableError(tstate, Py_TYPE(iterable), oparg); + next_instr += 4; + INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_NOARGS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + PyObject *res; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + assert(oparg == 0 || oparg == 1); + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; } - - Py_DECREF(iterable); - - if (iter != NULL && PyCoro_CheckExact(iter)) { - PyObject *yf = _PyGen_yf((PyGenObject*)iter); - if (yf != NULL) { - /* `iter` is a coroutine object that is being - awaited, `yf` is a pointer to the current awaitable - being awaited on. */ - Py_DECREF(yf); - Py_CLEAR(iter); - _PyErr_SetString(tstate, PyExc_RuntimeError, - "coroutine is being awaited already"); - /* The code below jumps to `error` if `iter` is NULL. */ - } + DEOPT_IF(total_args != 1, CALL); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable; + DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); + PyMethodDef *meth = method->d_method; + PyObject *self = args[0]; + DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); + DEOPT_IF(meth->ml_flags != METH_NOARGS, CALL); + STAT_INC(CALL, hit); + PyCFunction cfunc = meth->ml_meth; + // This is slower but CPython promises to check all non-vectorcall + // function calls. + if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { + GOTO_ERROR(error); } - - if (iter == NULL) goto pop_1_error; - stack_pointer[-1] = iter; + res = _PyCFunction_TrampolineCall(cfunc, self, NULL); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(self); + Py_DECREF(callable); + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(SEND) { + TARGET(CALL_METHOD_DESCRIPTOR_O) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(SEND); - PREDICTED(SEND); - _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_SEND == 1, "incorrect cache size"); - PyObject *receiver; - PyObject *v; - PyObject *retval; - // _SPECIALIZE_SEND - receiver = stack_pointer[-2]; - { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - next_instr = this_instr; - _Py_Specialize_Send(receiver, next_instr); - DISPATCH_SAME_OPARG(); - } - STAT_INC(SEND, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ - } - // _SEND - v = stack_pointer[-1]; - { - assert(frame != &entry_frame); - if ((tstate->interp->eval_frame == NULL) && - (Py_TYPE(receiver) == &PyGen_Type || Py_TYPE(receiver) == &PyCoro_Type) && - ((PyGenObject *)receiver)->gi_frame_state < FRAME_EXECUTING) - { - PyGenObject *gen = (PyGenObject *)receiver; - _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; - STACK_SHRINK(1); - _PyFrame_StackPush(gen_frame, v); - gen->gi_frame_state = FRAME_EXECUTING; - gen->gi_exc_state.previous_item = tstate->exc_info; - tstate->exc_info = &gen->gi_exc_state; - assert(next_instr - this_instr + oparg <= UINT16_MAX); - frame->return_offset = (uint16_t)(next_instr - this_instr + oparg); - DISPATCH_INLINED(gen_frame); - } - if (Py_IsNone(v) && PyIter_Check(receiver)) { - retval = Py_TYPE(receiver)->tp_iternext(receiver); - } - else { - retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v); - } - if (retval == NULL) { - if (_PyErr_ExceptionMatches(tstate, PyExc_StopIteration) - ) { - monitor_raise(tstate, frame, this_instr); - } - if (_PyGen_FetchStopIterationValue(&retval) == 0) { - assert(retval != NULL); - JUMPBY(oparg); - } - else { - GOTO_ERROR(error); - } - } - Py_DECREF(v); - } - stack_pointer[-1] = retval; - DISPATCH(); - } - - TARGET(SEND_GEN) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(SEND_GEN); - PyObject *v; - PyObject *receiver; - v = stack_pointer[-1]; - receiver = stack_pointer[-2]; - DEOPT_IF(tstate->interp->eval_frame, SEND); - PyGenObject *gen = (PyGenObject *)receiver; - DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type, SEND); - DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, SEND); - STAT_INC(SEND, hit); - _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; - STACK_SHRINK(1); - _PyFrame_StackPush(gen_frame, v); - gen->gi_frame_state = FRAME_EXECUTING; - gen->gi_exc_state.previous_item = tstate->exc_info; - tstate->exc_info = &gen->gi_exc_state; - assert(next_instr - this_instr + oparg <= UINT16_MAX); - frame->return_offset = (uint16_t)(next_instr - this_instr + oparg); - DISPATCH_INLINED(gen_frame); - } - - TARGET(INSTRUMENTED_YIELD_VALUE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_YIELD_VALUE); - PyObject *retval; - retval = stack_pointer[-1]; - assert(frame != &entry_frame); - frame->instr_ptr = next_instr; - PyGenObject *gen = _PyFrame_GetGenerator(frame); - assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); - assert(oparg == 0 || oparg == 1); - gen->gi_frame_state = FRAME_SUSPENDED + oparg; - _PyFrame_SetStackPointer(frame, stack_pointer - 1); - int err = _Py_call_instrumentation_arg( - tstate, PY_MONITORING_EVENT_PY_YIELD, - frame, this_instr, retval); - if (err) GOTO_ERROR(error); - tstate->exc_info = gen->gi_exc_state.previous_item; - gen->gi_exc_state.previous_item = NULL; - _Py_LeaveRecursiveCallPy(tstate); - _PyInterpreterFrame *gen_frame = frame; - frame = tstate->current_frame = frame->previous; - gen_frame->previous = NULL; - _PyFrame_StackPush(frame, retval); - /* We don't know which of these is relevant here, so keep them equal */ - assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); - LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); - goto resume_frame; - } - - TARGET(YIELD_VALUE) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(YIELD_VALUE); - PyObject *retval; - retval = stack_pointer[-1]; - // NOTE: It's important that YIELD_VALUE never raises an exception! - // The compiler treats any exception raised here as a failed close() - // or throw() call. - assert(frame != &entry_frame); - frame->instr_ptr = next_instr; - PyGenObject *gen = _PyFrame_GetGenerator(frame); - assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); - assert(oparg == 0 || oparg == 1); - gen->gi_frame_state = FRAME_SUSPENDED + oparg; - _PyFrame_SetStackPointer(frame, stack_pointer - 1); - tstate->exc_info = gen->gi_exc_state.previous_item; - gen->gi_exc_state.previous_item = NULL; - _Py_LeaveRecursiveCallPy(tstate); - _PyInterpreterFrame *gen_frame = frame; - frame = tstate->current_frame = frame->previous; - gen_frame->previous = NULL; - _PyFrame_StackPush(frame, retval); - /* We don't know which of these is relevant here, so keep them equal */ - assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); - LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); - goto resume_frame; - } - - TARGET(POP_EXCEPT) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(POP_EXCEPT); - PyObject *exc_value; - exc_value = stack_pointer[-1]; - _PyErr_StackItem *exc_info = tstate->exc_info; - Py_XSETREF(exc_info->exc_value, exc_value); - STACK_SHRINK(1); - DISPATCH(); - } - - TARGET(RERAISE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(RERAISE); - PyObject *exc; - PyObject **values; - exc = stack_pointer[-1]; - values = stack_pointer - 1 - oparg; - TIER_ONE_ONLY - assert(oparg >= 0 && oparg <= 2); - if (oparg) { - PyObject *lasti = values[0]; - if (PyLong_Check(lasti)) { - frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame)) + PyLong_AsLong(lasti); - assert(!_PyErr_Occurred(tstate)); - } - else { - assert(PyLong_Check(lasti)); - _PyErr_SetString(tstate, PyExc_SystemError, "lasti is not an int"); - GOTO_ERROR(error); - } - } - assert(exc && PyExceptionInstance_Check(exc)); - Py_INCREF(exc); - _PyErr_SetRaisedException(tstate, exc); - monitor_reraise(tstate, frame, this_instr); - goto exception_unwind; - } - - TARGET(END_ASYNC_FOR) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(END_ASYNC_FOR); - PyObject *exc; - PyObject *awaitable; - exc = stack_pointer[-1]; - awaitable = stack_pointer[-2]; - TIER_ONE_ONLY - assert(exc && PyExceptionInstance_Check(exc)); - if (PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration)) { - Py_DECREF(awaitable); - Py_DECREF(exc); + next_instr += 4; + INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_O); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + PyObject *res; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int total_args = oparg; + if (self_or_null != NULL) { + args--; + total_args++; } - else { - Py_INCREF(exc); - _PyErr_SetRaisedException(tstate, exc); - monitor_reraise(tstate, frame, this_instr); - goto exception_unwind; + PyMethodDescrObject *method = (PyMethodDescrObject *)callable; + DEOPT_IF(total_args != 2, CALL); + DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); + PyMethodDef *meth = method->d_method; + DEOPT_IF(meth->ml_flags != METH_O, CALL); + PyObject *arg = args[1]; + PyObject *self = args[0]; + DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); + STAT_INC(CALL, hit); + PyCFunction cfunc = meth->ml_meth; + // This is slower but CPython promises to check all non-vectorcall + // function calls. + if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { + GOTO_ERROR(error); } - STACK_SHRINK(2); + res = _PyCFunction_TrampolineCall(cfunc, self, arg); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(self); + Py_DECREF(arg); + Py_DECREF(callable); + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(CLEANUP_THROW) { + TARGET(CALL_PY_EXACT_ARGS) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(CLEANUP_THROW); - PyObject *exc_value; - PyObject *last_sent_val; - PyObject *sub_iter; - PyObject *none; - PyObject *value; - exc_value = stack_pointer[-1]; - last_sent_val = stack_pointer[-2]; - sub_iter = stack_pointer[-3]; - TIER_ONE_ONLY - assert(throwflag); - assert(exc_value && PyExceptionInstance_Check(exc_value)); - if (PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration)) { - value = Py_NewRef(((PyStopIterationObject *)exc_value)->value); - Py_DECREF(sub_iter); - Py_DECREF(last_sent_val); - Py_DECREF(exc_value); - none = Py_None; - } - else { - _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value)); - monitor_reraise(tstate, frame, this_instr); - goto exception_unwind; - } - STACK_SHRINK(1); - stack_pointer[-2] = none; - stack_pointer[-1] = value; - DISPATCH(); - } - - TARGET(LOAD_ASSERTION_ERROR) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(LOAD_ASSERTION_ERROR); - PyObject *value; - value = Py_NewRef(PyExc_AssertionError); - STACK_GROW(1); - stack_pointer[-1] = value; - DISPATCH(); - } - - TARGET(LOAD_BUILD_CLASS) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(LOAD_BUILD_CLASS); - PyObject *bc; - if (PyMapping_GetOptionalItem(BUILTINS(), &_Py_ID(__build_class__), &bc) < 0) goto error; - if (bc == NULL) { - _PyErr_SetString(tstate, PyExc_NameError, - "__build_class__ not found"); - if (true) goto error; - } - STACK_GROW(1); - stack_pointer[-1] = bc; - DISPATCH(); - } - - TARGET(STORE_NAME) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(STORE_NAME); - PyObject *v; - v = stack_pointer[-1]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - PyObject *ns = LOCALS(); - int err; - if (ns == NULL) { - _PyErr_Format(tstate, PyExc_SystemError, - "no locals found when storing %R", name); - Py_DECREF(v); - if (true) goto pop_1_error; + next_instr += 4; + INSTRUCTION_STATS(CALL_PY_EXACT_ARGS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject *self_or_null; + PyObject *callable; + PyObject **args; + _PyInterpreterFrame *new_frame; + /* Skip 1 cache entry */ + // _CHECK_PEP_523 + { + DEOPT_IF(tstate->interp->eval_frame, CALL); } - if (PyDict_CheckExact(ns)) - err = PyDict_SetItem(ns, name, v); - else - err = PyObject_SetItem(ns, name, v); - Py_DECREF(v); - if (err) goto pop_1_error; - STACK_SHRINK(1); - DISPATCH(); - } - - TARGET(DELETE_NAME) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(DELETE_NAME); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - PyObject *ns = LOCALS(); - int err; - if (ns == NULL) { - _PyErr_Format(tstate, PyExc_SystemError, - "no locals when deleting %R", name); - GOTO_ERROR(error); + // _CHECK_FUNCTION_EXACT_ARGS + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + { + uint32_t func_version = read_u32(&this_instr[2].cache); + DEOPT_IF(!PyFunction_Check(callable), CALL); + PyFunctionObject *func = (PyFunctionObject *)callable; + DEOPT_IF(func->func_version != func_version, CALL); + PyCodeObject *code = (PyCodeObject *)func->func_code; + DEOPT_IF(code->co_argcount != oparg + (self_or_null != NULL), CALL); } - err = PyObject_DelItem(ns, name); - // Can't use ERROR_IF here. - if (err != 0) { - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, - name); - GOTO_ERROR(error); + // _CHECK_STACK_SPACE + { + PyFunctionObject *func = (PyFunctionObject *)callable; + PyCodeObject *code = (PyCodeObject *)func->func_code; + DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); + DEOPT_IF(tstate->py_recursion_remaining <= 1, CALL); } - DISPATCH(); - } - - TARGET(UNPACK_SEQUENCE) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(UNPACK_SEQUENCE); - PREDICTED(UNPACK_SEQUENCE); - _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); - PyObject *seq; - // _SPECIALIZE_UNPACK_SEQUENCE - seq = stack_pointer[-1]; + // _INIT_CALL_PY_EXACT_ARGS + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - next_instr = this_instr; - _Py_Specialize_UnpackSequence(seq, next_instr, oparg); - DISPATCH_SAME_OPARG(); + int argcount = oparg; + if (self_or_null != NULL) { + args--; + argcount++; + } + STAT_INC(CALL, hit); + PyFunctionObject *func = (PyFunctionObject *)callable; + new_frame = _PyFrame_PushUnchecked(tstate, func, argcount); + for (int i = 0; i < argcount; i++) { + new_frame->localsplus[i] = args[i]; } - STAT_INC(UNPACK_SEQUENCE, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ - (void)seq; - (void)counter; } - // _UNPACK_SEQUENCE + // _SAVE_RETURN_OFFSET { - PyObject **top = stack_pointer + oparg - 1; - int res = _PyEval_UnpackIterable(tstate, seq, oparg, -1, top); - Py_DECREF(seq); - if (res == 0) goto pop_1_error; - } - STACK_SHRINK(1); - STACK_GROW(oparg); - DISPATCH(); - } - - TARGET(UNPACK_SEQUENCE_TWO_TUPLE) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(UNPACK_SEQUENCE_TWO_TUPLE); - PyObject *seq; - PyObject **values; - seq = stack_pointer[-1]; - values = stack_pointer - 1; - DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); - DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE); - assert(oparg == 2); - STAT_INC(UNPACK_SEQUENCE, hit); - values[0] = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); - values[1] = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); - Py_DECREF(seq); - STACK_SHRINK(1); - STACK_GROW(oparg); - DISPATCH(); - } - - TARGET(UNPACK_SEQUENCE_TUPLE) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(UNPACK_SEQUENCE_TUPLE); - PyObject *seq; - PyObject **values; - seq = stack_pointer[-1]; - values = stack_pointer - 1; - DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); - DEOPT_IF(PyTuple_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); - STAT_INC(UNPACK_SEQUENCE, hit); - PyObject **items = _PyTuple_ITEMS(seq); - for (int i = oparg; --i >= 0; ) { - *values++ = Py_NewRef(items[i]); + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif } - Py_DECREF(seq); - STACK_SHRINK(1); - STACK_GROW(oparg); - DISPATCH(); - } - - TARGET(UNPACK_SEQUENCE_LIST) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(UNPACK_SEQUENCE_LIST); - PyObject *seq; - PyObject **values; - seq = stack_pointer[-1]; - values = stack_pointer - 1; - DEOPT_IF(!PyList_CheckExact(seq), UNPACK_SEQUENCE); - DEOPT_IF(PyList_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); - STAT_INC(UNPACK_SEQUENCE, hit); - PyObject **items = _PyList_ITEMS(seq); - for (int i = oparg; --i >= 0; ) { - *values++ = Py_NewRef(items[i]); + // _PUSH_FRAME + { + // Write it out explicitly because it's subtly different. + // Eventually this should be the only occurrence of this code. + assert(tstate->interp->eval_frame == NULL); + stack_pointer += -2 - oparg; + _PyFrame_SetStackPointer(frame, stack_pointer); + new_frame->previous = frame; + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = new_frame; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + #if LLTRACE && TIER_ONE + lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); + if (lltrace < 0) { + goto exit_unwind; + } + #endif } - Py_DECREF(seq); - STACK_SHRINK(1); - STACK_GROW(oparg); - DISPATCH(); - } - - TARGET(UNPACK_EX) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(UNPACK_EX); - PyObject *seq; - seq = stack_pointer[-1]; - int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8); - PyObject **top = stack_pointer + totalargs - 1; - int res = _PyEval_UnpackIterable(tstate, seq, oparg & 0xFF, oparg >> 8, top); - Py_DECREF(seq); - if (res == 0) goto pop_1_error; - STACK_GROW((oparg & 0xFF) + (oparg >> 8)); + stack_pointer += (((0) ? 1 : 0)); DISPATCH(); } - TARGET(STORE_ATTR) { - frame->instr_ptr = next_instr; - next_instr += 5; - INSTRUCTION_STATS(STORE_ATTR); - PREDICTED(STORE_ATTR); - _Py_CODEUNIT *this_instr = next_instr - 5; - static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); - PyObject *owner; - PyObject *v; - // _SPECIALIZE_STORE_ATTR - owner = stack_pointer[-1]; - { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - next_instr = this_instr; - _Py_Specialize_StoreAttr(owner, next_instr, name); - DISPATCH_SAME_OPARG(); - } - STAT_INC(STORE_ATTR, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ + TARGET(CALL_PY_WITH_DEFAULTS) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_PY_WITH_DEFAULTS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *self_or_null; + PyObject *callable; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + uint32_t func_version = read_u32(&this_instr[2].cache); + DEOPT_IF(tstate->interp->eval_frame, CALL); + int argcount = oparg; + if (self_or_null != NULL) { + args--; + argcount++; } - // _STORE_ATTR - v = stack_pointer[-2]; - { - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyObject_SetAttr(owner, name, v); - Py_DECREF(v); - Py_DECREF(owner); - if (err) goto pop_2_error; + DEOPT_IF(!PyFunction_Check(callable), CALL); + PyFunctionObject *func = (PyFunctionObject *)callable; + DEOPT_IF(func->func_version != func_version, CALL); + PyCodeObject *code = (PyCodeObject *)func->func_code; + assert(func->func_defaults); + assert(PyTuple_CheckExact(func->func_defaults)); + int defcount = (int)PyTuple_GET_SIZE(func->func_defaults); + assert(defcount <= code->co_argcount); + int min_args = code->co_argcount - defcount; + DEOPT_IF(argcount > code->co_argcount, CALL); + DEOPT_IF(argcount < min_args, CALL); + DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); + STAT_INC(CALL, hit); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, code->co_argcount); + for (int i = 0; i < argcount; i++) { + new_frame->localsplus[i] = args[i]; } - STACK_SHRINK(2); - DISPATCH(); + for (int i = argcount; i < code->co_argcount; i++) { + PyObject *def = PyTuple_GET_ITEM(func->func_defaults, i - min_args); + new_frame->localsplus[i] = Py_NewRef(def); + } + // Manipulate stack and cache directly since we leave using DISPATCH_INLINED(). + STACK_SHRINK(oparg + 2); + frame->return_offset = (uint16_t)(next_instr - this_instr); + DISPATCH_INLINED(new_frame); } - TARGET(DELETE_ATTR) { + TARGET(CALL_STR_1) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(DELETE_ATTR); - PyObject *owner; - owner = stack_pointer[-1]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyObject_DelAttr(owner, name); - Py_DECREF(owner); - if (err) goto pop_1_error; - STACK_SHRINK(1); + next_instr += 4; + INSTRUCTION_STATS(CALL_STR_1); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *null; + PyObject *callable; + PyObject *res; + args = &stack_pointer[-oparg]; + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + assert(oparg == 1); + DEOPT_IF(null != NULL, CALL); + DEOPT_IF(callable != (PyObject *)&PyUnicode_Type, CALL); + STAT_INC(CALL, hit); + PyObject *arg = args[0]; + res = PyObject_Str(arg); + Py_DECREF(arg); + Py_DECREF(&PyUnicode_Type); // I.e., callable + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(STORE_GLOBAL) { + TARGET(CALL_TUPLE_1) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(STORE_GLOBAL); - PyObject *v; - v = stack_pointer[-1]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyDict_SetItem(GLOBALS(), name, v); - Py_DECREF(v); - if (err) goto pop_1_error; - STACK_SHRINK(1); + next_instr += 4; + INSTRUCTION_STATS(CALL_TUPLE_1); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *null; + PyObject *callable; + PyObject *res; + args = &stack_pointer[-oparg]; + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + assert(oparg == 1); + DEOPT_IF(null != NULL, CALL); + DEOPT_IF(callable != (PyObject *)&PyTuple_Type, CALL); + STAT_INC(CALL, hit); + PyObject *arg = args[0]; + res = PySequence_Tuple(arg); + Py_DECREF(arg); + Py_DECREF(&PyTuple_Type); // I.e., tuple + if (res == NULL) { stack_pointer += -2 - oparg; goto error; } + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + CHECK_EVAL_BREAKER(); DISPATCH(); } - TARGET(DELETE_GLOBAL) { + TARGET(CALL_TYPE_1) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(DELETE_GLOBAL); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err; - err = PyDict_DelItem(GLOBALS(), name); - // Can't use ERROR_IF here. - if (err != 0) { - if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - } - GOTO_ERROR(error); - } + next_instr += 4; + INSTRUCTION_STATS(CALL_TYPE_1); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + PyObject **args; + PyObject *null; + PyObject *callable; + PyObject *res; + args = &stack_pointer[-oparg]; + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + assert(oparg == 1); + DEOPT_IF(null != NULL, CALL); + PyObject *obj = args[0]; + DEOPT_IF(callable != (PyObject *)&PyType_Type, CALL); + STAT_INC(CALL, hit); + res = Py_NewRef(Py_TYPE(obj)); + Py_DECREF(obj); + Py_DECREF(&PyType_Type); // I.e., callable + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; DISPATCH(); } - TARGET(LOAD_LOCALS) { + TARGET(CHECK_EG_MATCH) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(LOAD_LOCALS); - PyObject *locals; - locals = LOCALS(); - if (locals == NULL) { - _PyErr_SetString(tstate, PyExc_SystemError, - "no locals found"); - if (true) goto error; + INSTRUCTION_STATS(CHECK_EG_MATCH); + PyObject *match_type; + PyObject *exc_value; + PyObject *rest; + PyObject *match; + match_type = stack_pointer[-1]; + exc_value = stack_pointer[-2]; + if (_PyEval_CheckExceptStarTypeValid(tstate, match_type) < 0) { + Py_DECREF(exc_value); + Py_DECREF(match_type); + if (true) goto pop_2_error; } - Py_INCREF(locals); - STACK_GROW(1); - stack_pointer[-1] = locals; + match = NULL; + rest = NULL; + int res = _PyEval_ExceptionGroupMatch(exc_value, match_type, + &match, &rest); + Py_DECREF(exc_value); + Py_DECREF(match_type); + if (res < 0) goto pop_2_error; + assert((match == NULL) == (rest == NULL)); + if (match == NULL) goto pop_2_error; + if (!Py_IsNone(match)) { + PyErr_SetHandledException(match); + } + stack_pointer[-2] = rest; + stack_pointer[-1] = match; DISPATCH(); } - TARGET(LOAD_FROM_DICT_OR_GLOBALS) { + TARGET(CHECK_EXC_MATCH) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(LOAD_FROM_DICT_OR_GLOBALS); - PyObject *mod_or_class_dict; - PyObject *v; - mod_or_class_dict = stack_pointer[-1]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) { - GOTO_ERROR(error); - } - if (v == NULL) { - if (PyDict_GetItemRef(GLOBALS(), name, &v) < 0) { - GOTO_ERROR(error); - } - if (v == NULL) { - if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) { - GOTO_ERROR(error); - } - if (v == NULL) { - _PyEval_FormatExcCheckArg( - tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - GOTO_ERROR(error); - } - } + INSTRUCTION_STATS(CHECK_EXC_MATCH); + PyObject *right; + PyObject *left; + PyObject *b; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + assert(PyExceptionInstance_Check(left)); + if (_PyEval_CheckExceptTypeValid(tstate, right) < 0) { + Py_DECREF(right); + if (true) goto pop_1_error; } - Py_DECREF(mod_or_class_dict); - stack_pointer[-1] = v; + int res = PyErr_GivenExceptionMatches(left, right); + Py_DECREF(right); + b = res ? Py_True : Py_False; + stack_pointer[-1] = b; DISPATCH(); } - TARGET(LOAD_NAME) { - frame->instr_ptr = next_instr; + TARGET(CLEANUP_THROW) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(LOAD_NAME); - PyObject *v; - PyObject *mod_or_class_dict = LOCALS(); - if (mod_or_class_dict == NULL) { - _PyErr_SetString(tstate, PyExc_SystemError, - "no locals found"); - if (true) goto error; - } - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) { - GOTO_ERROR(error); + INSTRUCTION_STATS(CLEANUP_THROW); + PyObject *exc_value; + PyObject *last_sent_val; + PyObject *sub_iter; + PyObject *none; + PyObject *value; + exc_value = stack_pointer[-1]; + last_sent_val = stack_pointer[-2]; + sub_iter = stack_pointer[-3]; + TIER_ONE_ONLY + assert(throwflag); + assert(exc_value && PyExceptionInstance_Check(exc_value)); + if (PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration)) { + value = Py_NewRef(((PyStopIterationObject *)exc_value)->value); + Py_DECREF(sub_iter); + Py_DECREF(last_sent_val); + Py_DECREF(exc_value); + none = Py_None; } - if (v == NULL) { - if (PyDict_GetItemRef(GLOBALS(), name, &v) < 0) { - GOTO_ERROR(error); - } - if (v == NULL) { - if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) { - GOTO_ERROR(error); - } - if (v == NULL) { - _PyEval_FormatExcCheckArg( - tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - GOTO_ERROR(error); - } - } + else { + _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value)); + monitor_reraise(tstate, frame, this_instr); + goto exception_unwind; } - STACK_GROW(1); - stack_pointer[-1] = v; + stack_pointer[-3] = none; + stack_pointer[-2] = value; + stack_pointer += -1; DISPATCH(); } - TARGET(LOAD_GLOBAL) { + TARGET(COMPARE_OP) { frame->instr_ptr = next_instr; - next_instr += 5; - INSTRUCTION_STATS(LOAD_GLOBAL); - PREDICTED(LOAD_GLOBAL); - _Py_CODEUNIT *this_instr = next_instr - 5; - static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size"); + next_instr += 2; + INSTRUCTION_STATS(COMPARE_OP); + PREDICTED(COMPARE_OP); + _Py_CODEUNIT *this_instr = next_instr - 2; + PyObject *right; + PyObject *left; PyObject *res; - PyObject *null = NULL; - // _SPECIALIZE_LOAD_GLOBAL + // _SPECIALIZE_COMPARE_OP + right = stack_pointer[-1]; + left = stack_pointer[-2]; { uint16_t counter = read_u16(&this_instr[1].cache); TIER_ONE_ONLY #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); next_instr = this_instr; - _Py_Specialize_LoadGlobal(GLOBALS(), BUILTINS(), next_instr, name); + _Py_Specialize_CompareOp(left, right, next_instr, oparg); DISPATCH_SAME_OPARG(); } - STAT_INC(LOAD_GLOBAL, deferred); + STAT_INC(COMPARE_OP, deferred); DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); #endif /* ENABLE_SPECIALIZATION */ } - // _LOAD_GLOBAL + // _COMPARE_OP { - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); - if (PyDict_CheckExact(GLOBALS()) - && PyDict_CheckExact(BUILTINS())) - { - res = _PyDict_LoadGlobal((PyDictObject *)GLOBALS(), - (PyDictObject *)BUILTINS(), - name); - if (res == NULL) { - if (!_PyErr_Occurred(tstate)) { - /* _PyDict_LoadGlobal() returns NULL without raising - * an exception if the key doesn't exist */ - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - } - if (true) goto error; - } - Py_INCREF(res); - } - else { - /* Slow-path if globals or builtins is not a dict */ - /* namespace 1: globals */ - if (PyMapping_GetOptionalItem(GLOBALS(), name, &res) < 0) goto error; - if (res == NULL) { - /* namespace 2: builtins */ - if (PyMapping_GetOptionalItem(BUILTINS(), name, &res) < 0) goto error; - if (res == NULL) { - _PyEval_FormatExcCheckArg( - tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - if (true) goto error; - } - } + assert((oparg >> 5) <= Py_GE); + res = PyObject_RichCompare(left, right, oparg >> 5); + Py_DECREF(left); + Py_DECREF(right); + if (res == NULL) goto pop_2_error; + if (oparg & 16) { + int res_bool = PyObject_IsTrue(res); + Py_DECREF(res); + if (res_bool < 0) goto pop_2_error; + res = res_bool ? Py_True : Py_False; } - null = NULL; } - STACK_GROW(1); - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = res; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(LOAD_GLOBAL_MODULE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 5; - INSTRUCTION_STATS(LOAD_GLOBAL_MODULE); + TARGET(COMPARE_OP_FLOAT) { + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(COMPARE_OP_FLOAT); + static_assert(INLINE_CACHE_ENTRIES_COMPARE_OP == 1, "incorrect cache size"); + PyObject *right; + PyObject *left; PyObject *res; - PyObject *null = NULL; - // _GUARD_GLOBALS_VERSION - { - uint16_t version = read_u16(&this_instr[2].cache); - PyDictObject *dict = (PyDictObject *)GLOBALS(); - DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL); - DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); - assert(DK_IS_UNICODE(dict->ma_keys)); - } - // _LOAD_GLOBAL_MODULE - { - uint16_t index = read_u16(&this_instr[4].cache); - PyDictObject *dict = (PyDictObject *)GLOBALS(); - PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys); - res = entries[index].me_value; - DEOPT_IF(res == NULL, LOAD_GLOBAL); - Py_INCREF(res); - STAT_INC(LOAD_GLOBAL, hit); - null = NULL; - } - STACK_GROW(1); - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = res; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + right = stack_pointer[-1]; + left = stack_pointer[-2]; + DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_OP); + DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_OP); + STAT_INC(COMPARE_OP, hit); + double dleft = PyFloat_AS_DOUBLE(left); + double dright = PyFloat_AS_DOUBLE(right); + // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg + int sign_ish = COMPARISON_BIT(dleft, dright); + _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); + _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); + res = (sign_ish & oparg) ? Py_True : Py_False; + // It's always a bool, so we don't care about oparg & 16. + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(LOAD_GLOBAL_BUILTIN) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 5; - INSTRUCTION_STATS(LOAD_GLOBAL_BUILTIN); + TARGET(COMPARE_OP_INT) { + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(COMPARE_OP_INT); + static_assert(INLINE_CACHE_ENTRIES_COMPARE_OP == 1, "incorrect cache size"); + PyObject *right; + PyObject *left; PyObject *res; - PyObject *null = NULL; - // _GUARD_GLOBALS_VERSION - { - uint16_t version = read_u16(&this_instr[2].cache); - PyDictObject *dict = (PyDictObject *)GLOBALS(); - DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL); - DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); - assert(DK_IS_UNICODE(dict->ma_keys)); - } - // _GUARD_BUILTINS_VERSION - { - uint16_t version = read_u16(&this_instr[3].cache); - PyDictObject *dict = (PyDictObject *)BUILTINS(); - DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL); - DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); - assert(DK_IS_UNICODE(dict->ma_keys)); - } - // _LOAD_GLOBAL_BUILTINS - { - uint16_t index = read_u16(&this_instr[4].cache); - PyDictObject *bdict = (PyDictObject *)BUILTINS(); - PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(bdict->ma_keys); - res = entries[index].me_value; - DEOPT_IF(res == NULL, LOAD_GLOBAL); - Py_INCREF(res); - STAT_INC(LOAD_GLOBAL, hit); - null = NULL; - } - STACK_GROW(1); - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = res; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + right = stack_pointer[-1]; + left = stack_pointer[-2]; + DEOPT_IF(!PyLong_CheckExact(left), COMPARE_OP); + DEOPT_IF(!PyLong_CheckExact(right), COMPARE_OP); + DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)left), COMPARE_OP); + DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)right), COMPARE_OP); + STAT_INC(COMPARE_OP, hit); + assert(_PyLong_DigitCount((PyLongObject *)left) <= 1 && + _PyLong_DigitCount((PyLongObject *)right) <= 1); + Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left); + Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right); + // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg + int sign_ish = COMPARISON_BIT(ileft, iright); + _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); + _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); + res = (sign_ish & oparg) ? Py_True : Py_False; + // It's always a bool, so we don't care about oparg & 16. + stack_pointer[-2] = res; + stack_pointer += -1; + DISPATCH(); + } + + TARGET(COMPARE_OP_STR) { + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(COMPARE_OP_STR); + static_assert(INLINE_CACHE_ENTRIES_COMPARE_OP == 1, "incorrect cache size"); + PyObject *right; + PyObject *left; + PyObject *res; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_OP); + DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_OP); + STAT_INC(COMPARE_OP, hit); + int eq = _PyUnicode_Equal(left, right); + assert((oparg >> 5) == Py_EQ || (oparg >> 5) == Py_NE); + _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); + _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); + assert(eq == 0 || eq == 1); + assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); + assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); + res = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? Py_True : Py_False; + // It's always a bool, so we don't care about oparg & 16. + stack_pointer[-2] = res; + stack_pointer += -1; + DISPATCH(); + } + + TARGET(CONTAINS_OP) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(CONTAINS_OP); + PyObject *right; + PyObject *left; + PyObject *b; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + int res = PySequence_Contains(right, left); + Py_DECREF(left); + Py_DECREF(right); + if (res < 0) goto pop_2_error; + b = (res ^ oparg) ? Py_True : Py_False; + stack_pointer[-2] = b; + stack_pointer += -1; DISPATCH(); } - TARGET(DELETE_FAST) { + TARGET(CONVERT_VALUE) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(DELETE_FAST); - PyObject *v = GETLOCAL(oparg); - if (v == NULL) goto unbound_local_error; - SETLOCAL(oparg, NULL); + INSTRUCTION_STATS(CONVERT_VALUE); + PyObject *value; + PyObject *result; + value = stack_pointer[-1]; + convertion_func_ptr conv_fn; + assert(oparg >= FVC_STR && oparg <= FVC_ASCII); + conv_fn = CONVERSION_FUNCTIONS[oparg]; + result = conv_fn(value); + Py_DECREF(value); + if (result == NULL) goto pop_1_error; + stack_pointer[-1] = result; DISPATCH(); } - TARGET(MAKE_CELL) { + TARGET(COPY) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(MAKE_CELL); - // "initial" is probably NULL but not if it's an arg (or set - // via PyFrame_LocalsToFast() before MAKE_CELL has run). - PyObject *initial = GETLOCAL(oparg); - PyObject *cell = PyCell_New(initial); - if (cell == NULL) { - GOTO_ERROR(error); + INSTRUCTION_STATS(COPY); + PyObject *bottom; + PyObject *top; + bottom = stack_pointer[-1 - (oparg-1)]; + assert(oparg > 0); + top = Py_NewRef(bottom); + stack_pointer[0] = top; + stack_pointer += 1; + DISPATCH(); + } + + TARGET(COPY_FREE_VARS) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(COPY_FREE_VARS); + /* Copy closure variables to free variables */ + PyCodeObject *co = _PyFrame_GetCode(frame); + assert(PyFunction_Check(frame->f_funcobj)); + PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure; + assert(oparg == co->co_nfreevars); + int offset = co->co_nlocalsplus - oparg; + for (int i = 0; i < oparg; ++i) { + PyObject *o = PyTuple_GET_ITEM(closure, i); + frame->localsplus[offset + i] = Py_NewRef(o); } - SETLOCAL(oparg, cell); + DISPATCH(); + } + + TARGET(DELETE_ATTR) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(DELETE_ATTR); + PyObject *owner; + owner = stack_pointer[-1]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + int err = PyObject_DelAttr(owner, name); + Py_DECREF(owner); + if (err) goto pop_1_error; + stack_pointer += -1; DISPATCH(); } @@ -2184,3580 +2152,3637 @@ DISPATCH(); } - TARGET(LOAD_FROM_DICT_OR_DEREF) { + TARGET(DELETE_FAST) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(LOAD_FROM_DICT_OR_DEREF); - PyObject *class_dict; - PyObject *value; - class_dict = stack_pointer[-1]; - PyObject *name; - assert(class_dict); - assert(oparg >= 0 && oparg < _PyFrame_GetCode(frame)->co_nlocalsplus); - name = PyTuple_GET_ITEM(_PyFrame_GetCode(frame)->co_localsplusnames, oparg); - if (PyMapping_GetOptionalItem(class_dict, name, &value) < 0) { + INSTRUCTION_STATS(DELETE_FAST); + PyObject *v = GETLOCAL(oparg); + if (v == NULL) goto unbound_local_error; + SETLOCAL(oparg, NULL); + DISPATCH(); + } + + TARGET(DELETE_GLOBAL) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(DELETE_GLOBAL); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + int err; + err = PyDict_DelItem(GLOBALS(), name); + // Can't use ERROR_IF here. + if (err != 0) { + if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + } GOTO_ERROR(error); } - if (!value) { - PyObject *cell = GETLOCAL(oparg); - value = PyCell_GET(cell); - if (value == NULL) { - _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); - GOTO_ERROR(error); - } - Py_INCREF(value); + DISPATCH(); + } + + TARGET(DELETE_NAME) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(DELETE_NAME); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + PyObject *ns = LOCALS(); + int err; + if (ns == NULL) { + _PyErr_Format(tstate, PyExc_SystemError, + "no locals when deleting %R", name); + GOTO_ERROR(error); + } + err = PyObject_DelItem(ns, name); + // Can't use ERROR_IF here. + if (err != 0) { + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, + name); + GOTO_ERROR(error); } - Py_DECREF(class_dict); - stack_pointer[-1] = value; DISPATCH(); } - TARGET(LOAD_DEREF) { + TARGET(DELETE_SUBSCR) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(LOAD_DEREF); - PyObject *value; - PyObject *cell = GETLOCAL(oparg); - value = PyCell_GET(cell); - if (value == NULL) { - _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); - if (true) goto error; + INSTRUCTION_STATS(DELETE_SUBSCR); + PyObject *sub; + PyObject *container; + sub = stack_pointer[-1]; + container = stack_pointer[-2]; + /* del container[sub] */ + int err = PyObject_DelItem(container, sub); + Py_DECREF(container); + Py_DECREF(sub); + if (err) goto pop_2_error; + stack_pointer += -2; + DISPATCH(); + } + + TARGET(DICT_MERGE) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(DICT_MERGE); + PyObject *update; + PyObject *dict; + PyObject *callable; + update = stack_pointer[-1]; + dict = stack_pointer[-2 - (oparg - 1)]; + callable = stack_pointer[-5 - (oparg - 1)]; + if (_PyDict_MergeEx(dict, update, 2) < 0) { + _PyEval_FormatKwargsError(tstate, callable, update); + Py_DECREF(update); + if (true) goto pop_1_error; } - Py_INCREF(value); - STACK_GROW(1); - stack_pointer[-1] = value; + Py_DECREF(update); + stack_pointer += -1; DISPATCH(); } - TARGET(STORE_DEREF) { + TARGET(DICT_UPDATE) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(STORE_DEREF); - PyObject *v; - v = stack_pointer[-1]; - PyObject *cell = GETLOCAL(oparg); - PyObject *oldobj = PyCell_GET(cell); - PyCell_SET(cell, v); - Py_XDECREF(oldobj); - STACK_SHRINK(1); + INSTRUCTION_STATS(DICT_UPDATE); + PyObject *update; + PyObject *dict; + update = stack_pointer[-1]; + dict = stack_pointer[-2 - (oparg - 1)]; + if (PyDict_Update(dict, update) < 0) { + if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) { + _PyErr_Format(tstate, PyExc_TypeError, + "'%.200s' object is not a mapping", + Py_TYPE(update)->tp_name); + } + Py_DECREF(update); + if (true) goto pop_1_error; + } + Py_DECREF(update); + stack_pointer += -1; DISPATCH(); } - TARGET(COPY_FREE_VARS) { + TARGET(END_ASYNC_FOR) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(END_ASYNC_FOR); + PyObject *exc; + PyObject *awaitable; + exc = stack_pointer[-1]; + awaitable = stack_pointer[-2]; + TIER_ONE_ONLY + assert(exc && PyExceptionInstance_Check(exc)); + if (PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration)) { + Py_DECREF(awaitable); + Py_DECREF(exc); + } + else { + Py_INCREF(exc); + _PyErr_SetRaisedException(tstate, exc); + monitor_reraise(tstate, frame, this_instr); + goto exception_unwind; + } + stack_pointer += -2; + DISPATCH(); + } + + TARGET(END_FOR) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(COPY_FREE_VARS); - /* Copy closure variables to free variables */ - PyCodeObject *co = _PyFrame_GetCode(frame); - assert(PyFunction_Check(frame->f_funcobj)); - PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure; - assert(oparg == co->co_nfreevars); - int offset = co->co_nlocalsplus - oparg; - for (int i = 0; i < oparg; ++i) { - PyObject *o = PyTuple_GET_ITEM(closure, i); - frame->localsplus[offset + i] = Py_NewRef(o); + INSTRUCTION_STATS(END_FOR); + PyObject *value; + // _POP_TOP + value = stack_pointer[-1]; + { + Py_DECREF(value); + } + // _POP_TOP + value = stack_pointer[-2]; + { + Py_DECREF(value); } + stack_pointer += -2; DISPATCH(); } - TARGET(BUILD_STRING) { + TARGET(END_SEND) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(BUILD_STRING); - PyObject **pieces; - PyObject *str; - pieces = stack_pointer - oparg; - str = _PyUnicode_JoinArray(&_Py_STR(empty), pieces, oparg); - for (int _i = oparg; --_i >= 0;) { - Py_DECREF(pieces[_i]); + INSTRUCTION_STATS(END_SEND); + PyObject *value; + PyObject *receiver; + value = stack_pointer[-1]; + receiver = stack_pointer[-2]; + Py_DECREF(receiver); + stack_pointer[-2] = value; + stack_pointer += -1; + DISPATCH(); + } + + TARGET(ENTER_EXECUTOR) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(ENTER_EXECUTOR); + TIER_ONE_ONLY + CHECK_EVAL_BREAKER(); + PyCodeObject *code = _PyFrame_GetCode(frame); + _PyExecutorObject *executor = (_PyExecutorObject *)code->co_executors->executors[oparg&255]; + int original_oparg = executor->vm_data.oparg | (oparg & 0xfffff00); + JUMPBY(1-original_oparg); + frame->instr_ptr = next_instr; + Py_INCREF(executor); + if (executor->execute == _PyUOpExecute) { + current_executor = (_PyUOpExecutorObject *)executor; + GOTO_TIER_TWO(); } - if (str == NULL) { STACK_SHRINK(oparg); goto error; } - STACK_SHRINK(oparg); - STACK_GROW(1); - stack_pointer[-1] = str; - DISPATCH(); + frame = executor->execute(executor, frame, stack_pointer); + if (frame == NULL) { + frame = tstate->current_frame; + goto resume_with_error; + } + goto enter_tier_one; } - TARGET(BUILD_TUPLE) { + TARGET(EXIT_INIT_CHECK) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(BUILD_TUPLE); - PyObject **values; - PyObject *tup; - values = stack_pointer - oparg; - tup = _PyTuple_FromArraySteal(values, oparg); - if (tup == NULL) { STACK_SHRINK(oparg); goto error; } - STACK_SHRINK(oparg); - STACK_GROW(1); - stack_pointer[-1] = tup; + INSTRUCTION_STATS(EXIT_INIT_CHECK); + PyObject *should_be_none; + should_be_none = stack_pointer[-1]; + assert(STACK_LEVEL() == 2); + if (should_be_none != Py_None) { + PyErr_Format(PyExc_TypeError, + "__init__() should return None, not '%.200s'", + Py_TYPE(should_be_none)->tp_name); + GOTO_ERROR(error); + } + stack_pointer += -1; DISPATCH(); } - TARGET(BUILD_LIST) { + TARGET(EXTENDED_ARG) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(BUILD_LIST); - PyObject **values; - PyObject *list; - values = stack_pointer - oparg; - list = _PyList_FromArraySteal(values, oparg); - if (list == NULL) { STACK_SHRINK(oparg); goto error; } - STACK_SHRINK(oparg); - STACK_GROW(1); - stack_pointer[-1] = list; - DISPATCH(); + INSTRUCTION_STATS(EXTENDED_ARG); + assert(oparg); + opcode = next_instr->op.code; + oparg = oparg << 8 | next_instr->op.arg; + PRE_DISPATCH_GOTO(); + DISPATCH_GOTO(); } - TARGET(LIST_EXTEND) { + TARGET(FORMAT_SIMPLE) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(LIST_EXTEND); - PyObject *iterable; - PyObject *list; - iterable = stack_pointer[-1]; - list = stack_pointer[-2 - (oparg-1)]; - PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable); - if (none_val == NULL) { - if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError) && - (Py_TYPE(iterable)->tp_iter == NULL && !PySequence_Check(iterable))) - { - _PyErr_Clear(tstate); - _PyErr_Format(tstate, PyExc_TypeError, - "Value after * must be an iterable, not %.200s", - Py_TYPE(iterable)->tp_name); - } - Py_DECREF(iterable); - if (true) goto pop_1_error; + INSTRUCTION_STATS(FORMAT_SIMPLE); + PyObject *value; + PyObject *res; + value = stack_pointer[-1]; + /* If value is a unicode object, then we know the result + * of format(value) is value itself. */ + if (!PyUnicode_CheckExact(value)) { + res = PyObject_Format(value, NULL); + Py_DECREF(value); + if (res == NULL) goto pop_1_error; } - assert(Py_IsNone(none_val)); - Py_DECREF(iterable); - STACK_SHRINK(1); + else { + res = value; + } + stack_pointer[-1] = res; DISPATCH(); } - TARGET(SET_UPDATE) { + TARGET(FORMAT_WITH_SPEC) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(SET_UPDATE); - PyObject *iterable; - PyObject *set; - iterable = stack_pointer[-1]; - set = stack_pointer[-2 - (oparg-1)]; - int err = _PySet_Update(set, iterable); - Py_DECREF(iterable); - if (err < 0) goto pop_1_error; - STACK_SHRINK(1); + INSTRUCTION_STATS(FORMAT_WITH_SPEC); + PyObject *fmt_spec; + PyObject *value; + PyObject *res; + fmt_spec = stack_pointer[-1]; + value = stack_pointer[-2]; + res = PyObject_Format(value, fmt_spec); + Py_DECREF(value); + Py_DECREF(fmt_spec); + if (res == NULL) goto pop_2_error; + stack_pointer[-2] = res; + stack_pointer += -1; DISPATCH(); } - TARGET(BUILD_SET) { + TARGET(FOR_ITER) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(BUILD_SET); - PyObject **values; - PyObject *set; - values = stack_pointer - oparg; - set = PySet_New(NULL); - if (set == NULL) - GOTO_ERROR(error); - int err = 0; - for (int i = 0; i < oparg; i++) { - PyObject *item = values[i]; - if (err == 0) - err = PySet_Add(set, item); - Py_DECREF(item); + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER); + PREDICTED(FOR_ITER); + _Py_CODEUNIT *this_instr = next_instr - 2; + PyObject *iter; + PyObject *next; + // _SPECIALIZE_FOR_ITER + iter = stack_pointer[-1]; + { + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + next_instr = this_instr; + _Py_Specialize_ForIter(iter, next_instr, oparg); + DISPATCH_SAME_OPARG(); + } + STAT_INC(FOR_ITER, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ } - if (err != 0) { - Py_DECREF(set); - if (true) { STACK_SHRINK(oparg); goto error; } + // _FOR_ITER + { + /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ + next = (*Py_TYPE(iter)->tp_iternext)(iter); + if (next == NULL) { + if (_PyErr_Occurred(tstate)) { + if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { + GOTO_ERROR(error); + } + monitor_raise(tstate, frame, this_instr); + _PyErr_Clear(tstate); + } + /* iterator ended normally */ + assert(next_instr[oparg].op.code == END_FOR || + next_instr[oparg].op.code == INSTRUMENTED_END_FOR); + Py_DECREF(iter); + STACK_SHRINK(1); + /* Jump forward oparg, then skip following END_FOR instruction */ + JUMPBY(oparg + 1); + DISPATCH(); + } + // Common case: no jump, leave it to the code generator } - STACK_SHRINK(oparg); - STACK_GROW(1); - stack_pointer[-1] = set; + stack_pointer[0] = next; + stack_pointer += 1; DISPATCH(); } - TARGET(BUILD_MAP) { + TARGET(FOR_ITER_GEN) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER_GEN); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); + PyObject *iter; + iter = stack_pointer[-1]; + DEOPT_IF(tstate->interp->eval_frame, FOR_ITER); + PyGenObject *gen = (PyGenObject *)iter; + DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); + DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER); + STAT_INC(FOR_ITER, hit); + _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + _PyFrame_StackPush(gen_frame, Py_None); + gen->gi_frame_state = FRAME_EXECUTING; + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + assert(next_instr[oparg].op.code == END_FOR || + next_instr[oparg].op.code == INSTRUMENTED_END_FOR); + assert(next_instr - this_instr + oparg <= UINT16_MAX); + frame->return_offset = (uint16_t)(next_instr - this_instr + oparg); + DISPATCH_INLINED(gen_frame); + } + + TARGET(FOR_ITER_LIST) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(BUILD_MAP); - PyObject **values; - PyObject *map; - values = stack_pointer - oparg*2; - map = _PyDict_FromItems( - values, 2, - values+1, 2, - oparg); - for (int _i = oparg*2; --_i >= 0;) { - Py_DECREF(values[_i]); + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER_LIST); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); + PyObject *iter; + PyObject *next; + /* Skip 1 cache entry */ + // _ITER_CHECK_LIST + iter = stack_pointer[-1]; + { + DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, FOR_ITER); + } + // _ITER_JUMP_LIST + { + _PyListIterObject *it = (_PyListIterObject *)iter; + assert(Py_TYPE(iter) == &PyListIter_Type); + STAT_INC(FOR_ITER, hit); + PyListObject *seq = it->it_seq; + if (seq == NULL || it->it_index >= PyList_GET_SIZE(seq)) { + if (seq != NULL) { + it->it_seq = NULL; + Py_DECREF(seq); + } + Py_DECREF(iter); + STACK_SHRINK(1); + /* Jump forward oparg, then skip following END_FOR instruction */ + JUMPBY(oparg + 1); + DISPATCH(); + } + } + // _ITER_NEXT_LIST + { + _PyListIterObject *it = (_PyListIterObject *)iter; + assert(Py_TYPE(iter) == &PyListIter_Type); + PyListObject *seq = it->it_seq; + assert(seq); + assert(it->it_index < PyList_GET_SIZE(seq)); + next = Py_NewRef(PyList_GET_ITEM(seq, it->it_index++)); } - if (map == NULL) { STACK_SHRINK(oparg*2); goto error; } - STACK_SHRINK(oparg*2); - STACK_GROW(1); - stack_pointer[-1] = map; + stack_pointer[0] = next; + stack_pointer += 1; DISPATCH(); } - TARGET(SETUP_ANNOTATIONS) { + TARGET(FOR_ITER_RANGE) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(SETUP_ANNOTATIONS); - int err; - PyObject *ann_dict; - if (LOCALS() == NULL) { - _PyErr_Format(tstate, PyExc_SystemError, - "no locals found when setting up annotations"); - if (true) goto error; + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER_RANGE); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); + PyObject *iter; + PyObject *next; + /* Skip 1 cache entry */ + // _ITER_CHECK_RANGE + iter = stack_pointer[-1]; + { + _PyRangeIterObject *r = (_PyRangeIterObject *)iter; + DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); } - /* check if __annotations__ in locals()... */ - if (PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict) < 0) goto error; - if (ann_dict == NULL) { - ann_dict = PyDict_New(); - if (ann_dict == NULL) goto error; - err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__), - ann_dict); - Py_DECREF(ann_dict); - if (err) goto error; + // _ITER_JUMP_RANGE + { + _PyRangeIterObject *r = (_PyRangeIterObject *)iter; + assert(Py_TYPE(r) == &PyRangeIter_Type); + STAT_INC(FOR_ITER, hit); + if (r->len <= 0) { + STACK_SHRINK(1); + Py_DECREF(r); + // Jump over END_FOR instruction. + JUMPBY(oparg + 1); + DISPATCH(); + } } - else { - Py_DECREF(ann_dict); + // _ITER_NEXT_RANGE + { + _PyRangeIterObject *r = (_PyRangeIterObject *)iter; + assert(Py_TYPE(r) == &PyRangeIter_Type); + assert(r->len > 0); + long value = r->start; + r->start = value + r->step; + r->len--; + next = PyLong_FromLong(value); + if (next == NULL) goto error; } + stack_pointer[0] = next; + stack_pointer += 1; DISPATCH(); } - TARGET(BUILD_CONST_KEY_MAP) { + TARGET(FOR_ITER_TUPLE) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(BUILD_CONST_KEY_MAP); - PyObject *keys; - PyObject **values; - PyObject *map; - keys = stack_pointer[-1]; - values = stack_pointer - 1 - oparg; - if (!PyTuple_CheckExact(keys) || - PyTuple_GET_SIZE(keys) != (Py_ssize_t)oparg) { - _PyErr_SetString(tstate, PyExc_SystemError, - "bad BUILD_CONST_KEY_MAP keys argument"); - GOTO_ERROR(error); // Pop the keys and values. + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER_TUPLE); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); + PyObject *iter; + PyObject *next; + /* Skip 1 cache entry */ + // _ITER_CHECK_TUPLE + iter = stack_pointer[-1]; + { + DEOPT_IF(Py_TYPE(iter) != &PyTupleIter_Type, FOR_ITER); } - map = _PyDict_FromItems( - &PyTuple_GET_ITEM(keys, 0), 1, - values, 1, oparg); - for (int _i = oparg; --_i >= 0;) { - Py_DECREF(values[_i]); + // _ITER_JUMP_TUPLE + { + _PyTupleIterObject *it = (_PyTupleIterObject *)iter; + assert(Py_TYPE(iter) == &PyTupleIter_Type); + STAT_INC(FOR_ITER, hit); + PyTupleObject *seq = it->it_seq; + if (seq == NULL || it->it_index >= PyTuple_GET_SIZE(seq)) { + if (seq != NULL) { + it->it_seq = NULL; + Py_DECREF(seq); + } + Py_DECREF(iter); + STACK_SHRINK(1); + /* Jump forward oparg, then skip following END_FOR instruction */ + JUMPBY(oparg + 1); + DISPATCH(); + } } - Py_DECREF(keys); - if (map == NULL) { STACK_SHRINK(oparg); goto pop_1_error; } - STACK_SHRINK(oparg); - stack_pointer[-1] = map; + // _ITER_NEXT_TUPLE + { + _PyTupleIterObject *it = (_PyTupleIterObject *)iter; + assert(Py_TYPE(iter) == &PyTupleIter_Type); + PyTupleObject *seq = it->it_seq; + assert(seq); + assert(it->it_index < PyTuple_GET_SIZE(seq)); + next = Py_NewRef(PyTuple_GET_ITEM(seq, it->it_index++)); + } + stack_pointer[0] = next; + stack_pointer += 1; DISPATCH(); } - TARGET(DICT_UPDATE) { + TARGET(GET_AITER) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(DICT_UPDATE); - PyObject *update; - PyObject *dict; - update = stack_pointer[-1]; - dict = stack_pointer[-2 - (oparg - 1)]; - if (PyDict_Update(dict, update) < 0) { - if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) { - _PyErr_Format(tstate, PyExc_TypeError, - "'%.200s' object is not a mapping", - Py_TYPE(update)->tp_name); - } - Py_DECREF(update); + INSTRUCTION_STATS(GET_AITER); + PyObject *obj; + PyObject *iter; + obj = stack_pointer[-1]; + unaryfunc getter = NULL; + PyTypeObject *type = Py_TYPE(obj); + if (type->tp_as_async != NULL) { + getter = type->tp_as_async->am_aiter; + } + if (getter == NULL) { + _PyErr_Format(tstate, PyExc_TypeError, + "'async for' requires an object with " + "__aiter__ method, got %.100s", + type->tp_name); + Py_DECREF(obj); if (true) goto pop_1_error; } - Py_DECREF(update); - STACK_SHRINK(1); - DISPATCH(); - } - - TARGET(DICT_MERGE) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(DICT_MERGE); - PyObject *update; - PyObject *dict; - PyObject *callable; - update = stack_pointer[-1]; - dict = stack_pointer[-2 - (oparg - 1)]; - callable = stack_pointer[-5 - (oparg - 1)]; - if (_PyDict_MergeEx(dict, update, 2) < 0) { - _PyEval_FormatKwargsError(tstate, callable, update); - Py_DECREF(update); + iter = (*getter)(obj); + Py_DECREF(obj); + if (iter == NULL) goto pop_1_error; + if (Py_TYPE(iter)->tp_as_async == NULL || + Py_TYPE(iter)->tp_as_async->am_anext == NULL) { + _PyErr_Format(tstate, PyExc_TypeError, + "'async for' received an object from __aiter__ " + "that does not implement __anext__: %.100s", + Py_TYPE(iter)->tp_name); + Py_DECREF(iter); if (true) goto pop_1_error; } - Py_DECREF(update); - STACK_SHRINK(1); + stack_pointer[-1] = iter; DISPATCH(); } - TARGET(MAP_ADD) { + TARGET(GET_ANEXT) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(MAP_ADD); - PyObject *value; - PyObject *key; - PyObject *dict; - value = stack_pointer[-1]; - key = stack_pointer[-2]; - dict = stack_pointer[-3 - (oparg - 1)]; - assert(PyDict_CheckExact(dict)); - /* dict[key] = value */ - // Do not DECREF INPUTS because the function steals the references - if (_PyDict_SetItem_Take2((PyDictObject *)dict, key, value) != 0) goto pop_2_error; - STACK_SHRINK(2); + INSTRUCTION_STATS(GET_ANEXT); + PyObject *aiter; + PyObject *awaitable; + aiter = stack_pointer[-1]; + unaryfunc getter = NULL; + PyObject *next_iter = NULL; + PyTypeObject *type = Py_TYPE(aiter); + if (PyAsyncGen_CheckExact(aiter)) { + awaitable = type->tp_as_async->am_anext(aiter); + if (awaitable == NULL) { + GOTO_ERROR(error); + } + } else { + if (type->tp_as_async != NULL){ + getter = type->tp_as_async->am_anext; + } + if (getter != NULL) { + next_iter = (*getter)(aiter); + if (next_iter == NULL) { + GOTO_ERROR(error); + } + } + else { + _PyErr_Format(tstate, PyExc_TypeError, + "'async for' requires an iterator with " + "__anext__ method, got %.100s", + type->tp_name); + GOTO_ERROR(error); + } + awaitable = _PyCoro_GetAwaitableIter(next_iter); + if (awaitable == NULL) { + _PyErr_FormatFromCause( + PyExc_TypeError, + "'async for' received an invalid object " + "from __anext__: %.100s", + Py_TYPE(next_iter)->tp_name); + Py_DECREF(next_iter); + GOTO_ERROR(error); + } else { + Py_DECREF(next_iter); + } + } + stack_pointer[0] = awaitable; + stack_pointer += 1; DISPATCH(); } - TARGET(INSTRUMENTED_LOAD_SUPER_ATTR) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(INSTRUMENTED_LOAD_SUPER_ATTR); - // cancel out the decrement that will happen in LOAD_SUPER_ATTR; we - // don't want to specialize instrumented instructions - INCREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - GO_TO_INSTRUCTION(LOAD_SUPER_ATTR); - } - - TARGET(LOAD_SUPER_ATTR) { + TARGET(GET_AWAITABLE) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(LOAD_SUPER_ATTR); - PREDICTED(LOAD_SUPER_ATTR); - _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR == 1, "incorrect cache size"); - PyObject *class; - PyObject *global_super; - PyObject *self; - PyObject *attr; - PyObject *null = NULL; - // _SPECIALIZE_LOAD_SUPER_ATTR - class = stack_pointer[-2]; - global_super = stack_pointer[-3]; - { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - int load_method = oparg & 1; - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - next_instr = this_instr; - _Py_Specialize_LoadSuperAttr(global_super, class, next_instr, load_method); - DISPATCH_SAME_OPARG(); - } - STAT_INC(LOAD_SUPER_ATTR, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ - } - // _LOAD_SUPER_ATTR - self = stack_pointer[-1]; - { - TIER_ONE_ONLY - if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { - PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_CALL, - frame, this_instr, global_super, arg); - if (err) goto pop_3_error; - } - // we make no attempt to optimize here; specializations should - // handle any case whose performance we care about - PyObject *stack[] = {class, self}; - PyObject *super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL); - if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { - PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; - if (super == NULL) { - _Py_call_instrumentation_exc2( - tstate, PY_MONITORING_EVENT_C_RAISE, - frame, this_instr, global_super, arg); - } - else { - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_C_RETURN, - frame, this_instr, global_super, arg); - if (err < 0) { - Py_CLEAR(super); - } - } + next_instr += 1; + INSTRUCTION_STATS(GET_AWAITABLE); + PyObject *iterable; + PyObject *iter; + iterable = stack_pointer[-1]; + iter = _PyCoro_GetAwaitableIter(iterable); + if (iter == NULL) { + _PyEval_FormatAwaitableError(tstate, Py_TYPE(iterable), oparg); + } + Py_DECREF(iterable); + if (iter != NULL && PyCoro_CheckExact(iter)) { + PyObject *yf = _PyGen_yf((PyGenObject*)iter); + if (yf != NULL) { + /* `iter` is a coroutine object that is being + awaited, `yf` is a pointer to the current awaitable + being awaited on. */ + Py_DECREF(yf); + Py_CLEAR(iter); + _PyErr_SetString(tstate, PyExc_RuntimeError, + "coroutine is being awaited already"); + /* The code below jumps to `error` if `iter` is NULL. */ } - Py_DECREF(global_super); - Py_DECREF(class); - Py_DECREF(self); - if (super == NULL) goto pop_3_error; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); - attr = PyObject_GetAttr(super, name); - Py_DECREF(super); - if (attr == NULL) goto pop_3_error; - null = NULL; } - STACK_SHRINK(2); - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + if (iter == NULL) goto pop_1_error; + stack_pointer[-1] = iter; DISPATCH(); } - TARGET(LOAD_SUPER_ATTR_ATTR) { + TARGET(GET_ITER) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(LOAD_SUPER_ATTR_ATTR); - PyObject *self; - PyObject *class; - PyObject *global_super; - PyObject *attr; - self = stack_pointer[-1]; - class = stack_pointer[-2]; - global_super = stack_pointer[-3]; - assert(!(oparg & 1)); - DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR); - DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR); - STAT_INC(LOAD_SUPER_ATTR, hit); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); - attr = _PySuper_Lookup((PyTypeObject *)class, self, name, NULL); - Py_DECREF(global_super); - Py_DECREF(class); - Py_DECREF(self); - if (attr == NULL) goto pop_3_error; - STACK_SHRINK(2); - stack_pointer[-1] = attr; + next_instr += 1; + INSTRUCTION_STATS(GET_ITER); + PyObject *iterable; + PyObject *iter; + iterable = stack_pointer[-1]; + /* before: [obj]; after [getiter(obj)] */ + iter = PyObject_GetIter(iterable); + Py_DECREF(iterable); + if (iter == NULL) goto pop_1_error; + stack_pointer[-1] = iter; DISPATCH(); } - TARGET(LOAD_SUPER_ATTR_METHOD) { + TARGET(GET_LEN) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(LOAD_SUPER_ATTR_METHOD); - PyObject *self; - PyObject *class; - PyObject *global_super; - PyObject *attr; - PyObject *self_or_null; - self = stack_pointer[-1]; - class = stack_pointer[-2]; - global_super = stack_pointer[-3]; - assert(oparg & 1); - DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR); - DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR); - STAT_INC(LOAD_SUPER_ATTR, hit); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); - PyTypeObject *cls = (PyTypeObject *)class; - int method_found = 0; - attr = _PySuper_Lookup(cls, self, name, - Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); - Py_DECREF(global_super); - Py_DECREF(class); - if (attr == NULL) { - Py_DECREF(self); - if (true) goto pop_3_error; - } - if (method_found) { - self_or_null = self; // transfer ownership - } else { - Py_DECREF(self); - self_or_null = NULL; - } - STACK_SHRINK(1); - stack_pointer[-2] = attr; - stack_pointer[-1] = self_or_null; + next_instr += 1; + INSTRUCTION_STATS(GET_LEN); + PyObject *obj; + PyObject *len_o; + obj = stack_pointer[-1]; + // PUSH(len(TOS)) + Py_ssize_t len_i = PyObject_Length(obj); + if (len_i < 0) goto error; + len_o = PyLong_FromSsize_t(len_i); + if (len_o == NULL) goto error; + stack_pointer[0] = len_o; + stack_pointer += 1; DISPATCH(); } - TARGET(LOAD_ATTR) { + TARGET(GET_YIELD_FROM_ITER) { frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR); - PREDICTED(LOAD_ATTR); - _Py_CODEUNIT *this_instr = next_instr - 10; - static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); - PyObject *owner; - PyObject *attr; - PyObject *self_or_null = NULL; - // _SPECIALIZE_LOAD_ATTR - owner = stack_pointer[-1]; - { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); - next_instr = this_instr; - _Py_Specialize_LoadAttr(owner, next_instr, name); - DISPATCH_SAME_OPARG(); + next_instr += 1; + INSTRUCTION_STATS(GET_YIELD_FROM_ITER); + PyObject *iterable; + PyObject *iter; + iterable = stack_pointer[-1]; + /* before: [obj]; after [getiter(obj)] */ + if (PyCoro_CheckExact(iterable)) { + /* `iterable` is a coroutine */ + if (!(_PyFrame_GetCode(frame)->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) { + /* and it is used in a 'yield from' expression of a + regular generator. */ + _PyErr_SetString(tstate, PyExc_TypeError, + "cannot 'yield from' a coroutine object " + "in a non-coroutine generator"); + GOTO_ERROR(error); } - STAT_INC(LOAD_ATTR, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ + iter = iterable; } - // _LOAD_ATTR - { - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); - if (oparg & 1) { - /* Designed to work in tandem with CALL, pushes two values. */ - attr = NULL; - if (_PyObject_GetMethod(owner, name, &attr)) { - /* We can bypass temporary bound method object. - meth is unbound method and obj is self. - meth | self | arg1 | ... | argN - */ - assert(attr != NULL); // No errors on this branch - self_or_null = owner; // Transfer ownership - } - else { - /* meth is not an unbound method (but a regular attr, or - something was returned by a descriptor protocol). Set - the second element of the stack to NULL, to signal - CALL that it's not a method call. - NULL | meth | arg1 | ... | argN - */ - Py_DECREF(owner); - if (attr == NULL) goto pop_1_error; - self_or_null = NULL; - } + else if (PyGen_CheckExact(iterable)) { + iter = iterable; + } + else { + /* `iterable` is not a generator. */ + iter = PyObject_GetIter(iterable); + if (iter == NULL) { + GOTO_ERROR(error); } - else { - /* Classic, pushes one value. */ - attr = PyObject_GetAttr(owner, name); - Py_DECREF(owner); - if (attr == NULL) goto pop_1_error; + Py_DECREF(iterable); + } + stack_pointer[-1] = iter; + DISPATCH(); + } + + TARGET(IMPORT_FROM) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(IMPORT_FROM); + PyObject *from; + PyObject *res; + from = stack_pointer[-1]; + TIER_ONE_ONLY + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + res = import_from(tstate, from, name); + if (res == NULL) goto error; + stack_pointer[0] = res; + stack_pointer += 1; + DISPATCH(); + } + + TARGET(IMPORT_NAME) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(IMPORT_NAME); + PyObject *fromlist; + PyObject *level; + PyObject *res; + fromlist = stack_pointer[-1]; + level = stack_pointer[-2]; + TIER_ONE_ONLY + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + res = import_name(tstate, frame, name, fromlist, level); + Py_DECREF(level); + Py_DECREF(fromlist); + if (res == NULL) goto pop_2_error; + stack_pointer[-2] = res; + stack_pointer += -1; + DISPATCH(); + } + + TARGET(INSTRUMENTED_CALL) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(INSTRUMENTED_CALL); + int is_meth = PEEK(oparg + 1) != NULL; + int total_args = oparg + is_meth; + PyObject *function = PEEK(oparg + 2); + PyObject *arg = total_args == 0 ? + &_PyInstrumentation_MISSING : PEEK(total_args); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, function, arg); + if (err) goto error; + INCREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + GO_TO_INSTRUCTION(CALL); + } + + TARGET(INSTRUMENTED_CALL_FUNCTION_EX) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_CALL_FUNCTION_EX); + GO_TO_INSTRUCTION(CALL_FUNCTION_EX); + } + + TARGET(INSTRUMENTED_CALL_KW) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_CALL_KW); + int is_meth = PEEK(oparg + 2) != NULL; + int total_args = oparg + is_meth; + PyObject *function = PEEK(oparg + 3); + PyObject *arg = total_args == 0 ? &_PyInstrumentation_MISSING + : PEEK(total_args + 1); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, function, arg); + if (err) goto error; + GO_TO_INSTRUCTION(CALL_KW); + } + + TARGET(INSTRUMENTED_END_FOR) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_END_FOR); + PyObject *value; + PyObject *receiver; + value = stack_pointer[-1]; + receiver = stack_pointer[-2]; + TIER_ONE_ONLY + /* Need to create a fake StopIteration error here, + * to conform to PEP 380 */ + if (PyGen_Check(receiver)) { + PyErr_SetObject(PyExc_StopIteration, value); + if (monitor_stop_iteration(tstate, frame, this_instr)) { + GOTO_ERROR(error); } + PyErr_SetRaisedException(NULL); } - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = self_or_null; } + Py_DECREF(receiver); + Py_DECREF(value); + stack_pointer += -2; DISPATCH(); } - TARGET(LOAD_ATTR_INSTANCE_VALUE) { + TARGET(INSTRUMENTED_END_SEND) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_INSTANCE_VALUE); - PyObject *owner; - PyObject *attr; - PyObject *null = NULL; - // _GUARD_TYPE_VERSION - owner = stack_pointer[-1]; - { - uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); - } - // _CHECK_MANAGED_OBJECT_HAS_VALUES - { - assert(Py_TYPE(owner)->tp_dictoffset < 0); - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); - } - // _LOAD_ATTR_INSTANCE_VALUE - { - uint16_t index = read_u16(&this_instr[4].cache); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - attr = _PyDictOrValues_GetValues(dorv)->values[index]; - DEOPT_IF(attr == NULL, LOAD_ATTR); - STAT_INC(LOAD_ATTR, hit); - Py_INCREF(attr); - null = NULL; - Py_DECREF(owner); + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_END_SEND); + PyObject *value; + PyObject *receiver; + value = stack_pointer[-1]; + receiver = stack_pointer[-2]; + TIER_ONE_ONLY + if (PyGen_Check(receiver) || PyCoro_CheckExact(receiver)) { + PyErr_SetObject(PyExc_StopIteration, value); + if (monitor_stop_iteration(tstate, frame, this_instr)) { + GOTO_ERROR(error); + } + PyErr_SetRaisedException(NULL); } - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + Py_DECREF(receiver); + stack_pointer[-2] = value; + stack_pointer += -1; DISPATCH(); } - TARGET(LOAD_ATTR_MODULE) { + TARGET(INSTRUMENTED_FOR_ITER) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_MODULE); - PyObject *owner; - PyObject *attr; - PyObject *null = NULL; - // _CHECK_ATTR_MODULE - owner = stack_pointer[-1]; - { - uint32_t type_version = read_u32(&this_instr[2].cache); - DEOPT_IF(!PyModule_CheckExact(owner), LOAD_ATTR); - PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; - assert(dict != NULL); - DEOPT_IF(dict->ma_keys->dk_version != type_version, LOAD_ATTR); + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_FOR_ITER); + _Py_CODEUNIT *target; + PyObject *iter = TOP(); + PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); + if (next != NULL) { + PUSH(next); + target = next_instr; } - // _LOAD_ATTR_MODULE - { - uint16_t index = read_u16(&this_instr[4].cache); - PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; - assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); - assert(index < dict->ma_keys->dk_nentries); - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index; - attr = ep->me_value; - DEOPT_IF(attr == NULL, LOAD_ATTR); - STAT_INC(LOAD_ATTR, hit); - Py_INCREF(attr); - null = NULL; - Py_DECREF(owner); + else { + if (_PyErr_Occurred(tstate)) { + if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { + GOTO_ERROR(error); + } + monitor_raise(tstate, frame, this_instr); + _PyErr_Clear(tstate); + } + /* iterator ended normally */ + assert(next_instr[oparg].op.code == END_FOR || + next_instr[oparg].op.code == INSTRUMENTED_END_FOR); + STACK_SHRINK(1); + Py_DECREF(iter); + /* Skip END_FOR */ + target = next_instr + oparg + 1; } - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } - TARGET(LOAD_ATTR_WITH_HINT) { + TARGET(INSTRUMENTED_INSTRUCTION) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_WITH_HINT); - PyObject *owner; - PyObject *attr; - PyObject *null = NULL; - // _GUARD_TYPE_VERSION - owner = stack_pointer[-1]; - { - uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); - } - // _CHECK_ATTR_WITH_HINT - { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(_PyDictOrValues_IsValues(dorv), LOAD_ATTR); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); - DEOPT_IF(dict == NULL, LOAD_ATTR); - assert(PyDict_CheckExact((PyObject *)dict)); - } - // _LOAD_ATTR_WITH_HINT - { - uint16_t hint = read_u16(&this_instr[4].cache); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); - DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); - if (DK_IS_UNICODE(dict->ma_keys)) { - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; - DEOPT_IF(ep->me_key != name, LOAD_ATTR); - attr = ep->me_value; - } - else { - PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + hint; - DEOPT_IF(ep->me_key != name, LOAD_ATTR); - attr = ep->me_value; - } - DEOPT_IF(attr == NULL, LOAD_ATTR); - STAT_INC(LOAD_ATTR, hit); - Py_INCREF(attr); - null = NULL; - Py_DECREF(owner); + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_INSTRUCTION); + int next_opcode = _Py_call_instrumentation_instruction( + tstate, frame, this_instr); + if (next_opcode < 0) goto error; + next_instr = this_instr; + if (_PyOpcode_Caches[next_opcode]) { + INCREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); } - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + assert(next_opcode > 0 && next_opcode < 256); + opcode = next_opcode; + DISPATCH_GOTO(); + } + + TARGET(INSTRUMENTED_JUMP_BACKWARD) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_JUMP_BACKWARD); + CHECK_EVAL_BREAKER(); + INSTRUMENTED_JUMP(this_instr, next_instr - oparg, PY_MONITORING_EVENT_JUMP); DISPATCH(); } - TARGET(LOAD_ATTR_SLOT) { + TARGET(INSTRUMENTED_JUMP_FORWARD) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_SLOT); - PyObject *owner; - PyObject *attr; - PyObject *null = NULL; - // _GUARD_TYPE_VERSION - owner = stack_pointer[-1]; - { - uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); - } - // _LOAD_ATTR_SLOT - { - uint16_t index = read_u16(&this_instr[4].cache); - char *addr = (char *)owner + index; - attr = *(PyObject **)addr; - DEOPT_IF(attr == NULL, LOAD_ATTR); - STAT_INC(LOAD_ATTR, hit); - Py_INCREF(attr); - null = NULL; - Py_DECREF(owner); - } - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_JUMP_FORWARD); + INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_JUMP); DISPATCH(); } - TARGET(LOAD_ATTR_CLASS) { + TARGET(INSTRUMENTED_LOAD_SUPER_ATTR) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_CLASS); - PyObject *owner; - PyObject *attr; - PyObject *null = NULL; - // _CHECK_ATTR_CLASS - owner = stack_pointer[-1]; - { - uint32_t type_version = read_u32(&this_instr[2].cache); - DEOPT_IF(!PyType_Check(owner), LOAD_ATTR); - assert(type_version != 0); - DEOPT_IF(((PyTypeObject *)owner)->tp_version_tag != type_version, LOAD_ATTR); - } - // _LOAD_ATTR_CLASS - { - PyObject *descr = read_obj(&this_instr[6].cache); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - attr = Py_NewRef(descr); - null = NULL; - Py_DECREF(owner); + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_LOAD_SUPER_ATTR); + // cancel out the decrement that will happen in LOAD_SUPER_ATTR; we + // don't want to specialize instrumented instructions + INCREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + GO_TO_INSTRUCTION(LOAD_SUPER_ATTR); + } + + TARGET(INSTRUMENTED_POP_JUMP_IF_FALSE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_FALSE); + PyObject *cond = POP(); + assert(PyBool_Check(cond)); + int flag = Py_IsFalse(cond); + int offset = flag * oparg; + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | flag; + #endif + INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + DISPATCH(); + } + + TARGET(INSTRUMENTED_POP_JUMP_IF_NONE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NONE); + PyObject *value = POP(); + int flag = Py_IsNone(value); + int offset; + if (flag) { + offset = oparg; + } + else { + Py_DECREF(value); + offset = 0; } - STACK_GROW(((oparg & 1) ? 1 : 0)); - stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; - if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | flag; + #endif + INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } - TARGET(LOAD_ATTR_PROPERTY) { + TARGET(INSTRUMENTED_POP_JUMP_IF_NOT_NONE) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_PROPERTY); - PyObject *owner; - owner = stack_pointer[-1]; - uint32_t type_version = read_u32(&this_instr[2].cache); - uint32_t func_version = read_u32(&this_instr[4].cache); - PyObject *fget = read_obj(&this_instr[6].cache); - assert((oparg & 1) == 0); - DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); - - PyTypeObject *cls = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR); - assert(Py_IS_TYPE(fget, &PyFunction_Type)); - PyFunctionObject *f = (PyFunctionObject *)fget; - assert(func_version != 0); - DEOPT_IF(f->func_version != func_version, LOAD_ATTR); - PyCodeObject *code = (PyCodeObject *)f->func_code; - assert(code->co_argcount == 1); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); - STAT_INC(LOAD_ATTR, hit); - Py_INCREF(fget); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1); - // Manipulate stack directly because we exit with DISPATCH_INLINED(). - STACK_SHRINK(1); - new_frame->localsplus[0] = owner; - frame->return_offset = (uint16_t)(next_instr - this_instr); - DISPATCH_INLINED(new_frame); + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NOT_NONE); + PyObject *value = POP(); + int offset; + int nflag = Py_IsNone(value); + if (nflag) { + offset = 0; + } + else { + Py_DECREF(value); + offset = oparg; + } + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | !nflag; + #endif + INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + DISPATCH(); } - TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) { + TARGET(INSTRUMENTED_POP_JUMP_IF_TRUE) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN); - PyObject *owner; - owner = stack_pointer[-1]; - uint32_t type_version = read_u32(&this_instr[2].cache); - uint32_t func_version = read_u32(&this_instr[4].cache); - PyObject *getattribute = read_obj(&this_instr[6].cache); - assert((oparg & 1) == 0); - DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); - PyTypeObject *cls = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR); - assert(Py_IS_TYPE(getattribute, &PyFunction_Type)); - PyFunctionObject *f = (PyFunctionObject *)getattribute; - assert(func_version != 0); - DEOPT_IF(f->func_version != func_version, LOAD_ATTR); - PyCodeObject *code = (PyCodeObject *)f->func_code; - assert(code->co_argcount == 2); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); - STAT_INC(LOAD_ATTR, hit); - - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); - Py_INCREF(f); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 2); - // Manipulate stack directly because we exit with DISPATCH_INLINED(). - STACK_SHRINK(1); - new_frame->localsplus[0] = owner; - new_frame->localsplus[1] = Py_NewRef(name); - frame->return_offset = (uint16_t)(next_instr - this_instr); - DISPATCH_INLINED(new_frame); + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_TRUE); + PyObject *cond = POP(); + assert(PyBool_Check(cond)); + int flag = Py_IsTrue(cond); + int offset = flag * oparg; + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | flag; + #endif + INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + DISPATCH(); } - TARGET(STORE_ATTR_INSTANCE_VALUE) { + TARGET(INSTRUMENTED_RESUME) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 5; - INSTRUCTION_STATS(STORE_ATTR_INSTANCE_VALUE); - PyObject *owner; - PyObject *value; - // _GUARD_TYPE_VERSION - owner = stack_pointer[-1]; - { - uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); - } - // _GUARD_DORV_VALUES - { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(dorv), STORE_ATTR); + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_RESUME); + uintptr_t global_version = _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker) & ~_PY_EVAL_EVENTS_MASK; + uintptr_t code_version = _PyFrame_GetCode(frame)->_co_instrumentation_version; + if (code_version != global_version) { + if (_Py_Instrument(_PyFrame_GetCode(frame), tstate->interp)) { + GOTO_ERROR(error); + } + next_instr = this_instr; } - // _STORE_ATTR_INSTANCE_VALUE - value = stack_pointer[-2]; - { - uint16_t index = read_u16(&this_instr[4].cache); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - STAT_INC(STORE_ATTR, hit); - PyDictValues *values = _PyDictOrValues_GetValues(dorv); - PyObject *old_value = values->values[index]; - values->values[index] = value; - if (old_value == NULL) { - _PyDictValues_AddToInsertionOrder(values, index); + else { + if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { + CHECK_EVAL_BREAKER(); } - else { - Py_DECREF(old_value); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation( + tstate, oparg > 0, frame, this_instr); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) goto error; + if (frame->instr_ptr != this_instr) { + /* Instrumentation has jumped */ + next_instr = this_instr; + DISPATCH(); } - Py_DECREF(owner); } - STACK_SHRINK(2); DISPATCH(); } - TARGET(STORE_ATTR_WITH_HINT) { + TARGET(INSTRUMENTED_RETURN_CONST) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 5; - INSTRUCTION_STATS(STORE_ATTR_WITH_HINT); - PyObject *owner; - PyObject *value; - owner = stack_pointer[-1]; - value = stack_pointer[-2]; - uint32_t type_version = read_u32(&this_instr[2].cache); - uint16_t hint = read_u16(&this_instr[4].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); - assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(_PyDictOrValues_IsValues(dorv), STORE_ATTR); - PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); - DEOPT_IF(dict == NULL, STORE_ATTR); - assert(PyDict_CheckExact((PyObject *)dict)); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR); - PyObject *old_value; - uint64_t new_version; - if (DK_IS_UNICODE(dict->ma_keys)) { - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; - DEOPT_IF(ep->me_key != name, STORE_ATTR); - old_value = ep->me_value; - DEOPT_IF(old_value == NULL, STORE_ATTR); - new_version = _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, value); - ep->me_value = value; - } - else { - PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + hint; - DEOPT_IF(ep->me_key != name, STORE_ATTR); - old_value = ep->me_value; - DEOPT_IF(old_value == NULL, STORE_ATTR); - new_version = _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, value); - ep->me_value = value; - } - Py_DECREF(old_value); - STAT_INC(STORE_ATTR, hit); - /* Ensure dict is GC tracked if it needs to be */ - if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(value)) { - _PyObject_GC_TRACK(dict); - } - /* PEP 509 */ - dict->ma_version_tag = new_version; - Py_DECREF(owner); - STACK_SHRINK(2); - DISPATCH(); + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_RETURN_CONST); + PyObject *retval = GETITEM(FRAME_CO_CONSTS, oparg); + int err = _Py_call_instrumentation_arg( + tstate, PY_MONITORING_EVENT_PY_RETURN, + frame, this_instr, retval); + if (err) GOTO_ERROR(error); + Py_INCREF(retval); + assert(EMPTY()); + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_LeaveRecursiveCallPy(tstate); + assert(frame != &entry_frame); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + _PyFrame_StackPush(frame, retval); + LOAD_IP(frame->return_offset); + goto resume_frame; + } + + TARGET(INSTRUMENTED_RETURN_VALUE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_RETURN_VALUE); + PyObject *retval; + retval = stack_pointer[-1]; + int err = _Py_call_instrumentation_arg( + tstate, PY_MONITORING_EVENT_PY_RETURN, + frame, this_instr, retval); + if (err) GOTO_ERROR(error); + STACK_SHRINK(1); + assert(EMPTY()); + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_LeaveRecursiveCallPy(tstate); + assert(frame != &entry_frame); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + _PyFrame_StackPush(frame, retval); + LOAD_IP(frame->return_offset); + goto resume_frame; + } + + TARGET(INSTRUMENTED_YIELD_VALUE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_YIELD_VALUE); + PyObject *retval; + retval = stack_pointer[-1]; + assert(frame != &entry_frame); + frame->instr_ptr = next_instr; + PyGenObject *gen = _PyFrame_GetGenerator(frame); + assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); + assert(oparg == 0 || oparg == 1); + gen->gi_frame_state = FRAME_SUSPENDED + oparg; + _PyFrame_SetStackPointer(frame, stack_pointer - 1); + int err = _Py_call_instrumentation_arg( + tstate, PY_MONITORING_EVENT_PY_YIELD, + frame, this_instr, retval); + if (err) GOTO_ERROR(error); + tstate->exc_info = gen->gi_exc_state.previous_item; + gen->gi_exc_state.previous_item = NULL; + _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *gen_frame = frame; + frame = tstate->current_frame = frame->previous; + gen_frame->previous = NULL; + _PyFrame_StackPush(frame, retval); + /* We don't know which of these is relevant here, so keep them equal */ + assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); + LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); + goto resume_frame; } - TARGET(STORE_ATTR_SLOT) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 5; - INSTRUCTION_STATS(STORE_ATTR_SLOT); - PyObject *owner; - PyObject *value; - // _GUARD_TYPE_VERSION - owner = stack_pointer[-1]; - { - uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); - } - // _STORE_ATTR_SLOT - value = stack_pointer[-2]; - { - uint16_t index = read_u16(&this_instr[4].cache); - char *addr = (char *)owner + index; - STAT_INC(STORE_ATTR, hit); - PyObject *old_value = *(PyObject **)addr; - *(PyObject **)addr = value; - Py_XDECREF(old_value); - Py_DECREF(owner); - } - STACK_SHRINK(2); - DISPATCH(); + TARGET(INTERPRETER_EXIT) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INTERPRETER_EXIT); + PyObject *retval; + retval = stack_pointer[-1]; + assert(frame == &entry_frame); + assert(_PyFrame_IsIncomplete(frame)); + /* Restore previous frame and return. */ + tstate->current_frame = frame->previous; + assert(!_PyErr_Occurred(tstate)); + tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS; + return retval; } - TARGET(COMPARE_OP) { + TARGET(IS_OP) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(COMPARE_OP); - PREDICTED(COMPARE_OP); - _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_COMPARE_OP == 1, "incorrect cache size"); + next_instr += 1; + INSTRUCTION_STATS(IS_OP); PyObject *right; PyObject *left; - PyObject *res; - // _SPECIALIZE_COMPARE_OP + PyObject *b; right = stack_pointer[-1]; left = stack_pointer[-2]; - { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + int res = Py_Is(left, right) ^ oparg; + Py_DECREF(left); + Py_DECREF(right); + b = res ? Py_True : Py_False; + stack_pointer[-2] = b; + stack_pointer += -1; + DISPATCH(); + } + + TARGET(JUMP_BACKWARD) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(JUMP_BACKWARD); + CHECK_EVAL_BREAKER(); + assert(oparg <= INSTR_OFFSET()); + JUMPBY(-oparg); + #if ENABLE_SPECIALIZATION + this_instr[1].cache += (1 << OPTIMIZER_BITS_IN_COUNTER); + /* We are using unsigned values, but we really want signed values, so + * do the 2s complement comparison manually */ + uint16_t ucounter = this_instr[1].cache + (1 << 15); + uint16_t threshold = tstate->interp->optimizer_backedge_threshold + (1 << 15); + // Double-check that the opcode isn't instrumented or something: + if (ucounter > threshold && this_instr->op.code == JUMP_BACKWARD) { + OPT_STAT_INC(attempts); + int optimized = _PyOptimizer_BackEdge(frame, this_instr, next_instr, stack_pointer); + if (optimized < 0) goto error; + if (optimized) { + // Rewind and enter the executor: + assert(this_instr->op.code == ENTER_EXECUTOR); next_instr = this_instr; - _Py_Specialize_CompareOp(left, right, next_instr, oparg); - DISPATCH_SAME_OPARG(); + this_instr[1].cache &= ((1 << OPTIMIZER_BITS_IN_COUNTER) - 1); } - STAT_INC(COMPARE_OP, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ - } - // _COMPARE_OP - { - assert((oparg >> 5) <= Py_GE); - res = PyObject_RichCompare(left, right, oparg >> 5); - Py_DECREF(left); - Py_DECREF(right); - if (res == NULL) goto pop_2_error; - if (oparg & 16) { - int res_bool = PyObject_IsTrue(res); - Py_DECREF(res); - if (res_bool < 0) goto pop_2_error; - res = res_bool ? Py_True : Py_False; + else { + int backoff = this_instr[1].cache & ((1 << OPTIMIZER_BITS_IN_COUNTER) - 1); + if (backoff < MINIMUM_TIER2_BACKOFF) { + backoff = MINIMUM_TIER2_BACKOFF; + } + else if (backoff < 15 - OPTIMIZER_BITS_IN_COUNTER) { + backoff++; + } + assert(backoff <= 15 - OPTIMIZER_BITS_IN_COUNTER); + this_instr[1].cache = ((1 << 16) - ((1 << OPTIMIZER_BITS_IN_COUNTER) << backoff)) | backoff; } } - STACK_SHRINK(1); - stack_pointer[-1] = res; + #endif /* ENABLE_SPECIALIZATION */ DISPATCH(); } - TARGET(COMPARE_OP_FLOAT) { + TARGET(JUMP_BACKWARD_NO_INTERRUPT) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(COMPARE_OP_FLOAT); - PyObject *right; - PyObject *left; - PyObject *res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_OP); - DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_OP); - STAT_INC(COMPARE_OP, hit); - double dleft = PyFloat_AS_DOUBLE(left); - double dright = PyFloat_AS_DOUBLE(right); - // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg - int sign_ish = COMPARISON_BIT(dleft, dright); - _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); - _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); - res = (sign_ish & oparg) ? Py_True : Py_False; - // It's always a bool, so we don't care about oparg & 16. - STACK_SHRINK(1); - stack_pointer[-1] = res; + next_instr += 1; + INSTRUCTION_STATS(JUMP_BACKWARD_NO_INTERRUPT); + /* This bytecode is used in the `yield from` or `await` loop. + * If there is an interrupt, we want it handled in the innermost + * generator or coroutine, so we deliberately do not check it here. + * (see bpo-30039). + */ + JUMPBY(-oparg); DISPATCH(); } - TARGET(COMPARE_OP_INT) { + TARGET(JUMP_FORWARD) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(COMPARE_OP_INT); - PyObject *right; - PyObject *left; - PyObject *res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - DEOPT_IF(!PyLong_CheckExact(left), COMPARE_OP); - DEOPT_IF(!PyLong_CheckExact(right), COMPARE_OP); - DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)left), COMPARE_OP); - DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)right), COMPARE_OP); - STAT_INC(COMPARE_OP, hit); - assert(_PyLong_DigitCount((PyLongObject *)left) <= 1 && - _PyLong_DigitCount((PyLongObject *)right) <= 1); - Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left); - Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right); - // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg - int sign_ish = COMPARISON_BIT(ileft, iright); - _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); - res = (sign_ish & oparg) ? Py_True : Py_False; - // It's always a bool, so we don't care about oparg & 16. - STACK_SHRINK(1); - stack_pointer[-1] = res; + next_instr += 1; + INSTRUCTION_STATS(JUMP_FORWARD); + JUMPBY(oparg); DISPATCH(); } - TARGET(COMPARE_OP_STR) { + TARGET(LIST_APPEND) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(COMPARE_OP_STR); - PyObject *right; - PyObject *left; - PyObject *res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_OP); - DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_OP); - STAT_INC(COMPARE_OP, hit); - int eq = _PyUnicode_Equal(left, right); - assert((oparg >> 5) == Py_EQ || (oparg >> 5) == Py_NE); - _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); - _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); - assert(eq == 0 || eq == 1); - assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); - assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); - res = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? Py_True : Py_False; - // It's always a bool, so we don't care about oparg & 16. - STACK_SHRINK(1); - stack_pointer[-1] = res; + next_instr += 1; + INSTRUCTION_STATS(LIST_APPEND); + PyObject *v; + PyObject *list; + v = stack_pointer[-1]; + list = stack_pointer[-2 - (oparg-1)]; + if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0) goto pop_1_error; + stack_pointer += -1; DISPATCH(); } - TARGET(IS_OP) { + TARGET(LIST_EXTEND) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(IS_OP); - PyObject *right; - PyObject *left; - PyObject *b; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - int res = Py_Is(left, right) ^ oparg; - Py_DECREF(left); - Py_DECREF(right); - b = res ? Py_True : Py_False; - STACK_SHRINK(1); - stack_pointer[-1] = b; + INSTRUCTION_STATS(LIST_EXTEND); + PyObject *iterable; + PyObject *list; + iterable = stack_pointer[-1]; + list = stack_pointer[-2 - (oparg-1)]; + PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable); + if (none_val == NULL) { + if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError) && + (Py_TYPE(iterable)->tp_iter == NULL && !PySequence_Check(iterable))) + { + _PyErr_Clear(tstate); + _PyErr_Format(tstate, PyExc_TypeError, + "Value after * must be an iterable, not %.200s", + Py_TYPE(iterable)->tp_name); + } + Py_DECREF(iterable); + if (true) goto pop_1_error; + } + assert(Py_IsNone(none_val)); + Py_DECREF(iterable); + stack_pointer += -1; DISPATCH(); } - TARGET(CONTAINS_OP) { + TARGET(LOAD_ASSERTION_ERROR) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(CONTAINS_OP); - PyObject *right; - PyObject *left; - PyObject *b; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - int res = PySequence_Contains(right, left); - Py_DECREF(left); - Py_DECREF(right); - if (res < 0) goto pop_2_error; - b = (res ^ oparg) ? Py_True : Py_False; - STACK_SHRINK(1); - stack_pointer[-1] = b; + INSTRUCTION_STATS(LOAD_ASSERTION_ERROR); + PyObject *value; + value = Py_NewRef(PyExc_AssertionError); + stack_pointer[0] = value; + stack_pointer += 1; DISPATCH(); } - TARGET(CHECK_EG_MATCH) { + TARGET(LOAD_ATTR) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(CHECK_EG_MATCH); - PyObject *match_type; - PyObject *exc_value; - PyObject *rest; - PyObject *match; - match_type = stack_pointer[-1]; - exc_value = stack_pointer[-2]; - if (_PyEval_CheckExceptStarTypeValid(tstate, match_type) < 0) { - Py_DECREF(exc_value); - Py_DECREF(match_type); - if (true) goto pop_2_error; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR); + PREDICTED(LOAD_ATTR); + _Py_CODEUNIT *this_instr = next_instr - 10; + PyObject *owner; + PyObject *attr; + PyObject *self_or_null = NULL; + // _SPECIALIZE_LOAD_ATTR + owner = stack_pointer[-1]; + { + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); + next_instr = this_instr; + _Py_Specialize_LoadAttr(owner, next_instr, name); + DISPATCH_SAME_OPARG(); + } + STAT_INC(LOAD_ATTR, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ } - - match = NULL; - rest = NULL; - int res = _PyEval_ExceptionGroupMatch(exc_value, match_type, - &match, &rest); - Py_DECREF(exc_value); - Py_DECREF(match_type); - if (res < 0) goto pop_2_error; - - assert((match == NULL) == (rest == NULL)); - if (match == NULL) goto pop_2_error; - - if (!Py_IsNone(match)) { - PyErr_SetHandledException(match); + // _LOAD_ATTR + { + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); + if (oparg & 1) { + /* Designed to work in tandem with CALL, pushes two values. */ + attr = NULL; + if (_PyObject_GetMethod(owner, name, &attr)) { + /* We can bypass temporary bound method object. + meth is unbound method and obj is self. + meth | self | arg1 | ... | argN + */ + assert(attr != NULL); // No errors on this branch + self_or_null = owner; // Transfer ownership + } + else { + /* meth is not an unbound method (but a regular attr, or + something was returned by a descriptor protocol). Set + the second element of the stack to NULL, to signal + CALL that it's not a method call. + NULL | meth | arg1 | ... | argN + */ + Py_DECREF(owner); + if (attr == NULL) goto pop_1_error; + self_or_null = NULL; + } + } + else { + /* Classic, pushes one value. */ + attr = PyObject_GetAttr(owner, name); + Py_DECREF(owner); + if (attr == NULL) goto pop_1_error; + } } - stack_pointer[-2] = rest; - stack_pointer[-1] = match; + stack_pointer[-1] = attr; + if (oparg & 1) stack_pointer[0] = self_or_null; + stack_pointer += ((oparg & 1)); DISPATCH(); } - TARGET(CHECK_EXC_MATCH) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(CHECK_EXC_MATCH); - PyObject *right; - PyObject *left; - PyObject *b; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - assert(PyExceptionInstance_Check(left)); - if (_PyEval_CheckExceptTypeValid(tstate, right) < 0) { - Py_DECREF(right); - if (true) goto pop_1_error; + TARGET(LOAD_ATTR_CLASS) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_CLASS); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + PyObject *owner; + PyObject *attr; + PyObject *null = NULL; + /* Skip 1 cache entry */ + // _CHECK_ATTR_CLASS + owner = stack_pointer[-1]; + { + uint32_t type_version = read_u32(&this_instr[2].cache); + DEOPT_IF(!PyType_Check(owner), LOAD_ATTR); + assert(type_version != 0); + DEOPT_IF(((PyTypeObject *)owner)->tp_version_tag != type_version, LOAD_ATTR); } - - int res = PyErr_GivenExceptionMatches(left, right); - Py_DECREF(right); - b = res ? Py_True : Py_False; - stack_pointer[-1] = b; + /* Skip 2 cache entries */ + // _LOAD_ATTR_CLASS + { + PyObject *descr = read_obj(&this_instr[6].cache); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + attr = Py_NewRef(descr); + null = NULL; + Py_DECREF(owner); + } + stack_pointer[-1] = attr; + if (oparg & 1) stack_pointer[0] = null; + stack_pointer += ((oparg & 1)); DISPATCH(); } - TARGET(IMPORT_NAME) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(IMPORT_NAME); - PyObject *fromlist; - PyObject *level; - PyObject *res; - fromlist = stack_pointer[-1]; - level = stack_pointer[-2]; - TIER_ONE_ONLY - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - res = import_name(tstate, frame, name, fromlist, level); - Py_DECREF(level); - Py_DECREF(fromlist); - if (res == NULL) goto pop_2_error; + TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + PyObject *owner; + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + uint32_t func_version = read_u32(&this_instr[4].cache); + PyObject *getattribute = read_obj(&this_instr[6].cache); + assert((oparg & 1) == 0); + DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); + PyTypeObject *cls = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR); + assert(Py_IS_TYPE(getattribute, &PyFunction_Type)); + PyFunctionObject *f = (PyFunctionObject *)getattribute; + assert(func_version != 0); + DEOPT_IF(f->func_version != func_version, LOAD_ATTR); + PyCodeObject *code = (PyCodeObject *)f->func_code; + assert(code->co_argcount == 2); + DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); + Py_INCREF(f); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 2); + // Manipulate stack directly because we exit with DISPATCH_INLINED(). STACK_SHRINK(1); - stack_pointer[-1] = res; + new_frame->localsplus[0] = owner; + new_frame->localsplus[1] = Py_NewRef(name); + frame->return_offset = (uint16_t)(next_instr - this_instr); + DISPATCH_INLINED(new_frame); + } + + TARGET(LOAD_ATTR_INSTANCE_VALUE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_INSTANCE_VALUE); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + PyObject *owner; + PyObject *attr; + PyObject *null = NULL; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + owner = stack_pointer[-1]; + { + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + } + // _CHECK_MANAGED_OBJECT_HAS_VALUES + { + assert(Py_TYPE(owner)->tp_dictoffset < 0); + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); + } + // _LOAD_ATTR_INSTANCE_VALUE + { + uint16_t index = read_u16(&this_instr[4].cache); + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + attr = _PyDictOrValues_GetValues(dorv)->values[index]; + DEOPT_IF(attr == NULL, LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); + Py_INCREF(attr); + null = NULL; + Py_DECREF(owner); + } + /* Skip 5 cache entries */ + stack_pointer[-1] = attr; + if (oparg & 1) stack_pointer[0] = null; + stack_pointer += ((oparg & 1)); DISPATCH(); } - TARGET(IMPORT_FROM) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(IMPORT_FROM); - PyObject *from; - PyObject *res; - from = stack_pointer[-1]; - TIER_ONE_ONLY - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - res = import_from(tstate, from, name); - if (res == NULL) goto error; - STACK_GROW(1); - stack_pointer[-1] = res; + TARGET(LOAD_ATTR_METHOD_LAZY_DICT) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_METHOD_LAZY_DICT); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + PyObject *owner; + PyObject *attr; + PyObject *self = NULL; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + owner = stack_pointer[-1]; + { + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + } + // _CHECK_ATTR_METHOD_LAZY_DICT + { + Py_ssize_t dictoffset = Py_TYPE(owner)->tp_dictoffset; + assert(dictoffset > 0); + PyObject *dict = *(PyObject **)((char *)owner + dictoffset); + /* This object has a __dict__, just not yet created */ + DEOPT_IF(dict != NULL, LOAD_ATTR); + } + /* Skip 2 cache entries */ + // _LOAD_ATTR_METHOD_LAZY_DICT + { + PyObject *descr = read_obj(&this_instr[6].cache); + assert(oparg & 1); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = Py_NewRef(descr); + self = owner; + } + stack_pointer[-1] = attr; + if (1) stack_pointer[0] = self; + stack_pointer += (((1) ? 1 : 0)); DISPATCH(); } - TARGET(JUMP_FORWARD) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(JUMP_FORWARD); - JUMPBY(oparg); + TARGET(LOAD_ATTR_METHOD_NO_DICT) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_METHOD_NO_DICT); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + PyObject *owner; + PyObject *attr; + PyObject *self = NULL; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + owner = stack_pointer[-1]; + { + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + } + /* Skip 2 cache entries */ + // _LOAD_ATTR_METHOD_NO_DICT + { + PyObject *descr = read_obj(&this_instr[6].cache); + assert(oparg & 1); + assert(Py_TYPE(owner)->tp_dictoffset == 0); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = Py_NewRef(descr); + self = owner; + } + stack_pointer[-1] = attr; + if (1) stack_pointer[0] = self; + stack_pointer += (((1) ? 1 : 0)); DISPATCH(); } - TARGET(JUMP_BACKWARD) { + TARGET(LOAD_ATTR_METHOD_WITH_VALUES) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(JUMP_BACKWARD); - CHECK_EVAL_BREAKER(); - assert(oparg <= INSTR_OFFSET()); - JUMPBY(-oparg); - #if ENABLE_SPECIALIZATION - this_instr[1].cache += (1 << OPTIMIZER_BITS_IN_COUNTER); - /* We are using unsigned values, but we really want signed values, so - * do the 2s complement comparison manually */ - uint16_t ucounter = this_instr[1].cache + (1 << 15); - uint16_t threshold = tstate->interp->optimizer_backedge_threshold + (1 << 15); - // Double-check that the opcode isn't instrumented or something: - if (ucounter > threshold && this_instr->op.code == JUMP_BACKWARD) { - OPT_STAT_INC(attempts); - int optimized = _PyOptimizer_BackEdge(frame, this_instr, next_instr, stack_pointer); - if (optimized < 0) goto error; - if (optimized) { - // Rewind and enter the executor: - assert(this_instr->op.code == ENTER_EXECUTOR); - next_instr = this_instr; - this_instr[1].cache &= ((1 << OPTIMIZER_BITS_IN_COUNTER) - 1); - } - else { - int backoff = this_instr[1].cache & ((1 << OPTIMIZER_BITS_IN_COUNTER) - 1); - if (backoff < MINIMUM_TIER2_BACKOFF) { - backoff = MINIMUM_TIER2_BACKOFF; - } - else if (backoff < 15 - OPTIMIZER_BITS_IN_COUNTER) { - backoff++; - } - assert(backoff <= 15 - OPTIMIZER_BITS_IN_COUNTER); - this_instr[1].cache = ((1 << 16) - ((1 << OPTIMIZER_BITS_IN_COUNTER) << backoff)) | backoff; - } + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_METHOD_WITH_VALUES); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + PyObject *owner; + PyObject *attr; + PyObject *self = NULL; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + owner = stack_pointer[-1]; + { + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); } - #endif /* ENABLE_SPECIALIZATION */ + // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT + { + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); + } + // _GUARD_KEYS_VERSION + { + uint32_t keys_version = read_u32(&this_instr[4].cache); + PyTypeObject *owner_cls = Py_TYPE(owner); + PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; + DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version, LOAD_ATTR); + } + // _LOAD_ATTR_METHOD_WITH_VALUES + { + PyObject *descr = read_obj(&this_instr[6].cache); + assert(oparg & 1); + /* Cached method object */ + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + attr = Py_NewRef(descr); + assert(_PyType_HasFeature(Py_TYPE(attr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + self = owner; + } + stack_pointer[-1] = attr; + if (1) stack_pointer[0] = self; + stack_pointer += (((1) ? 1 : 0)); DISPATCH(); } - TARGET(ENTER_EXECUTOR) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(ENTER_EXECUTOR); - TIER_ONE_ONLY - CHECK_EVAL_BREAKER(); - - PyCodeObject *code = _PyFrame_GetCode(frame); - _PyExecutorObject *executor = (_PyExecutorObject *)code->co_executors->executors[oparg&255]; - int original_oparg = executor->vm_data.oparg | (oparg & 0xfffff00); - JUMPBY(1-original_oparg); - frame->instr_ptr = next_instr; - Py_INCREF(executor); - if (executor->execute == _PyUopExecute) { - current_executor = (_PyUOpExecutorObject *)executor; - GOTO_TIER_TWO(); + TARGET(LOAD_ATTR_MODULE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_MODULE); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + PyObject *owner; + PyObject *attr; + PyObject *null = NULL; + /* Skip 1 cache entry */ + // _CHECK_ATTR_MODULE + owner = stack_pointer[-1]; + { + uint32_t type_version = read_u32(&this_instr[2].cache); + DEOPT_IF(!PyModule_CheckExact(owner), LOAD_ATTR); + PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; + assert(dict != NULL); + DEOPT_IF(dict->ma_keys->dk_version != type_version, LOAD_ATTR); } - frame = executor->execute(executor, frame, stack_pointer); - if (frame == NULL) { - frame = tstate->current_frame; - goto resume_with_error; + // _LOAD_ATTR_MODULE + { + uint16_t index = read_u16(&this_instr[4].cache); + PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; + assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); + assert(index < dict->ma_keys->dk_nentries); + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index; + attr = ep->me_value; + DEOPT_IF(attr == NULL, LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); + Py_INCREF(attr); + null = NULL; + Py_DECREF(owner); } - goto enter_tier_one; + /* Skip 5 cache entries */ + stack_pointer[-1] = attr; + if (oparg & 1) stack_pointer[0] = null; + stack_pointer += ((oparg & 1)); + DISPATCH(); } - TARGET(POP_JUMP_IF_TRUE) { + TARGET(LOAD_ATTR_NONDESCRIPTOR_NO_DICT) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(POP_JUMP_IF_TRUE); - PyObject *cond; - cond = stack_pointer[-1]; - assert(PyBool_Check(cond)); - int flag = Py_IsTrue(cond); - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif - JUMPBY(oparg * flag); - STACK_SHRINK(1); + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_NO_DICT); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + PyObject *owner; + PyObject *attr; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + owner = stack_pointer[-1]; + { + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + } + /* Skip 2 cache entries */ + // _LOAD_ATTR_NONDESCRIPTOR_NO_DICT + { + PyObject *descr = read_obj(&this_instr[6].cache); + assert((oparg & 1) == 0); + assert(Py_TYPE(owner)->tp_dictoffset == 0); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + Py_DECREF(owner); + attr = Py_NewRef(descr); + } + stack_pointer[-1] = attr; + stack_pointer += (((0) ? 1 : 0)); DISPATCH(); } - TARGET(POP_JUMP_IF_FALSE) { + TARGET(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(POP_JUMP_IF_FALSE); - PyObject *cond; - cond = stack_pointer[-1]; - assert(PyBool_Check(cond)); - int flag = Py_IsFalse(cond); - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif - JUMPBY(oparg * flag); - STACK_SHRINK(1); + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + PyObject *owner; + PyObject *attr; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + owner = stack_pointer[-1]; + { + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + } + // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT + { + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); + } + // _GUARD_KEYS_VERSION + { + uint32_t keys_version = read_u32(&this_instr[4].cache); + PyTypeObject *owner_cls = Py_TYPE(owner); + PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; + DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version, LOAD_ATTR); + } + // _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES + { + PyObject *descr = read_obj(&this_instr[6].cache); + assert((oparg & 1) == 0); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + Py_DECREF(owner); + attr = Py_NewRef(descr); + } + stack_pointer[-1] = attr; + stack_pointer += (((0) ? 1 : 0)); DISPATCH(); } - TARGET(POP_JUMP_IF_NONE) { + TARGET(LOAD_ATTR_PROPERTY) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(POP_JUMP_IF_NONE); - PyObject *value; - PyObject *b; - PyObject *cond; - // _IS_NONE - value = stack_pointer[-1]; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_PROPERTY); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + PyObject *owner; + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + uint32_t func_version = read_u32(&this_instr[4].cache); + PyObject *fget = read_obj(&this_instr[6].cache); + assert((oparg & 1) == 0); + DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); + PyTypeObject *cls = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR); + assert(Py_IS_TYPE(fget, &PyFunction_Type)); + PyFunctionObject *f = (PyFunctionObject *)fget; + assert(func_version != 0); + DEOPT_IF(f->func_version != func_version, LOAD_ATTR); + PyCodeObject *code = (PyCodeObject *)f->func_code; + assert(code->co_argcount == 1); + DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); + Py_INCREF(fget); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1); + // Manipulate stack directly because we exit with DISPATCH_INLINED(). + STACK_SHRINK(1); + new_frame->localsplus[0] = owner; + frame->return_offset = (uint16_t)(next_instr - this_instr); + DISPATCH_INLINED(new_frame); + } + + TARGET(LOAD_ATTR_SLOT) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_SLOT); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + PyObject *owner; + PyObject *attr; + PyObject *null = NULL; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + owner = stack_pointer[-1]; { - if (Py_IsNone(value)) { - b = Py_True; - } - else { - b = Py_False; - Py_DECREF(value); - } + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); } - // _POP_JUMP_IF_TRUE - cond = b; + // _LOAD_ATTR_SLOT { - assert(PyBool_Check(cond)); - int flag = Py_IsTrue(cond); - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif - JUMPBY(oparg * flag); + uint16_t index = read_u16(&this_instr[4].cache); + char *addr = (char *)owner + index; + attr = *(PyObject **)addr; + DEOPT_IF(attr == NULL, LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); + Py_INCREF(attr); + null = NULL; + Py_DECREF(owner); } - STACK_SHRINK(1); + /* Skip 5 cache entries */ + stack_pointer[-1] = attr; + if (oparg & 1) stack_pointer[0] = null; + stack_pointer += ((oparg & 1)); DISPATCH(); } - TARGET(POP_JUMP_IF_NOT_NONE) { + TARGET(LOAD_ATTR_WITH_HINT) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(POP_JUMP_IF_NOT_NONE); - PyObject *value; - PyObject *b; - PyObject *cond; - // _IS_NONE - value = stack_pointer[-1]; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_WITH_HINT); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + PyObject *owner; + PyObject *attr; + PyObject *null = NULL; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + owner = stack_pointer[-1]; { - if (Py_IsNone(value)) { - b = Py_True; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + } + // _CHECK_ATTR_WITH_HINT + { + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + DEOPT_IF(_PyDictOrValues_IsValues(dorv), LOAD_ATTR); + PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + DEOPT_IF(dict == NULL, LOAD_ATTR); + assert(PyDict_CheckExact((PyObject *)dict)); + } + // _LOAD_ATTR_WITH_HINT + { + uint16_t hint = read_u16(&this_instr[4].cache); + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); + if (DK_IS_UNICODE(dict->ma_keys)) { + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; + DEOPT_IF(ep->me_key != name, LOAD_ATTR); + attr = ep->me_value; } else { - b = Py_False; - Py_DECREF(value); + PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + hint; + DEOPT_IF(ep->me_key != name, LOAD_ATTR); + attr = ep->me_value; } + DEOPT_IF(attr == NULL, LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); + Py_INCREF(attr); + null = NULL; + Py_DECREF(owner); } - // _POP_JUMP_IF_FALSE - cond = b; - { - assert(PyBool_Check(cond)); - int flag = Py_IsFalse(cond); - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif - JUMPBY(oparg * flag); - } - STACK_SHRINK(1); + /* Skip 5 cache entries */ + stack_pointer[-1] = attr; + if (oparg & 1) stack_pointer[0] = null; + stack_pointer += ((oparg & 1)); DISPATCH(); } - TARGET(JUMP_BACKWARD_NO_INTERRUPT) { + TARGET(LOAD_BUILD_CLASS) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(JUMP_BACKWARD_NO_INTERRUPT); - /* This bytecode is used in the `yield from` or `await` loop. - * If there is an interrupt, we want it handled in the innermost - * generator or coroutine, so we deliberately do not check it here. - * (see bpo-30039). - */ - JUMPBY(-oparg); + INSTRUCTION_STATS(LOAD_BUILD_CLASS); + PyObject *bc; + if (PyMapping_GetOptionalItem(BUILTINS(), &_Py_ID(__build_class__), &bc) < 0) goto error; + if (bc == NULL) { + _PyErr_SetString(tstate, PyExc_NameError, + "__build_class__ not found"); + if (true) goto error; + } + stack_pointer[0] = bc; + stack_pointer += 1; DISPATCH(); } - TARGET(GET_LEN) { + TARGET(LOAD_CONST) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(GET_LEN); - PyObject *obj; - PyObject *len_o; - obj = stack_pointer[-1]; - // PUSH(len(TOS)) - Py_ssize_t len_i = PyObject_Length(obj); - if (len_i < 0) goto error; - len_o = PyLong_FromSsize_t(len_i); - if (len_o == NULL) goto error; - STACK_GROW(1); - stack_pointer[-1] = len_o; + INSTRUCTION_STATS(LOAD_CONST); + PyObject *value; + value = GETITEM(FRAME_CO_CONSTS, oparg); + Py_INCREF(value); + stack_pointer[0] = value; + stack_pointer += 1; DISPATCH(); } - TARGET(MATCH_CLASS) { + TARGET(LOAD_DEREF) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(MATCH_CLASS); - PyObject *names; - PyObject *type; - PyObject *subject; - PyObject *attrs; - names = stack_pointer[-1]; - type = stack_pointer[-2]; - subject = stack_pointer[-3]; - // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or - // None on failure. - assert(PyTuple_CheckExact(names)); - attrs = _PyEval_MatchClass(tstate, subject, type, oparg, names); - Py_DECREF(subject); - Py_DECREF(type); - Py_DECREF(names); - if (attrs) { - assert(PyTuple_CheckExact(attrs)); // Success! - } - else { - if (_PyErr_Occurred(tstate)) goto pop_3_error; - attrs = Py_None; // Failure! + INSTRUCTION_STATS(LOAD_DEREF); + PyObject *value; + PyObject *cell = GETLOCAL(oparg); + value = PyCell_GET(cell); + if (value == NULL) { + _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); + if (true) goto error; } - STACK_SHRINK(2); - stack_pointer[-1] = attrs; + Py_INCREF(value); + stack_pointer[0] = value; + stack_pointer += 1; DISPATCH(); } - TARGET(MATCH_MAPPING) { + TARGET(LOAD_FAST) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(MATCH_MAPPING); - PyObject *subject; - PyObject *res; - subject = stack_pointer[-1]; - int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; - res = match ? Py_True : Py_False; - STACK_GROW(1); - stack_pointer[-1] = res; + INSTRUCTION_STATS(LOAD_FAST); + PyObject *value; + value = GETLOCAL(oparg); + assert(value != NULL); + Py_INCREF(value); + stack_pointer[0] = value; + stack_pointer += 1; DISPATCH(); } - TARGET(MATCH_SEQUENCE) { + TARGET(LOAD_FAST_AND_CLEAR) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(MATCH_SEQUENCE); - PyObject *subject; - PyObject *res; - subject = stack_pointer[-1]; - int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; - res = match ? Py_True : Py_False; - STACK_GROW(1); - stack_pointer[-1] = res; + INSTRUCTION_STATS(LOAD_FAST_AND_CLEAR); + PyObject *value; + value = GETLOCAL(oparg); + // do not use SETLOCAL here, it decrefs the old value + GETLOCAL(oparg) = NULL; + stack_pointer[0] = value; + stack_pointer += 1; DISPATCH(); } - TARGET(MATCH_KEYS) { + TARGET(LOAD_FAST_CHECK) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(MATCH_KEYS); - PyObject *keys; - PyObject *subject; - PyObject *values_or_none; - keys = stack_pointer[-1]; - subject = stack_pointer[-2]; - // On successful match, PUSH(values). Otherwise, PUSH(None). - values_or_none = _PyEval_MatchKeys(tstate, subject, keys); - if (values_or_none == NULL) goto error; - STACK_GROW(1); - stack_pointer[-1] = values_or_none; + INSTRUCTION_STATS(LOAD_FAST_CHECK); + PyObject *value; + value = GETLOCAL(oparg); + if (value == NULL) goto unbound_local_error; + Py_INCREF(value); + stack_pointer[0] = value; + stack_pointer += 1; DISPATCH(); } - TARGET(GET_ITER) { + TARGET(LOAD_FAST_LOAD_FAST) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(GET_ITER); - PyObject *iterable; - PyObject *iter; - iterable = stack_pointer[-1]; - /* before: [obj]; after [getiter(obj)] */ - iter = PyObject_GetIter(iterable); - Py_DECREF(iterable); - if (iter == NULL) goto pop_1_error; - stack_pointer[-1] = iter; + INSTRUCTION_STATS(LOAD_FAST_LOAD_FAST); + PyObject *value1; + PyObject *value2; + uint32_t oparg1 = oparg >> 4; + uint32_t oparg2 = oparg & 15; + value1 = GETLOCAL(oparg1); + value2 = GETLOCAL(oparg2); + Py_INCREF(value1); + Py_INCREF(value2); + stack_pointer[0] = value1; + stack_pointer[1] = value2; + stack_pointer += 2; DISPATCH(); } - TARGET(GET_YIELD_FROM_ITER) { + TARGET(LOAD_FROM_DICT_OR_DEREF) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(GET_YIELD_FROM_ITER); - PyObject *iterable; - PyObject *iter; - iterable = stack_pointer[-1]; - /* before: [obj]; after [getiter(obj)] */ - if (PyCoro_CheckExact(iterable)) { - /* `iterable` is a coroutine */ - if (!(_PyFrame_GetCode(frame)->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) { - /* and it is used in a 'yield from' expression of a - regular generator. */ - _PyErr_SetString(tstate, PyExc_TypeError, - "cannot 'yield from' a coroutine object " - "in a non-coroutine generator"); + INSTRUCTION_STATS(LOAD_FROM_DICT_OR_DEREF); + PyObject *class_dict; + PyObject *value; + class_dict = stack_pointer[-1]; + PyObject *name; + assert(class_dict); + assert(oparg >= 0 && oparg < _PyFrame_GetCode(frame)->co_nlocalsplus); + name = PyTuple_GET_ITEM(_PyFrame_GetCode(frame)->co_localsplusnames, oparg); + if (PyMapping_GetOptionalItem(class_dict, name, &value) < 0) { + GOTO_ERROR(error); + } + if (!value) { + PyObject *cell = GETLOCAL(oparg); + value = PyCell_GET(cell); + if (value == NULL) { + _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); GOTO_ERROR(error); } - iter = iterable; + Py_INCREF(value); } - else if (PyGen_CheckExact(iterable)) { - iter = iterable; + Py_DECREF(class_dict); + stack_pointer[-1] = value; + DISPATCH(); + } + + TARGET(LOAD_FROM_DICT_OR_GLOBALS) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_FROM_DICT_OR_GLOBALS); + PyObject *mod_or_class_dict; + PyObject *v; + mod_or_class_dict = stack_pointer[-1]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) { + GOTO_ERROR(error); } - else { - /* `iterable` is not a generator. */ - iter = PyObject_GetIter(iterable); - if (iter == NULL) { + if (v == NULL) { + if (PyDict_GetItemRef(GLOBALS(), name, &v) < 0) { GOTO_ERROR(error); } - Py_DECREF(iterable); + if (v == NULL) { + if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) { + GOTO_ERROR(error); + } + if (v == NULL) { + _PyEval_FormatExcCheckArg( + tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + GOTO_ERROR(error); + } + } } - stack_pointer[-1] = iter; + Py_DECREF(mod_or_class_dict); + stack_pointer[-1] = v; DISPATCH(); } - TARGET(FOR_ITER) { + TARGET(LOAD_GLOBAL) { frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(FOR_ITER); - PREDICTED(FOR_ITER); - _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); - PyObject *iter; - PyObject *next; - // _SPECIALIZE_FOR_ITER - iter = stack_pointer[-1]; + next_instr += 5; + INSTRUCTION_STATS(LOAD_GLOBAL); + PREDICTED(LOAD_GLOBAL); + _Py_CODEUNIT *this_instr = next_instr - 5; + PyObject *res; + PyObject *null = NULL; + // _SPECIALIZE_LOAD_GLOBAL { uint16_t counter = read_u16(&this_instr[1].cache); TIER_ONE_ONLY #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); next_instr = this_instr; - _Py_Specialize_ForIter(iter, next_instr, oparg); + _Py_Specialize_LoadGlobal(GLOBALS(), BUILTINS(), next_instr, name); DISPATCH_SAME_OPARG(); } - STAT_INC(FOR_ITER, deferred); + STAT_INC(LOAD_GLOBAL, deferred); DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); #endif /* ENABLE_SPECIALIZATION */ } - // _FOR_ITER + // _LOAD_GLOBAL { - /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ - next = (*Py_TYPE(iter)->tp_iternext)(iter); - if (next == NULL) { - if (_PyErr_Occurred(tstate)) { - if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { - GOTO_ERROR(error); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); + if (PyDict_CheckExact(GLOBALS()) + && PyDict_CheckExact(BUILTINS())) + { + res = _PyDict_LoadGlobal((PyDictObject *)GLOBALS(), + (PyDictObject *)BUILTINS(), + name); + if (res == NULL) { + if (!_PyErr_Occurred(tstate)) { + /* _PyDict_LoadGlobal() returns NULL without raising + * an exception if the key doesn't exist */ + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); } - monitor_raise(tstate, frame, this_instr); - _PyErr_Clear(tstate); - } - /* iterator ended normally */ - assert(next_instr[oparg].op.code == END_FOR || - next_instr[oparg].op.code == INSTRUMENTED_END_FOR); - Py_DECREF(iter); - STACK_SHRINK(1); - /* Jump forward oparg, then skip following END_FOR instruction */ - JUMPBY(oparg + 1); - DISPATCH(); - } - // Common case: no jump, leave it to the code generator - } - STACK_GROW(1); - stack_pointer[-1] = next; - DISPATCH(); - } - - TARGET(INSTRUMENTED_FOR_ITER) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(INSTRUMENTED_FOR_ITER); - _Py_CODEUNIT *target; - PyObject *iter = TOP(); - PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); - if (next != NULL) { - PUSH(next); - target = next_instr; - } - else { - if (_PyErr_Occurred(tstate)) { - if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { - GOTO_ERROR(error); - } - monitor_raise(tstate, frame, this_instr); - _PyErr_Clear(tstate); - } - /* iterator ended normally */ - assert(next_instr[oparg].op.code == END_FOR || - next_instr[oparg].op.code == INSTRUMENTED_END_FOR); - STACK_SHRINK(1); - Py_DECREF(iter); - /* Skip END_FOR */ - target = next_instr + oparg + 1; - } - INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH); - DISPATCH(); - } - - TARGET(FOR_ITER_LIST) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(FOR_ITER_LIST); - PyObject *iter; - PyObject *next; - // _ITER_CHECK_LIST - iter = stack_pointer[-1]; - { - DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, FOR_ITER); - } - // _ITER_JUMP_LIST - { - _PyListIterObject *it = (_PyListIterObject *)iter; - assert(Py_TYPE(iter) == &PyListIter_Type); - STAT_INC(FOR_ITER, hit); - PyListObject *seq = it->it_seq; - if (seq == NULL || it->it_index >= PyList_GET_SIZE(seq)) { - if (seq != NULL) { - it->it_seq = NULL; - Py_DECREF(seq); + if (true) goto error; } - Py_DECREF(iter); - STACK_SHRINK(1); - /* Jump forward oparg, then skip following END_FOR instruction */ - JUMPBY(oparg + 1); - DISPATCH(); + Py_INCREF(res); } - } - // _ITER_NEXT_LIST - { - _PyListIterObject *it = (_PyListIterObject *)iter; - assert(Py_TYPE(iter) == &PyListIter_Type); - PyListObject *seq = it->it_seq; - assert(seq); - assert(it->it_index < PyList_GET_SIZE(seq)); - next = Py_NewRef(PyList_GET_ITEM(seq, it->it_index++)); - } - STACK_GROW(1); - stack_pointer[-1] = next; - DISPATCH(); - } - - TARGET(FOR_ITER_TUPLE) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(FOR_ITER_TUPLE); - PyObject *iter; - PyObject *next; - // _ITER_CHECK_TUPLE - iter = stack_pointer[-1]; - { - DEOPT_IF(Py_TYPE(iter) != &PyTupleIter_Type, FOR_ITER); - } - // _ITER_JUMP_TUPLE - { - _PyTupleIterObject *it = (_PyTupleIterObject *)iter; - assert(Py_TYPE(iter) == &PyTupleIter_Type); - STAT_INC(FOR_ITER, hit); - PyTupleObject *seq = it->it_seq; - if (seq == NULL || it->it_index >= PyTuple_GET_SIZE(seq)) { - if (seq != NULL) { - it->it_seq = NULL; - Py_DECREF(seq); - } - Py_DECREF(iter); - STACK_SHRINK(1); - /* Jump forward oparg, then skip following END_FOR instruction */ - JUMPBY(oparg + 1); - DISPATCH(); + else { + /* Slow-path if globals or builtins is not a dict */ + /* namespace 1: globals */ + if (PyMapping_GetOptionalItem(GLOBALS(), name, &res) < 0) goto error; + if (res == NULL) { + /* namespace 2: builtins */ + if (PyMapping_GetOptionalItem(BUILTINS(), name, &res) < 0) goto error; + if (res == NULL) { + _PyEval_FormatExcCheckArg( + tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + if (true) goto error; + } + } } + null = NULL; } - // _ITER_NEXT_TUPLE - { - _PyTupleIterObject *it = (_PyTupleIterObject *)iter; - assert(Py_TYPE(iter) == &PyTupleIter_Type); - PyTupleObject *seq = it->it_seq; - assert(seq); - assert(it->it_index < PyTuple_GET_SIZE(seq)); - next = Py_NewRef(PyTuple_GET_ITEM(seq, it->it_index++)); - } - STACK_GROW(1); - stack_pointer[-1] = next; + stack_pointer[0] = res; + if (oparg & 1) stack_pointer[1] = null; + stack_pointer += 1 + ((oparg & 1)); DISPATCH(); } - TARGET(FOR_ITER_RANGE) { - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(FOR_ITER_RANGE); - PyObject *iter; - PyObject *next; - // _ITER_CHECK_RANGE - iter = stack_pointer[-1]; + TARGET(LOAD_GLOBAL_BUILTIN) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 5; + INSTRUCTION_STATS(LOAD_GLOBAL_BUILTIN); + static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size"); + PyObject *res; + PyObject *null = NULL; + /* Skip 1 cache entry */ + // _GUARD_GLOBALS_VERSION { - _PyRangeIterObject *r = (_PyRangeIterObject *)iter; - DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); + uint16_t version = read_u16(&this_instr[2].cache); + PyDictObject *dict = (PyDictObject *)GLOBALS(); + DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL); + DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); + assert(DK_IS_UNICODE(dict->ma_keys)); } - // _ITER_JUMP_RANGE + // _GUARD_BUILTINS_VERSION { - _PyRangeIterObject *r = (_PyRangeIterObject *)iter; - assert(Py_TYPE(r) == &PyRangeIter_Type); - STAT_INC(FOR_ITER, hit); - if (r->len <= 0) { - STACK_SHRINK(1); - Py_DECREF(r); - // Jump over END_FOR instruction. - JUMPBY(oparg + 1); - DISPATCH(); - } + uint16_t version = read_u16(&this_instr[3].cache); + PyDictObject *dict = (PyDictObject *)BUILTINS(); + DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL); + DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); + assert(DK_IS_UNICODE(dict->ma_keys)); } - // _ITER_NEXT_RANGE + // _LOAD_GLOBAL_BUILTINS { - _PyRangeIterObject *r = (_PyRangeIterObject *)iter; - assert(Py_TYPE(r) == &PyRangeIter_Type); - assert(r->len > 0); - long value = r->start; - r->start = value + r->step; - r->len--; - next = PyLong_FromLong(value); - if (next == NULL) goto error; + uint16_t index = read_u16(&this_instr[4].cache); + PyDictObject *bdict = (PyDictObject *)BUILTINS(); + PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(bdict->ma_keys); + res = entries[index].me_value; + DEOPT_IF(res == NULL, LOAD_GLOBAL); + Py_INCREF(res); + STAT_INC(LOAD_GLOBAL, hit); + null = NULL; } - STACK_GROW(1); - stack_pointer[-1] = next; + stack_pointer[0] = res; + if (oparg & 1) stack_pointer[1] = null; + stack_pointer += 1 + ((oparg & 1)); DISPATCH(); } - TARGET(FOR_ITER_GEN) { + TARGET(LOAD_GLOBAL_MODULE) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(FOR_ITER_GEN); - PyObject *iter; - iter = stack_pointer[-1]; - DEOPT_IF(tstate->interp->eval_frame, FOR_ITER); - PyGenObject *gen = (PyGenObject *)iter; - DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); - DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER); - STAT_INC(FOR_ITER, hit); - _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; - _PyFrame_StackPush(gen_frame, Py_None); - gen->gi_frame_state = FRAME_EXECUTING; - gen->gi_exc_state.previous_item = tstate->exc_info; - tstate->exc_info = &gen->gi_exc_state; - assert(next_instr[oparg].op.code == END_FOR || - next_instr[oparg].op.code == INSTRUMENTED_END_FOR); - assert(next_instr - this_instr + oparg <= UINT16_MAX); - frame->return_offset = (uint16_t)(next_instr - this_instr + oparg); - DISPATCH_INLINED(gen_frame); + next_instr += 5; + INSTRUCTION_STATS(LOAD_GLOBAL_MODULE); + static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size"); + PyObject *res; + PyObject *null = NULL; + /* Skip 1 cache entry */ + // _GUARD_GLOBALS_VERSION + { + uint16_t version = read_u16(&this_instr[2].cache); + PyDictObject *dict = (PyDictObject *)GLOBALS(); + DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL); + DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); + assert(DK_IS_UNICODE(dict->ma_keys)); + } + /* Skip 1 cache entry */ + // _LOAD_GLOBAL_MODULE + { + uint16_t index = read_u16(&this_instr[4].cache); + PyDictObject *dict = (PyDictObject *)GLOBALS(); + PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys); + res = entries[index].me_value; + DEOPT_IF(res == NULL, LOAD_GLOBAL); + Py_INCREF(res); + STAT_INC(LOAD_GLOBAL, hit); + null = NULL; + } + stack_pointer[0] = res; + if (oparg & 1) stack_pointer[1] = null; + stack_pointer += 1 + ((oparg & 1)); + DISPATCH(); } - TARGET(BEFORE_ASYNC_WITH) { + TARGET(LOAD_LOCALS) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(BEFORE_ASYNC_WITH); - PyObject *mgr; - PyObject *exit; - PyObject *res; - mgr = stack_pointer[-1]; - PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__aenter__)); - if (enter == NULL) { - if (!_PyErr_Occurred(tstate)) { - _PyErr_Format(tstate, PyExc_TypeError, - "'%.200s' object does not support the " - "asynchronous context manager protocol", - Py_TYPE(mgr)->tp_name); - } - GOTO_ERROR(error); + INSTRUCTION_STATS(LOAD_LOCALS); + PyObject *locals; + locals = LOCALS(); + if (locals == NULL) { + _PyErr_SetString(tstate, PyExc_SystemError, + "no locals found"); + if (true) goto error; } - exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__)); - if (exit == NULL) { - if (!_PyErr_Occurred(tstate)) { - _PyErr_Format(tstate, PyExc_TypeError, - "'%.200s' object does not support the " - "asynchronous context manager protocol " - "(missed __aexit__ method)", - Py_TYPE(mgr)->tp_name); - } - Py_DECREF(enter); + Py_INCREF(locals); + stack_pointer[0] = locals; + stack_pointer += 1; + DISPATCH(); + } + + TARGET(LOAD_NAME) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_NAME); + PyObject *v; + PyObject *mod_or_class_dict = LOCALS(); + if (mod_or_class_dict == NULL) { + _PyErr_SetString(tstate, PyExc_SystemError, + "no locals found"); + if (true) goto error; + } + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) { GOTO_ERROR(error); } - Py_DECREF(mgr); - res = _PyObject_CallNoArgsTstate(tstate, enter); - Py_DECREF(enter); - if (res == NULL) { - Py_DECREF(exit); - if (true) goto pop_1_error; + if (v == NULL) { + if (PyDict_GetItemRef(GLOBALS(), name, &v) < 0) { + GOTO_ERROR(error); + } + if (v == NULL) { + if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) { + GOTO_ERROR(error); + } + if (v == NULL) { + _PyEval_FormatExcCheckArg( + tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + GOTO_ERROR(error); + } + } } - STACK_GROW(1); - stack_pointer[-2] = exit; - stack_pointer[-1] = res; + stack_pointer[0] = v; + stack_pointer += 1; DISPATCH(); } - TARGET(BEFORE_WITH) { + TARGET(LOAD_SUPER_ATTR) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(BEFORE_WITH); - PyObject *mgr; - PyObject *exit; - PyObject *res; - mgr = stack_pointer[-1]; - /* pop the context manager, push its __exit__ and the - * value returned from calling its __enter__ - */ - PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__enter__)); - if (enter == NULL) { - if (!_PyErr_Occurred(tstate)) { - _PyErr_Format(tstate, PyExc_TypeError, - "'%.200s' object does not support the " - "context manager protocol", - Py_TYPE(mgr)->tp_name); + next_instr += 2; + INSTRUCTION_STATS(LOAD_SUPER_ATTR); + PREDICTED(LOAD_SUPER_ATTR); + _Py_CODEUNIT *this_instr = next_instr - 2; + PyObject *class; + PyObject *global_super; + PyObject *self; + PyObject *attr; + PyObject *null = NULL; + // _SPECIALIZE_LOAD_SUPER_ATTR + class = stack_pointer[-2]; + global_super = stack_pointer[-3]; + { + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + int load_method = oparg & 1; + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + next_instr = this_instr; + _Py_Specialize_LoadSuperAttr(global_super, class, next_instr, load_method); + DISPATCH_SAME_OPARG(); } - GOTO_ERROR(error); + STAT_INC(LOAD_SUPER_ATTR, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ } - exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__)); - if (exit == NULL) { - if (!_PyErr_Occurred(tstate)) { - _PyErr_Format(tstate, PyExc_TypeError, - "'%.200s' object does not support the " - "context manager protocol " - "(missed __exit__ method)", - Py_TYPE(mgr)->tp_name); + // _LOAD_SUPER_ATTR + self = stack_pointer[-1]; + { + TIER_ONE_ONLY + if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { + PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, global_super, arg); + if (err) goto pop_3_error; + } + // we make no attempt to optimize here; specializations should + // handle any case whose performance we care about + PyObject *stack[] = {class, self}; + PyObject *super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL); + if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { + PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; + if (super == NULL) { + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, global_super, arg); + } + else { + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, global_super, arg); + if (err < 0) { + Py_CLEAR(super); + } + } } - Py_DECREF(enter); - GOTO_ERROR(error); - } - Py_DECREF(mgr); - res = _PyObject_CallNoArgsTstate(tstate, enter); - Py_DECREF(enter); - if (res == NULL) { - Py_DECREF(exit); - if (true) goto pop_1_error; + Py_DECREF(global_super); + Py_DECREF(class); + Py_DECREF(self); + if (super == NULL) goto pop_3_error; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + attr = PyObject_GetAttr(super, name); + Py_DECREF(super); + if (attr == NULL) goto pop_3_error; + null = NULL; } - STACK_GROW(1); - stack_pointer[-2] = exit; - stack_pointer[-1] = res; + stack_pointer[-3] = attr; + if (oparg & 1) stack_pointer[-2] = null; + stack_pointer += -2 + ((oparg & 1)); DISPATCH(); } - TARGET(WITH_EXCEPT_START) { + TARGET(LOAD_SUPER_ATTR_ATTR) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(WITH_EXCEPT_START); - PyObject *val; - PyObject *lasti; - PyObject *exit_func; - PyObject *res; - val = stack_pointer[-1]; - lasti = stack_pointer[-3]; - exit_func = stack_pointer[-4]; - /* At the top of the stack are 4 values: - - val: TOP = exc_info() - - unused: SECOND = previous exception - - lasti: THIRD = lasti of exception in exc_info() - - exit_func: FOURTH = the context.__exit__ bound method - We call FOURTH(type(TOP), TOP, GetTraceback(TOP)). - Then we push the __exit__ return value. - */ - PyObject *exc, *tb; - - assert(val && PyExceptionInstance_Check(val)); - exc = PyExceptionInstance_Class(val); - tb = PyException_GetTraceback(val); - if (tb == NULL) { - tb = Py_None; - } - else { - Py_DECREF(tb); - } - assert(PyLong_Check(lasti)); - (void)lasti; // Shut up compiler warning if asserts are off - PyObject *stack[4] = {NULL, exc, val, tb}; - res = PyObject_Vectorcall(exit_func, stack + 1, - 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); - if (res == NULL) goto error; - STACK_GROW(1); - stack_pointer[-1] = res; + next_instr += 2; + INSTRUCTION_STATS(LOAD_SUPER_ATTR_ATTR); + static_assert(INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR == 1, "incorrect cache size"); + PyObject *self; + PyObject *class; + PyObject *global_super; + PyObject *attr; + self = stack_pointer[-1]; + class = stack_pointer[-2]; + global_super = stack_pointer[-3]; + assert(!(oparg & 1)); + DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR); + DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR); + STAT_INC(LOAD_SUPER_ATTR, hit); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + attr = _PySuper_Lookup((PyTypeObject *)class, self, name, NULL); + Py_DECREF(global_super); + Py_DECREF(class); + Py_DECREF(self); + if (attr == NULL) goto pop_3_error; + stack_pointer[-3] = attr; + stack_pointer += -2 + (((0) ? 1 : 0)); DISPATCH(); } - TARGET(PUSH_EXC_INFO) { + TARGET(LOAD_SUPER_ATTR_METHOD) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(PUSH_EXC_INFO); - PyObject *new_exc; - PyObject *prev_exc; - new_exc = stack_pointer[-1]; - _PyErr_StackItem *exc_info = tstate->exc_info; - if (exc_info->exc_value != NULL) { - prev_exc = exc_info->exc_value; + next_instr += 2; + INSTRUCTION_STATS(LOAD_SUPER_ATTR_METHOD); + static_assert(INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR == 1, "incorrect cache size"); + PyObject *self; + PyObject *class; + PyObject *global_super; + PyObject *attr; + PyObject *self_or_null; + self = stack_pointer[-1]; + class = stack_pointer[-2]; + global_super = stack_pointer[-3]; + assert(oparg & 1); + DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR); + DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR); + STAT_INC(LOAD_SUPER_ATTR, hit); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + PyTypeObject *cls = (PyTypeObject *)class; + int method_found = 0; + attr = _PySuper_Lookup(cls, self, name, + Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); + Py_DECREF(global_super); + Py_DECREF(class); + if (attr == NULL) { + Py_DECREF(self); + if (true) goto pop_3_error; } - else { - prev_exc = Py_None; + if (method_found) { + self_or_null = self; // transfer ownership + } else { + Py_DECREF(self); + self_or_null = NULL; } - assert(PyExceptionInstance_Check(new_exc)); - exc_info->exc_value = Py_NewRef(new_exc); - STACK_GROW(1); - stack_pointer[-2] = prev_exc; - stack_pointer[-1] = new_exc; + stack_pointer[-3] = attr; + stack_pointer[-2] = self_or_null; + stack_pointer += -1; DISPATCH(); } - TARGET(LOAD_ATTR_METHOD_WITH_VALUES) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_METHOD_WITH_VALUES); - PyObject *owner; - PyObject *attr; - PyObject *self; - // _GUARD_TYPE_VERSION - owner = stack_pointer[-1]; - { - uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); - } - // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT - { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); - } - // _GUARD_KEYS_VERSION - { - uint32_t keys_version = read_u32(&this_instr[4].cache); - PyTypeObject *owner_cls = Py_TYPE(owner); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version, LOAD_ATTR); - } - // _LOAD_ATTR_METHOD_WITH_VALUES - { - PyObject *descr = read_obj(&this_instr[6].cache); - assert(oparg & 1); - /* Cached method object */ - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - attr = Py_NewRef(descr); - assert(_PyType_HasFeature(Py_TYPE(attr), Py_TPFLAGS_METHOD_DESCRIPTOR)); - self = owner; + TARGET(MAKE_CELL) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MAKE_CELL); + // "initial" is probably NULL but not if it's an arg (or set + // via PyFrame_LocalsToFast() before MAKE_CELL has run). + PyObject *initial = GETLOCAL(oparg); + PyObject *cell = PyCell_New(initial); + if (cell == NULL) { + GOTO_ERROR(error); } - STACK_GROW(1); - stack_pointer[-2] = attr; - stack_pointer[-1] = self; + SETLOCAL(oparg, cell); DISPATCH(); } - TARGET(LOAD_ATTR_METHOD_NO_DICT) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_METHOD_NO_DICT); - PyObject *owner; - PyObject *attr; - PyObject *self; - // _GUARD_TYPE_VERSION - owner = stack_pointer[-1]; - { - uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); - } - // _LOAD_ATTR_METHOD_NO_DICT - { - PyObject *descr = read_obj(&this_instr[6].cache); - assert(oparg & 1); - assert(Py_TYPE(owner)->tp_dictoffset == 0); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); - attr = Py_NewRef(descr); - self = owner; + TARGET(MAKE_FUNCTION) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MAKE_FUNCTION); + PyObject *codeobj; + PyObject *func; + codeobj = stack_pointer[-1]; + PyFunctionObject *func_obj = (PyFunctionObject *) + PyFunction_New(codeobj, GLOBALS()); + Py_DECREF(codeobj); + if (func_obj == NULL) { + GOTO_ERROR(error); } - STACK_GROW(1); - stack_pointer[-2] = attr; - stack_pointer[-1] = self; + _PyFunction_SetVersion( + func_obj, ((PyCodeObject *)codeobj)->co_version); + func = (PyObject *)func_obj; + stack_pointer[-1] = func; DISPATCH(); } - TARGET(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES); - PyObject *owner; - PyObject *attr; - // _GUARD_TYPE_VERSION - owner = stack_pointer[-1]; - { - uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); - } - // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT - { - assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), LOAD_ATTR); - } - // _GUARD_KEYS_VERSION - { - uint32_t keys_version = read_u32(&this_instr[4].cache); - PyTypeObject *owner_cls = Py_TYPE(owner); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version, LOAD_ATTR); - } - // _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES - { - PyObject *descr = read_obj(&this_instr[6].cache); - assert((oparg & 1) == 0); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - Py_DECREF(owner); - attr = Py_NewRef(descr); - } - stack_pointer[-1] = attr; + TARGET(MAP_ADD) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MAP_ADD); + PyObject *value; + PyObject *key; + PyObject *dict; + value = stack_pointer[-1]; + key = stack_pointer[-2]; + dict = stack_pointer[-3 - (oparg - 1)]; + assert(PyDict_CheckExact(dict)); + /* dict[key] = value */ + // Do not DECREF INPUTS because the function steals the references + if (_PyDict_SetItem_Take2((PyDictObject *)dict, key, value) != 0) goto pop_2_error; + stack_pointer += -2; DISPATCH(); } - TARGET(LOAD_ATTR_NONDESCRIPTOR_NO_DICT) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_NO_DICT); - PyObject *owner; - PyObject *attr; - // _GUARD_TYPE_VERSION - owner = stack_pointer[-1]; - { - uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + TARGET(MATCH_CLASS) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MATCH_CLASS); + PyObject *names; + PyObject *type; + PyObject *subject; + PyObject *attrs; + names = stack_pointer[-1]; + type = stack_pointer[-2]; + subject = stack_pointer[-3]; + // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or + // None on failure. + assert(PyTuple_CheckExact(names)); + attrs = _PyEval_MatchClass(tstate, subject, type, oparg, names); + Py_DECREF(subject); + Py_DECREF(type); + Py_DECREF(names); + if (attrs) { + assert(PyTuple_CheckExact(attrs)); // Success! } - // _LOAD_ATTR_NONDESCRIPTOR_NO_DICT - { - PyObject *descr = read_obj(&this_instr[6].cache); - assert((oparg & 1) == 0); - assert(Py_TYPE(owner)->tp_dictoffset == 0); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - Py_DECREF(owner); - attr = Py_NewRef(descr); + else { + if (_PyErr_Occurred(tstate)) goto pop_3_error; + // Error! + attrs = Py_None; // Failure! } - stack_pointer[-1] = attr; + stack_pointer[-3] = attrs; + stack_pointer += -2; DISPATCH(); } - TARGET(LOAD_ATTR_METHOD_LAZY_DICT) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 10; - INSTRUCTION_STATS(LOAD_ATTR_METHOD_LAZY_DICT); - PyObject *owner; - PyObject *attr; - PyObject *self; - // _GUARD_TYPE_VERSION - owner = stack_pointer[-1]; - { - uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); - } - // _CHECK_ATTR_METHOD_LAZY_DICT - { - Py_ssize_t dictoffset = Py_TYPE(owner)->tp_dictoffset; - assert(dictoffset > 0); - PyObject *dict = *(PyObject **)((char *)owner + dictoffset); - /* This object has a __dict__, just not yet created */ - DEOPT_IF(dict != NULL, LOAD_ATTR); - } - // _LOAD_ATTR_METHOD_LAZY_DICT - { - PyObject *descr = read_obj(&this_instr[6].cache); - assert(oparg & 1); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); - attr = Py_NewRef(descr); - self = owner; - } - STACK_GROW(1); - stack_pointer[-2] = attr; - stack_pointer[-1] = self; + TARGET(MATCH_KEYS) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MATCH_KEYS); + PyObject *keys; + PyObject *subject; + PyObject *values_or_none; + keys = stack_pointer[-1]; + subject = stack_pointer[-2]; + // On successful match, PUSH(values). Otherwise, PUSH(None). + values_or_none = _PyEval_MatchKeys(tstate, subject, keys); + if (values_or_none == NULL) goto error; + stack_pointer[0] = values_or_none; + stack_pointer += 1; DISPATCH(); } - TARGET(INSTRUMENTED_CALL) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(INSTRUMENTED_CALL); - int is_meth = PEEK(oparg + 1) != NULL; - int total_args = oparg + is_meth; - PyObject *function = PEEK(oparg + 2); - PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : PEEK(total_args); - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_CALL, - frame, this_instr, function, arg); - if (err) goto error; - INCREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - GO_TO_INSTRUCTION(CALL); + TARGET(MATCH_MAPPING) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MATCH_MAPPING); + PyObject *subject; + PyObject *res; + subject = stack_pointer[-1]; + int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; + res = match ? Py_True : Py_False; + stack_pointer[0] = res; + stack_pointer += 1; + DISPATCH(); } - TARGET(CALL) { + TARGET(MATCH_SEQUENCE) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL); - PREDICTED(CALL); - _Py_CODEUNIT *this_instr = next_instr - 4; - static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; + next_instr += 1; + INSTRUCTION_STATS(MATCH_SEQUENCE); + PyObject *subject; PyObject *res; - // _SPECIALIZE_CALL - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - { - uint16_t counter = read_u16(&this_instr[1].cache); - TIER_ONE_ONLY - #if ENABLE_SPECIALIZATION - if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { - next_instr = this_instr; - _Py_Specialize_Call(callable, next_instr, oparg + (self_or_null != NULL)); - DISPATCH_SAME_OPARG(); - } - STAT_INC(CALL, deferred); - DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - #endif /* ENABLE_SPECIALIZATION */ - } - // _CALL + subject = stack_pointer[-1]; + int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; + res = match ? Py_True : Py_False; + stack_pointer[0] = res; + stack_pointer += 1; + DISPATCH(); + } + + TARGET(NOP) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(NOP); + DISPATCH(); + } + + TARGET(POP_EXCEPT) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(POP_EXCEPT); + PyObject *exc_value; + exc_value = stack_pointer[-1]; + _PyErr_StackItem *exc_info = tstate->exc_info; + Py_XSETREF(exc_info->exc_value, exc_value); + stack_pointer += -1; + DISPATCH(); + } + + TARGET(POP_JUMP_IF_FALSE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(POP_JUMP_IF_FALSE); + PyObject *cond; + cond = stack_pointer[-1]; + assert(PyBool_Check(cond)); + int flag = Py_IsFalse(cond); + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | flag; + #endif + JUMPBY(oparg * flag); + stack_pointer += -1; + DISPATCH(); + } + + TARGET(POP_JUMP_IF_NONE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(POP_JUMP_IF_NONE); + PyObject *value; + PyObject *b; + PyObject *cond; + // _IS_NONE + value = stack_pointer[-1]; { - // oparg counts all of the args, but *not* self: - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - else if (Py_TYPE(callable) == &PyMethod_Type) { - args--; - total_args++; - PyObject *self = ((PyMethodObject *)callable)->im_self; - args[0] = Py_NewRef(self); - PyObject *method = ((PyMethodObject *)callable)->im_func; - args[-1] = Py_NewRef(method); - Py_DECREF(callable); - callable = method; - } - // Check if the call can be inlined or not - if (Py_TYPE(callable) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && - ((PyFunctionObject *)callable)->vectorcall == _PyFunction_Vectorcall) - { - int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; - PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); - _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( - tstate, (PyFunctionObject *)callable, locals, - args, total_args, NULL - ); - // Manipulate stack directly since we leave using DISPATCH_INLINED(). - STACK_SHRINK(oparg + 2); - // The frame has stolen all the arguments from the stack, - // so there is no need to clean them up. - if (new_frame == NULL) { - GOTO_ERROR(error); - } - frame->return_offset = (uint16_t)(next_instr - this_instr); - DISPATCH_INLINED(new_frame); - } - /* Callable is not a normal Python function */ - res = PyObject_Vectorcall( - callable, args, - total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - NULL); - if (opcode == INSTRUMENTED_CALL) { - PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : args[0]; - if (res == NULL) { - _Py_call_instrumentation_exc2( - tstate, PY_MONITORING_EVENT_C_RAISE, - frame, this_instr, callable, arg); - } - else { - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_C_RETURN, - frame, this_instr, callable, arg); - if (err < 0) { - Py_CLEAR(res); - } - } + if (Py_IsNone(value)) { + b = Py_True; } - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - Py_DECREF(callable); - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); + else { + b = Py_False; + Py_DECREF(value); } - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + // _POP_JUMP_IF_TRUE + cond = b; + { + assert(PyBool_Check(cond)); + int flag = Py_IsTrue(cond); + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | flag; + #endif + JUMPBY(oparg * flag); + } + stack_pointer += -1; DISPATCH(); } - TARGET(CALL_BOUND_METHOD_EXACT_ARGS) { + TARGET(POP_JUMP_IF_NOT_NONE) { _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_BOUND_METHOD_EXACT_ARGS); - PyObject *null; - PyObject *callable; - PyObject *self; - PyObject *self_or_null; - PyObject *func; - PyObject **args; - _PyInterpreterFrame *new_frame; - // _CHECK_PEP_523 + next_instr += 2; + INSTRUCTION_STATS(POP_JUMP_IF_NOT_NONE); + PyObject *value; + PyObject *b; + PyObject *cond; + // _IS_NONE + value = stack_pointer[-1]; { - DEOPT_IF(tstate->interp->eval_frame, CALL); + if (Py_IsNone(value)) { + b = Py_True; + } + else { + b = Py_False; + Py_DECREF(value); + } } - // _CHECK_CALL_BOUND_METHOD_EXACT_ARGS - null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; + // _POP_JUMP_IF_FALSE + cond = b; { - DEOPT_IF(null != NULL, CALL); - DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type, CALL); + assert(PyBool_Check(cond)); + int flag = Py_IsFalse(cond); + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | flag; + #endif + JUMPBY(oparg * flag); } - // _INIT_CALL_BOUND_METHOD_EXACT_ARGS - { - STAT_INC(CALL, hit); - self = Py_NewRef(((PyMethodObject *)callable)->im_self); - stack_pointer[-1 - oparg] = self; // Patch stack as it is used by _INIT_CALL_PY_EXACT_ARGS - func = Py_NewRef(((PyMethodObject *)callable)->im_func); - stack_pointer[-2 - oparg] = func; // This is used by CALL, upon deoptimization - Py_DECREF(callable); + stack_pointer += -1; + DISPATCH(); + } + + TARGET(POP_JUMP_IF_TRUE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(POP_JUMP_IF_TRUE); + PyObject *cond; + cond = stack_pointer[-1]; + assert(PyBool_Check(cond)); + int flag = Py_IsTrue(cond); + #if ENABLE_SPECIALIZATION + this_instr[1].cache = (this_instr[1].cache << 1) | flag; + #endif + JUMPBY(oparg * flag); + stack_pointer += -1; + DISPATCH(); + } + + TARGET(POP_TOP) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(POP_TOP); + PyObject *value; + value = stack_pointer[-1]; + Py_DECREF(value); + stack_pointer += -1; + DISPATCH(); + } + + TARGET(PUSH_EXC_INFO) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(PUSH_EXC_INFO); + PyObject *new_exc; + PyObject *prev_exc; + new_exc = stack_pointer[-1]; + _PyErr_StackItem *exc_info = tstate->exc_info; + if (exc_info->exc_value != NULL) { + prev_exc = exc_info->exc_value; } - // _CHECK_FUNCTION_EXACT_ARGS - self_or_null = self; - callable = func; - { - uint32_t func_version = read_u32(&this_instr[2].cache); - DEOPT_IF(!PyFunction_Check(callable), CALL); - PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != func_version, CALL); - PyCodeObject *code = (PyCodeObject *)func->func_code; - DEOPT_IF(code->co_argcount != oparg + (self_or_null != NULL), CALL); + else { + prev_exc = Py_None; } - // _CHECK_STACK_SPACE - { - PyFunctionObject *func = (PyFunctionObject *)callable; - PyCodeObject *code = (PyCodeObject *)func->func_code; - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); - DEOPT_IF(tstate->py_recursion_remaining <= 1, CALL); + assert(PyExceptionInstance_Check(new_exc)); + exc_info->exc_value = Py_NewRef(new_exc); + stack_pointer[-1] = prev_exc; + stack_pointer[0] = new_exc; + stack_pointer += 1; + DISPATCH(); + } + + TARGET(PUSH_NULL) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(PUSH_NULL); + PyObject *res; + res = NULL; + stack_pointer[0] = res; + stack_pointer += 1; + DISPATCH(); + } + + TARGET(RAISE_VARARGS) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RAISE_VARARGS); + PyObject **args; + args = &stack_pointer[-oparg]; + TIER_ONE_ONLY + PyObject *cause = NULL, *exc = NULL; + switch (oparg) { + case 2: + cause = args[1]; + /* fall through */ + case 1: + exc = args[0]; + /* fall through */ + case 0: + if (do_raise(tstate, exc, cause)) { + assert(oparg == 0); + monitor_reraise(tstate, frame, this_instr); + goto exception_unwind; + } + break; + default: + _PyErr_SetString(tstate, PyExc_SystemError, + "bad RAISE_VARARGS oparg"); + break; } - // _INIT_CALL_PY_EXACT_ARGS - args = stack_pointer - oparg; - { - int argcount = oparg; - if (self_or_null != NULL) { - args--; - argcount++; + if (true) { stack_pointer += -oparg; goto error; } + } + + TARGET(RERAISE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RERAISE); + PyObject *exc; + PyObject **values; + exc = stack_pointer[-1]; + values = &stack_pointer[-1 - oparg]; + TIER_ONE_ONLY + assert(oparg >= 0 && oparg <= 2); + if (oparg) { + PyObject *lasti = values[0]; + if (PyLong_Check(lasti)) { + frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame)) + PyLong_AsLong(lasti); + assert(!_PyErr_Occurred(tstate)); } - STAT_INC(CALL, hit); - PyFunctionObject *func = (PyFunctionObject *)callable; - new_frame = _PyFrame_PushUnchecked(tstate, func, argcount); - for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = args[i]; + else { + assert(PyLong_Check(lasti)); + _PyErr_SetString(tstate, PyExc_SystemError, "lasti is not an int"); + GOTO_ERROR(error); } } - // _SAVE_RETURN_OFFSET - { - #if TIER_ONE - frame->return_offset = (uint16_t)(next_instr - this_instr); - #endif - #if TIER_TWO - frame->return_offset = oparg; - #endif + assert(exc && PyExceptionInstance_Check(exc)); + Py_INCREF(exc); + _PyErr_SetRaisedException(tstate, exc); + monitor_reraise(tstate, frame, this_instr); + goto exception_unwind; + } + + TARGET(RESERVED) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RESERVED); + assert(0 && "Executing RESERVED instruction."); + Py_UNREACHABLE(); + } + + TARGET(RESUME) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RESUME); + PREDICTED(RESUME); + _Py_CODEUNIT *this_instr = next_instr - 1; + TIER_ONE_ONLY + assert(frame == tstate->current_frame); + uintptr_t global_version = + _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker) & + ~_PY_EVAL_EVENTS_MASK; + uintptr_t code_version = _PyFrame_GetCode(frame)->_co_instrumentation_version; + assert((code_version & 255) == 0); + if (code_version != global_version) { + int err = _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); + if (err) goto error; + next_instr = this_instr; } - // _PUSH_FRAME - STACK_SHRINK(oparg); - STACK_SHRINK(2); - { - // Write it out explicitly because it's subtly different. - // Eventually this should be the only occurrence of this code. - assert(tstate->interp->eval_frame == NULL); - STORE_SP(); - new_frame->previous = frame; - CALL_STAT_INC(inlined_py_calls); - frame = tstate->current_frame = new_frame; - tstate->py_recursion_remaining--; - LOAD_SP(); - LOAD_IP(0); - #if LLTRACE && TIER_ONE - lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); - if (lltrace < 0) { - goto exit_unwind; + else { + if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { + CHECK_EVAL_BREAKER(); } - #endif + this_instr->op.code = RESUME_CHECK; } DISPATCH(); } - TARGET(CALL_PY_EXACT_ARGS) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_PY_EXACT_ARGS); - PyObject *self_or_null; - PyObject *callable; - PyObject **args; - _PyInterpreterFrame *new_frame; - // _CHECK_PEP_523 - { - DEOPT_IF(tstate->interp->eval_frame, CALL); - } - // _CHECK_FUNCTION_EXACT_ARGS - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - { - uint32_t func_version = read_u32(&this_instr[2].cache); - DEOPT_IF(!PyFunction_Check(callable), CALL); - PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != func_version, CALL); - PyCodeObject *code = (PyCodeObject *)func->func_code; - DEOPT_IF(code->co_argcount != oparg + (self_or_null != NULL), CALL); - } - // _CHECK_STACK_SPACE - { - PyFunctionObject *func = (PyFunctionObject *)callable; - PyCodeObject *code = (PyCodeObject *)func->func_code; - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); - DEOPT_IF(tstate->py_recursion_remaining <= 1, CALL); - } - // _INIT_CALL_PY_EXACT_ARGS - args = stack_pointer - oparg; + TARGET(RESUME_CHECK) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RESUME_CHECK); + static_assert(0 == 0, "incorrect cache size"); + #if defined(__EMSCRIPTEN__) + DEOPT_IF(_Py_emscripten_signal_clock == 0, RESUME); + _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; + #endif + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker); + uintptr_t version = _PyFrame_GetCode(frame)->_co_instrumentation_version; + assert((version & _PY_EVAL_EVENTS_MASK) == 0); + DEOPT_IF(eval_breaker != version, RESUME); + DISPATCH(); + } + + TARGET(RETURN_CONST) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RETURN_CONST); + PyObject *value; + PyObject *retval; + // _LOAD_CONST { - int argcount = oparg; - if (self_or_null != NULL) { - args--; - argcount++; - } - STAT_INC(CALL, hit); - PyFunctionObject *func = (PyFunctionObject *)callable; - new_frame = _PyFrame_PushUnchecked(tstate, func, argcount); - for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = args[i]; - } + value = GETITEM(FRAME_CO_CONSTS, oparg); + Py_INCREF(value); } - // _SAVE_RETURN_OFFSET + // _POP_FRAME + retval = value; { #if TIER_ONE - frame->return_offset = (uint16_t)(next_instr - this_instr); - #endif - #if TIER_TWO - frame->return_offset = oparg; + assert(frame != &entry_frame); #endif - } - // _PUSH_FRAME - STACK_SHRINK(oparg); - STACK_SHRINK(2); - { - // Write it out explicitly because it's subtly different. - // Eventually this should be the only occurrence of this code. - assert(tstate->interp->eval_frame == NULL); - STORE_SP(); - new_frame->previous = frame; - CALL_STAT_INC(inlined_py_calls); - frame = tstate->current_frame = new_frame; - tstate->py_recursion_remaining--; + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(EMPTY()); + _Py_LeaveRecursiveCallPy(tstate); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + _PyFrame_StackPush(frame, retval); LOAD_SP(); - LOAD_IP(0); - #if LLTRACE && TIER_ONE + LOAD_IP(frame->return_offset); + #if LLTRACE && TIER_ONE lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); if (lltrace < 0) { goto exit_unwind; } - #endif + #endif } DISPATCH(); } - TARGET(CALL_PY_WITH_DEFAULTS) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_PY_WITH_DEFAULTS); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - uint32_t func_version = read_u32(&this_instr[2].cache); - DEOPT_IF(tstate->interp->eval_frame, CALL); - int argcount = oparg; - if (self_or_null != NULL) { - args--; - argcount++; - } - DEOPT_IF(!PyFunction_Check(callable), CALL); - PyFunctionObject *func = (PyFunctionObject *)callable; - DEOPT_IF(func->func_version != func_version, CALL); - PyCodeObject *code = (PyCodeObject *)func->func_code; - assert(func->func_defaults); - assert(PyTuple_CheckExact(func->func_defaults)); - int defcount = (int)PyTuple_GET_SIZE(func->func_defaults); - assert(defcount <= code->co_argcount); - int min_args = code->co_argcount - defcount; - DEOPT_IF(argcount > code->co_argcount, CALL); - DEOPT_IF(argcount < min_args, CALL); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); - STAT_INC(CALL, hit); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, code->co_argcount); - for (int i = 0; i < argcount; i++) { - new_frame->localsplus[i] = args[i]; + TARGET(RETURN_GENERATOR) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RETURN_GENERATOR); + assert(PyFunction_Check(frame->f_funcobj)); + PyFunctionObject *func = (PyFunctionObject *)frame->f_funcobj; + PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); + if (gen == NULL) { + GOTO_ERROR(error); } - for (int i = argcount; i < code->co_argcount; i++) { - PyObject *def = PyTuple_GET_ITEM(func->func_defaults, i - min_args); - new_frame->localsplus[i] = Py_NewRef(def); + assert(EMPTY()); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + frame->instr_ptr = next_instr; + _PyFrame_Copy(frame, gen_frame); + assert(frame->frame_obj == NULL); + gen->gi_frame_state = FRAME_CREATED; + gen_frame->owner = FRAME_OWNED_BY_GENERATOR; + _Py_LeaveRecursiveCallPy(tstate); + assert(frame != &entry_frame); + _PyInterpreterFrame *prev = frame->previous; + _PyThreadState_PopFrame(tstate, frame); + frame = tstate->current_frame = prev; + _PyFrame_StackPush(frame, (PyObject *)gen); + LOAD_IP(frame->return_offset); + goto resume_frame; + } + + TARGET(RETURN_VALUE) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RETURN_VALUE); + PyObject *retval; + retval = stack_pointer[-1]; + #if TIER_ONE + assert(frame != &entry_frame); + #endif + stack_pointer += -1; + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(EMPTY()); + _Py_LeaveRecursiveCallPy(tstate); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + _PyFrame_StackPush(frame, retval); + LOAD_SP(); + LOAD_IP(frame->return_offset); + #if LLTRACE && TIER_ONE + lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); + if (lltrace < 0) { + goto exit_unwind; } - // Manipulate stack and cache directly since we leave using DISPATCH_INLINED(). - STACK_SHRINK(oparg + 2); - frame->return_offset = (uint16_t)(next_instr - this_instr); - DISPATCH_INLINED(new_frame); + #endif + DISPATCH(); } - TARGET(CALL_TYPE_1) { + TARGET(SEND) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_TYPE_1); - PyObject **args; - PyObject *null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - assert(oparg == 1); - DEOPT_IF(null != NULL, CALL); - PyObject *obj = args[0]; - DEOPT_IF(callable != (PyObject *)&PyType_Type, CALL); - STAT_INC(CALL, hit); - res = Py_NewRef(Py_TYPE(obj)); - Py_DECREF(obj); - Py_DECREF(&PyType_Type); // I.e., callable - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + next_instr += 2; + INSTRUCTION_STATS(SEND); + PREDICTED(SEND); + _Py_CODEUNIT *this_instr = next_instr - 2; + PyObject *receiver; + PyObject *v; + PyObject *retval; + // _SPECIALIZE_SEND + receiver = stack_pointer[-2]; + { + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + next_instr = this_instr; + _Py_Specialize_Send(receiver, next_instr); + DISPATCH_SAME_OPARG(); + } + STAT_INC(SEND, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ + } + // _SEND + v = stack_pointer[-1]; + { + assert(frame != &entry_frame); + if ((tstate->interp->eval_frame == NULL) && + (Py_TYPE(receiver) == &PyGen_Type || Py_TYPE(receiver) == &PyCoro_Type) && + ((PyGenObject *)receiver)->gi_frame_state < FRAME_EXECUTING) + { + PyGenObject *gen = (PyGenObject *)receiver; + _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; + STACK_SHRINK(1); + _PyFrame_StackPush(gen_frame, v); + gen->gi_frame_state = FRAME_EXECUTING; + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + assert(next_instr - this_instr + oparg <= UINT16_MAX); + frame->return_offset = (uint16_t)(next_instr - this_instr + oparg); + DISPATCH_INLINED(gen_frame); + } + if (Py_IsNone(v) && PyIter_Check(receiver)) { + retval = Py_TYPE(receiver)->tp_iternext(receiver); + } + else { + retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v); + } + if (retval == NULL) { + if (_PyErr_ExceptionMatches(tstate, PyExc_StopIteration) + ) { + monitor_raise(tstate, frame, this_instr); + } + if (_PyGen_FetchStopIterationValue(&retval) == 0) { + assert(retval != NULL); + JUMPBY(oparg); + } + else { + GOTO_ERROR(error); + } + } + Py_DECREF(v); + } + stack_pointer[-1] = retval; DISPATCH(); } - TARGET(CALL_STR_1) { - frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_STR_1); - PyObject **args; - PyObject *null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - assert(oparg == 1); - DEOPT_IF(null != NULL, CALL); - DEOPT_IF(callable != (PyObject *)&PyUnicode_Type, CALL); - STAT_INC(CALL, hit); - PyObject *arg = args[0]; - res = PyObject_Str(arg); - Py_DECREF(arg); - Py_DECREF(&PyUnicode_Type); // I.e., callable - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); + TARGET(SEND_GEN) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(SEND_GEN); + static_assert(INLINE_CACHE_ENTRIES_SEND == 1, "incorrect cache size"); + PyObject *v; + PyObject *receiver; + v = stack_pointer[-1]; + receiver = stack_pointer[-2]; + DEOPT_IF(tstate->interp->eval_frame, SEND); + PyGenObject *gen = (PyGenObject *)receiver; + DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type, SEND); + DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, SEND); + STAT_INC(SEND, hit); + _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); - DISPATCH(); + _PyFrame_StackPush(gen_frame, v); + gen->gi_frame_state = FRAME_EXECUTING; + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + assert(next_instr - this_instr + oparg <= UINT16_MAX); + frame->return_offset = (uint16_t)(next_instr - this_instr + oparg); + DISPATCH_INLINED(gen_frame); } - TARGET(CALL_TUPLE_1) { + TARGET(SETUP_ANNOTATIONS) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_TUPLE_1); - PyObject **args; - PyObject *null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - assert(oparg == 1); - DEOPT_IF(null != NULL, CALL); - DEOPT_IF(callable != (PyObject *)&PyTuple_Type, CALL); - STAT_INC(CALL, hit); - PyObject *arg = args[0]; - res = PySequence_Tuple(arg); - Py_DECREF(arg); - Py_DECREF(&PyTuple_Type); // I.e., tuple - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); - DISPATCH(); - } - - TARGET(CALL_ALLOC_AND_ENTER_INIT) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_ALLOC_AND_ENTER_INIT); - PyObject **args; - PyObject *null; - PyObject *callable; - args = stack_pointer - oparg; - null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - /* This instruction does the following: - * 1. Creates the object (by calling ``object.__new__``) - * 2. Pushes a shim frame to the frame stack (to cleanup after ``__init__``) - * 3. Pushes the frame for ``__init__`` to the frame stack - * */ - _PyCallCache *cache = (_PyCallCache *)&this_instr[1]; - DEOPT_IF(null != NULL, CALL); - DEOPT_IF(!PyType_Check(callable), CALL); - PyTypeObject *tp = (PyTypeObject *)callable; - DEOPT_IF(tp->tp_version_tag != read_u32(cache->func_version), CALL); - PyHeapTypeObject *cls = (PyHeapTypeObject *)callable; - PyFunctionObject *init = (PyFunctionObject *)cls->_spec_cache.init; - PyCodeObject *code = (PyCodeObject *)init->func_code; - DEOPT_IF(code->co_argcount != oparg+1, CALL); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize), CALL); - STAT_INC(CALL, hit); - PyObject *self = _PyType_NewManagedObject(tp); - if (self == NULL) { - GOTO_ERROR(error); + next_instr += 1; + INSTRUCTION_STATS(SETUP_ANNOTATIONS); + int err; + PyObject *ann_dict; + if (LOCALS() == NULL) { + _PyErr_Format(tstate, PyExc_SystemError, + "no locals found when setting up annotations"); + if (true) goto error; } - Py_DECREF(tp); - _PyInterpreterFrame *shim = _PyFrame_PushTrampolineUnchecked( - tstate, (PyCodeObject *)&_Py_InitCleanup, 1); - assert(_PyCode_CODE((PyCodeObject *)shim->f_executable)[0].op.code == EXIT_INIT_CHECK); - /* Push self onto stack of shim */ - Py_INCREF(self); - shim->localsplus[0] = self; - Py_INCREF(init); - _PyInterpreterFrame *init_frame = _PyFrame_PushUnchecked(tstate, init, oparg+1); - /* Copy self followed by args to __init__ frame */ - init_frame->localsplus[0] = self; - for (int i = 0; i < oparg; i++) { - init_frame->localsplus[i+1] = args[i]; + /* check if __annotations__ in locals()... */ + if (PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict) < 0) goto error; + if (ann_dict == NULL) { + ann_dict = PyDict_New(); + if (ann_dict == NULL) goto error; + err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__), + ann_dict); + Py_DECREF(ann_dict); + if (err) goto error; } - frame->return_offset = (uint16_t)(next_instr - this_instr); - STACK_SHRINK(oparg+2); - _PyFrame_SetStackPointer(frame, stack_pointer); - /* Link frames */ - init_frame->previous = shim; - shim->previous = frame; - frame = tstate->current_frame = init_frame; - CALL_STAT_INC(inlined_py_calls); - /* Account for pushing the extra frame. - * We don't check recursion depth here, - * as it will be checked after start_frame */ - tstate->py_recursion_remaining--; - goto start_frame; + else { + Py_DECREF(ann_dict); + } + DISPATCH(); } - TARGET(EXIT_INIT_CHECK) { + TARGET(SET_ADD) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(EXIT_INIT_CHECK); - PyObject *should_be_none; - should_be_none = stack_pointer[-1]; - assert(STACK_LEVEL() == 2); - if (should_be_none != Py_None) { - PyErr_Format(PyExc_TypeError, - "__init__() should return None, not '%.200s'", - Py_TYPE(should_be_none)->tp_name); - GOTO_ERROR(error); - } - STACK_SHRINK(1); + INSTRUCTION_STATS(SET_ADD); + PyObject *v; + PyObject *set; + v = stack_pointer[-1]; + set = stack_pointer[-2 - (oparg-1)]; + int err = PySet_Add(set, v); + Py_DECREF(v); + if (err) goto pop_1_error; + stack_pointer += -1; DISPATCH(); } - TARGET(CALL_BUILTIN_CLASS) { + TARGET(SET_FUNCTION_ATTRIBUTE) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_BUILTIN_CLASS); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - DEOPT_IF(!PyType_Check(callable), CALL); - PyTypeObject *tp = (PyTypeObject *)callable; - DEOPT_IF(tp->tp_vectorcall == NULL, CALL); - STAT_INC(CALL, hit); - res = tp->tp_vectorcall((PyObject *)tp, args, total_args, NULL); - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); + next_instr += 1; + INSTRUCTION_STATS(SET_FUNCTION_ATTRIBUTE); + PyObject *func; + PyObject *attr; + func = stack_pointer[-1]; + attr = stack_pointer[-2]; + assert(PyFunction_Check(func)); + PyFunctionObject *func_obj = (PyFunctionObject *)func; + switch(oparg) { + case MAKE_FUNCTION_CLOSURE: + assert(func_obj->func_closure == NULL); + func_obj->func_closure = attr; + break; + case MAKE_FUNCTION_ANNOTATIONS: + assert(func_obj->func_annotations == NULL); + func_obj->func_annotations = attr; + break; + case MAKE_FUNCTION_KWDEFAULTS: + assert(PyDict_CheckExact(attr)); + assert(func_obj->func_kwdefaults == NULL); + func_obj->func_kwdefaults = attr; + break; + case MAKE_FUNCTION_DEFAULTS: + assert(PyTuple_CheckExact(attr)); + assert(func_obj->func_defaults == NULL); + func_obj->func_defaults = attr; + break; + default: + Py_UNREACHABLE(); } - Py_DECREF(tp); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + stack_pointer[-2] = func; + stack_pointer += -1; DISPATCH(); } - TARGET(CALL_BUILTIN_O) { + TARGET(SET_UPDATE) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_BUILTIN_O); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - /* Builtin METH_O functions */ - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - DEOPT_IF(total_args != 1, CALL); - DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); - DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_O, CALL); - STAT_INC(CALL, hit); - PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); - // This is slower but CPython promises to check all non-vectorcall - // function calls. - if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { - GOTO_ERROR(error); - } - PyObject *arg = args[0]; - res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); - _Py_LeaveRecursiveCallTstate(tstate); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - - Py_DECREF(arg); - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + next_instr += 1; + INSTRUCTION_STATS(SET_UPDATE); + PyObject *iterable; + PyObject *set; + iterable = stack_pointer[-1]; + set = stack_pointer[-2 - (oparg-1)]; + int err = _PySet_Update(set, iterable); + Py_DECREF(iterable); + if (err < 0) goto pop_1_error; + stack_pointer += -1; DISPATCH(); } - TARGET(CALL_BUILTIN_FAST) { + TARGET(STORE_ATTR) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_BUILTIN_FAST); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - /* Builtin METH_FASTCALL functions, without keywords */ - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; + next_instr += 5; + INSTRUCTION_STATS(STORE_ATTR); + PREDICTED(STORE_ATTR); + _Py_CODEUNIT *this_instr = next_instr - 5; + PyObject *owner; + PyObject *v; + // _SPECIALIZE_STORE_ATTR + owner = stack_pointer[-1]; + { + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + next_instr = this_instr; + _Py_Specialize_StoreAttr(owner, next_instr, name); + DISPATCH_SAME_OPARG(); + } + STAT_INC(STORE_ATTR, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ } - DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); - DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL, CALL); - STAT_INC(CALL, hit); - PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); - /* res = func(self, args, nargs) */ - res = ((_PyCFunctionFast)(void(*)(void))cfunc)( - PyCFunction_GET_SELF(callable), - args, - total_args); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); + // _STORE_ATTR + v = stack_pointer[-2]; + { + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + int err = PyObject_SetAttr(owner, name, v); + Py_DECREF(v); + Py_DECREF(owner); + if (err) goto pop_2_error; } - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - /* Not deopting because this doesn't mean our optimization was - wrong. `res` can be NULL for valid reasons. Eg. getattr(x, - 'invalid'). In those cases an exception is set, so we must - handle it. - */ - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + stack_pointer += -2; DISPATCH(); } - TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) { - frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_BUILTIN_FAST_WITH_KEYWORDS); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; + TARGET(STORE_ATTR_INSTANCE_VALUE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 5; + INSTRUCTION_STATS(STORE_ATTR_INSTANCE_VALUE); + static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); + PyObject *owner; + PyObject *value; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + owner = stack_pointer[-1]; + { + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); } - DEOPT_IF(!PyCFunction_CheckExact(callable), CALL); - DEOPT_IF(PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS), CALL); - STAT_INC(CALL, hit); - /* res = func(self, args, nargs, kwnames) */ - _PyCFunctionFastWithKeywords cfunc = - (_PyCFunctionFastWithKeywords)(void(*)(void)) - PyCFunction_GET_FUNCTION(callable); - res = cfunc(PyCFunction_GET_SELF(callable), args, total_args, NULL); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); + // _GUARD_DORV_VALUES + { + assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(dorv), STORE_ATTR); } - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + // _STORE_ATTR_INSTANCE_VALUE + value = stack_pointer[-2]; + { + uint16_t index = read_u16(&this_instr[4].cache); + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + STAT_INC(STORE_ATTR, hit); + PyDictValues *values = _PyDictOrValues_GetValues(dorv); + PyObject *old_value = values->values[index]; + values->values[index] = value; + if (old_value == NULL) { + _PyDictValues_AddToInsertionOrder(values, index); + } + else { + Py_DECREF(old_value); + } + Py_DECREF(owner); + } + stack_pointer += -2; DISPATCH(); } - TARGET(CALL_LEN) { - frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_LEN); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - /* len(o) */ - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; + TARGET(STORE_ATTR_SLOT) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 5; + INSTRUCTION_STATS(STORE_ATTR_SLOT); + static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); + PyObject *owner; + PyObject *value; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + owner = stack_pointer[-1]; + { + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); } - DEOPT_IF(total_args != 1, CALL); - PyInterpreterState *interp = tstate->interp; - DEOPT_IF(callable != interp->callable_cache.len, CALL); - STAT_INC(CALL, hit); - PyObject *arg = args[0]; - Py_ssize_t len_i = PyObject_Length(arg); - if (len_i < 0) { - GOTO_ERROR(error); + // _STORE_ATTR_SLOT + value = stack_pointer[-2]; + { + uint16_t index = read_u16(&this_instr[4].cache); + char *addr = (char *)owner + index; + STAT_INC(STORE_ATTR, hit); + PyObject *old_value = *(PyObject **)addr; + *(PyObject **)addr = value; + Py_XDECREF(old_value); + Py_DECREF(owner); } - res = PyLong_FromSsize_t(len_i); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - - Py_DECREF(callable); - Py_DECREF(arg); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer += -2; DISPATCH(); } - TARGET(CALL_ISINSTANCE) { - frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_ISINSTANCE); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - /* isinstance(o, o2) */ - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; + TARGET(STORE_ATTR_WITH_HINT) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 5; + INSTRUCTION_STATS(STORE_ATTR_WITH_HINT); + static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); + PyObject *owner; + PyObject *value; + owner = stack_pointer[-1]; + value = stack_pointer[-2]; + uint32_t type_version = read_u32(&this_instr[2].cache); + uint16_t hint = read_u16(&this_instr[4].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); + assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + DEOPT_IF(_PyDictOrValues_IsValues(dorv), STORE_ATTR); + PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); + DEOPT_IF(dict == NULL, STORE_ATTR); + assert(PyDict_CheckExact((PyObject *)dict)); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR); + PyObject *old_value; + uint64_t new_version; + if (DK_IS_UNICODE(dict->ma_keys)) { + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; + DEOPT_IF(ep->me_key != name, STORE_ATTR); + old_value = ep->me_value; + DEOPT_IF(old_value == NULL, STORE_ATTR); + new_version = _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, value); + ep->me_value = value; } - DEOPT_IF(total_args != 2, CALL); - PyInterpreterState *interp = tstate->interp; - DEOPT_IF(callable != interp->callable_cache.isinstance, CALL); - STAT_INC(CALL, hit); - PyObject *cls = args[1]; - PyObject *inst = args[0]; - int retval = PyObject_IsInstance(inst, cls); - if (retval < 0) { - GOTO_ERROR(error); + else { + PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + hint; + DEOPT_IF(ep->me_key != name, STORE_ATTR); + old_value = ep->me_value; + DEOPT_IF(old_value == NULL, STORE_ATTR); + new_version = _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, value); + ep->me_value = value; } - res = PyBool_FromLong(retval); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + Py_DECREF(old_value); + STAT_INC(STORE_ATTR, hit); + /* Ensure dict is GC tracked if it needs to be */ + if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(value)) { + _PyObject_GC_TRACK(dict); + } + /* PEP 509 */ + dict->ma_version_tag = new_version; + Py_DECREF(owner); + stack_pointer += -2; + DISPATCH(); + } - Py_DECREF(inst); - Py_DECREF(cls); - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; + TARGET(STORE_DEREF) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(STORE_DEREF); + PyObject *v; + v = stack_pointer[-1]; + PyObject *cell = GETLOCAL(oparg); + PyObject *oldobj = PyCell_GET(cell); + PyCell_SET(cell, v); + Py_XDECREF(oldobj); + stack_pointer += -1; DISPATCH(); } - TARGET(CALL_LIST_APPEND) { + TARGET(STORE_FAST) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_LIST_APPEND); - PyObject **args; - PyObject *self; - PyObject *callable; - args = stack_pointer - oparg; - self = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - assert(oparg == 1); - PyInterpreterState *interp = tstate->interp; - DEOPT_IF(callable != interp->callable_cache.list_append, CALL); - assert(self != NULL); - DEOPT_IF(!PyList_Check(self), CALL); - STAT_INC(CALL, hit); - if (_PyList_AppendTakeRef((PyListObject *)self, args[0]) < 0) { - goto pop_1_error; // Since arg is DECREF'ed already - } - Py_DECREF(self); - Py_DECREF(callable); - STACK_SHRINK(3); - // Skip POP_TOP - assert(next_instr->op.code == POP_TOP); - SKIP_OVER(1); + next_instr += 1; + INSTRUCTION_STATS(STORE_FAST); + PyObject *value; + value = stack_pointer[-1]; + SETLOCAL(oparg, value); + stack_pointer += -1; DISPATCH(); } - TARGET(CALL_METHOD_DESCRIPTOR_O) { + TARGET(STORE_FAST_LOAD_FAST) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_O); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - PyMethodDescrObject *method = (PyMethodDescrObject *)callable; - DEOPT_IF(total_args != 2, CALL); - DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); - PyMethodDef *meth = method->d_method; - DEOPT_IF(meth->ml_flags != METH_O, CALL); - PyObject *arg = args[1]; - PyObject *self = args[0]; - DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); - STAT_INC(CALL, hit); - PyCFunction cfunc = meth->ml_meth; - // This is slower but CPython promises to check all non-vectorcall - // function calls. - if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { - GOTO_ERROR(error); - } - res = _PyCFunction_TrampolineCall(cfunc, self, arg); - _Py_LeaveRecursiveCallTstate(tstate); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - Py_DECREF(self); - Py_DECREF(arg); - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + next_instr += 1; + INSTRUCTION_STATS(STORE_FAST_LOAD_FAST); + PyObject *value1; + PyObject *value2; + value1 = stack_pointer[-1]; + uint32_t oparg1 = oparg >> 4; + uint32_t oparg2 = oparg & 15; + SETLOCAL(oparg1, value1); + value2 = GETLOCAL(oparg2); + Py_INCREF(value2); + stack_pointer[-1] = value2; DISPATCH(); } - TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) { + TARGET(STORE_FAST_STORE_FAST) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - PyMethodDescrObject *method = (PyMethodDescrObject *)callable; - DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); - PyMethodDef *meth = method->d_method; - DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), CALL); - PyTypeObject *d_type = method->d_common.d_type; - PyObject *self = args[0]; - DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL); - STAT_INC(CALL, hit); - int nargs = total_args - 1; - _PyCFunctionFastWithKeywords cfunc = - (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; - res = cfunc(self, args + 1, nargs, NULL); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + next_instr += 1; + INSTRUCTION_STATS(STORE_FAST_STORE_FAST); + PyObject *value1; + PyObject *value2; + value1 = stack_pointer[-1]; + value2 = stack_pointer[-2]; + uint32_t oparg1 = oparg >> 4; + uint32_t oparg2 = oparg & 15; + SETLOCAL(oparg1, value1); + SETLOCAL(oparg2, value2); + stack_pointer += -2; + DISPATCH(); + } - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); - } - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + TARGET(STORE_GLOBAL) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(STORE_GLOBAL); + PyObject *v; + v = stack_pointer[-1]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + int err = PyDict_SetItem(GLOBALS(), name, v); + Py_DECREF(v); + if (err) goto pop_1_error; + stack_pointer += -1; DISPATCH(); } - TARGET(CALL_METHOD_DESCRIPTOR_NOARGS) { + TARGET(STORE_NAME) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_NOARGS); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - assert(oparg == 0 || oparg == 1); - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - DEOPT_IF(total_args != 1, CALL); - PyMethodDescrObject *method = (PyMethodDescrObject *)callable; - DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); - PyMethodDef *meth = method->d_method; - PyObject *self = args[0]; - DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); - DEOPT_IF(meth->ml_flags != METH_NOARGS, CALL); - STAT_INC(CALL, hit); - PyCFunction cfunc = meth->ml_meth; - // This is slower but CPython promises to check all non-vectorcall - // function calls. - if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { - GOTO_ERROR(error); + next_instr += 1; + INSTRUCTION_STATS(STORE_NAME); + PyObject *v; + v = stack_pointer[-1]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + PyObject *ns = LOCALS(); + int err; + if (ns == NULL) { + _PyErr_Format(tstate, PyExc_SystemError, + "no locals found when storing %R", name); + Py_DECREF(v); + if (true) goto pop_1_error; } - res = _PyCFunction_TrampolineCall(cfunc, self, NULL); - _Py_LeaveRecursiveCallTstate(tstate); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - Py_DECREF(self); - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + if (PyDict_CheckExact(ns)) + err = PyDict_SetItem(ns, name, v); + else + err = PyObject_SetItem(ns, name, v); + Py_DECREF(v); + if (err) goto pop_1_error; + stack_pointer += -1; DISPATCH(); } - TARGET(CALL_METHOD_DESCRIPTOR_FAST) { + TARGET(STORE_SLICE) { frame->instr_ptr = next_instr; - next_instr += 4; - INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_FAST); - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - args = stack_pointer - oparg; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - PyMethodDescrObject *method = (PyMethodDescrObject *)callable; - /* Builtin METH_FASTCALL methods, without keywords */ - DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); - PyMethodDef *meth = method->d_method; - DEOPT_IF(meth->ml_flags != METH_FASTCALL, CALL); - PyObject *self = args[0]; - DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); - STAT_INC(CALL, hit); - _PyCFunctionFast cfunc = - (_PyCFunctionFast)(void(*)(void))meth->ml_meth; - int nargs = total_args - 1; - res = cfunc(self, args + 1, nargs); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - /* Clear the stack of the arguments. */ - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); + next_instr += 1; + INSTRUCTION_STATS(STORE_SLICE); + PyObject *stop; + PyObject *start; + PyObject *container; + PyObject *v; + stop = stack_pointer[-1]; + start = stack_pointer[-2]; + container = stack_pointer[-3]; + v = stack_pointer[-4]; + PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop); + int err; + if (slice == NULL) { + err = 1; } - Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(1); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + else { + err = PyObject_SetItem(container, slice, v); + Py_DECREF(slice); + } + Py_DECREF(v); + Py_DECREF(container); + if (err) goto pop_4_error; + stack_pointer += -4; DISPATCH(); } - TARGET(INSTRUMENTED_CALL_KW) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_CALL_KW); - int is_meth = PEEK(oparg + 2) != NULL; - int total_args = oparg + is_meth; - PyObject *function = PEEK(oparg + 3); - PyObject *arg = total_args == 0 ? &_PyInstrumentation_MISSING - : PEEK(total_args + 1); - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_CALL, - frame, this_instr, function, arg); - if (err) goto error; - GO_TO_INSTRUCTION(CALL_KW); - } - - TARGET(CALL_KW) { + TARGET(STORE_SUBSCR) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(CALL_KW); - PREDICTED(CALL_KW); - _Py_CODEUNIT *this_instr = next_instr - 1; - PyObject *kwnames; - PyObject **args; - PyObject *self_or_null; - PyObject *callable; - PyObject *res; - kwnames = stack_pointer[-1]; - args = stack_pointer - 1 - oparg; - self_or_null = stack_pointer[-2 - oparg]; - callable = stack_pointer[-3 - oparg]; - // oparg counts all of the args, but *not* self: - int total_args = oparg; - if (self_or_null != NULL) { - args--; - total_args++; - } - if (self_or_null == NULL && Py_TYPE(callable) == &PyMethod_Type) { - args--; - total_args++; - PyObject *self = ((PyMethodObject *)callable)->im_self; - args[0] = Py_NewRef(self); - PyObject *method = ((PyMethodObject *)callable)->im_func; - args[-1] = Py_NewRef(method); - Py_DECREF(callable); - callable = method; - } - int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames); - // Check if the call can be inlined or not - if (Py_TYPE(callable) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && - ((PyFunctionObject *)callable)->vectorcall == _PyFunction_Vectorcall) + next_instr += 2; + INSTRUCTION_STATS(STORE_SUBSCR); + PREDICTED(STORE_SUBSCR); + _Py_CODEUNIT *this_instr = next_instr - 2; + PyObject *sub; + PyObject *container; + PyObject *v; + // _SPECIALIZE_STORE_SUBSCR + sub = stack_pointer[-1]; + container = stack_pointer[-2]; { - int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable))->co_flags; - PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable)); - _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( - tstate, (PyFunctionObject *)callable, locals, - args, positional_args, kwnames - ); - Py_DECREF(kwnames); - // Manipulate stack directly since we leave using DISPATCH_INLINED(). - STACK_SHRINK(oparg + 3); - // The frame has stolen all the arguments from the stack, - // so there is no need to clean them up. - if (new_frame == NULL) { - GOTO_ERROR(error); - } - assert(next_instr - this_instr == 1); - frame->return_offset = 1; - DISPATCH_INLINED(new_frame); - } - /* Callable is not a normal Python function */ - res = PyObject_Vectorcall( - callable, args, - positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - kwnames); - if (opcode == INSTRUMENTED_CALL_KW) { - PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : args[0]; - if (res == NULL) { - _Py_call_instrumentation_exc2( - tstate, PY_MONITORING_EVENT_C_RAISE, - frame, this_instr, callable, arg); - } - else { - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_C_RETURN, - frame, this_instr, callable, arg); - if (err < 0) { - Py_CLEAR(res); - } + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + next_instr = this_instr; + _Py_Specialize_StoreSubscr(container, sub, next_instr); + DISPATCH_SAME_OPARG(); } + STAT_INC(STORE_SUBSCR, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ } - Py_DECREF(kwnames); - assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - Py_DECREF(callable); - for (int i = 0; i < total_args; i++) { - Py_DECREF(args[i]); + // _STORE_SUBSCR + v = stack_pointer[-3]; + { + /* container[sub] = v */ + int err = PyObject_SetItem(container, sub, v); + Py_DECREF(v); + Py_DECREF(container); + Py_DECREF(sub); + if (err) goto pop_3_error; } - if (res == NULL) { STACK_SHRINK(oparg); goto pop_3_error; } - STACK_SHRINK(oparg); - STACK_SHRINK(2); - stack_pointer[-1] = res; - CHECK_EVAL_BREAKER(); + stack_pointer += -3; DISPATCH(); } - TARGET(INSTRUMENTED_CALL_FUNCTION_EX) { + TARGET(STORE_SUBSCR_DICT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_CALL_FUNCTION_EX); - GO_TO_INSTRUCTION(CALL_FUNCTION_EX); + next_instr += 2; + INSTRUCTION_STATS(STORE_SUBSCR_DICT); + static_assert(INLINE_CACHE_ENTRIES_STORE_SUBSCR == 1, "incorrect cache size"); + PyObject *sub; + PyObject *dict; + PyObject *value; + sub = stack_pointer[-1]; + dict = stack_pointer[-2]; + value = stack_pointer[-3]; + DEOPT_IF(!PyDict_CheckExact(dict), STORE_SUBSCR); + STAT_INC(STORE_SUBSCR, hit); + int err = _PyDict_SetItem_Take2((PyDictObject *)dict, sub, value); + Py_DECREF(dict); + if (err) goto pop_3_error; + stack_pointer += -3; + DISPATCH(); } - TARGET(CALL_FUNCTION_EX) { + TARGET(STORE_SUBSCR_LIST_INT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(CALL_FUNCTION_EX); - PREDICTED(CALL_FUNCTION_EX); - _Py_CODEUNIT *this_instr = next_instr - 1; - PyObject *kwargs = NULL; - PyObject *callargs; - PyObject *func; - PyObject *result; - if (oparg & 1) { kwargs = stack_pointer[-(oparg & 1 ? 1 : 0)]; } - callargs = stack_pointer[-1 - (oparg & 1 ? 1 : 0)]; - func = stack_pointer[-3 - (oparg & 1 ? 1 : 0)]; - // DICT_MERGE is called before this opcode if there are kwargs. - // It converts all dict subtypes in kwargs into regular dicts. - assert(kwargs == NULL || PyDict_CheckExact(kwargs)); - if (!PyTuple_CheckExact(callargs)) { - if (check_args_iterable(tstate, func, callargs) < 0) { - GOTO_ERROR(error); - } - PyObject *tuple = PySequence_Tuple(callargs); - if (tuple == NULL) { - GOTO_ERROR(error); - } - Py_SETREF(callargs, tuple); - } - assert(PyTuple_CheckExact(callargs)); - EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_FUNCTION_EX, func); - if (opcode == INSTRUMENTED_CALL_FUNCTION_EX && - !PyFunction_Check(func) && !PyMethod_Check(func) - ) { - PyObject *arg = PyTuple_GET_SIZE(callargs) > 0 ? - PyTuple_GET_ITEM(callargs, 0) : Py_None; - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_CALL, - frame, this_instr, func, arg); - if (err) GOTO_ERROR(error); - result = PyObject_Call(func, callargs, kwargs); - if (result == NULL) { - _Py_call_instrumentation_exc2( - tstate, PY_MONITORING_EVENT_C_RAISE, - frame, this_instr, func, arg); - } - else { - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_C_RETURN, - frame, this_instr, func, arg); - if (err < 0) { - Py_CLEAR(result); - } - } - } - else { - if (Py_TYPE(func) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && - ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { - assert(PyTuple_CheckExact(callargs)); - Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); - int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; - PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); + next_instr += 2; + INSTRUCTION_STATS(STORE_SUBSCR_LIST_INT); + static_assert(INLINE_CACHE_ENTRIES_STORE_SUBSCR == 1, "incorrect cache size"); + PyObject *sub; + PyObject *list; + PyObject *value; + sub = stack_pointer[-1]; + list = stack_pointer[-2]; + value = stack_pointer[-3]; + DEOPT_IF(!PyLong_CheckExact(sub), STORE_SUBSCR); + DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR); + // Ensure nonnegative, zero-or-one-digit ints. + DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), STORE_SUBSCR); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + // Ensure index < len(list) + DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR); + STAT_INC(STORE_SUBSCR, hit); + PyObject *old_value = PyList_GET_ITEM(list, index); + PyList_SET_ITEM(list, index, value); + assert(old_value != NULL); + Py_DECREF(old_value); + _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free); + Py_DECREF(list); + stack_pointer += -3; + DISPATCH(); + } - _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex(tstate, - (PyFunctionObject *)func, locals, - nargs, callargs, kwargs); - // Need to manually shrink the stack since we exit with DISPATCH_INLINED. - STACK_SHRINK(oparg + 3); - if (new_frame == NULL) { - GOTO_ERROR(error); - } - assert(next_instr - this_instr == 1); - frame->return_offset = 1; - DISPATCH_INLINED(new_frame); - } - result = PyObject_Call(func, callargs, kwargs); - } - Py_DECREF(func); - Py_DECREF(callargs); - Py_XDECREF(kwargs); - assert(PEEK(2 + (oparg & 1)) == NULL); - if (result == NULL) { STACK_SHRINK(((oparg & 1) ? 1 : 0)); goto pop_3_error; } - STACK_SHRINK(((oparg & 1) ? 1 : 0)); - STACK_SHRINK(2); - stack_pointer[-1] = result; - CHECK_EVAL_BREAKER(); + TARGET(SWAP) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(SWAP); + PyObject *top; + PyObject *bottom; + top = stack_pointer[-1]; + bottom = stack_pointer[-2 - (oparg-2)]; + assert(oparg >= 2); + stack_pointer[-2 - (oparg-2)] = top; + stack_pointer[-1] = bottom; DISPATCH(); } - TARGET(MAKE_FUNCTION) { + TARGET(TO_BOOL) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(MAKE_FUNCTION); - PyObject *codeobj; - PyObject *func; - codeobj = stack_pointer[-1]; - - PyFunctionObject *func_obj = (PyFunctionObject *) - PyFunction_New(codeobj, GLOBALS()); - - Py_DECREF(codeobj); - if (func_obj == NULL) { - GOTO_ERROR(error); + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL); + PREDICTED(TO_BOOL); + _Py_CODEUNIT *this_instr = next_instr - 4; + PyObject *value; + PyObject *res; + // _SPECIALIZE_TO_BOOL + value = stack_pointer[-1]; + { + uint16_t counter = read_u16(&this_instr[1].cache); + TIER_ONE_ONLY + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { + next_instr = this_instr; + _Py_Specialize_ToBool(value, next_instr); + DISPATCH_SAME_OPARG(); + } + STAT_INC(TO_BOOL, deferred); + DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); + #endif /* ENABLE_SPECIALIZATION */ + } + // _TO_BOOL + { + int err = PyObject_IsTrue(value); + Py_DECREF(value); + if (err < 0) goto pop_1_error; + res = err ? Py_True : Py_False; } + stack_pointer[-1] = res; + DISPATCH(); + } - _PyFunction_SetVersion( - func_obj, ((PyCodeObject *)codeobj)->co_version); - func = (PyObject *)func_obj; - stack_pointer[-1] = func; + TARGET(TO_BOOL_ALWAYS_TRUE) { + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_ALWAYS_TRUE); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); + PyObject *value; + PyObject *res; + value = stack_pointer[-1]; + uint32_t version = read_u32(&this_instr[2].cache); + // This one is a bit weird, because we expect *some* failures: + assert(version); + DEOPT_IF(Py_TYPE(value)->tp_version_tag != version, TO_BOOL); + STAT_INC(TO_BOOL, hit); + Py_DECREF(value); + res = Py_True; + stack_pointer[-1] = res; DISPATCH(); } - TARGET(SET_FUNCTION_ATTRIBUTE) { + TARGET(TO_BOOL_BOOL) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(SET_FUNCTION_ATTRIBUTE); - PyObject *func; - PyObject *attr; - func = stack_pointer[-1]; - attr = stack_pointer[-2]; - assert(PyFunction_Check(func)); - PyFunctionObject *func_obj = (PyFunctionObject *)func; - switch(oparg) { - case MAKE_FUNCTION_CLOSURE: - assert(func_obj->func_closure == NULL); - func_obj->func_closure = attr; - break; - case MAKE_FUNCTION_ANNOTATIONS: - assert(func_obj->func_annotations == NULL); - func_obj->func_annotations = attr; - break; - case MAKE_FUNCTION_KWDEFAULTS: - assert(PyDict_CheckExact(attr)); - assert(func_obj->func_kwdefaults == NULL); - func_obj->func_kwdefaults = attr; - break; - case MAKE_FUNCTION_DEFAULTS: - assert(PyTuple_CheckExact(attr)); - assert(func_obj->func_defaults == NULL); - func_obj->func_defaults = attr; - break; - default: - Py_UNREACHABLE(); - } - STACK_SHRINK(1); - stack_pointer[-1] = func; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_BOOL); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); + PyObject *value; + value = stack_pointer[-1]; + DEOPT_IF(!PyBool_Check(value), TO_BOOL); + STAT_INC(TO_BOOL, hit); DISPATCH(); } - TARGET(RETURN_GENERATOR) { + TARGET(TO_BOOL_INT) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(RETURN_GENERATOR); - assert(PyFunction_Check(frame->f_funcobj)); - PyFunctionObject *func = (PyFunctionObject *)frame->f_funcobj; - PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); - if (gen == NULL) { - GOTO_ERROR(error); + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_INT); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); + PyObject *value; + PyObject *res; + value = stack_pointer[-1]; + DEOPT_IF(!PyLong_CheckExact(value), TO_BOOL); + STAT_INC(TO_BOOL, hit); + if (_PyLong_IsZero((PyLongObject *)value)) { + assert(_Py_IsImmortal(value)); + res = Py_False; } - assert(EMPTY()); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; - frame->instr_ptr = next_instr; - _PyFrame_Copy(frame, gen_frame); - assert(frame->frame_obj == NULL); - gen->gi_frame_state = FRAME_CREATED; - gen_frame->owner = FRAME_OWNED_BY_GENERATOR; - _Py_LeaveRecursiveCallPy(tstate); - assert(frame != &entry_frame); - _PyInterpreterFrame *prev = frame->previous; - _PyThreadState_PopFrame(tstate, frame); - frame = tstate->current_frame = prev; - _PyFrame_StackPush(frame, (PyObject *)gen); - LOAD_IP(frame->return_offset); - goto resume_frame; + else { + Py_DECREF(value); + res = Py_True; + } + stack_pointer[-1] = res; + DISPATCH(); } - TARGET(BUILD_SLICE) { + TARGET(TO_BOOL_LIST) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(BUILD_SLICE); - PyObject *step = NULL; - PyObject *stop; - PyObject *start; - PyObject *slice; - if (oparg == 3) { step = stack_pointer[-(oparg == 3 ? 1 : 0)]; } - stop = stack_pointer[-1 - (oparg == 3 ? 1 : 0)]; - start = stack_pointer[-2 - (oparg == 3 ? 1 : 0)]; - slice = PySlice_New(start, stop, step); - Py_DECREF(start); - Py_DECREF(stop); - Py_XDECREF(step); - if (slice == NULL) { STACK_SHRINK(((oparg == 3) ? 1 : 0)); goto pop_2_error; } - STACK_SHRINK(((oparg == 3) ? 1 : 0)); - STACK_SHRINK(1); - stack_pointer[-1] = slice; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_LIST); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); + PyObject *value; + PyObject *res; + value = stack_pointer[-1]; + DEOPT_IF(!PyList_CheckExact(value), TO_BOOL); + STAT_INC(TO_BOOL, hit); + res = Py_SIZE(value) ? Py_True : Py_False; + Py_DECREF(value); + stack_pointer[-1] = res; DISPATCH(); } - TARGET(CONVERT_VALUE) { + TARGET(TO_BOOL_NONE) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(CONVERT_VALUE); + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_NONE); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); PyObject *value; - PyObject *result; + PyObject *res; value = stack_pointer[-1]; - convertion_func_ptr conv_fn; - assert(oparg >= FVC_STR && oparg <= FVC_ASCII); - conv_fn = CONVERSION_FUNCTIONS[oparg]; - result = conv_fn(value); - Py_DECREF(value); - if (result == NULL) goto pop_1_error; - stack_pointer[-1] = result; + // This one is a bit weird, because we expect *some* failures: + DEOPT_IF(!Py_IsNone(value), TO_BOOL); + STAT_INC(TO_BOOL, hit); + res = Py_False; + stack_pointer[-1] = res; DISPATCH(); } - TARGET(FORMAT_SIMPLE) { + TARGET(TO_BOOL_STR) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(FORMAT_SIMPLE); + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_STR); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); PyObject *value; PyObject *res; value = stack_pointer[-1]; - /* If value is a unicode object, then we know the result - * of format(value) is value itself. */ - if (!PyUnicode_CheckExact(value)) { - res = PyObject_Format(value, NULL); - Py_DECREF(value); - if (res == NULL) goto pop_1_error; + DEOPT_IF(!PyUnicode_CheckExact(value), TO_BOOL); + STAT_INC(TO_BOOL, hit); + if (value == &_Py_STR(empty)) { + assert(_Py_IsImmortal(value)); + res = Py_False; } else { - res = value; + assert(Py_SIZE(value)); + Py_DECREF(value); + res = Py_True; } stack_pointer[-1] = res; DISPATCH(); } - TARGET(FORMAT_WITH_SPEC) { + TARGET(UNARY_INVERT) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(FORMAT_WITH_SPEC); - PyObject *fmt_spec; + INSTRUCTION_STATS(UNARY_INVERT); PyObject *value; PyObject *res; - fmt_spec = stack_pointer[-1]; - value = stack_pointer[-2]; - res = PyObject_Format(value, fmt_spec); + value = stack_pointer[-1]; + res = PyNumber_Invert(value); Py_DECREF(value); - Py_DECREF(fmt_spec); - if (res == NULL) goto pop_2_error; - STACK_SHRINK(1); + if (res == NULL) goto pop_1_error; stack_pointer[-1] = res; DISPATCH(); } - TARGET(COPY) { + TARGET(UNARY_NEGATIVE) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(COPY); - PyObject *bottom; - PyObject *top; - bottom = stack_pointer[-1 - (oparg-1)]; - assert(oparg > 0); - top = Py_NewRef(bottom); - STACK_GROW(1); - stack_pointer[-1] = top; + INSTRUCTION_STATS(UNARY_NEGATIVE); + PyObject *value; + PyObject *res; + value = stack_pointer[-1]; + res = PyNumber_Negative(value); + Py_DECREF(value); + if (res == NULL) goto pop_1_error; + stack_pointer[-1] = res; DISPATCH(); } - TARGET(BINARY_OP) { + TARGET(UNARY_NOT) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(UNARY_NOT); + PyObject *value; + PyObject *res; + value = stack_pointer[-1]; + assert(PyBool_Check(value)); + res = Py_IsFalse(value) ? Py_True : Py_False; + stack_pointer[-1] = res; + DISPATCH(); + } + + TARGET(UNPACK_EX) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(UNPACK_EX); + PyObject *seq; + seq = stack_pointer[-1]; + int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8); + PyObject **top = stack_pointer + totalargs - 1; + int res = _PyEval_UnpackIterable(tstate, seq, oparg & 0xFF, oparg >> 8, top); + Py_DECREF(seq); + if (res == 0) goto pop_1_error; + stack_pointer += (oparg >> 8) + (oparg & 0xFF); + DISPATCH(); + } + + TARGET(UNPACK_SEQUENCE) { frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(BINARY_OP); - PREDICTED(BINARY_OP); + INSTRUCTION_STATS(UNPACK_SEQUENCE); + PREDICTED(UNPACK_SEQUENCE); _Py_CODEUNIT *this_instr = next_instr - 2; - static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); - PyObject *rhs; - PyObject *lhs; - PyObject *res; - // _SPECIALIZE_BINARY_OP - rhs = stack_pointer[-1]; - lhs = stack_pointer[-2]; + PyObject *seq; + // _SPECIALIZE_UNPACK_SEQUENCE + seq = stack_pointer[-1]; { uint16_t counter = read_u16(&this_instr[1].cache); TIER_ONE_ONLY #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { next_instr = this_instr; - _Py_Specialize_BinaryOp(lhs, rhs, next_instr, oparg, LOCALS_ARRAY); + _Py_Specialize_UnpackSequence(seq, next_instr, oparg); DISPATCH_SAME_OPARG(); } - STAT_INC(BINARY_OP, deferred); + STAT_INC(UNPACK_SEQUENCE, deferred); DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); #endif /* ENABLE_SPECIALIZATION */ - assert(NB_ADD <= oparg); - assert(oparg <= NB_INPLACE_XOR); + (void)seq; + (void)counter; } - // _BINARY_OP + // _UNPACK_SEQUENCE { - assert(_PyEval_BinaryOps[oparg]); - res = _PyEval_BinaryOps[oparg](lhs, rhs); - Py_DECREF(lhs); - Py_DECREF(rhs); - if (res == NULL) goto pop_2_error; + PyObject **top = stack_pointer + oparg - 1; + int res = _PyEval_UnpackIterable(tstate, seq, oparg, -1, top); + Py_DECREF(seq); + if (res == 0) goto pop_1_error; } - STACK_SHRINK(1); - stack_pointer[-1] = res; + stack_pointer += -1 + oparg; DISPATCH(); } - TARGET(SWAP) { + TARGET(UNPACK_SEQUENCE_LIST) { frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(SWAP); - PyObject *top; - PyObject *bottom; - top = stack_pointer[-1]; - bottom = stack_pointer[-2 - (oparg-2)]; - assert(oparg >= 2); - stack_pointer[-2 - (oparg-2)] = top; - stack_pointer[-1] = bottom; - DISPATCH(); - } - - TARGET(INSTRUMENTED_INSTRUCTION) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_INSTRUCTION); - int next_opcode = _Py_call_instrumentation_instruction( - tstate, frame, this_instr); - if (next_opcode < 0) goto error; - next_instr = this_instr; - if (_PyOpcode_Caches[next_opcode]) { - INCREMENT_ADAPTIVE_COUNTER(this_instr[1].cache); - } - assert(next_opcode > 0 && next_opcode < 256); - opcode = next_opcode; - DISPATCH_GOTO(); - } - - TARGET(INSTRUMENTED_JUMP_FORWARD) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(INSTRUMENTED_JUMP_FORWARD); - INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_JUMP); - DISPATCH(); - } - - TARGET(INSTRUMENTED_JUMP_BACKWARD) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(INSTRUMENTED_JUMP_BACKWARD); - CHECK_EVAL_BREAKER(); - INSTRUMENTED_JUMP(this_instr, next_instr - oparg, PY_MONITORING_EVENT_JUMP); - DISPATCH(); - } - - TARGET(INSTRUMENTED_POP_JUMP_IF_TRUE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_TRUE); - PyObject *cond = POP(); - assert(PyBool_Check(cond)); - int flag = Py_IsTrue(cond); - int offset = flag * oparg; - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + INSTRUCTION_STATS(UNPACK_SEQUENCE_LIST); + static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); + PyObject *seq; + PyObject **values; + seq = stack_pointer[-1]; + values = &stack_pointer[-1]; + DEOPT_IF(!PyList_CheckExact(seq), UNPACK_SEQUENCE); + DEOPT_IF(PyList_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); + STAT_INC(UNPACK_SEQUENCE, hit); + PyObject **items = _PyList_ITEMS(seq); + for (int i = oparg; --i >= 0; ) { + *values++ = Py_NewRef(items[i]); + } + Py_DECREF(seq); + stack_pointer += -1 + oparg; DISPATCH(); } - TARGET(INSTRUMENTED_POP_JUMP_IF_FALSE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + TARGET(UNPACK_SEQUENCE_TUPLE) { + frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_FALSE); - PyObject *cond = POP(); - assert(PyBool_Check(cond)); - int flag = Py_IsFalse(cond); - int offset = flag * oparg; - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + INSTRUCTION_STATS(UNPACK_SEQUENCE_TUPLE); + static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); + PyObject *seq; + PyObject **values; + seq = stack_pointer[-1]; + values = &stack_pointer[-1]; + DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); + DEOPT_IF(PyTuple_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE); + STAT_INC(UNPACK_SEQUENCE, hit); + PyObject **items = _PyTuple_ITEMS(seq); + for (int i = oparg; --i >= 0; ) { + *values++ = Py_NewRef(items[i]); + } + Py_DECREF(seq); + stack_pointer += -1 + oparg; DISPATCH(); } - TARGET(INSTRUMENTED_POP_JUMP_IF_NONE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + TARGET(UNPACK_SEQUENCE_TWO_TUPLE) { + frame->instr_ptr = next_instr; next_instr += 2; - INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NONE); - PyObject *value = POP(); - int flag = Py_IsNone(value); - int offset; - if (flag) { - offset = oparg; - } - else { - Py_DECREF(value); - offset = 0; - } - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | flag; - #endif - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + INSTRUCTION_STATS(UNPACK_SEQUENCE_TWO_TUPLE); + static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); + PyObject *seq; + PyObject **values; + seq = stack_pointer[-1]; + values = &stack_pointer[-1]; + DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE); + DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE); + assert(oparg == 2); + STAT_INC(UNPACK_SEQUENCE, hit); + values[0] = Py_NewRef(PyTuple_GET_ITEM(seq, 1)); + values[1] = Py_NewRef(PyTuple_GET_ITEM(seq, 0)); + Py_DECREF(seq); + stack_pointer += -1 + oparg; DISPATCH(); } - TARGET(INSTRUMENTED_POP_JUMP_IF_NOT_NONE) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NOT_NONE); - PyObject *value = POP(); - int offset; - int nflag = Py_IsNone(value); - if (nflag) { - offset = 0; + TARGET(WITH_EXCEPT_START) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(WITH_EXCEPT_START); + PyObject *val; + PyObject *lasti; + PyObject *exit_func; + PyObject *res; + val = stack_pointer[-1]; + lasti = stack_pointer[-3]; + exit_func = stack_pointer[-4]; + /* At the top of the stack are 4 values: + - val: TOP = exc_info() + - unused: SECOND = previous exception + - lasti: THIRD = lasti of exception in exc_info() + - exit_func: FOURTH = the context.__exit__ bound method + We call FOURTH(type(TOP), TOP, GetTraceback(TOP)). + Then we push the __exit__ return value. + */ + PyObject *exc, *tb; + assert(val && PyExceptionInstance_Check(val)); + exc = PyExceptionInstance_Class(val); + tb = PyException_GetTraceback(val); + if (tb == NULL) { + tb = Py_None; } else { - Py_DECREF(value); - offset = oparg; + Py_DECREF(tb); } - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | !nflag; - #endif - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + assert(PyLong_Check(lasti)); + (void)lasti; // Shut up compiler warning if asserts are off + PyObject *stack[4] = {NULL, exc, val, tb}; + res = PyObject_Vectorcall(exit_func, stack + 1, + 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); + if (res == NULL) goto error; + stack_pointer[0] = res; + stack_pointer += 1; DISPATCH(); } - TARGET(EXTENDED_ARG) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(EXTENDED_ARG); - assert(oparg); - opcode = next_instr->op.code; - oparg = oparg << 8 | next_instr->op.arg; - PRE_DISPATCH_GOTO(); - DISPATCH_GOTO(); - } - - TARGET(CACHE) { + TARGET(YIELD_VALUE) { frame->instr_ptr = next_instr; next_instr += 1; - INSTRUCTION_STATS(CACHE); - assert(0 && "Executing a cache."); - Py_UNREACHABLE(); - } - - TARGET(RESERVED) { + INSTRUCTION_STATS(YIELD_VALUE); + PyObject *retval; + retval = stack_pointer[-1]; + // NOTE: It's important that YIELD_VALUE never raises an exception! + // The compiler treats any exception raised here as a failed close() + // or throw() call. + assert(frame != &entry_frame); frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(RESERVED); - assert(0 && "Executing RESERVED instruction."); - Py_UNREACHABLE(); + PyGenObject *gen = _PyFrame_GetGenerator(frame); + assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); + assert(oparg == 0 || oparg == 1); + gen->gi_frame_state = FRAME_SUSPENDED + oparg; + _PyFrame_SetStackPointer(frame, stack_pointer - 1); + tstate->exc_info = gen->gi_exc_state.previous_item; + gen->gi_exc_state.previous_item = NULL; + _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *gen_frame = frame; + frame = tstate->current_frame = frame->previous; + gen_frame->previous = NULL; + _PyFrame_StackPush(frame, retval); + /* We don't know which of these is relevant here, so keep them equal */ + assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); + LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); + goto resume_frame; } - #undef TIER_ONE diff --git a/Python/getargs.c b/Python/getargs.c index c0c2eb27184e3c..0c4ce282f48764 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -1,6 +1,7 @@ /* New getargs implementation */ +#define PY_CXX_CONST const #include "Python.h" #include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_dict.h" // _PyDict_HasOnlyStringKeys() @@ -12,10 +13,10 @@ PyAPI_FUNC(int) _PyArg_Parse_SizeT(PyObject *, const char *, ...); PyAPI_FUNC(int) _PyArg_ParseTuple_SizeT(PyObject *, const char *, ...); PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywords_SizeT(PyObject *, PyObject *, - const char *, char **, ...); + const char *, const char * const *, ...); PyAPI_FUNC(int) _PyArg_VaParse_SizeT(PyObject *, const char *, va_list); PyAPI_FUNC(int) _PyArg_VaParseTupleAndKeywords_SizeT(PyObject *, PyObject *, - const char *, char **, va_list); + const char *, const char * const *, va_list); #define FLAG_COMPAT 1 @@ -54,7 +55,7 @@ static Py_ssize_t convertbuffer(PyObject *, const void **p, const char **); static int getbuffer(PyObject *, Py_buffer *, const char**); static int vgetargskeywords(PyObject *, PyObject *, - const char *, char **, va_list *, int); + const char *, const char * const *, va_list *, int); static int vgetargskeywordsfast(PyObject *, PyObject *, struct _PyArg_Parser *, va_list *, int); static int vgetargskeywordsfast_impl(PyObject *const *args, Py_ssize_t nargs, @@ -477,7 +478,7 @@ converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags, } else if (c == ':' || c == ';' || c == '\0') break; - else if (level == 0 && Py_ISALPHA(c)) + else if (level == 0 && Py_ISALPHA(c) && c != 'e') n++; } @@ -1247,7 +1248,7 @@ int PyArg_ParseTupleAndKeywords(PyObject *args, PyObject *keywords, const char *format, - char **kwlist, ...) + const char * const *kwlist, ...) { int retval; va_list va; @@ -1271,7 +1272,7 @@ int _PyArg_ParseTupleAndKeywords_SizeT(PyObject *args, PyObject *keywords, const char *format, - char **kwlist, ...) + const char * const *kwlist, ...) { int retval; va_list va; @@ -1297,7 +1298,7 @@ int PyArg_VaParseTupleAndKeywords(PyObject *args, PyObject *keywords, const char *format, - char **kwlist, va_list va) + const char * const *kwlist, va_list va) { int retval; va_list lva; @@ -1322,7 +1323,7 @@ int _PyArg_VaParseTupleAndKeywords_SizeT(PyObject *args, PyObject *keywords, const char *format, - char **kwlist, va_list va) + const char * const *kwlist, va_list va) { int retval; va_list lva; @@ -1460,7 +1461,7 @@ PyArg_ValidateKeywordArguments(PyObject *kwargs) static int vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, - char **kwlist, va_list *p_va, int flags) + const char * const *kwlist, va_list *p_va, int flags) { char msgbuf[512]; int levels[32]; diff --git a/Python/hashtable.c b/Python/hashtable.c index 8f5e8168ba1339..faf68fe4ff0bca 100644 --- a/Python/hashtable.c +++ b/Python/hashtable.c @@ -45,7 +45,7 @@ */ #include "Python.h" -#include "pycore_hashtable.h" +#include "pycore_hashtable.h" // export _Py_hashtable_new() #include "pycore_pyhash.h" // _Py_HashPointerRaw() #define HASHTABLE_MIN_SIZE 16 diff --git a/Python/import.c b/Python/import.c index f37393bbdc4269..2dd95d8364a0be 100644 --- a/Python/import.c +++ b/Python/import.c @@ -252,18 +252,21 @@ import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *n NOTE: because of this, initializing must be set *before* stuffing the new module in sys.modules. */ - spec = PyObject_GetAttr(mod, &_Py_ID(__spec__)); - int busy = _PyModuleSpec_IsInitializing(spec); - Py_XDECREF(spec); - if (busy) { - /* Wait until module is done importing. */ - PyObject *value = PyObject_CallMethodOneArg( - IMPORTLIB(interp), &_Py_ID(_lock_unlock_module), name); - if (value == NULL) { - return -1; - } - Py_DECREF(value); + int rc = PyObject_GetOptionalAttr(mod, &_Py_ID(__spec__), &spec); + if (rc > 0) { + rc = _PyModuleSpec_IsInitializing(spec); + Py_DECREF(spec); + } + if (rc <= 0) { + return rc; + } + /* Wait until module is done importing. */ + PyObject *value = PyObject_CallMethodOneArg( + IMPORTLIB(interp), &_Py_ID(_lock_unlock_module), name); + if (value == NULL) { + return -1; } + Py_DECREF(value); return 0; } @@ -415,11 +418,7 @@ remove_module(PyThreadState *tstate, PyObject *name) Py_ssize_t _PyImport_GetNextModuleIndex(void) { - PyThread_acquire_lock(EXTENSIONS.mutex, WAIT_LOCK); - LAST_MODULE_INDEX++; - Py_ssize_t index = LAST_MODULE_INDEX; - PyThread_release_lock(EXTENSIONS.mutex); - return index; + return _Py_atomic_add_ssize(&LAST_MODULE_INDEX, 1) + 1; } static const char * @@ -879,13 +878,13 @@ gets even messier. static inline void extensions_lock_acquire(void) { - PyThread_acquire_lock(_PyRuntime.imports.extensions.mutex, WAIT_LOCK); + PyMutex_Lock(&_PyRuntime.imports.extensions.mutex); } static inline void extensions_lock_release(void) { - PyThread_release_lock(_PyRuntime.imports.extensions.mutex); + PyMutex_Unlock(&_PyRuntime.imports.extensions.mutex); } /* Magic for extension modules (built-in as well as dynamically diff --git a/Python/initconfig.c b/Python/initconfig.c index d7f3195ed5fcf0..06e317907b8ec9 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -293,6 +293,8 @@ static const char usage_envvars[] = "PYTHON_FROZEN_MODULES : if this variable is set, it determines whether or not \n" " frozen modules should be used. The default is \"on\" (or \"off\" if you are \n" " running a local build).\n" +"PYTHON_COLORS : If this variable is set to 1, the interpreter will" +" colorize various kinds of output. Setting it to 0 deactivates this behavior.\n" "These variables have equivalent command-line parameters (see --help for details):\n" "PYTHONDEBUG : enable parser debug mode (-d)\n" "PYTHONDONTWRITEBYTECODE : don't write .pyc files (-B)\n" diff --git a/Python/lock.c b/Python/lock.c index bc43b1ab192fea..e9279f0b92a5e7 100644 --- a/Python/lock.c +++ b/Python/lock.c @@ -21,7 +21,7 @@ static const _PyTime_t TIME_TO_BE_FAIR_NS = 1000*1000; // Spin for a bit before parking the thread. This is only enabled for // `--disable-gil` builds because it is unlikely to be helpful if the GIL is // enabled. -#if Py_NOGIL +#if Py_GIL_DISABLED static const int MAX_SPIN_COUNT = 40; #else static const int MAX_SPIN_COUNT = 0; diff --git a/Python/optimizer.c b/Python/optimizer.c index 64a15e0dd6696e..dd24fbebbfd2a9 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -325,7 +325,7 @@ uop_dealloc(_PyUOpExecutorObject *self) { } const char * -_PyUopName(int index) +_PyUOpName(int index) { if (index <= MAX_REAL_OPCODE) { return _PyOpcode_OpName[index]; @@ -347,7 +347,7 @@ uop_item(_PyUOpExecutorObject *self, Py_ssize_t index) PyErr_SetNone(PyExc_IndexError); return NULL; } - const char *name = _PyUopName(self->trace[index].opcode); + const char *name = _PyUOpName(self->trace[index].opcode); if (name == NULL) { name = ""; } @@ -388,10 +388,11 @@ PyTypeObject _PyUOpExecutor_Type = { /* TO DO -- Generate these tables */ static const uint16_t -_PyUop_Replacements[OPCODE_METADATA_SIZE] = { +_PyUOp_Replacements[OPCODE_METADATA_SIZE] = { [_ITER_JUMP_RANGE] = _GUARD_NOT_EXHAUSTED_RANGE, [_ITER_JUMP_LIST] = _GUARD_NOT_EXHAUSTED_LIST, [_ITER_JUMP_TUPLE] = _GUARD_NOT_EXHAUSTED_TUPLE, + [_FOR_ITER] = _FOR_ITER_TIER_TWO, }; static const uint16_t @@ -425,7 +426,6 @@ translate_bytecode_to_trace( _Py_CODEUNIT *initial_instr = instr; int trace_length = 0; int max_length = buffer_size; - int reserved = 0; struct { PyCodeObject *code; _Py_CODEUNIT *instr; @@ -451,12 +451,10 @@ translate_bytecode_to_trace( #define ADD_TO_TRACE(OPCODE, OPARG, OPERAND, TARGET) \ DPRINTF(2, \ " ADD_TO_TRACE(%s, %d, %" PRIu64 ")\n", \ - _PyUopName(OPCODE), \ + _PyUOpName(OPCODE), \ (OPARG), \ (uint64_t)(OPERAND)); \ assert(trace_length < max_length); \ - assert(reserved > 0); \ - reserved--; \ trace[trace_length].opcode = (OPCODE); \ trace[trace_length].oparg = (OPARG); \ trace[trace_length].operand = (OPERAND); \ @@ -473,11 +471,10 @@ translate_bytecode_to_trace( (opname), (n), max_length - trace_length); \ OPT_STAT_INC(trace_too_long); \ goto done; \ - } \ - reserved = (n); // Keep ADD_TO_TRACE honest + } -// Reserve space for main+stub uops, plus 3 for _SET_IP, _CHECK_VALIDITY and _EXIT_TRACE -#define RESERVE(main, stub) RESERVE_RAW((main) + (stub) + 3, _PyUopName(opcode)) +// Reserve space for N uops, plus 3 for _SET_IP, _CHECK_VALIDITY and _EXIT_TRACE +#define RESERVE(needed) RESERVE_RAW((needed) + 3, _PyUOpName(opcode)) // Trace stack operations (used by _PUSH_FRAME, _POP_FRAME) #define TRACE_STACK_PUSH() \ @@ -542,15 +539,15 @@ translate_bytecode_to_trace( case POP_JUMP_IF_FALSE: case POP_JUMP_IF_TRUE: { - RESERVE(1, 0); + RESERVE(1); int counter = instr[1].cache; int bitcount = _Py_popcount32(counter); int jump_likely = bitcount > 8; uint32_t uopcode = BRANCH_TO_GUARD[opcode - POP_JUMP_IF_FALSE][jump_likely]; _Py_CODEUNIT *next_instr = instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]]; DPRINTF(4, "%s(%d): counter=%x, bitcount=%d, likely=%d, uopcode=%s\n", - _PyUopName(opcode), oparg, - counter, bitcount, jump_likely, _PyUopName(uopcode)); + _PyUOpName(opcode), oparg, + counter, bitcount, jump_likely, _PyUOpName(uopcode)); ADD_TO_TRACE(uopcode, max_length, 0, target); if (jump_likely) { _Py_CODEUNIT *target_instr = next_instr + oparg; @@ -565,7 +562,7 @@ translate_bytecode_to_trace( case JUMP_BACKWARD: { if (instr + 2 - oparg == initial_instr && code == initial_code) { - RESERVE(1, 0); + RESERVE(1); ADD_TO_TRACE(_JUMP_TO_TOP, 0, 0, 0); } else { @@ -577,7 +574,7 @@ translate_bytecode_to_trace( case JUMP_FORWARD: { - RESERVE(0, 0); + RESERVE(0); // This will emit two _SET_IP instructions; leave it to the optimizer instr += oparg; break; @@ -589,7 +586,7 @@ translate_bytecode_to_trace( if (expansion->nuops > 0) { // Reserve space for nuops (+ _SET_IP + _EXIT_TRACE) int nuops = expansion->nuops; - RESERVE(nuops, 0); + RESERVE(nuops); if (expansion->uops[nuops-1].uop == _POP_FRAME) { // Check for trace stack underflow now: // We can't bail e.g. in the middle of @@ -618,8 +615,13 @@ translate_bytecode_to_trace( oparg += extras; } } - if (_PyUop_Replacements[uop]) { - uop = _PyUop_Replacements[uop]; + if (_PyUOp_Replacements[uop]) { + uop = _PyUOp_Replacements[uop]; + if (uop == _FOR_ITER_TIER_TWO) { + target += 1 + INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1; + assert(_PyCode_CODE(code)[target-1].op.code == END_FOR || + _PyCode_CODE(code)[target-1].op.code == INSTRUMENTED_END_FOR); + } } break; case OPARG_CACHE_1: @@ -710,7 +712,7 @@ translate_bytecode_to_trace( } break; } - DPRINTF(2, "Unsupported opcode %s\n", _PyUopName(opcode)); + DPRINTF(2, "Unsupported opcode %s\n", _PyUOpName(opcode)); OPT_UNSUPPORTED_OPCODE(opcode); goto done; // Break out of loop } // End default @@ -731,13 +733,12 @@ translate_bytecode_to_trace( if (trace_length > 4) { ADD_TO_TRACE(_EXIT_TRACE, 0, 0, target); DPRINTF(1, - "Created a trace for %s (%s:%d) at byte offset %d -- length %d+%d\n", + "Created a trace for %s (%s:%d) at byte offset %d -- length %d\n", PyUnicode_AsUTF8(code->co_qualname), PyUnicode_AsUTF8(code->co_filename), code->co_firstlineno, 2 * INSTR_IP(initial_instr, code), - trace_length, - buffer_size - max_length); + trace_length); OPT_HIST(trace_length + buffer_size - max_length, trace_length_hist); return 1; } @@ -831,7 +832,7 @@ make_executor_from_uops(_PyUOpInstruction *buffer, _PyBloomFilter *dependencies) dest--; } assert(dest == -1); - executor->base.execute = _PyUopExecute; + executor->base.execute = _PyUOpExecute; _Py_ExecutorInit((_PyExecutorObject *)executor, dependencies); #ifdef Py_DEBUG char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); @@ -844,7 +845,7 @@ make_executor_from_uops(_PyUOpInstruction *buffer, _PyBloomFilter *dependencies) for (int i = 0; i < length; i++) { printf("%4d %s(%d, %d, %" PRIu64 ")\n", i, - _PyUopName(executor->trace[i].opcode), + _PyUOpName(executor->trace[i].opcode), executor->trace[i].oparg, executor->trace[i].target, executor->trace[i].operand); @@ -887,11 +888,11 @@ uop_optimize( return 1; } -/* Dummy execute() function for Uop Executor. +/* Dummy execute() function for UOp Executor. * The actual implementation is inlined in ceval.c, * in _PyEval_EvalFrameDefault(). */ _PyInterpreterFrame * -_PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject **stack_pointer) +_PyUOpExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject **stack_pointer) { Py_FatalError("Tier 2 is now inlined into Tier 1"); } diff --git a/Python/parking_lot.c b/Python/parking_lot.c index 664e622cc17474..d44c1b4b93b4d2 100644 --- a/Python/parking_lot.c +++ b/Python/parking_lot.c @@ -118,10 +118,19 @@ _PySemaphore_PlatformWait(_PySemaphore *sema, _PyTime_t timeout) if (timeout >= 0) { struct timespec ts; +#if defined(CLOCK_MONOTONIC) && defined(HAVE_SEM_CLOCKWAIT) + _PyTime_t deadline = _PyTime_Add(_PyTime_GetMonotonicClock(), timeout); + + _PyTime_AsTimespec_clamp(deadline, &ts); + + err = sem_clockwait(&sema->platform_sem, CLOCK_MONOTONIC, &ts); +#else _PyTime_t deadline = _PyTime_Add(_PyTime_GetSystemClock(), timeout); - _PyTime_AsTimespec(deadline, &ts); + + _PyTime_AsTimespec_clamp(deadline, &ts); err = sem_timedwait(&sema->platform_sem, &ts); +#endif } else { err = sem_wait(&sema->platform_sem); @@ -151,7 +160,7 @@ _PySemaphore_PlatformWait(_PySemaphore *sema, _PyTime_t timeout) struct timespec ts; _PyTime_t deadline = _PyTime_Add(_PyTime_GetSystemClock(), timeout); - _PyTime_AsTimespec(deadline, &ts); + _PyTime_AsTimespec_clamp(deadline, &ts); err = pthread_cond_timedwait(&sema->cond, &sema->mutex, &ts); } diff --git a/Python/perf_trampoline.c b/Python/perf_trampoline.c index 208ced6c101dce..540b650192ed34 100644 --- a/Python/perf_trampoline.c +++ b/Python/perf_trampoline.c @@ -216,10 +216,24 @@ perf_map_write_entry(void *state, const void *code_addr, PyMem_RawFree(perf_map_entry); } +static void* +perf_map_init_state(void) +{ + PyUnstable_PerfMapState_Init(); + return NULL; +} + +static int +perf_map_free_state(void *state) +{ + PyUnstable_PerfMapState_Fini(); + return 0; +} + _PyPerf_Callbacks _Py_perfmap_callbacks = { - NULL, + &perf_map_init_state, &perf_map_write_entry, - NULL, + &perf_map_free_state, }; static int @@ -415,7 +429,6 @@ _PyPerfTrampoline_SetCallbacks(_PyPerf_Callbacks *callbacks) trampoline_api.write_state = callbacks->write_state; trampoline_api.free_state = callbacks->free_state; trampoline_api.state = NULL; - perf_status = PERF_STATUS_OK; #endif return 0; } @@ -434,6 +447,7 @@ _PyPerfTrampoline_Init(int activate) } if (!activate) { tstate->interp->eval_frame = NULL; + perf_status = PERF_STATUS_NO_INIT; } else { tstate->interp->eval_frame = py_trampoline_evaluator; @@ -444,6 +458,9 @@ _PyPerfTrampoline_Init(int activate) if (extra_code_index == -1) { return -1; } + if (trampoline_api.state == NULL && trampoline_api.init_state != NULL) { + trampoline_api.state = trampoline_api.init_state(); + } perf_status = PERF_STATUS_OK; } #endif @@ -454,16 +471,29 @@ int _PyPerfTrampoline_Fini(void) { #ifdef PY_HAVE_PERF_TRAMPOLINE + if (perf_status != PERF_STATUS_OK) { + return 0; + } PyThreadState *tstate = _PyThreadState_GET(); if (tstate->interp->eval_frame == py_trampoline_evaluator) { tstate->interp->eval_frame = NULL; } - free_code_arenas(); + if (perf_status == PERF_STATUS_OK) { + trampoline_api.free_state(trampoline_api.state); + } extra_code_index = -1; + perf_status = PERF_STATUS_NO_INIT; #endif return 0; } +void _PyPerfTrampoline_FreeArenas(void) { +#ifdef PY_HAVE_PERF_TRAMPOLINE + free_code_arenas(); +#endif + return; +} + int PyUnstable_PerfTrampoline_SetPersistAfterFork(int enable){ #ifdef PY_HAVE_PERF_TRAMPOLINE @@ -477,8 +507,8 @@ PyStatus _PyPerfTrampoline_AfterFork_Child(void) { #ifdef PY_HAVE_PERF_TRAMPOLINE - PyUnstable_PerfMapState_Fini(); if (persist_after_fork) { + _PyPerfTrampoline_Fini(); char filename[256]; pid_t parent_pid = getppid(); snprintf(filename, sizeof(filename), "/tmp/perf-%d.map", parent_pid); diff --git a/Python/pyhash.c b/Python/pyhash.c index f9060b8003a0a7..141407c265677a 100644 --- a/Python/pyhash.c +++ b/Python/pyhash.c @@ -83,8 +83,6 @@ static Py_ssize_t hashstats[Py_HASH_STATS_MAX + 1] = {0}; */ -Py_hash_t _Py_HashPointer(const void *); - Py_hash_t _Py_HashDouble(PyObject *inst, double v) { @@ -132,23 +130,13 @@ _Py_HashDouble(PyObject *inst, double v) } Py_hash_t -_Py_HashPointerRaw(const void *p) -{ - size_t y = (size_t)p; - /* bottom 3 or 4 bits are likely to be 0; rotate y by 4 to avoid - excessive hash collisions for dicts and sets */ - y = (y >> 4) | (y << (8 * SIZEOF_VOID_P - 4)); - return (Py_hash_t)y; -} - -Py_hash_t -_Py_HashPointer(const void *p) +Py_HashPointer(const void *ptr) { - Py_hash_t x = _Py_HashPointerRaw(p); - if (x == -1) { - x = -2; + Py_hash_t hash = _Py_HashPointerRaw(ptr); + if (hash == -1) { + hash = -2; } - return x; + return hash; } Py_hash_t diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index ac8d5208322882..45a119fcca7f2c 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -528,11 +528,6 @@ pycore_init_runtime(_PyRuntimeState *runtime, return status; } - status = _PyTime_Init(); - if (_PyStatus_EXCEPTION(status)) { - return status; - } - status = _PyImport_Init(); if (_PyStatus_EXCEPTION(status)) { return status; @@ -825,6 +820,11 @@ pycore_interp_init(PyThreadState *tstate) return status; } + status = _PyDtoa_Init(interp); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + // The GC must be initialized before the first GC collection. status = _PyGC_Init(interp); if (_PyStatus_EXCEPTION(status)) { @@ -1781,6 +1781,7 @@ finalize_interp_clear(PyThreadState *tstate) _PyXI_Fini(tstate->interp); _PyExc_ClearExceptionGroupType(tstate->interp); _Py_clear_generic_types(tstate->interp); + _PyDtoa_Fini(tstate->interp); /* Clear interpreter state and all thread states */ _PyInterpreterState_Clear(tstate); @@ -1797,6 +1798,7 @@ finalize_interp_clear(PyThreadState *tstate) _PyArg_Fini(); _Py_ClearFileSystemEncoding(); _PyPerfTrampoline_Fini(); + _PyPerfTrampoline_FreeArenas(); } finalize_interp_types(tstate->interp); @@ -1854,7 +1856,6 @@ Py_FinalizeEx(void) */ _PyAtExit_Call(tstate->interp); - PyUnstable_PerfMapState_Fini(); /* Copy the core config, PyInterpreterState_Delete() free the core config memory */ @@ -3055,13 +3056,13 @@ wait_for_thread_shutdown(PyThreadState *tstate) int Py_AtExit(void (*func)(void)) { struct _atexit_runtime_state *state = &_PyRuntime.atexit; - PyThread_acquire_lock(state->mutex, WAIT_LOCK); + PyMutex_Lock(&state->mutex); if (state->ncallbacks >= NEXITFUNCS) { - PyThread_release_lock(state->mutex); + PyMutex_Unlock(&state->mutex); return -1; } state->callbacks[state->ncallbacks++] = func; - PyThread_release_lock(state->mutex); + PyMutex_Unlock(&state->mutex); return 0; } @@ -3071,18 +3072,18 @@ call_ll_exitfuncs(_PyRuntimeState *runtime) atexit_callbackfunc exitfunc; struct _atexit_runtime_state *state = &runtime->atexit; - PyThread_acquire_lock(state->mutex, WAIT_LOCK); + PyMutex_Lock(&state->mutex); while (state->ncallbacks > 0) { /* pop last function from the list */ state->ncallbacks--; exitfunc = state->callbacks[state->ncallbacks]; state->callbacks[state->ncallbacks] = NULL; - PyThread_release_lock(state->mutex); + PyMutex_Unlock(&state->mutex); exitfunc(); - PyThread_acquire_lock(state->mutex, WAIT_LOCK); + PyMutex_Lock(&state->mutex); } - PyThread_release_lock(state->mutex); + PyMutex_Unlock(&state->mutex); fflush(stdout); fflush(stderr); diff --git a/Python/pystate.c b/Python/pystate.c index 89e9bddc6de060..1a7c0c968504d1 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -379,49 +379,23 @@ _Py_COMP_DIAG_IGNORE_DEPR_DECLS static const _PyRuntimeState initial = _PyRuntimeState_INIT(_PyRuntime); _Py_COMP_DIAG_POP -#define NUMLOCKS 8 #define LOCKS_INIT(runtime) \ { \ &(runtime)->interpreters.mutex, \ &(runtime)->xi.registry.mutex, \ - &(runtime)->unicode_state.ids.lock, \ + &(runtime)->unicode_state.ids.mutex, \ &(runtime)->imports.extensions.mutex, \ - &(runtime)->ceval.pending_mainthread.lock, \ + &(runtime)->ceval.pending_mainthread.mutex, \ &(runtime)->atexit.mutex, \ &(runtime)->audit_hooks.mutex, \ &(runtime)->allocators.mutex, \ } -static int -alloc_for_runtime(PyThread_type_lock locks[NUMLOCKS]) -{ - /* Force default allocator, since _PyRuntimeState_Fini() must - use the same allocator than this function. */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - for (int i = 0; i < NUMLOCKS; i++) { - PyThread_type_lock lock = PyThread_allocate_lock(); - if (lock == NULL) { - for (int j = 0; j < i; j++) { - PyThread_free_lock(locks[j]); - locks[j] = NULL; - } - break; - } - locks[i] = lock; - } - - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - return 0; -} - static void init_runtime(_PyRuntimeState *runtime, void *open_code_hook, void *open_code_userdata, _Py_AuditHookEntry *audit_hook_head, - Py_ssize_t unicode_next_index, - PyThread_type_lock locks[NUMLOCKS]) + Py_ssize_t unicode_next_index) { assert(!runtime->preinitializing); assert(!runtime->preinitialized); @@ -435,12 +409,6 @@ init_runtime(_PyRuntimeState *runtime, PyPreConfig_InitPythonConfig(&runtime->preconfig); - PyThread_type_lock *lockptrs[NUMLOCKS] = LOCKS_INIT(runtime); - for (int i = 0; i < NUMLOCKS; i++) { - assert(locks[i] != NULL); - *lockptrs[i] = locks[i]; - } - // Set it to the ID of the main thread of the main interpreter. runtime->main_thread = PyThread_get_thread_ident(); @@ -466,11 +434,6 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) // is called multiple times. Py_ssize_t unicode_next_index = runtime->unicode_state.ids.next_index; - PyThread_type_lock locks[NUMLOCKS]; - if (alloc_for_runtime(locks) != 0) { - return _PyStatus_NO_MEMORY(); - } - if (runtime->_initialized) { // Py_Initialize() must be running again. // Reset to _PyRuntimeState_INIT. @@ -489,7 +452,7 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) } init_runtime(runtime, open_code_hook, open_code_userdata, audit_hook_head, - unicode_next_index, locks); + unicode_next_index); return _PyStatus_OK(); } @@ -509,23 +472,6 @@ _PyRuntimeState_Fini(_PyRuntimeState *runtime) if (PyThread_tss_is_created(&runtime->trashTSSkey)) { PyThread_tss_delete(&runtime->trashTSSkey); } - - /* Force the allocator used by _PyRuntimeState_Init(). */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); -#define FREE_LOCK(LOCK) \ - if (LOCK != NULL) { \ - PyThread_free_lock(LOCK); \ - LOCK = NULL; \ - } - - PyThread_type_lock *lockptrs[NUMLOCKS] = LOCKS_INIT(runtime); - for (int i = 0; i < NUMLOCKS; i++) { - FREE_LOCK(*lockptrs[i]); - } - -#undef FREE_LOCK - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); } #ifdef HAVE_FORK @@ -537,28 +483,19 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime) // This was initially set in _PyRuntimeState_Init(). runtime->main_thread = PyThread_get_thread_ident(); - /* Force default allocator, since _PyRuntimeState_Fini() must - use the same allocator than this function. */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - PyThread_type_lock *lockptrs[NUMLOCKS] = LOCKS_INIT(runtime); - int reinit_err = 0; - for (int i = 0; i < NUMLOCKS; i++) { - reinit_err += _PyThread_at_fork_reinit(lockptrs[i]); - } - - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - // Clears the parking lot. Any waiting threads are dead. This must be // called before releasing any locks that use the parking lot. _PyParkingLot_AfterFork(); + // Re-initialize global locks + PyMutex *locks[] = LOCKS_INIT(runtime); + for (size_t i = 0; i < Py_ARRAY_LENGTH(locks); i++) { + _PyMutex_at_fork_reinit(locks[i]); + } + /* bpo-42540: id_mutex is freed by _PyInterpreterState_Delete, which does * not force the default allocator. */ - reinit_err += _PyThread_at_fork_reinit(&runtime->interpreters.main->id_mutex); - - if (reinit_err < 0) { + if (_PyThread_at_fork_reinit(&runtime->interpreters.main->id_mutex) < 0) { return _PyStatus_ERR("Failed to reinitialize runtime locks"); } @@ -594,24 +531,6 @@ _PyInterpreterState_Enable(_PyRuntimeState *runtime) { struct pyinterpreters *interpreters = &runtime->interpreters; interpreters->next_id = 0; - - /* Py_Finalize() calls _PyRuntimeState_Fini() which clears the mutex. - Create a new mutex if needed. */ - if (interpreters->mutex == NULL) { - /* Force default allocator, since _PyRuntimeState_Fini() must - use the same allocator than this function. */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - interpreters->mutex = PyThread_allocate_lock(); - - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - if (interpreters->mutex == NULL) { - return _PyStatus_ERR("Can't initialize threads for interpreter"); - } - } - return _PyStatus_OK(); } @@ -654,8 +573,7 @@ free_interpreter(PyInterpreterState *interp) static PyStatus init_interpreter(PyInterpreterState *interp, _PyRuntimeState *runtime, int64_t id, - PyInterpreterState *next, - PyThread_type_lock pending_lock) + PyInterpreterState *next) { if (interp->_initialized) { return _PyStatus_ERR("interpreter already initialized"); @@ -684,7 +602,7 @@ init_interpreter(PyInterpreterState *interp, return status; } - _PyEval_InitState(interp, pending_lock); + _PyEval_InitState(interp); _PyGC_InitState(&interp->gc); PyConfig_InitPythonConfig(&interp->config); _PyType_InitCache(interp); @@ -730,11 +648,6 @@ _PyInterpreterState_New(PyThreadState *tstate, PyInterpreterState **pinterp) } } - PyThread_type_lock pending_lock = PyThread_allocate_lock(); - if (pending_lock == NULL) { - return _PyStatus_NO_MEMORY(); - } - /* We completely serialize creation of multiple interpreters, since it simplifies things here and blocking concurrent calls isn't a problem. Regardless, we must fully block subinterpreter creation until @@ -781,11 +694,10 @@ _PyInterpreterState_New(PyThreadState *tstate, PyInterpreterState **pinterp) interpreters->head = interp; status = init_interpreter(interp, runtime, - id, old_head, pending_lock); + id, old_head); if (_PyStatus_EXCEPTION(status)) { goto error; } - pending_lock = NULL; HEAD_UNLOCK(runtime); @@ -796,9 +708,6 @@ _PyInterpreterState_New(PyThreadState *tstate, PyInterpreterState **pinterp) error: HEAD_UNLOCK(runtime); - if (pending_lock != NULL) { - PyThread_free_lock(pending_lock); - } if (interp != NULL) { free_interpreter(interp); } @@ -1003,8 +912,6 @@ PyInterpreterState_Delete(PyInterpreterState *interp) zapthreads(interp); - _PyEval_FiniState(&interp->ceval); - // XXX These two calls should be done at the end of clear_interpreter(), // but currently some objects get decref'ed after that. #ifdef Py_REF_DEBUG @@ -1353,20 +1260,19 @@ allocate_chunk(int size_in_bytes, _PyStackChunk* previous) return res; } -static PyThreadState * +static _PyThreadStateImpl * alloc_threadstate(void) { - return PyMem_RawCalloc(1, sizeof(PyThreadState)); + return PyMem_RawCalloc(1, sizeof(_PyThreadStateImpl)); } static void -free_threadstate(PyThreadState *tstate) +free_threadstate(_PyThreadStateImpl *tstate) { // The initial thread state of the interpreter is allocated // as part of the interpreter state so should not be freed. - if (tstate == &tstate->interp->_initial_thread) { + if (tstate == &tstate->base.interp->_initial_thread) { // Restore to _PyThreadState_INIT. - tstate = &tstate->interp->_initial_thread; memcpy(tstate, &initial._main_interpreter._initial_thread, sizeof(*tstate)); @@ -1385,9 +1291,10 @@ free_threadstate(PyThreadState *tstate) */ static void -init_threadstate(PyThreadState *tstate, +init_threadstate(_PyThreadStateImpl *_tstate, PyInterpreterState *interp, uint64_t id, int whence) { + PyThreadState *tstate = (PyThreadState *)_tstate; if (tstate->_status.initialized) { Py_FatalError("thread state already initialized"); } @@ -1444,13 +1351,13 @@ add_threadstate(PyInterpreterState *interp, PyThreadState *tstate, static PyThreadState * new_threadstate(PyInterpreterState *interp, int whence) { - PyThreadState *tstate; + _PyThreadStateImpl *tstate; _PyRuntimeState *runtime = interp->runtime; // We don't need to allocate a thread state for the main interpreter // (the common case), but doing it later for the other case revealed a // reentrancy problem (deadlock). So for now we always allocate before // taking the interpreters lock. See GH-96071. - PyThreadState *new_tstate = alloc_threadstate(); + _PyThreadStateImpl *new_tstate = alloc_threadstate(); int used_newtstate; if (new_tstate == NULL) { return NULL; @@ -1482,14 +1389,14 @@ new_threadstate(PyInterpreterState *interp, int whence) } init_threadstate(tstate, interp, id, whence); - add_threadstate(interp, tstate, old_head); + add_threadstate(interp, (PyThreadState *)tstate, old_head); HEAD_UNLOCK(runtime); if (!used_newtstate) { // Must be called with lock unlocked to avoid re-entrancy deadlock. PyMem_RawFree(new_tstate); } - return tstate; + return (PyThreadState *)tstate; } PyThreadState * @@ -1678,7 +1585,7 @@ zapthreads(PyInterpreterState *interp) while ((tstate = interp->threads.head) != NULL) { tstate_verify_not_active(tstate); tstate_delete_common(tstate); - free_threadstate(tstate); + free_threadstate((_PyThreadStateImpl *)tstate); } } @@ -1689,7 +1596,7 @@ PyThreadState_Delete(PyThreadState *tstate) _Py_EnsureTstateNotNULL(tstate); tstate_verify_not_active(tstate); tstate_delete_common(tstate); - free_threadstate(tstate); + free_threadstate((_PyThreadStateImpl *)tstate); } @@ -1701,7 +1608,7 @@ _PyThreadState_DeleteCurrent(PyThreadState *tstate) tstate_delete_common(tstate); current_fast_clear(tstate->interp->runtime); _PyEval_ReleaseLock(tstate->interp, NULL); - free_threadstate(tstate); + free_threadstate((_PyThreadStateImpl *)tstate); } void @@ -1751,7 +1658,7 @@ _PyThreadState_DeleteExcept(PyThreadState *tstate) for (p = list; p; p = next) { next = p->next; PyThreadState_Clear(p); - free_threadstate(p); + free_threadstate((_PyThreadStateImpl *)p); } } @@ -1857,7 +1764,7 @@ tstate_deactivate(PyThreadState *tstate) static int tstate_try_attach(PyThreadState *tstate) { -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED int expected = _Py_THREAD_DETACHED; if (_Py_atomic_compare_exchange_int( &tstate->state, @@ -1877,7 +1784,7 @@ static void tstate_set_detached(PyThreadState *tstate) { assert(tstate->state == _Py_THREAD_ATTACHED); -#ifdef Py_NOGIL +#ifdef Py_GIL_DISABLED _Py_atomic_store_int(&tstate->state, _Py_THREAD_DETACHED); #else tstate->state = _Py_THREAD_DETACHED; diff --git a/Python/pytime.c b/Python/pytime.c index e4813d4a9c2a2a..77cb95f8feb179 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -55,6 +55,43 @@ #endif +static _PyTime_t +_PyTime_GCD(_PyTime_t x, _PyTime_t y) +{ + // Euclidean algorithm + assert(x >= 1); + assert(y >= 1); + while (y != 0) { + _PyTime_t tmp = y; + y = x % y; + x = tmp; + } + assert(x >= 1); + return x; +} + + +int +_PyTimeFraction_Set(_PyTimeFraction *frac, _PyTime_t numer, _PyTime_t denom) +{ + if (numer < 1 || denom < 1) { + return -1; + } + + _PyTime_t gcd = _PyTime_GCD(numer, denom); + frac->numer = numer / gcd; + frac->denom = denom / gcd; + return 0; +} + + +double +_PyTimeFraction_Resolution(const _PyTimeFraction *frac) +{ + return (double)frac->numer / (double)frac->denom / 1e9; +} + + static void pytime_time_t_overflow(void) { @@ -152,11 +189,17 @@ _PyTime_Mul(_PyTime_t t, _PyTime_t k) } - - _PyTime_t -_PyTime_MulDiv(_PyTime_t ticks, _PyTime_t mul, _PyTime_t div) +_PyTimeFraction_Mul(_PyTime_t ticks, const _PyTimeFraction *frac) { + const _PyTime_t mul = frac->numer; + const _PyTime_t div = frac->denom; + + if (div == 1) { + // Fast-path taken by mach_absolute_time() with 1/1 time base. + return _PyTime_Mul(ticks, mul); + } + /* Compute (ticks * mul / div) in two parts to reduce the risk of integer overflow: compute the integer part, and then the remaining part. @@ -1016,51 +1059,34 @@ _PyTime_GetSystemClockWithInfo(_PyTime_t *t, _Py_clock_info_t *info) #ifdef __APPLE__ static int -py_mach_timebase_info(_PyTime_t *pnumer, _PyTime_t *pdenom, int raise) +py_mach_timebase_info(_PyTimeFraction *base, int raise) { - static mach_timebase_info_data_t timebase; - /* According to the Technical Q&A QA1398, mach_timebase_info() cannot - fail: https://developer.apple.com/library/mac/#qa/qa1398/ */ + mach_timebase_info_data_t timebase; + // According to the Technical Q&A QA1398, mach_timebase_info() cannot + // fail: https://developer.apple.com/library/mac/#qa/qa1398/ (void)mach_timebase_info(&timebase); - /* Sanity check: should never occur in practice */ - if (timebase.numer < 1 || timebase.denom < 1) { + // Check that timebase.numer and timebase.denom can be casted to + // _PyTime_t. In practice, timebase uses uint32_t, so casting cannot + // overflow. At the end, only make sure that the type is uint32_t + // (_PyTime_t is 64-bit long). + Py_BUILD_ASSERT(sizeof(timebase.numer) <= sizeof(_PyTime_t)); + Py_BUILD_ASSERT(sizeof(timebase.denom) <= sizeof(_PyTime_t)); + _PyTime_t numer = (_PyTime_t)timebase.numer; + _PyTime_t denom = (_PyTime_t)timebase.denom; + + // Known time bases: + // + // * (1, 1) on Intel: 1 ns + // * (1000000000, 33333335) on PowerPC: ~30 ns + // * (1000000000, 25000000) on PowerPC: 40 ns + if (_PyTimeFraction_Set(base, numer, denom) < 0) { if (raise) { PyErr_SetString(PyExc_RuntimeError, "invalid mach_timebase_info"); } return -1; } - - /* Check that timebase.numer and timebase.denom can be casted to - _PyTime_t. In practice, timebase uses uint32_t, so casting cannot - overflow. At the end, only make sure that the type is uint32_t - (_PyTime_t is 64-bit long). */ - static_assert(sizeof(timebase.numer) <= sizeof(_PyTime_t), - "timebase.numer is larger than _PyTime_t"); - static_assert(sizeof(timebase.denom) <= sizeof(_PyTime_t), - "timebase.denom is larger than _PyTime_t"); - - /* Make sure that _PyTime_MulDiv(ticks, timebase_numer, timebase_denom) - cannot overflow. - - Known time bases: - - * (1, 1) on Intel - * (1000000000, 33333335) or (1000000000, 25000000) on PowerPC - - None of these time bases can overflow with 64-bit _PyTime_t, but - check for overflow, just in case. */ - if ((_PyTime_t)timebase.numer > _PyTime_MAX / (_PyTime_t)timebase.denom) { - if (raise) { - PyErr_SetString(PyExc_OverflowError, - "mach_timebase_info is too large"); - } - return -1; - } - - *pnumer = (_PyTime_t)timebase.numer; - *pdenom = (_PyTime_t)timebase.denom; return 0; } #endif @@ -1109,17 +1135,16 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) } #elif defined(__APPLE__) - static _PyTime_t timebase_numer = 0; - static _PyTime_t timebase_denom = 0; - if (timebase_denom == 0) { - if (py_mach_timebase_info(&timebase_numer, &timebase_denom, raise_exc) < 0) { + static _PyTimeFraction base = {0, 0}; + if (base.denom == 0) { + if (py_mach_timebase_info(&base, raise_exc) < 0) { return -1; } } if (info) { info->implementation = "mach_absolute_time()"; - info->resolution = (double)timebase_numer / (double)timebase_denom * 1e-9; + info->resolution = _PyTimeFraction_Resolution(&base); info->monotonic = 1; info->adjustable = 0; } @@ -1129,7 +1154,7 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) assert(uticks <= (uint64_t)_PyTime_MAX); _PyTime_t ticks = (_PyTime_t)uticks; - _PyTime_t ns = _PyTime_MulDiv(ticks, timebase_numer, timebase_denom); + _PyTime_t ns = _PyTimeFraction_Mul(ticks, &base); *tp = pytime_from_nanoseconds(ns); #elif defined(__hpux) @@ -1213,7 +1238,7 @@ _PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) #ifdef MS_WINDOWS static int -py_win_perf_counter_frequency(LONGLONG *pfrequency, int raise) +py_win_perf_counter_frequency(_PyTimeFraction *base, int raise) { LONGLONG frequency; @@ -1225,25 +1250,20 @@ py_win_perf_counter_frequency(LONGLONG *pfrequency, int raise) // Since Windows XP, frequency cannot be zero. assert(frequency >= 1); - /* Make also sure that (ticks * SEC_TO_NS) cannot overflow in - _PyTime_MulDiv(), with ticks < frequency. + Py_BUILD_ASSERT(sizeof(_PyTime_t) == sizeof(frequency)); + _PyTime_t denom = (_PyTime_t)frequency; - Known QueryPerformanceFrequency() values: - - * 10,000,000 (10 MHz): 100 ns resolution - * 3,579,545 Hz (3.6 MHz): 279 ns resolution - - None of these frequencies can overflow with 64-bit _PyTime_t, but - check for integer overflow just in case. */ - if (frequency > _PyTime_MAX / SEC_TO_NS) { + // Known QueryPerformanceFrequency() values: + // + // * 10,000,000 (10 MHz): 100 ns resolution + // * 3,579,545 Hz (3.6 MHz): 279 ns resolution + if (_PyTimeFraction_Set(base, SEC_TO_NS, denom) < 0) { if (raise) { - PyErr_SetString(PyExc_OverflowError, - "QueryPerformanceFrequency is too large"); + PyErr_SetString(PyExc_RuntimeError, + "invalid QueryPerformanceFrequency"); } return -1; } - - *pfrequency = frequency; return 0; } @@ -1253,16 +1273,16 @@ py_get_win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) { assert(info == NULL || raise_exc); - static LONGLONG frequency = 0; - if (frequency == 0) { - if (py_win_perf_counter_frequency(&frequency, raise_exc) < 0) { + static _PyTimeFraction base = {0, 0}; + if (base.denom == 0) { + if (py_win_perf_counter_frequency(&base, raise_exc) < 0) { return -1; } } if (info) { info->implementation = "QueryPerformanceCounter()"; - info->resolution = 1.0 / (double)frequency; + info->resolution = _PyTimeFraction_Resolution(&base); info->monotonic = 1; info->adjustable = 0; } @@ -1278,7 +1298,7 @@ py_get_win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) "LONGLONG is larger than _PyTime_t"); ticks = (_PyTime_t)ticksll; - _PyTime_t ns = _PyTime_MulDiv(ticks, SEC_TO_NS, (_PyTime_t)frequency); + _PyTime_t ns = _PyTimeFraction_Mul(ticks, &base); *tp = pytime_from_nanoseconds(ns); return 0; } diff --git a/Python/symtable.c b/Python/symtable.c index da7fec0ee7cf0c..52d5932896b263 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -497,18 +497,14 @@ _PySymtable_Lookup(struct symtable *st, void *key) k = PyLong_FromVoidPtr(key); if (k == NULL) return NULL; - v = PyDict_GetItemWithError(st->st_blocks, k); - Py_DECREF(k); - - if (v) { - assert(PySTEntry_Check(v)); - } - else if (!PyErr_Occurred()) { + if (PyDict_GetItemRef(st->st_blocks, k, &v) == 0) { PyErr_SetString(PyExc_KeyError, "unknown symbol table entry"); } + Py_DECREF(k); - return (PySTEntryObject *)Py_XNewRef(v); + assert(v == NULL || PySTEntry_Check(v)); + return (PySTEntryObject *)v; } long diff --git a/Python/sysmodule.c b/Python/sysmodule.c index c17de44731b703..57dc4a1226ce75 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -451,15 +451,9 @@ PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData) e->hookCFunction = (Py_AuditHookFunction)hook; e->userData = userData; - if (runtime->audit_hooks.mutex == NULL) { - /* The runtime must not be initialized yet. */ - add_audit_hook_entry_unlocked(runtime, e); - } - else { - PyThread_acquire_lock(runtime->audit_hooks.mutex, WAIT_LOCK); - add_audit_hook_entry_unlocked(runtime, e); - PyThread_release_lock(runtime->audit_hooks.mutex); - } + PyMutex_Lock(&runtime->audit_hooks.mutex); + add_audit_hook_entry_unlocked(runtime, e); + PyMutex_Unlock(&runtime->audit_hooks.mutex); return 0; } @@ -989,6 +983,23 @@ sys_intern_impl(PyObject *module, PyObject *s) } +/*[clinic input] +sys._is_interned -> bool + + string: unicode + / + +Return True if the given string is "interned". +[clinic start generated code]*/ + +static int +sys__is_interned_impl(PyObject *module, PyObject *string) +/*[clinic end generated code: output=c3678267b4e9d7ed input=039843e17883b606]*/ +{ + return PyUnicode_CHECK_INTERNED(string); +} + + /* * Cached interned string objects used for calling the profile and * trace functions. @@ -2462,6 +2473,7 @@ static PyMethodDef sys_methods[] = { SYS_GETWINDOWSVERSION_METHODDEF SYS__ENABLELEGACYWINDOWSFSENCODING_METHODDEF SYS_INTERN_METHODDEF + SYS__IS_INTERNED_METHODDEF SYS_IS_FINALIZING_METHODDEF SYS_MDEBUG_METHODDEF SYS_SETSWITCHINTERVAL_METHODDEF diff --git a/README.rst b/README.rst index 9ba06f76ba5da8..4c8602c97ac8ef 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.13.0 alpha 1 +This is Python version 3.13.0 alpha 2 ===================================== .. image:: https://github.com/python/cpython/workflows/Tests/badge.svg diff --git a/Tools/build/freeze_modules.py b/Tools/build/freeze_modules.py index c5a397129201b6..6a54f45bac3a86 100644 --- a/Tools/build/freeze_modules.py +++ b/Tools/build/freeze_modules.py @@ -468,6 +468,17 @@ def replace_block(lines, start_marker, end_marker, replacements, file): return lines[:start_pos + 1] + replacements + lines[end_pos:] +class UniqueList(list): + def __init__(self): + self._seen = set() + + def append(self, item): + if item in self._seen: + return + super().append(item) + self._seen.add(item) + + def regen_frozen(modules): headerlines = [] parentdir = os.path.dirname(FROZEN_FILE) @@ -477,7 +488,7 @@ def regen_frozen(modules): header = relpath_for_posix_display(src.frozenfile, parentdir) headerlines.append(f'#include "{header}"') - externlines = [] + externlines = UniqueList() bootstraplines = [] stdliblines = [] testlines = [] diff --git a/Tools/build/generate_sbom.py b/Tools/build/generate_sbom.py new file mode 100644 index 00000000000000..0089db81af9b9d --- /dev/null +++ b/Tools/build/generate_sbom.py @@ -0,0 +1,179 @@ +"""Tool for generating Software Bill of Materials (SBOM) for Python's dependencies""" + +import re +import hashlib +import json +import glob +import pathlib +import subprocess +import typing + +# Before adding a new entry to this list, double check that +# the license expression is a valid SPDX license expression: +# See: https://spdx.org/licenses +ALLOWED_LICENSE_EXPRESSIONS = { + "MIT", + "CC0-1.0", + "Apache-2.0", + "BSD-2-Clause", +} + +# Properties which are required for our purposes. +REQUIRED_PROPERTIES_PACKAGE = frozenset([ + "SPDXID", + "name", + "versionInfo", + "downloadLocation", + "checksums", + "licenseConcluded", + "externalRefs", + "originator", + "primaryPackagePurpose", +]) + + +class PackageFiles(typing.NamedTuple): + """Structure for describing the files of a package""" + include: list[str] + exclude: list[str] | None = None + + +# SBOMS don't have a method to specify the sources of files +# so we need to do that external to the SBOM itself. Add new +# values to 'exclude' if we create new files within tracked +# directories that aren't sourced from third-party packages. +PACKAGE_TO_FILES = { + "mpdecimal": PackageFiles( + include=["Modules/_decimal/libmpdec/**"] + ), + "expat": PackageFiles( + include=["Modules/expat/**"] + ), + "pip": PackageFiles( + include=["Lib/ensurepip/_bundled/pip-23.3.1-py3-none-any.whl"] + ), + "macholib": PackageFiles( + include=["Lib/ctypes/macholib/**"], + exclude=[ + "Lib/ctypes/macholib/README.ctypes", + "Lib/ctypes/macholib/fetch_macholib", + "Lib/ctypes/macholib/fetch_macholib.bat", + ], + ), + "libb2": PackageFiles( + include=["Modules/_blake2/impl/**"] + ), + "hacl-star": PackageFiles( + include=["Modules/_hacl/**"], + exclude=[ + "Modules/_hacl/refresh.sh", + "Modules/_hacl/README.md", + "Modules/_hacl/python_hacl_namespace.h", + ] + ), +} + + +def spdx_id(value: str) -> str: + """Encode a value into characters that are valid in an SPDX ID""" + return re.sub(r"[^a-zA-Z0-9.\-]+", "-", value) + + +def filter_gitignored_paths(paths: list[str]) -> list[str]: + """ + Filter out paths excluded by the gitignore file. + The output of 'git check-ignore --non-matching --verbose' looks + like this for non-matching (included) files: + + '::' + + And looks like this for matching (excluded) files: + + '.gitignore:9:*.a Tools/lib.a' + """ + # Filter out files in gitignore. + # Non-matching files show up as '::' + git_check_ignore_proc = subprocess.run( + ["git", "check-ignore", "--verbose", "--non-matching", *paths], + check=False, + stdout=subprocess.PIPE, + ) + # 1 means matches, 0 means no matches. + assert git_check_ignore_proc.returncode in (0, 1) + + # Return the list of paths sorted + git_check_ignore_lines = git_check_ignore_proc.stdout.decode().splitlines() + return sorted([line.split()[-1] for line in git_check_ignore_lines if line.startswith("::")]) + + +def main() -> None: + root_dir = pathlib.Path(__file__).parent.parent.parent + sbom_path = root_dir / "Misc/sbom.spdx.json" + sbom_data = json.loads(sbom_path.read_bytes()) + + # Make a bunch of assertions about the SBOM data to ensure it's consistent. + assert {package["name"] for package in sbom_data["packages"]} == set(PACKAGE_TO_FILES) + for package in sbom_data["packages"]: + + # Properties and ID must be properly formed. + assert set(package.keys()) == REQUIRED_PROPERTIES_PACKAGE + assert package["SPDXID"] == spdx_id(f"SPDXRef-PACKAGE-{package['name']}") + + # Version must be in the download and external references. + version = package["versionInfo"] + assert version in package["downloadLocation"] + assert all(version in ref["referenceLocator"] for ref in package["externalRefs"]) + + # License must be on the approved list for SPDX. + assert package["licenseConcluded"] in ALLOWED_LICENSE_EXPRESSIONS, package["licenseConcluded"] + + # Regenerate file information from current data. + sbom_files = [] + sbom_relationships = [] + + # We call 'sorted()' here a lot to avoid filesystem scan order issues. + for name, files in sorted(PACKAGE_TO_FILES.items()): + package_spdx_id = spdx_id(f"SPDXRef-PACKAGE-{name}") + exclude = files.exclude or () + for include in sorted(files.include): + + # Find all the paths and then filter them through .gitignore. + paths = glob.glob(include, root_dir=root_dir, recursive=True) + paths = filter_gitignored_paths(paths) + assert paths, include # Make sure that every value returns something! + + for path in paths: + # Skip directories and excluded files + if not (root_dir / path).is_file() or path in exclude: + continue + + # SPDX requires SHA1 to be used for files, but we provide SHA256 too. + data = (root_dir / path).read_bytes() + checksum_sha1 = hashlib.sha1(data).hexdigest() + checksum_sha256 = hashlib.sha256(data).hexdigest() + + file_spdx_id = spdx_id(f"SPDXRef-FILE-{path}") + sbom_files.append({ + "SPDXID": file_spdx_id, + "fileName": path, + "checksums": [ + {"algorithm": "SHA1", "checksumValue": checksum_sha1}, + {"algorithm": "SHA256", "checksumValue": checksum_sha256}, + ], + }) + + # Tie each file back to its respective package. + sbom_relationships.append({ + "spdxElementId": package_spdx_id, + "relatedSpdxElement": file_spdx_id, + "relationshipType": "CONTAINS", + }) + + # Update the SBOM on disk + sbom_data["files"] = sbom_files + sbom_data["relationships"] = sbom_relationships + sbom_path.write_text(json.dumps(sbom_data, indent=2, sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/Tools/build/mypy.ini b/Tools/build/mypy.ini new file mode 100644 index 00000000000000..cf1dac7fde5ac5 --- /dev/null +++ b/Tools/build/mypy.ini @@ -0,0 +1,13 @@ +[mypy] +files = Tools/build/generate_sbom.py +pretty = True + +# Make sure Python can still be built +# using Python 3.10 for `PYTHON_FOR_REGEN`... +python_version = 3.10 + +# ...And be strict: +strict = True +strict_concatenate = True +enable_error_code = ignore-without-code,redundant-expr,truthy-bool,possibly-undefined +warn_unreachable = True diff --git a/Tools/build/stable_abi.py b/Tools/build/stable_abi.py index 7cba788ff33578..85c437d521a15a 100644 --- a/Tools/build/stable_abi.py +++ b/Tools/build/stable_abi.py @@ -521,7 +521,7 @@ def gcc_get_limited_api_macros(headers): api_hexversion = sys.version_info.major << 24 | sys.version_info.minor << 16 - preprocesor_output_with_macros = subprocess.check_output( + preprocessor_output_with_macros = subprocess.check_output( sysconfig.get_config_var("CC").split() + [ # Prevent the expansion of the exported macros so we can @@ -540,7 +540,7 @@ def gcc_get_limited_api_macros(headers): return { target for target in re.findall( - r"#define (\w+)", preprocesor_output_with_macros + r"#define (\w+)", preprocessor_output_with_macros ) } @@ -561,7 +561,7 @@ def gcc_get_limited_api_definitions(headers): Requires Python built with a GCC-compatible compiler. (clang might work) """ api_hexversion = sys.version_info.major << 24 | sys.version_info.minor << 16 - preprocesor_output = subprocess.check_output( + preprocessor_output = subprocess.check_output( sysconfig.get_config_var("CC").split() + [ # Prevent the expansion of the exported macros so we can capture @@ -581,13 +581,13 @@ def gcc_get_limited_api_definitions(headers): stderr=subprocess.DEVNULL, ) stable_functions = set( - re.findall(r"__PyAPI_FUNC\(.*?\)\s*(.*?)\s*\(", preprocesor_output) + re.findall(r"__PyAPI_FUNC\(.*?\)\s*(.*?)\s*\(", preprocessor_output) ) stable_exported_data = set( - re.findall(r"__EXPORT_DATA\((.*?)\)", preprocesor_output) + re.findall(r"__EXPORT_DATA\((.*?)\)", preprocessor_output) ) stable_data = set( - re.findall(r"__PyAPI_DATA\(.*?\)[\s\*\(]*([^);]*)\)?.*;", preprocesor_output) + re.findall(r"__PyAPI_DATA\(.*?\)[\s\*\(]*([^);]*)\)?.*;", preprocessor_output) ) return stable_data | stable_exported_data | stable_functions diff --git a/Tools/c-analyzer/c_analyzer/__init__.py b/Tools/c-analyzer/c_analyzer/__init__.py index 171fa25102bffc..b83ffc087a08d8 100644 --- a/Tools/c-analyzer/c_analyzer/__init__.py +++ b/Tools/c-analyzer/c_analyzer/__init__.py @@ -18,7 +18,7 @@ def analyze(filenmes, **kwargs): - results = iter_analyis_results(filenames, **kwargs) + results = iter_analysis_results(filenames, **kwargs) return Analysis.from_results(results) diff --git a/Tools/cases_generator/analysis.py b/Tools/cases_generator/analysis.py index 603b15596f16de..26d92c13cd82ab 100644 --- a/Tools/cases_generator/analysis.py +++ b/Tools/cases_generator/analysis.py @@ -390,9 +390,14 @@ def analyze_pseudo(self, pseudo: parsing.Pseudo) -> PseudoInstruction: else: targets.append(self.macro_instrs[target_name]) assert targets - ignored_flags = {"HAS_EVAL_BREAK_FLAG", "HAS_DEOPT_FLAG", "HAS_ERROR_FLAG", "HAS_ESCAPES_FLAG"} + ignored_flags = {"HAS_EVAL_BREAK_FLAG", "HAS_DEOPT_FLAG", "HAS_ERROR_FLAG", + "HAS_ESCAPES_FLAG"} assert len({t.instr_flags.bitmap(ignore=ignored_flags) for t in targets}) == 1 - return PseudoInstruction(pseudo.name, targets, targets[0].instr_flags) + + flags = InstructionFlags(**{f"{f}_FLAG" : True for f in pseudo.flags}) + for t in targets: + flags.add(t.instr_flags) + return PseudoInstruction(pseudo.name, targets, flags) def analyze_instruction( self, instr: Instruction, offset: int diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py new file mode 100644 index 00000000000000..027f9861a1c0eb --- /dev/null +++ b/Tools/cases_generator/analyzer.py @@ -0,0 +1,456 @@ +from dataclasses import dataclass +import lexer +import parser +from typing import Optional + + +@dataclass +class Properties: + escapes: bool + infallible: bool + deopts: bool + oparg: bool + jumps: bool + ends_with_eval_breaker: bool + needs_this: bool + always_exits: bool + stores_sp: bool + + def dump(self, indent: str) -> None: + print(indent, end="") + text = ", ".join([f"{key}: {value}" for (key, value) in self.__dict__.items()]) + print(indent, text, sep="") + + @staticmethod + def from_list(properties: list["Properties"]) -> "Properties": + return Properties( + escapes=any(p.escapes for p in properties), + infallible=all(p.infallible for p in properties), + deopts=any(p.deopts for p in properties), + oparg=any(p.oparg for p in properties), + jumps=any(p.jumps for p in properties), + ends_with_eval_breaker=any(p.ends_with_eval_breaker for p in properties), + needs_this=any(p.needs_this for p in properties), + always_exits=any(p.always_exits for p in properties), + stores_sp=any(p.stores_sp for p in properties), + ) + + +SKIP_PROPERTIES = Properties( + escapes=False, + infallible=True, + deopts=False, + oparg=False, + jumps=False, + ends_with_eval_breaker=False, + needs_this=False, + always_exits=False, + stores_sp=False, +) + + +@dataclass +class Skip: + "Unused cache entry" + size: int + + @property + def name(self) -> str: + return f"unused/{self.size}" + + @property + def properties(self) -> Properties: + return SKIP_PROPERTIES + + +@dataclass +class StackItem: + name: str + type: str | None + condition: str | None + size: str + peek: bool = False + + def __str__(self) -> str: + cond = f" if ({self.condition})" if self.condition else "" + size = f"[{self.size}]" if self.size != "1" else "" + type = "" if self.type is None else f"{self.type} " + return f"{type}{self.name}{size}{cond} {self.peek}" + + def is_array(self) -> bool: + return self.type == "PyObject **" + + +@dataclass +class StackEffect: + inputs: list[StackItem] + outputs: list[StackItem] + + def __str__(self) -> str: + return f"({', '.join([str(i) for i in self.inputs])} -- {', '.join([str(i) for i in self.outputs])})" + + +@dataclass +class CacheEntry: + name: str + size: int + + def __str__(self) -> str: + return f"{self.name}/{self.size}" + + +@dataclass +class Uop: + name: str + context: parser.Context | None + annotations: list[str] + stack: StackEffect + caches: list[CacheEntry] + body: list[lexer.Token] + properties: Properties + _size: int = -1 + + def dump(self, indent: str) -> None: + print( + indent, self.name, ", ".join(self.annotations) if self.annotations else "" + ) + print(indent, self.stack, ", ".join([str(c) for c in self.caches])) + self.properties.dump(" " + indent) + + @property + def size(self) -> int: + if self._size < 0: + self._size = sum(c.size for c in self.caches) + return self._size + + +Part = Uop | Skip + + +@dataclass +class Instruction: + name: str + parts: list[Part] + _properties: Properties | None + is_target: bool = False + family: Optional["Family"] = None + + @property + def properties(self) -> Properties: + if self._properties is None: + self._properties = self._compute_properties() + return self._properties + + def _compute_properties(self) -> Properties: + return Properties.from_list([part.properties for part in self.parts]) + + def dump(self, indent: str) -> None: + print(indent, self.name, "=", ", ".join([part.name for part in self.parts])) + self.properties.dump(" " + indent) + + @property + def size(self) -> int: + return 1 + sum(part.size for part in self.parts) + + +@dataclass +class PseudoInstruction: + name: str + targets: list[Instruction] + flags: list[str] + + def dump(self, indent: str) -> None: + print(indent, self.name, "->", " or ".join([t.name for t in self.targets])) + + +@dataclass +class Family: + name: str + size: str + members: list[Instruction] + + def dump(self, indent: str) -> None: + print(indent, self.name, "= ", ", ".join([m.name for m in self.members])) + + +@dataclass +class Analysis: + instructions: dict[str, Instruction] + uops: dict[str, Uop] + families: dict[str, Family] + pseudos: dict[str, PseudoInstruction] + + +def analysis_error(message: str, tkn: lexer.Token) -> SyntaxError: + # To do -- support file and line output + # Construct a SyntaxError instance from message and token + return lexer.make_syntax_error(message, "", tkn.line, tkn.column, "") + + +def override_error( + name: str, + context: parser.Context | None, + prev_context: parser.Context | None, + token: lexer.Token, +) -> SyntaxError: + return analysis_error( + f"Duplicate definition of '{name}' @ {context} " + f"previous definition @ {prev_context}", + token, + ) + + +def convert_stack_item(item: parser.StackEffect) -> StackItem: + return StackItem(item.name, item.type, item.cond, (item.size or "1")) + + +def analyze_stack(op: parser.InstDef) -> StackEffect: + inputs: list[StackItem] = [ + convert_stack_item(i) for i in op.inputs if isinstance(i, parser.StackEffect) + ] + outputs: list[StackItem] = [convert_stack_item(i) for i in op.outputs] + for input, output in zip(inputs, outputs): + if input.name == output.name: + input.peek = output.peek = True + return StackEffect(inputs, outputs) + + +def analyze_caches(op: parser.InstDef) -> list[CacheEntry]: + caches: list[parser.CacheEffect] = [ + i for i in op.inputs if isinstance(i, parser.CacheEffect) + ] + return [CacheEntry(i.name, int(i.size)) for i in caches] + + +def variable_used(node: parser.InstDef, name: str) -> bool: + """Determine whether a variable with a given name is used in a node.""" + return any( + token.kind == "IDENTIFIER" and token.text == name for token in node.tokens + ) + + +def is_infallible(op: parser.InstDef) -> bool: + return not ( + variable_used(op, "ERROR_IF") + or variable_used(op, "error") + or variable_used(op, "pop_1_error") + or variable_used(op, "exception_unwind") + or variable_used(op, "resume_with_error") + ) + + +from flags import makes_escaping_api_call + +EXITS = { + "DISPATCH", + "GO_TO_INSTRUCTION", + "Py_UNREACHABLE", + "DISPATCH_INLINED", + "DISPATCH_GOTO", +} + + +def eval_breaker_at_end(op: parser.InstDef) -> bool: + return op.tokens[-5].text == "CHECK_EVAL_BREAKER" + + +def always_exits(op: parser.InstDef) -> bool: + depth = 0 + tkn_iter = iter(op.tokens) + for tkn in tkn_iter: + if tkn.kind == "LBRACE": + depth += 1 + elif tkn.kind == "RBRACE": + depth -= 1 + elif depth > 1: + continue + elif tkn.kind == "GOTO" or tkn.kind == "RETURN": + return True + elif tkn.kind == "KEYWORD": + if tkn.text in EXITS: + return True + elif tkn.kind == "IDENTIFIER": + if tkn.text in EXITS: + return True + if tkn.text == "DEOPT_IF" or tkn.text == "ERROR_IF": + next(tkn_iter) # '(' + t = next(tkn_iter) + if t.text == "true": + return True + return False + + +def compute_properties(op: parser.InstDef) -> Properties: + return Properties( + escapes=makes_escaping_api_call(op), + infallible=is_infallible(op), + deopts=variable_used(op, "DEOPT_IF"), + oparg=variable_used(op, "oparg"), + jumps=variable_used(op, "JUMPBY"), + ends_with_eval_breaker=eval_breaker_at_end(op), + needs_this=variable_used(op, "this_instr"), + always_exits=always_exits(op), + stores_sp=variable_used(op, "STORE_SP"), + ) + + +def make_uop(name: str, op: parser.InstDef) -> Uop: + return Uop( + name=name, + context=op.context, + annotations=op.annotations, + stack=analyze_stack(op), + caches=analyze_caches(op), + body=op.block.tokens, + properties=compute_properties(op), + ) + + +def add_op(op: parser.InstDef, uops: dict[str, Uop]) -> None: + assert op.kind == "op" + if op.name in uops: + if "override" not in op.annotations: + raise override_error( + op.name, op.context, uops[op.name].context, op.tokens[0] + ) + uops[op.name] = make_uop(op.name, op) + + +def add_instruction( + name: str, parts: list[Part], instructions: dict[str, Instruction] +) -> None: + instructions[name] = Instruction(name, parts, None) + + +def desugar_inst( + inst: parser.InstDef, instructions: dict[str, Instruction], uops: dict[str, Uop] +) -> None: + assert inst.kind == "inst" + name = inst.name + uop = make_uop("_" + inst.name, inst) + uops[inst.name] = uop + add_instruction(name, [uop], instructions) + + +def add_macro( + macro: parser.Macro, instructions: dict[str, Instruction], uops: dict[str, Uop] +) -> None: + parts: list[Uop | Skip] = [] + for part in macro.uops: + match part: + case parser.OpName(): + if part.name not in uops: + analysis_error(f"No Uop named {part.name}", macro.tokens[0]) + parts.append(uops[part.name]) + case parser.CacheEffect(): + parts.append(Skip(part.size)) + case _: + assert False + assert parts + add_instruction(macro.name, parts, instructions) + + +def add_family( + pfamily: parser.Family, + instructions: dict[str, Instruction], + families: dict[str, Family], +) -> None: + family = Family( + pfamily.name, + pfamily.size, + [instructions[member_name] for member_name in pfamily.members], + ) + for member in family.members: + member.family = family + # The head of the family is an implicit jump target for DEOPTs + instructions[family.name].is_target = True + families[family.name] = family + + +def add_pseudo( + pseudo: parser.Pseudo, + instructions: dict[str, Instruction], + pseudos: dict[str, PseudoInstruction], +) -> None: + pseudos[pseudo.name] = PseudoInstruction( + pseudo.name, + [instructions[target] for target in pseudo.targets], + pseudo.flags, + ) + + +def analyze_forest(forest: list[parser.AstNode]) -> Analysis: + instructions: dict[str, Instruction] = {} + uops: dict[str, Uop] = {} + families: dict[str, Family] = {} + pseudos: dict[str, PseudoInstruction] = {} + for node in forest: + match node: + case parser.InstDef(name): + if node.kind == "inst": + desugar_inst(node, instructions, uops) + else: + assert node.kind == "op" + add_op(node, uops) + case parser.Macro(): + pass + case parser.Family(): + pass + case parser.Pseudo(): + pass + case _: + assert False + for node in forest: + if isinstance(node, parser.Macro): + add_macro(node, instructions, uops) + for node in forest: + match node: + case parser.Family(): + add_family(node, instructions, families) + case parser.Pseudo(): + add_pseudo(node, instructions, pseudos) + case _: + pass + for uop in uops.values(): + tkn_iter = iter(uop.body) + for tkn in tkn_iter: + if tkn.kind == "IDENTIFIER" and tkn.text == "GO_TO_INSTRUCTION": + if next(tkn_iter).kind != "LPAREN": + continue + target = next(tkn_iter) + if target.kind != "IDENTIFIER": + continue + if target.text in instructions: + instructions[target.text].is_target = True + # Hack + instructions["BINARY_OP_INPLACE_ADD_UNICODE"].family = families["BINARY_OP"] + return Analysis(instructions, uops, families, pseudos) + + +def analyze_files(filenames: list[str]) -> Analysis: + return analyze_forest(parser.parse_files(filenames)) + + +def dump_analysis(analysis: Analysis) -> None: + print("Uops:") + for u in analysis.uops.values(): + u.dump(" ") + print("Instructions:") + for i in analysis.instructions.values(): + i.dump(" ") + print("Families:") + for f in analysis.families.values(): + f.dump(" ") + print("Pseudos:") + for p in analysis.pseudos.values(): + p.dump(" ") + + +if __name__ == "__main__": + import sys + + if len(sys.argv) < 2: + print("No input") + else: + filenames = sys.argv[1:] + dump_analysis(analyze_files(filenames)) diff --git a/Tools/cases_generator/cwriter.py b/Tools/cases_generator/cwriter.py new file mode 100644 index 00000000000000..34e39855a9b40a --- /dev/null +++ b/Tools/cases_generator/cwriter.py @@ -0,0 +1,116 @@ +from lexer import Token +from typing import TextIO + + +class CWriter: + "A writer that understands tokens and how to format C code" + + last_token: Token | None + + def __init__(self, out: TextIO, indent: int, line_directives: bool): + self.out = out + self.base_column = indent * 4 + self.indents = [i * 4 for i in range(indent + 1)] + self.line_directives = line_directives + self.last_token = None + self.newline = True + + def set_position(self, tkn: Token) -> None: + if self.last_token is not None: + if self.last_token.line < tkn.line: + self.out.write("\n") + if self.line_directives: + self.out.write(f'#line {tkn.line} "{tkn.filename}"\n') + self.out.write(" " * self.indents[-1]) + else: + gap = tkn.column - self.last_token.end_column + self.out.write(" " * gap) + elif self.newline: + self.out.write(" " * self.indents[-1]) + self.last_token = tkn + self.newline = False + + def emit_at(self, txt: str, where: Token) -> None: + self.set_position(where) + self.out.write(txt) + + def maybe_dedent(self, txt: str) -> None: + parens = txt.count("(") - txt.count(")") + if parens < 0: + self.indents.pop() + elif "}" in txt or is_label(txt): + self.indents.pop() + + def maybe_indent(self, txt: str) -> None: + parens = txt.count("(") - txt.count(")") + if parens > 0 and self.last_token: + offset = self.last_token.end_column - 1 + if offset <= self.indents[-1] or offset > 40: + offset = self.indents[-1] + 4 + self.indents.append(offset) + if is_label(txt): + self.indents.append(self.indents[-1] + 4) + elif "{" in txt: + if 'extern "C"' in txt: + self.indents.append(self.indents[-1]) + else: + self.indents.append(self.indents[-1] + 4) + + def emit_text(self, txt: str) -> None: + self.out.write(txt) + + def emit_multiline_comment(self, tkn: Token) -> None: + self.set_position(tkn) + lines = tkn.text.splitlines(True) + first = True + for line in lines: + text = line.lstrip() + if first: + spaces = 0 + else: + spaces = self.indents[-1] + if text.startswith("*"): + spaces += 1 + else: + spaces += 3 + first = False + self.out.write(" " * spaces) + self.out.write(text) + + def emit_token(self, tkn: Token) -> None: + if tkn.kind == "COMMENT" and "\n" in tkn.text: + return self.emit_multiline_comment(tkn) + self.maybe_dedent(tkn.text) + self.set_position(tkn) + self.emit_text(tkn.text) + self.maybe_indent(tkn.text) + + def emit_str(self, txt: str) -> None: + self.maybe_dedent(txt) + if self.newline and txt: + if txt[0] != "\n": + self.out.write(" " * self.indents[-1]) + self.newline = False + self.emit_text(txt) + if txt.endswith("\n"): + self.newline = True + self.maybe_indent(txt) + self.last_token = None + + def emit(self, txt: str | Token) -> None: + if isinstance(txt, Token): + self.emit_token(txt) + elif isinstance(txt, str): + self.emit_str(txt) + else: + assert False + + def start_line(self) -> None: + if not self.newline: + self.out.write("\n") + self.newline = True + self.last_token = None + + +def is_label(txt: str) -> bool: + return not txt.startswith("//") and txt.endswith(":") diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 149558e1640364..d0fdc4a0aeb7b0 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -101,13 +101,6 @@ arg_parser.add_argument( "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT ) -arg_parser.add_argument( - "-n", - "--opcode_ids_h", - type=str, - help="Header file with opcode number definitions", - default=DEFAULT_OPCODE_IDS_H_OUTPUT, -) arg_parser.add_argument( "-t", "--opcode_targets_h", @@ -334,42 +327,8 @@ def map_op(op: int, name: str) -> None: self.opmap = opmap self.markers = markers - def write_opcode_ids( - self, opcode_ids_h_filename: str, opcode_targets_filename: str - ) -> None: - """Write header file that defined the opcode IDs""" - - with open(opcode_ids_h_filename, "w") as f: - # Create formatter - self.out = Formatter(f, 0) - - self.write_provenance_header() - - self.out.emit("") - self.out.emit("#ifndef Py_OPCODE_IDS_H") - self.out.emit("#define Py_OPCODE_IDS_H") - self.out.emit("#ifdef __cplusplus") - self.out.emit('extern "C" {') - self.out.emit("#endif") - self.out.emit("") - self.out.emit("/* Instruction opcodes for compiled code */") - - def define(name: str, opcode: int) -> None: - self.out.emit(f"#define {name:<38} {opcode:>3}") - - all_pairs: list[tuple[int, int, str]] = [] - # the second item in the tuple sorts the markers before the ops - all_pairs.extend((i, 1, name) for (name, i) in self.markers.items()) - all_pairs.extend((i, 2, name) for (name, i) in self.opmap.items()) - for i, _, name in sorted(all_pairs): - assert name is not None - define(name, i) - - self.out.emit("") - self.out.emit("#ifdef __cplusplus") - self.out.emit("}") - self.out.emit("#endif") - self.out.emit("#endif /* !Py_OPCODE_IDS_H */") + def write_opcode_targets(self, opcode_targets_filename: str) -> None: + """Write header file that defines the jump target table""" with open(opcode_targets_filename, "w") as f: # Create formatter @@ -769,6 +728,7 @@ def write_instructions( # Write and count instructions of all kinds n_macros = 0 + cases = [] for thing in self.everything: match thing: case parsing.InstDef(): @@ -776,11 +736,14 @@ def write_instructions( case parsing.Macro(): n_macros += 1 mac = self.macro_instrs[thing.name] - stacking.write_macro_instr(mac, self.out) + cases.append((mac.name, mac)) case parsing.Pseudo(): pass case _: assert_never(thing) + cases.sort() + for _, mac in cases: + stacking.write_macro_instr(mac, self.out) self.out.write_raw("\n") self.out.write_raw("#undef TIER_ONE\n") @@ -810,6 +773,8 @@ def write_executor_instructions( n_uops += 1 self.out.emit("") with self.out.block(f"case {instr.name}:"): + if instr.instr_flags.HAS_ARG_FLAG: + self.out.emit("oparg = CURRENT_OPARG();") stacking.write_single_instr(instr, self.out, tier=TIER_TWO) if instr.check_eval_breaker: self.out.emit("CHECK_EVAL_BREAKER();") @@ -877,10 +842,9 @@ def main() -> None: return # These raise OSError if output can't be written - a.write_instructions(args.output, args.emit_line_directives) a.assign_opcode_ids() - a.write_opcode_ids(args.opcode_ids_h, args.opcode_targets_h) + a.write_opcode_targets(args.opcode_targets_h) a.write_metadata(args.metadata, args.pymetadata) a.write_executor_instructions(args.executor_cases, args.emit_line_directives) a.write_abstract_interpreter_instructions( diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py new file mode 100644 index 00000000000000..76900d1efffd5d --- /dev/null +++ b/Tools/cases_generator/generators_common.py @@ -0,0 +1,19 @@ +from pathlib import Path +from typing import TextIO + +ROOT = Path(__file__).parent.parent.parent +DEFAULT_INPUT = (ROOT / "Python/bytecodes.c").absolute() + + +def root_relative_path(filename: str) -> str: + return Path(filename).relative_to(ROOT).as_posix() + + +def write_header(generator: str, source: str, outfile: TextIO) -> None: + outfile.write( + f"""// This file is generated by {root_relative_path(generator)} +// from: +// {source} +// Do not edit! +""" + ) diff --git a/Tools/cases_generator/instructions.py b/Tools/cases_generator/instructions.py index 9039ac5c6f127e..149a08810e4ae5 100644 --- a/Tools/cases_generator/instructions.py +++ b/Tools/cases_generator/instructions.py @@ -166,7 +166,7 @@ def write_body( f"{func}(&this_instr[{active.offset + 1}].cache);" ) else: - out.emit(f"{typ}{ceffect.name} = ({typ.strip()})operand;") + out.emit(f"{typ}{ceffect.name} = ({typ.strip()})CURRENT_OPERAND();") # Write the body, substituting a goto for ERROR_IF() and other stuff assert dedent <= 0 diff --git a/Tools/cases_generator/lexer.py b/Tools/cases_generator/lexer.py index 1185c855785939..c3c2954a42083f 100644 --- a/Tools/cases_generator/lexer.py +++ b/Tools/cases_generator/lexer.py @@ -112,7 +112,7 @@ def choice(*opts: str) -> str: char = r"\'.\'" # TODO: escape sequence CHARACTER = "CHARACTER" -comment_re = r"//.*|/\*([^*]|\*[^/])*\*/" +comment_re = r"(//.*)|/\*([^*]|\*[^/])*\*/" COMMENT = "COMMENT" newline = r"\n" @@ -234,6 +234,7 @@ def make_syntax_error( @dataclass(slots=True) class Token: + filename: str kind: str text: str begin: tuple[int, int] @@ -261,7 +262,7 @@ def width(self) -> int: def replaceText(self, txt: str) -> "Token": assert isinstance(txt, str) - return Token(self.kind, txt, self.begin, self.end) + return Token(self.filename, self.kind, txt, self.begin, self.end) def __repr__(self) -> str: b0, b1 = self.begin @@ -272,7 +273,7 @@ def __repr__(self) -> str: return f"{self.kind}({self.text!r}, {b0}:{b1}, {e0}:{e1})" -def tokenize(src: str, line: int = 1, filename: str | None = None) -> Iterator[Token]: +def tokenize(src: str, line: int = 1, filename: str = "") -> Iterator[Token]: linestart = -1 for m in matcher.finditer(src): start, end = m.span() @@ -323,7 +324,7 @@ def tokenize(src: str, line: int = 1, filename: str | None = None) -> Iterator[T else: begin = line, start - linestart if kind != "\n": - yield Token(kind, text, begin, (line, start - linestart + len(text))) + yield Token(filename, kind, text, begin, (line, start - linestart + len(text))) def to_text(tkns: list[Token], dedent: int = 0) -> str: diff --git a/Tools/cases_generator/mypy.ini b/Tools/cases_generator/mypy.ini index e7175e263350b2..8e5a31851c596e 100644 --- a/Tools/cases_generator/mypy.ini +++ b/Tools/cases_generator/mypy.ini @@ -11,3 +11,5 @@ strict = True strict_concatenate = True enable_error_code = ignore-without-code,redundant-expr,truthy-bool,possibly-undefined warn_unreachable = True +allow_redefinition = True +implicit_reexport = True diff --git a/Tools/cases_generator/opcode_id_generator.py b/Tools/cases_generator/opcode_id_generator.py new file mode 100644 index 00000000000000..a1f6f62156ebd3 --- /dev/null +++ b/Tools/cases_generator/opcode_id_generator.py @@ -0,0 +1,153 @@ +"""Generate the list of opcode IDs. +Reads the instruction definitions from bytecodes.c. +Writes the IDs to opcode._ids.h by default. +""" + +import argparse +import os.path +import sys + +from analyzer import ( + Analysis, + Instruction, + analyze_files, +) +from generators_common import ( + DEFAULT_INPUT, + ROOT, + write_header, +) +from cwriter import CWriter +from typing import TextIO + + +DEFAULT_OUTPUT = ROOT / "Include/opcode_ids.h" + + +def generate_opcode_header(filenames: str, analysis: Analysis, outfile: TextIO) -> None: + write_header(__file__, filenames, outfile) + out = CWriter(outfile, 0, False) + out.emit("\n") + instmap: dict[str, int] = {} + + # 0 is reserved for cache entries. This helps debugging. + instmap["CACHE"] = 0 + + # 17 is reserved as it is the initial value for the specializing counter. + # This helps catch cases where we attempt to execute a cache. + instmap["RESERVED"] = 17 + + # 149 is RESUME - it is hard coded as such in Tools/build/deepfreeze.py + instmap["RESUME"] = 149 + instmap["INSTRUMENTED_LINE"] = 254 + + instrumented = [ + name for name in analysis.instructions if name.startswith("INSTRUMENTED") + ] + + # Special case: this instruction is implemented in ceval.c + # rather than bytecodes.c, so we need to add it explicitly + # here (at least until we add something to bytecodes.c to + # declare external instructions). + instrumented.append("INSTRUMENTED_LINE") + + specialized: set[str] = set() + no_arg: list[str] = [] + has_arg: list[str] = [] + + for family in analysis.families.values(): + specialized.update(inst.name for inst in family.members) + + for inst in analysis.instructions.values(): + name = inst.name + if name in specialized: + continue + if name in instrumented: + continue + if inst.properties.oparg: + has_arg.append(name) + else: + no_arg.append(name) + + # Specialized ops appear in their own section + # Instrumented opcodes are at the end of the valid range + min_internal = 150 + min_instrumented = 254 - (len(instrumented) - 1) + assert min_internal + len(specialized) < min_instrumented + + next_opcode = 1 + + def add_instruction(name: str) -> None: + nonlocal next_opcode + if name in instmap: + return # Pre-defined name + while next_opcode in instmap.values(): + next_opcode += 1 + instmap[name] = next_opcode + next_opcode += 1 + + for name in sorted(no_arg): + add_instruction(name) + for name in sorted(has_arg): + add_instruction(name) + # For compatibility + next_opcode = min_internal + for name in sorted(specialized): + add_instruction(name) + next_opcode = min_instrumented + for name in instrumented: + add_instruction(name) + + for op, name in enumerate(sorted(analysis.pseudos), 256): + instmap[name] = op + + assert 255 not in instmap.values() + + out.emit( + """#ifndef Py_OPCODE_IDS_H +#define Py_OPCODE_IDS_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Instruction opcodes for compiled code */ +""" + ) + + def write_define(name: str, op: int) -> None: + out.emit(f"#define {name:<38} {op:>3}\n") + + for op, name in sorted([(op, name) for (name, op) in instmap.items()]): + write_define(name, op) + + out.emit("\n") + write_define("HAVE_ARGUMENT", len(no_arg)) + write_define("MIN_INSTRUMENTED_OPCODE", min_instrumented) + + out.emit("\n") + out.emit("#ifdef __cplusplus\n") + out.emit("}\n") + out.emit("#endif\n") + out.emit("#endif /* !Py_OPCODE_IDS_H */\n") + + +arg_parser = argparse.ArgumentParser( + description="Generate the header file with all opcode IDs.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, +) + +arg_parser.add_argument( + "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT +) + +arg_parser.add_argument( + "input", nargs=argparse.REMAINDER, help="Instruction definition file(s)" +) + +if __name__ == "__main__": + args = arg_parser.parse_args() + if len(args.input) == 0: + args.input.append(DEFAULT_INPUT) + data = analyze_files(args.input) + with open(args.output, "w") as outfile: + generate_opcode_header(args.input, data, outfile) diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py new file mode 100644 index 00000000000000..12173a61199700 --- /dev/null +++ b/Tools/cases_generator/parser.py @@ -0,0 +1,55 @@ +from parsing import ( + InstDef, + Macro, + Pseudo, + Family, + Parser, + Context, + CacheEffect, + StackEffect, + OpName, + AstNode, +) +from formatting import prettify_filename + + +BEGIN_MARKER = "// BEGIN BYTECODES //" +END_MARKER = "// END BYTECODES //" + + +def parse_files(filenames: list[str]) -> list[AstNode]: + result: list[AstNode] = [] + for filename in filenames: + with open(filename) as file: + src = file.read() + + psr = Parser(src, filename=prettify_filename(filename)) + + # Skip until begin marker + while tkn := psr.next(raw=True): + if tkn.text == BEGIN_MARKER: + break + else: + raise psr.make_syntax_error( + f"Couldn't find {BEGIN_MARKER!r} in {psr.filename}" + ) + start = psr.getpos() + + # Find end marker, then delete everything after it + while tkn := psr.next(raw=True): + if tkn.text == END_MARKER: + break + del psr.tokens[psr.getpos() - 1 :] + + # Parse from start + psr.setpos(start) + thing_first_token = psr.peek() + while node := psr.definition(): + assert node is not None + result.append(node) # type: ignore[arg-type] + if not psr.eof(): + psr.backup() + raise psr.make_syntax_error( + f"Extra stuff at the end of {filename}", psr.next(True) + ) + return result diff --git a/Tools/cases_generator/parsing.py b/Tools/cases_generator/parsing.py index d36bd52b022ea9..60c185dcef58e9 100644 --- a/Tools/cases_generator/parsing.py +++ b/Tools/cases_generator/parsing.py @@ -138,12 +138,14 @@ class Family(Node): @dataclass class Pseudo(Node): name: str - targets: list[str] # opcodes this can be replaced by + flags: list[str] # instr flags to set on the pseudo instruction + targets: list[str] # opcodes this can be replaced by +AstNode = InstDef | Macro | Pseudo | Family class Parser(PLexer): @contextual - def definition(self) -> InstDef | Macro | Pseudo | Family | None: + def definition(self) -> AstNode | None: if macro := self.macro_def(): return macro if family := self.family_def(): @@ -374,19 +376,39 @@ def family_def(self) -> Family | None: ) return None + def flags(self) -> list[str]: + here = self.getpos() + if self.expect(lx.LPAREN): + if tkn := self.expect(lx.IDENTIFIER): + flags = [tkn.text] + while self.expect(lx.COMMA): + if tkn := self.expect(lx.IDENTIFIER): + flags.append(tkn.text) + else: + break + if not self.expect(lx.RPAREN): + raise self.make_syntax_error("Expected comma or right paren") + return flags + self.setpos(here) + return [] + @contextual def pseudo_def(self) -> Pseudo | None: if (tkn := self.expect(lx.IDENTIFIER)) and tkn.text == "pseudo": size = None if self.expect(lx.LPAREN): if tkn := self.expect(lx.IDENTIFIER): + if self.expect(lx.COMMA): + flags = self.flags() + else: + flags = [] if self.expect(lx.RPAREN): if self.expect(lx.EQUALS): if not self.expect(lx.LBRACE): raise self.make_syntax_error("Expected {") if members := self.members(): if self.expect(lx.RBRACE) and self.expect(lx.SEMI): - return Pseudo(tkn.text, members) + return Pseudo(tkn.text, flags, members) return None def members(self) -> list[str] | None: diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py new file mode 100644 index 00000000000000..9cd8e1a3b2edf8 --- /dev/null +++ b/Tools/cases_generator/stack.py @@ -0,0 +1,81 @@ +import sys +from analyzer import StackItem +from dataclasses import dataclass +from formatting import maybe_parenthesize + + +def var_size(var: StackItem) -> str: + if var.condition: + # Special case simplification + if var.condition == "oparg & 1" and var.size == "1": + return f"({var.condition})" + else: + return f"(({var.condition}) ? {var.size} : 0)" + else: + return var.size + + +class StackOffset: + "The stack offset of the virtual base of the stack from the physical stack pointer" + + def __init__(self) -> None: + self.popped: list[str] = [] + self.pushed: list[str] = [] + + def pop(self, item: StackItem) -> None: + self.popped.append(var_size(item)) + + def push(self, item: StackItem) -> None: + self.pushed.append(var_size(item)) + + def simplify(self) -> None: + "Remove matching values from both the popped and pushed list" + if not self.popped or not self.pushed: + return + # Sort the list so the lexically largest element is last. + popped = sorted(self.popped) + pushed = sorted(self.pushed) + self.popped = [] + self.pushed = [] + while popped and pushed: + pop = popped.pop() + push = pushed.pop() + if pop == push: + pass + elif pop > push: + # if pop > push, there can be no element in pushed matching pop. + self.popped.append(pop) + pushed.append(push) + else: + self.pushed.append(push) + popped.append(pop) + self.popped.extend(popped) + self.pushed.extend(pushed) + + def to_c(self) -> str: + self.simplify() + int_offset = 0 + symbol_offset = "" + for item in self.popped: + try: + int_offset -= int(item) + except ValueError: + symbol_offset += f" - {maybe_parenthesize(item)}" + for item in self.pushed: + try: + int_offset += int(item) + except ValueError: + symbol_offset += f" + {maybe_parenthesize(item)}" + if symbol_offset and not int_offset: + res = symbol_offset + else: + res = f"{int_offset}{symbol_offset}" + if res.startswith(" + "): + res = res[3:] + if res.startswith(" - "): + res = "-" + res[3:] + return res + + def clear(self) -> None: + self.popped = [] + self.pushed = [] diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py new file mode 100644 index 00000000000000..9787403b3bbc47 --- /dev/null +++ b/Tools/cases_generator/tier1_generator.py @@ -0,0 +1,410 @@ +"""Generate the main interpreter switch. +Reads the instruction definitions from bytecodes.c. +Writes the cases to generated_cases.c.h, which is #included in ceval.c. +""" + +import argparse +import os.path +import sys + +from analyzer import ( + Analysis, + Instruction, + Uop, + Part, + analyze_files, + Skip, + StackItem, + analysis_error, +) +from generators_common import ( + DEFAULT_INPUT, + ROOT, + write_header, +) +from cwriter import CWriter +from typing import TextIO, Iterator +from lexer import Token +from stack import StackOffset + + +DEFAULT_OUTPUT = ROOT / "Python/generated_cases.c.h" + + +FOOTER = "#undef TIER_ONE\n" + + +class SizeMismatch(Exception): + pass + + +class Stack: + def __init__(self) -> None: + self.top_offset = StackOffset() + self.base_offset = StackOffset() + self.peek_offset = StackOffset() + self.variables: list[StackItem] = [] + self.defined: set[str] = set() + + def pop(self, var: StackItem) -> str: + self.top_offset.pop(var) + if not var.peek: + self.peek_offset.pop(var) + indirect = "&" if var.is_array() else "" + if self.variables: + popped = self.variables.pop() + if popped.size != var.size: + raise SizeMismatch( + f"Size mismatch when popping '{popped.name}' from stack to assign to {var.name}. " + f"Expected {var.size} got {popped.size}" + ) + if popped.name == var.name: + return "" + elif popped.name == "unused": + self.defined.add(var.name) + return ( + f"{var.name} = {indirect}stack_pointer[{self.top_offset.to_c()}];\n" + ) + elif var.name == "unused": + return "" + else: + self.defined.add(var.name) + return f"{var.name} = {popped.name};\n" + self.base_offset.pop(var) + if var.name == "unused": + return "" + else: + self.defined.add(var.name) + assign = f"{var.name} = {indirect}stack_pointer[{self.base_offset.to_c()}];" + if var.condition: + return f"if ({var.condition}) {{ {assign} }}\n" + return f"{assign}\n" + + def push(self, var: StackItem) -> str: + self.variables.append(var) + if var.is_array() and var.name not in self.defined and var.name != "unused": + c_offset = self.top_offset.to_c() + self.top_offset.push(var) + self.defined.add(var.name) + return f"{var.name} = &stack_pointer[{c_offset}];\n" + else: + self.top_offset.push(var) + return "" + + def flush(self, out: CWriter) -> None: + for var in self.variables: + if not var.peek: + if var.name != "unused" and not var.is_array(): + if var.condition: + out.emit(f" if ({var.condition}) ") + out.emit( + f"stack_pointer[{self.base_offset.to_c()}] = {var.name};\n" + ) + self.base_offset.push(var) + if self.base_offset.to_c() != self.top_offset.to_c(): + print("base", self.base_offset.to_c(), "top", self.top_offset.to_c()) + assert False + number = self.base_offset.to_c() + if number != "0": + out.emit(f"stack_pointer += {number};\n") + self.variables = [] + self.base_offset.clear() + self.top_offset.clear() + self.peek_offset.clear() + + def as_comment(self) -> str: + return f"/* Variables: {[v.name for v in self.variables]}. Base offset: {self.base_offset.to_c()}. Top offset: {self.top_offset.to_c()} */" + + +def declare_variables(inst: Instruction, out: CWriter) -> None: + variables = {"unused"} + for uop in inst.parts: + if isinstance(uop, Uop): + for var in reversed(uop.stack.inputs): + if var.name not in variables: + type = var.type if var.type else "PyObject *" + variables.add(var.name) + if var.condition: + out.emit(f"{type}{var.name} = NULL;\n") + else: + out.emit(f"{type}{var.name};\n") + for var in uop.stack.outputs: + if var.name not in variables: + variables.add(var.name) + type = var.type if var.type else "PyObject *" + if var.condition: + out.emit(f"{type}{var.name} = NULL;\n") + else: + out.emit(f"{type}{var.name};\n") + + +def emit_to(out: CWriter, tkn_iter: Iterator[Token], end: str) -> None: + parens = 0 + for tkn in tkn_iter: + if tkn.kind == end and parens == 0: + return + if tkn.kind == "LPAREN": + parens += 1 + if tkn.kind == "RPAREN": + parens -= 1 + out.emit(tkn) + + +def replace_deopt( + out: CWriter, + tkn: Token, + tkn_iter: Iterator[Token], + uop: Uop, + unused: Stack, + inst: Instruction, +) -> None: + out.emit_at("DEOPT_IF", tkn) + out.emit(next(tkn_iter)) + emit_to(out, tkn_iter, "RPAREN") + next(tkn_iter) # Semi colon + out.emit(", ") + assert inst.family is not None + out.emit(inst.family.name) + out.emit(");\n") + + +def replace_error( + out: CWriter, + tkn: Token, + tkn_iter: Iterator[Token], + uop: Uop, + stack: Stack, + inst: Instruction, +) -> None: + out.emit_at("if ", tkn) + out.emit(next(tkn_iter)) + emit_to(out, tkn_iter, "COMMA") + label = next(tkn_iter).text + next(tkn_iter) # RPAREN + next(tkn_iter) # Semi colon + out.emit(") ") + c_offset = stack.peek_offset.to_c() + try: + offset = -int(c_offset) + close = ";\n" + except ValueError: + offset = None + out.emit(f"{{ stack_pointer += {c_offset}; ") + close = "; }\n" + out.emit("goto ") + if offset: + out.emit(f"pop_{offset}_") + out.emit(label) + out.emit(close) + + +def replace_decrefs( + out: CWriter, + tkn: Token, + tkn_iter: Iterator[Token], + uop: Uop, + stack: Stack, + inst: Instruction, +) -> None: + next(tkn_iter) + next(tkn_iter) + next(tkn_iter) + out.emit_at("", tkn) + for var in uop.stack.inputs: + if var.name == "unused" or var.name == "null" or var.peek: + continue + if var.size != "1": + out.emit(f"for (int _i = {var.size}; --_i >= 0;) {{\n") + out.emit(f"Py_DECREF({var.name}[_i]);\n") + out.emit("}\n") + elif var.condition: + out.emit(f"Py_XDECREF({var.name});\n") + else: + out.emit(f"Py_DECREF({var.name});\n") + + +def replace_store_sp( + out: CWriter, + tkn: Token, + tkn_iter: Iterator[Token], + uop: Uop, + stack: Stack, + inst: Instruction, +) -> None: + next(tkn_iter) + next(tkn_iter) + next(tkn_iter) + out.emit_at("", tkn) + stack.flush(out) + out.emit("_PyFrame_SetStackPointer(frame, stack_pointer);\n") + + +def replace_check_eval_breaker( + out: CWriter, + tkn: Token, + tkn_iter: Iterator[Token], + uop: Uop, + stack: Stack, + inst: Instruction, +) -> None: + next(tkn_iter) + next(tkn_iter) + next(tkn_iter) + if not uop.properties.ends_with_eval_breaker: + out.emit_at("CHECK_EVAL_BREAKER();", tkn) + + +REPLACEMENT_FUNCTIONS = { + "DEOPT_IF": replace_deopt, + "ERROR_IF": replace_error, + "DECREF_INPUTS": replace_decrefs, + "CHECK_EVAL_BREAKER": replace_check_eval_breaker, + "STORE_SP": replace_store_sp, +} + + +# Move this to formatter +def emit_tokens(out: CWriter, uop: Uop, stack: Stack, inst: Instruction) -> None: + tkns = uop.body[1:-1] + if not tkns: + return + tkn_iter = iter(tkns) + out.start_line() + for tkn in tkn_iter: + if tkn.kind == "IDENTIFIER" and tkn.text in REPLACEMENT_FUNCTIONS: + REPLACEMENT_FUNCTIONS[tkn.text](out, tkn, tkn_iter, uop, stack, inst) + else: + out.emit(tkn) + + +def write_uop( + uop: Part, out: CWriter, offset: int, stack: Stack, inst: Instruction, braces: bool +) -> int: + # out.emit(stack.as_comment() + "\n") + if isinstance(uop, Skip): + entries = "entries" if uop.size > 1 else "entry" + out.emit(f"/* Skip {uop.size} cache {entries} */\n") + return offset + uop.size + try: + out.start_line() + if braces: + out.emit(f"// {uop.name}\n") + for var in reversed(uop.stack.inputs): + out.emit(stack.pop(var)) + if braces: + out.emit("{\n") + if not uop.properties.stores_sp: + for i, var in enumerate(uop.stack.outputs): + out.emit(stack.push(var)) + for cache in uop.caches: + if cache.name != "unused": + if cache.size == 4: + type = "PyObject *" + reader = "read_obj" + else: + type = f"uint{cache.size*16}_t " + reader = f"read_u{cache.size*16}" + out.emit( + f"{type}{cache.name} = {reader}(&this_instr[{offset}].cache);\n" + ) + offset += cache.size + emit_tokens(out, uop, stack, inst) + if uop.properties.stores_sp: + for i, var in enumerate(uop.stack.outputs): + out.emit(stack.push(var)) + if braces: + out.start_line() + out.emit("}\n") + # out.emit(stack.as_comment() + "\n") + return offset + except SizeMismatch as ex: + raise analysis_error(ex.args[0], uop.body[0]) + + +def uses_this(inst: Instruction) -> bool: + if inst.properties.needs_this: + return True + for uop in inst.parts: + if isinstance(uop, Skip): + continue + for cache in uop.caches: + if cache.name != "unused": + return True + return False + + +def generate_tier1( + filenames: str, analysis: Analysis, outfile: TextIO, lines: bool +) -> None: + write_header(__file__, filenames, outfile) + outfile.write( + """ +#ifdef TIER_TWO + #error "This file is for Tier 1 only" +#endif +#define TIER_ONE 1 +""" + ) + out = CWriter(outfile, 2, lines) + out.emit("\n") + for name, inst in sorted(analysis.instructions.items()): + needs_this = uses_this(inst) + out.emit("\n") + out.emit(f"TARGET({name}) {{\n") + if needs_this and not inst.is_target: + out.emit(f"_Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr;\n") + else: + out.emit(f"frame->instr_ptr = next_instr;\n") + out.emit(f"next_instr += {inst.size};\n") + out.emit(f"INSTRUCTION_STATS({name});\n") + if inst.is_target: + out.emit(f"PREDICTED({name});\n") + if needs_this: + out.emit(f"_Py_CODEUNIT *this_instr = next_instr - {inst.size};\n") + if inst.family is not None: + out.emit( + f"static_assert({inst.family.size} == {inst.size-1}" + ', "incorrect cache size");\n' + ) + declare_variables(inst, out) + offset = 1 # The instruction itself + stack = Stack() + for part in inst.parts: + # Only emit braces if more than one uop + offset = write_uop(part, out, offset, stack, inst, len(inst.parts) > 1) + out.start_line() + if not inst.parts[-1].properties.always_exits: + stack.flush(out) + if inst.parts[-1].properties.ends_with_eval_breaker: + out.emit("CHECK_EVAL_BREAKER();\n") + out.emit("DISPATCH();\n") + out.start_line() + out.emit("}") + out.emit("\n") + outfile.write(FOOTER) + + +arg_parser = argparse.ArgumentParser( + description="Generate the code for the interpreter switch.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, +) + +arg_parser.add_argument( + "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT +) + +arg_parser.add_argument( + "-l", "--emit-line-directives", help="Emit #line directives", action="store_true" +) + +arg_parser.add_argument( + "input", nargs=argparse.REMAINDER, help="Instruction definition file(s)" +) + +if __name__ == "__main__": + args = arg_parser.parse_args() + if len(args.input) == 0: + args.input.append(DEFAULT_INPUT) + data = analyze_files(args.input) + with open(args.output, "w") as outfile: + generate_tier1(args.input, data, outfile, args.emit_line_directives) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index a205a5182ee99f..6c76f66a81abd4 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -406,7 +406,7 @@ def flush() -> None: flush() return tuple(version) -def version_comparitor(version1: str, version2: str) -> Literal[-1, 0, 1]: +def version_comparator(version1: str, version2: str) -> Literal[-1, 0, 1]: iterator = itertools.zip_longest( version_splitter(version1), version_splitter(version2), fillvalue=0 ) @@ -846,6 +846,10 @@ class CLanguage(Language): static PyObject * {c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored)) """) + PARSER_PROTOTYPE_GETTER: Final[str] = normalize_snippet(""" + static PyObject * + {c_basename}({self_type}{self_name}, void *Py_UNUSED(context)) + """) METH_O_PROTOTYPE: Final[str] = normalize_snippet(""" static PyObject * {c_basename}({impl_parameters}) @@ -865,6 +869,10 @@ class CLanguage(Language): #define {methoddef_name} \ {{"{name}", {methoddef_cast}{c_basename}{methoddef_cast_end}, {methoddef_flags}, {c_basename}__doc__}}, """) + GETTERDEF_PROTOTYPE_DEFINE: Final[str] = normalize_snippet(r""" + #define {getter_name} \ + {{"{name}", (getter){c_basename}, NULL, NULL}}, + """) METHODDEF_PROTOTYPE_IFNDEF: Final[str] = normalize_snippet(""" #ifndef {methoddef_name} #define {methoddef_name} @@ -1111,7 +1119,8 @@ def output_templates( if include: clinic.add_include(include.filename, include.reason, condition=include.condition) - + if f.critical_section: + clinic.add_include('pycore_critical_section.h', 'Py_BEGIN_CRITICAL_SECTION()') has_option_groups = parameters and (parameters[0].group or parameters[-1].group) simple_return = (f.return_converter.type == 'PyObject *' and not f.critical_section) @@ -1160,6 +1169,9 @@ def output_templates( methoddef_define = self.METHODDEF_PROTOTYPE_DEFINE if new_or_init and not f.docstring: docstring_prototype = docstring_definition = '' + elif f.kind is GETTER: + methoddef_define = self.GETTERDEF_PROTOTYPE_DEFINE + docstring_prototype = docstring_definition = '' else: docstring_prototype = self.DOCSTRING_PROTOTYPE_VAR docstring_definition = self.DOCSTRING_PROTOTYPE_STRVAR @@ -1216,7 +1228,11 @@ def parser_body( parsearg: str | None if not parameters: parser_code: list[str] | None - if not requires_defining_class: + if f.kind is GETTER: + flags = "" # This should end up unused + parser_prototype = self.PARSER_PROTOTYPE_GETTER + parser_code = [] + elif not requires_defining_class: # no parameters, METH_NOARGS flags = "METH_NOARGS" parser_prototype = self.PARSER_PROTOTYPE_NOARGS @@ -1669,6 +1685,8 @@ def parser_body( methoddef_cast_end = "" if flags in ('METH_NOARGS', 'METH_O', 'METH_VARARGS'): methoddef_cast = "(PyCFunction)" + elif f.kind is GETTER: + methoddef_cast = "" # This should end up unused elif limited_capi: methoddef_cast = "(PyCFunction)(void(*)(void))" else: @@ -1866,8 +1884,18 @@ def render_function( assert isinstance(f_self.converter, self_converter), "No self parameter in " + repr(f.full_name) + "!" if f.critical_section: - data.lock.append('Py_BEGIN_CRITICAL_SECTION({self_name});') - data.unlock.append('Py_END_CRITICAL_SECTION();') + match len(f.target_critical_section): + case 0: + lock = 'Py_BEGIN_CRITICAL_SECTION({self_name});' + unlock = 'Py_END_CRITICAL_SECTION();' + case 1: + lock = 'Py_BEGIN_CRITICAL_SECTION({target_critical_section});' + unlock = 'Py_END_CRITICAL_SECTION();' + case _: + lock = 'Py_BEGIN_CRITICAL_SECTION2({target_critical_section});' + unlock = 'Py_END_CRITICAL_SECTION2();' + data.lock.append(lock) + data.unlock.append(unlock) last_group = 0 first_optional = len(selfless) @@ -1916,12 +1944,17 @@ def render_function( full_name = f.full_name template_dict = {'full_name': full_name} template_dict['name'] = f.displayname - template_dict['c_basename'] = f.c_basename - template_dict['methoddef_name'] = f.c_basename.upper() + "_METHODDEF" + if f.kind is GETTER: + template_dict['getter_name'] = f.c_basename.upper() + "_GETTERDEF" + template_dict['c_basename'] = f.c_basename + "_get" + else: + template_dict['methoddef_name'] = f.c_basename.upper() + "_METHODDEF" + template_dict['c_basename'] = f.c_basename template_dict['docstring'] = self.docstring_for_c_string(f) template_dict['self_name'] = template_dict['self_type'] = template_dict['self_type_check'] = '' + template_dict['target_critical_section'] = ', '.join(f.target_critical_section) for converter in converters: converter.set_template_dict(template_dict) @@ -2920,6 +2953,7 @@ class FunctionKind(enum.Enum): CLASS_METHOD = enum.auto() METHOD_INIT = enum.auto() METHOD_NEW = enum.auto() + GETTER = enum.auto() @functools.cached_property def new_or_init(self) -> bool: @@ -2935,6 +2969,7 @@ def __repr__(self) -> str: CLASS_METHOD: Final = FunctionKind.CLASS_METHOD METHOD_INIT: Final = FunctionKind.METHOD_INIT METHOD_NEW: Final = FunctionKind.METHOD_NEW +GETTER: Final = FunctionKind.GETTER ParamDict = dict[str, "Parameter"] ReturnConverterType = Callable[..., "CReturnConverter"] @@ -2970,6 +3005,7 @@ class Function: # those accurately with inspect.Signature in 3.4. docstring_only: bool = False critical_section: bool = False + target_critical_section: list[str] = dc.field(default_factory=list) def __post_init__(self) -> None: self.parent = self.cls or self.module @@ -3020,7 +3056,8 @@ def methoddef_flags(self) -> str | None: case FunctionKind.STATIC_METHOD: flags.append('METH_STATIC') case _ as kind: - assert kind is FunctionKind.CALLABLE, f"unknown kind: {kind!r}" + acceptable_kinds = {FunctionKind.CALLABLE, FunctionKind.GETTER} + assert kind in acceptable_kinds, f"unknown kind: {kind!r}" if self.coexist: flags.append('METH_COEXIST') return '|'.join(flags) @@ -4665,7 +4702,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st def correct_name_for_self( f: Function ) -> tuple[str, str]: - if f.kind in (CALLABLE, METHOD_INIT): + if f.kind in {CALLABLE, METHOD_INIT, GETTER}: if f.cls: return "PyObject *", "self" return "PyObject *", "module" @@ -5122,9 +5159,11 @@ class DSLParser: indent: IndentStack kind: FunctionKind coexist: bool + forced_text_signature: str | None parameter_continuation: str preserve_output: bool critical_section: bool + target_critical_section: list[str] from_version_re = re.compile(r'([*/]) +\[from +(.+)\]') def __init__(self, clinic: Clinic) -> None: @@ -5156,14 +5195,15 @@ def reset(self) -> None: self.indent = IndentStack() self.kind = CALLABLE self.coexist = False - self.forced_text_signature: str | None = None + self.forced_text_signature = None self.parameter_continuation = '' self.preserve_output = False self.critical_section = False + self.target_critical_section = [] def directive_version(self, required: str) -> None: global version - if version_comparitor(version, required) < 0: + if version_comparator(version, required) < 0: fail("Insufficient Clinic version!\n" f" Version: {version}\n" f" Required: {required}") @@ -5288,9 +5328,15 @@ def at_classmethod(self) -> None: fail("Can't set @classmethod, function is not a normal callable") self.kind = CLASS_METHOD - def at_critical_section(self) -> None: + def at_critical_section(self, *args: str) -> None: + if len(args) > 2: + fail("Up to 2 critical section variables are supported") + self.target_critical_section.extend(args) self.critical_section = True + def at_getter(self) -> None: + self.kind = GETTER + def at_staticmethod(self) -> None: if self.kind is not CALLABLE: fail("Can't set @staticmethod, function is not a normal callable") @@ -5400,14 +5446,20 @@ def update_function_kind(self, fullname: str) -> None: _, cls = self.clinic._module_and_class(fields) if name in unsupported_special_methods: fail(f"{name!r} is a special method and cannot be converted to Argument Clinic!") + if name == '__new__': - if (self.kind is not CLASS_METHOD) or (not cls): + if (self.kind is CLASS_METHOD) and cls: + self.kind = METHOD_NEW + else: fail("'__new__' must be a class method!") - self.kind = METHOD_NEW elif name == '__init__': - if (self.kind is not CALLABLE) or (not cls): - fail("'__init__' must be a normal method, not a class or static method!") - self.kind = METHOD_INIT + if (self.kind is CALLABLE) and cls: + self.kind = METHOD_INIT + else: + fail( + "'__init__' must be a normal method; " + f"got '{self.kind}'!" + ) def state_modulename_name(self, line: str) -> None: # looking for declaration, which establishes the leftmost column @@ -5514,7 +5566,8 @@ def state_modulename_name(self, line: str) -> None: self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename, return_converter=return_converter, kind=self.kind, coexist=self.coexist, - critical_section=self.critical_section) + critical_section=self.critical_section, + target_critical_section=self.target_critical_section) self.block.signatures.append(self.function) # insert a self converter automatically diff --git a/Tools/peg_generator/pegen/c_generator.py b/Tools/peg_generator/pegen/c_generator.py index 301949bdae96fe..7cdd5debe9a225 100644 --- a/Tools/peg_generator/pegen/c_generator.py +++ b/Tools/peg_generator/pegen/c_generator.py @@ -38,7 +38,11 @@ #endif #ifdef __wasi__ -# define MAXSTACK 4000 +# ifdef Py_DEBUG +# define MAXSTACK 1000 +# else +# define MAXSTACK 4000 +# endif #else # define MAXSTACK 6000 #endif diff --git a/Tools/requirements-dev.txt b/Tools/requirements-dev.txt index 591baac33c7e8f..3a2e62f70bbb60 100644 --- a/Tools/requirements-dev.txt +++ b/Tools/requirements-dev.txt @@ -1,7 +1,7 @@ # Requirements file for external linters and checks we run on # Tools/clinic, Tools/cases_generator/, and Tools/peg_generator/ in CI -mypy==1.7.0 +mypy==1.7.1 # needed for peg_generator: types-psutil==5.9.5.17 -types-setuptools==68.2.0.0 +types-setuptools==69.0.0.0 diff --git a/Tools/requirements-hypothesis.txt b/Tools/requirements-hypothesis.txt index 0fcc72ac69073d..1bca5d2367f4b2 100644 --- a/Tools/requirements-hypothesis.txt +++ b/Tools/requirements-hypothesis.txt @@ -1,4 +1,4 @@ # Requirements file for hypothesis that # we use to run our property-based tests in CI. -hypothesis==6.88.1 +hypothesis==6.91.0 diff --git a/Tools/wasm/README.md b/Tools/wasm/README.md index 8ef63c6dcd9ddc..beb857f69e40da 100644 --- a/Tools/wasm/README.md +++ b/Tools/wasm/README.md @@ -298,102 +298,68 @@ AddType application/wasm wasm ## WASI (wasm32-wasi) -WASI builds require the [WASI SDK](https://github.com/WebAssembly/wasi-sdk) 16.0+. -See `.devcontainer/Dockerfile` for an example of how to download and -install the WASI SDK. +**NOTE**: The instructions below assume a Unix-based OS due to cross-compilation for CPython being set up for `./configure`. -### Build +### Prerequisites + +Developing for WASI requires two additional tools to be installed beyond the typical tools required to build CPython: + +1. The [WASI SDK](https://github.com/WebAssembly/wasi-sdk) 16.0+ +2. A WASI host/runtime ([wasmtime](https://wasmtime.dev) 14+ is recommended and what the instructions below assume) -The script ``wasi-env`` sets necessary compiler and linker flags as well as -``pkg-config`` overrides. The script assumes that WASI-SDK is installed in -``/opt/wasi-sdk`` or ``$WASI_SDK_PATH``. +All of this is provided in the [devcontainer](https://devguide.python.org/getting-started/setup-building/#contribute-using-github-codespaces) if you don't want to install these tools locally. -There are two scripts you can use to do a WASI build from a source checkout. You can either use: +### Building +Building for WASI requires doing a cross-build where you have a "build" Python to help produce a WASI build of CPython (technically it's a "host x host" cross-build because the build Python is also the target Python while the host build is the WASI build; yes, it's confusing terminology). In the end you should have a build Python in `cross-build/build` and a WASI build in `cross-build/wasm32-wasi`. + +The easiest way to do a build is to use the `wasi.py` script. You can either have it perform the entire build process from start to finish in one step, or you can do it in discrete steps that mirror running `configure` and `make` for each of the two builds of Python you end up producing (which are beneficial when you only need to do a specific step after getting a complete build, e.g. editing some code and you just need to run `make` for the WASI build). The script is designed to self-document what actions it is performing on your behalf, both as a way to check its work but also for educaitonal purposes. + +The discrete steps for building via `wasi.py` are: ```shell -./Tools/wasm/wasm_build.py wasi build +python Tools/wasm/wasi.py configure-build-python +python Tools/wasm/wasi.py make-build-python +python Tools/wasm/wasi.py configure-host +python Tools/wasm/wasi.py make-host ``` -or: +To do it all in a single command, run: ```shell -./Tools/wasm/build_wasi.sh +python Tools/wasm/wasi.py build ``` -The commands are equivalent to the following steps: - -- Make sure `Modules/Setup.local` exists -- Make sure the necessary build tools are installed: - - [WASI SDK](https://github.com/WebAssembly/wasi-sdk) (which ships with `clang`) - - `make` - - `pkg-config` (on Linux) -- Create the build Python - - `mkdir -p builddir/build` - - `pushd builddir/build` - - Get the build platform - - Python: `sysconfig.get_config_var("BUILD_GNU_TYPE")` - - Shell: `../../config.guess` - - `../../configure -C` - - `make all` - - ```PYTHON_VERSION=`./python -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")'` ``` - - `popd` -- Create the host/WASI Python - - `mkdir builddir/wasi` - - `pushd builddir/wasi` - - `../../Tools/wasm/wasi-env ../../configure -C --host=wasm32-unknown-wasi --build=$(../../config.guess) --with-build-python=../build/python` - - `CONFIG_SITE=../../Tools/wasm/config.site-wasm32-wasi` - - `HOSTRUNNER="wasmtime run --mapdir /::$(dirname $(dirname $(pwd))) --env PYTHONPATH=/builddir/wasi/build/lib.wasi-wasm32-$PYTHON_VERSION $(pwd)/python.wasm --"` - - Maps the source checkout to `/` in the WASI runtime - - Stdlib gets loaded from `/Lib` - - Gets `_sysconfigdata__wasi_wasm32-wasi.py` on to `sys.path` via `PYTHONPATH` - - Set by `wasi-env` - - `WASI_SDK_PATH` - - `WASI_SYSROOT` - - `CC` - - `CPP` - - `CXX` - - `LDSHARED` - - `AR` - - `RANLIB` - - `CFLAGS` - - `LDFLAGS` - - `PKG_CONFIG_PATH` - - `PKG_CONFIG_LIBDIR` - - `PKG_CONFIG_SYSROOT_DIR` - - `PATH` - - `make all` - +That will: -### Running - -If you followed the instructions above, you can run the interpreter via e.g., `wasmtime` from within the `Tools/wasi` directory (make sure to set/change `$PYTHON_VERSION` and do note the paths are relative to running in`builddir/wasi` for simplicity only): +1. Run `configure` for the build Python (same as `wasi.py configure-build-python`) +2. Run `make` for the build Python (`wasi.py make-build-python`) +3. Run `configure` for the WASI build (`wasi.py configure-host`) +4. Run `make` for the WASI build (`wasi.py make-host`) +See the `--help` for the various options available for each of the subcommands which controls things like the location of the WASI SDK, the command to use with the WASI host/runtime, etc. Also note that you can use `--` as a separator for any of the `configure`-related commands -- including `build` itself -- to pass arguments to the underlying `configure` call. For example, if you want a pydebug build that also caches the results from `configure`, you can do: ```shell -wasmtime run --mapdir /::../.. --env PYTHONPATH=/builddir/wasi/build/lib.wasi-wasm32-$PYTHON_VERSION python.wasm -- +python Tools/wasm/wasi.py build -- -C --with-pydebug ``` -There are also helpers provided by `Tools/wasm/wasm_build.py` as listed below. Also, if you used `Tools/wasm/build_wasi.sh`, a `run_wasi.sh` file will be created in `builddir/wasi` which will run the above command for you (it also uses absolute paths, so it can be executed from anywhere). - -#### REPL - +The `wasi.py` script is able to infer details from the build Python, and so you only technically need to specify `--with-pydebug` once via `configure-build-python` as this will lead to `configure-host` detecting its use if you use the discrete steps: ```shell -./Tools/wasm/wasm_build.py wasi repl +python Tools/wasm/wasi.py configure-build-python -- -C --with-pydebug +python Tools/wasm/wasi.py make-build-python +python Tools/wasm/wasi.py configure-host -- -C +python Tools/wasm/wasi.py make-host ``` -#### Tests +### Running + +If you used `wasi.py` to do your build then there will be a `cross-build/wasm32-wasi/python.sh` file which you can use to run the `python.wasm` file (see the output from the `configure-host` subcommand): ```shell -./Tools/wasm/wasm_build.py wasi test +cross-build/wasm32-wasi/python.sh --version ``` -### Debugging +While you _can_ run `python.wasm` directly, Python will fail to start up without certain things being set (e.g. `PYTHONPATH` for `sysconfig` data). As such, the `python.sh` file records these details for you. -* ``wasmtime run -g`` generates debugging symbols for gdb and lldb. The - feature is currently broken, see - https://github.com/bytecodealliance/wasmtime/issues/4669 . -* The environment variable ``RUST_LOG=wasi_common`` enables debug and - trace logging. -## Detect WebAssembly builds +## Detecting WebAssembly builds ### Python code @@ -402,15 +368,17 @@ import os, sys if sys.platform == "emscripten": # Python on Emscripten + ... if sys.platform == "wasi": # Python on WASI + ... if os.name == "posix": # WASM platforms identify as POSIX-like. # Windows does not provide os.uname(). machine = os.uname().machine if machine.startswith("wasm"): - # WebAssembly (wasm32, wasm64 in the future) + # WebAssembly (wasm32, wasm64 potentially in the future) ``` ```python diff --git a/Tools/wasm/mypy.ini b/Tools/wasm/mypy.ini index c62598f89eba69..4de0a30c260f5f 100644 --- a/Tools/wasm/mypy.ini +++ b/Tools/wasm/mypy.ini @@ -1,5 +1,5 @@ [mypy] -files = Tools/wasm +files = Tools/wasm/wasm_*.py pretty = True show_traceback = True @@ -9,6 +9,3 @@ python_version = 3.8 # Be strict... strict = True enable_error_code = truthy-bool,ignore-without-code - -# except for incomplete defs, which are useful for module authors: -disallow_incomplete_defs = False diff --git a/Tools/wasm/wasi.py b/Tools/wasm/wasi.py new file mode 100644 index 00000000000000..34c0e9375e24c8 --- /dev/null +++ b/Tools/wasm/wasi.py @@ -0,0 +1,328 @@ +#!/usr/bin/env python3 + +import argparse +import contextlib +import functools +import os +try: + from os import process_cpu_count as cpu_count +except ImportError: + from os import cpu_count +import pathlib +import shutil +import subprocess +import sys +import sysconfig +import tempfile + + +CHECKOUT = pathlib.Path(__file__).parent.parent.parent +CROSS_BUILD_DIR = CHECKOUT / "cross-build" +BUILD_DIR = CROSS_BUILD_DIR / "build" +HOST_TRIPLE = "wasm32-wasi" +HOST_DIR = CROSS_BUILD_DIR / HOST_TRIPLE + + +def updated_env(updates={}): + """Create a new dict representing the environment to use. + + The changes made to the execution environment are printed out. + """ + env_defaults = {} + # https://reproducible-builds.org/docs/source-date-epoch/ + git_epoch_cmd = ["git", "log", "-1", "--pretty=%ct"] + try: + epoch = subprocess.check_output(git_epoch_cmd, encoding="utf-8").strip() + env_defaults["SOURCE_DATE_EPOCH"] = epoch + except subprocess.CalledProcessError: + pass # Might be building from a tarball. + # This layering lets SOURCE_DATE_EPOCH from os.environ takes precedence. + environment = env_defaults | os.environ | updates + + env_diff = {} + for key, value in environment.items(): + if os.environ.get(key) != value: + env_diff[key] = value + + print("🌎 Environment changes:") + for key in sorted(env_diff.keys()): + print(f" {key}={env_diff[key]}") + + return environment + + +def subdir(working_dir, *, clean_ok=False): + """Decorator to change to a working directory.""" + def decorator(func): + @functools.wraps(func) + def wrapper(context): + try: + tput_output = subprocess.check_output(["tput", "cols"], + encoding="utf-8") + terminal_width = int(tput_output.strip()) + except subprocess.CalledProcessError: + terminal_width = 80 + print("⎯" * terminal_width) + print("📁", working_dir) + if clean_ok and context.clean and working_dir.exists(): + print(f"🚮 Deleting directory (--clean)...") + shutil.rmtree(working_dir) + + working_dir.mkdir(parents=True, exist_ok=True) + + with contextlib.chdir(working_dir): + return func(context, working_dir) + + return wrapper + + return decorator + + +def call(command, *, quiet, **kwargs): + """Execute a command. + + If 'quiet' is true, then redirect stdout and stderr to a temporary file. + """ + print("❯", " ".join(map(str, command))) + if not quiet: + stdout = None + stderr = None + else: + stdout = tempfile.NamedTemporaryFile("w", encoding="utf-8", + delete=False, + prefix="cpython-wasi-", + suffix=".log") + stderr = subprocess.STDOUT + print(f"📝 Logging output to {stdout.name} (--quiet)...") + + subprocess.check_call(command, **kwargs, stdout=stdout, stderr=stderr) + + +def build_platform(): + """The name of the build/host platform.""" + # Can also be found via `config.guess`.` + return sysconfig.get_config_var("BUILD_GNU_TYPE") + + +def build_python_path(): + """The path to the build Python binary.""" + binary = BUILD_DIR / "python" + if not binary.is_file(): + binary = binary.with_suffix(".exe") + if not binary.is_file(): + raise FileNotFoundError("Unable to find `python(.exe)` in " + f"{BUILD_DIR}") + + return binary + + +@subdir(BUILD_DIR, clean_ok=True) +def configure_build_python(context, working_dir): + """Configure the build/host Python.""" + local_setup = CHECKOUT / "Modules" / "Setup.local" + if local_setup.exists(): + print(f"👍 {local_setup} exists ...") + else: + print(f"📝 Touching {local_setup} ...") + local_setup.touch() + + configure = [os.path.relpath(CHECKOUT / 'configure', working_dir)] + if context.args: + configure.extend(context.args) + + call(configure, quiet=context.quiet) + + +@subdir(BUILD_DIR) +def make_build_python(context, working_dir): + """Make/build the build Python.""" + call(["make", "--jobs", str(cpu_count()), "all"], + quiet=context.quiet) + + binary = build_python_path() + cmd = [binary, "-c", + "import sys; " + "print(f'{sys.version_info.major}.{sys.version_info.minor}')"] + version = subprocess.check_output(cmd, encoding="utf-8").strip() + + print(f"🎉 {binary} {version}") + + +def find_wasi_sdk(): + """Find the path to wasi-sdk.""" + if wasi_sdk_path := os.environ.get("WASI_SDK_PATH"): + return pathlib.Path(wasi_sdk_path) + elif (default_path := pathlib.Path("/opt/wasi-sdk")).exists(): + return default_path + + +def wasi_sdk_env(context): + """Calculate environment variables for building with wasi-sdk.""" + wasi_sdk_path = context.wasi_sdk_path + sysroot = wasi_sdk_path / "share" / "wasi-sysroot" + env = {"CC": "clang", "CPP": "clang-cpp", "CXX": "clang++", + "LDSHARED": "wasm-ld", "AR": "llvm-ar", "RANLIB": "ranlib"} + + for env_var, binary_name in list(env.items()): + env[env_var] = os.fsdecode(wasi_sdk_path / "bin" / binary_name) + + if wasi_sdk_path != pathlib.Path("/opt/wasi-sdk"): + for compiler in ["CC", "CPP", "CXX"]: + env[compiler] += f" --sysroot={sysroot}" + + env["PKG_CONFIG_PATH"] = "" + env["PKG_CONFIG_LIBDIR"] = os.pathsep.join( + map(os.fsdecode, + [sysroot / "lib" / "pkgconfig", + sysroot / "share" / "pkgconfig"])) + env["PKG_CONFIG_SYSROOT_DIR"] = os.fsdecode(sysroot) + + env["WASI_SDK_PATH"] = os.fsdecode(wasi_sdk_path) + env["WASI_SYSROOT"] = os.fsdecode(sysroot) + + env["PATH"] = os.pathsep.join([os.fsdecode(wasi_sdk_path / "bin"), + os.environ["PATH"]]) + + return env + + +@subdir(HOST_DIR, clean_ok=True) +def configure_wasi_python(context, working_dir): + """Configure the WASI/host build.""" + if not context.wasi_sdk_path or not context.wasi_sdk_path.exists(): + raise ValueError("WASI-SDK not found; " + "download from " + "https://github.com/WebAssembly/wasi-sdk and/or " + "specify via $WASI_SDK_PATH or --wasi-sdk") + + config_site = os.fsdecode(CHECKOUT / "Tools" / "wasm" / "config.site-wasm32-wasi") + + wasi_build_dir = working_dir.relative_to(CHECKOUT) + + python_build_dir = BUILD_DIR / "build" + lib_dirs = list(python_build_dir.glob("lib.*")) + assert len(lib_dirs) == 1, f"Expected a single lib.* directory in {python_build_dir}" + lib_dir = os.fsdecode(lib_dirs[0]) + pydebug = lib_dir.endswith("-pydebug") + python_version = lib_dir.removesuffix("-pydebug").rpartition("-")[-1] + sysconfig_data = f"{wasi_build_dir}/build/lib.wasi-wasm32-{python_version}" + if pydebug: + sysconfig_data += "-pydebug" + + # Use PYTHONPATH to include sysconfig data which must be anchored to the + # WASI guest's `/` directory. + host_runner = context.host_runner.format(GUEST_DIR="/", + HOST_DIR=CHECKOUT, + ENV_VAR_NAME="PYTHONPATH", + ENV_VAR_VALUE=f"/{sysconfig_data}", + PYTHON_WASM=working_dir / "python.wasm") + env_additions = {"CONFIG_SITE": config_site, "HOSTRUNNER": host_runner} + build_python = os.fsdecode(build_python_path()) + # The path to `configure` MUST be relative, else `python.wasm` is unable + # to find the stdlib due to Python not recognizing that it's being + # executed from within a checkout. + configure = [os.path.relpath(CHECKOUT / 'configure', working_dir), + f"--host={HOST_TRIPLE}", + f"--build={build_platform()}", + f"--with-build-python={build_python}"] + if pydebug: + configure.append("--with-pydebug") + if context.args: + configure.extend(context.args) + call(configure, + env=updated_env(env_additions | wasi_sdk_env(context)), + quiet=context.quiet) + + exec_script = working_dir / "python.sh" + with exec_script.open("w", encoding="utf-8") as file: + file.write(f'#!/bin/sh\nexec {host_runner} "$@"\n') + exec_script.chmod(0o755) + print(f"🏃‍♀️ Created {exec_script} ... ") + sys.stdout.flush() + + +@subdir(HOST_DIR) +def make_wasi_python(context, working_dir): + """Run `make` for the WASI/host build.""" + call(["make", "--jobs", str(cpu_count()), "all"], + env=updated_env(), + quiet=context.quiet) + + exec_script = working_dir / "python.sh" + subprocess.check_call([exec_script, "--version"]) + + +def build_all(context): + """Build everything.""" + steps = [configure_build_python, make_build_python, configure_wasi_python, + make_wasi_python] + for step in steps: + step(context) + + +def main(): + default_host_runner = (f"{shutil.which('wasmtime')} run " + # Make sure the stack size will work for a pydebug + # build. + # The 8388608 value comes from `ulimit -s` under Linux + # which equates to 8291 KiB. + "--wasm max-wasm-stack=8388608 " + # Enable thread support. + "--wasm threads=y --wasi threads=y " + # Map the checkout to / to load the stdlib from /Lib. + "--dir {HOST_DIR}::{GUEST_DIR} " + # Set PYTHONPATH to the sysconfig data. + "--env {ENV_VAR_NAME}={ENV_VAR_VALUE} " + # Path to the WASM binary. + "{PYTHON_WASM}") + + parser = argparse.ArgumentParser() + subcommands = parser.add_subparsers(dest="subcommand") + build = subcommands.add_parser("build", help="Build everything") + configure_build = subcommands.add_parser("configure-build-python", + help="Run `configure` for the " + "build Python") + make_build = subcommands.add_parser("make-build-python", + help="Run `make` for the build Python") + configure_host = subcommands.add_parser("configure-host", + help="Run `configure` for the " + "host/WASI (pydebug builds " + "are inferred from the build " + "Python)") + make_host = subcommands.add_parser("make-host", + help="Run `make` for the host/WASI") + for subcommand in build, configure_build, make_build, configure_host, make_host: + subcommand.add_argument("--quiet", action="store_true", default=False, + dest="quiet", + help="Redirect output from subprocesses to a log file") + for subcommand in build, configure_build, configure_host: + subcommand.add_argument("--clean", action="store_true", default=False, + dest="clean", + help="Delete any relevant directories before building") + for subcommand in build, configure_build, configure_host: + subcommand.add_argument("args", nargs="*", + help="Extra arguments to pass to `configure`") + for subcommand in build, configure_host: + subcommand.add_argument("--wasi-sdk", type=pathlib.Path, + dest="wasi_sdk_path", + default=find_wasi_sdk(), + help="Path to wasi-sdk; defaults to " + "$WASI_SDK_PATH or /opt/wasi-sdk") + subcommand.add_argument("--host-runner", action="store", + default=default_host_runner, dest="host_runner", + help="Command template for running the WebAssembly " + "code (default meant for wasmtime 14 or newer: " + f"`{default_host_runner}`)") + + context = parser.parse_args() + + dispatch = {"configure-build-python": configure_build_python, + "make-build-python": make_build_python, + "configure-host": configure_wasi_python, + "make-host": make_wasi_python, + "build": build_all} + dispatch[context.subcommand](context) + + +if __name__ == "__main__": + main() diff --git a/configure b/configure index 5e86e8255c03d0..8894122a11e86e 100755 --- a/configure +++ b/configure @@ -7883,7 +7883,7 @@ printf "%s\n" "$disable_gil" >&6; } if test "$disable_gil" = "yes" then -printf "%s\n" "#define Py_NOGIL 1" >>confdefs.h +printf "%s\n" "#define Py_GIL_DISABLED 1" >>confdefs.h # Add "t" for "threaded" ABIFLAGS="${ABIFLAGS}t" @@ -23971,7 +23971,6 @@ fi - # Check whether --with-readline was given. if test ${with_readline+y} then : @@ -23994,22 +23993,6 @@ else $as_nop fi -# gh-105323: Need to handle the macOS editline as an alias of readline. -case $ac_sys_system/$ac_sys_release in #( - Darwin/*) : - ac_fn_c_check_type "$LINENO" "Function" "ac_cv_type_Function" "#include -" -if test "x$ac_cv_type_Function" = xyes -then : - printf "%s\n" "#define WITH_APPLE_EDITLINE 1" >>confdefs.h - -fi - ;; #( - *) : - - ;; -esac - if test "x$with_readline" = xreadline then : diff --git a/configure.ac b/configure.ac index f46e19ba4be3f8..1512e6d9e8c42a 100644 --- a/configure.ac +++ b/configure.ac @@ -1505,7 +1505,7 @@ AC_MSG_RESULT([$disable_gil]) if test "$disable_gil" = "yes" then - AC_DEFINE([Py_NOGIL], [1], + AC_DEFINE([Py_GIL_DISABLED], [1], [Define if you want to disable the GIL]) # Add "t" for "threaded" ABIFLAGS="${ABIFLAGS}t" @@ -5914,7 +5914,6 @@ dnl library (tinfo ncursesw ncurses termcap). We now assume that libreadline dnl or readline.pc provide correct linker information. AH_TEMPLATE([WITH_EDITLINE], [Define to build the readline module against libedit.]) -AH_TEMPLATE([WITH_APPLE_EDITLINE], [Define to build the readline module against Apple BSD editline.]) AC_ARG_WITH( [readline], @@ -5931,15 +5930,6 @@ AC_ARG_WITH( [with_readline=readline] ) -# gh-105323: Need to handle the macOS editline as an alias of readline. -AS_CASE([$ac_sys_system/$ac_sys_release], - [Darwin/*], [AC_CHECK_TYPE([Function], - [AC_DEFINE([WITH_APPLE_EDITLINE])], - [], - [@%:@include ])], - [] -) - AS_VAR_IF([with_readline], [readline], [ PKG_CHECK_MODULES([LIBREADLINE], [readline], [ LIBREADLINE=readline diff --git a/pyconfig.h.in b/pyconfig.h.in index 2bbc1bdd52dc0e..2978fa2c17301f 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1611,13 +1611,13 @@ /* Defined if Python is built as a shared library. */ #undef Py_ENABLE_SHARED +/* Define if you want to disable the GIL */ +#undef Py_GIL_DISABLED + /* Define hash algorithm for str, bytes and memoryview. SipHash24: 1, FNV: 2, SipHash13: 3, externally defined: 0 */ #undef Py_HASH_ALGORITHM -/* Define if you want to disable the GIL */ -#undef Py_NOGIL - /* Define if you want to enable internal statistics gathering. */ #undef Py_STATS @@ -1800,9 +1800,6 @@ /* Define if WINDOW in curses.h offers a field _flags. */ #undef WINDOW_HAS_FLAGS -/* Define to build the readline module against Apple BSD editline. */ -#undef WITH_APPLE_EDITLINE - /* Define if you want build the _decimal module using a coroutine-local rather than a thread-local context */ #undef WITH_DECIMAL_CONTEXTVAR