diff --git a/Doc/c-api/contextvars.rst b/Doc/c-api/contextvars.rst index 8eba54a80dc80d..b7c6550ff34aac 100644 --- a/Doc/c-api/contextvars.rst +++ b/Doc/c-api/contextvars.rst @@ -123,16 +123,10 @@ Context object management functions: Enumeration of possible context object watcher events: - - ``Py_CONTEXT_EVENT_ENTER``: A context has been entered, causing the - :term:`current context` to switch to it. The object passed to the watch - callback is the now-current :class:`contextvars.Context` object. Each - enter event will eventually have a corresponding exit event for the same - context object after any subsequently entered contexts have themselves been - exited. - - ``Py_CONTEXT_EVENT_EXIT``: A context is about to be exited, which will - cause the :term:`current context` to switch back to what it was before the - context was entered. The object passed to the watch callback is the - still-current :class:`contextvars.Context` object. + - ``Py_CONTEXT_SWITCHED``: The :term:`current context` has switched to a + different context. The object passed to the watch callback is the + now-current :class:`contextvars.Context` object, or None if no context is + current. .. versionadded:: 3.14 diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index 6f8962afc7af0d..6194d7446c73e4 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -1621,6 +1621,8 @@ Create Config Free memory of the initialization configuration *config*. + If *config* is ``NULL``, no operation is performed. + Error Handling -------------- @@ -1823,14 +1825,18 @@ return ``-1`` on error: PyInitConfig_Free(config); return 0; - // Display the error message - const char *err_msg; error: - (void)PyInitConfig_GetError(config, &err_msg); - printf("PYTHON INIT ERROR: %s\n", err_msg); - PyInitConfig_Free(config); + { + // Display the error message + // This uncommon braces style is used, because you cannot make + // goto targets point to variable declarations. + const char *err_msg; + (void)PyInitConfig_GetError(config, &err_msg); + printf("PYTHON INIT ERROR: %s\n", err_msg); + PyInitConfig_Free(config); - return -1; + return -1; + } } diff --git a/Doc/conf.py b/Doc/conf.py index d7197b17865854..839beaad08bebd 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -614,7 +614,7 @@ # Sphinx 8.1 has in-built CVE and CWE roles. extlinks |= { "cve": ( - "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-%s", + "https://www.cve.org/CVERecord?id=CVE-%s", "CVE-%s", ), "cwe": ("https://cwe.mitre.org/data/definitions/%s.html", "CWE-%s"), diff --git a/Doc/deprecations/c-api-pending-removal-in-3.14.rst b/Doc/deprecations/c-api-pending-removal-in-3.14.rst index d16da66c29abe7..9e10bf2691e5c8 100644 --- a/Doc/deprecations/c-api-pending-removal-in-3.14.rst +++ b/Doc/deprecations/c-api-pending-removal-in-3.14.rst @@ -1,4 +1,4 @@ -Pending Removal in Python 3.14 +Pending removal in Python 3.14 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * The ``ma_version_tag`` field in :c:type:`PyDictObject` for extension modules diff --git a/Doc/deprecations/c-api-pending-removal-in-3.15.rst b/Doc/deprecations/c-api-pending-removal-in-3.15.rst index e3974415e0cc89..1bb49e5b4874f2 100644 --- a/Doc/deprecations/c-api-pending-removal-in-3.15.rst +++ b/Doc/deprecations/c-api-pending-removal-in-3.15.rst @@ -1,4 +1,4 @@ -Pending Removal in Python 3.15 +Pending removal in Python 3.15 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * The bundled copy of ``libmpdecimal``. diff --git a/Doc/deprecations/c-api-pending-removal-in-future.rst b/Doc/deprecations/c-api-pending-removal-in-future.rst index 0c3ae52b87ff74..8fc1c80c35d092 100644 --- a/Doc/deprecations/c-api-pending-removal-in-future.rst +++ b/Doc/deprecations/c-api-pending-removal-in-future.rst @@ -1,4 +1,4 @@ -Pending Removal in Future Versions +Pending removal in future versions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The following APIs are deprecated and will be removed, diff --git a/Doc/deprecations/index.rst b/Doc/deprecations/index.rst index a9efb0bc744335..bac6e3f18d4594 100644 --- a/Doc/deprecations/index.rst +++ b/Doc/deprecations/index.rst @@ -7,7 +7,7 @@ Deprecations .. include:: pending-removal-in-future.rst -C API Deprecations +C API deprecations ------------------ .. include:: c-api-pending-removal-in-3.15.rst diff --git a/Doc/deprecations/pending-removal-in-3.13.rst b/Doc/deprecations/pending-removal-in-3.13.rst index 89790497816e83..2fd2f12cc6a2c4 100644 --- a/Doc/deprecations/pending-removal-in-3.13.rst +++ b/Doc/deprecations/pending-removal-in-3.13.rst @@ -1,4 +1,4 @@ -Pending Removal in Python 3.13 +Pending removal in Python 3.13 ------------------------------ Modules (see :pep:`594`): diff --git a/Doc/deprecations/pending-removal-in-3.14.rst b/Doc/deprecations/pending-removal-in-3.14.rst index de30f4695059ed..b8791b8d6c387e 100644 --- a/Doc/deprecations/pending-removal-in-3.14.rst +++ b/Doc/deprecations/pending-removal-in-3.14.rst @@ -1,4 +1,4 @@ -Pending Removal in Python 3.14 +Pending removal in Python 3.14 ------------------------------ * The import system: diff --git a/Doc/deprecations/pending-removal-in-3.15.rst b/Doc/deprecations/pending-removal-in-3.15.rst index a55fb6bea3fdaa..17029b8d4773bd 100644 --- a/Doc/deprecations/pending-removal-in-3.15.rst +++ b/Doc/deprecations/pending-removal-in-3.15.rst @@ -1,4 +1,4 @@ -Pending Removal in Python 3.15 +Pending removal in Python 3.15 ------------------------------ * The import system: @@ -63,7 +63,7 @@ Pending Removal in Python 3.15 * The undocumented keyword argument syntax for creating :class:`~typing.NamedTuple` classes - (e.g. ``Point = NamedTuple("Point", x=int, y=int)``) + (for example, ``Point = NamedTuple("Point", x=int, y=int)``) has been deprecated since Python 3.13. Use the class-based syntax or the functional syntax instead. diff --git a/Doc/deprecations/pending-removal-in-3.16.rst b/Doc/deprecations/pending-removal-in-3.16.rst index fc2ef33de5e5cc..fac500d34742ca 100644 --- a/Doc/deprecations/pending-removal-in-3.16.rst +++ b/Doc/deprecations/pending-removal-in-3.16.rst @@ -1,15 +1,6 @@ -Pending Removal in Python 3.16 +Pending removal in Python 3.16 ------------------------------ -* :mod:`builtins`: - - * Bitwise inversion on boolean types, ``~True`` or ``~False`` - has been deprecated since Python 3.12, - as it produces surprising and unintuitive results (``-2`` and ``-1``). - Use ``not x`` instead for the logical negation of a Boolean. - In the rare case that you need the bitwise inversion of - the underlying integer, convert to ``int`` explicitly (``~int(x)``). - * :mod:`array`: * The ``'u'`` format code (:c:type:`wchar_t`) @@ -20,11 +11,19 @@ Pending Removal in Python 3.16 * :mod:`asyncio`: - * :mod:`asyncio`: - :func:`!asyncio.iscoroutinefunction` is deprecated - and will be removed in Python 3.16, - use :func:`inspect.iscoroutinefunction` instead. - (Contributed by Jiahao Li and Kumar Aditya in :gh:`122875`.) + * :func:`!asyncio.iscoroutinefunction` is deprecated + and will be removed in Python 3.16, + use :func:`inspect.iscoroutinefunction` instead. + (Contributed by Jiahao Li and Kumar Aditya in :gh:`122875`.) + +* :mod:`builtins`: + + * Bitwise inversion on boolean types, ``~True`` or ``~False`` + has been deprecated since Python 3.12, + as it produces surprising and unintuitive results (``-2`` and ``-1``). + Use ``not x`` instead for the logical negation of a Boolean. + In the rare case that you need the bitwise inversion of + the underlying integer, convert to ``int`` explicitly (``~int(x)``). * :mod:`shutil`: diff --git a/Doc/deprecations/pending-removal-in-future.rst b/Doc/deprecations/pending-removal-in-future.rst index 3f9cf6f208221a..f916797c07a068 100644 --- a/Doc/deprecations/pending-removal-in-future.rst +++ b/Doc/deprecations/pending-removal-in-future.rst @@ -1,4 +1,4 @@ -Pending Removal in Future Versions +Pending removal in future versions ---------------------------------- The following APIs will be removed in the future, diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index f0b465bc9ce39c..2f81080d525f86 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -180,19 +180,19 @@ Objects of the :class:`date` type are always naive. An object of type :class:`.time` or :class:`.datetime` may be aware or naive. -A :class:`.datetime` object *d* is aware if both of the following hold: +A :class:`.datetime` object ``d`` is aware if both of the following hold: 1. ``d.tzinfo`` is not ``None`` 2. ``d.tzinfo.utcoffset(d)`` does not return ``None`` -Otherwise, *d* is naive. +Otherwise, ``d`` is naive. -A :class:`.time` object *t* is aware if both of the following hold: +A :class:`.time` object ``t`` is aware if both of the following hold: 1. ``t.tzinfo`` is not ``None`` 2. ``t.tzinfo.utcoffset(None)`` does not return ``None``. -Otherwise, *t* is naive. +Otherwise, ``t`` is naive. The distinction between aware and naive doesn't apply to :class:`timedelta` objects. @@ -358,8 +358,8 @@ Supported operations: +--------------------------------+-----------------------------------------------+ | ``q, r = divmod(t1, t2)`` | Computes the quotient and the remainder: | | | ``q = t1 // t2`` (3) and ``r = t1 % t2``. | -| | q is an integer and r is a :class:`timedelta` | -| | object. | +| | ``q`` is an integer and ``r`` is a | +| | :class:`timedelta` object. | +--------------------------------+-----------------------------------------------+ | ``+t1`` | Returns a :class:`timedelta` object with the | | | same value. (2) | @@ -526,7 +526,7 @@ Other constructors, all class methods: January 1 of year 1 has ordinal 1. :exc:`ValueError` is raised unless ``1 <= ordinal <= - date.max.toordinal()``. For any date *d*, + date.max.toordinal()``. For any date ``d``, ``date.fromordinal(d.toordinal()) == d``. @@ -730,7 +730,7 @@ Instance methods: .. method:: date.toordinal() Return the proleptic Gregorian ordinal of the date, where January 1 of year 1 - has ordinal 1. For any :class:`date` object *d*, + has ordinal 1. For any :class:`date` object ``d``, ``date.fromordinal(d.toordinal()) == d``. @@ -782,7 +782,7 @@ Instance methods: .. method:: date.__str__() - For a date *d*, ``str(d)`` is equivalent to ``d.isoformat()``. + For a date ``d``, ``str(d)`` is equivalent to ``d.isoformat()``. .. method:: date.ctime() @@ -1063,7 +1063,7 @@ Other constructors, all class methods: is used. If the *date* argument is a :class:`.datetime` object, its time components and :attr:`.tzinfo` attributes are ignored. - For any :class:`.datetime` object *d*, + For any :class:`.datetime` object ``d``, ``d == datetime.combine(d.date(), d.time(), d.tzinfo)``. .. versionchanged:: 3.6 @@ -1270,11 +1270,11 @@ Supported operations: If both are naive, or both are aware and have the same :attr:`~.datetime.tzinfo` attribute, the :attr:`~.datetime.tzinfo` attributes are ignored, and the result is a :class:`timedelta` - object *t* such that ``datetime2 + t == datetime1``. No time zone adjustments + object ``t`` such that ``datetime2 + t == datetime1``. No time zone adjustments are done in this case. If both are aware and have different :attr:`~.datetime.tzinfo` attributes, ``a-b`` acts - as if *a* and *b* were first converted to naive UTC datetimes. The + as if ``a`` and ``b`` were first converted to naive UTC datetimes. The result is ``(a.replace(tzinfo=None) - a.utcoffset()) - (b.replace(tzinfo=None) - b.utcoffset())`` except that the implementation never overflows. @@ -1454,11 +1454,11 @@ Instance methods: .. method:: datetime.utctimetuple() - If :class:`.datetime` instance *d* is naive, this is the same as + If :class:`.datetime` instance ``d`` is naive, this is the same as ``d.timetuple()`` except that :attr:`~.time.struct_time.tm_isdst` is forced to 0 regardless of what ``d.dst()`` returns. DST is never in effect for a UTC time. - If *d* is aware, *d* is normalized to UTC time, by subtracting + If ``d`` is aware, ``d`` is normalized to UTC time, by subtracting ``d.utcoffset()``, and a :class:`time.struct_time` for the normalized time is returned. :attr:`!tm_isdst` is forced to 0. Note that an :exc:`OverflowError` may be raised if ``d.year`` was @@ -1606,7 +1606,7 @@ Instance methods: .. method:: datetime.__str__() - For a :class:`.datetime` instance *d*, ``str(d)`` is equivalent to + For a :class:`.datetime` instance ``d``, ``str(d)`` is equivalent to ``d.isoformat(' ')``. @@ -1853,7 +1853,7 @@ Instance attributes (read-only): .. versionadded:: 3.6 :class:`.time` objects support equality and order comparisons, -where *a* is considered less than *b* when *a* precedes *b* in time. +where ``a`` is considered less than ``b`` when ``a`` precedes ``b`` in time. Naive and aware :class:`!time` objects are never equal. Order comparison between naive and aware :class:`!time` objects raises @@ -2000,7 +2000,7 @@ Instance methods: .. method:: time.__str__() - For a time *t*, ``str(t)`` is equivalent to ``t.isoformat()``. + For a time ``t``, ``str(t)`` is equivalent to ``t.isoformat()``. .. method:: time.strftime(format) diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst index ae5408ee386bbd..f7167032ad7df9 100644 --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -284,11 +284,10 @@ UAX-31, with elaboration and changes as defined below; see also :pep:`3131` for further details. Within the ASCII range (U+0001..U+007F), the valid characters for identifiers -are the same as in Python 2.x: the uppercase and lowercase letters ``A`` through +include the uppercase and lowercase letters ``A`` through ``Z``, the underscore ``_`` and, except for the first character, the digits ``0`` through ``9``. - -Python 3.0 introduces additional characters from outside the ASCII range (see +Python 3.0 introduced additional characters from outside the ASCII range (see :pep:`3131`). For these characters, the classification uses the version of the Unicode Character Database as included in the :mod:`unicodedata` module. diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 4976418ba33cf8..10cdf2376229ff 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -29,7 +29,7 @@ Features and minimum versions required to build CPython: * Tcl/Tk 8.5.12 for the :mod:`tkinter` module. -* Autoconf 2.71 and aclocal 1.16.4 are required to regenerate the +* Autoconf 2.71 and aclocal 1.16.5 are required to regenerate the :file:`configure` script. .. versionchanged:: 3.1 @@ -56,7 +56,7 @@ Features and minimum versions required to build CPython: Tcl/Tk version 8.5.12 is now required for the :mod:`tkinter` module. .. versionchanged:: 3.13 - Autoconf 2.71, aclocal 1.16.4 and SQLite 3.15.2 are now required. + Autoconf 2.71, aclocal 1.16.5 and SQLite 3.15.2 are now required. See also :pep:`7` "Style Guide for C Code" and :pep:`11` "CPython platform support". diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index a2897097aaba57..f9e74a9b8ff9c6 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -2495,9 +2495,9 @@ Build Changes * Building CPython now requires a compiler with support for the C11 atomic library, GCC built-in atomic functions, or MSVC interlocked intrinsics. -* Autoconf 2.71 and aclocal 1.16.4 are now required to regenerate +* Autoconf 2.71 and aclocal 1.16.5 are now required to regenerate the :file:`configure` script. - (Contributed by Christian Heimes in :gh:`89886`.) + (Contributed by Christian Heimes in :gh:`89886` and by Victor Stinner in :gh:`112090`.) * SQLite 3.15.2 or newer is required to build the :mod:`sqlite3` extension module. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index e1908133601776..c4558de85f4cf1 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1,6 +1,6 @@ **************************** - What's New In Python 3.14 + What's new in Python 3.14 **************************** :Editor: TBD @@ -56,7 +56,7 @@ For full details, see the :ref:`changelog `. so it's worth checking back even after reading earlier versions. -Summary -- Release highlights +Summary -- release highlights ============================= .. This section singles out the most important changes in Python 3.14. @@ -67,12 +67,12 @@ Summary -- Release highlights -New Features +New features ============ -.. _whatsnew-314-pep649: +.. _whatsnew314-pep649: -PEP 649: Deferred Evaluation of Annotations +PEP 649: deferred evaluation of annotations ------------------------------------------- The :term:`annotations ` on functions, classes, and modules are no @@ -150,12 +150,12 @@ In Python 3.7, :pep:`563` introduced the ``from __future__ import annotations`` directive, which turns all annotations into strings. This directive is now considered deprecated and it is expected to be removed in a future version of Python. However, this removal will not happen until after Python 3.13, the last version of -Python without deferred evaluation of annotations, reaches its end of life. +Python without deferred evaluation of annotations, reaches its end of life in 2029. In Python 3.14, the behavior of code using ``from __future__ import annotations`` is unchanged. -Improved Error Messages +Improved error messages ----------------------- * When unpacking assignment fails due to incorrect number of variables, the @@ -195,16 +195,16 @@ The behavior of :func:`!gc.collect` changes slightly: (Contributed by Mark Shannon in :gh:`108362`.) -Other Language Changes +Other language changes ====================== * Incorrect usage of :keyword:`await` and asynchronous comprehensions is now detected even if the code is optimized away by the :option:`-O` - command line option. For example, ``python -O -c 'assert await 1'`` + command-line option. For example, ``python -O -c 'assert await 1'`` now produces a :exc:`SyntaxError`. (Contributed by Jelle Zijlstra in :gh:`121637`.) * Writes to ``__debug__`` are now detected even if the code is optimized - away by the :option:`-O` command line option. For example, + away by the :option:`-O` command-line option. For example, ``python -O -c 'assert (__debug__ := 1)'`` now produces a :exc:`SyntaxError`. (Contributed by Irit Katriel in :gh:`122245`.) @@ -214,7 +214,7 @@ Other Language Changes (Contributed by Serhiy Storchaka in :gh:`84978`.) -New Modules +New modules =========== * :mod:`annotationlib`: For introspecting :term:`annotations `. @@ -222,7 +222,7 @@ New Modules (Contributed by Jelle Zijlstra in :gh:`119180`.) -Improved Modules +Improved modules ================ argparse @@ -237,7 +237,7 @@ ast --- * Add :func:`ast.compare` for comparing two ASTs. - (Contributed by Batuhan Taskaya and Jeremy Hylton in :issue:`15987`.) + (Contributed by Batuhan Taskaya and Jeremy Hylton in :gh:`60191`.) * Add support for :func:`copy.replace` for AST nodes. (Contributed by Bénédikt Tran in :gh:`121141`.) @@ -269,6 +269,12 @@ decimal :meth:`Decimal.from_number() `. (Contributed by Serhiy Storchaka in :gh:`121798`.) +datetime +-------- + +* Add :meth:`datetime.time.strptime` and :meth:`datetime.date.strptime`. + (Contributed by Wannes Boeykens in :gh:`41431`.) + dis --- @@ -277,9 +283,10 @@ dis This feature is added to the following interfaces via the *show_positions* keyword argument: - - :class:`dis.Bytecode`, - - :func:`dis.dis`, :func:`dis.distb`, and - - :func:`dis.disassemble`. + - :class:`dis.Bytecode` + - :func:`dis.dis` + - :func:`dis.distb` + - :func:`dis.disassemble` This feature is also exposed via :option:`dis --show-positions`. (Contributed by Bénédikt Tran in :gh:`123165`.) @@ -362,7 +369,8 @@ json of the error. (Contributed by Serhiy Storchaka in :gh:`122163`.) -* Enable the :mod:`json` module to work as a script using the :option:`-m` switch: ``python -m json``. +* Enable the :mod:`json` module to work as a script using the :option:`-m` + switch: :program:`python -m json`. See the :ref:`JSON command-line interface ` documentation. (Contributed by Trey Hunner in :gh:`122873`.) @@ -377,12 +385,6 @@ operator (Contributed by Raymond Hettinger and Nico Mexis in :gh:`115808`.) -datetime --------- - -* Add :meth:`datetime.time.strptime` and :meth:`datetime.date.strptime`. - (Contributed by Wannes Boeykens in :gh:`41431`.) - os -- @@ -409,11 +411,11 @@ pathlib pdb --- -* Hard-coded breakpoints (:func:`breakpoint` and :func:`pdb.set_trace`) now +* Hardcoded breakpoints (:func:`breakpoint` and :func:`pdb.set_trace`) now reuse the most recent :class:`~pdb.Pdb` instance that calls :meth:`~pdb.Pdb.set_trace`, instead of creating a new one each time. As a result, all the instance specific data like :pdbcmd:`display` and - :pdbcmd:`commands` are preserved across hard-coded breakpoints. + :pdbcmd:`commands` are preserved across hardcoded breakpoints. (Contributed by Tian Gao in :gh:`121450`.) * Add a new argument *mode* to :class:`pdb.Pdb`. Disable the ``restart`` @@ -443,9 +445,9 @@ symtable * Expose the following :class:`symtable.Symbol` methods: - * :meth:`~symtable.Symbol.is_free_class` - * :meth:`~symtable.Symbol.is_comp_iter` * :meth:`~symtable.Symbol.is_comp_cell` + * :meth:`~symtable.Symbol.is_comp_iter` + * :meth:`~symtable.Symbol.is_free_class` (Contributed by Bénédikt Tran in :gh:`120029`.) @@ -532,11 +534,11 @@ ast * Remove the following classes. They were all deprecated since Python 3.8, and have emitted deprecation warnings since Python 3.12: - * :class:`!ast.Num` - * :class:`!ast.Str` * :class:`!ast.Bytes` - * :class:`!ast.NameConstant` * :class:`!ast.Ellipsis` + * :class:`!ast.NameConstant` + * :class:`!ast.Num` + * :class:`!ast.Str` Use :class:`ast.Constant` instead. As a consequence of these removals, user-defined ``visit_Num``, ``visit_Str``, ``visit_Bytes``, @@ -561,16 +563,16 @@ asyncio * Remove the following classes and functions. They were all deprecated and emitted deprecation warnings since Python 3.12: + * :func:`!asyncio.get_child_watcher` + * :func:`!asyncio.set_child_watcher` + * :meth:`!asyncio.AbstractEventLoopPolicy.get_child_watcher` + * :meth:`!asyncio.AbstractEventLoopPolicy.set_child_watcher` * :class:`!asyncio.AbstractChildWatcher` - * :class:`!asyncio.SafeChildWatcher` - * :class:`!asyncio.MultiLoopChildWatcher` * :class:`!asyncio.FastChildWatcher` - * :class:`!asyncio.ThreadedChildWatcher` + * :class:`!asyncio.MultiLoopChildWatcher` * :class:`!asyncio.PidfdChildWatcher` - * :meth:`!asyncio.AbstractEventLoopPolicy.get_child_watcher` - * :meth:`!asyncio.AbstractEventLoopPolicy.set_child_watcher` - * :func:`!asyncio.get_child_watcher` - * :func:`!asyncio.set_child_watcher` + * :class:`!asyncio.SafeChildWatcher` + * :class:`!asyncio.ThreadedChildWatcher` (Contributed by Kumar Aditya in :gh:`120804`.) @@ -690,14 +692,14 @@ Changes in the Python API (Contributed by Serhiy Storchaka in :gh:`69998`.) -Build Changes +Build changes ============= -C API Changes +C API changes ============= -New Features +New features ------------ * Add :c:func:`PyLong_GetSign` function to get the sign of :class:`int` objects. @@ -707,17 +709,17 @@ New Features object: * :c:func:`PyUnicodeWriter_Create` + * :c:func:`PyUnicodeWriter_DecodeUTF8Stateful` * :c:func:`PyUnicodeWriter_Discard` * :c:func:`PyUnicodeWriter_Finish` + * :c:func:`PyUnicodeWriter_Format` * :c:func:`PyUnicodeWriter_WriteChar` - * :c:func:`PyUnicodeWriter_WriteUTF8` - * :c:func:`PyUnicodeWriter_WriteUCS4` - * :c:func:`PyUnicodeWriter_WriteWideChar` - * :c:func:`PyUnicodeWriter_WriteStr` * :c:func:`PyUnicodeWriter_WriteRepr` + * :c:func:`PyUnicodeWriter_WriteStr` * :c:func:`PyUnicodeWriter_WriteSubstring` - * :c:func:`PyUnicodeWriter_Format` - * :c:func:`PyUnicodeWriter_DecodeUTF8Stateful` + * :c:func:`PyUnicodeWriter_WriteUCS4` + * :c:func:`PyUnicodeWriter_WriteUTF8` + * :c:func:`PyUnicodeWriter_WriteWideChar` (Contributed by Victor Stinner in :gh:`119182`.) @@ -738,14 +740,14 @@ New Features * Add new functions to convert C ```` numbers from/to Python :class:`int`: - * :c:func:`PyLong_FromInt32` - * :c:func:`PyLong_FromInt64` - * :c:func:`PyLong_FromUInt32` - * :c:func:`PyLong_FromUInt64` * :c:func:`PyLong_AsInt32` * :c:func:`PyLong_AsInt64` * :c:func:`PyLong_AsUInt32` * :c:func:`PyLong_AsUInt64` + * :c:func:`PyLong_FromInt32` + * :c:func:`PyLong_FromInt64` + * :c:func:`PyLong_FromUInt32` + * :c:func:`PyLong_FromUInt64` (Contributed by Victor Stinner in :gh:`120389`.) @@ -768,20 +770,20 @@ New Features * Add functions to configure the Python initialization (:pep:`741`): + * :c:func:`Py_InitializeFromInitConfig` + * :c:func:`PyInitConfig_AddModule` * :c:func:`PyInitConfig_Create` * :c:func:`PyInitConfig_Free` + * :c:func:`PyInitConfig_FreeStrList` * :c:func:`PyInitConfig_GetError` * :c:func:`PyInitConfig_GetExitCode` - * :c:func:`PyInitConfig_HasOption` * :c:func:`PyInitConfig_GetInt` * :c:func:`PyInitConfig_GetStr` * :c:func:`PyInitConfig_GetStrList` - * :c:func:`PyInitConfig_FreeStrList` + * :c:func:`PyInitConfig_HasOption` * :c:func:`PyInitConfig_SetInt` * :c:func:`PyInitConfig_SetStr` * :c:func:`PyInitConfig_SetStrList` - * :c:func:`PyInitConfig_AddModule` - * :c:func:`Py_InitializeFromInitConfig` (Contributed by Victor Stinner in :gh:`107954`.) diff --git a/Include/cpython/context.h b/Include/cpython/context.h index 3c9be7873b9399..3a7a4b459c09ad 100644 --- a/Include/cpython/context.h +++ b/Include/cpython/context.h @@ -29,20 +29,11 @@ PyAPI_FUNC(int) PyContext_Exit(PyObject *); typedef enum { /* - * A context has been entered, causing the "current context" to switch to - * it. The object passed to the watch callback is the now-current - * contextvars.Context object. Each enter event will eventually have a - * corresponding exit event for the same context object after any - * subsequently entered contexts have themselves been exited. + * The current context has switched to a different context. The object + * passed to the watch callback is the now-current contextvars.Context + * object, or None if no context is current. */ - Py_CONTEXT_EVENT_ENTER, - /* - * A context is about to be exited, which will cause the "current context" - * to switch back to what it was before the context was entered. The - * object passed to the watch callback is the still-current - * contextvars.Context object. - */ - Py_CONTEXT_EVENT_EXIT, + Py_CONTEXT_SWITCHED = 1, } PyContextEvent; /* diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 594fbb1c8e443b..cff2b1f7114793 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -316,6 +316,8 @@ _Py_eval_breaker_bit_is_set(PyThreadState *tstate, uintptr_t bit) void _Py_set_eval_breaker_bit_all(PyInterpreterState *interp, uintptr_t bit); void _Py_unset_eval_breaker_bit_all(PyInterpreterState *interp, uintptr_t bit); +PyAPI_FUNC(PyObject *) _PyFloat_FromDouble_ConsumeInputs(_PyStackRef left, _PyStackRef right, double value); + #ifdef __cplusplus } diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 8fec45b1e8d5c3..c18423476d3962 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1015,13 +1015,13 @@ extern const struct opcode_metadata _PyOpcode_opcode_metadata[266]; #ifdef NEED_OPCODE_METADATA const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [BINARY_OP] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [BINARY_OP_ADD_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG }, + [BINARY_OP_ADD_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_ADD_INT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_ADD_UNICODE] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_INPLACE_ADD_UNICODE] = { true, INSTR_FMT_IXC, HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG }, - [BINARY_OP_MULTIPLY_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG }, + [BINARY_OP_MULTIPLY_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_MULTIPLY_INT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, - [BINARY_OP_SUBTRACT_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG }, + [BINARY_OP_SUBTRACT_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_SUBTRACT_INT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_SLICE] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [BINARY_SUBSCR] = { true, INSTR_FMT_IXC, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, diff --git a/Include/internal/pycore_stackref.h b/Include/internal/pycore_stackref.h index 0e6410466b924b..588e57f6cd97e0 100644 --- a/Include/internal/pycore_stackref.h +++ b/Include/internal/pycore_stackref.h @@ -76,6 +76,13 @@ PyStackRef_AsPyObjectBorrow(_PyStackRef stackref) #define PyStackRef_IsDeferred(ref) (((ref).bits & Py_TAG_BITS) == Py_TAG_DEFERRED) +static inline PyObject * +PyStackRef_NotDeferred_AsPyObject(_PyStackRef stackref) +{ + assert(!PyStackRef_IsDeferred(stackref)); + return (PyObject *)stackref.bits; +} + static inline PyObject * PyStackRef_AsPyObjectSteal(_PyStackRef stackref) { diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index fd41e9a5fe862b..2f0a7fb2f6e549 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -69,9 +69,9 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_GUARD_BOTH_FLOAT] = HAS_EXIT_FLAG, [_GUARD_NOS_FLOAT] = HAS_EXIT_FLAG, [_GUARD_TOS_FLOAT] = HAS_EXIT_FLAG, - [_BINARY_OP_MULTIPLY_FLOAT] = HAS_PURE_FLAG, - [_BINARY_OP_ADD_FLOAT] = HAS_PURE_FLAG, - [_BINARY_OP_SUBTRACT_FLOAT] = HAS_PURE_FLAG, + [_BINARY_OP_MULTIPLY_FLOAT] = HAS_ERROR_FLAG | HAS_PURE_FLAG, + [_BINARY_OP_ADD_FLOAT] = HAS_ERROR_FLAG | HAS_PURE_FLAG, + [_BINARY_OP_SUBTRACT_FLOAT] = HAS_ERROR_FLAG | HAS_PURE_FLAG, [_GUARD_BOTH_UNICODE] = HAS_EXIT_FLAG, [_BINARY_OP_ADD_UNICODE] = HAS_ERROR_FLAG | HAS_PURE_FLAG, [_BINARY_OP_INPLACE_ADD_UNICODE] = HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG, diff --git a/InternalDocs/README.md b/InternalDocs/README.md index 805e2f97937e1e..0a6ecf899458ed 100644 --- a/InternalDocs/README.md +++ b/InternalDocs/README.md @@ -11,6 +11,8 @@ The core dev team attempts to keep this documentation up to date. If it is not, please report that through the [issue tracker](https://github.com/python/cpython/issues). +Index: +----- [Guide to the parser](parser.md) diff --git a/Lib/_pyrepl/console.py b/Lib/_pyrepl/console.py index 3e72a56807f6fb..03266c4dfc2dd8 100644 --- a/Lib/_pyrepl/console.py +++ b/Lib/_pyrepl/console.py @@ -174,7 +174,13 @@ def _excepthook(self, typ, value, tb): def runsource(self, source, filename="", symbol="single"): try: - tree = ast.parse(source) + tree = self.compile.compiler( + source, + filename, + "exec", + ast.PyCF_ONLY_AST, + incomplete_input=False, + ) except (SyntaxError, OverflowError, ValueError): self.showsyntaxerror(filename, source=source) return False @@ -185,7 +191,7 @@ def runsource(self, source, filename="", symbol="single"): the_symbol = symbol if stmt is last_stmt else "exec" item = wrapper([stmt]) try: - code = self.compile.compiler(item, filename, the_symbol, dont_inherit=True) + code = self.compile.compiler(item, filename, the_symbol) except SyntaxError as e: if e.args[0] == "'await' outside function": python = os.path.basename(sys.executable) diff --git a/Lib/_strptime.py b/Lib/_strptime.py index 89adc174e5ad30..5f4d2475c0169b 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -15,6 +15,7 @@ import locale import calendar from re import compile as re_compile +from re import sub as re_sub from re import IGNORECASE from re import escape as re_escape from datetime import (date as datetime_date, @@ -129,11 +130,23 @@ def __calc_date_time(self): time_tuple = time.struct_time((1999,3,17,22,44,55,2,76,0)) time_tuple2 = time.struct_time((1999,1,3,1,1,1,6,3,0)) replacement_pairs = [ - ('1999', '%Y'), ('99', '%y'), ('22', '%H'), - ('44', '%M'), ('55', '%S'), ('76', '%j'), - ('17', '%d'), ('03', '%m'), ('3', '%m'), - # '3' needed for when no leading zero. - ('2', '%w'), ('10', '%I')] + ('1999', '%Y'), ('99', '%y'), ('22', '%H'), + ('44', '%M'), ('55', '%S'), ('76', '%j'), + ('17', '%d'), ('03', '%m'), ('3', '%m'), + # '3' needed for when no leading zero. + ('2', '%w'), ('10', '%I'), + # Non-ASCII digits + ('\u0661\u0669\u0669\u0669', '%Y'), + ('\u0669\u0669', '%Oy'), + ('\u0662\u0662', '%OH'), + ('\u0664\u0664', '%OM'), + ('\u0665\u0665', '%OS'), + ('\u0661\u0667', '%Od'), + ('\u0660\u0663', '%Om'), + ('\u0663', '%Om'), + ('\u0662', '%Ow'), + ('\u0661\u0660', '%OI'), + ] date_time = [] for directive in ('%c', '%x', '%X'): current_format = time.strftime(directive, time_tuple).lower() @@ -158,6 +171,10 @@ def __calc_date_time(self): for tz in tz_values: if tz: current_format = current_format.replace(tz, "%Z") + # Transform all non-ASCII digits to digits in range U+0660 to U+0669. + current_format = re_sub(r'\d(?3[0-1]|[1-2]\d|0[1-9]|[1-9]| [1-9])", 'f': r"(?P[0-9]{1,6})", @@ -296,11 +313,15 @@ def __init__(self, locale_time=None): 'Z': self.__seqToRE((tz for tz_names in self.locale_time.timezone for tz in tz_names), 'Z'), - '%': '%'}) - base.__setitem__('W', base.__getitem__('U').replace('U', 'W')) - base.__setitem__('c', self.pattern(self.locale_time.LC_date_time)) - base.__setitem__('x', self.pattern(self.locale_time.LC_date)) + '%': '%'} + for d in 'dmyHIMS': + mapping['O' + d] = r'(?P<%s>\d\d|\d| \d)' % d + mapping['Ow'] = r'(?P\d)' + mapping['W'] = mapping['U'].replace('U', 'W') + base.__init__(mapping) base.__setitem__('X', self.pattern(self.locale_time.LC_time)) + base.__setitem__('x', self.pattern(self.locale_time.LC_date)) + base.__setitem__('c', self.pattern(self.locale_time.LC_date_time)) def __seqToRE(self, to_convert, directive): """Convert a list to a regex string for matching a directive. @@ -328,28 +349,25 @@ def pattern(self, format): regex syntax are escaped. """ - processed_format = '' # The sub() call escapes all characters that might be misconstrued # as regex syntax. Cannot use re.escape since we have to deal with # format directives (%m, etc.). - regex_chars = re_compile(r"([\\.^$*+?\(\){}\[\]|])") - format = regex_chars.sub(r"\\\1", format) - whitespace_replacement = re_compile(r'\s+') - format = whitespace_replacement.sub(r'\\s+', format) + format = re_sub(r"([\\.^$*+?\(\){}\[\]|])", r"\\\1", format) + format = re_sub(r'\s+', r'\\s+', format) + format = re_sub(r"'", "['\u02bc]", format) # needed for br_FR year_in_format = False day_of_month_in_format = False - while '%' in format: - directive_index = format.index('%')+1 - format_char = format[directive_index] - processed_format = "%s%s%s" % (processed_format, - format[:directive_index-1], - self[format_char]) - format = format[directive_index+1:] + def repl(m): + format_char = m[1] match format_char: case 'Y' | 'y' | 'G': + nonlocal year_in_format year_in_format = True case 'd': + nonlocal day_of_month_in_format day_of_month_in_format = True + return self[format_char] + format = re_sub(r'%(O?.)', repl, format) if day_of_month_in_format and not year_in_format: import warnings warnings.warn("""\ @@ -360,7 +378,7 @@ def pattern(self, format): See https://github.com/python/cpython/issues/70647.""", DeprecationWarning, skip_file_prefixes=(os.path.dirname(__file__),)) - return "%s%s" % (processed_format, format) + return format def compile(self, format): """Return a compiled re object for the format string.""" @@ -434,8 +452,8 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"): _regex_cache[format] = format_regex found = format_regex.match(data_string) if not found: - raise ValueError("time data %r does not match format %r :: /%s/" % - (data_string, format, format_regex.pattern)) + raise ValueError("time data %r does not match format %r" % + (data_string, format)) if len(data_string) != found.end(): raise ValueError("unconverted data remains: %s" % data_string[found.end():]) diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index 5f6fa2348726cf..c95fce035cd548 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -190,8 +190,7 @@ def result(self): the future is done and has an exception set, this exception is raised. """ if self._state == _CANCELLED: - exc = self._make_cancelled_error() - raise exc + raise self._make_cancelled_error() if self._state != _FINISHED: raise exceptions.InvalidStateError('Result is not ready.') self.__log_traceback = False @@ -208,8 +207,7 @@ def exception(self): InvalidStateError. """ if self._state == _CANCELLED: - exc = self._make_cancelled_error() - raise exc + raise self._make_cancelled_error() if self._state != _FINISHED: raise exceptions.InvalidStateError('Exception is not set.') self.__log_traceback = False diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py index f2ee9648c43876..9fa772ca9d02cc 100644 --- a/Lib/asyncio/taskgroups.py +++ b/Lib/asyncio/taskgroups.py @@ -66,6 +66,20 @@ async def __aenter__(self): return self async def __aexit__(self, et, exc, tb): + tb = None + try: + return await self._aexit(et, exc) + finally: + # Exceptions are heavy objects that can have object + # cycles (bad for GC); let's not keep a reference to + # a bunch of them. It would be nicer to use a try/finally + # in __aexit__ directly but that introduced some diff noise + self._parent_task = None + self._errors = None + self._base_error = None + exc = None + + async def _aexit(self, et, exc): self._exiting = True if (exc is not None and @@ -122,7 +136,10 @@ async def __aexit__(self, et, exc, tb): assert not self._tasks if self._base_error is not None: - raise self._base_error + try: + raise self._base_error + finally: + exc = None if self._parent_cancel_requested: # If this flag is set we *must* call uncancel(). @@ -133,8 +150,14 @@ async def __aexit__(self, et, exc, tb): # Propagate CancelledError if there is one, except if there # are other errors -- those have priority. - if propagate_cancellation_error is not None and not self._errors: - raise propagate_cancellation_error + try: + if propagate_cancellation_error is not None and not self._errors: + try: + raise propagate_cancellation_error + finally: + exc = None + finally: + propagate_cancellation_error = None if et is not None and not issubclass(et, exceptions.CancelledError): self._errors.append(exc) @@ -146,14 +169,14 @@ async def __aexit__(self, et, exc, tb): if self._parent_task.cancelling(): self._parent_task.uncancel() self._parent_task.cancel() - # Exceptions are heavy objects that can have object - # cycles (bad for GC); let's not keep a reference to - # a bunch of them. try: - me = BaseExceptionGroup('unhandled errors in a TaskGroup', self._errors) - raise me from None + raise BaseExceptionGroup( + 'unhandled errors in a TaskGroup', + self._errors, + ) from None finally: - self._errors = None + exc = None + def create_task(self, coro, *, name=None, context=None): """Create a new task in this group and return it. diff --git a/Lib/codeop.py b/Lib/codeop.py index a0276b52d484e3..adf000ba29f88c 100644 --- a/Lib/codeop.py +++ b/Lib/codeop.py @@ -44,6 +44,7 @@ # Caveat emptor: These flags are undocumented on purpose and depending # on their effect outside the standard library is **unsupported**. PyCF_DONT_IMPLY_DEDENT = 0x200 +PyCF_ONLY_AST = 0x400 PyCF_ALLOW_INCOMPLETE_INPUT = 0x4000 def _maybe_compile(compiler, source, filename, symbol): @@ -109,12 +110,14 @@ class Compile: def __init__(self): self.flags = PyCF_DONT_IMPLY_DEDENT | PyCF_ALLOW_INCOMPLETE_INPUT - def __call__(self, source, filename, symbol, **kwargs): - flags = self.flags + def __call__(self, source, filename, symbol, flags=0, **kwargs): + flags |= self.flags if kwargs.get('incomplete_input', True) is False: flags &= ~PyCF_DONT_IMPLY_DEDENT flags &= ~PyCF_ALLOW_INCOMPLETE_INPUT codeob = compile(source, filename, symbol, flags, True) + if flags & PyCF_ONLY_AST: + return codeob # this is an ast.Module in this case for feature in _features: if codeob.co_flags & feature.compiler_flag: self.flags |= feature.compiler_flag diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index 827d230b54e159..2a4adc6a4d395f 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -5,7 +5,7 @@ - IDLE — Python 3.13.0a2 documentation + IDLE — Python 3.14.0a0 documentation @@ -18,7 +18,7 @@ @@ -26,6 +26,7 @@ + @@ -45,6 +46,8 @@ + + @@ -184,7 +187,7 @@

Navigation

  • - 3.13.0a2 Documentation » + 3.14.0a0 Documentation »
  • @@ -554,7 +557,7 @@

    Key bindingsControl key on Windows and -Unix and the Command key on macOS. (And all such dicussions +Unix and the Command key on macOS. (And all such discussions assume that the keys have not been re-bound to something else.)

    @@ -694,7 +697,7 @@

    Shell window -
  • C-c attemps to interrupt statement execution (but may fail).

  • +
  • C-c attempts to interrupt statement execution (but may fail).

  • C-d closes Shell if typed at a >>> prompt.

  • Alt-p and Alt-n (C-p and C-n on macOS) retrieve to the current prompt the previous or next previously @@ -1136,7 +1139,7 @@

    Navigation

  • - 3.13.0a2 Documentation » + 3.14.0a0 Documentation »
  • @@ -1180,7 +1183,7 @@

    Navigation



    - Last updated on Jan 17, 2024 (06:57 UTC). + Last updated on Oct 14, 2024 (20:27 UTC). Found a bug?
    diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index 8f98e73258e778..a30db99a619a93 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -108,11 +108,11 @@ def handle_tk_events(tcl=tcl): # Thread shared globals: Establish a queue between a subthread (which handles # the socket) and the main thread (which runs user code), plus global -# completion, exit and interruptable (the main thread) flags: +# completion, exit and interruptible (the main thread) flags: exit_now = False quitting = False -interruptable = False +interruptible = False def main(del_exitfunc=False): """Start the Python execution server in a subprocess @@ -582,14 +582,14 @@ def __init__(self, rpchandler): self.locals = {} def runcode(self, code): - global interruptable + global interruptible try: self.user_exc_info = None - interruptable = True + interruptible = True try: exec(code, self.locals) finally: - interruptable = False + interruptible = False except SystemExit as e: if e.args: # SystemExit called with an argument. ob = e.args[0] @@ -615,7 +615,7 @@ def runcode(self, code): flush_stdout() def interrupt_the_server(self): - if interruptable: + if interruptible: thread.interrupt_main() def start_the_debugger(self, gui_adap_oid): diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index d768bead7120c7..f05be2b6bdf496 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2625,9 +2625,9 @@ def exceeds_recursion_limit(): return get_c_recursion_limit() * 3 -#Windows doesn't have os.uname() but it doesn't support s390x. -skip_on_s390x = unittest.skipIf(hasattr(os, 'uname') and os.uname().machine == 's390x', - 'skipped on s390x') +# Windows doesn't have os.uname() but it doesn't support s390x. +is_s390x = hasattr(os, 'uname') and os.uname().machine == 's390x' +skip_on_s390x = unittest.skipIf(is_s390x, 'skipped on s390x') Py_TRACE_REFS = hasattr(sys, 'getobjects') diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 458b70451a306a..c566b28adb2408 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -659,6 +659,28 @@ def __del__(self): fut = self._new_future(loop=self.loop) fut.set_result(Evil()) + def test_future_cancelled_result_refcycles(self): + f = self._new_future(loop=self.loop) + f.cancel() + exc = None + try: + f.result() + except asyncio.CancelledError as e: + exc = e + self.assertIsNotNone(exc) + self.assertListEqual(gc.get_referrers(exc), []) + + def test_future_cancelled_exception_refcycles(self): + f = self._new_future(loop=self.loop) + f.cancel() + exc = None + try: + f.exception() + except asyncio.CancelledError as e: + exc = e + self.assertIsNotNone(exc) + self.assertListEqual(gc.get_referrers(exc), []) + @unittest.skipUnless(hasattr(futures, '_CFuture'), 'requires the C _asyncio module') diff --git a/Lib/test/test_asyncio/test_taskgroups.py b/Lib/test/test_asyncio/test_taskgroups.py index 4852536defc93d..1b4de96a572fb9 100644 --- a/Lib/test/test_asyncio/test_taskgroups.py +++ b/Lib/test/test_asyncio/test_taskgroups.py @@ -1,7 +1,8 @@ # Adapted with permission from the EdgeDB project; # license: PSFL. - +import sys +import gc import asyncio import contextvars import contextlib @@ -11,7 +12,6 @@ from test.test_asyncio.utils import await_without_task - # To prevent a warning "test altered the execution environment" def tearDownModule(): asyncio.set_event_loop_policy(None) @@ -29,6 +29,15 @@ def get_error_types(eg): return {type(exc) for exc in eg.exceptions} +def no_other_refs(): + # due to gh-124392 coroutines now refer to their locals + coro = asyncio.current_task().get_coro() + frame = sys._getframe(1) + while coro.cr_frame != frame: + coro = coro.cr_await + return [coro] + + class TestTaskGroup(unittest.IsolatedAsyncioTestCase): async def test_taskgroup_01(self): @@ -899,6 +908,95 @@ async def outer(): await outer() + async def test_exception_refcycles_direct(self): + """Test that TaskGroup doesn't keep a reference to the raised ExceptionGroup""" + tg = asyncio.TaskGroup() + exc = None + + class _Done(Exception): + pass + + try: + async with tg: + raise _Done + except ExceptionGroup as e: + exc = e + + self.assertIsNotNone(exc) + self.assertListEqual(gc.get_referrers(exc), no_other_refs()) + + + async def test_exception_refcycles_errors(self): + """Test that TaskGroup deletes self._errors, and __aexit__ args""" + tg = asyncio.TaskGroup() + exc = None + + class _Done(Exception): + pass + + try: + async with tg: + raise _Done + except* _Done as excs: + exc = excs.exceptions[0] + + self.assertIsInstance(exc, _Done) + self.assertListEqual(gc.get_referrers(exc), no_other_refs()) + + + async def test_exception_refcycles_parent_task(self): + """Test that TaskGroup deletes self._parent_task""" + tg = asyncio.TaskGroup() + exc = None + + class _Done(Exception): + pass + + async def coro_fn(): + async with tg: + raise _Done + + try: + async with asyncio.TaskGroup() as tg2: + tg2.create_task(coro_fn()) + except* _Done as excs: + exc = excs.exceptions[0].exceptions[0] + + self.assertIsInstance(exc, _Done) + self.assertListEqual(gc.get_referrers(exc), no_other_refs()) + + async def test_exception_refcycles_propagate_cancellation_error(self): + """Test that TaskGroup deletes propagate_cancellation_error""" + tg = asyncio.TaskGroup() + exc = None + + try: + async with asyncio.timeout(-1): + async with tg: + await asyncio.sleep(0) + except TimeoutError as e: + exc = e.__cause__ + + self.assertIsInstance(exc, asyncio.CancelledError) + self.assertListEqual(gc.get_referrers(exc), no_other_refs()) + + async def test_exception_refcycles_base_error(self): + """Test that TaskGroup deletes self._base_error""" + class MyKeyboardInterrupt(KeyboardInterrupt): + pass + + tg = asyncio.TaskGroup() + exc = None + + try: + async with tg: + raise MyKeyboardInterrupt + except MyKeyboardInterrupt as e: + exc = e + + self.assertIsNotNone(exc) + self.assertListEqual(gc.get_referrers(exc), no_other_refs()) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_capi/test_watchers.py b/Lib/test/test_capi/test_watchers.py index f21d2627c6094b..4680d6765de122 100644 --- a/Lib/test/test_capi/test_watchers.py +++ b/Lib/test/test_capi/test_watchers.py @@ -577,68 +577,62 @@ class TestContextObjectWatchers(unittest.TestCase): def context_watcher(self, which_watcher): wid = _testcapi.add_context_watcher(which_watcher) try: - yield wid + switches = _testcapi.get_context_switches(which_watcher) + except ValueError: + switches = None + try: + yield switches finally: _testcapi.clear_context_watcher(wid) - def assert_event_counts(self, exp_enter_0, exp_exit_0, - exp_enter_1, exp_exit_1): - self.assertEqual( - exp_enter_0, _testcapi.get_context_watcher_num_enter_events(0)) - self.assertEqual( - exp_exit_0, _testcapi.get_context_watcher_num_exit_events(0)) - self.assertEqual( - exp_enter_1, _testcapi.get_context_watcher_num_enter_events(1)) - self.assertEqual( - exp_exit_1, _testcapi.get_context_watcher_num_exit_events(1)) + def assert_event_counts(self, want_0, want_1): + self.assertEqual(len(_testcapi.get_context_switches(0)), want_0) + self.assertEqual(len(_testcapi.get_context_switches(1)), want_1) def test_context_object_events_dispatched(self): # verify that all counts are zero before any watchers are registered - self.assert_event_counts(0, 0, 0, 0) + self.assert_event_counts(0, 0) # verify that all counts remain zero when a context object is # entered and exited with no watchers registered ctx = contextvars.copy_context() - ctx.run(self.assert_event_counts, 0, 0, 0, 0) - self.assert_event_counts(0, 0, 0, 0) + ctx.run(self.assert_event_counts, 0, 0) + self.assert_event_counts(0, 0) # verify counts are as expected when first watcher is registered with self.context_watcher(0): - self.assert_event_counts(0, 0, 0, 0) - ctx.run(self.assert_event_counts, 1, 0, 0, 0) - self.assert_event_counts(1, 1, 0, 0) + self.assert_event_counts(0, 0) + ctx.run(self.assert_event_counts, 1, 0) + self.assert_event_counts(2, 0) # again with second watcher registered with self.context_watcher(1): - self.assert_event_counts(1, 1, 0, 0) - ctx.run(self.assert_event_counts, 2, 1, 1, 0) - self.assert_event_counts(2, 2, 1, 1) + self.assert_event_counts(2, 0) + ctx.run(self.assert_event_counts, 3, 1) + self.assert_event_counts(4, 2) # verify counts are reset and don't change after both watchers are cleared - ctx.run(self.assert_event_counts, 0, 0, 0, 0) - self.assert_event_counts(0, 0, 0, 0) - - def test_enter_error(self): - with self.context_watcher(2): - with catch_unraisable_exception() as cm: - ctx = contextvars.copy_context() - ctx.run(int, 0) - self.assertEqual( - cm.unraisable.err_msg, - "Exception ignored in " - f"Py_CONTEXT_EVENT_EXIT watcher callback for {ctx!r}" - ) - self.assertEqual(str(cm.unraisable.exc_value), "boom!") - - def test_exit_error(self): - ctx = contextvars.copy_context() - def _in_context(stack): - stack.enter_context(self.context_watcher(2)) - - with catch_unraisable_exception() as cm: - with ExitStack() as stack: - ctx.run(_in_context, stack) - self.assertEqual(str(cm.unraisable.exc_value), "boom!") + ctx.run(self.assert_event_counts, 0, 0) + self.assert_event_counts(0, 0) + + def test_callback_error(self): + ctx_outer = contextvars.copy_context() + ctx_inner = contextvars.copy_context() + unraisables = [] + + def _in_outer(): + with self.context_watcher(2): + with catch_unraisable_exception() as cm: + ctx_inner.run(lambda: unraisables.append(cm.unraisable)) + unraisables.append(cm.unraisable) + + ctx_outer.run(_in_outer) + self.assertEqual([x.err_msg for x in unraisables], + ["Exception ignored in Py_CONTEXT_SWITCHED " + f"watcher callback for {ctx!r}" + for ctx in [ctx_inner, ctx_outer]]) + self.assertEqual([str(x.exc_value) for x in unraisables], + ["boom!", "boom!"]) def test_clear_out_of_range_watcher_id(self): with self.assertRaisesRegex(ValueError, r"Invalid context watcher ID -1"): @@ -654,5 +648,12 @@ def test_allocate_too_many_watchers(self): with self.assertRaisesRegex(RuntimeError, r"no more context watcher IDs available"): _testcapi.allocate_too_many_context_watchers() + def test_exit_base_context(self): + ctx = contextvars.Context() + _testcapi.clear_context_stack() + with self.context_watcher(0) as switches: + ctx.run(lambda: None) + self.assertEqual(switches, [ctx, None]) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_pyrepl/test_interact.py b/Lib/test/test_pyrepl/test_interact.py index b7adaffbac0e22..0c6df4e5dae869 100644 --- a/Lib/test/test_pyrepl/test_interact.py +++ b/Lib/test/test_pyrepl/test_interact.py @@ -119,13 +119,38 @@ def test_runsource_shows_syntax_error_for_failed_compilation(self): def test_no_active_future(self): console = InteractiveColoredConsole() - source = "x: int = 1; print(__annotate__(1))" + source = dedent("""\ + x: int = 1 + print(__annotate__(1)) + """) f = io.StringIO() with contextlib.redirect_stdout(f): result = console.runsource(source) self.assertFalse(result) self.assertEqual(f.getvalue(), "{'x': }\n") + def test_future_annotations(self): + console = InteractiveColoredConsole() + source = dedent("""\ + from __future__ import annotations + def g(x: int): ... + print(g.__annotations__) + """) + f = io.StringIO() + with contextlib.redirect_stdout(f): + result = console.runsource(source) + self.assertFalse(result) + self.assertEqual(f.getvalue(), "{'x': 'int'}\n") + + def test_future_barry_as_flufl(self): + console = InteractiveColoredConsole() + f = io.StringIO() + with contextlib.redirect_stdout(f): + result = console.runsource("from __future__ import barry_as_FLUFL\n") + result = console.runsource("""print("black" <> 'blue')\n""") + self.assertFalse(result) + self.assertEqual(f.getvalue(), "True\n") + class TestMoreLines(unittest.TestCase): def test_invalid_syntax_single_line(self): diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index 79f48dfe44abde..09f6f656bfcb0d 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -292,7 +292,7 @@ def test_strptime_exception_context(self): # additional check for IndexError branch (issue #19545) with self.assertRaises(ValueError) as e: _strptime._strptime_time('19', '%Y %') - self.assertIs(e.exception.__suppress_context__, True) + self.assertIsNone(e.exception.__context__) def test_unconverteddata(self): # Check ValueError is raised when there is unconverted data @@ -485,12 +485,14 @@ def test_bad_timezone(self): # id_ID, ms_MY. # * Year is not included: ha_NG. # * Use non-Gregorian calendar: lo_LA, thai, th_TH. + # On Windows: ar_IN, ar_SA, fa_IR, ps_AF. # # BUG: Generates regexp that does not match the current date and time - # for az_IR, fa_IR, lzh_TW, my_MM, or_IN, shn_MM. + # for lzh_TW. @run_with_locales('LC_TIME', 'C', 'en_US', 'fr_FR', 'de_DE', 'ja_JP', 'he_IL', 'eu_ES', 'ar_AE', 'mfe_MU', 'yo_NG', - 'csb_PL', 'br_FR', 'gez_ET', 'brx_IN') + 'csb_PL', 'br_FR', 'gez_ET', 'brx_IN', + 'my_MM', 'or_IN', 'shn_MM', 'az_IR') def test_date_time_locale(self): # Test %c directive loc = locale.getlocale(locale.LC_TIME)[0] @@ -512,20 +514,23 @@ def test_date_time_locale(self): self.roundtrip('%c', slice(0, 6), time.localtime(now - 366*24*3600)) # NB: Dates before 1969 do not roundtrip on some locales: - # bo_CN, bo_IN, dz_BT, eu_ES, eu_FR. + # az_IR, bo_CN, bo_IN, dz_BT, eu_ES, eu_FR, fa_IR, or_IN. @run_with_locales('LC_TIME', 'C', 'en_US', 'fr_FR', 'de_DE', 'ja_JP', 'he_IL', 'ar_AE', 'mfe_MU', 'yo_NG', - 'csb_PL', 'br_FR', 'gez_ET', 'brx_IN') + 'csb_PL', 'br_FR', 'gez_ET', 'brx_IN', + 'my_MM', 'shn_MM') def test_date_time_locale2(self): # Test %c directive self.roundtrip('%c', slice(0, 6), (1900, 1, 1, 0, 0, 0, 0, 1, 0)) + self.roundtrip('%c', slice(0, 6), (1800, 1, 1, 0, 0, 0, 0, 1, 0)) # NB: Does not roundtrip because use non-Gregorian calendar: - # lo_LA, thai, th_TH. + # lo_LA, thai, th_TH. On Windows: ar_IN, ar_SA, fa_IR, ps_AF. # BUG: Generates regexp that does not match the current date - # for az_IR, fa_IR, lzh_TW, my_MM, or_IN, shn_MM. + # for lzh_TW. @run_with_locales('LC_TIME', 'C', 'en_US', 'fr_FR', 'de_DE', 'ja_JP', - 'he_IL', 'eu_ES', 'ar_AE') + 'he_IL', 'eu_ES', 'ar_AE', + 'az_IR', 'my_MM', 'or_IN', 'shn_MM') def test_date_locale(self): # Test %x directive now = time.time() @@ -545,10 +550,11 @@ def test_date_locale(self): "musl libc issue on Emscripten, bpo-46390" ) @run_with_locales('LC_TIME', 'en_US', 'fr_FR', 'de_DE', 'ja_JP', - 'eu_ES', 'ar_AE') + 'eu_ES', 'ar_AE', 'my_MM', 'shn_MM') def test_date_locale2(self): # Test %x directive self.roundtrip('%x', slice(0, 3), (1900, 1, 1, 0, 0, 0, 0, 1, 0)) + self.roundtrip('%x', slice(0, 3), (1800, 1, 1, 0, 0, 0, 0, 1, 0)) # NB: Does not roundtrip in some locales due to the ambiguity of # the time representation (bugs in locales?): @@ -556,19 +562,27 @@ def test_date_locale2(self): # norwegian, nynorsk. # * Hours are in 12-hour notation without AM/PM indication: hy_AM, # ms_MY, sm_WS. - # BUG: Generates regexp that does not match the current time for - # aa_DJ, aa_ER, aa_ET, am_ET, az_IR, byn_ER, fa_IR, gez_ER, gez_ET, - # lzh_TW, my_MM, om_ET, om_KE, or_IN, shn_MM, sid_ET, so_DJ, so_ET, - # so_SO, ti_ER, ti_ET, tig_ER, wal_ET. - @run_with_locales('LC_TIME', 'C', 'en_US', 'fr_FR', 'de_DE', 'ja_JP') + # BUG: Generates regexp that does not match the current time for lzh_TW. + @run_with_locales('LC_TIME', 'C', 'en_US', 'fr_FR', 'de_DE', 'ja_JP', + 'aa_ET', 'am_ET', 'az_IR', 'byn_ER', 'fa_IR', 'gez_ET', + 'my_MM', 'om_ET', 'or_IN', 'shn_MM', 'sid_ET', 'so_SO', + 'ti_ET', 'tig_ER', 'wal_ET') def test_time_locale(self): # Test %X directive + loc = locale.getlocale(locale.LC_TIME)[0] + pos = slice(3, 6) + if glibc_ver and glibc_ver < (2, 29) and loc in { + 'aa_ET', 'am_ET', 'byn_ER', 'gez_ET', 'om_ET', + 'sid_ET', 'so_SO', 'ti_ET', 'tig_ER', 'wal_ET'}: + # Hours are in 12-hour notation without AM/PM indication. + # Ignore hours. + pos = slice(4, 6) now = time.time() - self.roundtrip('%X', slice(3, 6), time.localtime(now)) + self.roundtrip('%X', pos, time.localtime(now)) # 1 hour 20 minutes 30 seconds ago - self.roundtrip('%X', slice(3, 6), time.localtime(now - 4830)) + self.roundtrip('%X', pos, time.localtime(now - 4830)) # 12 hours ago - self.roundtrip('%X', slice(3, 6), time.localtime(now - 12*3600)) + self.roundtrip('%X', pos, time.localtime(now - 12*3600)) def test_percent(self): # Make sure % signs are handled properly diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index 5b5779231f06ce..27c0f51acc58ab 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -298,7 +298,7 @@ def test_strptime_exception_context(self): # additional check for IndexError branch (issue #19545) with self.assertRaises(ValueError) as e: time.strptime('19', '%Y %') - self.assertIs(e.exception.__suppress_context__, True) + self.assertIsNone(e.exception.__context__) def test_strptime_leap_year(self): # GH-70647: warns if parsing a format with a day and no year. diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index ef02c64f886f8a..8b4bb8750f8f5c 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -6,7 +6,7 @@ import pickle import random import sys -from test.support import bigmemtest, _1G, _4G, skip_on_s390x +from test.support import bigmemtest, _1G, _4G, is_s390x zlib = import_helper.import_module('zlib') @@ -33,8 +33,9 @@ def _zlib_runtime_version_tuple(zlib_version=zlib.ZLIB_RUNTIME_VERSION): 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. +# bpo-46623: When a hardware accelerator is used (currently only on s390x), +# using different ways to compress data with zlib can produce different +# compressed data. # Simplified test_pair() code: # # def func1(data): @@ -57,8 +58,10 @@ def _zlib_runtime_version_tuple(zlib_version=zlib.ZLIB_RUNTIME_VERSION): # # zlib.decompress(func1(data)) == zlib.decompress(func2(data)) == data # -# Make the assumption that s390x always has an accelerator to simplify the skip -# condition. +# To simplify the skip condition, make the assumption that s390x always has an +# accelerator, and nothing else has it. +HW_ACCELERATED = is_s390x + class VersionTestCase(unittest.TestCase): @@ -223,12 +226,14 @@ def test_keywords(self): bufsize=zlib.DEF_BUF_SIZE), HAMLET_SCENE) - @skip_on_s390x def test_speech128(self): # compress more data data = HAMLET_SCENE * 128 x = zlib.compress(data) - self.assertEqual(zlib.compress(bytearray(data)), x) + # With hardware acceleration, the compressed bytes + # might not be identical. + if not HW_ACCELERATED: + self.assertEqual(zlib.compress(bytearray(data)), x) for ob in x, bytearray(x): self.assertEqual(zlib.decompress(ob), data) @@ -275,7 +280,6 @@ def test_64bit_compress(self, size): class CompressObjectTestCase(BaseCompressTestCase, unittest.TestCase): # Test compression object - @skip_on_s390x def test_pair(self): # straightforward compress/decompress objects datasrc = HAMLET_SCENE * 128 @@ -286,7 +290,10 @@ def test_pair(self): x1 = co.compress(data) x2 = co.flush() self.assertRaises(zlib.error, co.flush) # second flush should not work - self.assertEqual(x1 + x2, datazip) + # With hardware acceleration, the compressed bytes might not + # be identical. + if not HW_ACCELERATED: + self.assertEqual(x1 + x2, datazip) for v1, v2 in ((x1, x2), (bytearray(x1), bytearray(x2))): dco = zlib.decompressobj() y1 = dco.decompress(v1 + v2) diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index a00fa690fa0b88..a5d348ba4cf121 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -306,7 +306,7 @@ def setup_python(self, context): suffixes = ['python', 'python3', f'python3.{sys.version_info[1]}'] if sys.version_info[:2] == (3, 14): - suffixes.append('python𝜋') + suffixes.append('𝜋thon') for suffix in suffixes: path = os.path.join(binpath, suffix) if not os.path.exists(path): diff --git a/Misc/NEWS.d/next/Build/2024-10-13-21-11-30.gh-issue-125269.BC-fdo.rst b/Misc/NEWS.d/next/Build/2024-10-13-21-11-30.gh-issue-125269.BC-fdo.rst new file mode 100644 index 00000000000000..24f5469e8a664b --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-10-13-21-11-30.gh-issue-125269.BC-fdo.rst @@ -0,0 +1,2 @@ +Fix detection of whether ``-latomic`` is needed when cross-compiling CPython +using the configure script. diff --git a/Misc/NEWS.d/next/Library/2024-10-03-17-13-22.gh-issue-124944.YyLAzf.rst b/Misc/NEWS.d/next/Library/2024-10-03-17-13-22.gh-issue-124944.YyLAzf.rst new file mode 100644 index 00000000000000..66af712c5ae5a8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-03-17-13-22.gh-issue-124944.YyLAzf.rst @@ -0,0 +1 @@ +Add ``SO_ORIGINAL_DST`` to the :mod:`socket` module. diff --git a/Misc/NEWS.d/next/Library/2024-10-04-08-46-00.gh-issue-124958.rea9-x.rst b/Misc/NEWS.d/next/Library/2024-10-04-08-46-00.gh-issue-124958.rea9-x.rst new file mode 100644 index 00000000000000..534d5bb8c898da --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-04-08-46-00.gh-issue-124958.rea9-x.rst @@ -0,0 +1 @@ +Fix refcycles in exceptions raised from :class:`asyncio.TaskGroup` and the python implementation of :class:`asyncio.Future` diff --git a/Misc/NEWS.d/next/Library/2024-10-05-15-49-53.gh-issue-124960.Bol9hT.rst b/Misc/NEWS.d/next/Library/2024-10-05-15-49-53.gh-issue-124960.Bol9hT.rst new file mode 100644 index 00000000000000..332d6bb54d80c7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-05-15-49-53.gh-issue-124960.Bol9hT.rst @@ -0,0 +1 @@ +Fix support for the ``barry_as_FLUFL`` future flag in the new REPL. diff --git a/Misc/NEWS.d/next/Library/2024-10-13-20-21-35.gh-issue-53203.Rz1c8A.rst b/Misc/NEWS.d/next/Library/2024-10-13-20-21-35.gh-issue-53203.Rz1c8A.rst new file mode 100644 index 00000000000000..cdfa8c191e8242 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-13-20-21-35.gh-issue-53203.Rz1c8A.rst @@ -0,0 +1,2 @@ +Fix :func:`time.strptime` for ``%c``, ``%x`` and ``%X`` formats in many +locales that use non-ASCII digits, like Persian, Burmese, Odia and Shan. diff --git a/Misc/NEWS.d/next/Tests/2024-10-07-14-13-38.gh-issue-125041.PKLWDf.rst b/Misc/NEWS.d/next/Tests/2024-10-07-14-13-38.gh-issue-125041.PKLWDf.rst new file mode 100644 index 00000000000000..c7181eb9c1f3a9 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2024-10-07-14-13-38.gh-issue-125041.PKLWDf.rst @@ -0,0 +1,3 @@ +Re-enable skipped tests for :mod:`zlib` on the s390x architecture: only skip +checks of the compressed bytes, which can be different between zlib's +software implementation and the hardware-accelerated implementation. diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 4ab3adc0fe44cc..802b1cf792c555 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -144,10 +144,13 @@ typedef struct { vectorcallfunc vectorcall; } partialobject; +// cast a PyObject pointer PTR to a partialobject pointer (no type checks) +#define _PyPartialObject_CAST(PTR) ((partialobject *)(PTR)) + static void partial_setvectorcall(partialobject *pto); static struct PyModuleDef _functools_module; static PyObject * -partial_call(partialobject *pto, PyObject *args, PyObject *kwargs); +partial_call(PyObject *pto, PyObject *args, PyObject *kwargs); static inline _functools_state * get_functools_state_by_type(PyTypeObject *type) @@ -307,8 +310,9 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) } static int -partial_clear(partialobject *pto) +partial_clear(PyObject *self) { + partialobject *pto = _PyPartialObject_CAST(self); Py_CLEAR(pto->fn); Py_CLEAR(pto->args); Py_CLEAR(pto->kw); @@ -317,8 +321,9 @@ partial_clear(partialobject *pto) } static int -partial_traverse(partialobject *pto, visitproc visit, void *arg) +partial_traverse(PyObject *self, visitproc visit, void *arg) { + partialobject *pto = _PyPartialObject_CAST(self); Py_VISIT(Py_TYPE(pto)); Py_VISIT(pto->fn); Py_VISIT(pto->args); @@ -328,16 +333,16 @@ partial_traverse(partialobject *pto, visitproc visit, void *arg) } static void -partial_dealloc(partialobject *pto) +partial_dealloc(PyObject *self) { - PyTypeObject *tp = Py_TYPE(pto); + PyTypeObject *tp = Py_TYPE(self); /* bpo-31095: UnTrack is needed before calling any callbacks */ - PyObject_GC_UnTrack(pto); - if (pto->weakreflist != NULL) { - PyObject_ClearWeakRefs((PyObject *) pto); + PyObject_GC_UnTrack(self); + if (_PyPartialObject_CAST(self)->weakreflist != NULL) { + PyObject_ClearWeakRefs(self); } - (void)partial_clear(pto); - tp->tp_free(pto); + (void)partial_clear(self); + tp->tp_free(self); Py_DECREF(tp); } @@ -360,14 +365,14 @@ partial_vectorcall_fallback(PyThreadState *tstate, partialobject *pto, { pto->vectorcall = NULL; Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - return _PyObject_MakeTpCall(tstate, (PyObject *)pto, - args, nargs, kwnames); + return _PyObject_MakeTpCall(tstate, (PyObject *)pto, args, nargs, kwnames); } static PyObject * -partial_vectorcall(partialobject *pto, PyObject *const *args, +partial_vectorcall(PyObject *self, PyObject *const *args, size_t nargsf, PyObject *kwnames) { + partialobject *pto = _PyPartialObject_CAST(self);; PyThreadState *tstate = _PyThreadState_GET(); Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); @@ -468,15 +473,16 @@ partial_setvectorcall(partialobject *pto) * but that is unlikely (why use partial without arguments?), * so we don't optimize that */ else { - pto->vectorcall = (vectorcallfunc)partial_vectorcall; + pto->vectorcall = partial_vectorcall; } } // Not converted to argument clinic, because of `*args, **kwargs` arguments. static PyObject * -partial_call(partialobject *pto, PyObject *args, PyObject *kwargs) +partial_call(PyObject *self, PyObject *args, PyObject *kwargs) { + partialobject *pto = _PyPartialObject_CAST(self); assert(PyCallable_Check(pto->fn)); assert(PyTuple_Check(pto->args)); assert(PyDict_Check(pto->kw)); @@ -587,8 +593,9 @@ static PyGetSetDef partial_getsetlist[] = { }; static PyObject * -partial_repr(partialobject *pto) +partial_repr(PyObject *self) { + partialobject *pto = _PyPartialObject_CAST(self); PyObject *result = NULL; PyObject *arglist; PyObject *mod; @@ -597,7 +604,7 @@ partial_repr(partialobject *pto) PyObject *key, *value; int status; - status = Py_ReprEnter((PyObject *)pto); + status = Py_ReprEnter(self); if (status != 0) { if (status < 0) return NULL; @@ -608,7 +615,7 @@ partial_repr(partialobject *pto) if (arglist == NULL) goto done; /* Pack positional arguments */ - assert (PyTuple_Check(pto->args)); + assert(PyTuple_Check(pto->args)); n = PyTuple_GET_SIZE(pto->args); for (i = 0; i < n; i++) { Py_SETREF(arglist, PyUnicode_FromFormat("%U, %R", arglist, @@ -643,11 +650,11 @@ partial_repr(partialobject *pto) Py_DECREF(arglist); done: - Py_ReprLeave((PyObject *)pto); + Py_ReprLeave(self); return result; error: Py_DECREF(arglist); - Py_ReprLeave((PyObject *)pto); + Py_ReprLeave(self); return NULL; } @@ -659,16 +666,18 @@ partial_repr(partialobject *pto) */ static PyObject * -partial_reduce(partialobject *pto, PyObject *unused) +partial_reduce(PyObject *self, PyObject *Py_UNUSED(args)) { + partialobject *pto = _PyPartialObject_CAST(self); return Py_BuildValue("O(O)(OOOO)", Py_TYPE(pto), pto->fn, pto->fn, pto->args, pto->kw, pto->dict ? pto->dict : Py_None); } static PyObject * -partial_setstate(partialobject *pto, PyObject *state) +partial_setstate(PyObject *self, PyObject *state) { + partialobject *pto = _PyPartialObject_CAST(self); PyObject *fn, *fnargs, *kw, *dict; if (!PyTuple_Check(state)) { @@ -730,8 +739,8 @@ partial_setstate(partialobject *pto, PyObject *state) } static PyMethodDef partial_methods[] = { - {"__reduce__", (PyCFunction)partial_reduce, METH_NOARGS}, - {"__setstate__", (PyCFunction)partial_setstate, METH_O}, + {"__reduce__", partial_reduce, METH_NOARGS}, + {"__setstate__", partial_setstate, METH_O}, {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, {NULL, NULL} /* sentinel */ @@ -749,7 +758,7 @@ static PyType_Slot partial_type_slots[] = { {Py_tp_methods, partial_methods}, {Py_tp_members, partial_memberlist}, {Py_tp_getset, partial_getsetlist}, - {Py_tp_descr_get, (descrgetfunc)partial_descr_get}, + {Py_tp_descr_get, partial_descr_get}, {Py_tp_new, partial_new}, {Py_tp_free, PyObject_GC_Del}, {0, 0} diff --git a/Modules/_testcapi/watchers.c b/Modules/_testcapi/watchers.c index b4233d07134aea..321d3aeffb6ad1 100644 --- a/Modules/_testcapi/watchers.c +++ b/Modules/_testcapi/watchers.c @@ -626,16 +626,12 @@ allocate_too_many_func_watchers(PyObject *self, PyObject *args) // Test contexct object watchers #define NUM_CONTEXT_WATCHERS 2 static int context_watcher_ids[NUM_CONTEXT_WATCHERS] = {-1, -1}; -static int num_context_object_enter_events[NUM_CONTEXT_WATCHERS] = {0, 0}; -static int num_context_object_exit_events[NUM_CONTEXT_WATCHERS] = {0, 0}; +static PyObject *context_switches[NUM_CONTEXT_WATCHERS]; static int handle_context_watcher_event(int which_watcher, PyContextEvent event, PyObject *ctx) { - if (event == Py_CONTEXT_EVENT_ENTER) { - num_context_object_enter_events[which_watcher]++; - } - else if (event == Py_CONTEXT_EVENT_EXIT) { - num_context_object_exit_events[which_watcher]++; + if (event == Py_CONTEXT_SWITCHED) { + PyList_Append(context_switches[which_watcher], ctx); } else { return -1; @@ -667,31 +663,28 @@ error_context_event_handler(PyContextEvent event, PyObject *ctx) { static PyObject * add_context_watcher(PyObject *self, PyObject *which_watcher) { - int watcher_id; + static const PyContext_WatchCallback callbacks[] = { + &first_context_watcher_callback, + &second_context_watcher_callback, + &error_context_event_handler, + }; assert(PyLong_Check(which_watcher)); long which_l = PyLong_AsLong(which_watcher); - if (which_l == 0) { - watcher_id = PyContext_AddWatcher(first_context_watcher_callback); - context_watcher_ids[0] = watcher_id; - num_context_object_enter_events[0] = 0; - num_context_object_exit_events[0] = 0; - } - else if (which_l == 1) { - watcher_id = PyContext_AddWatcher(second_context_watcher_callback); - context_watcher_ids[1] = watcher_id; - num_context_object_enter_events[1] = 0; - num_context_object_exit_events[1] = 0; - } - else if (which_l == 2) { - watcher_id = PyContext_AddWatcher(error_context_event_handler); - } - else { + if (which_l < 0 || which_l >= (long)Py_ARRAY_LENGTH(callbacks)) { PyErr_Format(PyExc_ValueError, "invalid watcher %d", which_l); return NULL; } + int watcher_id = PyContext_AddWatcher(callbacks[which_l]); if (watcher_id < 0) { return NULL; } + if (which_l >= 0 && which_l < NUM_CONTEXT_WATCHERS) { + context_watcher_ids[which_l] = watcher_id; + Py_XSETREF(context_switches[which_l], PyList_New(0)); + if (context_switches[which_l] == NULL) { + return NULL; + } + } return PyLong_FromLong(watcher_id); } @@ -708,8 +701,7 @@ clear_context_watcher(PyObject *self, PyObject *watcher_id) for (int i = 0; i < NUM_CONTEXT_WATCHERS; i++) { if (watcher_id_l == context_watcher_ids[i]) { context_watcher_ids[i] = -1; - num_context_object_enter_events[i] = 0; - num_context_object_exit_events[i] = 0; + Py_CLEAR(context_switches[i]); } } } @@ -717,21 +709,34 @@ clear_context_watcher(PyObject *self, PyObject *watcher_id) } static PyObject * -get_context_watcher_num_enter_events(PyObject *self, PyObject *watcher_id) +clear_context_stack(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) { - assert(PyLong_Check(watcher_id)); - long watcher_id_l = PyLong_AsLong(watcher_id); - assert(watcher_id_l >= 0 && watcher_id_l < NUM_CONTEXT_WATCHERS); - return PyLong_FromLong(num_context_object_enter_events[watcher_id_l]); + PyThreadState *tstate = PyThreadState_Get(); + if (tstate->context == NULL) { + Py_RETURN_NONE; + } + if (((PyContext *)tstate->context)->ctx_prev != NULL) { + PyErr_SetString(PyExc_RuntimeError, + "must first exit all non-base contexts"); + return NULL; + } + Py_CLEAR(tstate->context); + Py_RETURN_NONE; } static PyObject * -get_context_watcher_num_exit_events(PyObject *self, PyObject *watcher_id) +get_context_switches(PyObject *Py_UNUSED(self), PyObject *watcher_id) { assert(PyLong_Check(watcher_id)); long watcher_id_l = PyLong_AsLong(watcher_id); - assert(watcher_id_l >= 0 && watcher_id_l < NUM_CONTEXT_WATCHERS); - return PyLong_FromLong(num_context_object_exit_events[watcher_id_l]); + if (watcher_id_l < 0 || watcher_id_l >= NUM_CONTEXT_WATCHERS) { + PyErr_Format(PyExc_ValueError, "invalid watcher %ld", watcher_id_l); + return NULL; + } + if (context_switches[watcher_id_l] == NULL) { + return PyList_New(0); + } + return Py_NewRef(context_switches[watcher_id_l]); } static PyObject * @@ -835,10 +840,8 @@ static PyMethodDef test_methods[] = { // Code object watchers. {"add_context_watcher", add_context_watcher, METH_O, NULL}, {"clear_context_watcher", clear_context_watcher, METH_O, NULL}, - {"get_context_watcher_num_enter_events", - get_context_watcher_num_enter_events, METH_O, NULL}, - {"get_context_watcher_num_exit_events", - get_context_watcher_num_exit_events, METH_O, NULL}, + {"clear_context_stack", clear_context_stack, METH_NOARGS, NULL}, + {"get_context_switches", get_context_switches, METH_O, NULL}, {"allocate_too_many_context_watchers", (PyCFunction) allocate_too_many_context_watchers, METH_NOARGS, NULL}, {NULL}, diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 0829d2358129d2..744e5e0c0b2b54 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -7921,6 +7921,9 @@ socket_exec(PyObject *m) #ifdef SO_OOBINLINE ADD_INT_MACRO(m, SO_OOBINLINE); #endif +#ifdef SO_ORIGINAL_DST + ADD_INT_MACRO(m, SO_ORIGINAL_DST); +#endif #ifndef __GNU__ #ifdef SO_REUSEPORT ADD_INT_MACRO(m, SO_REUSEPORT); diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h index a77c620c2ef630..e1d96377728eb9 100644 --- a/Modules/socketmodule.h +++ b/Modules/socketmodule.h @@ -172,6 +172,10 @@ typedef int socklen_t; # undef AF_VSOCK #endif +#ifdef HAVE_LINUX_NETFILTER_IPV4_H +# include +#endif + #ifdef HAVE_SOCKADDR_ALG # include diff --git a/Objects/floatobject.c b/Objects/floatobject.c index a48a210adee3b9..d66863febe8c86 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -134,6 +134,41 @@ PyFloat_FromDouble(double fval) return (PyObject *) op; } +#ifdef Py_GIL_DISABLED + +PyObject *_PyFloat_FromDouble_ConsumeInputs(_PyStackRef left, _PyStackRef right, double value) +{ + PyStackRef_CLOSE(left); + PyStackRef_CLOSE(right); + return PyFloat_FromDouble(value); +} + +#else // Py_GIL_DISABLED + +PyObject *_PyFloat_FromDouble_ConsumeInputs(_PyStackRef left, _PyStackRef right, double value) +{ + PyObject *left_o = PyStackRef_AsPyObjectSteal(left); + PyObject *right_o = PyStackRef_AsPyObjectSteal(right); + if (Py_REFCNT(left_o) == 1) { + ((PyFloatObject *)left_o)->ob_fval = value; + _Py_DECREF_SPECIALIZED(right_o, _PyFloat_ExactDealloc); + return left_o; + } + else if (Py_REFCNT(right_o) == 1) { + ((PyFloatObject *)right_o)->ob_fval = value; + _Py_DECREF_NO_DEALLOC(left_o); + return right_o; + } + else { + PyObject *result = PyFloat_FromDouble(value); + _Py_DECREF_NO_DEALLOC(left_o); + _Py_DECREF_NO_DEALLOC(right_o); + return result; + } +} + +#endif // Py_GIL_DISABLED + static PyObject * float_from_string_inner(const char *s, Py_ssize_t len, void *obj) { diff --git a/Programs/_testembed.c b/Programs/_testembed.c index ab619e32429d63..d15dd519dbf6af 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -1896,16 +1896,19 @@ static int test_initconfig_api(void) goto error; } PyInitConfig_Free(config); + PyInitConfig_Free(NULL); dump_config(); Py_Finalize(); return 0; - const char *err_msg; error: - (void)PyInitConfig_GetError(config, &err_msg); - printf("Python init failed: %s\n", err_msg); - exit(1); + { + const char *err_msg; + (void)PyInitConfig_GetError(config, &err_msg); + printf("Python init failed: %s\n", err_msg); + exit(1); + } } @@ -2049,11 +2052,13 @@ static int test_initconfig_module(void) Py_Finalize(); return 0; - const char *err_msg; error: - (void)PyInitConfig_GetError(config, &err_msg); - printf("Python init failed: %s\n", err_msg); - exit(1); + { + const char *err_msg; + (void)PyInitConfig_GetError(config, &err_msg); + printf("Python init failed: %s\n", err_msg); + exit(1); + } } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 299608f252c546..e6525657cabc2b 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -539,9 +539,9 @@ dummy_func( double dres = ((PyFloatObject *)left_o)->ob_fval * ((PyFloatObject *)right_o)->ob_fval; - PyObject *res_o; - DECREF_INPUTS_AND_REUSE_FLOAT(left_o, right_o, dres, res_o); + PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); INPUTS_DEAD(); + ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -553,9 +553,9 @@ dummy_func( double dres = ((PyFloatObject *)left_o)->ob_fval + ((PyFloatObject *)right_o)->ob_fval; - PyObject *res_o; - DECREF_INPUTS_AND_REUSE_FLOAT(left_o, right_o, dres, res_o); + PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); INPUTS_DEAD(); + ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -567,9 +567,9 @@ dummy_func( double dres = ((PyFloatObject *)left_o)->ob_fval - ((PyFloatObject *)right_o)->ob_fval; - PyObject *res_o; - DECREF_INPUTS_AND_REUSE_FLOAT(left_o, right_o, dres, res_o); + PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); INPUTS_DEAD(); + ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -611,7 +611,9 @@ dummy_func( // specializations, but there is no output. // At the end we just skip over the STORE_FAST. op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right --)) { + #ifndef NDEBUG PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + #endif PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); int next_oparg; diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index e0e9cc156ed62f..6674c4ccf9f693 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -327,26 +327,6 @@ GETITEM(PyObject *v, Py_ssize_t i) { " in enclosing scope" #define NAME_ERROR_MSG "name '%.200s' is not defined" -#define DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dval, result) \ -do { \ - if (Py_REFCNT(left) == 1) { \ - ((PyFloatObject *)left)->ob_fval = (dval); \ - _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);\ - result = (left); \ - } \ - else if (Py_REFCNT(right) == 1) {\ - ((PyFloatObject *)right)->ob_fval = (dval); \ - _Py_DECREF_NO_DEALLOC(left); \ - result = (right); \ - }\ - else { \ - result = PyFloat_FromDouble(dval); \ - if ((result) == NULL) GOTO_ERROR(error); \ - _Py_DECREF_NO_DEALLOC(left); \ - _Py_DECREF_NO_DEALLOC(right); \ - } \ -} while (0) - // If a trace function sets a new f_lineno and // *then* raises, we use the destination when searching // for an exception handler, displaying the traceback, and so on diff --git a/Python/context.c b/Python/context.c index 8bc487a33c890b..95aa82206270f9 100644 --- a/Python/context.c +++ b/Python/context.c @@ -102,10 +102,8 @@ PyContext_CopyCurrent(void) static const char * context_event_name(PyContextEvent event) { switch (event) { - case Py_CONTEXT_EVENT_ENTER: - return "Py_CONTEXT_EVENT_ENTER"; - case Py_CONTEXT_EVENT_EXIT: - return "Py_CONTEXT_EVENT_EXIT"; + case Py_CONTEXT_SWITCHED: + return "Py_CONTEXT_SWITCHED"; default: return "?"; } @@ -115,6 +113,13 @@ context_event_name(PyContextEvent event) { static void notify_context_watchers(PyThreadState *ts, PyContextEvent event, PyObject *ctx) { + if (ctx == NULL) { + // This will happen after exiting the last context in the stack, which + // can occur if context_get was never called before entering a context + // (e.g., called `contextvars.Context().run()` on a fresh thread, as + // PyContext_Enter doesn't call context_get). + ctx = Py_None; + } assert(Py_REFCNT(ctx) > 0); PyInterpreterState *interp = ts->interp; assert(interp->_initialized); @@ -175,6 +180,16 @@ PyContext_ClearWatcher(int watcher_id) } +static inline void +context_switched(PyThreadState *ts) +{ + ts->context_ver++; + // ts->context is used instead of context_get() because context_get() might + // throw if ts->context is NULL. + notify_context_watchers(ts, Py_CONTEXT_SWITCHED, ts->context); +} + + static int _PyContext_Enter(PyThreadState *ts, PyObject *octx) { @@ -191,9 +206,7 @@ _PyContext_Enter(PyThreadState *ts, PyObject *octx) ctx->ctx_entered = 1; ts->context = Py_NewRef(ctx); - ts->context_ver++; - - notify_context_watchers(ts, Py_CONTEXT_EVENT_ENTER, octx); + context_switched(ts); return 0; } @@ -227,13 +240,11 @@ _PyContext_Exit(PyThreadState *ts, PyObject *octx) return -1; } - notify_context_watchers(ts, Py_CONTEXT_EVENT_EXIT, octx); Py_SETREF(ts->context, (PyObject *)ctx->ctx_prev); - ts->context_ver++; ctx->ctx_prev = NULL; ctx->ctx_entered = 0; - + context_switched(ts); return 0; } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 5532c04e497a75..15a6c7bc1a7966 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -643,8 +643,8 @@ double dres = ((PyFloatObject *)left_o)->ob_fval * ((PyFloatObject *)right_o)->ob_fval; - PyObject *res_o; - DECREF_INPUTS_AND_REUSE_FLOAT(left_o, right_o, dres, res_o); + PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); + if (res_o == NULL) JUMP_TO_ERROR(); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -664,8 +664,8 @@ double dres = ((PyFloatObject *)left_o)->ob_fval + ((PyFloatObject *)right_o)->ob_fval; - PyObject *res_o; - DECREF_INPUTS_AND_REUSE_FLOAT(left_o, right_o, dres, res_o); + PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); + if (res_o == NULL) JUMP_TO_ERROR(); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -685,8 +685,8 @@ double dres = ((PyFloatObject *)left_o)->ob_fval - ((PyFloatObject *)right_o)->ob_fval; - PyObject *res_o; - DECREF_INPUTS_AND_REUSE_FLOAT(left_o, right_o, dres, res_o); + PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); + if (res_o == NULL) JUMP_TO_ERROR(); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -737,7 +737,9 @@ _PyStackRef left; right = stack_pointer[-1]; left = stack_pointer[-2]; + #ifndef NDEBUG PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + #endif PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); int next_oparg; #if TIER_ONE diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 0eeb566a0adadc..a9290986c24f45 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -84,8 +84,8 @@ double dres = ((PyFloatObject *)left_o)->ob_fval + ((PyFloatObject *)right_o)->ob_fval; - PyObject *res_o; - DECREF_INPUTS_AND_REUSE_FLOAT(left_o, right_o, dres, res_o); + PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); + if (res_o == NULL) goto pop_2_error; res = PyStackRef_FromPyObjectSteal(res_o); } stack_pointer[-2] = res; @@ -183,7 +183,9 @@ /* Skip 1 cache entry */ // _BINARY_OP_INPLACE_ADD_UNICODE { + #ifndef NDEBUG PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + #endif PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); int next_oparg; #if TIER_ONE @@ -251,8 +253,8 @@ double dres = ((PyFloatObject *)left_o)->ob_fval * ((PyFloatObject *)right_o)->ob_fval; - PyObject *res_o; - DECREF_INPUTS_AND_REUSE_FLOAT(left_o, right_o, dres, res_o); + PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); + if (res_o == NULL) goto pop_2_error; res = PyStackRef_FromPyObjectSteal(res_o); } stack_pointer[-2] = res; @@ -322,8 +324,8 @@ double dres = ((PyFloatObject *)left_o)->ob_fval - ((PyFloatObject *)right_o)->ob_fval; - PyObject *res_o; - DECREF_INPUTS_AND_REUSE_FLOAT(left_o, right_o, dres, res_o); + PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); + if (res_o == NULL) goto pop_2_error; res = PyStackRef_FromPyObjectSteal(res_o); } stack_pointer[-2] = res; diff --git a/Python/hamt.c b/Python/hamt.c index a8fbb00b807934..cfd211f4541446 100644 --- a/Python/hamt.c +++ b/Python/hamt.c @@ -349,7 +349,7 @@ hamt_node_find(PyHamtNode *node, #ifdef Py_DEBUG static int hamt_node_dump(PyHamtNode *node, - _PyUnicodeWriter *writer, int level); + PyUnicodeWriter *writer, int level); #endif static PyHamtNode * @@ -444,7 +444,7 @@ hamt_bitindex(uint32_t bitmap, uint32_t bit) #ifdef Py_DEBUG static int -_hamt_dump_ident(_PyUnicodeWriter *writer, int level) +_hamt_dump_ident(PyUnicodeWriter *writer, int level) { /* Write `' ' * level` to the `writer` */ PyObject *str = NULL; @@ -467,7 +467,7 @@ _hamt_dump_ident(_PyUnicodeWriter *writer, int level) goto error; } - ret = _PyUnicodeWriter_WriteStr(writer, res); + ret = PyUnicodeWriter_WriteStr(writer, res); error: Py_XDECREF(res); @@ -476,29 +476,6 @@ _hamt_dump_ident(_PyUnicodeWriter *writer, int level) return ret; } -static int -_hamt_dump_format(_PyUnicodeWriter *writer, const char *format, ...) -{ - /* A convenient helper combining _PyUnicodeWriter_WriteStr and - PyUnicode_FromFormatV. - */ - PyObject* msg; - int ret; - - va_list vargs; - va_start(vargs, format); - msg = PyUnicode_FromFormatV(format, vargs); - va_end(vargs); - - if (msg == NULL) { - return -1; - } - - ret = _PyUnicodeWriter_WriteStr(writer, msg); - Py_DECREF(msg); - return ret; -} - #endif /* Py_DEBUG */ /////////////////////////////////// Bitmap Node @@ -1154,7 +1131,7 @@ hamt_node_bitmap_dealloc(PyHamtNode_Bitmap *self) #ifdef Py_DEBUG static int hamt_node_bitmap_dump(PyHamtNode_Bitmap *node, - _PyUnicodeWriter *writer, int level) + PyUnicodeWriter *writer, int level) { /* Debug build: __dump__() method implementation for Bitmap nodes. */ @@ -1166,8 +1143,8 @@ hamt_node_bitmap_dump(PyHamtNode_Bitmap *node, goto error; } - if (_hamt_dump_format(writer, "BitmapNode(size=%zd count=%zd ", - Py_SIZE(node), Py_SIZE(node) / 2)) + if (PyUnicodeWriter_Format(writer, "BitmapNode(size=%zd count=%zd ", + Py_SIZE(node), Py_SIZE(node) / 2) < 0) { goto error; } @@ -1181,7 +1158,9 @@ hamt_node_bitmap_dump(PyHamtNode_Bitmap *node, if (tmp2 == NULL) { goto error; } - if (_hamt_dump_format(writer, "bitmap=%S id=%p):\n", tmp2, node)) { + if (PyUnicodeWriter_Format(writer, "bitmap=%S id=%p):\n", + tmp2, node) < 0) + { Py_DECREF(tmp2); goto error; } @@ -1196,7 +1175,7 @@ hamt_node_bitmap_dump(PyHamtNode_Bitmap *node, } if (key_or_null == NULL) { - if (_hamt_dump_format(writer, "NULL:\n")) { + if (PyUnicodeWriter_WriteUTF8(writer, "NULL:\n", -1) < 0) { goto error; } @@ -1207,14 +1186,14 @@ hamt_node_bitmap_dump(PyHamtNode_Bitmap *node, } } else { - if (_hamt_dump_format(writer, "%R: %R", key_or_null, - val_or_node)) + if (PyUnicodeWriter_Format(writer, "%R: %R", + key_or_null, val_or_node) < 0) { goto error; } } - if (_hamt_dump_format(writer, "\n")) { + if (PyUnicodeWriter_WriteUTF8(writer, "\n", 1) < 0) { goto error; } } @@ -1548,7 +1527,7 @@ hamt_node_collision_dealloc(PyHamtNode_Collision *self) #ifdef Py_DEBUG static int hamt_node_collision_dump(PyHamtNode_Collision *node, - _PyUnicodeWriter *writer, int level) + PyUnicodeWriter *writer, int level) { /* Debug build: __dump__() method implementation for Collision nodes. */ @@ -1558,8 +1537,8 @@ hamt_node_collision_dump(PyHamtNode_Collision *node, goto error; } - if (_hamt_dump_format(writer, "CollisionNode(size=%zd id=%p):\n", - Py_SIZE(node), node)) + if (PyUnicodeWriter_Format(writer, "CollisionNode(size=%zd id=%p):\n", + Py_SIZE(node), node) < 0) { goto error; } @@ -1572,7 +1551,7 @@ hamt_node_collision_dump(PyHamtNode_Collision *node, goto error; } - if (_hamt_dump_format(writer, "%R: %R\n", key, val)) { + if (PyUnicodeWriter_Format(writer, "%R: %R\n", key, val) < 0) { goto error; } } @@ -1924,7 +1903,7 @@ hamt_node_array_dealloc(PyHamtNode_Array *self) #ifdef Py_DEBUG static int hamt_node_array_dump(PyHamtNode_Array *node, - _PyUnicodeWriter *writer, int level) + PyUnicodeWriter *writer, int level) { /* Debug build: __dump__() method implementation for Array nodes. */ @@ -1934,7 +1913,7 @@ hamt_node_array_dump(PyHamtNode_Array *node, goto error; } - if (_hamt_dump_format(writer, "ArrayNode(id=%p):\n", node)) { + if (PyUnicodeWriter_Format(writer, "ArrayNode(id=%p):\n", node) < 0) { goto error; } @@ -1947,7 +1926,7 @@ hamt_node_array_dump(PyHamtNode_Array *node, goto error; } - if (_hamt_dump_format(writer, "%zd::\n", i)) { + if (PyUnicodeWriter_Format(writer, "%zd::\n", i) < 0) { goto error; } @@ -1955,7 +1934,7 @@ hamt_node_array_dump(PyHamtNode_Array *node, goto error; } - if (_hamt_dump_format(writer, "\n")) { + if (PyUnicodeWriter_WriteUTF8(writer, "\n", 1) < 0) { goto error; } } @@ -2071,7 +2050,7 @@ hamt_node_find(PyHamtNode *node, #ifdef Py_DEBUG static int hamt_node_dump(PyHamtNode *node, - _PyUnicodeWriter *writer, int level) + PyUnicodeWriter *writer, int level) { /* Debug build: __dump__() method implementation for a node. @@ -2440,22 +2419,24 @@ _PyHamt_New(void) static PyObject * hamt_dump(PyHamtObject *self) { - _PyUnicodeWriter writer; - - _PyUnicodeWriter_Init(&writer); + PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); + if (writer == NULL) { + return NULL; + } - if (_hamt_dump_format(&writer, "HAMT(len=%zd):\n", self->h_count)) { + if (PyUnicodeWriter_Format(writer, "HAMT(len=%zd):\n", + self->h_count) < 0) { goto error; } - if (hamt_node_dump(self->h_root, &writer, 0)) { + if (hamt_node_dump(self->h_root, writer, 0)) { goto error; } - return _PyUnicodeWriter_Finish(&writer); + return PyUnicodeWriter_Finish(writer); error: - _PyUnicodeWriter_Dealloc(&writer); + PyUnicodeWriter_Discard(writer); return NULL; } #endif /* Py_DEBUG */ diff --git a/Python/initconfig.c b/Python/initconfig.c index 58ac5e7d7eaeff..c142438b02bfd9 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -3457,6 +3457,9 @@ PyInitConfig_Create(void) void PyInitConfig_Free(PyInitConfig *config) { + if (config == NULL) { + return; + } free(config->err_msg); free(config); } diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index e6c599a2ac4a46..2605825d3d0078 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -455,8 +455,8 @@ Modules/_testcapi/watchers.c - pyfunc_watchers - Modules/_testcapi/watchers.c - func_watcher_ids - Modules/_testcapi/watchers.c - func_watcher_callbacks - Modules/_testcapi/watchers.c - context_watcher_ids - -Modules/_testcapi/watchers.c - num_context_object_enter_events - -Modules/_testcapi/watchers.c - num_context_object_exit_events - +Modules/_testcapi/watchers.c - context_switches - +Modules/_testcapi/watchers.c add_context_watcher callbacks - Modules/_testcapimodule.c - BasicStaticTypes - Modules/_testcapimodule.c - num_basic_static_types_used - Modules/_testcapimodule.c - ContainerNoGC_members - diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 19fdeac65cf2df..381ad3a4e2082c 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -575,6 +575,7 @@ def has_error_without_pop(op: parser.InstDef) -> bool: "_PyDictValues_AddToInsertionOrder", "_PyErr_Occurred", "_PyEval_FrameClearAndPop", + "_PyFloat_FromDouble_ConsumeInputs", "_PyFrame_GetCode", "_PyFrame_IsIncomplete", "_PyFrame_PushUnchecked", diff --git a/Tools/cases_generator/interpreter_definition.md b/Tools/cases_generator/interpreter_definition.md index ba09931c541646..6cf36f343d5fa7 100644 --- a/Tools/cases_generator/interpreter_definition.md +++ b/Tools/cases_generator/interpreter_definition.md @@ -74,7 +74,7 @@ We update it as the need arises. ### Syntax Each op definition has a kind, a name, a stack and instruction stream effect, -and a piece of C code describing its semantics:: +and a piece of C code describing its semantics: ``` file: @@ -245,7 +245,8 @@ The same is true for all members of a pseudo instruction ## Examples -(Another source of examples can be found in the [tests](test_generator.py).) +(Another source of examples can be found in the +[tests](https://github.com/python/cpython/blob/main/Lib/test/test_generated_cases.py).) Some examples: diff --git a/configure b/configure index 0cc73e4e66552d..be119f108a060b 100755 --- a/configure +++ b/configure @@ -11092,6 +11092,12 @@ if test "x$ac_cv_header_linux_memfd_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_MEMFD_H 1" >>confdefs.h +fi +ac_fn_c_check_header_compile "$LINENO" "linux/netfilter_ipv4.h" "ac_cv_header_linux_netfilter_ipv4_h" "$ac_includes_default" +if test "x$ac_cv_header_linux_netfilter_ipv4_h" = xyes +then : + printf "%s\n" "#define HAVE_LINUX_NETFILTER_IPV4_H 1" >>confdefs.h + fi ac_fn_c_check_header_compile "$LINENO" "linux/random.h" "ac_cv_header_linux_random_h" "$ac_includes_default" if test "x$ac_cv_header_linux_random_h" = xyes @@ -28993,10 +28999,6 @@ printf %s "checking whether libatomic is needed by ... " >&6; } if test ${ac_cv_libatomic_needed+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "$cross_compiling" = yes -then : - ac_cv_libatomic_needed=no else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -29038,16 +29040,14 @@ int main() } _ACEOF -if ac_fn_c_try_run "$LINENO" +if ac_fn_c_try_link "$LINENO" then : ac_cv_libatomic_needed=no else $as_nop ac_cv_libatomic_needed=yes fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libatomic_needed" >&5 printf "%s\n" "$ac_cv_libatomic_needed" >&6; } diff --git a/configure.ac b/configure.ac index 1864e94ace9243..582851695e400f 100644 --- a/configure.ac +++ b/configure.ac @@ -3013,7 +3013,7 @@ AC_DEFINE([STDC_HEADERS], [1], AC_CHECK_HEADERS([ \ alloca.h asm/types.h bluetooth.h conio.h direct.h dlfcn.h endian.h errno.h fcntl.h grp.h \ io.h langinfo.h libintl.h libutil.h linux/auxvec.h sys/auxv.h linux/fs.h linux/limits.h linux/memfd.h \ - linux/random.h linux/soundcard.h \ + linux/netfilter_ipv4.h linux/random.h linux/soundcard.h \ linux/tipc.h linux/wait.h netdb.h net/ethernet.h netinet/in.h netpacket/packet.h poll.h process.h pthread.h pty.h \ sched.h setjmp.h shadow.h signal.h spawn.h stropts.h sys/audioio.h sys/bsdtty.h sys/devpoll.h \ sys/endian.h sys/epoll.h sys/event.h sys/eventfd.h sys/file.h sys/ioctl.h sys/kern_control.h \ @@ -7497,7 +7497,7 @@ CPPFLAGS="${BASECPPFLAGS} -I. -I${srcdir}/Include ${CPPFLAGS}" AC_CACHE_CHECK([whether libatomic is needed by ], [ac_cv_libatomic_needed], -[AC_RUN_IFELSE([AC_LANG_SOURCE([[ +[AC_LINK_IFELSE([AC_LANG_SOURCE([[ // pyatomic.h needs uint64_t and Py_ssize_t types #include // int64_t, intptr_t #ifdef HAVE_SYS_TYPES_H @@ -7534,9 +7534,8 @@ int main() return 0; // all good } ]])], - [ac_cv_libatomic_needed=no], dnl build succeeded - [ac_cv_libatomic_needed=yes], dnl build failed - [ac_cv_libatomic_needed=no]) dnl cross compilation + [ac_cv_libatomic_needed=no], dnl build and link succeeded + [ac_cv_libatomic_needed=yes]) dnl build and link failed ]) AS_VAR_IF([ac_cv_libatomic_needed], [yes], diff --git a/pyconfig.h.in b/pyconfig.h.in index 7f02603e26f5d0..1947d8ee14f83e 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -739,6 +739,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_MEMFD_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_NETFILTER_IPV4_H + /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_NETLINK_H