From webhook-mailer at python.org Sun Jan 1 03:44:53 2023 From: webhook-mailer at python.org (hauntsaninja) Date: Sun, 01 Jan 2023 08:44:53 -0000 Subject: [Python-checkins] gh-100488: Add is_integer method to fractions.Fraction (#100489) Message-ID: https://github.com/python/cpython/commit/e83f88a455261ed53530a960f1514ab7af7d2e82 commit: e83f88a455261ed53530a960f1514ab7af7d2e82 branch: main author: Shantanu <12621235+hauntsaninja at users.noreply.github.com> committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com> date: 2023-01-01T01:44:48-07:00 summary: gh-100488: Add is_integer method to fractions.Fraction (#100489) files: A Misc/NEWS.d/next/Library/2022-12-24-04-13-54.gh-issue-100488.Ut8HbE.rst M Doc/library/fractions.rst M Lib/fractions.py M Lib/test/test_fractions.py diff --git a/Doc/library/fractions.rst b/Doc/library/fractions.rst index c46d88b2297a..dc9d5fc18215 100644 --- a/Doc/library/fractions.rst +++ b/Doc/library/fractions.rst @@ -117,6 +117,12 @@ another rational number, or from a string. .. versionadded:: 3.8 + .. method:: is_integer() + + Return ``True`` if the Fraction is an integer. + + .. versionadded:: 3.12 + .. classmethod:: from_float(flt) Alternative constructor which only accepts instances of diff --git a/Lib/fractions.py b/Lib/fractions.py index 75c7df14e1b9..4302f3f1b98d 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -225,6 +225,10 @@ def from_decimal(cls, dec): (cls.__name__, dec, type(dec).__name__)) return cls(*dec.as_integer_ratio()) + def is_integer(self): + """Return True if the Fraction is an integer.""" + return self._denominator == 1 + def as_integer_ratio(self): """Return the integer ratio as a tuple. diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py index 7fa9dbea905b..819d680fd88e 100644 --- a/Lib/test/test_fractions.py +++ b/Lib/test/test_fractions.py @@ -340,6 +340,19 @@ def testFromDecimal(self): ValueError, "cannot convert NaN to integer ratio", F.from_decimal, Decimal("snan")) + def test_is_integer(self): + self.assertTrue(F(1, 1).is_integer()) + self.assertTrue(F(-1, 1).is_integer()) + self.assertTrue(F(1, -1).is_integer()) + self.assertTrue(F(2, 2).is_integer()) + self.assertTrue(F(-2, 2).is_integer()) + self.assertTrue(F(2, -2).is_integer()) + + self.assertFalse(F(1, 2).is_integer()) + self.assertFalse(F(-1, 2).is_integer()) + self.assertFalse(F(1, -2).is_integer()) + self.assertFalse(F(-1, -2).is_integer()) + def test_as_integer_ratio(self): self.assertEqual(F(4, 6).as_integer_ratio(), (2, 3)) self.assertEqual(F(-4, 6).as_integer_ratio(), (-2, 3)) diff --git a/Misc/NEWS.d/next/Library/2022-12-24-04-13-54.gh-issue-100488.Ut8HbE.rst b/Misc/NEWS.d/next/Library/2022-12-24-04-13-54.gh-issue-100488.Ut8HbE.rst new file mode 100644 index 000000000000..f7c07c0ab4c2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-24-04-13-54.gh-issue-100488.Ut8HbE.rst @@ -0,0 +1 @@ +Add :meth:`Fraction.is_integer` to check whether a :class:`fractions.Fraction` is an integer. This improves duck type compatibility with :class:`float` and :class:`int`. From webhook-mailer at python.org Sun Jan 1 09:20:43 2023 From: webhook-mailer at python.org (corona10) Date: Sun, 01 Jan 2023 14:20:43 -0000 Subject: [Python-checkins] gh-100649: Update native_thread_id after fork (gh-100650) Message-ID: https://github.com/python/cpython/commit/d52d4942cfdd52a50f88b87b1ff2a67375dbcf47 commit: d52d4942cfdd52a50f88b87b1ff2a67375dbcf47 branch: main author: Gabriele N. Tornetta committer: corona10 date: 2023-01-01T23:20:38+09:00 summary: gh-100649: Update native_thread_id after fork (gh-100650) Update native_thread_id after fork files: A Misc/NEWS.d/next/Core and Builtins/2022-12-31-23-32-09.gh-issue-100649.C0fY4S.rst M Modules/posixmodule.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-31-23-32-09.gh-issue-100649.C0fY4S.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-31-23-32-09.gh-issue-100649.C0fY4S.rst new file mode 100644 index 000000000000..7ee929ad09fb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-12-31-23-32-09.gh-issue-100649.C0fY4S.rst @@ -0,0 +1 @@ +Update the native_thread_id field of PyThreadState after fork. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 1d9a33ab7bc9..0d4c179368ce 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -595,6 +595,10 @@ PyOS_AfterFork_Child(void) PyThreadState *tstate = _PyThreadState_GET(); _Py_EnsureTstateNotNULL(tstate); +#ifdef PY_HAVE_THREAD_NATIVE_ID + tstate->native_thread_id = PyThread_get_thread_native_id(); +#endif + status = _PyEval_ReInitThreads(tstate); if (_PyStatus_EXCEPTION(status)) { goto fatal_error; From webhook-mailer at python.org Sun Jan 1 09:51:01 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 01 Jan 2023 14:51:01 -0000 Subject: [Python-checkins] gh-100649: Update native_thread_id after fork (gh-100650) Message-ID: https://github.com/python/cpython/commit/d0a9bc5a89728a66f66835cbeec6a33aa7de26f0 commit: d0a9bc5a89728a66f66835cbeec6a33aa7de26f0 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-01T06:50:56-08:00 summary: gh-100649: Update native_thread_id after fork (gh-100650) Update native_thread_id after fork (cherry picked from commit d52d4942cfdd52a50f88b87b1ff2a67375dbcf47) Co-authored-by: Gabriele N. Tornetta files: A Misc/NEWS.d/next/Core and Builtins/2022-12-31-23-32-09.gh-issue-100649.C0fY4S.rst M Modules/posixmodule.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-31-23-32-09.gh-issue-100649.C0fY4S.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-31-23-32-09.gh-issue-100649.C0fY4S.rst new file mode 100644 index 000000000000..7ee929ad09fb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-12-31-23-32-09.gh-issue-100649.C0fY4S.rst @@ -0,0 +1 @@ +Update the native_thread_id field of PyThreadState after fork. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 2dfed7c583c4..150bb78cb41c 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -591,6 +591,10 @@ PyOS_AfterFork_Child(void) PyThreadState *tstate = _PyThreadState_GET(); _Py_EnsureTstateNotNULL(tstate); +#ifdef PY_HAVE_THREAD_NATIVE_ID + tstate->native_thread_id = PyThread_get_thread_native_id(); +#endif + status = _PyEval_ReInitThreads(tstate); if (_PyStatus_EXCEPTION(status)) { goto fatal_error; From webhook-mailer at python.org Sun Jan 1 10:06:23 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sun, 01 Jan 2023 15:06:23 -0000 Subject: [Python-checkins] gh-96773 Fix documentation of socket backlog (#96778) Message-ID: https://github.com/python/cpython/commit/1d1480fefc6ae77d14d6eff007b180ff5d1cd5d4 commit: 1d1480fefc6ae77d14d6eff007b180ff5d1cd5d4 branch: main author: Mehrdad Moradizadeh committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-01T20:36:17+05:30 summary: gh-96773 Fix documentation of socket backlog (#96778) files: M Doc/library/socket.rst diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index de2e1aa3868b..cffb19c8b7b5 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -803,8 +803,8 @@ The following functions all create :ref:`socket objects `. ``(host, port)``) and returns the socket object. *family* should be either :data:`AF_INET` or :data:`AF_INET6`. - *backlog* is the queue size passed to :meth:`socket.listen`; when ``0`` - a default reasonable value is chosen. + *backlog* is the queue size passed to :meth:`socket.listen`; if not specified + , a default reasonable value is chosen. *reuse_port* dictates whether to set the :data:`SO_REUSEPORT` socket option. If *dualstack_ipv6* is true and the platform supports it the socket will From webhook-mailer at python.org Sun Jan 1 10:13:56 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 01 Jan 2023 15:13:56 -0000 Subject: [Python-checkins] gh-96773 Fix documentation of socket backlog (GH-96778) Message-ID: https://github.com/python/cpython/commit/c7dcfdaef61df31e98444a12dd3a3337ba4d759c commit: c7dcfdaef61df31e98444a12dd3a3337ba4d759c branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-01T07:13:50-08:00 summary: gh-96773 Fix documentation of socket backlog (GH-96778) (cherry picked from commit 1d1480fefc6ae77d14d6eff007b180ff5d1cd5d4) Co-authored-by: Mehrdad Moradizadeh files: M Doc/library/socket.rst diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 8c260897dbaa..bb689c4df8de 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -717,8 +717,8 @@ The following functions all create :ref:`socket objects `. ``(host, port)``) and return the socket object. *family* should be either :data:`AF_INET` or :data:`AF_INET6`. - *backlog* is the queue size passed to :meth:`socket.listen`; when ``0`` - a default reasonable value is chosen. + *backlog* is the queue size passed to :meth:`socket.listen`; if not specified + , a default reasonable value is chosen. *reuse_port* dictates whether to set the :data:`SO_REUSEPORT` socket option. If *dualstack_ipv6* is true and the platform supports it the socket will From webhook-mailer at python.org Sun Jan 1 10:16:31 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 01 Jan 2023 15:16:31 -0000 Subject: [Python-checkins] gh-96773 Fix documentation of socket backlog (GH-96778) Message-ID: https://github.com/python/cpython/commit/e3b303a020eaf12dcec4db8bb2b0fb354a14cb68 commit: e3b303a020eaf12dcec4db8bb2b0fb354a14cb68 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-01T07:16:25-08:00 summary: gh-96773 Fix documentation of socket backlog (GH-96778) (cherry picked from commit 1d1480fefc6ae77d14d6eff007b180ff5d1cd5d4) Co-authored-by: Mehrdad Moradizadeh files: M Doc/library/socket.rst diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 0a8f35eed4c8..6fb6d5fd8b1b 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -670,8 +670,8 @@ The following functions all create :ref:`socket objects `. ``(host, port)``) and return the socket object. *family* should be either :data:`AF_INET` or :data:`AF_INET6`. - *backlog* is the queue size passed to :meth:`socket.listen`; when ``0`` - a default reasonable value is chosen. + *backlog* is the queue size passed to :meth:`socket.listen`; if not specified + , a default reasonable value is chosen. *reuse_port* dictates whether to set the :data:`SO_REUSEPORT` socket option. If *dualstack_ipv6* is true and the platform supports it the socket will From webhook-mailer at python.org Sun Jan 1 10:36:18 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sun, 01 Jan 2023 15:36:18 -0000 Subject: [Python-checkins] gh-100201: Document behavior with a bare `yield` statement (#100416) Message-ID: https://github.com/python/cpython/commit/1aab269d4acbf0b29573ad0a21c54fddee233243 commit: 1aab269d4acbf0b29573ad0a21c54fddee233243 branch: main author: ram vikram singh committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-01T21:06:13+05:30 summary: gh-100201: Document behavior with a bare `yield` statement (#100416) Co-authored-by: C.A.M. Gerlach files: M Doc/reference/expressions.rst diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 920e4d19b82d..6692e50c4ff4 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -454,7 +454,9 @@ generator. That generator then controls the execution of the generator function. The execution starts when one of the generator's methods is called. At that time, the execution proceeds to the first yield expression, where it is suspended again, returning the value of :token:`~python-grammar:expression_list` -to the generator's caller. By suspended, we mean that all local state is +to the generator's caller, +or ``None`` if :token:`~python-grammer:expression_list` is omitted. +By suspended, we mean that all local state is retained, including the current bindings of local variables, the instruction pointer, the internal evaluation stack, and the state of any exception handling. When the execution is resumed by calling one of the generator's methods, the From webhook-mailer at python.org Sun Jan 1 10:41:38 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sun, 01 Jan 2023 15:41:38 -0000 Subject: [Python-checkins] gh-87980: Fix the error message for disallowed __weakref__ slots (#25362) Message-ID: https://github.com/python/cpython/commit/ba1342ce998c6c0c36078411d169f29179fbc9f6 commit: ba1342ce998c6c0c36078411d169f29179fbc9f6 branch: main author: G?ry Ogam committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-01T21:11:33+05:30 summary: gh-87980: Fix the error message for disallowed __weakref__ slots (#25362) Fix the error message for disallowed `__weakref__` slots. files: M Objects/typeobject.c diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 16b1a3035d56..43633f044751 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2712,8 +2712,7 @@ type_new_visit_slots(type_new_ctx *ctx) if (!ctx->may_add_weak || ctx->add_weak != 0) { PyErr_SetString(PyExc_TypeError, "__weakref__ slot disallowed: " - "either we already got one, " - "or __itemsize__ != 0"); + "we already got one"); return -1; } ctx->add_weak++; From webhook-mailer at python.org Sun Jan 1 10:42:37 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 01 Jan 2023 15:42:37 -0000 Subject: [Python-checkins] gh-100201: Document behavior with a bare `yield` statement (GH-100416) Message-ID: https://github.com/python/cpython/commit/fd108e5439e8cf3481e7f836aa0fb5622f57d551 commit: fd108e5439e8cf3481e7f836aa0fb5622f57d551 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-01T07:42:31-08:00 summary: gh-100201: Document behavior with a bare `yield` statement (GH-100416) (cherry picked from commit 1aab269d4acbf0b29573ad0a21c54fddee233243) Co-authored-by: ram vikram singh Co-authored-by: C.A.M. Gerlach files: M Doc/reference/expressions.rst diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 60c340767f87..21cca5bfc5d5 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -449,7 +449,9 @@ generator. That generator then controls the execution of the generator function. The execution starts when one of the generator's methods is called. At that time, the execution proceeds to the first yield expression, where it is suspended again, returning the value of :token:`~python-grammar:expression_list` -to the generator's caller. By suspended, we mean that all local state is +to the generator's caller, +or ``None`` if :token:`~python-grammer:expression_list` is omitted. +By suspended, we mean that all local state is retained, including the current bindings of local variables, the instruction pointer, the internal evaluation stack, and the state of any exception handling. When the execution is resumed by calling one of the generator's methods, the From webhook-mailer at python.org Sun Jan 1 10:44:33 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 01 Jan 2023 15:44:33 -0000 Subject: [Python-checkins] gh-100201: Document behavior with a bare `yield` statement (GH-100416) Message-ID: https://github.com/python/cpython/commit/18ccb846970a808e6a46e4cd5779f7719cc4752a commit: 18ccb846970a808e6a46e4cd5779f7719cc4752a branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-01T07:44:27-08:00 summary: gh-100201: Document behavior with a bare `yield` statement (GH-100416) (cherry picked from commit 1aab269d4acbf0b29573ad0a21c54fddee233243) Co-authored-by: ram vikram singh Co-authored-by: C.A.M. Gerlach files: M Doc/reference/expressions.rst diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 0cdf91e75b34..655d19969a58 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -454,7 +454,9 @@ generator. That generator then controls the execution of the generator function. The execution starts when one of the generator's methods is called. At that time, the execution proceeds to the first yield expression, where it is suspended again, returning the value of :token:`~python-grammar:expression_list` -to the generator's caller. By suspended, we mean that all local state is +to the generator's caller, +or ``None`` if :token:`~python-grammer:expression_list` is omitted. +By suspended, we mean that all local state is retained, including the current bindings of local variables, the instruction pointer, the internal evaluation stack, and the state of any exception handling. When the execution is resumed by calling one of the generator's methods, the From webhook-mailer at python.org Sun Jan 1 11:07:37 2023 From: webhook-mailer at python.org (jaraco) Date: Sun, 01 Jan 2023 16:07:37 -0000 Subject: [Python-checkins] gh-97930: Apply changes from importlib_resources 5.10. (GH-100598) Message-ID: https://github.com/python/cpython/commit/447d061bc7b978afedd3b0148715d2153ac726c5 commit: 447d061bc7b978afedd3b0148715d2153ac726c5 branch: main author: Jason R. Coombs committer: jaraco date: 2023-01-01T11:07:32-05:00 summary: gh-97930: Apply changes from importlib_resources 5.10. (GH-100598) files: A Lib/test/test_importlib/resources/_path.py A Misc/NEWS.d/next/Library/2022-12-29-11-45-22.gh-issue-97930.hrtmJe.rst M Doc/library/importlib.resources.rst M Lib/importlib/resources/_common.py M Lib/importlib/resources/_legacy.py M Lib/importlib/resources/abc.py M Lib/importlib/resources/simple.py M Lib/test/test_importlib/resources/test_files.py M Lib/test/test_importlib/resources/util.py diff --git a/Doc/library/importlib.resources.rst b/Doc/library/importlib.resources.rst index 399191301a36..4c6aa59bf9f5 100644 --- a/Doc/library/importlib.resources.rst +++ b/Doc/library/importlib.resources.rst @@ -14,12 +14,13 @@ This module leverages Python's import system to provide access to *resources* within *packages*. "Resources" are file-like resources associated with a module or package in -Python. The resources may be contained directly in a package or within a -subdirectory contained in that package. Resources may be text or binary. As a -result, Python module sources (.py) of a package and compilation artifacts -(pycache) are technically de-facto resources of that package. In practice, -however, resources are primarily those non-Python artifacts exposed -specifically by the package author. +Python. The resources may be contained directly in a package, within a +subdirectory contained in that package, or adjacent to modules outside a +package. Resources may be text or binary. As a result, Python module sources +(.py) of a package and compilation artifacts (pycache) are technically +de-facto resources of that package. In practice, however, resources are +primarily those non-Python artifacts exposed specifically by the package +author. Resources can be opened or read in either binary or text mode. @@ -49,27 +50,35 @@ for example, a package and its resources can be imported from a zip file using ``get_resource_reader(fullname)`` method as specified by :class:`importlib.resources.abc.ResourceReader`. -.. data:: Package +.. data:: Anchor - Whenever a function accepts a ``Package`` argument, you can pass in - either a :class:`module object ` or a module name - as a string. You can only pass module objects whose - ``__spec__.submodule_search_locations`` is not ``None``. + Represents an anchor for resources, either a :class:`module object + ` or a module name as a string. Defined as + ``Union[str, ModuleType]``. - The ``Package`` type is defined as ``Union[str, ModuleType]``. - -.. function:: files(package) +.. function:: files(anchor: Optional[Anchor] = None) Returns a :class:`~importlib.resources.abc.Traversable` object - representing the resource container for the package (think directory) - and its resources (think files). A Traversable may contain other - containers (think subdirectories). + representing the resource container (think directory) and its resources + (think files). A Traversable may contain other containers (think + subdirectories). - *package* is either a name or a module object which conforms to the - :data:`Package` requirements. + *anchor* is an optional :data:`Anchor`. If the anchor is a + package, resources are resolved from that package. If a module, + resources are resolved adjacent to that module (in the same package + or the package root). If the anchor is omitted, the caller's module + is used. .. versionadded:: 3.9 + .. versionchanged:: 3.12 + "package" parameter was renamed to "anchor". "anchor" can now + be a non-package module and if omitted will default to the caller's + module. "package" is still accepted for compatibility but will raise + a DeprecationWarning. Consider passing the anchor positionally or + using ``importlib_resources >= 5.10`` for a compatible interface + on older Pythons. + .. function:: as_file(traversable) Given a :class:`~importlib.resources.abc.Traversable` object representing @@ -86,6 +95,7 @@ for example, a package and its resources can be imported from a zip file using .. versionadded:: 3.9 + Deprecated functions -------------------- @@ -94,6 +104,18 @@ scheduled for removal in a future version of Python. The main drawback of these functions is that they do not support directories: they assume all resources are located directly within a *package*. +.. data:: Package + + Whenever a function accepts a ``Package`` argument, you can pass in + either a :class:`module object ` or a module name + as a string. You can only pass module objects whose + ``__spec__.submodule_search_locations`` is not ``None``. + + The ``Package`` type is defined as ``Union[str, ModuleType]``. + + .. deprecated:: 3.12 + + .. data:: Resource For *resource* arguments of the functions below, you can pass in @@ -102,6 +124,7 @@ directories: they assume all resources are located directly within a *package*. The ``Resource`` type is defined as ``Union[str, os.PathLike]``. + .. function:: open_binary(package, resource) Open for binary reading the *resource* within *package*. diff --git a/Lib/importlib/resources/_common.py b/Lib/importlib/resources/_common.py index 92a37e2c12b8..a39025353426 100644 --- a/Lib/importlib/resources/_common.py +++ b/Lib/importlib/resources/_common.py @@ -5,25 +5,58 @@ import contextlib import types import importlib +import inspect +import warnings +import itertools -from typing import Union, Optional +from typing import Union, Optional, cast from .abc import ResourceReader, Traversable from ._adapters import wrap_spec Package = Union[types.ModuleType, str] +Anchor = Package -def files(package): - # type: (Package) -> Traversable +def package_to_anchor(func): """ - Get a Traversable resource from a package + Replace 'package' parameter as 'anchor' and warn about the change. + + Other errors should fall through. + + >>> files('a', 'b') + Traceback (most recent call last): + TypeError: files() takes from 0 to 1 positional arguments but 2 were given + """ + undefined = object() + + @functools.wraps(func) + def wrapper(anchor=undefined, package=undefined): + if package is not undefined: + if anchor is not undefined: + return func(anchor, package) + warnings.warn( + "First parameter to files is renamed to 'anchor'", + DeprecationWarning, + stacklevel=2, + ) + return func(package) + elif anchor is undefined: + return func() + return func(anchor) + + return wrapper + + + at package_to_anchor +def files(anchor: Optional[Anchor] = None) -> Traversable: + """ + Get a Traversable resource for an anchor. """ - return from_package(get_package(package)) + return from_package(resolve(anchor)) -def get_resource_reader(package): - # type: (types.ModuleType) -> Optional[ResourceReader] +def get_resource_reader(package: types.ModuleType) -> Optional[ResourceReader]: """ Return the package's loader if it's a ResourceReader. """ @@ -39,24 +72,39 @@ def get_resource_reader(package): return reader(spec.name) # type: ignore -def resolve(cand): - # type: (Package) -> types.ModuleType - return cand if isinstance(cand, types.ModuleType) else importlib.import_module(cand) + at functools.singledispatch +def resolve(cand: Optional[Anchor]) -> types.ModuleType: + return cast(types.ModuleType, cand) + + + at resolve.register +def _(cand: str) -> types.ModuleType: + return importlib.import_module(cand) + + at resolve.register +def _(cand: None) -> types.ModuleType: + return resolve(_infer_caller().f_globals['__name__']) -def get_package(package): - # type: (Package) -> types.ModuleType - """Take a package name or module object and return the module. - Raise an exception if the resolved module is not a package. +def _infer_caller(): """ - resolved = resolve(package) - if wrap_spec(resolved).submodule_search_locations is None: - raise TypeError(f'{package!r} is not a package') - return resolved + Walk the stack and find the frame of the first caller not in this module. + """ + + def is_this_file(frame_info): + return frame_info.filename == __file__ + + def is_wrapper(frame_info): + return frame_info.function == 'wrapper' + + not_this_file = itertools.filterfalse(is_this_file, inspect.stack()) + # also exclude 'wrapper' due to singledispatch in the call stack + callers = itertools.filterfalse(is_wrapper, not_this_file) + return next(callers).frame -def from_package(package): +def from_package(package: types.ModuleType): """ Return a Traversable object for the given package. diff --git a/Lib/importlib/resources/_legacy.py b/Lib/importlib/resources/_legacy.py index 1d5d3f1fbb1f..b1ea8105dad6 100644 --- a/Lib/importlib/resources/_legacy.py +++ b/Lib/importlib/resources/_legacy.py @@ -27,8 +27,7 @@ def wrapper(*args, **kwargs): return wrapper -def normalize_path(path): - # type: (Any) -> str +def normalize_path(path: Any) -> str: """Normalize a path by ensuring it is a string. If the resulting string contains path separators, an exception is raised. diff --git a/Lib/importlib/resources/abc.py b/Lib/importlib/resources/abc.py index 67c78c078567..6750a7aaf14a 100644 --- a/Lib/importlib/resources/abc.py +++ b/Lib/importlib/resources/abc.py @@ -142,7 +142,8 @@ def open(self, mode='r', *args, **kwargs): accepted by io.TextIOWrapper. """ - @abc.abstractproperty + @property + @abc.abstractmethod def name(self) -> str: """ The base name of this object without any parent references. diff --git a/Lib/importlib/resources/simple.py b/Lib/importlib/resources/simple.py index b85e4694aced..7770c922c84f 100644 --- a/Lib/importlib/resources/simple.py +++ b/Lib/importlib/resources/simple.py @@ -16,31 +16,28 @@ class SimpleReader(abc.ABC): provider. """ - @abc.abstractproperty - def package(self): - # type: () -> str + @property + @abc.abstractmethod + def package(self) -> str: """ The name of the package for which this reader loads resources. """ @abc.abstractmethod - def children(self): - # type: () -> List['SimpleReader'] + def children(self) -> List['SimpleReader']: """ Obtain an iterable of SimpleReader for available child containers (e.g. directories). """ @abc.abstractmethod - def resources(self): - # type: () -> List[str] + def resources(self) -> List[str]: """ Obtain available named resources for this virtual package. """ @abc.abstractmethod - def open_binary(self, resource): - # type: (str) -> BinaryIO + def open_binary(self, resource: str) -> BinaryIO: """ Obtain a File-like for a named resource. """ @@ -50,13 +47,35 @@ def name(self): return self.package.split('.')[-1] +class ResourceContainer(Traversable): + """ + Traversable container for a package's resources via its reader. + """ + + def __init__(self, reader: SimpleReader): + self.reader = reader + + def is_dir(self): + return True + + def is_file(self): + return False + + def iterdir(self): + files = (ResourceHandle(self, name) for name in self.reader.resources) + dirs = map(ResourceContainer, self.reader.children()) + return itertools.chain(files, dirs) + + def open(self, *args, **kwargs): + raise IsADirectoryError() + + class ResourceHandle(Traversable): """ Handle to a named resource in a ResourceReader. """ - def __init__(self, parent, name): - # type: (ResourceContainer, str) -> None + def __init__(self, parent: ResourceContainer, name: str): self.parent = parent self.name = name # type: ignore @@ -76,30 +95,6 @@ def joinpath(self, name): raise RuntimeError("Cannot traverse into a resource") -class ResourceContainer(Traversable): - """ - Traversable container for a package's resources via its reader. - """ - - def __init__(self, reader): - # type: (SimpleReader) -> None - self.reader = reader - - def is_dir(self): - return True - - def is_file(self): - return False - - def iterdir(self): - files = (ResourceHandle(self, name) for name in self.reader.resources) - dirs = map(ResourceContainer, self.reader.children()) - return itertools.chain(files, dirs) - - def open(self, *args, **kwargs): - raise IsADirectoryError() - - class TraversableReader(TraversableResources, SimpleReader): """ A TraversableResources based on SimpleReader. Resource providers diff --git a/Lib/test/test_importlib/resources/_path.py b/Lib/test/test_importlib/resources/_path.py new file mode 100644 index 000000000000..c630e4d3d3f3 --- /dev/null +++ b/Lib/test/test_importlib/resources/_path.py @@ -0,0 +1,50 @@ +import pathlib +import functools + + +#### +# from jaraco.path 3.4 + + +def build(spec, prefix=pathlib.Path()): + """ + Build a set of files/directories, as described by the spec. + + Each key represents a pathname, and the value represents + the content. Content may be a nested directory. + + >>> spec = { + ... 'README.txt': "A README file", + ... "foo": { + ... "__init__.py": "", + ... "bar": { + ... "__init__.py": "", + ... }, + ... "baz.py": "# Some code", + ... } + ... } + >>> tmpdir = getfixture('tmpdir') + >>> build(spec, tmpdir) + """ + for name, contents in spec.items(): + create(contents, pathlib.Path(prefix) / name) + + + at functools.singledispatch +def create(content, path): + path.mkdir(exist_ok=True) + build(content, prefix=path) # type: ignore + + + at create.register +def _(content: bytes, path): + path.write_bytes(content) + + + at create.register +def _(content: str, path): + path.write_text(content) + + +# end from jaraco.path +#### diff --git a/Lib/test/test_importlib/resources/test_files.py b/Lib/test/test_importlib/resources/test_files.py index 779e5a12b5d5..fe813ae7d088 100644 --- a/Lib/test/test_importlib/resources/test_files.py +++ b/Lib/test/test_importlib/resources/test_files.py @@ -1,10 +1,24 @@ import typing +import textwrap import unittest +import warnings +import importlib +import contextlib from importlib import resources from importlib.resources.abc import Traversable from . import data01 from . import util +from . import _path +from test.support import os_helper +from test.support import import_helper + + + at contextlib.contextmanager +def suppress_known_deprecation(): + with warnings.catch_warnings(record=True) as ctx: + warnings.simplefilter('default', category=DeprecationWarning) + yield ctx class FilesTests: @@ -25,6 +39,14 @@ def test_read_text(self): def test_traversable(self): assert isinstance(resources.files(self.data), Traversable) + def test_old_parameter(self): + """ + Files used to take a 'package' parameter. Make sure anyone + passing by name is still supported. + """ + with suppress_known_deprecation(): + resources.files(package=self.data) + class OpenDiskTests(FilesTests, unittest.TestCase): def setUp(self): @@ -42,5 +64,50 @@ def setUp(self): self.data = namespacedata01 +class SiteDir: + def setUp(self): + self.fixtures = contextlib.ExitStack() + self.addCleanup(self.fixtures.close) + self.site_dir = self.fixtures.enter_context(os_helper.temp_dir()) + self.fixtures.enter_context(import_helper.DirsOnSysPath(self.site_dir)) + self.fixtures.enter_context(import_helper.CleanImport()) + + +class ModulesFilesTests(SiteDir, unittest.TestCase): + def test_module_resources(self): + """ + A module can have resources found adjacent to the module. + """ + spec = { + 'mod.py': '', + 'res.txt': 'resources are the best', + } + _path.build(spec, self.site_dir) + import mod + + actual = resources.files(mod).joinpath('res.txt').read_text() + assert actual == spec['res.txt'] + + +class ImplicitContextFilesTests(SiteDir, unittest.TestCase): + def test_implicit_files(self): + """ + Without any parameter, files() will infer the location as the caller. + """ + spec = { + 'somepkg': { + '__init__.py': textwrap.dedent( + """ + import importlib.resources as res + val = res.files().joinpath('res.txt').read_text() + """ + ), + 'res.txt': 'resources are the best', + }, + } + _path.build(spec, self.site_dir) + assert importlib.import_module('somepkg').val == 'resources are the best' + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_importlib/resources/util.py b/Lib/test/test_importlib/resources/util.py index eb2291f15de1..1e72b91ff6b3 100644 --- a/Lib/test/test_importlib/resources/util.py +++ b/Lib/test/test_importlib/resources/util.py @@ -3,7 +3,7 @@ import io import sys import types -from pathlib import Path, PurePath +import pathlib from . import data01 from . import zipdata01 @@ -94,7 +94,7 @@ def test_string_path(self): def test_pathlib_path(self): # Passing in a pathlib.PurePath object for the path should succeed. - path = PurePath('utf-8.file') + path = pathlib.PurePath('utf-8.file') self.execute(data01, path) def test_importing_module_as_side_effect(self): @@ -102,17 +102,6 @@ def test_importing_module_as_side_effect(self): del sys.modules[data01.__name__] self.execute(data01.__name__, 'utf-8.file') - def test_non_package_by_name(self): - # The anchor package cannot be a module. - with self.assertRaises(TypeError): - self.execute(__name__, 'utf-8.file') - - def test_non_package_by_package(self): - # The anchor package cannot be a module. - with self.assertRaises(TypeError): - module = sys.modules['test.test_importlib.resources.util'] - self.execute(module, 'utf-8.file') - def test_missing_path(self): # Attempting to open or read or request the path for a # non-existent path should succeed if open_resource @@ -144,7 +133,7 @@ class ZipSetupBase: @classmethod def setUpClass(cls): - data_path = Path(cls.ZIP_MODULE.__file__) + data_path = pathlib.Path(cls.ZIP_MODULE.__file__) data_dir = data_path.parent cls._zip_path = str(data_dir / 'ziptestdata.zip') sys.path.append(cls._zip_path) diff --git a/Misc/NEWS.d/next/Library/2022-12-29-11-45-22.gh-issue-97930.hrtmJe.rst b/Misc/NEWS.d/next/Library/2022-12-29-11-45-22.gh-issue-97930.hrtmJe.rst new file mode 100644 index 000000000000..1fe4a9d6b777 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-29-11-45-22.gh-issue-97930.hrtmJe.rst @@ -0,0 +1,6 @@ +``importlib.resources.files`` now accepts a module as an anchor instead of +only accepting packages. If a module is passed, resources are resolved +adjacent to that module (in the same package or at the package root). The +parameter was renamed from ``package`` to ``anchor`` with a compatibility +shim for those passing by keyword. Additionally, the new ``anchor`` +parameter is now optional and will default to the caller's module. From webhook-mailer at python.org Sun Jan 1 16:49:16 2023 From: webhook-mailer at python.org (hugovk) Date: Sun, 01 Jan 2023 21:49:16 -0000 Subject: [Python-checkins] build(deps): bump actions/stale from 6 to 7 (#100656) Message-ID: https://github.com/python/cpython/commit/2366b27565ab57f053b4a551ade0e71796a1896c commit: 2366b27565ab57f053b4a551ade0e71796a1896c branch: main author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> committer: hugovk date: 2023-01-01T23:49:10+02:00 summary: build(deps): bump actions/stale from 6 to 7 (#100656) files: M .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 1c6f1641c885..07dbcfe31d65 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -15,7 +15,7 @@ jobs: steps: - name: "Check PRs" - uses: actions/stale at v6 + uses: actions/stale at v7 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-pr-message: 'This PR is stale because it has been open for 30 days with no activity.' From webhook-mailer at python.org Sun Jan 1 17:26:30 2023 From: webhook-mailer at python.org (erlend-aasland) Date: Sun, 01 Jan 2023 22:26:30 -0000 Subject: [Python-checkins] gh-99953: Purge mention of numeric param style from sqlite3 docs (#100630) Message-ID: https://github.com/python/cpython/commit/b7a68ab824249ebf053b8149ebb83cd8578781c9 commit: b7a68ab824249ebf053b8149ebb83cd8578781c9 branch: main author: Erlend E. Aasland committer: erlend-aasland date: 2023-01-01T23:26:23+01:00 summary: gh-99953: Purge mention of numeric param style from sqlite3 docs (#100630) The PEP-249 numeric style has never been supported by sqlite3. files: M Doc/library/sqlite3.rst diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 3622864a4b06..0d929d1297f5 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -501,11 +501,7 @@ Module constants .. note:: - The :mod:`!sqlite3` module supports ``qmark``, ``numeric``, - and ``named`` DB-API parameter styles, - because that is what the underlying SQLite library supports. - However, the DB-API does not allow multiple values for - the ``paramstyle`` attribute. + The ``named`` DB-API parameter style is also supported. .. data:: sqlite_version From webhook-mailer at python.org Sun Jan 1 17:35:47 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 01 Jan 2023 22:35:47 -0000 Subject: [Python-checkins] gh-99953: Purge mention of numeric param style from sqlite3 docs (GH-100630) Message-ID: https://github.com/python/cpython/commit/e7a2659b4c8d4f7ac045cd5b4b813b4fde9f67c1 commit: e7a2659b4c8d4f7ac045cd5b4b813b4fde9f67c1 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-01T14:35:39-08:00 summary: gh-99953: Purge mention of numeric param style from sqlite3 docs (GH-100630) The PEP-249 numeric style has never been supported by sqlite3. (cherry picked from commit b7a68ab824249ebf053b8149ebb83cd8578781c9) Co-authored-by: Erlend E. Aasland files: M Doc/library/sqlite3.rst diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index e8db6c55c938..6d0422122a3d 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -476,11 +476,7 @@ Module constants .. note:: - The :mod:`!sqlite3` module supports ``qmark``, ``numeric``, - and ``named`` DB-API parameter styles, - because that is what the underlying SQLite library supports. - However, the DB-API does not allow multiple values for - the ``paramstyle`` attribute. + The ``named`` DB-API parameter style is also supported. .. data:: sqlite_version From webhook-mailer at python.org Sun Jan 1 17:35:51 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 01 Jan 2023 22:35:51 -0000 Subject: [Python-checkins] gh-99953: Purge mention of numeric param style from sqlite3 docs (GH-100630) Message-ID: https://github.com/python/cpython/commit/8e386deee9919a10a4604eaa54f620cd7eef022d commit: 8e386deee9919a10a4604eaa54f620cd7eef022d branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-01T14:35:46-08:00 summary: gh-99953: Purge mention of numeric param style from sqlite3 docs (GH-100630) The PEP-249 numeric style has never been supported by sqlite3. (cherry picked from commit b7a68ab824249ebf053b8149ebb83cd8578781c9) Co-authored-by: Erlend E. Aasland files: M Doc/library/sqlite3.rst diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 65fa1b613153..ec1b643dac92 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -453,11 +453,7 @@ Module constants .. note:: - The :mod:`!sqlite3` module supports ``qmark``, ``numeric``, - and ``named`` DB-API parameter styles, - because that is what the underlying SQLite library supports. - However, the DB-API does not allow multiple values for - the ``paramstyle`` attribute. + The ``named`` DB-API parameter style is also supported. .. data:: sqlite_version From webhook-mailer at python.org Sun Jan 1 22:14:34 2023 From: webhook-mailer at python.org (hauntsaninja) Date: Mon, 02 Jan 2023 03:14:34 -0000 Subject: [Python-checkins] gh-100428: Make int documentation more accurate (#100436) Message-ID: https://github.com/python/cpython/commit/edfbf56f4ca6588dfd20b53f534a4465e43c82bd commit: edfbf56f4ca6588dfd20b53f534a4465e43c82bd branch: main author: Shantanu <12621235+hauntsaninja at users.noreply.github.com> committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com> date: 2023-01-01T19:14:18-08:00 summary: gh-100428: Make int documentation more accurate (#100436) - Remove first link to lexical definition of integer literal, since it doesn't apply (differs in handling of leading zeros, base needs to be explicitly specified, unicode digits are allowed) - Better describe handling of leading zeros, unicode digits, underscores - Base 0 does not work exactly as like a code literal, since it allows Unicode digits. Link code literal to lexical definition of integer literal. files: M Doc/library/functions.rst diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 77ebdd0367c3..cc7142854b1b 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -893,17 +893,21 @@ are always available. They are listed here in alphabetical order. For floating point numbers, this truncates towards zero. If *x* is not a number or if *base* is given, then *x* must be a string, - :class:`bytes`, or :class:`bytearray` instance representing an :ref:`integer - literal ` in radix *base*. Optionally, the literal can be - preceded by ``+`` or ``-`` (with no space in between) and surrounded by - whitespace. A base-n literal consists of the digits 0 to n-1, with ``a`` - to ``z`` (or ``A`` to ``Z``) having - values 10 to 35. The default *base* is 10. The allowed values are 0 and 2--36. - Base-2, -8, and -16 literals can be optionally prefixed with ``0b``/``0B``, - ``0o``/``0O``, or ``0x``/``0X``, as with integer literals in code. Base 0 - means to interpret exactly as a code literal, so that the actual base is 2, - 8, 10, or 16, and so that ``int('010', 0)`` is not legal, while - ``int('010')`` is, as well as ``int('010', 8)``. + :class:`bytes`, or :class:`bytearray` instance representing an integer + in radix *base*. Optionally, the string can be preceded by ``+`` or ``-`` + (with no space in between), have leading zeros, be surrounded by whitespace, + and have single underscores interspersed between digits. + + A base-n integer string contains digits, each representing a value from 0 to + n-1. The values 0--9 can be represented by any Unicode decimal digit. The + values 10--35 can be represented by ``a`` to ``z`` (or ``A`` to ``Z``). The + default *base* is 10. The allowed bases are 0 and 2--36. Base-2, -8, and -16 + strings can be optionally prefixed with ``0b``/``0B``, ``0o``/``0O``, or + ``0x``/``0X``, as with integer literals in code. For base 0, the string is + interpreted in a similar way to an :ref:`integer literal in code `, + in that the actual base is 2, 8, 10, or 16 as determined by the prefix. Base + 0 also disallows leading zeros: ``int('010', 0)`` is not legal, while + ``int('010')`` and ``int('010', 8)`` are. The integer type is described in :ref:`typesnumeric`. From webhook-mailer at python.org Sun Jan 1 22:21:56 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 02 Jan 2023 03:21:56 -0000 Subject: [Python-checkins] gh-100428: Make int documentation more accurate (GH-100436) Message-ID: https://github.com/python/cpython/commit/c92c5513b3e9b9cadd3e337bb9b4141cae09ec1d commit: c92c5513b3e9b9cadd3e337bb9b4141cae09ec1d branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-01T19:21:50-08:00 summary: gh-100428: Make int documentation more accurate (GH-100436) - Remove first link to lexical definition of integer literal, since it doesn't apply (differs in handling of leading zeros, base needs to be explicitly specified, unicode digits are allowed) - Better describe handling of leading zeros, unicode digits, underscores - Base 0 does not work exactly as like a code literal, since it allows Unicode digits. Link code literal to lexical definition of integer literal. (cherry picked from commit edfbf56f4ca6588dfd20b53f534a4465e43c82bd) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> files: M Doc/library/functions.rst diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 5400feb35567..4e4c49ce3a6b 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -868,17 +868,21 @@ are always available. They are listed here in alphabetical order. For floating point numbers, this truncates towards zero. If *x* is not a number or if *base* is given, then *x* must be a string, - :class:`bytes`, or :class:`bytearray` instance representing an :ref:`integer - literal ` in radix *base*. Optionally, the literal can be - preceded by ``+`` or ``-`` (with no space in between) and surrounded by - whitespace. A base-n literal consists of the digits 0 to n-1, with ``a`` - to ``z`` (or ``A`` to ``Z``) having - values 10 to 35. The default *base* is 10. The allowed values are 0 and 2--36. - Base-2, -8, and -16 literals can be optionally prefixed with ``0b``/``0B``, - ``0o``/``0O``, or ``0x``/``0X``, as with integer literals in code. Base 0 - means to interpret exactly as a code literal, so that the actual base is 2, - 8, 10, or 16, and so that ``int('010', 0)`` is not legal, while - ``int('010')`` is, as well as ``int('010', 8)``. + :class:`bytes`, or :class:`bytearray` instance representing an integer + in radix *base*. Optionally, the string can be preceded by ``+`` or ``-`` + (with no space in between), have leading zeros, be surrounded by whitespace, + and have single underscores interspersed between digits. + + A base-n integer string contains digits, each representing a value from 0 to + n-1. The values 0--9 can be represented by any Unicode decimal digit. The + values 10--35 can be represented by ``a`` to ``z`` (or ``A`` to ``Z``). The + default *base* is 10. The allowed bases are 0 and 2--36. Base-2, -8, and -16 + strings can be optionally prefixed with ``0b``/``0B``, ``0o``/``0O``, or + ``0x``/``0X``, as with integer literals in code. For base 0, the string is + interpreted in a similar way to an :ref:`integer literal in code `, + in that the actual base is 2, 8, 10, or 16 as determined by the prefix. Base + 0 also disallows leading zeros: ``int('010', 0)`` is not legal, while + ``int('010')`` and ``int('010', 8)`` are. The integer type is described in :ref:`typesnumeric`. From webhook-mailer at python.org Sun Jan 1 22:22:04 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 02 Jan 2023 03:22:04 -0000 Subject: [Python-checkins] gh-100428: Make int documentation more accurate (GH-100436) Message-ID: https://github.com/python/cpython/commit/af136e987777a2e81b63a170a370667c48815ead commit: af136e987777a2e81b63a170a370667c48815ead branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-01T19:21:58-08:00 summary: gh-100428: Make int documentation more accurate (GH-100436) - Remove first link to lexical definition of integer literal, since it doesn't apply (differs in handling of leading zeros, base needs to be explicitly specified, unicode digits are allowed) - Better describe handling of leading zeros, unicode digits, underscores - Base 0 does not work exactly as like a code literal, since it allows Unicode digits. Link code literal to lexical definition of integer literal. (cherry picked from commit edfbf56f4ca6588dfd20b53f534a4465e43c82bd) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> files: M Doc/library/functions.rst diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index e9e3177a99c7..2afd72f3cddc 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -892,17 +892,21 @@ are always available. They are listed here in alphabetical order. For floating point numbers, this truncates towards zero. If *x* is not a number or if *base* is given, then *x* must be a string, - :class:`bytes`, or :class:`bytearray` instance representing an :ref:`integer - literal ` in radix *base*. Optionally, the literal can be - preceded by ``+`` or ``-`` (with no space in between) and surrounded by - whitespace. A base-n literal consists of the digits 0 to n-1, with ``a`` - to ``z`` (or ``A`` to ``Z``) having - values 10 to 35. The default *base* is 10. The allowed values are 0 and 2--36. - Base-2, -8, and -16 literals can be optionally prefixed with ``0b``/``0B``, - ``0o``/``0O``, or ``0x``/``0X``, as with integer literals in code. Base 0 - means to interpret exactly as a code literal, so that the actual base is 2, - 8, 10, or 16, and so that ``int('010', 0)`` is not legal, while - ``int('010')`` is, as well as ``int('010', 8)``. + :class:`bytes`, or :class:`bytearray` instance representing an integer + in radix *base*. Optionally, the string can be preceded by ``+`` or ``-`` + (with no space in between), have leading zeros, be surrounded by whitespace, + and have single underscores interspersed between digits. + + A base-n integer string contains digits, each representing a value from 0 to + n-1. The values 0--9 can be represented by any Unicode decimal digit. The + values 10--35 can be represented by ``a`` to ``z`` (or ``A`` to ``Z``). The + default *base* is 10. The allowed bases are 0 and 2--36. Base-2, -8, and -16 + strings can be optionally prefixed with ``0b``/``0B``, ``0o``/``0O``, or + ``0x``/``0X``, as with integer literals in code. For base 0, the string is + interpreted in a similar way to an :ref:`integer literal in code `, + in that the actual base is 2, 8, 10, or 16 as determined by the prefix. Base + 0 also disallows leading zeros: ``int('010', 0)`` is not legal, while + ``int('010')`` and ``int('010', 8)`` are. The integer type is described in :ref:`typesnumeric`. From webhook-mailer at python.org Mon Jan 2 06:14:01 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Mon, 02 Jan 2023 11:14:01 -0000 Subject: [Python-checkins] Fix deprecation doc for `PyEval_InitThreads` (#100667) Message-ID: https://github.com/python/cpython/commit/254ab42240e0b18caedd4d5c3f45440bdaebf157 commit: 254ab42240e0b18caedd4d5c3f45440bdaebf157 branch: main author: Alexander Shadchin committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-02T16:43:55+05:30 summary: Fix deprecation doc for `PyEval_InitThreads` (#100667) files: M Doc/c-api/init.rst diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index afb17719a77a..ad06616eeb0e 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1049,7 +1049,7 @@ code, or when embedding the Python interpreter: .. versionchanged:: 3.2 This function cannot be called before :c:func:`Py_Initialize()` anymore. - .. deprecated-removed:: 3.9 3.11 + .. deprecated:: 3.9 .. index:: module: _thread @@ -1063,7 +1063,7 @@ code, or when embedding the Python interpreter: .. versionchanged:: 3.7 The :term:`GIL` is now initialized by :c:func:`Py_Initialize()`. - .. deprecated-removed:: 3.9 3.11 + .. deprecated:: 3.9 .. c:function:: PyThreadState* PyEval_SaveThread() From webhook-mailer at python.org Mon Jan 2 06:21:25 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 02 Jan 2023 11:21:25 -0000 Subject: [Python-checkins] Fix deprecation doc for `PyEval_InitThreads` (GH-100667) Message-ID: https://github.com/python/cpython/commit/1bc0705e643f69cdfe18e79d6c2692d00cabcb86 commit: 1bc0705e643f69cdfe18e79d6c2692d00cabcb86 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-02T03:21:20-08:00 summary: Fix deprecation doc for `PyEval_InitThreads` (GH-100667) (cherry picked from commit 254ab42240e0b18caedd4d5c3f45440bdaebf157) Co-authored-by: Alexander Shadchin files: M Doc/c-api/init.rst diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 55c05096ae91..a59667c4a95b 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -940,7 +940,7 @@ code, or when embedding the Python interpreter: .. versionchanged:: 3.2 This function cannot be called before :c:func:`Py_Initialize()` anymore. - .. deprecated-removed:: 3.9 3.11 + .. deprecated:: 3.9 .. index:: module: _thread @@ -954,7 +954,7 @@ code, or when embedding the Python interpreter: .. versionchanged:: 3.7 The :term:`GIL` is now initialized by :c:func:`Py_Initialize()`. - .. deprecated-removed:: 3.9 3.11 + .. deprecated:: 3.9 .. c:function:: PyThreadState* PyEval_SaveThread() From webhook-mailer at python.org Mon Jan 2 06:49:12 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 02 Jan 2023 11:49:12 -0000 Subject: [Python-checkins] fix `grammer` -> `grammar` typo in expressions.rst (GH-100683) Message-ID: https://github.com/python/cpython/commit/7feb6d2f85d69fbabfc0598d8947124883167f12 commit: 7feb6d2f85d69fbabfc0598d8947124883167f12 branch: main author: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-02T03:49:06-08:00 summary: fix `grammer` -> `grammar` typo in expressions.rst (GH-100683) Automerge-Triggered-By: GH:AlexWaygood files: M Doc/reference/expressions.rst diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 6692e50c4ff4..1e4a13fbd6a3 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -455,7 +455,7 @@ function. The execution starts when one of the generator's methods is called. At that time, the execution proceeds to the first yield expression, where it is suspended again, returning the value of :token:`~python-grammar:expression_list` to the generator's caller, -or ``None`` if :token:`~python-grammer:expression_list` is omitted. +or ``None`` if :token:`~python-grammar:expression_list` is omitted. By suspended, we mean that all local state is retained, including the current bindings of local variables, the instruction pointer, the internal evaluation stack, and the state of any exception handling. From webhook-mailer at python.org Mon Jan 2 06:57:28 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 02 Jan 2023 11:57:28 -0000 Subject: [Python-checkins] fix `grammer` -> `grammar` typo in expressions.rst (GH-100683) Message-ID: https://github.com/python/cpython/commit/a63949374bbccc2b57eb62dd559a3f835956f901 commit: a63949374bbccc2b57eb62dd559a3f835956f901 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-02T03:57:22-08:00 summary: fix `grammer` -> `grammar` typo in expressions.rst (GH-100683) (cherry picked from commit 7feb6d2f85d69fbabfc0598d8947124883167f12) Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> Automerge-Triggered-By: GH:AlexWaygood files: M Doc/reference/expressions.rst diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 21cca5bfc5d5..447fbc2a88f8 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -450,7 +450,7 @@ function. The execution starts when one of the generator's methods is called. At that time, the execution proceeds to the first yield expression, where it is suspended again, returning the value of :token:`~python-grammar:expression_list` to the generator's caller, -or ``None`` if :token:`~python-grammer:expression_list` is omitted. +or ``None`` if :token:`~python-grammar:expression_list` is omitted. By suspended, we mean that all local state is retained, including the current bindings of local variables, the instruction pointer, the internal evaluation stack, and the state of any exception handling. From webhook-mailer at python.org Mon Jan 2 06:57:58 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 02 Jan 2023 11:57:58 -0000 Subject: [Python-checkins] fix `grammer` -> `grammar` typo in expressions.rst (GH-100683) Message-ID: https://github.com/python/cpython/commit/b99ac1dbc081e4f2d2e68906e9c7c535e628611a commit: b99ac1dbc081e4f2d2e68906e9c7c535e628611a branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-02T03:57:51-08:00 summary: fix `grammer` -> `grammar` typo in expressions.rst (GH-100683) (cherry picked from commit 7feb6d2f85d69fbabfc0598d8947124883167f12) Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> Automerge-Triggered-By: GH:AlexWaygood files: M Doc/reference/expressions.rst diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 655d19969a58..ea9db1824c70 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -455,7 +455,7 @@ function. The execution starts when one of the generator's methods is called. At that time, the execution proceeds to the first yield expression, where it is suspended again, returning the value of :token:`~python-grammar:expression_list` to the generator's caller, -or ``None`` if :token:`~python-grammer:expression_list` is omitted. +or ``None`` if :token:`~python-grammar:expression_list` is omitted. By suspended, we mean that all local state is retained, including the current bindings of local variables, the instruction pointer, the internal evaluation stack, and the state of any exception handling. From webhook-mailer at python.org Mon Jan 2 07:18:04 2023 From: webhook-mailer at python.org (vsajip) Date: Mon, 02 Jan 2023 12:18:04 -0000 Subject: [Python-checkins] gh-100676: Improve description for venv --upgrade-deps (GH-100678) Message-ID: https://github.com/python/cpython/commit/9dee9731663d670652586c929190f227ab56bd8f commit: 9dee9731663d670652586c929190f227ab56bd8f branch: main author: Rupa Lahiri <35212670+rblcoder at users.noreply.github.com> committer: vsajip date: 2023-01-02T12:17:58Z summary: gh-100676: Improve description for venv --upgrade-deps (GH-100678) files: M Lib/venv/__init__.py diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index 978c98336f2b..2f87c62ccba8 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -523,7 +523,7 @@ def main(args=None): 'this environment.') parser.add_argument('--upgrade-deps', default=False, action='store_true', dest='upgrade_deps', - help=f'Upgrade core dependencies: {" ".join(CORE_VENV_DEPS)} ' + help=f'Upgrade core dependencies: {", ".join(CORE_VENV_DEPS)} ' 'to the latest version in PyPI') options = parser.parse_args(args) if options.upgrade and options.clear: From webhook-mailer at python.org Mon Jan 2 16:11:59 2023 From: webhook-mailer at python.org (mdickinson) Date: Mon, 02 Jan 2023 21:11:59 -0000 Subject: [Python-checkins] gh-100637: Fix int and bool __sizeof__ calculation to include the 1 element ob_digit array for 0 and False (#100663) Message-ID: https://github.com/python/cpython/commit/d7e7f79ca7c2029e46a06d21a7a5abea631b5d13 commit: d7e7f79ca7c2029e46a06d21a7a5abea631b5d13 branch: main author: Ionite committer: mdickinson date: 2023-01-02T21:11:49Z summary: gh-100637: Fix int and bool __sizeof__ calculation to include the 1 element ob_digit array for 0 and False (#100663) Fixes behaviour where int (and subtypes like bool) __sizeof__ under-reports true size as it did not take into account the size 1 `ob_digit` array for the zero int. Co-authored-by: Mark Dickinson files: A Misc/NEWS.d/next/Core and Builtins/2023-01-01-15-59-48.gh-issue-100637.M2n6Kg.rst M Lib/test/test_sys.py M Objects/longobject.c diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 17a5026e2571..232b79971dc2 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1322,6 +1322,7 @@ def test_objecttypes(self): check = self.check_sizeof # bool check(True, vsize('') + self.longdigit) + check(False, vsize('') + self.longdigit) # buffer # XXX # builtin_function_or_method @@ -1459,7 +1460,7 @@ def get_gen(): yield 1 # listreverseiterator (list) check(reversed([]), size('nP')) # int - check(0, vsize('')) + check(0, vsize('') + self.longdigit) check(1, vsize('') + self.longdigit) check(-1, vsize('') + self.longdigit) PyLong_BASE = 2**sys.int_info.bits_per_digit diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-01-15-59-48.gh-issue-100637.M2n6Kg.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-01-15-59-48.gh-issue-100637.M2n6Kg.rst new file mode 100644 index 000000000000..164f6f5f2f44 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-01-15-59-48.gh-issue-100637.M2n6Kg.rst @@ -0,0 +1 @@ +Fix :func:`int.__sizeof__` calculation to include the 1 element ob_digit array for 0 and False. diff --git a/Objects/longobject.c b/Objects/longobject.c index 0df3b9a9d564..1db4ca418e06 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5879,7 +5879,10 @@ int___sizeof___impl(PyObject *self) { Py_ssize_t res; - res = offsetof(PyLongObject, ob_digit) + Py_ABS(Py_SIZE(self))*sizeof(digit); + res = offsetof(PyLongObject, ob_digit) + /* using Py_MAX(..., 1) because we always allocate space for at least + one digit, even though the integer zero has a Py_SIZE of 0 */ + + Py_MAX(Py_ABS(Py_SIZE(self)), 1)*sizeof(digit); return res; } From webhook-mailer at python.org Mon Jan 2 16:41:25 2023 From: webhook-mailer at python.org (JelleZijlstra) Date: Mon, 02 Jan 2023 21:41:25 -0000 Subject: [Python-checkins] gh-89727: Improve os.walk complexity (#100671) Message-ID: https://github.com/python/cpython/commit/73097d91a64620ae7f620705864b84234d85cc82 commit: 73097d91a64620ae7f620705864b84234d85cc82 branch: main author: Stanislav Zmiev committer: JelleZijlstra date: 2023-01-02T13:41:19-08:00 summary: gh-89727: Improve os.walk complexity (#100671) files: A Misc/NEWS.d/next/Library/2023-01-01-23-57-00.gh-issue-89727.ojedHN.rst M Lib/os.py diff --git a/Lib/os.py b/Lib/os.py index 73a5442ee8b8..598c9e502301 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -341,11 +341,11 @@ def walk(top, topdown=True, onerror=None, followlinks=False): """ sys.audit("os.walk", top, topdown, onerror, followlinks) - stack = [(False, fspath(top))] + stack = [fspath(top)] islink, join = path.islink, path.join while stack: - must_yield, top = stack.pop() - if must_yield: + top = stack.pop() + if isinstance(top, tuple): yield top continue @@ -422,13 +422,13 @@ def walk(top, topdown=True, onerror=None, followlinks=False): # the caller can replace the directory entry during the "yield" # above. if followlinks or not islink(new_path): - stack.append((False, new_path)) + stack.append(new_path) else: # Yield after sub-directory traversal if going bottom up - stack.append((True, (top, dirs, nondirs))) + stack.append((top, dirs, nondirs)) # Traverse into sub-directories for new_path in reversed(walk_dirs): - stack.append((False, new_path)) + stack.append(new_path) __all__.append("walk") diff --git a/Misc/NEWS.d/next/Library/2023-01-01-23-57-00.gh-issue-89727.ojedHN.rst b/Misc/NEWS.d/next/Library/2023-01-01-23-57-00.gh-issue-89727.ojedHN.rst new file mode 100644 index 000000000000..38c0d5c4d5fb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-01-23-57-00.gh-issue-89727.ojedHN.rst @@ -0,0 +1 @@ +Simplify and optimize :func:`os.walk` by using :func:`isinstance` checks to check the top of the stack. From webhook-mailer at python.org Tue Jan 3 03:17:06 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Tue, 03 Jan 2023 08:17:06 -0000 Subject: [Python-checkins] gh-81611: Improve `range` paragraph in 8.3 of language reference (#98353) Message-ID: https://github.com/python/cpython/commit/8b1f1251215651c4ef988622345c5cb134e54d69 commit: 8b1f1251215651c4ef988622345c5cb134e54d69 branch: main author: 4l4k4z4m committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-03T13:46:51+05:30 summary: gh-81611: Improve `range` paragraph in 8.3 of language reference (#98353) Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> Co-authored-by: Terry Jan Reedy files: M Doc/reference/compound_stmts.rst diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index fe9dda933c80..d5cb2899c231 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -192,9 +192,8 @@ those made in the suite of the for-loop:: Names in the target list are not deleted when the loop is finished, but if the sequence is empty, they will not have been assigned to at all by the loop. Hint: -the built-in function :func:`range` returns an iterator of integers suitable to -emulate the effect of Pascal's ``for i := a to b do``; e.g., ``list(range(3))`` -returns the list ``[0, 1, 2]``. +the built-in type :func:`range` represents immutable arithmetic sequences of integers. +For instance, iterating ``range(3)`` successively yields 0, 1, and then 2. .. versionchanged:: 3.11 Starred elements are now allowed in the expression list. From webhook-mailer at python.org Tue Jan 3 09:57:26 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Tue, 03 Jan 2023 14:57:26 -0000 Subject: [Python-checkins] gh-89419: gdb: fix bug causing `AttributeError` in py-locals when no frame is available (#100611) Message-ID: https://github.com/python/cpython/commit/85869498331f7020e18bb243c89cd694f674b911 commit: 85869498331f7020e18bb243c89cd694f674b911 branch: main author: Eli Schwartz committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-03T20:26:57+05:30 summary: gh-89419: gdb: fix bug causing `AttributeError` in py-locals when no frame is available (#100611) files: A Misc/NEWS.d/next/Tools-Demos/2022-12-29-19-22-11.bpo-45256.a0ee_H.rst M Tools/gdb/libpython.py diff --git a/Misc/NEWS.d/next/Tools-Demos/2022-12-29-19-22-11.bpo-45256.a0ee_H.rst b/Misc/NEWS.d/next/Tools-Demos/2022-12-29-19-22-11.bpo-45256.a0ee_H.rst new file mode 100644 index 000000000000..9c1aa5762583 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2022-12-29-19-22-11.bpo-45256.a0ee_H.rst @@ -0,0 +1 @@ +Fix a bug that caused an :exc:`AttributeError` to be raised in ``python-gdb.py`` when ``py-locals`` is used without a frame. diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index c003c1ab4a23..6453dff95df4 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -2108,6 +2108,7 @@ def invoke(self, args, from_tty): while True: if not pyop_frame: print(UNABLE_READ_INFO_PYTHON_FRAME) + break if pyop_frame.is_shim(): break From webhook-mailer at python.org Tue Jan 3 10:47:33 2023 From: webhook-mailer at python.org (iritkatriel) Date: Tue, 03 Jan 2023 15:47:33 -0000 Subject: [Python-checkins] gh-95882: fix regression in the traceback of exceptions propagated from inside a contextlib context manager (#95883) Message-ID: https://github.com/python/cpython/commit/b3722ca058f6a6d6505cf2ea9ffabaf7fb6b6e19 commit: b3722ca058f6a6d6505cf2ea9ffabaf7fb6b6e19 branch: main author: Thomas Grainger committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-03T15:47:13Z summary: gh-95882: fix regression in the traceback of exceptions propagated from inside a contextlib context manager (#95883) files: A Misc/NEWS.d/next/Library/2022-08-11-10-02-19.gh-issue-95882.FsUr72.rst M Lib/contextlib.py M Lib/test/test_contextlib.py M Lib/test/test_contextlib_async.py M Misc/ACKS diff --git a/Lib/contextlib.py b/Lib/contextlib.py index d5822219b3e2..30d9ac25b2bb 100644 --- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -173,7 +173,7 @@ def __exit__(self, typ, value, traceback): isinstance(value, StopIteration) and exc.__cause__ is value ): - exc.__traceback__ = traceback + value.__traceback__ = traceback return False raise except BaseException as exc: @@ -228,6 +228,7 @@ async def __aexit__(self, typ, value, traceback): except RuntimeError as exc: # Don't re-raise the passed in exception. (issue27122) if exc is value: + exc.__traceback__ = traceback return False # Avoid suppressing if a Stop(Async)Iteration exception # was passed to athrow() and later wrapped into a RuntimeError @@ -239,6 +240,7 @@ async def __aexit__(self, typ, value, traceback): isinstance(value, (StopIteration, StopAsyncIteration)) and exc.__cause__ is value ): + value.__traceback__ = traceback return False raise except BaseException as exc: @@ -250,6 +252,7 @@ async def __aexit__(self, typ, value, traceback): # and the __exit__() protocol. if exc is not value: raise + exc.__traceback__ = traceback return False raise RuntimeError("generator didn't stop after athrow()") diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index 31f5c74572b6..ec06785b5667 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -104,15 +104,39 @@ def f(): self.assertEqual(frames[0].line, '1/0') # Repeat with RuntimeError (which goes through a different code path) + class RuntimeErrorSubclass(RuntimeError): + pass + try: with f(): - raise NotImplementedError(42) - except NotImplementedError as e: + raise RuntimeErrorSubclass(42) + except RuntimeErrorSubclass as e: frames = traceback.extract_tb(e.__traceback__) self.assertEqual(len(frames), 1) self.assertEqual(frames[0].name, 'test_contextmanager_traceback') - self.assertEqual(frames[0].line, 'raise NotImplementedError(42)') + self.assertEqual(frames[0].line, 'raise RuntimeErrorSubclass(42)') + + class StopIterationSubclass(StopIteration): + pass + + for stop_exc in ( + StopIteration('spam'), + StopIterationSubclass('spam'), + ): + with self.subTest(type=type(stop_exc)): + try: + with f(): + raise stop_exc + except type(stop_exc) as e: + self.assertIs(e, stop_exc) + frames = traceback.extract_tb(e.__traceback__) + else: + self.fail(f'{stop_exc} was suppressed') + + self.assertEqual(len(frames), 1) + self.assertEqual(frames[0].name, 'test_contextmanager_traceback') + self.assertEqual(frames[0].line, 'raise stop_exc') def test_contextmanager_no_reraise(self): @contextmanager diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py index b64673d2c31e..3d43ed0fcab1 100644 --- a/Lib/test/test_contextlib_async.py +++ b/Lib/test/test_contextlib_async.py @@ -5,6 +5,7 @@ import functools from test import support import unittest +import traceback from test.test_contextlib import TestBaseExitStack @@ -125,6 +126,62 @@ async def woohoo(): raise ZeroDivisionError() self.assertEqual(state, [1, 42, 999]) + @_async_test + async def test_contextmanager_traceback(self): + @asynccontextmanager + async def f(): + yield + + try: + async with f(): + 1/0 + except ZeroDivisionError as e: + frames = traceback.extract_tb(e.__traceback__) + + self.assertEqual(len(frames), 1) + self.assertEqual(frames[0].name, 'test_contextmanager_traceback') + self.assertEqual(frames[0].line, '1/0') + + # Repeat with RuntimeError (which goes through a different code path) + class RuntimeErrorSubclass(RuntimeError): + pass + + try: + async with f(): + raise RuntimeErrorSubclass(42) + except RuntimeErrorSubclass as e: + frames = traceback.extract_tb(e.__traceback__) + + self.assertEqual(len(frames), 1) + self.assertEqual(frames[0].name, 'test_contextmanager_traceback') + self.assertEqual(frames[0].line, 'raise RuntimeErrorSubclass(42)') + + class StopIterationSubclass(StopIteration): + pass + + class StopAsyncIterationSubclass(StopAsyncIteration): + pass + + for stop_exc in ( + StopIteration('spam'), + StopAsyncIteration('ham'), + StopIterationSubclass('spam'), + StopAsyncIterationSubclass('spam') + ): + with self.subTest(type=type(stop_exc)): + try: + async with f(): + raise stop_exc + except type(stop_exc) as e: + self.assertIs(e, stop_exc) + frames = traceback.extract_tb(e.__traceback__) + else: + self.fail(f'{stop_exc} was suppressed') + + self.assertEqual(len(frames), 1) + self.assertEqual(frames[0].name, 'test_contextmanager_traceback') + self.assertEqual(frames[0].line, 'raise stop_exc') + @_async_test async def test_contextmanager_no_reraise(self): @asynccontextmanager diff --git a/Misc/ACKS b/Misc/ACKS index d50cb3c2d1ee..b4e309c6905b 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -645,6 +645,7 @@ Hans de Graaff Tim Graham Kim Gr?sman Alex Gr?nholm +Thomas Grainger Nathaniel Gray Eddy De Greef Duane Griffin diff --git a/Misc/NEWS.d/next/Library/2022-08-11-10-02-19.gh-issue-95882.FsUr72.rst b/Misc/NEWS.d/next/Library/2022-08-11-10-02-19.gh-issue-95882.FsUr72.rst new file mode 100644 index 000000000000..9cdb237d5c87 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-08-11-10-02-19.gh-issue-95882.FsUr72.rst @@ -0,0 +1 @@ +Fix a 3.11 regression in :func:`~contextlib.asynccontextmanager`, which caused it to propagate exceptions with incorrect tracebacks and fix a 3.11 regression in :func:`~contextlib.contextmanager`, which caused it to propagate exceptions with incorrect tracebacks for :exc:`StopIteration`. From webhook-mailer at python.org Tue Jan 3 11:18:52 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 03 Jan 2023 16:18:52 -0000 Subject: [Python-checkins] gh-95882: fix regression in the traceback of exceptions propagated from inside a contextlib context manager (GH-95883) Message-ID: https://github.com/python/cpython/commit/861cdefc1bccd419bd268a9f60b34bffbaff9ea2 commit: 861cdefc1bccd419bd268a9f60b34bffbaff9ea2 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-03T08:18:45-08:00 summary: gh-95882: fix regression in the traceback of exceptions propagated from inside a contextlib context manager (GH-95883) (cherry picked from commit b3722ca058f6a6d6505cf2ea9ffabaf7fb6b6e19) Co-authored-by: Thomas Grainger files: A Misc/NEWS.d/next/Library/2022-08-11-10-02-19.gh-issue-95882.FsUr72.rst M Lib/contextlib.py M Lib/test/test_contextlib.py M Lib/test/test_contextlib_async.py M Misc/ACKS diff --git a/Lib/contextlib.py b/Lib/contextlib.py index 625bb33b12d5..58e9a498878d 100644 --- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -173,7 +173,7 @@ def __exit__(self, typ, value, traceback): isinstance(value, StopIteration) and exc.__cause__ is value ): - exc.__traceback__ = traceback + value.__traceback__ = traceback return False raise except BaseException as exc: @@ -228,6 +228,7 @@ async def __aexit__(self, typ, value, traceback): except RuntimeError as exc: # Don't re-raise the passed in exception. (issue27122) if exc is value: + exc.__traceback__ = traceback return False # Avoid suppressing if a Stop(Async)Iteration exception # was passed to athrow() and later wrapped into a RuntimeError @@ -239,6 +240,7 @@ async def __aexit__(self, typ, value, traceback): isinstance(value, (StopIteration, StopAsyncIteration)) and exc.__cause__ is value ): + value.__traceback__ = traceback return False raise except BaseException as exc: @@ -250,6 +252,7 @@ async def __aexit__(self, typ, value, traceback): # and the __exit__() protocol. if exc is not value: raise + exc.__traceback__ = traceback return False raise RuntimeError("generator didn't stop after athrow()") diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index 31f5c74572b6..ec06785b5667 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -104,15 +104,39 @@ def f(): self.assertEqual(frames[0].line, '1/0') # Repeat with RuntimeError (which goes through a different code path) + class RuntimeErrorSubclass(RuntimeError): + pass + try: with f(): - raise NotImplementedError(42) - except NotImplementedError as e: + raise RuntimeErrorSubclass(42) + except RuntimeErrorSubclass as e: frames = traceback.extract_tb(e.__traceback__) self.assertEqual(len(frames), 1) self.assertEqual(frames[0].name, 'test_contextmanager_traceback') - self.assertEqual(frames[0].line, 'raise NotImplementedError(42)') + self.assertEqual(frames[0].line, 'raise RuntimeErrorSubclass(42)') + + class StopIterationSubclass(StopIteration): + pass + + for stop_exc in ( + StopIteration('spam'), + StopIterationSubclass('spam'), + ): + with self.subTest(type=type(stop_exc)): + try: + with f(): + raise stop_exc + except type(stop_exc) as e: + self.assertIs(e, stop_exc) + frames = traceback.extract_tb(e.__traceback__) + else: + self.fail(f'{stop_exc} was suppressed') + + self.assertEqual(len(frames), 1) + self.assertEqual(frames[0].name, 'test_contextmanager_traceback') + self.assertEqual(frames[0].line, 'raise stop_exc') def test_contextmanager_no_reraise(self): @contextmanager diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py index b64673d2c31e..3d43ed0fcab1 100644 --- a/Lib/test/test_contextlib_async.py +++ b/Lib/test/test_contextlib_async.py @@ -5,6 +5,7 @@ import functools from test import support import unittest +import traceback from test.test_contextlib import TestBaseExitStack @@ -125,6 +126,62 @@ async def woohoo(): raise ZeroDivisionError() self.assertEqual(state, [1, 42, 999]) + @_async_test + async def test_contextmanager_traceback(self): + @asynccontextmanager + async def f(): + yield + + try: + async with f(): + 1/0 + except ZeroDivisionError as e: + frames = traceback.extract_tb(e.__traceback__) + + self.assertEqual(len(frames), 1) + self.assertEqual(frames[0].name, 'test_contextmanager_traceback') + self.assertEqual(frames[0].line, '1/0') + + # Repeat with RuntimeError (which goes through a different code path) + class RuntimeErrorSubclass(RuntimeError): + pass + + try: + async with f(): + raise RuntimeErrorSubclass(42) + except RuntimeErrorSubclass as e: + frames = traceback.extract_tb(e.__traceback__) + + self.assertEqual(len(frames), 1) + self.assertEqual(frames[0].name, 'test_contextmanager_traceback') + self.assertEqual(frames[0].line, 'raise RuntimeErrorSubclass(42)') + + class StopIterationSubclass(StopIteration): + pass + + class StopAsyncIterationSubclass(StopAsyncIteration): + pass + + for stop_exc in ( + StopIteration('spam'), + StopAsyncIteration('ham'), + StopIterationSubclass('spam'), + StopAsyncIterationSubclass('spam') + ): + with self.subTest(type=type(stop_exc)): + try: + async with f(): + raise stop_exc + except type(stop_exc) as e: + self.assertIs(e, stop_exc) + frames = traceback.extract_tb(e.__traceback__) + else: + self.fail(f'{stop_exc} was suppressed') + + self.assertEqual(len(frames), 1) + self.assertEqual(frames[0].name, 'test_contextmanager_traceback') + self.assertEqual(frames[0].line, 'raise stop_exc') + @_async_test async def test_contextmanager_no_reraise(self): @asynccontextmanager diff --git a/Misc/ACKS b/Misc/ACKS index 58b29c19400e..dc47e894b764 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -641,6 +641,7 @@ Hans de Graaff Tim Graham Kim Gr?sman Alex Gr?nholm +Thomas Grainger Nathaniel Gray Eddy De Greef Duane Griffin diff --git a/Misc/NEWS.d/next/Library/2022-08-11-10-02-19.gh-issue-95882.FsUr72.rst b/Misc/NEWS.d/next/Library/2022-08-11-10-02-19.gh-issue-95882.FsUr72.rst new file mode 100644 index 000000000000..9cdb237d5c87 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-08-11-10-02-19.gh-issue-95882.FsUr72.rst @@ -0,0 +1 @@ +Fix a 3.11 regression in :func:`~contextlib.asynccontextmanager`, which caused it to propagate exceptions with incorrect tracebacks and fix a 3.11 regression in :func:`~contextlib.contextmanager`, which caused it to propagate exceptions with incorrect tracebacks for :exc:`StopIteration`. From webhook-mailer at python.org Tue Jan 3 13:00:02 2023 From: webhook-mailer at python.org (mdickinson) Date: Tue, 03 Jan 2023 18:00:02 -0000 Subject: [Python-checkins] [3.11] gh-100637: Fix int and bool __sizeof__ calculation to include the 1 element ob_digit array for 0 and False (GH-100663) (#100717) Message-ID: https://github.com/python/cpython/commit/50409a29685ca600d1893920515a094954e83f56 commit: 50409a29685ca600d1893920515a094954e83f56 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: mdickinson date: 2023-01-03T17:59:40Z summary: [3.11] gh-100637: Fix int and bool __sizeof__ calculation to include the 1 element ob_digit array for 0 and False (GH-100663) (#100717) gh-100637: Fix int and bool __sizeof__ calculation to include the 1 element ob_digit array for 0 and False (GH-100663) Fixes behaviour where int (and subtypes like bool) __sizeof__ under-reports true size as it did not take into account the size 1 `ob_digit` array for the zero int. (cherry picked from commit d7e7f79ca7c2029e46a06d21a7a5abea631b5d13) Co-authored-by: Ionite Co-authored-by: Mark Dickinson files: A Misc/NEWS.d/next/Core and Builtins/2023-01-01-15-59-48.gh-issue-100637.M2n6Kg.rst M Lib/test/test_sys.py M Objects/longobject.c diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 461e312cb97c..6f56c9ef97e7 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1322,6 +1322,7 @@ def test_objecttypes(self): check = self.check_sizeof # bool check(True, vsize('') + self.longdigit) + check(False, vsize('') + self.longdigit) # buffer # XXX # builtin_function_or_method @@ -1459,7 +1460,7 @@ def get_gen(): yield 1 # listreverseiterator (list) check(reversed([]), size('nP')) # int - check(0, vsize('')) + check(0, vsize('') + self.longdigit) check(1, vsize('') + self.longdigit) check(-1, vsize('') + self.longdigit) PyLong_BASE = 2**sys.int_info.bits_per_digit diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-01-15-59-48.gh-issue-100637.M2n6Kg.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-01-15-59-48.gh-issue-100637.M2n6Kg.rst new file mode 100644 index 000000000000..164f6f5f2f44 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-01-15-59-48.gh-issue-100637.M2n6Kg.rst @@ -0,0 +1 @@ +Fix :func:`int.__sizeof__` calculation to include the 1 element ob_digit array for 0 and False. diff --git a/Objects/longobject.c b/Objects/longobject.c index 1b289d5468bd..c8dd5761a3ce 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5661,7 +5661,10 @@ int___sizeof___impl(PyObject *self) { Py_ssize_t res; - res = offsetof(PyLongObject, ob_digit) + Py_ABS(Py_SIZE(self))*sizeof(digit); + res = offsetof(PyLongObject, ob_digit) + /* using Py_MAX(..., 1) because we always allocate space for at least + one digit, even though the integer zero has a Py_SIZE of 0 */ + + Py_MAX(Py_ABS(Py_SIZE(self)), 1)*sizeof(digit); return res; } From webhook-mailer at python.org Tue Jan 3 13:50:35 2023 From: webhook-mailer at python.org (gvanrossum) Date: Tue, 03 Jan 2023 18:50:35 -0000 Subject: [Python-checkins] gh-100146: Steal references from stack when building a list (#100147) Message-ID: https://github.com/python/cpython/commit/e6d44407827490a5345e8393fbdc78fd6c14f5b1 commit: e6d44407827490a5345e8393fbdc78fd6c14f5b1 branch: main author: L. A. F. Pereira committer: gvanrossum date: 2023-01-03T10:49:49-08:00 summary: gh-100146: Steal references from stack when building a list (#100147) When executing the BUILD_LIST opcode, steal the references from the stack, in a manner similar to the BUILD_TUPLE opcode. Implement this by offloading the logic to a new private API, _PyList_FromArraySteal(), that works similarly to _PyTuple_FromArraySteal(). This way, instead of performing multiple stack pointer adjustments while the list is being initialized, the stack is adjusted only once and a fast memory copy operation is performed in one fell swoop. files: A Misc/NEWS.d/next/Core and Builtins/2022-12-09-13-18-42.gh-issue-100146.xLVKg0.rst M Include/internal/pycore_list.h M Objects/listobject.c M Python/bytecodes.c M Python/generated_cases.c.h diff --git a/Include/internal/pycore_list.h b/Include/internal/pycore_list.h index 628267cc8a96..2fcbe12cd655 100644 --- a/Include/internal/pycore_list.h +++ b/Include/internal/pycore_list.h @@ -75,6 +75,8 @@ typedef struct { PyListObject *it_seq; /* Set to NULL when iterator is exhausted */ } _PyListIterObject; +extern PyObject *_PyList_FromArraySteal(PyObject *const *src, Py_ssize_t n); + #ifdef __cplusplus } #endif diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-09-13-18-42.gh-issue-100146.xLVKg0.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-09-13-18-42.gh-issue-100146.xLVKg0.rst new file mode 100644 index 000000000000..8023a366a0e6 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-12-09-13-18-42.gh-issue-100146.xLVKg0.rst @@ -0,0 +1,4 @@ +Improve ``BUILD_LIST`` opcode so that it works similarly to the +``BUILD_TUPLE`` opcode, by stealing references from the stack rather than +repeatedly using stack operations to set list elements. Implementation +details are in a new private API :c:func:`_PyList_FromArraySteal`. diff --git a/Objects/listobject.c b/Objects/listobject.c index b093f88a35fc..6629775604b6 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -2565,6 +2565,27 @@ PyList_AsTuple(PyObject *v) return _PyTuple_FromArray(((PyListObject *)v)->ob_item, Py_SIZE(v)); } +PyObject * +_PyList_FromArraySteal(PyObject *const *src, Py_ssize_t n) +{ + if (n == 0) { + return PyList_New(0); + } + + PyListObject *list = (PyListObject *)PyList_New(n); + if (list == NULL) { + for (Py_ssize_t i = 0; i < n; i++) { + Py_DECREF(src[i]); + } + return NULL; + } + + PyObject **dst = list->ob_item; + memcpy(dst, src, n * sizeof(PyObject *)); + + return (PyObject *)list; +} + /*[clinic input] list.index diff --git a/Python/bytecodes.c b/Python/bytecodes.c index e1c73ab6b32f..839fac3fcd11 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1390,13 +1390,10 @@ dummy_func( // stack effect: (__array[oparg] -- __0) inst(BUILD_LIST) { - PyObject *list = PyList_New(oparg); + STACK_SHRINK(oparg); + PyObject *list = _PyList_FromArraySteal(stack_pointer, oparg); if (list == NULL) goto error; - while (--oparg >= 0) { - PyObject *item = POP(); - PyList_SET_ITEM(list, oparg, item); - } PUSH(list); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 1179bdfc696c..ed89e90b7c56 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1608,13 +1608,10 @@ } TARGET(BUILD_LIST) { - PyObject *list = PyList_New(oparg); + STACK_SHRINK(oparg); + PyObject *list = _PyList_FromArraySteal(stack_pointer, oparg); if (list == NULL) goto error; - while (--oparg >= 0) { - PyObject *item = POP(); - PyList_SET_ITEM(list, oparg, item); - } PUSH(list); DISPATCH(); } From webhook-mailer at python.org Tue Jan 3 17:00:32 2023 From: webhook-mailer at python.org (zware) Date: Tue, 03 Jan 2023 22:00:32 -0000 Subject: [Python-checkins] gh-100700: Remove Date and Release fields in past whatsnews (GH-100728) Message-ID: https://github.com/python/cpython/commit/e196d8c10a669e1996140a0e594489aa9421a38b commit: e196d8c10a669e1996140a0e594489aa9421a38b branch: main author: Zachary Ware committer: zware date: 2023-01-03T16:00:26-06:00 summary: gh-100700: Remove Date and Release fields in past whatsnews (GH-100728) files: M Doc/whatsnew/3.10.rst M Doc/whatsnew/3.11.rst M Doc/whatsnew/3.9.rst diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 1c21caf355f0..8296fb040d67 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -2,8 +2,6 @@ What's New In Python 3.10 **************************** -:Release: |release| -:Date: |today| :Editor: Pablo Galindo Salgado .. Rules for maintenance: diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 6f283f1d8002..bffb8d03aa7c 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -2,8 +2,7 @@ What's New In Python 3.11 **************************** -:Release: |release| -:Date: |today| +:Editor: Pablo Galindo Salgado .. Rules for maintenance: diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 624e71f9254c..e974ee3a3f73 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -2,8 +2,6 @@ What's New In Python 3.9 **************************** -:Release: |release| -:Date: |today| :Editor: ?ukasz Langa .. Rules for maintenance: From webhook-mailer at python.org Tue Jan 3 17:08:57 2023 From: webhook-mailer at python.org (ethanfurman) Date: Tue, 03 Jan 2023 22:08:57 -0000 Subject: [Python-checkins] [Enum] docs: replace 'last value' by 'highest value' for Flag auto (GH-100709) Message-ID: https://github.com/python/cpython/commit/64ed609c532a12b27f67a1e12e9e02f136ee3a94 commit: 64ed609c532a12b27f67a1e12e9e02f136ee3a94 branch: main author: Christophe Nanteuil <35002064+christopheNan at users.noreply.github.com> committer: ethanfurman date: 2023-01-03T14:08:50-08:00 summary: [Enum] docs: replace 'last value' by 'highest value' for Flag auto (GH-100709) files: M Doc/library/enum.rst diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 25a6e1f0b616..47d9e39305d0 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -807,9 +807,9 @@ Utilities and Decorators call an *Enum*'s :meth:`_generate_next_value_` to get an appropriate value. For *Enum* and *IntEnum* that appropriate value will be the last value plus one; for *Flag* and *IntFlag* it will be the first power-of-two greater - than the last value; for *StrEnum* it will be the lower-cased version of the - member's name. Care must be taken if mixing *auto()* with manually specified - values. + than the highest value; for *StrEnum* it will be the lower-cased version of + the member's name. Care must be taken if mixing *auto()* with manually + specified values. *auto* instances are only resolved when at the top level of an assignment: From webhook-mailer at python.org Tue Jan 3 17:11:04 2023 From: webhook-mailer at python.org (zware) Date: Tue, 03 Jan 2023 22:11:04 -0000 Subject: [Python-checkins] gh-100700: Remove Date and Release fields in past whatsnews (GH-100729) Message-ID: https://github.com/python/cpython/commit/f49006e6a2a538dcce3fcc67980fccd811d60327 commit: f49006e6a2a538dcce3fcc67980fccd811d60327 branch: 3.11 author: Zachary Ware committer: zware date: 2023-01-03T16:10:57-06:00 summary: gh-100700: Remove Date and Release fields in past whatsnews (GH-100729) files: M Doc/whatsnew/3.10.rst M Doc/whatsnew/3.9.rst diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index d0b436664ac3..33fdc6f0604c 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -2,8 +2,6 @@ What's New In Python 3.10 **************************** -:Release: |release| -:Date: |today| :Editor: Pablo Galindo Salgado .. Rules for maintenance: diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 624e71f9254c..e974ee3a3f73 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -2,8 +2,6 @@ What's New In Python 3.9 **************************** -:Release: |release| -:Date: |today| :Editor: ?ukasz Langa .. Rules for maintenance: From webhook-mailer at python.org Tue Jan 3 17:11:24 2023 From: webhook-mailer at python.org (zware) Date: Tue, 03 Jan 2023 22:11:24 -0000 Subject: [Python-checkins] gh-100700: Remove Date and Release fields in whatsnew/3.9 (GH-100730) Message-ID: https://github.com/python/cpython/commit/242836c3f28706e4c940f19f7583fc85936fdbbb commit: 242836c3f28706e4c940f19f7583fc85936fdbbb branch: 3.10 author: Zachary Ware committer: zware date: 2023-01-03T16:11:18-06:00 summary: gh-100700: Remove Date and Release fields in whatsnew/3.9 (GH-100730) files: M Doc/whatsnew/3.9.rst diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 34fd1c116bc3..8ed13d1b2e92 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -2,8 +2,6 @@ What's New In Python 3.9 **************************** -:Release: |release| -:Date: |today| :Editor: ?ukasz Langa .. Rules for maintenance: From webhook-mailer at python.org Tue Jan 3 17:24:25 2023 From: webhook-mailer at python.org (iritkatriel) Date: Tue, 03 Jan 2023 22:24:25 -0000 Subject: [Python-checkins] [3.10] gh-95882: Add tests for traceback from contextlib context managers (GH-95883) (#100715) Message-ID: https://github.com/python/cpython/commit/ff9ac5807172499d29757fc27dee4378164db3be commit: ff9ac5807172499d29757fc27dee4378164db3be branch: 3.10 author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-03T22:24:19Z summary: [3.10] gh-95882: Add tests for traceback from contextlib context managers (GH-95883) (#100715) files: M Lib/test/test_contextlib.py M Lib/test/test_contextlib_async.py diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index 68bd45d97232..bcedf17fc806 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -87,6 +87,56 @@ def woohoo(): raise ZeroDivisionError() self.assertEqual(state, [1, 42, 999]) + def test_contextmanager_traceback(self): + @contextmanager + def f(): + yield + + try: + with f(): + 1/0 + except ZeroDivisionError as e: + frames = traceback.extract_tb(e.__traceback__) + + self.assertEqual(len(frames), 1) + self.assertEqual(frames[0].name, 'test_contextmanager_traceback') + self.assertEqual(frames[0].line, '1/0') + + # Repeat with RuntimeError (which goes through a different code path) + class RuntimeErrorSubclass(RuntimeError): + pass + + try: + with f(): + raise RuntimeErrorSubclass(42) + except RuntimeErrorSubclass as e: + frames = traceback.extract_tb(e.__traceback__) + + self.assertEqual(len(frames), 1) + self.assertEqual(frames[0].name, 'test_contextmanager_traceback') + self.assertEqual(frames[0].line, 'raise RuntimeErrorSubclass(42)') + + class StopIterationSubclass(StopIteration): + pass + + for stop_exc in ( + StopIteration('spam'), + StopIterationSubclass('spam'), + ): + with self.subTest(type=type(stop_exc)): + try: + with f(): + raise stop_exc + except type(stop_exc) as e: + self.assertIs(e, stop_exc) + frames = traceback.extract_tb(e.__traceback__) + else: + self.fail(f'{stop_exc} was suppressed') + + self.assertEqual(len(frames), 1) + self.assertEqual(frames[0].name, 'test_contextmanager_traceback') + self.assertEqual(frames[0].line, 'raise stop_exc') + def test_contextmanager_no_reraise(self): @contextmanager def whee(): diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py index d44d36209c70..3d1079c205ac 100644 --- a/Lib/test/test_contextlib_async.py +++ b/Lib/test/test_contextlib_async.py @@ -4,6 +4,7 @@ AsyncExitStack, nullcontext, aclosing, contextmanager) import functools from test import support +import traceback import unittest from test.test_contextlib import TestBaseExitStack @@ -124,6 +125,63 @@ async def woohoo(): raise ZeroDivisionError() self.assertEqual(state, [1, 42, 999]) + @_async_test + async def test_contextmanager_traceback(self): + @asynccontextmanager + async def f(): + yield + + try: + async with f(): + 1/0 + except ZeroDivisionError as e: + frames = traceback.extract_tb(e.__traceback__) + + self.assertEqual(len(frames), 1) + self.assertEqual(frames[0].name, 'test_contextmanager_traceback') + self.assertEqual(frames[0].line, '1/0') + + # Repeat with RuntimeError (which goes through a different code path) + class RuntimeErrorSubclass(RuntimeError): + pass + + try: + async with f(): + raise RuntimeErrorSubclass(42) + except RuntimeErrorSubclass as e: + frames = traceback.extract_tb(e.__traceback__) + + self.assertEqual(len(frames), 1) + self.assertEqual(frames[0].name, 'test_contextmanager_traceback') + self.assertEqual(frames[0].line, 'raise RuntimeErrorSubclass(42)') + + class StopIterationSubclass(StopIteration): + pass + + class StopAsyncIterationSubclass(StopAsyncIteration): + pass + + for stop_exc in ( + StopIteration('spam'), + StopAsyncIteration('ham'), + StopIterationSubclass('spam'), + StopAsyncIterationSubclass('spam') + ): + with self.subTest(type=type(stop_exc)): + try: + async with f(): + raise stop_exc + except type(stop_exc) as e: + self.assertIs(e, stop_exc) + frames = traceback.extract_tb(e.__traceback__) + else: + self.fail(f'{stop_exc} was suppressed') + + self.assertEqual(len(frames), 1) + self.assertEqual(frames[0].name, 'test_contextmanager_traceback') + self.assertEqual(frames[0].line, 'raise stop_exc') + + @_async_test async def test_contextmanager_no_reraise(self): @asynccontextmanager From webhook-mailer at python.org Tue Jan 3 17:27:43 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 03 Jan 2023 22:27:43 -0000 Subject: [Python-checkins] [Enum] docs: replace 'last value' by 'highest value' for Flag auto (GH-100709) Message-ID: https://github.com/python/cpython/commit/7e9b2b0aa735331eafc0a16b7eb7efaa871bebe0 commit: 7e9b2b0aa735331eafc0a16b7eb7efaa871bebe0 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-03T14:27:38-08:00 summary: [Enum] docs: replace 'last value' by 'highest value' for Flag auto (GH-100709) (cherry picked from commit 64ed609c532a12b27f67a1e12e9e02f136ee3a94) Co-authored-by: Christophe Nanteuil <35002064+christopheNan at users.noreply.github.com> files: M Doc/library/enum.rst diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index b7b4b949790b..26cee1a13acf 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -799,9 +799,9 @@ Utilities and Decorators call an *Enum*'s :meth:`_generate_next_value_` to get an appropriate value. For *Enum* and *IntEnum* that appropriate value will be the last value plus one; for *Flag* and *IntFlag* it will be the first power-of-two greater - than the last value; for *StrEnum* it will be the lower-cased version of the - member's name. Care must be taken if mixing *auto()* with manually specified - values. + than the highest value; for *StrEnum* it will be the lower-cased version of + the member's name. Care must be taken if mixing *auto()* with manually + specified values. *auto* instances are only resolved when at the top level of an assignment: From webhook-mailer at python.org Tue Jan 3 17:51:45 2023 From: webhook-mailer at python.org (zware) Date: Tue, 03 Jan 2023 22:51:45 -0000 Subject: [Python-checkins] [3.11] Docs: Fix the Show Source link (GH-100733) Message-ID: https://github.com/python/cpython/commit/b93bd153d5b12df021d5677a56aafb6034850cc6 commit: b93bd153d5b12df021d5677a56aafb6034850cc6 branch: 3.11 author: Zachary Ware committer: zware date: 2023-01-03T16:51:36-06:00 summary: [3.11] Docs: Fix the Show Source link (GH-100733) See also python/release-tools#10 files: M Doc/tools/templates/customsourcelink.html diff --git a/Doc/tools/templates/customsourcelink.html b/Doc/tools/templates/customsourcelink.html index eb9db9e341bd..21af621efce9 100644 --- a/Doc/tools/templates/customsourcelink.html +++ b/Doc/tools/templates/customsourcelink.html @@ -4,7 +4,7 @@

{{ _('This Page') }}

  • {% trans %}Report a Bug{% endtrans %}
  • - {{ _('Show Source') }}
  • From webhook-mailer at python.org Tue Jan 3 18:20:14 2023 From: webhook-mailer at python.org (ethanfurman) Date: Tue, 03 Jan 2023 23:20:14 -0000 Subject: [Python-checkins] gh-91219: http - use subclassing to override index_pages attribute (GH-100731) Message-ID: https://github.com/python/cpython/commit/a286caa937405f7415dcc095a7ad5097c4433246 commit: a286caa937405f7415dcc095a7ad5097c4433246 branch: main author: Ethan Furman committer: ethanfurman date: 2023-01-03T15:20:08-08:00 summary: gh-91219: http - use subclassing to override index_pages attribute (GH-100731) Remove previously added parameter to `__init__`, and recommend subclassing to modify the `index_pages` attribute instead. files: A Misc/NEWS.d/next/Library/2023-01-03-11-06-28.gh-issue-91219.s5IFCw.rst M Doc/library/http.server.rst M Lib/http/server.py diff --git a/Doc/library/http.server.rst b/Doc/library/http.server.rst index 3290b9beab3e..ae75e6dc5fdc 100644 --- a/Doc/library/http.server.rst +++ b/Doc/library/http.server.rst @@ -413,6 +413,11 @@ the current directory:: print("serving at port", PORT) httpd.serve_forever() + +:class:`SimpleHTTPRequestHandler` can also be subclassed to enhance behavior, +such as using different index file names by overriding the class attribute +:attr:`index_pages`. + .. _http-server-cli: :mod:`http.server` can also be invoked directly using the :option:`-m` diff --git a/Lib/http/server.py b/Lib/http/server.py index 221c8be4ae4b..971f08046d50 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -652,8 +652,8 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): """ - index_pages = ["index.html", "index.htm"] server_version = "SimpleHTTP/" + __version__ + index_pages = ("index.html", "index.htm") extensions_map = _encodings_map_default = { '.gz': 'application/gzip', '.Z': 'application/octet-stream', @@ -661,11 +661,9 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): '.xz': 'application/x-xz', } - def __init__(self, *args, directory=None, index_pages=None, **kwargs): + def __init__(self, *args, directory=None, **kwargs): if directory is None: directory = os.getcwd() - if index_pages is not None: - self.index_pages = index_pages self.directory = os.fspath(directory) super().__init__(*args, **kwargs) diff --git a/Misc/NEWS.d/next/Library/2023-01-03-11-06-28.gh-issue-91219.s5IFCw.rst b/Misc/NEWS.d/next/Library/2023-01-03-11-06-28.gh-issue-91219.s5IFCw.rst new file mode 100644 index 000000000000..cb5e2e4630cc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-03-11-06-28.gh-issue-91219.s5IFCw.rst @@ -0,0 +1,2 @@ +Change ``SimpleHTTPRequestHandler`` to support subclassing to provide a +different set of index file names instead of using ``__init__`` parameters. From webhook-mailer at python.org Wed Jan 4 02:59:48 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Wed, 04 Jan 2023 07:59:48 -0000 Subject: [Python-checkins] [3.11] gh-89419: gdb: fix bug causing AttributeError in py-locals when no frame is available (GH-100611) (#100738) Message-ID: https://github.com/python/cpython/commit/d8073ee6f318773c2600f1e486ccab2504a03cdd commit: d8073ee6f318773c2600f1e486ccab2504a03cdd branch: 3.11 author: Eli Schwartz committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-04T13:29:21+05:30 summary: [3.11] gh-89419: gdb: fix bug causing AttributeError in py-locals when no frame is available (GH-100611) (#100738) gh-89419: gdb: fix bug causing AttributeError in py-locals when no frame is available (GH-100611) ``` Unable to read information on python frame Python Exception : 'NoneType' object has no attribute 'co_name' ``` Regression in commit b4903afd4debbbd71dc49a2c8fefa74a3b6c6832. While refactoring the code into a while loop, the previous early return when no frame exists went missing. We have just printed a message that we cannot get information about this, so the frame will be None, and we cannot attempt to use it. Discovered on python 3.11, in python 3.12a2 this should error out with `.is_shim()` instead of `co_name`. (cherry picked from commit 85869498331f7020e18bb243c89cd694f674b911) files: A Misc/NEWS.d/next/Tools-Demos/2022-12-29-19-22-11.bpo-45256.a0ee_H.rst M Tools/gdb/libpython.py diff --git a/Misc/NEWS.d/next/Tools-Demos/2022-12-29-19-22-11.bpo-45256.a0ee_H.rst b/Misc/NEWS.d/next/Tools-Demos/2022-12-29-19-22-11.bpo-45256.a0ee_H.rst new file mode 100644 index 000000000000..9c1aa5762583 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2022-12-29-19-22-11.bpo-45256.a0ee_H.rst @@ -0,0 +1 @@ +Fix a bug that caused an :exc:`AttributeError` to be raised in ``python-gdb.py`` when ``py-locals`` is used without a frame. diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 857e52f00a06..a99ce945265f 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -2126,6 +2126,7 @@ def invoke(self, args, from_tty): while True: if not pyop_frame: print(UNABLE_READ_INFO_PYTHON_FRAME) + break sys.stdout.write('Locals for %s\n' % (pyop_frame.co_name.proxyval(set()))) From webhook-mailer at python.org Wed Jan 4 03:00:32 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Wed, 04 Jan 2023 08:00:32 -0000 Subject: [Python-checkins] GH-86508: skip binding to local addresses of different family in `asyncio.open_connection` (#100615) Message-ID: https://github.com/python/cpython/commit/ba8dcdbcab5fd9989be6c9a51002394e782c463c commit: ba8dcdbcab5fd9989be6c9a51002394e782c463c branch: main author: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-04T13:30:26+05:30 summary: GH-86508: skip binding to local addresses of different family in `asyncio.open_connection` (#100615) files: A Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst M Lib/asyncio/base_events.py M Lib/test/test_asyncio/test_events.py diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index f2f93758c3a8..cbabb43ae060 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -961,7 +961,10 @@ async def _connect_sock(self, exceptions, addr_info, local_addr_infos=None): sock = socket.socket(family=family, type=type_, proto=proto) sock.setblocking(False) if local_addr_infos is not None: - for _, _, _, _, laddr in local_addr_infos: + for lfamily, _, _, _, laddr in local_addr_infos: + # skip local addresses of different family + if lfamily != family: + continue try: sock.bind(laddr) break @@ -974,7 +977,10 @@ async def _connect_sock(self, exceptions, addr_info, local_addr_infos=None): exc = OSError(exc.errno, msg) my_exceptions.append(exc) else: # all bind attempts failed - raise my_exceptions.pop() + if my_exceptions: + raise my_exceptions.pop() + else: + raise OSError(f"no matching local address with {family=} found") await self.sock_connect(sock, address) return sock except OSError as exc: diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index e7771edd2e4a..093f8c19b180 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -670,6 +670,47 @@ def test_create_connection_local_addr(self): self.assertEqual(port, expected) tr.close() + def test_create_connection_local_addr_skip_different_family(self): + # See https://github.com/python/cpython/issues/86508 + port1 = socket_helper.find_unused_port() + port2 = socket_helper.find_unused_port() + getaddrinfo_orig = self.loop.getaddrinfo + + async def getaddrinfo(host, port, *args, **kwargs): + if port == port2: + return [(socket.AF_INET6, socket.SOCK_STREAM, 0, '', ('::1', 0, 0, 0)), + (socket.AF_INET, socket.SOCK_STREAM, 0, '', ('127.0.0.1', 0))] + return await getaddrinfo_orig(host, port, *args, **kwargs) + + self.loop.getaddrinfo = getaddrinfo + + f = self.loop.create_connection( + lambda: MyProto(loop=self.loop), + 'localhost', port1, local_addr=('localhost', port2)) + + with self.assertRaises(OSError): + self.loop.run_until_complete(f) + + def test_create_connection_local_addr_nomatch_family(self): + # See https://github.com/python/cpython/issues/86508 + port1 = socket_helper.find_unused_port() + port2 = socket_helper.find_unused_port() + getaddrinfo_orig = self.loop.getaddrinfo + + async def getaddrinfo(host, port, *args, **kwargs): + if port == port2: + return [(socket.AF_INET6, socket.SOCK_STREAM, 0, '', ('::1', 0, 0, 0))] + return await getaddrinfo_orig(host, port, *args, **kwargs) + + self.loop.getaddrinfo = getaddrinfo + + f = self.loop.create_connection( + lambda: MyProto(loop=self.loop), + 'localhost', port1, local_addr=('localhost', port2)) + + with self.assertRaises(OSError): + self.loop.run_until_complete(f) + def test_create_connection_local_addr_in_use(self): with test_utils.run_test_server() as httpd: f = self.loop.create_connection( diff --git a/Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst b/Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst new file mode 100644 index 000000000000..aedad0f252c2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst @@ -0,0 +1 @@ +Fix :func:`asyncio.open_connection` to skip binding to local addresses of different family. Patch by Kumar Aditya. From webhook-mailer at python.org Wed Jan 4 03:17:49 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Wed, 04 Jan 2023 08:17:49 -0000 Subject: [Python-checkins] docs: fix `ssizeobjargproc` parameters (#100736) Message-ID: https://github.com/python/cpython/commit/5fb1c08e15b864d8ea9353a0e013166e2e0e2160 commit: 5fb1c08e15b864d8ea9353a0e013166e2e0e2160 branch: main author: David Lechner committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-04T13:47:42+05:30 summary: docs: fix `ssizeobjargproc` parameters (#100736) files: M Doc/c-api/typeobj.rst diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 016a92f11dbd..644830b940b4 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -452,6 +452,7 @@ slot typedefs | | | | | | :c:type:`PyObject` * | | | | :c:type:`Py_ssize_t` | | +| | :c:type:`PyObject` * | | +-----------------------------+-----------------------------+----------------------+ | :c:type:`objobjproc` | .. line-block:: | int | | | | | @@ -2633,7 +2634,7 @@ Slot Type typedefs .. c:type:: PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t) -.. c:type:: int (*ssizeobjargproc)(PyObject *, Py_ssize_t) +.. c:type:: int (*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *) .. c:type:: int (*objobjproc)(PyObject *, PyObject *) From webhook-mailer at python.org Wed Jan 4 03:24:42 2023 From: webhook-mailer at python.org (miss-islington) Date: Wed, 04 Jan 2023 08:24:42 -0000 Subject: [Python-checkins] GH-86508: skip binding to local addresses of different family in `asyncio.open_connection` (GH-100615) Message-ID: https://github.com/python/cpython/commit/35a010f1f92e272561f4aaf5918215c4953bce91 commit: 35a010f1f92e272561f4aaf5918215c4953bce91 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-04T00:24:36-08:00 summary: GH-86508: skip binding to local addresses of different family in `asyncio.open_connection` (GH-100615) (cherry picked from commit ba8dcdbcab5fd9989be6c9a51002394e782c463c) Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> files: A Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst M Lib/asyncio/base_events.py M Lib/test/test_asyncio/test_events.py diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 9a05abf45fe7..f4c30c1551a3 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -946,7 +946,10 @@ async def _connect_sock(self, exceptions, addr_info, local_addr_infos=None): sock = socket.socket(family=family, type=type_, proto=proto) sock.setblocking(False) if local_addr_infos is not None: - for _, _, _, _, laddr in local_addr_infos: + for lfamily, _, _, _, laddr in local_addr_infos: + # skip local addresses of different family + if lfamily != family: + continue try: sock.bind(laddr) break @@ -959,7 +962,10 @@ async def _connect_sock(self, exceptions, addr_info, local_addr_infos=None): exc = OSError(exc.errno, msg) my_exceptions.append(exc) else: # all bind attempts failed - raise my_exceptions.pop() + if my_exceptions: + raise my_exceptions.pop() + else: + raise OSError(f"no matching local address with {family=} found") await self.sock_connect(sock, address) return sock except OSError as exc: diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 92c69de742ba..28d5f39ef611 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -670,6 +670,47 @@ def test_create_connection_local_addr(self): self.assertEqual(port, expected) tr.close() + def test_create_connection_local_addr_skip_different_family(self): + # See https://github.com/python/cpython/issues/86508 + port1 = socket_helper.find_unused_port() + port2 = socket_helper.find_unused_port() + getaddrinfo_orig = self.loop.getaddrinfo + + async def getaddrinfo(host, port, *args, **kwargs): + if port == port2: + return [(socket.AF_INET6, socket.SOCK_STREAM, 0, '', ('::1', 0, 0, 0)), + (socket.AF_INET, socket.SOCK_STREAM, 0, '', ('127.0.0.1', 0))] + return await getaddrinfo_orig(host, port, *args, **kwargs) + + self.loop.getaddrinfo = getaddrinfo + + f = self.loop.create_connection( + lambda: MyProto(loop=self.loop), + 'localhost', port1, local_addr=('localhost', port2)) + + with self.assertRaises(OSError): + self.loop.run_until_complete(f) + + def test_create_connection_local_addr_nomatch_family(self): + # See https://github.com/python/cpython/issues/86508 + port1 = socket_helper.find_unused_port() + port2 = socket_helper.find_unused_port() + getaddrinfo_orig = self.loop.getaddrinfo + + async def getaddrinfo(host, port, *args, **kwargs): + if port == port2: + return [(socket.AF_INET6, socket.SOCK_STREAM, 0, '', ('::1', 0, 0, 0))] + return await getaddrinfo_orig(host, port, *args, **kwargs) + + self.loop.getaddrinfo = getaddrinfo + + f = self.loop.create_connection( + lambda: MyProto(loop=self.loop), + 'localhost', port1, local_addr=('localhost', port2)) + + with self.assertRaises(OSError): + self.loop.run_until_complete(f) + def test_create_connection_local_addr_in_use(self): with test_utils.run_test_server() as httpd: f = self.loop.create_connection( diff --git a/Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst b/Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst new file mode 100644 index 000000000000..aedad0f252c2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst @@ -0,0 +1 @@ +Fix :func:`asyncio.open_connection` to skip binding to local addresses of different family. Patch by Kumar Aditya. From webhook-mailer at python.org Wed Jan 4 03:28:09 2023 From: webhook-mailer at python.org (miss-islington) Date: Wed, 04 Jan 2023 08:28:09 -0000 Subject: [Python-checkins] GH-86508: skip binding to local addresses of different family in `asyncio.open_connection` (GH-100615) Message-ID: https://github.com/python/cpython/commit/ad40764f5b1e0a11a8b4a30b7ce2dacc20fda12f commit: ad40764f5b1e0a11a8b4a30b7ce2dacc20fda12f branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-04T00:28:00-08:00 summary: GH-86508: skip binding to local addresses of different family in `asyncio.open_connection` (GH-100615) (cherry picked from commit ba8dcdbcab5fd9989be6c9a51002394e782c463c) Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> files: A Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst M Lib/asyncio/base_events.py M Lib/test/test_asyncio/test_events.py diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 8f3e1b3df2a3..4692f95b80eb 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -950,7 +950,10 @@ async def _connect_sock(self, exceptions, addr_info, local_addr_infos=None): sock = socket.socket(family=family, type=type_, proto=proto) sock.setblocking(False) if local_addr_infos is not None: - for _, _, _, _, laddr in local_addr_infos: + for lfamily, _, _, _, laddr in local_addr_infos: + # skip local addresses of different family + if lfamily != family: + continue try: sock.bind(laddr) break @@ -963,7 +966,10 @@ async def _connect_sock(self, exceptions, addr_info, local_addr_infos=None): exc = OSError(exc.errno, msg) my_exceptions.append(exc) else: # all bind attempts failed - raise my_exceptions.pop() + if my_exceptions: + raise my_exceptions.pop() + else: + raise OSError(f"no matching local address with {family=} found") await self.sock_connect(sock, address) return sock except OSError as exc: diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index c431fea40163..3f92122b0c76 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -671,6 +671,47 @@ def test_create_connection_local_addr(self): self.assertEqual(port, expected) tr.close() + def test_create_connection_local_addr_skip_different_family(self): + # See https://github.com/python/cpython/issues/86508 + port1 = socket_helper.find_unused_port() + port2 = socket_helper.find_unused_port() + getaddrinfo_orig = self.loop.getaddrinfo + + async def getaddrinfo(host, port, *args, **kwargs): + if port == port2: + return [(socket.AF_INET6, socket.SOCK_STREAM, 0, '', ('::1', 0, 0, 0)), + (socket.AF_INET, socket.SOCK_STREAM, 0, '', ('127.0.0.1', 0))] + return await getaddrinfo_orig(host, port, *args, **kwargs) + + self.loop.getaddrinfo = getaddrinfo + + f = self.loop.create_connection( + lambda: MyProto(loop=self.loop), + 'localhost', port1, local_addr=('localhost', port2)) + + with self.assertRaises(OSError): + self.loop.run_until_complete(f) + + def test_create_connection_local_addr_nomatch_family(self): + # See https://github.com/python/cpython/issues/86508 + port1 = socket_helper.find_unused_port() + port2 = socket_helper.find_unused_port() + getaddrinfo_orig = self.loop.getaddrinfo + + async def getaddrinfo(host, port, *args, **kwargs): + if port == port2: + return [(socket.AF_INET6, socket.SOCK_STREAM, 0, '', ('::1', 0, 0, 0))] + return await getaddrinfo_orig(host, port, *args, **kwargs) + + self.loop.getaddrinfo = getaddrinfo + + f = self.loop.create_connection( + lambda: MyProto(loop=self.loop), + 'localhost', port1, local_addr=('localhost', port2)) + + with self.assertRaises(OSError): + self.loop.run_until_complete(f) + def test_create_connection_local_addr_in_use(self): with test_utils.run_test_server() as httpd: f = self.loop.create_connection( diff --git a/Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst b/Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst new file mode 100644 index 000000000000..aedad0f252c2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst @@ -0,0 +1 @@ +Fix :func:`asyncio.open_connection` to skip binding to local addresses of different family. Patch by Kumar Aditya. From webhook-mailer at python.org Wed Jan 4 08:37:14 2023 From: webhook-mailer at python.org (iritkatriel) Date: Wed, 04 Jan 2023 13:37:14 -0000 Subject: [Python-checkins] gh-100720: refactor calculation of number of frame slots for a code object into the new function _PyFrame_NumSlotsForCodeObject (#100722) Message-ID: https://github.com/python/cpython/commit/c31e356a10aa60b5967b9aaf80b9984059e46461 commit: c31e356a10aa60b5967b9aaf80b9984059e46461 branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-04T13:37:06Z summary: gh-100720: refactor calculation of number of frame slots for a code object into the new function _PyFrame_NumSlotsForCodeObject (#100722) files: A Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-50-42.gh-issue-100720.UhE7P-.rst M Include/internal/pycore_frame.h M Objects/frameobject.c M Objects/genobject.c M Tools/build/deepfreeze.py diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index f18723b30322..d5c1dcc82a88 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -92,7 +92,16 @@ static inline void _PyFrame_StackPush(_PyInterpreterFrame *f, PyObject *value) { f->stacktop++; } -#define FRAME_SPECIALS_SIZE ((sizeof(_PyInterpreterFrame)-1)/sizeof(PyObject *)) +#define FRAME_SPECIALS_SIZE ((int)((sizeof(_PyInterpreterFrame)-1)/sizeof(PyObject *))) + +static inline int +_PyFrame_NumSlotsForCodeObject(PyCodeObject *code) +{ + /* This function needs to remain in sync with the calculation of + * co_framesize in Tools/build/deepfreeze.py */ + assert(code->co_framesize >= FRAME_SPECIALS_SIZE); + return code->co_framesize - FRAME_SPECIALS_SIZE; +} void _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest); diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-50-42.gh-issue-100720.UhE7P-.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-50-42.gh-issue-100720.UhE7P-.rst new file mode 100644 index 000000000000..4c194eccf8a5 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-50-42.gh-issue-100720.UhE7P-.rst @@ -0,0 +1 @@ +Added ``_PyFrame_NumSlotsForCodeObject``, which returns the number of slots needed in a frame for a given code object. diff --git a/Objects/frameobject.c b/Objects/frameobject.c index eab85c08fc01..98f0b3838723 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -946,7 +946,7 @@ frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) Py_ssize_t res; res = offsetof(PyFrameObject, _f_frame_data) + offsetof(_PyInterpreterFrame, localsplus); PyCodeObject *code = f->f_frame->f_code; - res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *); + res += _PyFrame_NumSlotsForCodeObject(code) * sizeof(PyObject *); return PyLong_FromSsize_t(res); } diff --git a/Objects/genobject.c b/Objects/genobject.c index c006f1af2177..ea3382d3e31a 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -769,7 +769,7 @@ gen_sizeof(PyGenObject *gen, PyObject *Py_UNUSED(ignored)) Py_ssize_t res; res = offsetof(PyGenObject, gi_iframe) + offsetof(_PyInterpreterFrame, localsplus); PyCodeObject *code = gen->gi_code; - res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *); + res += _PyFrame_NumSlotsForCodeObject(code) * sizeof(PyObject *); return PyLong_FromSsize_t(res); } @@ -850,7 +850,7 @@ static PyObject * make_gen(PyTypeObject *type, PyFunctionObject *func) { PyCodeObject *code = (PyCodeObject *)func->func_code; - int slots = code->co_nlocalsplus + code->co_stacksize; + int slots = _PyFrame_NumSlotsForCodeObject(code); PyGenObject *gen = PyObject_GC_NewVar(PyGenObject, type, slots); if (gen == NULL) { return NULL; diff --git a/Tools/build/deepfreeze.py b/Tools/build/deepfreeze.py index 7f4e24280133..e4b422820f7d 100644 --- a/Tools/build/deepfreeze.py +++ b/Tools/build/deepfreeze.py @@ -262,6 +262,7 @@ def generate_code(self, name: str, code: types.CodeType) -> str: self.field(code, "co_argcount") self.field(code, "co_posonlyargcount") self.field(code, "co_kwonlyargcount") + # The following should remain in sync with _PyFrame_NumSlotsForCodeObject self.write(f".co_framesize = {code.co_stacksize + len(localsplusnames)} + FRAME_SPECIALS_SIZE,") self.field(code, "co_stacksize") self.field(code, "co_firstlineno") From webhook-mailer at python.org Wed Jan 4 10:36:51 2023 From: webhook-mailer at python.org (miss-islington) Date: Wed, 04 Jan 2023 15:36:51 -0000 Subject: [Python-checkins] docs: fix `ssizeobjargproc` parameters (GH-100736) Message-ID: https://github.com/python/cpython/commit/8b023134bde5cf861d7242f03885e9bb442c2955 commit: 8b023134bde5cf861d7242f03885e9bb442c2955 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-04T07:36:44-08:00 summary: docs: fix `ssizeobjargproc` parameters (GH-100736) (cherry picked from commit 5fb1c08e15b864d8ea9353a0e013166e2e0e2160) Co-authored-by: David Lechner files: M Doc/c-api/typeobj.rst diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 53062e1d651b..937f52b07329 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -450,6 +450,7 @@ slot typedefs | | | | | | :c:type:`PyObject` * | | | | :c:type:`Py_ssize_t` | | +| | :c:type:`PyObject` * | | +-----------------------------+-----------------------------+----------------------+ | :c:type:`objobjproc` | .. line-block:: | int | | | | | @@ -2595,7 +2596,7 @@ Slot Type typedefs .. c:type:: PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t) -.. c:type:: int (*ssizeobjargproc)(PyObject *, Py_ssize_t) +.. c:type:: int (*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *) .. c:type:: int (*objobjproc)(PyObject *, PyObject *) From webhook-mailer at python.org Wed Jan 4 10:40:13 2023 From: webhook-mailer at python.org (miss-islington) Date: Wed, 04 Jan 2023 15:40:13 -0000 Subject: [Python-checkins] docs: fix `ssizeobjargproc` parameters (GH-100736) Message-ID: https://github.com/python/cpython/commit/c915f00ecaa1bf8adb3a4aa6aed4c3aaa95f8d8c commit: c915f00ecaa1bf8adb3a4aa6aed4c3aaa95f8d8c branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-04T07:40:06-08:00 summary: docs: fix `ssizeobjargproc` parameters (GH-100736) (cherry picked from commit 5fb1c08e15b864d8ea9353a0e013166e2e0e2160) Co-authored-by: David Lechner files: M Doc/c-api/typeobj.rst diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index dc82620cc3ba..aa2de3344778 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -450,6 +450,7 @@ slot typedefs | | | | | | :c:type:`PyObject` * | | | | :c:type:`Py_ssize_t` | | +| | :c:type:`PyObject` * | | +-----------------------------+-----------------------------+----------------------+ | :c:type:`objobjproc` | .. line-block:: | int | | | | | @@ -2589,7 +2590,7 @@ Slot Type typedefs .. c:type:: PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t) -.. c:type:: int (*ssizeobjargproc)(PyObject *, Py_ssize_t) +.. c:type:: int (*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *) .. c:type:: int (*objobjproc)(PyObject *, PyObject *) From webhook-mailer at python.org Wed Jan 4 10:41:45 2023 From: webhook-mailer at python.org (markshannon) Date: Wed, 04 Jan 2023 15:41:45 -0000 Subject: [Python-checkins] GH-100719: Remove the `co_nplaincellvars` field from code objects. (GH-100721) Message-ID: https://github.com/python/cpython/commit/15aecf8dd70f82eb507d74fae9662072a377bdc8 commit: 15aecf8dd70f82eb507d74fae9662072a377bdc8 branch: main author: Mark Shannon committer: markshannon date: 2023-01-04T15:41:39Z summary: GH-100719: Remove the `co_nplaincellvars` field from code objects. (GH-100721) files: A Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-38-18.gh-issue-100719.2C--ko.rst M Include/cpython/code.h M Objects/codeobject.c M Objects/frameobject.c M Objects/typeobject.c M Python/bytecodes.c M Python/ceval.c M Python/compile.c M Python/generated_cases.c.h M Tools/build/deepfreeze.py diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 1c619322926e..0cf49f06c877 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -87,7 +87,6 @@ typedef struct { int co_nlocalsplus; /* number of local + cell + free variables */ \ int co_framesize; /* Size of frame in words */ \ int co_nlocals; /* number of local variables */ \ - int co_nplaincellvars; /* number of non-arg cell variables */ \ int co_ncellvars; /* total number of cell variables */ \ int co_nfreevars; /* number of free variables */ \ uint32_t co_version; /* version number */ \ @@ -157,6 +156,11 @@ static inline Py_ssize_t PyCode_GetNumFree(PyCodeObject *op) { return op->co_nfreevars; } +static inline int PyCode_GetFirstFree(PyCodeObject *op) { + assert(PyCode_Check(op)); + return op->co_nlocalsplus - op->co_nfreevars; +} + #define _PyCode_CODE(CO) _Py_RVALUE((_Py_CODEUNIT *)(CO)->co_code_adaptive) #define _PyCode_NBYTES(CO) (Py_SIZE(CO) * (Py_ssize_t)sizeof(_Py_CODEUNIT)) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-38-18.gh-issue-100719.2C--ko.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-38-18.gh-issue-100719.2C--ko.rst new file mode 100644 index 000000000000..bb5d5619c2d0 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-38-18.gh-issue-100719.2C--ko.rst @@ -0,0 +1,2 @@ +Removed the co_nplaincellvars field from the code object, as it is +redundant. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index e174c6fee9cc..6facfef4c9b4 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -247,11 +247,10 @@ _Py_set_localsplus_info(int offset, PyObject *name, _PyLocals_Kind kind, static void get_localsplus_counts(PyObject *names, PyObject *kinds, - int *pnlocals, int *pnplaincellvars, int *pncellvars, + int *pnlocals, int *pncellvars, int *pnfreevars) { int nlocals = 0; - int nplaincellvars = 0; int ncellvars = 0; int nfreevars = 0; Py_ssize_t nlocalsplus = PyTuple_GET_SIZE(names); @@ -265,7 +264,6 @@ get_localsplus_counts(PyObject *names, PyObject *kinds, } else if (kind & CO_FAST_CELL) { ncellvars += 1; - nplaincellvars += 1; } else if (kind & CO_FAST_FREE) { nfreevars += 1; @@ -274,9 +272,6 @@ get_localsplus_counts(PyObject *names, PyObject *kinds, if (pnlocals != NULL) { *pnlocals = nlocals; } - if (pnplaincellvars != NULL) { - *pnplaincellvars = nplaincellvars; - } if (pncellvars != NULL) { *pncellvars = ncellvars; } @@ -351,7 +346,7 @@ _PyCode_Validate(struct _PyCodeConstructor *con) * here to avoid the possibility of overflow (however remote). */ int nlocals; get_localsplus_counts(con->localsplusnames, con->localspluskinds, - &nlocals, NULL, NULL, NULL); + &nlocals, NULL, NULL); int nplainlocals = nlocals - con->argcount - con->kwonlyargcount - @@ -371,9 +366,9 @@ static void init_code(PyCodeObject *co, struct _PyCodeConstructor *con) { int nlocalsplus = (int)PyTuple_GET_SIZE(con->localsplusnames); - int nlocals, nplaincellvars, ncellvars, nfreevars; + int nlocals, ncellvars, nfreevars; get_localsplus_counts(con->localsplusnames, con->localspluskinds, - &nlocals, &nplaincellvars, &ncellvars, &nfreevars); + &nlocals, &ncellvars, &nfreevars); co->co_filename = Py_NewRef(con->filename); co->co_name = Py_NewRef(con->name); @@ -401,7 +396,6 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) co->co_nlocalsplus = nlocalsplus; co->co_nlocals = nlocals; co->co_framesize = nlocalsplus + con->stacksize + FRAME_SPECIALS_SIZE; - co->co_nplaincellvars = nplaincellvars; co->co_ncellvars = ncellvars; co->co_nfreevars = nfreevars; co->co_version = _Py_next_func_version; diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 98f0b3838723..8d7ee4b81eb7 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1119,7 +1119,7 @@ frame_init_get_vars(_PyInterpreterFrame *frame) /* Free vars have not been initialized -- Do that */ PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure; - int offset = co->co_nlocals + co->co_nplaincellvars; + int offset = PyCode_GetFirstFree(co); for (int i = 0; i < co->co_nfreevars; ++i) { PyObject *o = PyTuple_GET_ITEM(closure, i); frame->localsplus[offset + i] = Py_NewRef(o); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 43633f044751..f2d78cf50913 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -9514,7 +9514,7 @@ super_init_without_args(_PyInterpreterFrame *cframe, PyCodeObject *co, // Look for __class__ in the free vars. PyTypeObject *type = NULL; - int i = co->co_nlocals + co->co_nplaincellvars; + int i = PyCode_GetFirstFree(co); for (; i < co->co_nlocalsplus; i++) { assert((_PyLocals_GetKind(co->co_localspluskinds, i) & CO_FAST_FREE) != 0); PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 839fac3fcd11..dec122a78223 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1357,8 +1357,8 @@ dummy_func( PyCodeObject *co = frame->f_code; assert(PyFunction_Check(frame->f_funcobj)); PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure; - int offset = co->co_nlocals + co->co_nplaincellvars; assert(oparg == co->co_nfreevars); + int offset = co->co_nlocalsplus - oparg; for (int i = 0; i < oparg; ++i) { PyObject *o = PyTuple_GET_ITEM(closure, i); frame->localsplus[offset + i] = Py_NewRef(o); diff --git a/Python/ceval.c b/Python/ceval.c index 45f42800d7ce..54df1c512502 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3417,7 +3417,7 @@ format_exc_unbound(PyThreadState *tstate, PyCodeObject *co, int oparg) if (_PyErr_Occurred(tstate)) return; name = PyTuple_GET_ITEM(co->co_localsplusnames, oparg); - if (oparg < co->co_nplaincellvars + co->co_nlocals) { + if (oparg < PyCode_GetFirstFree(co)) { format_exc_check_arg(tstate, PyExc_UnboundLocalError, UNBOUNDLOCAL_ERROR_MSG, name); } else { diff --git a/Python/compile.c b/Python/compile.c index cbbdfb9e9467..c2e77fefb084 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -2260,7 +2260,7 @@ compiler_make_closure(struct compiler *c, location loc, qualname = co->co_name; if (co->co_nfreevars) { - int i = co->co_nlocals + co->co_nplaincellvars; + int i = PyCode_GetFirstFree(co); for (; i < co->co_nlocalsplus; ++i) { /* Bypass com_addop_varname because it will generate LOAD_DEREF but LOAD_CLOSURE is needed. diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ed89e90b7c56..3218bd091433 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1575,8 +1575,8 @@ PyCodeObject *co = frame->f_code; assert(PyFunction_Check(frame->f_funcobj)); PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure; - int offset = co->co_nlocals + co->co_nplaincellvars; assert(oparg == co->co_nfreevars); + int offset = co->co_nlocalsplus - oparg; for (int i = 0; i < oparg; ++i) { PyObject *o = PyTuple_GET_ITEM(closure, i); frame->localsplus[offset + i] = Py_NewRef(o); diff --git a/Tools/build/deepfreeze.py b/Tools/build/deepfreeze.py index e4b422820f7d..511b26a5ce3d 100644 --- a/Tools/build/deepfreeze.py +++ b/Tools/build/deepfreeze.py @@ -61,7 +61,6 @@ def get_localsplus_counts(code: types.CodeType, names: Tuple[str, ...], kinds: bytes) -> Tuple[int, int, int, int]: nlocals = 0 - nplaincellvars = 0 ncellvars = 0 nfreevars = 0 assert len(names) == len(kinds) @@ -72,15 +71,13 @@ def get_localsplus_counts(code: types.CodeType, ncellvars += 1 elif kind & CO_FAST_CELL: ncellvars += 1 - nplaincellvars += 1 elif kind & CO_FAST_FREE: nfreevars += 1 assert nlocals == len(code.co_varnames) == code.co_nlocals, \ (nlocals, len(code.co_varnames), code.co_nlocals) assert ncellvars == len(code.co_cellvars) assert nfreevars == len(code.co_freevars) - assert len(names) == nlocals + nplaincellvars + nfreevars - return nlocals, nplaincellvars, ncellvars, nfreevars + return nlocals, ncellvars, nfreevars PyUnicode_1BYTE_KIND = 1 @@ -243,7 +240,7 @@ def generate_code(self, name: str, code: types.CodeType) -> str: co_localsplusnames = self.generate(name + "_localsplusnames", localsplusnames) co_localspluskinds = self.generate(name + "_localspluskinds", localspluskinds) # Derived values - nlocals, nplaincellvars, ncellvars, nfreevars = \ + nlocals, ncellvars, nfreevars = \ get_localsplus_counts(code, localsplusnames, localspluskinds) co_code_adaptive = make_string_literal(code.co_code) self.write("static") @@ -268,7 +265,6 @@ def generate_code(self, name: str, code: types.CodeType) -> str: self.field(code, "co_firstlineno") self.write(f".co_nlocalsplus = {len(localsplusnames)},") self.field(code, "co_nlocals") - self.write(f".co_nplaincellvars = {nplaincellvars},") self.write(f".co_ncellvars = {ncellvars},") self.write(f".co_nfreevars = {nfreevars},") self.write(f".co_version = {next_code_version},") From webhook-mailer at python.org Wed Jan 4 15:19:27 2023 From: webhook-mailer at python.org (iritkatriel) Date: Wed, 04 Jan 2023 20:19:27 -0000 Subject: [Python-checkins] gh-100747: some compiler macros use c instead of C to access the compiler (#100748) Message-ID: https://github.com/python/cpython/commit/52017dbe1681a7cd4fe0e8d6fbbf81fd711a0506 commit: 52017dbe1681a7cd4fe0e8d6fbbf81fd711a0506 branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-04T20:19:20Z summary: gh-100747: some compiler macros use c instead of C to access the compiler (#100748) files: M Python/compile.c diff --git a/Python/compile.c b/Python/compile.c index c2e77fefb084..2527023d18e8 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -131,9 +131,9 @@ (opcode) == STORE_FAST__LOAD_FAST || \ (opcode) == STORE_FAST__STORE_FAST) -#define IS_TOP_LEVEL_AWAIT(c) ( \ - (c->c_flags.cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT) \ - && (c->u->u_ste->ste_type == ModuleBlock)) +#define IS_TOP_LEVEL_AWAIT(C) ( \ + ((C)->c_flags.cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT) \ + && ((C)->u->u_ste->ste_type == ModuleBlock)) typedef _PyCompilerSrcLocation location; @@ -479,7 +479,7 @@ struct compiler { PyArena *c_arena; /* pointer to memory allocation arena */ }; -#define CFG_BUILDER(c) (&((c)->u->u_cfg_builder)) +#define CFG_BUILDER(C) (&((C)->u->u_cfg_builder)) typedef struct { @@ -1626,7 +1626,7 @@ cfg_builder_addop_j(cfg_builder *g, location loc, #define ADDOP_IN_SCOPE(C, LOC, OP) { \ if (cfg_builder_addop_noarg(CFG_BUILDER(C), (OP), (LOC)) < 0) { \ - compiler_exit_scope(c); \ + compiler_exit_scope(C); \ return -1; \ } \ } @@ -1692,7 +1692,7 @@ cfg_builder_addop_j(cfg_builder *g, location loc, #define VISIT_IN_SCOPE(C, TYPE, V) {\ if (compiler_visit_ ## TYPE((C), (V)) < 0) { \ - compiler_exit_scope(c); \ + compiler_exit_scope(C); \ return ERROR; \ } \ } @@ -1713,7 +1713,7 @@ cfg_builder_addop_j(cfg_builder *g, location loc, for (_i = 0; _i < asdl_seq_LEN(seq); _i++) { \ TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, _i); \ if (compiler_visit_ ## TYPE((C), elt) < 0) { \ - compiler_exit_scope(c); \ + compiler_exit_scope(C); \ return ERROR; \ } \ } \ From webhook-mailer at python.org Wed Jan 4 15:57:32 2023 From: webhook-mailer at python.org (terryjreedy) Date: Wed, 04 Jan 2023 20:57:32 -0000 Subject: [Python-checkins] [3.11] gh-81611: Improve `range` paragraph in 8.3 of language reference (GH-98353) (#100704) Message-ID: https://github.com/python/cpython/commit/eba6b009530ddeff338937923698ccac49866051 commit: eba6b009530ddeff338937923698ccac49866051 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: terryjreedy date: 2023-01-04T15:57:24-05:00 summary: [3.11] gh-81611: Improve `range` paragraph in 8.3 of language reference (GH-98353) (#100704) gh-81611: Improve `range` paragraph in 8.3 of language reference (GH-98353) (cherry picked from commit 8b1f1251215651c4ef988622345c5cb134e54d69) Co-authored-by: 4l4k4z4m Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> Co-authored-by: Terry Jan Reedy files: M Doc/reference/compound_stmts.rst diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index fe9dda933c80..d5cb2899c231 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -192,9 +192,8 @@ those made in the suite of the for-loop:: Names in the target list are not deleted when the loop is finished, but if the sequence is empty, they will not have been assigned to at all by the loop. Hint: -the built-in function :func:`range` returns an iterator of integers suitable to -emulate the effect of Pascal's ``for i := a to b do``; e.g., ``list(range(3))`` -returns the list ``[0, 1, 2]``. +the built-in type :func:`range` represents immutable arithmetic sequences of integers. +For instance, iterating ``range(3)`` successively yields 0, 1, and then 2. .. versionchanged:: 3.11 Starred elements are now allowed in the expression list. From webhook-mailer at python.org Wed Jan 4 16:00:33 2023 From: webhook-mailer at python.org (terryjreedy) Date: Wed, 04 Jan 2023 21:00:33 -0000 Subject: [Python-checkins] [3.10] gh-81611: Improve `range` paragraph in 8.3 of language reference (GH-98353) (#100705) Message-ID: https://github.com/python/cpython/commit/fee4059d213401011457531933e64790b181f33c commit: fee4059d213401011457531933e64790b181f33c branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: terryjreedy date: 2023-01-04T16:00:27-05:00 summary: [3.10] gh-81611: Improve `range` paragraph in 8.3 of language reference (GH-98353) (#100705) gh-81611: Improve `range` paragraph in 8.3 of language reference (GH-98353) (cherry picked from commit 8b1f1251215651c4ef988622345c5cb134e54d69) Co-authored-by: 4l4k4z4m Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> Co-authored-by: Terry Jan Reedy files: M Doc/reference/compound_stmts.rst diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 58b88c4d37f1..20c85a9c8677 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -192,9 +192,8 @@ those made in the suite of the for-loop:: Names in the target list are not deleted when the loop is finished, but if the sequence is empty, they will not have been assigned to at all by the loop. Hint: -the built-in function :func:`range` returns an iterator of integers suitable to -emulate the effect of Pascal's ``for i := a to b do``; e.g., ``list(range(3))`` -returns the list ``[0, 1, 2]``. +the built-in type :func:`range` represents immutable arithmetic sequences of integers. +For instance, iterating ``range(3)`` successively yields 0, 1, and then 2. .. _try: From webhook-mailer at python.org Wed Jan 4 17:31:35 2023 From: webhook-mailer at python.org (cjw296) Date: Wed, 04 Jan 2023 22:31:35 -0000 Subject: [Python-checkins] gh-100739: Respect mock spec when checking for unsafe prefixes (#100740) Message-ID: https://github.com/python/cpython/commit/7f1eefc6f4843f0fca60308f557a71af11d18a53 commit: 7f1eefc6f4843f0fca60308f557a71af11d18a53 branch: main author: Christian Klein <167265+cklein at users.noreply.github.com> committer: cjw296 date: 2023-01-04T22:31:29Z summary: gh-100739: Respect mock spec when checking for unsafe prefixes (#100740) Co-authored-by: Nikita Sobolev files: A Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst M Lib/test/test_unittest/testmock/testmock.py M Lib/unittest/mock.py diff --git a/Lib/test/test_unittest/testmock/testmock.py b/Lib/test/test_unittest/testmock/testmock.py index 8a92490137f9..b224f87fa3e4 100644 --- a/Lib/test/test_unittest/testmock/testmock.py +++ b/Lib/test/test_unittest/testmock/testmock.py @@ -1652,6 +1652,22 @@ def test_mock_unsafe(self): m.aseert_foo_call() m.assrt_foo_call() + # gh-100739 + def test_mock_safe_with_spec(self): + class Foo(object): + def assert_bar(self): + pass + + def assertSome(self): + pass + + m = Mock(spec=Foo) + m.assert_bar() + m.assertSome() + + m.assert_bar.assert_called_once() + m.assertSome.assert_called_once() + #Issue21262 def test_assert_not_called(self): m = Mock() diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 994947cad518..47928e564850 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -652,7 +652,7 @@ def __getattr__(self, name): raise AttributeError("Mock object has no attribute %r" % name) elif _is_magic(name): raise AttributeError(name) - if not self._mock_unsafe: + if not self._mock_unsafe and (not self._mock_methods or name not in self._mock_methods): if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')): raise AttributeError( f"{name!r} is not a valid assertion. Use a spec " diff --git a/Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst b/Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst new file mode 100644 index 000000000000..4753e7b40825 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst @@ -0,0 +1 @@ +Fix ``unittest.mock.Mock`` not respecting the spec for attribute names prefixed with ``assert``. From webhook-mailer at python.org Wed Jan 4 17:59:57 2023 From: webhook-mailer at python.org (brettcannon) Date: Wed, 04 Jan 2023 22:59:57 -0000 Subject: [Python-checkins] Drop myself from pathlib maintenance (#100757) Message-ID: https://github.com/python/cpython/commit/31b639a99281bd9582e88137a3be2868c4154a88 commit: 31b639a99281bd9582e88137a3be2868c4154a88 branch: main author: Brett Cannon committer: brettcannon date: 2023-01-04T14:59:48-08:00 summary: Drop myself from pathlib maintenance (#100757) files: M .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5d30c0928e5a..1d7c1e843fe9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -63,7 +63,7 @@ Python/traceback.c @iritkatriel # bytecode. **/*import*.c @brettcannon @encukou @ericsnowcurrently @ncoghlan @warsaw **/*import*.py @brettcannon @encukou @ericsnowcurrently @ncoghlan @warsaw -**/*importlib/resources/* @jaraco @warsaw @brettcannon @FFY00 +**/*importlib/resources/* @jaraco @warsaw @FFY00 **/importlib/metadata/* @jaraco @warsaw # Dates and times @@ -152,8 +152,5 @@ Lib/ast.py @isidentical /Mac/ @python/macos-team **/*osx_support* @python/macos-team -# pathlib -**/*pathlib* @brettcannon - # zipfile.Path **/*zipfile/*_path.py @jaraco From webhook-mailer at python.org Wed Jan 4 18:08:43 2023 From: webhook-mailer at python.org (miss-islington) Date: Wed, 04 Jan 2023 23:08:43 -0000 Subject: [Python-checkins] gh-100739: Respect mock spec when checking for unsafe prefixes (GH-100740) Message-ID: https://github.com/python/cpython/commit/541e7b8029be8dff9af5efff5e52bf3068534d62 commit: 541e7b8029be8dff9af5efff5e52bf3068534d62 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-04T15:08:37-08:00 summary: gh-100739: Respect mock spec when checking for unsafe prefixes (GH-100740) (cherry picked from commit 7f1eefc6f4843f0fca60308f557a71af11d18a53) Co-authored-by: Christian Klein <167265+cklein at users.noreply.github.com> Co-authored-by: Nikita Sobolev files: A Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst M Lib/unittest/mock.py M Lib/unittest/test/testmock/testmock.py diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 6a1c932081e0..fa0bd9131a21 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -647,7 +647,7 @@ def __getattr__(self, name): raise AttributeError("Mock object has no attribute %r" % name) elif _is_magic(name): raise AttributeError(name) - if not self._mock_unsafe: + if not self._mock_unsafe and (not self._mock_methods or name not in self._mock_methods): if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')): raise AttributeError( f"{name!r} is not a valid assertion. Use a spec " diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index c99098dc4ea8..eaae22e854e9 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -1652,6 +1652,22 @@ def test_mock_unsafe(self): m.aseert_foo_call() m.assrt_foo_call() + # gh-100739 + def test_mock_safe_with_spec(self): + class Foo(object): + def assert_bar(self): + pass + + def assertSome(self): + pass + + m = Mock(spec=Foo) + m.assert_bar() + m.assertSome() + + m.assert_bar.assert_called_once() + m.assertSome.assert_called_once() + #Issue21262 def test_assert_not_called(self): m = Mock() diff --git a/Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst b/Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst new file mode 100644 index 000000000000..4753e7b40825 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst @@ -0,0 +1 @@ +Fix ``unittest.mock.Mock`` not respecting the spec for attribute names prefixed with ``assert``. From webhook-mailer at python.org Wed Jan 4 18:11:51 2023 From: webhook-mailer at python.org (miss-islington) Date: Wed, 04 Jan 2023 23:11:51 -0000 Subject: [Python-checkins] gh-100739: Respect mock spec when checking for unsafe prefixes (GH-100740) Message-ID: https://github.com/python/cpython/commit/f49cc3c805dbc1bc54edeba3c248b875b3e6f3d3 commit: f49cc3c805dbc1bc54edeba3c248b875b3e6f3d3 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-04T15:11:46-08:00 summary: gh-100739: Respect mock spec when checking for unsafe prefixes (GH-100740) (cherry picked from commit 7f1eefc6f4843f0fca60308f557a71af11d18a53) Co-authored-by: Christian Klein <167265+cklein at users.noreply.github.com> Co-authored-by: Nikita Sobolev files: A Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst M Lib/unittest/mock.py M Lib/unittest/test/testmock/testmock.py diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 7832092d4b98..7453dfa187e2 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -643,7 +643,7 @@ def __getattr__(self, name): raise AttributeError("Mock object has no attribute %r" % name) elif _is_magic(name): raise AttributeError(name) - if not self._mock_unsafe: + if not self._mock_unsafe and (not self._mock_methods or name not in self._mock_methods): if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')): raise AttributeError( f"{name!r} is not a valid assertion. Use a spec " diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index fdba543b5351..535f9083b76c 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -1644,6 +1644,22 @@ def test_mock_unsafe(self): m.aseert_foo_call() m.assrt_foo_call() + # gh-100739 + def test_mock_safe_with_spec(self): + class Foo(object): + def assert_bar(self): + pass + + def assertSome(self): + pass + + m = Mock(spec=Foo) + m.assert_bar() + m.assertSome() + + m.assert_bar.assert_called_once() + m.assertSome.assert_called_once() + #Issue21262 def test_assert_not_called(self): m = Mock() diff --git a/Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst b/Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst new file mode 100644 index 000000000000..4753e7b40825 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst @@ -0,0 +1 @@ +Fix ``unittest.mock.Mock`` not respecting the spec for attribute names prefixed with ``assert``. From webhook-mailer at python.org Wed Jan 4 22:34:50 2023 From: webhook-mailer at python.org (FFY00) Date: Thu, 05 Jan 2023 03:34:50 -0000 Subject: [Python-checkins] GH-100766: Note that locale.LC_MESSAGES is not universal (GH-100702) Message-ID: https://github.com/python/cpython/commit/105e37395dbd9790f81677e39ec9bd000590728f commit: 105e37395dbd9790f81677e39ec9bd000590728f branch: main author: Tzu-ping Chung committer: FFY00 date: 2023-01-05T03:34:30Z summary: GH-100766: Note that locale.LC_MESSAGES is not universal (GH-100702) files: M Doc/library/locale.rst diff --git a/Doc/library/locale.rst b/Doc/library/locale.rst index 1e46b0f3bf5b..f726f8397c96 100644 --- a/Doc/library/locale.rst +++ b/Doc/library/locale.rst @@ -496,6 +496,9 @@ The :mod:`locale` module defines the following exception and functions: system, like those returned by :func:`os.strerror` might be affected by this category. + This value may not be available on operating systems not conforming to the + POSIX standard, most notably Windows. + .. data:: LC_NUMERIC From webhook-mailer at python.org Wed Jan 4 22:46:19 2023 From: webhook-mailer at python.org (FFY00) Date: Thu, 05 Jan 2023 03:46:19 -0000 Subject: [Python-checkins] GH-100766: Note that locale.LC_MESSAGES is not universal (GH-100767) Message-ID: https://github.com/python/cpython/commit/a34f0bdcf6a3c91f4b80a788205416e82e95f70f commit: a34f0bdcf6a3c91f4b80a788205416e82e95f70f branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: FFY00 date: 2023-01-05T03:46:13Z summary: GH-100766: Note that locale.LC_MESSAGES is not universal (GH-100767) Co-authored-by: Tzu-ping Chung files: M Doc/library/locale.rst diff --git a/Doc/library/locale.rst b/Doc/library/locale.rst index fd5d41a76a13..0614930cb01e 100644 --- a/Doc/library/locale.rst +++ b/Doc/library/locale.rst @@ -508,6 +508,9 @@ The :mod:`locale` module defines the following exception and functions: system, like those returned by :func:`os.strerror` might be affected by this category. + This value may not be available on operating systems not conforming to the + POSIX standard, most notably Windows. + .. data:: LC_NUMERIC From webhook-mailer at python.org Wed Jan 4 22:46:35 2023 From: webhook-mailer at python.org (FFY00) Date: Thu, 05 Jan 2023 03:46:35 -0000 Subject: [Python-checkins] GH-100766: Note that locale.LC_MESSAGES is not universal (GH-100768) Message-ID: https://github.com/python/cpython/commit/825ad05e1f948d6f19c3564a392e027aaebf0d37 commit: 825ad05e1f948d6f19c3564a392e027aaebf0d37 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: FFY00 date: 2023-01-05T03:46:29Z summary: GH-100766: Note that locale.LC_MESSAGES is not universal (GH-100768) Co-authored-by: Tzu-ping Chung files: M Doc/library/locale.rst diff --git a/Doc/library/locale.rst b/Doc/library/locale.rst index cc1f5b427acc..61ca31623108 100644 --- a/Doc/library/locale.rst +++ b/Doc/library/locale.rst @@ -484,6 +484,9 @@ The :mod:`locale` module defines the following exception and functions: system, like those returned by :func:`os.strerror` might be affected by this category. + This value may not be available on operating systems not conforming to the + POSIX standard, most notably Windows. + .. data:: LC_NUMERIC From webhook-mailer at python.org Thu Jan 5 07:20:15 2023 From: webhook-mailer at python.org (markshannon) Date: Thu, 05 Jan 2023 12:20:15 -0000 Subject: [Python-checkins] GH-100288: Remove LOAD_ATTR_METHOD_WITH_DICT instruction. (GH-100753) Message-ID: https://github.com/python/cpython/commit/f20c553a458659f247fac1fb829f8172aa32f69a commit: f20c553a458659f247fac1fb829f8172aa32f69a branch: main author: Mark Shannon committer: markshannon date: 2023-01-05T12:20:09Z summary: GH-100288: Remove LOAD_ATTR_METHOD_WITH_DICT instruction. (GH-100753) files: A Misc/NEWS.d/next/Core and Builtins/2023-01-04-16-40-55.gh-issue-100288.hRSRaT.rst M Include/internal/pycore_opcode.h M Include/opcode.h M Lib/opcode.py M Python/bytecodes.c M Python/generated_cases.c.h M Python/opcode_targets.h M Python/specialize.c diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index da8a272f2fa2..685ad53c7b16 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -153,7 +153,6 @@ const uint8_t _PyOpcode_Deopt[256] = { [LOAD_ATTR_INSTANCE_VALUE] = LOAD_ATTR, [LOAD_ATTR_METHOD_LAZY_DICT] = LOAD_ATTR, [LOAD_ATTR_METHOD_NO_DICT] = LOAD_ATTR, - [LOAD_ATTR_METHOD_WITH_DICT] = LOAD_ATTR, [LOAD_ATTR_METHOD_WITH_VALUES] = LOAD_ATTR, [LOAD_ATTR_MODULE] = LOAD_ATTR, [LOAD_ATTR_PROPERTY] = LOAD_ATTR, @@ -313,12 +312,12 @@ static const char *const _PyOpcode_OpName[263] = { [LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT", [LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT", [LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT", - [LOAD_ATTR_METHOD_WITH_DICT] = "LOAD_ATTR_METHOD_WITH_DICT", + [LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES", [LIST_TO_TUPLE] = "LIST_TO_TUPLE", [RETURN_VALUE] = "RETURN_VALUE", [IMPORT_STAR] = "IMPORT_STAR", [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS", - [LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES", + [LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST", [ASYNC_GEN_WRAP] = "ASYNC_GEN_WRAP", [PREP_RERAISE_STAR] = "PREP_RERAISE_STAR", [POP_EXCEPT] = "POP_EXCEPT", @@ -345,7 +344,7 @@ static const char *const _PyOpcode_OpName[263] = { [JUMP_FORWARD] = "JUMP_FORWARD", [JUMP_IF_FALSE_OR_POP] = "JUMP_IF_FALSE_OR_POP", [JUMP_IF_TRUE_OR_POP] = "JUMP_IF_TRUE_OR_POP", - [LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST", + [LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST", [POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE", [POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE", [LOAD_GLOBAL] = "LOAD_GLOBAL", @@ -353,7 +352,7 @@ static const char *const _PyOpcode_OpName[263] = { [CONTAINS_OP] = "CONTAINS_OP", [RERAISE] = "RERAISE", [COPY] = "COPY", - [LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST", + [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST", [BINARY_OP] = "BINARY_OP", [SEND] = "SEND", [LOAD_FAST] = "LOAD_FAST", @@ -373,9 +372,9 @@ static const char *const _PyOpcode_OpName[263] = { [STORE_DEREF] = "STORE_DEREF", [DELETE_DEREF] = "DELETE_DEREF", [JUMP_BACKWARD] = "JUMP_BACKWARD", - [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST", - [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX", [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN", + [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX", + [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE", [EXTENDED_ARG] = "EXTENDED_ARG", [LIST_APPEND] = "LIST_APPEND", [SET_ADD] = "SET_ADD", @@ -385,24 +384,24 @@ static const char *const _PyOpcode_OpName[263] = { [YIELD_VALUE] = "YIELD_VALUE", [RESUME] = "RESUME", [MATCH_CLASS] = "MATCH_CLASS", - [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE", [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE", + [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT", [FORMAT_VALUE] = "FORMAT_VALUE", [BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP", [BUILD_STRING] = "BUILD_STRING", - [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT", [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT", [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", + [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT", [LIST_EXTEND] = "LIST_EXTEND", [SET_UPDATE] = "SET_UPDATE", [DICT_MERGE] = "DICT_MERGE", [DICT_UPDATE] = "DICT_UPDATE", - [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT", [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST", [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE", [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE", + [170] = "<170>", [CALL] = "CALL", [KW_NAMES] = "KW_NAMES", [173] = "<173>", @@ -499,6 +498,7 @@ static const char *const _PyOpcode_OpName[263] = { #endif #define EXTRA_CASES \ + case 170: \ case 173: \ case 174: \ case 175: \ diff --git a/Include/opcode.h b/Include/opcode.h index 888250ed37e8..246a463be5e0 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -174,23 +174,22 @@ extern "C" { #define LOAD_ATTR_WITH_HINT 78 #define LOAD_ATTR_METHOD_LAZY_DICT 79 #define LOAD_ATTR_METHOD_NO_DICT 80 -#define LOAD_ATTR_METHOD_WITH_DICT 81 -#define LOAD_ATTR_METHOD_WITH_VALUES 86 -#define LOAD_CONST__LOAD_FAST 113 -#define LOAD_FAST__LOAD_CONST 121 -#define LOAD_FAST__LOAD_FAST 141 -#define LOAD_GLOBAL_BUILTIN 143 -#define LOAD_GLOBAL_MODULE 153 -#define STORE_ATTR_INSTANCE_VALUE 154 -#define STORE_ATTR_SLOT 158 -#define STORE_ATTR_WITH_HINT 159 -#define STORE_FAST__LOAD_FAST 160 -#define STORE_FAST__STORE_FAST 161 -#define STORE_SUBSCR_DICT 166 -#define STORE_SUBSCR_LIST_INT 167 -#define UNPACK_SEQUENCE_LIST 168 -#define UNPACK_SEQUENCE_TUPLE 169 -#define UNPACK_SEQUENCE_TWO_TUPLE 170 +#define LOAD_ATTR_METHOD_WITH_VALUES 81 +#define LOAD_CONST__LOAD_FAST 86 +#define LOAD_FAST__LOAD_CONST 113 +#define LOAD_FAST__LOAD_FAST 121 +#define LOAD_GLOBAL_BUILTIN 141 +#define LOAD_GLOBAL_MODULE 143 +#define STORE_ATTR_INSTANCE_VALUE 153 +#define STORE_ATTR_SLOT 154 +#define STORE_ATTR_WITH_HINT 158 +#define STORE_FAST__LOAD_FAST 159 +#define STORE_FAST__STORE_FAST 160 +#define STORE_SUBSCR_DICT 161 +#define STORE_SUBSCR_LIST_INT 166 +#define UNPACK_SEQUENCE_LIST 167 +#define UNPACK_SEQUENCE_TUPLE 168 +#define UNPACK_SEQUENCE_TWO_TUPLE 169 #define DO_TRACING 255 #define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\ diff --git a/Lib/opcode.py b/Lib/opcode.py index fc57affbac58..9271eb47e3a4 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -336,7 +336,6 @@ def pseudo_op(name, op, real_ops): # These will always push [unbound method, self] onto the stack. "LOAD_ATTR_METHOD_LAZY_DICT", "LOAD_ATTR_METHOD_NO_DICT", - "LOAD_ATTR_METHOD_WITH_DICT", "LOAD_ATTR_METHOD_WITH_VALUES", ], "LOAD_CONST": [ diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-04-16-40-55.gh-issue-100288.hRSRaT.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-04-16-40-55.gh-issue-100288.hRSRaT.rst new file mode 100644 index 000000000000..5a1b50babbf5 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-04-16-40-55.gh-issue-100288.hRSRaT.rst @@ -0,0 +1,2 @@ +Remove the LOAD_ATTR_METHOD_WITH_DICT specialized instruction. Stats show it +is not useful. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index dec122a78223..5f8871c4b3de 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2605,33 +2605,6 @@ dummy_func( JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); } - // error: LOAD_ATTR has irregular stack effect - inst(LOAD_ATTR_METHOD_WITH_DICT) { - /* Can be either a managed dict, or a tp_dictoffset offset.*/ - assert(cframe.use_tracing == 0); - PyObject *self = TOP(); - PyTypeObject *self_cls = Py_TYPE(self); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - - DEOPT_IF(self_cls->tp_version_tag != read_u32(cache->type_version), - LOAD_ATTR); - /* Treat index as a signed 16 bit value */ - Py_ssize_t dictoffset = self_cls->tp_dictoffset; - assert(dictoffset > 0); - PyDictObject **dictptr = (PyDictObject**)(((char *)self)+dictoffset); - PyDictObject *dict = *dictptr; - DEOPT_IF(dict == NULL, LOAD_ATTR); - DEOPT_IF(dict->ma_keys->dk_version != read_u32(cache->keys_version), - LOAD_ATTR); - STAT_INC(LOAD_ATTR, hit); - PyObject *res = read_obj(cache->descr); - assert(res != NULL); - assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)); - SET_TOP(Py_NewRef(res)); - PUSH(self); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); - } - // error: LOAD_ATTR has irregular stack effect inst(LOAD_ATTR_METHOD_NO_DICT) { assert(cframe.use_tracing == 0); @@ -3517,7 +3490,7 @@ family(load_attr) = { LOAD_ATTR, LOAD_ATTR_CLASS, LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, LOAD_ATTR_INSTANCE_VALUE, LOAD_ATTR_MODULE, LOAD_ATTR_PROPERTY, LOAD_ATTR_SLOT, LOAD_ATTR_WITH_HINT, - LOAD_ATTR_METHOD_LAZY_DICT, LOAD_ATTR_METHOD_NO_DICT, LOAD_ATTR_METHOD_WITH_DICT, + LOAD_ATTR_METHOD_LAZY_DICT, LOAD_ATTR_METHOD_NO_DICT, LOAD_ATTR_METHOD_WITH_VALUES }; family(load_global) = { LOAD_GLOBAL, LOAD_GLOBAL_BUILTIN, diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 3218bd091433..fa1f941044e0 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2960,33 +2960,6 @@ DISPATCH(); } - TARGET(LOAD_ATTR_METHOD_WITH_DICT) { - /* Can be either a managed dict, or a tp_dictoffset offset.*/ - assert(cframe.use_tracing == 0); - PyObject *self = TOP(); - PyTypeObject *self_cls = Py_TYPE(self); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - - DEOPT_IF(self_cls->tp_version_tag != read_u32(cache->type_version), - LOAD_ATTR); - /* Treat index as a signed 16 bit value */ - Py_ssize_t dictoffset = self_cls->tp_dictoffset; - assert(dictoffset > 0); - PyDictObject **dictptr = (PyDictObject**)(((char *)self)+dictoffset); - PyDictObject *dict = *dictptr; - DEOPT_IF(dict == NULL, LOAD_ATTR); - DEOPT_IF(dict->ma_keys->dk_version != read_u32(cache->keys_version), - LOAD_ATTR); - STAT_INC(LOAD_ATTR, hit); - PyObject *res = read_obj(cache->descr); - assert(res != NULL); - assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)); - SET_TOP(Py_NewRef(res)); - PUSH(self); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); - DISPATCH(); - } - TARGET(LOAD_ATTR_METHOD_NO_DICT) { assert(cframe.use_tracing == 0); PyObject *self = TOP(); diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index be3ad01c151c..0716fccb964e 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -80,12 +80,12 @@ static void *opcode_targets[256] = { &&TARGET_LOAD_ATTR_WITH_HINT, &&TARGET_LOAD_ATTR_METHOD_LAZY_DICT, &&TARGET_LOAD_ATTR_METHOD_NO_DICT, - &&TARGET_LOAD_ATTR_METHOD_WITH_DICT, + &&TARGET_LOAD_ATTR_METHOD_WITH_VALUES, &&TARGET_LIST_TO_TUPLE, &&TARGET_RETURN_VALUE, &&TARGET_IMPORT_STAR, &&TARGET_SETUP_ANNOTATIONS, - &&TARGET_LOAD_ATTR_METHOD_WITH_VALUES, + &&TARGET_LOAD_CONST__LOAD_FAST, &&TARGET_ASYNC_GEN_WRAP, &&TARGET_PREP_RERAISE_STAR, &&TARGET_POP_EXCEPT, @@ -112,7 +112,7 @@ static void *opcode_targets[256] = { &&TARGET_JUMP_FORWARD, &&TARGET_JUMP_IF_FALSE_OR_POP, &&TARGET_JUMP_IF_TRUE_OR_POP, - &&TARGET_LOAD_CONST__LOAD_FAST, + &&TARGET_LOAD_FAST__LOAD_CONST, &&TARGET_POP_JUMP_IF_FALSE, &&TARGET_POP_JUMP_IF_TRUE, &&TARGET_LOAD_GLOBAL, @@ -120,7 +120,7 @@ static void *opcode_targets[256] = { &&TARGET_CONTAINS_OP, &&TARGET_RERAISE, &&TARGET_COPY, - &&TARGET_LOAD_FAST__LOAD_CONST, + &&TARGET_LOAD_FAST__LOAD_FAST, &&TARGET_BINARY_OP, &&TARGET_SEND, &&TARGET_LOAD_FAST, @@ -140,9 +140,9 @@ static void *opcode_targets[256] = { &&TARGET_STORE_DEREF, &&TARGET_DELETE_DEREF, &&TARGET_JUMP_BACKWARD, - &&TARGET_LOAD_FAST__LOAD_FAST, - &&TARGET_CALL_FUNCTION_EX, &&TARGET_LOAD_GLOBAL_BUILTIN, + &&TARGET_CALL_FUNCTION_EX, + &&TARGET_LOAD_GLOBAL_MODULE, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD, @@ -152,24 +152,24 @@ static void *opcode_targets[256] = { &&TARGET_YIELD_VALUE, &&TARGET_RESUME, &&TARGET_MATCH_CLASS, - &&TARGET_LOAD_GLOBAL_MODULE, &&TARGET_STORE_ATTR_INSTANCE_VALUE, + &&TARGET_STORE_ATTR_SLOT, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, - &&TARGET_STORE_ATTR_SLOT, &&TARGET_STORE_ATTR_WITH_HINT, &&TARGET_STORE_FAST__LOAD_FAST, &&TARGET_STORE_FAST__STORE_FAST, + &&TARGET_STORE_SUBSCR_DICT, &&TARGET_LIST_EXTEND, &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, &&TARGET_DICT_UPDATE, - &&TARGET_STORE_SUBSCR_DICT, &&TARGET_STORE_SUBSCR_LIST_INT, &&TARGET_UNPACK_SEQUENCE_LIST, &&TARGET_UNPACK_SEQUENCE_TUPLE, &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, + &&_unknown_opcode, &&TARGET_CALL, &&TARGET_KW_NAMES, &&_unknown_opcode, diff --git a/Python/specialize.c b/Python/specialize.c index d9af7b742d54..48814dad671e 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1091,9 +1091,8 @@ PyObject *descr, DescriptorClassification kind) SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_HAS_MANAGED_DICT); goto fail; case OFFSET_DICT: - assert(owner_cls->tp_dictoffset > 0 && owner_cls->tp_dictoffset <= INT16_MAX); - _py_set_opcode(instr, LOAD_ATTR_METHOD_WITH_DICT); - break; + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NOT_MANAGED_DICT); + goto fail; case LAZY_DICT: assert(owner_cls->tp_dictoffset > 0 && owner_cls->tp_dictoffset <= INT16_MAX); _py_set_opcode(instr, LOAD_ATTR_METHOD_LAZY_DICT); From webhook-mailer at python.org Thu Jan 5 11:06:26 2023 From: webhook-mailer at python.org (markshannon) Date: Thu, 05 Jan 2023 16:06:26 -0000 Subject: [Python-checkins] GH-99005: Add `CALL_INTRINSIC_1` instruction (GH-100771) Message-ID: https://github.com/python/cpython/commit/28187141cc34063ef857976ddbca87ba09a882c2 commit: 28187141cc34063ef857976ddbca87ba09a882c2 branch: main author: Mark Shannon committer: markshannon date: 2023-01-05T16:05:51Z summary: GH-99005: Add `CALL_INTRINSIC_1` instruction (GH-100771) * Remove PRINT_EXPR instruction * Remove STOPITERATION_ERROR instruction * Remove IMPORT_STAR instruction files: A Include/internal/pycore_intrinsics.h A Misc/NEWS.d/next/Core and Builtins/2023-01-05-13-54-00.gh-issue-99005.D7H6j4.rst A Python/intrinsics.c M Doc/library/dis.rst M Include/internal/pycore_opcode.h M Include/opcode.h M Lib/importlib/_bootstrap_external.py M Lib/opcode.py M Lib/test/test_dis.py M Makefile.pre.in M PCbuild/_freeze_module.vcxproj M PCbuild/_freeze_module.vcxproj.filters M PCbuild/pythoncore.vcxproj M PCbuild/pythoncore.vcxproj.filters M Python/bytecodes.c M Python/ceval.c M Python/compile.c M Python/generated_cases.c.h M Python/opcode_targets.h diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 30bbf95be634..33ef6d7ed495 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -607,15 +607,6 @@ the original TOS1. .. versionadded:: 3.12 -.. opcode:: STOPITERATION_ERROR - - Handles a StopIteration raised in a generator or coroutine. - If TOS is an instance of :exc:`StopIteration`, or :exc:`StopAsyncIteration` - replace it with a :exc:`RuntimeError`. - - .. versionadded:: 3.12 - - .. opcode:: BEFORE_ASYNC_WITH Resolves ``__aenter__`` and ``__aexit__`` from the object on top of the @@ -627,13 +618,6 @@ the original TOS1. **Miscellaneous opcodes** -.. opcode:: PRINT_EXPR - - Implements the expression statement for the interactive mode. TOS is removed - from the stack and printed. In non-interactive mode, an expression statement - is terminated with :opcode:`POP_TOP`. - - .. opcode:: SET_ADD (i) Calls ``set.add(TOS1[-i], TOS)``. Used to implement set comprehensions. @@ -682,13 +666,6 @@ iterations of the loop. .. versionadded:: 3.6 -.. opcode:: IMPORT_STAR - - Loads all symbols not starting with ``'_'`` directly from the module TOS to - the local namespace. The module is popped after loading all names. This - opcode implements ``from module import *``. - - .. opcode:: POP_EXCEPT Pops a value from the stack, which is used to restore the exception state. @@ -1422,6 +1399,22 @@ iterations of the loop. they use their arg. +.. opcode:: CALL_INTRINSIC_1 + + Calls an intrinsic function with one argument. Passes the TOS as the argument + and sets TOS to the result. Used to implement functionality that is necessary + but not performance critical. + + The operand determines which intrinsic function is called: + + * ``0`` Not valid + * ``1`` Prints the argument to standard out. Used in the REPL. + * ``2`` Performs ``import *`` for the named module. + * ``3`` Extracts the return value from a ``StopIteration`` exception. + + .. versionadded:: 3.12 + + **Pseudo-instructions** These opcodes do not appear in python bytecode, they are used by the compiler diff --git a/Include/internal/pycore_intrinsics.h b/Include/internal/pycore_intrinsics.h new file mode 100644 index 000000000000..92f06c736267 --- /dev/null +++ b/Include/internal/pycore_intrinsics.h @@ -0,0 +1,10 @@ + +#define INTRINSIC_PRINT 1 +#define INTRINSIC_IMPORT_STAR 2 +#define INTRINSIC_STOPITERATION_ERROR 3 + +#define MAX_INTRINSIC_1 3 + +typedef PyObject *(*instrinsic_func1)(PyThreadState* tstate, PyObject *value); + +extern instrinsic_func1 _PyIntrinsics_UnaryFunctions[]; diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index 685ad53c7b16..e952690cba3a 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -85,6 +85,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [CALL_BUILTIN_CLASS] = CALL, [CALL_BUILTIN_FAST_WITH_KEYWORDS] = CALL, [CALL_FUNCTION_EX] = CALL_FUNCTION_EX, + [CALL_INTRINSIC_1] = CALL_INTRINSIC_1, [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = CALL, [CALL_NO_KW_BUILTIN_FAST] = CALL, [CALL_NO_KW_BUILTIN_O] = CALL, @@ -134,7 +135,6 @@ const uint8_t _PyOpcode_Deopt[256] = { [GET_YIELD_FROM_ITER] = GET_YIELD_FROM_ITER, [IMPORT_FROM] = IMPORT_FROM, [IMPORT_NAME] = IMPORT_NAME, - [IMPORT_STAR] = IMPORT_STAR, [INTERPRETER_EXIT] = INTERPRETER_EXIT, [IS_OP] = IS_OP, [JUMP_BACKWARD] = JUMP_BACKWARD, @@ -187,7 +187,6 @@ const uint8_t _PyOpcode_Deopt[256] = { [POP_JUMP_IF_TRUE] = POP_JUMP_IF_TRUE, [POP_TOP] = POP_TOP, [PREP_RERAISE_STAR] = PREP_RERAISE_STAR, - [PRINT_EXPR] = PRINT_EXPR, [PUSH_EXC_INFO] = PUSH_EXC_INFO, [PUSH_NULL] = PUSH_NULL, [RAISE_VARARGS] = RAISE_VARARGS, @@ -199,7 +198,6 @@ const uint8_t _PyOpcode_Deopt[256] = { [SETUP_ANNOTATIONS] = SETUP_ANNOTATIONS, [SET_ADD] = SET_ADD, [SET_UPDATE] = SET_UPDATE, - [STOPITERATION_ERROR] = STOPITERATION_ERROR, [STORE_ATTR] = STORE_ATTR, [STORE_ATTR_INSTANCE_VALUE] = STORE_ATTR, [STORE_ATTR_SLOT] = STORE_ATTR, @@ -294,30 +292,30 @@ static const char *const _PyOpcode_OpName[263] = { [STORE_SUBSCR] = "STORE_SUBSCR", [DELETE_SUBSCR] = "DELETE_SUBSCR", [FOR_ITER_TUPLE] = "FOR_ITER_TUPLE", - [STOPITERATION_ERROR] = "STOPITERATION_ERROR", [FOR_ITER_RANGE] = "FOR_ITER_RANGE", [FOR_ITER_GEN] = "FOR_ITER_GEN", [LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS", [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN", + [LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE", [GET_ITER] = "GET_ITER", [GET_YIELD_FROM_ITER] = "GET_YIELD_FROM_ITER", - [PRINT_EXPR] = "PRINT_EXPR", - [LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS", - [LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE", [LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE", - [LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR", - [RETURN_GENERATOR] = "RETURN_GENERATOR", + [LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS", [LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY", [LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT", + [LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR", + [RETURN_GENERATOR] = "RETURN_GENERATOR", [LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT", [LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT", [LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT", [LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES", + [LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST", + [LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST", [LIST_TO_TUPLE] = "LIST_TO_TUPLE", [RETURN_VALUE] = "RETURN_VALUE", - [IMPORT_STAR] = "IMPORT_STAR", + [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST", [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS", - [LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST", + [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN", [ASYNC_GEN_WRAP] = "ASYNC_GEN_WRAP", [PREP_RERAISE_STAR] = "PREP_RERAISE_STAR", [POP_EXCEPT] = "POP_EXCEPT", @@ -344,7 +342,7 @@ static const char *const _PyOpcode_OpName[263] = { [JUMP_FORWARD] = "JUMP_FORWARD", [JUMP_IF_FALSE_OR_POP] = "JUMP_IF_FALSE_OR_POP", [JUMP_IF_TRUE_OR_POP] = "JUMP_IF_TRUE_OR_POP", - [LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST", + [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE", [POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE", [POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE", [LOAD_GLOBAL] = "LOAD_GLOBAL", @@ -352,7 +350,7 @@ static const char *const _PyOpcode_OpName[263] = { [CONTAINS_OP] = "CONTAINS_OP", [RERAISE] = "RERAISE", [COPY] = "COPY", - [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST", + [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE", [BINARY_OP] = "BINARY_OP", [SEND] = "SEND", [LOAD_FAST] = "LOAD_FAST", @@ -372,9 +370,9 @@ static const char *const _PyOpcode_OpName[263] = { [STORE_DEREF] = "STORE_DEREF", [DELETE_DEREF] = "DELETE_DEREF", [JUMP_BACKWARD] = "JUMP_BACKWARD", - [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN", + [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT", [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX", - [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE", + [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT", [EXTENDED_ARG] = "EXTENDED_ARG", [LIST_APPEND] = "LIST_APPEND", [SET_ADD] = "SET_ADD", @@ -384,27 +382,27 @@ static const char *const _PyOpcode_OpName[263] = { [YIELD_VALUE] = "YIELD_VALUE", [RESUME] = "RESUME", [MATCH_CLASS] = "MATCH_CLASS", - [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE", - [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT", + [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", + [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", [FORMAT_VALUE] = "FORMAT_VALUE", [BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP", [BUILD_STRING] = "BUILD_STRING", - [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT", - [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", - [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT", + [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", + [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST", + [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE", [LIST_EXTEND] = "LIST_EXTEND", [SET_UPDATE] = "SET_UPDATE", [DICT_MERGE] = "DICT_MERGE", [DICT_UPDATE] = "DICT_UPDATE", - [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", - [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST", - [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE", [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE", + [167] = "<167>", + [168] = "<168>", + [169] = "<169>", [170] = "<170>", [CALL] = "CALL", [KW_NAMES] = "KW_NAMES", - [173] = "<173>", + [CALL_INTRINSIC_1] = "CALL_INTRINSIC_1", [174] = "<174>", [175] = "<175>", [176] = "<176>", @@ -498,8 +496,10 @@ static const char *const _PyOpcode_OpName[263] = { #endif #define EXTRA_CASES \ + case 167: \ + case 168: \ + case 169: \ case 170: \ - case 173: \ case 174: \ case 175: \ case 176: \ diff --git a/Include/opcode.h b/Include/opcode.h index 246a463be5e0..44b77250ad68 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -37,16 +37,13 @@ extern "C" { #define CLEANUP_THROW 55 #define STORE_SUBSCR 60 #define DELETE_SUBSCR 61 -#define STOPITERATION_ERROR 63 #define GET_ITER 68 #define GET_YIELD_FROM_ITER 69 -#define PRINT_EXPR 70 #define LOAD_BUILD_CLASS 71 #define LOAD_ASSERTION_ERROR 74 #define RETURN_GENERATOR 75 #define LIST_TO_TUPLE 82 #define RETURN_VALUE 83 -#define IMPORT_STAR 84 #define SETUP_ANNOTATIONS 85 #define ASYNC_GEN_WRAP 87 #define PREP_RERAISE_STAR 88 @@ -120,6 +117,7 @@ extern "C" { #define DICT_UPDATE 165 #define CALL 171 #define KW_NAMES 172 +#define CALL_INTRINSIC_1 173 #define MIN_PSEUDO_OPCODE 256 #define SETUP_FINALLY 256 #define SETUP_CLEANUP 257 @@ -163,33 +161,33 @@ extern "C" { #define COMPARE_OP_STR_JUMP 58 #define FOR_ITER_LIST 59 #define FOR_ITER_TUPLE 62 -#define FOR_ITER_RANGE 64 -#define FOR_ITER_GEN 65 -#define LOAD_ATTR_CLASS 66 -#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 67 -#define LOAD_ATTR_INSTANCE_VALUE 72 -#define LOAD_ATTR_MODULE 73 -#define LOAD_ATTR_PROPERTY 76 -#define LOAD_ATTR_SLOT 77 -#define LOAD_ATTR_WITH_HINT 78 -#define LOAD_ATTR_METHOD_LAZY_DICT 79 -#define LOAD_ATTR_METHOD_NO_DICT 80 -#define LOAD_ATTR_METHOD_WITH_VALUES 81 -#define LOAD_CONST__LOAD_FAST 86 -#define LOAD_FAST__LOAD_CONST 113 -#define LOAD_FAST__LOAD_FAST 121 -#define LOAD_GLOBAL_BUILTIN 141 -#define LOAD_GLOBAL_MODULE 143 -#define STORE_ATTR_INSTANCE_VALUE 153 -#define STORE_ATTR_SLOT 154 -#define STORE_ATTR_WITH_HINT 158 -#define STORE_FAST__LOAD_FAST 159 -#define STORE_FAST__STORE_FAST 160 -#define STORE_SUBSCR_DICT 161 -#define STORE_SUBSCR_LIST_INT 166 -#define UNPACK_SEQUENCE_LIST 167 -#define UNPACK_SEQUENCE_TUPLE 168 -#define UNPACK_SEQUENCE_TWO_TUPLE 169 +#define FOR_ITER_RANGE 63 +#define FOR_ITER_GEN 64 +#define LOAD_ATTR_CLASS 65 +#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 66 +#define LOAD_ATTR_INSTANCE_VALUE 67 +#define LOAD_ATTR_MODULE 70 +#define LOAD_ATTR_PROPERTY 72 +#define LOAD_ATTR_SLOT 73 +#define LOAD_ATTR_WITH_HINT 76 +#define LOAD_ATTR_METHOD_LAZY_DICT 77 +#define LOAD_ATTR_METHOD_NO_DICT 78 +#define LOAD_ATTR_METHOD_WITH_VALUES 79 +#define LOAD_CONST__LOAD_FAST 80 +#define LOAD_FAST__LOAD_CONST 81 +#define LOAD_FAST__LOAD_FAST 84 +#define LOAD_GLOBAL_BUILTIN 86 +#define LOAD_GLOBAL_MODULE 113 +#define STORE_ATTR_INSTANCE_VALUE 121 +#define STORE_ATTR_SLOT 141 +#define STORE_ATTR_WITH_HINT 143 +#define STORE_FAST__LOAD_FAST 153 +#define STORE_FAST__STORE_FAST 154 +#define STORE_SUBSCR_DICT 158 +#define STORE_SUBSCR_LIST_INT 159 +#define UNPACK_SEQUENCE_LIST 160 +#define UNPACK_SEQUENCE_TUPLE 161 +#define UNPACK_SEQUENCE_TWO_TUPLE 166 #define DO_TRACING 255 #define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\ diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 71a16064b8ec..e700ce634aca 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -426,6 +426,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.12a1 3510 (FOR_ITER leaves iterator on the stack) # Python 3.12a1 3511 (Add STOPITERATION_ERROR instruction) # Python 3.12a1 3512 (Remove all unused consts from code objects) +# Python 3.12a1 3513 (Add CALL_INTRINSIC_1 instruction, removed STOPITERATION_ERROR, PRINT_EXPR, IMPORT_STAR) # Python 3.13 will start with 3550 @@ -438,7 +439,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3512).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3513).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c diff --git a/Lib/opcode.py b/Lib/opcode.py index 9271eb47e3a4..a5dea8c6fd24 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -112,11 +112,9 @@ def pseudo_op(name, op, real_ops): def_op('STORE_SUBSCR', 60) def_op('DELETE_SUBSCR', 61) -def_op('STOPITERATION_ERROR', 63) - def_op('GET_ITER', 68) def_op('GET_YIELD_FROM_ITER', 69) -def_op('PRINT_EXPR', 70) + def_op('LOAD_BUILD_CLASS', 71) def_op('LOAD_ASSERTION_ERROR', 74) @@ -124,7 +122,7 @@ def pseudo_op(name, op, real_ops): def_op('LIST_TO_TUPLE', 82) def_op('RETURN_VALUE', 83) -def_op('IMPORT_STAR', 84) + def_op('SETUP_ANNOTATIONS', 85) def_op('ASYNC_GEN_WRAP', 87) @@ -220,7 +218,7 @@ def pseudo_op(name, op, real_ops): def_op('CALL', 171) def_op('KW_NAMES', 172) hasconst.append(172) - +def_op('CALL_INTRINSIC_1', 173) hasarg.extend([op for op in opmap.values() if op >= HAVE_ARGUMENT]) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 950af3ceb24f..c2a339ac7cba 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -543,7 +543,7 @@ async def _asyncwith(c): >> COPY 3 POP_EXCEPT RERAISE 1 - >> STOPITERATION_ERROR + >> CALL_INTRINSIC_1 3 RERAISE 1 ExceptionTable: 12 rows diff --git a/Makefile.pre.in b/Makefile.pre.in index 1f8bd561f61d..397fc996192d 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -395,6 +395,7 @@ PYTHON_OBJS= \ Python/import.o \ Python/importdl.o \ Python/initconfig.o \ + Python/intrinsics.o \ Python/marshal.o \ Python/modsupport.o \ Python/mysnprintf.o \ @@ -1650,6 +1651,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_initconfig.h \ $(srcdir)/Include/internal/pycore_interp.h \ $(srcdir)/Include/internal/pycore_interpreteridobject.h \ + $(srcdir)/Include/internal/pycore_intrinsics.h \ $(srcdir)/Include/internal/pycore_list.h \ $(srcdir)/Include/internal/pycore_long.h \ $(srcdir)/Include/internal/pycore_moduleobject.h \ diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-05-13-54-00.gh-issue-99005.D7H6j4.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-05-13-54-00.gh-issue-99005.D7H6j4.rst new file mode 100644 index 000000000000..bd81cde45bc9 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-05-13-54-00.gh-issue-99005.D7H6j4.rst @@ -0,0 +1,4 @@ +Add new :opcode:`CALL_INSTRINSIC_1` instruction. Remove +:opcode:`IMPORT_STAR`, :opcode:`PRINT_EXPR` and +:opcode:`STOPITERATION_ERROR`, replacing them with the +:opcode:`CALL_INSTRINSIC_1` instruction. diff --git a/PCbuild/_freeze_module.vcxproj b/PCbuild/_freeze_module.vcxproj index fce1f6705100..4f39756019e6 100644 --- a/PCbuild/_freeze_module.vcxproj +++ b/PCbuild/_freeze_module.vcxproj @@ -206,6 +206,7 @@ + diff --git a/PCbuild/_freeze_module.vcxproj.filters b/PCbuild/_freeze_module.vcxproj.filters index dce6278987c5..7d7c4587b9a3 100644 --- a/PCbuild/_freeze_module.vcxproj.filters +++ b/PCbuild/_freeze_module.vcxproj.filters @@ -205,6 +205,9 @@ Source Files + + Source Files + Source Files diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index bb2aaae3317b..78f5234c6342 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -231,6 +231,7 @@ + @@ -520,6 +521,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 339e7cc4937a..c1b531fd818a 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -600,6 +600,9 @@ Include\cpython + + Include\cpython + Include\internal @@ -1151,6 +1154,9 @@ Python + + Source Files + Python diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 5f8871c4b3de..9283f590582a 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -12,6 +12,7 @@ #include "pycore_ceval.h" // _PyEval_SignalAsyncExc() #include "pycore_code.h" #include "pycore_function.h" +#include "pycore_intrinsics.h" #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_moduleobject.h" // PyModuleObject @@ -538,20 +539,11 @@ dummy_func( ERROR_IF(err, error); } - inst(PRINT_EXPR, (value --)) { - PyObject *hook = _PySys_GetAttr(tstate, &_Py_ID(displayhook)); - PyObject *res; - // Can't use ERROR_IF here. - if (hook == NULL) { - _PyErr_SetString(tstate, PyExc_RuntimeError, - "lost sys.displayhook"); - DECREF_INPUTS(); - ERROR_IF(true, error); - } - res = PyObject_CallOneArg(hook, value); - DECREF_INPUTS(); + inst(CALL_INTRINSIC_1, (value -- res)) { + assert(oparg <= MAX_INTRINSIC_1); + res = _PyIntrinsics_UnaryFunctions[oparg](tstate, value); + Py_DECREF(value); ERROR_IF(res == NULL, error); - Py_DECREF(res); } // stack effect: (__array[oparg] -- ) @@ -867,47 +859,6 @@ dummy_func( } } - inst(STOPITERATION_ERROR) { - assert(frame->owner == FRAME_OWNED_BY_GENERATOR); - PyObject *exc = TOP(); - assert(PyExceptionInstance_Check(exc)); - const char *msg = NULL; - if (PyErr_GivenExceptionMatches(exc, PyExc_StopIteration)) { - msg = "generator raised StopIteration"; - if (frame->f_code->co_flags & CO_ASYNC_GENERATOR) { - msg = "async generator raised StopIteration"; - } - else if (frame->f_code->co_flags & CO_COROUTINE) { - msg = "coroutine raised StopIteration"; - } - } - else if ((frame->f_code->co_flags & CO_ASYNC_GENERATOR) && - PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration)) - { - /* code in `gen` raised a StopAsyncIteration error: - raise a RuntimeError. - */ - msg = "async generator raised StopAsyncIteration"; - } - if (msg != NULL) { - PyObject *message = _PyUnicode_FromASCII(msg, strlen(msg)); - if (message == NULL) { - goto error; - } - PyObject *error = PyObject_CallOneArg(PyExc_RuntimeError, message); - if (error == NULL) { - Py_DECREF(message); - goto error; - } - assert(PyExceptionInstance_Check(error)); - SET_TOP(error); - PyException_SetCause(error, Py_NewRef(exc)); - // Steal exc reference, rather than Py_NewRef+Py_DECREF - PyException_SetContext(error, exc); - Py_DECREF(message); - } - } - inst(LOAD_ASSERTION_ERROR, ( -- value)) { value = Py_NewRef(PyExc_AssertionError); } @@ -2065,27 +2016,6 @@ dummy_func( ERROR_IF(res == NULL, error); } - inst(IMPORT_STAR, (from --)) { - PyObject *locals; - int err; - if (_PyFrame_FastToLocalsWithError(frame) < 0) { - DECREF_INPUTS(); - ERROR_IF(true, error); - } - - locals = LOCALS(); - if (locals == NULL) { - _PyErr_SetString(tstate, PyExc_SystemError, - "no locals found during 'import *'"); - DECREF_INPUTS(); - ERROR_IF(true, error); - } - err = import_all_from(tstate, locals, from); - _PyFrame_LocalsToFast(frame, 0); - DECREF_INPUTS(); - ERROR_IF(err, error); - } - inst(IMPORT_FROM, (from -- from, res)) { PyObject *name = GETITEM(names, oparg); res = import_from(tstate, from, name); diff --git a/Python/ceval.c b/Python/ceval.c index 54df1c512502..149b88e1545d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -13,6 +13,7 @@ #include "pycore_ceval.h" // _PyEval_SignalAsyncExc() #include "pycore_code.h" #include "pycore_function.h" +#include "pycore_intrinsics.h" #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_moduleobject.h" // PyModuleObject @@ -204,7 +205,6 @@ static void dtrace_function_return(_PyInterpreterFrame *); static PyObject * import_name(PyThreadState *, _PyInterpreterFrame *, PyObject *, PyObject *, PyObject *); static PyObject * import_from(PyThreadState *, PyObject *, PyObject *); -static int import_all_from(PyThreadState *, PyObject *, PyObject *); static void format_exc_check_arg(PyThreadState *, PyObject *, const char *, PyObject *); static void format_exc_unbound(PyThreadState *tstate, PyCodeObject *co, int oparg); static int check_args_iterable(PyThreadState *, PyObject *func, PyObject *vararg); @@ -3156,95 +3156,6 @@ import_from(PyThreadState *tstate, PyObject *v, PyObject *name) return NULL; } -static int -import_all_from(PyThreadState *tstate, PyObject *locals, PyObject *v) -{ - PyObject *all, *dict, *name, *value; - int skip_leading_underscores = 0; - int pos, err; - - if (_PyObject_LookupAttr(v, &_Py_ID(__all__), &all) < 0) { - return -1; /* Unexpected error */ - } - if (all == NULL) { - if (_PyObject_LookupAttr(v, &_Py_ID(__dict__), &dict) < 0) { - return -1; - } - if (dict == NULL) { - _PyErr_SetString(tstate, PyExc_ImportError, - "from-import-* object has no __dict__ and no __all__"); - return -1; - } - all = PyMapping_Keys(dict); - Py_DECREF(dict); - if (all == NULL) - return -1; - skip_leading_underscores = 1; - } - - for (pos = 0, err = 0; ; pos++) { - name = PySequence_GetItem(all, pos); - if (name == NULL) { - if (!_PyErr_ExceptionMatches(tstate, PyExc_IndexError)) { - err = -1; - } - else { - _PyErr_Clear(tstate); - } - break; - } - if (!PyUnicode_Check(name)) { - PyObject *modname = PyObject_GetAttr(v, &_Py_ID(__name__)); - if (modname == NULL) { - Py_DECREF(name); - err = -1; - break; - } - if (!PyUnicode_Check(modname)) { - _PyErr_Format(tstate, PyExc_TypeError, - "module __name__ must be a string, not %.100s", - Py_TYPE(modname)->tp_name); - } - else { - _PyErr_Format(tstate, PyExc_TypeError, - "%s in %U.%s must be str, not %.100s", - skip_leading_underscores ? "Key" : "Item", - modname, - skip_leading_underscores ? "__dict__" : "__all__", - Py_TYPE(name)->tp_name); - } - Py_DECREF(modname); - Py_DECREF(name); - err = -1; - break; - } - if (skip_leading_underscores) { - if (PyUnicode_READY(name) == -1) { - Py_DECREF(name); - err = -1; - break; - } - if (PyUnicode_READ_CHAR(name, 0) == '_') { - Py_DECREF(name); - continue; - } - } - value = PyObject_GetAttr(v, name); - if (value == NULL) - err = -1; - else if (PyDict_CheckExact(locals)) - err = PyDict_SetItem(locals, name, value); - else - err = PyObject_SetItem(locals, name, value); - Py_DECREF(name); - Py_XDECREF(value); - if (err != 0) - break; - } - Py_DECREF(all); - return err; -} - #define CANNOT_CATCH_MSG "catching classes that do not inherit from "\ "BaseException is not allowed" diff --git a/Python/compile.c b/Python/compile.c index 2527023d18e8..ff29fb42c5d1 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -30,6 +30,7 @@ #include "pycore_ast.h" // _PyAST_GetDocString() #include "pycore_code.h" // _PyCode_New() #include "pycore_compile.h" // _PyFuture_FromAST() +#include "pycore_intrinsics.h" #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_opcode.h" // _PyOpcode_Caches #include "pycore_pymem.h" // _PyMem_IsPtrFreed() @@ -1113,15 +1114,11 @@ stack_effect(int opcode, int oparg, int jump) case GET_ITER: return 0; - case PRINT_EXPR: - return -1; case LOAD_BUILD_CLASS: return 1; case RETURN_VALUE: return -1; - case IMPORT_STAR: - return -1; case SETUP_ANNOTATIONS: return 0; case ASYNC_GEN_WRAP: @@ -1216,10 +1213,6 @@ stack_effect(int opcode, int oparg, int jump) * of __(a)enter__ and push 2 values before jumping to the handler * if an exception be raised. */ return jump ? 1 : 0; - - case STOPITERATION_ERROR: - return 0; - case PREP_RERAISE_STAR: return -1; case RERAISE: @@ -1249,7 +1242,8 @@ stack_effect(int opcode, int oparg, int jump) return 0; case CALL: return -1-oparg; - + case CALL_INTRINSIC_1: + return 0; case CALL_FUNCTION_EX: return -2 - ((oparg & 0x01) != 0); case MAKE_FUNCTION: @@ -2604,7 +2598,7 @@ wrap_in_stopiteration_handler(struct compiler *c) ADDOP_LOAD_CONST(c, NO_LOCATION, Py_None); ADDOP(c, NO_LOCATION, RETURN_VALUE); USE_LABEL(c, handler); - ADDOP(c, NO_LOCATION, STOPITERATION_ERROR); + ADDOP_I(c, NO_LOCATION, CALL_INTRINSIC_1, INTRINSIC_STOPITERATION_ERROR); ADDOP_I(c, NO_LOCATION, RERAISE, 1); return SUCCESS; } @@ -3953,7 +3947,8 @@ compiler_from_import(struct compiler *c, stmt_ty s) if (i == 0 && PyUnicode_READ_CHAR(alias->name, 0) == '*') { assert(n == 1); - ADDOP(c, LOC(s), IMPORT_STAR); + ADDOP_I(c, LOC(s), CALL_INTRINSIC_1, INTRINSIC_IMPORT_STAR); + ADDOP(c, NO_LOCATION, POP_TOP); return SUCCESS; } @@ -4005,7 +4000,8 @@ compiler_stmt_expr(struct compiler *c, location loc, expr_ty value) { if (c->c_interactive && c->c_nestlevel <= 1) { VISIT(c, expr, value); - ADDOP(c, loc, PRINT_EXPR); + ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_PRINT); + ADDOP(c, NO_LOCATION, POP_TOP); return SUCCESS; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index fa1f941044e0..0d4dad40ea54 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -684,22 +684,14 @@ DISPATCH(); } - TARGET(PRINT_EXPR) { + TARGET(CALL_INTRINSIC_1) { PyObject *value = PEEK(1); - PyObject *hook = _PySys_GetAttr(tstate, &_Py_ID(displayhook)); PyObject *res; - // Can't use ERROR_IF here. - if (hook == NULL) { - _PyErr_SetString(tstate, PyExc_RuntimeError, - "lost sys.displayhook"); - Py_DECREF(value); - if (true) goto pop_1_error; - } - res = PyObject_CallOneArg(hook, value); + assert(oparg <= MAX_INTRINSIC_1); + res = _PyIntrinsics_UnaryFunctions[oparg](tstate, value); Py_DECREF(value); if (res == NULL) goto pop_1_error; - Py_DECREF(res); - STACK_SHRINK(1); + POKE(1, res); DISPATCH(); } @@ -1045,48 +1037,6 @@ DISPATCH(); } - TARGET(STOPITERATION_ERROR) { - assert(frame->owner == FRAME_OWNED_BY_GENERATOR); - PyObject *exc = TOP(); - assert(PyExceptionInstance_Check(exc)); - const char *msg = NULL; - if (PyErr_GivenExceptionMatches(exc, PyExc_StopIteration)) { - msg = "generator raised StopIteration"; - if (frame->f_code->co_flags & CO_ASYNC_GENERATOR) { - msg = "async generator raised StopIteration"; - } - else if (frame->f_code->co_flags & CO_COROUTINE) { - msg = "coroutine raised StopIteration"; - } - } - else if ((frame->f_code->co_flags & CO_ASYNC_GENERATOR) && - PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration)) - { - /* code in `gen` raised a StopAsyncIteration error: - raise a RuntimeError. - */ - msg = "async generator raised StopAsyncIteration"; - } - if (msg != NULL) { - PyObject *message = _PyUnicode_FromASCII(msg, strlen(msg)); - if (message == NULL) { - goto error; - } - PyObject *error = PyObject_CallOneArg(PyExc_RuntimeError, message); - if (error == NULL) { - Py_DECREF(message); - goto error; - } - assert(PyExceptionInstance_Check(error)); - SET_TOP(error); - PyException_SetCause(error, Py_NewRef(exc)); - // Steal exc reference, rather than Py_NewRef+Py_DECREF - PyException_SetContext(error, exc); - Py_DECREF(message); - } - DISPATCH(); - } - TARGET(LOAD_ASSERTION_ERROR) { PyObject *value; value = Py_NewRef(PyExc_AssertionError); @@ -2401,30 +2351,6 @@ DISPATCH(); } - TARGET(IMPORT_STAR) { - PyObject *from = PEEK(1); - PyObject *locals; - int err; - if (_PyFrame_FastToLocalsWithError(frame) < 0) { - Py_DECREF(from); - if (true) goto pop_1_error; - } - - locals = LOCALS(); - if (locals == NULL) { - _PyErr_SetString(tstate, PyExc_SystemError, - "no locals found during 'import *'"); - Py_DECREF(from); - if (true) goto pop_1_error; - } - err = import_all_from(tstate, locals, from); - _PyFrame_LocalsToFast(frame, 0); - Py_DECREF(from); - if (err) goto pop_1_error; - STACK_SHRINK(1); - DISPATCH(); - } - TARGET(IMPORT_FROM) { PyObject *from = PEEK(1); PyObject *res; diff --git a/Python/intrinsics.c b/Python/intrinsics.c new file mode 100644 index 000000000000..07b9c6a97c6e --- /dev/null +++ b/Python/intrinsics.c @@ -0,0 +1,194 @@ + +#define _PY_INTERPRETER + +#include "Python.h" +#include "pycore_frame.h" +#include "pycore_runtime.h" +#include "pycore_global_objects.h" +#include "pycore_intrinsics.h" +#include "pycore_pyerrors.h" + + + +static PyObject * +no_intrinsic(PyThreadState* tstate, PyObject *unused) +{ + _PyErr_SetString(tstate, PyExc_SystemError, "invalid intrinsic function"); + return NULL; +} + +static PyObject * +print_expr(PyThreadState* tstate, PyObject *value) +{ + PyObject *hook = _PySys_GetAttr(tstate, &_Py_ID(displayhook)); + // Can't use ERROR_IF here. + if (hook == NULL) { + _PyErr_SetString(tstate, PyExc_RuntimeError, + "lost sys.displayhook"); + return NULL; + } + return PyObject_CallOneArg(hook, value); +} + +static int +import_all_from(PyThreadState *tstate, PyObject *locals, PyObject *v) +{ + PyObject *all, *dict, *name, *value; + int skip_leading_underscores = 0; + int pos, err; + + if (_PyObject_LookupAttr(v, &_Py_ID(__all__), &all) < 0) { + return -1; /* Unexpected error */ + } + if (all == NULL) { + if (_PyObject_LookupAttr(v, &_Py_ID(__dict__), &dict) < 0) { + return -1; + } + if (dict == NULL) { + _PyErr_SetString(tstate, PyExc_ImportError, + "from-import-* object has no __dict__ and no __all__"); + return -1; + } + all = PyMapping_Keys(dict); + Py_DECREF(dict); + if (all == NULL) + return -1; + skip_leading_underscores = 1; + } + + for (pos = 0, err = 0; ; pos++) { + name = PySequence_GetItem(all, pos); + if (name == NULL) { + if (!_PyErr_ExceptionMatches(tstate, PyExc_IndexError)) { + err = -1; + } + else { + _PyErr_Clear(tstate); + } + break; + } + if (!PyUnicode_Check(name)) { + PyObject *modname = PyObject_GetAttr(v, &_Py_ID(__name__)); + if (modname == NULL) { + Py_DECREF(name); + err = -1; + break; + } + if (!PyUnicode_Check(modname)) { + _PyErr_Format(tstate, PyExc_TypeError, + "module __name__ must be a string, not %.100s", + Py_TYPE(modname)->tp_name); + } + else { + _PyErr_Format(tstate, PyExc_TypeError, + "%s in %U.%s must be str, not %.100s", + skip_leading_underscores ? "Key" : "Item", + modname, + skip_leading_underscores ? "__dict__" : "__all__", + Py_TYPE(name)->tp_name); + } + Py_DECREF(modname); + Py_DECREF(name); + err = -1; + break; + } + if (skip_leading_underscores) { + if (PyUnicode_READY(name) == -1) { + Py_DECREF(name); + err = -1; + break; + } + if (PyUnicode_READ_CHAR(name, 0) == '_') { + Py_DECREF(name); + continue; + } + } + value = PyObject_GetAttr(v, name); + if (value == NULL) + err = -1; + else if (PyDict_CheckExact(locals)) + err = PyDict_SetItem(locals, name, value); + else + err = PyObject_SetItem(locals, name, value); + Py_DECREF(name); + Py_XDECREF(value); + if (err < 0) + break; + } + Py_DECREF(all); + return err; +} + +static PyObject * +import_star(PyThreadState* tstate, PyObject *from) +{ + _PyInterpreterFrame *frame = tstate->cframe->current_frame; + if (_PyFrame_FastToLocalsWithError(frame) < 0) { + return NULL; + } + + PyObject *locals = frame->f_locals; + if (locals == NULL) { + _PyErr_SetString(tstate, PyExc_SystemError, + "no locals found during 'import *'"); + return NULL; + } + int err = import_all_from(tstate, locals, from); + _PyFrame_LocalsToFast(frame, 0); + if (err < 0) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +stopiteration_error(PyThreadState* tstate, PyObject *exc) +{ + _PyInterpreterFrame *frame = tstate->cframe->current_frame; + assert(frame->owner == FRAME_OWNED_BY_GENERATOR); + assert(PyExceptionInstance_Check(exc)); + const char *msg = NULL; + if (PyErr_GivenExceptionMatches(exc, PyExc_StopIteration)) { + msg = "generator raised StopIteration"; + if (frame->f_code->co_flags & CO_ASYNC_GENERATOR) { + msg = "async generator raised StopIteration"; + } + else if (frame->f_code->co_flags & CO_COROUTINE) { + msg = "coroutine raised StopIteration"; + } + } + else if ((frame->f_code->co_flags & CO_ASYNC_GENERATOR) && + PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration)) + { + /* code in `gen` raised a StopAsyncIteration error: + raise a RuntimeError. + */ + msg = "async generator raised StopAsyncIteration"; + } + if (msg != NULL) { + PyObject *message = _PyUnicode_FromASCII(msg, strlen(msg)); + if (message == NULL) { + return NULL; + } + PyObject *error = PyObject_CallOneArg(PyExc_RuntimeError, message); + if (error == NULL) { + Py_DECREF(message); + return NULL; + } + assert(PyExceptionInstance_Check(error)); + PyException_SetCause(error, Py_NewRef(exc)); + // Steal exc reference, rather than Py_NewRef+Py_DECREF + PyException_SetContext(error, Py_NewRef(exc)); + Py_DECREF(message); + return error; + } + return Py_NewRef(exc); +} + +instrinsic_func1 +_PyIntrinsics_UnaryFunctions[] = { + [0] = no_intrinsic, + [INTRINSIC_PRINT] = print_expr, + [INTRINSIC_IMPORT_STAR] = import_star, + [INTRINSIC_STOPITERATION_ERROR] = stopiteration_error, +}; diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 0716fccb964e..4bb6d53e995d 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -62,30 +62,30 @@ static void *opcode_targets[256] = { &&TARGET_STORE_SUBSCR, &&TARGET_DELETE_SUBSCR, &&TARGET_FOR_ITER_TUPLE, - &&TARGET_STOPITERATION_ERROR, &&TARGET_FOR_ITER_RANGE, &&TARGET_FOR_ITER_GEN, &&TARGET_LOAD_ATTR_CLASS, &&TARGET_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, + &&TARGET_LOAD_ATTR_INSTANCE_VALUE, &&TARGET_GET_ITER, &&TARGET_GET_YIELD_FROM_ITER, - &&TARGET_PRINT_EXPR, - &&TARGET_LOAD_BUILD_CLASS, - &&TARGET_LOAD_ATTR_INSTANCE_VALUE, &&TARGET_LOAD_ATTR_MODULE, - &&TARGET_LOAD_ASSERTION_ERROR, - &&TARGET_RETURN_GENERATOR, + &&TARGET_LOAD_BUILD_CLASS, &&TARGET_LOAD_ATTR_PROPERTY, &&TARGET_LOAD_ATTR_SLOT, + &&TARGET_LOAD_ASSERTION_ERROR, + &&TARGET_RETURN_GENERATOR, &&TARGET_LOAD_ATTR_WITH_HINT, &&TARGET_LOAD_ATTR_METHOD_LAZY_DICT, &&TARGET_LOAD_ATTR_METHOD_NO_DICT, &&TARGET_LOAD_ATTR_METHOD_WITH_VALUES, + &&TARGET_LOAD_CONST__LOAD_FAST, + &&TARGET_LOAD_FAST__LOAD_CONST, &&TARGET_LIST_TO_TUPLE, &&TARGET_RETURN_VALUE, - &&TARGET_IMPORT_STAR, + &&TARGET_LOAD_FAST__LOAD_FAST, &&TARGET_SETUP_ANNOTATIONS, - &&TARGET_LOAD_CONST__LOAD_FAST, + &&TARGET_LOAD_GLOBAL_BUILTIN, &&TARGET_ASYNC_GEN_WRAP, &&TARGET_PREP_RERAISE_STAR, &&TARGET_POP_EXCEPT, @@ -112,7 +112,7 @@ static void *opcode_targets[256] = { &&TARGET_JUMP_FORWARD, &&TARGET_JUMP_IF_FALSE_OR_POP, &&TARGET_JUMP_IF_TRUE_OR_POP, - &&TARGET_LOAD_FAST__LOAD_CONST, + &&TARGET_LOAD_GLOBAL_MODULE, &&TARGET_POP_JUMP_IF_FALSE, &&TARGET_POP_JUMP_IF_TRUE, &&TARGET_LOAD_GLOBAL, @@ -120,7 +120,7 @@ static void *opcode_targets[256] = { &&TARGET_CONTAINS_OP, &&TARGET_RERAISE, &&TARGET_COPY, - &&TARGET_LOAD_FAST__LOAD_FAST, + &&TARGET_STORE_ATTR_INSTANCE_VALUE, &&TARGET_BINARY_OP, &&TARGET_SEND, &&TARGET_LOAD_FAST, @@ -140,9 +140,9 @@ static void *opcode_targets[256] = { &&TARGET_STORE_DEREF, &&TARGET_DELETE_DEREF, &&TARGET_JUMP_BACKWARD, - &&TARGET_LOAD_GLOBAL_BUILTIN, + &&TARGET_STORE_ATTR_SLOT, &&TARGET_CALL_FUNCTION_EX, - &&TARGET_LOAD_GLOBAL_MODULE, + &&TARGET_STORE_ATTR_WITH_HINT, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD, @@ -152,27 +152,27 @@ static void *opcode_targets[256] = { &&TARGET_YIELD_VALUE, &&TARGET_RESUME, &&TARGET_MATCH_CLASS, - &&TARGET_STORE_ATTR_INSTANCE_VALUE, - &&TARGET_STORE_ATTR_SLOT, + &&TARGET_STORE_FAST__LOAD_FAST, + &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, - &&TARGET_STORE_ATTR_WITH_HINT, - &&TARGET_STORE_FAST__LOAD_FAST, - &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_STORE_SUBSCR_DICT, + &&TARGET_STORE_SUBSCR_LIST_INT, + &&TARGET_UNPACK_SEQUENCE_LIST, + &&TARGET_UNPACK_SEQUENCE_TUPLE, &&TARGET_LIST_EXTEND, &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, &&TARGET_DICT_UPDATE, - &&TARGET_STORE_SUBSCR_LIST_INT, - &&TARGET_UNPACK_SEQUENCE_LIST, - &&TARGET_UNPACK_SEQUENCE_TUPLE, &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, &&TARGET_CALL, &&TARGET_KW_NAMES, - &&_unknown_opcode, + &&TARGET_CALL_INTRINSIC_1, &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, From webhook-mailer at python.org Thu Jan 5 16:01:14 2023 From: webhook-mailer at python.org (gvanrossum) Date: Thu, 05 Jan 2023 21:01:14 -0000 Subject: [Python-checkins] GH-98831: Update generate_cases.py: register inst, opcode_metadata.h (#100735) Message-ID: https://github.com/python/cpython/commit/14b7f00fdf9890739b43a3e198e4ce93f54c0552 commit: 14b7f00fdf9890739b43a3e198e4ce93f54c0552 branch: main author: Guido van Rossum committer: gvanrossum date: 2023-01-05T13:01:07-08:00 summary: GH-98831: Update generate_cases.py: register inst, opcode_metadata.h (#100735) (These aren't used yet, but may be coming soon, and it's easier to keep this tool the same between branches.) Added a sanity check for all this to compile.c. Co-authored-by: Irit Katriel files: A Python/opcode_metadata.h M Makefile.pre.in M Python/bytecodes.c M Python/compile.c M Tools/cases_generator/generate_cases.py M Tools/cases_generator/parser.py diff --git a/Makefile.pre.in b/Makefile.pre.in index 397fc996192d..a6b5f212160f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1456,6 +1456,15 @@ regen-cases: -i $(srcdir)/Python/bytecodes.c \ -o $(srcdir)/Python/generated_cases.c.h.new $(UPDATE_FILE) $(srcdir)/Python/generated_cases.c.h $(srcdir)/Python/generated_cases.c.h.new + # Regenerate Python/opcode_metadata.h from Python/bytecodes.c + # using Tools/cases_generator/generate_cases.py --metadata + PYTHONPATH=$(srcdir)/Tools/cases_generator \ + $(PYTHON_FOR_REGEN) \ + $(srcdir)/Tools/cases_generator/generate_cases.py \ + --metadata \ + -i $(srcdir)/Python/bytecodes.c \ + -o $(srcdir)/Python/opcode_metadata.h.new + $(UPDATE_FILE) $(srcdir)/Python/opcode_metadata.h $(srcdir)/Python/opcode_metadata.h.new Python/ceval.o: $(srcdir)/Python/opcode_targets.h $(srcdir)/Python/condvar.h $(srcdir)/Python/generated_cases.c.h diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 9283f590582a..04ba33ebef80 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -764,7 +764,7 @@ dummy_func( ERROR_IF(w == NULL, error); } - inst(YIELD_VALUE, (retval --)) { + inst(YIELD_VALUE, (retval -- unused)) { // NOTE: It's important that YIELD_VALUE never raises an exception! // The compiler treats any exception raised here as a failed close() // or throw() call. diff --git a/Python/compile.c b/Python/compile.c index ff29fb42c5d1..e7804469fec6 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -36,6 +36,8 @@ #include "pycore_pymem.h" // _PyMem_IsPtrFreed() #include "pycore_symtable.h" // PySTEntryObject +#include "opcode_metadata.h" // _PyOpcode_opcode_metadata + #define DEFAULT_BLOCK_SIZE 16 #define DEFAULT_CODE_SIZE 128 @@ -8664,6 +8666,31 @@ no_redundant_jumps(cfg_builder *g) { return true; } +static bool +opcode_metadata_is_sane(cfg_builder *g) { + for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { + for (int i = 0; i < b->b_iused; i++) { + struct instr *instr = &b->b_instr[i]; + int opcode = instr->i_opcode; + assert(opcode <= MAX_REAL_OPCODE); + int pushed = _PyOpcode_opcode_metadata[opcode].n_pushed; + int popped = _PyOpcode_opcode_metadata[opcode].n_popped; + assert((pushed < 0) == (popped < 0)); + if (pushed >= 0) { + assert(_PyOpcode_opcode_metadata[opcode].valid_entry); + int effect = stack_effect(opcode, instr->i_oparg, -1); + if (effect != pushed - popped) { + fprintf(stderr, + "op=%d: stack_effect (%d) != pushed (%d) - popped (%d)\n", + opcode, effect, pushed, popped); + return false; + } + } + } + } + return true; +} + static bool no_empty_basic_blocks(cfg_builder *g) { for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { @@ -8847,6 +8874,7 @@ assemble(struct compiler *c, int addNone) } assert(no_redundant_jumps(g)); + assert(opcode_metadata_is_sane(g)); /* Can't modify the bytecode after computing jump offsets. */ assemble_jump_offsets(g->g_entryblock); diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h new file mode 100644 index 000000000000..2d539896844d --- /dev/null +++ b/Python/opcode_metadata.h @@ -0,0 +1,187 @@ +// This file is generated by Tools/cases_generator/generate_cases.py --metadata +// from Python/bytecodes.c +// Do not edit! +enum Direction { DIR_NONE, DIR_READ, DIR_WRITE }; +static const struct { + short n_popped; + short n_pushed; + enum Direction dir_op1; + enum Direction dir_op2; + enum Direction dir_op3; + bool valid_entry; + char instr_format[10]; +} _PyOpcode_opcode_metadata[256] = { + [NOP] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [RESUME] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_CLOSURE] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_FAST_CHECK] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_FAST] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_CONST] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [STORE_FAST] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_FAST__LOAD_FAST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBIB" }, + [LOAD_FAST__LOAD_CONST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBIB" }, + [STORE_FAST__LOAD_FAST] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBIB" }, + [STORE_FAST__STORE_FAST] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBIB" }, + [LOAD_CONST__LOAD_FAST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBIB" }, + [POP_TOP] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [PUSH_NULL] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [END_FOR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [UNARY_POSITIVE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [UNARY_NEGATIVE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [UNARY_NOT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [UNARY_INVERT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [BINARY_OP_MULTIPLY_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, + [BINARY_OP_MULTIPLY_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, + [BINARY_OP_SUBTRACT_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, + [BINARY_OP_SUBTRACT_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, + [BINARY_OP_ADD_UNICODE] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, + [BINARY_OP_INPLACE_ADD_UNICODE] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [BINARY_OP_ADD_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, + [BINARY_OP_ADD_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, + [BINARY_SUBSCR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, + [BINARY_SLICE] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [STORE_SLICE] = { 4, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [BINARY_SUBSCR_LIST_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, + [BINARY_SUBSCR_TUPLE_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, + [BINARY_SUBSCR_DICT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, + [BINARY_SUBSCR_GETITEM] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, + [LIST_APPEND] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [SET_ADD] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [STORE_SUBSCR] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, + [STORE_SUBSCR_LIST_INT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, + [STORE_SUBSCR_DICT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, + [DELETE_SUBSCR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [PRINT_EXPR] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [RAISE_VARARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [INTERPRETER_EXIT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [RETURN_VALUE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [GET_AITER] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [GET_ANEXT] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [GET_AWAITABLE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [SEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [ASYNC_GEN_WRAP] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [YIELD_VALUE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [POP_EXCEPT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [RERAISE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [PREP_RERAISE_STAR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [END_ASYNC_FOR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CLEANUP_THROW] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [STOPITERATION_ERROR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_ASSERTION_ERROR] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_BUILD_CLASS] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [STORE_NAME] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [DELETE_NAME] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [UNPACK_SEQUENCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [UNPACK_SEQUENCE_TWO_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [UNPACK_SEQUENCE_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [UNPACK_SEQUENCE_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [UNPACK_EX] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [STORE_ATTR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, + [DELETE_ATTR] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [STORE_GLOBAL] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [DELETE_GLOBAL] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_NAME] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_GLOBAL] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_GLOBAL_MODULE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_GLOBAL_BUILTIN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [DELETE_FAST] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [MAKE_CELL] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [DELETE_DEREF] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_CLASSDEREF] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_DEREF] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [STORE_DEREF] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [COPY_FREE_VARS] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [BUILD_STRING] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [BUILD_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [BUILD_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LIST_TO_TUPLE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LIST_EXTEND] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [SET_UPDATE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [BUILD_SET] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [BUILD_MAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [SETUP_ANNOTATIONS] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [BUILD_CONST_KEY_MAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [DICT_UPDATE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [DICT_MERGE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [MAP_ADD] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_ATTR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_ATTR_INSTANCE_VALUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_ATTR_MODULE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_ATTR_WITH_HINT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_ATTR_SLOT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_ATTR_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_ATTR_PROPERTY] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [STORE_ATTR_INSTANCE_VALUE] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, + [STORE_ATTR_WITH_HINT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, + [STORE_ATTR_SLOT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, + [COMPARE_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC0" }, + [COMPARE_OP_FLOAT_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC0IB" }, + [COMPARE_OP_INT_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC0IB" }, + [COMPARE_OP_STR_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC0IB" }, + [IS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CONTAINS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CHECK_EG_MATCH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CHECK_EXC_MATCH] = { 2, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [IMPORT_NAME] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [IMPORT_STAR] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [IMPORT_FROM] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [JUMP_FORWARD] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [JUMP_BACKWARD] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [POP_JUMP_IF_FALSE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [POP_JUMP_IF_TRUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [POP_JUMP_IF_NOT_NONE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [POP_JUMP_IF_NONE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [JUMP_IF_FALSE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [JUMP_IF_TRUE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [JUMP_BACKWARD_NO_INTERRUPT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [GET_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [MATCH_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [MATCH_MAPPING] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [MATCH_SEQUENCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [MATCH_KEYS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [GET_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [GET_YIELD_FROM_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [FOR_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [FOR_ITER_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [FOR_ITER_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [FOR_ITER_RANGE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [FOR_ITER_GEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [BEFORE_ASYNC_WITH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [BEFORE_WITH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [WITH_EXCEPT_START] = { 4, 5, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [PUSH_EXC_INFO] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_ATTR_METHOD_WITH_VALUES] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_ATTR_METHOD_WITH_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_ATTR_METHOD_NO_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [LOAD_ATTR_METHOD_LAZY_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_BOUND_METHOD_EXACT_ARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [KW_NAMES] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_PY_EXACT_ARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_PY_WITH_DEFAULTS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_NO_KW_TYPE_1] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_NO_KW_STR_1] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_NO_KW_TUPLE_1] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_BUILTIN_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_NO_KW_BUILTIN_O] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_NO_KW_BUILTIN_FAST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_NO_KW_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_NO_KW_ISINSTANCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_NO_KW_LIST_APPEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_FUNCTION_EX] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [MAKE_FUNCTION] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [RETURN_GENERATOR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [BUILD_SLICE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [FORMAT_VALUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [COPY] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [BINARY_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, + [SWAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [EXTENDED_ARG] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CACHE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, +}; diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 5eed74c5e147..7452b2ced052 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -21,6 +21,9 @@ DEFAULT_OUTPUT = os.path.relpath( os.path.join(os.path.dirname(__file__), "../../Python/generated_cases.c.h") ) +DEFAULT_METADATA_OUTPUT = os.path.relpath( + os.path.join(os.path.dirname(__file__), "../../Python/opcode_metadata.h") +) BEGIN_MARKER = "// BEGIN BYTECODES //" END_MARKER = "// END BYTECODES //" RE_PREDICTED = r"^\s*(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);\s*$" @@ -37,6 +40,12 @@ arg_parser.add_argument( "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT ) +arg_parser.add_argument( + "-m", + "--metadata", + action="store_true", + help=f"Generate metadata instead, changes output default to {DEFAULT_METADATA_OUTPUT}", +) class Formatter: @@ -96,6 +105,8 @@ def assign(self, dst: StackEffect, src: StackEffect): cast = self.cast(dst, src) if m := re.match(r"^PEEK\((\d+)\)$", dst.name): self.emit(f"POKE({m.group(1)}, {cast}{src.name});") + elif m := re.match(r"^REG\(oparg(\d+)\)$", dst.name): + self.emit(f"Py_XSETREF({dst.name}, {cast}{src.name});") else: self.emit(f"{dst.name} = {cast}{src.name};") @@ -109,7 +120,8 @@ class Instruction: # Parts of the underlying instruction definition inst: parser.InstDef - kind: typing.Literal["inst", "op"] + register: bool + kind: typing.Literal["inst", "op", "legacy"] # Legacy means no (input -- output) name: str block: parser.Block block_text: list[str] # Block.text, less curlies, less PREDICT() calls @@ -121,6 +133,9 @@ class Instruction: cache_effects: list[parser.CacheEffect] input_effects: list[StackEffect] output_effects: list[StackEffect] + # Parallel to input_effects + input_registers: list[str] = dataclasses.field(repr=False) + output_registers: list[str] = dataclasses.field(repr=False) # Set later family: parser.Family | None = None @@ -129,6 +144,7 @@ class Instruction: def __init__(self, inst: parser.InstDef): self.inst = inst + self.register = inst.register self.kind = inst.kind self.name = inst.name self.block = inst.block @@ -150,9 +166,24 @@ def __init__(self, inst: parser.InstDef): break self.unmoved_names = frozenset(unmoved_names) + def analyze_registers(self, a: "Analyzer") -> None: + regs = iter(("REG(oparg1)", "REG(oparg2)", "REG(oparg3)")) + try: + self.input_registers = [ + next(regs) for ieff in self.input_effects if ieff.name != UNUSED + ] + self.output_registers = [ + next(regs) for oeff in self.output_effects if oeff.name != UNUSED + ] + except StopIteration: # Running out of registers + a.error( + f"Instruction {self.name} has too many register effects", node=self.inst + ) + def write(self, out: Formatter) -> None: """Write one instruction, sans prologue and epilogue.""" # Write a static assertion that a family's cache size is correct + if family := self.family: if self.name == family.members[0]: if cache_size := family.size: @@ -161,10 +192,16 @@ def write(self, out: Formatter) -> None: f'{self.cache_offset}, "incorrect cache size");' ) - # Write input stack effect variable declarations and initializations - for i, ieffect in enumerate(reversed(self.input_effects), 1): - src = StackEffect(f"PEEK({i})", "") - out.declare(ieffect, src) + if not self.register: + # Write input stack effect variable declarations and initializations + for i, ieffect in enumerate(reversed(self.input_effects), 1): + src = StackEffect(f"PEEK({i})", "") + out.declare(ieffect, src) + else: + # Write input register variable declarations and initializations + for ieffect, reg in zip(self.input_effects, self.input_registers): + src = StackEffect(reg, "") + out.declare(ieffect, src) # Write output stack effect variable declarations input_names = {ieffect.name for ieffect in self.input_effects} @@ -172,20 +209,28 @@ def write(self, out: Formatter) -> None: if oeffect.name not in input_names: out.declare(oeffect, None) + # out.emit(f"JUMPBY(OPSIZE({self.inst.name}) - 1);") + self.write_body(out, 0) # Skip the rest if the block always exits if self.always_exits: return - # Write net stack growth/shrinkage - diff = len(self.output_effects) - len(self.input_effects) - out.stack_adjust(diff) + if not self.register: + # Write net stack growth/shrinkage + diff = len(self.output_effects) - len(self.input_effects) + out.stack_adjust(diff) - # Write output stack effect assignments - for i, oeffect in enumerate(reversed(self.output_effects), 1): - if oeffect.name not in self.unmoved_names: - dst = StackEffect(f"PEEK({i})", "") + # Write output stack effect assignments + for i, oeffect in enumerate(reversed(self.output_effects), 1): + if oeffect.name not in self.unmoved_names: + dst = StackEffect(f"PEEK({i})", "") + out.assign(dst, oeffect) + else: + # Write output register assignments + for oeffect, reg in zip(self.output_effects, self.output_registers): + dst = StackEffect(reg, "") out.assign(dst, oeffect) # Write cache effect @@ -209,7 +254,9 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None else: typ = f"uint{bits}_t " func = f"read_u{bits}" - out.emit(f"{typ}{ceffect.name} = {func}(&next_instr[{cache_offset}].cache);") + out.emit( + f"{typ}{ceffect.name} = {func}(&next_instr[{cache_offset}].cache);" + ) cache_offset += ceffect.size assert cache_offset == self.cache_offset + cache_adjust @@ -222,14 +269,17 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None # ERROR_IF() must pop the inputs from the stack. # The code block is responsible for DECREF()ing them. # NOTE: If the label doesn't exist, just add it to ceval.c. - ninputs = len(self.input_effects) - # Don't pop common input/output effects at the bottom! - # These aren't DECREF'ed so they can stay. - for ieff, oeff in zip(self.input_effects, self.output_effects): - if ieff.name == oeff.name: - ninputs -= 1 - else: - break + if not self.register: + ninputs = len(self.input_effects) + # Don't pop common input/output effects at the bottom! + # These aren't DECREF'ed so they can stay. + for ieff, oeff in zip(self.input_effects, self.output_effects): + if ieff.name == oeff.name: + ninputs -= 1 + else: + break + else: + ninputs = 0 if ninputs: out.write_raw( f"{extra}{space}if ({cond}) goto pop_{ninputs}_{label};\n" @@ -237,10 +287,11 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None else: out.write_raw(f"{extra}{space}if ({cond}) goto {label};\n") elif m := re.match(r"(\s*)DECREF_INPUTS\(\);\s*$", line): - space = m.group(1) - for ieff in self.input_effects: - if ieff.name not in self.unmoved_names: - out.write_raw(f"{extra}{space}Py_DECREF({ieff.name});\n") + if not self.register: + space = m.group(1) + for ieff in self.input_effects: + if ieff.name not in self.unmoved_names: + out.write_raw(f"{extra}{space}Py_DECREF({ieff.name});\n") else: out.write_raw(extra + line) @@ -392,6 +443,7 @@ def analyze(self) -> None: self.find_predictions() self.map_families() self.check_families() + self.analyze_register_instrs() self.analyze_supers_and_macros() def find_predictions(self) -> None: @@ -458,6 +510,11 @@ def check_families(self) -> None: family, ) + def analyze_register_instrs(self) -> None: + for instr in self.instrs.values(): + if instr.register: + instr.analyze_registers(self) + def analyze_supers_and_macros(self) -> None: """Analyze each super- and macro instruction.""" self.super_instrs = {} @@ -563,6 +620,129 @@ def stack_analysis( ] return stack, -lowest + def write_metadata(self) -> None: + """Write instruction metadata to output file.""" + with open(self.output_filename, "w") as f: + # Write provenance header + f.write( + f"// This file is generated by {os.path.relpath(__file__)} --metadata\n" + ) + f.write(f"// from {os.path.relpath(self.filename)}\n") + f.write(f"// Do not edit!\n") + + # Create formatter; the rest of the code uses this + self.out = Formatter(f, 0) + + # Write variable definition + self.out.emit("enum Direction { DIR_NONE, DIR_READ, DIR_WRITE };") + self.out.emit("static const struct {") + with self.out.indent(): + self.out.emit("short n_popped;") + self.out.emit("short n_pushed;") + self.out.emit("enum Direction dir_op1;") + self.out.emit("enum Direction dir_op2;") + self.out.emit("enum Direction dir_op3;") + self.out.emit("bool valid_entry;") + self.out.emit("char instr_format[10];") + self.out.emit("} _PyOpcode_opcode_metadata[256] = {") + + # Write metadata for each instruction + for thing in self.everything: + match thing: + case parser.InstDef(): + if thing.kind != "op": + self.write_metadata_for_inst(self.instrs[thing.name]) + case parser.Super(): + self.write_metadata_for_super(self.super_instrs[thing.name]) + case parser.Macro(): + self.write_metadata_for_macro(self.macro_instrs[thing.name]) + case _: + typing.assert_never(thing) + + # Write end of array + self.out.emit("};") + + def get_format(self, thing: Instruction | SuperInstruction | MacroInstruction) -> str: + """Get the format string for a single instruction.""" + def instr_format(instr: Instruction) -> str: + if instr.register: + fmt = "IBBB" + else: + fmt = "IB" + cache = "C" + for ce in instr.cache_effects: + for _ in range(ce.size): + fmt += cache + cache = "0" + return fmt + match thing: + case Instruction(): + format = instr_format(thing) + case SuperInstruction(): + format = "" + for part in thing.parts: + format += instr_format(part.instr) + case MacroInstruction(): + # Macros don't support register instructions yet + format = "IB" + cache = "C" + for part in thing.parts: + if isinstance(part, parser.CacheEffect): + for _ in range(part.size): + format += cache + cache = "0" + else: + assert isinstance(part, Component) + for ce in part.instr.cache_effects: + for _ in range(ce.size): + format += cache + cache = "0" + case _: + typing.assert_never(thing) + assert len(format) < 10 # Else update the size of instr_format above + return format + + def write_metadata_for_inst(self, instr: Instruction) -> None: + """Write metadata for a single instruction.""" + dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" + if instr.kind == "legacy": + n_popped = n_pushed = -1 + assert not instr.register + else: + n_popped = len(instr.input_effects) + n_pushed = len(instr.output_effects) + if instr.register: + directions: list[str] = [] + directions.extend("DIR_READ" for _ in instr.input_effects) + directions.extend("DIR_WRITE" for _ in instr.output_effects) + directions.extend("DIR_NONE" for _ in range(3)) + dir_op1, dir_op2, dir_op3 = directions[:3] + format = self.get_format(instr) + self.out.emit( + f' [{instr.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, "{format}" }},' + ) + + def write_metadata_for_super(self, sup: SuperInstruction) -> None: + """Write metadata for a super-instruction.""" + n_popped = sum(len(comp.instr.input_effects) for comp in sup.parts) + n_pushed = sum(len(comp.instr.output_effects) for comp in sup.parts) + dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" + format = self.get_format(sup) + self.out.emit( + f' [{sup.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, "{format}" }},' + ) + + def write_metadata_for_macro(self, mac: MacroInstruction) -> None: + """Write metadata for a macro-instruction.""" + parts = [comp for comp in mac.parts if isinstance(comp, Component)] + n_popped = sum(len(comp.instr.input_effects) for comp in parts) + n_pushed = sum(len(comp.instr.output_effects) for comp in parts) + dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" + format = self.get_format(mac) + self.out.emit( + f' [{mac.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, "{format}" }},' + ) + def write_instructions(self) -> None: """Write instructions to output file.""" with open(self.output_filename, "w") as f: @@ -571,7 +751,7 @@ def write_instructions(self) -> None: f.write(f"// from {os.path.relpath(self.filename)}\n") f.write(f"// Do not edit!\n") - # Create formatter; the rest of the code uses this. + # Create formatter; the rest of the code uses this self.out = Formatter(f, 8) # Write and count instructions of all kinds @@ -581,7 +761,7 @@ def write_instructions(self) -> None: for thing in self.everything: match thing: case parser.InstDef(): - if thing.kind == "inst": + if thing.kind != "op": n_instrs += 1 self.write_instr(self.instrs[thing.name]) case parser.Super(): @@ -616,9 +796,13 @@ def write_super(self, sup: SuperInstruction) -> None: with self.wrap_super_or_macro(sup): first = True for comp in sup.parts: - if not first: + if first: + pass + # self.out.emit("JUMPBY(OPSIZE(opcode) - 1);") + else: self.out.emit("NEXTOPARG();") self.out.emit("JUMPBY(1);") + # self.out.emit("JUMPBY(OPSIZE(opcode));") first = False comp.write_body(self.out, 0) if comp.instr.cache_offset: @@ -711,12 +895,18 @@ def always_exits(lines: list[str]) -> bool: def main(): """Parse command line, parse input, analyze, write output.""" args = arg_parser.parse_args() # Prints message and sys.exit(2) on error + if args.metadata: + if args.output == DEFAULT_OUTPUT: + args.output = DEFAULT_METADATA_OUTPUT a = Analyzer(args.input, args.output) # Raises OSError if input unreadable a.parse() # Raises SyntaxError on failure a.analyze() # Prints messages and sets a.errors on failure if a.errors: sys.exit(f"Found {a.errors} errors") - a.write_instructions() # Raises OSError if output can't be written + if args.metadata: + a.write_metadata() + else: + a.write_instructions() # Raises OSError if output can't be written if __name__ == "__main__": diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py index d802c733dfd1..4885394bf6b1 100644 --- a/Tools/cases_generator/parser.py +++ b/Tools/cases_generator/parser.py @@ -84,7 +84,8 @@ class OpName(Node): @dataclass class InstHeader(Node): - kind: Literal["inst", "op"] + register: bool + kind: Literal["inst", "op", "legacy"] # Legacy means no (inputs -- outputs) name: str inputs: list[InputEffect] outputs: list[OutputEffect] @@ -92,7 +93,8 @@ class InstHeader(Node): @dataclass class InstDef(Node): - kind: Literal["inst", "op"] + register: bool + kind: Literal["inst", "op", "legacy"] name: str inputs: list[InputEffect] outputs: list[OutputEffect] @@ -134,16 +136,19 @@ def definition(self) -> InstDef | Super | Macro | Family | None: def inst_def(self) -> InstDef | None: if hdr := self.inst_header(): if block := self.block(): - return InstDef(hdr.kind, hdr.name, hdr.inputs, hdr.outputs, block) + return InstDef( + hdr.register, hdr.kind, hdr.name, hdr.inputs, hdr.outputs, block + ) raise self.make_syntax_error("Expected block") return None @contextual def inst_header(self) -> InstHeader | None: # inst(NAME) - # | inst(NAME, (inputs -- outputs)) - # | op(NAME, (inputs -- outputs)) + # | [register] inst(NAME, (inputs -- outputs)) + # | [register] op(NAME, (inputs -- outputs)) # TODO: Make INST a keyword in the lexer. + register = bool(self.expect(lx.REGISTER)) if (tkn := self.expect(lx.IDENTIFIER)) and (kind := tkn.text) in ("inst", "op"): if self.expect(lx.LPAREN) and (tkn := self.expect(lx.IDENTIFIER)): name = tkn.text @@ -151,10 +156,10 @@ def inst_header(self) -> InstHeader | None: inp, outp = self.io_effect() if self.expect(lx.RPAREN): if (tkn := self.peek()) and tkn.kind == lx.LBRACE: - return InstHeader(kind, name, inp, outp) + return InstHeader(register, kind, name, inp, outp) elif self.expect(lx.RPAREN) and kind == "inst": # No legacy stack effect if kind is "op". - return InstHeader(kind, name, [], []) + return InstHeader(register, "legacy", name, [], []) return None def io_effect(self) -> tuple[list[InputEffect], list[OutputEffect]]: From webhook-mailer at python.org Thu Jan 5 16:47:24 2023 From: webhook-mailer at python.org (zware) Date: Thu, 05 Jan 2023 21:47:24 -0000 Subject: [Python-checkins] gh-98831: Regenerate Python/opcode_metadata.h (GH-100778) Message-ID: https://github.com/python/cpython/commit/af5149f30b652737ef3b495b303819d985f439b1 commit: af5149f30b652737ef3b495b303819d985f439b1 branch: main author: Zachary Ware committer: zware date: 2023-01-05T15:47:18-06:00 summary: gh-98831: Regenerate Python/opcode_metadata.h (GH-100778) files: M Python/opcode_metadata.h diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 2d539896844d..34c6757afd32 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -51,7 +51,7 @@ static const struct { [STORE_SUBSCR_LIST_INT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, [STORE_SUBSCR_DICT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, [DELETE_SUBSCR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [PRINT_EXPR] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [CALL_INTRINSIC_1] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [RAISE_VARARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [INTERPRETER_EXIT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [RETURN_VALUE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, @@ -66,7 +66,6 @@ static const struct { [PREP_RERAISE_STAR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [END_ASYNC_FOR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [CLEANUP_THROW] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [STOPITERATION_ERROR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [LOAD_ASSERTION_ERROR] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [LOAD_BUILD_CLASS] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [STORE_NAME] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, @@ -124,7 +123,6 @@ static const struct { [CHECK_EG_MATCH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [CHECK_EXC_MATCH] = { 2, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [IMPORT_NAME] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [IMPORT_STAR] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [IMPORT_FROM] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [JUMP_FORWARD] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [JUMP_BACKWARD] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, @@ -152,7 +150,6 @@ static const struct { [WITH_EXCEPT_START] = { 4, 5, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [PUSH_EXC_INFO] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [LOAD_ATTR_METHOD_WITH_VALUES] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_ATTR_METHOD_WITH_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [LOAD_ATTR_METHOD_NO_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [LOAD_ATTR_METHOD_LAZY_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [CALL_BOUND_METHOD_EXACT_ARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, From webhook-mailer at python.org Thu Jan 5 17:11:56 2023 From: webhook-mailer at python.org (miss-islington) Date: Thu, 05 Jan 2023 22:11:56 -0000 Subject: [Python-checkins] gh-100562: improve performance of `pathlib.Path.absolute()` (GH-100563) Message-ID: https://github.com/python/cpython/commit/7fba99eadb3349a6d49d02f13b1fddf44c674393 commit: 7fba99eadb3349a6d49d02f13b1fddf44c674393 branch: main author: Barney Gale committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-05T14:11:50-08:00 summary: gh-100562: improve performance of `pathlib.Path.absolute()` (GH-100563) Increase performance of the `absolute()` method by calling `os.getcwd()` directly, rather than using the `Path.cwd()` class method. This avoids constructing an extra `Path` object (and the parsing/normalization that comes with it). Decrease performance of the `cwd()` class method by calling the `Path.absolute()` method, rather than using `os.getcwd()` directly. This involves constructing an extra `Path` object. We do this to maintain a longstanding pattern where `os` functions are called from only one place, which allows them to be more readily replaced by users. As `cwd()` is generally called at most once within user programs, it's a good bargain. ```shell # before $ ./python -m timeit -s 'from pathlib import Path; p = Path("foo", "bar")' 'p.absolute()' 50000 loops, best of 5: 9.04 usec per loop # after $ ./python -m timeit -s 'from pathlib import Path; p = Path("foo", "bar")' 'p.absolute()' 50000 loops, best of 5: 5.02 usec per loop ``` Automerge-Triggered-By: GH:AlexWaygood files: A Misc/NEWS.d/next/Library/2022-12-28-00-28-43.gh-issue-100562.Hic0Z0.rst M Lib/pathlib.py diff --git a/Lib/pathlib.py b/Lib/pathlib.py index b959e85d1840..a0678f61b632 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -748,10 +748,12 @@ def __exit__(self, t, v, tb): @classmethod def cwd(cls): - """Return a new path pointing to the current working directory - (as returned by os.getcwd()). - """ - return cls(os.getcwd()) + """Return a new path pointing to the current working directory.""" + # We call 'absolute()' rather than using 'os.getcwd()' directly to + # enable users to replace the implementation of 'absolute()' in a + # subclass and benefit from the new behaviour here. This works because + # os.path.abspath('.') == os.getcwd(). + return cls().absolute() @classmethod def home(cls): @@ -825,7 +827,7 @@ def absolute(self): """ if self.is_absolute(): return self - return self._from_parts([self.cwd()] + self._parts) + return self._from_parts([os.getcwd()] + self._parts) def resolve(self, strict=False): """ diff --git a/Misc/NEWS.d/next/Library/2022-12-28-00-28-43.gh-issue-100562.Hic0Z0.rst b/Misc/NEWS.d/next/Library/2022-12-28-00-28-43.gh-issue-100562.Hic0Z0.rst new file mode 100644 index 000000000000..56a426589719 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-28-00-28-43.gh-issue-100562.Hic0Z0.rst @@ -0,0 +1,3 @@ +Improve performance of :meth:`pathlib.Path.absolute` by nearly 2x. This comes +at the cost of a performance regression in :meth:`pathlib.Path.cwd`, which is +generally used less frequently in user code. From webhook-mailer at python.org Thu Jan 5 17:28:11 2023 From: webhook-mailer at python.org (JelleZijlstra) Date: Thu, 05 Jan 2023 22:28:11 -0000 Subject: [Python-checkins] Add hauntsaninja as tomllib CODEOWNER (#100779) Message-ID: https://github.com/python/cpython/commit/d84b1a97f942ed25693e9b3c735196399ca4a791 commit: d84b1a97f942ed25693e9b3c735196399ca4a791 branch: main author: Shantanu <12621235+hauntsaninja at users.noreply.github.com> committer: JelleZijlstra date: 2023-01-05T14:28:02-08:00 summary: Add hauntsaninja as tomllib CODEOWNER (#100779) files: M .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1d7c1e843fe9..351bf7c39555 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -144,7 +144,7 @@ Lib/ast.py @isidentical **/*cgi* @ethanfurman **/*tarfile* @ethanfurman -**/*tomllib* @encukou +**/*tomllib* @encukou @hauntsaninja **/*sysconfig* @FFY00 From webhook-mailer at python.org Thu Jan 5 17:55:55 2023 From: webhook-mailer at python.org (miss-islington) Date: Thu, 05 Jan 2023 22:55:55 -0000 Subject: [Python-checkins] gh-87691: add an absolute path pathlib example in / operator docs (GH-100737) Message-ID: https://github.com/python/cpython/commit/1ae619c911ec8e096f83eeb7cc57fcd966950a3d commit: 1ae619c911ec8e096f83eeb7cc57fcd966950a3d branch: main author: Shantanu <12621235+hauntsaninja at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-05T14:55:35-08:00 summary: gh-87691: add an absolute path pathlib example in / operator docs (GH-100737) The behaviour is fully explained a couple paragraphs above, but it may be useful to have a brief example to cover the behaviour. Automerge-Triggered-By: GH:hauntsaninja files: M Doc/library/pathlib.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 47687400c14e..c90758c9306c 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -212,7 +212,10 @@ Paths of a different flavour compare unequal and cannot be ordered:: Operators ^^^^^^^^^ -The slash operator helps create child paths, similarly to :func:`os.path.join`:: +The slash operator helps create child paths, mimicking the behaviour of +:func:`os.path.join`. For instance, when several absolute paths are given, the +last is taken as an anchor; for a Windows path, changing the local root doesn't +discard the previous drive setting:: >>> p = PurePath('/etc') >>> p @@ -222,6 +225,10 @@ The slash operator helps create child paths, similarly to :func:`os.path.join`:: >>> q = PurePath('bin') >>> '/usr' / q PurePosixPath('/usr/bin') + >>> p / '/an_absolute_path' + PurePosixPath('/an_absolute_path') + >>> PureWindowsPath('c:/Windows', '/Program Files') + PureWindowsPath('c:/Program Files') A path object can be used anywhere an object implementing :class:`os.PathLike` is accepted:: From webhook-mailer at python.org Thu Jan 5 17:57:37 2023 From: webhook-mailer at python.org (gpshead) Date: Thu, 05 Jan 2023 22:57:37 -0000 Subject: [Python-checkins] gh-86082: bpo-41916: allow cross-compiled python to have -pthread set for CXX (#22525) Message-ID: https://github.com/python/cpython/commit/cc8748712e78805c5be4a0a3f98cfb5c35026d0e commit: cc8748712e78805c5be4a0a3f98cfb5c35026d0e branch: main author: Dustin Spicuzza committer: gpshead date: 2023-01-05T14:57:31-08:00 summary: gh-86082: bpo-41916: allow cross-compiled python to have -pthread set for CXX (#22525) When cross-compiling, the compile/run test for -pthread always fails so -pthread will never be automatically set without an override from the cache. ac_cv_pthread can already be overridden, so do the same thing for ac_cv_cxx_thread. files: A Misc/NEWS.d/next/Build/2022-03-04-10-47-23.bpo-41916.1d2GLU.rst M configure M configure.ac diff --git a/Misc/NEWS.d/next/Build/2022-03-04-10-47-23.bpo-41916.1d2GLU.rst b/Misc/NEWS.d/next/Build/2022-03-04-10-47-23.bpo-41916.1d2GLU.rst new file mode 100644 index 000000000000..2fc5f8ec77b1 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2022-03-04-10-47-23.bpo-41916.1d2GLU.rst @@ -0,0 +1,2 @@ +Allow override of ac_cv_cxx_thread so that cross compiled python can set +-pthread for CXX. diff --git a/configure b/configure index 946218fd8d85..4841c1d8ad5f 100755 --- a/configure +++ b/configure @@ -9395,12 +9395,14 @@ fi # If we have set a CC compiler flag for thread support then # check if it works for CXX, too. -ac_cv_cxx_thread=no if test ! -z "$CXX" then { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX also accepts flags for thread support" >&5 $as_echo_n "checking whether $CXX also accepts flags for thread support... " >&6; } -ac_save_cxx="$CXX" +if ${ac_cv_cxx_thread+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx="$CXX" if test "$ac_cv_kpthread" = "yes" then @@ -9414,6 +9416,8 @@ elif test "$ac_cv_pthread" = "yes" then CXX="$CXX -pthread" ac_cv_cxx_thread=yes +else + ac_cv_cxx_thread=no fi if test $ac_cv_cxx_thread = yes @@ -9429,10 +9433,13 @@ then fi rm -fr conftest* fi +CXX="$ac_save_cxx" +fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_thread" >&5 $as_echo "$ac_cv_cxx_thread" >&6; } +else + ac_cv_cxx_thread=no fi -CXX="$ac_save_cxx" diff --git a/configure.ac b/configure.ac index 22028972cb3d..0ed5a24e8683 100644 --- a/configure.ac +++ b/configure.ac @@ -2653,11 +2653,10 @@ fi # If we have set a CC compiler flag for thread support then # check if it works for CXX, too. -ac_cv_cxx_thread=no if test ! -z "$CXX" then -AC_MSG_CHECKING(whether $CXX also accepts flags for thread support) -ac_save_cxx="$CXX" +AC_CACHE_CHECK([whether $CXX also accepts flags for thread support], [ac_cv_cxx_thread], +[ac_save_cxx="$CXX" if test "$ac_cv_kpthread" = "yes" then @@ -2671,6 +2670,8 @@ elif test "$ac_cv_pthread" = "yes" then CXX="$CXX -pthread" ac_cv_cxx_thread=yes +else + ac_cv_cxx_thread=no fi if test $ac_cv_cxx_thread = yes @@ -2686,9 +2687,10 @@ then fi rm -fr conftest* fi -AC_MSG_RESULT($ac_cv_cxx_thread) +CXX="$ac_save_cxx"]) +else + ac_cv_cxx_thread=no fi -CXX="$ac_save_cxx" dnl # check for ANSI or K&R ("traditional") preprocessor dnl AC_MSG_CHECKING(for C preprocessor type) From webhook-mailer at python.org Thu Jan 5 18:04:44 2023 From: webhook-mailer at python.org (miss-islington) Date: Thu, 05 Jan 2023 23:04:44 -0000 Subject: [Python-checkins] gh-87691: add an absolute path pathlib example in / operator docs (GH-100737) Message-ID: https://github.com/python/cpython/commit/75861006d8e01d4e672995573a1651995418b5d2 commit: 75861006d8e01d4e672995573a1651995418b5d2 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-05T15:04:39-08:00 summary: gh-87691: add an absolute path pathlib example in / operator docs (GH-100737) The behaviour is fully explained a couple paragraphs above, but it may be useful to have a brief example to cover the behaviour. (cherry picked from commit 1ae619c911ec8e096f83eeb7cc57fcd966950a3d) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> Automerge-Triggered-By: GH:hauntsaninja files: M Doc/library/pathlib.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 843513c5fc54..8f0966fb7ca5 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -212,7 +212,10 @@ Paths of a different flavour compare unequal and cannot be ordered:: Operators ^^^^^^^^^ -The slash operator helps create child paths, similarly to :func:`os.path.join`:: +The slash operator helps create child paths, mimicking the behaviour of +:func:`os.path.join`. For instance, when several absolute paths are given, the +last is taken as an anchor; for a Windows path, changing the local root doesn't +discard the previous drive setting:: >>> p = PurePath('/etc') >>> p @@ -222,6 +225,10 @@ The slash operator helps create child paths, similarly to :func:`os.path.join`:: >>> q = PurePath('bin') >>> '/usr' / q PurePosixPath('/usr/bin') + >>> p / '/an_absolute_path' + PurePosixPath('/an_absolute_path') + >>> PureWindowsPath('c:/Windows', '/Program Files') + PureWindowsPath('c:/Program Files') A path object can be used anywhere an object implementing :class:`os.PathLike` is accepted:: From webhook-mailer at python.org Thu Jan 5 18:05:03 2023 From: webhook-mailer at python.org (miss-islington) Date: Thu, 05 Jan 2023 23:05:03 -0000 Subject: [Python-checkins] gh-87691: add an absolute path pathlib example in / operator docs (GH-100737) Message-ID: https://github.com/python/cpython/commit/4bdc57a3d2aceadbc9150febe6717ef92b8d6aa5 commit: 4bdc57a3d2aceadbc9150febe6717ef92b8d6aa5 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-05T15:04:57-08:00 summary: gh-87691: add an absolute path pathlib example in / operator docs (GH-100737) The behaviour is fully explained a couple paragraphs above, but it may be useful to have a brief example to cover the behaviour. (cherry picked from commit 1ae619c911ec8e096f83eeb7cc57fcd966950a3d) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> Automerge-Triggered-By: GH:hauntsaninja files: M Doc/library/pathlib.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index f969b1104f08..316fee2a0401 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -212,7 +212,10 @@ Paths of a different flavour compare unequal and cannot be ordered:: Operators ^^^^^^^^^ -The slash operator helps create child paths, similarly to :func:`os.path.join`:: +The slash operator helps create child paths, mimicking the behaviour of +:func:`os.path.join`. For instance, when several absolute paths are given, the +last is taken as an anchor; for a Windows path, changing the local root doesn't +discard the previous drive setting:: >>> p = PurePath('/etc') >>> p @@ -222,6 +225,10 @@ The slash operator helps create child paths, similarly to :func:`os.path.join`:: >>> q = PurePath('bin') >>> '/usr' / q PurePosixPath('/usr/bin') + >>> p / '/an_absolute_path' + PurePosixPath('/an_absolute_path') + >>> PureWindowsPath('c:/Windows', '/Program Files') + PureWindowsPath('c:/Program Files') A path object can be used anywhere an object implementing :class:`os.PathLike` is accepted:: From webhook-mailer at python.org Thu Jan 5 19:19:53 2023 From: webhook-mailer at python.org (ericvsmith) Date: Fri, 06 Jan 2023 00:19:53 -0000 Subject: [Python-checkins] gh-90104: avoid RecursionError on recursive dataclass field repr (gh-100756) Message-ID: https://github.com/python/cpython/commit/0a7936a38f0bab1619ee9fe257880a51c9d839d5 commit: 0a7936a38f0bab1619ee9fe257880a51c9d839d5 branch: main author: Carl Meyer committer: ericvsmith date: 2023-01-05T19:19:40-05:00 summary: gh-90104: avoid RecursionError on recursive dataclass field repr (gh-100756) Avoid RecursionError on recursive dataclass field repr files: A Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst M Lib/dataclasses.py M Lib/test/test_dataclasses.py diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index b54e16984cb1..5c0257eba186 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -223,6 +223,26 @@ def __repr__(self): # https://bugs.python.org/issue33453 for details. _MODULE_IDENTIFIER_RE = re.compile(r'^(?:\s*(\w+)\s*\.)?\s*(\w+)') +# This function's logic is copied from "recursive_repr" function in +# reprlib module to avoid dependency. +def _recursive_repr(user_function): + # Decorator to make a repr function return "..." for a recursive + # call. + repr_running = set() + + @functools.wraps(user_function) + def wrapper(self): + key = id(self), _thread.get_ident() + if key in repr_running: + return '...' + repr_running.add(key) + try: + result = user_function(self) + finally: + repr_running.discard(key) + return result + return wrapper + class InitVar: __slots__ = ('type', ) @@ -280,6 +300,7 @@ def __init__(self, default, default_factory, init, repr, hash, compare, self.kw_only = kw_only self._field_type = None + @_recursive_repr def __repr__(self): return ('Field(' f'name={self.name!r},' @@ -403,27 +424,6 @@ def _tuple_str(obj_name, fields): return f'({",".join([f"{obj_name}.{f.name}" for f in fields])},)' -# This function's logic is copied from "recursive_repr" function in -# reprlib module to avoid dependency. -def _recursive_repr(user_function): - # Decorator to make a repr function return "..." for a recursive - # call. - repr_running = set() - - @functools.wraps(user_function) - def wrapper(self): - key = id(self), _thread.get_ident() - if key in repr_running: - return '...' - repr_running.add(key) - try: - result = user_function(self) - finally: - repr_running.discard(key) - return result - return wrapper - - def _create_fn(name, args, body, *, globals=None, locals=None, return_type=MISSING): # Note that we may mutate locals. Callers beware! diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index a09f36c3dac1..81a36aa241ac 100644 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -68,6 +68,24 @@ def test_field_repr(self): self.assertEqual(repr_output, expected_output) + def test_field_recursive_repr(self): + rec_field = field() + rec_field.type = rec_field + rec_field.name = "id" + repr_output = repr(rec_field) + + self.assertIn(",type=...,", repr_output) + + def test_recursive_annotation(self): + class C: + pass + + @dataclass + class D: + C: C = field() + + self.assertIn(",type=...,", repr(D.__dataclass_fields__["C"])) + def test_dataclass_params_repr(self): # Even though this is testing an internal implementation detail, # it's testing a feature we want to make sure is correctly implemented diff --git a/Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst b/Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst new file mode 100644 index 000000000000..6695c920ee24 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst @@ -0,0 +1 @@ +Avoid RecursionError on ``repr`` if a dataclass field definition has a cyclic reference. From webhook-mailer at python.org Thu Jan 5 20:49:39 2023 From: webhook-mailer at python.org (hauntsaninja) Date: Fri, 06 Jan 2023 01:49:39 -0000 Subject: [Python-checkins] gh-87691: clarify use of anchor in pathlib docs (#100782) Message-ID: https://github.com/python/cpython/commit/2f2fa03ff3d566b675020787e23de8fb4ca78e99 commit: 2f2fa03ff3d566b675020787e23de8fb4ca78e99 branch: main author: Shantanu <12621235+hauntsaninja at users.noreply.github.com> committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com> date: 2023-01-05T17:49:33-08:00 summary: gh-87691: clarify use of anchor in pathlib docs (#100782) This is feedback from https://github.com/python/cpython/pull/100737#discussion_r1062968696 This matches the wording from the `os.path.join` docs better: https://docs.python.org/3/library/os.path.html#os.path.join In particular, the previous use of "anchor" was incorrect given the pathlib definition of "anchor". Co-authored-by: Barney Gale files: M Doc/library/pathlib.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index c90758c9306c..c5cf409f5f1f 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -118,16 +118,16 @@ we also call *flavours*: >>> PurePath() PurePosixPath('.') - When several absolute paths are given, the last is taken as an anchor - (mimicking :func:`os.path.join`'s behaviour):: + If a segment is an absolute path, all previous segments are ignored + (like :func:`os.path.join`):: >>> PurePath('/etc', '/usr', 'lib64') PurePosixPath('/usr/lib64') >>> PureWindowsPath('c:/Windows', 'd:bar') PureWindowsPath('d:bar') - However, in a Windows path, changing the local root doesn't discard the - previous drive setting:: + On Windows, the drive is not reset when a rooted relative path + segment (e.g., ``r'\foo'``) is encountered:: >>> PureWindowsPath('c:/Windows', '/Program Files') PureWindowsPath('c:/Program Files') @@ -212,10 +212,10 @@ Paths of a different flavour compare unequal and cannot be ordered:: Operators ^^^^^^^^^ -The slash operator helps create child paths, mimicking the behaviour of -:func:`os.path.join`. For instance, when several absolute paths are given, the -last is taken as an anchor; for a Windows path, changing the local root doesn't -discard the previous drive setting:: +The slash operator helps create child paths, like :func:`os.path.join`. +If the argument is an absolute path, the previous path is ignored. +On Windows, the drive is not reset when the argument is a rooted +relative path (e.g., ``r'\foo'``):: >>> p = PurePath('/etc') >>> p From webhook-mailer at python.org Thu Jan 5 20:56:39 2023 From: webhook-mailer at python.org (miss-islington) Date: Fri, 06 Jan 2023 01:56:39 -0000 Subject: [Python-checkins] gh-87691: clarify use of anchor in pathlib docs (GH-100782) Message-ID: https://github.com/python/cpython/commit/f5fa2c1e7f158fbc2ac4ad936e39fbe6253ae354 commit: f5fa2c1e7f158fbc2ac4ad936e39fbe6253ae354 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-05T17:56:34-08:00 summary: gh-87691: clarify use of anchor in pathlib docs (GH-100782) This is feedback from https://github.com/python/cpython/pull/100737GH-discussion_r1062968696 This matches the wording from the `os.path.join` docs better: https://docs.python.org/3/library/os.path.htmlGH-os.path.join In particular, the previous use of "anchor" was incorrect given the pathlib definition of "anchor". (cherry picked from commit 2f2fa03ff3d566b675020787e23de8fb4ca78e99) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> Co-authored-by: Barney Gale files: M Doc/library/pathlib.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 316fee2a0401..881f8d6bee61 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -118,16 +118,16 @@ we also call *flavours*: >>> PurePath() PurePosixPath('.') - When several absolute paths are given, the last is taken as an anchor - (mimicking :func:`os.path.join`'s behaviour):: + If a segment is an absolute path, all previous segments are ignored + (like :func:`os.path.join`):: >>> PurePath('/etc', '/usr', 'lib64') PurePosixPath('/usr/lib64') >>> PureWindowsPath('c:/Windows', 'd:bar') PureWindowsPath('d:bar') - However, in a Windows path, changing the local root doesn't discard the - previous drive setting:: + On Windows, the drive is not reset when a rooted relative path + segment (e.g., ``r'\foo'``) is encountered:: >>> PureWindowsPath('c:/Windows', '/Program Files') PureWindowsPath('c:/Program Files') @@ -212,10 +212,10 @@ Paths of a different flavour compare unequal and cannot be ordered:: Operators ^^^^^^^^^ -The slash operator helps create child paths, mimicking the behaviour of -:func:`os.path.join`. For instance, when several absolute paths are given, the -last is taken as an anchor; for a Windows path, changing the local root doesn't -discard the previous drive setting:: +The slash operator helps create child paths, like :func:`os.path.join`. +If the argument is an absolute path, the previous path is ignored. +On Windows, the drive is not reset when the argument is a rooted +relative path (e.g., ``r'\foo'``):: >>> p = PurePath('/etc') >>> p From webhook-mailer at python.org Thu Jan 5 20:59:34 2023 From: webhook-mailer at python.org (miss-islington) Date: Fri, 06 Jan 2023 01:59:34 -0000 Subject: [Python-checkins] gh-87691: clarify use of anchor in pathlib docs (GH-100782) Message-ID: https://github.com/python/cpython/commit/d6b8413e94d68649bdd3cac5d441f1cf4609b540 commit: d6b8413e94d68649bdd3cac5d441f1cf4609b540 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-05T17:59:27-08:00 summary: gh-87691: clarify use of anchor in pathlib docs (GH-100782) This is feedback from https://github.com/python/cpython/pull/100737GH-discussion_r1062968696 This matches the wording from the `os.path.join` docs better: https://docs.python.org/3/library/os.path.htmlGH-os.path.join In particular, the previous use of "anchor" was incorrect given the pathlib definition of "anchor". (cherry picked from commit 2f2fa03ff3d566b675020787e23de8fb4ca78e99) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> Co-authored-by: Barney Gale files: M Doc/library/pathlib.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 8f0966fb7ca5..9321c1d50e9c 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -118,16 +118,16 @@ we also call *flavours*: >>> PurePath() PurePosixPath('.') - When several absolute paths are given, the last is taken as an anchor - (mimicking :func:`os.path.join`'s behaviour):: + If a segment is an absolute path, all previous segments are ignored + (like :func:`os.path.join`):: >>> PurePath('/etc', '/usr', 'lib64') PurePosixPath('/usr/lib64') >>> PureWindowsPath('c:/Windows', 'd:bar') PureWindowsPath('d:bar') - However, in a Windows path, changing the local root doesn't discard the - previous drive setting:: + On Windows, the drive is not reset when a rooted relative path + segment (e.g., ``r'\foo'``) is encountered:: >>> PureWindowsPath('c:/Windows', '/Program Files') PureWindowsPath('c:/Program Files') @@ -212,10 +212,10 @@ Paths of a different flavour compare unequal and cannot be ordered:: Operators ^^^^^^^^^ -The slash operator helps create child paths, mimicking the behaviour of -:func:`os.path.join`. For instance, when several absolute paths are given, the -last is taken as an anchor; for a Windows path, changing the local root doesn't -discard the previous drive setting:: +The slash operator helps create child paths, like :func:`os.path.join`. +If the argument is an absolute path, the previous path is ignored. +On Windows, the drive is not reset when the argument is a rooted +relative path (e.g., ``r'\foo'``):: >>> p = PurePath('/etc') >>> p From webhook-mailer at python.org Thu Jan 5 21:08:38 2023 From: webhook-mailer at python.org (miss-islington) Date: Fri, 06 Jan 2023 02:08:38 -0000 Subject: [Python-checkins] [3.11] gh-90104: avoid RecursionError on recursive dataclass field repr (gh-100756) (GH-100784) Message-ID: https://github.com/python/cpython/commit/f4888315769a2267d9a1351cf182ebef0ae9c4b5 commit: f4888315769a2267d9a1351cf182ebef0ae9c4b5 branch: 3.11 author: Carl Meyer committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-05T18:08:33-08:00 summary: [3.11] gh-90104: avoid RecursionError on recursive dataclass field repr (gh-100756) (GH-100784) Avoid RecursionError on recursive dataclass field repr (cherry picked from commit 0a7936a38f0bab1619ee9fe257880a51c9d839d5) Automerge-Triggered-By: GH:ericvsmith files: A Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst M Lib/dataclasses.py M Lib/test/test_dataclasses.py diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 37e4ff702d61..2bfeea515b65 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -223,6 +223,26 @@ def __repr__(self): # https://bugs.python.org/issue33453 for details. _MODULE_IDENTIFIER_RE = re.compile(r'^(?:\s*(\w+)\s*\.)?\s*(\w+)') +# This function's logic is copied from "recursive_repr" function in +# reprlib module to avoid dependency. +def _recursive_repr(user_function): + # Decorator to make a repr function return "..." for a recursive + # call. + repr_running = set() + + @functools.wraps(user_function) + def wrapper(self): + key = id(self), _thread.get_ident() + if key in repr_running: + return '...' + repr_running.add(key) + try: + result = user_function(self) + finally: + repr_running.discard(key) + return result + return wrapper + class InitVar: __slots__ = ('type', ) @@ -280,6 +300,7 @@ def __init__(self, default, default_factory, init, repr, hash, compare, self.kw_only = kw_only self._field_type = None + @_recursive_repr def __repr__(self): return ('Field(' f'name={self.name!r},' @@ -389,27 +410,6 @@ def _tuple_str(obj_name, fields): return f'({",".join([f"{obj_name}.{f.name}" for f in fields])},)' -# This function's logic is copied from "recursive_repr" function in -# reprlib module to avoid dependency. -def _recursive_repr(user_function): - # Decorator to make a repr function return "..." for a recursive - # call. - repr_running = set() - - @functools.wraps(user_function) - def wrapper(self): - key = id(self), _thread.get_ident() - if key in repr_running: - return '...' - repr_running.add(key) - try: - result = user_function(self) - finally: - repr_running.discard(key) - return result - return wrapper - - def _create_fn(name, args, body, *, globals=None, locals=None, return_type=MISSING): # Note that we may mutate locals. Callers beware! diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index 0d809bd2eb9a..9af43771fac0 100644 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -68,6 +68,24 @@ def test_field_repr(self): self.assertEqual(repr_output, expected_output) + def test_field_recursive_repr(self): + rec_field = field() + rec_field.type = rec_field + rec_field.name = "id" + repr_output = repr(rec_field) + + self.assertIn(",type=...,", repr_output) + + def test_recursive_annotation(self): + class C: + pass + + @dataclass + class D: + C: C = field() + + self.assertIn(",type=...,", repr(D.__dataclass_fields__["C"])) + def test_named_init_params(self): @dataclass class C: diff --git a/Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst b/Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst new file mode 100644 index 000000000000..6695c920ee24 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst @@ -0,0 +1 @@ +Avoid RecursionError on ``repr`` if a dataclass field definition has a cyclic reference. From webhook-mailer at python.org Thu Jan 5 21:15:33 2023 From: webhook-mailer at python.org (miss-islington) Date: Fri, 06 Jan 2023 02:15:33 -0000 Subject: [Python-checkins] [3.10] gh-90104: avoid RecursionError on recursive dataclass field repr (gh-100756) (GH-100785) Message-ID: https://github.com/python/cpython/commit/ebe8d2340733a36fd7555e1c75bb103d9ef465ec commit: ebe8d2340733a36fd7555e1c75bb103d9ef465ec branch: 3.10 author: Carl Meyer committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-05T18:15:27-08:00 summary: [3.10] gh-90104: avoid RecursionError on recursive dataclass field repr (gh-100756) (GH-100785) Avoid RecursionError on recursive dataclass field repr (cherry picked from commit 0a7936a38f0bab1619ee9fe257880a51c9d839d5) Automerge-Triggered-By: GH:ericvsmith files: A Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst M Lib/dataclasses.py M Lib/test/test_dataclasses.py diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index e1687a117d6f..9dc67fa47708 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -222,6 +222,26 @@ def __repr__(self): # https://bugs.python.org/issue33453 for details. _MODULE_IDENTIFIER_RE = re.compile(r'^(?:\s*(\w+)\s*\.)?\s*(\w+)') +# This function's logic is copied from "recursive_repr" function in +# reprlib module to avoid dependency. +def _recursive_repr(user_function): + # Decorator to make a repr function return "..." for a recursive + # call. + repr_running = set() + + @functools.wraps(user_function) + def wrapper(self): + key = id(self), _thread.get_ident() + if key in repr_running: + return '...' + repr_running.add(key) + try: + result = user_function(self) + finally: + repr_running.discard(key) + return result + return wrapper + class InitVar: __slots__ = ('type', ) @@ -279,6 +299,7 @@ def __init__(self, default, default_factory, init, repr, hash, compare, self.kw_only = kw_only self._field_type = None + @_recursive_repr def __repr__(self): return ('Field(' f'name={self.name!r},' @@ -388,27 +409,6 @@ def _tuple_str(obj_name, fields): return f'({",".join([f"{obj_name}.{f.name}" for f in fields])},)' -# This function's logic is copied from "recursive_repr" function in -# reprlib module to avoid dependency. -def _recursive_repr(user_function): - # Decorator to make a repr function return "..." for a recursive - # call. - repr_running = set() - - @functools.wraps(user_function) - def wrapper(self): - key = id(self), _thread.get_ident() - if key in repr_running: - return '...' - repr_running.add(key) - try: - result = user_function(self) - finally: - repr_running.discard(key) - return result - return wrapper - - def _create_fn(name, args, body, *, globals=None, locals=None, return_type=MISSING): # Note that we may mutate locals. Callers beware! diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index e805f0cf4cc7..a642ed96c7ff 100644 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -67,6 +67,24 @@ def test_field_repr(self): self.assertEqual(repr_output, expected_output) + def test_field_recursive_repr(self): + rec_field = field() + rec_field.type = rec_field + rec_field.name = "id" + repr_output = repr(rec_field) + + self.assertIn(",type=...,", repr_output) + + def test_recursive_annotation(self): + class C: + pass + + @dataclass + class D: + C: C = field() + + self.assertIn(",type=...,", repr(D.__dataclass_fields__["C"])) + def test_named_init_params(self): @dataclass class C: diff --git a/Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst b/Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst new file mode 100644 index 000000000000..6695c920ee24 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst @@ -0,0 +1 @@ +Avoid RecursionError on ``repr`` if a dataclass field definition has a cyclic reference. From webhook-mailer at python.org Fri Jan 6 09:26:50 2023 From: webhook-mailer at python.org (ericvsmith) Date: Fri, 06 Jan 2023 14:26:50 -0000 Subject: [Python-checkins] gh-99026 update dataclasses docs for when annotations are inspected (gh-100798) Message-ID: https://github.com/python/cpython/commit/659c2607f5b44a8a18a0840d1ac39df8a3219dd5 commit: 659c2607f5b44a8a18a0840d1ac39df8a3219dd5 branch: main author: Akshit Tyagi <37214399+exitflynn at users.noreply.github.com> committer: ericvsmith date: 2023-01-06T09:26:44-05:00 summary: gh-99026 update dataclasses docs for when annotations are inspected (gh-100798) update dataclasses docs for when annotations are inspected files: M Doc/library/dataclasses.rst diff --git a/Doc/library/dataclasses.rst b/Doc/library/dataclasses.rst index 32c524a73487..82faa7b77450 100644 --- a/Doc/library/dataclasses.rst +++ b/Doc/library/dataclasses.rst @@ -552,7 +552,7 @@ parameters to :meth:`__post_init__`. Also see the warning about how Class variables --------------- -One of two places where :func:`dataclass` actually inspects the type +One of the few places where :func:`dataclass` actually inspects the type of a field is to determine if a field is a class variable as defined in :pep:`526`. It does this by checking if the type of the field is ``typing.ClassVar``. If a field is a ``ClassVar``, it is excluded @@ -563,7 +563,7 @@ module-level :func:`fields` function. Init-only variables ------------------- -The other place where :func:`dataclass` inspects a type annotation is to +Another place where :func:`dataclass` inspects a type annotation is to determine if a field is an init-only variable. It does this by seeing if the type of a field is of type ``dataclasses.InitVar``. If a field is an ``InitVar``, it is considered a pseudo-field called an init-only From webhook-mailer at python.org Fri Jan 6 09:34:18 2023 From: webhook-mailer at python.org (miss-islington) Date: Fri, 06 Jan 2023 14:34:18 -0000 Subject: [Python-checkins] gh-99026 update dataclasses docs for when annotations are inspected (gh-100798) Message-ID: https://github.com/python/cpython/commit/8af15cfc8eb2fe9584d441260743a53e0d74eb57 commit: 8af15cfc8eb2fe9584d441260743a53e0d74eb57 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-06T06:34:12-08:00 summary: gh-99026 update dataclasses docs for when annotations are inspected (gh-100798) update dataclasses docs for when annotations are inspected (cherry picked from commit 659c2607f5b44a8a18a0840d1ac39df8a3219dd5) Co-authored-by: Akshit Tyagi <37214399+exitflynn at users.noreply.github.com> files: M Doc/library/dataclasses.rst diff --git a/Doc/library/dataclasses.rst b/Doc/library/dataclasses.rst index add6043b6066..5b3e831f25a5 100644 --- a/Doc/library/dataclasses.rst +++ b/Doc/library/dataclasses.rst @@ -535,7 +535,7 @@ parameters to :meth:`__post_init__`. Also see the warning about how Class variables --------------- -One of two places where :func:`dataclass` actually inspects the type +One of the few places where :func:`dataclass` actually inspects the type of a field is to determine if a field is a class variable as defined in :pep:`526`. It does this by checking if the type of the field is ``typing.ClassVar``. If a field is a ``ClassVar``, it is excluded @@ -546,7 +546,7 @@ module-level :func:`fields` function. Init-only variables ------------------- -The other place where :func:`dataclass` inspects a type annotation is to +Another place where :func:`dataclass` inspects a type annotation is to determine if a field is an init-only variable. It does this by seeing if the type of a field is of type ``dataclasses.InitVar``. If a field is an ``InitVar``, it is considered a pseudo-field called an init-only From webhook-mailer at python.org Fri Jan 6 09:34:36 2023 From: webhook-mailer at python.org (miss-islington) Date: Fri, 06 Jan 2023 14:34:36 -0000 Subject: [Python-checkins] gh-99026 update dataclasses docs for when annotations are inspected (gh-100798) Message-ID: https://github.com/python/cpython/commit/72263f2a20002ceff443e3a231c713f2e14fe3fe commit: 72263f2a20002ceff443e3a231c713f2e14fe3fe branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-06T06:34:30-08:00 summary: gh-99026 update dataclasses docs for when annotations are inspected (gh-100798) update dataclasses docs for when annotations are inspected (cherry picked from commit 659c2607f5b44a8a18a0840d1ac39df8a3219dd5) Co-authored-by: Akshit Tyagi <37214399+exitflynn at users.noreply.github.com> files: M Doc/library/dataclasses.rst diff --git a/Doc/library/dataclasses.rst b/Doc/library/dataclasses.rst index 32c524a73487..82faa7b77450 100644 --- a/Doc/library/dataclasses.rst +++ b/Doc/library/dataclasses.rst @@ -552,7 +552,7 @@ parameters to :meth:`__post_init__`. Also see the warning about how Class variables --------------- -One of two places where :func:`dataclass` actually inspects the type +One of the few places where :func:`dataclass` actually inspects the type of a field is to determine if a field is a class variable as defined in :pep:`526`. It does this by checking if the type of the field is ``typing.ClassVar``. If a field is a ``ClassVar``, it is excluded @@ -563,7 +563,7 @@ module-level :func:`fields` function. Init-only variables ------------------- -The other place where :func:`dataclass` inspects a type annotation is to +Another place where :func:`dataclass` inspects a type annotation is to determine if a field is an init-only variable. It does this by seeing if the type of a field is of type ``dataclasses.InitVar``. If a field is an ``InitVar``, it is considered a pseudo-field called an init-only From webhook-mailer at python.org Fri Jan 6 09:48:03 2023 From: webhook-mailer at python.org (markshannon) Date: Fri, 06 Jan 2023 14:48:03 -0000 Subject: [Python-checkins] GH-99005: More intrinsics (GH-100774) Message-ID: https://github.com/python/cpython/commit/78068126a1f2172ff61a0871ba43d8530bc73905 commit: 78068126a1f2172ff61a0871ba43d8530bc73905 branch: main author: Mark Shannon committer: markshannon date: 2023-01-06T14:47:57Z summary: GH-99005: More intrinsics (GH-100774) * Remove UNARY_POSITIVE, LIST_TO_TUPLE and ASYNC_GEN_WRAP, replacing them with intrinsics. files: A Misc/NEWS.d/next/Core and Builtins/2023-01-05-17-54-29.gh-issue-99005.cmGwxv.rst M Doc/library/dis.rst M Include/internal/pycore_genobject.h M Include/internal/pycore_intrinsics.h M Include/internal/pycore_opcode.h M Include/opcode.h M Lib/importlib/_bootstrap_external.py M Lib/opcode.py M Lib/test/test_lltrace.py M Objects/genobject.c M Python/bytecodes.c M Python/compile.c M Python/generated_cases.c.h M Python/intrinsics.c M Python/opcode_metadata.h M Python/opcode_targets.h diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 33ef6d7ed495..334b9df4fc1f 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -460,10 +460,6 @@ The Python compiler currently generates the following bytecode instructions. Unary operations take the top of the stack, apply the operation, and push the result back on the stack. -.. opcode:: UNARY_POSITIVE - - Implements ``TOS = +TOS``. - .. opcode:: UNARY_NEGATIVE @@ -906,13 +902,6 @@ iterations of the loop. .. versionadded:: 3.6 -.. opcode:: LIST_TO_TUPLE - - Pops a list from the stack and pushes a tuple containing the same values. - - .. versionadded:: 3.9 - - .. opcode:: LIST_EXTEND (i) Calls ``list.extend(TOS1[-i], TOS)``. Used to build lists. @@ -1372,14 +1361,6 @@ iterations of the loop. .. versionadded:: 3.11 -.. opcode:: ASYNC_GEN_WRAP - - Wraps the value on top of the stack in an ``async_generator_wrapped_value``. - Used to yield in async generators. - - .. versionadded:: 3.11 - - .. opcode:: HAVE_ARGUMENT This is not really an opcode. It identifies the dividing line between @@ -1411,6 +1392,9 @@ iterations of the loop. * ``1`` Prints the argument to standard out. Used in the REPL. * ``2`` Performs ``import *`` for the named module. * ``3`` Extracts the return value from a ``StopIteration`` exception. + * ``4`` Wraps an aync generator value + * ``5`` Performs the unary ``+`` operation + * ``6`` Converts a list to a tuple .. versionadded:: 3.12 diff --git a/Include/internal/pycore_genobject.h b/Include/internal/pycore_genobject.h index 42db0d87d1f4..dc60b4ca7051 100644 --- a/Include/internal/pycore_genobject.h +++ b/Include/internal/pycore_genobject.h @@ -10,7 +10,7 @@ extern "C" { extern PyObject *_PyGen_yf(PyGenObject *); extern PyObject *_PyCoro_GetAwaitableIter(PyObject *o); -extern PyObject *_PyAsyncGenValueWrapperNew(PyObject *); +extern PyObject *_PyAsyncGenValueWrapperNew(PyThreadState *state, PyObject *); /* runtime lifecycle */ diff --git a/Include/internal/pycore_intrinsics.h b/Include/internal/pycore_intrinsics.h index 92f06c736267..1da618f2b4a5 100644 --- a/Include/internal/pycore_intrinsics.h +++ b/Include/internal/pycore_intrinsics.h @@ -2,8 +2,11 @@ #define INTRINSIC_PRINT 1 #define INTRINSIC_IMPORT_STAR 2 #define INTRINSIC_STOPITERATION_ERROR 3 +#define INTRINSIC_ASYNC_GEN_WRAP 4 +#define INTRINSIC_UNARY_POSITIVE 5 +#define INTRINSIC_LIST_TO_TUPLE 6 -#define MAX_INTRINSIC_1 3 +#define MAX_INTRINSIC_1 6 typedef PyObject *(*instrinsic_func1)(PyThreadState* tstate, PyObject *value); diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index e952690cba3a..8fdf121aecc1 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -54,7 +54,6 @@ const uint8_t _PyOpcode_Caches[256] = { }; const uint8_t _PyOpcode_Deopt[256] = { - [ASYNC_GEN_WRAP] = ASYNC_GEN_WRAP, [BEFORE_ASYNC_WITH] = BEFORE_ASYNC_WITH, [BEFORE_WITH] = BEFORE_WITH, [BINARY_OP] = BINARY_OP, @@ -145,7 +144,6 @@ const uint8_t _PyOpcode_Deopt[256] = { [KW_NAMES] = KW_NAMES, [LIST_APPEND] = LIST_APPEND, [LIST_EXTEND] = LIST_EXTEND, - [LIST_TO_TUPLE] = LIST_TO_TUPLE, [LOAD_ASSERTION_ERROR] = LOAD_ASSERTION_ERROR, [LOAD_ATTR] = LOAD_ATTR, [LOAD_ATTR_CLASS] = LOAD_ATTR, @@ -216,7 +214,6 @@ const uint8_t _PyOpcode_Deopt[256] = { [UNARY_INVERT] = UNARY_INVERT, [UNARY_NEGATIVE] = UNARY_NEGATIVE, [UNARY_NOT] = UNARY_NOT, - [UNARY_POSITIVE] = UNARY_POSITIVE, [UNPACK_EX] = UNPACK_EX, [UNPACK_SEQUENCE] = UNPACK_SEQUENCE, [UNPACK_SEQUENCE_LIST] = UNPACK_SEQUENCE, @@ -239,13 +236,12 @@ static const char *const _PyOpcode_OpName[263] = { [BINARY_OP_ADD_UNICODE] = "BINARY_OP_ADD_UNICODE", [BINARY_OP_INPLACE_ADD_UNICODE] = "BINARY_OP_INPLACE_ADD_UNICODE", [NOP] = "NOP", - [UNARY_POSITIVE] = "UNARY_POSITIVE", + [BINARY_OP_MULTIPLY_FLOAT] = "BINARY_OP_MULTIPLY_FLOAT", [UNARY_NEGATIVE] = "UNARY_NEGATIVE", [UNARY_NOT] = "UNARY_NOT", - [BINARY_OP_MULTIPLY_FLOAT] = "BINARY_OP_MULTIPLY_FLOAT", [BINARY_OP_MULTIPLY_INT] = "BINARY_OP_MULTIPLY_INT", - [UNARY_INVERT] = "UNARY_INVERT", [BINARY_OP_SUBTRACT_FLOAT] = "BINARY_OP_SUBTRACT_FLOAT", + [UNARY_INVERT] = "UNARY_INVERT", [BINARY_OP_SUBTRACT_INT] = "BINARY_OP_SUBTRACT_INT", [BINARY_SUBSCR_DICT] = "BINARY_SUBSCR_DICT", [BINARY_SUBSCR_GETITEM] = "BINARY_SUBSCR_GETITEM", @@ -254,20 +250,20 @@ static const char *const _PyOpcode_OpName[263] = { [CALL_PY_EXACT_ARGS] = "CALL_PY_EXACT_ARGS", [CALL_PY_WITH_DEFAULTS] = "CALL_PY_WITH_DEFAULTS", [CALL_BOUND_METHOD_EXACT_ARGS] = "CALL_BOUND_METHOD_EXACT_ARGS", + [CALL_BUILTIN_CLASS] = "CALL_BUILTIN_CLASS", [BINARY_SUBSCR] = "BINARY_SUBSCR", [BINARY_SLICE] = "BINARY_SLICE", [STORE_SLICE] = "STORE_SLICE", - [CALL_BUILTIN_CLASS] = "CALL_BUILTIN_CLASS", [CALL_BUILTIN_FAST_WITH_KEYWORDS] = "CALL_BUILTIN_FAST_WITH_KEYWORDS", + [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS", [GET_LEN] = "GET_LEN", [MATCH_MAPPING] = "MATCH_MAPPING", [MATCH_SEQUENCE] = "MATCH_SEQUENCE", [MATCH_KEYS] = "MATCH_KEYS", - [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS", + [CALL_NO_KW_BUILTIN_FAST] = "CALL_NO_KW_BUILTIN_FAST", [PUSH_EXC_INFO] = "PUSH_EXC_INFO", [CHECK_EXC_MATCH] = "CHECK_EXC_MATCH", [CHECK_EG_MATCH] = "CHECK_EG_MATCH", - [CALL_NO_KW_BUILTIN_FAST] = "CALL_NO_KW_BUILTIN_FAST", [CALL_NO_KW_BUILTIN_O] = "CALL_NO_KW_BUILTIN_O", [CALL_NO_KW_ISINSTANCE] = "CALL_NO_KW_ISINSTANCE", [CALL_NO_KW_LEN] = "CALL_NO_KW_LEN", @@ -278,6 +274,7 @@ static const char *const _PyOpcode_OpName[263] = { [CALL_NO_KW_STR_1] = "CALL_NO_KW_STR_1", [CALL_NO_KW_TUPLE_1] = "CALL_NO_KW_TUPLE_1", [CALL_NO_KW_TYPE_1] = "CALL_NO_KW_TYPE_1", + [COMPARE_OP_FLOAT_JUMP] = "COMPARE_OP_FLOAT_JUMP", [WITH_EXCEPT_START] = "WITH_EXCEPT_START", [GET_AITER] = "GET_AITER", [GET_ANEXT] = "GET_ANEXT", @@ -285,38 +282,38 @@ static const char *const _PyOpcode_OpName[263] = { [BEFORE_WITH] = "BEFORE_WITH", [END_ASYNC_FOR] = "END_ASYNC_FOR", [CLEANUP_THROW] = "CLEANUP_THROW", - [COMPARE_OP_FLOAT_JUMP] = "COMPARE_OP_FLOAT_JUMP", [COMPARE_OP_INT_JUMP] = "COMPARE_OP_INT_JUMP", [COMPARE_OP_STR_JUMP] = "COMPARE_OP_STR_JUMP", [FOR_ITER_LIST] = "FOR_ITER_LIST", + [FOR_ITER_TUPLE] = "FOR_ITER_TUPLE", [STORE_SUBSCR] = "STORE_SUBSCR", [DELETE_SUBSCR] = "DELETE_SUBSCR", - [FOR_ITER_TUPLE] = "FOR_ITER_TUPLE", [FOR_ITER_RANGE] = "FOR_ITER_RANGE", [FOR_ITER_GEN] = "FOR_ITER_GEN", [LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS", [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN", [LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE", + [LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE", [GET_ITER] = "GET_ITER", [GET_YIELD_FROM_ITER] = "GET_YIELD_FROM_ITER", - [LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE", - [LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS", [LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY", + [LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS", [LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT", + [LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT", [LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR", [RETURN_GENERATOR] = "RETURN_GENERATOR", - [LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT", [LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT", [LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT", [LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES", [LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST", [LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST", - [LIST_TO_TUPLE] = "LIST_TO_TUPLE", - [RETURN_VALUE] = "RETURN_VALUE", [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST", - [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS", [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN", - [ASYNC_GEN_WRAP] = "ASYNC_GEN_WRAP", + [RETURN_VALUE] = "RETURN_VALUE", + [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE", + [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS", + [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE", + [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT", [PREP_RERAISE_STAR] = "PREP_RERAISE_STAR", [POP_EXCEPT] = "POP_EXCEPT", [STORE_NAME] = "STORE_NAME", @@ -342,7 +339,7 @@ static const char *const _PyOpcode_OpName[263] = { [JUMP_FORWARD] = "JUMP_FORWARD", [JUMP_IF_FALSE_OR_POP] = "JUMP_IF_FALSE_OR_POP", [JUMP_IF_TRUE_OR_POP] = "JUMP_IF_TRUE_OR_POP", - [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE", + [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT", [POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE", [POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE", [LOAD_GLOBAL] = "LOAD_GLOBAL", @@ -350,7 +347,7 @@ static const char *const _PyOpcode_OpName[263] = { [CONTAINS_OP] = "CONTAINS_OP", [RERAISE] = "RERAISE", [COPY] = "COPY", - [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE", + [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", [BINARY_OP] = "BINARY_OP", [SEND] = "SEND", [LOAD_FAST] = "LOAD_FAST", @@ -370,9 +367,9 @@ static const char *const _PyOpcode_OpName[263] = { [STORE_DEREF] = "STORE_DEREF", [DELETE_DEREF] = "DELETE_DEREF", [JUMP_BACKWARD] = "JUMP_BACKWARD", - [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT", + [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX", - [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT", + [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT", [EXTENDED_ARG] = "EXTENDED_ARG", [LIST_APPEND] = "LIST_APPEND", [SET_ADD] = "SET_ADD", @@ -382,20 +379,20 @@ static const char *const _PyOpcode_OpName[263] = { [YIELD_VALUE] = "YIELD_VALUE", [RESUME] = "RESUME", [MATCH_CLASS] = "MATCH_CLASS", - [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", - [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", + [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", + [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST", [FORMAT_VALUE] = "FORMAT_VALUE", [BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP", [BUILD_STRING] = "BUILD_STRING", - [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT", - [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", - [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST", [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE", + [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE", + [160] = "<160>", + [161] = "<161>", [LIST_EXTEND] = "LIST_EXTEND", [SET_UPDATE] = "SET_UPDATE", [DICT_MERGE] = "DICT_MERGE", [DICT_UPDATE] = "DICT_UPDATE", - [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE", + [166] = "<166>", [167] = "<167>", [168] = "<168>", [169] = "<169>", @@ -496,6 +493,9 @@ static const char *const _PyOpcode_OpName[263] = { #endif #define EXTRA_CASES \ + case 160: \ + case 161: \ + case 166: \ case 167: \ case 168: \ case 169: \ diff --git a/Include/opcode.h b/Include/opcode.h index 44b77250ad68..fcc46a3ede9d 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -14,7 +14,6 @@ extern "C" { #define INTERPRETER_EXIT 3 #define END_FOR 4 #define NOP 9 -#define UNARY_POSITIVE 10 #define UNARY_NEGATIVE 11 #define UNARY_NOT 12 #define UNARY_INVERT 15 @@ -42,10 +41,8 @@ extern "C" { #define LOAD_BUILD_CLASS 71 #define LOAD_ASSERTION_ERROR 74 #define RETURN_GENERATOR 75 -#define LIST_TO_TUPLE 82 #define RETURN_VALUE 83 #define SETUP_ANNOTATIONS 85 -#define ASYNC_GEN_WRAP 87 #define PREP_RERAISE_STAR 88 #define POP_EXCEPT 89 #define HAVE_ARGUMENT 90 @@ -131,63 +128,63 @@ extern "C" { #define BINARY_OP_ADD_INT 6 #define BINARY_OP_ADD_UNICODE 7 #define BINARY_OP_INPLACE_ADD_UNICODE 8 -#define BINARY_OP_MULTIPLY_FLOAT 13 -#define BINARY_OP_MULTIPLY_INT 14 -#define BINARY_OP_SUBTRACT_FLOAT 16 -#define BINARY_OP_SUBTRACT_INT 17 -#define BINARY_SUBSCR_DICT 18 -#define BINARY_SUBSCR_GETITEM 19 -#define BINARY_SUBSCR_LIST_INT 20 -#define BINARY_SUBSCR_TUPLE_INT 21 -#define CALL_PY_EXACT_ARGS 22 -#define CALL_PY_WITH_DEFAULTS 23 -#define CALL_BOUND_METHOD_EXACT_ARGS 24 -#define CALL_BUILTIN_CLASS 28 -#define CALL_BUILTIN_FAST_WITH_KEYWORDS 29 -#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 34 -#define CALL_NO_KW_BUILTIN_FAST 38 -#define CALL_NO_KW_BUILTIN_O 39 -#define CALL_NO_KW_ISINSTANCE 40 -#define CALL_NO_KW_LEN 41 -#define CALL_NO_KW_LIST_APPEND 42 -#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 43 -#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 44 -#define CALL_NO_KW_METHOD_DESCRIPTOR_O 45 -#define CALL_NO_KW_STR_1 46 -#define CALL_NO_KW_TUPLE_1 47 -#define CALL_NO_KW_TYPE_1 48 -#define COMPARE_OP_FLOAT_JUMP 56 -#define COMPARE_OP_INT_JUMP 57 -#define COMPARE_OP_STR_JUMP 58 -#define FOR_ITER_LIST 59 -#define FOR_ITER_TUPLE 62 -#define FOR_ITER_RANGE 63 -#define FOR_ITER_GEN 64 -#define LOAD_ATTR_CLASS 65 -#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 66 -#define LOAD_ATTR_INSTANCE_VALUE 67 -#define LOAD_ATTR_MODULE 70 -#define LOAD_ATTR_PROPERTY 72 -#define LOAD_ATTR_SLOT 73 -#define LOAD_ATTR_WITH_HINT 76 -#define LOAD_ATTR_METHOD_LAZY_DICT 77 -#define LOAD_ATTR_METHOD_NO_DICT 78 -#define LOAD_ATTR_METHOD_WITH_VALUES 79 -#define LOAD_CONST__LOAD_FAST 80 -#define LOAD_FAST__LOAD_CONST 81 -#define LOAD_FAST__LOAD_FAST 84 -#define LOAD_GLOBAL_BUILTIN 86 -#define LOAD_GLOBAL_MODULE 113 -#define STORE_ATTR_INSTANCE_VALUE 121 -#define STORE_ATTR_SLOT 141 -#define STORE_ATTR_WITH_HINT 143 -#define STORE_FAST__LOAD_FAST 153 -#define STORE_FAST__STORE_FAST 154 -#define STORE_SUBSCR_DICT 158 -#define STORE_SUBSCR_LIST_INT 159 -#define UNPACK_SEQUENCE_LIST 160 -#define UNPACK_SEQUENCE_TUPLE 161 -#define UNPACK_SEQUENCE_TWO_TUPLE 166 +#define BINARY_OP_MULTIPLY_FLOAT 10 +#define BINARY_OP_MULTIPLY_INT 13 +#define BINARY_OP_SUBTRACT_FLOAT 14 +#define BINARY_OP_SUBTRACT_INT 16 +#define BINARY_SUBSCR_DICT 17 +#define BINARY_SUBSCR_GETITEM 18 +#define BINARY_SUBSCR_LIST_INT 19 +#define BINARY_SUBSCR_TUPLE_INT 20 +#define CALL_PY_EXACT_ARGS 21 +#define CALL_PY_WITH_DEFAULTS 22 +#define CALL_BOUND_METHOD_EXACT_ARGS 23 +#define CALL_BUILTIN_CLASS 24 +#define CALL_BUILTIN_FAST_WITH_KEYWORDS 28 +#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 29 +#define CALL_NO_KW_BUILTIN_FAST 34 +#define CALL_NO_KW_BUILTIN_O 38 +#define CALL_NO_KW_ISINSTANCE 39 +#define CALL_NO_KW_LEN 40 +#define CALL_NO_KW_LIST_APPEND 41 +#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 42 +#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 43 +#define CALL_NO_KW_METHOD_DESCRIPTOR_O 44 +#define CALL_NO_KW_STR_1 45 +#define CALL_NO_KW_TUPLE_1 46 +#define CALL_NO_KW_TYPE_1 47 +#define COMPARE_OP_FLOAT_JUMP 48 +#define COMPARE_OP_INT_JUMP 56 +#define COMPARE_OP_STR_JUMP 57 +#define FOR_ITER_LIST 58 +#define FOR_ITER_TUPLE 59 +#define FOR_ITER_RANGE 62 +#define FOR_ITER_GEN 63 +#define LOAD_ATTR_CLASS 64 +#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 65 +#define LOAD_ATTR_INSTANCE_VALUE 66 +#define LOAD_ATTR_MODULE 67 +#define LOAD_ATTR_PROPERTY 70 +#define LOAD_ATTR_SLOT 72 +#define LOAD_ATTR_WITH_HINT 73 +#define LOAD_ATTR_METHOD_LAZY_DICT 76 +#define LOAD_ATTR_METHOD_NO_DICT 77 +#define LOAD_ATTR_METHOD_WITH_VALUES 78 +#define LOAD_CONST__LOAD_FAST 79 +#define LOAD_FAST__LOAD_CONST 80 +#define LOAD_FAST__LOAD_FAST 81 +#define LOAD_GLOBAL_BUILTIN 82 +#define LOAD_GLOBAL_MODULE 84 +#define STORE_ATTR_INSTANCE_VALUE 86 +#define STORE_ATTR_SLOT 87 +#define STORE_ATTR_WITH_HINT 113 +#define STORE_FAST__LOAD_FAST 121 +#define STORE_FAST__STORE_FAST 141 +#define STORE_SUBSCR_DICT 143 +#define STORE_SUBSCR_LIST_INT 153 +#define UNPACK_SEQUENCE_LIST 154 +#define UNPACK_SEQUENCE_TUPLE 158 +#define UNPACK_SEQUENCE_TWO_TUPLE 159 #define DO_TRACING 255 #define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\ diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index e700ce634aca..8a21e0e46469 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -427,6 +427,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.12a1 3511 (Add STOPITERATION_ERROR instruction) # Python 3.12a1 3512 (Remove all unused consts from code objects) # Python 3.12a1 3513 (Add CALL_INTRINSIC_1 instruction, removed STOPITERATION_ERROR, PRINT_EXPR, IMPORT_STAR) +# Python 3.12a1 3514 (Remove ASYNC_GEN_WRAP, LIST_TO_TUPLE, and UNARY_POSITIVE) # Python 3.13 will start with 3550 @@ -439,7 +440,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3513).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3514).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c diff --git a/Lib/opcode.py b/Lib/opcode.py index a5dea8c6fd24..9ba39e2eec75 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -82,7 +82,7 @@ def pseudo_op(name, op, real_ops): def_op('END_FOR', 4) def_op('NOP', 9) -def_op('UNARY_POSITIVE', 10) + def_op('UNARY_NEGATIVE', 11) def_op('UNARY_NOT', 12) @@ -120,12 +120,10 @@ def pseudo_op(name, op, real_ops): def_op('LOAD_ASSERTION_ERROR', 74) def_op('RETURN_GENERATOR', 75) -def_op('LIST_TO_TUPLE', 82) def_op('RETURN_VALUE', 83) def_op('SETUP_ANNOTATIONS', 85) -def_op('ASYNC_GEN_WRAP', 87) def_op('PREP_RERAISE_STAR', 88) def_op('POP_EXCEPT', 89) diff --git a/Lib/test/test_lltrace.py b/Lib/test/test_lltrace.py index 747666e25670..f638b649bfb9 100644 --- a/Lib/test/test_lltrace.py +++ b/Lib/test/test_lltrace.py @@ -54,7 +54,7 @@ def dont_trace_2(): """) self.assertIn("GET_ITER", stdout) self.assertIn("FOR_ITER", stdout) - self.assertIn("UNARY_POSITIVE", stdout) + self.assertIn("CALL_INTRINSIC_1", stdout) self.assertIn("POP_TOP", stdout) self.assertNotIn("BINARY_OP", stdout) self.assertNotIn("UNARY_NEGATIVE", stdout) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-05-17-54-29.gh-issue-99005.cmGwxv.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-05-17-54-29.gh-issue-99005.cmGwxv.rst new file mode 100644 index 000000000000..e6ee44225345 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-05-17-54-29.gh-issue-99005.cmGwxv.rst @@ -0,0 +1,2 @@ +Remove :opcode:`UNARY_POSITIVE`, :opcode:`ASYNC_GEN_WRAP` and +:opcode:`LIST_TO_TUPLE`, replacing them with intrinsics. diff --git a/Objects/genobject.c b/Objects/genobject.c index ea3382d3e31a..6f4046eaa0ef 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -1970,13 +1970,13 @@ PyTypeObject _PyAsyncGenWrappedValue_Type = { PyObject * -_PyAsyncGenValueWrapperNew(PyObject *val) +_PyAsyncGenValueWrapperNew(PyThreadState *tstate, PyObject *val) { _PyAsyncGenWrappedValue *o; assert(val); #if _PyAsyncGen_MAXFREELIST > 0 - struct _Py_async_gen_state *state = get_async_gen_state(); + struct _Py_async_gen_state *state = &tstate->interp->async_gen; #ifdef Py_DEBUG // _PyAsyncGenValueWrapperNew() must not be called after _PyAsyncGen_Fini() assert(state->value_numfree != -1); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 04ba33ebef80..6e20ba67968b 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -173,12 +173,6 @@ dummy_func( macro(END_FOR) = POP_TOP + POP_TOP; - inst(UNARY_POSITIVE, (value -- res)) { - res = PyNumber_Positive(value); - DECREF_INPUTS(); - ERROR_IF(res == NULL, error); - } - inst(UNARY_NEGATIVE, (value -- res)) { res = PyNumber_Negative(value); DECREF_INPUTS(); @@ -757,13 +751,6 @@ dummy_func( } } - inst(ASYNC_GEN_WRAP, (v -- w)) { - assert(frame->f_code->co_flags & CO_ASYNC_GENERATOR); - w = _PyAsyncGenValueWrapperNew(v); - DECREF_INPUTS(); - ERROR_IF(w == NULL, error); - } - inst(YIELD_VALUE, (retval -- unused)) { // NOTE: It's important that YIELD_VALUE never raises an exception! // The compiler treats any exception raised here as a failed close() @@ -1348,12 +1335,6 @@ dummy_func( PUSH(list); } - inst(LIST_TO_TUPLE, (list -- tuple)) { - tuple = PyList_AsTuple(list); - DECREF_INPUTS(); - ERROR_IF(tuple == NULL, error); - } - inst(LIST_EXTEND, (iterable -- )) { PyObject *list = PEEK(oparg + 1); // iterable is still on the stack PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable); diff --git a/Python/compile.c b/Python/compile.c index e7804469fec6..62f889eb35a5 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1090,7 +1090,6 @@ stack_effect(int opcode, int oparg, int jump) return -2; /* Unary operators */ - case UNARY_POSITIVE: case UNARY_NEGATIVE: case UNARY_NOT: case UNARY_INVERT: @@ -1123,7 +1122,6 @@ stack_effect(int opcode, int oparg, int jump) return -1; case SETUP_ANNOTATIONS: return 0; - case ASYNC_GEN_WRAP: case YIELD_VALUE: return 0; case POP_BLOCK: @@ -1296,8 +1294,6 @@ stack_effect(int opcode, int oparg, int jump) return 1; case LOAD_ASSERTION_ERROR: return 1; - case LIST_TO_TUPLE: - return 0; case LIST_EXTEND: case SET_UPDATE: case DICT_MERGE: @@ -4122,8 +4118,6 @@ unaryop(unaryop_ty op) return UNARY_INVERT; case Not: return UNARY_NOT; - case UAdd: - return UNARY_POSITIVE; case USub: return UNARY_NEGATIVE; default: @@ -4191,7 +4185,7 @@ addop_binary(struct compiler *c, location loc, operator_ty binop, static int addop_yield(struct compiler *c, location loc) { if (c->u->u_ste->ste_generator && c->u->u_ste->ste_coroutine) { - ADDOP(c, loc, ASYNC_GEN_WRAP); + ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_ASYNC_GEN_WRAP); } ADDOP_I(c, loc, YIELD_VALUE, 0); ADDOP_I(c, loc, RESUME, 1); @@ -4358,7 +4352,7 @@ starunpack_helper(struct compiler *c, location loc, ADDOP_LOAD_CONST_NEW(c, loc, folded); ADDOP_I(c, loc, extend, 1); if (tuple) { - ADDOP(c, loc, LIST_TO_TUPLE); + ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_LIST_TO_TUPLE); } } return SUCCESS; @@ -4409,7 +4403,7 @@ starunpack_helper(struct compiler *c, location loc, } assert(sequence_built); if (tuple) { - ADDOP(c, loc, LIST_TO_TUPLE); + ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_LIST_TO_TUPLE); } return SUCCESS; } @@ -5784,7 +5778,12 @@ compiler_visit_expr1(struct compiler *c, expr_ty e) break; case UnaryOp_kind: VISIT(c, expr, e->v.UnaryOp.operand); - ADDOP(c, loc, unaryop(e->v.UnaryOp.op)); + if (e->v.UnaryOp.op == UAdd) { + ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_UNARY_POSITIVE); + } + else { + ADDOP(c, loc, unaryop(e->v.UnaryOp.op)); + } break; case Lambda_kind: return compiler_lambda(c, e); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 0d4dad40ea54..beb5beccd457 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -202,16 +202,6 @@ DISPATCH(); } - TARGET(UNARY_POSITIVE) { - PyObject *value = PEEK(1); - PyObject *res; - res = PyNumber_Positive(value); - Py_DECREF(value); - if (res == NULL) goto pop_1_error; - POKE(1, res); - DISPATCH(); - } - TARGET(UNARY_NEGATIVE) { PyObject *value = PEEK(1); PyObject *res; @@ -921,17 +911,6 @@ DISPATCH(); } - TARGET(ASYNC_GEN_WRAP) { - PyObject *v = PEEK(1); - PyObject *w; - assert(frame->f_code->co_flags & CO_ASYNC_GENERATOR); - w = _PyAsyncGenValueWrapperNew(v); - Py_DECREF(v); - if (w == NULL) goto pop_1_error; - POKE(1, w); - DISPATCH(); - } - TARGET(YIELD_VALUE) { PyObject *retval = PEEK(1); // NOTE: It's important that YIELD_VALUE never raises an exception! @@ -1566,16 +1545,6 @@ DISPATCH(); } - TARGET(LIST_TO_TUPLE) { - PyObject *list = PEEK(1); - PyObject *tuple; - tuple = PyList_AsTuple(list); - Py_DECREF(list); - if (tuple == NULL) goto pop_1_error; - POKE(1, tuple); - DISPATCH(); - } - TARGET(LIST_EXTEND) { PyObject *iterable = PEEK(1); PyObject *list = PEEK(oparg + 1); // iterable is still on the stack diff --git a/Python/intrinsics.c b/Python/intrinsics.c index 07b9c6a97c6e..ae1775862d94 100644 --- a/Python/intrinsics.c +++ b/Python/intrinsics.c @@ -185,10 +185,26 @@ stopiteration_error(PyThreadState* tstate, PyObject *exc) return Py_NewRef(exc); } +static PyObject * +unary_pos(PyThreadState* unused, PyObject *value) +{ + return PyNumber_Positive(value); +} + +static PyObject * +list_to_tuple(PyThreadState* unused, PyObject *v) +{ + assert(PyList_Check(v)); + return _PyTuple_FromArray(((PyListObject *)v)->ob_item, Py_SIZE(v)); +} + instrinsic_func1 _PyIntrinsics_UnaryFunctions[] = { [0] = no_intrinsic, [INTRINSIC_PRINT] = print_expr, [INTRINSIC_IMPORT_STAR] = import_star, [INTRINSIC_STOPITERATION_ERROR] = stopiteration_error, + [INTRINSIC_ASYNC_GEN_WRAP] = _PyAsyncGenValueWrapperNew, + [INTRINSIC_UNARY_POSITIVE] = unary_pos, + [INTRINSIC_LIST_TO_TUPLE] = list_to_tuple, }; diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 34c6757afd32..fd6f6164888d 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -26,7 +26,6 @@ static const struct { [POP_TOP] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [PUSH_NULL] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [END_FOR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [UNARY_POSITIVE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [UNARY_NEGATIVE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [UNARY_NOT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [UNARY_INVERT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, @@ -59,7 +58,6 @@ static const struct { [GET_ANEXT] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [GET_AWAITABLE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [SEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [ASYNC_GEN_WRAP] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [YIELD_VALUE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [POP_EXCEPT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [RERAISE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, @@ -93,7 +91,6 @@ static const struct { [BUILD_STRING] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [BUILD_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [BUILD_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LIST_TO_TUPLE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [LIST_EXTEND] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [SET_UPDATE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, [BUILD_SET] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 4bb6d53e995d..83b3af7c0b1d 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -9,13 +9,12 @@ static void *opcode_targets[256] = { &&TARGET_BINARY_OP_ADD_UNICODE, &&TARGET_BINARY_OP_INPLACE_ADD_UNICODE, &&TARGET_NOP, - &&TARGET_UNARY_POSITIVE, + &&TARGET_BINARY_OP_MULTIPLY_FLOAT, &&TARGET_UNARY_NEGATIVE, &&TARGET_UNARY_NOT, - &&TARGET_BINARY_OP_MULTIPLY_FLOAT, &&TARGET_BINARY_OP_MULTIPLY_INT, - &&TARGET_UNARY_INVERT, &&TARGET_BINARY_OP_SUBTRACT_FLOAT, + &&TARGET_UNARY_INVERT, &&TARGET_BINARY_OP_SUBTRACT_INT, &&TARGET_BINARY_SUBSCR_DICT, &&TARGET_BINARY_SUBSCR_GETITEM, @@ -24,20 +23,20 @@ static void *opcode_targets[256] = { &&TARGET_CALL_PY_EXACT_ARGS, &&TARGET_CALL_PY_WITH_DEFAULTS, &&TARGET_CALL_BOUND_METHOD_EXACT_ARGS, + &&TARGET_CALL_BUILTIN_CLASS, &&TARGET_BINARY_SUBSCR, &&TARGET_BINARY_SLICE, &&TARGET_STORE_SLICE, - &&TARGET_CALL_BUILTIN_CLASS, &&TARGET_CALL_BUILTIN_FAST_WITH_KEYWORDS, + &&TARGET_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, &&TARGET_GET_LEN, &&TARGET_MATCH_MAPPING, &&TARGET_MATCH_SEQUENCE, &&TARGET_MATCH_KEYS, - &&TARGET_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, + &&TARGET_CALL_NO_KW_BUILTIN_FAST, &&TARGET_PUSH_EXC_INFO, &&TARGET_CHECK_EXC_MATCH, &&TARGET_CHECK_EG_MATCH, - &&TARGET_CALL_NO_KW_BUILTIN_FAST, &&TARGET_CALL_NO_KW_BUILTIN_O, &&TARGET_CALL_NO_KW_ISINSTANCE, &&TARGET_CALL_NO_KW_LEN, @@ -48,6 +47,7 @@ static void *opcode_targets[256] = { &&TARGET_CALL_NO_KW_STR_1, &&TARGET_CALL_NO_KW_TUPLE_1, &&TARGET_CALL_NO_KW_TYPE_1, + &&TARGET_COMPARE_OP_FLOAT_JUMP, &&TARGET_WITH_EXCEPT_START, &&TARGET_GET_AITER, &&TARGET_GET_ANEXT, @@ -55,38 +55,38 @@ static void *opcode_targets[256] = { &&TARGET_BEFORE_WITH, &&TARGET_END_ASYNC_FOR, &&TARGET_CLEANUP_THROW, - &&TARGET_COMPARE_OP_FLOAT_JUMP, &&TARGET_COMPARE_OP_INT_JUMP, &&TARGET_COMPARE_OP_STR_JUMP, &&TARGET_FOR_ITER_LIST, + &&TARGET_FOR_ITER_TUPLE, &&TARGET_STORE_SUBSCR, &&TARGET_DELETE_SUBSCR, - &&TARGET_FOR_ITER_TUPLE, &&TARGET_FOR_ITER_RANGE, &&TARGET_FOR_ITER_GEN, &&TARGET_LOAD_ATTR_CLASS, &&TARGET_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, &&TARGET_LOAD_ATTR_INSTANCE_VALUE, + &&TARGET_LOAD_ATTR_MODULE, &&TARGET_GET_ITER, &&TARGET_GET_YIELD_FROM_ITER, - &&TARGET_LOAD_ATTR_MODULE, - &&TARGET_LOAD_BUILD_CLASS, &&TARGET_LOAD_ATTR_PROPERTY, + &&TARGET_LOAD_BUILD_CLASS, &&TARGET_LOAD_ATTR_SLOT, + &&TARGET_LOAD_ATTR_WITH_HINT, &&TARGET_LOAD_ASSERTION_ERROR, &&TARGET_RETURN_GENERATOR, - &&TARGET_LOAD_ATTR_WITH_HINT, &&TARGET_LOAD_ATTR_METHOD_LAZY_DICT, &&TARGET_LOAD_ATTR_METHOD_NO_DICT, &&TARGET_LOAD_ATTR_METHOD_WITH_VALUES, &&TARGET_LOAD_CONST__LOAD_FAST, &&TARGET_LOAD_FAST__LOAD_CONST, - &&TARGET_LIST_TO_TUPLE, - &&TARGET_RETURN_VALUE, &&TARGET_LOAD_FAST__LOAD_FAST, - &&TARGET_SETUP_ANNOTATIONS, &&TARGET_LOAD_GLOBAL_BUILTIN, - &&TARGET_ASYNC_GEN_WRAP, + &&TARGET_RETURN_VALUE, + &&TARGET_LOAD_GLOBAL_MODULE, + &&TARGET_SETUP_ANNOTATIONS, + &&TARGET_STORE_ATTR_INSTANCE_VALUE, + &&TARGET_STORE_ATTR_SLOT, &&TARGET_PREP_RERAISE_STAR, &&TARGET_POP_EXCEPT, &&TARGET_STORE_NAME, @@ -112,7 +112,7 @@ static void *opcode_targets[256] = { &&TARGET_JUMP_FORWARD, &&TARGET_JUMP_IF_FALSE_OR_POP, &&TARGET_JUMP_IF_TRUE_OR_POP, - &&TARGET_LOAD_GLOBAL_MODULE, + &&TARGET_STORE_ATTR_WITH_HINT, &&TARGET_POP_JUMP_IF_FALSE, &&TARGET_POP_JUMP_IF_TRUE, &&TARGET_LOAD_GLOBAL, @@ -120,7 +120,7 @@ static void *opcode_targets[256] = { &&TARGET_CONTAINS_OP, &&TARGET_RERAISE, &&TARGET_COPY, - &&TARGET_STORE_ATTR_INSTANCE_VALUE, + &&TARGET_STORE_FAST__LOAD_FAST, &&TARGET_BINARY_OP, &&TARGET_SEND, &&TARGET_LOAD_FAST, @@ -140,9 +140,9 @@ static void *opcode_targets[256] = { &&TARGET_STORE_DEREF, &&TARGET_DELETE_DEREF, &&TARGET_JUMP_BACKWARD, - &&TARGET_STORE_ATTR_SLOT, + &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_CALL_FUNCTION_EX, - &&TARGET_STORE_ATTR_WITH_HINT, + &&TARGET_STORE_SUBSCR_DICT, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD, @@ -152,20 +152,20 @@ static void *opcode_targets[256] = { &&TARGET_YIELD_VALUE, &&TARGET_RESUME, &&TARGET_MATCH_CLASS, - &&TARGET_STORE_FAST__LOAD_FAST, - &&TARGET_STORE_FAST__STORE_FAST, + &&TARGET_STORE_SUBSCR_LIST_INT, + &&TARGET_UNPACK_SEQUENCE_LIST, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, - &&TARGET_STORE_SUBSCR_DICT, - &&TARGET_STORE_SUBSCR_LIST_INT, - &&TARGET_UNPACK_SEQUENCE_LIST, &&TARGET_UNPACK_SEQUENCE_TUPLE, + &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, + &&_unknown_opcode, + &&_unknown_opcode, &&TARGET_LIST_EXTEND, &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, &&TARGET_DICT_UPDATE, - &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, + &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, From webhook-mailer at python.org Fri Jan 6 09:56:09 2023 From: webhook-mailer at python.org (markshannon) Date: Fri, 06 Jan 2023 14:56:09 -0000 Subject: [Python-checkins] gh-100758: Refactor initialisation of frame headers into a single function (_PyFrame_Initialize) (GH-100759) Message-ID: https://github.com/python/cpython/commit/15c44789bb125b93e96815a336ec73423c47508e commit: 15c44789bb125b93e96815a336ec73423c47508e branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: markshannon date: 2023-01-06T14:55:56Z summary: gh-100758: Refactor initialisation of frame headers into a single function (_PyFrame_Initialize) (GH-100759) files: M Include/internal/pycore_frame.h M Objects/frameobject.c M Python/bytecodes.c M Python/ceval.c M Python/generated_cases.c.h diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index d5c1dcc82a88..4e2051f23f9b 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -110,9 +110,9 @@ void _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest); when frame is linked into the frame stack. */ static inline void -_PyFrame_InitializeSpecials( +_PyFrame_Initialize( _PyInterpreterFrame *frame, PyFunctionObject *func, - PyObject *locals, PyCodeObject *code) + PyObject *locals, PyCodeObject *code, int null_locals_from) { frame->f_funcobj = (PyObject *)func; frame->f_code = (PyCodeObject *)Py_NewRef(code); @@ -124,6 +124,10 @@ _PyFrame_InitializeSpecials( frame->prev_instr = _PyCode_CODE(code) - 1; frame->yield_offset = 0; frame->owner = FRAME_OWNED_BY_THREAD; + + for (int i = null_locals_from; i < code->co_nlocalsplus; i++) { + frame->localsplus[i] = NULL; + } } /* Gets the pointer to the locals array @@ -224,14 +228,14 @@ void _PyThreadState_PopFrame(PyThreadState *tstate, _PyInterpreterFrame *frame); * Must be guarded by _PyThreadState_HasStackSpace() * Consumes reference to func. */ static inline _PyInterpreterFrame * -_PyFrame_PushUnchecked(PyThreadState *tstate, PyFunctionObject *func) +_PyFrame_PushUnchecked(PyThreadState *tstate, PyFunctionObject *func, int null_locals_from) { CALL_STAT_INC(frames_pushed); PyCodeObject *code = (PyCodeObject *)func->func_code; _PyInterpreterFrame *new_frame = (_PyInterpreterFrame *)tstate->datastack_top; tstate->datastack_top += code->co_framesize; assert(tstate->datastack_top < tstate->datastack_limit); - _PyFrame_InitializeSpecials(new_frame, func, NULL, code); + _PyFrame_Initialize(new_frame, func, NULL, code, null_locals_from); return new_frame; } diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 8d7ee4b81eb7..ebe3bfe76d5d 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1011,12 +1011,9 @@ static void init_frame(_PyInterpreterFrame *frame, PyFunctionObject *func, PyObject *locals) { PyCodeObject *code = (PyCodeObject *)func->func_code; - _PyFrame_InitializeSpecials(frame, (PyFunctionObject*)Py_NewRef(func), - Py_XNewRef(locals), code); + _PyFrame_Initialize(frame, (PyFunctionObject*)Py_NewRef(func), + Py_XNewRef(locals), code, 0); frame->previous = NULL; - for (Py_ssize_t i = 0; i < code->co_nlocalsplus; i++) { - frame->localsplus[i] = NULL; - } } PyFrameObject* diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 6e20ba67968b..251ee564eb72 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -448,13 +448,10 @@ dummy_func( DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), BINARY_SUBSCR); STAT_INC(BINARY_SUBSCR, hit); Py_INCREF(getitem); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, getitem); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, getitem, 2); STACK_SHRINK(2); new_frame->localsplus[0] = container; new_frame->localsplus[1] = sub; - for (int i = 2; i < code->co_nlocalsplus; i++) { - new_frame->localsplus[i] = NULL; - } JUMPBY(INLINE_CACHE_ENTRIES_BINARY_SUBSCR); DISPATCH_INLINED(new_frame); } @@ -1714,14 +1711,11 @@ dummy_func( DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(fget); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1); SET_TOP(NULL); int shrink_stack = !(oparg & 1); STACK_SHRINK(shrink_stack); new_frame->localsplus[0] = owner; - for (int i = 1; i < code->co_nlocalsplus; i++) { - new_frame->localsplus[i] = NULL; - } JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); DISPATCH_INLINED(new_frame); } @@ -1749,15 +1743,12 @@ dummy_func( PyObject *name = GETITEM(names, oparg >> 1); Py_INCREF(f); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 2); SET_TOP(NULL); int shrink_stack = !(oparg & 1); STACK_SHRINK(shrink_stack); new_frame->localsplus[0] = owner; new_frame->localsplus[1] = Py_NewRef(name); - for (int i = 2; i < code->co_nlocalsplus; i++) { - new_frame->localsplus[i] = NULL; - } JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); DISPATCH_INLINED(new_frame); } @@ -2672,14 +2663,11 @@ dummy_func( DEOPT_IF(code->co_argcount != argcount, CALL); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); STAT_INC(CALL, hit); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, argcount); STACK_SHRINK(argcount); for (int i = 0; i < argcount; i++) { new_frame->localsplus[i] = stack_pointer[i]; } - for (int i = argcount; i < code->co_nlocalsplus; i++) { - new_frame->localsplus[i] = NULL; - } STACK_SHRINK(2-is_meth); JUMPBY(INLINE_CACHE_ENTRIES_CALL); DISPATCH_INLINED(new_frame); @@ -2702,7 +2690,7 @@ dummy_func( DEOPT_IF(argcount < minargs, CALL); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); STAT_INC(CALL, hit); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, code->co_argcount); STACK_SHRINK(argcount); for (int i = 0; i < argcount; i++) { new_frame->localsplus[i] = stack_pointer[i]; @@ -2712,9 +2700,6 @@ dummy_func( i - minargs); new_frame->localsplus[i] = Py_NewRef(def); } - for (int i = code->co_argcount; i < code->co_nlocalsplus; i++) { - new_frame->localsplus[i] = NULL; - } STACK_SHRINK(2-is_meth); JUMPBY(INLINE_CACHE_ENTRIES_CALL); DISPATCH_INLINED(new_frame); diff --git a/Python/ceval.c b/Python/ceval.c index 149b88e1545d..56cd9ad62963 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1974,11 +1974,8 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, if (frame == NULL) { goto fail; } - _PyFrame_InitializeSpecials(frame, func, locals, code); + _PyFrame_Initialize(frame, func, locals, code, 0); PyObject **localsarray = &frame->localsplus[0]; - for (int i = 0; i < code->co_nlocalsplus; i++) { - localsarray[i] = NULL; - } if (initialize_locals(tstate, func, localsarray, args, argcount, kwnames)) { assert(frame->owner != FRAME_OWNED_BY_GENERATOR); _PyEvalFrameClearAndPop(tstate, frame); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index beb5beccd457..218bc624fa15 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -564,13 +564,10 @@ DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), BINARY_SUBSCR); STAT_INC(BINARY_SUBSCR, hit); Py_INCREF(getitem); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, getitem); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, getitem, 2); STACK_SHRINK(2); new_frame->localsplus[0] = container; new_frame->localsplus[1] = sub; - for (int i = 2; i < code->co_nlocalsplus; i++) { - new_frame->localsplus[i] = NULL; - } JUMPBY(INLINE_CACHE_ENTRIES_BINARY_SUBSCR); DISPATCH_INLINED(new_frame); } @@ -1941,14 +1938,11 @@ DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(fget); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1); SET_TOP(NULL); int shrink_stack = !(oparg & 1); STACK_SHRINK(shrink_stack); new_frame->localsplus[0] = owner; - for (int i = 1; i < code->co_nlocalsplus; i++) { - new_frame->localsplus[i] = NULL; - } JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); DISPATCH_INLINED(new_frame); } @@ -1975,15 +1969,12 @@ PyObject *name = GETITEM(names, oparg >> 1); Py_INCREF(f); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 2); SET_TOP(NULL); int shrink_stack = !(oparg & 1); STACK_SHRINK(shrink_stack); new_frame->localsplus[0] = owner; new_frame->localsplus[1] = Py_NewRef(name); - for (int i = 2; i < code->co_nlocalsplus; i++) { - new_frame->localsplus[i] = NULL; - } JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); DISPATCH_INLINED(new_frame); } @@ -3011,14 +3002,11 @@ DEOPT_IF(code->co_argcount != argcount, CALL); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); STAT_INC(CALL, hit); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, argcount); STACK_SHRINK(argcount); for (int i = 0; i < argcount; i++) { new_frame->localsplus[i] = stack_pointer[i]; } - for (int i = argcount; i < code->co_nlocalsplus; i++) { - new_frame->localsplus[i] = NULL; - } STACK_SHRINK(2-is_meth); JUMPBY(INLINE_CACHE_ENTRIES_CALL); DISPATCH_INLINED(new_frame); @@ -3040,7 +3028,7 @@ DEOPT_IF(argcount < minargs, CALL); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); STAT_INC(CALL, hit); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, code->co_argcount); STACK_SHRINK(argcount); for (int i = 0; i < argcount; i++) { new_frame->localsplus[i] = stack_pointer[i]; @@ -3050,9 +3038,6 @@ i - minargs); new_frame->localsplus[i] = Py_NewRef(def); } - for (int i = code->co_argcount; i < code->co_nlocalsplus; i++) { - new_frame->localsplus[i] = NULL; - } STACK_SHRINK(2-is_meth); JUMPBY(INLINE_CACHE_ENTRIES_CALL); DISPATCH_INLINED(new_frame); From webhook-mailer at python.org Fri Jan 6 10:37:46 2023 From: webhook-mailer at python.org (mdickinson) Date: Fri, 06 Jan 2023 15:37:46 -0000 Subject: [Python-checkins] gh-91851: Trivial optimizations in Fraction (#100791) Message-ID: https://github.com/python/cpython/commit/0e640260dac2db081e56f52f8efb0e43e463ff2f commit: 0e640260dac2db081e56f52f8efb0e43e463ff2f branch: main author: Sergey B Kirpichev committer: mdickinson date: 2023-01-06T15:37:34Z summary: gh-91851: Trivial optimizations in Fraction (#100791) Make some trivial performance optimizations in Fraction Uses private class attributes `_numerator` and `_denominator` in place of the `numerator` and `denominator` property accesses. Co-authored-by: hauntsaninja files: A Misc/NEWS.d/next/Library/2023-01-05-23-04-15.gh-issue-91851.AuCzU5.rst M Lib/fractions.py diff --git a/Lib/fractions.py b/Lib/fractions.py index 4302f3f1b98d..939741115f91 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -650,12 +650,12 @@ def __trunc__(a): def __floor__(a): """math.floor(a)""" - return a.numerator // a.denominator + return a._numerator // a._denominator def __ceil__(a): """math.ceil(a)""" # The negations cleverly convince floordiv to return the ceiling. - return -(-a.numerator // a.denominator) + return -(-a._numerator // a._denominator) def __round__(self, ndigits=None): """round(self, ndigits) @@ -663,10 +663,11 @@ def __round__(self, ndigits=None): Rounds half toward even. """ if ndigits is None: - floor, remainder = divmod(self.numerator, self.denominator) - if remainder * 2 < self.denominator: + d = self._denominator + floor, remainder = divmod(self._numerator, d) + if remainder * 2 < d: return floor - elif remainder * 2 > self.denominator: + elif remainder * 2 > d: return floor + 1 # Deal with the half case: elif floor % 2 == 0: diff --git a/Misc/NEWS.d/next/Library/2023-01-05-23-04-15.gh-issue-91851.AuCzU5.rst b/Misc/NEWS.d/next/Library/2023-01-05-23-04-15.gh-issue-91851.AuCzU5.rst new file mode 100644 index 000000000000..f427e8ae6791 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-05-23-04-15.gh-issue-91851.AuCzU5.rst @@ -0,0 +1,3 @@ +Microoptimizations for :meth:`fractions.Fraction.__round__`, +:meth:`fractions.Fraction.__ceil__` and +:meth:`fractions.Fraction.__floor__`. From webhook-mailer at python.org Fri Jan 6 11:04:27 2023 From: webhook-mailer at python.org (gvanrossum) Date: Fri, 06 Jan 2023 16:04:27 -0000 Subject: [Python-checkins] GH-98831: Add some tests for generate_cases.py (#100763) Message-ID: https://github.com/python/cpython/commit/9ffbc58f5cb6d2b002f8785886588d646af517db commit: 9ffbc58f5cb6d2b002f8785886588d646af517db branch: main author: Guido van Rossum committer: gvanrossum date: 2023-01-06T08:04:20-08:00 summary: GH-98831: Add some tests for generate_cases.py (#100763) - This doesn't cover everything (far from it) but it's a start. - This uses pytest, which isn't ideal, but was quickest to get started. Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> files: A Tools/cases_generator/test_generator.py diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py new file mode 100644 index 000000000000..e707a5334268 --- /dev/null +++ b/Tools/cases_generator/test_generator.py @@ -0,0 +1,310 @@ +# Sorry for using pytest, these tests are mostly just for me. +# Use pytest -vv for best results. + +import tempfile + +import generate_cases + + +def run_cases_test(input: str, expected: str): + temp_input = tempfile.NamedTemporaryFile("w+") + temp_input.write(generate_cases.BEGIN_MARKER) + temp_input.write(input) + temp_input.write(generate_cases.END_MARKER) + temp_input.flush() + temp_output = tempfile.NamedTemporaryFile("w+") + a = generate_cases.Analyzer(temp_input.name, temp_output.name) + a.parse() + a.analyze() + if a.errors: + raise RuntimeError(f"Found {a.errors} errors") + a.write_instructions() + temp_output.seek(0) + lines = temp_output.readlines() + while lines and lines[0].startswith("// "): + lines.pop(0) + actual = "".join(lines) + assert actual.rstrip() == expected.rstrip() + +def test_legacy(): + input = """ + inst(OP) { + spam(); + } + """ + output = """ + TARGET(OP) { + spam(); + DISPATCH(); + } + """ + run_cases_test(input, output) + +def test_inst_no_args(): + input = """ + inst(OP, (--)) { + spam(); + } + """ + output = """ + TARGET(OP) { + spam(); + DISPATCH(); + } + """ + run_cases_test(input, output) + +def test_inst_one_pop(): + input = """ + inst(OP, (value --)) { + spam(); + } + """ + output = """ + TARGET(OP) { + PyObject *value = PEEK(1); + spam(); + STACK_SHRINK(1); + DISPATCH(); + } + """ + run_cases_test(input, output) + +def test_inst_one_push(): + input = """ + inst(OP, (-- res)) { + spam(); + } + """ + output = """ + TARGET(OP) { + PyObject *res; + spam(); + STACK_GROW(1); + POKE(1, res); + DISPATCH(); + } + """ + run_cases_test(input, output) + +def test_inst_one_push_one_pop(): + input = """ + inst(OP, (value -- res)) { + spam(); + } + """ + output = """ + TARGET(OP) { + PyObject *value = PEEK(1); + PyObject *res; + spam(); + POKE(1, res); + DISPATCH(); + } + """ + run_cases_test(input, output) + +def test_binary_op(): + input = """ + inst(OP, (left, right -- res)) { + spam(); + } + """ + output = """ + TARGET(OP) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + PyObject *res; + spam(); + STACK_SHRINK(1); + POKE(1, res); + DISPATCH(); + } + """ + run_cases_test(input, output) + +def test_predictions(): + input = """ + inst(OP1, (--)) { + } + inst(OP2, (--)) { + } + inst(OP3, (--)) { + DEOPT_IF(xxx, OP1); + PREDICT(OP2); + } + """ + output = """ + TARGET(OP1) { + PREDICTED(OP1); + DISPATCH(); + } + + TARGET(OP2) { + PREDICTED(OP2); + DISPATCH(); + } + + TARGET(OP3) { + DEOPT_IF(xxx, OP1); + PREDICT(OP2); + DISPATCH(); + } + """ + run_cases_test(input, output) + +def test_error_if_plain(): + input = """ + inst(OP, (--)) { + ERROR_IF(cond, label); + } + """ + output = """ + TARGET(OP) { + if (cond) goto label; + DISPATCH(); + } + """ + run_cases_test(input, output) + +def test_error_if_pop(): + input = """ + inst(OP, (left, right -- res)) { + ERROR_IF(cond, label); + } + """ + output = """ + TARGET(OP) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + PyObject *res; + if (cond) goto pop_2_label; + STACK_SHRINK(1); + POKE(1, res); + DISPATCH(); + } + """ + run_cases_test(input, output) + +def test_cache_effect(): + input = """ + inst(OP, (counter/1, extra/2, value --)) { + } + """ + output = """ + TARGET(OP) { + PyObject *value = PEEK(1); + uint16_t counter = read_u16(&next_instr[0].cache); + uint32_t extra = read_u32(&next_instr[1].cache); + STACK_SHRINK(1); + JUMPBY(3); + DISPATCH(); + } + """ + run_cases_test(input, output) + +def test_suppress_dispatch(): + input = """ + inst(OP, (--)) { + goto somewhere; + } + """ + output = """ + TARGET(OP) { + goto somewhere; + } + """ + run_cases_test(input, output) + +def test_super_instruction(): + # TODO: Test cache effect + input = """ + inst(OP1, (counter/1, arg --)) { + op1(); + } + inst(OP2, (extra/2, arg --)) { + op2(); + } + super(OP) = OP1 + OP2; + """ + output = """ + TARGET(OP1) { + PyObject *arg = PEEK(1); + uint16_t counter = read_u16(&next_instr[0].cache); + op1(); + STACK_SHRINK(1); + JUMPBY(1); + DISPATCH(); + } + + TARGET(OP2) { + PyObject *arg = PEEK(1); + uint32_t extra = read_u32(&next_instr[0].cache); + op2(); + STACK_SHRINK(1); + JUMPBY(2); + DISPATCH(); + } + + TARGET(OP) { + PyObject *_tmp_1 = PEEK(1); + PyObject *_tmp_2 = PEEK(2); + { + PyObject *arg = _tmp_1; + uint16_t counter = read_u16(&next_instr[0].cache); + op1(); + } + JUMPBY(1); + NEXTOPARG(); + JUMPBY(1); + { + PyObject *arg = _tmp_2; + uint32_t extra = read_u32(&next_instr[0].cache); + op2(); + } + JUMPBY(2); + STACK_SHRINK(2); + DISPATCH(); + } + """ + run_cases_test(input, output) + +def test_macro_instruction(): + input = """ + inst(OP1, (counter/1, arg --)) { + op1(); + } + op(OP2, (extra/2, arg --)) { + op2(); + } + macro(OP) = OP1 + cache/2 + OP2; + """ + output = """ + TARGET(OP1) { + PyObject *arg = PEEK(1); + uint16_t counter = read_u16(&next_instr[0].cache); + op1(); + STACK_SHRINK(1); + JUMPBY(1); + DISPATCH(); + } + + TARGET(OP) { + PyObject *_tmp_1 = PEEK(1); + PyObject *_tmp_2 = PEEK(2); + { + PyObject *arg = _tmp_1; + uint16_t counter = read_u16(&next_instr[0].cache); + op1(); + } + { + PyObject *arg = _tmp_2; + uint32_t extra = read_u32(&next_instr[3].cache); + op2(); + } + JUMPBY(5); + STACK_SHRINK(2); + DISPATCH(); + } + """ + run_cases_test(input, output) From webhook-mailer at python.org Fri Jan 6 13:38:58 2023 From: webhook-mailer at python.org (cjw296) Date: Fri, 06 Jan 2023 18:38:58 -0000 Subject: [Python-checkins] gh-100690: Raise an AttributeError when the assert_ prefix is forgotten when using Mock (#100691) Message-ID: https://github.com/python/cpython/commit/1d4d677d1c90fcf4886ded0bf04b8f9d5b60b909 commit: 1d4d677d1c90fcf4886ded0bf04b8f9d5b60b909 branch: main author: Christian Klein <167265+cklein at users.noreply.github.com> committer: cjw296 date: 2023-01-06T18:38:50Z summary: gh-100690: Raise an AttributeError when the assert_ prefix is forgotten when using Mock (#100691) Mock objects which are not unsafe will now raise an AttributeError when accessing an attribute that matches the name of an assertion but without the prefix `assert_`, e.g. accessing `called_once` instead of `assert_called_once`. This is in addition to this already happening for accessing attributes with prefixes assert, assret, asert, aseert, and assrt. files: A Misc/NEWS.d/next/Library/2023-01-02-16-59-49.gh-issue-100690.2EgWPS.rst M Lib/test/test_unittest/testmock/testmock.py M Lib/unittest/mock.py diff --git a/Lib/test/test_unittest/testmock/testmock.py b/Lib/test/test_unittest/testmock/testmock.py index b224f87fa3e4..97fe66fa4ca2 100644 --- a/Lib/test/test_unittest/testmock/testmock.py +++ b/Lib/test/test_unittest/testmock/testmock.py @@ -1645,12 +1645,36 @@ def test_mock_unsafe(self): m.aseert_foo_call() with self.assertRaisesRegex(AttributeError, msg): m.assrt_foo_call() + with self.assertRaisesRegex(AttributeError, msg): + m.called_once_with() + with self.assertRaisesRegex(AttributeError, msg): + m.called_once() + with self.assertRaisesRegex(AttributeError, msg): + m.has_calls() + + class Foo(object): + def called_once(self): + pass + + def has_calls(self): + pass + + m = Mock(spec=Foo) + m.called_once() + m.has_calls() + + m.called_once.assert_called_once() + m.has_calls.assert_called_once() + m = Mock(unsafe=True) m.assert_foo_call() m.assret_foo_call() m.asert_foo_call() m.aseert_foo_call() m.assrt_foo_call() + m.called_once() + m.called_once_with() + m.has_calls() # gh-100739 def test_mock_safe_with_spec(self): diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 47928e564850..78827d61b69d 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -653,7 +653,7 @@ def __getattr__(self, name): elif _is_magic(name): raise AttributeError(name) if not self._mock_unsafe and (not self._mock_methods or name not in self._mock_methods): - if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')): + if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')) or name in ATTRIB_DENY_LIST: raise AttributeError( f"{name!r} is not a valid assertion. Use a spec " f"for the mock if {name!r} is meant to be an attribute.") @@ -1062,6 +1062,10 @@ def _calls_repr(self, prefix="Calls"): return f"\n{prefix}: {safe_repr(self.mock_calls)}." +# Denylist for forbidden attribute names in safe mode +ATTRIB_DENY_LIST = {name.removeprefix("assert_") for name in dir(NonCallableMock) if name.startswith("assert_")} + + class _AnyComparer(list): """A list which checks if it contains a call which may have an argument of ANY, flipping the components of item and self from @@ -1231,9 +1235,11 @@ class or instance) that acts as the specification for the mock object. If `return_value` attribute. * `unsafe`: By default, accessing any attribute whose name starts with - *assert*, *assret*, *asert*, *aseert* or *assrt* will raise an - AttributeError. Passing `unsafe=True` will allow access to - these attributes. + *assert*, *assret*, *asert*, *aseert*, or *assrt* raises an AttributeError. + Additionally, an AttributeError is raised when accessing + attributes that match the name of an assertion method without the prefix + `assert_`, e.g. accessing `called_once` instead of `assert_called_once`. + Passing `unsafe=True` will allow access to these attributes. * `wraps`: Item for the mock object to wrap. If `wraps` is not None then calling the Mock will pass the call through to the wrapped object diff --git a/Misc/NEWS.d/next/Library/2023-01-02-16-59-49.gh-issue-100690.2EgWPS.rst b/Misc/NEWS.d/next/Library/2023-01-02-16-59-49.gh-issue-100690.2EgWPS.rst new file mode 100644 index 000000000000..3796772aebdf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-02-16-59-49.gh-issue-100690.2EgWPS.rst @@ -0,0 +1,7 @@ +``Mock`` objects which are not unsafe will now raise an +``AttributeError`` when accessing an attribute that matches the name +of an assertion but without the prefix ``assert_``, e.g. accessing +``called_once`` instead of ``assert_called_once``. +This is in addition to this already happening for accessing attributes +with prefixes ``assert``, ``assret``, ``asert``, ``aseert``, +and ``assrt``. From webhook-mailer at python.org Fri Jan 6 15:38:18 2023 From: webhook-mailer at python.org (AlexWaygood) Date: Fri, 06 Jan 2023 20:38:18 -0000 Subject: [Python-checkins] Add barneygale to CODEOWNERS for pathlib (#100808) Message-ID: https://github.com/python/cpython/commit/26ff43625ed7bf09542ad8f149cb6af710b41e15 commit: 26ff43625ed7bf09542ad8f149cb6af710b41e15 branch: main author: Barney Gale committer: AlexWaygood date: 2023-01-06T20:38:12Z summary: Add barneygale to CODEOWNERS for pathlib (#100808) files: M .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 351bf7c39555..d68f1eb328d0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -152,5 +152,8 @@ Lib/ast.py @isidentical /Mac/ @python/macos-team **/*osx_support* @python/macos-team +# pathlib +**/*pathlib* @barneygale + # zipfile.Path **/*zipfile/*_path.py @jaraco From webhook-mailer at python.org Sat Jan 7 05:25:34 2023 From: webhook-mailer at python.org (cjw296) Date: Sat, 07 Jan 2023 10:25:34 -0000 Subject: [Python-checkins] gh-100690: [mock] hide `ATTRIB_DENY_LIST` and make it immutable (#100819) Message-ID: https://github.com/python/cpython/commit/a109454e828ce2d9bde15dea78405f8ffee653ec commit: a109454e828ce2d9bde15dea78405f8ffee653ec branch: main author: Nikita Sobolev committer: cjw296 date: 2023-01-07T10:25:05Z summary: gh-100690: [mock] hide `ATTRIB_DENY_LIST` and make it immutable (#100819) files: M Lib/unittest/mock.py diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 78827d61b69d..b3c0e28c698b 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -653,7 +653,7 @@ def __getattr__(self, name): elif _is_magic(name): raise AttributeError(name) if not self._mock_unsafe and (not self._mock_methods or name not in self._mock_methods): - if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')) or name in ATTRIB_DENY_LIST: + if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')) or name in _ATTRIB_DENY_LIST: raise AttributeError( f"{name!r} is not a valid assertion. Use a spec " f"for the mock if {name!r} is meant to be an attribute.") @@ -1063,7 +1063,11 @@ def _calls_repr(self, prefix="Calls"): # Denylist for forbidden attribute names in safe mode -ATTRIB_DENY_LIST = {name.removeprefix("assert_") for name in dir(NonCallableMock) if name.startswith("assert_")} +_ATTRIB_DENY_LIST = frozenset({ + name.removeprefix("assert_") + for name in dir(NonCallableMock) + if name.startswith("assert_") +}) class _AnyComparer(list): From webhook-mailer at python.org Sat Jan 7 05:49:21 2023 From: webhook-mailer at python.org (cjw296) Date: Sat, 07 Jan 2023 10:49:21 -0000 Subject: [Python-checkins] gh-96127: Fix `inspect.signature` call on mocks (#96335) Message-ID: https://github.com/python/cpython/commit/9e7d7266ecdcccc02385fe4ccb094f3444102e26 commit: 9e7d7266ecdcccc02385fe4ccb094f3444102e26 branch: main author: Nikita Sobolev committer: cjw296 date: 2023-01-07T10:49:15Z summary: gh-96127: Fix `inspect.signature` call on mocks (#96335) files: A Misc/NEWS.d/next/Library/2022-08-27-10-35-50.gh-issue-96127.8RdLre.rst M Lib/test/test_inspect.py M Lib/unittest/mock.py diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 1b589c8df2a2..aa757241aca9 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -3283,6 +3283,25 @@ def test_signature_on_lambdas(self): ((('a', 10, ..., "positional_or_keyword"),), ...)) + def test_signature_on_mocks(self): + # https://github.com/python/cpython/issues/96127 + for mock in ( + unittest.mock.Mock(), + unittest.mock.AsyncMock(), + unittest.mock.MagicMock(), + ): + with self.subTest(mock=mock): + self.assertEqual(str(inspect.signature(mock)), '(*args, **kwargs)') + + def test_signature_on_noncallable_mocks(self): + for mock in ( + unittest.mock.NonCallableMock(), + unittest.mock.NonCallableMagicMock(), + ): + with self.subTest(mock=mock): + with self.assertRaises(TypeError): + inspect.signature(mock) + def test_signature_equality(self): def foo(a, *, b:int) -> float: pass self.assertFalse(inspect.signature(foo) == 42) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index b3c0e28c698b..0f93cb53c3d5 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -2217,7 +2217,15 @@ def __init__(self, /, *args, **kwargs): code_mock = NonCallableMock(spec_set=_CODE_ATTRS) code_mock.__dict__["_spec_class"] = CodeType code_mock.__dict__["_spec_signature"] = _CODE_SIG - code_mock.co_flags = inspect.CO_COROUTINE + code_mock.co_flags = ( + inspect.CO_COROUTINE + + inspect.CO_VARARGS + + inspect.CO_VARKEYWORDS + ) + code_mock.co_argcount = 0 + code_mock.co_varnames = ('args', 'kwargs') + code_mock.co_posonlyargcount = 0 + code_mock.co_kwonlyargcount = 0 self.__dict__['__code__'] = code_mock self.__dict__['__name__'] = 'AsyncMock' self.__dict__['__defaults__'] = tuple() diff --git a/Misc/NEWS.d/next/Library/2022-08-27-10-35-50.gh-issue-96127.8RdLre.rst b/Misc/NEWS.d/next/Library/2022-08-27-10-35-50.gh-issue-96127.8RdLre.rst new file mode 100644 index 000000000000..79edd8fd5d8f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-08-27-10-35-50.gh-issue-96127.8RdLre.rst @@ -0,0 +1,2 @@ +``inspect.signature`` was raising ``TypeError`` on call with mock objects. +Now it correctly returns ``(*args, **kwargs)`` as infered signature. From webhook-mailer at python.org Sat Jan 7 07:21:08 2023 From: webhook-mailer at python.org (markshannon) Date: Sat, 07 Jan 2023 12:21:08 -0000 Subject: [Python-checkins] gh-88696: clean up dead argument to compiler_make_closure (GH-100806) Message-ID: https://github.com/python/cpython/commit/7116030a25f7dd2140ef3e889f3f5471334d6d0b commit: 7116030a25f7dd2140ef3e889f3f5471334d6d0b branch: main author: Carl Meyer committer: markshannon date: 2023-01-07T12:20:48Z summary: gh-88696: clean up dead argument to compiler_make_closure (GH-100806) files: M Python/compile.c diff --git a/Python/compile.c b/Python/compile.c index 62f889eb35a5..943168ba693d 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -2246,11 +2246,8 @@ compiler_lookup_arg(PyObject *dict, PyObject *name) static int compiler_make_closure(struct compiler *c, location loc, - PyCodeObject *co, Py_ssize_t flags, PyObject *qualname) + PyCodeObject *co, Py_ssize_t flags) { - if (qualname == NULL) - qualname = co->co_name; - if (co->co_nfreevars) { int i = PyCode_GetFirstFree(co); for (; i < co->co_nlocalsplus; ++i) { @@ -2605,7 +2602,7 @@ static int compiler_function(struct compiler *c, stmt_ty s, int is_async) { PyCodeObject *co; - PyObject *qualname, *docstring = NULL; + PyObject *docstring = NULL; arguments_ty args; expr_ty returns; identifier name; @@ -2682,19 +2679,15 @@ compiler_function(struct compiler *c, stmt_ty s, int is_async) } } co = assemble(c, 1); - qualname = Py_NewRef(c->u->u_qualname); compiler_exit_scope(c); if (co == NULL) { - Py_XDECREF(qualname); Py_XDECREF(co); return ERROR; } - if (compiler_make_closure(c, loc, co, funcflags, qualname) < 0) { - Py_DECREF(qualname); + if (compiler_make_closure(c, loc, co, funcflags) < 0) { Py_DECREF(co); return ERROR; } - Py_DECREF(qualname); Py_DECREF(co); RETURN_IF_ERROR(compiler_apply_decorators(c, decos)); @@ -2794,7 +2787,7 @@ compiler_class(struct compiler *c, stmt_ty s) ADDOP(c, loc, LOAD_BUILD_CLASS); /* 3. load a function (or closure) made from the code object */ - if (compiler_make_closure(c, loc, co, 0, NULL) < 0) { + if (compiler_make_closure(c, loc, co, 0) < 0) { Py_DECREF(co); return ERROR; } @@ -3015,7 +3008,6 @@ static int compiler_lambda(struct compiler *c, expr_ty e) { PyCodeObject *co; - PyObject *qualname; Py_ssize_t funcflags; arguments_ty args = e->v.Lambda.args; assert(e->kind == Lambda_kind); @@ -3049,19 +3041,15 @@ compiler_lambda(struct compiler *c, expr_ty e) ADDOP_IN_SCOPE(c, loc, RETURN_VALUE); co = assemble(c, 1); } - qualname = Py_NewRef(c->u->u_qualname); compiler_exit_scope(c); if (co == NULL) { - Py_DECREF(qualname); return ERROR; } - if (compiler_make_closure(c, loc, co, funcflags, qualname) < 0) { - Py_DECREF(qualname); + if (compiler_make_closure(c, loc, co, funcflags) < 0) { Py_DECREF(co); return ERROR; } - Py_DECREF(qualname); Py_DECREF(co); return SUCCESS; @@ -5392,7 +5380,6 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, { PyCodeObject *co = NULL; comprehension_ty outermost; - PyObject *qualname = NULL; int scope_type = c->u->u_scope_type; int is_async_generator = 0; int is_top_level_await = IS_TOP_LEVEL_AWAIT(c); @@ -5453,7 +5440,6 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, } co = assemble(c, 1); - qualname = Py_NewRef(c->u->u_qualname); compiler_exit_scope(c); if (is_top_level_await && is_async_generator){ c->u->u_ste->ste_coroutine = 1; @@ -5463,10 +5449,9 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, } loc = LOC(e); - if (compiler_make_closure(c, loc, co, 0, qualname) < 0) { + if (compiler_make_closure(c, loc, co, 0) < 0) { goto error; } - Py_DECREF(qualname); Py_DECREF(co); VISIT(c, expr, outermost->iter); @@ -5490,7 +5475,6 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, error_in_scope: compiler_exit_scope(c); error: - Py_XDECREF(qualname); Py_XDECREF(co); return ERROR; } @@ -8671,7 +8655,7 @@ opcode_metadata_is_sane(cfg_builder *g) { for (int i = 0; i < b->b_iused; i++) { struct instr *instr = &b->b_instr[i]; int opcode = instr->i_opcode; - assert(opcode <= MAX_REAL_OPCODE); + assert(opcode <= MAX_REAL_OPCODE); int pushed = _PyOpcode_opcode_metadata[opcode].n_pushed; int popped = _PyOpcode_opcode_metadata[opcode].n_popped; assert((pushed < 0) == (popped < 0)); From webhook-mailer at python.org Sat Jan 7 12:31:52 2023 From: webhook-mailer at python.org (AlexWaygood) Date: Sat, 07 Jan 2023 17:31:52 -0000 Subject: [Python-checkins] gh-100673: Removed erroneous note in the get_type_hints docs (#100701) Message-ID: https://github.com/python/cpython/commit/deaf090699a7312cccb0637409f44de3f382389b commit: deaf090699a7312cccb0637409f44de3f382389b branch: main author: FrozenBob <30644137+FrozenBob at users.noreply.github.com> committer: AlexWaygood date: 2023-01-07T17:31:47Z summary: gh-100673: Removed erroneous note in the get_type_hints docs (#100701) Removed erroneous note in the get_type_hints docs typing.get_type_hints still includes base class type hints. files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 4eed6b4ea887..356f919a1897 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2777,10 +2777,6 @@ Introspection helpers .. versionchanged:: 3.9 Added ``include_extras`` parameter as part of :pep:`593`. - .. versionchanged:: 3.10 - Calling ``get_type_hints()`` on a class no longer returns the annotations - of its base classes. - .. versionchanged:: 3.11 Previously, ``Optional[t]`` was added for function and method annotations if a default value equal to ``None`` was set. From webhook-mailer at python.org Sat Jan 7 12:38:37 2023 From: webhook-mailer at python.org (miss-islington) Date: Sat, 07 Jan 2023 17:38:37 -0000 Subject: [Python-checkins] gh-100673: Removed erroneous note in the get_type_hints docs (GH-100701) Message-ID: https://github.com/python/cpython/commit/a3e2407f5c9c1191c6e93e04bc432b7358b8826e commit: a3e2407f5c9c1191c6e93e04bc432b7358b8826e branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-07T09:38:32-08:00 summary: gh-100673: Removed erroneous note in the get_type_hints docs (GH-100701) Removed erroneous note in the get_type_hints docs typing.get_type_hints still includes base class type hints. (cherry picked from commit deaf090699a7312cccb0637409f44de3f382389b) Co-authored-by: FrozenBob <30644137+FrozenBob at users.noreply.github.com> files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index a3a6181eba29..7fc0aa3a8e62 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2767,10 +2767,6 @@ Introspection helpers .. versionchanged:: 3.9 Added ``include_extras`` parameter as part of :pep:`593`. - .. versionchanged:: 3.10 - Calling ``get_type_hints()`` on a class no longer returns the annotations - of its base classes. - .. versionchanged:: 3.11 Previously, ``Optional[t]`` was added for function and method annotations if a default value equal to ``None`` was set. From webhook-mailer at python.org Sat Jan 7 13:46:50 2023 From: webhook-mailer at python.org (rhettinger) Date: Sat, 07 Jan 2023 18:46:50 -0000 Subject: [Python-checkins] GH-100485: Add math.sumprod() (GH-100677) Message-ID: https://github.com/python/cpython/commit/47b9f83a83db288c652e43567c7b0f74d87a29be commit: 47b9f83a83db288c652e43567c7b0f74d87a29be branch: main author: Raymond Hettinger committer: rhettinger date: 2023-01-07T12:46:35-06:00 summary: GH-100485: Add math.sumprod() (GH-100677) files: A Misc/NEWS.d/next/Library/2023-01-01-21-54-46.gh-issue-100485.geNrHS.rst M Doc/library/itertools.rst M Doc/library/math.rst M Lib/test/test_math.py M Modules/clinic/mathmodule.c.h M Modules/mathmodule.c diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 4ad679dfccdb..e7d2e13626fa 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -33,7 +33,7 @@ by combining :func:`map` and :func:`count` to form ``map(f, count())``. These tools and their built-in counterparts also work well with the high-speed functions in the :mod:`operator` module. For example, the multiplication operator can be mapped across two vectors to form an efficient dot-product: -``sum(map(operator.mul, vector1, vector2))``. +``sum(starmap(operator.mul, zip(vec1, vec2, strict=True)))``. **Infinite iterators:** @@ -838,10 +838,6 @@ which incur interpreter overhead. "Returns the sequence elements n times" return chain.from_iterable(repeat(tuple(iterable), n)) - def dotproduct(vec1, vec2): - "Compute a sum of products." - return sum(starmap(operator.mul, zip(vec1, vec2, strict=True))) - def convolve(signal, kernel): # See: https://betterexplained.com/articles/intuitive-convolution/ # convolve(data, [0.25, 0.25, 0.25, 0.25]) --> Moving average (blur) @@ -852,7 +848,7 @@ which incur interpreter overhead. window = collections.deque([0], maxlen=n) * n for x in chain(signal, repeat(0, n-1)): window.append(x) - yield dotproduct(kernel, window) + yield math.sumprod(kernel, window) def polynomial_from_roots(roots): """Compute a polynomial's coefficients from its roots. @@ -1211,9 +1207,6 @@ which incur interpreter overhead. >>> list(ncycles('abc', 3)) ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c'] - >>> dotproduct([1,2,3], [4,5,6]) - 32 - >>> data = [20, 40, 24, 32, 20, 28, 16] >>> list(convolve(data, [0.25, 0.25, 0.25, 0.25])) [5.0, 15.0, 21.0, 29.0, 29.0, 26.0, 24.0, 16.0, 11.0, 4.0] diff --git a/Doc/library/math.rst b/Doc/library/math.rst index aeebcaf6ab08..0e888c4d4e42 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -291,6 +291,22 @@ Number-theoretic and representation functions .. versionadded:: 3.7 +.. function:: sumprod(p, q) + + Return the sum of products of values from two iterables *p* and *q*. + + Raises :exc:`ValueError` if the inputs do not have the same length. + + Roughly equivalent to:: + + sum(itertools.starmap(operator.mul, zip(p, q, strict=true))) + + For float and mixed int/float inputs, the intermediate products + and sums are computed with extended precision. + + .. versionadded:: 3.12 + + .. function:: trunc(x) Return *x* with the fractional part diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index bf0d0a56e6ac..65fe169671ea 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -4,6 +4,7 @@ from test.support import verbose, requires_IEEE_754 from test import support import unittest +import fractions import itertools import decimal import math @@ -1202,6 +1203,171 @@ def testLog10(self): self.assertEqual(math.log(INF), INF) self.assertTrue(math.isnan(math.log10(NAN))) + def testSumProd(self): + sumprod = math.sumprod + Decimal = decimal.Decimal + Fraction = fractions.Fraction + + # Core functionality + self.assertEqual(sumprod(iter([10, 20, 30]), (1, 2, 3)), 140) + self.assertEqual(sumprod([1.5, 2.5], [3.5, 4.5]), 16.5) + self.assertEqual(sumprod([], []), 0) + + # Type preservation and coercion + for v in [ + (10, 20, 30), + (1.5, -2.5), + (Fraction(3, 5), Fraction(4, 5)), + (Decimal(3.5), Decimal(4.5)), + (2.5, 10), # float/int + (2.5, Fraction(3, 5)), # float/fraction + (25, Fraction(3, 5)), # int/fraction + (25, Decimal(4.5)), # int/decimal + ]: + for p, q in [(v, v), (v, v[::-1])]: + with self.subTest(p=p, q=q): + expected = sum(p_i * q_i for p_i, q_i in zip(p, q, strict=True)) + actual = sumprod(p, q) + self.assertEqual(expected, actual) + self.assertEqual(type(expected), type(actual)) + + # Bad arguments + self.assertRaises(TypeError, sumprod) # No args + self.assertRaises(TypeError, sumprod, []) # One arg + self.assertRaises(TypeError, sumprod, [], [], []) # Three args + self.assertRaises(TypeError, sumprod, None, [10]) # Non-iterable + self.assertRaises(TypeError, sumprod, [10], None) # Non-iterable + + # Uneven lengths + self.assertRaises(ValueError, sumprod, [10, 20], [30]) + self.assertRaises(ValueError, sumprod, [10], [20, 30]) + + # Error in iterator + def raise_after(n): + for i in range(n): + yield i + raise RuntimeError + with self.assertRaises(RuntimeError): + sumprod(range(10), raise_after(5)) + with self.assertRaises(RuntimeError): + sumprod(raise_after(5), range(10)) + + # Error in multiplication + class BadMultiply: + def __mul__(self, other): + raise RuntimeError + def __rmul__(self, other): + raise RuntimeError + with self.assertRaises(RuntimeError): + sumprod([10, BadMultiply(), 30], [1, 2, 3]) + with self.assertRaises(RuntimeError): + sumprod([1, 2, 3], [10, BadMultiply(), 30]) + + # Error in addition + with self.assertRaises(TypeError): + sumprod(['abc', 3], [5, 10]) + with self.assertRaises(TypeError): + sumprod([5, 10], ['abc', 3]) + + # Special values should give the same as the pure python recipe + self.assertEqual(sumprod([10.1, math.inf], [20.2, 30.3]), math.inf) + self.assertEqual(sumprod([10.1, math.inf], [math.inf, 30.3]), math.inf) + self.assertEqual(sumprod([10.1, math.inf], [math.inf, math.inf]), math.inf) + self.assertEqual(sumprod([10.1, -math.inf], [20.2, 30.3]), -math.inf) + self.assertTrue(math.isnan(sumprod([10.1, math.inf], [-math.inf, math.inf]))) + self.assertTrue(math.isnan(sumprod([10.1, math.nan], [20.2, 30.3]))) + self.assertTrue(math.isnan(sumprod([10.1, math.inf], [math.nan, 30.3]))) + self.assertTrue(math.isnan(sumprod([10.1, math.inf], [20.3, math.nan]))) + + # Error cases that arose during development + args = ((-5, -5, 10), (1.5, 4611686018427387904, 2305843009213693952)) + self.assertEqual(sumprod(*args), 0.0) + + + @requires_IEEE_754 + @unittest.skipIf(HAVE_DOUBLE_ROUNDING, + "sumprod() accuracy not guaranteed on machines with double rounding") + @support.cpython_only # Other implementations may choose a different algorithm + def test_sumprod_accuracy(self): + sumprod = math.sumprod + self.assertEqual(sumprod([0.1] * 10, [1]*10), 1.0) + self.assertEqual(sumprod([0.1] * 20, [True, False] * 10), 1.0) + self.assertEqual(sumprod([1.0, 10E100, 1.0, -10E100], [1.0]*4), 2.0) + + def test_sumprod_stress(self): + sumprod = math.sumprod + product = itertools.product + Decimal = decimal.Decimal + Fraction = fractions.Fraction + + class Int(int): + def __add__(self, other): + return Int(int(self) + int(other)) + def __mul__(self, other): + return Int(int(self) * int(other)) + __radd__ = __add__ + __rmul__ = __mul__ + def __repr__(self): + return f'Int({int(self)})' + + class Flt(float): + def __add__(self, other): + return Int(int(self) + int(other)) + def __mul__(self, other): + return Int(int(self) * int(other)) + __radd__ = __add__ + __rmul__ = __mul__ + def __repr__(self): + return f'Flt({int(self)})' + + def baseline_sumprod(p, q): + """This defines the target behavior including expections and special values. + However, it is subject to rounding errors, so float inputs should be exactly + representable with only a few bits. + """ + total = 0 + for p_i, q_i in zip(p, q, strict=True): + total += p_i * q_i + return total + + def run(func, *args): + "Make comparing functions easier. Returns error status, type, and result." + try: + result = func(*args) + except (AssertionError, NameError): + raise + except Exception as e: + return type(e), None, 'None' + return None, type(result), repr(result) + + pools = [ + (-5, 10, -2**20, 2**31, 2**40, 2**61, 2**62, 2**80, 1.5, Int(7)), + (5.25, -3.5, 4.75, 11.25, 400.5, 0.046875, 0.25, -1.0, -0.078125), + (-19.0*2**500, 11*2**1000, -3*2**1500, 17*2*333, + 5.25, -3.25, -3.0*2**(-333), 3, 2**513), + (3.75, 2.5, -1.5, float('inf'), -float('inf'), float('NaN'), 14, + 9, 3+4j, Flt(13), 0.0), + (13.25, -4.25, Decimal('10.5'), Decimal('-2.25'), Fraction(13, 8), + Fraction(-11, 16), 4.75 + 0.125j, 97, -41, Int(3)), + (Decimal('6.125'), Decimal('12.375'), Decimal('-2.75'), Decimal(0), + Decimal('Inf'), -Decimal('Inf'), Decimal('NaN'), 12, 13.5), + (-2.0 ** -1000, 11*2**1000, 3, 7, -37*2**32, -2*2**-537, -2*2**-538, + 2*2**-513), + (-7 * 2.0 ** -510, 5 * 2.0 ** -520, 17, -19.0, -6.25), + (11.25, -3.75, -0.625, 23.375, True, False, 7, Int(5)), + ] + + for pool in pools: + for size in range(4): + for args1 in product(pool, repeat=size): + for args2 in product(pool, repeat=size): + args = (args1, args2) + self.assertEqual( + run(baseline_sumprod, *args), + run(sumprod, *args), + args, + ) + def testModf(self): self.assertRaises(TypeError, math.modf) diff --git a/Misc/NEWS.d/next/Library/2023-01-01-21-54-46.gh-issue-100485.geNrHS.rst b/Misc/NEWS.d/next/Library/2023-01-01-21-54-46.gh-issue-100485.geNrHS.rst new file mode 100644 index 000000000000..9f6e593113bb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-01-21-54-46.gh-issue-100485.geNrHS.rst @@ -0,0 +1 @@ +Add math.sumprod() to compute the sum of products. diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 9fac1037e525..1f9725883b98 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -333,6 +333,43 @@ math_dist(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } +PyDoc_STRVAR(math_sumprod__doc__, +"sumprod($module, p, q, /)\n" +"--\n" +"\n" +"Return the sum of products of values from two iterables p and q.\n" +"\n" +"Roughly equivalent to:\n" +"\n" +" sum(itertools.starmap(operator.mul, zip(p, q, strict=True)))\n" +"\n" +"For float and mixed int/float inputs, the intermediate products\n" +"and sums are computed with extended precision."); + +#define MATH_SUMPROD_METHODDEF \ + {"sumprod", _PyCFunction_CAST(math_sumprod), METH_FASTCALL, math_sumprod__doc__}, + +static PyObject * +math_sumprod_impl(PyObject *module, PyObject *p, PyObject *q); + +static PyObject * +math_sumprod(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *p; + PyObject *q; + + if (!_PyArg_CheckPositional("sumprod", nargs, 2, 2)) { + goto exit; + } + p = args[0]; + q = args[1]; + return_value = math_sumprod_impl(module, p, q); + +exit: + return return_value; +} + PyDoc_STRVAR(math_pow__doc__, "pow($module, x, y, /)\n" "--\n" @@ -917,4 +954,4 @@ math_ulp(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=c2c2f42452d63734 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=899211ec70e4506c input=a9049054013a1b77]*/ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 49c0293d4f5c..0bcb336efbb0 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -68,6 +68,7 @@ raised for division by zero and mod by zero. #include /* For _Py_log1p with workarounds for buggy handling of zeros. */ #include "_math.h" +#include #include "clinic/mathmodule.c.h" @@ -2819,6 +2820,329 @@ For example, the hypotenuse of a 3/4/5 right triangle is:\n\ 5.0\n\ "); +/** sumprod() ***************************************************************/ + +/* Forward declaration */ +static inline int _check_long_mult_overflow(long a, long b); + +static inline bool +long_add_would_overflow(long a, long b) +{ + return (a > 0) ? (b > LONG_MAX - a) : (b < LONG_MIN - a); +} + +/* +Double length extended precision floating point arithmetic +based on ideas from three sources: + + Improved Kahan?Babu?ka algorithm by Arnold Neumaier + https://www.mat.univie.ac.at/~neum/scan/01.pdf + + A Floating-Point Technique for Extending the Available Precision + by T. J. Dekker + https://csclub.uwaterloo.ca/~pbarfuss/dekker1971.pdf + + Ultimately Fast Accurate Summation by Siegfried M. Rump + https://www.tuhh.de/ti3/paper/rump/Ru08b.pdf + +The double length routines allow for quite a bit of instruction +level parallelism. On a 3.22 Ghz Apple M1 Max, the incremental +cost of increasing the input vector size by one is 6.25 nsec. + +dl_zero() returns an extended precision zero +dl_split() exactly splits a double into two half precision components. +dl_add() performs compensated summation to keep a running total. +dl_mul() implements lossless multiplication of doubles. +dl_fma() implements an extended precision fused-multiply-add. +dl_to_d() converts from extended precision to double precision. + +*/ + +typedef struct{ double hi; double lo; } DoubleLength; + +static inline DoubleLength +dl_zero() +{ + return (DoubleLength) {0.0, 0.0}; +} +static inline DoubleLength +dl_add(DoubleLength total, double x) +{ + double s = total.hi + x; + double c = total.lo; + if (fabs(total.hi) >= fabs(x)) { + c += (total.hi - s) + x; + } else { + c += (x - s) + total.hi; + } + return (DoubleLength) {s, c}; +} + +static inline DoubleLength +dl_split(double x) { + double t = x * 134217729.0; /* Veltkamp constant = float(0x8000001) */ + double hi = t - (t - x); + double lo = x - hi; + return (DoubleLength) {hi, lo}; +} + +static inline DoubleLength +dl_mul(double x, double y) +{ + /* Dekker mul12(). Section (5.12) */ + DoubleLength xx = dl_split(x); + DoubleLength yy = dl_split(y); + double p = xx.hi * yy.hi; + double q = xx.hi * yy.lo + xx.lo * yy.hi; + double z = p + q; + double zz = p - z + q + xx.lo * yy.lo; + return (DoubleLength) {z, zz}; +} + +static inline DoubleLength +dl_fma(DoubleLength total, double p, double q) +{ + DoubleLength product = dl_mul(p, q); + total = dl_add(total, product.hi); + return dl_add(total, product.lo); +} + +static inline double +dl_to_d(DoubleLength total) +{ + return total.hi + total.lo; +} + +/*[clinic input] +math.sumprod + + p: object + q: object + / + +Return the sum of products of values from two iterables p and q. + +Roughly equivalent to: + + sum(itertools.starmap(operator.mul, zip(p, q, strict=True))) + +For float and mixed int/float inputs, the intermediate products +and sums are computed with extended precision. +[clinic start generated code]*/ + +static PyObject * +math_sumprod_impl(PyObject *module, PyObject *p, PyObject *q) +/*[clinic end generated code: output=6722dbfe60664554 input=82be54fe26f87e30]*/ +{ + PyObject *p_i = NULL, *q_i = NULL, *term_i = NULL, *new_total = NULL; + PyObject *p_it, *q_it, *total; + iternextfunc p_next, q_next; + bool p_stopped = false, q_stopped = false; + bool int_path_enabled = true, int_total_in_use = false; + bool flt_path_enabled = true, flt_total_in_use = false; + long int_total = 0; + DoubleLength flt_total = dl_zero(); + + p_it = PyObject_GetIter(p); + if (p_it == NULL) { + return NULL; + } + q_it = PyObject_GetIter(q); + if (q_it == NULL) { + Py_DECREF(p_it); + return NULL; + } + total = PyLong_FromLong(0); + if (total == NULL) { + Py_DECREF(p_it); + Py_DECREF(q_it); + return NULL; + } + p_next = *Py_TYPE(p_it)->tp_iternext; + q_next = *Py_TYPE(q_it)->tp_iternext; + while (1) { + bool finished; + + assert (p_i == NULL); + assert (q_i == NULL); + assert (term_i == NULL); + assert (new_total == NULL); + + assert (p_it != NULL); + assert (q_it != NULL); + assert (total != NULL); + + p_i = p_next(p_it); + if (p_i == NULL) { + if (PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_StopIteration)) { + goto err_exit; + } + PyErr_Clear(); + } + p_stopped = true; + } + q_i = q_next(q_it); + if (q_i == NULL) { + if (PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_StopIteration)) { + goto err_exit; + } + PyErr_Clear(); + } + q_stopped = true; + } + if (p_stopped != q_stopped) { + PyErr_Format(PyExc_ValueError, "Inputs are not the same length"); + goto err_exit; + } + finished = p_stopped & q_stopped; + + if (int_path_enabled) { + + if (!finished && PyLong_CheckExact(p_i) & PyLong_CheckExact(q_i)) { + int overflow; + long int_p, int_q, int_prod; + + int_p = PyLong_AsLongAndOverflow(p_i, &overflow); + if (overflow) { + goto finalize_int_path; + } + int_q = PyLong_AsLongAndOverflow(q_i, &overflow); + if (overflow) { + goto finalize_int_path; + } + if (_check_long_mult_overflow(int_p, int_q)) { + goto finalize_int_path; + } + int_prod = int_p * int_q; + if (long_add_would_overflow(int_total, int_prod)) { + goto finalize_int_path; + } + int_total += int_prod; + int_total_in_use = true; + Py_CLEAR(p_i); + Py_CLEAR(q_i); + continue; + } + + finalize_int_path: + // # We're finished, overflowed, or have a non-int + int_path_enabled = false; + if (int_total_in_use) { + term_i = PyLong_FromLong(int_total); + if (term_i == NULL) { + goto err_exit; + } + new_total = PyNumber_Add(total, term_i); + if (new_total == NULL) { + goto err_exit; + } + Py_SETREF(total, new_total); + new_total = NULL; + Py_CLEAR(term_i); + int_total = 0; // An ounce of prevention, ... + int_total_in_use = false; + } + } + + if (flt_path_enabled) { + + if (!finished) { + double flt_p, flt_q; + bool p_type_float = PyFloat_CheckExact(p_i); + bool q_type_float = PyFloat_CheckExact(q_i); + if (p_type_float && q_type_float) { + flt_p = PyFloat_AS_DOUBLE(p_i); + flt_q = PyFloat_AS_DOUBLE(q_i); + } else if (p_type_float && (PyLong_CheckExact(q_i) || PyBool_Check(q_i))) { + /* We care about float/int pairs and int/float pairs because + they arise naturally in several use cases such as price + times quantity, measurements with integer weights, or + data selected by a vector of bools. */ + flt_p = PyFloat_AS_DOUBLE(p_i); + flt_q = PyLong_AsDouble(q_i); + if (flt_q == -1.0 && PyErr_Occurred()) { + PyErr_Clear(); + goto finalize_flt_path; + } + } else if (q_type_float && (PyLong_CheckExact(p_i) || PyBool_Check(q_i))) { + flt_q = PyFloat_AS_DOUBLE(q_i); + flt_p = PyLong_AsDouble(p_i); + if (flt_p == -1.0 && PyErr_Occurred()) { + PyErr_Clear(); + goto finalize_flt_path; + } + } else { + goto finalize_flt_path; + } + DoubleLength new_flt_total = dl_fma(flt_total, flt_p, flt_q); + if (isfinite(new_flt_total.hi)) { + flt_total = new_flt_total; + flt_total_in_use = true; + Py_CLEAR(p_i); + Py_CLEAR(q_i); + continue; + } + } + + finalize_flt_path: + // We're finished, overflowed, have a non-float, or got a non-finite value + flt_path_enabled = false; + if (flt_total_in_use) { + term_i = PyFloat_FromDouble(dl_to_d(flt_total)); + if (term_i == NULL) { + goto err_exit; + } + new_total = PyNumber_Add(total, term_i); + if (new_total == NULL) { + goto err_exit; + } + Py_SETREF(total, new_total); + new_total = NULL; + Py_CLEAR(term_i); + flt_total = dl_zero(); + flt_total_in_use = false; + } + } + + assert(!int_total_in_use); + assert(!flt_total_in_use); + if (finished) { + goto normal_exit; + } + term_i = PyNumber_Multiply(p_i, q_i); + if (term_i == NULL) { + goto err_exit; + } + new_total = PyNumber_Add(total, term_i); + if (new_total == NULL) { + goto err_exit; + } + Py_SETREF(total, new_total); + new_total = NULL; + Py_CLEAR(p_i); + Py_CLEAR(q_i); + Py_CLEAR(term_i); + } + + normal_exit: + Py_DECREF(p_it); + Py_DECREF(q_it); + return total; + + err_exit: + Py_DECREF(p_it); + Py_DECREF(q_it); + Py_DECREF(total); + Py_XDECREF(p_i); + Py_XDECREF(q_i); + Py_XDECREF(term_i); + Py_XDECREF(new_total); + return NULL; +} + + /* pow can't use math_2, but needs its own wrapper: the problem is that an infinite result can arise either as a result of overflow (in which case OverflowError should be raised) or as a result of @@ -3933,6 +4257,7 @@ static PyMethodDef math_methods[] = { {"sqrt", math_sqrt, METH_O, math_sqrt_doc}, {"tan", math_tan, METH_O, math_tan_doc}, {"tanh", math_tanh, METH_O, math_tanh_doc}, + MATH_SUMPROD_METHODDEF MATH_TRUNC_METHODDEF MATH_PROD_METHODDEF MATH_PERM_METHODDEF From webhook-mailer at python.org Sat Jan 7 15:55:54 2023 From: webhook-mailer at python.org (miss-islington) Date: Sat, 07 Jan 2023 20:55:54 -0000 Subject: [Python-checkins] [3.10] gh-100673: Removed erroneous note in the get_type_hints docs (#100701) (GH-100826) Message-ID: https://github.com/python/cpython/commit/67e52e54bb85ed10d2e21b7e8f7337085304b299 commit: 67e52e54bb85ed10d2e21b7e8f7337085304b299 branch: 3.10 author: Alex Waygood committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-07T12:55:41-08:00 summary: [3.10] gh-100673: Removed erroneous note in the get_type_hints docs (#100701) (GH-100826) Removed erroneous note in the get_type_hints docs typing.get_type_hints still includes base class type hints. (cherry picked from commit deaf090699a7312cccb0637409f44de3f382389b) Automerge-Triggered-By: GH:AlexWaygood files: M Doc/library/typing.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index d3275209e938..d415f149027f 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2233,10 +2233,6 @@ Introspection helpers .. versionchanged:: 3.9 Added ``include_extras`` parameter as part of :pep:`593`. - .. versionchanged:: 3.10 - Calling ``get_type_hints()`` on a class no longer returns the annotations - of its base classes. - .. function:: get_args(tp) .. function:: get_origin(tp) From webhook-mailer at python.org Sat Jan 7 16:16:43 2023 From: webhook-mailer at python.org (rhettinger) Date: Sat, 07 Jan 2023 21:16:43 -0000 Subject: [Python-checkins] [3.11] Future-proof recipe by renaming dotproduct() to sumprod() (GH-100828) Message-ID: https://github.com/python/cpython/commit/4b4e6da7b53f9eb2d3654d321f6acfbd599bd990 commit: 4b4e6da7b53f9eb2d3654d321f6acfbd599bd990 branch: 3.11 author: Raymond Hettinger committer: rhettinger date: 2023-01-07T15:16:38-06:00 summary: [3.11] Future-proof recipe by renaming dotproduct() to sumprod() (GH-100828) files: M Doc/library/itertools.rst diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index fbbc96c60aba..a1d1ef67bebd 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -33,7 +33,7 @@ by combining :func:`map` and :func:`count` to form ``map(f, count())``. These tools and their built-in counterparts also work well with the high-speed functions in the :mod:`operator` module. For example, the multiplication operator can be mapped across two vectors to form an efficient dot-product: -``sum(map(operator.mul, vector1, vector2))``. +``sum(starmap(operator.mul, zip(vec1, vec2, strict=True)))``. **Infinite iterators:** @@ -799,7 +799,7 @@ which incur interpreter overhead. "Returns the sequence elements n times" return chain.from_iterable(repeat(tuple(iterable), n)) - def dotproduct(vec1, vec2): + def sumprod(vec1, vec2): "Compute a sum of products." return sum(starmap(operator.mul, zip(vec1, vec2, strict=True))) @@ -813,7 +813,7 @@ which incur interpreter overhead. window = collections.deque([0], maxlen=n) * n for x in chain(signal, repeat(0, n-1)): window.append(x) - yield dotproduct(kernel, window) + yield sumprod(kernel, window) def polynomial_from_roots(roots): """Compute a polynomial's coefficients from its roots. @@ -1181,7 +1181,7 @@ which incur interpreter overhead. >>> list(ncycles('abc', 3)) ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c'] - >>> dotproduct([1,2,3], [4,5,6]) + >>> sumprod([1,2,3], [4,5,6]) 32 >>> data = [20, 40, 24, 32, 20, 28, 16] From webhook-mailer at python.org Sat Jan 7 16:26:11 2023 From: webhook-mailer at python.org (hauntsaninja) Date: Sat, 07 Jan 2023 21:26:11 -0000 Subject: [Python-checkins] gh-100792: Make `email.message.Message.__contains__` twice as fast (#100793) Message-ID: https://github.com/python/cpython/commit/6746135b0722a5359ce6346554c847afba603b5a commit: 6746135b0722a5359ce6346554c847afba603b5a branch: main author: Nikita Sobolev committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com> date: 2023-01-07T13:26:05-08:00 summary: gh-100792: Make `email.message.Message.__contains__` twice as fast (#100793) files: A Misc/NEWS.d/next/Library/2023-01-06-14-05-15.gh-issue-100792.CEOJth.rst M Lib/email/message.py diff --git a/Lib/email/message.py b/Lib/email/message.py index 65fda507251c..b540c33984a7 100644 --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -448,7 +448,11 @@ def __delitem__(self, name): self._headers = newheaders def __contains__(self, name): - return name.lower() in [k.lower() for k, v in self._headers] + name_lower = name.lower() + for k, v in self._headers: + if name_lower == k.lower(): + return True + return False def __iter__(self): for field, value in self._headers: diff --git a/Misc/NEWS.d/next/Library/2023-01-06-14-05-15.gh-issue-100792.CEOJth.rst b/Misc/NEWS.d/next/Library/2023-01-06-14-05-15.gh-issue-100792.CEOJth.rst new file mode 100644 index 000000000000..5966bc1e6051 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-06-14-05-15.gh-issue-100792.CEOJth.rst @@ -0,0 +1 @@ +Make :meth:`email.message.Message.__contains__` twice as fast. From webhook-mailer at python.org Sat Jan 7 16:29:58 2023 From: webhook-mailer at python.org (AlexWaygood) Date: Sat, 07 Jan 2023 21:29:58 -0000 Subject: [Python-checkins] gh-100815: Normalize `types` module usage in `copy` module (#100816) Message-ID: https://github.com/python/cpython/commit/951303fd855838d47765dcd05471e14311dc9fdd commit: 951303fd855838d47765dcd05471e14311dc9fdd branch: main author: Nikita Sobolev committer: AlexWaygood date: 2023-01-07T21:29:53Z summary: gh-100815: Normalize `types` module usage in `copy` module (#100816) files: M Lib/copy.py diff --git a/Lib/copy.py b/Lib/copy.py index 6e8c19bc652d..da2908ef623d 100644 --- a/Lib/copy.py +++ b/Lib/copy.py @@ -101,13 +101,11 @@ def copy(x): def _copy_immutable(x): return x -for t in (type(None), int, float, bool, complex, str, tuple, +for t in (types.NoneType, int, float, bool, complex, str, tuple, bytes, frozenset, type, range, slice, property, - types.BuiltinFunctionType, type(Ellipsis), type(NotImplemented), - types.FunctionType, weakref.ref): - d[t] = _copy_immutable -t = getattr(types, "CodeType", None) -if t is not None: + types.BuiltinFunctionType, types.EllipsisType, + types.NotImplementedType, types.FunctionType, types.CodeType, + weakref.ref): d[t] = _copy_immutable d[list] = list.copy @@ -173,9 +171,9 @@ def deepcopy(x, memo=None, _nil=[]): def _deepcopy_atomic(x, memo): return x -d[type(None)] = _deepcopy_atomic -d[type(Ellipsis)] = _deepcopy_atomic -d[type(NotImplemented)] = _deepcopy_atomic +d[types.NoneType] = _deepcopy_atomic +d[types.EllipsisType] = _deepcopy_atomic +d[types.NotImplementedType] = _deepcopy_atomic d[int] = _deepcopy_atomic d[float] = _deepcopy_atomic d[bool] = _deepcopy_atomic From webhook-mailer at python.org Sat Jan 7 22:37:16 2023 From: webhook-mailer at python.org (rhettinger) Date: Sun, 08 Jan 2023 03:37:16 -0000 Subject: [Python-checkins] GH-100485: Convert from Fast2Sum to 2Sum (GH-100836) Message-ID: https://github.com/python/cpython/commit/df3851fe4ab8c9013aeb5dfd75d6e8edf2ece9a8 commit: df3851fe4ab8c9013aeb5dfd75d6e8edf2ece9a8 branch: main author: Raymond Hettinger committer: rhettinger date: 2023-01-07T21:37:08-06:00 summary: GH-100485: Convert from Fast2Sum to 2Sum (GH-100836) files: M Modules/mathmodule.c diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 0bcb336efbb0..9545ad2a99eb 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2847,7 +2847,7 @@ based on ideas from three sources: The double length routines allow for quite a bit of instruction level parallelism. On a 3.22 Ghz Apple M1 Max, the incremental -cost of increasing the input vector size by one is 6.25 nsec. +cost of increasing the input vector size by one is 6.0 nsec. dl_zero() returns an extended precision zero dl_split() exactly splits a double into two half precision components. @@ -2860,22 +2860,25 @@ dl_to_d() converts from extended precision to double precision. typedef struct{ double hi; double lo; } DoubleLength; +static const DoubleLength dl_zero = {0.0, 0.0}; + static inline DoubleLength -dl_zero() +twosum(double a, double b) { - return (DoubleLength) {0.0, 0.0}; + double s = a + b; + double ap = s - b; + double bp = s - a; + double da = a - ap; + double db = b - bp; + double t = da + db; + return (DoubleLength) {s, t}; } + static inline DoubleLength dl_add(DoubleLength total, double x) { - double s = total.hi + x; - double c = total.lo; - if (fabs(total.hi) >= fabs(x)) { - c += (total.hi - s) + x; - } else { - c += (x - s) + total.hi; - } - return (DoubleLength) {s, c}; + DoubleLength s = twosum(total.hi, x); + return (DoubleLength) {s.hi, total.lo + s.lo}; } static inline DoubleLength @@ -2941,7 +2944,7 @@ math_sumprod_impl(PyObject *module, PyObject *p, PyObject *q) bool int_path_enabled = true, int_total_in_use = false; bool flt_path_enabled = true, flt_total_in_use = false; long int_total = 0; - DoubleLength flt_total = dl_zero(); + DoubleLength flt_total = dl_zero; p_it = PyObject_GetIter(p); if (p_it == NULL) { @@ -3101,7 +3104,7 @@ math_sumprod_impl(PyObject *module, PyObject *p, PyObject *q) Py_SETREF(total, new_total); new_total = NULL; Py_CLEAR(term_i); - flt_total = dl_zero(); + flt_total = dl_zero; flt_total_in_use = false; } } From webhook-mailer at python.org Sun Jan 8 02:58:00 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sun, 08 Jan 2023 07:58:00 -0000 Subject: [Python-checkins] gh-100776: Fix misleading default value in help(input) (#100788) Message-ID: https://github.com/python/cpython/commit/a2141882f259e21bb09fa0b7cba8142147b9e3d7 commit: a2141882f259e21bb09fa0b7cba8142147b9e3d7 branch: main author: Shantanu <12621235+hauntsaninja at users.noreply.github.com> committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-08T13:27:41+05:30 summary: gh-100776: Fix misleading default value in help(input) (#100788) files: A Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst M Python/bltinmodule.c M Python/clinic/bltinmodule.c.h diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst new file mode 100644 index 000000000000..b94121ea5f29 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst @@ -0,0 +1 @@ +Fix misleading default value in :func:`input`'s ``__text_signature__``. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 9ebe4c8353d0..599cc18361ac 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2063,7 +2063,7 @@ builtin_print_impl(PyObject *module, PyObject *args, PyObject *sep, /*[clinic input] input as builtin_input - prompt: object(c_default="NULL") = None + prompt: object(c_default="NULL") = "" / Read a string from standard input. The trailing newline is stripped. @@ -2077,7 +2077,7 @@ On *nix systems, readline is used if available. static PyObject * builtin_input_impl(PyObject *module, PyObject *prompt) -/*[clinic end generated code: output=83db5a191e7a0d60 input=5e8bb70c2908fe3c]*/ +/*[clinic end generated code: output=83db5a191e7a0d60 input=159c46d4ae40977e]*/ { PyThreadState *tstate = _PyThreadState_GET(); PyObject *fin = _PySys_GetAttr( diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index baf955558a21..b77b4a1e4b41 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -1126,7 +1126,7 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec } PyDoc_STRVAR(builtin_input__doc__, -"input($module, prompt=None, /)\n" +"input($module, prompt=\'\', /)\n" "--\n" "\n" "Read a string from standard input. The trailing newline is stripped.\n" @@ -1409,4 +1409,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=0a6a8efe82cf8b81 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=84a04e7446debf58 input=a9049054013a1b77]*/ From webhook-mailer at python.org Sun Jan 8 03:07:22 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sun, 08 Jan 2023 08:07:22 -0000 Subject: [Python-checkins] gh-100824: Fix typo in the documentation of unittest.TestLoader.testNamePatterns (#100825) Message-ID: https://github.com/python/cpython/commit/6d3bc4a795e7a60f665e41b2d4b6803f3844fc48 commit: 6d3bc4a795e7a60f665e41b2d4b6803f3844fc48 branch: main author: busywhitespace committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-08T13:37:16+05:30 summary: gh-100824: Fix typo in the documentation of unittest.TestLoader.testNamePatterns (#100825) files: M Doc/library/unittest.rst diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 7f48146ca830..1577149e9764 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -1947,12 +1947,12 @@ Loading and running tests .. attribute:: testNamePatterns List of Unix shell-style wildcard test name patterns that test methods - have to match to be included in test suites (see ``-v`` option). + have to match to be included in test suites (see ``-k`` option). If this attribute is not ``None`` (the default), all test methods to be included in test suites must match one of the patterns in this list. Note that matches are always performed using :meth:`fnmatch.fnmatchcase`, - so unlike patterns passed to the ``-v`` option, simple substring patterns + so unlike patterns passed to the ``-k`` option, simple substring patterns will have to be converted using ``*`` wildcards. This affects all the :meth:`loadTestsFrom\*` methods. From webhook-mailer at python.org Sun Jan 8 03:16:56 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 08 Jan 2023 08:16:56 -0000 Subject: [Python-checkins] gh-100824: Fix typo in the documentation of unittest.TestLoader.testNamePatterns (GH-100825) Message-ID: https://github.com/python/cpython/commit/caed845fb53d633f4544ac99b043bad66ef5639f commit: caed845fb53d633f4544ac99b043bad66ef5639f branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-08T00:16:49-08:00 summary: gh-100824: Fix typo in the documentation of unittest.TestLoader.testNamePatterns (GH-100825) (cherry picked from commit 6d3bc4a795e7a60f665e41b2d4b6803f3844fc48) Co-authored-by: busywhitespace files: M Doc/library/unittest.rst diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 42d17db3a3a2..82fded4ab002 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -1983,12 +1983,12 @@ Loading and running tests .. attribute:: testNamePatterns List of Unix shell-style wildcard test name patterns that test methods - have to match to be included in test suites (see ``-v`` option). + have to match to be included in test suites (see ``-k`` option). If this attribute is not ``None`` (the default), all test methods to be included in test suites must match one of the patterns in this list. Note that matches are always performed using :meth:`fnmatch.fnmatchcase`, - so unlike patterns passed to the ``-v`` option, simple substring patterns + so unlike patterns passed to the ``-k`` option, simple substring patterns will have to be converted using ``*`` wildcards. This affects all the :meth:`loadTestsFrom\*` methods. From webhook-mailer at python.org Sun Jan 8 03:17:04 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 08 Jan 2023 08:17:04 -0000 Subject: [Python-checkins] gh-100824: Fix typo in the documentation of unittest.TestLoader.testNamePatterns (GH-100825) Message-ID: https://github.com/python/cpython/commit/89ea4f92bc6380ca052dba3f5f54292133db9b8e commit: 89ea4f92bc6380ca052dba3f5f54292133db9b8e branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-08T00:16:59-08:00 summary: gh-100824: Fix typo in the documentation of unittest.TestLoader.testNamePatterns (GH-100825) (cherry picked from commit 6d3bc4a795e7a60f665e41b2d4b6803f3844fc48) Co-authored-by: busywhitespace files: M Doc/library/unittest.rst diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index fa4383b2d992..7dc70a8e17c4 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -1920,12 +1920,12 @@ Loading and running tests .. attribute:: testNamePatterns List of Unix shell-style wildcard test name patterns that test methods - have to match to be included in test suites (see ``-v`` option). + have to match to be included in test suites (see ``-k`` option). If this attribute is not ``None`` (the default), all test methods to be included in test suites must match one of the patterns in this list. Note that matches are always performed using :meth:`fnmatch.fnmatchcase`, - so unlike patterns passed to the ``-v`` option, simple substring patterns + so unlike patterns passed to the ``-k`` option, simple substring patterns will have to be converted using ``*`` wildcards. This affects all the :meth:`loadTestsFrom\*` methods. From webhook-mailer at python.org Sun Jan 8 03:34:25 2023 From: webhook-mailer at python.org (hauntsaninja) Date: Sun, 08 Jan 2023 08:34:25 -0000 Subject: [Python-checkins] gh-91851: Micro optimizations for arithmetic between Fractions (#25518) Message-ID: https://github.com/python/cpython/commit/909982e82aa73fe5a75d5ab75fbaf84539a0c5e3 commit: 909982e82aa73fe5a75d5ab75fbaf84539a0c5e3 branch: main author: Sergey B Kirpichev committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com> date: 2023-01-08T00:34:20-08:00 summary: gh-91851: Micro optimizations for arithmetic between Fractions (#25518) Adapted from https://github.com/python/cpython/pull/24779/commits/046c84e8f9 This makes arithmetic between Fractions with small components just as fast as before python/cpython#24779, at some expense of mixed arithmetic (e.g. Fraction + int). files: A Misc/NEWS.d/next/Library/2022-04-23-08-12-14.gh-issue-91851.Jd47V6.rst M Lib/fractions.py diff --git a/Lib/fractions.py b/Lib/fractions.py index 939741115f91..bdba6c339593 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -395,8 +395,10 @@ class doesn't subclass a concrete type, there's no """ def forward(a, b): - if isinstance(b, (int, Fraction)): + if isinstance(b, Fraction): return monomorphic_operator(a, b) + elif isinstance(b, int): + return monomorphic_operator(a, Fraction(b)) elif isinstance(b, float): return fallback_operator(float(a), b) elif isinstance(b, complex): @@ -409,7 +411,7 @@ def forward(a, b): def reverse(b, a): if isinstance(a, numbers.Rational): # Includes ints. - return monomorphic_operator(a, b) + return monomorphic_operator(Fraction(a), b) elif isinstance(a, numbers.Real): return fallback_operator(float(a), float(b)) elif isinstance(a, numbers.Complex): @@ -491,8 +493,8 @@ def reverse(b, a): def _add(a, b): """a + b""" - na, da = a.numerator, a.denominator - nb, db = b.numerator, b.denominator + na, da = a._numerator, a._denominator + nb, db = b._numerator, b._denominator g = math.gcd(da, db) if g == 1: return Fraction(na * db + da * nb, da * db, _normalize=False) @@ -507,8 +509,8 @@ def _add(a, b): def _sub(a, b): """a - b""" - na, da = a.numerator, a.denominator - nb, db = b.numerator, b.denominator + na, da = a._numerator, a._denominator + nb, db = b._numerator, b._denominator g = math.gcd(da, db) if g == 1: return Fraction(na * db - da * nb, da * db, _normalize=False) @@ -523,8 +525,8 @@ def _sub(a, b): def _mul(a, b): """a * b""" - na, da = a.numerator, a.denominator - nb, db = b.numerator, b.denominator + na, da = a._numerator, a._denominator + nb, db = b._numerator, b._denominator g1 = math.gcd(na, db) if g1 > 1: na //= g1 @@ -540,8 +542,8 @@ def _mul(a, b): def _div(a, b): """a / b""" # Same as _mul(), with inversed b. - na, da = a.numerator, a.denominator - nb, db = b.numerator, b.denominator + na, da = a._numerator, a._denominator + nb, db = b._numerator, b._denominator g1 = math.gcd(na, nb) if g1 > 1: na //= g1 diff --git a/Misc/NEWS.d/next/Library/2022-04-23-08-12-14.gh-issue-91851.Jd47V6.rst b/Misc/NEWS.d/next/Library/2022-04-23-08-12-14.gh-issue-91851.Jd47V6.rst new file mode 100644 index 000000000000..a918bffb601b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-04-23-08-12-14.gh-issue-91851.Jd47V6.rst @@ -0,0 +1 @@ +Optimize the :class:`~fractions.Fraction` arithmetics for small components. From webhook-mailer at python.org Sun Jan 8 03:51:36 2023 From: webhook-mailer at python.org (hauntsaninja) Date: Sun, 08 Jan 2023 08:51:36 -0000 Subject: [Python-checkins] gh-100783: fix os.path.join documentation (#100811) Message-ID: https://github.com/python/cpython/commit/53455a319f3f2e5609fca2a313ea356fba318665 commit: 53455a319f3f2e5609fca2a313ea356fba318665 branch: main author: Shantanu <12621235+hauntsaninja at users.noreply.github.com> committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com> date: 2023-01-08T00:51:30-08:00 summary: gh-100783: fix os.path.join documentation (#100811) - Use "drive", not "drive letter", because of UNC paths - Previous components are not thrown away from relative drive letters - Use "segment" instead of "component" for consistency with pathlib - Other miscellaneous improvements files: M Doc/library/os.path.rst diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index 50e089653fe7..42bbe24830e6 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -306,17 +306,18 @@ the :mod:`glob` module.) .. function:: join(path, *paths) - Join one or more path components intelligently. The return value is the - concatenation of *path* and any members of *\*paths* with exactly one - directory separator following each non-empty part except the last, meaning - that the result will only end in a separator if the last part is empty. If - a component is an absolute path, all previous components are thrown away - and joining continues from the absolute path component. - - On Windows, the drive letter is not reset when an absolute path component - (e.g., ``r'\foo'``) is encountered. If a component contains a drive - letter, all previous components are thrown away and the drive letter is - reset. Note that since there is a current directory for each drive, + Join one or more path segments intelligently. The return value is the + concatenation of *path* and all members of *\*paths*, with exactly one + directory separator following each non-empty part except the last. That is, + if the last part is empty, the result will end in a separator. If + a segment is an absolute path (which on Windows requires both a drive and a + root), then all previous segments are ignored and joining continues from the + absolute path segment. + + On Windows, the drive is not reset when a rooted path segment (e.g., + ``r'\foo'``) is encountered. If a segment is on a different drive or is an + absolute path, all previous segments are ignored and the drive is reset. Note + that since there is a current directory for each drive, ``os.path.join("c:", "foo")`` represents a path relative to the current directory on drive :file:`C:` (:file:`c:foo`), not :file:`c:\\foo`. From webhook-mailer at python.org Sun Jan 8 04:22:59 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 08 Jan 2023 09:22:59 -0000 Subject: [Python-checkins] gh-100783: fix os.path.join documentation (GH-100811) Message-ID: https://github.com/python/cpython/commit/87076d880cdd9f5537a6a27c1076bc6c7c1d2ee9 commit: 87076d880cdd9f5537a6a27c1076bc6c7c1d2ee9 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-08T01:22:44-08:00 summary: gh-100783: fix os.path.join documentation (GH-100811) - Use "drive", not "drive letter", because of UNC paths - Previous components are not thrown away from relative drive letters - Use "segment" instead of "component" for consistency with pathlib - Other miscellaneous improvements (cherry picked from commit 53455a319f3f2e5609fca2a313ea356fba318665) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> files: M Doc/library/os.path.rst diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index 0becb592b41e..319b664dd46d 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -297,17 +297,18 @@ the :mod:`glob` module.) .. function:: join(path, *paths) - Join one or more path components intelligently. The return value is the - concatenation of *path* and any members of *\*paths* with exactly one - directory separator following each non-empty part except the last, meaning - that the result will only end in a separator if the last part is empty. If - a component is an absolute path, all previous components are thrown away - and joining continues from the absolute path component. - - On Windows, the drive letter is not reset when an absolute path component - (e.g., ``r'\foo'``) is encountered. If a component contains a drive - letter, all previous components are thrown away and the drive letter is - reset. Note that since there is a current directory for each drive, + Join one or more path segments intelligently. The return value is the + concatenation of *path* and all members of *\*paths*, with exactly one + directory separator following each non-empty part except the last. That is, + if the last part is empty, the result will end in a separator. If + a segment is an absolute path (which on Windows requires both a drive and a + root), then all previous segments are ignored and joining continues from the + absolute path segment. + + On Windows, the drive is not reset when a rooted path segment (e.g., + ``r'\foo'``) is encountered. If a segment is on a different drive or is an + absolute path, all previous segments are ignored and the drive is reset. Note + that since there is a current directory for each drive, ``os.path.join("c:", "foo")`` represents a path relative to the current directory on drive :file:`C:` (:file:`c:foo`), not :file:`c:\\foo`. From webhook-mailer at python.org Sun Jan 8 04:23:00 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 08 Jan 2023 09:23:00 -0000 Subject: [Python-checkins] gh-100783: fix os.path.join documentation (GH-100811) Message-ID: https://github.com/python/cpython/commit/fa8d396e100f9bfdc5125b02a612aee4574c9e3a commit: fa8d396e100f9bfdc5125b02a612aee4574c9e3a branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-08T01:22:53-08:00 summary: gh-100783: fix os.path.join documentation (GH-100811) - Use "drive", not "drive letter", because of UNC paths - Previous components are not thrown away from relative drive letters - Use "segment" instead of "component" for consistency with pathlib - Other miscellaneous improvements (cherry picked from commit 53455a319f3f2e5609fca2a313ea356fba318665) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> files: M Doc/library/os.path.rst diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index 0becb592b41e..319b664dd46d 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -297,17 +297,18 @@ the :mod:`glob` module.) .. function:: join(path, *paths) - Join one or more path components intelligently. The return value is the - concatenation of *path* and any members of *\*paths* with exactly one - directory separator following each non-empty part except the last, meaning - that the result will only end in a separator if the last part is empty. If - a component is an absolute path, all previous components are thrown away - and joining continues from the absolute path component. - - On Windows, the drive letter is not reset when an absolute path component - (e.g., ``r'\foo'``) is encountered. If a component contains a drive - letter, all previous components are thrown away and the drive letter is - reset. Note that since there is a current directory for each drive, + Join one or more path segments intelligently. The return value is the + concatenation of *path* and all members of *\*paths*, with exactly one + directory separator following each non-empty part except the last. That is, + if the last part is empty, the result will end in a separator. If + a segment is an absolute path (which on Windows requires both a drive and a + root), then all previous segments are ignored and joining continues from the + absolute path segment. + + On Windows, the drive is not reset when a rooted path segment (e.g., + ``r'\foo'``) is encountered. If a segment is on a different drive or is an + absolute path, all previous segments are ignored and the drive is reset. Note + that since there is a current directory for each drive, ``os.path.join("c:", "foo")`` represents a path relative to the current directory on drive :file:`C:` (:file:`c:foo`), not :file:`c:\\foo`. From webhook-mailer at python.org Sun Jan 8 04:23:18 2023 From: webhook-mailer at python.org (hauntsaninja) Date: Sun, 08 Jan 2023 09:23:18 -0000 Subject: [Python-checkins] [3.11] gh-100776: Fix misleading default value in help(input) (GH-100788) (#100841) Message-ID: https://github.com/python/cpython/commit/be7c19723fa3fea3a4efe5c9c795c1e4a2fc05f5 commit: be7c19723fa3fea3a4efe5c9c795c1e4a2fc05f5 branch: 3.11 author: Shantanu <12621235+hauntsaninja at users.noreply.github.com> committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com> date: 2023-01-08T01:23:12-08:00 summary: [3.11] gh-100776: Fix misleading default value in help(input) (GH-100788) (#100841) (cherry picked from commit a2141882f259e21bb09fa0b7cba8142147b9e3d7) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> files: A Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst M Python/bltinmodule.c M Python/clinic/bltinmodule.c.h diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst new file mode 100644 index 000000000000..b94121ea5f29 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst @@ -0,0 +1 @@ +Fix misleading default value in :func:`input`'s ``__text_signature__``. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 94a7819c252e..a3fdb394a136 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2065,7 +2065,7 @@ builtin_print_impl(PyObject *module, PyObject *args, PyObject *sep, /*[clinic input] input as builtin_input - prompt: object(c_default="NULL") = None + prompt: object(c_default="NULL") = "" / Read a string from standard input. The trailing newline is stripped. @@ -2079,7 +2079,7 @@ On *nix systems, readline is used if available. static PyObject * builtin_input_impl(PyObject *module, PyObject *prompt) -/*[clinic end generated code: output=83db5a191e7a0d60 input=5e8bb70c2908fe3c]*/ +/*[clinic end generated code: output=83db5a191e7a0d60 input=159c46d4ae40977e]*/ { PyThreadState *tstate = _PyThreadState_GET(); PyObject *fin = _PySys_GetAttr( diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index 5d9a16a7b682..10cd6c820bcc 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -843,7 +843,7 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec } PyDoc_STRVAR(builtin_input__doc__, -"input($module, prompt=None, /)\n" +"input($module, prompt=\'\', /)\n" "--\n" "\n" "Read a string from standard input. The trailing newline is stripped.\n" @@ -1045,4 +1045,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=cc844ea007c1241f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c45d5fe414f7a8d7 input=a9049054013a1b77]*/ From webhook-mailer at python.org Sun Jan 8 04:23:39 2023 From: webhook-mailer at python.org (hauntsaninja) Date: Sun, 08 Jan 2023 09:23:39 -0000 Subject: [Python-checkins] [3.10] gh-100776: Fix misleading default value in help(input) (GH-100788) (#100842) Message-ID: https://github.com/python/cpython/commit/a8702bb8c8487e500e5cfcf47759fa739489f122 commit: a8702bb8c8487e500e5cfcf47759fa739489f122 branch: 3.10 author: Shantanu <12621235+hauntsaninja at users.noreply.github.com> committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com> date: 2023-01-08T01:23:34-08:00 summary: [3.10] gh-100776: Fix misleading default value in help(input) (GH-100788) (#100842) (cherry picked from commit a2141882f259e21bb09fa0b7cba8142147b9e3d7) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> files: A Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst M Python/bltinmodule.c M Python/clinic/bltinmodule.c.h diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst new file mode 100644 index 000000000000..b94121ea5f29 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst @@ -0,0 +1 @@ +Fix misleading default value in :func:`input`'s ``__text_signature__``. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index b0162e5e8721..659b78edc3de 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2034,7 +2034,7 @@ flush: whether to forcibly flush the stream."); /*[clinic input] input as builtin_input - prompt: object(c_default="NULL") = None + prompt: object(c_default="NULL") = "" / Read a string from standard input. The trailing newline is stripped. @@ -2048,7 +2048,7 @@ On *nix systems, readline is used if available. static PyObject * builtin_input_impl(PyObject *module, PyObject *prompt) -/*[clinic end generated code: output=83db5a191e7a0d60 input=5e8bb70c2908fe3c]*/ +/*[clinic end generated code: output=83db5a191e7a0d60 input=159c46d4ae40977e]*/ { PyObject *fin = _PySys_GetObjectId(&PyId_stdin); PyObject *fout = _PySys_GetObjectId(&PyId_stdout); diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index 545f5b53f6e5..6f547921c7e9 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -672,7 +672,7 @@ builtin_pow(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject } PyDoc_STRVAR(builtin_input__doc__, -"input($module, prompt=None, /)\n" +"input($module, prompt=\'\', /)\n" "--\n" "\n" "Read a string from standard input. The trailing newline is stripped.\n" @@ -874,4 +874,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=da9ae459e9233259 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b5c1a7a1621b7cca input=a9049054013a1b77]*/ From webhook-mailer at python.org Sun Jan 8 07:55:03 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sun, 08 Jan 2023 12:55:03 -0000 Subject: [Python-checkins] gh-100689: Revert "bpo-41798: pyexpat: Allocate the expat_CAPI on the heap memory (GH-24061)" (#100745) Message-ID: https://github.com/python/cpython/commit/b034fd3e5926c63a681a211087b4c666834c7525 commit: b034fd3e5926c63a681a211087b4c666834c7525 branch: main author: Nikita Sobolev committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-08T18:24:40+05:30 summary: gh-100689: Revert "bpo-41798: pyexpat: Allocate the expat_CAPI on the heap memory (GH-24061)" (#100745) * gh-100689: Revert "bpo-41798: pyexpat: Allocate the expat_CAPI on the heap memory (GH-24061)" This reverts commit 7c83eaa536d2f436ae46211ca48692f576c732f0. files: A Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst M Modules/pyexpat.c diff --git a/Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst b/Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst new file mode 100644 index 000000000000..09aeab7bceaa --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst @@ -0,0 +1 @@ +Fix crash in :mod:`pyexpat` by statically allocating ``PyExpat_CAPI`` capsule. diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index 2440798bff7e..63a3392d5efe 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -1878,13 +1878,6 @@ add_features(PyObject *mod) } #endif -static void -pyexpat_destructor(PyObject *op) -{ - void *p = PyCapsule_GetPointer(op, PyExpat_CAPSULE_NAME); - PyMem_Free(p); -} - static int pyexpat_exec(PyObject *mod) { @@ -1972,46 +1965,40 @@ pyexpat_exec(PyObject *mod) MYCONST(XML_PARAM_ENTITY_PARSING_ALWAYS); #undef MYCONST - struct PyExpat_CAPI *capi = PyMem_Calloc(1, sizeof(struct PyExpat_CAPI)); - if (capi == NULL) { - PyErr_NoMemory(); - return -1; - } + static struct PyExpat_CAPI capi; /* initialize pyexpat dispatch table */ - capi->size = sizeof(*capi); - capi->magic = PyExpat_CAPI_MAGIC; - capi->MAJOR_VERSION = XML_MAJOR_VERSION; - capi->MINOR_VERSION = XML_MINOR_VERSION; - capi->MICRO_VERSION = XML_MICRO_VERSION; - capi->ErrorString = XML_ErrorString; - capi->GetErrorCode = XML_GetErrorCode; - capi->GetErrorColumnNumber = XML_GetErrorColumnNumber; - capi->GetErrorLineNumber = XML_GetErrorLineNumber; - capi->Parse = XML_Parse; - capi->ParserCreate_MM = XML_ParserCreate_MM; - capi->ParserFree = XML_ParserFree; - capi->SetCharacterDataHandler = XML_SetCharacterDataHandler; - capi->SetCommentHandler = XML_SetCommentHandler; - capi->SetDefaultHandlerExpand = XML_SetDefaultHandlerExpand; - capi->SetElementHandler = XML_SetElementHandler; - capi->SetNamespaceDeclHandler = XML_SetNamespaceDeclHandler; - capi->SetProcessingInstructionHandler = XML_SetProcessingInstructionHandler; - capi->SetUnknownEncodingHandler = XML_SetUnknownEncodingHandler; - capi->SetUserData = XML_SetUserData; - capi->SetStartDoctypeDeclHandler = XML_SetStartDoctypeDeclHandler; - capi->SetEncoding = XML_SetEncoding; - capi->DefaultUnknownEncodingHandler = PyUnknownEncodingHandler; + capi.size = sizeof(capi); + capi.magic = PyExpat_CAPI_MAGIC; + capi.MAJOR_VERSION = XML_MAJOR_VERSION; + capi.MINOR_VERSION = XML_MINOR_VERSION; + capi.MICRO_VERSION = XML_MICRO_VERSION; + capi.ErrorString = XML_ErrorString; + capi.GetErrorCode = XML_GetErrorCode; + capi.GetErrorColumnNumber = XML_GetErrorColumnNumber; + capi.GetErrorLineNumber = XML_GetErrorLineNumber; + capi.Parse = XML_Parse; + capi.ParserCreate_MM = XML_ParserCreate_MM; + capi.ParserFree = XML_ParserFree; + capi.SetCharacterDataHandler = XML_SetCharacterDataHandler; + capi.SetCommentHandler = XML_SetCommentHandler; + capi.SetDefaultHandlerExpand = XML_SetDefaultHandlerExpand; + capi.SetElementHandler = XML_SetElementHandler; + capi.SetNamespaceDeclHandler = XML_SetNamespaceDeclHandler; + capi.SetProcessingInstructionHandler = XML_SetProcessingInstructionHandler; + capi.SetUnknownEncodingHandler = XML_SetUnknownEncodingHandler; + capi.SetUserData = XML_SetUserData; + capi.SetStartDoctypeDeclHandler = XML_SetStartDoctypeDeclHandler; + capi.SetEncoding = XML_SetEncoding; + capi.DefaultUnknownEncodingHandler = PyUnknownEncodingHandler; #if XML_COMBINED_VERSION >= 20100 - capi->SetHashSalt = XML_SetHashSalt; + capi.SetHashSalt = XML_SetHashSalt; #else - capi->SetHashSalt = NULL; + capi.SetHashSalt = NULL; #endif /* export using capsule */ - PyObject *capi_object = PyCapsule_New(capi, PyExpat_CAPSULE_NAME, - pyexpat_destructor); + PyObject *capi_object = PyCapsule_New(&capi, PyExpat_CAPSULE_NAME, NULL); if (capi_object == NULL) { - PyMem_Free(capi); return -1; } From webhook-mailer at python.org Sun Jan 8 08:19:43 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 08 Jan 2023 13:19:43 -0000 Subject: [Python-checkins] gh-100689: Revert "bpo-41798: pyexpat: Allocate the expat_CAPI on the heap memory (GH-24061)" (GH-100745) Message-ID: https://github.com/python/cpython/commit/6c7e32f6a82fac22d52b4b27330286bd8982cc75 commit: 6c7e32f6a82fac22d52b4b27330286bd8982cc75 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-08T05:19:37-08:00 summary: gh-100689: Revert "bpo-41798: pyexpat: Allocate the expat_CAPI on the heap memory (GH-24061)" (GH-100745) * gh-100689: Revert "bpo-41798: pyexpat: Allocate the expat_CAPI on the heap memory (GH-24061)" This reverts commit 7c83eaa536d2f436ae46211ca48692f576c732f0. (cherry picked from commit b034fd3e5926c63a681a211087b4c666834c7525) Co-authored-by: Nikita Sobolev files: A Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst M Modules/pyexpat.c diff --git a/Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst b/Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst new file mode 100644 index 000000000000..09aeab7bceaa --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst @@ -0,0 +1 @@ +Fix crash in :mod:`pyexpat` by statically allocating ``PyExpat_CAPI`` capsule. diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index a9713422220a..a74d8046c487 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -1886,13 +1886,6 @@ add_features(PyObject *mod) } #endif -static void -pyexpat_destructor(PyObject *op) -{ - void *p = PyCapsule_GetPointer(op, PyExpat_CAPSULE_NAME); - PyMem_Free(p); -} - static int pyexpat_exec(PyObject *mod) { @@ -1980,46 +1973,40 @@ pyexpat_exec(PyObject *mod) MYCONST(XML_PARAM_ENTITY_PARSING_ALWAYS); #undef MYCONST - struct PyExpat_CAPI *capi = PyMem_Calloc(1, sizeof(struct PyExpat_CAPI)); - if (capi == NULL) { - PyErr_NoMemory(); - return -1; - } + static struct PyExpat_CAPI capi; /* initialize pyexpat dispatch table */ - capi->size = sizeof(*capi); - capi->magic = PyExpat_CAPI_MAGIC; - capi->MAJOR_VERSION = XML_MAJOR_VERSION; - capi->MINOR_VERSION = XML_MINOR_VERSION; - capi->MICRO_VERSION = XML_MICRO_VERSION; - capi->ErrorString = XML_ErrorString; - capi->GetErrorCode = XML_GetErrorCode; - capi->GetErrorColumnNumber = XML_GetErrorColumnNumber; - capi->GetErrorLineNumber = XML_GetErrorLineNumber; - capi->Parse = XML_Parse; - capi->ParserCreate_MM = XML_ParserCreate_MM; - capi->ParserFree = XML_ParserFree; - capi->SetCharacterDataHandler = XML_SetCharacterDataHandler; - capi->SetCommentHandler = XML_SetCommentHandler; - capi->SetDefaultHandlerExpand = XML_SetDefaultHandlerExpand; - capi->SetElementHandler = XML_SetElementHandler; - capi->SetNamespaceDeclHandler = XML_SetNamespaceDeclHandler; - capi->SetProcessingInstructionHandler = XML_SetProcessingInstructionHandler; - capi->SetUnknownEncodingHandler = XML_SetUnknownEncodingHandler; - capi->SetUserData = XML_SetUserData; - capi->SetStartDoctypeDeclHandler = XML_SetStartDoctypeDeclHandler; - capi->SetEncoding = XML_SetEncoding; - capi->DefaultUnknownEncodingHandler = PyUnknownEncodingHandler; + capi.size = sizeof(capi); + capi.magic = PyExpat_CAPI_MAGIC; + capi.MAJOR_VERSION = XML_MAJOR_VERSION; + capi.MINOR_VERSION = XML_MINOR_VERSION; + capi.MICRO_VERSION = XML_MICRO_VERSION; + capi.ErrorString = XML_ErrorString; + capi.GetErrorCode = XML_GetErrorCode; + capi.GetErrorColumnNumber = XML_GetErrorColumnNumber; + capi.GetErrorLineNumber = XML_GetErrorLineNumber; + capi.Parse = XML_Parse; + capi.ParserCreate_MM = XML_ParserCreate_MM; + capi.ParserFree = XML_ParserFree; + capi.SetCharacterDataHandler = XML_SetCharacterDataHandler; + capi.SetCommentHandler = XML_SetCommentHandler; + capi.SetDefaultHandlerExpand = XML_SetDefaultHandlerExpand; + capi.SetElementHandler = XML_SetElementHandler; + capi.SetNamespaceDeclHandler = XML_SetNamespaceDeclHandler; + capi.SetProcessingInstructionHandler = XML_SetProcessingInstructionHandler; + capi.SetUnknownEncodingHandler = XML_SetUnknownEncodingHandler; + capi.SetUserData = XML_SetUserData; + capi.SetStartDoctypeDeclHandler = XML_SetStartDoctypeDeclHandler; + capi.SetEncoding = XML_SetEncoding; + capi.DefaultUnknownEncodingHandler = PyUnknownEncodingHandler; #if XML_COMBINED_VERSION >= 20100 - capi->SetHashSalt = XML_SetHashSalt; + capi.SetHashSalt = XML_SetHashSalt; #else - capi->SetHashSalt = NULL; + capi.SetHashSalt = NULL; #endif /* export using capsule */ - PyObject *capi_object = PyCapsule_New(capi, PyExpat_CAPSULE_NAME, - pyexpat_destructor); + PyObject *capi_object = PyCapsule_New(&capi, PyExpat_CAPSULE_NAME, NULL); if (capi_object == NULL) { - PyMem_Free(capi); return -1; } From webhook-mailer at python.org Sun Jan 8 08:35:45 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sun, 08 Jan 2023 13:35:45 -0000 Subject: [Python-checkins] [3.10] gh-100689: Revert "bpo-41798: pyexpat: Allocate the expat_CAPI on the heap memory (GH-24061)" (GH-100745) (#100847) Message-ID: https://github.com/python/cpython/commit/0e00bce5619003009255c638998d9878eedac106 commit: 0e00bce5619003009255c638998d9878eedac106 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-08T19:05:40+05:30 summary: [3.10] gh-100689: Revert "bpo-41798: pyexpat: Allocate the expat_CAPI on the heap memory (GH-24061)" (GH-100745) (#100847) gh-100689: Revert "bpo-41798: pyexpat: Allocate the expat_CAPI on the heap memory (GH-24061)" (GH-100745) * gh-100689: Revert "bpo-41798: pyexpat: Allocate the expat_CAPI on the heap memory (GH-24061)" This reverts commit 7c83eaa536d2f436ae46211ca48692f576c732f0. (cherry picked from commit b034fd3e5926c63a681a211087b4c666834c7525) Co-authored-by: Nikita Sobolev Co-authored-by: Nikita Sobolev files: A Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst M Modules/pyexpat.c diff --git a/Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst b/Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst new file mode 100644 index 000000000000..09aeab7bceaa --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst @@ -0,0 +1 @@ +Fix crash in :mod:`pyexpat` by statically allocating ``PyExpat_CAPI`` capsule. diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index b3d9bdda7e7a..4d7196a8348f 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -1843,13 +1843,6 @@ add_features(PyObject *mod) } #endif -static void -pyexpat_destructor(PyObject *op) -{ - void *p = PyCapsule_GetPointer(op, PyExpat_CAPSULE_NAME); - PyMem_Free(p); -} - static int pyexpat_exec(PyObject *mod) { @@ -1933,46 +1926,40 @@ pyexpat_exec(PyObject *mod) MYCONST(XML_PARAM_ENTITY_PARSING_ALWAYS); #undef MYCONST - struct PyExpat_CAPI *capi = PyMem_Calloc(1, sizeof(struct PyExpat_CAPI)); - if (capi == NULL) { - PyErr_NoMemory(); - return -1; - } + static struct PyExpat_CAPI capi; /* initialize pyexpat dispatch table */ - capi->size = sizeof(*capi); - capi->magic = PyExpat_CAPI_MAGIC; - capi->MAJOR_VERSION = XML_MAJOR_VERSION; - capi->MINOR_VERSION = XML_MINOR_VERSION; - capi->MICRO_VERSION = XML_MICRO_VERSION; - capi->ErrorString = XML_ErrorString; - capi->GetErrorCode = XML_GetErrorCode; - capi->GetErrorColumnNumber = XML_GetErrorColumnNumber; - capi->GetErrorLineNumber = XML_GetErrorLineNumber; - capi->Parse = XML_Parse; - capi->ParserCreate_MM = XML_ParserCreate_MM; - capi->ParserFree = XML_ParserFree; - capi->SetCharacterDataHandler = XML_SetCharacterDataHandler; - capi->SetCommentHandler = XML_SetCommentHandler; - capi->SetDefaultHandlerExpand = XML_SetDefaultHandlerExpand; - capi->SetElementHandler = XML_SetElementHandler; - capi->SetNamespaceDeclHandler = XML_SetNamespaceDeclHandler; - capi->SetProcessingInstructionHandler = XML_SetProcessingInstructionHandler; - capi->SetUnknownEncodingHandler = XML_SetUnknownEncodingHandler; - capi->SetUserData = XML_SetUserData; - capi->SetStartDoctypeDeclHandler = XML_SetStartDoctypeDeclHandler; - capi->SetEncoding = XML_SetEncoding; - capi->DefaultUnknownEncodingHandler = PyUnknownEncodingHandler; + capi.size = sizeof(capi); + capi.magic = PyExpat_CAPI_MAGIC; + capi.MAJOR_VERSION = XML_MAJOR_VERSION; + capi.MINOR_VERSION = XML_MINOR_VERSION; + capi.MICRO_VERSION = XML_MICRO_VERSION; + capi.ErrorString = XML_ErrorString; + capi.GetErrorCode = XML_GetErrorCode; + capi.GetErrorColumnNumber = XML_GetErrorColumnNumber; + capi.GetErrorLineNumber = XML_GetErrorLineNumber; + capi.Parse = XML_Parse; + capi.ParserCreate_MM = XML_ParserCreate_MM; + capi.ParserFree = XML_ParserFree; + capi.SetCharacterDataHandler = XML_SetCharacterDataHandler; + capi.SetCommentHandler = XML_SetCommentHandler; + capi.SetDefaultHandlerExpand = XML_SetDefaultHandlerExpand; + capi.SetElementHandler = XML_SetElementHandler; + capi.SetNamespaceDeclHandler = XML_SetNamespaceDeclHandler; + capi.SetProcessingInstructionHandler = XML_SetProcessingInstructionHandler; + capi.SetUnknownEncodingHandler = XML_SetUnknownEncodingHandler; + capi.SetUserData = XML_SetUserData; + capi.SetStartDoctypeDeclHandler = XML_SetStartDoctypeDeclHandler; + capi.SetEncoding = XML_SetEncoding; + capi.DefaultUnknownEncodingHandler = PyUnknownEncodingHandler; #if XML_COMBINED_VERSION >= 20100 - capi->SetHashSalt = XML_SetHashSalt; + capi.SetHashSalt = XML_SetHashSalt; #else - capi->SetHashSalt = NULL; + capi.SetHashSalt = NULL; #endif /* export using capsule */ - PyObject *capi_object = PyCapsule_New(capi, PyExpat_CAPSULE_NAME, - pyexpat_destructor); + PyObject *capi_object = PyCapsule_New(&capi, PyExpat_CAPSULE_NAME, NULL); if (capi_object == NULL) { - PyMem_Free(capi); return -1; } From webhook-mailer at python.org Sun Jan 8 08:51:25 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sun, 08 Jan 2023 13:51:25 -0000 Subject: [Python-checkins] GH-90829: Fix empty iterable error message in min/max (#31181) Message-ID: https://github.com/python/cpython/commit/0741da8d28790cebe94a4392af37d63b1510080a commit: 0741da8d28790cebe94a4392af37d63b1510080a branch: main author: Nnarol committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-08T19:21:20+05:30 summary: GH-90829: Fix empty iterable error message in min/max (#31181) files: M Lib/test/test_builtin.py M Python/bltinmodule.c diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index c65600483258..9e19af0ae90f 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1155,7 +1155,11 @@ def test_max(self): max() self.assertRaises(TypeError, max, 42) - self.assertRaises(ValueError, max, ()) + with self.assertRaisesRegex( + ValueError, + r'max\(\) iterable argument is empty' + ): + max(()) class BadSeq: def __getitem__(self, index): raise ValueError @@ -1214,7 +1218,11 @@ def test_min(self): min() self.assertRaises(TypeError, min, 42) - self.assertRaises(ValueError, min, ()) + with self.assertRaisesRegex( + ValueError, + r'min\(\) iterable argument is empty' + ): + min(()) class BadSeq: def __getitem__(self, index): raise ValueError diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 599cc18361ac..f97dd67269a2 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1814,7 +1814,7 @@ min_max(PyObject *args, PyObject *kwds, int op) maxitem = Py_NewRef(defaultval); } else { PyErr_Format(PyExc_ValueError, - "%s() arg is an empty sequence", name); + "%s() iterable argument is empty", name); } } else From webhook-mailer at python.org Sun Jan 8 10:13:31 2023 From: webhook-mailer at python.org (benjaminp) Date: Sun, 08 Jan 2023 15:13:31 -0000 Subject: [Python-checkins] Update copyright years to 2023. (gh-100848) Message-ID: https://github.com/python/cpython/commit/11f99323c2ae0ec428c370a335695e3d8d4afc1d commit: 11f99323c2ae0ec428c370a335695e3d8d4afc1d branch: main author: Benjamin Peterson committer: benjaminp date: 2023-01-08T09:13:25-06:00 summary: Update copyright years to 2023. (gh-100848) files: M Doc/copyright.rst M Doc/license.rst M LICENSE M Mac/IDLE/IDLE.app/Contents/Info.plist M Mac/PythonLauncher/Info.plist.in M Mac/Resources/app/Info.plist.in M PC/python_ver_rc.h M Python/getcopyright.c M README.rst diff --git a/Doc/copyright.rst b/Doc/copyright.rst index e64a49328b47..9b71683155ee 100644 --- a/Doc/copyright.rst +++ b/Doc/copyright.rst @@ -4,7 +4,7 @@ Copyright Python and this documentation is: -Copyright ? 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. Copyright ? 2000 BeOpen.com. All rights reserved. diff --git a/Doc/license.rst b/Doc/license.rst index a934c60698f0..005d048b6eb2 100644 --- a/Doc/license.rst +++ b/Doc/license.rst @@ -100,7 +100,7 @@ PSF LICENSE AGREEMENT FOR PYTHON |release| analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python |release| alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of - copyright, i.e., "Copyright ? 2001-2022 Python Software Foundation; All Rights + copyright, i.e., "Copyright ? 2001-2023 Python Software Foundation; All Rights Reserved" are retained in Python |release| alone or in any derivative version prepared by Licensee. diff --git a/LICENSE b/LICENSE index 9838d443f9fc..f26bcf4d2de6 100644 --- a/LICENSE +++ b/LICENSE @@ -84,7 +84,7 @@ analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation; +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. diff --git a/Mac/IDLE/IDLE.app/Contents/Info.plist b/Mac/IDLE/IDLE.app/Contents/Info.plist index 799a164cbdf2..fea06d463249 100644 --- a/Mac/IDLE/IDLE.app/Contents/Info.plist +++ b/Mac/IDLE/IDLE.app/Contents/Info.plist @@ -37,7 +37,7 @@ CFBundleExecutable IDLE CFBundleGetInfoString - %version%, ? 2001-2022 Python Software Foundation + %version%, ? 2001-2023 Python Software Foundation CFBundleIconFile IDLE.icns CFBundleIdentifier diff --git a/Mac/PythonLauncher/Info.plist.in b/Mac/PythonLauncher/Info.plist.in index dec0a2eaf5c5..b7cddac0729f 100644 --- a/Mac/PythonLauncher/Info.plist.in +++ b/Mac/PythonLauncher/Info.plist.in @@ -40,9 +40,9 @@ CFBundleExecutable Python Launcher NSHumanReadableCopyright - Copyright ? 2001-2022 Python Software Foundation + Copyright ? 2001-2023 Python Software Foundation CFBundleGetInfoString - %VERSION%, ? 2001-2022 Python Software Foundation + %VERSION%, ? 2001-2023 Python Software Foundation CFBundleIconFile PythonLauncher.icns CFBundleIdentifier diff --git a/Mac/Resources/app/Info.plist.in b/Mac/Resources/app/Info.plist.in index 84843b734e3d..c063192a05a2 100644 --- a/Mac/Resources/app/Info.plist.in +++ b/Mac/Resources/app/Info.plist.in @@ -37,7 +37,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString - %version%, (c) 2001-2022 Python Software Foundation. + %version%, (c) 2001-2023 Python Software Foundation. CFBundleName Python CFBundlePackageType diff --git a/PC/python_ver_rc.h b/PC/python_ver_rc.h index e6c1d2437041..5b55b810cd21 100644 --- a/PC/python_ver_rc.h +++ b/PC/python_ver_rc.h @@ -5,7 +5,7 @@ #include "winver.h" #define PYTHON_COMPANY "Python Software Foundation" -#define PYTHON_COPYRIGHT "Copyright \xA9 2001-2022 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." +#define PYTHON_COPYRIGHT "Copyright \xA9 2001-2023 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." #define MS_WINDOWS #include "modsupport.h" diff --git a/Python/getcopyright.c b/Python/getcopyright.c index 88d1d0536253..c1f1aad9b845 100644 --- a/Python/getcopyright.c +++ b/Python/getcopyright.c @@ -4,7 +4,7 @@ static const char cprt[] = "\ -Copyright (c) 2001-2022 Python Software Foundation.\n\ +Copyright (c) 2001-2023 Python Software Foundation.\n\ All Rights Reserved.\n\ \n\ Copyright (c) 2000 BeOpen.com.\n\ diff --git a/README.rst b/README.rst index ab9d3a6ea71c..9b565ce522f1 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,7 @@ This is Python version 3.12.0 alpha 3 :target: https://discuss.python.org/ -Copyright ? 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. See the end of this file for further copyright and license information. From webhook-mailer at python.org Sun Jan 8 14:38:41 2023 From: webhook-mailer at python.org (rhettinger) Date: Sun, 08 Jan 2023 19:38:41 -0000 Subject: [Python-checkins] GH-100485: Tweaks to sumprod() (GH-100857) Message-ID: https://github.com/python/cpython/commit/b139bcd8922b47bf77c75d5f6704cc9147852546 commit: b139bcd8922b47bf77c75d5f6704cc9147852546 branch: main author: Raymond Hettinger committer: rhettinger date: 2023-01-08T13:38:24-06:00 summary: GH-100485: Tweaks to sumprod() (GH-100857) files: M Doc/whatsnew/3.12.rst M Lib/test/test_math.py M Modules/mathmodule.c diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 2f50ece4dab3..b882bb607f91 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -262,6 +262,12 @@ dis :data:`~dis.hasarg` collection instead. (Contributed by Irit Katriel in :gh:`94216`.) +math +---- + +* Added :func:`math.sumprod` for computing a sum of products. + (Contributed by Raymond Hettinger in :gh:`100485`.) + os -- diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 65fe169671ea..b8ac8f32055d 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1294,6 +1294,7 @@ def test_sumprod_accuracy(self): self.assertEqual(sumprod([0.1] * 20, [True, False] * 10), 1.0) self.assertEqual(sumprod([1.0, 10E100, 1.0, -10E100], [1.0]*4), 2.0) + @support.requires_resource('cpu') def test_sumprod_stress(self): sumprod = math.sumprod product = itertools.product diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 9545ad2a99eb..11e815c6f17e 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2832,7 +2832,7 @@ long_add_would_overflow(long a, long b) } /* -Double length extended precision floating point arithmetic +Double and triple length extended precision floating point arithmetic based on ideas from three sources: Improved Kahan?Babu?ka algorithm by Arnold Neumaier @@ -2845,22 +2845,22 @@ based on ideas from three sources: Ultimately Fast Accurate Summation by Siegfried M. Rump https://www.tuhh.de/ti3/paper/rump/Ru08b.pdf -The double length routines allow for quite a bit of instruction -level parallelism. On a 3.22 Ghz Apple M1 Max, the incremental -cost of increasing the input vector size by one is 6.0 nsec. +Double length functions: +* dl_split() exact split of a C double into two half precision components. +* dl_mul() exact multiplication of two C doubles. -dl_zero() returns an extended precision zero -dl_split() exactly splits a double into two half precision components. -dl_add() performs compensated summation to keep a running total. -dl_mul() implements lossless multiplication of doubles. -dl_fma() implements an extended precision fused-multiply-add. -dl_to_d() converts from extended precision to double precision. +Triple length functions and constant: +* tl_zero is a triple length zero for starting or resetting an accumulation. +* tl_add() compensated addition of a C double to a triple length number. +* tl_fma() performs a triple length fused-multiply-add. +* tl_to_d() converts from triple length number back to a C double. */ typedef struct{ double hi; double lo; } DoubleLength; +typedef struct{ double hi; double lo; double tiny; } TripleLength; -static const DoubleLength dl_zero = {0.0, 0.0}; +static const TripleLength tl_zero = {0.0, 0.0, 0.0}; static inline DoubleLength twosum(double a, double b) @@ -2874,11 +2874,20 @@ twosum(double a, double b) return (DoubleLength) {s, t}; } -static inline DoubleLength -dl_add(DoubleLength total, double x) +static inline TripleLength +tl_add(TripleLength total, double x) { - DoubleLength s = twosum(total.hi, x); - return (DoubleLength) {s.hi, total.lo + s.lo}; + /* Input: x total.hi total.lo total.tiny + |--- twosum ---| + s.hi s.lo + |--- twosum ---| + t.hi t.lo + |--- single sum ---| + Output: s.hi t.hi tiny + */ + DoubleLength s = twosum(x, total.hi); + DoubleLength t = twosum(s.lo, total.lo); + return (TripleLength) {s.hi, t.hi, t.lo + total.tiny}; } static inline DoubleLength @@ -2902,18 +2911,18 @@ dl_mul(double x, double y) return (DoubleLength) {z, zz}; } -static inline DoubleLength -dl_fma(DoubleLength total, double p, double q) +static inline TripleLength +tl_fma(TripleLength total, double p, double q) { DoubleLength product = dl_mul(p, q); - total = dl_add(total, product.hi); - return dl_add(total, product.lo); + total = tl_add(total, product.hi); + return tl_add(total, product.lo); } static inline double -dl_to_d(DoubleLength total) +tl_to_d(TripleLength total) { - return total.hi + total.lo; + return total.tiny + total.lo + total.hi; } /*[clinic input] @@ -2944,7 +2953,7 @@ math_sumprod_impl(PyObject *module, PyObject *p, PyObject *q) bool int_path_enabled = true, int_total_in_use = false; bool flt_path_enabled = true, flt_total_in_use = false; long int_total = 0; - DoubleLength flt_total = dl_zero; + TripleLength flt_total = tl_zero; p_it = PyObject_GetIter(p); if (p_it == NULL) { @@ -3079,7 +3088,7 @@ math_sumprod_impl(PyObject *module, PyObject *p, PyObject *q) } else { goto finalize_flt_path; } - DoubleLength new_flt_total = dl_fma(flt_total, flt_p, flt_q); + TripleLength new_flt_total = tl_fma(flt_total, flt_p, flt_q); if (isfinite(new_flt_total.hi)) { flt_total = new_flt_total; flt_total_in_use = true; @@ -3093,7 +3102,7 @@ math_sumprod_impl(PyObject *module, PyObject *p, PyObject *q) // We're finished, overflowed, have a non-float, or got a non-finite value flt_path_enabled = false; if (flt_total_in_use) { - term_i = PyFloat_FromDouble(dl_to_d(flt_total)); + term_i = PyFloat_FromDouble(tl_to_d(flt_total)); if (term_i == NULL) { goto err_exit; } @@ -3104,7 +3113,7 @@ math_sumprod_impl(PyObject *module, PyObject *p, PyObject *q) Py_SETREF(total, new_total); new_total = NULL; Py_CLEAR(term_i); - flt_total = dl_zero; + flt_total = tl_zero; flt_total_in_use = false; } } From webhook-mailer at python.org Sun Jan 8 14:40:21 2023 From: webhook-mailer at python.org (mdickinson) Date: Sun, 08 Jan 2023 19:40:21 -0000 Subject: [Python-checkins] gh-100833: Remove 'volatile' qualifiers in fsum algorithm (#100845) Message-ID: https://github.com/python/cpython/commit/87d3bd0e02cddc415a42573052110eb9301d2c3d commit: 87d3bd0e02cddc415a42573052110eb9301d2c3d branch: main author: Mark Dickinson committer: mdickinson date: 2023-01-08T19:40:15Z summary: gh-100833: Remove 'volatile' qualifiers in fsum algorithm (#100845) This PR removes the `volatile` qualifier on various intermediate quantities in the `math.fsum` implementation, and updates the notes preceding the algorithm accordingly (as well as fixing some of the exsting notes). See the linked issue #100833 for discussion. files: A Misc/NEWS.d/next/Library/2023-01-08-12-10-17.gh-issue-100833.f6cT7E.rst M Modules/mathmodule.c diff --git a/Misc/NEWS.d/next/Library/2023-01-08-12-10-17.gh-issue-100833.f6cT7E.rst b/Misc/NEWS.d/next/Library/2023-01-08-12-10-17.gh-issue-100833.f6cT7E.rst new file mode 100644 index 000000000000..b572e92d9871 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-08-12-10-17.gh-issue-100833.f6cT7E.rst @@ -0,0 +1 @@ +Speed up :func:`math.fsum` by removing defensive ``volatile`` qualifiers. diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 11e815c6f17e..9e4c6fded933 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -1358,30 +1358,30 @@ FUNC1(tanh, tanh, 0, Dickinson's post at . See those links for more details, proofs and other references. - Note 1: IEEE 754R floating point semantics are assumed, - but the current implementation does not re-establish special - value semantics across iterations (i.e. handling -Inf + Inf). + Note 1: IEEE 754 floating-point semantics with a rounding mode of + roundTiesToEven are assumed. - Note 2: No provision is made for intermediate overflow handling; - therefore, sum([1e+308, 1e-308, 1e+308]) returns 1e+308 while - sum([1e+308, 1e+308, 1e-308]) raises an OverflowError due to the + Note 2: No provision is made for intermediate overflow handling; + therefore, fsum([1e+308, -1e+308, 1e+308]) returns 1e+308 while + fsum([1e+308, 1e+308, -1e+308]) raises an OverflowError due to the overflow of the first partial sum. - Note 3: The intermediate values lo, yr, and hi are declared volatile so - aggressive compilers won't algebraically reduce lo to always be exactly 0.0. - Also, the volatile declaration forces the values to be stored in memory as - regular doubles instead of extended long precision (80-bit) values. This - prevents double rounding because any addition or subtraction of two doubles - can be resolved exactly into double-sized hi and lo values. As long as the - hi value gets forced into a double before yr and lo are computed, the extra - bits in downstream extended precision operations (x87 for example) will be - exactly zero and therefore can be losslessly stored back into a double, - thereby preventing double rounding. - - Note 4: A similar implementation is in Modules/cmathmodule.c. - Be sure to update both when making changes. - - Note 5: The signature of math.fsum() differs from builtins.sum() + Note 3: The algorithm has two potential sources of fragility. First, C + permits arithmetic operations on `double`s to be performed in an + intermediate format whose range and precision may be greater than those of + `double` (see for example C99 ?5.2.4.2.2, paragraph 8). This can happen for + example on machines using the now largely historical x87 FPUs. In this case, + `fsum` can produce incorrect results. If `FLT_EVAL_METHOD` is `0` or `1`, or + `FLT_EVAL_METHOD` is `2` and `long double` is identical to `double`, then we + should be safe from this source of errors. Second, an aggressively + optimizing compiler can re-associate operations so that (for example) the + statement `yr = hi - x;` is treated as `yr = (x + y) - x` and then + re-associated as `yr = y + (x - x)`, giving `y = yr` and `lo = 0.0`. That + re-association would be in violation of the C standard, and should not occur + except possibly in the presence of unsafe optimizations (e.g., -ffast-math, + -fassociative-math). Such optimizations should be avoided for this module. + + Note 4: The signature of math.fsum() differs from builtins.sum() because the start argument doesn't make sense in the context of accurate summation. Since the partials table is collapsed before returning a result, sum(seq2, start=sum(seq1)) may not equal the @@ -1467,7 +1467,7 @@ math_fsum(PyObject *module, PyObject *seq) Py_ssize_t i, j, n = 0, m = NUM_PARTIALS; double x, y, t, ps[NUM_PARTIALS], *p = ps; double xsave, special_sum = 0.0, inf_sum = 0.0; - volatile double hi, yr, lo; + double hi, yr, lo; iter = PyObject_GetIter(seq); if (iter == NULL) From webhook-mailer at python.org Sun Jan 8 14:40:40 2023 From: webhook-mailer at python.org (rhettinger) Date: Sun, 08 Jan 2023 19:40:40 -0000 Subject: [Python-checkins] GH-100805: Support numpy.array() in random.choice(). (GH-100830) Message-ID: https://github.com/python/cpython/commit/9a68ff12c3e647a4f8dd935919ae296593770a6b commit: 9a68ff12c3e647a4f8dd935919ae296593770a6b branch: main author: Raymond Hettinger committer: rhettinger date: 2023-01-08T13:40:35-06:00 summary: GH-100805: Support numpy.array() in random.choice(). (GH-100830) files: A Misc/NEWS.d/next/Library/2023-01-07-15-13-47.gh-issue-100805.05rBz9.rst M Lib/random.py M Lib/test/test_random.py diff --git a/Lib/random.py b/Lib/random.py index e60b7294b6dd..1c9e1a48b662 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -336,7 +336,10 @@ def randint(self, a, b): def choice(self, seq): """Choose a random element from a non-empty sequence.""" - if not seq: + + # As an accommodation for NumPy, we don't use "if not seq" + # because bool(numpy.array()) raises a ValueError. + if not len(seq): raise IndexError('Cannot choose from an empty sequence') return seq[self._randbelow(len(seq))] diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 67de54c7db8b..50bea7be6d54 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -111,6 +111,21 @@ def test_choice(self): self.assertEqual(choice([50]), 50) self.assertIn(choice([25, 75]), [25, 75]) + def test_choice_with_numpy(self): + # Accommodation for NumPy arrays which have disabled __bool__(). + # See: https://github.com/python/cpython/issues/100805 + choice = self.gen.choice + + class NA(list): + "Simulate numpy.array() behavior" + def __bool__(self): + raise RuntimeError + + with self.assertRaises(IndexError): + choice(NA([])) + self.assertEqual(choice(NA([50])), 50) + self.assertIn(choice(NA([25, 75])), [25, 75]) + def test_sample(self): # For the entire allowable range of 0 <= k <= N, validate that # the sample is of the correct length and contains only unique items diff --git a/Misc/NEWS.d/next/Library/2023-01-07-15-13-47.gh-issue-100805.05rBz9.rst b/Misc/NEWS.d/next/Library/2023-01-07-15-13-47.gh-issue-100805.05rBz9.rst new file mode 100644 index 000000000000..4424d7cff21b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-07-15-13-47.gh-issue-100805.05rBz9.rst @@ -0,0 +1,2 @@ +Modify :func:`random.choice` implementation to once again work with NumPy +arrays. From webhook-mailer at python.org Sun Jan 8 15:05:14 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 08 Jan 2023 20:05:14 -0000 Subject: [Python-checkins] GH-100805: Support numpy.array() in random.choice(). (GH-100830) Message-ID: https://github.com/python/cpython/commit/6184b800ee403d8c8c93b94fed1dbc3e0bade336 commit: 6184b800ee403d8c8c93b94fed1dbc3e0bade336 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-08T12:04:49-08:00 summary: GH-100805: Support numpy.array() in random.choice(). (GH-100830) (cherry picked from commit 9a68ff12c3e647a4f8dd935919ae296593770a6b) Co-authored-by: Raymond Hettinger files: A Misc/NEWS.d/next/Library/2023-01-07-15-13-47.gh-issue-100805.05rBz9.rst M Lib/random.py M Lib/test/test_random.py diff --git a/Lib/random.py b/Lib/random.py index f94616e048c5..22dcb4d3991c 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -366,7 +366,10 @@ def randint(self, a, b): def choice(self, seq): """Choose a random element from a non-empty sequence.""" - if not seq: + + # As an accommodation for NumPy, we don't use "if not seq" + # because bool(numpy.array()) raises a ValueError. + if not len(seq): raise IndexError('Cannot choose from an empty sequence') return seq[self._randbelow(len(seq))] diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 32e7868ba4de..f32d592ce815 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -111,6 +111,21 @@ def test_choice(self): self.assertEqual(choice([50]), 50) self.assertIn(choice([25, 75]), [25, 75]) + def test_choice_with_numpy(self): + # Accommodation for NumPy arrays which have disabled __bool__(). + # See: https://github.com/python/cpython/issues/100805 + choice = self.gen.choice + + class NA(list): + "Simulate numpy.array() behavior" + def __bool__(self): + raise RuntimeError + + with self.assertRaises(IndexError): + choice(NA([])) + self.assertEqual(choice(NA([50])), 50) + self.assertIn(choice(NA([25, 75])), [25, 75]) + def test_sample(self): # For the entire allowable range of 0 <= k <= N, validate that # the sample is of the correct length and contains only unique items diff --git a/Misc/NEWS.d/next/Library/2023-01-07-15-13-47.gh-issue-100805.05rBz9.rst b/Misc/NEWS.d/next/Library/2023-01-07-15-13-47.gh-issue-100805.05rBz9.rst new file mode 100644 index 000000000000..4424d7cff21b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-07-15-13-47.gh-issue-100805.05rBz9.rst @@ -0,0 +1,2 @@ +Modify :func:`random.choice` implementation to once again work with NumPy +arrays. From webhook-mailer at python.org Sun Jan 8 17:05:33 2023 From: webhook-mailer at python.org (ned-deily) Date: Sun, 08 Jan 2023 22:05:33 -0000 Subject: [Python-checkins] Update additional copyright years to 2023. (GH-100859) Message-ID: https://github.com/python/cpython/commit/8d69828092f734cd9275459173a0f89bde109659 commit: 8d69828092f734cd9275459173a0f89bde109659 branch: main author: Ned Deily committer: ned-deily date: 2023-01-08T17:05:28-05:00 summary: Update additional copyright years to 2023. (GH-100859) files: M Mac/Resources/app/Info.plist.in M Mac/Resources/framework/Info.plist.in diff --git a/Mac/Resources/app/Info.plist.in b/Mac/Resources/app/Info.plist.in index c063192a05a2..4ec828ff176e 100644 --- a/Mac/Resources/app/Info.plist.in +++ b/Mac/Resources/app/Info.plist.in @@ -20,7 +20,7 @@ CFBundleExecutable Python CFBundleGetInfoString - %version%, (c) 2001-2020 Python Software Foundation. + %version%, (c) 2001-2023 Python Software Foundation. CFBundleHelpBookFolder Documentation @@ -55,7 +55,7 @@ NSAppleScriptEnabled NSHumanReadableCopyright - (c) 2001-2020 Python Software Foundation. + (c) 2001-2023 Python Software Foundation. NSHighResolutionCapable diff --git a/Mac/Resources/framework/Info.plist.in b/Mac/Resources/framework/Info.plist.in index 863b45238fe4..e131c205ef0b 100644 --- a/Mac/Resources/framework/Info.plist.in +++ b/Mac/Resources/framework/Info.plist.in @@ -17,9 +17,9 @@ CFBundlePackageType FMWK CFBundleShortVersionString - %VERSION%, (c) 2001-2021 Python Software Foundation. + %VERSION%, (c) 2001-2023 Python Software Foundation. CFBundleLongVersionString - %VERSION%, (c) 2001-2021 Python Software Foundation. + %VERSION%, (c) 2001-2023 Python Software Foundation. CFBundleSignature ???? CFBundleVersion From webhook-mailer at python.org Sun Jan 8 17:51:38 2023 From: webhook-mailer at python.org (emilyemorehouse) Date: Sun, 08 Jan 2023 22:51:38 -0000 Subject: [Python-checkins] gh-87447: Fix walrus comprehension rebind checking (#100581) Message-ID: https://github.com/python/cpython/commit/bc0a686f820d7d298a0b1450b155a717972de0fc commit: bc0a686f820d7d298a0b1450b155a717972de0fc branch: main author: Nikita Sobolev committer: emilyemorehouse date: 2023-01-08T15:51:29-07:00 summary: gh-87447: Fix walrus comprehension rebind checking (#100581) Co-authored-by: Pablo Galindo Salgado Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> files: A Misc/NEWS.d/next/Core and Builtins/2022-12-28-15-02-53.gh-issue-87447.7-aekA.rst M Doc/whatsnew/3.12.rst M Lib/test/test_named_expressions.py M Python/symtable.c diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index b882bb607f91..7d318cac0193 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -182,6 +182,13 @@ Other Language Changes arguments of any type instead of just :class:`bool` and :class:`int`. (Contributed by Serhiy Storchaka in :gh:`60203`.) +* Variables used in the target part of comprehensions that are not stored to + can now be used in assignment expressions (``:=``). + For example, in ``[(b := 1) for a, b.prop in some_iter]``, the assignment to + ``b`` is now allowed. Note that assigning to variables stored to in the target + part of comprehensions (like ``a``) is still disallowed, as per :pep:`572`. + (Contributed by Nikita Sobolev in :gh:`100581`.) + New Modules =========== diff --git a/Lib/test/test_named_expressions.py b/Lib/test/test_named_expressions.py index 20ac2e699f0c..7b2fa844827a 100644 --- a/Lib/test/test_named_expressions.py +++ b/Lib/test/test_named_expressions.py @@ -114,6 +114,69 @@ def test_named_expression_invalid_in_class_body(self): "assignment expression within a comprehension cannot be used in a class body"): exec(code, {}, {}) + def test_named_expression_valid_rebinding_iteration_variable(self): + # This test covers that we can reassign variables + # that are not directly assigned in the + # iterable part of a comprehension. + cases = [ + # Regression tests from https://github.com/python/cpython/issues/87447 + ("Complex expression: c", + "{0}(c := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}"), + ("Complex expression: d", + "{0}(d := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}"), + ("Complex expression: e", + "{0}(e := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}"), + ("Complex expression: f", + "{0}(f := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}"), + ("Complex expression: g", + "{0}(g := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}"), + ("Complex expression: h", + "{0}(h := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}"), + ("Complex expression: i", + "{0}(i := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}"), + ("Complex expression: j", + "{0}(j := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}"), + ] + for test_case, code in cases: + for lpar, rpar in [('(', ')'), ('[', ']'), ('{', '}')]: + code = code.format(lpar, rpar) + with self.subTest(case=test_case, lpar=lpar, rpar=rpar): + # Names used in snippets are not defined, + # but we are fine with it: just must not be a SyntaxError. + # Names used in snippets are not defined, + # but we are fine with it: just must not be a SyntaxError. + with self.assertRaises(NameError): + exec(code, {}) # Module scope + with self.assertRaises(NameError): + exec(code, {}, {}) # Class scope + exec(f"lambda: {code}", {}) # Function scope + + def test_named_expression_invalid_rebinding_iteration_variable(self): + # This test covers that we cannot reassign variables + # that are directly assigned in the iterable part of a comprehension. + cases = [ + # Regression tests from https://github.com/python/cpython/issues/87447 + ("Complex expression: a", "a", + "{0}(a := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}"), + ("Complex expression: b", "b", + "{0}(b := 1) for a, (*b, c[d+e::f(g)], h.i) in j{1}"), + ] + for test_case, target, code in cases: + msg = f"assignment expression cannot rebind comprehension iteration variable '{target}'" + for lpar, rpar in [('(', ')'), ('[', ']'), ('{', '}')]: + code = code.format(lpar, rpar) + with self.subTest(case=test_case, lpar=lpar, rpar=rpar): + # Names used in snippets are not defined, + # but we are fine with it: just must not be a SyntaxError. + # Names used in snippets are not defined, + # but we are fine with it: just must not be a SyntaxError. + with self.assertRaisesRegex(SyntaxError, msg): + exec(code, {}) # Module scope + with self.assertRaisesRegex(SyntaxError, msg): + exec(code, {}, {}) # Class scope + with self.assertRaisesRegex(SyntaxError, msg): + exec(f"lambda: {code}", {}) # Function scope + def test_named_expression_invalid_rebinding_list_comprehension_iteration_variable(self): cases = [ ("Local reuse", 'i', "[i := 0 for i in range(5)]"), @@ -129,7 +192,11 @@ def test_named_expression_invalid_rebinding_list_comprehension_iteration_variabl msg = f"assignment expression cannot rebind comprehension iteration variable '{target}'" with self.subTest(case=case): with self.assertRaisesRegex(SyntaxError, msg): - exec(code, {}, {}) + exec(code, {}) # Module scope + with self.assertRaisesRegex(SyntaxError, msg): + exec(code, {}, {}) # Class scope + with self.assertRaisesRegex(SyntaxError, msg): + exec(f"lambda: {code}", {}) # Function scope def test_named_expression_invalid_rebinding_list_comprehension_inner_loop(self): cases = [ @@ -178,12 +245,21 @@ def test_named_expression_invalid_rebinding_set_comprehension_iteration_variable ("Unreachable reuse", 'i', "{False or (i:=0) for i in range(5)}"), ("Unreachable nested reuse", 'i', "{(i, j) for i in range(5) for j in range(5) if True or (i:=10)}"), + # Regression tests from https://github.com/python/cpython/issues/87447 + ("Complex expression: a", "a", + "{(a := 1) for a, (*b, c[d+e::f(g)], h.i) in j}"), + ("Complex expression: b", "b", + "{(b := 1) for a, (*b, c[d+e::f(g)], h.i) in j}"), ] for case, target, code in cases: msg = f"assignment expression cannot rebind comprehension iteration variable '{target}'" with self.subTest(case=case): with self.assertRaisesRegex(SyntaxError, msg): - exec(code, {}, {}) + exec(code, {}) # Module scope + with self.assertRaisesRegex(SyntaxError, msg): + exec(code, {}, {}) # Class scope + with self.assertRaisesRegex(SyntaxError, msg): + exec(f"lambda: {code}", {}) # Function scope def test_named_expression_invalid_rebinding_set_comprehension_inner_loop(self): cases = [ diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-28-15-02-53.gh-issue-87447.7-aekA.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-28-15-02-53.gh-issue-87447.7-aekA.rst new file mode 100644 index 000000000000..af60acf1103a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-12-28-15-02-53.gh-issue-87447.7-aekA.rst @@ -0,0 +1,5 @@ +Fix :exc:`SyntaxError` on comprehension rebind checking with names that are +not actually redefined. + +Now reassigning ``b`` in ``[(b := 1) for a, b.prop in some_iter]`` is allowed. +Reassigning ``a`` is still disallowed as per :pep:`572`. diff --git a/Python/symtable.c b/Python/symtable.c index 3c130186d0ad..89a2bc437dfa 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -1488,7 +1488,8 @@ symtable_extend_namedexpr_scope(struct symtable *st, expr_ty e) */ if (ste->ste_comprehension) { long target_in_scope = _PyST_GetSymbol(ste, target_name); - if (target_in_scope & DEF_COMP_ITER) { + if ((target_in_scope & DEF_COMP_ITER) && + (target_in_scope & DEF_LOCAL)) { PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_CONFLICT, target_name); PyErr_RangedSyntaxLocationObject(st->st_filename, e->lineno, From webhook-mailer at python.org Sun Jan 8 18:00:15 2023 From: webhook-mailer at python.org (benjaminp) Date: Sun, 08 Jan 2023 23:00:15 -0000 Subject: [Python-checkins] [3.9] Update copyright years to 2023. (gh-100851) Message-ID: https://github.com/python/cpython/commit/08210c62e95c7f313b76223dfd8ed1215cfafab6 commit: 08210c62e95c7f313b76223dfd8ed1215cfafab6 branch: 3.9 author: Benjamin Peterson committer: benjaminp date: 2023-01-08T17:00:10-06:00 summary: [3.9] Update copyright years to 2023. (gh-100851) * [3.9] Update copyright years to 2023. (gh-100848). (cherry picked from commit 11f99323c2ae0ec428c370a335695e3d8d4afc1d) Co-authored-by: Benjamin Peterson * Update additional copyright years to 2023. Co-authored-by: Ned Deily files: M Doc/copyright.rst M Doc/license.rst M LICENSE M Mac/IDLE/IDLE.app/Contents/Info.plist M Mac/PythonLauncher/Info.plist.in M Mac/Resources/app/Info.plist.in M Mac/Resources/framework/Info.plist.in M PC/python_ver_rc.h M Python/getcopyright.c M README.rst diff --git a/Doc/copyright.rst b/Doc/copyright.rst index e64a49328b47..9b71683155ee 100644 --- a/Doc/copyright.rst +++ b/Doc/copyright.rst @@ -4,7 +4,7 @@ Copyright Python and this documentation is: -Copyright ? 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. Copyright ? 2000 BeOpen.com. All rights reserved. diff --git a/Doc/license.rst b/Doc/license.rst index 842cf03a31c6..5c42301a5469 100644 --- a/Doc/license.rst +++ b/Doc/license.rst @@ -100,7 +100,7 @@ PSF LICENSE AGREEMENT FOR PYTHON |release| analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python |release| alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of - copyright, i.e., "Copyright ? 2001-2022 Python Software Foundation; All Rights + copyright, i.e., "Copyright ? 2001-2023 Python Software Foundation; All Rights Reserved" are retained in Python |release| alone or in any derivative version prepared by Licensee. diff --git a/LICENSE b/LICENSE index 739c90c28400..4d64e0ba3d16 100644 --- a/LICENSE +++ b/LICENSE @@ -84,7 +84,7 @@ analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation; +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. diff --git a/Mac/IDLE/IDLE.app/Contents/Info.plist b/Mac/IDLE/IDLE.app/Contents/Info.plist index d197c77ed4b1..411a24cf5ba0 100644 --- a/Mac/IDLE/IDLE.app/Contents/Info.plist +++ b/Mac/IDLE/IDLE.app/Contents/Info.plist @@ -36,7 +36,7 @@ CFBundleExecutable IDLE CFBundleGetInfoString - %version%, ? 2001-2022 Python Software Foundation + %version%, ? 2001-2023 Python Software Foundation CFBundleIconFile IDLE.icns CFBundleIdentifier diff --git a/Mac/PythonLauncher/Info.plist.in b/Mac/PythonLauncher/Info.plist.in index dec0a2eaf5c5..b7cddac0729f 100644 --- a/Mac/PythonLauncher/Info.plist.in +++ b/Mac/PythonLauncher/Info.plist.in @@ -40,9 +40,9 @@ CFBundleExecutable Python Launcher NSHumanReadableCopyright - Copyright ? 2001-2022 Python Software Foundation + Copyright ? 2001-2023 Python Software Foundation CFBundleGetInfoString - %VERSION%, ? 2001-2022 Python Software Foundation + %VERSION%, ? 2001-2023 Python Software Foundation CFBundleIconFile PythonLauncher.icns CFBundleIdentifier diff --git a/Mac/Resources/app/Info.plist.in b/Mac/Resources/app/Info.plist.in index 84843b734e3d..4ec828ff176e 100644 --- a/Mac/Resources/app/Info.plist.in +++ b/Mac/Resources/app/Info.plist.in @@ -20,7 +20,7 @@ CFBundleExecutable Python CFBundleGetInfoString - %version%, (c) 2001-2020 Python Software Foundation. + %version%, (c) 2001-2023 Python Software Foundation. CFBundleHelpBookFolder Documentation @@ -37,7 +37,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString - %version%, (c) 2001-2022 Python Software Foundation. + %version%, (c) 2001-2023 Python Software Foundation. CFBundleName Python CFBundlePackageType @@ -55,7 +55,7 @@ NSAppleScriptEnabled NSHumanReadableCopyright - (c) 2001-2020 Python Software Foundation. + (c) 2001-2023 Python Software Foundation. NSHighResolutionCapable diff --git a/Mac/Resources/framework/Info.plist.in b/Mac/Resources/framework/Info.plist.in index 863b45238fe4..e131c205ef0b 100644 --- a/Mac/Resources/framework/Info.plist.in +++ b/Mac/Resources/framework/Info.plist.in @@ -17,9 +17,9 @@ CFBundlePackageType FMWK CFBundleShortVersionString - %VERSION%, (c) 2001-2021 Python Software Foundation. + %VERSION%, (c) 2001-2023 Python Software Foundation. CFBundleLongVersionString - %VERSION%, (c) 2001-2021 Python Software Foundation. + %VERSION%, (c) 2001-2023 Python Software Foundation. CFBundleSignature ???? CFBundleVersion diff --git a/PC/python_ver_rc.h b/PC/python_ver_rc.h index e6c1d2437041..5b55b810cd21 100644 --- a/PC/python_ver_rc.h +++ b/PC/python_ver_rc.h @@ -5,7 +5,7 @@ #include "winver.h" #define PYTHON_COMPANY "Python Software Foundation" -#define PYTHON_COPYRIGHT "Copyright \xA9 2001-2022 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." +#define PYTHON_COPYRIGHT "Copyright \xA9 2001-2023 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." #define MS_WINDOWS #include "modsupport.h" diff --git a/Python/getcopyright.c b/Python/getcopyright.c index 88d1d0536253..c1f1aad9b845 100644 --- a/Python/getcopyright.c +++ b/Python/getcopyright.c @@ -4,7 +4,7 @@ static const char cprt[] = "\ -Copyright (c) 2001-2022 Python Software Foundation.\n\ +Copyright (c) 2001-2023 Python Software Foundation.\n\ All Rights Reserved.\n\ \n\ Copyright (c) 2000 BeOpen.com.\n\ diff --git a/README.rst b/README.rst index 4c804d677cf9..5705ac283867 100644 --- a/README.rst +++ b/README.rst @@ -22,7 +22,7 @@ This is Python version 3.9.16 :target: https://discuss.python.org/ -Copyright (c) 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. See the end of this file for further copyright and license information. From webhook-mailer at python.org Sun Jan 8 18:00:29 2023 From: webhook-mailer at python.org (benjaminp) Date: Sun, 08 Jan 2023 23:00:29 -0000 Subject: [Python-checkins] [3.10] Update copyright years to 2023. (gh-100850) Message-ID: https://github.com/python/cpython/commit/3e9543e4071edbe85ab70c5dca001067f800b988 commit: 3e9543e4071edbe85ab70c5dca001067f800b988 branch: 3.10 author: Benjamin Peterson committer: benjaminp date: 2023-01-08T17:00:24-06:00 summary: [3.10] Update copyright years to 2023. (gh-100850) * [3.10] Update copyright years to 2023. (gh-100848). (cherry picked from commit 11f99323c2ae0ec428c370a335695e3d8d4afc1d) Co-authored-by: Benjamin Peterson * Update additional copyright years to 2023. Co-authored-by: Ned Deily files: M Doc/copyright.rst M Doc/license.rst M LICENSE M Mac/IDLE/IDLE.app/Contents/Info.plist M Mac/PythonLauncher/Info.plist.in M Mac/Resources/app/Info.plist.in M Mac/Resources/framework/Info.plist.in M PC/python_ver_rc.h M Python/getcopyright.c M README.rst diff --git a/Doc/copyright.rst b/Doc/copyright.rst index e64a49328b47..9b71683155ee 100644 --- a/Doc/copyright.rst +++ b/Doc/copyright.rst @@ -4,7 +4,7 @@ Copyright Python and this documentation is: -Copyright ? 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. Copyright ? 2000 BeOpen.com. All rights reserved. diff --git a/Doc/license.rst b/Doc/license.rst index 4caecdce7777..37a3d4fa9e5f 100644 --- a/Doc/license.rst +++ b/Doc/license.rst @@ -100,7 +100,7 @@ PSF LICENSE AGREEMENT FOR PYTHON |release| analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python |release| alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of - copyright, i.e., "Copyright ? 2001-2022 Python Software Foundation; All Rights + copyright, i.e., "Copyright ? 2001-2023 Python Software Foundation; All Rights Reserved" are retained in Python |release| alone or in any derivative version prepared by Licensee. diff --git a/LICENSE b/LICENSE index 9838d443f9fc..f26bcf4d2de6 100644 --- a/LICENSE +++ b/LICENSE @@ -84,7 +84,7 @@ analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation; +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. diff --git a/Mac/IDLE/IDLE.app/Contents/Info.plist b/Mac/IDLE/IDLE.app/Contents/Info.plist index 799a164cbdf2..fea06d463249 100644 --- a/Mac/IDLE/IDLE.app/Contents/Info.plist +++ b/Mac/IDLE/IDLE.app/Contents/Info.plist @@ -37,7 +37,7 @@ CFBundleExecutable IDLE CFBundleGetInfoString - %version%, ? 2001-2022 Python Software Foundation + %version%, ? 2001-2023 Python Software Foundation CFBundleIconFile IDLE.icns CFBundleIdentifier diff --git a/Mac/PythonLauncher/Info.plist.in b/Mac/PythonLauncher/Info.plist.in index dec0a2eaf5c5..b7cddac0729f 100644 --- a/Mac/PythonLauncher/Info.plist.in +++ b/Mac/PythonLauncher/Info.plist.in @@ -40,9 +40,9 @@ CFBundleExecutable Python Launcher NSHumanReadableCopyright - Copyright ? 2001-2022 Python Software Foundation + Copyright ? 2001-2023 Python Software Foundation CFBundleGetInfoString - %VERSION%, ? 2001-2022 Python Software Foundation + %VERSION%, ? 2001-2023 Python Software Foundation CFBundleIconFile PythonLauncher.icns CFBundleIdentifier diff --git a/Mac/Resources/app/Info.plist.in b/Mac/Resources/app/Info.plist.in index 84843b734e3d..4ec828ff176e 100644 --- a/Mac/Resources/app/Info.plist.in +++ b/Mac/Resources/app/Info.plist.in @@ -20,7 +20,7 @@ CFBundleExecutable Python CFBundleGetInfoString - %version%, (c) 2001-2020 Python Software Foundation. + %version%, (c) 2001-2023 Python Software Foundation. CFBundleHelpBookFolder Documentation @@ -37,7 +37,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString - %version%, (c) 2001-2022 Python Software Foundation. + %version%, (c) 2001-2023 Python Software Foundation. CFBundleName Python CFBundlePackageType @@ -55,7 +55,7 @@ NSAppleScriptEnabled NSHumanReadableCopyright - (c) 2001-2020 Python Software Foundation. + (c) 2001-2023 Python Software Foundation. NSHighResolutionCapable diff --git a/Mac/Resources/framework/Info.plist.in b/Mac/Resources/framework/Info.plist.in index 863b45238fe4..e131c205ef0b 100644 --- a/Mac/Resources/framework/Info.plist.in +++ b/Mac/Resources/framework/Info.plist.in @@ -17,9 +17,9 @@ CFBundlePackageType FMWK CFBundleShortVersionString - %VERSION%, (c) 2001-2021 Python Software Foundation. + %VERSION%, (c) 2001-2023 Python Software Foundation. CFBundleLongVersionString - %VERSION%, (c) 2001-2021 Python Software Foundation. + %VERSION%, (c) 2001-2023 Python Software Foundation. CFBundleSignature ???? CFBundleVersion diff --git a/PC/python_ver_rc.h b/PC/python_ver_rc.h index e6c1d2437041..5b55b810cd21 100644 --- a/PC/python_ver_rc.h +++ b/PC/python_ver_rc.h @@ -5,7 +5,7 @@ #include "winver.h" #define PYTHON_COMPANY "Python Software Foundation" -#define PYTHON_COPYRIGHT "Copyright \xA9 2001-2022 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." +#define PYTHON_COPYRIGHT "Copyright \xA9 2001-2023 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." #define MS_WINDOWS #include "modsupport.h" diff --git a/Python/getcopyright.c b/Python/getcopyright.c index 88d1d0536253..c1f1aad9b845 100644 --- a/Python/getcopyright.c +++ b/Python/getcopyright.c @@ -4,7 +4,7 @@ static const char cprt[] = "\ -Copyright (c) 2001-2022 Python Software Foundation.\n\ +Copyright (c) 2001-2023 Python Software Foundation.\n\ All Rights Reserved.\n\ \n\ Copyright (c) 2000 BeOpen.com.\n\ diff --git a/README.rst b/README.rst index 4e40478cf9af..7f2acfa179b3 100644 --- a/README.rst +++ b/README.rst @@ -18,7 +18,7 @@ This is Python version 3.10.9 :target: https://discuss.python.org/ -Copyright (c) 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. See the end of this file for further copyright and license information. From webhook-mailer at python.org Sun Jan 8 18:00:36 2023 From: webhook-mailer at python.org (benjaminp) Date: Sun, 08 Jan 2023 23:00:36 -0000 Subject: [Python-checkins] [3.8] Update copyright years to 2023. (gh-100852) Message-ID: https://github.com/python/cpython/commit/30afa75ad8deca57a2bd0218f8fd6b3437c89507 commit: 30afa75ad8deca57a2bd0218f8fd6b3437c89507 branch: 3.8 author: Benjamin Peterson committer: benjaminp date: 2023-01-08T17:00:31-06:00 summary: [3.8] Update copyright years to 2023. (gh-100852) * [3.8] Update copyright years to 2023. (gh-100848). (cherry picked from commit 11f99323c2ae0ec428c370a335695e3d8d4afc1d) Co-authored-by: Benjamin Peterson * Update additional copyright years to 2023. Co-authored-by: Ned Deily files: M Doc/copyright.rst M Doc/license.rst M LICENSE M Mac/IDLE/IDLE.app/Contents/Info.plist M Mac/PythonLauncher/Info.plist.in M Mac/Resources/app/Info.plist.in M Mac/Resources/framework/Info.plist.in M PC/python_ver_rc.h M Python/getcopyright.c M README.rst diff --git a/Doc/copyright.rst b/Doc/copyright.rst index e64a49328b47..9b71683155ee 100644 --- a/Doc/copyright.rst +++ b/Doc/copyright.rst @@ -4,7 +4,7 @@ Copyright Python and this documentation is: -Copyright ? 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. Copyright ? 2000 BeOpen.com. All rights reserved. diff --git a/Doc/license.rst b/Doc/license.rst index 250e6cda35e7..0de692d16106 100644 --- a/Doc/license.rst +++ b/Doc/license.rst @@ -100,7 +100,7 @@ PSF LICENSE AGREEMENT FOR PYTHON |release| analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python |release| alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of - copyright, i.e., "Copyright ? 2001-2022 Python Software Foundation; All Rights + copyright, i.e., "Copyright ? 2001-2023 Python Software Foundation; All Rights Reserved" are retained in Python |release| alone or in any derivative version prepared by Licensee. diff --git a/LICENSE b/LICENSE index 739c90c28400..4d64e0ba3d16 100644 --- a/LICENSE +++ b/LICENSE @@ -84,7 +84,7 @@ analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation; +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. diff --git a/Mac/IDLE/IDLE.app/Contents/Info.plist b/Mac/IDLE/IDLE.app/Contents/Info.plist index d197c77ed4b1..411a24cf5ba0 100644 --- a/Mac/IDLE/IDLE.app/Contents/Info.plist +++ b/Mac/IDLE/IDLE.app/Contents/Info.plist @@ -36,7 +36,7 @@ CFBundleExecutable IDLE CFBundleGetInfoString - %version%, ? 2001-2022 Python Software Foundation + %version%, ? 2001-2023 Python Software Foundation CFBundleIconFile IDLE.icns CFBundleIdentifier diff --git a/Mac/PythonLauncher/Info.plist.in b/Mac/PythonLauncher/Info.plist.in index 70f215d07249..17be7a8303e2 100644 --- a/Mac/PythonLauncher/Info.plist.in +++ b/Mac/PythonLauncher/Info.plist.in @@ -40,7 +40,7 @@ CFBundleExecutable Python Launcher CFBundleGetInfoString - %VERSION%, ? 2001-2022 Python Software Foundation + %VERSION%, ? 2001-2023 Python Software Foundation CFBundleIconFile PythonLauncher.icns CFBundleIdentifier diff --git a/Mac/Resources/app/Info.plist.in b/Mac/Resources/app/Info.plist.in index 84843b734e3d..4ec828ff176e 100644 --- a/Mac/Resources/app/Info.plist.in +++ b/Mac/Resources/app/Info.plist.in @@ -20,7 +20,7 @@ CFBundleExecutable Python CFBundleGetInfoString - %version%, (c) 2001-2020 Python Software Foundation. + %version%, (c) 2001-2023 Python Software Foundation. CFBundleHelpBookFolder Documentation @@ -37,7 +37,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString - %version%, (c) 2001-2022 Python Software Foundation. + %version%, (c) 2001-2023 Python Software Foundation. CFBundleName Python CFBundlePackageType @@ -55,7 +55,7 @@ NSAppleScriptEnabled NSHumanReadableCopyright - (c) 2001-2020 Python Software Foundation. + (c) 2001-2023 Python Software Foundation. NSHighResolutionCapable diff --git a/Mac/Resources/framework/Info.plist.in b/Mac/Resources/framework/Info.plist.in index 0dc2e17156f1..e131c205ef0b 100644 --- a/Mac/Resources/framework/Info.plist.in +++ b/Mac/Resources/framework/Info.plist.in @@ -17,9 +17,9 @@ CFBundlePackageType FMWK CFBundleShortVersionString - %VERSION%, (c) 2001-2019 Python Software Foundation. + %VERSION%, (c) 2001-2023 Python Software Foundation. CFBundleLongVersionString - %VERSION%, (c) 2001-2019 Python Software Foundation. + %VERSION%, (c) 2001-2023 Python Software Foundation. CFBundleSignature ???? CFBundleVersion diff --git a/PC/python_ver_rc.h b/PC/python_ver_rc.h index 15a4d09d801e..0e4dc66c1508 100644 --- a/PC/python_ver_rc.h +++ b/PC/python_ver_rc.h @@ -5,7 +5,7 @@ #include "winver.h" #define PYTHON_COMPANY "Python Software Foundation" -#define PYTHON_COPYRIGHT "Copyright \xA9 2001-2022 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." +#define PYTHON_COPYRIGHT "Copyright \xA9 2001-2023 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." #define MS_WINDOWS #include "modsupport.h" diff --git a/Python/getcopyright.c b/Python/getcopyright.c index 88d1d0536253..c1f1aad9b845 100644 --- a/Python/getcopyright.c +++ b/Python/getcopyright.c @@ -4,7 +4,7 @@ static const char cprt[] = "\ -Copyright (c) 2001-2022 Python Software Foundation.\n\ +Copyright (c) 2001-2023 Python Software Foundation.\n\ All Rights Reserved.\n\ \n\ Copyright (c) 2000 BeOpen.com.\n\ diff --git a/README.rst b/README.rst index 3aed3a5c47d4..4dd5f8650541 100644 --- a/README.rst +++ b/README.rst @@ -18,7 +18,7 @@ This is Python version 3.8.16 :target: https://discuss.python.org/ -Copyright (c) 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. See the end of this file for further copyright and license information. From webhook-mailer at python.org Sun Jan 8 18:00:49 2023 From: webhook-mailer at python.org (benjaminp) Date: Sun, 08 Jan 2023 23:00:49 -0000 Subject: [Python-checkins] [3.7] Update copyright years to 2023. (gh-100853) Message-ID: https://github.com/python/cpython/commit/798260f5c087836deb761d72e07ca2f6fd1419cb commit: 798260f5c087836deb761d72e07ca2f6fd1419cb branch: 3.7 author: Benjamin Peterson committer: benjaminp date: 2023-01-08T17:00:43-06:00 summary: [3.7] Update copyright years to 2023. (gh-100853) * [3.7] Update copyright years to 2023. (gh-100848). (cherry picked from commit 11f99323c2ae0ec428c370a335695e3d8d4afc1d) Co-authored-by: Benjamin Peterson * Update additional copyright years to 2023. Co-authored-by: Ned Deily files: M Doc/copyright.rst M Doc/license.rst M LICENSE M Mac/IDLE/IDLE.app/Contents/Info.plist M Mac/PythonLauncher/Info.plist.in M Mac/Resources/app/Info.plist.in M Mac/Resources/framework/Info.plist.in M PC/python_ver_rc.h M Python/getcopyright.c M README.rst diff --git a/Doc/copyright.rst b/Doc/copyright.rst index e64a49328b47..9b71683155ee 100644 --- a/Doc/copyright.rst +++ b/Doc/copyright.rst @@ -4,7 +4,7 @@ Copyright Python and this documentation is: -Copyright ? 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. Copyright ? 2000 BeOpen.com. All rights reserved. diff --git a/Doc/license.rst b/Doc/license.rst index c5528834514b..6b6c0761a40d 100644 --- a/Doc/license.rst +++ b/Doc/license.rst @@ -87,7 +87,7 @@ PSF LICENSE AGREEMENT FOR PYTHON |release| analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python |release| alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of - copyright, i.e., "Copyright ? 2001-2022 Python Software Foundation; All Rights + copyright, i.e., "Copyright ? 2001-2023 Python Software Foundation; All Rights Reserved" are retained in Python |release| alone or in any derivative version prepared by Licensee. diff --git a/LICENSE b/LICENSE index ea33d9c47b41..44b1288db4a2 100644 --- a/LICENSE +++ b/LICENSE @@ -73,7 +73,7 @@ analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation; +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. diff --git a/Mac/IDLE/IDLE.app/Contents/Info.plist b/Mac/IDLE/IDLE.app/Contents/Info.plist index d197c77ed4b1..411a24cf5ba0 100644 --- a/Mac/IDLE/IDLE.app/Contents/Info.plist +++ b/Mac/IDLE/IDLE.app/Contents/Info.plist @@ -36,7 +36,7 @@ CFBundleExecutable IDLE CFBundleGetInfoString - %version%, ? 2001-2022 Python Software Foundation + %version%, ? 2001-2023 Python Software Foundation CFBundleIconFile IDLE.icns CFBundleIdentifier diff --git a/Mac/PythonLauncher/Info.plist.in b/Mac/PythonLauncher/Info.plist.in index 70f215d07249..17be7a8303e2 100644 --- a/Mac/PythonLauncher/Info.plist.in +++ b/Mac/PythonLauncher/Info.plist.in @@ -40,7 +40,7 @@ CFBundleExecutable Python Launcher CFBundleGetInfoString - %VERSION%, ? 2001-2022 Python Software Foundation + %VERSION%, ? 2001-2023 Python Software Foundation CFBundleIconFile PythonLauncher.icns CFBundleIdentifier diff --git a/Mac/Resources/app/Info.plist.in b/Mac/Resources/app/Info.plist.in index 84843b734e3d..4ec828ff176e 100644 --- a/Mac/Resources/app/Info.plist.in +++ b/Mac/Resources/app/Info.plist.in @@ -20,7 +20,7 @@ CFBundleExecutable Python CFBundleGetInfoString - %version%, (c) 2001-2020 Python Software Foundation. + %version%, (c) 2001-2023 Python Software Foundation. CFBundleHelpBookFolder Documentation @@ -37,7 +37,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString - %version%, (c) 2001-2022 Python Software Foundation. + %version%, (c) 2001-2023 Python Software Foundation. CFBundleName Python CFBundlePackageType @@ -55,7 +55,7 @@ NSAppleScriptEnabled NSHumanReadableCopyright - (c) 2001-2020 Python Software Foundation. + (c) 2001-2023 Python Software Foundation. NSHighResolutionCapable diff --git a/Mac/Resources/framework/Info.plist.in b/Mac/Resources/framework/Info.plist.in index 0dc2e17156f1..e131c205ef0b 100644 --- a/Mac/Resources/framework/Info.plist.in +++ b/Mac/Resources/framework/Info.plist.in @@ -17,9 +17,9 @@ CFBundlePackageType FMWK CFBundleShortVersionString - %VERSION%, (c) 2001-2019 Python Software Foundation. + %VERSION%, (c) 2001-2023 Python Software Foundation. CFBundleLongVersionString - %VERSION%, (c) 2001-2019 Python Software Foundation. + %VERSION%, (c) 2001-2023 Python Software Foundation. CFBundleSignature ???? CFBundleVersion diff --git a/PC/python_ver_rc.h b/PC/python_ver_rc.h index f0255d988cf8..dce7691b4cbf 100644 --- a/PC/python_ver_rc.h +++ b/PC/python_ver_rc.h @@ -4,7 +4,7 @@ #include "winver.h" #define PYTHON_COMPANY "Python Software Foundation" -#define PYTHON_COPYRIGHT "Copyright \xA9 2001-2022 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." +#define PYTHON_COPYRIGHT "Copyright \xA9 2001-2023 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." #define MS_WINDOWS #include "modsupport.h" diff --git a/Python/getcopyright.c b/Python/getcopyright.c index 88d1d0536253..c1f1aad9b845 100644 --- a/Python/getcopyright.c +++ b/Python/getcopyright.c @@ -4,7 +4,7 @@ static const char cprt[] = "\ -Copyright (c) 2001-2022 Python Software Foundation.\n\ +Copyright (c) 2001-2023 Python Software Foundation.\n\ All Rights Reserved.\n\ \n\ Copyright (c) 2000 BeOpen.com.\n\ diff --git a/README.rst b/README.rst index 28fb2a398f14..cf1a844edf5b 100644 --- a/README.rst +++ b/README.rst @@ -13,7 +13,7 @@ This is Python version 3.7.16 :alt: CPython code coverage on Codecov :target: https://codecov.io/gh/python/cpython/branch/3.7 -Copyright (c) 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. See the end of this file for further copyright and license information. From webhook-mailer at python.org Sun Jan 8 20:57:35 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 09 Jan 2023 01:57:35 -0000 Subject: [Python-checkins] [3.11] Update copyright years to 2023. (gh-100848) (GH-100849) Message-ID: https://github.com/python/cpython/commit/07d1f99e8faefcf24dab2113fb0df27202fa6f71 commit: 07d1f99e8faefcf24dab2113fb0df27202fa6f71 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-08T17:57:17-08:00 summary: [3.11] Update copyright years to 2023. (gh-100848) (GH-100849) (cherry picked from commit 11f99323c2ae0ec428c370a335695e3d8d4afc1d) Co-authored-by: Benjamin Peterson files: M Doc/copyright.rst M Doc/license.rst M LICENSE M Mac/IDLE/IDLE.app/Contents/Info.plist M Mac/PythonLauncher/Info.plist.in M Mac/Resources/app/Info.plist.in M Mac/Resources/framework/Info.plist.in M PC/python_ver_rc.h M Python/getcopyright.c M README.rst diff --git a/Doc/copyright.rst b/Doc/copyright.rst index e64a49328b47..9b71683155ee 100644 --- a/Doc/copyright.rst +++ b/Doc/copyright.rst @@ -4,7 +4,7 @@ Copyright Python and this documentation is: -Copyright ? 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. Copyright ? 2000 BeOpen.com. All rights reserved. diff --git a/Doc/license.rst b/Doc/license.rst index 4caecdce7777..37a3d4fa9e5f 100644 --- a/Doc/license.rst +++ b/Doc/license.rst @@ -100,7 +100,7 @@ PSF LICENSE AGREEMENT FOR PYTHON |release| analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python |release| alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of - copyright, i.e., "Copyright ? 2001-2022 Python Software Foundation; All Rights + copyright, i.e., "Copyright ? 2001-2023 Python Software Foundation; All Rights Reserved" are retained in Python |release| alone or in any derivative version prepared by Licensee. diff --git a/LICENSE b/LICENSE index 9838d443f9fc..f26bcf4d2de6 100644 --- a/LICENSE +++ b/LICENSE @@ -84,7 +84,7 @@ analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation; +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. diff --git a/Mac/IDLE/IDLE.app/Contents/Info.plist b/Mac/IDLE/IDLE.app/Contents/Info.plist index 799a164cbdf2..fea06d463249 100644 --- a/Mac/IDLE/IDLE.app/Contents/Info.plist +++ b/Mac/IDLE/IDLE.app/Contents/Info.plist @@ -37,7 +37,7 @@ CFBundleExecutable IDLE CFBundleGetInfoString - %version%, ? 2001-2022 Python Software Foundation + %version%, ? 2001-2023 Python Software Foundation CFBundleIconFile IDLE.icns CFBundleIdentifier diff --git a/Mac/PythonLauncher/Info.plist.in b/Mac/PythonLauncher/Info.plist.in index dec0a2eaf5c5..b7cddac0729f 100644 --- a/Mac/PythonLauncher/Info.plist.in +++ b/Mac/PythonLauncher/Info.plist.in @@ -40,9 +40,9 @@ CFBundleExecutable Python Launcher NSHumanReadableCopyright - Copyright ? 2001-2022 Python Software Foundation + Copyright ? 2001-2023 Python Software Foundation CFBundleGetInfoString - %VERSION%, ? 2001-2022 Python Software Foundation + %VERSION%, ? 2001-2023 Python Software Foundation CFBundleIconFile PythonLauncher.icns CFBundleIdentifier diff --git a/Mac/Resources/app/Info.plist.in b/Mac/Resources/app/Info.plist.in index 84843b734e3d..4ec828ff176e 100644 --- a/Mac/Resources/app/Info.plist.in +++ b/Mac/Resources/app/Info.plist.in @@ -20,7 +20,7 @@ CFBundleExecutable Python CFBundleGetInfoString - %version%, (c) 2001-2020 Python Software Foundation. + %version%, (c) 2001-2023 Python Software Foundation. CFBundleHelpBookFolder Documentation @@ -37,7 +37,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString - %version%, (c) 2001-2022 Python Software Foundation. + %version%, (c) 2001-2023 Python Software Foundation. CFBundleName Python CFBundlePackageType @@ -55,7 +55,7 @@ NSAppleScriptEnabled NSHumanReadableCopyright - (c) 2001-2020 Python Software Foundation. + (c) 2001-2023 Python Software Foundation. NSHighResolutionCapable diff --git a/Mac/Resources/framework/Info.plist.in b/Mac/Resources/framework/Info.plist.in index 863b45238fe4..e131c205ef0b 100644 --- a/Mac/Resources/framework/Info.plist.in +++ b/Mac/Resources/framework/Info.plist.in @@ -17,9 +17,9 @@ CFBundlePackageType FMWK CFBundleShortVersionString - %VERSION%, (c) 2001-2021 Python Software Foundation. + %VERSION%, (c) 2001-2023 Python Software Foundation. CFBundleLongVersionString - %VERSION%, (c) 2001-2021 Python Software Foundation. + %VERSION%, (c) 2001-2023 Python Software Foundation. CFBundleSignature ???? CFBundleVersion diff --git a/PC/python_ver_rc.h b/PC/python_ver_rc.h index e6c1d2437041..5b55b810cd21 100644 --- a/PC/python_ver_rc.h +++ b/PC/python_ver_rc.h @@ -5,7 +5,7 @@ #include "winver.h" #define PYTHON_COMPANY "Python Software Foundation" -#define PYTHON_COPYRIGHT "Copyright \xA9 2001-2022 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." +#define PYTHON_COPYRIGHT "Copyright \xA9 2001-2023 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." #define MS_WINDOWS #include "modsupport.h" diff --git a/Python/getcopyright.c b/Python/getcopyright.c index 88d1d0536253..c1f1aad9b845 100644 --- a/Python/getcopyright.c +++ b/Python/getcopyright.c @@ -4,7 +4,7 @@ static const char cprt[] = "\ -Copyright (c) 2001-2022 Python Software Foundation.\n\ +Copyright (c) 2001-2023 Python Software Foundation.\n\ All Rights Reserved.\n\ \n\ Copyright (c) 2000 BeOpen.com.\n\ diff --git a/README.rst b/README.rst index 8d9ace93b6c9..d09a0618682d 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,7 @@ This is Python version 3.11.1 :target: https://discuss.python.org/ -Copyright ? 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. See the end of this file for further copyright and license information. From webhook-mailer at python.org Sun Jan 8 21:17:18 2023 From: webhook-mailer at python.org (ned-deily) Date: Mon, 09 Jan 2023 02:17:18 -0000 Subject: [Python-checkins] [3.7] Correct CVE-2020-10735 documentation (GH-100306). (GH-100699) Message-ID: https://github.com/python/cpython/commit/fbda1c26039c0c527db5ac02c41879edf0c37337 commit: fbda1c26039c0c527db5ac02c41879edf0c37337 branch: 3.7 author: Gregory P. Smith committer: ned-deily date: 2023-01-08T21:17:13-05:00 summary: [3.7] Correct CVE-2020-10735 documentation (GH-100306). (GH-100699) Co-authored-by: Jeremy Paige . (cherry picked from commit 88fe8d701af3316c8869ea18ea1c7acec6f68c04) Co-authored-by: Gregory P. Smith files: M Doc/library/stdtypes.rst M Python/clinic/sysmodule.c.h M Python/sysmodule.c diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 99dc1b2be3d4..f64957ff7fc1 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4751,7 +4751,7 @@ to mitigate denial of service attacks. This limit *only* applies to decimal or other non-power-of-two number bases. Hexadecimal, octal, and binary conversions are unlimited. The limit can be configured. -The :class:`int` type in CPython is an abitrary length number stored in binary +The :class:`int` type in CPython is an arbitrary length number stored in binary form (commonly known as a "bignum"). There exists no algorithm that can convert a string to a binary integer or a binary integer to a string in linear time, *unless* the base is a power of 2. Even the best known algorithms for base 10 @@ -4815,7 +4815,7 @@ and :class:`str` or :class:`bytes`: * ``int(string)`` with default base 10. * ``int(string, base)`` for all bases that are not a power of 2. * ``str(integer)``. -* ``repr(integer)`` +* ``repr(integer)``. * any other string conversion to base 10, for example ``f"{integer}"``, ``"{}".format(integer)``, or ``b"%d" % integer``. @@ -4843,7 +4843,7 @@ command line flag to configure the limit: :envvar:`PYTHONINTMAXSTRDIGITS` or :option:`-X int_max_str_digits <-X>`. If both the env var and the ``-X`` option are set, the ``-X`` option takes precedence. A value of *-1* indicates that both were unset, thus a value of - :data:`sys.int_info.default_max_str_digits` was used during initilization. + :data:`sys.int_info.default_max_str_digits` was used during initialization. From code, you can inspect the current limit and set a new one using these :mod:`sys` APIs: diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index bc6c99a5d659..2ea64314c734 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -68,7 +68,7 @@ PyDoc_STRVAR(sys_get_int_max_str_digits__doc__, "get_int_max_str_digits($module, /)\n" "--\n" "\n" -"Set the maximum string digits limit for non-binary int<->str conversions."); +"Return the maximum string digits limit for non-binary int<->str conversions."); #define SYS_GET_INT_MAX_STR_DIGITS_METHODDEF \ {"get_int_max_str_digits", (PyCFunction)sys_get_int_max_str_digits, METH_NOARGS, sys_get_int_max_str_digits__doc__}, @@ -111,4 +111,4 @@ sys_set_int_max_str_digits(PyObject *module, PyObject *const *args, Py_ssize_t n exit: return return_value; } -/*[clinic end generated code: output=c566fcdbb8f6ae2c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5351eba7518cdf76 input=a9049054013a1b77]*/ diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 82e029fd3801..8b5e8dad2177 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1223,12 +1223,12 @@ sys_mdebug(PyObject *self, PyObject *args) /*[clinic input] sys.get_int_max_str_digits -Set the maximum string digits limit for non-binary int<->str conversions. +Return the maximum string digits limit for non-binary int<->str conversions. [clinic start generated code]*/ static PyObject * sys_get_int_max_str_digits_impl(PyObject *module) -/*[clinic end generated code: output=0042f5e8ae0e8631 input=8dab13e2023e60d5]*/ +/*[clinic end generated code: output=0042f5e8ae0e8631 input=61bf9f99bc8b112d]*/ { return PyLong_FromSsize_t(_PyRuntime.int_max_str_digits); } From webhook-mailer at python.org Sun Jan 8 21:54:01 2023 From: webhook-mailer at python.org (ned-deily) Date: Mon, 09 Jan 2023 02:54:01 -0000 Subject: [Python-checkins] Update copyright year in README (GH-100863) Message-ID: https://github.com/python/cpython/commit/e47b13934b2eb50914e4dbae91f1dc59f8325e30 commit: e47b13934b2eb50914e4dbae91f1dc59f8325e30 branch: main author: Ned Deily committer: ned-deily date: 2023-01-08T21:53:56-05:00 summary: Update copyright year in README (GH-100863) Co-authored-by: HARSHA VARDHAN <75431678+Thunder-007 at users.noreply.github.com> files: M README.rst diff --git a/README.rst b/README.rst index 9b565ce522f1..4e7ad25949fa 100644 --- a/README.rst +++ b/README.rst @@ -245,7 +245,7 @@ Copyright and License Information --------------------------------- -Copyright ? 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. Copyright ? 2000 BeOpen.com. All rights reserved. From webhook-mailer at python.org Sun Jan 8 21:55:58 2023 From: webhook-mailer at python.org (ned-deily) Date: Mon, 09 Jan 2023 02:55:58 -0000 Subject: [Python-checkins] [3.11] Update copyright year in README (GH-100863) (GH-100864) Message-ID: https://github.com/python/cpython/commit/074b881e1125bc431ef18681034ba43a9d6e2cb7 commit: 074b881e1125bc431ef18681034ba43a9d6e2cb7 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: ned-deily date: 2023-01-08T21:55:52-05:00 summary: [3.11] Update copyright year in README (GH-100863) (GH-100864) (cherry picked from commit e47b13934b2eb50914e4dbae91f1dc59f8325e30) Co-authored-by: Ned Deily Co-authored-by: HARSHA VARDHAN <75431678+Thunder-007 at users.noreply.github.com> files: M README.rst diff --git a/README.rst b/README.rst index d09a0618682d..193fc9c3de8e 100644 --- a/README.rst +++ b/README.rst @@ -245,7 +245,7 @@ Copyright and License Information --------------------------------- -Copyright ? 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. Copyright ? 2000 BeOpen.com. All rights reserved. From webhook-mailer at python.org Sun Jan 8 22:08:13 2023 From: webhook-mailer at python.org (ned-deily) Date: Mon, 09 Jan 2023 03:08:13 -0000 Subject: [Python-checkins] [3.10] Update copyright year in README (GH-100863) (GH-100865) Message-ID: https://github.com/python/cpython/commit/30a6cc418a60fccb91ba574b552203425e594c47 commit: 30a6cc418a60fccb91ba574b552203425e594c47 branch: 3.10 author: Ned Deily committer: ned-deily date: 2023-01-08T22:08:08-05:00 summary: [3.10] Update copyright year in README (GH-100863) (GH-100865) Co-authored-by: HARSHA VARDHAN <75431678+Thunder-007 at users.noreply.github.com> files: M README.rst diff --git a/README.rst b/README.rst index 7f2acfa179b3..f8b9c88e9282 100644 --- a/README.rst +++ b/README.rst @@ -247,14 +247,14 @@ See :pep:`619` for Python 3.10 release details. Copyright and License Information --------------------------------- -Copyright (c) 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. -Copyright (c) 2000 BeOpen.com. All rights reserved. +Copyright ? 2000 BeOpen.com. All rights reserved. -Copyright (c) 1995-2001 Corporation for National Research Initiatives. All +Copyright ? 1995-2001 Corporation for National Research Initiatives. All rights reserved. -Copyright (c) 1991-1995 Stichting Mathematisch Centrum. All rights reserved. +Copyright ? 1991-1995 Stichting Mathematisch Centrum. All rights reserved. See the `LICENSE `_ for information on the history of this software, terms & conditions for usage, and a From webhook-mailer at python.org Sun Jan 8 22:09:46 2023 From: webhook-mailer at python.org (ned-deily) Date: Mon, 09 Jan 2023 03:09:46 -0000 Subject: [Python-checkins] [3.7] Update copyright year in README (GH-100863) (GH-100865) (#100868) Message-ID: https://github.com/python/cpython/commit/a6b889e99d5a8cc63924efacda9e4d4eded4b2fc commit: a6b889e99d5a8cc63924efacda9e4d4eded4b2fc branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: ned-deily date: 2023-01-08T22:09:41-05:00 summary: [3.7] Update copyright year in README (GH-100863) (GH-100865) (#100868) (cherry picked from commit 30a6cc418a60fccb91ba574b552203425e594c47) Co-authored-by: Ned Deily Co-authored-by: HARSHA VARDHAN <75431678+Thunder-007 at users.noreply.github.com> files: M README.rst diff --git a/README.rst b/README.rst index cf1a844edf5b..1c454c295ac6 100644 --- a/README.rst +++ b/README.rst @@ -242,14 +242,14 @@ See :pep:`537` for Python 3.7 release details. Copyright and License Information --------------------------------- -Copyright (c) 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. -Copyright (c) 2000 BeOpen.com. All rights reserved. +Copyright ? 2000 BeOpen.com. All rights reserved. -Copyright (c) 1995-2001 Corporation for National Research Initiatives. All +Copyright ? 1995-2001 Corporation for National Research Initiatives. All rights reserved. -Copyright (c) 1991-1995 Stichting Mathematisch Centrum. All rights reserved. +Copyright ? 1991-1995 Stichting Mathematisch Centrum. All rights reserved. See the file "LICENSE" for information on the history of this software, terms & conditions for usage, and a DISCLAIMER OF ALL WARRANTIES. From webhook-mailer at python.org Sun Jan 8 22:11:54 2023 From: webhook-mailer at python.org (ned-deily) Date: Mon, 09 Jan 2023 03:11:54 -0000 Subject: [Python-checkins] [3.9] Update copyright year in README (GH-100863) (GH-100865) (GH-100866) Message-ID: https://github.com/python/cpython/commit/5ef90eebfd90147c7e774cd5335ad4fe69a7227c commit: 5ef90eebfd90147c7e774cd5335ad4fe69a7227c branch: 3.9 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: ned-deily date: 2023-01-08T22:11:49-05:00 summary: [3.9] Update copyright year in README (GH-100863) (GH-100865) (GH-100866) (cherry picked from commit 30a6cc418a60fccb91ba574b552203425e594c47) Co-authored-by: Ned Deily Co-authored-by: HARSHA VARDHAN <75431678+Thunder-007 at users.noreply.github.com> files: M README.rst diff --git a/README.rst b/README.rst index 5705ac283867..0e33d1376b59 100644 --- a/README.rst +++ b/README.rst @@ -251,14 +251,14 @@ See :pep:`596` for Python 3.9 release details. Copyright and License Information --------------------------------- -Copyright (c) 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. -Copyright (c) 2000 BeOpen.com. All rights reserved. +Copyright ? 2000 BeOpen.com. All rights reserved. -Copyright (c) 1995-2001 Corporation for National Research Initiatives. All +Copyright ? 1995-2001 Corporation for National Research Initiatives. All rights reserved. -Copyright (c) 1991-1995 Stichting Mathematisch Centrum. All rights reserved. +Copyright ? 1991-1995 Stichting Mathematisch Centrum. All rights reserved. See the file "LICENSE" for information on the history of this software, terms & conditions for usage, and a DISCLAIMER OF ALL WARRANTIES. From webhook-mailer at python.org Sun Jan 8 22:13:29 2023 From: webhook-mailer at python.org (ned-deily) Date: Mon, 09 Jan 2023 03:13:29 -0000 Subject: [Python-checkins] [3.8] Update copyright year in README (GH-100863) (GH-100867) Message-ID: https://github.com/python/cpython/commit/6924cba8bcf6fe4eb6a00a4ff19f47e7b7ce8ea7 commit: 6924cba8bcf6fe4eb6a00a4ff19f47e7b7ce8ea7 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: ned-deily date: 2023-01-08T22:13:24-05:00 summary: [3.8] Update copyright year in README (GH-100863) (GH-100867) (cherry picked from commit 30a6cc418a60fccb91ba574b552203425e594c47) Co-authored-by: Ned Deily Co-authored-by: HARSHA VARDHAN <75431678+Thunder-007 at users.noreply.github.com> files: M README.rst diff --git a/README.rst b/README.rst index 4dd5f8650541..61244379a7cc 100644 --- a/README.rst +++ b/README.rst @@ -246,14 +246,14 @@ See :pep:`569` for Python 3.8 release details. Copyright and License Information --------------------------------- -Copyright (c) 2001-2022 Python Software Foundation. All rights reserved. +Copyright ? 2001-2023 Python Software Foundation. All rights reserved. -Copyright (c) 2000 BeOpen.com. All rights reserved. +Copyright ? 2000 BeOpen.com. All rights reserved. -Copyright (c) 1995-2001 Corporation for National Research Initiatives. All +Copyright ? 1995-2001 Corporation for National Research Initiatives. All rights reserved. -Copyright (c) 1991-1995 Stichting Mathematisch Centrum. All rights reserved. +Copyright ? 1991-1995 Stichting Mathematisch Centrum. All rights reserved. See the file "LICENSE" for information on the history of this software, terms & conditions for usage, and a DISCLAIMER OF ALL WARRANTIES. From webhook-mailer at python.org Mon Jan 9 04:01:17 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Mon, 09 Jan 2023 09:01:17 -0000 Subject: [Python-checkins] GH-100813: Add `socket.IP_PKTINFO` constant (#10294) Message-ID: https://github.com/python/cpython/commit/7a50d6b5b09a88e915891757fdd6d371310d2e96 commit: 7a50d6b5b09a88e915891757fdd6d371310d2e96 branch: main author: dsentinel <14005083+dsentinel at users.noreply.github.com> committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-09T14:30:40+05:30 summary: GH-100813: Add `socket.IP_PKTINFO` constant (#10294) files: A Misc/NEWS.d/next/Library/2023-01-06-22-36-27.gh-issue-100813.mHRdQn.rst M Doc/library/socket.rst M Modules/socketmodule.c diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index cffb19c8b7b5..aec79da57f05 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -436,6 +436,7 @@ Constants ``TCP_FASTOPEN_CONNECT``, ``TCP_ULP``, ``TCP_MD5SIG_EXT``, ``TCP_FASTOPEN_KEY``, ``TCP_FASTOPEN_NO_COOKIE``, ``TCP_ZEROCOPY_RECEIVE``, ``TCP_INQ``, ``TCP_TX_DELAY``. + Added ``IP_PKTINFO``. .. data:: AF_CAN PF_CAN diff --git a/Misc/NEWS.d/next/Library/2023-01-06-22-36-27.gh-issue-100813.mHRdQn.rst b/Misc/NEWS.d/next/Library/2023-01-06-22-36-27.gh-issue-100813.mHRdQn.rst new file mode 100644 index 000000000000..5bb876d6f3d4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-06-22-36-27.gh-issue-100813.mHRdQn.rst @@ -0,0 +1 @@ +Add :data:`socket.IP_PKTINFO` constant. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 2c59c2f2c89b..4747a23e8317 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -8351,6 +8351,9 @@ PyInit__socket(void) #ifdef IP_TRANSPARENT PyModule_AddIntMacro(m, IP_TRANSPARENT); #endif +#ifdef IP_PKTINFO + PyModule_AddIntMacro(m, IP_PKTINFO); +#endif #ifdef IP_BIND_ADDRESS_NO_PORT PyModule_AddIntMacro(m, IP_BIND_ADDRESS_NO_PORT); #endif From webhook-mailer at python.org Mon Jan 9 04:06:02 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Mon, 09 Jan 2023 09:06:02 -0000 Subject: [Python-checkins] gh-100764: add `pycore_frame.h` to PYTHON_HEADERS and Windows build files(#100765) Message-ID: https://github.com/python/cpython/commit/0cd597fef10d30a100fa4d5e132b3d385a5ac0a4 commit: 0cd597fef10d30a100fa4d5e132b3d385a5ac0a4 branch: main author: Carl Meyer committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-09T14:35:56+05:30 summary: gh-100764: add `pycore_frame.h` to PYTHON_HEADERS and Windows build files(#100765) files: M Makefile.pre.in M PCbuild/pythoncore.vcxproj diff --git a/Makefile.pre.in b/Makefile.pre.in index a6b5f212160f..8c7a17be9e16 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1648,6 +1648,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_fileutils.h \ $(srcdir)/Include/internal/pycore_floatobject.h \ $(srcdir)/Include/internal/pycore_format.h \ + $(srcdir)/Include/internal/pycore_frame.h \ $(srcdir)/Include/internal/pycore_function.h \ $(srcdir)/Include/internal/pycore_genobject.h \ $(srcdir)/Include/internal/pycore_getopt.h \ diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 78f5234c6342..d3bd5b378e0d 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -218,6 +218,7 @@ + From webhook-mailer at python.org Mon Jan 9 05:09:03 2023 From: webhook-mailer at python.org (cjw296) Date: Mon, 09 Jan 2023 10:09:03 -0000 Subject: [Python-checkins] fix up mock tests coverage (#100874) Message-ID: https://github.com/python/cpython/commit/4e544eafcb603babe0db01270bd1c6d5d0f5d6ea commit: 4e544eafcb603babe0db01270bd1c6d5d0f5d6ea branch: main author: Chris Withers committer: cjw296 date: 2023-01-09T10:08:56Z summary: fix up mock tests coverage (#100874) files: M Lib/test/test_unittest/testmock/testmock.py diff --git a/Lib/test/test_unittest/testmock/testmock.py b/Lib/test/test_unittest/testmock/testmock.py index 97fe66fa4ca2..d1cae47a40ee 100644 --- a/Lib/test/test_unittest/testmock/testmock.py +++ b/Lib/test/test_unittest/testmock/testmock.py @@ -1653,11 +1653,9 @@ def test_mock_unsafe(self): m.has_calls() class Foo(object): - def called_once(self): - pass + def called_once(self): pass - def has_calls(self): - pass + def has_calls(self): pass m = Mock(spec=Foo) m.called_once() @@ -1679,11 +1677,9 @@ def has_calls(self): # gh-100739 def test_mock_safe_with_spec(self): class Foo(object): - def assert_bar(self): - pass + def assert_bar(self): pass - def assertSome(self): - pass + def assertSome(self): pass m = Mock(spec=Foo) m.assert_bar() From webhook-mailer at python.org Mon Jan 9 05:13:10 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Mon, 09 Jan 2023 10:13:10 -0000 Subject: [Python-checkins] GH-81061: Fix refcount issue when returning `None` from a `ctypes.py_object` callback (#13364) Message-ID: https://github.com/python/cpython/commit/837ba052672d1a5f85a46c1b6d4b6e7d192af6f3 commit: 837ba052672d1a5f85a46c1b6d4b6e7d192af6f3 branch: main author: dgelessus committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-09T15:43:04+05:30 summary: GH-81061: Fix refcount issue when returning `None` from a `ctypes.py_object` callback (#13364) files: A Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst M Lib/test/test_ctypes/test_refcounts.py M Modules/_ctypes/callbacks.c diff --git a/Lib/test/test_ctypes/test_refcounts.py b/Lib/test/test_ctypes/test_refcounts.py index f2edfa6400ef..48958cd2a601 100644 --- a/Lib/test/test_ctypes/test_refcounts.py +++ b/Lib/test/test_ctypes/test_refcounts.py @@ -97,5 +97,20 @@ def func(a, b): f(1, 2) self.assertEqual(sys.getrefcount(ctypes.c_int), a) + @support.refcount_test + def test_callback_py_object_none_return(self): + # bpo-36880: test that returning None from a py_object callback + # does not decrement the refcount of None. + + for FUNCTYPE in (ctypes.CFUNCTYPE, ctypes.PYFUNCTYPE): + with self.subTest(FUNCTYPE=FUNCTYPE): + @FUNCTYPE(ctypes.py_object) + def func(): + return None + + # Check that calling func does not affect None's refcount. + for _ in range(10000): + func() + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst b/Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst new file mode 100644 index 000000000000..f65323813d05 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst @@ -0,0 +1,2 @@ +Fix a reference counting issue when a :mod:`ctypes` callback with return +type :class:`~ctypes.py_object` returns ``None``, which could cause crashes. diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index f6880889dc0f..bc8750091f65 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -275,15 +275,14 @@ static void _CallPythonObject(void *mem, "of ctypes callback function", callable); } - else if (keep == Py_None) { - /* Nothing to keep */ - Py_DECREF(keep); - } else if (setfunc != _ctypes_get_fielddesc("O")->setfunc) { - if (-1 == PyErr_WarnEx(PyExc_RuntimeWarning, - "memory leak in callback function.", - 1)) - { + if (keep == Py_None) { + /* Nothing to keep */ + Py_DECREF(keep); + } + else if (PyErr_WarnEx(PyExc_RuntimeWarning, + "memory leak in callback function.", + 1) == -1) { _PyErr_WriteUnraisableMsg("on converting result " "of ctypes callback function", callable); From webhook-mailer at python.org Mon Jan 9 10:51:32 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Mon, 09 Jan 2023 15:51:32 -0000 Subject: [Python-checkins] =?utf-8?b?Z2gtMTAwODczOiBGaXggIuKAmGxv4oCZIG1h?= =?utf-8?q?y_be_used_uninitialized_in_this_function=22_warning_in_=60mathm?= =?utf-8?b?b2R1bGUuY2AgKCMxMDA4ODEp?= Message-ID: https://github.com/python/cpython/commit/36f2329367f3608d15562f1c9e89c50a1bd07b0b commit: 36f2329367f3608d15562f1c9e89c50a1bd07b0b branch: main author: Nikita Sobolev committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-09T21:21:24+05:30 summary: gh-100873: Fix "?lo? may be used uninitialized in this function" warning in `mathmodule.c` (#100881) files: M Modules/mathmodule.c diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 9e4c6fded933..1342162fa74b 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -1467,7 +1467,7 @@ math_fsum(PyObject *module, PyObject *seq) Py_ssize_t i, j, n = 0, m = NUM_PARTIALS; double x, y, t, ps[NUM_PARTIALS], *p = ps; double xsave, special_sum = 0.0, inf_sum = 0.0; - double hi, yr, lo; + double hi, yr, lo = 0.0; iter = PyObject_GetIter(seq); if (iter == NULL) From webhook-mailer at python.org Mon Jan 9 10:54:07 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 09 Jan 2023 15:54:07 -0000 Subject: [Python-checkins] GH-81061: Fix refcount issue when returning `None` from a `ctypes.py_object` callback (GH-13364) Message-ID: https://github.com/python/cpython/commit/b3744816163c8cb7e6b2603831d5731ef15a6281 commit: b3744816163c8cb7e6b2603831d5731ef15a6281 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-09T07:54:00-08:00 summary: GH-81061: Fix refcount issue when returning `None` from a `ctypes.py_object` callback (GH-13364) (cherry picked from commit 837ba052672d1a5f85a46c1b6d4b6e7d192af6f3) Co-authored-by: dgelessus files: A Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst M Lib/ctypes/test/test_refcounts.py M Modules/_ctypes/callbacks.c diff --git a/Lib/ctypes/test/test_refcounts.py b/Lib/ctypes/test/test_refcounts.py index f2edfa6400ef..48958cd2a601 100644 --- a/Lib/ctypes/test/test_refcounts.py +++ b/Lib/ctypes/test/test_refcounts.py @@ -97,5 +97,20 @@ def func(a, b): f(1, 2) self.assertEqual(sys.getrefcount(ctypes.c_int), a) + @support.refcount_test + def test_callback_py_object_none_return(self): + # bpo-36880: test that returning None from a py_object callback + # does not decrement the refcount of None. + + for FUNCTYPE in (ctypes.CFUNCTYPE, ctypes.PYFUNCTYPE): + with self.subTest(FUNCTYPE=FUNCTYPE): + @FUNCTYPE(ctypes.py_object) + def func(): + return None + + # Check that calling func does not affect None's refcount. + for _ in range(10000): + func() + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst b/Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst new file mode 100644 index 000000000000..f65323813d05 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst @@ -0,0 +1,2 @@ +Fix a reference counting issue when a :mod:`ctypes` callback with return +type :class:`~ctypes.py_object` returns ``None``, which could cause crashes. diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index c0ff7891c9a4..5a5e02628bc2 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -282,15 +282,14 @@ static void _CallPythonObject(void *mem, "of ctypes callback function", callable); } - else if (keep == Py_None) { - /* Nothing to keep */ - Py_DECREF(keep); - } else if (setfunc != _ctypes_get_fielddesc("O")->setfunc) { - if (-1 == PyErr_WarnEx(PyExc_RuntimeWarning, - "memory leak in callback function.", - 1)) - { + if (keep == Py_None) { + /* Nothing to keep */ + Py_DECREF(keep); + } + else if (PyErr_WarnEx(PyExc_RuntimeWarning, + "memory leak in callback function.", + 1) == -1) { _PyErr_WriteUnraisableMsg("on converting result " "of ctypes callback function", callable); From webhook-mailer at python.org Mon Jan 9 11:06:27 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 09 Jan 2023 16:06:27 -0000 Subject: [Python-checkins] GH-81061: Fix refcount issue when returning `None` from a `ctypes.py_object` callback (GH-13364) Message-ID: https://github.com/python/cpython/commit/2d1128e9eb9be56e3cd105f4a38564ac4c01e68e commit: 2d1128e9eb9be56e3cd105f4a38564ac4c01e68e branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-09T08:06:17-08:00 summary: GH-81061: Fix refcount issue when returning `None` from a `ctypes.py_object` callback (GH-13364) (cherry picked from commit 837ba052672d1a5f85a46c1b6d4b6e7d192af6f3) Co-authored-by: dgelessus files: A Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst M Lib/ctypes/test/test_refcounts.py M Modules/_ctypes/callbacks.c diff --git a/Lib/ctypes/test/test_refcounts.py b/Lib/ctypes/test/test_refcounts.py index f2edfa6400ef..48958cd2a601 100644 --- a/Lib/ctypes/test/test_refcounts.py +++ b/Lib/ctypes/test/test_refcounts.py @@ -97,5 +97,20 @@ def func(a, b): f(1, 2) self.assertEqual(sys.getrefcount(ctypes.c_int), a) + @support.refcount_test + def test_callback_py_object_none_return(self): + # bpo-36880: test that returning None from a py_object callback + # does not decrement the refcount of None. + + for FUNCTYPE in (ctypes.CFUNCTYPE, ctypes.PYFUNCTYPE): + with self.subTest(FUNCTYPE=FUNCTYPE): + @FUNCTYPE(ctypes.py_object) + def func(): + return None + + # Check that calling func does not affect None's refcount. + for _ in range(10000): + func() + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst b/Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst new file mode 100644 index 000000000000..f65323813d05 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst @@ -0,0 +1,2 @@ +Fix a reference counting issue when a :mod:`ctypes` callback with return +type :class:`~ctypes.py_object` returns ``None``, which could cause crashes. diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index c28762d49ba4..b46067bf2fa6 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -276,15 +276,14 @@ static void _CallPythonObject(void *mem, "of ctypes callback function", callable); } - else if (keep == Py_None) { - /* Nothing to keep */ - Py_DECREF(keep); - } else if (setfunc != _ctypes_get_fielddesc("O")->setfunc) { - if (-1 == PyErr_WarnEx(PyExc_RuntimeWarning, - "memory leak in callback function.", - 1)) - { + if (keep == Py_None) { + /* Nothing to keep */ + Py_DECREF(keep); + } + else if (PyErr_WarnEx(PyExc_RuntimeWarning, + "memory leak in callback function.", + 1) == -1) { _PyErr_WriteUnraisableMsg("on converting result " "of ctypes callback function", callable); From webhook-mailer at python.org Mon Jan 9 12:48:30 2023 From: webhook-mailer at python.org (zooba) Date: Mon, 09 Jan 2023 17:48:30 -0000 Subject: [Python-checkins] gh-99191: Use correct check for MSVC C++ version support in _wmimodule.cpp (GH-100381) Message-ID: https://github.com/python/cpython/commit/f08209874e58d0adbb08bd1dba4f58ba63f571c5 commit: f08209874e58d0adbb08bd1dba4f58ba63f571c5 branch: main author: C.A.M. Gerlach committer: zooba date: 2023-01-09T17:48:24Z summary: gh-99191: Use correct check for MSVC C++ version support in _wmimodule.cpp (GH-100381) files: A Misc/NEWS.d/next/Windows/2022-12-20-18-36-17.gh-issue-99191.0cfRja.rst M PC/_wmimodule.cpp diff --git a/Misc/NEWS.d/next/Windows/2022-12-20-18-36-17.gh-issue-99191.0cfRja.rst b/Misc/NEWS.d/next/Windows/2022-12-20-18-36-17.gh-issue-99191.0cfRja.rst new file mode 100644 index 000000000000..57a95ab3ffcd --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2022-12-20-18-36-17.gh-issue-99191.0cfRja.rst @@ -0,0 +1,2 @@ +Use ``_MSVC_LANG >= 202002L`` instead of less-precise ``_MSC_VER >=1929`` +to more accurately test for C++20 support in :file:`PC/_wmimodule.cpp`. diff --git a/PC/_wmimodule.cpp b/PC/_wmimodule.cpp index de22049dd33f..310aa86d94d9 100644 --- a/PC/_wmimodule.cpp +++ b/PC/_wmimodule.cpp @@ -17,7 +17,7 @@ #include -#if _MSC_VER >= 1929 +#if _MSVC_LANG >= 202002L // We can use clinic directly when the C++ compiler supports C++20 #include "clinic/_wmimodule.cpp.h" #else @@ -96,9 +96,9 @@ _query_thread(LPVOID param) } if (SUCCEEDED(hr)) { hr = services->ExecQuery( - bstr_t("WQL"), + bstr_t("WQL"), bstrQuery, - WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &enumerator ); From webhook-mailer at python.org Mon Jan 9 14:20:32 2023 From: webhook-mailer at python.org (pablogsal) Date: Mon, 09 Jan 2023 19:20:32 -0000 Subject: [Python-checkins] gh-100882: Improve `test_pickling` case in `test_ast.py` (#100883) Message-ID: https://github.com/python/cpython/commit/2e80c2a976c13dcb69a654b386164dca362295a3 commit: 2e80c2a976c13dcb69a654b386164dca362295a3 branch: main author: Nikita Sobolev committer: pablogsal date: 2023-01-09T19:20:25Z summary: gh-100882: Improve `test_pickling` case in `test_ast.py` (#100883) files: M Lib/test/test_ast.py diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index ab6a63faa590..53a6418329e5 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -639,18 +639,11 @@ def test_no_fields(self): def test_pickling(self): import pickle - mods = [pickle] - try: - import cPickle - mods.append(cPickle) - except ImportError: - pass - protocols = [0, 1, 2] - for mod in mods: - for protocol in protocols: - for ast in (compile(i, "?", "exec", 0x400) for i in exec_tests): - ast2 = mod.loads(mod.dumps(ast, protocol)) - self.assertEqual(to_tuple(ast2), to_tuple(ast)) + + for protocol in range(pickle.HIGHEST_PROTOCOL + 1): + for ast in (compile(i, "?", "exec", 0x400) for i in exec_tests): + ast2 = pickle.loads(pickle.dumps(ast, protocol)) + self.assertEqual(to_tuple(ast2), to_tuple(ast)) def test_invalid_sum(self): pos = dict(lineno=2, col_offset=3) From webhook-mailer at python.org Mon Jan 9 15:20:10 2023 From: webhook-mailer at python.org (brandtbucher) Date: Mon, 09 Jan 2023 20:20:10 -0000 Subject: [Python-checkins] GH-100126: Skip incomplete frames in more places (GH-100613) Message-ID: https://github.com/python/cpython/commit/61762b93871419b34f02d83cee5ca0d94d4a2903 commit: 61762b93871419b34f02d83cee5ca0d94d4a2903 branch: main author: Brandt Bucher committer: brandtbucher date: 2023-01-09T12:20:04-08:00 summary: GH-100126: Skip incomplete frames in more places (GH-100613) files: A Misc/NEWS.d/next/Core and Builtins/2022-12-29-04-39-38.gh-issue-100126.pfFJd-.rst M Include/internal/pycore_frame.h M Lib/test/test_frame.py M Modules/_tracemalloc.c M Modules/signalmodule.c M Objects/frameobject.c M Objects/genobject.c M Objects/typeobject.c M Python/ceval.c M Python/frame.c M Python/pystate.c M Python/sysmodule.c diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 4e2051f23f9b..f12b225ebfcc 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -166,6 +166,21 @@ _PyFrame_IsIncomplete(_PyInterpreterFrame *frame) frame->prev_instr < _PyCode_CODE(frame->f_code) + frame->f_code->_co_firsttraceable; } +static inline _PyInterpreterFrame * +_PyFrame_GetFirstComplete(_PyInterpreterFrame *frame) +{ + while (frame && _PyFrame_IsIncomplete(frame)) { + frame = frame->previous; + } + return frame; +} + +static inline _PyInterpreterFrame * +_PyThreadState_GetFrame(PyThreadState *tstate) +{ + return _PyFrame_GetFirstComplete(tstate->cframe->current_frame); +} + /* For use by _PyFrame_GetFrameObject Do not call directly. */ PyFrameObject * diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py index 40c734b6e33a..6bb0144e9b1e 100644 --- a/Lib/test/test_frame.py +++ b/Lib/test/test_frame.py @@ -1,4 +1,5 @@ import gc +import operator import re import sys import textwrap @@ -372,6 +373,26 @@ def run(self): ) sneaky_frame_object = sneaky_frame_object.f_back + def test_entry_frames_are_invisible_during_teardown(self): + class C: + """A weakref'able class.""" + + def f(): + """Try to find globals and locals as this frame is being cleared.""" + ref = C() + # Ignore the fact that exec(C()) is a nonsense callback. We're only + # using exec here because it tries to access the current frame's + # globals and locals. If it's trying to get those from a shim frame, + # we'll crash before raising: + return weakref.ref(ref, exec) + + with support.catch_unraisable_exception() as catcher: + # Call from C, so there is a shim frame directly above f: + weak = operator.call(f) # BOOM! + # Cool, we didn't crash. Check that the callback actually happened: + self.assertIs(catcher.unraisable.exc_type, TypeError) + self.assertIsNone(weak()) + @unittest.skipIf(_testcapi is None, 'need _testcapi') class TestCAPI(unittest.TestCase): def getframe(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-29-04-39-38.gh-issue-100126.pfFJd-.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-29-04-39-38.gh-issue-100126.pfFJd-.rst new file mode 100644 index 000000000000..0ec14253ceff --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-12-29-04-39-38.gh-issue-100126.pfFJd-.rst @@ -0,0 +1,3 @@ +Fix an issue where "incomplete" frames could be briefly visible to C code +while other frames are being torn down, possibly resulting in corruption or +hard crashes of the interpreter while running finalizers. diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index ac16626f2101..9826ad2935be 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -347,14 +347,8 @@ traceback_get_frames(traceback_t *traceback) return; } - _PyInterpreterFrame *pyframe = tstate->cframe->current_frame; - for (;;) { - while (pyframe && _PyFrame_IsIncomplete(pyframe)) { - pyframe = pyframe->previous; - } - if (pyframe == NULL) { - break; - } + _PyInterpreterFrame *pyframe = _PyThreadState_GetFrame(tstate); + while (pyframe) { if (traceback->nframe < tracemalloc_config.max_nframe) { tracemalloc_get_frame(pyframe, &traceback->frames[traceback->nframe]); assert(traceback->frames[traceback->nframe].filename != NULL); @@ -363,8 +357,7 @@ traceback_get_frames(traceback_t *traceback) if (traceback->total_nframe < UINT16_MAX) { traceback->total_nframe++; } - - pyframe = pyframe->previous; + pyframe = _PyFrame_GetFirstComplete(pyframe->previous); } } diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 538a7e85bc95..44a5ecf63e9d 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -1803,10 +1803,7 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) */ _Py_atomic_store(&is_tripped, 0); - _PyInterpreterFrame *frame = tstate->cframe->current_frame; - while (frame && _PyFrame_IsIncomplete(frame)) { - frame = frame->previous; - } + _PyInterpreterFrame *frame = _PyThreadState_GetFrame(tstate); signal_state_t *state = &signal_global_state; for (int i = 1; i < Py_NSIG; i++) { if (!_Py_atomic_load_relaxed(&Handlers[i].tripped)) { diff --git a/Objects/frameobject.c b/Objects/frameobject.c index ebe3bfe76d5d..39ccca70f9cb 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1405,9 +1405,7 @@ PyFrame_GetBack(PyFrameObject *frame) PyFrameObject *back = frame->f_back; if (back == NULL) { _PyInterpreterFrame *prev = frame->f_frame->previous; - while (prev && _PyFrame_IsIncomplete(prev)) { - prev = prev->previous; - } + prev = _PyFrame_GetFirstComplete(prev); if (prev) { back = _PyFrame_GetFrameObject(prev); } diff --git a/Objects/genobject.c b/Objects/genobject.c index 6f4046eaa0ef..2adb1e4bf308 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -903,8 +903,11 @@ _Py_MakeCoro(PyFunctionObject *func) if (origin_depth == 0) { ((PyCoroObject *)coro)->cr_origin_or_finalizer = NULL; } else { - assert(_PyEval_GetFrame()); - PyObject *cr_origin = compute_cr_origin(origin_depth, _PyEval_GetFrame()->previous); + _PyInterpreterFrame *frame = tstate->cframe->current_frame; + assert(frame); + assert(_PyFrame_IsIncomplete(frame)); + frame = _PyFrame_GetFirstComplete(frame->previous); + PyObject *cr_origin = compute_cr_origin(origin_depth, frame); ((PyCoroObject *)coro)->cr_origin_or_finalizer = cr_origin; if (!cr_origin) { Py_DECREF(coro); @@ -1286,7 +1289,7 @@ compute_cr_origin(int origin_depth, _PyInterpreterFrame *current_frame) /* First count how many frames we have */ int frame_count = 0; for (; frame && frame_count < origin_depth; ++frame_count) { - frame = frame->previous; + frame = _PyFrame_GetFirstComplete(frame->previous); } /* Now collect them */ @@ -1305,7 +1308,7 @@ compute_cr_origin(int origin_depth, _PyInterpreterFrame *current_frame) return NULL; } PyTuple_SET_ITEM(cr_origin, i, frameinfo); - frame = frame->previous; + frame = _PyFrame_GetFirstComplete(frame->previous); } return cr_origin; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index f2d78cf50913..e4da5b24006d 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -9578,13 +9578,13 @@ super_init_impl(PyObject *self, PyTypeObject *type, PyObject *obj) { /* Call super(), without args -- fill in from __class__ and first local variable on the stack. */ PyThreadState *tstate = _PyThreadState_GET(); - _PyInterpreterFrame *cframe = tstate->cframe->current_frame; - if (cframe == NULL) { + _PyInterpreterFrame *frame = _PyThreadState_GetFrame(tstate); + if (frame == NULL) { PyErr_SetString(PyExc_RuntimeError, "super(): no current frame"); return -1; } - int res = super_init_without_args(cframe, cframe->f_code, &type, &obj); + int res = super_init_without_args(frame, frame->f_code, &type, &obj); if (res < 0) { return -1; diff --git a/Python/ceval.c b/Python/ceval.c index 56cd9ad62963..7deee76cc5b8 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2749,16 +2749,13 @@ _PyInterpreterFrame * _PyEval_GetFrame(void) { PyThreadState *tstate = _PyThreadState_GET(); - return tstate->cframe->current_frame; + return _PyThreadState_GetFrame(tstate); } PyFrameObject * PyEval_GetFrame(void) { _PyInterpreterFrame *frame = _PyEval_GetFrame(); - while (frame && _PyFrame_IsIncomplete(frame)) { - frame = frame->previous; - } if (frame == NULL) { return NULL; } @@ -2772,7 +2769,7 @@ PyEval_GetFrame(void) PyObject * _PyEval_GetBuiltins(PyThreadState *tstate) { - _PyInterpreterFrame *frame = tstate->cframe->current_frame; + _PyInterpreterFrame *frame = _PyThreadState_GetFrame(tstate); if (frame != NULL) { return frame->f_builtins; } @@ -2811,7 +2808,7 @@ PyObject * PyEval_GetLocals(void) { PyThreadState *tstate = _PyThreadState_GET(); - _PyInterpreterFrame *current_frame = tstate->cframe->current_frame; + _PyInterpreterFrame *current_frame = _PyThreadState_GetFrame(tstate); if (current_frame == NULL) { _PyErr_SetString(tstate, PyExc_SystemError, "frame does not exist"); return NULL; @@ -2830,7 +2827,7 @@ PyObject * PyEval_GetGlobals(void) { PyThreadState *tstate = _PyThreadState_GET(); - _PyInterpreterFrame *current_frame = tstate->cframe->current_frame; + _PyInterpreterFrame *current_frame = _PyThreadState_GetFrame(tstate); if (current_frame == NULL) { return NULL; } diff --git a/Python/frame.c b/Python/frame.c index b1525cca5112..6a287d472405 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -96,10 +96,7 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) } assert(!_PyFrame_IsIncomplete(frame)); assert(f->f_back == NULL); - _PyInterpreterFrame *prev = frame->previous; - while (prev && _PyFrame_IsIncomplete(prev)) { - prev = prev->previous; - } + _PyInterpreterFrame *prev = _PyFrame_GetFirstComplete(frame->previous); frame->previous = NULL; if (prev) { assert(prev->owner != FRAME_OWNED_BY_CSTACK); diff --git a/Python/pystate.c b/Python/pystate.c index f52fc38b3586..f2f571faf401 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1302,10 +1302,7 @@ PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) { assert(tstate != NULL); - _PyInterpreterFrame *f = tstate->cframe->current_frame; - while (f && _PyFrame_IsIncomplete(f)) { - f = f->previous; - } + _PyInterpreterFrame *f = _PyThreadState_GetFrame(tstate); if (f == NULL) { return NULL; } @@ -1431,9 +1428,7 @@ _PyThread_CurrentFrames(void) PyThreadState *t; for (t = i->threads.head; t != NULL; t = t->next) { _PyInterpreterFrame *frame = t->cframe->current_frame; - while (frame && _PyFrame_IsIncomplete(frame)) { - frame = frame->previous; - } + frame = _PyFrame_GetFirstComplete(frame); if (frame == NULL) { continue; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 3f0baf98890b..acee794864f9 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1884,13 +1884,10 @@ sys__getframe_impl(PyObject *module, int depth) if (frame != NULL) { while (depth > 0) { - frame = frame->previous; + frame = _PyFrame_GetFirstComplete(frame->previous); if (frame == NULL) { break; } - if (_PyFrame_IsIncomplete(frame)) { - continue; - } --depth; } } From webhook-mailer at python.org Mon Jan 9 16:43:15 2023 From: webhook-mailer at python.org (zooba) Date: Mon, 09 Jan 2023 21:43:15 -0000 Subject: [Python-checkins] bpo-28356: Document os.rename() behavior on Windows for differing volumes (GH-27376) Message-ID: https://github.com/python/cpython/commit/e098137cd3250af05f19380590b8dec79dc5942f commit: e098137cd3250af05f19380590b8dec79dc5942f branch: main author: Ryan Ozawa committer: zooba date: 2023-01-09T21:43:09Z summary: bpo-28356: Document os.rename() behavior on Windows for differing volumes (GH-27376) files: M Doc/library/os.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index b06f9bbcd831..fb091176767f 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -2467,6 +2467,8 @@ features: will fail with an :exc:`OSError` subclass in a number of cases: On Windows, if *dst* exists a :exc:`FileExistsError` is always raised. + The operation may fail if *src* and *dst* are on different filesystems. Use + :func:`shutil.move` to support moves to a different filesystem. On Unix, if *src* is a file and *dst* is a directory or vice-versa, an :exc:`IsADirectoryError` or a :exc:`NotADirectoryError` will be raised From webhook-mailer at python.org Mon Jan 9 16:52:29 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 09 Jan 2023 21:52:29 -0000 Subject: [Python-checkins] bpo-28356: Document os.rename() behavior on Windows for differing volumes (GH-27376) Message-ID: https://github.com/python/cpython/commit/5c4afdf8bdcbd63f9833ca1223066d767f177001 commit: 5c4afdf8bdcbd63f9833ca1223066d767f177001 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-09T13:52:22-08:00 summary: bpo-28356: Document os.rename() behavior on Windows for differing volumes (GH-27376) (cherry picked from commit e098137cd3250af05f19380590b8dec79dc5942f) Co-authored-by: Ryan Ozawa files: M Doc/library/os.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 7a5efcf5d79b..e5fc63afdcf5 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -2302,6 +2302,8 @@ features: will fail with an :exc:`OSError` subclass in a number of cases: On Windows, if *dst* exists a :exc:`FileExistsError` is always raised. + The operation may fail if *src* and *dst* are on different filesystems. Use + :func:`shutil.move` to support moves to a different filesystem. On Unix, if *src* is a file and *dst* is a directory or vice-versa, an :exc:`IsADirectoryError` or a :exc:`NotADirectoryError` will be raised From webhook-mailer at python.org Mon Jan 9 16:52:49 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 09 Jan 2023 21:52:49 -0000 Subject: [Python-checkins] bpo-28356: Document os.rename() behavior on Windows for differing volumes (GH-27376) Message-ID: https://github.com/python/cpython/commit/4e096ec647a3500b72aae30de3ce6cef3b96b606 commit: 4e096ec647a3500b72aae30de3ce6cef3b96b606 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-09T13:52:43-08:00 summary: bpo-28356: Document os.rename() behavior on Windows for differing volumes (GH-27376) (cherry picked from commit e098137cd3250af05f19380590b8dec79dc5942f) Co-authored-by: Ryan Ozawa files: M Doc/library/os.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index d119e8dc5e84..759064a6e52e 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -2372,6 +2372,8 @@ features: will fail with an :exc:`OSError` subclass in a number of cases: On Windows, if *dst* exists a :exc:`FileExistsError` is always raised. + The operation may fail if *src* and *dst* are on different filesystems. Use + :func:`shutil.move` to support moves to a different filesystem. On Unix, if *src* is a file and *dst* is a directory or vice-versa, an :exc:`IsADirectoryError` or a :exc:`NotADirectoryError` will be raised From webhook-mailer at python.org Mon Jan 9 18:51:08 2023 From: webhook-mailer at python.org (zooba) Date: Mon, 09 Jan 2023 23:51:08 -0000 Subject: [Python-checkins] gh-100180: Update Windows installer to OpenSSL 1.1.1s (GH-100901) Message-ID: https://github.com/python/cpython/commit/d7ab7149f83e4f194cf0e3a438fb6ca177832c99 commit: d7ab7149f83e4f194cf0e3a438fb6ca177832c99 branch: main author: Steve Dower committer: zooba date: 2023-01-09T23:50:59Z summary: gh-100180: Update Windows installer to OpenSSL 1.1.1s (GH-100901) files: A Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst M PCbuild/get_externals.bat M PCbuild/python.props diff --git a/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst b/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst new file mode 100644 index 000000000000..5b0f42568d92 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst @@ -0,0 +1 @@ +Update Windows installer to OpenSSL 1.1.1s diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 98cca979fdfc..7efdeb2d30a7 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -53,7 +53,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.3 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1q +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1s set libraries=%libraries% sqlite-3.39.4.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.12.1 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.12.1 @@ -77,7 +77,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.3 -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1q +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1s if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.12.1 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/python.props b/PCbuild/python.props index 320d41f4cc20..971c1490d9f1 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -67,8 +67,8 @@ $(ExternalsDir)libffi-3.4.3\ $(ExternalsDir)libffi-3.4.3\$(ArchName)\ $(libffiOutDir)include - $(ExternalsDir)openssl-1.1.1q\ - $(ExternalsDir)openssl-bin-1.1.1q\$(ArchName)\ + $(ExternalsDir)openssl-1.1.1s\ + $(ExternalsDir)openssl-bin-1.1.1s\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.13\ From webhook-mailer at python.org Mon Jan 9 18:53:07 2023 From: webhook-mailer at python.org (gvanrossum) Date: Mon, 09 Jan 2023 23:53:07 -0000 Subject: [Python-checkins] GH-98831: Refactor instr format code and change to enum (#100895) Message-ID: https://github.com/python/cpython/commit/3f3c78e32fc67766232ee4ddf17b560c9836a2d1 commit: 3f3c78e32fc67766232ee4ddf17b560c9836a2d1 branch: main author: Guido van Rossum committer: gvanrossum date: 2023-01-09T15:53:01-08:00 summary: GH-98831: Refactor instr format code and change to enum (#100895) files: M Python/opcode_metadata.h M Tools/cases_generator/generate_cases.py diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index fd6f6164888d..34f8145828c2 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -2,6 +2,7 @@ // from Python/bytecodes.c // Do not edit! enum Direction { DIR_NONE, DIR_READ, DIR_WRITE }; +enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBC0IB, INSTR_FMT_IBIB }; static const struct { short n_popped; short n_pushed; @@ -9,173 +10,173 @@ static const struct { enum Direction dir_op2; enum Direction dir_op3; bool valid_entry; - char instr_format[10]; + enum InstructionFormat instr_format; } _PyOpcode_opcode_metadata[256] = { - [NOP] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [RESUME] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_CLOSURE] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_FAST_CHECK] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_FAST] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_CONST] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [STORE_FAST] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_FAST__LOAD_FAST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBIB" }, - [LOAD_FAST__LOAD_CONST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBIB" }, - [STORE_FAST__LOAD_FAST] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBIB" }, - [STORE_FAST__STORE_FAST] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBIB" }, - [LOAD_CONST__LOAD_FAST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBIB" }, - [POP_TOP] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [PUSH_NULL] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [END_FOR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [UNARY_NEGATIVE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [UNARY_NOT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [UNARY_INVERT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [BINARY_OP_MULTIPLY_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, - [BINARY_OP_MULTIPLY_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, - [BINARY_OP_SUBTRACT_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, - [BINARY_OP_SUBTRACT_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, - [BINARY_OP_ADD_UNICODE] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, - [BINARY_OP_INPLACE_ADD_UNICODE] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [BINARY_OP_ADD_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, - [BINARY_OP_ADD_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, - [BINARY_SUBSCR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, - [BINARY_SLICE] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [STORE_SLICE] = { 4, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [BINARY_SUBSCR_LIST_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, - [BINARY_SUBSCR_TUPLE_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, - [BINARY_SUBSCR_DICT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, - [BINARY_SUBSCR_GETITEM] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, - [LIST_APPEND] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [SET_ADD] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [STORE_SUBSCR] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, - [STORE_SUBSCR_LIST_INT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, - [STORE_SUBSCR_DICT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, - [DELETE_SUBSCR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_INTRINSIC_1] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [RAISE_VARARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [INTERPRETER_EXIT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [RETURN_VALUE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [GET_AITER] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [GET_ANEXT] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [GET_AWAITABLE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [SEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [YIELD_VALUE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [POP_EXCEPT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [RERAISE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [PREP_RERAISE_STAR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [END_ASYNC_FOR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CLEANUP_THROW] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_ASSERTION_ERROR] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_BUILD_CLASS] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [STORE_NAME] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [DELETE_NAME] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [UNPACK_SEQUENCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [UNPACK_SEQUENCE_TWO_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [UNPACK_SEQUENCE_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [UNPACK_SEQUENCE_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [UNPACK_EX] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [STORE_ATTR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, - [DELETE_ATTR] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [STORE_GLOBAL] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [DELETE_GLOBAL] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_NAME] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_GLOBAL] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_GLOBAL_MODULE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_GLOBAL_BUILTIN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [DELETE_FAST] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [MAKE_CELL] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [DELETE_DEREF] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_CLASSDEREF] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_DEREF] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [STORE_DEREF] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [COPY_FREE_VARS] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [BUILD_STRING] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [BUILD_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [BUILD_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LIST_EXTEND] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [SET_UPDATE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [BUILD_SET] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [BUILD_MAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [SETUP_ANNOTATIONS] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [BUILD_CONST_KEY_MAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [DICT_UPDATE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [DICT_MERGE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [MAP_ADD] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_ATTR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_ATTR_INSTANCE_VALUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_ATTR_MODULE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_ATTR_WITH_HINT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_ATTR_SLOT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_ATTR_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_ATTR_PROPERTY] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [STORE_ATTR_INSTANCE_VALUE] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, - [STORE_ATTR_WITH_HINT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, - [STORE_ATTR_SLOT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC000" }, - [COMPARE_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC0" }, - [COMPARE_OP_FLOAT_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC0IB" }, - [COMPARE_OP_INT_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC0IB" }, - [COMPARE_OP_STR_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC0IB" }, - [IS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CONTAINS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CHECK_EG_MATCH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CHECK_EXC_MATCH] = { 2, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [IMPORT_NAME] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [IMPORT_FROM] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [JUMP_FORWARD] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [JUMP_BACKWARD] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [POP_JUMP_IF_FALSE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [POP_JUMP_IF_TRUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [POP_JUMP_IF_NOT_NONE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [POP_JUMP_IF_NONE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [JUMP_IF_FALSE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [JUMP_IF_TRUE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [JUMP_BACKWARD_NO_INTERRUPT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [GET_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [MATCH_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [MATCH_MAPPING] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [MATCH_SEQUENCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [MATCH_KEYS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [GET_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [GET_YIELD_FROM_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [FOR_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [FOR_ITER_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [FOR_ITER_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [FOR_ITER_RANGE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [FOR_ITER_GEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [BEFORE_ASYNC_WITH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [BEFORE_WITH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [WITH_EXCEPT_START] = { 4, 5, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [PUSH_EXC_INFO] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_ATTR_METHOD_WITH_VALUES] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_ATTR_METHOD_NO_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [LOAD_ATTR_METHOD_LAZY_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_BOUND_METHOD_EXACT_ARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [KW_NAMES] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_PY_EXACT_ARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_PY_WITH_DEFAULTS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_NO_KW_TYPE_1] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_NO_KW_STR_1] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_NO_KW_TUPLE_1] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_BUILTIN_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_NO_KW_BUILTIN_O] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_NO_KW_BUILTIN_FAST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_NO_KW_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_NO_KW_ISINSTANCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_NO_KW_LIST_APPEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CALL_FUNCTION_EX] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [MAKE_FUNCTION] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [RETURN_GENERATOR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [BUILD_SLICE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [FORMAT_VALUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [COPY] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [BINARY_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IBC" }, - [SWAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [EXTENDED_ARG] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, - [CACHE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, "IB" }, + [NOP] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [RESUME] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_CLOSURE] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_FAST_CHECK] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_FAST] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_CONST] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_FAST] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_FAST__LOAD_FAST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, + [LOAD_FAST__LOAD_CONST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, + [STORE_FAST__LOAD_FAST] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, + [STORE_FAST__STORE_FAST] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, + [LOAD_CONST__LOAD_FAST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, + [POP_TOP] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [PUSH_NULL] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [END_FOR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNARY_NEGATIVE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNARY_NOT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNARY_INVERT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BINARY_OP_MULTIPLY_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [BINARY_OP_MULTIPLY_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [BINARY_OP_SUBTRACT_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [BINARY_OP_SUBTRACT_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [BINARY_OP_ADD_UNICODE] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [BINARY_OP_INPLACE_ADD_UNICODE] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BINARY_OP_ADD_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [BINARY_OP_ADD_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [BINARY_SUBSCR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [BINARY_SLICE] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_SLICE] = { 4, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BINARY_SUBSCR_LIST_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [BINARY_SUBSCR_TUPLE_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [BINARY_SUBSCR_DICT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [BINARY_SUBSCR_GETITEM] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [LIST_APPEND] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SET_ADD] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_SUBSCR] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [STORE_SUBSCR_LIST_INT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [STORE_SUBSCR_DICT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [DELETE_SUBSCR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_INTRINSIC_1] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [RAISE_VARARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [INTERPRETER_EXIT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [RETURN_VALUE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [GET_AITER] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [GET_ANEXT] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [GET_AWAITABLE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [YIELD_VALUE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [POP_EXCEPT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [RERAISE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [PREP_RERAISE_STAR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [END_ASYNC_FOR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CLEANUP_THROW] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ASSERTION_ERROR] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_BUILD_CLASS] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_NAME] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DELETE_NAME] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_SEQUENCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_SEQUENCE_TWO_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_SEQUENCE_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_SEQUENCE_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_EX] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_ATTR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [DELETE_ATTR] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_GLOBAL] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DELETE_GLOBAL] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_NAME] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_GLOBAL] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_GLOBAL_MODULE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_GLOBAL_BUILTIN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DELETE_FAST] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MAKE_CELL] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DELETE_DEREF] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_CLASSDEREF] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_DEREF] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_DEREF] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [COPY_FREE_VARS] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_STRING] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LIST_EXTEND] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SET_UPDATE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_SET] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_MAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SETUP_ANNOTATIONS] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_CONST_KEY_MAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DICT_UPDATE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DICT_MERGE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MAP_ADD] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_INSTANCE_VALUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_MODULE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_WITH_HINT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_SLOT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_PROPERTY] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_ATTR_INSTANCE_VALUE] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [STORE_ATTR_WITH_HINT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [STORE_ATTR_SLOT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [COMPARE_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, + [COMPARE_OP_FLOAT_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0IB }, + [COMPARE_OP_INT_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0IB }, + [COMPARE_OP_STR_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0IB }, + [IS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CONTAINS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CHECK_EG_MATCH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CHECK_EXC_MATCH] = { 2, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [IMPORT_NAME] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [IMPORT_FROM] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [JUMP_FORWARD] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [JUMP_BACKWARD] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [POP_JUMP_IF_FALSE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [POP_JUMP_IF_TRUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [POP_JUMP_IF_NOT_NONE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [POP_JUMP_IF_NONE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [JUMP_IF_FALSE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [JUMP_IF_TRUE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [JUMP_BACKWARD_NO_INTERRUPT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [GET_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MATCH_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MATCH_MAPPING] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MATCH_SEQUENCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MATCH_KEYS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [GET_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [GET_YIELD_FROM_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_RANGE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_GEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BEFORE_ASYNC_WITH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BEFORE_WITH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [WITH_EXCEPT_START] = { 4, 5, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [PUSH_EXC_INFO] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_METHOD_WITH_VALUES] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_METHOD_NO_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_METHOD_LAZY_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_BOUND_METHOD_EXACT_ARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [KW_NAMES] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_PY_EXACT_ARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_PY_WITH_DEFAULTS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_TYPE_1] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_STR_1] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_TUPLE_1] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_BUILTIN_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_BUILTIN_O] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_BUILTIN_FAST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_ISINSTANCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_LIST_APPEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_FUNCTION_EX] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MAKE_FUNCTION] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [RETURN_GENERATOR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_SLICE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FORMAT_VALUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [COPY] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BINARY_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [SWAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [EXTENDED_ARG] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CACHE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, }; diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 7452b2ced052..608df6bb3aa4 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -133,14 +133,16 @@ class Instruction: cache_effects: list[parser.CacheEffect] input_effects: list[StackEffect] output_effects: list[StackEffect] - # Parallel to input_effects + unmoved_names: frozenset[str] + instr_fmt: str + + # Parallel to input_effects; set later input_registers: list[str] = dataclasses.field(repr=False) output_registers: list[str] = dataclasses.field(repr=False) # Set later family: parser.Family | None = None predicted: bool = False - unmoved_names: frozenset[str] = frozenset() def __init__(self, inst: parser.InstDef): self.inst = inst @@ -165,6 +167,16 @@ def __init__(self, inst: parser.InstDef): else: break self.unmoved_names = frozenset(unmoved_names) + if self.register: + fmt = "IBBB" + else: + fmt = "IB" + cache = "C" + for ce in self.cache_effects: + for _ in range(ce.size): + fmt += cache + cache = "0" + self.instr_fmt = fmt def analyze_registers(self, a: "Analyzer") -> None: regs = iter(("REG(oparg1)", "REG(oparg2)", "REG(oparg3)")) @@ -327,6 +339,7 @@ class SuperOrMacroInstruction: stack: list[StackEffect] initial_sp: int final_sp: int + instr_fmt: str @dataclasses.dataclass @@ -345,6 +358,9 @@ class MacroInstruction(SuperOrMacroInstruction): parts: list[Component | parser.CacheEffect] +INSTR_FMT_PREFIX = "INSTR_FMT_" + + class Analyzer: """Parse input, analyze it, and write to output.""" @@ -529,28 +545,39 @@ def analyze_super(self, super: parser.Super) -> SuperInstruction: stack, initial_sp = self.stack_analysis(components) sp = initial_sp parts: list[Component] = [] + format = "" for instr in components: part, sp = self.analyze_instruction(instr, stack, sp) parts.append(part) + format += instr.instr_fmt final_sp = sp - return SuperInstruction(super.name, stack, initial_sp, final_sp, super, parts) + return SuperInstruction(super.name, stack, initial_sp, final_sp, format, super, parts) def analyze_macro(self, macro: parser.Macro) -> MacroInstruction: components = self.check_macro_components(macro) stack, initial_sp = self.stack_analysis(components) sp = initial_sp parts: list[Component | parser.CacheEffect] = [] + format = "IB" # Macros don't support register instructions yet + cache = "C" for component in components: match component: case parser.CacheEffect() as ceffect: parts.append(ceffect) + for _ in range(ceffect.size): + format += cache + cache = "0" case Instruction() as instr: part, sp = self.analyze_instruction(instr, stack, sp) parts.append(part) + for ce in instr.cache_effects: + for _ in range(ce.size): + format += cache + cache = "0" case _: typing.assert_never(component) final_sp = sp - return MacroInstruction(macro.name, stack, initial_sp, final_sp, macro, parts) + return MacroInstruction(macro.name, stack, initial_sp, final_sp, format, macro, parts) def analyze_instruction( self, instr: Instruction, stack: list[StackEffect], sp: int @@ -622,6 +649,23 @@ def stack_analysis( def write_metadata(self) -> None: """Write instruction metadata to output file.""" + + # Compute the set of all instruction formats. + all_formats: set[str] = set() + for thing in self.everything: + match thing: + case parser.InstDef(): + format = self.instrs[thing.name].instr_fmt + case parser.Super(): + format = self.super_instrs[thing.name].instr_fmt + case parser.Macro(): + format = self.macro_instrs[thing.name].instr_fmt + case _: + typing.assert_never(thing) + all_formats.add(format) + # Turn it into a list of enum definitions. + format_enums = [INSTR_FMT_PREFIX + format for format in sorted(all_formats)] + with open(self.output_filename, "w") as f: # Write provenance header f.write( @@ -635,6 +679,7 @@ def write_metadata(self) -> None: # Write variable definition self.out.emit("enum Direction { DIR_NONE, DIR_READ, DIR_WRITE };") + self.out.emit(f"enum InstructionFormat {{ {', '.join(format_enums)} }};") self.out.emit("static const struct {") with self.out.indent(): self.out.emit("short n_popped;") @@ -643,7 +688,7 @@ def write_metadata(self) -> None: self.out.emit("enum Direction dir_op2;") self.out.emit("enum Direction dir_op3;") self.out.emit("bool valid_entry;") - self.out.emit("char instr_format[10];") + self.out.emit("enum InstructionFormat instr_format;") self.out.emit("} _PyOpcode_opcode_metadata[256] = {") # Write metadata for each instruction @@ -662,46 +707,6 @@ def write_metadata(self) -> None: # Write end of array self.out.emit("};") - def get_format(self, thing: Instruction | SuperInstruction | MacroInstruction) -> str: - """Get the format string for a single instruction.""" - def instr_format(instr: Instruction) -> str: - if instr.register: - fmt = "IBBB" - else: - fmt = "IB" - cache = "C" - for ce in instr.cache_effects: - for _ in range(ce.size): - fmt += cache - cache = "0" - return fmt - match thing: - case Instruction(): - format = instr_format(thing) - case SuperInstruction(): - format = "" - for part in thing.parts: - format += instr_format(part.instr) - case MacroInstruction(): - # Macros don't support register instructions yet - format = "IB" - cache = "C" - for part in thing.parts: - if isinstance(part, parser.CacheEffect): - for _ in range(part.size): - format += cache - cache = "0" - else: - assert isinstance(part, Component) - for ce in part.instr.cache_effects: - for _ in range(ce.size): - format += cache - cache = "0" - case _: - typing.assert_never(thing) - assert len(format) < 10 # Else update the size of instr_format above - return format - def write_metadata_for_inst(self, instr: Instruction) -> None: """Write metadata for a single instruction.""" dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" @@ -717,9 +722,8 @@ def write_metadata_for_inst(self, instr: Instruction) -> None: directions.extend("DIR_WRITE" for _ in instr.output_effects) directions.extend("DIR_NONE" for _ in range(3)) dir_op1, dir_op2, dir_op3 = directions[:3] - format = self.get_format(instr) self.out.emit( - f' [{instr.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, "{format}" }},' + f' [{instr.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{instr.instr_fmt} }},' ) def write_metadata_for_super(self, sup: SuperInstruction) -> None: @@ -727,9 +731,8 @@ def write_metadata_for_super(self, sup: SuperInstruction) -> None: n_popped = sum(len(comp.instr.input_effects) for comp in sup.parts) n_pushed = sum(len(comp.instr.output_effects) for comp in sup.parts) dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" - format = self.get_format(sup) self.out.emit( - f' [{sup.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, "{format}" }},' + f' [{sup.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{sup.instr_fmt} }},' ) def write_metadata_for_macro(self, mac: MacroInstruction) -> None: @@ -738,9 +741,8 @@ def write_metadata_for_macro(self, mac: MacroInstruction) -> None: n_popped = sum(len(comp.instr.input_effects) for comp in parts) n_pushed = sum(len(comp.instr.output_effects) for comp in parts) dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" - format = self.get_format(mac) self.out.emit( - f' [{mac.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, "{format}" }},' + f' [{mac.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{mac.instr_fmt} }},' ) def write_instructions(self) -> None: From webhook-mailer at python.org Mon Jan 9 19:15:37 2023 From: webhook-mailer at python.org (zooba) Date: Tue, 10 Jan 2023 00:15:37 -0000 Subject: [Python-checkins] gh-100180: Update Windows installer to OpenSSL 1.1.1s (GH-100903) Message-ID: https://github.com/python/cpython/commit/40d87ffe4a8f29ca3f981fc63d5a027856327e5b commit: 40d87ffe4a8f29ca3f981fc63d5a027856327e5b branch: 3.10 author: Steve Dower committer: zooba date: 2023-01-10T00:15:31Z summary: gh-100180: Update Windows installer to OpenSSL 1.1.1s (GH-100903) files: A Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst M PCbuild/get_externals.bat M PCbuild/python.props diff --git a/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst b/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst new file mode 100644 index 000000000000..5b0f42568d92 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst @@ -0,0 +1 @@ +Update Windows installer to OpenSSL 1.1.1s diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 57761342ab66..6452bbdd7760 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -53,7 +53,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.3.0 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1q +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1s set libraries=%libraries% sqlite-3.39.4.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.12.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.12.0 @@ -77,7 +77,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.3.0 -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1q +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1s if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.12.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/python.props b/PCbuild/python.props index 56060f798e19..f9dd872a93aa 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -63,8 +63,8 @@ $(ExternalsDir)libffi-3.3.0\ $(ExternalsDir)libffi-3.3.0\$(ArchName)\ $(libffiOutDir)include - $(ExternalsDir)openssl-1.1.1q\ - $(ExternalsDir)openssl-bin-1.1.1q\$(ArchName)\ + $(ExternalsDir)openssl-1.1.1s\ + $(ExternalsDir)openssl-bin-1.1.1s\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.13\ From webhook-mailer at python.org Mon Jan 9 19:52:11 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 10 Jan 2023 00:52:11 -0000 Subject: [Python-checkins] gh-100180: Update Windows installer to OpenSSL 1.1.1s (GH-100901) Message-ID: https://github.com/python/cpython/commit/13453a205bb40b23dc1ef4901714135a54fdd0d3 commit: 13453a205bb40b23dc1ef4901714135a54fdd0d3 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-09T16:51:49-08:00 summary: gh-100180: Update Windows installer to OpenSSL 1.1.1s (GH-100901) (cherry picked from commit d7ab7149f83e4f194cf0e3a438fb6ca177832c99) Co-authored-by: Steve Dower files: A Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst M PCbuild/get_externals.bat M PCbuild/python.props diff --git a/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst b/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst new file mode 100644 index 000000000000..5b0f42568d92 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst @@ -0,0 +1 @@ +Update Windows installer to OpenSSL 1.1.1s diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 98cca979fdfc..7efdeb2d30a7 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -53,7 +53,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.3 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1q +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1s set libraries=%libraries% sqlite-3.39.4.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.12.1 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.12.1 @@ -77,7 +77,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.3 -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1q +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1s if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.12.1 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/python.props b/PCbuild/python.props index 320d41f4cc20..971c1490d9f1 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -67,8 +67,8 @@ $(ExternalsDir)libffi-3.4.3\ $(ExternalsDir)libffi-3.4.3\$(ArchName)\ $(libffiOutDir)include - $(ExternalsDir)openssl-1.1.1q\ - $(ExternalsDir)openssl-bin-1.1.1q\$(ArchName)\ + $(ExternalsDir)openssl-1.1.1s\ + $(ExternalsDir)openssl-bin-1.1.1s\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.13\ From webhook-mailer at python.org Mon Jan 9 22:37:16 2023 From: webhook-mailer at python.org (ned-deily) Date: Tue, 10 Jan 2023 03:37:16 -0000 Subject: [Python-checkins] gh-100893: update bpo reference in Mac README (GH-100905) Message-ID: https://github.com/python/cpython/commit/be23a202426385ad99dcb2611152783780c7bc42 commit: be23a202426385ad99dcb2611152783780c7bc42 branch: main author: Ned Deily committer: ned-deily date: 2023-01-09T22:37:10-05:00 summary: gh-100893: update bpo reference in Mac README (GH-100905) files: M Mac/README.rst diff --git a/Mac/README.rst b/Mac/README.rst index bc40b41f7f38..e32566d5475a 100644 --- a/Mac/README.rst +++ b/Mac/README.rst @@ -347,9 +347,9 @@ The configure script sometimes emits warnings like the one below:: configure: WARNING: libintl.h: section "Present But Cannot Be Compiled" configure: WARNING: libintl.h: proceeding with the preprocessor's result configure: WARNING: libintl.h: in the future, the compiler will take precedence - configure: WARNING: ## --------------------------------------- ## - configure: WARNING: ## Report this to https://bugs.python.org/ ## - configure: WARNING: ## --------------------------------------- ## + configure: WARNING: ## -------------------------------------------------------- ## + configure: WARNING: ## Report this to https://github.com/python/cpython/issues/ ## + configure: WARNING: ## -------------------------------------------------------- ## This almost always means you are trying to build a universal binary for Python and have libraries in ``/usr/local`` that don't contain the required From webhook-mailer at python.org Mon Jan 9 22:40:40 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 10 Jan 2023 03:40:40 -0000 Subject: [Python-checkins] gh-100893: update bpo reference in Mac README (GH-100905) Message-ID: https://github.com/python/cpython/commit/03732b1bd96ebb6a81824b4b744fed3cf12ea49e commit: 03732b1bd96ebb6a81824b4b744fed3cf12ea49e branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-09T19:40:34-08:00 summary: gh-100893: update bpo reference in Mac README (GH-100905) (cherry picked from commit be23a202426385ad99dcb2611152783780c7bc42) Co-authored-by: Ned Deily files: M Mac/README.rst diff --git a/Mac/README.rst b/Mac/README.rst index bc40b41f7f38..e32566d5475a 100644 --- a/Mac/README.rst +++ b/Mac/README.rst @@ -347,9 +347,9 @@ The configure script sometimes emits warnings like the one below:: configure: WARNING: libintl.h: section "Present But Cannot Be Compiled" configure: WARNING: libintl.h: proceeding with the preprocessor's result configure: WARNING: libintl.h: in the future, the compiler will take precedence - configure: WARNING: ## --------------------------------------- ## - configure: WARNING: ## Report this to https://bugs.python.org/ ## - configure: WARNING: ## --------------------------------------- ## + configure: WARNING: ## -------------------------------------------------------- ## + configure: WARNING: ## Report this to https://github.com/python/cpython/issues/ ## + configure: WARNING: ## -------------------------------------------------------- ## This almost always means you are trying to build a universal binary for Python and have libraries in ``/usr/local`` that don't contain the required From webhook-mailer at python.org Mon Jan 9 22:42:43 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 10 Jan 2023 03:42:43 -0000 Subject: [Python-checkins] gh-100893: update bpo reference in Mac README (GH-100905) Message-ID: https://github.com/python/cpython/commit/bc87339d1e2de23d3dc3a848784dfffe6559c915 commit: bc87339d1e2de23d3dc3a848784dfffe6559c915 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-09T19:42:38-08:00 summary: gh-100893: update bpo reference in Mac README (GH-100905) (cherry picked from commit be23a202426385ad99dcb2611152783780c7bc42) Co-authored-by: Ned Deily files: M Mac/README.rst diff --git a/Mac/README.rst b/Mac/README.rst index e746ba8734dc..099efbe1e9f2 100644 --- a/Mac/README.rst +++ b/Mac/README.rst @@ -347,9 +347,9 @@ The configure script sometimes emits warnings like the one below:: configure: WARNING: libintl.h: section "Present But Cannot Be Compiled" configure: WARNING: libintl.h: proceeding with the preprocessor's result configure: WARNING: libintl.h: in the future, the compiler will take precedence - configure: WARNING: ## --------------------------------------- ## - configure: WARNING: ## Report this to https://bugs.python.org/ ## - configure: WARNING: ## --------------------------------------- ## + configure: WARNING: ## -------------------------------------------------------- ## + configure: WARNING: ## Report this to https://github.com/python/cpython/issues/ ## + configure: WARNING: ## -------------------------------------------------------- ## This almost always means you are trying to build a universal binary for Python and have libraries in ``/usr/local`` that don't contain the required From webhook-mailer at python.org Tue Jan 10 00:10:37 2023 From: webhook-mailer at python.org (ned-deily) Date: Tue, 10 Jan 2023 05:10:37 -0000 Subject: [Python-checkins] gh-100180: Update macOS installer to OpenSSL 1.1.1s (GH-100908) Message-ID: https://github.com/python/cpython/commit/57a5259a438c717755fc667fcbedb515ef8e0c85 commit: 57a5259a438c717755fc667fcbedb515ef8e0c85 branch: main author: Ned Deily committer: ned-deily date: 2023-01-10T00:10:31-05:00 summary: gh-100180: Update macOS installer to OpenSSL 1.1.1s (GH-100908) files: A Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst D Mac/BuildScript/openssl1.1.1q-pr-18719.patch M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index b789e30946a8..cf97b5558c2d 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -246,10 +246,9 @@ def library_recipes(): result.extend([ dict( - name="OpenSSL 1.1.1q", - url="https://www.openssl.org/source/openssl-1.1.1q.tar.gz", - checksum='d7939ce614029cdff0b6c20f0e2e5703158a489a72b2507b8bd51bf8c8fd10ca', - patches=['openssl1.1.1q-pr-18719.patch'], + name="OpenSSL 1.1.1s", + url="https://www.openssl.org/source/openssl-1.1.1s.tar.gz", + checksum='c5ac01e760ee6ff0dab61d6b2bbd30146724d063eb322180c6f18a6f74e4b6aa', buildrecipe=build_universal_openssl, configure=None, install=None, diff --git a/Mac/BuildScript/openssl1.1.1q-pr-18719.patch b/Mac/BuildScript/openssl1.1.1q-pr-18719.patch deleted file mode 100644 index 6b4b1883159f..000000000000 --- a/Mac/BuildScript/openssl1.1.1q-pr-18719.patch +++ /dev/null @@ -1,17 +0,0 @@ -https://github.com/openssl/openssl/commit/60f011f584d80447e86cae1d1bd3ae24bc13235b -This fixes a regression in 1.1.1q: - -test/v3ext.c:201:24: error: implicitly declaring library function 'memcmp' with type 'int (const void *, const void *, unsigned long)' [-Werror,-Wimplicit-function-declaration] - if (!TEST_true(memcmp(ip1->data, ip2->data, ip1->length) <= 0)) - -diff -Naur openssl-1.1.1q/test/v3ext.c openssl-1.1.1q-patched/test/v3ext.c ---- openssl-1.1.1q/test/v3ext.c 2022-07-05 09:08:33.000000000 +0000 -+++ openssl-1.1.1q-patched/test/v3ext.c 2022-09-05 16:54:45.740859256 +0000 -@@ -8,6 +8,7 @@ - */ - - #include -+#include - #include - #include - #include diff --git a/Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst b/Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst new file mode 100644 index 000000000000..b7d94a0a94a0 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst @@ -0,0 +1 @@ +Update macOS installer to OpenSSL 1.1.1s From webhook-mailer at python.org Tue Jan 10 00:37:56 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 10 Jan 2023 05:37:56 -0000 Subject: [Python-checkins] gh-100180: Update macOS installer to OpenSSL 1.1.1s (GH-100908) Message-ID: https://github.com/python/cpython/commit/f6decc57f7b31a131275fb03ff9f841c761a2865 commit: f6decc57f7b31a131275fb03ff9f841c761a2865 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-09T21:37:50-08:00 summary: gh-100180: Update macOS installer to OpenSSL 1.1.1s (GH-100908) (cherry picked from commit 57a5259a438c717755fc667fcbedb515ef8e0c85) Co-authored-by: Ned Deily files: A Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst D Mac/BuildScript/openssl1.1.1q-pr-18719.patch M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index a200fb3fc942..73a725cb44f5 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -246,10 +246,9 @@ def library_recipes(): result.extend([ dict( - name="OpenSSL 1.1.1q", - url="https://www.openssl.org/source/openssl-1.1.1q.tar.gz", - checksum='d7939ce614029cdff0b6c20f0e2e5703158a489a72b2507b8bd51bf8c8fd10ca', - patches=['openssl1.1.1q-pr-18719.patch'], + name="OpenSSL 1.1.1s", + url="https://www.openssl.org/source/openssl-1.1.1s.tar.gz", + checksum='c5ac01e760ee6ff0dab61d6b2bbd30146724d063eb322180c6f18a6f74e4b6aa', buildrecipe=build_universal_openssl, configure=None, install=None, diff --git a/Mac/BuildScript/openssl1.1.1q-pr-18719.patch b/Mac/BuildScript/openssl1.1.1q-pr-18719.patch deleted file mode 100644 index 6b4b1883159f..000000000000 --- a/Mac/BuildScript/openssl1.1.1q-pr-18719.patch +++ /dev/null @@ -1,17 +0,0 @@ -https://github.com/openssl/openssl/commit/60f011f584d80447e86cae1d1bd3ae24bc13235b -This fixes a regression in 1.1.1q: - -test/v3ext.c:201:24: error: implicitly declaring library function 'memcmp' with type 'int (const void *, const void *, unsigned long)' [-Werror,-Wimplicit-function-declaration] - if (!TEST_true(memcmp(ip1->data, ip2->data, ip1->length) <= 0)) - -diff -Naur openssl-1.1.1q/test/v3ext.c openssl-1.1.1q-patched/test/v3ext.c ---- openssl-1.1.1q/test/v3ext.c 2022-07-05 09:08:33.000000000 +0000 -+++ openssl-1.1.1q-patched/test/v3ext.c 2022-09-05 16:54:45.740859256 +0000 -@@ -8,6 +8,7 @@ - */ - - #include -+#include - #include - #include - #include diff --git a/Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst b/Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst new file mode 100644 index 000000000000..b7d94a0a94a0 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst @@ -0,0 +1 @@ +Update macOS installer to OpenSSL 1.1.1s From webhook-mailer at python.org Tue Jan 10 00:39:43 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 10 Jan 2023 05:39:43 -0000 Subject: [Python-checkins] gh-100180: Update macOS installer to OpenSSL 1.1.1s (GH-100908) Message-ID: https://github.com/python/cpython/commit/ecb3be91b4e429b5dc81eddd52eb515926be5465 commit: ecb3be91b4e429b5dc81eddd52eb515926be5465 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-09T21:39:38-08:00 summary: gh-100180: Update macOS installer to OpenSSL 1.1.1s (GH-100908) (cherry picked from commit 57a5259a438c717755fc667fcbedb515ef8e0c85) Co-authored-by: Ned Deily files: A Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst D Mac/BuildScript/openssl1.1.1q-pr-18719.patch M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 86eed5ff9569..f756a9943759 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -246,10 +246,9 @@ def library_recipes(): result.extend([ dict( - name="OpenSSL 1.1.1q", - url="https://www.openssl.org/source/openssl-1.1.1q.tar.gz", - checksum='d7939ce614029cdff0b6c20f0e2e5703158a489a72b2507b8bd51bf8c8fd10ca', - patches=['openssl1.1.1q-pr-18719.patch'], + name="OpenSSL 1.1.1s", + url="https://www.openssl.org/source/openssl-1.1.1s.tar.gz", + checksum='c5ac01e760ee6ff0dab61d6b2bbd30146724d063eb322180c6f18a6f74e4b6aa', buildrecipe=build_universal_openssl, configure=None, install=None, diff --git a/Mac/BuildScript/openssl1.1.1q-pr-18719.patch b/Mac/BuildScript/openssl1.1.1q-pr-18719.patch deleted file mode 100644 index 6b4b1883159f..000000000000 --- a/Mac/BuildScript/openssl1.1.1q-pr-18719.patch +++ /dev/null @@ -1,17 +0,0 @@ -https://github.com/openssl/openssl/commit/60f011f584d80447e86cae1d1bd3ae24bc13235b -This fixes a regression in 1.1.1q: - -test/v3ext.c:201:24: error: implicitly declaring library function 'memcmp' with type 'int (const void *, const void *, unsigned long)' [-Werror,-Wimplicit-function-declaration] - if (!TEST_true(memcmp(ip1->data, ip2->data, ip1->length) <= 0)) - -diff -Naur openssl-1.1.1q/test/v3ext.c openssl-1.1.1q-patched/test/v3ext.c ---- openssl-1.1.1q/test/v3ext.c 2022-07-05 09:08:33.000000000 +0000 -+++ openssl-1.1.1q-patched/test/v3ext.c 2022-09-05 16:54:45.740859256 +0000 -@@ -8,6 +8,7 @@ - */ - - #include -+#include - #include - #include - #include diff --git a/Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst b/Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst new file mode 100644 index 000000000000..b7d94a0a94a0 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst @@ -0,0 +1 @@ +Update macOS installer to OpenSSL 1.1.1s From webhook-mailer at python.org Tue Jan 10 04:04:13 2023 From: webhook-mailer at python.org (hugovk) Date: Tue, 10 Jan 2023 09:04:13 -0000 Subject: [Python-checkins] Fix typos in Doc folder (#100880) Message-ID: https://github.com/python/cpython/commit/35650f25383efce83b62d5273110ab8dcdbcc254 commit: 35650f25383efce83b62d5273110ab8dcdbcc254 branch: main author: Semen Zhydenko committer: hugovk date: 2023-01-10T11:04:06+02:00 summary: Fix typos in Doc folder (#100880) files: M Doc/c-api/structures.rst M Doc/library/ctypes.rst M Doc/library/sqlite3.rst diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index be6e7b5faf9f..9618a0cf6769 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -615,7 +615,7 @@ Defining Getters and Setters .. c:member:: getter PyGetSetDef.get - C funtion to get the attribute. + C function to get the attribute. .. c:member:: setter PyGetSetDef.set diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index fd5df875ed74..ac533a939d6a 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -390,7 +390,7 @@ regular, non-variadic, function arguments: libc.printf.argtypes = [ctypes.c_char_p] -Because specifying the attribute does inhibit portability it is adviced to always +Because specifying the attribute does inhibit portability it is advised to always specify ``argtypes`` for all variadic functions. diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 0d929d1297f5..1708cd9d9580 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -2463,7 +2463,7 @@ Transaction control via the ``autocommit`` attribute The recommended way of controlling transaction behaviour is through the :attr:`Connection.autocommit` attribute, -which should preferrably be set using the *autocommit* parameter +which should preferably be set using the *autocommit* parameter of :func:`connect`. It is suggested to set *autocommit* to ``False``, From webhook-mailer at python.org Tue Jan 10 04:42:09 2023 From: webhook-mailer at python.org (hugovk) Date: Tue, 10 Jan 2023 09:42:09 -0000 Subject: [Python-checkins] [3.11] Fix typos in Doc folder (GH-100880). (#100914) Message-ID: https://github.com/python/cpython/commit/1e74a12bde76de203a23edf114941fd46d91e9cd commit: 1e74a12bde76de203a23edf114941fd46d91e9cd branch: 3.11 author: Hugo van Kemenade committer: hugovk date: 2023-01-10T11:42:03+02:00 summary: [3.11] Fix typos in Doc folder (GH-100880). (#100914) Co-authored-by: Semen Zhydenko files: M Doc/library/ctypes.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 5208f254a5a9..282dff38c206 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -389,7 +389,7 @@ regular, non-variadic, function arguments: libc.printf.argtypes = [ctypes.c_char_p] -Because specifying the attribute does inhibit portability it is adviced to always +Because specifying the attribute does inhibit portability it is advised to always specify ``argtypes`` for all variadic functions. From webhook-mailer at python.org Tue Jan 10 04:42:19 2023 From: webhook-mailer at python.org (hugovk) Date: Tue, 10 Jan 2023 09:42:19 -0000 Subject: [Python-checkins] [3.10] Fix typos in Doc folder (GH-100880). (#100915) Message-ID: https://github.com/python/cpython/commit/65b01b23bca905d2b5ef62b03425f40ef522f314 commit: 65b01b23bca905d2b5ef62b03425f40ef522f314 branch: 3.10 author: Hugo van Kemenade committer: hugovk date: 2023-01-10T11:42:14+02:00 summary: [3.10] Fix typos in Doc folder (GH-100880). (#100915) Co-authored-by: Semen Zhydenko files: M Doc/library/ctypes.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 077a205695a5..d1922aaf165a 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -390,7 +390,7 @@ regular, non-variadic, function arguments: libc.printf.argtypes = [ctypes.c_char_p] -Because specifying the attribute does inhibit portability it is adviced to always +Because specifying the attribute does inhibit portability it is advised to always specify ``argtypes`` for all variadic functions. From webhook-mailer at python.org Tue Jan 10 05:18:27 2023 From: webhook-mailer at python.org (vsajip) Date: Tue, 10 Jan 2023 10:18:27 -0000 Subject: [Python-checkins] gh-100916: Convert argument to appropriate type (GH-100917) Message-ID: https://github.com/python/cpython/commit/b2f7b2ef0b5421e01efb8c7bee2ef95d3bab77eb commit: b2f7b2ef0b5421e01efb8c7bee2ef95d3bab77eb branch: main author: Yao-Ching Huang committer: vsajip date: 2023-01-10T10:17:57Z summary: gh-100916: Convert argument to appropriate type (GH-100917) files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index bf6f54a841a7..8aec8e5e5ee0 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -1131,7 +1131,7 @@ each request is handled by a thread: 'context can be used to ' 'populate logs') aa = ap.add_argument - aa('--count', '-c', default=100, help='How many requests to simulate') + aa('--count', '-c', type=int, default=100, help='How many requests to simulate') options = ap.parse_args() # Create the dummy webapps and put them in a list which we can use to select From webhook-mailer at python.org Tue Jan 10 05:39:03 2023 From: webhook-mailer at python.org (vsajip) Date: Tue, 10 Jan 2023 10:39:03 -0000 Subject: [Python-checkins] [3.11] gh-100916: Convert argument to appropriate type (GH-100917) (GH-100918) Message-ID: https://github.com/python/cpython/commit/a7f9afdd465886ad6fce94e34f1ebff8730a3faa commit: a7f9afdd465886ad6fce94e34f1ebff8730a3faa branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: vsajip date: 2023-01-10T10:38:57Z summary: [3.11] gh-100916: Convert argument to appropriate type (GH-100917) (GH-100918) Co-authored-by: Yao-Ching Huang files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index bf6f54a841a7..8aec8e5e5ee0 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -1131,7 +1131,7 @@ each request is handled by a thread: 'context can be used to ' 'populate logs') aa = ap.add_argument - aa('--count', '-c', default=100, help='How many requests to simulate') + aa('--count', '-c', type=int, default=100, help='How many requests to simulate') options = ap.parse_args() # Create the dummy webapps and put them in a list which we can use to select From webhook-mailer at python.org Tue Jan 10 05:39:17 2023 From: webhook-mailer at python.org (vsajip) Date: Tue, 10 Jan 2023 10:39:17 -0000 Subject: [Python-checkins] [3.10] gh-100916: Convert argument to appropriate type (GH-100917) (GH-100919) Message-ID: https://github.com/python/cpython/commit/5aa8b9e70c44862cf3f600bdc329a20790b67056 commit: 5aa8b9e70c44862cf3f600bdc329a20790b67056 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: vsajip date: 2023-01-10T10:39:11Z summary: [3.10] gh-100916: Convert argument to appropriate type (GH-100917) (GH-100919) Co-authored-by: Yao-Ching Huang files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index fecc729cc03a..1fa8dc10ad3c 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -1131,7 +1131,7 @@ each request is handled by a thread: 'context can be used to ' 'populate logs') aa = ap.add_argument - aa('--count', '-c', default=100, help='How many requests to simulate') + aa('--count', '-c', type=int, default=100, help='How many requests to simulate') options = ap.parse_args() # Create the dummy webapps and put them in a list which we can use to select From webhook-mailer at python.org Tue Jan 10 05:57:17 2023 From: webhook-mailer at python.org (markshannon) Date: Tue, 10 Jan 2023 10:57:17 -0000 Subject: [Python-checkins] GH-100117: Make `co_lines` more efficient (GH-100447) Message-ID: https://github.com/python/cpython/commit/f07daaf4f7a637f9f9324e7c8bf78e8a3faae7e0 commit: f07daaf4f7a637f9f9324e7c8bf78e8a3faae7e0 branch: main author: Brandt Bucher committer: markshannon date: 2023-01-10T10:56:53Z summary: GH-100117: Make `co_lines` more efficient (GH-100447) files: A Misc/NEWS.d/next/Core and Builtins/2022-12-17-19-44-57.gh-issue-100117.yRWQ1y.rst M Lib/test/test_code.py M Lib/test/test_compile.py M Objects/codeobject.c diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index b13d5770abe8..67ed1694205c 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -702,7 +702,8 @@ def test_positions(self): def check_lines(self, func): co = func.__code__ - lines1 = list(dedup(l for (_, _, l) in co.co_lines())) + lines1 = [line for _, _, line in co.co_lines()] + self.assertEqual(lines1, list(dedup(lines1))) lines2 = list(lines_from_postions(positions_from_location_table(co))) for l1, l2 in zip(lines1, lines2): self.assertEqual(l1, l2) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 998ce57927f1..f74d2ed2c420 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -161,9 +161,8 @@ def test_leading_newlines(self): s256 = "".join(["\n"] * 256 + ["spam"]) co = compile(s256, 'fn', 'exec') self.assertEqual(co.co_firstlineno, 1) - lines = list(co.co_lines()) - self.assertEqual(lines[0][2], 0) - self.assertEqual(lines[1][2], 257) + lines = [line for _, _, line in co.co_lines()] + self.assertEqual(lines, [0, 257]) def test_literals_with_leading_zeroes(self): for arg in ["077787", "0xj", "0x.", "0e", "090000000000000", @@ -955,9 +954,9 @@ def no_code2(): for func in (no_code1, no_code2): with self.subTest(func=func): code = func.__code__ - lines = list(code.co_lines()) - start, end, line = lines[0] + [(start, end, line)] = code.co_lines() self.assertEqual(start, 0) + self.assertEqual(end, len(code.co_code)) self.assertEqual(line, code.co_firstlineno) def get_code_lines(self, code): diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-17-19-44-57.gh-issue-100117.yRWQ1y.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-17-19-44-57.gh-issue-100117.yRWQ1y.rst new file mode 100644 index 000000000000..0ba6ee6f96e3 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-12-17-19-44-57.gh-issue-100117.yRWQ1y.rst @@ -0,0 +1,2 @@ +Improve the output of ``co_lines`` by emitting only one entry for each line +range. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 6facfef4c9b4..ab31b6582cda 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1183,6 +1183,14 @@ lineiter_dealloc(lineiterator *li) Py_TYPE(li)->tp_free(li); } +static PyObject * +_source_offset_converter(int *value) { + if (*value == -1) { + Py_RETURN_NONE; + } + return PyLong_FromLong(*value); +} + static PyObject * lineiter_next(lineiterator *li) { @@ -1190,31 +1198,17 @@ lineiter_next(lineiterator *li) if (!_PyLineTable_NextAddressRange(bounds)) { return NULL; } - PyObject *start = NULL; - PyObject *end = NULL; - PyObject *line = NULL; - PyObject *result = PyTuple_New(3); - start = PyLong_FromLong(bounds->ar_start); - end = PyLong_FromLong(bounds->ar_end); - if (bounds->ar_line < 0) { - line = Py_NewRef(Py_None); - } - else { - line = PyLong_FromLong(bounds->ar_line); - } - if (result == NULL || start == NULL || end == NULL || line == NULL) { - goto error; + int start = bounds->ar_start; + int line = bounds->ar_line; + // Merge overlapping entries: + while (_PyLineTable_NextAddressRange(bounds)) { + if (bounds->ar_line != line) { + _PyLineTable_PreviousAddressRange(bounds); + break; + } } - PyTuple_SET_ITEM(result, 0, start); - PyTuple_SET_ITEM(result, 1, end); - PyTuple_SET_ITEM(result, 2, line); - return result; -error: - Py_XDECREF(start); - Py_XDECREF(end); - Py_XDECREF(line); - Py_XDECREF(result); - return result; + return Py_BuildValue("iiO&", start, bounds->ar_end, + _source_offset_converter, &line); } PyTypeObject _PyLineIterator = { @@ -1290,14 +1284,6 @@ positionsiter_dealloc(positionsiterator* pi) Py_TYPE(pi)->tp_free(pi); } -static PyObject* -_source_offset_converter(int* value) { - if (*value == -1) { - Py_RETURN_NONE; - } - return PyLong_FromLong(*value); -} - static PyObject* positionsiter_next(positionsiterator* pi) { From webhook-mailer at python.org Tue Jan 10 11:43:08 2023 From: webhook-mailer at python.org (Yhg1s) Date: Tue, 10 Jan 2023 16:43:08 -0000 Subject: [Python-checkins] Python 3.12.0a4 Message-ID: https://github.com/python/cpython/commit/3d5d3f7af6498effbc60a3db1d2b5f41ae6c0a75 commit: 3d5d3f7af6498effbc60a3db1d2b5f41ae6c0a75 branch: main author: Thomas Wouters committer: Yhg1s date: 2023-01-10T13:09:15+01:00 summary: Python 3.12.0a4 files: A Misc/NEWS.d/3.12.0a4.rst D Misc/NEWS.d/next/Build/2022-03-04-10-47-23.bpo-41916.1d2GLU.rst D Misc/NEWS.d/next/Build/2022-12-08-14-00-04.gh-issue-88267.MqtRbm.rst D Misc/NEWS.d/next/Build/2022-12-26-15-07-48.gh-issue-100540.l6ToSY.rst D Misc/NEWS.d/next/C API/2022-11-04-16-13-35.gh-issue-98724.p0urWO.rst D Misc/NEWS.d/next/C API/2022-11-30-16-39-22.gh-issue-99240.67nAX-.rst D Misc/NEWS.d/next/C API/2022-12-02-09-31-19.gh-issue-99947.Ski7OC.rst D Misc/NEWS.d/next/Core and Builtins/2018-02-06-23-21-13.bpo-32782.EJVSfR.rst D Misc/NEWS.d/next/Core and Builtins/2022-06-17-08-00-34.gh-issue-89051.yP4Na0.rst D Misc/NEWS.d/next/Core and Builtins/2022-07-06-18-44-00.gh-issue-94603.Q_03xV.rst D Misc/NEWS.d/next/Core and Builtins/2022-10-21-16-10-39.gh-issue-98522.s_SixG.rst D Misc/NEWS.d/next/Core and Builtins/2022-11-16-05-57-24.gh-issue-99554.A_Ywd2.rst D Misc/NEWS.d/next/Core and Builtins/2022-11-19-01-11-06.gh-issue-99582.wvOBVy.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-04-00-38-33.gh-issue-92216.CJXuWB.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-06-22-24-01.gh-issue-100050.lcrPqQ.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-08-12-26-34.gh-issue-100110.ertac-.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-09-13-18-42.gh-issue-100146.xLVKg0.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-09-14-27-36.gh-issue-100143.5g9rb4.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-10-20-00-13.gh-issue-99540.ZZZHeP.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-12-00-59-11.gh-issue-94155.LWE9y_.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-12-01-05-16.gh-issue-99110.1JqtIg.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-12-05-30-12.gh-issue-100188.sGCSMR.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-12-11-27-54.gh-issue-99955.Ix5Rrg.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-13-16-05-18.gh-issue-100222.OVVvYe.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-15-00-50-25.gh-issue-90043.gyoKdx.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-17-19-44-57.gh-issue-100117.yRWQ1y.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-20-09-56-56.gh-issue-100357.hPyTwY.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-20-16-14-19.gh-issue-100374.YRrVHT.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-21-22-48-41.gh-issue-100425.U64yLu.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-22-21-56-08.gh-issue-100268.xw_phB.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-28-15-02-53.gh-issue-87447.7-aekA.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-29-04-39-38.gh-issue-100126.pfFJd-.rst D Misc/NEWS.d/next/Core and Builtins/2022-12-31-23-32-09.gh-issue-100649.C0fY4S.rst D Misc/NEWS.d/next/Core and Builtins/2023-01-01-15-59-48.gh-issue-100637.M2n6Kg.rst D Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-38-18.gh-issue-100719.2C--ko.rst D Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-50-42.gh-issue-100720.UhE7P-.rst D Misc/NEWS.d/next/Core and Builtins/2023-01-04-16-40-55.gh-issue-100288.hRSRaT.rst D Misc/NEWS.d/next/Core and Builtins/2023-01-05-13-54-00.gh-issue-99005.D7H6j4.rst D Misc/NEWS.d/next/Core and Builtins/2023-01-05-17-54-29.gh-issue-99005.cmGwxv.rst D Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst D Misc/NEWS.d/next/Documentation/2020-06-17-14-47-48.bpo-25377.CTxC6o.rst D Misc/NEWS.d/next/Documentation/2022-12-23-21-42-26.gh-issue-100472.NNixfO.rst D Misc/NEWS.d/next/Documentation/2022-12-30-00-42-23.gh-issue-100616.eu80ij.rst D Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst D Misc/NEWS.d/next/Library/2020-05-03-12-55-55.bpo-40447.oKR0Lj.rst D Misc/NEWS.d/next/Library/2022-03-05-02-14-09.bpo-24132.W6iORO.rst D Misc/NEWS.d/next/Library/2022-04-23-08-12-14.gh-issue-91851.Jd47V6.rst D Misc/NEWS.d/next/Library/2022-05-06-01-53-34.gh-issue-92122.96Lf2p.rst D Misc/NEWS.d/next/Library/2022-07-01-00-01-22.gh-issue-78707.fHGSuM.rst D Misc/NEWS.d/next/Library/2022-08-11-10-02-19.gh-issue-95882.FsUr72.rst D Misc/NEWS.d/next/Library/2022-08-27-10-35-50.gh-issue-96127.8RdLre.rst D Misc/NEWS.d/next/Library/2022-09-16-08-21-46.gh-issue-88500.jQ0pCc.rst D Misc/NEWS.d/next/Library/2022-10-07-18-16-00.gh-issue-98030.2oQCZy.rst D Misc/NEWS.d/next/Library/2022-10-24-07-31-11.gh-issue-91166.-IG06R.rst D Misc/NEWS.d/next/Library/2022-10-28-07-24-34.gh-issue-85267.xUy_Wm.rst D Misc/NEWS.d/next/Library/2022-11-13-15-32-19.gh-issue-99433.Ys6y0A.rst D Misc/NEWS.d/next/Library/2022-11-14-19-58-36.gh-issue-99482.XmZyUr.rst D Misc/NEWS.d/next/Library/2022-11-15-18-45-01.gh-issue-99509.FLK0xU.rst D Misc/NEWS.d/next/Library/2022-11-17-10-02-18.gh-issue-94912.G2aa-E.rst D Misc/NEWS.d/next/Library/2022-11-20-11-59-54.gh-issue-99576.ZD7jU6.rst D Misc/NEWS.d/next/Library/2022-11-21-16-24-01.gh-issue-83035.qZIujU.rst D Misc/NEWS.d/next/Library/2022-11-23-23-58-45.gh-issue-94943.Oog0Zo.rst D Misc/NEWS.d/next/Library/2022-11-29-20-44-54.gh-issue-89727.UJZjkk.rst D Misc/NEWS.d/next/Library/2022-12-01-15-44-58.gh-issue-99925.x4y6pF.rst D Misc/NEWS.d/next/Library/2022-12-03-20-06-16.gh-issue-98778.t5U9uc.rst D Misc/NEWS.d/next/Library/2022-12-04-16-12-04.gh-issue-85432.l_ehmI.rst D Misc/NEWS.d/next/Library/2022-12-08-06-18-06.gh-issue-100098.uBvPlp.rst D Misc/NEWS.d/next/Library/2022-12-09-10-35-36.bpo-44592.z-P3oe.rst D Misc/NEWS.d/next/Library/2022-12-10-08-36-07.gh-issue-100133.g-zQlp.rst D Misc/NEWS.d/next/Library/2022-12-10-20-52-28.gh-issue-100039.zDqjT4.rst D Misc/NEWS.d/next/Library/2022-12-13-17-29-09.gh-issue-100228.bgtzMV.rst D Misc/NEWS.d/next/Library/2022-12-14-11-45-38.gh-issue-100234.kn6yWV.rst D Misc/NEWS.d/next/Library/2022-12-14-17-37-01.gh-issue-83076.NaYzWT.rst D Misc/NEWS.d/next/Library/2022-12-15-18-28-13.gh-issue-100272.D1O9Ey.rst D Misc/NEWS.d/next/Library/2022-12-19-12-18-28.gh-issue-100344.lfCqpE.rst D Misc/NEWS.d/next/Library/2022-12-19-19-30-06.gh-issue-100348.o7IAHh.rst D Misc/NEWS.d/next/Library/2022-12-19-20-54-04.gh-issue-78878.JrkYqJ.rst D Misc/NEWS.d/next/Library/2022-12-20-11-07-30.gh-issue-100363.Wo_Beg.rst D Misc/NEWS.d/next/Library/2022-12-23-21-02-43.gh-issue-100474.gppA4U.rst D Misc/NEWS.d/next/Library/2022-12-24-04-13-54.gh-issue-100488.Ut8HbE.rst D Misc/NEWS.d/next/Library/2022-12-24-08-42-05.gh-issue-100287.n0oEuG.rst D Misc/NEWS.d/next/Library/2022-12-24-16-39-53.gh-issue-100519.G_dZLP.rst D Misc/NEWS.d/next/Library/2022-12-28-00-28-43.gh-issue-100562.Hic0Z0.rst D Misc/NEWS.d/next/Library/2022-12-28-17-38-39.gh-issue-100585.BiiTlG.rst D Misc/NEWS.d/next/Library/2022-12-29-11-45-22.gh-issue-97930.hrtmJe.rst D Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst D Misc/NEWS.d/next/Library/2023-01-01-21-54-46.gh-issue-100485.geNrHS.rst D Misc/NEWS.d/next/Library/2023-01-01-23-57-00.gh-issue-89727.ojedHN.rst D Misc/NEWS.d/next/Library/2023-01-02-16-59-49.gh-issue-100690.2EgWPS.rst D Misc/NEWS.d/next/Library/2023-01-03-11-06-28.gh-issue-91219.s5IFCw.rst D Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst D Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst D Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst D Misc/NEWS.d/next/Library/2023-01-05-23-04-15.gh-issue-91851.AuCzU5.rst D Misc/NEWS.d/next/Library/2023-01-06-14-05-15.gh-issue-100792.CEOJth.rst D Misc/NEWS.d/next/Library/2023-01-06-22-36-27.gh-issue-100813.mHRdQn.rst D Misc/NEWS.d/next/Library/2023-01-07-15-13-47.gh-issue-100805.05rBz9.rst D Misc/NEWS.d/next/Library/2023-01-08-12-10-17.gh-issue-100833.f6cT7E.rst D Misc/NEWS.d/next/Tests/2022-06-16-13-26-31.gh-issue-93018.wvNx76.rst D Misc/NEWS.d/next/Tests/2022-12-08-00-03-37.gh-issue-100086.1zYpto.rst D Misc/NEWS.d/next/Tests/2022-12-23-13-29-55.gh-issue-100454.3no0cW.rst D Misc/NEWS.d/next/Tools-Demos/2022-12-19-10-08-53.gh-issue-100342.qDFlQG.rst D Misc/NEWS.d/next/Tools-Demos/2022-12-29-19-22-11.bpo-45256.a0ee_H.rst D Misc/NEWS.d/next/Windows/2021-04-08-00-36-37.bpo-34816.4Xe0id.rst D Misc/NEWS.d/next/Windows/2021-05-02-15-29-33.bpo-43984.U92jiv.rst D Misc/NEWS.d/next/Windows/2022-12-06-11-16-46.gh-issue-99941.GmUQ6o.rst D Misc/NEWS.d/next/Windows/2022-12-09-22-47-42.gh-issue-79218.Yiot2e.rst D Misc/NEWS.d/next/Windows/2022-12-20-18-36-17.gh-issue-99191.0cfRja.rst D Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst D Misc/NEWS.d/next/macOS/2022-12-26-14-52-37.gh-issue-100540.kYZLtX.rst D Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst M Include/patchlevel.h M Lib/pydoc_data/topics.py M README.rst diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 3a7e3b47a885..3031946450d0 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -20,10 +20,10 @@ #define PY_MINOR_VERSION 12 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA -#define PY_RELEASE_SERIAL 3 +#define PY_RELEASE_SERIAL 4 /* Version as a string */ -#define PY_VERSION "3.12.0a3+" +#define PY_VERSION "3.12.0a4" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index 4ba680cceda9..11b75037e78b 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Tue Dec 6 19:31:49 2022 +# Autogenerated by Sphinx on Tue Jan 10 13:08:32 2023 topics = {'assert': 'The "assert" statement\n' '**********************\n' '\n' @@ -2382,12 +2382,10 @@ 'finished,\n' 'but if the sequence is empty, they will not have been assigned ' 'to at\n' - 'all by the loop. Hint: the built-in function "range()" returns ' - 'an\n' - 'iterator of integers suitable to emulate the effect of Pascal?s ' - '"for i\n' - ':= a to b do"; e.g., "list(range(3))" returns the list "[0, 1, ' - '2]".\n' + 'all by the loop. Hint: the built-in type "range()" represents\n' + 'immutable arithmetic sequences of integers. For instance, ' + 'iterating\n' + '"range(3)" successively yields 0, 1, and then 2.\n' '\n' 'Changed in version 3.11: Starred elements are now allowed in ' 'the\n' @@ -2726,7 +2724,7 @@ 'the\n' ' target list, it will be treated the same as an error ' 'occurring\n' - ' within the suite would be. See step 6 below.\n' + ' within the suite would be. See step 7 below.\n' '\n' '6. The suite is executed.\n' '\n' @@ -5668,7 +5666,8 @@ 'be\n' 'determined by scanning the entire text of the block for name ' 'binding\n' - 'operations.\n' + 'operations. See the FAQ entry on UnboundLocalError for ' + 'examples.\n' '\n' 'If the "global" statement occurs within a block, all uses of ' 'the names\n' @@ -5970,10 +5969,9 @@ '\n' 'Names in the target list are not deleted when the loop is finished,\n' 'but if the sequence is empty, they will not have been assigned to at\n' - 'all by the loop. Hint: the built-in function "range()" returns an\n' - 'iterator of integers suitable to emulate the effect of Pascal?s "for ' - 'i\n' - ':= a to b do"; e.g., "list(range(3))" returns the list "[0, 1, 2]".\n' + 'all by the loop. Hint: the built-in type "range()" represents\n' + 'immutable arithmetic sequences of integers. For instance, iterating\n' + '"range(3)" successively yields 0, 1, and then 2.\n' '\n' 'Changed in version 3.11: Starred elements are now allowed in the\n' 'expression list.\n', @@ -7781,7 +7779,7 @@ 'within a code block. The local variables of a code block can be\n' 'determined by scanning the entire text of the block for name ' 'binding\n' - 'operations.\n' + 'operations. See the FAQ entry on UnboundLocalError for examples.\n' '\n' 'If the "global" statement occurs within a block, all uses of the ' 'names\n' @@ -11322,35 +11320,35 @@ '\n' "str.encode(encoding='utf-8', errors='strict')\n" '\n' - ' Return an encoded version of the string as a bytes ' - 'object. Default\n' - ' encoding is "\'utf-8\'". *errors* may be given to set a ' - 'different\n' - ' error handling scheme. The default for *errors* is ' - '"\'strict\'",\n' - ' meaning that encoding errors raise a "UnicodeError". ' + ' Return the string encoded to "bytes".\n' + '\n' + ' *encoding* defaults to "\'utf-8\'"; see Standard ' + 'Encodings for\n' + ' possible values.\n' + '\n' + ' *errors* controls how encoding errors are handled. If ' + '"\'strict\'"\n' + ' (the default), a "UnicodeError" exception is raised. ' 'Other possible\n' ' values are "\'ignore\'", "\'replace\'", ' '"\'xmlcharrefreplace\'",\n' ' "\'backslashreplace\'" and any other name registered ' 'via\n' - ' "codecs.register_error()", see section Error Handlers. ' - 'For a list\n' - ' of possible encodings, see section Standard Encodings.\n' + ' "codecs.register_error()". See Error Handlers for ' + 'details.\n' '\n' - ' By default, the *errors* argument is not checked for ' - 'best\n' - ' performances, but only used at the first encoding ' - 'error. Enable the\n' - ' Python Development Mode, or use a debug build to check ' - '*errors*.\n' + ' For performance reasons, the value of *errors* is not ' + 'checked for\n' + ' validity unless an encoding error actually occurs, ' + 'Python\n' + ' Development Mode is enabled or a debug build is used.\n' '\n' - ' Changed in version 3.1: Support for keyword arguments ' - 'added.\n' + ' Changed in version 3.1: Added support for keyword ' + 'arguments.\n' '\n' - ' Changed in version 3.9: The *errors* is now checked in ' - 'development\n' - ' mode and in debug mode.\n' + ' Changed in version 3.9: The value of the *errors* ' + 'argument is now\n' + ' checked in Python Development Mode and in debug mode.\n' '\n' 'str.endswith(suffix[, start[, end]])\n' '\n' @@ -14099,6 +14097,7 @@ ' >>> class Counter(dict):\n' ' ... def __missing__(self, key):\n' ' ... return 0\n' + ' ...\n' ' >>> c = Counter()\n' " >>> c['red']\n" ' 0\n' @@ -14425,6 +14424,7 @@ ' >>> n = 0\n' ' >>> for val in values:\n' ' ... n += val\n' + ' ...\n' ' >>> print(n)\n' ' 504\n' '\n' @@ -14452,8 +14452,7 @@ ' >>> # get back a read-only proxy for the original ' 'dictionary\n' ' >>> values.mapping\n' - " mappingproxy({'eggs': 2, 'sausage': 1, 'bacon': 1, " - "'spam': 500})\n" + " mappingproxy({'bacon': 1, 'spam': 500})\n" " >>> values.mapping['spam']\n" ' 500\n', 'typesmethods': 'Methods\n' @@ -15499,7 +15498,7 @@ ' returns without an error, then "__exit__()" will always be\n' ' called. Thus, if an error occurs during the assignment to the\n' ' target list, it will be treated the same as an error occurring\n' - ' within the suite would be. See step 6 below.\n' + ' within the suite would be. See step 7 below.\n' '\n' '6. The suite is executed.\n' '\n' diff --git a/Misc/NEWS.d/3.12.0a4.rst b/Misc/NEWS.d/3.12.0a4.rst new file mode 100644 index 000000000000..dd26d4d964d6 --- /dev/null +++ b/Misc/NEWS.d/3.12.0a4.rst @@ -0,0 +1,1132 @@ +.. date: 2023-01-06-02-02-11 +.. gh-issue: 100776 +.. nonce: pP8xux +.. release date: 2023-01-10 +.. section: Core and Builtins + +Fix misleading default value in :func:`input`'s ``__text_signature__``. + +.. + +.. date: 2023-01-05-17-54-29 +.. gh-issue: 99005 +.. nonce: cmGwxv +.. section: Core and Builtins + +Remove :opcode:`UNARY_POSITIVE`, :opcode:`ASYNC_GEN_WRAP` and +:opcode:`LIST_TO_TUPLE`, replacing them with intrinsics. + +.. + +.. date: 2023-01-05-13-54-00 +.. gh-issue: 99005 +.. nonce: D7H6j4 +.. section: Core and Builtins + +Add new :opcode:`CALL_INSTRINSIC_1` instruction. Remove +:opcode:`IMPORT_STAR`, :opcode:`PRINT_EXPR` and +:opcode:`STOPITERATION_ERROR`, replacing them with the +:opcode:`CALL_INSTRINSIC_1` instruction. + +.. + +.. date: 2023-01-04-16-40-55 +.. gh-issue: 100288 +.. nonce: hRSRaT +.. section: Core and Builtins + +Remove the LOAD_ATTR_METHOD_WITH_DICT specialized instruction. Stats show it +is not useful. + +.. + +.. date: 2023-01-03-16-50-42 +.. gh-issue: 100720 +.. nonce: UhE7P- +.. section: Core and Builtins + +Added ``_PyFrame_NumSlotsForCodeObject``, which returns the number of slots +needed in a frame for a given code object. + +.. + +.. date: 2023-01-03-16-38-18 +.. gh-issue: 100719 +.. nonce: 2C--ko +.. section: Core and Builtins + +Removed the co_nplaincellvars field from the code object, as it is +redundant. + +.. + +.. date: 2023-01-01-15-59-48 +.. gh-issue: 100637 +.. nonce: M2n6Kg +.. section: Core and Builtins + +Fix :func:`int.__sizeof__` calculation to include the 1 element ob_digit +array for 0 and False. + +.. + +.. date: 2022-12-31-23-32-09 +.. gh-issue: 100649 +.. nonce: C0fY4S +.. section: Core and Builtins + +Update the native_thread_id field of PyThreadState after fork. + +.. + +.. date: 2022-12-29-04-39-38 +.. gh-issue: 100126 +.. nonce: pfFJd- +.. section: Core and Builtins + +Fix an issue where "incomplete" frames could be briefly visible to C code +while other frames are being torn down, possibly resulting in corruption or +hard crashes of the interpreter while running finalizers. + +.. + +.. date: 2022-12-28-15-02-53 +.. gh-issue: 87447 +.. nonce: 7-aekA +.. section: Core and Builtins + +Fix :exc:`SyntaxError` on comprehension rebind checking with names that are +not actually redefined. + +Now reassigning ``b`` in ``[(b := 1) for a, b.prop in some_iter]`` is +allowed. Reassigning ``a`` is still disallowed as per :pep:`572`. + +.. + +.. date: 2022-12-22-21-56-08 +.. gh-issue: 100268 +.. nonce: xw_phB +.. section: Core and Builtins + +Add :meth:`int.is_integer` to improve duck type compatibility between +:class:`int` and :class:`float`. + +.. + +.. date: 2022-12-21-22-48-41 +.. gh-issue: 100425 +.. nonce: U64yLu +.. section: Core and Builtins + +Improve the accuracy of ``sum()`` with compensated summation. + +.. + +.. date: 2022-12-20-16-14-19 +.. gh-issue: 100374 +.. nonce: YRrVHT +.. section: Core and Builtins + +Fix incorrect result and delay in :func:`socket.getfqdn`. Patch by Dominic +Socular. + +.. + +.. date: 2022-12-20-09-56-56 +.. gh-issue: 100357 +.. nonce: hPyTwY +.. section: Core and Builtins + +Convert ``vars``, ``dir``, ``next``, ``getattr``, and ``iter`` to argument +clinic. + +.. + +.. date: 2022-12-17-19-44-57 +.. gh-issue: 100117 +.. nonce: yRWQ1y +.. section: Core and Builtins + +Improve the output of ``co_lines`` by emitting only one entry for each line +range. + +.. + +.. date: 2022-12-15-00-50-25 +.. gh-issue: 90043 +.. nonce: gyoKdx +.. section: Core and Builtins + +Handle NaNs when specializing :opcode:`COMPARE_OP` for :class:`float` +values. + +.. + +.. date: 2022-12-13-16-05-18 +.. gh-issue: 100222 +.. nonce: OVVvYe +.. section: Core and Builtins + +Redefine the ``_Py_CODEUNIT`` typedef as a union to describe its layout to +the C compiler, avoiding type punning and improving clarity. + +.. + +.. date: 2022-12-12-11-27-54 +.. gh-issue: 99955 +.. nonce: Ix5Rrg +.. section: Core and Builtins + +Internal compiler functions (in compile.c) now consistently return -1 on +error and 0 on success. + +.. + +.. date: 2022-12-12-05-30-12 +.. gh-issue: 100188 +.. nonce: sGCSMR +.. section: Core and Builtins + +The ``BINARY_SUBSCR_LIST_INT`` and ``BINARY_SUBSCR_TUPLE_INT`` instructions +are no longer used for negative integers because those instructions always +miss when encountering negative integers. + +.. + +.. date: 2022-12-12-01-05-16 +.. gh-issue: 99110 +.. nonce: 1JqtIg +.. section: Core and Builtins + +Initialize frame->previous in frameobject.c to fix a segmentation fault when +accessing frames created by :c:func:`PyFrame_New`. + +.. + +.. date: 2022-12-12-00-59-11 +.. gh-issue: 94155 +.. nonce: LWE9y_ +.. section: Core and Builtins + +Improved the hashing algorithm for code objects, mitigating some hash +collisions. + +.. + +.. date: 2022-12-10-20-00-13 +.. gh-issue: 99540 +.. nonce: ZZZHeP +.. section: Core and Builtins + +``None`` now hashes to a constant value. This is not a requirements change. + +.. + +.. date: 2022-12-09-14-27-36 +.. gh-issue: 100143 +.. nonce: 5g9rb4 +.. section: Core and Builtins + +When built with ``--enable-pystats``, stats collection is now off by +default. To enable it early at startup, pass the ``-Xpystats`` flag. Stats +are now always dumped, even if switched off. + +.. + +.. date: 2022-12-09-13-18-42 +.. gh-issue: 100146 +.. nonce: xLVKg0 +.. section: Core and Builtins + +Improve ``BUILD_LIST`` opcode so that it works similarly to the +``BUILD_TUPLE`` opcode, by stealing references from the stack rather than +repeatedly using stack operations to set list elements. Implementation +details are in a new private API :c:func:`_PyList_FromArraySteal`. + +.. + +.. date: 2022-12-08-12-26-34 +.. gh-issue: 100110 +.. nonce: ertac- +.. section: Core and Builtins + +Specialize ``FOR_ITER`` for tuples. + +.. + +.. date: 2022-12-06-22-24-01 +.. gh-issue: 100050 +.. nonce: lcrPqQ +.. section: Core and Builtins + +Honor existing errors obtained when searching for mismatching parentheses in +the tokenizer. Patch by Pablo Galindo + +.. + +.. date: 2022-12-04-00-38-33 +.. gh-issue: 92216 +.. nonce: CJXuWB +.. section: Core and Builtins + +Improve the performance of :func:`hasattr` for type objects with a missing +attribute. + +.. + +.. date: 2022-11-19-01-11-06 +.. gh-issue: 99582 +.. nonce: wvOBVy +.. section: Core and Builtins + +Freeze :mod:`zipimport` module into ``_bootstrap_python``. + +.. + +.. date: 2022-11-16-05-57-24 +.. gh-issue: 99554 +.. nonce: A_Ywd2 +.. section: Core and Builtins + +Pack debugging location tables more efficiently during bytecode compilation. + +.. + +.. date: 2022-10-21-16-10-39 +.. gh-issue: 98522 +.. nonce: s_SixG +.. section: Core and Builtins + +Add an internal version number to code objects, to give better versioning of +inner functions and comprehensions, and thus better specialization of those +functions. This change is invisible to both Python and C extensions. + +.. + +.. date: 2022-07-06-18-44-00 +.. gh-issue: 94603 +.. nonce: Q_03xV +.. section: Core and Builtins + +Improve performance of ``list.pop`` for small lists. + +.. + +.. date: 2022-06-17-08-00-34 +.. gh-issue: 89051 +.. nonce: yP4Na0 +.. section: Core and Builtins + +Add :data:`ssl.OP_LEGACY_SERVER_CONNECT` + +.. + +.. bpo: 32782 +.. date: 2018-02-06-23-21-13 +.. nonce: EJVSfR +.. section: Core and Builtins + +``ctypes`` arrays of length 0 now report a correct itemsize when a +``memoryview`` is constructed from them, rather than always giving a value +of 0. + +.. + +.. date: 2023-01-08-12-10-17 +.. gh-issue: 100833 +.. nonce: f6cT7E +.. section: Library + +Speed up :func:`math.fsum` by removing defensive ``volatile`` qualifiers. + +.. + +.. date: 2023-01-07-15-13-47 +.. gh-issue: 100805 +.. nonce: 05rBz9 +.. section: Library + +Modify :func:`random.choice` implementation to once again work with NumPy +arrays. + +.. + +.. date: 2023-01-06-22-36-27 +.. gh-issue: 100813 +.. nonce: mHRdQn +.. section: Library + +Add :data:`socket.IP_PKTINFO` constant. + +.. + +.. date: 2023-01-06-14-05-15 +.. gh-issue: 100792 +.. nonce: CEOJth +.. section: Library + +Make :meth:`email.message.Message.__contains__` twice as fast. + +.. + +.. date: 2023-01-05-23-04-15 +.. gh-issue: 91851 +.. nonce: AuCzU5 +.. section: Library + +Microoptimizations for :meth:`fractions.Fraction.__round__`, +:meth:`fractions.Fraction.__ceil__` and +:meth:`fractions.Fraction.__floor__`. + +.. + +.. date: 2023-01-04-22-10-31 +.. gh-issue: 90104 +.. nonce: yZk5EX +.. section: Library + +Avoid RecursionError on ``repr`` if a dataclass field definition has a +cyclic reference. + +.. + +.. date: 2023-01-04-12-58-59 +.. gh-issue: 100689 +.. nonce: Ce0ITG +.. section: Library + +Fix crash in :mod:`pyexpat` by statically allocating ``PyExpat_CAPI`` +capsule. + +.. + +.. date: 2023-01-04-09-53-38 +.. gh-issue: 100740 +.. nonce: -j5UjI +.. section: Library + +Fix ``unittest.mock.Mock`` not respecting the spec for attribute names +prefixed with ``assert``. + +.. + +.. date: 2023-01-03-11-06-28 +.. gh-issue: 91219 +.. nonce: s5IFCw +.. section: Library + +Change ``SimpleHTTPRequestHandler`` to support subclassing to provide a +different set of index file names instead of using ``__init__`` parameters. + +.. + +.. date: 2023-01-02-16-59-49 +.. gh-issue: 100690 +.. nonce: 2EgWPS +.. section: Library + +``Mock`` objects which are not unsafe will now raise an ``AttributeError`` +when accessing an attribute that matches the name of an assertion but +without the prefix ``assert_``, e.g. accessing ``called_once`` instead of +``assert_called_once``. This is in addition to this already happening for +accessing attributes with prefixes ``assert``, ``assret``, ``asert``, +``aseert``, and ``assrt``. + +.. + +.. date: 2023-01-01-23-57-00 +.. gh-issue: 89727 +.. nonce: ojedHN +.. section: Library + +Simplify and optimize :func:`os.walk` by using :func:`isinstance` checks to +check the top of the stack. + +.. + +.. date: 2023-01-01-21-54-46 +.. gh-issue: 100485 +.. nonce: geNrHS +.. section: Library + +Add math.sumprod() to compute the sum of products. + +.. + +.. date: 2022-12-30-07-49-08 +.. gh-issue: 86508 +.. nonce: nGZDzC +.. section: Library + +Fix :func:`asyncio.open_connection` to skip binding to local addresses of +different family. Patch by Kumar Aditya. + +.. + +.. date: 2022-12-29-11-45-22 +.. gh-issue: 97930 +.. nonce: hrtmJe +.. section: Library + +``importlib.resources.files`` now accepts a module as an anchor instead of +only accepting packages. If a module is passed, resources are resolved +adjacent to that module (in the same package or at the package root). The +parameter was renamed from ``package`` to ``anchor`` with a compatibility +shim for those passing by keyword. Additionally, the new ``anchor`` +parameter is now optional and will default to the caller's module. + +.. + +.. date: 2022-12-28-17-38-39 +.. gh-issue: 100585 +.. nonce: BiiTlG +.. section: Library + +Fixed a bug where importlib.resources.as_file was leaving file pointers open + +.. + +.. date: 2022-12-28-00-28-43 +.. gh-issue: 100562 +.. nonce: Hic0Z0 +.. section: Library + +Improve performance of :meth:`pathlib.Path.absolute` by nearly 2x. This +comes at the cost of a performance regression in :meth:`pathlib.Path.cwd`, +which is generally used less frequently in user code. + +.. + +.. date: 2022-12-24-16-39-53 +.. gh-issue: 100519 +.. nonce: G_dZLP +.. section: Library + +Small simplification of :func:`http.cookiejar.eff_request_host` that +improves readability and better matches the RFC wording. + +.. + +.. date: 2022-12-24-08-42-05 +.. gh-issue: 100287 +.. nonce: n0oEuG +.. section: Library + +Fix the interaction of :func:`unittest.mock.seal` with +:class:`unittest.mock.AsyncMock`. + +.. + +.. date: 2022-12-24-04-13-54 +.. gh-issue: 100488 +.. nonce: Ut8HbE +.. section: Library + +Add :meth:`Fraction.is_integer` to check whether a +:class:`fractions.Fraction` is an integer. This improves duck type +compatibility with :class:`float` and :class:`int`. + +.. + +.. date: 2022-12-23-21-02-43 +.. gh-issue: 100474 +.. nonce: gppA4U +.. section: Library + +:mod:`http.server` now checks that an index page is actually a regular file +before trying to serve it. This avoids issues with directories named +``index.html``. + +.. + +.. date: 2022-12-20-11-07-30 +.. gh-issue: 100363 +.. nonce: Wo_Beg +.. section: Library + +Speed up :func:`asyncio.get_running_loop` by removing redundant ``getpid`` +checks. Patch by Kumar Aditya. + +.. + +.. date: 2022-12-19-20-54-04 +.. gh-issue: 78878 +.. nonce: JrkYqJ +.. section: Library + +Fix crash when creating an instance of :class:`!_ctypes.CField`. + +.. + +.. date: 2022-12-19-19-30-06 +.. gh-issue: 100348 +.. nonce: o7IAHh +.. section: Library + +Fix ref cycle in :class:`!asyncio._SelectorSocketTransport` by removing +``_read_ready_cb`` in ``close``. + +.. + +.. date: 2022-12-19-12-18-28 +.. gh-issue: 100344 +.. nonce: lfCqpE +.. section: Library + +Provide C implementation for :func:`asyncio.current_task` for a 4x-6x +speedup. + +.. + +.. date: 2022-12-15-18-28-13 +.. gh-issue: 100272 +.. nonce: D1O9Ey +.. section: Library + +Fix JSON serialization of OrderedDict. It now preserves the order of keys. + +.. + +.. date: 2022-12-14-17-37-01 +.. gh-issue: 83076 +.. nonce: NaYzWT +.. section: Library + +Instantiation of ``Mock()`` and ``AsyncMock()`` is now 3.8x faster. + +.. + +.. date: 2022-12-14-11-45-38 +.. gh-issue: 100234 +.. nonce: kn6yWV +.. section: Library + +Set a default value of 1.0 for the ``lambd`` parameter in +random.expovariate(). + +.. + +.. date: 2022-12-13-17-29-09 +.. gh-issue: 100228 +.. nonce: bgtzMV +.. section: Library + +A :exc:`DeprecationWarning` may be raised when :func:`os.fork()` or +:func:`os.forkpty()` is called from multi-threaded processes. Forking with +threads is unsafe and can cause deadlocks, crashes and subtle problems. Lack +of a warning does not indicate that the fork call was actually safe, as +Python may not be aware of all threads. + +.. + +.. date: 2022-12-10-20-52-28 +.. gh-issue: 100039 +.. nonce: zDqjT4 +.. section: Library + +Improve signatures for enums and flags. + +.. + +.. date: 2022-12-10-08-36-07 +.. gh-issue: 100133 +.. nonce: g-zQlp +.. section: Library + +Fix regression in :mod:`asyncio` where a subprocess would sometimes lose +data received from pipe. + +.. + +.. bpo: 44592 +.. date: 2022-12-09-10-35-36 +.. nonce: z-P3oe +.. section: Library + +Fixes inconsistent handling of case sensitivity of *extrasaction* arg in +:class:`csv.DictWriter`. + +.. + +.. date: 2022-12-08-06-18-06 +.. gh-issue: 100098 +.. nonce: uBvPlp +.. section: Library + +Fix ``tuple`` subclasses being cast to ``tuple`` when used as enum values. + +.. + +.. date: 2022-12-04-16-12-04 +.. gh-issue: 85432 +.. nonce: l_ehmI +.. section: Library + +Rename the *fmt* parameter of the pure-Python implementation of +:meth:`datetime.time.strftime` to *format*. Rename the *t* parameter of +:meth:`datetime.datetime.fromtimestamp` to *timestamp*. These changes mean +the parameter names in the pure-Python implementation now match the +parameter names in the C implementation. Patch by Alex Waygood. + +.. + +.. date: 2022-12-03-20-06-16 +.. gh-issue: 98778 +.. nonce: t5U9uc +.. section: Library + +Update :exc:`~urllib.error.HTTPError` to be initialized properly, even if +the ``fp`` is ``None``. Patch by Dong-hee Na. + +.. + +.. date: 2022-12-01-15-44-58 +.. gh-issue: 99925 +.. nonce: x4y6pF +.. section: Library + +Unify error messages in JSON serialization between +``json.dumps(float('nan'), allow_nan=False)`` and ``json.dumps(float('nan'), +allow_nan=False, indent=)``. Now both include the representation +of the value that could not be serialized. + +.. + +.. date: 2022-11-29-20-44-54 +.. gh-issue: 89727 +.. nonce: UJZjkk +.. section: Library + +Fix issue with :func:`os.walk` where a :exc:`RecursionError` would occur on +deep directory structures by adjusting the implementation of :func:`os.walk` +to be iterative instead of recursive. + +.. + +.. date: 2022-11-23-23-58-45 +.. gh-issue: 94943 +.. nonce: Oog0Zo +.. section: Library + +Add :ref:`enum-dataclass-support` to the :class:`~enum.Enum` +:meth:`~enum.Enum.__repr__`. When inheriting from a +:class:`~dataclasses.dataclass`, only show the field names in the value +section of the member :func:`repr`, and not the dataclass' class name. + +.. + +.. date: 2022-11-21-16-24-01 +.. gh-issue: 83035 +.. nonce: qZIujU +.. section: Library + +Fix :func:`inspect.getsource` handling of decorator calls with nested +parentheses. + +.. + +.. date: 2022-11-20-11-59-54 +.. gh-issue: 99576 +.. nonce: ZD7jU6 +.. section: Library + +Fix ``.save()`` method for ``LWPCookieJar`` and ``MozillaCookieJar``: saved +file was not truncated on repeated save. + +.. + +.. date: 2022-11-17-10-02-18 +.. gh-issue: 94912 +.. nonce: G2aa-E +.. section: Library + +Add :func:`inspect.markcoroutinefunction` decorator which manually marks a +function as a coroutine for the benefit of :func:`iscoroutinefunction`. + +.. + +.. date: 2022-11-15-18-45-01 +.. gh-issue: 99509 +.. nonce: FLK0xU +.. section: Library + +Add :pep:`585` support for :class:`multiprocessing.queues.Queue`. + +.. + +.. date: 2022-11-14-19-58-36 +.. gh-issue: 99482 +.. nonce: XmZyUr +.. section: Library + +Remove ``Jython`` partial compatibility code from several stdlib modules. + +.. + +.. date: 2022-11-13-15-32-19 +.. gh-issue: 99433 +.. nonce: Ys6y0A +.. section: Library + +Fix :mod:`doctest` failure on :class:`types.MethodWrapperType` in modules. + +.. + +.. date: 2022-10-28-07-24-34 +.. gh-issue: 85267 +.. nonce: xUy_Wm +.. section: Library + +Several improvements to :func:`inspect.signature`'s handling of +``__text_signature``. - Fixes a case where :func:`inspect.signature` dropped +parameters - Fixes a case where :func:`inspect.signature` raised +:exc:`tokenize.TokenError` - Allows :func:`inspect.signature` to understand +defaults involving binary operations of constants - +:func:`inspect.signature` is documented as only raising :exc:`TypeError` or +:exc:`ValueError`, but sometimes raised :exc:`RuntimeError`. These cases now +raise :exc:`ValueError` - Removed a dead code path + +.. + +.. date: 2022-10-24-07-31-11 +.. gh-issue: 91166 +.. nonce: -IG06R +.. section: Library + +:mod:`asyncio` is optimized to avoid excessive copying when writing to +socket and use :meth:`~socket.socket.sendmsg` if the platform supports it. +Patch by Kumar Aditya. + +.. + +.. date: 2022-10-07-18-16-00 +.. gh-issue: 98030 +.. nonce: 2oQCZy +.. section: Library + +Add missing TCP socket options from Linux: ``TCP_MD5SIG``, +``TCP_THIN_LINEAR_TIMEOUTS``, ``TCP_THIN_DUPACK``, ``TCP_REPAIR``, +``TCP_REPAIR_QUEUE``, ``TCP_QUEUE_SEQ``, ``TCP_REPAIR_OPTIONS``, +``TCP_TIMESTAMP``, ``TCP_CC_INFO``, ``TCP_SAVE_SYN``, ``TCP_SAVED_SYN``, +``TCP_REPAIR_WINDOW``, ``TCP_FASTOPEN_CONNECT``, ``TCP_ULP``, +``TCP_MD5SIG_EXT``, ``TCP_FASTOPEN_KEY``, ``TCP_FASTOPEN_NO_COOKIE``, +``TCP_ZEROCOPY_RECEIVE``, ``TCP_INQ``, ``TCP_TX_DELAY``. + +.. + +.. date: 2022-09-16-08-21-46 +.. gh-issue: 88500 +.. nonce: jQ0pCc +.. section: Library + +Reduced the memory usage of :func:`urllib.parse.unquote` and +:func:`urllib.parse.unquote_to_bytes` on large values. + +.. + +.. date: 2022-08-27-10-35-50 +.. gh-issue: 96127 +.. nonce: 8RdLre +.. section: Library + +``inspect.signature`` was raising ``TypeError`` on call with mock objects. +Now it correctly returns ``(*args, **kwargs)`` as infered signature. + +.. + +.. date: 2022-08-11-10-02-19 +.. gh-issue: 95882 +.. nonce: FsUr72 +.. section: Library + +Fix a 3.11 regression in :func:`~contextlib.asynccontextmanager`, which +caused it to propagate exceptions with incorrect tracebacks and fix a 3.11 +regression in :func:`~contextlib.contextmanager`, which caused it to +propagate exceptions with incorrect tracebacks for :exc:`StopIteration`. + +.. + +.. date: 2022-07-01-00-01-22 +.. gh-issue: 78707 +.. nonce: fHGSuM +.. section: Library + +Deprecate passing more than one positional argument to +:meth:`pathlib.PurePath.relative_to` and +:meth:`~pathlib.PurePath.is_relative_to`. + +.. + +.. date: 2022-05-06-01-53-34 +.. gh-issue: 92122 +.. nonce: 96Lf2p +.. section: Library + +Fix reStructuredText syntax errors in docstrings in the :mod:`enum` module. + +.. + +.. date: 2022-04-23-08-12-14 +.. gh-issue: 91851 +.. nonce: Jd47V6 +.. section: Library + +Optimize the :class:`~fractions.Fraction` arithmetics for small components. + +.. + +.. bpo: 24132 +.. date: 2022-03-05-02-14-09 +.. nonce: W6iORO +.. section: Library + +Make :class:`pathlib.PurePath` and :class:`~pathlib.Path` subclassable +(private to start). Previously, attempting to instantiate a subclass +resulted in an :exc:`AttributeError` being raised. Patch by Barney Gale. + +.. + +.. bpo: 40447 +.. date: 2020-05-03-12-55-55 +.. nonce: oKR0Lj +.. section: Library + +Accept :class:`os.PathLike` (such as :class:`pathlib.Path`) in the +``stripdir`` arguments of :meth:`compileall.compile_file` and +:meth:`compileall.compile_dir`. + +.. + +.. bpo: 36880 +.. date: 2019-05-13-11-37-30 +.. nonce: ZgBgH0 +.. section: Library + +Fix a reference counting issue when a :mod:`ctypes` callback with return +type :class:`~ctypes.py_object` returns ``None``, which could cause crashes. + +.. + +.. date: 2022-12-30-00-42-23 +.. gh-issue: 100616 +.. nonce: eu80ij +.. section: Documentation + +Document existing ``attr`` parameter to :func:`curses.window.vline` function +in :mod:`curses`. + +.. + +.. date: 2022-12-23-21-42-26 +.. gh-issue: 100472 +.. nonce: NNixfO +.. section: Documentation + +Remove claim in documentation that the ``stripdir``, ``prependdir`` and +``limit_sl_dest`` parameters of :func:`compileall.compile_dir` and +:func:`compileall.compile_file` could be :class:`bytes`. + +.. + +.. bpo: 25377 +.. date: 2020-06-17-14-47-48 +.. nonce: CTxC6o +.. section: Documentation + +Clarify use of octal format of mode argument in help(os.chmod) as well as +help(os.fchmod) + +.. + +.. date: 2022-12-23-13-29-55 +.. gh-issue: 100454 +.. nonce: 3no0cW +.. section: Tests + +Start running SSL tests with OpenSSL 3.1.0-beta1. + +.. + +.. date: 2022-12-08-00-03-37 +.. gh-issue: 100086 +.. nonce: 1zYpto +.. section: Tests + +The Python test runner (libregrtest) now logs Python build information like +"debug" vs "release" build, or LTO and PGO optimizations. Patch by Victor +Stinner. + +.. + +.. date: 2022-06-16-13-26-31 +.. gh-issue: 93018 +.. nonce: wvNx76 +.. section: Tests + +Make two tests forgiving towards host system libexpat with backported +security fixes applied. + +.. + +.. date: 2022-12-26-15-07-48 +.. gh-issue: 100540 +.. nonce: l6ToSY +.. section: Build + +Removed the ``--with-system-ffi`` ``configure`` option; ``libffi`` must now +always be supplied by the system on all non-Windows platforms. The option +has had no effect on non-Darwin platforms for several releases, and in 3.11 +only had the non-obvious effect of invoking ``pkg-config`` to find +``libffi`` and never setting ``-DUSING_APPLE_OS_LIBFFI``. Now on Darwin +platforms ``configure`` will first check for the OS ``libffi`` and then fall +back to the same processing as other platforms if it is not found. + +.. + +.. date: 2022-12-08-14-00-04 +.. gh-issue: 88267 +.. nonce: MqtRbm +.. section: Build + +Avoid exporting Python symbols in linked Windows applications when the core +is built as static. + +.. + +.. bpo: 41916 +.. date: 2022-03-04-10-47-23 +.. nonce: 1d2GLU +.. section: Build + +Allow override of ac_cv_cxx_thread so that cross compiled python can set +-pthread for CXX. + +.. + +.. date: 2023-01-09-23-03-57 +.. gh-issue: 100180 +.. nonce: b5phrg +.. section: Windows + +Update Windows installer to OpenSSL 1.1.1s + +.. + +.. date: 2022-12-20-18-36-17 +.. gh-issue: 99191 +.. nonce: 0cfRja +.. section: Windows + +Use ``_MSVC_LANG >= 202002L`` instead of less-precise ``_MSC_VER >=1929`` to +more accurately test for C++20 support in :file:`PC/_wmimodule.cpp`. + +.. + +.. date: 2022-12-09-22-47-42 +.. gh-issue: 79218 +.. nonce: Yiot2e +.. section: Windows + +Define ``MS_WIN64`` for Mingw-w64 64bit, fix cython compilation failure. + +.. + +.. date: 2022-12-06-11-16-46 +.. gh-issue: 99941 +.. nonce: GmUQ6o +.. section: Windows + +Ensure that :func:`asyncio.Protocol.data_received` receives an immutable +:class:`bytes` object (as documented), instead of :class:`bytearray`. + +.. + +.. bpo: 43984 +.. date: 2021-05-02-15-29-33 +.. nonce: U92jiv +.. section: Windows + +:meth:`winreg.SetValueEx` now leaves the target value untouched in the case +of conversion errors. Previously, ``-1`` would be written in case of such +errors. + +.. + +.. bpo: 34816 +.. date: 2021-04-08-00-36-37 +.. nonce: 4Xe0id +.. section: Windows + +``hasattr(ctypes.windll, 'nonexistant')`` now returns ``False`` instead of +raising :exc:`OSError`. + +.. + +.. date: 2023-01-09-22-04-21 +.. gh-issue: 100180 +.. nonce: WVhCny +.. section: macOS + +Update macOS installer to OpenSSL 1.1.1s + +.. + +.. date: 2022-12-26-14-52-37 +.. gh-issue: 100540 +.. nonce: kYZLtX +.. section: macOS + +Removed obsolete ``dlfcn.h`` shim from the ``_ctypes`` extension module, +which has not been necessary since Mac OS X 10.2. + +.. + +.. bpo: 45256 +.. date: 2022-12-29-19-22-11 +.. nonce: a0ee_H +.. section: Tools/Demos + +Fix a bug that caused an :exc:`AttributeError` to be raised in +``python-gdb.py`` when ``py-locals`` is used without a frame. + +.. + +.. date: 2022-12-19-10-08-53 +.. gh-issue: 100342 +.. nonce: qDFlQG +.. section: Tools/Demos + +Add missing ``NULL`` check for possible allocation failure in ``*args`` +parsing in Argument Clinic. + +.. + +.. date: 2022-12-02-09-31-19 +.. gh-issue: 99947 +.. nonce: Ski7OC +.. section: C API + +Raising SystemError on import will now have its cause be set to the original +unexpected exception. + +.. + +.. date: 2022-11-30-16-39-22 +.. gh-issue: 99240 +.. nonce: 67nAX- +.. section: C API + +In argument parsing, after deallocating newly allocated memory, reset its +pointer to NULL. + +.. + +.. date: 2022-11-04-16-13-35 +.. gh-issue: 98724 +.. nonce: p0urWO +.. section: C API + +The :c:macro:`Py_CLEAR`, :c:macro:`Py_SETREF` and :c:macro:`Py_XSETREF` +macros now only evaluate their arguments once. If an argument has side +effects, these side effects are no longer duplicated. Patch by Victor +Stinner. diff --git a/Misc/NEWS.d/next/Build/2022-03-04-10-47-23.bpo-41916.1d2GLU.rst b/Misc/NEWS.d/next/Build/2022-03-04-10-47-23.bpo-41916.1d2GLU.rst deleted file mode 100644 index 2fc5f8ec77b1..000000000000 --- a/Misc/NEWS.d/next/Build/2022-03-04-10-47-23.bpo-41916.1d2GLU.rst +++ /dev/null @@ -1,2 +0,0 @@ -Allow override of ac_cv_cxx_thread so that cross compiled python can set --pthread for CXX. diff --git a/Misc/NEWS.d/next/Build/2022-12-08-14-00-04.gh-issue-88267.MqtRbm.rst b/Misc/NEWS.d/next/Build/2022-12-08-14-00-04.gh-issue-88267.MqtRbm.rst deleted file mode 100644 index 29c3f5a1d763..000000000000 --- a/Misc/NEWS.d/next/Build/2022-12-08-14-00-04.gh-issue-88267.MqtRbm.rst +++ /dev/null @@ -1 +0,0 @@ -Avoid exporting Python symbols in linked Windows applications when the core is built as static. diff --git a/Misc/NEWS.d/next/Build/2022-12-26-15-07-48.gh-issue-100540.l6ToSY.rst b/Misc/NEWS.d/next/Build/2022-12-26-15-07-48.gh-issue-100540.l6ToSY.rst deleted file mode 100644 index c924ab520f3d..000000000000 --- a/Misc/NEWS.d/next/Build/2022-12-26-15-07-48.gh-issue-100540.l6ToSY.rst +++ /dev/null @@ -1,8 +0,0 @@ -Removed the ``--with-system-ffi`` ``configure`` option; ``libffi`` must -now always be supplied by the system on all non-Windows platforms. The -option has had no effect on non-Darwin platforms for several releases, and -in 3.11 only had the non-obvious effect of invoking ``pkg-config`` to -find ``libffi`` and never setting ``-DUSING_APPLE_OS_LIBFFI``. Now on -Darwin platforms ``configure`` will first check for the OS ``libffi`` and -then fall back to the same processing as other platforms if it is not -found. diff --git a/Misc/NEWS.d/next/C API/2022-11-04-16-13-35.gh-issue-98724.p0urWO.rst b/Misc/NEWS.d/next/C API/2022-11-04-16-13-35.gh-issue-98724.p0urWO.rst deleted file mode 100644 index e6c26cde168d..000000000000 --- a/Misc/NEWS.d/next/C API/2022-11-04-16-13-35.gh-issue-98724.p0urWO.rst +++ /dev/null @@ -1,3 +0,0 @@ -The :c:macro:`Py_CLEAR`, :c:macro:`Py_SETREF` and :c:macro:`Py_XSETREF` macros -now only evaluate their arguments once. If an argument has side effects, these -side effects are no longer duplicated. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C API/2022-11-30-16-39-22.gh-issue-99240.67nAX-.rst b/Misc/NEWS.d/next/C API/2022-11-30-16-39-22.gh-issue-99240.67nAX-.rst deleted file mode 100644 index 9a1bb3cf5a73..000000000000 --- a/Misc/NEWS.d/next/C API/2022-11-30-16-39-22.gh-issue-99240.67nAX-.rst +++ /dev/null @@ -1,2 +0,0 @@ -In argument parsing, after deallocating newly allocated memory, reset its -pointer to NULL. diff --git a/Misc/NEWS.d/next/C API/2022-12-02-09-31-19.gh-issue-99947.Ski7OC.rst b/Misc/NEWS.d/next/C API/2022-12-02-09-31-19.gh-issue-99947.Ski7OC.rst deleted file mode 100644 index fbed192d317b..000000000000 --- a/Misc/NEWS.d/next/C API/2022-12-02-09-31-19.gh-issue-99947.Ski7OC.rst +++ /dev/null @@ -1 +0,0 @@ -Raising SystemError on import will now have its cause be set to the original unexpected exception. diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-02-06-23-21-13.bpo-32782.EJVSfR.rst b/Misc/NEWS.d/next/Core and Builtins/2018-02-06-23-21-13.bpo-32782.EJVSfR.rst deleted file mode 100644 index 841740130bd1..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2018-02-06-23-21-13.bpo-32782.EJVSfR.rst +++ /dev/null @@ -1,3 +0,0 @@ -``ctypes`` arrays of length 0 now report a correct itemsize when a -``memoryview`` is constructed from them, rather than always giving a value -of 0. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-06-17-08-00-34.gh-issue-89051.yP4Na0.rst b/Misc/NEWS.d/next/Core and Builtins/2022-06-17-08-00-34.gh-issue-89051.yP4Na0.rst deleted file mode 100644 index 5c8164863b81..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-06-17-08-00-34.gh-issue-89051.yP4Na0.rst +++ /dev/null @@ -1 +0,0 @@ -Add :data:`ssl.OP_LEGACY_SERVER_CONNECT` diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-07-06-18-44-00.gh-issue-94603.Q_03xV.rst b/Misc/NEWS.d/next/Core and Builtins/2022-07-06-18-44-00.gh-issue-94603.Q_03xV.rst deleted file mode 100644 index de4fe4d6df8c..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-07-06-18-44-00.gh-issue-94603.Q_03xV.rst +++ /dev/null @@ -1 +0,0 @@ -Improve performance of ``list.pop`` for small lists. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-21-16-10-39.gh-issue-98522.s_SixG.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-21-16-10-39.gh-issue-98522.s_SixG.rst deleted file mode 100644 index d923af198f82..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-10-21-16-10-39.gh-issue-98522.s_SixG.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add an internal version number to code objects, to give better versioning of -inner functions and comprehensions, and thus better specialization of those -functions. This change is invisible to both Python and C extensions. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-11-16-05-57-24.gh-issue-99554.A_Ywd2.rst b/Misc/NEWS.d/next/Core and Builtins/2022-11-16-05-57-24.gh-issue-99554.A_Ywd2.rst deleted file mode 100644 index 96ec47db461d..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-11-16-05-57-24.gh-issue-99554.A_Ywd2.rst +++ /dev/null @@ -1 +0,0 @@ -Pack debugging location tables more efficiently during bytecode compilation. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-11-19-01-11-06.gh-issue-99582.wvOBVy.rst b/Misc/NEWS.d/next/Core and Builtins/2022-11-19-01-11-06.gh-issue-99582.wvOBVy.rst deleted file mode 100644 index 320d47cb9cf6..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-11-19-01-11-06.gh-issue-99582.wvOBVy.rst +++ /dev/null @@ -1 +0,0 @@ -Freeze :mod:`zipimport` module into ``_bootstrap_python``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-04-00-38-33.gh-issue-92216.CJXuWB.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-04-00-38-33.gh-issue-92216.CJXuWB.rst deleted file mode 100644 index f7ef52d97c27..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-04-00-38-33.gh-issue-92216.CJXuWB.rst +++ /dev/null @@ -1 +0,0 @@ -Improve the performance of :func:`hasattr` for type objects with a missing attribute. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-06-22-24-01.gh-issue-100050.lcrPqQ.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-06-22-24-01.gh-issue-100050.lcrPqQ.rst deleted file mode 100644 index 8e7c72d804f8..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-06-22-24-01.gh-issue-100050.lcrPqQ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Honor existing errors obtained when searching for mismatching parentheses in -the tokenizer. Patch by Pablo Galindo diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-08-12-26-34.gh-issue-100110.ertac-.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-08-12-26-34.gh-issue-100110.ertac-.rst deleted file mode 100644 index e494f44fd42c..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-08-12-26-34.gh-issue-100110.ertac-.rst +++ /dev/null @@ -1 +0,0 @@ -Specialize ``FOR_ITER`` for tuples. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-09-13-18-42.gh-issue-100146.xLVKg0.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-09-13-18-42.gh-issue-100146.xLVKg0.rst deleted file mode 100644 index 8023a366a0e6..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-09-13-18-42.gh-issue-100146.xLVKg0.rst +++ /dev/null @@ -1,4 +0,0 @@ -Improve ``BUILD_LIST`` opcode so that it works similarly to the -``BUILD_TUPLE`` opcode, by stealing references from the stack rather than -repeatedly using stack operations to set list elements. Implementation -details are in a new private API :c:func:`_PyList_FromArraySteal`. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-09-14-27-36.gh-issue-100143.5g9rb4.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-09-14-27-36.gh-issue-100143.5g9rb4.rst deleted file mode 100644 index 20a25f8b03d1..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-09-14-27-36.gh-issue-100143.5g9rb4.rst +++ /dev/null @@ -1,3 +0,0 @@ -When built with ``--enable-pystats``, stats collection is now off by -default. To enable it early at startup, pass the ``-Xpystats`` flag. Stats -are now always dumped, even if switched off. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-10-20-00-13.gh-issue-99540.ZZZHeP.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-10-20-00-13.gh-issue-99540.ZZZHeP.rst deleted file mode 100644 index ae043f3aba55..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-10-20-00-13.gh-issue-99540.ZZZHeP.rst +++ /dev/null @@ -1 +0,0 @@ -``None`` now hashes to a constant value. This is not a requirements change. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-12-00-59-11.gh-issue-94155.LWE9y_.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-12-00-59-11.gh-issue-94155.LWE9y_.rst deleted file mode 100644 index e7c7ed2fad0e..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-12-00-59-11.gh-issue-94155.LWE9y_.rst +++ /dev/null @@ -1 +0,0 @@ -Improved the hashing algorithm for code objects, mitigating some hash collisions. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-12-01-05-16.gh-issue-99110.1JqtIg.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-12-01-05-16.gh-issue-99110.1JqtIg.rst deleted file mode 100644 index 175740dfca07..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-12-01-05-16.gh-issue-99110.1JqtIg.rst +++ /dev/null @@ -1,2 +0,0 @@ -Initialize frame->previous in frameobject.c to fix a segmentation fault when -accessing frames created by :c:func:`PyFrame_New`. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-12-05-30-12.gh-issue-100188.sGCSMR.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-12-05-30-12.gh-issue-100188.sGCSMR.rst deleted file mode 100644 index ec62fbd582fb..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-12-05-30-12.gh-issue-100188.sGCSMR.rst +++ /dev/null @@ -1,3 +0,0 @@ -The ``BINARY_SUBSCR_LIST_INT`` and ``BINARY_SUBSCR_TUPLE_INT`` -instructions are no longer used for negative integers because -those instructions always miss when encountering negative integers. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-12-11-27-54.gh-issue-99955.Ix5Rrg.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-12-11-27-54.gh-issue-99955.Ix5Rrg.rst deleted file mode 100644 index e9867b39ad7d..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-12-11-27-54.gh-issue-99955.Ix5Rrg.rst +++ /dev/null @@ -1 +0,0 @@ -Internal compiler functions (in compile.c) now consistently return -1 on error and 0 on success. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-13-16-05-18.gh-issue-100222.OVVvYe.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-13-16-05-18.gh-issue-100222.OVVvYe.rst deleted file mode 100644 index 032b49420d8b..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-13-16-05-18.gh-issue-100222.OVVvYe.rst +++ /dev/null @@ -1,2 +0,0 @@ -Redefine the ``_Py_CODEUNIT`` typedef as a union to describe its layout to -the C compiler, avoiding type punning and improving clarity. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-15-00-50-25.gh-issue-90043.gyoKdx.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-15-00-50-25.gh-issue-90043.gyoKdx.rst deleted file mode 100644 index 9a4f896e2cf5..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-15-00-50-25.gh-issue-90043.gyoKdx.rst +++ /dev/null @@ -1,2 +0,0 @@ -Handle NaNs when specializing :opcode:`COMPARE_OP` for :class:`float` -values. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-17-19-44-57.gh-issue-100117.yRWQ1y.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-17-19-44-57.gh-issue-100117.yRWQ1y.rst deleted file mode 100644 index 0ba6ee6f96e3..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-17-19-44-57.gh-issue-100117.yRWQ1y.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve the output of ``co_lines`` by emitting only one entry for each line -range. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-20-09-56-56.gh-issue-100357.hPyTwY.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-20-09-56-56.gh-issue-100357.hPyTwY.rst deleted file mode 100644 index fb25de6c9a3c..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-20-09-56-56.gh-issue-100357.hPyTwY.rst +++ /dev/null @@ -1,2 +0,0 @@ -Convert ``vars``, ``dir``, ``next``, ``getattr``, and ``iter`` to argument -clinic. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-20-16-14-19.gh-issue-100374.YRrVHT.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-20-16-14-19.gh-issue-100374.YRrVHT.rst deleted file mode 100644 index e78352fb188e..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-20-16-14-19.gh-issue-100374.YRrVHT.rst +++ /dev/null @@ -1 +0,0 @@ -Fix incorrect result and delay in :func:`socket.getfqdn`. Patch by Dominic Socular. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-21-22-48-41.gh-issue-100425.U64yLu.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-21-22-48-41.gh-issue-100425.U64yLu.rst deleted file mode 100644 index 5559020b11d3..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-21-22-48-41.gh-issue-100425.U64yLu.rst +++ /dev/null @@ -1 +0,0 @@ -Improve the accuracy of ``sum()`` with compensated summation. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-22-21-56-08.gh-issue-100268.xw_phB.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-22-21-56-08.gh-issue-100268.xw_phB.rst deleted file mode 100644 index 73d04c19d1cc..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-22-21-56-08.gh-issue-100268.xw_phB.rst +++ /dev/null @@ -1 +0,0 @@ -Add :meth:`int.is_integer` to improve duck type compatibility between :class:`int` and :class:`float`. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-28-15-02-53.gh-issue-87447.7-aekA.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-28-15-02-53.gh-issue-87447.7-aekA.rst deleted file mode 100644 index af60acf1103a..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-28-15-02-53.gh-issue-87447.7-aekA.rst +++ /dev/null @@ -1,5 +0,0 @@ -Fix :exc:`SyntaxError` on comprehension rebind checking with names that are -not actually redefined. - -Now reassigning ``b`` in ``[(b := 1) for a, b.prop in some_iter]`` is allowed. -Reassigning ``a`` is still disallowed as per :pep:`572`. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-29-04-39-38.gh-issue-100126.pfFJd-.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-29-04-39-38.gh-issue-100126.pfFJd-.rst deleted file mode 100644 index 0ec14253ceff..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-29-04-39-38.gh-issue-100126.pfFJd-.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix an issue where "incomplete" frames could be briefly visible to C code -while other frames are being torn down, possibly resulting in corruption or -hard crashes of the interpreter while running finalizers. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-31-23-32-09.gh-issue-100649.C0fY4S.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-31-23-32-09.gh-issue-100649.C0fY4S.rst deleted file mode 100644 index 7ee929ad09fb..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-12-31-23-32-09.gh-issue-100649.C0fY4S.rst +++ /dev/null @@ -1 +0,0 @@ -Update the native_thread_id field of PyThreadState after fork. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-01-15-59-48.gh-issue-100637.M2n6Kg.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-01-15-59-48.gh-issue-100637.M2n6Kg.rst deleted file mode 100644 index 164f6f5f2f44..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-01-15-59-48.gh-issue-100637.M2n6Kg.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :func:`int.__sizeof__` calculation to include the 1 element ob_digit array for 0 and False. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-38-18.gh-issue-100719.2C--ko.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-38-18.gh-issue-100719.2C--ko.rst deleted file mode 100644 index bb5d5619c2d0..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-38-18.gh-issue-100719.2C--ko.rst +++ /dev/null @@ -1,2 +0,0 @@ -Removed the co_nplaincellvars field from the code object, as it is -redundant. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-50-42.gh-issue-100720.UhE7P-.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-50-42.gh-issue-100720.UhE7P-.rst deleted file mode 100644 index 4c194eccf8a5..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-16-50-42.gh-issue-100720.UhE7P-.rst +++ /dev/null @@ -1 +0,0 @@ -Added ``_PyFrame_NumSlotsForCodeObject``, which returns the number of slots needed in a frame for a given code object. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-04-16-40-55.gh-issue-100288.hRSRaT.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-04-16-40-55.gh-issue-100288.hRSRaT.rst deleted file mode 100644 index 5a1b50babbf5..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-04-16-40-55.gh-issue-100288.hRSRaT.rst +++ /dev/null @@ -1,2 +0,0 @@ -Remove the LOAD_ATTR_METHOD_WITH_DICT specialized instruction. Stats show it -is not useful. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-05-13-54-00.gh-issue-99005.D7H6j4.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-05-13-54-00.gh-issue-99005.D7H6j4.rst deleted file mode 100644 index bd81cde45bc9..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-05-13-54-00.gh-issue-99005.D7H6j4.rst +++ /dev/null @@ -1,4 +0,0 @@ -Add new :opcode:`CALL_INSTRINSIC_1` instruction. Remove -:opcode:`IMPORT_STAR`, :opcode:`PRINT_EXPR` and -:opcode:`STOPITERATION_ERROR`, replacing them with the -:opcode:`CALL_INSTRINSIC_1` instruction. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-05-17-54-29.gh-issue-99005.cmGwxv.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-05-17-54-29.gh-issue-99005.cmGwxv.rst deleted file mode 100644 index e6ee44225345..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-05-17-54-29.gh-issue-99005.cmGwxv.rst +++ /dev/null @@ -1,2 +0,0 @@ -Remove :opcode:`UNARY_POSITIVE`, :opcode:`ASYNC_GEN_WRAP` and -:opcode:`LIST_TO_TUPLE`, replacing them with intrinsics. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst deleted file mode 100644 index b94121ea5f29..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2023-01-06-02-02-11.gh-issue-100776.pP8xux.rst +++ /dev/null @@ -1 +0,0 @@ -Fix misleading default value in :func:`input`'s ``__text_signature__``. diff --git a/Misc/NEWS.d/next/Documentation/2020-06-17-14-47-48.bpo-25377.CTxC6o.rst b/Misc/NEWS.d/next/Documentation/2020-06-17-14-47-48.bpo-25377.CTxC6o.rst deleted file mode 100644 index 019a1c42d88e..000000000000 --- a/Misc/NEWS.d/next/Documentation/2020-06-17-14-47-48.bpo-25377.CTxC6o.rst +++ /dev/null @@ -1 +0,0 @@ -Clarify use of octal format of mode argument in help(os.chmod) as well as help(os.fchmod) diff --git a/Misc/NEWS.d/next/Documentation/2022-12-23-21-42-26.gh-issue-100472.NNixfO.rst b/Misc/NEWS.d/next/Documentation/2022-12-23-21-42-26.gh-issue-100472.NNixfO.rst deleted file mode 100644 index 4f4162150750..000000000000 --- a/Misc/NEWS.d/next/Documentation/2022-12-23-21-42-26.gh-issue-100472.NNixfO.rst +++ /dev/null @@ -1 +0,0 @@ -Remove claim in documentation that the ``stripdir``, ``prependdir`` and ``limit_sl_dest`` parameters of :func:`compileall.compile_dir` and :func:`compileall.compile_file` could be :class:`bytes`. diff --git a/Misc/NEWS.d/next/Documentation/2022-12-30-00-42-23.gh-issue-100616.eu80ij.rst b/Misc/NEWS.d/next/Documentation/2022-12-30-00-42-23.gh-issue-100616.eu80ij.rst deleted file mode 100644 index 97a7022c94b4..000000000000 --- a/Misc/NEWS.d/next/Documentation/2022-12-30-00-42-23.gh-issue-100616.eu80ij.rst +++ /dev/null @@ -1,2 +0,0 @@ -Document existing ``attr`` parameter to :func:`curses.window.vline` function -in :mod:`curses`. diff --git a/Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst b/Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst deleted file mode 100644 index f65323813d05..000000000000 --- a/Misc/NEWS.d/next/Library/2019-05-13-11-37-30.bpo-36880.ZgBgH0.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a reference counting issue when a :mod:`ctypes` callback with return -type :class:`~ctypes.py_object` returns ``None``, which could cause crashes. diff --git a/Misc/NEWS.d/next/Library/2020-05-03-12-55-55.bpo-40447.oKR0Lj.rst b/Misc/NEWS.d/next/Library/2020-05-03-12-55-55.bpo-40447.oKR0Lj.rst deleted file mode 100644 index 941038d095b3..000000000000 --- a/Misc/NEWS.d/next/Library/2020-05-03-12-55-55.bpo-40447.oKR0Lj.rst +++ /dev/null @@ -1,2 +0,0 @@ -Accept :class:`os.PathLike` (such as :class:`pathlib.Path`) in the ``stripdir`` arguments of -:meth:`compileall.compile_file` and :meth:`compileall.compile_dir`. diff --git a/Misc/NEWS.d/next/Library/2022-03-05-02-14-09.bpo-24132.W6iORO.rst b/Misc/NEWS.d/next/Library/2022-03-05-02-14-09.bpo-24132.W6iORO.rst deleted file mode 100644 index 8ca5213fb23a..000000000000 --- a/Misc/NEWS.d/next/Library/2022-03-05-02-14-09.bpo-24132.W6iORO.rst +++ /dev/null @@ -1,3 +0,0 @@ -Make :class:`pathlib.PurePath` and :class:`~pathlib.Path` subclassable -(private to start). Previously, attempting to instantiate a subclass -resulted in an :exc:`AttributeError` being raised. Patch by Barney Gale. diff --git a/Misc/NEWS.d/next/Library/2022-04-23-08-12-14.gh-issue-91851.Jd47V6.rst b/Misc/NEWS.d/next/Library/2022-04-23-08-12-14.gh-issue-91851.Jd47V6.rst deleted file mode 100644 index a918bffb601b..000000000000 --- a/Misc/NEWS.d/next/Library/2022-04-23-08-12-14.gh-issue-91851.Jd47V6.rst +++ /dev/null @@ -1 +0,0 @@ -Optimize the :class:`~fractions.Fraction` arithmetics for small components. diff --git a/Misc/NEWS.d/next/Library/2022-05-06-01-53-34.gh-issue-92122.96Lf2p.rst b/Misc/NEWS.d/next/Library/2022-05-06-01-53-34.gh-issue-92122.96Lf2p.rst deleted file mode 100644 index d585535ee38d..000000000000 --- a/Misc/NEWS.d/next/Library/2022-05-06-01-53-34.gh-issue-92122.96Lf2p.rst +++ /dev/null @@ -1 +0,0 @@ -Fix reStructuredText syntax errors in docstrings in the :mod:`enum` module. diff --git a/Misc/NEWS.d/next/Library/2022-07-01-00-01-22.gh-issue-78707.fHGSuM.rst b/Misc/NEWS.d/next/Library/2022-07-01-00-01-22.gh-issue-78707.fHGSuM.rst deleted file mode 100644 index c490a3c1bd7e..000000000000 --- a/Misc/NEWS.d/next/Library/2022-07-01-00-01-22.gh-issue-78707.fHGSuM.rst +++ /dev/null @@ -1,3 +0,0 @@ -Deprecate passing more than one positional argument to -:meth:`pathlib.PurePath.relative_to` and -:meth:`~pathlib.PurePath.is_relative_to`. diff --git a/Misc/NEWS.d/next/Library/2022-08-11-10-02-19.gh-issue-95882.FsUr72.rst b/Misc/NEWS.d/next/Library/2022-08-11-10-02-19.gh-issue-95882.FsUr72.rst deleted file mode 100644 index 9cdb237d5c87..000000000000 --- a/Misc/NEWS.d/next/Library/2022-08-11-10-02-19.gh-issue-95882.FsUr72.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a 3.11 regression in :func:`~contextlib.asynccontextmanager`, which caused it to propagate exceptions with incorrect tracebacks and fix a 3.11 regression in :func:`~contextlib.contextmanager`, which caused it to propagate exceptions with incorrect tracebacks for :exc:`StopIteration`. diff --git a/Misc/NEWS.d/next/Library/2022-08-27-10-35-50.gh-issue-96127.8RdLre.rst b/Misc/NEWS.d/next/Library/2022-08-27-10-35-50.gh-issue-96127.8RdLre.rst deleted file mode 100644 index 79edd8fd5d8f..000000000000 --- a/Misc/NEWS.d/next/Library/2022-08-27-10-35-50.gh-issue-96127.8RdLre.rst +++ /dev/null @@ -1,2 +0,0 @@ -``inspect.signature`` was raising ``TypeError`` on call with mock objects. -Now it correctly returns ``(*args, **kwargs)`` as infered signature. diff --git a/Misc/NEWS.d/next/Library/2022-09-16-08-21-46.gh-issue-88500.jQ0pCc.rst b/Misc/NEWS.d/next/Library/2022-09-16-08-21-46.gh-issue-88500.jQ0pCc.rst deleted file mode 100644 index ad01f5e16b16..000000000000 --- a/Misc/NEWS.d/next/Library/2022-09-16-08-21-46.gh-issue-88500.jQ0pCc.rst +++ /dev/null @@ -1,2 +0,0 @@ -Reduced the memory usage of :func:`urllib.parse.unquote` and -:func:`urllib.parse.unquote_to_bytes` on large values. diff --git a/Misc/NEWS.d/next/Library/2022-10-07-18-16-00.gh-issue-98030.2oQCZy.rst b/Misc/NEWS.d/next/Library/2022-10-07-18-16-00.gh-issue-98030.2oQCZy.rst deleted file mode 100644 index 7768ed0817e8..000000000000 --- a/Misc/NEWS.d/next/Library/2022-10-07-18-16-00.gh-issue-98030.2oQCZy.rst +++ /dev/null @@ -1,7 +0,0 @@ -Add missing TCP socket options from Linux: ``TCP_MD5SIG``, -``TCP_THIN_LINEAR_TIMEOUTS``, ``TCP_THIN_DUPACK``, ``TCP_REPAIR``, -``TCP_REPAIR_QUEUE``, ``TCP_QUEUE_SEQ``, ``TCP_REPAIR_OPTIONS``, -``TCP_TIMESTAMP``, ``TCP_CC_INFO``, ``TCP_SAVE_SYN``, ``TCP_SAVED_SYN``, -``TCP_REPAIR_WINDOW``, ``TCP_FASTOPEN_CONNECT``, ``TCP_ULP``, -``TCP_MD5SIG_EXT``, ``TCP_FASTOPEN_KEY``, ``TCP_FASTOPEN_NO_COOKIE``, -``TCP_ZEROCOPY_RECEIVE``, ``TCP_INQ``, ``TCP_TX_DELAY``. diff --git a/Misc/NEWS.d/next/Library/2022-10-24-07-31-11.gh-issue-91166.-IG06R.rst b/Misc/NEWS.d/next/Library/2022-10-24-07-31-11.gh-issue-91166.-IG06R.rst deleted file mode 100644 index 5ee08ec57843..000000000000 --- a/Misc/NEWS.d/next/Library/2022-10-24-07-31-11.gh-issue-91166.-IG06R.rst +++ /dev/null @@ -1 +0,0 @@ -:mod:`asyncio` is optimized to avoid excessive copying when writing to socket and use :meth:`~socket.socket.sendmsg` if the platform supports it. Patch by Kumar Aditya. diff --git a/Misc/NEWS.d/next/Library/2022-10-28-07-24-34.gh-issue-85267.xUy_Wm.rst b/Misc/NEWS.d/next/Library/2022-10-28-07-24-34.gh-issue-85267.xUy_Wm.rst deleted file mode 100644 index e69fd1ca1c2f..000000000000 --- a/Misc/NEWS.d/next/Library/2022-10-28-07-24-34.gh-issue-85267.xUy_Wm.rst +++ /dev/null @@ -1,6 +0,0 @@ -Several improvements to :func:`inspect.signature`'s handling of ``__text_signature``. -- Fixes a case where :func:`inspect.signature` dropped parameters -- Fixes a case where :func:`inspect.signature` raised :exc:`tokenize.TokenError` -- Allows :func:`inspect.signature` to understand defaults involving binary operations of constants -- :func:`inspect.signature` is documented as only raising :exc:`TypeError` or :exc:`ValueError`, but sometimes raised :exc:`RuntimeError`. These cases now raise :exc:`ValueError` -- Removed a dead code path diff --git a/Misc/NEWS.d/next/Library/2022-11-13-15-32-19.gh-issue-99433.Ys6y0A.rst b/Misc/NEWS.d/next/Library/2022-11-13-15-32-19.gh-issue-99433.Ys6y0A.rst deleted file mode 100644 index 8e13a9a66a7e..000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-13-15-32-19.gh-issue-99433.Ys6y0A.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :mod:`doctest` failure on :class:`types.MethodWrapperType` in modules. diff --git a/Misc/NEWS.d/next/Library/2022-11-14-19-58-36.gh-issue-99482.XmZyUr.rst b/Misc/NEWS.d/next/Library/2022-11-14-19-58-36.gh-issue-99482.XmZyUr.rst deleted file mode 100644 index dd2c925478c3..000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-14-19-58-36.gh-issue-99482.XmZyUr.rst +++ /dev/null @@ -1 +0,0 @@ -Remove ``Jython`` partial compatibility code from several stdlib modules. diff --git a/Misc/NEWS.d/next/Library/2022-11-15-18-45-01.gh-issue-99509.FLK0xU.rst b/Misc/NEWS.d/next/Library/2022-11-15-18-45-01.gh-issue-99509.FLK0xU.rst deleted file mode 100644 index 634281061cec..000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-15-18-45-01.gh-issue-99509.FLK0xU.rst +++ /dev/null @@ -1 +0,0 @@ -Add :pep:`585` support for :class:`multiprocessing.queues.Queue`. diff --git a/Misc/NEWS.d/next/Library/2022-11-17-10-02-18.gh-issue-94912.G2aa-E.rst b/Misc/NEWS.d/next/Library/2022-11-17-10-02-18.gh-issue-94912.G2aa-E.rst deleted file mode 100644 index ee00f9d8d03f..000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-17-10-02-18.gh-issue-94912.G2aa-E.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :func:`inspect.markcoroutinefunction` decorator which manually marks -a function as a coroutine for the benefit of :func:`iscoroutinefunction`. diff --git a/Misc/NEWS.d/next/Library/2022-11-20-11-59-54.gh-issue-99576.ZD7jU6.rst b/Misc/NEWS.d/next/Library/2022-11-20-11-59-54.gh-issue-99576.ZD7jU6.rst deleted file mode 100644 index 9cbeb64b5625..000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-20-11-59-54.gh-issue-99576.ZD7jU6.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix ``.save()`` method for ``LWPCookieJar`` and ``MozillaCookieJar``: saved -file was not truncated on repeated save. diff --git a/Misc/NEWS.d/next/Library/2022-11-21-16-24-01.gh-issue-83035.qZIujU.rst b/Misc/NEWS.d/next/Library/2022-11-21-16-24-01.gh-issue-83035.qZIujU.rst deleted file mode 100644 index 629d9aefb2d8..000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-21-16-24-01.gh-issue-83035.qZIujU.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :func:`inspect.getsource` handling of decorator calls with nested parentheses. diff --git a/Misc/NEWS.d/next/Library/2022-11-23-23-58-45.gh-issue-94943.Oog0Zo.rst b/Misc/NEWS.d/next/Library/2022-11-23-23-58-45.gh-issue-94943.Oog0Zo.rst deleted file mode 100644 index ed4754e49bd2..000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-23-23-58-45.gh-issue-94943.Oog0Zo.rst +++ /dev/null @@ -1,5 +0,0 @@ -Add :ref:`enum-dataclass-support` to the -:class:`~enum.Enum` :meth:`~enum.Enum.__repr__`. -When inheriting from a :class:`~dataclasses.dataclass`, -only show the field names in the value section of the member :func:`repr`, -and not the dataclass' class name. diff --git a/Misc/NEWS.d/next/Library/2022-11-29-20-44-54.gh-issue-89727.UJZjkk.rst b/Misc/NEWS.d/next/Library/2022-11-29-20-44-54.gh-issue-89727.UJZjkk.rst deleted file mode 100644 index 8a5fdb64b87f..000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-29-20-44-54.gh-issue-89727.UJZjkk.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix issue with :func:`os.walk` where a :exc:`RecursionError` would occur on -deep directory structures by adjusting the implementation of -:func:`os.walk` to be iterative instead of recursive. diff --git a/Misc/NEWS.d/next/Library/2022-12-01-15-44-58.gh-issue-99925.x4y6pF.rst b/Misc/NEWS.d/next/Library/2022-12-01-15-44-58.gh-issue-99925.x4y6pF.rst deleted file mode 100644 index 660635a03963..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-01-15-44-58.gh-issue-99925.x4y6pF.rst +++ /dev/null @@ -1,4 +0,0 @@ -Unify error messages in JSON serialization between -``json.dumps(float('nan'), allow_nan=False)`` and ``json.dumps(float('nan'), -allow_nan=False, indent=)``. Now both include the representation -of the value that could not be serialized. diff --git a/Misc/NEWS.d/next/Library/2022-12-03-20-06-16.gh-issue-98778.t5U9uc.rst b/Misc/NEWS.d/next/Library/2022-12-03-20-06-16.gh-issue-98778.t5U9uc.rst deleted file mode 100644 index b1c170dff3ea..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-03-20-06-16.gh-issue-98778.t5U9uc.rst +++ /dev/null @@ -1,2 +0,0 @@ -Update :exc:`~urllib.error.HTTPError` to be initialized properly, even if -the ``fp`` is ``None``. Patch by Dong-hee Na. diff --git a/Misc/NEWS.d/next/Library/2022-12-04-16-12-04.gh-issue-85432.l_ehmI.rst b/Misc/NEWS.d/next/Library/2022-12-04-16-12-04.gh-issue-85432.l_ehmI.rst deleted file mode 100644 index 68f5d7c942f5..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-04-16-12-04.gh-issue-85432.l_ehmI.rst +++ /dev/null @@ -1,5 +0,0 @@ -Rename the *fmt* parameter of the pure-Python implementation of -:meth:`datetime.time.strftime` to *format*. Rename the *t* parameter of -:meth:`datetime.datetime.fromtimestamp` to *timestamp*. These changes mean -the parameter names in the pure-Python implementation now match the -parameter names in the C implementation. Patch by Alex Waygood. diff --git a/Misc/NEWS.d/next/Library/2022-12-08-06-18-06.gh-issue-100098.uBvPlp.rst b/Misc/NEWS.d/next/Library/2022-12-08-06-18-06.gh-issue-100098.uBvPlp.rst deleted file mode 100644 index 256f2bcd39f8..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-08-06-18-06.gh-issue-100098.uBvPlp.rst +++ /dev/null @@ -1 +0,0 @@ -Fix ``tuple`` subclasses being cast to ``tuple`` when used as enum values. diff --git a/Misc/NEWS.d/next/Library/2022-12-09-10-35-36.bpo-44592.z-P3oe.rst b/Misc/NEWS.d/next/Library/2022-12-09-10-35-36.bpo-44592.z-P3oe.rst deleted file mode 100644 index 7f290605934d..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-09-10-35-36.bpo-44592.z-P3oe.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixes inconsistent handling of case sensitivity of *extrasaction* arg in -:class:`csv.DictWriter`. diff --git a/Misc/NEWS.d/next/Library/2022-12-10-08-36-07.gh-issue-100133.g-zQlp.rst b/Misc/NEWS.d/next/Library/2022-12-10-08-36-07.gh-issue-100133.g-zQlp.rst deleted file mode 100644 index 881e6ed80fed..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-10-08-36-07.gh-issue-100133.g-zQlp.rst +++ /dev/null @@ -1 +0,0 @@ -Fix regression in :mod:`asyncio` where a subprocess would sometimes lose data received from pipe. diff --git a/Misc/NEWS.d/next/Library/2022-12-10-20-52-28.gh-issue-100039.zDqjT4.rst b/Misc/NEWS.d/next/Library/2022-12-10-20-52-28.gh-issue-100039.zDqjT4.rst deleted file mode 100644 index d56643f7e871..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-10-20-52-28.gh-issue-100039.zDqjT4.rst +++ /dev/null @@ -1 +0,0 @@ -Improve signatures for enums and flags. diff --git a/Misc/NEWS.d/next/Library/2022-12-13-17-29-09.gh-issue-100228.bgtzMV.rst b/Misc/NEWS.d/next/Library/2022-12-13-17-29-09.gh-issue-100228.bgtzMV.rst deleted file mode 100644 index ca6e4a2a1f0d..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-13-17-29-09.gh-issue-100228.bgtzMV.rst +++ /dev/null @@ -1,5 +0,0 @@ -A :exc:`DeprecationWarning` may be raised when :func:`os.fork()` or -:func:`os.forkpty()` is called from multi-threaded processes. Forking -with threads is unsafe and can cause deadlocks, crashes and subtle -problems. Lack of a warning does not indicate that the fork call was -actually safe, as Python may not be aware of all threads. diff --git a/Misc/NEWS.d/next/Library/2022-12-14-11-45-38.gh-issue-100234.kn6yWV.rst b/Misc/NEWS.d/next/Library/2022-12-14-11-45-38.gh-issue-100234.kn6yWV.rst deleted file mode 100644 index 6d9b909d874b..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-14-11-45-38.gh-issue-100234.kn6yWV.rst +++ /dev/null @@ -1,2 +0,0 @@ -Set a default value of 1.0 for the ``lambd`` parameter in -random.expovariate(). diff --git a/Misc/NEWS.d/next/Library/2022-12-14-17-37-01.gh-issue-83076.NaYzWT.rst b/Misc/NEWS.d/next/Library/2022-12-14-17-37-01.gh-issue-83076.NaYzWT.rst deleted file mode 100644 index a4984e695b43..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-14-17-37-01.gh-issue-83076.NaYzWT.rst +++ /dev/null @@ -1 +0,0 @@ -Instantiation of ``Mock()`` and ``AsyncMock()`` is now 3.8x faster. diff --git a/Misc/NEWS.d/next/Library/2022-12-15-18-28-13.gh-issue-100272.D1O9Ey.rst b/Misc/NEWS.d/next/Library/2022-12-15-18-28-13.gh-issue-100272.D1O9Ey.rst deleted file mode 100644 index 2fb089261792..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-15-18-28-13.gh-issue-100272.D1O9Ey.rst +++ /dev/null @@ -1 +0,0 @@ -Fix JSON serialization of OrderedDict. It now preserves the order of keys. diff --git a/Misc/NEWS.d/next/Library/2022-12-19-12-18-28.gh-issue-100344.lfCqpE.rst b/Misc/NEWS.d/next/Library/2022-12-19-12-18-28.gh-issue-100344.lfCqpE.rst deleted file mode 100644 index d55f6888dbde..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-19-12-18-28.gh-issue-100344.lfCqpE.rst +++ /dev/null @@ -1,2 +0,0 @@ -Provide C implementation for :func:`asyncio.current_task` for a 4x-6x -speedup. diff --git a/Misc/NEWS.d/next/Library/2022-12-19-19-30-06.gh-issue-100348.o7IAHh.rst b/Misc/NEWS.d/next/Library/2022-12-19-19-30-06.gh-issue-100348.o7IAHh.rst deleted file mode 100644 index b5d4f7ca998c..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-19-19-30-06.gh-issue-100348.o7IAHh.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix ref cycle in :class:`!asyncio._SelectorSocketTransport` by removing ``_read_ready_cb`` in ``close``. - diff --git a/Misc/NEWS.d/next/Library/2022-12-19-20-54-04.gh-issue-78878.JrkYqJ.rst b/Misc/NEWS.d/next/Library/2022-12-19-20-54-04.gh-issue-78878.JrkYqJ.rst deleted file mode 100644 index 8b455fd2ef7f..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-19-20-54-04.gh-issue-78878.JrkYqJ.rst +++ /dev/null @@ -1 +0,0 @@ -Fix crash when creating an instance of :class:`!_ctypes.CField`. diff --git a/Misc/NEWS.d/next/Library/2022-12-20-11-07-30.gh-issue-100363.Wo_Beg.rst b/Misc/NEWS.d/next/Library/2022-12-20-11-07-30.gh-issue-100363.Wo_Beg.rst deleted file mode 100644 index 69bb52956137..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-20-11-07-30.gh-issue-100363.Wo_Beg.rst +++ /dev/null @@ -1 +0,0 @@ -Speed up :func:`asyncio.get_running_loop` by removing redundant ``getpid`` checks. Patch by Kumar Aditya. diff --git a/Misc/NEWS.d/next/Library/2022-12-23-21-02-43.gh-issue-100474.gppA4U.rst b/Misc/NEWS.d/next/Library/2022-12-23-21-02-43.gh-issue-100474.gppA4U.rst deleted file mode 100644 index 31abfb8b87fb..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-23-21-02-43.gh-issue-100474.gppA4U.rst +++ /dev/null @@ -1,2 +0,0 @@ -:mod:`http.server` now checks that an index page is actually a regular file before trying -to serve it. This avoids issues with directories named ``index.html``. diff --git a/Misc/NEWS.d/next/Library/2022-12-24-04-13-54.gh-issue-100488.Ut8HbE.rst b/Misc/NEWS.d/next/Library/2022-12-24-04-13-54.gh-issue-100488.Ut8HbE.rst deleted file mode 100644 index f7c07c0ab4c2..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-24-04-13-54.gh-issue-100488.Ut8HbE.rst +++ /dev/null @@ -1 +0,0 @@ -Add :meth:`Fraction.is_integer` to check whether a :class:`fractions.Fraction` is an integer. This improves duck type compatibility with :class:`float` and :class:`int`. diff --git a/Misc/NEWS.d/next/Library/2022-12-24-08-42-05.gh-issue-100287.n0oEuG.rst b/Misc/NEWS.d/next/Library/2022-12-24-08-42-05.gh-issue-100287.n0oEuG.rst deleted file mode 100644 index b353f0810c6a..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-24-08-42-05.gh-issue-100287.n0oEuG.rst +++ /dev/null @@ -1 +0,0 @@ -Fix the interaction of :func:`unittest.mock.seal` with :class:`unittest.mock.AsyncMock`. diff --git a/Misc/NEWS.d/next/Library/2022-12-24-16-39-53.gh-issue-100519.G_dZLP.rst b/Misc/NEWS.d/next/Library/2022-12-24-16-39-53.gh-issue-100519.G_dZLP.rst deleted file mode 100644 index 6b889b61c274..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-24-16-39-53.gh-issue-100519.G_dZLP.rst +++ /dev/null @@ -1,2 +0,0 @@ -Small simplification of :func:`http.cookiejar.eff_request_host` that -improves readability and better matches the RFC wording. diff --git a/Misc/NEWS.d/next/Library/2022-12-28-00-28-43.gh-issue-100562.Hic0Z0.rst b/Misc/NEWS.d/next/Library/2022-12-28-00-28-43.gh-issue-100562.Hic0Z0.rst deleted file mode 100644 index 56a426589719..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-28-00-28-43.gh-issue-100562.Hic0Z0.rst +++ /dev/null @@ -1,3 +0,0 @@ -Improve performance of :meth:`pathlib.Path.absolute` by nearly 2x. This comes -at the cost of a performance regression in :meth:`pathlib.Path.cwd`, which is -generally used less frequently in user code. diff --git a/Misc/NEWS.d/next/Library/2022-12-28-17-38-39.gh-issue-100585.BiiTlG.rst b/Misc/NEWS.d/next/Library/2022-12-28-17-38-39.gh-issue-100585.BiiTlG.rst deleted file mode 100644 index 0bf33b8bdd02..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-28-17-38-39.gh-issue-100585.BiiTlG.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed a bug where importlib.resources.as_file was leaving file pointers open diff --git a/Misc/NEWS.d/next/Library/2022-12-29-11-45-22.gh-issue-97930.hrtmJe.rst b/Misc/NEWS.d/next/Library/2022-12-29-11-45-22.gh-issue-97930.hrtmJe.rst deleted file mode 100644 index 1fe4a9d6b777..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-29-11-45-22.gh-issue-97930.hrtmJe.rst +++ /dev/null @@ -1,6 +0,0 @@ -``importlib.resources.files`` now accepts a module as an anchor instead of -only accepting packages. If a module is passed, resources are resolved -adjacent to that module (in the same package or at the package root). The -parameter was renamed from ``package`` to ``anchor`` with a compatibility -shim for those passing by keyword. Additionally, the new ``anchor`` -parameter is now optional and will default to the caller's module. diff --git a/Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst b/Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst deleted file mode 100644 index aedad0f252c2..000000000000 --- a/Misc/NEWS.d/next/Library/2022-12-30-07-49-08.gh-issue-86508.nGZDzC.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :func:`asyncio.open_connection` to skip binding to local addresses of different family. Patch by Kumar Aditya. diff --git a/Misc/NEWS.d/next/Library/2023-01-01-21-54-46.gh-issue-100485.geNrHS.rst b/Misc/NEWS.d/next/Library/2023-01-01-21-54-46.gh-issue-100485.geNrHS.rst deleted file mode 100644 index 9f6e593113bb..000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-01-21-54-46.gh-issue-100485.geNrHS.rst +++ /dev/null @@ -1 +0,0 @@ -Add math.sumprod() to compute the sum of products. diff --git a/Misc/NEWS.d/next/Library/2023-01-01-23-57-00.gh-issue-89727.ojedHN.rst b/Misc/NEWS.d/next/Library/2023-01-01-23-57-00.gh-issue-89727.ojedHN.rst deleted file mode 100644 index 38c0d5c4d5fb..000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-01-23-57-00.gh-issue-89727.ojedHN.rst +++ /dev/null @@ -1 +0,0 @@ -Simplify and optimize :func:`os.walk` by using :func:`isinstance` checks to check the top of the stack. diff --git a/Misc/NEWS.d/next/Library/2023-01-02-16-59-49.gh-issue-100690.2EgWPS.rst b/Misc/NEWS.d/next/Library/2023-01-02-16-59-49.gh-issue-100690.2EgWPS.rst deleted file mode 100644 index 3796772aebdf..000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-02-16-59-49.gh-issue-100690.2EgWPS.rst +++ /dev/null @@ -1,7 +0,0 @@ -``Mock`` objects which are not unsafe will now raise an -``AttributeError`` when accessing an attribute that matches the name -of an assertion but without the prefix ``assert_``, e.g. accessing -``called_once`` instead of ``assert_called_once``. -This is in addition to this already happening for accessing attributes -with prefixes ``assert``, ``assret``, ``asert``, ``aseert``, -and ``assrt``. diff --git a/Misc/NEWS.d/next/Library/2023-01-03-11-06-28.gh-issue-91219.s5IFCw.rst b/Misc/NEWS.d/next/Library/2023-01-03-11-06-28.gh-issue-91219.s5IFCw.rst deleted file mode 100644 index cb5e2e4630cc..000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-03-11-06-28.gh-issue-91219.s5IFCw.rst +++ /dev/null @@ -1,2 +0,0 @@ -Change ``SimpleHTTPRequestHandler`` to support subclassing to provide a -different set of index file names instead of using ``__init__`` parameters. diff --git a/Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst b/Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst deleted file mode 100644 index 4753e7b40825..000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-04-09-53-38.gh-issue-100740.-j5UjI.rst +++ /dev/null @@ -1 +0,0 @@ -Fix ``unittest.mock.Mock`` not respecting the spec for attribute names prefixed with ``assert``. diff --git a/Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst b/Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst deleted file mode 100644 index 09aeab7bceaa..000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-04-12-58-59.gh-issue-100689.Ce0ITG.rst +++ /dev/null @@ -1 +0,0 @@ -Fix crash in :mod:`pyexpat` by statically allocating ``PyExpat_CAPI`` capsule. diff --git a/Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst b/Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst deleted file mode 100644 index 6695c920ee24..000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-04-22-10-31.gh-issue-90104.yZk5EX.rst +++ /dev/null @@ -1 +0,0 @@ -Avoid RecursionError on ``repr`` if a dataclass field definition has a cyclic reference. diff --git a/Misc/NEWS.d/next/Library/2023-01-05-23-04-15.gh-issue-91851.AuCzU5.rst b/Misc/NEWS.d/next/Library/2023-01-05-23-04-15.gh-issue-91851.AuCzU5.rst deleted file mode 100644 index f427e8ae6791..000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-05-23-04-15.gh-issue-91851.AuCzU5.rst +++ /dev/null @@ -1,3 +0,0 @@ -Microoptimizations for :meth:`fractions.Fraction.__round__`, -:meth:`fractions.Fraction.__ceil__` and -:meth:`fractions.Fraction.__floor__`. diff --git a/Misc/NEWS.d/next/Library/2023-01-06-14-05-15.gh-issue-100792.CEOJth.rst b/Misc/NEWS.d/next/Library/2023-01-06-14-05-15.gh-issue-100792.CEOJth.rst deleted file mode 100644 index 5966bc1e6051..000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-06-14-05-15.gh-issue-100792.CEOJth.rst +++ /dev/null @@ -1 +0,0 @@ -Make :meth:`email.message.Message.__contains__` twice as fast. diff --git a/Misc/NEWS.d/next/Library/2023-01-06-22-36-27.gh-issue-100813.mHRdQn.rst b/Misc/NEWS.d/next/Library/2023-01-06-22-36-27.gh-issue-100813.mHRdQn.rst deleted file mode 100644 index 5bb876d6f3d4..000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-06-22-36-27.gh-issue-100813.mHRdQn.rst +++ /dev/null @@ -1 +0,0 @@ -Add :data:`socket.IP_PKTINFO` constant. diff --git a/Misc/NEWS.d/next/Library/2023-01-07-15-13-47.gh-issue-100805.05rBz9.rst b/Misc/NEWS.d/next/Library/2023-01-07-15-13-47.gh-issue-100805.05rBz9.rst deleted file mode 100644 index 4424d7cff21b..000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-07-15-13-47.gh-issue-100805.05rBz9.rst +++ /dev/null @@ -1,2 +0,0 @@ -Modify :func:`random.choice` implementation to once again work with NumPy -arrays. diff --git a/Misc/NEWS.d/next/Library/2023-01-08-12-10-17.gh-issue-100833.f6cT7E.rst b/Misc/NEWS.d/next/Library/2023-01-08-12-10-17.gh-issue-100833.f6cT7E.rst deleted file mode 100644 index b572e92d9871..000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-08-12-10-17.gh-issue-100833.f6cT7E.rst +++ /dev/null @@ -1 +0,0 @@ -Speed up :func:`math.fsum` by removing defensive ``volatile`` qualifiers. diff --git a/Misc/NEWS.d/next/Tests/2022-06-16-13-26-31.gh-issue-93018.wvNx76.rst b/Misc/NEWS.d/next/Tests/2022-06-16-13-26-31.gh-issue-93018.wvNx76.rst deleted file mode 100644 index a8fb98048e40..000000000000 --- a/Misc/NEWS.d/next/Tests/2022-06-16-13-26-31.gh-issue-93018.wvNx76.rst +++ /dev/null @@ -1 +0,0 @@ -Make two tests forgiving towards host system libexpat with backported security fixes applied. diff --git a/Misc/NEWS.d/next/Tests/2022-12-08-00-03-37.gh-issue-100086.1zYpto.rst b/Misc/NEWS.d/next/Tests/2022-12-08-00-03-37.gh-issue-100086.1zYpto.rst deleted file mode 100644 index a5f1bb9f5a5e..000000000000 --- a/Misc/NEWS.d/next/Tests/2022-12-08-00-03-37.gh-issue-100086.1zYpto.rst +++ /dev/null @@ -1,3 +0,0 @@ -The Python test runner (libregrtest) now logs Python build information like -"debug" vs "release" build, or LTO and PGO optimizations. Patch by Victor -Stinner. diff --git a/Misc/NEWS.d/next/Tests/2022-12-23-13-29-55.gh-issue-100454.3no0cW.rst b/Misc/NEWS.d/next/Tests/2022-12-23-13-29-55.gh-issue-100454.3no0cW.rst deleted file mode 100644 index 8b08ca0dcef7..000000000000 --- a/Misc/NEWS.d/next/Tests/2022-12-23-13-29-55.gh-issue-100454.3no0cW.rst +++ /dev/null @@ -1 +0,0 @@ -Start running SSL tests with OpenSSL 3.1.0-beta1. diff --git a/Misc/NEWS.d/next/Tools-Demos/2022-12-19-10-08-53.gh-issue-100342.qDFlQG.rst b/Misc/NEWS.d/next/Tools-Demos/2022-12-19-10-08-53.gh-issue-100342.qDFlQG.rst deleted file mode 100644 index 28f736337526..000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2022-12-19-10-08-53.gh-issue-100342.qDFlQG.rst +++ /dev/null @@ -1 +0,0 @@ -Add missing ``NULL`` check for possible allocation failure in ``*args`` parsing in Argument Clinic. diff --git a/Misc/NEWS.d/next/Tools-Demos/2022-12-29-19-22-11.bpo-45256.a0ee_H.rst b/Misc/NEWS.d/next/Tools-Demos/2022-12-29-19-22-11.bpo-45256.a0ee_H.rst deleted file mode 100644 index 9c1aa5762583..000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2022-12-29-19-22-11.bpo-45256.a0ee_H.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a bug that caused an :exc:`AttributeError` to be raised in ``python-gdb.py`` when ``py-locals`` is used without a frame. diff --git a/Misc/NEWS.d/next/Windows/2021-04-08-00-36-37.bpo-34816.4Xe0id.rst b/Misc/NEWS.d/next/Windows/2021-04-08-00-36-37.bpo-34816.4Xe0id.rst deleted file mode 100644 index 6fe5f12cc9d9..000000000000 --- a/Misc/NEWS.d/next/Windows/2021-04-08-00-36-37.bpo-34816.4Xe0id.rst +++ /dev/null @@ -1,3 +0,0 @@ -``hasattr(ctypes.windll, 'nonexistant')`` now returns ``False`` instead of raising :exc:`OSError`. - - diff --git a/Misc/NEWS.d/next/Windows/2021-05-02-15-29-33.bpo-43984.U92jiv.rst b/Misc/NEWS.d/next/Windows/2021-05-02-15-29-33.bpo-43984.U92jiv.rst deleted file mode 100644 index a5975b2d00c7..000000000000 --- a/Misc/NEWS.d/next/Windows/2021-05-02-15-29-33.bpo-43984.U92jiv.rst +++ /dev/null @@ -1,3 +0,0 @@ -:meth:`winreg.SetValueEx` now leaves the target value untouched in the case of conversion errors. -Previously, ``-1`` would be written in case of such errors. - diff --git a/Misc/NEWS.d/next/Windows/2022-12-06-11-16-46.gh-issue-99941.GmUQ6o.rst b/Misc/NEWS.d/next/Windows/2022-12-06-11-16-46.gh-issue-99941.GmUQ6o.rst deleted file mode 100644 index a019d7287207..000000000000 --- a/Misc/NEWS.d/next/Windows/2022-12-06-11-16-46.gh-issue-99941.GmUQ6o.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensure that :func:`asyncio.Protocol.data_received` receives an immutable -:class:`bytes` object (as documented), instead of :class:`bytearray`. diff --git a/Misc/NEWS.d/next/Windows/2022-12-09-22-47-42.gh-issue-79218.Yiot2e.rst b/Misc/NEWS.d/next/Windows/2022-12-09-22-47-42.gh-issue-79218.Yiot2e.rst deleted file mode 100644 index e2e6ca3c7796..000000000000 --- a/Misc/NEWS.d/next/Windows/2022-12-09-22-47-42.gh-issue-79218.Yiot2e.rst +++ /dev/null @@ -1 +0,0 @@ -Define ``MS_WIN64`` for Mingw-w64 64bit, fix cython compilation failure. diff --git a/Misc/NEWS.d/next/Windows/2022-12-20-18-36-17.gh-issue-99191.0cfRja.rst b/Misc/NEWS.d/next/Windows/2022-12-20-18-36-17.gh-issue-99191.0cfRja.rst deleted file mode 100644 index 57a95ab3ffcd..000000000000 --- a/Misc/NEWS.d/next/Windows/2022-12-20-18-36-17.gh-issue-99191.0cfRja.rst +++ /dev/null @@ -1,2 +0,0 @@ -Use ``_MSVC_LANG >= 202002L`` instead of less-precise ``_MSC_VER >=1929`` -to more accurately test for C++20 support in :file:`PC/_wmimodule.cpp`. diff --git a/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst b/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst deleted file mode 100644 index 5b0f42568d92..000000000000 --- a/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst +++ /dev/null @@ -1 +0,0 @@ -Update Windows installer to OpenSSL 1.1.1s diff --git a/Misc/NEWS.d/next/macOS/2022-12-26-14-52-37.gh-issue-100540.kYZLtX.rst b/Misc/NEWS.d/next/macOS/2022-12-26-14-52-37.gh-issue-100540.kYZLtX.rst deleted file mode 100644 index a42814e1861d..000000000000 --- a/Misc/NEWS.d/next/macOS/2022-12-26-14-52-37.gh-issue-100540.kYZLtX.rst +++ /dev/null @@ -1,2 +0,0 @@ -Removed obsolete ``dlfcn.h`` shim from the ``_ctypes`` extension module, -which has not been necessary since Mac OS X 10.2. diff --git a/Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst b/Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst deleted file mode 100644 index b7d94a0a94a0..000000000000 --- a/Misc/NEWS.d/next/macOS/2023-01-09-22-04-21.gh-issue-100180.WVhCny.rst +++ /dev/null @@ -1 +0,0 @@ -Update macOS installer to OpenSSL 1.1.1s diff --git a/README.rst b/README.rst index 4e7ad25949fa..814efef83a35 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.12.0 alpha 3 +This is Python version 3.12.0 alpha 4 ===================================== .. image:: https://github.com/python/cpython/workflows/Tests/badge.svg From webhook-mailer at python.org Tue Jan 10 15:20:16 2023 From: webhook-mailer at python.org (gvanrossum) Date: Tue, 10 Jan 2023 20:20:16 -0000 Subject: [Python-checkins] [3.11] gh-100160: Remove any deprecation warnings in asyncio.get_event_loop() (#100412) Message-ID: https://github.com/python/cpython/commit/1b2459dc64b1c3eea89312ea9bf422f8d7c75bb2 commit: 1b2459dc64b1c3eea89312ea9bf422f8d7c75bb2 branch: 3.11 author: Serhiy Storchaka committer: gvanrossum date: 2023-01-10T12:20:09-08:00 summary: [3.11] gh-100160: Remove any deprecation warnings in asyncio.get_event_loop() (#100412) Some deprecation warnings will reappear (in a slightly different form) in 3.12. Co-authored-by: Guido van Rossum files: A Misc/NEWS.d/next/Library/2022-12-21-18-29-24.gh-issue-100160.isBmL5.rst M Doc/library/asyncio-eventloop.rst M Doc/library/asyncio-policy.rst M Doc/whatsnew/3.10.rst M Lib/asyncio/events.py M Lib/test/test_asyncio/test_events.py diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index d32e848a8413..c7343826be3f 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -48,7 +48,7 @@ an event loop: running event loop. If there is no running event loop set, the function will return - the result of ``get_event_loop_policy().get_event_loop()`` call. + the result of the ``get_event_loop_policy().get_event_loop()`` call. Because this function has rather complex behavior (especially when custom event loop policies are in use), using the @@ -59,15 +59,15 @@ an event loop: instead of using these lower level functions to manually create and close an event loop. - .. deprecated:: 3.10 - Deprecation warning is emitted if there is no current event loop. - In Python 3.12 it will be an error. - .. note:: In Python versions 3.10.0--3.10.8 and 3.11.0 this function - (and other functions which used it implicitly) emitted a + (and other functions which use it implicitly) emitted a :exc:`DeprecationWarning` if there was no running event loop, even if - the current loop was set. + the current loop was set on the policy. + In Python versions 3.10.9, 3.11.1 and 3.12 they emit a + :exc:`DeprecationWarning` if there is no running event loop and no + current loop is set. + In some future Python release this will become an error. .. function:: set_event_loop(loop) diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst index d0af45febd14..eb043b3e5e7f 100644 --- a/Doc/library/asyncio-policy.rst +++ b/Doc/library/asyncio-policy.rst @@ -112,10 +112,11 @@ asyncio ships with the following built-in policies: On Windows, :class:`ProactorEventLoop` is now used by default. - .. deprecated:: 3.11.1 - :meth:`get_event_loop` now emits a :exc:`DeprecationWarning` if there - is no current event loop set and a new event loop has been implicitly - created. In Python 3.12 it will be an error. + .. note:: + In Python versions 3.10.9, 3.11.1 and 3.12 this function emits a + :exc:`DeprecationWarning` if there is no running event loop and no + current loop is set. + In some future Python release this will become an error. .. class:: WindowsSelectorEventLoopPolicy diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 33fdc6f0604c..ed72af717f66 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -1708,19 +1708,6 @@ Deprecated scheduled for removal in Python 3.12. (Contributed by Erlend E. Aasland in :issue:`42264`.) -* :func:`asyncio.get_event_loop` now emits a deprecation warning if there is - no running event loop. In the future it will be an alias of - :func:`~asyncio.get_running_loop`. - :mod:`asyncio` functions which implicitly create :class:`~asyncio.Future` - or :class:`~asyncio.Task` objects now emit - a deprecation warning if there is no running event loop and no explicit - *loop* argument is passed: :func:`~asyncio.ensure_future`, - :func:`~asyncio.wrap_future`, :func:`~asyncio.gather`, - :func:`~asyncio.shield`, :func:`~asyncio.as_completed` and constructors of - :class:`~asyncio.Future`, :class:`~asyncio.Task`, - :class:`~asyncio.StreamReader`, :class:`~asyncio.StreamReaderProtocol`. - (Contributed by Serhiy Storchaka in :issue:`39529`.) - * The undocumented built-in function ``sqlite3.enable_shared_cache`` is now deprecated, scheduled for removal in Python 3.12. Its use is strongly discouraged by the SQLite3 documentation. See `the SQLite3 docs diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index af3f9e970b51..b1799320eaa0 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -671,21 +671,6 @@ def get_event_loop(self): if (self._local._loop is None and not self._local._set_called and threading.current_thread() is threading.main_thread()): - stacklevel = 2 - try: - f = sys._getframe(1) - except AttributeError: - pass - else: - while f: - module = f.f_globals.get('__name__') - if not (module == 'asyncio' or module.startswith('asyncio.')): - break - f = f.f_back - stacklevel += 1 - import warnings - warnings.warn('There is no current event loop', - DeprecationWarning, stacklevel=stacklevel) self.set_event_loop(self.new_event_loop()) if self._local._loop is None: diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 3f92122b0c76..5728d25254f3 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -2588,9 +2588,7 @@ def test_event_loop_policy(self): def test_get_event_loop(self): policy = asyncio.DefaultEventLoopPolicy() self.assertIsNone(policy._local._loop) - with self.assertWarns(DeprecationWarning) as cm: - loop = policy.get_event_loop() - self.assertEqual(cm.filename, __file__) + loop = policy.get_event_loop() self.assertIsInstance(loop, asyncio.AbstractEventLoop) self.assertIs(policy._local._loop, loop) @@ -2604,10 +2602,8 @@ def test_get_event_loop_calls_set_event_loop(self): policy, "set_event_loop", wraps=policy.set_event_loop) as m_set_event_loop: - with self.assertWarns(DeprecationWarning) as cm: - loop = policy.get_event_loop() + loop = policy.get_event_loop() self.addCleanup(loop.close) - self.assertEqual(cm.filename, __file__) # policy._local._loop must be set through .set_event_loop() # (the unix DefaultEventLoopPolicy needs this call to attach @@ -2796,10 +2792,8 @@ def test_get_event_loop_returns_running_loop2(self): loop = asyncio.new_event_loop() self.addCleanup(loop.close) - with self.assertWarns(DeprecationWarning) as cm: - loop2 = asyncio.get_event_loop() + loop2 = asyncio.get_event_loop() self.addCleanup(loop2.close) - self.assertEqual(cm.filename, __file__) asyncio.set_event_loop(None) with self.assertRaisesRegex(RuntimeError, 'no current'): asyncio.get_event_loop() diff --git a/Misc/NEWS.d/next/Library/2022-12-21-18-29-24.gh-issue-100160.isBmL5.rst b/Misc/NEWS.d/next/Library/2022-12-21-18-29-24.gh-issue-100160.isBmL5.rst new file mode 100644 index 000000000000..c3b518ca85d7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-21-18-29-24.gh-issue-100160.isBmL5.rst @@ -0,0 +1,2 @@ +Remove any deprecation warnings in :func:`asyncio.get_event_loop`. They are +deferred to Python 3.12. From webhook-mailer at python.org Wed Jan 11 04:44:00 2023 From: webhook-mailer at python.org (AlexWaygood) Date: Wed, 11 Jan 2023 09:44:00 -0000 Subject: [Python-checkins] gh-100931: Test all `pickle` protocols in `test_slice` (#100932) Message-ID: https://github.com/python/cpython/commit/8795ad1bd0d6ee031543fcaf5a86a60b37950714 commit: 8795ad1bd0d6ee031543fcaf5a86a60b37950714 branch: main author: Nikita Sobolev committer: AlexWaygood date: 2023-01-11T09:43:45Z summary: gh-100931: Test all `pickle` protocols in `test_slice` (#100932) files: M Lib/test/test_json/test_attrdict.py M Lib/test/test_slice.py diff --git a/Lib/test/test_json/test_attrdict.py b/Lib/test/test_json/test_attrdict.py index 48d14f4db93c..143ea462d310 100644 --- a/Lib/test/test_json/test_attrdict.py +++ b/Lib/test/test_json/test_attrdict.py @@ -133,7 +133,7 @@ def test_pickle(self): cached_module = sys.modules.get('json') sys.modules['json'] = self.json try: - for protocol in range(6): + for protocol in range(pickle.HIGHEST_PROTOCOL + 1): kepler_ad2 = pickle.loads(pickle.dumps(kepler_ad, protocol)) self.assertEqual(kepler_ad2, kepler_ad) self.assertEqual(type(kepler_ad2), AttrDict) diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py index 4ae4142c60c8..c4bc8c82023d 100644 --- a/Lib/test/test_slice.py +++ b/Lib/test/test_slice.py @@ -235,8 +235,10 @@ def __setitem__(self, i, k): self.assertEqual(tmp, [(slice(1, 2), 42)]) def test_pickle(self): + import pickle + s = slice(10, 20, 3) - for protocol in (0,1,2): + for protocol in range(pickle.HIGHEST_PROTOCOL + 1): t = loads(dumps(s, protocol)) self.assertEqual(s, t) self.assertEqual(s.indices(15), t.indices(15)) From webhook-mailer at python.org Wed Jan 11 05:05:48 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Wed, 11 Jan 2023 10:05:48 -0000 Subject: [Python-checkins] gh-98763: Prefer "python" over "python3" for command line examples in docs. (#98761) Message-ID: https://github.com/python/cpython/commit/847d7708ba8739a5d5d31f22d71497527a7d8241 commit: 847d7708ba8739a5d5d31f22d71497527a7d8241 branch: main author: Mariusz Felisiak committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-11T15:35:41+05:30 summary: gh-98763: Prefer "python" over "python3" for command line examples in docs. (#98761) files: M Doc/howto/argparse.rst M Doc/howto/clinic.rst M Doc/howto/unicode.rst M Doc/library/__main__.rst M Doc/library/devmode.rst M Doc/library/faulthandler.rst M Doc/library/site.rst M Doc/library/timeit.rst diff --git a/Doc/howto/argparse.rst b/Doc/howto/argparse.rst index f4d08e75d946..f682587488a2 100644 --- a/Doc/howto/argparse.rst +++ b/Doc/howto/argparse.rst @@ -79,16 +79,16 @@ Following is a result of running the code: .. code-block:: shell-session - $ python3 prog.py - $ python3 prog.py --help + $ python prog.py + $ python prog.py --help usage: prog.py [-h] options: -h, --help show this help message and exit - $ python3 prog.py --verbose + $ python prog.py --verbose usage: prog.py [-h] prog.py: error: unrecognized arguments: --verbose - $ python3 prog.py foo + $ python prog.py foo usage: prog.py [-h] prog.py: error: unrecognized arguments: foo @@ -121,10 +121,10 @@ And running the code: .. code-block:: shell-session - $ python3 prog.py + $ python prog.py usage: prog.py [-h] echo prog.py: error: the following arguments are required: echo - $ python3 prog.py --help + $ python prog.py --help usage: prog.py [-h] echo positional arguments: @@ -132,7 +132,7 @@ And running the code: options: -h, --help show this help message and exit - $ python3 prog.py foo + $ python prog.py foo foo Here is what's happening: @@ -166,7 +166,7 @@ And we get: .. code-block:: shell-session - $ python3 prog.py -h + $ python prog.py -h usage: prog.py [-h] echo positional arguments: @@ -187,7 +187,7 @@ Following is a result of running the code: .. code-block:: shell-session - $ python3 prog.py 4 + $ python prog.py 4 Traceback (most recent call last): File "prog.py", line 5, in print(args.square**2) @@ -208,9 +208,9 @@ Following is a result of running the code: .. code-block:: shell-session - $ python3 prog.py 4 + $ python prog.py 4 16 - $ python3 prog.py four + $ python prog.py four usage: prog.py [-h] square prog.py: error: argument square: invalid int value: 'four' @@ -235,17 +235,17 @@ And the output: .. code-block:: shell-session - $ python3 prog.py --verbosity 1 + $ python prog.py --verbosity 1 verbosity turned on - $ python3 prog.py - $ python3 prog.py --help + $ python prog.py + $ python prog.py --help usage: prog.py [-h] [--verbosity VERBOSITY] options: -h, --help show this help message and exit --verbosity VERBOSITY increase output verbosity - $ python3 prog.py --verbosity + $ python prog.py --verbosity usage: prog.py [-h] [--verbosity VERBOSITY] prog.py: error: argument --verbosity: expected one argument @@ -281,12 +281,12 @@ And the output: .. code-block:: shell-session - $ python3 prog.py --verbose + $ python prog.py --verbose verbosity turned on - $ python3 prog.py --verbose 1 + $ python prog.py --verbose 1 usage: prog.py [-h] [--verbose] prog.py: error: unrecognized arguments: 1 - $ python3 prog.py --help + $ python prog.py --help usage: prog.py [-h] [--verbose] options: @@ -327,9 +327,9 @@ And here goes: .. code-block:: shell-session - $ python3 prog.py -v + $ python prog.py -v verbosity turned on - $ python3 prog.py --help + $ python prog.py --help usage: prog.py [-h] [-v] options: @@ -361,14 +361,14 @@ And now the output: .. code-block:: shell-session - $ python3 prog.py + $ python prog.py usage: prog.py [-h] [-v] square prog.py: error: the following arguments are required: square - $ python3 prog.py 4 + $ python prog.py 4 16 - $ python3 prog.py 4 --verbose + $ python prog.py 4 --verbose the square of 4 equals 16 - $ python3 prog.py --verbose 4 + $ python prog.py --verbose 4 the square of 4 equals 16 * We've brought back a positional argument, hence the complaint. @@ -397,16 +397,16 @@ And the output: .. code-block:: shell-session - $ python3 prog.py 4 + $ python prog.py 4 16 - $ python3 prog.py 4 -v + $ python prog.py 4 -v usage: prog.py [-h] [-v VERBOSITY] square prog.py: error: argument -v/--verbosity: expected one argument - $ python3 prog.py 4 -v 1 + $ python prog.py 4 -v 1 4^2 == 16 - $ python3 prog.py 4 -v 2 + $ python prog.py 4 -v 2 the square of 4 equals 16 - $ python3 prog.py 4 -v 3 + $ python prog.py 4 -v 3 16 These all look good except the last one, which exposes a bug in our program. @@ -431,10 +431,10 @@ And the output: .. code-block:: shell-session - $ python3 prog.py 4 -v 3 + $ python prog.py 4 -v 3 usage: prog.py [-h] [-v {0,1,2}] square prog.py: error: argument -v/--verbosity: invalid choice: 3 (choose from 0, 1, 2) - $ python3 prog.py 4 -h + $ python prog.py 4 -h usage: prog.py [-h] [-v {0,1,2}] square positional arguments: @@ -473,18 +473,18 @@ to count the number of occurrences of specific options. .. code-block:: shell-session - $ python3 prog.py 4 + $ python prog.py 4 16 - $ python3 prog.py 4 -v + $ python prog.py 4 -v 4^2 == 16 - $ python3 prog.py 4 -vv + $ python prog.py 4 -vv the square of 4 equals 16 - $ python3 prog.py 4 --verbosity --verbosity + $ python prog.py 4 --verbosity --verbosity the square of 4 equals 16 - $ python3 prog.py 4 -v 1 + $ python prog.py 4 -v 1 usage: prog.py [-h] [-v] square prog.py: error: unrecognized arguments: 1 - $ python3 prog.py 4 -h + $ python prog.py 4 -h usage: prog.py [-h] [-v] square positional arguments: @@ -493,7 +493,7 @@ to count the number of occurrences of specific options. options: -h, --help show this help message and exit -v, --verbosity increase output verbosity - $ python3 prog.py 4 -vvv + $ python prog.py 4 -vvv 16 * Yes, it's now more of a flag (similar to ``action="store_true"``) in the @@ -540,11 +540,11 @@ And this is what it gives: .. code-block:: shell-session - $ python3 prog.py 4 -vvv + $ python prog.py 4 -vvv the square of 4 equals 16 - $ python3 prog.py 4 -vvvv + $ python prog.py 4 -vvvv the square of 4 equals 16 - $ python3 prog.py 4 + $ python prog.py 4 Traceback (most recent call last): File "prog.py", line 11, in if args.verbosity >= 2: @@ -584,7 +584,7 @@ And: .. code-block:: shell-session - $ python3 prog.py 4 + $ python prog.py 4 16 You can go quite far just with what we've learned so far, @@ -617,10 +617,10 @@ Output: .. code-block:: shell-session - $ python3 prog.py + $ python prog.py usage: prog.py [-h] [-v] x y prog.py: error: the following arguments are required: x, y - $ python3 prog.py -h + $ python prog.py -h usage: prog.py [-h] [-v] x y positional arguments: @@ -630,7 +630,7 @@ Output: options: -h, --help show this help message and exit -v, --verbosity - $ python3 prog.py 4 2 -v + $ python prog.py 4 2 -v 4^2 == 16 @@ -655,11 +655,11 @@ Output: .. code-block:: shell-session - $ python3 prog.py 4 2 + $ python prog.py 4 2 16 - $ python3 prog.py 4 2 -v + $ python prog.py 4 2 -v 4^2 == 16 - $ python3 prog.py 4 2 -vv + $ python prog.py 4 2 -vv Running 'prog.py' 4^2 == 16 @@ -727,16 +727,16 @@ demonstration. Anyways, here's the output: .. code-block:: shell-session - $ python3 prog.py 4 2 + $ python prog.py 4 2 4^2 == 16 - $ python3 prog.py 4 2 -q + $ python prog.py 4 2 -q 16 - $ python3 prog.py 4 2 -v + $ python prog.py 4 2 -v 4 to the power 2 equals 16 - $ python3 prog.py 4 2 -vq + $ python prog.py 4 2 -vq usage: prog.py [-h] [-v | -q] x y prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose - $ python3 prog.py 4 2 -v --quiet + $ python prog.py 4 2 -v --quiet usage: prog.py [-h] [-v | -q] x y prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose @@ -771,7 +771,7 @@ but not both at the same time: .. code-block:: shell-session - $ python3 prog.py --help + $ python prog.py --help usage: prog.py [-h] [-v | -q] x y calculate X to the power of Y diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index a97f1d23f53f..8a10fe327358 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -86,7 +86,7 @@ If you run that script, specifying a C file as an argument: .. code-block:: shell-session - $ python3 Tools/clinic/clinic.py foo.c + $ python Tools/clinic/clinic.py foo.c Argument Clinic will scan over the file looking for lines that look exactly like this: diff --git a/Doc/howto/unicode.rst b/Doc/howto/unicode.rst index ca09aee72bf8..b0faa68d2408 100644 --- a/Doc/howto/unicode.rst +++ b/Doc/howto/unicode.rst @@ -449,7 +449,7 @@ When run, this outputs: .. code-block:: shell-session - $ python3 compare-strs.py + $ python compare-strs.py length of first string= 1 length of second string= 2 True diff --git a/Doc/library/__main__.rst b/Doc/library/__main__.rst index d0a65e76b842..6a2a7a7317f7 100644 --- a/Doc/library/__main__.rst +++ b/Doc/library/__main__.rst @@ -61,7 +61,7 @@ The top-level code environment can be: .. code-block:: shell-session - $ python3 helloworld.py + $ python helloworld.py Hello, world! * the Python module or package passed to the Python interpreter with the @@ -69,14 +69,14 @@ The top-level code environment can be: .. code-block:: shell-session - $ python3 -m tarfile + $ python -m tarfile usage: tarfile.py [-h] [-v] (...) * Python code read by the Python interpreter from standard input: .. code-block:: shell-session - $ echo "import this" | python3 + $ echo "import this" | python The Zen of Python, by Tim Peters Beautiful is better than ugly. @@ -87,7 +87,7 @@ The top-level code environment can be: .. code-block:: shell-session - $ python3 -c "import this" + $ python -c "import this" The Zen of Python, by Tim Peters Beautiful is better than ugly. @@ -178,7 +178,7 @@ that your function will return some value acceptable as an input to returned if your function does not have a return statement). By proactively following this convention ourselves, our module will have the -same behavior when run directly (i.e. ``python3 echo.py``) as it will have if +same behavior when run directly (i.e. ``python echo.py``) as it will have if we later package it as a console script entry-point in a pip-installable package. @@ -215,7 +215,7 @@ directly from the command line using the :option:`-m` flag. For example: .. code-block:: shell-session - $ python3 -m bandclass + $ python -m bandclass This command will cause ``__main__.py`` to run. How you utilize this mechanism will depend on the nature of the package you are writing, but in this @@ -320,7 +320,7 @@ Now, if we started our program, the result would look like this: .. code-block:: shell-session - $ python3 start.py + $ python start.py Define the variable `my_name`! The exit code of the program would be 1, indicating an error. Uncommenting the @@ -329,7 +329,7 @@ status code 0, indicating success: .. code-block:: shell-session - $ python3 start.py + $ python start.py Dinsdale found in file /path/to/start.py Note that importing ``__main__`` doesn't cause any issues with unintentionally diff --git a/Doc/library/devmode.rst b/Doc/library/devmode.rst index 44e7d4f541d8..977735990ffe 100644 --- a/Doc/library/devmode.rst +++ b/Doc/library/devmode.rst @@ -21,7 +21,7 @@ Effects of the Python Development Mode Enabling the Python Development Mode is similar to the following command, but with additional effects described below:: - PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python3 -W default -X faulthandler + PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python -W default -X faulthandler Effects of the Python Development Mode: @@ -128,14 +128,14 @@ any warning. Example using README.txt, which has 269 lines: .. code-block:: shell-session - $ python3 script.py README.txt + $ python script.py README.txt 269 Enabling the Python Development Mode displays a :exc:`ResourceWarning` warning: .. code-block:: shell-session - $ python3 -X dev script.py README.txt + $ python -X dev script.py README.txt 269 script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='README.rst' mode='r' encoding='UTF-8'> main() @@ -146,7 +146,7 @@ opened: .. code-block:: shell-session - $ python3 -X dev -X tracemalloc=5 script.py README.rst + $ python -X dev -X tracemalloc=5 script.py README.rst 269 script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='README.rst' mode='r' encoding='UTF-8'> main() @@ -190,7 +190,7 @@ By default, Python does not emit any warning: .. code-block:: shell-session - $ python3 script.py + $ python script.py import os The Python Development Mode shows a :exc:`ResourceWarning` and logs a "Bad file @@ -198,7 +198,7 @@ descriptor" error when finalizing the file object: .. code-block:: shell-session - $ python3 script.py + $ python script.py import os script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='script.py' mode='r' encoding='UTF-8'> main() diff --git a/Doc/library/faulthandler.rst b/Doc/library/faulthandler.rst index be0912376bd8..07a748994144 100644 --- a/Doc/library/faulthandler.rst +++ b/Doc/library/faulthandler.rst @@ -166,10 +166,10 @@ handler: .. code-block:: shell-session - $ python3 -c "import ctypes; ctypes.string_at(0)" + $ python -c "import ctypes; ctypes.string_at(0)" Segmentation fault - $ python3 -q -X faulthandler + $ python -q -X faulthandler >>> import ctypes >>> ctypes.string_at(0) Fatal Python error: Segmentation fault diff --git a/Doc/library/site.rst b/Doc/library/site.rst index 5941739ee694..4a88013f1d6e 100644 --- a/Doc/library/site.rst +++ b/Doc/library/site.rst @@ -250,8 +250,8 @@ command line: .. code-block:: shell-session - $ python3 -m site --user-site - /home/user/.local/lib/python3.3/site-packages + $ python -m site --user-site + /home/user/.local/lib/python3.11/site-packages If it is called without arguments, it will print the contents of :data:`sys.path` on the standard output, followed by the value of diff --git a/Doc/library/timeit.rst b/Doc/library/timeit.rst index 660a546e7218..5437704cec33 100644 --- a/Doc/library/timeit.rst +++ b/Doc/library/timeit.rst @@ -27,11 +27,11 @@ can be used to compare three different expressions: .. code-block:: shell-session - $ python3 -m timeit '"-".join(str(n) for n in range(100))' + $ python -m timeit '"-".join(str(n) for n in range(100))' 10000 loops, best of 5: 30.2 usec per loop - $ python3 -m timeit '"-".join([str(n) for n in range(100)])' + $ python -m timeit '"-".join([str(n) for n in range(100)])' 10000 loops, best of 5: 27.5 usec per loop - $ python3 -m timeit '"-".join(map(str, range(100)))' + $ python -m timeit '"-".join(map(str, range(100)))' 10000 loops, best of 5: 23.2 usec per loop This can be achieved from the :ref:`python-interface` with:: From webhook-mailer at python.org Wed Jan 11 05:33:49 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Wed, 11 Jan 2023 10:33:49 -0000 Subject: [Python-checkins] GH-100892: Fix race in clearing `threading.local` (#100922) Message-ID: https://github.com/python/cpython/commit/762745a124cbc297cf2fe6f3ec9ca1840bb2e873 commit: 762745a124cbc297cf2fe6f3ec9ca1840bb2e873 branch: main author: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-11T16:03:31+05:30 summary: GH-100892: Fix race in clearing `threading.local` (#100922) files: A Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst M Lib/test/test_threading_local.py M Modules/_testcapimodule.c M Modules/_threadmodule.c diff --git a/Lib/test/test_threading_local.py b/Lib/test/test_threading_local.py index 48e302930ff7..f0b829a978fe 100644 --- a/Lib/test/test_threading_local.py +++ b/Lib/test/test_threading_local.py @@ -3,6 +3,7 @@ from doctest import DocTestSuite from test import support from test.support import threading_helper +from test.support.import_helper import import_module import weakref # Modules under test @@ -196,6 +197,18 @@ class X: self.assertIsNone(wr()) + def test_threading_local_clear_race(self): + # See https://github.com/python/cpython/issues/100892 + + _testcapi = import_module('_testcapi') + _testcapi.call_in_temporary_c_thread(lambda: None, False) + + for _ in range(1000): + _ = threading.local() + + _testcapi.join_temporary_c_thread() + + class ThreadLocalTest(unittest.TestCase, BaseLocalTest): _local = _thread._local diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst new file mode 100644 index 000000000000..f2576becc2fc --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst @@ -0,0 +1 @@ +Fix race while iterating over thread states in clearing :class:`threading.local`. Patch by Kumar Aditya. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index c777c3efc492..486988b4e34c 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1879,12 +1879,19 @@ temporary_c_thread(void *data) PyThread_release_lock(test_c_thread->exit_event); } +static test_c_thread_t test_c_thread; + static PyObject * -call_in_temporary_c_thread(PyObject *self, PyObject *callback) +call_in_temporary_c_thread(PyObject *self, PyObject *args) { PyObject *res = NULL; - test_c_thread_t test_c_thread; + PyObject *callback = NULL; long thread; + int wait = 1; + if (!PyArg_ParseTuple(args, "O|i", &callback, &wait)) + { + return NULL; + } test_c_thread.start_event = PyThread_allocate_lock(); test_c_thread.exit_event = PyThread_allocate_lock(); @@ -1910,6 +1917,10 @@ call_in_temporary_c_thread(PyObject *self, PyObject *callback) PyThread_acquire_lock(test_c_thread.start_event, 1); PyThread_release_lock(test_c_thread.start_event); + if (!wait) { + Py_RETURN_NONE; + } + Py_BEGIN_ALLOW_THREADS PyThread_acquire_lock(test_c_thread.exit_event, 1); PyThread_release_lock(test_c_thread.exit_event); @@ -1919,13 +1930,32 @@ call_in_temporary_c_thread(PyObject *self, PyObject *callback) exit: Py_CLEAR(test_c_thread.callback); - if (test_c_thread.start_event) + if (test_c_thread.start_event) { PyThread_free_lock(test_c_thread.start_event); - if (test_c_thread.exit_event) + test_c_thread.start_event = NULL; + } + if (test_c_thread.exit_event) { PyThread_free_lock(test_c_thread.exit_event); + test_c_thread.exit_event = NULL; + } return res; } +static PyObject * +join_temporary_c_thread(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + Py_BEGIN_ALLOW_THREADS + PyThread_acquire_lock(test_c_thread.exit_event, 1); + PyThread_release_lock(test_c_thread.exit_event); + Py_END_ALLOW_THREADS + Py_CLEAR(test_c_thread.callback); + PyThread_free_lock(test_c_thread.start_event); + test_c_thread.start_event = NULL; + PyThread_free_lock(test_c_thread.exit_event); + test_c_thread.exit_event = NULL; + Py_RETURN_NONE; +} + /* marshal */ static PyObject* @@ -3275,8 +3305,9 @@ static PyMethodDef TestMethods[] = { METH_VARARGS | METH_KEYWORDS}, {"with_tp_del", with_tp_del, METH_VARARGS}, {"create_cfunction", create_cfunction, METH_NOARGS}, - {"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_O, + {"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_VARARGS, PyDoc_STR("set_error_class(error_class) -> None")}, + {"join_temporary_c_thread", join_temporary_c_thread, METH_NOARGS}, {"pymarshal_write_long_to_file", pymarshal_write_long_to_file, METH_VARARGS}, {"pymarshal_write_object_to_file", diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index d323ca83dcff..4fbf0e551b07 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -839,6 +839,11 @@ local_traverse(localobject *self, visitproc visit, void *arg) return 0; } +#define HEAD_LOCK(runtime) \ + PyThread_acquire_lock((runtime)->interpreters.mutex, WAIT_LOCK) +#define HEAD_UNLOCK(runtime) \ + PyThread_release_lock((runtime)->interpreters.mutex) + static int local_clear(localobject *self) { @@ -849,18 +854,23 @@ local_clear(localobject *self) /* Remove all strong references to dummies from the thread states */ if (self->key) { PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyRuntimeState *runtime = &_PyRuntime; + HEAD_LOCK(runtime); PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); - for(; tstate; tstate = PyThreadState_Next(tstate)) { - if (tstate->dict == NULL) { - continue; - } - PyObject *v = _PyDict_Pop(tstate->dict, self->key, Py_None); - if (v != NULL) { - Py_DECREF(v); - } - else { - PyErr_Clear(); + HEAD_UNLOCK(runtime); + while (tstate) { + if (tstate->dict) { + PyObject *v = _PyDict_Pop(tstate->dict, self->key, Py_None); + if (v != NULL) { + Py_DECREF(v); + } + else { + PyErr_Clear(); + } } + HEAD_LOCK(runtime); + tstate = PyThreadState_Next(tstate); + HEAD_UNLOCK(runtime); } } return 0; From webhook-mailer at python.org Wed Jan 11 10:31:55 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Wed, 11 Jan 2023 15:31:55 -0000 Subject: [Python-checkins] [3.11] GH-100892: Fix race in clearing `threading.local` (GH-100922). (#100937) Message-ID: https://github.com/python/cpython/commit/e7076716842dd3f45298adef5b3decdf44842603 commit: e7076716842dd3f45298adef5b3decdf44842603 branch: 3.11 author: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-11T21:01:48+05:30 summary: [3.11] GH-100892: Fix race in clearing `threading.local` (GH-100922). (#100937) (cherry picked from commit 762745a124cbc297cf2fe6f3ec9ca1840bb2e873) Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> files: A Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst M Lib/test/test_threading_local.py M Modules/_testcapimodule.c M Modules/_threadmodule.c diff --git a/Lib/test/test_threading_local.py b/Lib/test/test_threading_local.py index 9888e631881f..65d9fed7780b 100644 --- a/Lib/test/test_threading_local.py +++ b/Lib/test/test_threading_local.py @@ -3,6 +3,7 @@ from doctest import DocTestSuite from test import support from test.support import threading_helper +from test.support.import_helper import import_module import weakref import gc @@ -197,6 +198,18 @@ class X: self.assertIsNone(wr()) + def test_threading_local_clear_race(self): + # See https://github.com/python/cpython/issues/100892 + + _testcapi = import_module('_testcapi') + _testcapi.call_in_temporary_c_thread(lambda: None, False) + + for _ in range(1000): + _ = threading.local() + + _testcapi.join_temporary_c_thread() + + class ThreadLocalTest(unittest.TestCase, BaseLocalTest): _local = _thread._local diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst new file mode 100644 index 000000000000..f2576becc2fc --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst @@ -0,0 +1 @@ +Fix race while iterating over thread states in clearing :class:`threading.local`. Patch by Kumar Aditya. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index bb7de220a2db..dce948a8f0f1 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2746,6 +2746,7 @@ get_date_fromdate(PyObject *self, PyObject *args) return rv; } + static PyObject * get_datetime_fromdateandtime(PyObject *self, PyObject *args) { @@ -4526,12 +4527,19 @@ temporary_c_thread(void *data) PyThread_release_lock(test_c_thread->exit_event); } +static test_c_thread_t test_c_thread; + static PyObject * -call_in_temporary_c_thread(PyObject *self, PyObject *callback) +call_in_temporary_c_thread(PyObject *self, PyObject *args) { PyObject *res = NULL; - test_c_thread_t test_c_thread; + PyObject *callback = NULL; long thread; + int wait = 1; + if (!PyArg_ParseTuple(args, "O|i", &callback, &wait)) + { + return NULL; + } test_c_thread.start_event = PyThread_allocate_lock(); test_c_thread.exit_event = PyThread_allocate_lock(); @@ -4541,8 +4549,7 @@ call_in_temporary_c_thread(PyObject *self, PyObject *callback) goto exit; } - Py_INCREF(callback); - test_c_thread.callback = callback; + test_c_thread.callback = Py_NewRef(callback); PyThread_acquire_lock(test_c_thread.start_event, 1); PyThread_acquire_lock(test_c_thread.exit_event, 1); @@ -4558,23 +4565,45 @@ call_in_temporary_c_thread(PyObject *self, PyObject *callback) PyThread_acquire_lock(test_c_thread.start_event, 1); PyThread_release_lock(test_c_thread.start_event); + if (!wait) { + Py_RETURN_NONE; + } + Py_BEGIN_ALLOW_THREADS PyThread_acquire_lock(test_c_thread.exit_event, 1); PyThread_release_lock(test_c_thread.exit_event); Py_END_ALLOW_THREADS - Py_INCREF(Py_None); - res = Py_None; + res = Py_NewRef(Py_None); exit: Py_CLEAR(test_c_thread.callback); - if (test_c_thread.start_event) + if (test_c_thread.start_event) { PyThread_free_lock(test_c_thread.start_event); - if (test_c_thread.exit_event) + test_c_thread.start_event = NULL; + } + if (test_c_thread.exit_event) { PyThread_free_lock(test_c_thread.exit_event); + test_c_thread.exit_event = NULL; + } return res; } +static PyObject * +join_temporary_c_thread(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + Py_BEGIN_ALLOW_THREADS + PyThread_acquire_lock(test_c_thread.exit_event, 1); + PyThread_release_lock(test_c_thread.exit_event); + Py_END_ALLOW_THREADS + Py_CLEAR(test_c_thread.callback); + PyThread_free_lock(test_c_thread.start_event); + test_c_thread.start_event = NULL; + PyThread_free_lock(test_c_thread.exit_event); + test_c_thread.exit_event = NULL; + Py_RETURN_NONE; +} + /* marshal */ static PyObject* @@ -6418,8 +6447,9 @@ static PyMethodDef TestMethods[] = { {"docstring_with_signature_with_defaults", (PyCFunction)test_with_docstring, METH_NOARGS, docstring_with_signature_with_defaults}, - {"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_O, + {"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_VARARGS, PyDoc_STR("set_error_class(error_class) -> None")}, + {"join_temporary_c_thread", join_temporary_c_thread, METH_NOARGS}, {"pymarshal_write_long_to_file", pymarshal_write_long_to_file, METH_VARARGS}, {"pymarshal_write_object_to_file", diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 4ac90dc80689..879c94a10969 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -839,6 +839,11 @@ local_traverse(localobject *self, visitproc visit, void *arg) return 0; } +#define HEAD_LOCK(runtime) \ + PyThread_acquire_lock((runtime)->interpreters.mutex, WAIT_LOCK) +#define HEAD_UNLOCK(runtime) \ + PyThread_release_lock((runtime)->interpreters.mutex) + static int local_clear(localobject *self) { @@ -849,18 +854,23 @@ local_clear(localobject *self) /* Remove all strong references to dummies from the thread states */ if (self->key) { PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyRuntimeState *runtime = &_PyRuntime; + HEAD_LOCK(runtime); PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); - for(; tstate; tstate = PyThreadState_Next(tstate)) { - if (tstate->dict == NULL) { - continue; - } - PyObject *v = _PyDict_Pop(tstate->dict, self->key, Py_None); - if (v != NULL) { - Py_DECREF(v); - } - else { - PyErr_Clear(); + HEAD_UNLOCK(runtime); + while (tstate) { + if (tstate->dict) { + PyObject *v = _PyDict_Pop(tstate->dict, self->key, Py_None); + if (v != NULL) { + Py_DECREF(v); + } + else { + PyErr_Clear(); + } } + HEAD_LOCK(runtime); + tstate = PyThreadState_Next(tstate); + HEAD_UNLOCK(runtime); } } return 0; From webhook-mailer at python.org Wed Jan 11 10:32:09 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Wed, 11 Jan 2023 15:32:09 -0000 Subject: [Python-checkins] [3.10] GH-100892: Fix race in clearing `threading.local` (GH-100922). (#100938) Message-ID: https://github.com/python/cpython/commit/a3b65770a0c048c3feaf9c289deba9a8f0e34f72 commit: a3b65770a0c048c3feaf9c289deba9a8f0e34f72 branch: 3.10 author: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-11T21:02:02+05:30 summary: [3.10] GH-100892: Fix race in clearing `threading.local` (GH-100922). (#100938) (cherry picked from commit 762745a124cbc297cf2fe6f3ec9ca1840bb2e873) Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> files: A Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst M Lib/test/test_threading_local.py M Modules/_testcapimodule.c M Modules/_threadmodule.c diff --git a/Lib/test/test_threading_local.py b/Lib/test/test_threading_local.py index 13facb513367..f1ef30e8f1a0 100644 --- a/Lib/test/test_threading_local.py +++ b/Lib/test/test_threading_local.py @@ -3,6 +3,7 @@ from doctest import DocTestSuite from test import support from test.support import threading_helper +from test.support.import_helper import import_module import weakref import gc @@ -194,6 +195,18 @@ class X: self.assertIsNone(wr()) + def test_threading_local_clear_race(self): + # See https://github.com/python/cpython/issues/100892 + + _testcapi = import_module('_testcapi') + _testcapi.call_in_temporary_c_thread(lambda: None, False) + + for _ in range(1000): + _ = threading.local() + + _testcapi.join_temporary_c_thread() + + class ThreadLocalTest(unittest.TestCase, BaseLocalTest): _local = _thread._local diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst new file mode 100644 index 000000000000..f2576becc2fc --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst @@ -0,0 +1 @@ +Fix race while iterating over thread states in clearing :class:`threading.local`. Patch by Kumar Aditya. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index c0c86722eb3c..bafd0c4e697a 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -4368,12 +4368,19 @@ temporary_c_thread(void *data) PyThread_exit_thread(); } +static test_c_thread_t test_c_thread; + static PyObject * -call_in_temporary_c_thread(PyObject *self, PyObject *callback) +call_in_temporary_c_thread(PyObject *self, PyObject *args) { PyObject *res = NULL; - test_c_thread_t test_c_thread; + PyObject *callback = NULL; long thread; + int wait = 1; + if (!PyArg_ParseTuple(args, "O|i", &callback, &wait)) + { + return NULL; + } test_c_thread.start_event = PyThread_allocate_lock(); test_c_thread.exit_event = PyThread_allocate_lock(); @@ -4400,6 +4407,10 @@ call_in_temporary_c_thread(PyObject *self, PyObject *callback) PyThread_acquire_lock(test_c_thread.start_event, 1); PyThread_release_lock(test_c_thread.start_event); + if (!wait) { + Py_RETURN_NONE; + } + Py_BEGIN_ALLOW_THREADS PyThread_acquire_lock(test_c_thread.exit_event, 1); PyThread_release_lock(test_c_thread.exit_event); @@ -4410,13 +4421,32 @@ call_in_temporary_c_thread(PyObject *self, PyObject *callback) exit: Py_CLEAR(test_c_thread.callback); - if (test_c_thread.start_event) + if (test_c_thread.start_event) { PyThread_free_lock(test_c_thread.start_event); - if (test_c_thread.exit_event) + test_c_thread.start_event = NULL; + } + if (test_c_thread.exit_event) { PyThread_free_lock(test_c_thread.exit_event); + test_c_thread.exit_event = NULL; + } return res; } +static PyObject * +join_temporary_c_thread(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + Py_BEGIN_ALLOW_THREADS + PyThread_acquire_lock(test_c_thread.exit_event, 1); + PyThread_release_lock(test_c_thread.exit_event); + Py_END_ALLOW_THREADS + Py_CLEAR(test_c_thread.callback); + PyThread_free_lock(test_c_thread.start_event); + test_c_thread.start_event = NULL; + PyThread_free_lock(test_c_thread.exit_event); + test_c_thread.exit_event = NULL; + Py_RETURN_NONE; +} + /* marshal */ static PyObject* @@ -5878,8 +5908,9 @@ static PyMethodDef TestMethods[] = { {"docstring_with_signature_with_defaults", (PyCFunction)test_with_docstring, METH_NOARGS, docstring_with_signature_with_defaults}, - {"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_O, + {"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_VARARGS, PyDoc_STR("set_error_class(error_class) -> None")}, + {"join_temporary_c_thread", join_temporary_c_thread, METH_NOARGS}, {"pymarshal_write_long_to_file", pymarshal_write_long_to_file, METH_VARARGS}, {"pymarshal_write_object_to_file", diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 813d7ec232c3..8ac75b91097a 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -842,6 +842,11 @@ local_traverse(localobject *self, visitproc visit, void *arg) return 0; } +#define HEAD_LOCK(runtime) \ + PyThread_acquire_lock((runtime)->interpreters.mutex, WAIT_LOCK) +#define HEAD_UNLOCK(runtime) \ + PyThread_release_lock((runtime)->interpreters.mutex) + static int local_clear(localobject *self) { @@ -852,18 +857,23 @@ local_clear(localobject *self) /* Remove all strong references to dummies from the thread states */ if (self->key) { PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyRuntimeState *runtime = &_PyRuntime; + HEAD_LOCK(runtime); PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); - for(; tstate; tstate = PyThreadState_Next(tstate)) { - if (tstate->dict == NULL) { - continue; - } - PyObject *v = _PyDict_Pop(tstate->dict, self->key, Py_None); - if (v != NULL) { - Py_DECREF(v); - } - else { - PyErr_Clear(); + HEAD_UNLOCK(runtime); + while (tstate) { + if (tstate->dict) { + PyObject *v = _PyDict_Pop(tstate->dict, self->key, Py_None); + if (v != NULL) { + Py_DECREF(v); + } + else { + PyErr_Clear(); + } } + HEAD_LOCK(runtime); + tstate = PyThreadState_Next(tstate); + HEAD_UNLOCK(runtime); } } return 0; From webhook-mailer at python.org Wed Jan 11 12:15:15 2023 From: webhook-mailer at python.org (miss-islington) Date: Wed, 11 Jan 2023 17:15:15 -0000 Subject: [Python-checkins] gh-100871: Improve `copy` module tests (GH-100872) Message-ID: https://github.com/python/cpython/commit/729ab9b622957fef0e9b494af9a71ab02986c741 commit: 729ab9b622957fef0e9b494af9a71ab02986c741 branch: main author: Nikita Sobolev committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-11T09:14:41-08:00 summary: gh-100871: Improve `copy` module tests (GH-100872) CC @AlexWaygood as the reviewer of https://github.com/python/cpython/pull/100818 Automerge-Triggered-By: GH:AlexWaygood files: M Lib/test/test_copy.py M Lib/test/test_slice.py diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py index cc95a319d35d..826e46824e00 100644 --- a/Lib/test/test_copy.py +++ b/Lib/test/test_copy.py @@ -51,6 +51,9 @@ def pickle_C(obj): self.assertRaises(TypeError, copy.copy, x) copyreg.pickle(C, pickle_C, C) y = copy.copy(x) + self.assertIsNot(x, y) + self.assertEqual(type(y), C) + self.assertEqual(y.foo, x.foo) def test_copy_reduce_ex(self): class C(object): @@ -311,6 +314,9 @@ def pickle_C(obj): self.assertRaises(TypeError, copy.deepcopy, x) copyreg.pickle(C, pickle_C, C) y = copy.deepcopy(x) + self.assertIsNot(x, y) + self.assertEqual(type(y), C) + self.assertEqual(y.foo, x.foo) def test_deepcopy_reduce_ex(self): class C(object): @@ -352,8 +358,8 @@ class NewStyle: pass def f(): pass - tests = [None, 42, 2**100, 3.14, True, False, 1j, - "hello", "hello\u1234", f.__code__, + tests = [None, ..., NotImplemented, 42, 2**100, 3.14, True, False, 1j, + b"bytes", "hello", "hello\u1234", f.__code__, NewStyle, range(10), max, property()] for x in tests: self.assertIs(copy.deepcopy(x), x) diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py index c4bc8c82023d..03fde3275e14 100644 --- a/Lib/test/test_slice.py +++ b/Lib/test/test_slice.py @@ -5,6 +5,7 @@ import sys import unittest import weakref +import copy from pickle import loads, dumps from test import support @@ -244,6 +245,41 @@ def test_pickle(self): self.assertEqual(s.indices(15), t.indices(15)) self.assertNotEqual(id(s), id(t)) + def test_copy(self): + s = slice(1, 10) + c = copy.copy(s) + self.assertIs(s, c) + + s = slice(1, 10, 2) + c = copy.copy(s) + self.assertIs(s, c) + + # Corner case for mutable indices: + s = slice([1, 2], [3, 4], [5, 6]) + c = copy.copy(s) + self.assertIs(s, c) + self.assertIs(s.start, c.start) + self.assertIs(s.stop, c.stop) + self.assertIs(s.step, c.step) + + def test_deepcopy(self): + s = slice(1, 10) + c = copy.deepcopy(s) + self.assertEqual(s, c) + + s = slice(1, 10, 2) + c = copy.deepcopy(s) + self.assertEqual(s, c) + + # Corner case for mutable indices: + s = slice([1, 2], [3, 4], [5, 6]) + c = copy.deepcopy(s) + self.assertIsNot(s, c) + self.assertEqual(s, c) + self.assertIsNot(s.start, c.start) + self.assertIsNot(s.stop, c.stop) + self.assertIsNot(s.step, c.step) + def test_cycle(self): class myobj(): pass o = myobj() From webhook-mailer at python.org Wed Jan 11 12:25:59 2023 From: webhook-mailer at python.org (zooba) Date: Wed, 11 Jan 2023 17:25:59 -0000 Subject: [Python-checkins] Skip py.exe launcher tests in full layout CI test (GH-100948) Message-ID: https://github.com/python/cpython/commit/5ff029f7a30705ac00a1010d3d191edcd011f160 commit: 5ff029f7a30705ac00a1010d3d191edcd011f160 branch: main author: Steve Dower committer: zooba date: 2023-01-11T17:25:46Z summary: Skip py.exe launcher tests in full layout CI test (GH-100948) These tests become flaky when py.exe exists on the test machine but isn't the one that was just built. They also don't provide any useful information for this scenario, so easiest to just skip them. files: M .azure-pipelines/windows-layout-steps.yml diff --git a/.azure-pipelines/windows-layout-steps.yml b/.azure-pipelines/windows-layout-steps.yml index e15729fac344..afd897817904 100644 --- a/.azure-pipelines/windows-layout-steps.yml +++ b/.azure-pipelines/windows-layout-steps.yml @@ -12,7 +12,7 @@ steps: displayName: Show layout info (${{ parameters.kind }}) - ${{ if eq(parameters.fulltest, 'true') }}: - - script: .\python.exe -m test -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 --junit-xml="$(Build.BinariesDirectory)\test-results-${{ parameters.kind }}.xml" --tempdir "$(Build.BinariesDirectory)\tmp-${{ parameters.kind }}-$(arch)" + - script: .\python.exe -m test -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 --junit-xml="$(Build.BinariesDirectory)\test-results-${{ parameters.kind }}.xml" --tempdir "$(Build.BinariesDirectory)\tmp-${{ parameters.kind }}-$(arch)" -i test_launcher workingDirectory: $(Build.BinariesDirectory)\layout-${{ parameters.kind }}-$(arch) displayName: ${{ parameters.kind }} Tests env: From webhook-mailer at python.org Wed Jan 11 12:29:03 2023 From: webhook-mailer at python.org (zooba) Date: Wed, 11 Jan 2023 17:29:03 -0000 Subject: [Python-checkins] Ensure some build files include a newline at to avoid warnings (GH-100946) Message-ID: https://github.com/python/cpython/commit/61f12b8ff7073064040ff0e6220150408d24829b commit: 61f12b8ff7073064040ff0e6220150408d24829b branch: main author: th1722 <20001722 at ymail.ne.jp> committer: zooba date: 2023-01-11T17:28:57Z summary: Ensure some build files include a newline at to avoid warnings (GH-100946) files: M PCbuild/_ssl.vcxproj M PCbuild/openssl.props diff --git a/PCbuild/_ssl.vcxproj b/PCbuild/_ssl.vcxproj index 4907f49b6628..226ff506f8c6 100644 --- a/PCbuild/_ssl.vcxproj +++ b/PCbuild/_ssl.vcxproj @@ -119,4 +119,4 @@ - \ No newline at end of file + diff --git a/PCbuild/openssl.props b/PCbuild/openssl.props index 6081d3c8c696..7071bb57c06c 100644 --- a/PCbuild/openssl.props +++ b/PCbuild/openssl.props @@ -31,4 +31,4 @@ - \ No newline at end of file + From webhook-mailer at python.org Wed Jan 11 12:52:16 2023 From: webhook-mailer at python.org (miss-islington) Date: Wed, 11 Jan 2023 17:52:16 -0000 Subject: [Python-checkins] Skip py.exe launcher tests in full layout CI test (GH-100948) Message-ID: https://github.com/python/cpython/commit/b594f2532727a1ee2f0e6ba2b2801e1cf0c349e4 commit: b594f2532727a1ee2f0e6ba2b2801e1cf0c349e4 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-11T09:52:10-08:00 summary: Skip py.exe launcher tests in full layout CI test (GH-100948) These tests become flaky when py.exe exists on the test machine but isn't the one that was just built. They also don't provide any useful information for this scenario, so easiest to just skip them. (cherry picked from commit 5ff029f7a30705ac00a1010d3d191edcd011f160) Co-authored-by: Steve Dower files: M .azure-pipelines/windows-layout-steps.yml diff --git a/.azure-pipelines/windows-layout-steps.yml b/.azure-pipelines/windows-layout-steps.yml index e15729fac344..afd897817904 100644 --- a/.azure-pipelines/windows-layout-steps.yml +++ b/.azure-pipelines/windows-layout-steps.yml @@ -12,7 +12,7 @@ steps: displayName: Show layout info (${{ parameters.kind }}) - ${{ if eq(parameters.fulltest, 'true') }}: - - script: .\python.exe -m test -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 --junit-xml="$(Build.BinariesDirectory)\test-results-${{ parameters.kind }}.xml" --tempdir "$(Build.BinariesDirectory)\tmp-${{ parameters.kind }}-$(arch)" + - script: .\python.exe -m test -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 --junit-xml="$(Build.BinariesDirectory)\test-results-${{ parameters.kind }}.xml" --tempdir "$(Build.BinariesDirectory)\tmp-${{ parameters.kind }}-$(arch)" -i test_launcher workingDirectory: $(Build.BinariesDirectory)\layout-${{ parameters.kind }}-$(arch) displayName: ${{ parameters.kind }} Tests env: From webhook-mailer at python.org Wed Jan 11 15:40:50 2023 From: webhook-mailer at python.org (markshannon) Date: Wed, 11 Jan 2023 20:40:50 -0000 Subject: [Python-checkins] GH-100923: Embed jump mask in `COMPARE_OP` oparg (GH-100924) Message-ID: https://github.com/python/cpython/commit/6e4e14d98fe0868981f29701496d57a8223c5407 commit: 6e4e14d98fe0868981f29701496d57a8223c5407 branch: main author: Mark Shannon committer: markshannon date: 2023-01-11T20:40:43Z summary: GH-100923: Embed jump mask in `COMPARE_OP` oparg (GH-100924) files: A Misc/NEWS.d/next/Core and Builtins/2023-01-10-16-59-33.gh-issue-100923.ypJAX-.rst M Include/internal/pycore_code.h M Include/internal/pycore_opcode.h M Lib/dis.py M Lib/importlib/_bootstrap_external.py M Lib/opcode.py M Lib/test/test_dis.py M Python/bytecodes.c M Python/compile.c M Python/generated_cases.c.h M Python/opcode_metadata.h M Python/specialize.c diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 9e59fc98bf3d..b53657ac08a0 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -41,7 +41,6 @@ typedef struct { typedef struct { uint16_t counter; - uint16_t mask; } _PyCompareOpCache; #define INLINE_CACHE_ENTRIES_COMPARE_OP CACHE_ENTRIES(_PyCompareOpCache) @@ -477,6 +476,27 @@ _Py_MakeShimCode(const _PyShimCodeDef *code); extern uint32_t _Py_next_func_version; +/* Comparison bit masks. */ + +/* Note this evaluates its arguments twice each */ +#define COMPARISON_BIT(x, y) (1 << (2 * ((x) >= (y)) + ((x) <= (y)))) + +/* + * The following bits are chosen so that the value of + * COMPARSION_BIT(left, right) + * masked by the values below will be non-zero if the + * comparison is true, and zero if it is false */ + +/* This is for values that are unordered, ie. NaN, not types that are unordered, e.g. sets */ +#define COMPARISON_UNORDERED 1 + +#define COMPARISON_LESS_THAN 2 +#define COMPARISON_GREATER_THAN 4 +#define COMPARISON_EQUALS 8 + +#define COMPARISON_NOT_EQUALS (COMPARISON_UNORDERED | COMPARISON_LESS_THAN | COMPARISON_GREATER_THAN) + + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index 8fdf121aecc1..5806d69f98a9 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -47,7 +47,7 @@ const uint8_t _PyOpcode_Caches[256] = { [FOR_ITER] = 1, [STORE_ATTR] = 4, [LOAD_ATTR] = 9, - [COMPARE_OP] = 2, + [COMPARE_OP] = 1, [LOAD_GLOBAL] = 5, [BINARY_OP] = 1, [CALL] = 4, diff --git a/Lib/dis.py b/Lib/dis.py index 76104c6098d4..72ab9536a2bf 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -483,7 +483,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None, elif deop in haslocal or deop in hasfree: argval, argrepr = _get_name_info(arg, varname_from_oparg) elif deop in hascompare: - argval = cmp_op[arg] + argval = cmp_op[arg>>4] argrepr = argval elif deop == FORMAT_VALUE: argval, argrepr = FORMAT_VALUE_CONVERTERS[arg & 0x3] diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 8a21e0e46469..cbde91323422 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -428,6 +428,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.12a1 3512 (Remove all unused consts from code objects) # Python 3.12a1 3513 (Add CALL_INTRINSIC_1 instruction, removed STOPITERATION_ERROR, PRINT_EXPR, IMPORT_STAR) # Python 3.12a1 3514 (Remove ASYNC_GEN_WRAP, LIST_TO_TUPLE, and UNARY_POSITIVE) +# Python 3.12a1 3515 (Embed jump mask in COMPARE_OP oparg) # Python 3.13 will start with 3550 @@ -440,7 +441,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3514).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3515).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c diff --git a/Lib/opcode.py b/Lib/opcode.py index 9ba39e2eec75..414faa35ee89 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -391,7 +391,6 @@ def pseudo_op(name, op, real_ops): }, "COMPARE_OP": { "counter": 1, - "mask": 1, }, "BINARY_SUBSCR": { "counter": 1, diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index c2a339ac7cba..994bb370a575 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -45,7 +45,7 @@ def cm(cls, x): %3d LOAD_FAST 1 (x) LOAD_CONST 1 (1) - COMPARE_OP 2 (==) + COMPARE_OP 32 (==) LOAD_FAST 0 (self) STORE_ATTR 0 (x) LOAD_CONST 0 (None) @@ -56,7 +56,7 @@ def cm(cls, x): RESUME 0 LOAD_FAST 1 LOAD_CONST 1 - COMPARE_OP 2 (==) + COMPARE_OP 32 (==) LOAD_FAST 0 STORE_ATTR 0 LOAD_CONST 0 @@ -68,7 +68,7 @@ def cm(cls, x): %3d LOAD_FAST 1 (x) LOAD_CONST 1 (1) - COMPARE_OP 2 (==) + COMPARE_OP 32 (==) LOAD_FAST 0 (cls) STORE_ATTR 0 (x) LOAD_CONST 0 (None) @@ -80,7 +80,7 @@ def cm(cls, x): %3d LOAD_FAST 0 (x) LOAD_CONST 1 (1) - COMPARE_OP 2 (==) + COMPARE_OP 32 (==) STORE_FAST 0 (x) LOAD_CONST 0 (None) RETURN_VALUE @@ -1553,7 +1553,7 @@ def _prepare_test_cases(): Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=10, argrepr='10', offset=14, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=16, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='GET_ITER', opcode=68, arg=None, argval=None, argrepr='', offset=26, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='FOR_ITER', opcode=93, arg=30, argval=92, argrepr='to 92', offset=28, starts_line=None, is_jump_target=True, positions=None), + Instruction(opname='FOR_ITER', opcode=93, arg=28, argval=88, argrepr='to 88', offset=28, starts_line=None, is_jump_target=True, positions=None), Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=32, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=34, starts_line=4, is_jump_target=False, positions=None), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=46, starts_line=None, is_jump_target=False, positions=None), @@ -1561,107 +1561,107 @@ def _prepare_test_cases(): Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=58, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=60, starts_line=5, is_jump_target=False, positions=None), Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=62, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=107, arg=0, argval='<', argrepr='<', offset=64, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=74, argrepr='to 74', offset=70, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=140, arg=23, argval=28, argrepr='to 28', offset=72, starts_line=6, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=74, starts_line=7, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=76, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=107, arg=4, argval='>', argrepr='>', offset=78, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=1, argval=88, argrepr='to 88', offset=84, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=140, arg=30, argval=28, argrepr='to 28', offset=86, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=88, starts_line=8, is_jump_target=True, positions=None), - Instruction(opname='JUMP_FORWARD', opcode=110, arg=14, argval=120, argrepr='to 120', offset=90, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='END_FOR', opcode=4, arg=None, argval=None, argrepr='', offset=92, starts_line=3, is_jump_target=True, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=94, starts_line=10, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=106, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=108, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=118, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST_CHECK', opcode=127, arg=0, argval='i', argrepr='i', offset=120, starts_line=11, is_jump_target=True, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=35, argval=194, argrepr='to 194', offset=122, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=124, starts_line=12, is_jump_target=True, positions=None), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=136, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=138, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=148, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=150, starts_line=13, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=152, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='BINARY_OP', opcode=122, arg=23, argval=23, argrepr='-=', offset=154, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=158, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=160, starts_line=14, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=162, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=107, arg=4, argval='>', argrepr='>', offset=164, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=174, argrepr='to 174', offset=170, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=140, arg=27, argval=120, argrepr='to 120', offset=172, starts_line=15, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=174, starts_line=16, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=176, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=107, arg=0, argval='<', argrepr='<', offset=178, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=188, argrepr='to 188', offset=184, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='JUMP_FORWARD', opcode=110, arg=16, argval=220, argrepr='to 220', offset=186, starts_line=17, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=188, starts_line=11, is_jump_target=True, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=194, argrepr='to 194', offset=190, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=140, arg=35, argval=124, argrepr='to 124', offset=192, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=194, starts_line=19, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=206, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=208, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=218, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='NOP', opcode=9, arg=None, argval=None, argrepr='', offset=220, starts_line=20, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=222, starts_line=21, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval=0, argrepr='0', offset=224, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='BINARY_OP', opcode=122, arg=11, argval=11, argrepr='/', offset=226, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=230, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=232, starts_line=25, is_jump_target=False, positions=None), - Instruction(opname='BEFORE_WITH', opcode=53, arg=None, argval=None, argrepr='', offset=234, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='STORE_FAST', opcode=125, arg=1, argval='dodgy', argrepr='dodgy', offset=236, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=238, starts_line=26, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval='Never reach this', argrepr="'Never reach this'", offset=250, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=252, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=262, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=264, starts_line=25, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=266, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=268, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=171, arg=2, argval=2, argrepr='', offset=270, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=280, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=282, starts_line=28, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=294, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=296, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=306, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=308, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=310, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=312, starts_line=25, is_jump_target=False, positions=None), - Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=314, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=1, argval=320, argrepr='to 320', offset=316, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=2, argval=2, argrepr='', offset=318, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=320, starts_line=None, is_jump_target=True, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=322, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=324, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=326, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=140, arg=24, argval=282, argrepr='to 282', offset=328, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=330, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=332, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=334, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=336, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=338, starts_line=22, is_jump_target=False, positions=None), - Instruction(opname='CHECK_EXC_MATCH', opcode=36, arg=None, argval=None, argrepr='', offset=350, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=16, argval=386, argrepr='to 386', offset=352, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=354, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=356, starts_line=23, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=368, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=370, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=380, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COMPARE_OP', opcode=107, arg=13, argval='<', argrepr='<', offset=64, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=72, argrepr='to 72', offset=68, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=140, arg=22, argval=28, argrepr='to 28', offset=70, starts_line=6, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=72, starts_line=7, is_jump_target=True, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=74, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COMPARE_OP', opcode=107, arg=68, argval='>', argrepr='>', offset=76, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=1, argval=84, argrepr='to 84', offset=80, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=140, arg=28, argval=28, argrepr='to 28', offset=82, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=84, starts_line=8, is_jump_target=True, positions=None), + Instruction(opname='JUMP_FORWARD', opcode=110, arg=14, argval=116, argrepr='to 116', offset=86, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='END_FOR', opcode=4, arg=None, argval=None, argrepr='', offset=88, starts_line=3, is_jump_target=True, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=90, starts_line=10, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=102, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=104, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=114, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST_CHECK', opcode=127, arg=0, argval='i', argrepr='i', offset=116, starts_line=11, is_jump_target=True, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=33, argval=186, argrepr='to 186', offset=118, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=120, starts_line=12, is_jump_target=True, positions=None), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=132, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=134, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=144, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=146, starts_line=13, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=148, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='BINARY_OP', opcode=122, arg=23, argval=23, argrepr='-=', offset=150, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=154, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=156, starts_line=14, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=158, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COMPARE_OP', opcode=107, arg=75, argval='>', argrepr='>', offset=160, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=168, argrepr='to 168', offset=164, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=140, arg=26, argval=116, argrepr='to 116', offset=166, starts_line=15, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=168, starts_line=16, is_jump_target=True, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=170, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COMPARE_OP', opcode=107, arg=13, argval='<', argrepr='<', offset=172, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=180, argrepr='to 180', offset=176, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='JUMP_FORWARD', opcode=110, arg=16, argval=212, argrepr='to 212', offset=178, starts_line=17, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=180, starts_line=11, is_jump_target=True, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=186, argrepr='to 186', offset=182, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=140, arg=33, argval=120, argrepr='to 120', offset=184, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=186, starts_line=19, is_jump_target=True, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=198, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=200, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=210, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='NOP', opcode=9, arg=None, argval=None, argrepr='', offset=212, starts_line=20, is_jump_target=True, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=214, starts_line=21, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval=0, argrepr='0', offset=216, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='BINARY_OP', opcode=122, arg=11, argval=11, argrepr='/', offset=218, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=222, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=224, starts_line=25, is_jump_target=False, positions=None), + Instruction(opname='BEFORE_WITH', opcode=53, arg=None, argval=None, argrepr='', offset=226, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='STORE_FAST', opcode=125, arg=1, argval='dodgy', argrepr='dodgy', offset=228, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=230, starts_line=26, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval='Never reach this', argrepr="'Never reach this'", offset=242, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=244, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=254, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=256, starts_line=25, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=258, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=260, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=171, arg=2, argval=2, argrepr='', offset=262, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=272, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=274, starts_line=28, is_jump_target=True, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=286, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=288, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=298, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=300, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=302, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=304, starts_line=25, is_jump_target=False, positions=None), + Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=306, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=1, argval=312, argrepr='to 312', offset=308, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=2, argval=2, argrepr='', offset=310, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=312, starts_line=None, is_jump_target=True, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=314, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=316, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=318, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=140, arg=24, argval=274, argrepr='to 274', offset=320, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=322, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=324, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=326, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=328, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=330, starts_line=22, is_jump_target=False, positions=None), + Instruction(opname='CHECK_EXC_MATCH', opcode=36, arg=None, argval=None, argrepr='', offset=342, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=16, argval=378, argrepr='to 378', offset=344, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=346, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=348, starts_line=23, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=360, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=362, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=372, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=374, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=140, arg=52, argval=274, argrepr='to 274', offset=376, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=378, starts_line=22, is_jump_target=True, positions=None), + Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=380, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=382, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=140, arg=52, argval=282, argrepr='to 282', offset=384, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=386, starts_line=22, is_jump_target=True, positions=None), - Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=388, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=390, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=392, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=394, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=396, starts_line=28, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=408, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=410, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=420, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=422, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=424, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=426, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=428, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=384, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=386, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=388, starts_line=28, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=400, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=402, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=412, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=414, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=416, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=418, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=420, starts_line=None, is_jump_target=False, positions=None), ] # One last piece of inspect fodder to check the default line number handling diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-10-16-59-33.gh-issue-100923.ypJAX-.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-10-16-59-33.gh-issue-100923.ypJAX-.rst new file mode 100644 index 000000000000..b6b3f1d0c58f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-10-16-59-33.gh-issue-100923.ypJAX-.rst @@ -0,0 +1,2 @@ +Remove the ``mask`` cache entry for the :opcode:`COMPARE_OP` instruction and +embed the mask into the oparg. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 251ee564eb72..faa6df6a0a65 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -88,7 +88,7 @@ static PyObject *aiter, *awaitable, *iterable, *w, *exc_value, *bc; static PyObject *orig, *excs, *update, *b, *fromlist, *level, *from; static size_t jump; // Dummy variables for cache effects -static uint16_t when_to_jump_mask, invert, counter, index, hint; +static uint16_t invert, counter, index, hint; static uint32_t type_version; // Dummy opcode names for 'op' opcodes #define _COMPARE_OP_FLOAT 1003 @@ -1836,7 +1836,7 @@ dummy_func( _COMPARE_OP_STR, }; - inst(COMPARE_OP, (unused/2, left, right -- res)) { + inst(COMPARE_OP, (unused/1, left, right -- res)) { _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -1846,15 +1846,15 @@ dummy_func( } STAT_INC(COMPARE_OP, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); - assert(oparg <= Py_GE); - res = PyObject_RichCompare(left, right, oparg); + assert((oparg >> 4) <= Py_GE); + res = PyObject_RichCompare(left, right, oparg>>4); Py_DECREF(left); Py_DECREF(right); ERROR_IF(res == NULL, error); } // The result is an int disguised as an object pointer. - op(_COMPARE_OP_FLOAT, (unused/1, when_to_jump_mask/1, left, right -- jump: size_t)) { + op(_COMPARE_OP_FLOAT, (unused/1, left, right -- jump: size_t)) { assert(cframe.use_tracing == 0); // Combined: COMPARE_OP (float ? float) + POP_JUMP_IF_(true/false) DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_OP); @@ -1862,11 +1862,11 @@ dummy_func( STAT_INC(COMPARE_OP, hit); double dleft = PyFloat_AS_DOUBLE(left); double dright = PyFloat_AS_DOUBLE(right); - // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches when_to_jump_mask - int sign_ish = 1 << (2 * (dleft >= dright) + (dleft <= dright)); + // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg + int sign_ish = COMPARISON_BIT(dleft, dright); _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); - jump = sign_ish & when_to_jump_mask; + jump = sign_ish & oparg; } // The input is an int disguised as an object pointer! op(_JUMP_IF, (jump: size_t --)) { @@ -1879,7 +1879,7 @@ dummy_func( super(COMPARE_OP_FLOAT_JUMP) = _COMPARE_OP_FLOAT + _JUMP_IF; // Similar to COMPARE_OP_FLOAT - op(_COMPARE_OP_INT, (unused/1, when_to_jump_mask/1, left, right -- jump: size_t)) { + op(_COMPARE_OP_INT, (unused/1, left, right -- jump: size_t)) { assert(cframe.use_tracing == 0); // Combined: COMPARE_OP (int ? int) + POP_JUMP_IF_(true/false) DEOPT_IF(!PyLong_CheckExact(left), COMPARE_OP); @@ -1890,29 +1890,31 @@ dummy_func( assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1); Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0]; Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0]; - // 2 if <, 4 if >, 8 if ==; this matches when_to_jump_mask - int sign_ish = 1 << (2 * (ileft >= iright) + (ileft <= iright)); + // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg + int sign_ish = COMPARISON_BIT(ileft, iright); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); - jump = sign_ish & when_to_jump_mask; + jump = sign_ish & oparg; } super(COMPARE_OP_INT_JUMP) = _COMPARE_OP_INT + _JUMP_IF; // Similar to COMPARE_OP_FLOAT, but for ==, != only - op(_COMPARE_OP_STR, (unused/1, invert/1, left, right -- jump: size_t)) { + op(_COMPARE_OP_STR, (unused/1, left, right -- jump: size_t)) { assert(cframe.use_tracing == 0); // Combined: COMPARE_OP (str == str or str != str) + POP_JUMP_IF_(true/false) DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_OP); DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_OP); STAT_INC(COMPARE_OP, hit); int res = _PyUnicode_Equal(left, right); - assert(oparg == Py_EQ || oparg == Py_NE); + assert((oparg >>4) == Py_EQ || (oparg >>4) == Py_NE); _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); assert(res == 0 || res == 1); - assert(invert == 0 || invert == 1); - jump = res ^ invert; + assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); + assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); + jump = (res + COMPARISON_NOT_EQUALS) & oparg; } + super(COMPARE_OP_STR_JUMP) = _COMPARE_OP_STR + _JUMP_IF; inst(IS_OP, (left, right -- b)) { diff --git a/Python/compile.c b/Python/compile.c index 943168ba693d..c0177fbf3e33 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -2887,7 +2887,9 @@ static int compiler_addcompare(struct compiler *c, location loc, default: Py_UNREACHABLE(); } - ADDOP_I(c, loc, COMPARE_OP, cmp); + /* cmp goes in top bits of the oparg, while the low bits are used by quickened + * versions of this opcode to store the comparison mask. */ + ADDOP_I(c, loc, COMPARE_OP, cmp << 4); return SUCCESS; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 218bc624fa15..9874ddf206d8 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2090,14 +2090,14 @@ } STAT_INC(COMPARE_OP, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); - assert(oparg <= Py_GE); - res = PyObject_RichCompare(left, right, oparg); + assert((oparg >> 4) <= Py_GE); + res = PyObject_RichCompare(left, right, oparg>>4); Py_DECREF(left); Py_DECREF(right); if (res == NULL) goto pop_2_error; STACK_SHRINK(1); POKE(1, res); - JUMPBY(2); + JUMPBY(1); DISPATCH(); } @@ -2108,7 +2108,6 @@ PyObject *right = _tmp_1; PyObject *left = _tmp_2; size_t jump; - uint16_t when_to_jump_mask = read_u16(&next_instr[1].cache); assert(cframe.use_tracing == 0); // Combined: COMPARE_OP (float ? float) + POP_JUMP_IF_(true/false) DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_OP); @@ -2116,14 +2115,14 @@ STAT_INC(COMPARE_OP, hit); double dleft = PyFloat_AS_DOUBLE(left); double dright = PyFloat_AS_DOUBLE(right); - // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches when_to_jump_mask - int sign_ish = 1 << (2 * (dleft >= dright) + (dleft <= dright)); + // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg + int sign_ish = COMPARISON_BIT(dleft, dright); _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); - jump = sign_ish & when_to_jump_mask; + jump = sign_ish & oparg; _tmp_2 = (PyObject *)jump; } - JUMPBY(2); + JUMPBY(1); NEXTOPARG(); JUMPBY(1); { @@ -2144,7 +2143,6 @@ PyObject *right = _tmp_1; PyObject *left = _tmp_2; size_t jump; - uint16_t when_to_jump_mask = read_u16(&next_instr[1].cache); assert(cframe.use_tracing == 0); // Combined: COMPARE_OP (int ? int) + POP_JUMP_IF_(true/false) DEOPT_IF(!PyLong_CheckExact(left), COMPARE_OP); @@ -2155,14 +2153,14 @@ assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1); Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0]; Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0]; - // 2 if <, 4 if >, 8 if ==; this matches when_to_jump_mask - int sign_ish = 1 << (2 * (ileft >= iright) + (ileft <= iright)); + // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg + int sign_ish = COMPARISON_BIT(ileft, iright); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); - jump = sign_ish & when_to_jump_mask; + jump = sign_ish & oparg; _tmp_2 = (PyObject *)jump; } - JUMPBY(2); + JUMPBY(1); NEXTOPARG(); JUMPBY(1); { @@ -2183,22 +2181,22 @@ PyObject *right = _tmp_1; PyObject *left = _tmp_2; size_t jump; - uint16_t invert = read_u16(&next_instr[1].cache); assert(cframe.use_tracing == 0); // Combined: COMPARE_OP (str == str or str != str) + POP_JUMP_IF_(true/false) DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_OP); DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_OP); STAT_INC(COMPARE_OP, hit); int res = _PyUnicode_Equal(left, right); - assert(oparg == Py_EQ || oparg == Py_NE); + assert((oparg >>4) == Py_EQ || (oparg >>4) == Py_NE); _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); assert(res == 0 || res == 1); - assert(invert == 0 || invert == 1); - jump = res ^ invert; + assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); + assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); + jump = (res + COMPARISON_NOT_EQUALS) & oparg; _tmp_2 = (PyObject *)jump; } - JUMPBY(2); + JUMPBY(1); NEXTOPARG(); JUMPBY(1); { diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 34f8145828c2..3316ea66b4ef 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -2,7 +2,7 @@ // from Python/bytecodes.c // Do not edit! enum Direction { DIR_NONE, DIR_READ, DIR_WRITE }; -enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBC0IB, INSTR_FMT_IBIB }; +enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC000, INSTR_FMT_IBCIB, INSTR_FMT_IBIB }; static const struct { short n_popped; short n_pushed; @@ -112,10 +112,10 @@ static const struct { [STORE_ATTR_INSTANCE_VALUE] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, [STORE_ATTR_WITH_HINT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, [STORE_ATTR_SLOT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, - [COMPARE_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, - [COMPARE_OP_FLOAT_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0IB }, - [COMPARE_OP_INT_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0IB }, - [COMPARE_OP_STR_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0IB }, + [COMPARE_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [COMPARE_OP_FLOAT_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBCIB }, + [COMPARE_OP_INT_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBCIB }, + [COMPARE_OP_STR_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBCIB }, [IS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [CONTAINS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [CHECK_EG_MATCH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, diff --git a/Python/specialize.c b/Python/specialize.c index 48814dad671e..38bb151e7471 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -262,18 +262,27 @@ do { \ #define SPECIALIZATION_FAIL(opcode, kind) ((void)0) #endif +static int compare_masks[] = { + [Py_LT] = COMPARISON_LESS_THAN, + [Py_LE] = COMPARISON_LESS_THAN | COMPARISON_EQUALS, + [Py_EQ] = COMPARISON_EQUALS, + [Py_NE] = COMPARISON_NOT_EQUALS, + [Py_GT] = COMPARISON_GREATER_THAN, + [Py_GE] = COMPARISON_GREATER_THAN | COMPARISON_EQUALS, +}; + // Initialize warmup counters and insert superinstructions. This cannot fail. void _PyCode_Quicken(PyCodeObject *code) { - int previous_opcode = 0; + int opcode = 0; _Py_CODEUNIT *instructions = _PyCode_CODE(code); for (int i = 0; i < Py_SIZE(code); i++) { - int opcode = _PyOpcode_Deopt[_Py_OPCODE(instructions[i])]; + int previous_opcode = opcode; + opcode = _PyOpcode_Deopt[_Py_OPCODE(instructions[i])]; int caches = _PyOpcode_Caches[opcode]; if (caches) { instructions[i + 1].cache = adaptive_counter_warmup(); - previous_opcode = 0; i += caches; continue; } @@ -293,8 +302,19 @@ _PyCode_Quicken(PyCodeObject *code) case STORE_FAST << 8 | STORE_FAST: instructions[i - 1].opcode = STORE_FAST__STORE_FAST; break; + case COMPARE_OP << 8 | POP_JUMP_IF_TRUE: + case COMPARE_OP << 8 | POP_JUMP_IF_FALSE: + { + int oparg = instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].oparg; + assert((oparg >> 4) <= Py_GE); + int mask = compare_masks[oparg >> 4]; + if (opcode == POP_JUMP_IF_FALSE) { + mask = mask ^ 0xf; + } + instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].oparg = (oparg & 0xf0) | mask; + break; + } } - previous_opcode = opcode; } } @@ -1977,20 +1997,6 @@ compare_op_fail_kind(PyObject *lhs, PyObject *rhs) } #endif - -static int compare_masks[] = { - // 1-bit: jump if unordered - // 2-bit: jump if less - // 4-bit: jump if greater - // 8-bit: jump if equal - [Py_LT] = 0 | 2 | 0 | 0, - [Py_LE] = 0 | 2 | 0 | 8, - [Py_EQ] = 0 | 0 | 0 | 8, - [Py_NE] = 1 | 2 | 4 | 0, - [Py_GT] = 0 | 0 | 4 | 0, - [Py_GE] = 0 | 0 | 4 | 8, -}; - void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, int oparg) @@ -2006,24 +2012,17 @@ _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, SPECIALIZATION_FAIL(COMPARE_OP, SPEC_FAIL_COMPARE_OP_NOT_FOLLOWED_BY_COND_JUMP); goto failure; } - assert(oparg <= Py_GE); - int when_to_jump_mask = compare_masks[oparg]; - if (next_opcode == POP_JUMP_IF_FALSE) { - when_to_jump_mask = (1 | 2 | 4 | 8) & ~when_to_jump_mask; - } if (Py_TYPE(lhs) != Py_TYPE(rhs)) { SPECIALIZATION_FAIL(COMPARE_OP, compare_op_fail_kind(lhs, rhs)); goto failure; } if (PyFloat_CheckExact(lhs)) { _py_set_opcode(instr, COMPARE_OP_FLOAT_JUMP); - cache->mask = when_to_jump_mask; goto success; } if (PyLong_CheckExact(lhs)) { if (Py_ABS(Py_SIZE(lhs)) <= 1 && Py_ABS(Py_SIZE(rhs)) <= 1) { _py_set_opcode(instr, COMPARE_OP_INT_JUMP); - cache->mask = when_to_jump_mask; goto success; } else { @@ -2032,13 +2031,13 @@ _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, } } if (PyUnicode_CheckExact(lhs)) { - if (oparg != Py_EQ && oparg != Py_NE) { + int cmp = oparg >> 4; + if (cmp != Py_EQ && cmp != Py_NE) { SPECIALIZATION_FAIL(COMPARE_OP, SPEC_FAIL_COMPARE_OP_STRING); goto failure; } else { _py_set_opcode(instr, COMPARE_OP_STR_JUMP); - cache->mask = (when_to_jump_mask & 8) == 0; goto success; } } From webhook-mailer at python.org Wed Jan 11 16:17:35 2023 From: webhook-mailer at python.org (gvanrossum) Date: Wed, 11 Jan 2023 21:17:35 -0000 Subject: [Python-checkins] gh-94912: Adjusted check for non-standard coroutine function marker. (#100935) Message-ID: https://github.com/python/cpython/commit/07a87f74faf31cdd755ac7de6d44531139899d1b commit: 07a87f74faf31cdd755ac7de6d44531139899d1b branch: main author: Carlton Gibson committer: gvanrossum date: 2023-01-11T13:17:26-08:00 summary: gh-94912: Adjusted check for non-standard coroutine function marker. (#100935) The initial implementation did not correctly identify explicitly marked class instances. Follow up to 532aa4e4e019812d0388920768ede7c04232ebe1 files: M Lib/inspect.py M Lib/test/test_inspect.py diff --git a/Lib/inspect.py b/Lib/inspect.py index 3db7745e8a5e..8bb3a375735a 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -399,8 +399,6 @@ def _has_coroutine_mark(f): while ismethod(f): f = f.__func__ f = functools._unwrap_partial(f) - if not (isfunction(f) or _signature_is_functionlike(f)): - return False return getattr(f, "_is_coroutine_marker", None) is _is_coroutine_marker def markcoroutinefunction(func): diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index aa757241aca9..92aba519d28a 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -223,6 +223,10 @@ async def __call__(self): self.assertFalse(inspect.iscoroutinefunction(Cl)) # instances with async def __call__ are NOT recognised. self.assertFalse(inspect.iscoroutinefunction(Cl())) + # unless explicitly marked. + self.assertTrue(inspect.iscoroutinefunction( + inspect.markcoroutinefunction(Cl()) + )) class Cl2: @inspect.markcoroutinefunction @@ -232,6 +236,10 @@ def __call__(self): self.assertFalse(inspect.iscoroutinefunction(Cl2)) # instances with marked __call__ are NOT recognised. self.assertFalse(inspect.iscoroutinefunction(Cl2())) + # unless explicitly marked. + self.assertTrue(inspect.iscoroutinefunction( + inspect.markcoroutinefunction(Cl2()) + )) class Cl3: @inspect.markcoroutinefunction From webhook-mailer at python.org Wed Jan 11 16:46:37 2023 From: webhook-mailer at python.org (erlend-aasland) Date: Wed, 11 Jan 2023 21:46:37 -0000 Subject: [Python-checkins] gh-98636: Fix detecting gdbm_compat for _dbm module (#98643) Message-ID: https://github.com/python/cpython/commit/02a72f080dc89b037c304a85a0f96509de9ae688 commit: 02a72f080dc89b037c304a85a0f96509de9ae688 branch: main author: Micha? G?rny committer: erlend-aasland date: 2023-01-11T22:46:28+01:00 summary: gh-98636: Fix detecting gdbm_compat for _dbm module (#98643) Fix the gdbm_compat library detection logic to actually check for -lgdbm_compat independently of the ndbm detection. This fixes the build failure with `--with-dbmliborder=gdbm`, and implicit fallback to ndbm with the default value. files: A Misc/NEWS.d/next/Build/2022-10-25-11-53-55.gh-issue-98636.e0RPAr.rst M Misc/ACKS M configure M configure.ac diff --git a/Misc/ACKS b/Misc/ACKS index b4e309c6905b..a51658d79fa1 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -640,6 +640,7 @@ Tiago Gon?alves Chris Gonnerman Shelley Gooch David Goodger +Micha? G?rny Elliot Gorokhovsky Hans de Graaff Tim Graham diff --git a/Misc/NEWS.d/next/Build/2022-10-25-11-53-55.gh-issue-98636.e0RPAr.rst b/Misc/NEWS.d/next/Build/2022-10-25-11-53-55.gh-issue-98636.e0RPAr.rst new file mode 100644 index 000000000000..26a7cc8acaf2 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2022-10-25-11-53-55.gh-issue-98636.e0RPAr.rst @@ -0,0 +1,2 @@ +Fix a regression in detecting ``gdbm_compat`` library for the ``_gdbm`` +module build. diff --git a/configure b/configure index 4841c1d8ad5f..5b6750aaa8db 100755 --- a/configure +++ b/configure @@ -14710,6 +14710,7 @@ fi { ac_cv_header_gdbm_ndbm_h=; unset ac_cv_header_gdbm_ndbm_h;} if test "$ac_cv_header_gdbm_slash_ndbm_h" = yes -o "$ac_cv_header_gdbm_dash_ndbm_h" = yes; then + { ac_cv_search_dbm_open=; unset ac_cv_search_dbm_open;} save_CFLAGS=$CFLAGS save_CPPFLAGS=$CPPFLAGS save_LDFLAGS=$LDFLAGS @@ -14769,7 +14770,9 @@ $as_echo "$ac_cv_search_dbm_open" >&6; } ac_res=$ac_cv_search_dbm_open if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" - + have_gdbm_compat=yes +else + have_gdbm_compat=no fi diff --git a/configure.ac b/configure.ac index 0ed5a24e8683..956334a865f7 100644 --- a/configure.ac +++ b/configure.ac @@ -4120,8 +4120,9 @@ AS_VAR_IF([ac_cv_header_gdbm_dash_ndbm_h], [yes], [ AS_UNSET([ac_cv_header_gdbm_ndbm_h]) if test "$ac_cv_header_gdbm_slash_ndbm_h" = yes -o "$ac_cv_header_gdbm_dash_ndbm_h" = yes; then + AS_UNSET([ac_cv_search_dbm_open]) WITH_SAVE_ENV([ - AC_SEARCH_LIBS([dbm_open], [gdbm_compat]) + AC_SEARCH_LIBS([dbm_open], [gdbm_compat], [have_gdbm_compat=yes], [have_gdbm_compat=no]) ]) fi From webhook-mailer at python.org Wed Jan 11 19:11:17 2023 From: webhook-mailer at python.org (zooba) Date: Thu, 12 Jan 2023 00:11:17 -0000 Subject: [Python-checkins] GH-100894: Updated the doc for `sys.winver` to clarify its usual contents (GH-100913) Message-ID: https://github.com/python/cpython/commit/d9dff4c8b5ab41c47af002ad7fb083c953e75f31 commit: d9dff4c8b5ab41c47af002ad7fb083c953e75f31 branch: main author: ram vikram singh committer: zooba date: 2023-01-12T00:11:12Z summary: GH-100894: Updated the doc for `sys.winver` to clarify its usual contents (GH-100913) files: M Doc/library/sys.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 428ce51165c9..28adca1f618d 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -1843,7 +1843,7 @@ always available. The version number used to form registry keys on Windows platforms. This is stored as string resource 1000 in the Python DLL. The value is normally the - first three characters of :const:`version`. It is provided in the :mod:`sys` + major and minor versions of the running Python interpreter. It is provided in the :mod:`sys` module for informational purposes; modifying this value has no effect on the registry keys used by Python. From webhook-mailer at python.org Wed Jan 11 19:20:29 2023 From: webhook-mailer at python.org (miss-islington) Date: Thu, 12 Jan 2023 00:20:29 -0000 Subject: [Python-checkins] GH-100894: Updated the doc for `sys.winver` to clarify its usual contents (GH-100913) Message-ID: https://github.com/python/cpython/commit/e44120a286f64cd8534418fd22af7a36e89d0548 commit: e44120a286f64cd8534418fd22af7a36e89d0548 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-11T16:20:24-08:00 summary: GH-100894: Updated the doc for `sys.winver` to clarify its usual contents (GH-100913) (cherry picked from commit d9dff4c8b5ab41c47af002ad7fb083c953e75f31) Co-authored-by: ram vikram singh files: M Doc/library/sys.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 8785278fa39a..c4658424c9cf 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -1750,7 +1750,7 @@ always available. The version number used to form registry keys on Windows platforms. This is stored as string resource 1000 in the Python DLL. The value is normally the - first three characters of :const:`version`. It is provided in the :mod:`sys` + major and minor versions of the running Python interpreter. It is provided in the :mod:`sys` module for informational purposes; modifying this value has no effect on the registry keys used by Python. From webhook-mailer at python.org Wed Jan 11 19:21:13 2023 From: webhook-mailer at python.org (miss-islington) Date: Thu, 12 Jan 2023 00:21:13 -0000 Subject: [Python-checkins] GH-100894: Updated the doc for `sys.winver` to clarify its usual contents (GH-100913) Message-ID: https://github.com/python/cpython/commit/e8097d49f64e47f0ad7568e7181e6e2c466b6964 commit: e8097d49f64e47f0ad7568e7181e6e2c466b6964 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-11T16:21:07-08:00 summary: GH-100894: Updated the doc for `sys.winver` to clarify its usual contents (GH-100913) (cherry picked from commit d9dff4c8b5ab41c47af002ad7fb083c953e75f31) Co-authored-by: ram vikram singh files: M Doc/library/sys.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 5da5ffa07012..b458040c876b 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -1805,7 +1805,7 @@ always available. The version number used to form registry keys on Windows platforms. This is stored as string resource 1000 in the Python DLL. The value is normally the - first three characters of :const:`version`. It is provided in the :mod:`sys` + major and minor versions of the running Python interpreter. It is provided in the :mod:`sys` module for informational purposes; modifying this value has no effect on the registry keys used by Python. From webhook-mailer at python.org Thu Jan 12 06:13:10 2023 From: webhook-mailer at python.org (AlexWaygood) Date: Thu, 12 Jan 2023 11:13:10 -0000 Subject: [Python-checkins] [3.10] gh-100871: Improve `copy` module tests (GH-100872) (#100975) Message-ID: https://github.com/python/cpython/commit/ebc1fd3795971b843ba871f4d9c8330c2bf6a57b commit: ebc1fd3795971b843ba871f4d9c8330c2bf6a57b branch: 3.10 author: Nikita Sobolev committer: AlexWaygood date: 2023-01-12T11:12:39Z summary: [3.10] gh-100871: Improve `copy` module tests (GH-100872) (#100975) (cherry picked from commit 729ab9b622957fef0e9b494af9a71ab02986c741) Co-authored-by: Nikita Sobolev files: M Lib/test/test_copy.py M Lib/test/test_slice.py diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py index f4d91c168689..7b1a927b4e56 100644 --- a/Lib/test/test_copy.py +++ b/Lib/test/test_copy.py @@ -51,6 +51,9 @@ def pickle_C(obj): self.assertRaises(TypeError, copy.copy, x) copyreg.pickle(C, pickle_C, C) y = copy.copy(x) + self.assertIsNot(x, y) + self.assertEqual(type(y), C) + self.assertEqual(y.foo, x.foo) def test_copy_reduce_ex(self): class C(object): @@ -313,6 +316,9 @@ def pickle_C(obj): self.assertRaises(TypeError, copy.deepcopy, x) copyreg.pickle(C, pickle_C, C) y = copy.deepcopy(x) + self.assertIsNot(x, y) + self.assertEqual(type(y), C) + self.assertEqual(y.foo, x.foo) def test_deepcopy_reduce_ex(self): class C(object): @@ -356,8 +362,8 @@ class NewStyle(object): pass def f(): pass - tests = [None, 42, 2**100, 3.14, True, False, 1j, - "hello", "hello\u1234", f.__code__, + tests = [None, ..., NotImplemented, 42, 2**100, 3.14, True, False, 1j, + b"bytes", "hello", "hello\u1234", f.__code__, NewStyle, range(10), Classic, max, property()] for x in tests: self.assertIs(copy.deepcopy(x), x) diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py index 4ae4142c60c8..584e5c7f17c2 100644 --- a/Lib/test/test_slice.py +++ b/Lib/test/test_slice.py @@ -5,6 +5,7 @@ import sys import unittest import weakref +import copy from pickle import loads, dumps from test import support @@ -242,6 +243,41 @@ def test_pickle(self): self.assertEqual(s.indices(15), t.indices(15)) self.assertNotEqual(id(s), id(t)) + def test_copy(self): + s = slice(1, 10) + c = copy.copy(s) + self.assertIs(s, c) + + s = slice(1, 10, 2) + c = copy.copy(s) + self.assertIs(s, c) + + # Corner case for mutable indices: + s = slice([1, 2], [3, 4], [5, 6]) + c = copy.copy(s) + self.assertIs(s, c) + self.assertIs(s.start, c.start) + self.assertIs(s.stop, c.stop) + self.assertIs(s.step, c.step) + + def test_deepcopy(self): + s = slice(1, 10) + c = copy.deepcopy(s) + self.assertEqual(s, c) + + s = slice(1, 10, 2) + c = copy.deepcopy(s) + self.assertEqual(s, c) + + # Corner case for mutable indices: + s = slice([1, 2], [3, 4], [5, 6]) + c = copy.deepcopy(s) + self.assertIsNot(s, c) + self.assertEqual(s, c) + self.assertIsNot(s.start, c.start) + self.assertIsNot(s.stop, c.stop) + self.assertIsNot(s.step, c.step) + def test_cycle(self): class myobj(): pass o = myobj() From webhook-mailer at python.org Thu Jan 12 06:14:33 2023 From: webhook-mailer at python.org (miss-islington) Date: Thu, 12 Jan 2023 11:14:33 -0000 Subject: [Python-checkins] [3.10] gh-100931: Test all `pickle` protocols in `test_slice` (GH-100932). (GH-100978) Message-ID: https://github.com/python/cpython/commit/47b1eb9784bd828a5b6541f37c3e1a4b4eb80729 commit: 47b1eb9784bd828a5b6541f37c3e1a4b4eb80729 branch: 3.10 author: Nikita Sobolev committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-12T03:14:27-08:00 summary: [3.10] gh-100931: Test all `pickle` protocols in `test_slice` (GH-100932). (GH-100978) (cherry picked from commit 8795ad1bd0d6ee031543fcaf5a86a60b37950714) Co-authored-by: Nikita Sobolev Automerge-Triggered-By: GH:AlexWaygood files: M Lib/test/test_slice.py diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py index 584e5c7f17c2..03fde3275e14 100644 --- a/Lib/test/test_slice.py +++ b/Lib/test/test_slice.py @@ -236,8 +236,10 @@ def __setitem__(self, i, k): self.assertEqual(tmp, [(slice(1, 2), 42)]) def test_pickle(self): + import pickle + s = slice(10, 20, 3) - for protocol in (0,1,2): + for protocol in range(pickle.HIGHEST_PROTOCOL + 1): t = loads(dumps(s, protocol)) self.assertEqual(s, t) self.assertEqual(s.indices(15), t.indices(15)) From webhook-mailer at python.org Thu Jan 12 06:15:06 2023 From: webhook-mailer at python.org (AlexWaygood) Date: Thu, 12 Jan 2023 11:15:06 -0000 Subject: [Python-checkins] [3.11] gh-100871: Improve `copy` module tests (GH-100872) (#100976) Message-ID: https://github.com/python/cpython/commit/db2643737d21a72e2dcba87e6311c13953943379 commit: db2643737d21a72e2dcba87e6311c13953943379 branch: 3.11 author: Nikita Sobolev committer: AlexWaygood date: 2023-01-12T11:15:00Z summary: [3.11] gh-100871: Improve `copy` module tests (GH-100872) (#100976) (cherry picked from commit 729ab9b622957fef0e9b494af9a71ab02986c741) Co-authored-by: Nikita Sobolev files: M Lib/test/test_copy.py M Lib/test/test_slice.py diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py index f4d91c168689..7b1a927b4e56 100644 --- a/Lib/test/test_copy.py +++ b/Lib/test/test_copy.py @@ -51,6 +51,9 @@ def pickle_C(obj): self.assertRaises(TypeError, copy.copy, x) copyreg.pickle(C, pickle_C, C) y = copy.copy(x) + self.assertIsNot(x, y) + self.assertEqual(type(y), C) + self.assertEqual(y.foo, x.foo) def test_copy_reduce_ex(self): class C(object): @@ -313,6 +316,9 @@ def pickle_C(obj): self.assertRaises(TypeError, copy.deepcopy, x) copyreg.pickle(C, pickle_C, C) y = copy.deepcopy(x) + self.assertIsNot(x, y) + self.assertEqual(type(y), C) + self.assertEqual(y.foo, x.foo) def test_deepcopy_reduce_ex(self): class C(object): @@ -356,8 +362,8 @@ class NewStyle(object): pass def f(): pass - tests = [None, 42, 2**100, 3.14, True, False, 1j, - "hello", "hello\u1234", f.__code__, + tests = [None, ..., NotImplemented, 42, 2**100, 3.14, True, False, 1j, + b"bytes", "hello", "hello\u1234", f.__code__, NewStyle, range(10), Classic, max, property()] for x in tests: self.assertIs(copy.deepcopy(x), x) diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py index 4ae4142c60c8..584e5c7f17c2 100644 --- a/Lib/test/test_slice.py +++ b/Lib/test/test_slice.py @@ -5,6 +5,7 @@ import sys import unittest import weakref +import copy from pickle import loads, dumps from test import support @@ -242,6 +243,41 @@ def test_pickle(self): self.assertEqual(s.indices(15), t.indices(15)) self.assertNotEqual(id(s), id(t)) + def test_copy(self): + s = slice(1, 10) + c = copy.copy(s) + self.assertIs(s, c) + + s = slice(1, 10, 2) + c = copy.copy(s) + self.assertIs(s, c) + + # Corner case for mutable indices: + s = slice([1, 2], [3, 4], [5, 6]) + c = copy.copy(s) + self.assertIs(s, c) + self.assertIs(s.start, c.start) + self.assertIs(s.stop, c.stop) + self.assertIs(s.step, c.step) + + def test_deepcopy(self): + s = slice(1, 10) + c = copy.deepcopy(s) + self.assertEqual(s, c) + + s = slice(1, 10, 2) + c = copy.deepcopy(s) + self.assertEqual(s, c) + + # Corner case for mutable indices: + s = slice([1, 2], [3, 4], [5, 6]) + c = copy.deepcopy(s) + self.assertIsNot(s, c) + self.assertEqual(s, c) + self.assertIsNot(s.start, c.start) + self.assertIsNot(s.stop, c.stop) + self.assertIsNot(s.step, c.step) + def test_cycle(self): class myobj(): pass o = myobj() From webhook-mailer at python.org Thu Jan 12 06:24:29 2023 From: webhook-mailer at python.org (miss-islington) Date: Thu, 12 Jan 2023 11:24:29 -0000 Subject: [Python-checkins] [3.11] gh-100931: Test all `pickle` protocols in `test_slice` (GH-100932). (GH-100979) Message-ID: https://github.com/python/cpython/commit/cb6ff54b01118e01c26758d2f57cf6da60277bd4 commit: cb6ff54b01118e01c26758d2f57cf6da60277bd4 branch: 3.11 author: Nikita Sobolev committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-12T03:24:09-08:00 summary: [3.11] gh-100931: Test all `pickle` protocols in `test_slice` (GH-100932). (GH-100979) (cherry picked from commit 8795ad1bd0d6ee031543fcaf5a86a60b37950714) Co-authored-by: Nikita Sobolev Automerge-Triggered-By: GH:AlexWaygood files: M Lib/test/test_slice.py diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py index 584e5c7f17c2..03fde3275e14 100644 --- a/Lib/test/test_slice.py +++ b/Lib/test/test_slice.py @@ -236,8 +236,10 @@ def __setitem__(self, i, k): self.assertEqual(tmp, [(slice(1, 2), 42)]) def test_pickle(self): + import pickle + s = slice(10, 20, 3) - for protocol in (0,1,2): + for protocol in range(pickle.HIGHEST_PROTOCOL + 1): t = loads(dumps(s, protocol)) self.assertEqual(s, t) self.assertEqual(s.indices(15), t.indices(15)) From webhook-mailer at python.org Thu Jan 12 10:40:36 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Thu, 12 Jan 2023 15:40:36 -0000 Subject: [Python-checkins] Fix typo in `enum` module documentation (#100992) Message-ID: https://github.com/python/cpython/commit/2161bbf243983c625e8f24cdf43b757f2a21463b commit: 2161bbf243983c625e8f24cdf43b757f2a21463b branch: main author: Noam Cohen committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-12T21:10:29+05:30 summary: Fix typo in `enum` module documentation (#100992) files: M Doc/library/enum.rst diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 47d9e39305d0..9d1a297a903a 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -827,7 +827,7 @@ Utilities and Decorators ``_generate_next_value_`` can be overridden to customize the values used by *auto*. - .. note:: in 3.13 the default ``"generate_next_value_`` will always return + .. note:: in 3.13 the default ``_generate_next_value_`` will always return the highest member value incremented by 1, and will fail if any member is an incompatible type. From webhook-mailer at python.org Thu Jan 12 10:51:18 2023 From: webhook-mailer at python.org (miss-islington) Date: Thu, 12 Jan 2023 15:51:18 -0000 Subject: [Python-checkins] Fix typo in `enum` module documentation (GH-100992) Message-ID: https://github.com/python/cpython/commit/75717645bcf0ce42ccc56154f59ce4604812c50c commit: 75717645bcf0ce42ccc56154f59ce4604812c50c branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-12T07:51:11-08:00 summary: Fix typo in `enum` module documentation (GH-100992) (cherry picked from commit 2161bbf243983c625e8f24cdf43b757f2a21463b) Co-authored-by: Noam Cohen files: M Doc/library/enum.rst diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 26cee1a13acf..0768914c2a9e 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -819,7 +819,7 @@ Utilities and Decorators ``_generate_next_value_`` can be overridden to customize the values used by *auto*. - .. note:: in 3.13 the default ``"generate_next_value_`` will always return + .. note:: in 3.13 the default ``_generate_next_value_`` will always return the highest member value incremented by 1, and will fail if any member is an incompatible type. From webhook-mailer at python.org Thu Jan 12 13:04:14 2023 From: webhook-mailer at python.org (nascheme) Date: Thu, 12 Jan 2023 18:04:14 -0000 Subject: [Python-checkins] GH-81381: Add longer comment _PyType_AllocNoTrack() (GH-100954) Message-ID: https://github.com/python/cpython/commit/c549fcccbbcf6cbf7db3da928a09e81e2c8bc7f3 commit: c549fcccbbcf6cbf7db3da928a09e81e2c8bc7f3 branch: main author: Neil Schemenauer committer: nascheme date: 2023-01-12T10:03:50-08:00 summary: GH-81381: Add longer comment _PyType_AllocNoTrack() (GH-100954) The details on the "nitems+1" expression is a bit subtle so add a longer comment about it. files: M Objects/typeobject.c diff --git a/Objects/typeobject.c b/Objects/typeobject.c index e4da5b24006d..59e0bf2995ba 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1289,8 +1289,13 @@ PyObject * _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems) { PyObject *obj; + /* The +1 on nitems is needed for most types but not all. We could save a + * bit of space by allocating one less item in certain cases, depending on + * the type. However, given the extra complexity (e.g. an additional type + * flag to indicate when that is safe) it does not seem worth the memory + * savings. An example type that doesn't need the +1 is a subclass of + * tuple. See GH-100659 and GH-81381. */ const size_t size = _PyObject_VAR_SIZE(type, nitems+1); - /* note that we need to add one, for the sentinel */ const size_t presize = _PyType_PreHeaderSize(type); char *alloc = PyObject_Malloc(size + presize); From webhook-mailer at python.org Thu Jan 12 13:24:17 2023 From: webhook-mailer at python.org (markshannon) Date: Thu, 12 Jan 2023 18:24:17 -0000 Subject: [Python-checkins] gh-96461: Improve docs for bytecode instructions (GH-96462) Message-ID: https://github.com/python/cpython/commit/eecd422d1ba849f0724962666ba840581217812b commit: eecd422d1ba849f0724962666ba840581217812b branch: main author: Matthieu Dartiailh committer: markshannon date: 2023-01-12T18:23:50Z summary: gh-96461: Improve docs for bytecode instructions (GH-96462) * Clarify the meaning of the oparg for CACHE and COPY opcode in dis doc * Use STACK to describe stack operation in analogy with a Python list * Remove (delta) from BEFORE_WITH since BEFORE_WITH does not take an argument * Fix the description of the stack impact of multiple opcodes files: M Doc/library/dis.rst diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 334b9df4fc1f..8e586e6c5dda 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -402,6 +402,10 @@ The Python compiler currently generates the following bytecode instructions. **General instructions** +In the following, We will refer to the interpreter stack as STACK and describe +operations on it as if it was a Python list. The top of the stack corresponds to +``STACK[-1]`` in this language. + .. opcode:: NOP Do nothing code. Used as a placeholder by the bytecode optimizer, and to @@ -410,7 +414,9 @@ The Python compiler currently generates the following bytecode instructions. .. opcode:: POP_TOP - Removes the top-of-stack (TOS) item. + Removes the top-of-stack item.:: + + STACK.pop() .. opcode:: END_FOR @@ -424,15 +430,20 @@ The Python compiler currently generates the following bytecode instructions. .. opcode:: COPY (i) - Push the *i*-th item to the top of the stack. The item is not removed from its - original location. + Push the i-th item to the top of the stack without removing it from its original + location.:: + + assert i > 0 + STACK.append(STACK[-i]) .. versionadded:: 3.11 .. opcode:: SWAP (i) - Swap TOS with the item at position *i*. + Swap the top of the stack with the i-th element.:: + + STACK[-i], STACK[-1] = stack[-1], STACK[-i] .. versionadded:: 3.11 @@ -463,79 +474,102 @@ result back on the stack. .. opcode:: UNARY_NEGATIVE - Implements ``TOS = -TOS``. + Implements ``STACK[-1] = -STACK[-1]``. .. opcode:: UNARY_NOT - Implements ``TOS = not TOS``. + Implements ``STACK[-1] = not STACK[-1]``. .. opcode:: UNARY_INVERT - Implements ``TOS = ~TOS``. + Implements ``STACK[-1] = ~STACK[-1]``. .. opcode:: GET_ITER - Implements ``TOS = iter(TOS)``. + Implements ``STACK[-1] = iter(STACK[-1])``. .. opcode:: GET_YIELD_FROM_ITER - If ``TOS`` is a :term:`generator iterator` or :term:`coroutine` object - it is left as is. Otherwise, implements ``TOS = iter(TOS)``. + If ``STACK[-1]`` is a :term:`generator iterator` or :term:`coroutine` object + it is left as is. Otherwise, implements ``STACK[-1] = iter(STACK[-1])``. .. versionadded:: 3.5 **Binary and in-place operations** -In the following, TOS is the top-of-stack. -TOS1, TOS2, TOS3 are the second, third and fourth items on the stack, respectively. - -Binary operations remove the top two items from the stack (TOS and TOS1). -They perform the operation, then put the result back on the stack. +Binary operations remove the top two items from the stack (``STACK[-1]`` and +``STACK[-2]``). They perform the operation, then put the result back on the stack. In-place operations are like binary operations, but the operation is done in-place -when TOS1 supports it, and the resulting TOS may be (but does not have to be) -the original TOS1. +when ``STACK[-2]`` supports it, and the resulting ``STACK[-1]`` may be (but does +not have to be) the original ``STACK[-2]``. .. opcode:: BINARY_OP (op) Implements the binary and in-place operators (depending on the value of - *op*). - ``TOS = TOS1 op TOS``. + *op*).:: + + rhs = STACK.pop() + lhs = STACK.pop() + STACK.append(lhs op rhs) .. versionadded:: 3.11 .. opcode:: BINARY_SUBSCR - Implements ``TOS = TOS1[TOS]``. + Implements:: + + key = STACK.pop() + container = STACK.pop() + STACK.append(container[index]) .. opcode:: STORE_SUBSCR - Implements ``TOS1[TOS] = TOS2``. + Implements:: + + key = STACK.pop() + container = STACK.pop() + value = STACK.pop() + container[key] = value .. opcode:: DELETE_SUBSCR - Implements ``del TOS1[TOS]``. + Implements:: + key = STACK.pop() + container = STACK.pop() + del container[key] .. opcode:: BINARY_SLICE - Implements ``TOS = TOS2[TOS1:TOS]``. + Implements:: + + end = STACK.pop() + start = STACK.pop() + container = STACK.pop() + STACK.append(container[start:end]) .. versionadded:: 3.12 .. opcode:: STORE_SLICE - Implements ``TOS2[TOS1:TOS] = TOS3``. + Implements:: + + end = STACK.pop() + start = STACK.pop() + container = STACK.pop() + values = STACK.pop() + container[start:end] = value .. versionadded:: 3.12 @@ -544,7 +578,7 @@ the original TOS1. .. opcode:: GET_AWAITABLE (where) - Implements ``TOS = get_awaitable(TOS)``, where ``get_awaitable(o)`` + Implements ``STACK[-1] = get_awaitable(STACK[-1])``, where ``get_awaitable(o)`` returns ``o`` if ``o`` is a coroutine object or a generator object with the CO_ITERABLE_COROUTINE flag, or resolves ``o.__await__``. @@ -563,7 +597,7 @@ the original TOS1. .. opcode:: GET_AITER - Implements ``TOS = TOS.__aiter__()``. + Implements ``STACK[-1] = STACK[-1].__aiter__()``. .. versionadded:: 3.5 .. versionchanged:: 3.7 @@ -573,8 +607,8 @@ the original TOS1. .. opcode:: GET_ANEXT - Pushes ``get_awaitable(TOS.__anext__())`` to the stack. See - ``GET_AWAITABLE`` for details about ``get_awaitable``. + Implement ``STACK.append(get_awaitable(STACK[-1].__anext__()))`` to the stack. + See ``GET_AWAITABLE`` for details about ``get_awaitable``. .. versionadded:: 3.5 @@ -582,7 +616,7 @@ the original TOS1. .. opcode:: END_ASYNC_FOR Terminates an :keyword:`async for` loop. Handles an exception raised - when awaiting a next item. If TOS is :exc:`StopAsyncIteration` pop 3 + when awaiting a next item. If ``STACK[-1]`` is :exc:`StopAsyncIteration` pop 3 values from the stack and restore the exception state using the second of them. Otherwise re-raise the exception using the value from the stack. An exception handler block is removed from the block stack. @@ -596,17 +630,19 @@ the original TOS1. .. opcode:: CLEANUP_THROW Handles an exception raised during a :meth:`~generator.throw` or - :meth:`~generator.close` call through the current frame. If TOS is an + :meth:`~generator.close` call through the current frame. If ``STACK[-1]`` is an instance of :exc:`StopIteration`, pop three values from the stack and push - its ``value`` member. Otherwise, re-raise TOS. + its ``value`` member. Otherwise, re-raise ``STACK[-1]``. .. versionadded:: 3.12 .. opcode:: BEFORE_ASYNC_WITH - Resolves ``__aenter__`` and ``__aexit__`` from the object on top of the - stack. Pushes ``__aexit__`` and result of ``__aenter__()`` to the stack. + Resolves ``__aenter__`` and ``__aexit__`` from ``STACK[-1]``. + Pushes ``__aexit__`` and result of ``__aenter__()`` to the stack:: + + STACK.extend((__aexit__, __aenter__()) .. versionadded:: 3.5 @@ -616,22 +652,37 @@ the original TOS1. .. opcode:: SET_ADD (i) - Calls ``set.add(TOS1[-i], TOS)``. Used to implement set comprehensions. + Implements:: + item = STACK.pop() + set.add(STACK[-i], item) + + Used to implement set comprehensions. .. opcode:: LIST_APPEND (i) - Calls ``list.append(TOS1[-i], TOS)``. Used to implement list comprehensions. + Implements:: + + item = STACK.pop() + list.append(STACK[-i], item) + + Used to implement list comprehensions. .. opcode:: MAP_ADD (i) - Calls ``dict.__setitem__(TOS1[-i], TOS1, TOS)``. Used to implement dict - comprehensions. + Implements:: + + value = STACK.pop() + key = STACK.pop() + dict.__setitem__(STACK[-i], key, value) + + Used to implement dict comprehensions. .. versionadded:: 3.1 .. versionchanged:: 3.8 - Map value is TOS and map key is TOS1. Before, those were reversed. + Map value is ``STACK[-1]`` and map key is ``STACK[-2]``. Before, those + were reversed. For all of the :opcode:`SET_ADD`, :opcode:`LIST_APPEND` and :opcode:`MAP_ADD` instructions, while the added value or key/value pair is popped off, the @@ -641,12 +692,12 @@ iterations of the loop. .. opcode:: RETURN_VALUE - Returns with TOS to the caller of the function. + Returns with ``STACK[-1]`` to the caller of the function. .. opcode:: YIELD_VALUE - Pops TOS and yields it from a :term:`generator`. + Yields ``STACK.pop()`` from a :term:`generator`. .. versionchanged:: 3.11 oparg set to be the stack depth, for efficient handling on frames. @@ -690,15 +741,16 @@ iterations of the loop. .. opcode:: CHECK_EXC_MATCH - Performs exception matching for ``except``. Tests whether the TOS1 is an exception - matching TOS. Pops TOS and pushes the boolean result of the test. + Performs exception matching for ``except``. Tests whether the ``STACK[-2]`` + is an exception matching ``STACK[-1]``. Pops STACK[-1] and pushes the boolean + result of the test. .. versionadded:: 3.11 .. opcode:: CHECK_EG_MATCH - Performs exception matching for ``except*``. Applies ``split(TOS)`` on - the exception group representing TOS1. + Performs exception matching for ``except*``. Applies ``split(STACK[-1])`` on + the exception group representing ``STACK[-2]``. In case of a match, pops two items from the stack and pushes the non-matching subgroup (``None`` in case of full match) followed by the @@ -709,9 +761,9 @@ iterations of the loop. .. opcode:: PREP_RERAISE_STAR - Combines the raised and reraised exceptions list from TOS, into an exception - group to propagate from a try-except* block. Uses the original exception - group from TOS1 to reconstruct the structure of reraised exceptions. Pops + Combines the raised and reraised exceptions list from ``STACK[-1]``, into an + exception group to propagate from a try-except* block. Uses the original exception + group from ``STACK[-2]`` to reconstruct the structure of reraised exceptions. Pops two items from the stack and pushes the exception to reraise or ``None`` if there isn't one. @@ -745,7 +797,7 @@ iterations of the loop. to construct a class. -.. opcode:: BEFORE_WITH (delta) +.. opcode:: BEFORE_WITH This opcode performs several operations before a with block starts. First, it loads :meth:`~object.__exit__` from the context manager and pushes it onto @@ -758,24 +810,24 @@ iterations of the loop. .. opcode:: GET_LEN - Push ``len(TOS)`` onto the stack. + Perform ``STACK.append(len(STACK[-1]))``. .. versionadded:: 3.10 .. opcode:: MATCH_MAPPING - If TOS is an instance of :class:`collections.abc.Mapping` (or, more technically: if - it has the :const:`Py_TPFLAGS_MAPPING` flag set in its - :c:member:`~PyTypeObject.tp_flags`), push ``True`` onto the stack. Otherwise, push - ``False``. + If ``STACK[-1]`` is an instance of :class:`collections.abc.Mapping` (or, more + technically: if it has the :const:`Py_TPFLAGS_MAPPING` flag set in its + :c:member:`~PyTypeObject.tp_flags`), push ``True`` onto the stack. Otherwise, + push ``False``. .. versionadded:: 3.10 .. opcode:: MATCH_SEQUENCE - If TOS is an instance of :class:`collections.abc.Sequence` and is *not* an instance + If ``STACK[-1]`` is an instance of :class:`collections.abc.Sequence` and is *not* an instance of :class:`str`/:class:`bytes`/:class:`bytearray` (or, more technically: if it has the :const:`Py_TPFLAGS_SEQUENCE` flag set in its :c:member:`~PyTypeObject.tp_flags`), push ``True`` onto the stack. Otherwise, push ``False``. @@ -785,9 +837,9 @@ iterations of the loop. .. opcode:: MATCH_KEYS - TOS is a tuple of mapping keys, and TOS1 is the match subject. If TOS1 - contains all of the keys in TOS, push a :class:`tuple` containing the - corresponding values. Otherwise, push ``None``. + ``STACK[-1]`` is a tuple of mapping keys, and ``STACK[-2]`` is the match subject. + If ``STACK[-2]`` contains all of the keys in ``STACK[-1]``, push a :class:`tuple` + containing the corresponding values. Otherwise, push ``None``. .. versionadded:: 3.10 @@ -798,7 +850,7 @@ iterations of the loop. .. opcode:: STORE_NAME (namei) - Implements ``name = TOS``. *namei* is the index of *name* in the attribute + Implements ``name = STACK.pop()``. *namei* is the index of *name* in the attribute :attr:`co_names` of the code object. The compiler tries to use :opcode:`STORE_FAST` or :opcode:`STORE_GLOBAL` if possible. @@ -811,31 +863,49 @@ iterations of the loop. .. opcode:: UNPACK_SEQUENCE (count) - Unpacks TOS into *count* individual values, which are put onto the stack - right-to-left. + Unpacks ``STACK[-1]`` into *count* individual values, which are put onto the stack + right-to-left.:: + + STACK.extend(STACK.pop()[:count:-1]) .. opcode:: UNPACK_EX (counts) - Implements assignment with a starred target: Unpacks an iterable in TOS into - individual values, where the total number of values can be smaller than the + Implements assignment with a starred target: Unpacks an iterable in ``STACK[-1]`` + into individual values, where the total number of values can be smaller than the number of items in the iterable: one of the new values will be a list of all leftover items. - The low byte of *counts* is the number of values before the list value, the - high byte of *counts* the number of values after it. The resulting values - are put onto the stack right-to-left. + The number of values before and after the list value is limited to 255. + + The number of values before the list value is encoded in the argument of the + opcode. The number of values after the list if any is encoded using an + ``EXTENDED_ARG``. As a consequence, the argument can be seen as a two bytes values + where the low byte of *counts* is the number of values before the list value, the + high byte of *counts* the number of values after it. + + The extracted values are put onto the stack right-to-left, i.e. ``a, *b, c = d`` + will be stored after execution as ``STACK.extend((a, b, c))``. .. opcode:: STORE_ATTR (namei) - Implements ``TOS.name = TOS1``, where *namei* is the index of name in - :attr:`co_names`. + Implements:: + + obj = STACK.pop() + value = STACK.pop() + obj.name = value + where *namei* is the index of name in :attr:`co_names`. .. opcode:: DELETE_ATTR (namei) - Implements ``del TOS.name``, using *namei* as index into :attr:`co_names`. + Implements:: + + obj = STACK.pop() + del obj.name + + where *namei* is the index of name into :attr:`co_names`. .. opcode:: STORE_GLOBAL (namei) @@ -861,7 +931,11 @@ iterations of the loop. .. opcode:: BUILD_TUPLE (count) Creates a tuple consuming *count* items from the stack, and pushes the - resulting tuple onto the stack. + resulting tuple onto the stack.:: + + assert count > 0 + STACK, values = STACK[:-count], STACK[-count:] + STACK.append(tuple(values)) .. opcode:: BUILD_LIST (count) @@ -878,7 +952,7 @@ iterations of the loop. Pushes a new dictionary object onto the stack. Pops ``2 * count`` items so that the dictionary holds *count* entries: - ``{..., TOS3: TOS2, TOS1: TOS}``. + ``{..., STACK[-4]: STACK[-3], STACK[-2]: STACK[-1]}``. .. versionchanged:: 3.5 The dictionary is created from stack items instead of creating an @@ -889,7 +963,7 @@ iterations of the loop. The version of :opcode:`BUILD_MAP` specialized for constant keys. Pops the top element on the stack which contains a tuple of keys, then starting from - ``TOS1``, pops *count* values to form values in the built dictionary. + ``STACK[-2]``, pops *count* values to form values in the built dictionary. .. versionadded:: 3.6 @@ -904,21 +978,36 @@ iterations of the loop. .. opcode:: LIST_EXTEND (i) - Calls ``list.extend(TOS1[-i], TOS)``. Used to build lists. + Implements:: + + seq = STACK.pop() + list.extend(STACK[-i], seq) + + Used to build lists. .. versionadded:: 3.9 .. opcode:: SET_UPDATE (i) - Calls ``set.update(TOS1[-i], TOS)``. Used to build sets. + Implements:: + + seq = STACK.pop() + set.update(STACK[-i], seq) + + Used to build sets. .. versionadded:: 3.9 .. opcode:: DICT_UPDATE (i) - Calls ``dict.update(TOS1[-i], TOS)``. Used to build dicts. + Implements:: + + map = STACK.pop() + dict.update(STACK[-i], map) + + Used to build dicts. .. versionadded:: 3.9 @@ -932,16 +1021,16 @@ iterations of the loop. .. opcode:: LOAD_ATTR (namei) - If the low bit of ``namei`` is not set, this replaces TOS with - ``getattr(TOS, co_names[namei>>1])``. + If the low bit of ``namei`` is not set, this replaces ``STACK[-1]`` with + ``getattr(STACK[-1], co_names[namei>>1])``. If the low bit of ``namei`` is set, this will attempt to load a method named - ``co_names[namei>>1]`` from the TOS object. TOS is popped. - This bytecode distinguishes two cases: if TOS has a method with the correct - name, the bytecode pushes the unbound method and TOS. TOS will be used as - the first argument (``self``) by :opcode:`CALL` when calling the - unbound method. Otherwise, ``NULL`` and the object return by the attribute - lookup are pushed. + ``co_names[namei>>1]`` from the ``STACK[-1]`` object. ``STACK[-1]`` is popped. + This bytecode distinguishes two cases: if ``STACK[-1]`` has a method with the + correct name, the bytecode pushes the unbound method and ``STACK[-1]``. + ``STACK[-1]`` will be used as the first argument (``self``) by :opcode:`CALL` + when calling the unbound method. Otherwise, ``NULL`` and the object return by + the attribute lookup are pushed. .. versionchanged:: 3.12 If the low bit of ``namei`` is set, then a ``NULL`` or ``self`` is @@ -970,17 +1059,16 @@ iterations of the loop. .. opcode:: IMPORT_NAME (namei) - Imports the module ``co_names[namei]``. TOS and TOS1 are popped and provide - the *fromlist* and *level* arguments of :func:`__import__`. The module - object is pushed onto the stack. The current namespace is not affected: for - a proper import statement, a subsequent :opcode:`STORE_FAST` instruction + Imports the module ``co_names[namei]``. ``STACK[-1]`` and ``STACK[-2]`` are + popped and provide the *fromlist* and *level* arguments of :func:`__import__`. + The module object is pushed onto the stack. The current namespace is not affected: for a proper import statement, a subsequent :opcode:`STORE_FAST` instruction modifies the namespace. .. opcode:: IMPORT_FROM (namei) - Loads the attribute ``co_names[namei]`` from the module found in TOS. The - resulting object is pushed onto the stack, to be subsequently stored by a + Loads the attribute ``co_names[namei]`` from the module found in ``STACK[-1]``. + The resulting object is pushed onto the stack, to be subsequently stored by a :opcode:`STORE_FAST` instruction. @@ -1005,7 +1093,8 @@ iterations of the loop. .. opcode:: POP_JUMP_IF_TRUE (delta) - If TOS is true, increments the bytecode counter by *delta*. TOS is popped. + If ``STACK[-1]`` is true, increments the bytecode counter by *delta*. + ``STACK[-1]`` is popped. .. versionchanged:: 3.11 The oparg is now a relative delta rather than an absolute target. @@ -1017,7 +1106,8 @@ iterations of the loop. .. opcode:: POP_JUMP_IF_FALSE (delta) - If TOS is false, increments the bytecode counter by *delta*. TOS is popped. + If ``STACK[-1]`` is false, increments the bytecode counter by *delta*. + ``STACK[-1]`` is popped. .. versionchanged:: 3.11 The oparg is now a relative delta rather than an absolute target. @@ -1029,7 +1119,11 @@ iterations of the loop. .. opcode:: POP_JUMP_IF_NOT_NONE (delta) - If TOS is not ``None``, increments the bytecode counter by *delta*. TOS is popped. + If ``STACK[-1]`` is not ``None``, increments the bytecode counter by *delta*. + ``STACK[-1]`` is popped. + + This opcode is a pseudo-instruction, replaced in final bytecode by + the directed versions (forward/backward). .. versionadded:: 3.11 @@ -1039,7 +1133,11 @@ iterations of the loop. .. opcode:: POP_JUMP_IF_NONE (delta) - If TOS is ``None``, increments the bytecode counter by *delta*. TOS is popped. + If ``STACK[-1]`` is ``None``, increments the bytecode counter by *delta*. + ``STACK[-1]`` is popped. + + This opcode is a pseudo-instruction, replaced in final bytecode by + the directed versions (forward/backward). .. versionadded:: 3.11 @@ -1049,8 +1147,9 @@ iterations of the loop. .. opcode:: JUMP_IF_TRUE_OR_POP (delta) - If TOS is true, increments the bytecode counter by *delta* and leaves TOS on the - stack. Otherwise (TOS is false), TOS is popped. + If ``STACK[-1]`` is true, increments the bytecode counter by *delta* and leaves + ``STACK[-1]`` on the stack. Otherwise (``STACK[-1]`` is false), ``STACK[-1]`` + is popped. .. versionadded:: 3.1 @@ -1059,8 +1158,9 @@ iterations of the loop. .. opcode:: JUMP_IF_FALSE_OR_POP (delta) - If TOS is false, increments the bytecode counter by *delta* and leaves TOS on the - stack. Otherwise (TOS is true), TOS is popped. + If ``STACK[-1]`` is false, increments the bytecode counter by *delta* and leaves + ``STACK[-1]`` on the stack. Otherwise (``STACK[-1]`` is true), ``STACK[-1]`` is + popped. .. versionadded:: 3.1 @@ -1070,10 +1170,10 @@ iterations of the loop. .. opcode:: FOR_ITER (delta) - TOS is an :term:`iterator`. Call its :meth:`~iterator.__next__` method. If - this yields a new value, push it on the stack (leaving the iterator below - it). If the iterator indicates it is exhausted then the byte - code counter is incremented by *delta*. + ``STACK[-1]`` is an :term:`iterator`. Call its :meth:`~iterator.__next__` method. + If this yields a new value, push it on the stack (leaving the iterator below + it). If the iterator indicates it is exhausted then the byte code counter is + incremented by *delta*. .. versionchanged:: 3.12 Up until 3.11 the iterator was popped when it was exhausted. @@ -1104,7 +1204,7 @@ iterations of the loop. .. opcode:: STORE_FAST (var_num) - Stores TOS into the local ``co_varnames[var_num]``. + Stores ``STACK.pop()`` into the local ``co_varnames[var_num]``. .. opcode:: DELETE_FAST (var_num) @@ -1155,7 +1255,7 @@ iterations of the loop. .. opcode:: STORE_DEREF (i) - Stores TOS into the cell contained in slot ``i`` of the "fast locals" + Stores ``STACK.pop()`` into the cell contained in slot ``i`` of the "fast locals" storage. .. versionchanged:: 3.11 @@ -1188,9 +1288,9 @@ iterations of the loop. depending on the value of *argc*: * 0: ``raise`` (re-raise previous exception) - * 1: ``raise TOS`` (raise exception instance or type at ``TOS``) - * 2: ``raise TOS1 from TOS`` (raise exception instance or type at ``TOS1`` - with ``__cause__`` set to ``TOS``) + * 1: ``raise STACK[-1]`` (raise exception instance or type at ``STACK[-1]``) + * 2: ``raise STACK[-2] from STACK[-1]`` (raise exception instance or type at + ``STACK[-2]`` with ``__cause__`` set to ``STACK[-1]``) .. opcode:: CALL (argc) @@ -1246,7 +1346,7 @@ iterations of the loop. .. versionadded:: 3.11 -.. opcode:: KW_NAMES (i) +.. opcode:: KW_NAMES (consti) Prefixes :opcode:`CALL`. Stores a reference to ``co_consts[consti]`` into an internal variable @@ -1265,8 +1365,8 @@ iterations of the loop. * ``0x02`` a dictionary of keyword-only parameters' default values * ``0x04`` a tuple of strings containing parameters' annotations * ``0x08`` a tuple containing cells for free variables, making a closure - * the code associated with the function (at TOS1) - * the :term:`qualified name` of the function (at TOS) + * the code associated with the function (at ``STACK[-2]``) + * the :term:`qualified name` of the function (at ``STACK[-1]``) .. versionchanged:: 3.10 Flag value ``0x04`` is a tuple of strings instead of dictionary @@ -1275,9 +1375,20 @@ iterations of the loop. .. index:: builtin: slice - Pushes a slice object on the stack. *argc* must be 2 or 3. If it is 2, - ``slice(TOS1, TOS)`` is pushed; if it is 3, ``slice(TOS2, TOS1, TOS)`` is - pushed. See the :func:`slice` built-in function for more information. + Pushes a slice object on the stack. *argc* must be 2 or 3. If it is 2, implements:: + + end = STACK.pop() + start = STACK.pop() + STACK.append(slice(start, stop)) + + if it is 3, implements:: + + step = STACK.pop() + end = STACK.pop() + start = STACK.pop() + STACK.append(slice(start, end, step)) + + See the :func:`slice` built-in function for more information. .. opcode:: EXTENDED_ARG (ext) @@ -1312,13 +1423,14 @@ iterations of the loop. .. opcode:: MATCH_CLASS (count) - TOS is a tuple of keyword attribute names, TOS1 is the class being matched - against, and TOS2 is the match subject. *count* is the number of positional - sub-patterns. + ``STACK[-1]`` is a tuple of keyword attribute names, ``STACK[-2]`` is the class + being matched against, and ``STACK[-3]`` is the match subject. *count* is the + number of positional sub-patterns. - Pop TOS, TOS1, and TOS2. If TOS2 is an instance of TOS1 and has the - positional and keyword attributes required by *count* and TOS, push a tuple - of extracted attributes. Otherwise, push ``None``. + Pop ``STACK[-1]``, ``STACK[-2]``, and ``STACK[-3]``. If ``STACK[-3]`` is an + instance of ``STACK[-2]`` and has the positional and keyword attributes + required by *count* and ``STACK[-1]``, push a tuple of extracted attributes. + Otherwise, push ``None``. .. versionadded:: 3.10 @@ -1333,7 +1445,8 @@ iterations of the loop. The ``where`` operand marks where the ``RESUME`` occurs: - * ``0`` The start of a function + * ``0`` The start of a function, which is neither a generator, coroutine + nor an async generator * ``1`` After a ``yield`` expression * ``2`` After a ``yield from`` expression * ``3`` After an ``await`` expression @@ -1344,6 +1457,7 @@ iterations of the loop. .. opcode:: RETURN_GENERATOR Create a generator, coroutine, or async generator from the current frame. + Used as first opcode of in code object for the above mentioned callables. Clear the current frame and return the newly created generator. .. versionadded:: 3.11 @@ -1351,8 +1465,8 @@ iterations of the loop. .. opcode:: SEND (delta) - Equivalent to ``TOS = TOS1.send(TOS)``. Used in ``yield from`` and ``await`` - statements. + Equivalent to ``STACK[-1] = STACK[-2].send(STACK[-1])``. Used in ``yield from`` + and ``await`` statements. If the call raises :exc:`StopIteration`, pop both items, push the exception's ``value`` attribute, and increment the bytecode counter by @@ -1382,9 +1496,8 @@ iterations of the loop. .. opcode:: CALL_INTRINSIC_1 - Calls an intrinsic function with one argument. Passes the TOS as the argument - and sets TOS to the result. Used to implement functionality that is necessary - but not performance critical. + Calls an intrinsic function with one argument. Passes ``STACK[-1]`` as the + argument and sets ``STACK[-1]`` to the result. Used to implement functionality that is necessary but not performance critical. The operand determines which intrinsic function is called: From webhook-mailer at python.org Thu Jan 12 14:25:11 2023 From: webhook-mailer at python.org (zooba) Date: Thu, 12 Jan 2023 19:25:11 -0000 Subject: [Python-checkins] gh-96290: Support partial/invalid UNC drives in ntpath.normpath() and splitdrive() (GH-100351) Message-ID: https://github.com/python/cpython/commit/005e69403d638f9ff8f71e59960c600016e101a4 commit: 005e69403d638f9ff8f71e59960c600016e101a4 branch: main author: Barney Gale committer: zooba date: 2023-01-12T19:24:57Z summary: gh-96290: Support partial/invalid UNC drives in ntpath.normpath() and splitdrive() (GH-100351) This brings the Python implementation of `ntpath.normpath()` in line with the C implementation added in 99fcf15 Co-authored-by: Eryk Sun files: A Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst M Lib/ntpath.py M Lib/test/test_ntpath.py M Lib/test/test_zipfile/test_core.py diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 265eaa8d4b95..cd7fb58a88de 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -87,16 +87,20 @@ def normcase(s): def isabs(s): """Test whether a path is absolute""" s = os.fspath(s) - # Paths beginning with \\?\ are always absolute, but do not - # necessarily contain a drive. if isinstance(s, bytes): - if s.replace(b'/', b'\\').startswith(b'\\\\?\\'): - return True + sep = b'\\' + altsep = b'/' + colon_sep = b':\\' else: - if s.replace('/', '\\').startswith('\\\\?\\'): - return True - s = splitdrive(s)[1] - return len(s) > 0 and s[0] and s[0] in _get_bothseps(s) + sep = '\\' + altsep = '/' + colon_sep = ':\\' + s = s[:3].replace(altsep, sep) + # Absolute: UNC, device, and paths with a drive and root. + # LEGACY BUG: isabs("/x") should be false since the path has no drive. + if s.startswith(sep) or s.startswith(colon_sep, 1): + return True + return False # Join two (or more) paths. @@ -172,34 +176,26 @@ def splitdrive(p): sep = b'\\' altsep = b'/' colon = b':' - unc_prefix = b'\\\\?\\UNC' + unc_prefix = b'\\\\?\\UNC\\' else: sep = '\\' altsep = '/' colon = ':' - unc_prefix = '\\\\?\\UNC' + unc_prefix = '\\\\?\\UNC\\' normp = p.replace(altsep, sep) - if (normp[0:2] == sep*2) and (normp[2:3] != sep): - # is a UNC path: - # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path - # \\machine\mountpoint\directory\etc\... - # directory ^^^^^^^^^^^^^^^ - if normp[:8].upper().rstrip(sep) == unc_prefix: - start = 8 - else: - start = 2 + if normp[0:2] == sep * 2: + # UNC drives, e.g. \\server\share or \\?\UNC\server\share + # Device drives, e.g. \\.\device or \\?\device + start = 8 if normp[:8].upper() == unc_prefix else 2 index = normp.find(sep, start) if index == -1: - return p[:0], p + return p, p[:0] index2 = normp.find(sep, index + 1) - # a UNC path can't have two slashes in a row - # (after the initial two) - if index2 == index + 1: - return p[:0], p if index2 == -1: - index2 = len(p) + return p, p[:0] return p[:index2], p[index2:] if normp[1:2] == colon: + # Drive-letter drives, e.g. X: return p[:2], p[2:] return p[:0], p @@ -523,20 +519,11 @@ def normpath(path): altsep = b'/' curdir = b'.' pardir = b'..' - special_prefixes = (b'\\\\.\\', b'\\\\?\\') else: sep = '\\' altsep = '/' curdir = '.' pardir = '..' - special_prefixes = ('\\\\.\\', '\\\\?\\') - if path.startswith(special_prefixes): - # in the case of paths with these prefixes: - # \\.\ -> device names - # \\?\ -> literal paths - # do not do any normalization, but return the path - # unchanged apart from the call to os.fspath() - return path path = path.replace(altsep, sep) prefix, path = splitdrive(path) diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 336648273b6c..f56de0be7721 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -107,13 +107,13 @@ def test_splitdrive(self): tester('ntpath.splitdrive("//conky/mountpoint/foo/bar")', ('//conky/mountpoint', '/foo/bar')) tester('ntpath.splitdrive("\\\\\\conky\\mountpoint\\foo\\bar")', - ('', '\\\\\\conky\\mountpoint\\foo\\bar')) + ('\\\\\\conky', '\\mountpoint\\foo\\bar')) tester('ntpath.splitdrive("///conky/mountpoint/foo/bar")', - ('', '///conky/mountpoint/foo/bar')) + ('///conky', '/mountpoint/foo/bar')) tester('ntpath.splitdrive("\\\\conky\\\\mountpoint\\foo\\bar")', - ('', '\\\\conky\\\\mountpoint\\foo\\bar')) + ('\\\\conky\\', '\\mountpoint\\foo\\bar')) tester('ntpath.splitdrive("//conky//mountpoint/foo/bar")', - ('', '//conky//mountpoint/foo/bar')) + ('//conky/', '/mountpoint/foo/bar')) # Issue #19911: UNC part containing U+0130 self.assertEqual(ntpath.splitdrive('//conky/MOUNTPO?NT/foo/bar'), ('//conky/MOUNTPO?NT', '/foo/bar')) @@ -121,8 +121,8 @@ def test_splitdrive(self): tester('ntpath.splitdrive("//?/c:")', ("//?/c:", "")) tester('ntpath.splitdrive("//?/c:/")', ("//?/c:", "/")) tester('ntpath.splitdrive("//?/c:/dir")', ("//?/c:", "/dir")) - tester('ntpath.splitdrive("//?/UNC")', ("", "//?/UNC")) - tester('ntpath.splitdrive("//?/UNC/")', ("", "//?/UNC/")) + tester('ntpath.splitdrive("//?/UNC")', ("//?/UNC", "")) + tester('ntpath.splitdrive("//?/UNC/")', ("//?/UNC/", "")) tester('ntpath.splitdrive("//?/UNC/server/")', ("//?/UNC/server/", "")) tester('ntpath.splitdrive("//?/UNC/server/share")', ("//?/UNC/server/share", "")) tester('ntpath.splitdrive("//?/UNC/server/share/dir")', ("//?/UNC/server/share", "/dir")) @@ -133,8 +133,8 @@ def test_splitdrive(self): tester('ntpath.splitdrive("\\\\?\\c:")', ("\\\\?\\c:", "")) tester('ntpath.splitdrive("\\\\?\\c:\\")', ("\\\\?\\c:", "\\")) tester('ntpath.splitdrive("\\\\?\\c:\\dir")', ("\\\\?\\c:", "\\dir")) - tester('ntpath.splitdrive("\\\\?\\UNC")', ("", "\\\\?\\UNC")) - tester('ntpath.splitdrive("\\\\?\\UNC\\")', ("", "\\\\?\\UNC\\")) + tester('ntpath.splitdrive("\\\\?\\UNC")', ("\\\\?\\UNC", "")) + tester('ntpath.splitdrive("\\\\?\\UNC\\")', ("\\\\?\\UNC\\", "")) tester('ntpath.splitdrive("\\\\?\\UNC\\server\\")', ("\\\\?\\UNC\\server\\", "")) tester('ntpath.splitdrive("\\\\?\\UNC\\server\\share")', ("\\\\?\\UNC\\server\\share", "")) tester('ntpath.splitdrive("\\\\?\\UNC\\server\\share\\dir")', @@ -143,6 +143,13 @@ def test_splitdrive(self): ('\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}', '\\spam')) tester('ntpath.splitdrive("\\\\?\\BootPartition\\")', ("\\\\?\\BootPartition", "\\")) + # gh-96290: support partial/invalid UNC drives + tester('ntpath.splitdrive("//")', ("//", "")) # empty server & missing share + tester('ntpath.splitdrive("///")', ("///", "")) # empty server & empty share + tester('ntpath.splitdrive("///y")', ("///y", "")) # empty server & non-empty share + tester('ntpath.splitdrive("//x")', ("//x", "")) # non-empty server & missing share + tester('ntpath.splitdrive("//x/")', ("//x/", "")) # non-empty server & empty share + def test_split(self): tester('ntpath.split("c:\\foo\\bar")', ('c:\\foo', 'bar')) tester('ntpath.split("\\\\conky\\mountpoint\\foo\\bar")', @@ -161,6 +168,10 @@ def test_isabs(self): tester('ntpath.isabs("\\foo")', 1) tester('ntpath.isabs("\\foo\\bar")', 1) + # gh-96290: normal UNC paths and device paths without trailing backslashes + tester('ntpath.isabs("\\\\conky\\mountpoint")', 1) + tester('ntpath.isabs("\\\\.\\C:")', 1) + def test_commonprefix(self): tester('ntpath.commonprefix(["/home/swenson/spam", "/home/swen/spam"])', "/home/swen") @@ -270,6 +281,12 @@ def test_normpath(self): tester("ntpath.normpath('//server/share/../..')", '\\\\server\\share\\') tester("ntpath.normpath('//server/share/../../')", '\\\\server\\share\\') + # gh-96290: don't normalize partial/invalid UNC drives as rooted paths. + tester("ntpath.normpath('\\\\foo\\\\')", '\\\\foo\\\\') + tester("ntpath.normpath('\\\\foo\\')", '\\\\foo\\') + tester("ntpath.normpath('\\\\foo')", '\\\\foo') + tester("ntpath.normpath('\\\\')", '\\\\') + def test_realpath_curdir(self): expected = ntpath.normpath(os.getcwd()) tester("ntpath.realpath('.')", expected) diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py index bb0f1467735b..cf41d0e8cb8d 100644 --- a/Lib/test/test_zipfile/test_core.py +++ b/Lib/test/test_zipfile/test_core.py @@ -1469,10 +1469,10 @@ def test_extract_hackers_arcnames_windows_only(self): (r'C:\foo\bar', 'foo/bar'), (r'//conky/mountpoint/foo/bar', 'foo/bar'), (r'\\conky\mountpoint\foo\bar', 'foo/bar'), - (r'///conky/mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), - (r'\\\conky\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), - (r'//conky//mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), - (r'\\conky\\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), + (r'///conky/mountpoint/foo/bar', 'mountpoint/foo/bar'), + (r'\\\conky\mountpoint\foo\bar', 'mountpoint/foo/bar'), + (r'//conky//mountpoint/foo/bar', 'mountpoint/foo/bar'), + (r'\\conky\\mountpoint\foo\bar', 'mountpoint/foo/bar'), (r'//?/C:/foo/bar', 'foo/bar'), (r'\\?\C:\foo\bar', 'foo/bar'), (r'C:/../C:/foo/bar', 'C_/foo/bar'), diff --git a/Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst b/Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst new file mode 100644 index 000000000000..33f98602bd1b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst @@ -0,0 +1,5 @@ +Fix handling of partial and invalid UNC drives in ``ntpath.splitdrive()``, and in +``ntpath.normpath()`` on non-Windows systems. Paths such as '\\server' and '\\' are now considered +by ``splitdrive()`` to contain only a drive, and consequently are not modified by ``normpath()`` on +non-Windows systems. The behaviour of ``normpath()`` on Windows systems is unaffected, as native +OS APIs are used. Patch by Eryk Sun, with contributions by Barney Gale. From webhook-mailer at python.org Thu Jan 12 15:42:21 2023 From: webhook-mailer at python.org (ericsnowcurrently) Date: Thu, 12 Jan 2023 20:42:21 -0000 Subject: [Python-checkins] GH-100997: Implement Multi-Phase Init for the _testinternalcapi Module (gh-100998) Message-ID: https://github.com/python/cpython/commit/b511d3512ba334475201baf4d9db7c4d28f0a9ad commit: b511d3512ba334475201baf4d9db7c4d28f0a9ad branch: main author: Eric Snow committer: ericsnowcurrently date: 2023-01-12T13:42:03-07:00 summary: GH-100997: Implement Multi-Phase Init for the _testinternalcapi Module (gh-100998) _testinternalcapi is an internal module used for testing. https://github.com/python/cpython/issues/100997 files: M Modules/_testinternalcapi.c M Tools/c-analyzer/cpython/globals-to-fix.tsv M Tools/c-analyzer/cpython/ignored.tsv diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index b14b8ac3c740..f53929f80f8a 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -28,6 +28,60 @@ #include "clinic/_testinternalcapi.c.h" + +#define MODULE_NAME "_testinternalcapi" + + +static PyObject * +_get_current_module(void) +{ + // We ensured it was imported in _run_script(). + PyObject *name = PyUnicode_FromString(MODULE_NAME); + if (name == NULL) { + return NULL; + } + PyObject *mod = PyImport_GetModule(name); + Py_DECREF(name); + if (mod == NULL) { + return NULL; + } + assert(mod != Py_None); + return mod; +} + + +/* module state *************************************************************/ + +typedef struct { + PyObject *record_list; +} module_state; + +static inline module_state * +get_module_state(PyObject *mod) +{ + assert(mod != NULL); + module_state *state = PyModule_GetState(mod); + assert(state != NULL); + return state; +} + +static int +traverse_module_state(module_state *state, visitproc visit, void *arg) +{ + Py_VISIT(state->record_list); + return 0; +} + +static int +clear_module_state(module_state *state) +{ + Py_CLEAR(state->record_list); + return 0; +} + + +/* module functions *********************************************************/ + /*[clinic input] module _testinternalcapi [clinic start generated code]*/ @@ -496,13 +550,12 @@ decode_locale_ex(PyObject *self, PyObject *args) return res; } -static PyObject *record_list = NULL; - static PyObject * set_eval_frame_default(PyObject *self, PyObject *Py_UNUSED(args)) { + module_state *state = get_module_state(self); _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState_Get(), _PyEval_EvalFrameDefault); - Py_CLEAR(record_list); + Py_CLEAR(state->record_list); Py_RETURN_NONE; } @@ -510,7 +563,10 @@ static PyObject * record_eval(PyThreadState *tstate, struct _PyInterpreterFrame *f, int exc) { if (PyFunction_Check(f->f_funcobj)) { - PyList_Append(record_list, ((PyFunctionObject *)f->f_funcobj)->func_name); + PyObject *module = _get_current_module(); + assert(module != NULL); + module_state *state = get_module_state(module); + PyList_Append(state->record_list, ((PyFunctionObject *)f->f_funcobj)->func_name); } return _PyEval_EvalFrameDefault(tstate, f, exc); } @@ -519,11 +575,12 @@ record_eval(PyThreadState *tstate, struct _PyInterpreterFrame *f, int exc) static PyObject * set_eval_frame_record(PyObject *self, PyObject *list) { + module_state *state = get_module_state(self); if (!PyList_Check(list)) { PyErr_SetString(PyExc_TypeError, "argument must be a list"); return NULL; } - Py_XSETREF(record_list, Py_NewRef(list)); + Py_XSETREF(state->record_list, Py_NewRef(list)); _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState_Get(), record_eval); Py_RETURN_NONE; } @@ -613,7 +670,7 @@ get_interp_settings(PyObject *self, PyObject *args) } -static PyMethodDef TestMethods[] = { +static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, {"test_bswap", test_bswap, METH_NOARGS}, @@ -638,35 +695,65 @@ static PyMethodDef TestMethods[] = { }; -static struct PyModuleDef _testcapimodule = { - PyModuleDef_HEAD_INIT, - "_testinternalcapi", - NULL, - -1, - TestMethods, - NULL, - NULL, - NULL, - NULL -}; - +/* initialization function */ -PyMODINIT_FUNC -PyInit__testinternalcapi(void) +static int +module_exec(PyObject *module) { - PyObject *module = PyModule_Create(&_testcapimodule); - if (module == NULL) { - return NULL; - } - if (PyModule_AddObject(module, "SIZEOF_PYGC_HEAD", PyLong_FromSsize_t(sizeof(PyGC_Head))) < 0) { - goto error; + return 1; } - return module; + return 0; +} -error: - Py_DECREF(module); - return NULL; +static struct PyModuleDef_Slot module_slots[] = { + {Py_mod_exec, module_exec}, + {0, NULL}, +}; + +static int +module_traverse(PyObject *module, visitproc visit, void *arg) +{ + module_state *state = get_module_state(module); + assert(state != NULL); + traverse_module_state(state, visit, arg); + return 0; +} + +static int +module_clear(PyObject *module) +{ + module_state *state = get_module_state(module); + assert(state != NULL); + (void)clear_module_state(state); + return 0; +} + +static void +module_free(void *module) +{ + module_state *state = get_module_state(module); + assert(state != NULL); + (void)clear_module_state(state); +} + +static struct PyModuleDef _testcapimodule = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = MODULE_NAME, + .m_doc = NULL, + .m_size = sizeof(module_state), + .m_methods = module_functions, + .m_slots = module_slots, + .m_traverse = module_traverse, + .m_clear = module_clear, + .m_free = (freefunc)module_free, +}; + + +PyMODINIT_FUNC +PyInit__testinternalcapi(void) +{ + return PyModuleDef_Init(&_testcapimodule); } diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 479221cbd4b6..cd08782edce4 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -523,7 +523,6 @@ Modules/_asynciomodule.c - all_tasks - Modules/_asynciomodule.c - current_tasks - Modules/_asynciomodule.c - iscoroutine_typecache - Modules/_ctypes/_ctypes.c - _ctypes_ptrtype_cache - -Modules/_testinternalcapi.c - record_list - Modules/_tkinter.c - tcl_lock - Modules/_tkinter.c - excInCmd - Modules/_tkinter.c - valInCmd - diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index 02531692a884..849e20a1b0f4 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -483,8 +483,6 @@ Modules/_testcapimodule.c - g_type_watchers_installed - Modules/_testimportmultiple.c - _barmodule - Modules/_testimportmultiple.c - _foomodule - Modules/_testimportmultiple.c - _testimportmultiple - -Modules/_testinternalcapi.c - TestMethods - -Modules/_testinternalcapi.c - _testcapimodule - Modules/_testmultiphase.c - Example_Type_slots - Modules/_testmultiphase.c - Example_Type_spec - Modules/_testmultiphase.c - Example_methods - From webhook-mailer at python.org Thu Jan 12 16:32:32 2023 From: webhook-mailer at python.org (zooba) Date: Thu, 12 Jan 2023 21:32:32 -0000 Subject: [Python-checkins] gh-96290: Support partial/invalid UNC drives in ntpath.normpath() and splitdrive() (GH-100351) Message-ID: https://github.com/python/cpython/commit/55a26de6ba938962dc23f2495723cf0f4f3ab7c6 commit: 55a26de6ba938962dc23f2495723cf0f4f3ab7c6 branch: 3.11 author: Steve Dower committer: zooba date: 2023-01-12T21:32:26Z summary: gh-96290: Support partial/invalid UNC drives in ntpath.normpath() and splitdrive() (GH-100351) This brings the Python implementation of `ntpath.normpath()` in line with the C implementation added in 99fcf15 Co-authored-by: Barney Gale Co-authored-by: Eryk Sun files: A Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst M Lib/ntpath.py M Lib/test/test_ntpath.py M Lib/test/test_zipfile.py diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 10c6799300f6..1cfb15b77102 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -87,16 +87,20 @@ def normcase(s): def isabs(s): """Test whether a path is absolute""" s = os.fspath(s) - # Paths beginning with \\?\ are always absolute, but do not - # necessarily contain a drive. if isinstance(s, bytes): - if s.replace(b'/', b'\\').startswith(b'\\\\?\\'): - return True + sep = b'\\' + altsep = b'/' + colon_sep = b':\\' else: - if s.replace('/', '\\').startswith('\\\\?\\'): - return True - s = splitdrive(s)[1] - return len(s) > 0 and s[0] and s[0] in _get_bothseps(s) + sep = '\\' + altsep = '/' + colon_sep = ':\\' + s = s[:3].replace(altsep, sep) + # Absolute: UNC, device, and paths with a drive and root. + # LEGACY BUG: isabs("/x") should be false since the path has no drive. + if s.startswith(sep) or s.startswith(colon_sep, 1): + return True + return False # Join two (or more) paths. @@ -172,28 +176,26 @@ def splitdrive(p): sep = b'\\' altsep = b'/' colon = b':' + unc_prefix = b'\\\\?\\UNC\\' else: sep = '\\' altsep = '/' colon = ':' + unc_prefix = '\\\\?\\UNC\\' normp = p.replace(altsep, sep) - if (normp[0:2] == sep*2) and (normp[2:3] != sep): - # is a UNC path: - # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path - # \\machine\mountpoint\directory\etc\... - # directory ^^^^^^^^^^^^^^^ - index = normp.find(sep, 2) + if normp[0:2] == sep * 2: + # UNC drives, e.g. \\server\share or \\?\UNC\server\share + # Device drives, e.g. \\.\device or \\?\device + start = 8 if normp[:8].upper() == unc_prefix else 2 + index = normp.find(sep, start) if index == -1: - return p[:0], p + return p, p[:0] index2 = normp.find(sep, index + 1) - # a UNC path can't have two slashes in a row - # (after the initial two) - if index2 == index + 1: - return p[:0], p if index2 == -1: - index2 = len(p) + return p, p[:0] return p[:index2], p[index2:] if normp[1:2] == colon: + # Drive-letter drives, e.g. X: return p[:2], p[2:] return p[:0], p @@ -499,20 +501,11 @@ def normpath(path): altsep = b'/' curdir = b'.' pardir = b'..' - special_prefixes = (b'\\\\.\\', b'\\\\?\\') else: sep = '\\' altsep = '/' curdir = '.' pardir = '..' - special_prefixes = ('\\\\.\\', '\\\\?\\') - if path.startswith(special_prefixes): - # in the case of paths with these prefixes: - # \\.\ -> device names - # \\?\ -> literal paths - # do not do any normalization, but return the path - # unchanged apart from the call to os.fspath() - return path path = path.replace(altsep, sep) prefix, path = splitdrive(path) diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index ab3603bdd730..c26c74cdd619 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -107,17 +107,50 @@ def test_splitdrive(self): tester('ntpath.splitdrive("//conky/mountpoint/foo/bar")', ('//conky/mountpoint', '/foo/bar')) tester('ntpath.splitdrive("\\\\\\conky\\mountpoint\\foo\\bar")', - ('', '\\\\\\conky\\mountpoint\\foo\\bar')) + ('\\\\\\conky', '\\mountpoint\\foo\\bar')) tester('ntpath.splitdrive("///conky/mountpoint/foo/bar")', - ('', '///conky/mountpoint/foo/bar')) + ('///conky', '/mountpoint/foo/bar')) tester('ntpath.splitdrive("\\\\conky\\\\mountpoint\\foo\\bar")', - ('', '\\\\conky\\\\mountpoint\\foo\\bar')) + ('\\\\conky\\', '\\mountpoint\\foo\\bar')) tester('ntpath.splitdrive("//conky//mountpoint/foo/bar")', - ('', '//conky//mountpoint/foo/bar')) + ('//conky/', '/mountpoint/foo/bar')) # Issue #19911: UNC part containing U+0130 self.assertEqual(ntpath.splitdrive('//conky/MOUNTPO?NT/foo/bar'), ('//conky/MOUNTPO?NT', '/foo/bar')) + # gh-81790: support device namespace, including UNC drives. + tester('ntpath.splitdrive("//?/c:")', ("//?/c:", "")) + tester('ntpath.splitdrive("//?/c:/")', ("//?/c:", "/")) + tester('ntpath.splitdrive("//?/c:/dir")', ("//?/c:", "/dir")) + tester('ntpath.splitdrive("//?/UNC")', ("//?/UNC", "")) + tester('ntpath.splitdrive("//?/UNC/")', ("//?/UNC/", "")) + tester('ntpath.splitdrive("//?/UNC/server/")', ("//?/UNC/server/", "")) + tester('ntpath.splitdrive("//?/UNC/server/share")', ("//?/UNC/server/share", "")) + tester('ntpath.splitdrive("//?/UNC/server/share/dir")', ("//?/UNC/server/share", "/dir")) + tester('ntpath.splitdrive("//?/VOLUME{00000000-0000-0000-0000-000000000000}/spam")', + ('//?/VOLUME{00000000-0000-0000-0000-000000000000}', '/spam')) + tester('ntpath.splitdrive("//?/BootPartition/")', ("//?/BootPartition", "/")) + + tester('ntpath.splitdrive("\\\\?\\c:")', ("\\\\?\\c:", "")) + tester('ntpath.splitdrive("\\\\?\\c:\\")', ("\\\\?\\c:", "\\")) + tester('ntpath.splitdrive("\\\\?\\c:\\dir")', ("\\\\?\\c:", "\\dir")) + tester('ntpath.splitdrive("\\\\?\\UNC")', ("\\\\?\\UNC", "")) + tester('ntpath.splitdrive("\\\\?\\UNC\\")', ("\\\\?\\UNC\\", "")) + tester('ntpath.splitdrive("\\\\?\\UNC\\server\\")', ("\\\\?\\UNC\\server\\", "")) + tester('ntpath.splitdrive("\\\\?\\UNC\\server\\share")', ("\\\\?\\UNC\\server\\share", "")) + tester('ntpath.splitdrive("\\\\?\\UNC\\server\\share\\dir")', + ("\\\\?\\UNC\\server\\share", "\\dir")) + tester('ntpath.splitdrive("\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}\\spam")', + ('\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}', '\\spam')) + tester('ntpath.splitdrive("\\\\?\\BootPartition\\")', ("\\\\?\\BootPartition", "\\")) + + # gh-96290: support partial/invalid UNC drives + tester('ntpath.splitdrive("//")', ("//", "")) # empty server & missing share + tester('ntpath.splitdrive("///")', ("///", "")) # empty server & empty share + tester('ntpath.splitdrive("///y")', ("///y", "")) # empty server & non-empty share + tester('ntpath.splitdrive("//x")', ("//x", "")) # non-empty server & missing share + tester('ntpath.splitdrive("//x/")', ("//x/", "")) # non-empty server & empty share + def test_split(self): tester('ntpath.split("c:\\foo\\bar")', ('c:\\foo', 'bar')) tester('ntpath.split("\\\\conky\\mountpoint\\foo\\bar")', @@ -136,6 +169,10 @@ def test_isabs(self): tester('ntpath.isabs("\\foo")', 1) tester('ntpath.isabs("\\foo\\bar")', 1) + # gh-96290: normal UNC paths and device paths without trailing backslashes + tester('ntpath.isabs("\\\\conky\\mountpoint")', 1) + tester('ntpath.isabs("\\\\.\\C:")', 1) + def test_commonprefix(self): tester('ntpath.commonprefix(["/home/swenson/spam", "/home/swen/spam"])', "/home/swen") @@ -245,6 +282,12 @@ def test_normpath(self): tester("ntpath.normpath('//server/share/../..')", '\\\\server\\share\\') tester("ntpath.normpath('//server/share/../../')", '\\\\server\\share\\') + # gh-96290: don't normalize partial/invalid UNC drives as rooted paths. + tester("ntpath.normpath('\\\\foo\\\\')", '\\\\foo\\\\') + tester("ntpath.normpath('\\\\foo\\')", '\\\\foo\\') + tester("ntpath.normpath('\\\\foo')", '\\\\foo') + tester("ntpath.normpath('\\\\')", '\\\\') + def test_realpath_curdir(self): expected = ntpath.normpath(os.getcwd()) tester("ntpath.realpath('.')", expected) diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index f4c11d88c8a0..fd25e5a800d7 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -1468,10 +1468,10 @@ def test_extract_hackers_arcnames_windows_only(self): (r'C:\foo\bar', 'foo/bar'), (r'//conky/mountpoint/foo/bar', 'foo/bar'), (r'\\conky\mountpoint\foo\bar', 'foo/bar'), - (r'///conky/mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), - (r'\\\conky\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), - (r'//conky//mountpoint/foo/bar', 'conky/mountpoint/foo/bar'), - (r'\\conky\\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'), + (r'///conky/mountpoint/foo/bar', 'mountpoint/foo/bar'), + (r'\\\conky\mountpoint\foo\bar', 'mountpoint/foo/bar'), + (r'//conky//mountpoint/foo/bar', 'mountpoint/foo/bar'), + (r'\\conky\\mountpoint\foo\bar', 'mountpoint/foo/bar'), (r'//?/C:/foo/bar', 'foo/bar'), (r'\\?\C:\foo\bar', 'foo/bar'), (r'C:/../C:/foo/bar', 'C_/foo/bar'), diff --git a/Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst b/Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst new file mode 100644 index 000000000000..33f98602bd1b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-19-23-19-26.gh-issue-96290.qFjsi6.rst @@ -0,0 +1,5 @@ +Fix handling of partial and invalid UNC drives in ``ntpath.splitdrive()``, and in +``ntpath.normpath()`` on non-Windows systems. Paths such as '\\server' and '\\' are now considered +by ``splitdrive()`` to contain only a drive, and consequently are not modified by ``normpath()`` on +non-Windows systems. The behaviour of ``normpath()`` on Windows systems is unaffected, as native +OS APIs are used. Patch by Eryk Sun, with contributions by Barney Gale. From webhook-mailer at python.org Thu Jan 12 17:15:03 2023 From: webhook-mailer at python.org (rhettinger) Date: Thu, 12 Jan 2023 22:15:03 -0000 Subject: [Python-checkins] GH-100942: Fix incorrect cast in property_copy(). (#100965) Message-ID: https://github.com/python/cpython/commit/94fc7706b7bc3d57cdd6d15bf8e8c4499ae53a69 commit: 94fc7706b7bc3d57cdd6d15bf8e8c4499ae53a69 branch: main author: Raymond Hettinger committer: rhettinger date: 2023-01-12T16:13:56-06:00 summary: GH-100942: Fix incorrect cast in property_copy(). (#100965) files: A Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst M Lib/test/test_property.py M Objects/descrobject.c diff --git a/Lib/test/test_property.py b/Lib/test/test_property.py index d07b8632aa87..d4bdf50c0192 100644 --- a/Lib/test/test_property.py +++ b/Lib/test/test_property.py @@ -214,6 +214,23 @@ def test_property_set_name_incorrect_args(self): ): p.__set_name__(*([0] * i)) + def test_property_setname_on_property_subclass(self): + # https://github.com/python/cpython/issues/100942 + # Copy was setting the name field without first + # verifying that the copy was an actual property + # instance. As a result, the code below was + # causing a segfault. + + class pro(property): + def __new__(typ, *args, **kwargs): + return "abcdef" + + class A: + pass + + p = property.__new__(pro) + p.__set_name__(A, 1) + np = p.getter(lambda self: 1) # Issue 5890: subclasses of property do not preserve method __doc__ strings class PropertySub(property): diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst new file mode 100644 index 000000000000..daccea255b16 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst @@ -0,0 +1,2 @@ +Fixed segfault in property.getter/setter/deleter that occurred when a property +subclass overrode the ``__new__`` method to return a non-property instance. diff --git a/Objects/descrobject.c b/Objects/descrobject.c index c545b90c6283..334be75e8df9 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -1712,7 +1712,9 @@ property_copy(PyObject *old, PyObject *get, PyObject *set, PyObject *del) if (new == NULL) return NULL; - Py_XSETREF(((propertyobject *) new)->prop_name, Py_XNewRef(pold->prop_name)); + if (PyObject_TypeCheck((new), &PyProperty_Type)) { + Py_XSETREF(((propertyobject *) new)->prop_name, Py_XNewRef(pold->prop_name)); + } return new; } From webhook-mailer at python.org Fri Jan 13 02:35:01 2023 From: webhook-mailer at python.org (serhiy-storchaka) Date: Fri, 13 Jan 2023 07:35:01 -0000 Subject: [Python-checkins] [3.10] gh-100160: Remove any deprecation warnings in asyncio.get_event_loop() (GH-100412) (GH-100970) Message-ID: https://github.com/python/cpython/commit/87f9b1d53f12538c579598304105974378677012 commit: 87f9b1d53f12538c579598304105974378677012 branch: 3.10 author: Serhiy Storchaka committer: serhiy-storchaka date: 2023-01-13T09:34:55+02:00 summary: [3.10] gh-100160: Remove any deprecation warnings in asyncio.get_event_loop() (GH-100412) (GH-100970) Some deprecation warnings will reappear (in a slightly different form) in 3.12. Co-authored-by: Guido van Rossum . (cherry picked from commit 1b2459dc64b1c3eea89312ea9bf422f8d7c75bb2) files: A Misc/NEWS.d/next/Library/2022-12-21-18-29-24.gh-issue-100160.isBmL5.rst M Doc/library/asyncio-eventloop.rst M Doc/library/asyncio-policy.rst M Doc/whatsnew/3.10.rst M Lib/asyncio/events.py M Lib/test/test_asyncio/test_events.py M Lib/test/test_json/test_default.py diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 7b4c3c7682fe..80440da6be9b 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -48,7 +48,7 @@ an event loop: running event loop. If there is no running event loop set, the function will return - the result of ``get_event_loop_policy().get_event_loop()`` call. + the result of the ``get_event_loop_policy().get_event_loop()`` call. Because this function has rather complex behavior (especially when custom event loop policies are in use), using the @@ -59,15 +59,15 @@ an event loop: instead of using these lower level functions to manually create and close an event loop. - .. deprecated:: 3.10 - Deprecation warning is emitted if there is no current event loop. - In Python 3.12 it will be an error. - .. note:: - In Python versions 3.10.0--3.10.8 this function - (and other functions which used it implicitly) emitted a + In Python versions 3.10.0--3.10.8 and 3.11.0 this function + (and other functions which use it implicitly) emitted a :exc:`DeprecationWarning` if there was no running event loop, even if - the current loop was set. + the current loop was set on the policy. + In Python versions 3.10.9, 3.11.1 and 3.12 they emit a + :exc:`DeprecationWarning` if there is no running event loop and no + current loop is set. + In some future Python release this will become an error. .. function:: set_event_loop(loop) diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst index a4cd5aa992af..f846f76ca095 100644 --- a/Doc/library/asyncio-policy.rst +++ b/Doc/library/asyncio-policy.rst @@ -112,10 +112,11 @@ asyncio ships with the following built-in policies: On Windows, :class:`ProactorEventLoop` is now used by default. - .. deprecated:: 3.10.9 - :meth:`get_event_loop` now emits a :exc:`DeprecationWarning` if there - is no current event loop set and a new event loop has been implicitly - created. In Python 3.12 it will be an error. + .. note:: + In Python versions 3.10.9, 3.11.1 and 3.12 the :meth:`get_event_loop` + method of the default asyncio policy emits a :exc:`DeprecationWarning` + if there is no running event loop and no current loop is set. + In some future Python release this will become an error. .. class:: WindowsSelectorEventLoopPolicy diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index ab93491c3e63..47e38ae76bad 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -1706,19 +1706,6 @@ Deprecated scheduled for removal in Python 3.12. (Contributed by Erlend E. Aasland in :issue:`42264`.) -* :func:`asyncio.get_event_loop` now emits a deprecation warning if there is - no running event loop. In the future it will be an alias of - :func:`~asyncio.get_running_loop`. - :mod:`asyncio` functions which implicitly create :class:`~asyncio.Future` - or :class:`~asyncio.Task` objects now emit - a deprecation warning if there is no running event loop and no explicit - *loop* argument is passed: :func:`~asyncio.ensure_future`, - :func:`~asyncio.wrap_future`, :func:`~asyncio.gather`, - :func:`~asyncio.shield`, :func:`~asyncio.as_completed` and constructors of - :class:`~asyncio.Future`, :class:`~asyncio.Task`, - :class:`~asyncio.StreamReader`, :class:`~asyncio.StreamReaderProtocol`. - (Contributed by Serhiy Storchaka in :issue:`39529`.) - * The undocumented built-in function ``sqlite3.enable_shared_cache`` is now deprecated, scheduled for removal in Python 3.12. Its use is strongly discouraged by the SQLite3 documentation. See `the SQLite3 docs diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index faac53769412..e0882309edaa 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -650,21 +650,6 @@ def get_event_loop(self): if (self._local._loop is None and not self._local._set_called and threading.current_thread() is threading.main_thread()): - stacklevel = 2 - try: - f = sys._getframe(1) - except AttributeError: - pass - else: - while f: - module = f.f_globals.get('__name__') - if not (module == 'asyncio' or module.startswith('asyncio.')): - break - f = f.f_back - stacklevel += 1 - import warnings - warnings.warn('There is no current event loop', - DeprecationWarning, stacklevel=stacklevel) self.set_event_loop(self.new_event_loop()) if self._local._loop is None: diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 28d5f39ef611..253a6c119c9e 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -2602,9 +2602,7 @@ def test_event_loop_policy(self): def test_get_event_loop(self): policy = asyncio.DefaultEventLoopPolicy() self.assertIsNone(policy._local._loop) - with self.assertWarns(DeprecationWarning) as cm: - loop = policy.get_event_loop() - self.assertEqual(cm.filename, __file__) + loop = policy.get_event_loop() self.assertIsInstance(loop, asyncio.AbstractEventLoop) self.assertIs(policy._local._loop, loop) @@ -2618,10 +2616,8 @@ def test_get_event_loop_calls_set_event_loop(self): policy, "set_event_loop", wraps=policy.set_event_loop) as m_set_event_loop: - with self.assertWarns(DeprecationWarning) as cm: - loop = policy.get_event_loop() + loop = policy.get_event_loop() self.addCleanup(loop.close) - self.assertEqual(cm.filename, __file__) # policy._local._loop must be set through .set_event_loop() # (the unix DefaultEventLoopPolicy needs this call to attach @@ -2810,10 +2806,8 @@ def test_get_event_loop_returns_running_loop2(self): loop = asyncio.new_event_loop() self.addCleanup(loop.close) - with self.assertWarns(DeprecationWarning) as cm: - loop2 = asyncio.get_event_loop() + loop2 = asyncio.get_event_loop() self.addCleanup(loop2.close) - self.assertEqual(cm.warnings[0].filename, __file__) asyncio.set_event_loop(None) with self.assertRaisesRegex(RuntimeError, 'no current'): asyncio.get_event_loop() diff --git a/Lib/test/test_json/test_default.py b/Lib/test/test_json/test_default.py index 9b8325e9c381..2ce618285318 100644 --- a/Lib/test/test_json/test_default.py +++ b/Lib/test/test_json/test_default.py @@ -1,3 +1,4 @@ +import collections from test.test_json import PyTest, CTest @@ -7,6 +8,16 @@ def test_default(self): self.dumps(type, default=repr), self.dumps(repr(type))) + def test_ordereddict(self): + od = collections.OrderedDict(a=1, b=2) + od.move_to_end('a') + self.assertEqual( + self.dumps(od), + '{"b": 2, "a": 1}') + self.assertEqual( + self.dumps(od, sort_keys=True), + '{"a": 1, "b": 2}') + class TestPyDefault(TestDefault, PyTest): pass class TestCDefault(TestDefault, CTest): pass diff --git a/Misc/NEWS.d/next/Library/2022-12-21-18-29-24.gh-issue-100160.isBmL5.rst b/Misc/NEWS.d/next/Library/2022-12-21-18-29-24.gh-issue-100160.isBmL5.rst new file mode 100644 index 000000000000..c3b518ca85d7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-21-18-29-24.gh-issue-100160.isBmL5.rst @@ -0,0 +1,2 @@ +Remove any deprecation warnings in :func:`asyncio.get_event_loop`. They are +deferred to Python 3.12. From webhook-mailer at python.org Fri Jan 13 03:15:55 2023 From: webhook-mailer at python.org (serhiy-storchaka) Date: Fri, 13 Jan 2023 08:15:55 -0000 Subject: [Python-checkins] [3.11] gh-100160: Tweak the documentation for the default asyncio policy (GH-100969) Message-ID: https://github.com/python/cpython/commit/2834fdc689f52edba3ea1f9b2ee85fc33fc9e097 commit: 2834fdc689f52edba3ea1f9b2ee85fc33fc9e097 branch: 3.11 author: Serhiy Storchaka committer: serhiy-storchaka date: 2023-01-13T10:15:23+02:00 summary: [3.11] gh-100160: Tweak the documentation for the default asyncio policy (GH-100969) files: M Doc/library/asyncio-policy.rst diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst index eb043b3e5e7f..f846f76ca095 100644 --- a/Doc/library/asyncio-policy.rst +++ b/Doc/library/asyncio-policy.rst @@ -113,9 +113,9 @@ asyncio ships with the following built-in policies: On Windows, :class:`ProactorEventLoop` is now used by default. .. note:: - In Python versions 3.10.9, 3.11.1 and 3.12 this function emits a - :exc:`DeprecationWarning` if there is no running event loop and no - current loop is set. + In Python versions 3.10.9, 3.11.1 and 3.12 the :meth:`get_event_loop` + method of the default asyncio policy emits a :exc:`DeprecationWarning` + if there is no running event loop and no current loop is set. In some future Python release this will become an error. From webhook-mailer at python.org Fri Jan 13 06:31:13 2023 From: webhook-mailer at python.org (zooba) Date: Fri, 13 Jan 2023 11:31:13 -0000 Subject: [Python-checkins] gh-86682: Adds sys._getframemodulename as an alternative to using _getframe (GH-99520) Message-ID: https://github.com/python/cpython/commit/b5d4347950399800c6703736d716f08761b29245 commit: b5d4347950399800c6703736d716f08761b29245 branch: main author: Steve Dower committer: zooba date: 2023-01-13T11:31:06Z summary: gh-86682: Adds sys._getframemodulename as an alternative to using _getframe (GH-99520) Also updates calls in collections, doctest, enum, and typing modules to use _getframemodulename first when available. files: A Misc/NEWS.d/next/Library/2022-11-15-23-30-39.gh-issue-86682.gK9i1N.rst M Doc/library/sys.rst M Lib/_pydecimal.py M Lib/collections/__init__.py M Lib/doctest.py M Lib/enum.py M Lib/test/audit-tests.py M Lib/test/test_audit.py M Lib/test/test_sys.py M Lib/typing.py M Objects/funcobject.c M Python/clinic/sysmodule.c.h M Python/sysmodule.c diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 28adca1f618d..605e2c9a6710 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -808,6 +808,22 @@ always available. It is not guaranteed to exist in all implementations of Python. +.. function:: _getframemodulename([depth]) + + Return the name of a module from the call stack. If optional integer *depth* + is given, return the module that many calls below the top of the stack. If + that is deeper than the call stack, or if the module is unidentifiable, + ``None`` is returned. The default for *depth* is zero, returning the + module at the top of the call stack. + + .. audit-event:: sys._getframemodulename depth sys._getframemodulename + + .. impl-detail:: + + This function should be used for internal and specialized purposes only. + It is not guaranteed to exist in all implementations of Python. + + .. function:: getprofile() .. index:: diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index f9d6c9901f1f..2692f2fcba45 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -159,7 +159,7 @@ try: from collections import namedtuple as _namedtuple - DecimalTuple = _namedtuple('DecimalTuple', 'sign digits exponent') + DecimalTuple = _namedtuple('DecimalTuple', 'sign digits exponent', module='decimal') except ImportError: DecimalTuple = lambda *args: args diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index f07ee143a5af..b5e4d16e9dbc 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -507,9 +507,12 @@ def __getnewargs__(self): # specified a particular module. if module is None: try: - module = _sys._getframe(1).f_globals.get('__name__', '__main__') - except (AttributeError, ValueError): - pass + module = _sys._getframemodulename(1) or '__main__' + except AttributeError: + try: + module = _sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + pass if module is not None: result.__module__ = module diff --git a/Lib/doctest.py b/Lib/doctest.py index dafad505ef0e..2776d74bf9b5 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -207,7 +207,13 @@ def _normalize_module(module, depth=2): elif isinstance(module, str): return __import__(module, globals(), locals(), ["*"]) elif module is None: - return sys.modules[sys._getframe(depth).f_globals['__name__']] + try: + try: + return sys.modules[sys._getframemodulename(depth)] + except AttributeError: + return sys.modules[sys._getframe(depth).f_globals['__name__']] + except KeyError: + pass else: raise TypeError("Expected a module, string, or None") diff --git a/Lib/enum.py b/Lib/enum.py index 21f63881d78d..4658393d756e 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -862,13 +862,15 @@ def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, s member_name, member_value = item classdict[member_name] = member_value - # TODO: replace the frame hack if a blessed way to know the calling - # module is ever developed if module is None: try: - module = sys._getframe(2).f_globals['__name__'] - except (AttributeError, ValueError, KeyError): - pass + module = sys._getframemodulename(2) + except AttributeError: + # Fall back on _getframe if _getframemodulename is missing + try: + module = sys._getframe(2).f_globals['__name__'] + except (AttributeError, ValueError, KeyError): + pass if module is None: _make_class_unpicklable(classdict) else: diff --git a/Lib/test/audit-tests.py b/Lib/test/audit-tests.py index bf56cea541d1..0edc9d9c4727 100644 --- a/Lib/test/audit-tests.py +++ b/Lib/test/audit-tests.py @@ -419,6 +419,17 @@ def hook(event, args): sys._getframe() +def test_sys_getframemodulename(): + import sys + + def hook(event, args): + if event.startswith("sys."): + print(event, *args) + + sys.addaudithook(hook) + sys._getframemodulename() + + def test_threading(): import _thread diff --git a/Lib/test/test_audit.py b/Lib/test/test_audit.py index 70f8a77a4761..0b69864751d8 100644 --- a/Lib/test/test_audit.py +++ b/Lib/test/test_audit.py @@ -186,6 +186,18 @@ def test_sys_getframe(self): self.assertEqual(actual, expected) + def test_sys_getframemodulename(self): + returncode, events, stderr = self.run_python("test_sys_getframemodulename") + if returncode: + self.fail(stderr) + + if support.verbose: + print(*events, sep='\n') + actual = [(ev[0], ev[2]) for ev in events] + expected = [("sys._getframemodulename", "0")] + + self.assertEqual(actual, expected) + def test_threading(self): returncode, events, stderr = self.run_python("test_threading") diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 232b79971dc2..ab1a06594718 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -399,6 +399,26 @@ def test_getframe(self): is sys._getframe().f_code ) + def test_getframemodulename(self): + # Default depth gets ourselves + self.assertEqual(__name__, sys._getframemodulename()) + self.assertEqual("unittest.case", sys._getframemodulename(1)) + i = 0 + f = sys._getframe(i) + while f: + self.assertEqual( + f.f_globals['__name__'], + sys._getframemodulename(i) or '__main__' + ) + i += 1 + f2 = f.f_back + try: + f = sys._getframe(i) + except ValueError: + break + self.assertIs(f, f2) + self.assertIsNone(sys._getframemodulename(i)) + # sys._current_frames() is a CPython-only gimmick. @threading_helper.reap_threads @threading_helper.requires_working_threading() diff --git a/Lib/typing.py b/Lib/typing.py index 8bc38f98c867..4675af12d087 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1939,11 +1939,15 @@ def _no_init_or_replace_init(self, *args, **kwargs): def _caller(depth=1, default='__main__'): + try: + return sys._getframemodulename(depth + 1) or default + except AttributeError: # For platforms without _getframemodulename() + pass try: return sys._getframe(depth + 1).f_globals.get('__name__', default) except (AttributeError, ValueError): # For platforms without _getframe() - return None - + pass + return None def _allow_reckless_class_checks(depth=3): """Allow instance and class checks for special stdlib modules. diff --git a/Misc/NEWS.d/next/Library/2022-11-15-23-30-39.gh-issue-86682.gK9i1N.rst b/Misc/NEWS.d/next/Library/2022-11-15-23-30-39.gh-issue-86682.gK9i1N.rst new file mode 100644 index 000000000000..64ef42a9a1c0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-11-15-23-30-39.gh-issue-86682.gK9i1N.rst @@ -0,0 +1,2 @@ +Ensure runtime-created collections have the correct module name using +the newly added (internal) :func:`sys._getframemodulename`. diff --git a/Objects/funcobject.c b/Objects/funcobject.c index d5cf5b9277b3..baa360381a77 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -93,7 +93,10 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr) op->func_doc = Py_NewRef(Py_None); op->func_dict = NULL; op->func_weakreflist = NULL; - op->func_module = NULL; + op->func_module = Py_XNewRef(PyDict_GetItem(op->func_globals, &_Py_ID(__name__))); + if (!op->func_module) { + PyErr_Clear(); + } op->func_annotations = NULL; op->vectorcall = _PyFunction_Vectorcall; op->func_version = 0; diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 03eeda8126eb..46252dd40432 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -1275,6 +1275,75 @@ sys_is_stack_trampoline_active(PyObject *module, PyObject *Py_UNUSED(ignored)) return sys_is_stack_trampoline_active_impl(module); } +PyDoc_STRVAR(sys__getframemodulename__doc__, +"_getframemodulename($module, /, depth=0)\n" +"--\n" +"\n" +"Return the name of the module for a calling frame.\n" +"\n" +"The default depth returns the module containing the call to this API.\n" +"A more typical use in a library will pass a depth of 1 to get the user\'s\n" +"module rather than the library module.\n" +"\n" +"If no frame, module, or name can be found, returns None."); + +#define SYS__GETFRAMEMODULENAME_METHODDEF \ + {"_getframemodulename", _PyCFunction_CAST(sys__getframemodulename), METH_FASTCALL|METH_KEYWORDS, sys__getframemodulename__doc__}, + +static PyObject * +sys__getframemodulename_impl(PyObject *module, int depth); + +static PyObject * +sys__getframemodulename(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(depth), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"depth", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_getframemodulename", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int depth = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + depth = _PyLong_AsInt(args[0]); + if (depth == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_pos: + return_value = sys__getframemodulename_impl(module, depth); + +exit: + return return_value; +} + #ifndef SYS_GETWINDOWSVERSION_METHODDEF #define SYS_GETWINDOWSVERSION_METHODDEF #endif /* !defined(SYS_GETWINDOWSVERSION_METHODDEF) */ @@ -1318,4 +1387,4 @@ sys_is_stack_trampoline_active(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=b32b444538dfd354 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5c761f14326ced54 input=a9049054013a1b77]*/ diff --git a/Python/sysmodule.c b/Python/sysmodule.c index acee794864f9..f9f766a94d14 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2172,6 +2172,43 @@ sys_is_stack_trampoline_active_impl(PyObject *module) } +/*[clinic input] +sys._getframemodulename + + depth: int = 0 + +Return the name of the module for a calling frame. + +The default depth returns the module containing the call to this API. +A more typical use in a library will pass a depth of 1 to get the user's +module rather than the library module. + +If no frame, module, or name can be found, returns None. +[clinic start generated code]*/ + +static PyObject * +sys__getframemodulename_impl(PyObject *module, int depth) +/*[clinic end generated code: output=1d70ef691f09d2db input=d4f1a8ed43b8fb46]*/ +{ + if (PySys_Audit("sys._getframemodulename", "i", depth) < 0) { + return NULL; + } + _PyInterpreterFrame *f = _PyThreadState_GET()->cframe->current_frame; + while (f && (_PyFrame_IsIncomplete(f) || depth-- > 0)) { + f = f->previous; + } + if (f == NULL || f->f_funcobj == NULL) { + Py_RETURN_NONE; + } + PyObject *r = PyFunction_GetModule(f->f_funcobj); + if (!r) { + PyErr_Clear(); + r = Py_None; + } + return Py_NewRef(r); +} + + static PyMethodDef sys_methods[] = { /* Might as well keep this in alphabetic order */ SYS_ADDAUDITHOOK_METHODDEF @@ -2200,6 +2237,7 @@ static PyMethodDef sys_methods[] = { {"getsizeof", _PyCFunction_CAST(sys_getsizeof), METH_VARARGS | METH_KEYWORDS, getsizeof_doc}, SYS__GETFRAME_METHODDEF + SYS__GETFRAMEMODULENAME_METHODDEF SYS_GETWINDOWSVERSION_METHODDEF SYS__ENABLELEGACYWINDOWSFSENCODING_METHODDEF SYS_INTERN_METHODDEF From webhook-mailer at python.org Fri Jan 13 06:49:17 2023 From: webhook-mailer at python.org (zooba) Date: Fri, 13 Jan 2023 11:49:17 -0000 Subject: [Python-checkins] gh-100247: Fix py.exe launcher not using entire shebang command for finding custom commands (GH-100944) Message-ID: https://github.com/python/cpython/commit/468c3bf79890ef614764b4e7543608876c792794 commit: 468c3bf79890ef614764b4e7543608876c792794 branch: main author: Steve Dower committer: zooba date: 2023-01-13T11:49:01Z summary: gh-100247: Fix py.exe launcher not using entire shebang command for finding custom commands (GH-100944) files: A Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst M Doc/using/windows.rst M Lib/test/test_launcher.py M PC/launcher2.c diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index fdbe4c15a200..69bca4d7bd30 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -831,7 +831,7 @@ To allow shebang lines in Python scripts to be portable between Unix and Windows, this launcher supports a number of 'virtual' commands to specify which interpreter to use. The supported virtual commands are: -* ``/usr/bin/env python`` +* ``/usr/bin/env`` * ``/usr/bin/python`` * ``/usr/local/bin/python`` * ``python`` @@ -868,14 +868,28 @@ minor version. I.e. ``/usr/bin/python3.7-32`` will request usage of the The ``/usr/bin/env`` form of shebang line has one further special property. Before looking for installed Python interpreters, this form will search the -executable :envvar:`PATH` for a Python executable. This corresponds to the -behaviour of the Unix ``env`` program, which performs a :envvar:`PATH` search. +executable :envvar:`PATH` for a Python executable matching the name provided +as the first argument. This corresponds to the behaviour of the Unix ``env`` +program, which performs a :envvar:`PATH` search. If an executable matching the first argument after the ``env`` command cannot -be found, it will be handled as described below. Additionally, the environment -variable :envvar:`PYLAUNCHER_NO_SEARCH_PATH` may be set (to any value) to skip -this additional search. +be found, but the argument starts with ``python``, it will be handled as +described for the other virtual commands. +The environment variable :envvar:`PYLAUNCHER_NO_SEARCH_PATH` may be set +(to any value) to skip this search of :envvar:`PATH`. + +Shebang lines that do not match any of these patterns are looked up in the +``[commands]`` section of the launcher's :ref:`.INI file `. +This may be used to handle certain commands in a way that makes sense for your +system. The name of the command must be a single argument (no spaces), +and the value substituted is the full path to the executable (no arguments +may be added). -Shebang lines that do not match any of these patterns are treated as **Windows** +.. code-block:: ini + + [commands] + /bin/sh=C:\Program Files\Bash\bash.exe + +Any commands not found in the .INI file are treated as **Windows** executable paths that are absolute or relative to the directory containing the script file. This is a convenience for Windows-only scripts, such as those generated by an installer, since the behavior is not compatible with Unix-style shells. @@ -898,15 +912,16 @@ Then Python will be started with the ``-v`` option Customization ------------- +.. _launcher-ini: + Customization via INI files ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Two .ini files will be searched by the launcher - ``py.ini`` in the current -user's "application data" directory (i.e. the directory returned by calling the -Windows function ``SHGetFolderPath`` with ``CSIDL_LOCAL_APPDATA``) and ``py.ini`` in the -same directory as the launcher. The same .ini files are used for both the -'console' version of the launcher (i.e. py.exe) and for the 'windows' version -(i.e. pyw.exe). +user's application data directory (``%LOCALAPPDATA%`` or ``$env:LocalAppData``) +and ``py.ini`` in the same directory as the launcher. The same .ini files are +used for both the 'console' version of the launcher (i.e. py.exe) and for the +'windows' version (i.e. pyw.exe). Customization specified in the "application directory" will have precedence over the one next to the executable, so a user, who may not have write access to the diff --git a/Lib/test/test_launcher.py b/Lib/test/test_launcher.py index 47152d4a3c00..351a638c1dd3 100644 --- a/Lib/test/test_launcher.py +++ b/Lib/test/test_launcher.py @@ -67,12 +67,17 @@ ) -TEST_PY_COMMANDS = "\n".join([ +TEST_PY_DEFAULTS = "\n".join([ "[defaults]", - *[f"{k[3:].lower()}={v}" for k, v in TEST_PY_ENV.items()] + *[f"{k[3:].lower()}={v}" for k, v in TEST_PY_ENV.items()], ]) +TEST_PY_COMMANDS = "\n".join([ + "[commands]", + "test-command=TEST_EXE.exe", +]) + def create_registry_data(root, data): def _create_registry_data(root, key, value): if isinstance(value, dict): @@ -429,21 +434,21 @@ def test_search_major_2(self): self.assertTrue(data["env.tag"].startswith("2."), data["env.tag"]) def test_py_default(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): data = self.run_py(["-arg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) self.assertEqual("3.100", data["SearchInfo.tag"]) self.assertEqual("X.Y.exe -arg", data["stdout"].strip()) def test_py2_default(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): data = self.run_py(["-2", "-arg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) self.assertEqual("3.100-32", data["SearchInfo.tag"]) self.assertEqual("X.Y-32.exe -arg", data["stdout"].strip()) def test_py3_default(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): data = self.run_py(["-3", "-arg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) self.assertEqual("3.100-arm64", data["SearchInfo.tag"]) @@ -468,7 +473,7 @@ def test_py3_default_env(self): self.assertEqual("X.Y-arm64.exe -X fake_arg_for_test -arg", data["stdout"].strip()) def test_py_default_short_argv0(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): for argv0 in ['"py.exe"', 'py.exe', '"py"', 'py']: with self.subTest(argv0): data = self.run_py(["--version"], argv=f'{argv0} --version') @@ -518,7 +523,7 @@ def test_virtualenv_with_env(self): self.assertNotEqual(data2["SearchInfo.lowPriorityTag"], "True") def test_py_shebang(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! /usr/bin/python -prearg") as script: data = self.run_py([script, "-postarg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) @@ -526,7 +531,7 @@ def test_py_shebang(self): self.assertEqual(f"X.Y.exe -prearg {script} -postarg", data["stdout"].strip()) def test_python_shebang(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! python -prearg") as script: data = self.run_py([script, "-postarg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) @@ -534,7 +539,7 @@ def test_python_shebang(self): self.assertEqual(f"X.Y.exe -prearg {script} -postarg", data["stdout"].strip()) def test_py2_shebang(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! /usr/bin/python2 -prearg") as script: data = self.run_py([script, "-postarg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) @@ -542,7 +547,7 @@ def test_py2_shebang(self): self.assertEqual(f"X.Y-32.exe -prearg {script} -postarg", data["stdout"].strip()) def test_py3_shebang(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! /usr/bin/python3 -prearg") as script: data = self.run_py([script, "-postarg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) @@ -550,7 +555,7 @@ def test_py3_shebang(self): self.assertEqual(f"X.Y-arm64.exe -X fake_arg_for_test -prearg {script} -postarg", data["stdout"].strip()) def test_py_shebang_nl(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! /usr/bin/python -prearg\n") as script: data = self.run_py([script, "-postarg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) @@ -558,7 +563,7 @@ def test_py_shebang_nl(self): self.assertEqual(f"X.Y.exe -prearg {script} -postarg", data["stdout"].strip()) def test_py2_shebang_nl(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! /usr/bin/python2 -prearg\n") as script: data = self.run_py([script, "-postarg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) @@ -566,7 +571,7 @@ def test_py2_shebang_nl(self): self.assertEqual(f"X.Y-32.exe -prearg {script} -postarg", data["stdout"].strip()) def test_py3_shebang_nl(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! /usr/bin/python3 -prearg\n") as script: data = self.run_py([script, "-postarg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) @@ -574,7 +579,7 @@ def test_py3_shebang_nl(self): self.assertEqual(f"X.Y-arm64.exe -X fake_arg_for_test -prearg {script} -postarg", data["stdout"].strip()) def test_py_shebang_short_argv0(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! /usr/bin/python -prearg") as script: # Override argv to only pass "py.exe" as the command data = self.run_py([script, "-postarg"], argv=f'"py.exe" "{script}" -postarg') @@ -591,7 +596,7 @@ def test_py_handle_64_in_ini(self): def test_search_path(self): stem = Path(sys.executable).stem - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script(f"#! /usr/bin/env {stem} -prearg") as script: data = self.run_py( [script, "-postarg"], @@ -602,7 +607,7 @@ def test_search_path(self): def test_search_path_exe(self): # Leave the .exe on the name to ensure we don't add it a second time name = Path(sys.executable).name - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script(f"#! /usr/bin/env {name} -prearg") as script: data = self.run_py( [script, "-postarg"], @@ -612,7 +617,7 @@ def test_search_path_exe(self): def test_recursive_search_path(self): stem = self.get_py_exe().stem - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script(f"#! /usr/bin/env {stem}") as script: data = self.run_py( [script], @@ -673,3 +678,21 @@ def test_literal_shebang_quoted_escape(self): f'"{script.parent}\\some\\ random app" -witharg {script}', data["stdout"].strip(), ) + + def test_literal_shebang_command(self): + with self.py_ini(TEST_PY_COMMANDS): + with self.script('#! test-command arg1') as script: + data = self.run_py([script]) + self.assertEqual( + f"TEST_EXE.exe arg1 {script}", + data["stdout"].strip(), + ) + + def test_literal_shebang_invalid_template(self): + with self.script('#! /usr/bin/not-python arg1') as script: + data = self.run_py([script]) + expect = script.parent / "/usr/bin/not-python" + self.assertEqual( + f"{expect} arg1 {script}", + data["stdout"].strip(), + ) diff --git a/Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst b/Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst new file mode 100644 index 000000000000..7bfcbd7ddecf --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst @@ -0,0 +1,2 @@ +Restores support for the :file:`py.exe` launcher finding shebang commands in +its configuration file using the full command name. diff --git a/PC/launcher2.c b/PC/launcher2.c index 9b3db04aa48b..8371c6014cd9 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -16,6 +16,7 @@ #include #include #include +#include #define MS_WINDOWS #include "patchlevel.h" @@ -37,6 +38,7 @@ #define RC_INSTALLING 111 #define RC_NO_PYTHON_AT_ALL 112 #define RC_NO_SHEBANG 113 +#define RC_RECURSIVE_SHEBANG 114 static FILE * log_fp = NULL; @@ -702,16 +704,23 @@ _decodeShebang(SearchInfo *search, const char *buffer, int bufferLength, bool on bool -_shebangStartsWith(const wchar_t *buffer, int bufferLength, const wchar_t *prefix, const wchar_t **rest) +_shebangStartsWith(const wchar_t *buffer, int bufferLength, const wchar_t *prefix, const wchar_t **rest, int *firstArgumentLength) { int prefixLength = (int)wcsnlen_s(prefix, MAXLEN); - if (bufferLength < prefixLength) { + if (bufferLength < prefixLength || !_startsWithArgument(buffer, bufferLength, prefix, prefixLength)) { return false; } if (rest) { *rest = &buffer[prefixLength]; } - return _startsWithArgument(buffer, bufferLength, prefix, prefixLength); + if (firstArgumentLength) { + int i = prefixLength; + while (i < bufferLength && !isspace(buffer[i])) { + i += 1; + } + *firstArgumentLength = i - prefixLength; + } + return true; } @@ -723,26 +732,27 @@ searchPath(SearchInfo *search, const wchar_t *shebang, int shebangLength) } wchar_t *command; - if (!_shebangStartsWith(shebang, shebangLength, L"/usr/bin/env ", &command)) { + int commandLength; + if (!_shebangStartsWith(shebang, shebangLength, L"/usr/bin/env ", &command, &commandLength)) { return RC_NO_SHEBANG; } - wchar_t filename[MAXLEN]; - int lastDot = 0; - int commandLength = 0; - while (commandLength < MAXLEN && command[commandLength] && !isspace(command[commandLength])) { - if (command[commandLength] == L'.') { - lastDot = commandLength; - } - filename[commandLength] = command[commandLength]; - commandLength += 1; - } - if (!commandLength || commandLength == MAXLEN) { return RC_BAD_VIRTUAL_PATH; } - filename[commandLength] = L'\0'; + int lastDot = commandLength; + while (lastDot > 0 && command[lastDot] != L'.') { + lastDot -= 1; + } + if (!lastDot) { + lastDot = commandLength; + } + + wchar_t filename[MAXLEN]; + if (wcsncpy_s(filename, MAXLEN, command, lastDot)) { + return RC_BAD_VIRTUAL_PATH; + } const wchar_t *ext = L".exe"; // If the command already has an extension, we do not want to add it again @@ -780,7 +790,7 @@ searchPath(SearchInfo *search, const wchar_t *shebang, int shebangLength) if (GetModuleFileNameW(NULL, filename, MAXLEN) && 0 == _comparePath(filename, -1, buffer, -1)) { debug(L"# ignoring recursive shebang command\n"); - return RC_NO_SHEBANG; + return RC_RECURSIVE_SHEBANG; } wchar_t *buf = allocSearchInfoBuffer(search, n + 1); @@ -994,73 +1004,78 @@ checkShebang(SearchInfo *search) return exitCode; } - // Handle some known, case-sensitive shebang templates + // Handle some known, case-sensitive shebangs const wchar_t *command; int commandLength; + // Each template must end with "python" static const wchar_t *shebangTemplates[] = { - L"/usr/bin/env ", - L"/usr/bin/", - L"/usr/local/bin/", + L"/usr/bin/env python", + L"/usr/bin/python", + L"/usr/local/bin/python", L"python", NULL }; for (const wchar_t **tmpl = shebangTemplates; *tmpl; ++tmpl) { - if (_shebangStartsWith(shebang, shebangLength, *tmpl, &command)) { - commandLength = 0; - // Normally "python" is the start of the command, but we also need it - // as a shebang prefix for back-compat. We move the command marker back - // if we match on that one. - if (0 == wcscmp(*tmpl, L"python")) { - command -= 6; - } - while (command[commandLength] && !isspace(command[commandLength])) { - commandLength += 1; - } - if (!commandLength) { - } else if (_findCommand(search, command, commandLength)) { + // Just to make sure we don't mess this up in the future + assert(0 == wcscmp(L"python", (*tmpl) + wcslen(*tmpl) - 6)); + + if (_shebangStartsWith(shebang, shebangLength, *tmpl, &command, &commandLength)) { + // Search for "python{command}" overrides. All templates end with + // "python", so we prepend it by jumping back 6 characters + if (_findCommand(search, &command[-6], commandLength + 6)) { search->executableArgs = &command[commandLength]; search->executableArgsLength = shebangLength - commandLength; debug(L"# Treating shebang command '%.*s' as %s\n", - commandLength, command, search->executablePath); - } else if (_shebangStartsWith(command, commandLength, L"python", NULL)) { - search->tag = &command[6]; - search->tagLength = commandLength - 6; - // If we had 'python3.12.exe' then we want to strip the suffix - // off of the tag - if (search->tagLength > 4) { - const wchar_t *suffix = &search->tag[search->tagLength - 4]; - if (0 == _comparePath(suffix, 4, L".exe", -1)) { - search->tagLength -= 4; - } - } - // If we had 'python3_d' then we want to strip the '_d' (any - // '.exe' is already gone) - if (search->tagLength > 2) { - const wchar_t *suffix = &search->tag[search->tagLength - 2]; - if (0 == _comparePath(suffix, 2, L"_d", -1)) { - search->tagLength -= 2; - } + commandLength + 6, &command[-6], search->executablePath); + return 0; + } + + search->tag = command; + search->tagLength = commandLength; + // If we had 'python3.12.exe' then we want to strip the suffix + // off of the tag + if (search->tagLength > 4) { + const wchar_t *suffix = &search->tag[search->tagLength - 4]; + if (0 == _comparePath(suffix, 4, L".exe", -1)) { + search->tagLength -= 4; } - search->oldStyleTag = true; - search->executableArgs = &command[commandLength]; - search->executableArgsLength = shebangLength - commandLength; - if (search->tag && search->tagLength) { - debug(L"# Treating shebang command '%.*s' as 'py -%.*s'\n", - commandLength, command, search->tagLength, search->tag); - } else { - debug(L"# Treating shebang command '%.*s' as 'py'\n", - commandLength, command); + } + // If we had 'python3_d' then we want to strip the '_d' (any + // '.exe' is already gone) + if (search->tagLength > 2) { + const wchar_t *suffix = &search->tag[search->tagLength - 2]; + if (0 == _comparePath(suffix, 2, L"_d", -1)) { + search->tagLength -= 2; } + } + search->oldStyleTag = true; + search->executableArgs = &command[commandLength]; + search->executableArgsLength = shebangLength - commandLength; + if (search->tag && search->tagLength) { + debug(L"# Treating shebang command '%.*s' as 'py -%.*s'\n", + commandLength, command, search->tagLength, search->tag); } else { - debug(L"# Found shebang command but could not execute it: %.*s\n", + debug(L"# Treating shebang command '%.*s' as 'py'\n", commandLength, command); } - // search is done by this point return 0; } } + // Unrecognised executables are first tried as command aliases + commandLength = 0; + while (commandLength < shebangLength && !isspace(shebang[commandLength])) { + commandLength += 1; + } + if (_findCommand(search, shebang, commandLength)) { + search->executableArgs = &shebang[commandLength]; + search->executableArgsLength = shebangLength - commandLength; + debug(L"# Treating shebang command '%.*s' as %s\n", + commandLength, shebang, search->executablePath); + return 0; + } + // Unrecognised commands are joined to the script's directory and treated // as the executable path return _useShebangAsExecutable(search, shebang, shebangLength); @@ -2407,7 +2422,12 @@ performSearch(SearchInfo *search, EnvironmentInfo **envs) // Check for a shebang line in our script file // (or return quickly if no script file was specified) exitCode = checkShebang(search); - if (exitCode) { + switch (exitCode) { + case 0: + case RC_NO_SHEBANG: + case RC_RECURSIVE_SHEBANG: + break; + default: return exitCode; } From webhook-mailer at python.org Fri Jan 13 07:40:55 2023 From: webhook-mailer at python.org (serhiy-storchaka) Date: Fri, 13 Jan 2023 12:40:55 -0000 Subject: [Python-checkins] gh-100160: Restore and deprecate implicit creation of an event loop (GH-100410) Message-ID: https://github.com/python/cpython/commit/e5bd5ad70d9e549eeb80aadb4f3ccb0f2f23266d commit: e5bd5ad70d9e549eeb80aadb4f3ccb0f2f23266d branch: main author: Serhiy Storchaka committer: serhiy-storchaka date: 2023-01-13T14:40:29+02:00 summary: gh-100160: Restore and deprecate implicit creation of an event loop (GH-100410) Partially revert changes made in GH-93453. asyncio.DefaultEventLoopPolicy.get_event_loop() now emits a DeprecationWarning and creates and sets a new event loop instead of raising a RuntimeError if there is no current event loop set. Co-authored-by: Guido van Rossum files: A Misc/NEWS.d/next/Library/2022-12-21-17-49-50.gh-issue-100160.N0NHRj.rst M Doc/library/asyncio-eventloop.rst M Doc/library/asyncio-policy.rst M Doc/whatsnew/3.10.rst M Doc/whatsnew/3.12.rst M Lib/asyncio/events.py M Lib/test/test_asyncio/test_events.py M Lib/test/test_asyncio/test_unix_events.py diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 470d1aa130e4..db63a5dd11ad 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -48,7 +48,7 @@ an event loop: running event loop. If there is no running event loop set, the function will return - the result of calling ``get_event_loop_policy().get_event_loop()``. + the result of the ``get_event_loop_policy().get_event_loop()`` call. Because this function has rather complex behavior (especially when custom event loop policies are in use), using the @@ -59,11 +59,9 @@ an event loop: instead of using these lower level functions to manually create and close an event loop. - .. note:: - In Python versions 3.10.0--3.10.8 and 3.11.0 this function - (and other functions which used it implicitly) emitted a - :exc:`DeprecationWarning` if there was no running event loop, even if - the current loop was set. + .. deprecated:: 3.12 + Deprecation warning is emitted if there is no current event loop. + In some future Python release this will become an error. .. function:: set_event_loop(loop) diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst index ccd952449475..0d7821e608ec 100644 --- a/Doc/library/asyncio-policy.rst +++ b/Doc/library/asyncio-policy.rst @@ -116,9 +116,11 @@ asyncio ships with the following built-in policies: On Windows, :class:`ProactorEventLoop` is now used by default. - .. versionchanged:: 3.12 - :meth:`get_event_loop` now raises a :exc:`RuntimeError` if there is no - current event loop set. + .. deprecated:: 3.12 + The :meth:`get_event_loop` method of the default asyncio policy now emits + a :exc:`DeprecationWarning` if there is no current event loop set and it + decides to create one. + In some future Python release this will become an error. .. class:: WindowsSelectorEventLoopPolicy diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 8296fb040d67..f6a48ed2680c 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -1709,19 +1709,6 @@ Deprecated scheduled for removal in Python 3.12. (Contributed by Erlend E. Aasland in :issue:`42264`.) -* :func:`asyncio.get_event_loop` now emits a deprecation warning if there is - no running event loop. In the future it will be an alias of - :func:`~asyncio.get_running_loop`. - :mod:`asyncio` functions which implicitly create :class:`~asyncio.Future` - or :class:`~asyncio.Task` objects now emit - a deprecation warning if there is no running event loop and no explicit - *loop* argument is passed: :func:`~asyncio.ensure_future`, - :func:`~asyncio.wrap_future`, :func:`~asyncio.gather`, - :func:`~asyncio.shield`, :func:`~asyncio.as_completed` and constructors of - :class:`~asyncio.Future`, :class:`~asyncio.Task`, - :class:`~asyncio.StreamReader`, :class:`~asyncio.StreamReaderProtocol`. - (Contributed by Serhiy Storchaka in :issue:`39529`.) - * The undocumented built-in function ``sqlite3.enable_shared_cache`` is now deprecated, scheduled for removal in Python 3.12. Its use is strongly discouraged by the SQLite3 documentation. See `the SQLite3 docs diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 7d318cac0193..2c807caa0ed4 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -408,6 +408,11 @@ Deprecated :exc:`ImportWarning`). (Contributed by Brett Cannon in :gh:`65961`.) +* The :meth:`~asyncio.DefaultEventLoopPolicy.get_event_loop` method of the + default event loop policy now emits a :exc:`DeprecationWarning` if there + is no current event loop set and it decides to create one. + (Contributed by Serhiy Storchaka and Guido van Rossum in :gh:`100160`.) + Pending Removal in Python 3.13 ------------------------------ @@ -710,18 +715,6 @@ Changes in the Python API around process-global resources, which are best managed from the main interpreter. (Contributed by Dong-hee Na in :gh:`99127`.) -* :func:`asyncio.get_event_loop` and many other :mod:`asyncio` functions like - :func:`~asyncio.ensure_future`, :func:`~asyncio.shield` or - :func:`~asyncio.gather`, and also the - :meth:`~asyncio.BaseDefaultEventLoopPolicy.get_event_loop` method of - :class:`~asyncio.BaseDefaultEventLoopPolicy` now raise a :exc:`RuntimeError` - if called when there is no running event loop and the current event loop was - not set. - Previously they implicitly created and set a new current event loop. - :exc:`DeprecationWarning` is no longer emitted if there is no running - event loop but the current event loop is set in the policy. - (Contributed by Serhiy Storchaka in :gh:`93453`.) - Build Changes ============= diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index 6cff8c59ea47..ce44942186b2 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -619,7 +619,7 @@ def get_event_loop(self): Returns an event loop object implementing the BaseEventLoop interface, or raises an exception in case no event loop has been set for the - current context. + current context and the current policy does not specify to create one. It should never return None.""" raise NotImplementedError @@ -672,6 +672,28 @@ def get_event_loop(self): Returns an instance of EventLoop or raises an exception. """ + if (self._local._loop is None and + not self._local._set_called and + threading.current_thread() is threading.main_thread()): + stacklevel = 2 + try: + f = sys._getframe(1) + except AttributeError: + pass + else: + # Move up the call stack so that the warning is attached + # to the line outside asyncio itself. + while f: + module = f.f_globals.get('__name__') + if not (module == 'asyncio' or module.startswith('asyncio.')): + break + f = f.f_back + stacklevel += 1 + import warnings + warnings.warn('There is no current event loop', + DeprecationWarning, stacklevel=stacklevel) + self.set_event_loop(self.new_event_loop()) + if self._local._loop is None: raise RuntimeError('There is no current event loop in thread %r.' % threading.current_thread().name) diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 093f8c19b180..214544b89bc5 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -2614,8 +2614,33 @@ def test_event_loop_policy(self): def test_get_event_loop(self): policy = asyncio.DefaultEventLoopPolicy() self.assertIsNone(policy._local._loop) - with self.assertRaisesRegex(RuntimeError, 'no current event loop'): - policy.get_event_loop() + with self.assertWarns(DeprecationWarning) as cm: + loop = policy.get_event_loop() + self.assertEqual(cm.filename, __file__) + self.assertIsInstance(loop, asyncio.AbstractEventLoop) + + self.assertIs(policy._local._loop, loop) + self.assertIs(loop, policy.get_event_loop()) + loop.close() + + def test_get_event_loop_calls_set_event_loop(self): + policy = asyncio.DefaultEventLoopPolicy() + + with mock.patch.object( + policy, "set_event_loop", + wraps=policy.set_event_loop) as m_set_event_loop: + + with self.assertWarns(DeprecationWarning) as cm: + loop = policy.get_event_loop() + self.addCleanup(loop.close) + self.assertEqual(cm.filename, __file__) + + # policy._local._loop must be set through .set_event_loop() + # (the unix DefaultEventLoopPolicy needs this call to attach + # the child watcher correctly) + m_set_event_loop.assert_called_with(loop) + + loop.close() def test_get_event_loop_after_set_none(self): policy = asyncio.DefaultEventLoopPolicy() @@ -2801,8 +2826,10 @@ def test_get_event_loop_returns_running_loop2(self): loop = asyncio.new_event_loop() self.addCleanup(loop.close) - with self.assertRaisesRegex(RuntimeError, 'no current'): - asyncio.get_event_loop() + with self.assertWarns(DeprecationWarning) as cm: + loop2 = asyncio.get_event_loop() + self.addCleanup(loop2.close) + self.assertEqual(cm.filename, __file__) asyncio.set_event_loop(None) with self.assertRaisesRegex(RuntimeError, 'no current'): asyncio.get_event_loop() diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index 600a5900da08..33d0ea15c6de 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -1884,7 +1884,9 @@ async def test_fork_not_share_event_loop(self): if pid == 0: # child try: - loop = asyncio.get_event_loop_policy().get_event_loop() + with self.assertWarns(DeprecationWarning): + loop = asyncio.get_event_loop_policy().get_event_loop() + os.write(w, b'LOOP:' + str(id(loop)).encode()) except RuntimeError: os.write(w, b'NO LOOP') except: @@ -1893,7 +1895,9 @@ async def test_fork_not_share_event_loop(self): os._exit(0) else: # parent - self.assertEqual(os.read(r, 100), b'NO LOOP') + result = os.read(r, 100) + self.assertEqual(result[:5], b'LOOP:', result) + self.assertNotEqual(int(result[5:]), id(loop)) wait_process(pid, exitcode=0) @hashlib_helper.requires_hashdigest('md5') diff --git a/Misc/NEWS.d/next/Library/2022-12-21-17-49-50.gh-issue-100160.N0NHRj.rst b/Misc/NEWS.d/next/Library/2022-12-21-17-49-50.gh-issue-100160.N0NHRj.rst new file mode 100644 index 000000000000..d5cc785722d7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-21-17-49-50.gh-issue-100160.N0NHRj.rst @@ -0,0 +1,3 @@ +Emit a deprecation warning in +:meth:`asyncio.DefaultEventLoopPolicy.get_event_loop` if there is no current +event loop set and it decides to create one. From webhook-mailer at python.org Fri Jan 13 07:52:35 2023 From: webhook-mailer at python.org (miss-islington) Date: Fri, 13 Jan 2023 12:52:35 -0000 Subject: [Python-checkins] gh-100247: Fix py.exe launcher not using entire shebang command for finding custom commands (GH-100944) Message-ID: https://github.com/python/cpython/commit/6492492ce7844bf1193ff748be8c09e301b12cee commit: 6492492ce7844bf1193ff748be8c09e301b12cee branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-13T04:52:28-08:00 summary: gh-100247: Fix py.exe launcher not using entire shebang command for finding custom commands (GH-100944) (cherry picked from commit 468c3bf79890ef614764b4e7543608876c792794) Co-authored-by: Steve Dower files: A Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst M Doc/using/windows.rst M Lib/test/test_launcher.py M PC/launcher2.c diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index 4526dc34872d..7272f1b62c65 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -818,7 +818,7 @@ To allow shebang lines in Python scripts to be portable between Unix and Windows, this launcher supports a number of 'virtual' commands to specify which interpreter to use. The supported virtual commands are: -* ``/usr/bin/env python`` +* ``/usr/bin/env`` * ``/usr/bin/python`` * ``/usr/local/bin/python`` * ``python`` @@ -855,14 +855,28 @@ minor version. I.e. ``/usr/bin/python3.7-32`` will request usage of the The ``/usr/bin/env`` form of shebang line has one further special property. Before looking for installed Python interpreters, this form will search the -executable :envvar:`PATH` for a Python executable. This corresponds to the -behaviour of the Unix ``env`` program, which performs a :envvar:`PATH` search. +executable :envvar:`PATH` for a Python executable matching the name provided +as the first argument. This corresponds to the behaviour of the Unix ``env`` +program, which performs a :envvar:`PATH` search. If an executable matching the first argument after the ``env`` command cannot -be found, it will be handled as described below. Additionally, the environment -variable :envvar:`PYLAUNCHER_NO_SEARCH_PATH` may be set (to any value) to skip -this additional search. +be found, but the argument starts with ``python``, it will be handled as +described for the other virtual commands. +The environment variable :envvar:`PYLAUNCHER_NO_SEARCH_PATH` may be set +(to any value) to skip this search of :envvar:`PATH`. + +Shebang lines that do not match any of these patterns are looked up in the +``[commands]`` section of the launcher's :ref:`.INI file `. +This may be used to handle certain commands in a way that makes sense for your +system. The name of the command must be a single argument (no spaces), +and the value substituted is the full path to the executable (no arguments +may be added). -Shebang lines that do not match any of these patterns are treated as **Windows** +.. code-block:: ini + + [commands] + /bin/sh=C:\Program Files\Bash\bash.exe + +Any commands not found in the .INI file are treated as **Windows** executable paths that are absolute or relative to the directory containing the script file. This is a convenience for Windows-only scripts, such as those generated by an installer, since the behavior is not compatible with Unix-style shells. @@ -885,15 +899,16 @@ Then Python will be started with the ``-v`` option Customization ------------- +.. _launcher-ini: + Customization via INI files ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Two .ini files will be searched by the launcher - ``py.ini`` in the current -user's "application data" directory (i.e. the directory returned by calling the -Windows function ``SHGetFolderPath`` with ``CSIDL_LOCAL_APPDATA``) and ``py.ini`` in the -same directory as the launcher. The same .ini files are used for both the -'console' version of the launcher (i.e. py.exe) and for the 'windows' version -(i.e. pyw.exe). +user's application data directory (``%LOCALAPPDATA%`` or ``$env:LocalAppData``) +and ``py.ini`` in the same directory as the launcher. The same .ini files are +used for both the 'console' version of the launcher (i.e. py.exe) and for the +'windows' version (i.e. pyw.exe). Customization specified in the "application directory" will have precedence over the one next to the executable, so a user, who may not have write access to the diff --git a/Lib/test/test_launcher.py b/Lib/test/test_launcher.py index 3991a8b4606b..d7d51eb6ecaa 100644 --- a/Lib/test/test_launcher.py +++ b/Lib/test/test_launcher.py @@ -68,12 +68,17 @@ ) -TEST_PY_COMMANDS = "\n".join([ +TEST_PY_DEFAULTS = "\n".join([ "[defaults]", - *[f"{k[3:].lower()}={v}" for k, v in TEST_PY_ENV.items()] + *[f"{k[3:].lower()}={v}" for k, v in TEST_PY_ENV.items()], ]) +TEST_PY_COMMANDS = "\n".join([ + "[commands]", + "test-command=TEST_EXE.exe", +]) + def create_registry_data(root, data): def _create_registry_data(root, key, value): if isinstance(value, dict): @@ -430,21 +435,21 @@ def test_search_major_2(self): self.assertTrue(data["env.tag"].startswith("2."), data["env.tag"]) def test_py_default(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): data = self.run_py(["-arg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) self.assertEqual("3.100", data["SearchInfo.tag"]) self.assertEqual("X.Y.exe -arg", data["stdout"].strip()) def test_py2_default(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): data = self.run_py(["-2", "-arg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) self.assertEqual("3.100-32", data["SearchInfo.tag"]) self.assertEqual("X.Y-32.exe -arg", data["stdout"].strip()) def test_py3_default(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): data = self.run_py(["-3", "-arg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) self.assertEqual("3.100-arm64", data["SearchInfo.tag"]) @@ -469,7 +474,7 @@ def test_py3_default_env(self): self.assertEqual("X.Y-arm64.exe -X fake_arg_for_test -arg", data["stdout"].strip()) def test_py_default_short_argv0(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): for argv0 in ['"py.exe"', 'py.exe', '"py"', 'py']: with self.subTest(argv0): data = self.run_py(["--version"], argv=f'{argv0} --version') @@ -519,7 +524,7 @@ def test_virtualenv_with_env(self): self.assertNotEqual(data2["SearchInfo.lowPriorityTag"], "True") def test_py_shebang(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! /usr/bin/python -prearg") as script: data = self.run_py([script, "-postarg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) @@ -527,7 +532,7 @@ def test_py_shebang(self): self.assertEqual(f"X.Y.exe -prearg {script} -postarg", data["stdout"].strip()) def test_python_shebang(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! python -prearg") as script: data = self.run_py([script, "-postarg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) @@ -535,7 +540,7 @@ def test_python_shebang(self): self.assertEqual(f"X.Y.exe -prearg {script} -postarg", data["stdout"].strip()) def test_py2_shebang(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! /usr/bin/python2 -prearg") as script: data = self.run_py([script, "-postarg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) @@ -543,7 +548,7 @@ def test_py2_shebang(self): self.assertEqual(f"X.Y-32.exe -prearg {script} -postarg", data["stdout"].strip()) def test_py3_shebang(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! /usr/bin/python3 -prearg") as script: data = self.run_py([script, "-postarg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) @@ -551,7 +556,7 @@ def test_py3_shebang(self): self.assertEqual(f"X.Y-arm64.exe -X fake_arg_for_test -prearg {script} -postarg", data["stdout"].strip()) def test_py_shebang_nl(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! /usr/bin/python -prearg\n") as script: data = self.run_py([script, "-postarg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) @@ -559,7 +564,7 @@ def test_py_shebang_nl(self): self.assertEqual(f"X.Y.exe -prearg {script} -postarg", data["stdout"].strip()) def test_py2_shebang_nl(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! /usr/bin/python2 -prearg\n") as script: data = self.run_py([script, "-postarg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) @@ -567,7 +572,7 @@ def test_py2_shebang_nl(self): self.assertEqual(f"X.Y-32.exe -prearg {script} -postarg", data["stdout"].strip()) def test_py3_shebang_nl(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! /usr/bin/python3 -prearg\n") as script: data = self.run_py([script, "-postarg"]) self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) @@ -575,7 +580,7 @@ def test_py3_shebang_nl(self): self.assertEqual(f"X.Y-arm64.exe -X fake_arg_for_test -prearg {script} -postarg", data["stdout"].strip()) def test_py_shebang_short_argv0(self): - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script("#! /usr/bin/python -prearg") as script: # Override argv to only pass "py.exe" as the command data = self.run_py([script, "-postarg"], argv=f'"py.exe" "{script}" -postarg') @@ -592,7 +597,7 @@ def test_py_handle_64_in_ini(self): def test_search_path(self): stem = Path(sys.executable).stem - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script(f"#! /usr/bin/env {stem} -prearg") as script: data = self.run_py( [script, "-postarg"], @@ -603,7 +608,7 @@ def test_search_path(self): def test_search_path_exe(self): # Leave the .exe on the name to ensure we don't add it a second time name = Path(sys.executable).name - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script(f"#! /usr/bin/env {name} -prearg") as script: data = self.run_py( [script, "-postarg"], @@ -613,7 +618,7 @@ def test_search_path_exe(self): def test_recursive_search_path(self): stem = self.get_py_exe().stem - with self.py_ini(TEST_PY_COMMANDS): + with self.py_ini(TEST_PY_DEFAULTS): with self.script(f"#! /usr/bin/env {stem}") as script: data = self.run_py( [script], @@ -674,3 +679,21 @@ def test_literal_shebang_quoted_escape(self): f'"{script.parent}\\some\\ random app" -witharg {script}', data["stdout"].strip(), ) + + def test_literal_shebang_command(self): + with self.py_ini(TEST_PY_COMMANDS): + with self.script('#! test-command arg1') as script: + data = self.run_py([script]) + self.assertEqual( + f"TEST_EXE.exe arg1 {script}", + data["stdout"].strip(), + ) + + def test_literal_shebang_invalid_template(self): + with self.script('#! /usr/bin/not-python arg1') as script: + data = self.run_py([script]) + expect = script.parent / "/usr/bin/not-python" + self.assertEqual( + f"{expect} arg1 {script}", + data["stdout"].strip(), + ) diff --git a/Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst b/Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst new file mode 100644 index 000000000000..7bfcbd7ddecf --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-11-14-42-11.gh-issue-100247.YfEmSz.rst @@ -0,0 +1,2 @@ +Restores support for the :file:`py.exe` launcher finding shebang commands in +its configuration file using the full command name. diff --git a/PC/launcher2.c b/PC/launcher2.c index 9b3db04aa48b..8371c6014cd9 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -16,6 +16,7 @@ #include #include #include +#include #define MS_WINDOWS #include "patchlevel.h" @@ -37,6 +38,7 @@ #define RC_INSTALLING 111 #define RC_NO_PYTHON_AT_ALL 112 #define RC_NO_SHEBANG 113 +#define RC_RECURSIVE_SHEBANG 114 static FILE * log_fp = NULL; @@ -702,16 +704,23 @@ _decodeShebang(SearchInfo *search, const char *buffer, int bufferLength, bool on bool -_shebangStartsWith(const wchar_t *buffer, int bufferLength, const wchar_t *prefix, const wchar_t **rest) +_shebangStartsWith(const wchar_t *buffer, int bufferLength, const wchar_t *prefix, const wchar_t **rest, int *firstArgumentLength) { int prefixLength = (int)wcsnlen_s(prefix, MAXLEN); - if (bufferLength < prefixLength) { + if (bufferLength < prefixLength || !_startsWithArgument(buffer, bufferLength, prefix, prefixLength)) { return false; } if (rest) { *rest = &buffer[prefixLength]; } - return _startsWithArgument(buffer, bufferLength, prefix, prefixLength); + if (firstArgumentLength) { + int i = prefixLength; + while (i < bufferLength && !isspace(buffer[i])) { + i += 1; + } + *firstArgumentLength = i - prefixLength; + } + return true; } @@ -723,26 +732,27 @@ searchPath(SearchInfo *search, const wchar_t *shebang, int shebangLength) } wchar_t *command; - if (!_shebangStartsWith(shebang, shebangLength, L"/usr/bin/env ", &command)) { + int commandLength; + if (!_shebangStartsWith(shebang, shebangLength, L"/usr/bin/env ", &command, &commandLength)) { return RC_NO_SHEBANG; } - wchar_t filename[MAXLEN]; - int lastDot = 0; - int commandLength = 0; - while (commandLength < MAXLEN && command[commandLength] && !isspace(command[commandLength])) { - if (command[commandLength] == L'.') { - lastDot = commandLength; - } - filename[commandLength] = command[commandLength]; - commandLength += 1; - } - if (!commandLength || commandLength == MAXLEN) { return RC_BAD_VIRTUAL_PATH; } - filename[commandLength] = L'\0'; + int lastDot = commandLength; + while (lastDot > 0 && command[lastDot] != L'.') { + lastDot -= 1; + } + if (!lastDot) { + lastDot = commandLength; + } + + wchar_t filename[MAXLEN]; + if (wcsncpy_s(filename, MAXLEN, command, lastDot)) { + return RC_BAD_VIRTUAL_PATH; + } const wchar_t *ext = L".exe"; // If the command already has an extension, we do not want to add it again @@ -780,7 +790,7 @@ searchPath(SearchInfo *search, const wchar_t *shebang, int shebangLength) if (GetModuleFileNameW(NULL, filename, MAXLEN) && 0 == _comparePath(filename, -1, buffer, -1)) { debug(L"# ignoring recursive shebang command\n"); - return RC_NO_SHEBANG; + return RC_RECURSIVE_SHEBANG; } wchar_t *buf = allocSearchInfoBuffer(search, n + 1); @@ -994,73 +1004,78 @@ checkShebang(SearchInfo *search) return exitCode; } - // Handle some known, case-sensitive shebang templates + // Handle some known, case-sensitive shebangs const wchar_t *command; int commandLength; + // Each template must end with "python" static const wchar_t *shebangTemplates[] = { - L"/usr/bin/env ", - L"/usr/bin/", - L"/usr/local/bin/", + L"/usr/bin/env python", + L"/usr/bin/python", + L"/usr/local/bin/python", L"python", NULL }; for (const wchar_t **tmpl = shebangTemplates; *tmpl; ++tmpl) { - if (_shebangStartsWith(shebang, shebangLength, *tmpl, &command)) { - commandLength = 0; - // Normally "python" is the start of the command, but we also need it - // as a shebang prefix for back-compat. We move the command marker back - // if we match on that one. - if (0 == wcscmp(*tmpl, L"python")) { - command -= 6; - } - while (command[commandLength] && !isspace(command[commandLength])) { - commandLength += 1; - } - if (!commandLength) { - } else if (_findCommand(search, command, commandLength)) { + // Just to make sure we don't mess this up in the future + assert(0 == wcscmp(L"python", (*tmpl) + wcslen(*tmpl) - 6)); + + if (_shebangStartsWith(shebang, shebangLength, *tmpl, &command, &commandLength)) { + // Search for "python{command}" overrides. All templates end with + // "python", so we prepend it by jumping back 6 characters + if (_findCommand(search, &command[-6], commandLength + 6)) { search->executableArgs = &command[commandLength]; search->executableArgsLength = shebangLength - commandLength; debug(L"# Treating shebang command '%.*s' as %s\n", - commandLength, command, search->executablePath); - } else if (_shebangStartsWith(command, commandLength, L"python", NULL)) { - search->tag = &command[6]; - search->tagLength = commandLength - 6; - // If we had 'python3.12.exe' then we want to strip the suffix - // off of the tag - if (search->tagLength > 4) { - const wchar_t *suffix = &search->tag[search->tagLength - 4]; - if (0 == _comparePath(suffix, 4, L".exe", -1)) { - search->tagLength -= 4; - } - } - // If we had 'python3_d' then we want to strip the '_d' (any - // '.exe' is already gone) - if (search->tagLength > 2) { - const wchar_t *suffix = &search->tag[search->tagLength - 2]; - if (0 == _comparePath(suffix, 2, L"_d", -1)) { - search->tagLength -= 2; - } + commandLength + 6, &command[-6], search->executablePath); + return 0; + } + + search->tag = command; + search->tagLength = commandLength; + // If we had 'python3.12.exe' then we want to strip the suffix + // off of the tag + if (search->tagLength > 4) { + const wchar_t *suffix = &search->tag[search->tagLength - 4]; + if (0 == _comparePath(suffix, 4, L".exe", -1)) { + search->tagLength -= 4; } - search->oldStyleTag = true; - search->executableArgs = &command[commandLength]; - search->executableArgsLength = shebangLength - commandLength; - if (search->tag && search->tagLength) { - debug(L"# Treating shebang command '%.*s' as 'py -%.*s'\n", - commandLength, command, search->tagLength, search->tag); - } else { - debug(L"# Treating shebang command '%.*s' as 'py'\n", - commandLength, command); + } + // If we had 'python3_d' then we want to strip the '_d' (any + // '.exe' is already gone) + if (search->tagLength > 2) { + const wchar_t *suffix = &search->tag[search->tagLength - 2]; + if (0 == _comparePath(suffix, 2, L"_d", -1)) { + search->tagLength -= 2; } + } + search->oldStyleTag = true; + search->executableArgs = &command[commandLength]; + search->executableArgsLength = shebangLength - commandLength; + if (search->tag && search->tagLength) { + debug(L"# Treating shebang command '%.*s' as 'py -%.*s'\n", + commandLength, command, search->tagLength, search->tag); } else { - debug(L"# Found shebang command but could not execute it: %.*s\n", + debug(L"# Treating shebang command '%.*s' as 'py'\n", commandLength, command); } - // search is done by this point return 0; } } + // Unrecognised executables are first tried as command aliases + commandLength = 0; + while (commandLength < shebangLength && !isspace(shebang[commandLength])) { + commandLength += 1; + } + if (_findCommand(search, shebang, commandLength)) { + search->executableArgs = &shebang[commandLength]; + search->executableArgsLength = shebangLength - commandLength; + debug(L"# Treating shebang command '%.*s' as %s\n", + commandLength, shebang, search->executablePath); + return 0; + } + // Unrecognised commands are joined to the script's directory and treated // as the executable path return _useShebangAsExecutable(search, shebang, shebangLength); @@ -2407,7 +2422,12 @@ performSearch(SearchInfo *search, EnvironmentInfo **envs) // Check for a shebang line in our script file // (or return quickly if no script file was specified) exitCode = checkShebang(search); - if (exitCode) { + switch (exitCode) { + case 0: + case RC_NO_SHEBANG: + case RC_RECURSIVE_SHEBANG: + break; + default: return exitCode; } From webhook-mailer at python.org Fri Jan 13 10:58:01 2023 From: webhook-mailer at python.org (markshannon) Date: Fri, 13 Jan 2023 15:58:01 -0000 Subject: [Python-checkins] Don't double count misses. (GH-100984) Message-ID: https://github.com/python/cpython/commit/c00eb1eae6af3ee5b7e314add4606da4521bb8c5 commit: c00eb1eae6af3ee5b7e314add4606da4521bb8c5 branch: main author: Mark Shannon committer: markshannon date: 2023-01-13T15:57:39Z summary: Don't double count misses. (GH-100984) files: M Tools/scripts/summarize_stats.py diff --git a/Tools/scripts/summarize_stats.py b/Tools/scripts/summarize_stats.py index 1c8d10f70277..ce25374f3a9a 100644 --- a/Tools/scripts/summarize_stats.py +++ b/Tools/scripts/summarize_stats.py @@ -404,6 +404,9 @@ def emit_specialization_overview(opcode_stats, total): total = 0 counts = [] for i, opcode_stat in enumerate(opcode_stats): + # Avoid double counting misses + if title == "Misses" and "specializable" in opcode_stat: + continue value = opcode_stat.get(field, 0) counts.append((value, opname[i])) total += value From webhook-mailer at python.org Fri Jan 13 16:25:03 2023 From: webhook-mailer at python.org (gvanrossum) Date: Fri, 13 Jan 2023 21:25:03 -0000 Subject: [Python-checkins] GH-100573: Fix server hang caused by os.stat() on named pipe (Windows) (#100959) Message-ID: https://github.com/python/cpython/commit/1bc7a736837272b15ad3a7aa472977bc720d1033 commit: 1bc7a736837272b15ad3a7aa472977bc720d1033 branch: main author: Guido van Rossum committer: gvanrossum date: 2023-01-13T13:24:57-08:00 summary: GH-100573: Fix server hang caused by os.stat() on named pipe (Windows) (#100959) files: A Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst M Lib/asyncio/windows_events.py M Lib/test/test_asyncio/test_windows_events.py diff --git a/Lib/asyncio/windows_events.py b/Lib/asyncio/windows_events.py index 4dad436fb418..c9a5fb841cb1 100644 --- a/Lib/asyncio/windows_events.py +++ b/Lib/asyncio/windows_events.py @@ -366,6 +366,10 @@ def loop_accept_pipe(f=None): return f = self._proactor.accept_pipe(pipe) + except BrokenPipeError: + if pipe and pipe.fileno() != -1: + pipe.close() + self.call_soon(loop_accept_pipe) except OSError as exc: if pipe and pipe.fileno() != -1: self.call_exception_handler({ @@ -377,6 +381,7 @@ def loop_accept_pipe(f=None): elif self._debug: logger.warning("Accept pipe failed on pipe %r", pipe, exc_info=True) + self.call_soon(loop_accept_pipe) except exceptions.CancelledError: if pipe: pipe.close() diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index 5033acc05248..a36119a8004f 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -250,6 +250,46 @@ def test_address_argument_type_error(self): proactor.sendto(sock, b'abc', addr=bad_address) sock.close() + def test_client_pipe_stat(self): + res = self.loop.run_until_complete(self._test_client_pipe_stat()) + self.assertEqual(res, 'done') + + async def _test_client_pipe_stat(self): + # Regression test for https://github.com/python/cpython/issues/100573 + ADDRESS = r'\\.\pipe\test_client_pipe_stat-%s' % os.getpid() + + async def probe(): + # See https://github.com/python/cpython/pull/100959#discussion_r1068533658 + h = _overlapped.ConnectPipe(ADDRESS) + try: + _winapi.CloseHandle(_overlapped.ConnectPipe(ADDRESS)) + except OSError as e: + if e.winerror != _overlapped.ERROR_PIPE_BUSY: + raise + finally: + _winapi.CloseHandle(h) + + with self.assertRaises(FileNotFoundError): + await probe() + + [server] = await self.loop.start_serving_pipe(asyncio.Protocol, ADDRESS) + self.assertIsInstance(server, windows_events.PipeServer) + + errors = [] + self.loop.set_exception_handler(lambda _, data: errors.append(data)) + + for i in range(5): + await self.loop.create_task(probe()) + + self.assertEqual(len(errors), 0, errors) + + server.close() + + with self.assertRaises(FileNotFoundError): + await probe() + + return "done" + class WinPolicyTests(test_utils.TestCase): diff --git a/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst b/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst new file mode 100644 index 000000000000..97b95d18d1e4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst @@ -0,0 +1 @@ +Fix a Windows :mod:`asyncio` bug with named pipes where a client doing ``os.stat()`` on the pipe would cause an error in the server that disabled serving future requests. From webhook-mailer at python.org Fri Jan 13 16:58:06 2023 From: webhook-mailer at python.org (gvanrossum) Date: Fri, 13 Jan 2023 21:58:06 -0000 Subject: [Python-checkins] [3.10] GH-100573: Fix server hang caused by os.stat() on named pipe (Windows) (GH-100959) (#101020) Message-ID: https://github.com/python/cpython/commit/94f9198f58371ee0dc93e874b685545dcbd55eac commit: 94f9198f58371ee0dc93e874b685545dcbd55eac branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: gvanrossum date: 2023-01-13T13:57:52-08:00 summary: [3.10] GH-100573: Fix server hang caused by os.stat() on named pipe (Windows) (GH-100959) (#101020) (cherry picked from commit 1bc7a736837272b15ad3a7aa472977bc720d1033) files: A Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst M Lib/asyncio/windows_events.py M Lib/test/test_asyncio/test_windows_events.py diff --git a/Lib/asyncio/windows_events.py b/Lib/asyncio/windows_events.py index ed1cd19cf445..3204c7c9be96 100644 --- a/Lib/asyncio/windows_events.py +++ b/Lib/asyncio/windows_events.py @@ -366,6 +366,10 @@ def loop_accept_pipe(f=None): return f = self._proactor.accept_pipe(pipe) + except BrokenPipeError: + if pipe and pipe.fileno() != -1: + pipe.close() + self.call_soon(loop_accept_pipe) except OSError as exc: if pipe and pipe.fileno() != -1: self.call_exception_handler({ @@ -377,6 +381,7 @@ def loop_accept_pipe(f=None): elif self._debug: logger.warning("Accept pipe failed on pipe %r", pipe, exc_info=True) + self.call_soon(loop_accept_pipe) except exceptions.CancelledError: if pipe: pipe.close() diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index afd30288b50f..46eb7ecf7c4a 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -250,6 +250,46 @@ def test_address_argument_type_error(self): proactor.sendto(sock, b'abc', addr=bad_address) sock.close() + def test_client_pipe_stat(self): + res = self.loop.run_until_complete(self._test_client_pipe_stat()) + self.assertEqual(res, 'done') + + async def _test_client_pipe_stat(self): + # Regression test for https://github.com/python/cpython/issues/100573 + ADDRESS = r'\\.\pipe\test_client_pipe_stat-%s' % os.getpid() + + async def probe(): + # See https://github.com/python/cpython/pull/100959#discussion_r1068533658 + h = _overlapped.ConnectPipe(ADDRESS) + try: + _winapi.CloseHandle(_overlapped.ConnectPipe(ADDRESS)) + except OSError as e: + if e.winerror != _overlapped.ERROR_PIPE_BUSY: + raise + finally: + _winapi.CloseHandle(h) + + with self.assertRaises(FileNotFoundError): + await probe() + + [server] = await self.loop.start_serving_pipe(asyncio.Protocol, ADDRESS) + self.assertIsInstance(server, windows_events.PipeServer) + + errors = [] + self.loop.set_exception_handler(lambda _, data: errors.append(data)) + + for i in range(5): + await self.loop.create_task(probe()) + + self.assertEqual(len(errors), 0, errors) + + server.close() + + with self.assertRaises(FileNotFoundError): + await probe() + + return "done" + class WinPolicyTests(test_utils.TestCase): diff --git a/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst b/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst new file mode 100644 index 000000000000..97b95d18d1e4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst @@ -0,0 +1 @@ +Fix a Windows :mod:`asyncio` bug with named pipes where a client doing ``os.stat()`` on the pipe would cause an error in the server that disabled serving future requests. From webhook-mailer at python.org Fri Jan 13 16:58:26 2023 From: webhook-mailer at python.org (gvanrossum) Date: Fri, 13 Jan 2023 21:58:26 -0000 Subject: [Python-checkins] [3.11] GH-100573: Fix server hang caused by os.stat() on named pipe (Windows) (GH-100959) (#101019) Message-ID: https://github.com/python/cpython/commit/d06315a6fa7a6b7c1d3fd1b8fb2bb894aba5751d commit: d06315a6fa7a6b7c1d3fd1b8fb2bb894aba5751d branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: gvanrossum date: 2023-01-13T13:58:20-08:00 summary: [3.11] GH-100573: Fix server hang caused by os.stat() on named pipe (Windows) (GH-100959) (#101019) (cherry picked from commit 1bc7a736837272b15ad3a7aa472977bc720d1033) files: A Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst M Lib/asyncio/windows_events.py M Lib/test/test_asyncio/test_windows_events.py diff --git a/Lib/asyncio/windows_events.py b/Lib/asyncio/windows_events.py index 8ee7e1ebd94a..eb33551b4114 100644 --- a/Lib/asyncio/windows_events.py +++ b/Lib/asyncio/windows_events.py @@ -366,6 +366,10 @@ def loop_accept_pipe(f=None): return f = self._proactor.accept_pipe(pipe) + except BrokenPipeError: + if pipe and pipe.fileno() != -1: + pipe.close() + self.call_soon(loop_accept_pipe) except OSError as exc: if pipe and pipe.fileno() != -1: self.call_exception_handler({ @@ -377,6 +381,7 @@ def loop_accept_pipe(f=None): elif self._debug: logger.warning("Accept pipe failed on pipe %r", pipe, exc_info=True) + self.call_soon(loop_accept_pipe) except exceptions.CancelledError: if pipe: pipe.close() diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index 5033acc05248..a36119a8004f 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -250,6 +250,46 @@ def test_address_argument_type_error(self): proactor.sendto(sock, b'abc', addr=bad_address) sock.close() + def test_client_pipe_stat(self): + res = self.loop.run_until_complete(self._test_client_pipe_stat()) + self.assertEqual(res, 'done') + + async def _test_client_pipe_stat(self): + # Regression test for https://github.com/python/cpython/issues/100573 + ADDRESS = r'\\.\pipe\test_client_pipe_stat-%s' % os.getpid() + + async def probe(): + # See https://github.com/python/cpython/pull/100959#discussion_r1068533658 + h = _overlapped.ConnectPipe(ADDRESS) + try: + _winapi.CloseHandle(_overlapped.ConnectPipe(ADDRESS)) + except OSError as e: + if e.winerror != _overlapped.ERROR_PIPE_BUSY: + raise + finally: + _winapi.CloseHandle(h) + + with self.assertRaises(FileNotFoundError): + await probe() + + [server] = await self.loop.start_serving_pipe(asyncio.Protocol, ADDRESS) + self.assertIsInstance(server, windows_events.PipeServer) + + errors = [] + self.loop.set_exception_handler(lambda _, data: errors.append(data)) + + for i in range(5): + await self.loop.create_task(probe()) + + self.assertEqual(len(errors), 0, errors) + + server.close() + + with self.assertRaises(FileNotFoundError): + await probe() + + return "done" + class WinPolicyTests(test_utils.TestCase): diff --git a/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst b/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst new file mode 100644 index 000000000000..97b95d18d1e4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst @@ -0,0 +1 @@ +Fix a Windows :mod:`asyncio` bug with named pipes where a client doing ``os.stat()`` on the pipe would cause an error in the server that disabled serving future requests. From webhook-mailer at python.org Fri Jan 13 19:05:49 2023 From: webhook-mailer at python.org (miss-islington) Date: Sat, 14 Jan 2023 00:05:49 -0000 Subject: [Python-checkins] gh-74033: Fix bug when Path takes and ignores **kwargs (GH-19632) Message-ID: https://github.com/python/cpython/commit/080cb27829aa1a461d80504b32c362cbc14a5a75 commit: 080cb27829aa1a461d80504b32c362cbc14a5a75 branch: main author: Yurii Karabas <1998uriyyo at gmail.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-13T16:05:43-08:00 summary: gh-74033: Fix bug when Path takes and ignores **kwargs (GH-19632) Fix a bug where `Path` takes and ignores `**kwargs` by adding to `PurePath` class `__init__` method which can take only positional arguments. Automerge-Triggered-By: GH:brettcannon files: A Misc/NEWS.d/next/Library/2020-04-18-17-45-03.bpo-29847.Uxtbq0.rst M Lib/pathlib.py M Lib/test/test_pathlib.py diff --git a/Lib/pathlib.py b/Lib/pathlib.py index a0678f61b632..ae7a62f8a4cd 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -713,6 +713,10 @@ class Path(PurePath): __slots__ = () def __new__(cls, *args, **kwargs): + if kwargs: + msg = ("support for supplying keyword arguments to pathlib.PurePath " + "is deprecated and scheduled for removal in Python {remove}") + warnings._deprecated("pathlib.PurePath(**kwargs)", msg, remove=(3, 14)) if cls is Path: cls = WindowsPath if os.name == 'nt' else PosixPath self = cls._from_parts(args) diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 7d4d782cf5f0..1fe242b7f6ab 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -2571,6 +2571,11 @@ def test_complex_symlinks_relative(self): def test_complex_symlinks_relative_dot_dot(self): self._check_complex_symlinks(os.path.join('dirA', '..')) + def test_passing_kwargs_deprecated(self): + with self.assertWarns(DeprecationWarning): + self.cls(foo="bar") + + class WalkTests(unittest.TestCase): def setUp(self): diff --git a/Misc/NEWS.d/next/Library/2020-04-18-17-45-03.bpo-29847.Uxtbq0.rst b/Misc/NEWS.d/next/Library/2020-04-18-17-45-03.bpo-29847.Uxtbq0.rst new file mode 100644 index 000000000000..010d775a0d98 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-04-18-17-45-03.bpo-29847.Uxtbq0.rst @@ -0,0 +1 @@ +Fix a bug where :class:`pathlib.Path` accepted and ignored keyword arguments. Patch provided by Yurii Karabas. From webhook-mailer at python.org Fri Jan 13 19:53:11 2023 From: webhook-mailer at python.org (rhettinger) Date: Sat, 14 Jan 2023 00:53:11 -0000 Subject: [Python-checkins] Fix typo in docs (GH-101025) Message-ID: https://github.com/python/cpython/commit/010576c6ea7e687cf2cb572f3f40432df9692e4e commit: 010576c6ea7e687cf2cb572f3f40432df9692e4e branch: main author: Raymond Hettinger committer: rhettinger date: 2023-01-13T18:53:06-06:00 summary: Fix typo in docs (GH-101025) files: M Doc/library/math.rst diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 0e888c4d4e42..797f32408eac 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -299,7 +299,7 @@ Number-theoretic and representation functions Roughly equivalent to:: - sum(itertools.starmap(operator.mul, zip(p, q, strict=true))) + sum(itertools.starmap(operator.mul, zip(p, q, strict=True))) For float and mixed int/float inputs, the intermediate products and sums are computed with extended precision. From webhook-mailer at python.org Fri Jan 13 20:01:28 2023 From: webhook-mailer at python.org (rhettinger) Date: Sat, 14 Jan 2023 01:01:28 -0000 Subject: [Python-checkins] Add recipes to showcase tee(), zip*, batched, starmap, and product. (GH-101023) Message-ID: https://github.com/python/cpython/commit/6bde3d2fd323fde8b9abf19b996ef7e8f1c549c1 commit: 6bde3d2fd323fde8b9abf19b996ef7e8f1c549c1 branch: main author: Raymond Hettinger committer: rhettinger date: 2023-01-13T19:01:22-06:00 summary: Add recipes to showcase tee(), zip*, batched, starmap, and product. (GH-101023) files: M Doc/library/itertools.rst diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index e7d2e13626fa..8d83d92660d6 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -838,6 +838,22 @@ which incur interpreter overhead. "Returns the sequence elements n times" return chain.from_iterable(repeat(tuple(iterable), n)) + def sum_of_squares(it): + "Add up the squares of the input values." + # sum_of_squares([10, 20, 30]) -> 1400 + return math.sumprod(*tee(it)) + + def transpose(it): + "Swap the rows and columns of the input." + # transpose([(1, 2, 3), (11, 22, 33)]) --> (1, 11) (2, 22) (3, 33) + return zip(*it, strict=True) + + def matmul(m1, m2): + "Multiply two matrices." + # matmul([(7, 5), (3, 5)], [[2, 5], [7, 9]]) --> (49, 80), (41, 60) + n = len(m2[0]) + return batched(starmap(math.sumprod, product(m1, transpose(m2))), n) + def convolve(signal, kernel): # See: https://betterexplained.com/articles/intuitive-convolution/ # convolve(data, [0.25, 0.25, 0.25, 0.25]) --> Moving average (blur) @@ -1207,6 +1223,17 @@ which incur interpreter overhead. >>> list(ncycles('abc', 3)) ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c'] + >>> sum_of_squares([10, 20, 30]) + 1400 + + >>> list(transpose([(1, 2, 3), (11, 22, 33)])) + [(1, 11), (2, 22), (3, 33)] + + >>> list(matmul([(7, 5), (3, 5)], [[2, 5], [7, 9]])) + [(49, 80), (41, 60)] + >>> list(matmul([[2, 5], [7, 9], [3, 4]], [[7, 11, 5, 4, 9], [3, 5, 2, 6, 3]])) + [(29, 47, 20, 38, 33), (76, 122, 53, 82, 90), (33, 53, 23, 36, 39)] + >>> data = [20, 40, 24, 32, 20, 28, 16] >>> list(convolve(data, [0.25, 0.25, 0.25, 0.25])) [5.0, 15.0, 21.0, 29.0, 29.0, 26.0, 24.0, 16.0, 11.0, 4.0] From webhook-mailer at python.org Fri Jan 13 20:06:51 2023 From: webhook-mailer at python.org (gvanrossum) Date: Sat, 14 Jan 2023 01:06:51 -0000 Subject: [Python-checkins] GH-98831: Identify instructions that don't use oparg (#100957) Message-ID: https://github.com/python/cpython/commit/5134ef48784b07133ae40b09a8086c10f0cac324 commit: 5134ef48784b07133ae40b09a8086c10f0cac324 branch: main author: Guido van Rossum committer: gvanrossum date: 2023-01-13T17:06:45-08:00 summary: GH-98831: Identify instructions that don't use oparg (#100957) For these the instr_format field uses IX instead of IB. Register instructions use IX, IB, IBBX, IBBB, etc. Also: Include the closing '}' in Block.tokens, for completeness files: M Python/opcode_metadata.h M Tools/cases_generator/generate_cases.py M Tools/cases_generator/parser.py diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 3316ea66b4ef..4c8b9dde23a3 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -2,7 +2,7 @@ // from Python/bytecodes.c // Do not edit! enum Direction { DIR_NONE, DIR_READ, DIR_WRITE }; -enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC000, INSTR_FMT_IBCIB, INSTR_FMT_IBIB }; +enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC000, INSTR_FMT_IBCIB, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; static const struct { short n_popped; short n_pushed; @@ -12,7 +12,7 @@ static const struct { bool valid_entry; enum InstructionFormat instr_format; } _PyOpcode_opcode_metadata[256] = { - [NOP] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [NOP] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [RESUME] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_CLOSURE] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_FAST_CHECK] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, @@ -24,53 +24,53 @@ static const struct { [STORE_FAST__LOAD_FAST] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, [STORE_FAST__STORE_FAST] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, [LOAD_CONST__LOAD_FAST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, - [POP_TOP] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [PUSH_NULL] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [POP_TOP] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [PUSH_NULL] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [END_FOR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNARY_NEGATIVE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNARY_NOT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNARY_INVERT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BINARY_OP_MULTIPLY_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [BINARY_OP_MULTIPLY_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [BINARY_OP_SUBTRACT_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [BINARY_OP_SUBTRACT_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [BINARY_OP_ADD_UNICODE] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [BINARY_OP_INPLACE_ADD_UNICODE] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BINARY_OP_ADD_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [BINARY_OP_ADD_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [BINARY_SUBSCR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, - [BINARY_SLICE] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [STORE_SLICE] = { 4, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BINARY_SUBSCR_LIST_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, - [BINARY_SUBSCR_TUPLE_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, - [BINARY_SUBSCR_DICT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, - [BINARY_SUBSCR_GETITEM] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [UNARY_NEGATIVE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [UNARY_NOT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [UNARY_INVERT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BINARY_OP_MULTIPLY_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_MULTIPLY_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_SUBTRACT_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_SUBTRACT_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_ADD_UNICODE] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_INPLACE_ADD_UNICODE] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BINARY_OP_ADD_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_ADD_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_SUBSCR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [BINARY_SLICE] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [STORE_SLICE] = { 4, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BINARY_SUBSCR_LIST_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [BINARY_SUBSCR_TUPLE_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [BINARY_SUBSCR_DICT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [BINARY_SUBSCR_GETITEM] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, [LIST_APPEND] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [SET_ADD] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [STORE_SUBSCR] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [STORE_SUBSCR_LIST_INT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [STORE_SUBSCR_DICT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [DELETE_SUBSCR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_SUBSCR] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [STORE_SUBSCR_LIST_INT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [STORE_SUBSCR_DICT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [DELETE_SUBSCR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [CALL_INTRINSIC_1] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [RAISE_VARARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [INTERPRETER_EXIT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [RETURN_VALUE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [GET_AITER] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [GET_ANEXT] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [INTERPRETER_EXIT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [RETURN_VALUE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [GET_AITER] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [GET_ANEXT] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_AWAITABLE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [SEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [YIELD_VALUE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [POP_EXCEPT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [POP_EXCEPT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [RERAISE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [PREP_RERAISE_STAR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [END_ASYNC_FOR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CLEANUP_THROW] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ASSERTION_ERROR] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_BUILD_CLASS] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [PREP_RERAISE_STAR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [END_ASYNC_FOR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [CLEANUP_THROW] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_ASSERTION_ERROR] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_BUILD_CLASS] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [STORE_NAME] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [DELETE_NAME] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [UNPACK_SEQUENCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNPACK_SEQUENCE_TWO_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_SEQUENCE_TWO_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [UNPACK_SEQUENCE_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [UNPACK_SEQUENCE_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [UNPACK_EX] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, @@ -96,7 +96,7 @@ static const struct { [SET_UPDATE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [BUILD_SET] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [BUILD_MAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [SETUP_ANNOTATIONS] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SETUP_ANNOTATIONS] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [BUILD_CONST_KEY_MAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [DICT_UPDATE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [DICT_MERGE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, @@ -109,17 +109,17 @@ static const struct { [LOAD_ATTR_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_ATTR_PROPERTY] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [STORE_ATTR_INSTANCE_VALUE] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [STORE_ATTR_INSTANCE_VALUE] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, [STORE_ATTR_WITH_HINT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, - [STORE_ATTR_SLOT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [STORE_ATTR_SLOT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, [COMPARE_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [COMPARE_OP_FLOAT_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBCIB }, [COMPARE_OP_INT_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBCIB }, [COMPARE_OP_STR_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBCIB }, [IS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [CONTAINS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CHECK_EG_MATCH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CHECK_EXC_MATCH] = { 2, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CHECK_EG_MATCH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [CHECK_EXC_MATCH] = { 2, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [IMPORT_NAME] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [IMPORT_FROM] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [JUMP_FORWARD] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, @@ -131,25 +131,25 @@ static const struct { [JUMP_IF_FALSE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [JUMP_IF_TRUE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [JUMP_BACKWARD_NO_INTERRUPT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [GET_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [GET_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [MATCH_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [MATCH_MAPPING] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [MATCH_SEQUENCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [MATCH_KEYS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [GET_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [GET_YIELD_FROM_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MATCH_MAPPING] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [MATCH_SEQUENCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [MATCH_KEYS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [GET_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [GET_YIELD_FROM_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [FOR_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [FOR_ITER_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [FOR_ITER_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [FOR_ITER_RANGE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [FOR_ITER_GEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BEFORE_ASYNC_WITH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BEFORE_WITH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [WITH_EXCEPT_START] = { 4, 5, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [PUSH_EXC_INFO] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_METHOD_WITH_VALUES] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_METHOD_NO_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_METHOD_LAZY_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BEFORE_ASYNC_WITH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BEFORE_WITH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [WITH_EXCEPT_START] = { 4, 5, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [PUSH_EXC_INFO] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_ATTR_METHOD_WITH_VALUES] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_ATTR_METHOD_NO_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_ATTR_METHOD_LAZY_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [CALL_BOUND_METHOD_EXACT_ARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [KW_NAMES] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [CALL] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, @@ -171,12 +171,12 @@ static const struct { [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [CALL_FUNCTION_EX] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [MAKE_FUNCTION] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [RETURN_GENERATOR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [RETURN_GENERATOR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [BUILD_SLICE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [FORMAT_VALUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [COPY] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [BINARY_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [SWAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [EXTENDED_ARG] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CACHE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CACHE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, }; diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 608df6bb3aa4..4bd99b78f811 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -168,9 +168,14 @@ def __init__(self, inst: parser.InstDef): break self.unmoved_names = frozenset(unmoved_names) if self.register: - fmt = "IBBB" + num_regs = len(self.input_effects) + len(self.output_effects) + num_dummies = (num_regs // 2) * 2 + 1 - num_regs + fmt = "I" + "B"*num_regs + "X"*num_dummies else: - fmt = "IB" + if variable_used(inst.block, "oparg"): + fmt = "IB" + else: + fmt = "IX" cache = "C" for ce in self.cache_effects: for _ in range(ce.size): @@ -894,6 +899,11 @@ def always_exits(lines: list[str]) -> bool: ) +def variable_used(block: parser.Block, name: str) -> bool: + """Determine whether a variable with a given name is used in a block.""" + return any(token.kind == "IDENTIFIER" and token.text == name for token in block.tokens) + + def main(): """Parse command line, parse input, analyze, write output.""" args = arg_parser.parse_args() # Prints message and sys.exit(2) on error diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py index 4885394bf6b1..fad0cb90c69e 100644 --- a/Tools/cases_generator/parser.py +++ b/Tools/cases_generator/parser.py @@ -338,13 +338,13 @@ def c_blob(self) -> list[lx.Token]: tokens: list[lx.Token] = [] level = 0 while tkn := self.next(raw=True): + tokens.append(tkn) if tkn.kind in (lx.LBRACE, lx.LPAREN, lx.LBRACKET): level += 1 elif tkn.kind in (lx.RBRACE, lx.RPAREN, lx.RBRACKET): level -= 1 if level <= 0: break - tokens.append(tkn) return tokens From webhook-mailer at python.org Fri Jan 13 23:32:38 2023 From: webhook-mailer at python.org (rhettinger) Date: Sat, 14 Jan 2023 04:32:38 -0000 Subject: [Python-checkins] [3.11] Add recipes to showcase tee(), zip*, batched, starmap, and product. (GH-101028) Message-ID: https://github.com/python/cpython/commit/ba88628808af14ec904a9afe3b72dca9159d7ce8 commit: ba88628808af14ec904a9afe3b72dca9159d7ce8 branch: 3.11 author: Raymond Hettinger committer: rhettinger date: 2023-01-13T22:32:32-06:00 summary: [3.11] Add recipes to showcase tee(), zip*, batched, starmap, and product. (GH-101028) files: M Doc/library/itertools.rst diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index a1d1ef67bebd..06c73fc23490 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -799,10 +799,50 @@ which incur interpreter overhead. "Returns the sequence elements n times" return chain.from_iterable(repeat(tuple(iterable), n)) + def batched(iterable, n): + "Batch data into tuples of length n. The last batch may be shorter." + # batched('ABCDEFG', 3) --> ABC DEF G + if n < 1: + raise ValueError('n must be at least one') + it = iter(iterable) + while (batch := tuple(islice(it, n))): + yield batch + + def grouper(iterable, n, *, incomplete='fill', fillvalue=None): + "Collect data into non-overlapping fixed-length chunks or blocks" + # grouper('ABCDEFG', 3, fillvalue='x') --> ABC DEF Gxx + # grouper('ABCDEFG', 3, incomplete='strict') --> ABC DEF ValueError + # grouper('ABCDEFG', 3, incomplete='ignore') --> ABC DEF + args = [iter(iterable)] * n + if incomplete == 'fill': + return zip_longest(*args, fillvalue=fillvalue) + if incomplete == 'strict': + return zip(*args, strict=True) + if incomplete == 'ignore': + return zip(*args) + else: + raise ValueError('Expected fill, strict, or ignore') + def sumprod(vec1, vec2): "Compute a sum of products." return sum(starmap(operator.mul, zip(vec1, vec2, strict=True))) + def sum_of_squares(it): + "Add up the squares of the input values." + # sum_of_squares([10, 20, 30]) -> 1400 + return sumprod(*tee(it)) + + def transpose(it): + "Swap the rows and columns of the input." + # transpose([(1, 2, 3), (11, 22, 33)]) --> (1, 11) (2, 22) (3, 33) + return zip(*it, strict=True) + + def matmul(m1, m2): + "Multiply two matrices." + # matmul([(7, 5), (3, 5)], [[2, 5], [7, 9]]) --> (49, 80), (41, 60) + n = len(m2[0]) + return batched(starmap(sumprod, product(m1, transpose(m2))), n) + def convolve(signal, kernel): # See: https://betterexplained.com/articles/intuitive-convolution/ # convolve(data, [0.25, 0.25, 0.25, 0.25]) --> Moving average (blur) @@ -886,30 +926,6 @@ which incur interpreter overhead. return starmap(func, repeat(args)) return starmap(func, repeat(args, times)) - def grouper(iterable, n, *, incomplete='fill', fillvalue=None): - "Collect data into non-overlapping fixed-length chunks or blocks" - # grouper('ABCDEFG', 3, fillvalue='x') --> ABC DEF Gxx - # grouper('ABCDEFG', 3, incomplete='strict') --> ABC DEF ValueError - # grouper('ABCDEFG', 3, incomplete='ignore') --> ABC DEF - args = [iter(iterable)] * n - if incomplete == 'fill': - return zip_longest(*args, fillvalue=fillvalue) - if incomplete == 'strict': - return zip(*args, strict=True) - if incomplete == 'ignore': - return zip(*args) - else: - raise ValueError('Expected fill, strict, or ignore') - - def batched(iterable, n): - "Batch data into tuples of length n. The last batch may be shorter." - # batched('ABCDEFG', 3) --> ABC DEF G - if n < 1: - raise ValueError('n must be at least one') - it = iter(iterable) - while (batch := tuple(islice(it, n))): - yield batch - def triplewise(iterable): "Return overlapping triplets from an iterable" # triplewise('ABCDEFG') --> ABC BCD CDE DEF EFG @@ -1184,6 +1200,17 @@ which incur interpreter overhead. >>> sumprod([1,2,3], [4,5,6]) 32 + >>> sum_of_squares([10, 20, 30]) + 1400 + + >>> list(transpose([(1, 2, 3), (11, 22, 33)])) + [(1, 11), (2, 22), (3, 33)] + + >>> list(matmul([(7, 5), (3, 5)], [[2, 5], [7, 9]])) + [(49, 80), (41, 60)] + >>> list(matmul([[2, 5], [7, 9], [3, 4]], [[7, 11, 5, 4, 9], [3, 5, 2, 6, 3]])) + [(29, 47, 20, 38, 33), (76, 122, 53, 82, 90), (33, 53, 23, 36, 39)] + >>> data = [20, 40, 24, 32, 20, 28, 16] >>> list(convolve(data, [0.25, 0.25, 0.25, 0.25])) [5.0, 15.0, 21.0, 29.0, 29.0, 26.0, 24.0, 16.0, 11.0, 4.0] From webhook-mailer at python.org Sat Jan 14 00:55:40 2023 From: webhook-mailer at python.org (rhettinger) Date: Sat, 14 Jan 2023 05:55:40 -0000 Subject: [Python-checkins] Sync-up parameter name in equivalent code snippet of `enumerate` (GH-101029) Message-ID: https://github.com/python/cpython/commit/ef633e5000222a3dba74473c49d6a81fca0a44ec commit: ef633e5000222a3dba74473c49d6a81fca0a44ec branch: main author: JustAnotherArchivist committer: rhettinger date: 2023-01-13T23:55:35-06:00 summary: Sync-up parameter name in equivalent code snippet of `enumerate` (GH-101029) files: M Doc/library/functions.rst diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index cc7142854b1b..658d6768457d 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -505,9 +505,9 @@ are always available. They are listed here in alphabetical order. Equivalent to:: - def enumerate(sequence, start=0): + def enumerate(iterable, start=0): n = start - for elem in sequence: + for elem in iterable: yield n, elem n += 1 From webhook-mailer at python.org Sat Jan 14 14:01:33 2023 From: webhook-mailer at python.org (warsaw) Date: Sat, 14 Jan 2023 19:01:33 -0000 Subject: [Python-checkins] gh-101021: Document binary parameters as bytes (#101024) Message-ID: https://github.com/python/cpython/commit/49cae39ef020eaf242607bb2d2d193760b9855a6 commit: 49cae39ef020eaf242607bb2d2d193760b9855a6 branch: main author: Bob Kline committer: warsaw date: 2023-01-14T11:01:27-08:00 summary: gh-101021: Document binary parameters as bytes (#101024) files: M Doc/library/email.mime.rst M Lib/email/mime/application.py M Lib/email/mime/audio.py M Lib/email/mime/image.py diff --git a/Doc/library/email.mime.rst b/Doc/library/email.mime.rst index 3fe5fe88a094..d7c0d203d191 100644 --- a/Doc/library/email.mime.rst +++ b/Doc/library/email.mime.rst @@ -114,9 +114,9 @@ Here are the classes: A subclass of :class:`~email.mime.nonmultipart.MIMENonMultipart`, the :class:`MIMEApplication` class is used to represent MIME message objects of - major type :mimetype:`application`. *_data* is a string containing the raw - byte data. Optional *_subtype* specifies the MIME subtype and defaults to - :mimetype:`octet-stream`. + major type :mimetype:`application`. *_data* contains the bytes for the raw + application data. Optional *_subtype* specifies the MIME subtype and defaults + to :mimetype:`octet-stream`. Optional *_encoder* is a callable (i.e. function) which will perform the actual encoding of the data for transport. This callable takes one argument, which is @@ -145,7 +145,7 @@ Here are the classes: A subclass of :class:`~email.mime.nonmultipart.MIMENonMultipart`, the :class:`MIMEAudio` class is used to create MIME message objects of major type - :mimetype:`audio`. *_audiodata* is a string containing the raw audio data. If + :mimetype:`audio`. *_audiodata* contains the bytes for the raw audio data. If this data can be decoded as au, wav, aiff, or aifc, then the subtype will be automatically included in the :mailheader:`Content-Type` header. Otherwise you can explicitly specify the audio subtype via the *_subtype* @@ -179,7 +179,7 @@ Here are the classes: A subclass of :class:`~email.mime.nonmultipart.MIMENonMultipart`, the :class:`MIMEImage` class is used to create MIME message objects of major type - :mimetype:`image`. *_imagedata* is a string containing the raw image data. If + :mimetype:`image`. *_imagedata* contains the bytes for the raw image data. If this data type can be detected (jpeg, png, gif, tiff, rgb, pbm, pgm, ppm, rast, xbm, bmp, webp, and exr attempted), then the subtype will be automatically included in the :mailheader:`Content-Type` header. Otherwise diff --git a/Lib/email/mime/application.py b/Lib/email/mime/application.py index 6877e554e102..f67cbad3f034 100644 --- a/Lib/email/mime/application.py +++ b/Lib/email/mime/application.py @@ -17,7 +17,7 @@ def __init__(self, _data, _subtype='octet-stream', _encoder=encoders.encode_base64, *, policy=None, **_params): """Create an application/* type MIME document. - _data is a string containing the raw application data. + _data contains the bytes for the raw application data. _subtype is the MIME content type subtype, defaulting to 'octet-stream'. diff --git a/Lib/email/mime/audio.py b/Lib/email/mime/audio.py index 8815f5c5ec06..065819b2a210 100644 --- a/Lib/email/mime/audio.py +++ b/Lib/email/mime/audio.py @@ -18,7 +18,7 @@ def __init__(self, _audiodata, _subtype=None, _encoder=encoders.encode_base64, *, policy=None, **_params): """Create an audio/* type MIME document. - _audiodata is a string containing the raw audio data. If this data + _audiodata contains the bytes for the raw audio data. If this data can be decoded as au, wav, aiff, or aifc, then the subtype will be automatically included in the Content-Type header. Otherwise, you can specify the specific audio subtype via the diff --git a/Lib/email/mime/image.py b/Lib/email/mime/image.py index e19dea91c0c9..4b7f2f9cbad4 100644 --- a/Lib/email/mime/image.py +++ b/Lib/email/mime/image.py @@ -17,7 +17,7 @@ def __init__(self, _imagedata, _subtype=None, _encoder=encoders.encode_base64, *, policy=None, **_params): """Create an image/* type MIME document. - _imagedata is a string containing the raw image data. If the data + _imagedata contains the bytes for the raw image data. If the data type can be detected (jpeg, png, gif, tiff, rgb, pbm, pgm, ppm, rast, xbm, bmp, webp, and exr attempted), then the subtype will be automatically included in the Content-Type header. Otherwise, you can From webhook-mailer at python.org Sat Jan 14 15:11:15 2023 From: webhook-mailer at python.org (gpshead) Date: Sat, 14 Jan 2023 20:11:15 -0000 Subject: [Python-checkins] gh-94518: [_posixsubprocess] Replace variable validity flags with reserved values (#94687) Message-ID: https://github.com/python/cpython/commit/124af17b6e49f0f22fbe646fb57800393235d704 commit: 124af17b6e49f0f22fbe646fb57800393235d704 branch: main author: Oleg Iarygin committer: gpshead date: 2023-01-14T12:11:04-08:00 summary: gh-94518: [_posixsubprocess] Replace variable validity flags with reserved values (#94687) Have _posixsubprocess.c stop using boolean flags to say if gid and uid values were supplied and action is required. Such an implicit "either initialized or look somewhere else" confused both the reader (another mental connection to constantly track between functions) and a compiler (warnings on potentially uninitialized variables being passed). Instead, we can utilize a special group/user id as a flag value -1 defined by POSIX but used nowhere else. Namely: gid: call_setgid = False ? gid = -1 uid: call_setuid = False ? uid = -1 groups: call_setgroups = False ? groups = NULL (obtained with (groups_list != Py_None) ? groups : NULL) This PR is required for #94519. files: A Misc/NEWS.d/next/Library/2022-07-22-13-38-37.gh-issue-94518._ZP0cz.rst M Modules/_posixsubprocess.c diff --git a/Misc/NEWS.d/next/Library/2022-07-22-13-38-37.gh-issue-94518._ZP0cz.rst b/Misc/NEWS.d/next/Library/2022-07-22-13-38-37.gh-issue-94518._ZP0cz.rst new file mode 100644 index 000000000000..a9d6d69f7eff --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-07-22-13-38-37.gh-issue-94518._ZP0cz.rst @@ -0,0 +1,2 @@ +``_posixsubprocess`` now initializes all UID and GID variables using a +reserved ``-1`` value instead of a separate flag. Patch by Oleg Iarygin. diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index b7563ee8250a..516f11d3543d 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -518,9 +518,9 @@ child_exec(char *const exec_array[], int errpipe_read, int errpipe_write, int close_fds, int restore_signals, int call_setsid, pid_t pgid_to_set, - int call_setgid, gid_t gid, - int call_setgroups, size_t groups_size, const gid_t *groups, - int call_setuid, uid_t uid, int child_umask, + gid_t gid, + Py_ssize_t groups_size, const gid_t *groups, + uid_t uid, int child_umask, const void *child_sigmask, PyObject *py_fds_to_keep, PyObject *preexec_fn, @@ -619,17 +619,17 @@ child_exec(char *const exec_array[], #endif #ifdef HAVE_SETGROUPS - if (call_setgroups) + if (groups_size > 0) POSIX_CALL(setgroups(groups_size, groups)); #endif /* HAVE_SETGROUPS */ #ifdef HAVE_SETREGID - if (call_setgid) + if (gid != (gid_t)-1) POSIX_CALL(setregid(gid, gid)); #endif /* HAVE_SETREGID */ #ifdef HAVE_SETREUID - if (call_setuid) + if (uid != (uid_t)-1) POSIX_CALL(setreuid(uid, uid)); #endif /* HAVE_SETREUID */ @@ -724,9 +724,9 @@ do_fork_exec(char *const exec_array[], int errpipe_read, int errpipe_write, int close_fds, int restore_signals, int call_setsid, pid_t pgid_to_set, - int call_setgid, gid_t gid, - int call_setgroups, size_t groups_size, const gid_t *groups, - int call_setuid, uid_t uid, int child_umask, + gid_t gid, + Py_ssize_t groups_size, const gid_t *groups, + uid_t uid, int child_umask, const void *child_sigmask, PyObject *py_fds_to_keep, PyObject *preexec_fn, @@ -738,9 +738,9 @@ do_fork_exec(char *const exec_array[], #ifdef VFORK_USABLE if (child_sigmask) { /* These are checked by our caller; verify them in debug builds. */ - assert(!call_setuid); - assert(!call_setgid); - assert(!call_setgroups); + assert(uid == (uid_t)-1); + assert(gid == (gid_t)-1); + assert(groups_size < 0); assert(preexec_fn == Py_None); pid = vfork(); @@ -777,8 +777,8 @@ do_fork_exec(char *const exec_array[], p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, errpipe_read, errpipe_write, close_fds, restore_signals, call_setsid, pgid_to_set, - call_setgid, gid, call_setgroups, groups_size, groups, - call_setuid, uid, child_umask, child_sigmask, + gid, groups_size, groups, + uid, child_umask, child_sigmask, py_fds_to_keep, preexec_fn, preexec_fn_args_tuple); _exit(255); return 0; /* Dead code to avoid a potential compiler warning. */ @@ -799,9 +799,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args) int errpipe_read, errpipe_write, close_fds, restore_signals; int call_setsid; pid_t pgid_to_set = -1; - int call_setgid = 0, call_setgroups = 0, call_setuid = 0; - uid_t uid; - gid_t gid, *groups = NULL; + gid_t *groups = NULL; int child_umask; PyObject *cwd_obj, *cwd_obj2 = NULL; const char *cwd; @@ -899,9 +897,6 @@ subprocess_fork_exec(PyObject *module, PyObject *args) if (groups_list != Py_None) { #ifdef HAVE_SETGROUPS - Py_ssize_t i; - gid_t gid; - if (!PyList_Check(groups_list)) { PyErr_SetString(PyExc_TypeError, "setgroups argument must be a list"); @@ -917,13 +912,17 @@ subprocess_fork_exec(PyObject *module, PyObject *args) goto cleanup; } - if ((groups = PyMem_RawMalloc(num_groups * sizeof(gid_t))) == NULL) { - PyErr_SetString(PyExc_MemoryError, - "failed to allocate memory for group list"); - goto cleanup; + /* Deliberately keep groups == NULL for num_groups == 0 */ + if (num_groups > 0) { + groups = PyMem_RawMalloc(num_groups * sizeof(gid_t)); + if (groups == NULL) { + PyErr_SetString(PyExc_MemoryError, + "failed to allocate memory for group list"); + goto cleanup; + } } - for (i = 0; i < num_groups; i++) { + for (Py_ssize_t i = 0; i < num_groups; i++) { PyObject *elem; elem = PySequence_GetItem(groups_list, i); if (!elem) @@ -934,6 +933,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args) Py_DECREF(elem); goto cleanup; } else { + gid_t gid; if (!_Py_Gid_Converter(elem, &gid)) { Py_DECREF(elem); PyErr_SetString(PyExc_ValueError, "invalid group id"); @@ -943,7 +943,6 @@ subprocess_fork_exec(PyObject *module, PyObject *args) } Py_DECREF(elem); } - call_setgroups = 1; #else /* HAVE_SETGROUPS */ PyErr_BadInternalCall(); @@ -951,26 +950,24 @@ subprocess_fork_exec(PyObject *module, PyObject *args) #endif /* HAVE_SETGROUPS */ } + gid_t gid = (gid_t)-1; if (gid_object != Py_None) { #ifdef HAVE_SETREGID if (!_Py_Gid_Converter(gid_object, &gid)) goto cleanup; - call_setgid = 1; - #else /* HAVE_SETREGID */ PyErr_BadInternalCall(); goto cleanup; #endif /* HAVE_SETREUID */ } + uid_t uid = (uid_t)-1; if (uid_object != Py_None) { #ifdef HAVE_SETREUID if (!_Py_Uid_Converter(uid_object, &uid)) goto cleanup; - call_setuid = 1; - #else /* HAVE_SETREUID */ PyErr_BadInternalCall(); goto cleanup; @@ -994,7 +991,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args) /* Use vfork() only if it's safe. See the comment above child_exec(). */ sigset_t old_sigs; if (preexec_fn == Py_None && allow_vfork && - !call_setuid && !call_setgid && !call_setgroups) { + uid == (uid_t)-1 && gid == (gid_t)-1 && num_groups < 0) { /* Block all signals to ensure that no signal handlers are run in the * child process while it shares memory with us. Note that signals * used internally by C libraries won't be blocked by @@ -1017,8 +1014,8 @@ subprocess_fork_exec(PyObject *module, PyObject *args) p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, errpipe_read, errpipe_write, close_fds, restore_signals, call_setsid, pgid_to_set, - call_setgid, gid, call_setgroups, num_groups, groups, - call_setuid, uid, child_umask, old_sigmask, + gid, num_groups, groups, + uid, child_umask, old_sigmask, py_fds_to_keep, preexec_fn, preexec_fn_args_tuple); /* Parent (original) process */ From webhook-mailer at python.org Sat Jan 14 15:22:20 2023 From: webhook-mailer at python.org (miss-islington) Date: Sat, 14 Jan 2023 20:22:20 -0000 Subject: [Python-checkins] gh-101021: Document binary parameters as bytes (GH-101024) Message-ID: https://github.com/python/cpython/commit/39c1f68fff41766da673ccfd219e1f159f903103 commit: 39c1f68fff41766da673ccfd219e1f159f903103 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-14T12:22:08-08:00 summary: gh-101021: Document binary parameters as bytes (GH-101024) (cherry picked from commit 49cae39ef020eaf242607bb2d2d193760b9855a6) Co-authored-by: Bob Kline files: M Doc/library/email.mime.rst M Lib/email/mime/application.py M Lib/email/mime/audio.py M Lib/email/mime/image.py diff --git a/Doc/library/email.mime.rst b/Doc/library/email.mime.rst index 3fe5fe88a094..d7c0d203d191 100644 --- a/Doc/library/email.mime.rst +++ b/Doc/library/email.mime.rst @@ -114,9 +114,9 @@ Here are the classes: A subclass of :class:`~email.mime.nonmultipart.MIMENonMultipart`, the :class:`MIMEApplication` class is used to represent MIME message objects of - major type :mimetype:`application`. *_data* is a string containing the raw - byte data. Optional *_subtype* specifies the MIME subtype and defaults to - :mimetype:`octet-stream`. + major type :mimetype:`application`. *_data* contains the bytes for the raw + application data. Optional *_subtype* specifies the MIME subtype and defaults + to :mimetype:`octet-stream`. Optional *_encoder* is a callable (i.e. function) which will perform the actual encoding of the data for transport. This callable takes one argument, which is @@ -145,7 +145,7 @@ Here are the classes: A subclass of :class:`~email.mime.nonmultipart.MIMENonMultipart`, the :class:`MIMEAudio` class is used to create MIME message objects of major type - :mimetype:`audio`. *_audiodata* is a string containing the raw audio data. If + :mimetype:`audio`. *_audiodata* contains the bytes for the raw audio data. If this data can be decoded as au, wav, aiff, or aifc, then the subtype will be automatically included in the :mailheader:`Content-Type` header. Otherwise you can explicitly specify the audio subtype via the *_subtype* @@ -179,7 +179,7 @@ Here are the classes: A subclass of :class:`~email.mime.nonmultipart.MIMENonMultipart`, the :class:`MIMEImage` class is used to create MIME message objects of major type - :mimetype:`image`. *_imagedata* is a string containing the raw image data. If + :mimetype:`image`. *_imagedata* contains the bytes for the raw image data. If this data type can be detected (jpeg, png, gif, tiff, rgb, pbm, pgm, ppm, rast, xbm, bmp, webp, and exr attempted), then the subtype will be automatically included in the :mailheader:`Content-Type` header. Otherwise diff --git a/Lib/email/mime/application.py b/Lib/email/mime/application.py index 6877e554e102..f67cbad3f034 100644 --- a/Lib/email/mime/application.py +++ b/Lib/email/mime/application.py @@ -17,7 +17,7 @@ def __init__(self, _data, _subtype='octet-stream', _encoder=encoders.encode_base64, *, policy=None, **_params): """Create an application/* type MIME document. - _data is a string containing the raw application data. + _data contains the bytes for the raw application data. _subtype is the MIME content type subtype, defaulting to 'octet-stream'. diff --git a/Lib/email/mime/audio.py b/Lib/email/mime/audio.py index 8815f5c5ec06..065819b2a210 100644 --- a/Lib/email/mime/audio.py +++ b/Lib/email/mime/audio.py @@ -18,7 +18,7 @@ def __init__(self, _audiodata, _subtype=None, _encoder=encoders.encode_base64, *, policy=None, **_params): """Create an audio/* type MIME document. - _audiodata is a string containing the raw audio data. If this data + _audiodata contains the bytes for the raw audio data. If this data can be decoded as au, wav, aiff, or aifc, then the subtype will be automatically included in the Content-Type header. Otherwise, you can specify the specific audio subtype via the diff --git a/Lib/email/mime/image.py b/Lib/email/mime/image.py index e19dea91c0c9..4b7f2f9cbad4 100644 --- a/Lib/email/mime/image.py +++ b/Lib/email/mime/image.py @@ -17,7 +17,7 @@ def __init__(self, _imagedata, _subtype=None, _encoder=encoders.encode_base64, *, policy=None, **_params): """Create an image/* type MIME document. - _imagedata is a string containing the raw image data. If the data + _imagedata contains the bytes for the raw image data. If the data type can be detected (jpeg, png, gif, tiff, rgb, pbm, pgm, ppm, rast, xbm, bmp, webp, and exr attempted), then the subtype will be automatically included in the Content-Type header. Otherwise, you can From webhook-mailer at python.org Sat Jan 14 17:58:11 2023 From: webhook-mailer at python.org (erlend-aasland) Date: Sat, 14 Jan 2023 22:58:11 -0000 Subject: [Python-checkins] gh-100668: Clarify how sqlite3 maps parameters onto placeholders (#100960) Message-ID: https://github.com/python/cpython/commit/206f05a46b426eb374f724f8e7cd42f2f9643bb8 commit: 206f05a46b426eb374f724f8e7cd42f2f9643bb8 branch: main author: Erlend E. Aasland committer: erlend-aasland date: 2023-01-14T23:58:06+01:00 summary: gh-100668: Clarify how sqlite3 maps parameters onto placeholders (#100960) Co-authored-by: C.A.M. Gerlach files: M Doc/library/sqlite3.rst diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 1708cd9d9580..ba72b96c6741 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1940,15 +1940,18 @@ close the single quote and inject ``OR TRUE`` to select all rows:: Instead, use the DB-API's parameter substitution. To insert a variable into a query string, use a placeholder in the string, and substitute the actual values into the query by providing them as a :class:`tuple` of values to the second -argument of the cursor's :meth:`~Cursor.execute` method. An SQL statement may -use one of two kinds of placeholders: question marks (qmark style) or named -placeholders (named style). For the qmark style, ``parameters`` must be a -:term:`sequence `. For the named style, it can be either a -:term:`sequence ` or :class:`dict` instance. The length of the -:term:`sequence ` must match the number of placeholders, or a -:exc:`ProgrammingError` is raised. If a :class:`dict` is given, it must contain -keys for all named parameters. Any extra items are ignored. Here's an example of -both styles: +argument of the cursor's :meth:`~Cursor.execute` method. + +An SQL statement may use one of two kinds of placeholders: +question marks (qmark style) or named placeholders (named style). +For the qmark style, *parameters* must be a +:term:`sequence` whose length must match the number of placeholders, +or a :exc:`ProgrammingError` is raised. +For the named style, *parameters* should be +an instance of a :class:`dict` (or a subclass), +which must contain keys for all named parameters; +any extra items are ignored. +Here's an example of both styles: .. testcode:: @@ -1975,6 +1978,11 @@ both styles: [('C', 1972)] +.. note:: + + :pep:`249` numeric placeholders are *not* supported. + If used, they will be interpreted as named placeholders. + .. _sqlite3-adapters: From webhook-mailer at python.org Sat Jan 14 18:04:58 2023 From: webhook-mailer at python.org (miss-islington) Date: Sat, 14 Jan 2023 23:04:58 -0000 Subject: [Python-checkins] gh-100668: Clarify how sqlite3 maps parameters onto placeholders (GH-100960) Message-ID: https://github.com/python/cpython/commit/c7aa39286bcb7d331214dda6a480b0b560965cdb commit: c7aa39286bcb7d331214dda6a480b0b560965cdb branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-14T15:04:53-08:00 summary: gh-100668: Clarify how sqlite3 maps parameters onto placeholders (GH-100960) (cherry picked from commit 206f05a46b426eb374f724f8e7cd42f2f9643bb8) Co-authored-by: Erlend E. Aasland Co-authored-by: C.A.M. Gerlach files: M Doc/library/sqlite3.rst diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 6d0422122a3d..c52bdd7ca4ac 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1823,15 +1823,18 @@ close the single quote and inject ``OR TRUE`` to select all rows:: Instead, use the DB-API's parameter substitution. To insert a variable into a query string, use a placeholder in the string, and substitute the actual values into the query by providing them as a :class:`tuple` of values to the second -argument of the cursor's :meth:`~Cursor.execute` method. An SQL statement may -use one of two kinds of placeholders: question marks (qmark style) or named -placeholders (named style). For the qmark style, ``parameters`` must be a -:term:`sequence `. For the named style, it can be either a -:term:`sequence ` or :class:`dict` instance. The length of the -:term:`sequence ` must match the number of placeholders, or a -:exc:`ProgrammingError` is raised. If a :class:`dict` is given, it must contain -keys for all named parameters. Any extra items are ignored. Here's an example of -both styles: +argument of the cursor's :meth:`~Cursor.execute` method. + +An SQL statement may use one of two kinds of placeholders: +question marks (qmark style) or named placeholders (named style). +For the qmark style, *parameters* must be a +:term:`sequence` whose length must match the number of placeholders, +or a :exc:`ProgrammingError` is raised. +For the named style, *parameters* should be +an instance of a :class:`dict` (or a subclass), +which must contain keys for all named parameters; +any extra items are ignored. +Here's an example of both styles: .. testcode:: @@ -1858,6 +1861,11 @@ both styles: [('C', 1972)] +.. note:: + + :pep:`249` numeric placeholders are *not* supported. + If used, they will be interpreted as named placeholders. + .. _sqlite3-adapters: From webhook-mailer at python.org Sat Jan 14 18:08:38 2023 From: webhook-mailer at python.org (miss-islington) Date: Sat, 14 Jan 2023 23:08:38 -0000 Subject: [Python-checkins] gh-100668: Clarify how sqlite3 maps parameters onto placeholders (GH-100960) Message-ID: https://github.com/python/cpython/commit/1981db9de1f2ce4057de8db1497cd924be82a717 commit: 1981db9de1f2ce4057de8db1497cd924be82a717 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-14T15:08:32-08:00 summary: gh-100668: Clarify how sqlite3 maps parameters onto placeholders (GH-100960) (cherry picked from commit 206f05a46b426eb374f724f8e7cd42f2f9643bb8) Co-authored-by: Erlend E. Aasland Co-authored-by: C.A.M. Gerlach files: M Doc/library/sqlite3.rst diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index ec1b643dac92..2cc0d8a4b4c3 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1437,15 +1437,18 @@ close the single quote and inject ``OR TRUE`` to select all rows:: Instead, use the DB-API's parameter substitution. To insert a variable into a query string, use a placeholder in the string, and substitute the actual values into the query by providing them as a :class:`tuple` of values to the second -argument of the cursor's :meth:`~Cursor.execute` method. An SQL statement may -use one of two kinds of placeholders: question marks (qmark style) or named -placeholders (named style). For the qmark style, ``parameters`` must be a -:term:`sequence `. For the named style, it can be either a -:term:`sequence ` or :class:`dict` instance. The length of the -:term:`sequence ` must match the number of placeholders, or a -:exc:`ProgrammingError` is raised. If a :class:`dict` is given, it must contain -keys for all named parameters. Any extra items are ignored. Here's an example of -both styles: +argument of the cursor's :meth:`~Cursor.execute` method. + +An SQL statement may use one of two kinds of placeholders: +question marks (qmark style) or named placeholders (named style). +For the qmark style, *parameters* must be a +:term:`sequence` whose length must match the number of placeholders, +or a :exc:`ProgrammingError` is raised. +For the named style, *parameters* should be +an instance of a :class:`dict` (or a subclass), +which must contain keys for all named parameters; +any extra items are ignored. +Here's an example of both styles: .. testcode:: @@ -1472,6 +1475,11 @@ both styles: [('C', 1972)] +.. note:: + + :pep:`249` numeric placeholders are *not* supported. + If used, they will be interpreted as named placeholders. + .. _sqlite3-adapters: From webhook-mailer at python.org Sun Jan 15 00:32:05 2023 From: webhook-mailer at python.org (warsaw) Date: Sun, 15 Jan 2023 05:32:05 -0000 Subject: [Python-checkins] [3.10] gh-101021: Document binary parameters as bytes (GH-101024). (#101052) Message-ID: https://github.com/python/cpython/commit/78c9f39352a04e07ee4de7746c797ca79b5270d7 commit: 78c9f39352a04e07ee4de7746c797ca79b5270d7 branch: 3.10 author: Barry Warsaw committer: warsaw date: 2023-01-14T21:31:59-08:00 summary: [3.10] gh-101021: Document binary parameters as bytes (GH-101024). (#101052) (cherry picked from commit 49cae39ef020eaf242607bb2d2d193760b9855a6) Co-authored-by: Bob Kline Co-authored-by: Bob Kline files: M Doc/library/email.mime.rst M Lib/email/mime/application.py M Lib/email/mime/audio.py M Lib/email/mime/image.py diff --git a/Doc/library/email.mime.rst b/Doc/library/email.mime.rst index f37f6aa28dec..98e1258eb352 100644 --- a/Doc/library/email.mime.rst +++ b/Doc/library/email.mime.rst @@ -114,9 +114,9 @@ Here are the classes: A subclass of :class:`~email.mime.nonmultipart.MIMENonMultipart`, the :class:`MIMEApplication` class is used to represent MIME message objects of - major type :mimetype:`application`. *_data* is a string containing the raw - byte data. Optional *_subtype* specifies the MIME subtype and defaults to - :mimetype:`octet-stream`. + major type :mimetype:`application`. *_data* contains the bytes for the raw + application data. Optional *_subtype* specifies the MIME subtype and defaults + to :mimetype:`octet-stream`. Optional *_encoder* is a callable (i.e. function) which will perform the actual encoding of the data for transport. This callable takes one argument, which is @@ -145,7 +145,7 @@ Here are the classes: A subclass of :class:`~email.mime.nonmultipart.MIMENonMultipart`, the :class:`MIMEAudio` class is used to create MIME message objects of major type - :mimetype:`audio`. *_audiodata* is a string containing the raw audio data. If + :mimetype:`audio`. *_audiodata* contains the bytes for the raw audio data. If this data can be decoded by the standard Python module :mod:`sndhdr`, then the subtype will be automatically included in the :mailheader:`Content-Type` header. Otherwise you can explicitly specify the audio subtype via the *_subtype* @@ -179,7 +179,7 @@ Here are the classes: A subclass of :class:`~email.mime.nonmultipart.MIMENonMultipart`, the :class:`MIMEImage` class is used to create MIME message objects of major type - :mimetype:`image`. *_imagedata* is a string containing the raw image data. If + :mimetype:`image`. *_imagedata* contains the bytes for the raw image data. If this data can be decoded by the standard Python module :mod:`imghdr`, then the subtype will be automatically included in the :mailheader:`Content-Type` header. Otherwise you can explicitly specify the image subtype via the *_subtype* diff --git a/Lib/email/mime/application.py b/Lib/email/mime/application.py index 6877e554e102..f67cbad3f034 100644 --- a/Lib/email/mime/application.py +++ b/Lib/email/mime/application.py @@ -17,7 +17,7 @@ def __init__(self, _data, _subtype='octet-stream', _encoder=encoders.encode_base64, *, policy=None, **_params): """Create an application/* type MIME document. - _data is a string containing the raw application data. + _data contains the bytes for the raw application data. _subtype is the MIME content type subtype, defaulting to 'octet-stream'. diff --git a/Lib/email/mime/audio.py b/Lib/email/mime/audio.py index 4bcd7b224a86..7cca3f99f128 100644 --- a/Lib/email/mime/audio.py +++ b/Lib/email/mime/audio.py @@ -46,7 +46,7 @@ def __init__(self, _audiodata, _subtype=None, _encoder=encoders.encode_base64, *, policy=None, **_params): """Create an audio/* type MIME document. - _audiodata is a string containing the raw audio data. If this data + _audiodata contains the bytes for the raw audio data. If this data can be decoded by the standard Python `sndhdr' module, then the subtype will be automatically included in the Content-Type header. Otherwise, you can specify the specific audio subtype via the diff --git a/Lib/email/mime/image.py b/Lib/email/mime/image.py index 92724643cdee..6dc7ec4ca201 100644 --- a/Lib/email/mime/image.py +++ b/Lib/email/mime/image.py @@ -20,7 +20,7 @@ def __init__(self, _imagedata, _subtype=None, _encoder=encoders.encode_base64, *, policy=None, **_params): """Create an image/* type MIME document. - _imagedata is a string containing the raw image data. If this data + _imagedata contains the bytes for the raw image data. If the data can be decoded by the standard Python `imghdr' module, then the subtype will be automatically included in the Content-Type header. Otherwise, you can specify the specific image subtype via the _subtype From webhook-mailer at python.org Sun Jan 15 01:46:14 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sun, 15 Jan 2023 06:46:14 -0000 Subject: [Python-checkins] [3.11] Sync-up parameter name in equivalent code snippet of `enumerate` (GH-101029) (#101030) Message-ID: https://github.com/python/cpython/commit/6d98282ed41b16a42c0a3df0b0f7cebbbcea5e11 commit: 6d98282ed41b16a42c0a3df0b0f7cebbbcea5e11 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-15T12:16:06+05:30 summary: [3.11] Sync-up parameter name in equivalent code snippet of `enumerate` (GH-101029) (#101030) Sync-up parameter name in equivalent code snippet of `enumerate` (GH-101029) (cherry picked from commit ef633e5000222a3dba74473c49d6a81fca0a44ec) Co-authored-by: JustAnotherArchivist files: M Doc/library/functions.rst diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 2afd72f3cddc..d0da05f78416 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -504,9 +504,9 @@ are always available. They are listed here in alphabetical order. Equivalent to:: - def enumerate(sequence, start=0): + def enumerate(iterable, start=0): n = start - for elem in sequence: + for elem in iterable: yield n, elem n += 1 From webhook-mailer at python.org Sun Jan 15 02:08:31 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sun, 15 Jan 2023 07:08:31 -0000 Subject: [Python-checkins] [3.11] GH-100942: Fix incorrect cast in property_copy(). (GH-100965). (#101008) Message-ID: https://github.com/python/cpython/commit/855b1a935eebc134a4ccf5f2c9d8f7dda21deb70 commit: 855b1a935eebc134a4ccf5f2c9d8f7dda21deb70 branch: 3.11 author: Nikita Sobolev committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-15T12:38:25+05:30 summary: [3.11] GH-100942: Fix incorrect cast in property_copy(). (GH-100965). (#101008) (cherry picked from commit 94fc7706b7bc3d57cdd6d15bf8e8c4499ae53a69) Co-authored-by: Raymond Hettinger files: A Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst M Lib/test/test_property.py M Objects/descrobject.c diff --git a/Lib/test/test_property.py b/Lib/test/test_property.py index d91ad1c19127..953431504a72 100644 --- a/Lib/test/test_property.py +++ b/Lib/test/test_property.py @@ -214,6 +214,23 @@ def test_property_set_name_incorrect_args(self): ): p.__set_name__(*([0] * i)) + def test_property_setname_on_property_subclass(self): + # https://github.com/python/cpython/issues/100942 + # Copy was setting the name field without first + # verifying that the copy was an actual property + # instance. As a result, the code below was + # causing a segfault. + + class pro(property): + def __new__(typ, *args, **kwargs): + return "abcdef" + + class A: + pass + + p = property.__new__(pro) + p.__set_name__(A, 1) + np = p.getter(lambda self: 1) # Issue 5890: subclasses of property do not preserve method __doc__ strings class PropertySub(property): diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst new file mode 100644 index 000000000000..daccea255b16 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst @@ -0,0 +1,2 @@ +Fixed segfault in property.getter/setter/deleter that occurred when a property +subclass overrode the ``__new__`` method to return a non-property instance. diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 6a5c2a4cf999..4d8b83758b52 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -1723,9 +1723,10 @@ property_copy(PyObject *old, PyObject *get, PyObject *set, PyObject *del) Py_DECREF(type); if (new == NULL) return NULL; - - Py_XINCREF(pold->prop_name); - Py_XSETREF(((propertyobject *) new)->prop_name, pold->prop_name); + if (PyObject_TypeCheck((new), &PyProperty_Type)) { + Py_XINCREF(pold->prop_name); + Py_XSETREF(((propertyobject *) new)->prop_name, pold->prop_name); + } return new; } From webhook-mailer at python.org Sun Jan 15 02:09:04 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sun, 15 Jan 2023 07:09:04 -0000 Subject: [Python-checkins] [3.10] GH-100942: Fix incorrect cast in property_copy(). (GH-100965). (#101009) Message-ID: https://github.com/python/cpython/commit/b7b641a2ffaa0d370e73bed193b55aacb82f1069 commit: b7b641a2ffaa0d370e73bed193b55aacb82f1069 branch: 3.10 author: Nikita Sobolev committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-15T12:38:59+05:30 summary: [3.10] GH-100942: Fix incorrect cast in property_copy(). (GH-100965). (#101009) (cherry picked from commit 94fc7706b7bc3d57cdd6d15bf8e8c4499ae53a69) Co-authored-by: Raymond Hettinger files: A Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst M Lib/test/test_property.py M Objects/descrobject.c diff --git a/Lib/test/test_property.py b/Lib/test/test_property.py index 7f3813fc8cd1..4cd4283d8435 100644 --- a/Lib/test/test_property.py +++ b/Lib/test/test_property.py @@ -214,6 +214,23 @@ def test_property_set_name_incorrect_args(self): ): p.__set_name__(*([0] * i)) + def test_property_setname_on_property_subclass(self): + # https://github.com/python/cpython/issues/100942 + # Copy was setting the name field without first + # verifying that the copy was an actual property + # instance. As a result, the code below was + # causing a segfault. + + class pro(property): + def __new__(typ, *args, **kwargs): + return "abcdef" + + class A: + pass + + p = property.__new__(pro) + p.__set_name__(A, 1) + np = p.getter(lambda self: 1) # Issue 5890: subclasses of property do not preserve method __doc__ strings class PropertySub(property): diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst new file mode 100644 index 000000000000..daccea255b16 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-11-22-52-19.gh-issue-100942.ontOy_.rst @@ -0,0 +1,2 @@ +Fixed segfault in property.getter/setter/deleter that occurred when a property +subclass overrode the ``__new__`` method to return a non-property instance. diff --git a/Objects/descrobject.c b/Objects/descrobject.c index ee3ad1b7a328..1b1487a025f2 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -1675,9 +1675,10 @@ property_copy(PyObject *old, PyObject *get, PyObject *set, PyObject *del) Py_DECREF(type); if (new == NULL) return NULL; - - Py_XINCREF(pold->prop_name); - Py_XSETREF(((propertyobject *) new)->prop_name, pold->prop_name); + if (PyObject_TypeCheck((new), &PyProperty_Type)) { + Py_XINCREF(pold->prop_name); + Py_XSETREF(((propertyobject *) new)->prop_name, pold->prop_name); + } return new; } From webhook-mailer at python.org Sun Jan 15 10:09:32 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sun, 15 Jan 2023 15:09:32 -0000 Subject: [Python-checkins] GH-100892: consolidate `HEAD_LOCK/HEAD_UNLOCK` macros (#100953) Message-ID: https://github.com/python/cpython/commit/f6307d44167e9c110a1db442f7a33deb365e89a8 commit: f6307d44167e9c110a1db442f7a33deb365e89a8 branch: main author: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-15T20:39:26+05:30 summary: GH-100892: consolidate `HEAD_LOCK/HEAD_UNLOCK` macros (#100953) files: M Include/internal/pycore_pystate.h M Modules/_threadmodule.c M Python/ceval.c M Python/pystate.c diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 3d6d400f74dd..736c2b30d8e0 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -162,6 +162,12 @@ PyAPI_FUNC(int) _PyState_AddModule( PyAPI_FUNC(int) _PyOS_InterruptOccurred(PyThreadState *tstate); +#define HEAD_LOCK(runtime) \ + PyThread_acquire_lock((runtime)->interpreters.mutex, WAIT_LOCK) +#define HEAD_UNLOCK(runtime) \ + PyThread_release_lock((runtime)->interpreters.mutex) + + #ifdef __cplusplus } #endif diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 4fbf0e551b07..80f467a8e21d 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -839,11 +839,6 @@ local_traverse(localobject *self, visitproc visit, void *arg) return 0; } -#define HEAD_LOCK(runtime) \ - PyThread_acquire_lock((runtime)->interpreters.mutex, WAIT_LOCK) -#define HEAD_UNLOCK(runtime) \ - PyThread_release_lock((runtime)->interpreters.mutex) - static int local_clear(localobject *self) { diff --git a/Python/ceval.c b/Python/ceval.c index 7deee76cc5b8..ecbe2f9d5136 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -97,11 +97,6 @@ #define _Py_atomic_load_relaxed_int32(ATOMIC_VAL) _Py_atomic_load_relaxed(ATOMIC_VAL) #endif -#define HEAD_LOCK(runtime) \ - PyThread_acquire_lock((runtime)->interpreters.mutex, WAIT_LOCK) -#define HEAD_UNLOCK(runtime) \ - PyThread_release_lock((runtime)->interpreters.mutex) - /* Forward declarations */ static PyObject *trace_call_function( PyThreadState *tstate, PyObject *callable, PyObject **stack, diff --git a/Python/pystate.c b/Python/pystate.c index f2f571faf401..bf9b8db9a7ed 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -231,10 +231,6 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime) } #endif -#define HEAD_LOCK(runtime) \ - PyThread_acquire_lock((runtime)->interpreters.mutex, WAIT_LOCK) -#define HEAD_UNLOCK(runtime) \ - PyThread_release_lock((runtime)->interpreters.mutex) /* Forward declaration */ static void _PyGILState_NoteThreadState( From webhook-mailer at python.org Sun Jan 15 10:12:42 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sun, 15 Jan 2023 15:12:42 -0000 Subject: [Python-checkins] Fix `ctypes` typo in `set_exception` (#101040) Message-ID: https://github.com/python/cpython/commit/5e9df888dd5ab0e59d1cebc30c998a17aa65a3e2 commit: 5e9df888dd5ab0e59d1cebc30c998a17aa65a3e2 branch: main author: Cristi?n Maureira-Fredes committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-15T20:42:37+05:30 summary: Fix `ctypes` typo in `set_exception` (#101040) files: M Doc/library/ctypes.rst M Modules/_ctypes/callproc.c diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index ac533a939d6a..50ab29375623 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1656,12 +1656,12 @@ They are instances of a private class: passed arguments. -.. audit-event:: ctypes.seh_exception code foreign-functions +.. audit-event:: ctypes.set_exception code foreign-functions On Windows, when a foreign function call raises a system exception (for example, due to an access violation), it will be captured and replaced with a suitable Python exception. Further, an auditing event - ``ctypes.seh_exception`` with argument ``code`` will be raised, allowing an + ``ctypes.set_exception`` with argument ``code`` will be raised, allowing an audit hook to replace the exception with its own. .. audit-event:: ctypes.call_function func_pointer,arguments foreign-functions diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 1958758dd0cf..f6d98bbeebc2 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -286,7 +286,7 @@ static WCHAR *FormatError(DWORD code) #ifndef DONT_USE_SEH static void SetException(DWORD code, EXCEPTION_RECORD *pr) { - if (PySys_Audit("ctypes.seh_exception", "I", code) < 0) { + if (PySys_Audit("ctypes.set_exception", "I", code) < 0) { /* An exception was set by the audit hook */ return; } From webhook-mailer at python.org Sun Jan 15 15:36:37 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 15 Jan 2023 20:36:37 -0000 Subject: [Python-checkins] gh-101041: Fix a misspelled name of `utctimetuple` in a doc warning (GH-101042) Message-ID: https://github.com/python/cpython/commit/8e9d08b062bbabfe439bc73f82e3d7bb3800189e commit: 8e9d08b062bbabfe439bc73f82e3d7bb3800189e branch: main author: Oleg Iarygin committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-15T12:36:31-08:00 summary: gh-101041: Fix a misspelled name of `utctimetuple` in a doc warning (GH-101042) files: M Doc/library/datetime.rst diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 8bfed19d3fd2..eba8824f8351 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -1351,7 +1351,7 @@ Instance methods: Because naive ``datetime`` objects are treated by many ``datetime`` methods as local times, it is preferred to use aware datetimes to represent times - in UTC; as a result, using ``utcfromtimetuple`` may give misleading + in UTC; as a result, using :meth:`datetime.utctimetuple` may give misleading results. If you have a naive ``datetime`` representing UTC, use ``datetime.replace(tzinfo=timezone.utc)`` to make it aware, at which point you can use :meth:`.datetime.timetuple`. From webhook-mailer at python.org Mon Jan 16 05:42:32 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Mon, 16 Jan 2023 10:42:32 -0000 Subject: [Python-checkins] GH-100997: fix refleak in `_testinternalcapi.c` (#101058) Message-ID: https://github.com/python/cpython/commit/74c20b6ecee125e76c187c3f78c55f39501702ae commit: 74c20b6ecee125e76c187c3f78c55f39501702ae branch: main author: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-16T16:11:57+05:30 summary: GH-100997: fix refleak in `_testinternalcapi.c` (#101058) files: M Modules/_testinternalcapi.c diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index f53929f80f8a..ba57719d9209 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -566,6 +566,7 @@ record_eval(PyThreadState *tstate, struct _PyInterpreterFrame *f, int exc) PyObject *module = _get_current_module(); assert(module != NULL); module_state *state = get_module_state(module); + Py_DECREF(module); PyList_Append(state->record_list, ((PyFunctionObject *)f->f_funcobj)->func_name); } return _PyEval_EvalFrameDefault(tstate, f, exc); From webhook-mailer at python.org Mon Jan 16 05:44:58 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Mon, 16 Jan 2023 10:44:58 -0000 Subject: [Python-checkins] Docs: Remove duplicate `TraversableResources` reference (#101068) Message-ID: https://github.com/python/cpython/commit/b82049993f74185da71adf2eb8d6c8f15db063e1 commit: b82049993f74185da71adf2eb8d6c8f15db063e1 branch: main author: scrazzz committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-16T16:14:50+05:30 summary: Docs: Remove duplicate `TraversableResources` reference (#101068) files: M Doc/whatsnew/3.12.rst diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 2c807caa0ed4..72a3ad9f5c8c 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -464,7 +464,6 @@ Pending Removal in Python 3.14 Use :mod:`importlib.resources.abc` classes instead: - * :class:`importlib.resources.abc.TraversableResources` * :class:`importlib.resources.abc.Traversable` * :class:`importlib.resources.abc.TraversableResources` From webhook-mailer at python.org Mon Jan 16 05:46:14 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Mon, 16 Jan 2023 10:46:14 -0000 Subject: [Python-checkins] gh-101056: Fix memory leak in `formatfloat()` in `bytesobject.c` (#101057) Message-ID: https://github.com/python/cpython/commit/b1a74a182d8762bda51838401ac92b6ebad9632a commit: b1a74a182d8762bda51838401ac92b6ebad9632a branch: main author: Nikita Sobolev committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-16T16:16:07+05:30 summary: gh-101056: Fix memory leak in `formatfloat()` in `bytesobject.c` (#101057) files: M Objects/bytesobject.c diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 0fd10fa00d16..ba2c2e978c6e 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -434,8 +434,10 @@ formatfloat(PyObject *v, int flags, int prec, int type, len = strlen(p); if (writer != NULL) { str = _PyBytesWriter_Prepare(writer, str, len); - if (str == NULL) + if (str == NULL) { + PyMem_Free(p); return NULL; + } memcpy(str, p, len); PyMem_Free(p); str += len; From webhook-mailer at python.org Mon Jan 16 06:12:55 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 16 Jan 2023 11:12:55 -0000 Subject: [Python-checkins] gh-101056: Fix memory leak in `formatfloat()` in `bytesobject.c` (GH-101057) Message-ID: https://github.com/python/cpython/commit/63690e9af88602dbc5a4dd6bc1571f935b69821f commit: 63690e9af88602dbc5a4dd6bc1571f935b69821f branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-16T03:12:23-08:00 summary: gh-101056: Fix memory leak in `formatfloat()` in `bytesobject.c` (GH-101057) (cherry picked from commit b1a74a182d8762bda51838401ac92b6ebad9632a) Co-authored-by: Nikita Sobolev files: M Objects/bytesobject.c diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index b429f7687f7f..17a2661ef3be 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -441,8 +441,10 @@ formatfloat(PyObject *v, int flags, int prec, int type, len = strlen(p); if (writer != NULL) { str = _PyBytesWriter_Prepare(writer, str, len); - if (str == NULL) + if (str == NULL) { + PyMem_Free(p); return NULL; + } memcpy(str, p, len); PyMem_Free(p); str += len; From webhook-mailer at python.org Mon Jan 16 06:12:59 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 16 Jan 2023 11:12:59 -0000 Subject: [Python-checkins] gh-101056: Fix memory leak in `formatfloat()` in `bytesobject.c` (GH-101057) Message-ID: https://github.com/python/cpython/commit/664141e34c4df3aed6c21996e2476175d136c06b commit: 664141e34c4df3aed6c21996e2476175d136c06b branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-16T03:12:52-08:00 summary: gh-101056: Fix memory leak in `formatfloat()` in `bytesobject.c` (GH-101057) (cherry picked from commit b1a74a182d8762bda51838401ac92b6ebad9632a) Co-authored-by: Nikita Sobolev files: M Objects/bytesobject.c diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index eaedb0b5689b..4bcb2eb49298 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -470,8 +470,10 @@ formatfloat(PyObject *v, int flags, int prec, int type, len = strlen(p); if (writer != NULL) { str = _PyBytesWriter_Prepare(writer, str, len); - if (str == NULL) + if (str == NULL) { + PyMem_Free(p); return NULL; + } memcpy(str, p, len); PyMem_Free(p); str += len; From webhook-mailer at python.org Mon Jan 16 07:35:36 2023 From: webhook-mailer at python.org (markshannon) Date: Mon, 16 Jan 2023 12:35:36 -0000 Subject: [Python-checkins] GH-100982: Add `COMPARE_AND_BRANCH` instruction (GH-100983) Message-ID: https://github.com/python/cpython/commit/7b14c2ef194b6eed79670aa9d7e29ab8e2256a56 commit: 7b14c2ef194b6eed79670aa9d7e29ab8e2256a56 branch: main author: Mark Shannon committer: markshannon date: 2023-01-16T12:35:21Z summary: GH-100982: Add `COMPARE_AND_BRANCH` instruction (GH-100983) files: A Misc/NEWS.d/next/Core and Builtins/2023-01-12-13-46-49.gh-issue-100982.mJ234s.rst M Doc/library/dis.rst M Include/internal/pycore_code.h M Include/internal/pycore_opcode.h M Include/opcode.h M Lib/importlib/_bootstrap_external.py M Lib/opcode.py M Lib/test/test_compile.py M Lib/test/test_dis.py M Objects/frameobject.c M Python/bytecodes.c M Python/compile.c M Python/generated_cases.c.h M Python/opcode_metadata.h M Python/opcode_targets.h M Python/specialize.c M Tools/scripts/summarize_stats.py diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 8e586e6c5dda..7f309ce56dde 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -1043,6 +1043,15 @@ iterations of the loop. ``cmp_op[opname]``. +.. opcode:: COMPARE_AND_BRANCH (opname) + + Compares the top two values on the stack, popping them, then branches. + The direction and offset of the jump is embedded as a ``POP_JUMP_IF_TRUE`` + or ``POP_JUMP_IF_FALSE`` instruction immediately following the cache. + + .. versionadded:: 3.12 + + .. opcode:: IS_OP (invert) Performs ``is`` comparison, or ``is not`` if ``invert`` is 1. diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index b53657ac08a0..a287250acc19 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -228,7 +228,7 @@ extern void _Py_Specialize_Call(PyObject *callable, _Py_CODEUNIT *instr, int nargs, PyObject *kwnames); extern void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, int oparg, PyObject **locals); -extern void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, +extern void _Py_Specialize_CompareAndBranch(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, int oparg); extern void _Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr, int oparg); diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index 5806d69f98a9..05c0485b0641 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -50,6 +50,7 @@ const uint8_t _PyOpcode_Caches[256] = { [COMPARE_OP] = 1, [LOAD_GLOBAL] = 5, [BINARY_OP] = 1, + [COMPARE_AND_BRANCH] = 1, [CALL] = 4, }; @@ -102,10 +103,11 @@ const uint8_t _PyOpcode_Deopt[256] = { [CHECK_EG_MATCH] = CHECK_EG_MATCH, [CHECK_EXC_MATCH] = CHECK_EXC_MATCH, [CLEANUP_THROW] = CLEANUP_THROW, + [COMPARE_AND_BRANCH] = COMPARE_AND_BRANCH, + [COMPARE_AND_BRANCH_FLOAT] = COMPARE_AND_BRANCH, + [COMPARE_AND_BRANCH_INT] = COMPARE_AND_BRANCH, + [COMPARE_AND_BRANCH_STR] = COMPARE_AND_BRANCH, [COMPARE_OP] = COMPARE_OP, - [COMPARE_OP_FLOAT_JUMP] = COMPARE_OP, - [COMPARE_OP_INT_JUMP] = COMPARE_OP, - [COMPARE_OP_STR_JUMP] = COMPARE_OP, [CONTAINS_OP] = CONTAINS_OP, [COPY] = COPY, [COPY_FREE_VARS] = COPY_FREE_VARS, @@ -274,7 +276,7 @@ static const char *const _PyOpcode_OpName[263] = { [CALL_NO_KW_STR_1] = "CALL_NO_KW_STR_1", [CALL_NO_KW_TUPLE_1] = "CALL_NO_KW_TUPLE_1", [CALL_NO_KW_TYPE_1] = "CALL_NO_KW_TYPE_1", - [COMPARE_OP_FLOAT_JUMP] = "COMPARE_OP_FLOAT_JUMP", + [COMPARE_AND_BRANCH_FLOAT] = "COMPARE_AND_BRANCH_FLOAT", [WITH_EXCEPT_START] = "WITH_EXCEPT_START", [GET_AITER] = "GET_AITER", [GET_ANEXT] = "GET_ANEXT", @@ -282,8 +284,8 @@ static const char *const _PyOpcode_OpName[263] = { [BEFORE_WITH] = "BEFORE_WITH", [END_ASYNC_FOR] = "END_ASYNC_FOR", [CLEANUP_THROW] = "CLEANUP_THROW", - [COMPARE_OP_INT_JUMP] = "COMPARE_OP_INT_JUMP", - [COMPARE_OP_STR_JUMP] = "COMPARE_OP_STR_JUMP", + [COMPARE_AND_BRANCH_INT] = "COMPARE_AND_BRANCH_INT", + [COMPARE_AND_BRANCH_STR] = "COMPARE_AND_BRANCH_STR", [FOR_ITER_LIST] = "FOR_ITER_LIST", [FOR_ITER_TUPLE] = "FOR_ITER_TUPLE", [STORE_SUBSCR] = "STORE_SUBSCR", @@ -367,9 +369,9 @@ static const char *const _PyOpcode_OpName[263] = { [STORE_DEREF] = "STORE_DEREF", [DELETE_DEREF] = "DELETE_DEREF", [JUMP_BACKWARD] = "JUMP_BACKWARD", - [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", + [COMPARE_AND_BRANCH] = "COMPARE_AND_BRANCH", [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX", - [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT", + [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", [EXTENDED_ARG] = "EXTENDED_ARG", [LIST_APPEND] = "LIST_APPEND", [SET_ADD] = "SET_ADD", @@ -379,14 +381,14 @@ static const char *const _PyOpcode_OpName[263] = { [YIELD_VALUE] = "YIELD_VALUE", [RESUME] = "RESUME", [MATCH_CLASS] = "MATCH_CLASS", + [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT", [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", - [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST", [FORMAT_VALUE] = "FORMAT_VALUE", [BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP", [BUILD_STRING] = "BUILD_STRING", + [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST", [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE", [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE", - [160] = "<160>", [161] = "<161>", [LIST_EXTEND] = "LIST_EXTEND", [SET_UPDATE] = "SET_UPDATE", @@ -493,7 +495,6 @@ static const char *const _PyOpcode_OpName[263] = { #endif #define EXTRA_CASES \ - case 160: \ case 161: \ case 166: \ case 167: \ diff --git a/Include/opcode.h b/Include/opcode.h index fcc46a3ede9d..e057a44d1fd8 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -95,6 +95,7 @@ extern "C" { #define STORE_DEREF 138 #define DELETE_DEREF 139 #define JUMP_BACKWARD 140 +#define COMPARE_AND_BRANCH 141 #define CALL_FUNCTION_EX 142 #define EXTENDED_ARG 144 #define LIST_APPEND 145 @@ -153,9 +154,9 @@ extern "C" { #define CALL_NO_KW_STR_1 45 #define CALL_NO_KW_TUPLE_1 46 #define CALL_NO_KW_TYPE_1 47 -#define COMPARE_OP_FLOAT_JUMP 48 -#define COMPARE_OP_INT_JUMP 56 -#define COMPARE_OP_STR_JUMP 57 +#define COMPARE_AND_BRANCH_FLOAT 48 +#define COMPARE_AND_BRANCH_INT 56 +#define COMPARE_AND_BRANCH_STR 57 #define FOR_ITER_LIST 58 #define FOR_ITER_TUPLE 59 #define FOR_ITER_RANGE 62 @@ -179,12 +180,12 @@ extern "C" { #define STORE_ATTR_SLOT 87 #define STORE_ATTR_WITH_HINT 113 #define STORE_FAST__LOAD_FAST 121 -#define STORE_FAST__STORE_FAST 141 -#define STORE_SUBSCR_DICT 143 -#define STORE_SUBSCR_LIST_INT 153 -#define UNPACK_SEQUENCE_LIST 154 -#define UNPACK_SEQUENCE_TUPLE 158 -#define UNPACK_SEQUENCE_TWO_TUPLE 159 +#define STORE_FAST__STORE_FAST 143 +#define STORE_SUBSCR_DICT 153 +#define STORE_SUBSCR_LIST_INT 154 +#define UNPACK_SEQUENCE_LIST 158 +#define UNPACK_SEQUENCE_TUPLE 159 +#define UNPACK_SEQUENCE_TWO_TUPLE 160 #define DO_TRACING 255 #define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\ diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index cbde91323422..5a8cbb98ed3f 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -429,6 +429,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.12a1 3513 (Add CALL_INTRINSIC_1 instruction, removed STOPITERATION_ERROR, PRINT_EXPR, IMPORT_STAR) # Python 3.12a1 3514 (Remove ASYNC_GEN_WRAP, LIST_TO_TUPLE, and UNARY_POSITIVE) # Python 3.12a1 3515 (Embed jump mask in COMPARE_OP oparg) +# Python 3.12a1 3516 (Add COMAPRE_AND_BRANCH instruction) # Python 3.13 will start with 3550 @@ -441,7 +442,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3515).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3516).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c diff --git a/Lib/opcode.py b/Lib/opcode.py index 414faa35ee89..46051b28a0f1 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -189,6 +189,8 @@ def pseudo_op(name, op, real_ops): def_op('DELETE_DEREF', 139) hasfree.append(139) jrel_op('JUMP_BACKWARD', 140) # Number of words to skip (backwards) +def_op('COMPARE_AND_BRANCH', 141) # Comparison and jump +hascompare.append(141) def_op('CALL_FUNCTION_EX', 142) # Flags @@ -309,10 +311,10 @@ def pseudo_op(name, op, real_ops): "CALL_NO_KW_TUPLE_1", "CALL_NO_KW_TYPE_1", ], - "COMPARE_OP": [ - "COMPARE_OP_FLOAT_JUMP", - "COMPARE_OP_INT_JUMP", - "COMPARE_OP_STR_JUMP", + "COMPARE_AND_BRANCH": [ + "COMPARE_AND_BRANCH_FLOAT", + "COMPARE_AND_BRANCH_INT", + "COMPARE_AND_BRANCH_STR", ], "FOR_ITER": [ "FOR_ITER_LIST", @@ -392,6 +394,9 @@ def pseudo_op(name, op, real_ops): "COMPARE_OP": { "counter": 1, }, + "COMPARE_AND_BRANCH": { + "counter": 1, + }, "BINARY_SUBSCR": { "counter": 1, "type_version": 2, diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index f74d2ed2c420..1606c9cfda32 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -1121,11 +1121,11 @@ def aug(): check_op_count(aug, "BUILD_SLICE", 0) def test_compare_positions(self): - for opname, op in [ - ("COMPARE_OP", "<"), - ("COMPARE_OP", "<="), - ("COMPARE_OP", ">"), - ("COMPARE_OP", ">="), + for opname_prefix, op in [ + ("COMPARE_", "<"), + ("COMPARE_", "<="), + ("COMPARE_", ">"), + ("COMPARE_", ">="), ("CONTAINS_OP", "in"), ("CONTAINS_OP", "not in"), ("IS_OP", "is"), @@ -1140,7 +1140,7 @@ def test_compare_positions(self): actual_positions = [ instruction.positions for instruction in dis.get_instructions(code) - if instruction.opname == opname + if instruction.opname.startswith(opname_prefix) ] with self.subTest(source): self.assertEqual(actual_positions, expected_positions) @@ -1270,7 +1270,7 @@ def test_multiline_boolean_expression(self): self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_FALSE', line=2, end_line=2, column=15, end_column=16, occurrence=2) # compare d and 0 - self.assertOpcodeSourcePositionIs(compiled_code, 'COMPARE_OP', + self.assertOpcodeSourcePositionIs(compiled_code, 'COMPARE_AND_BRANCH', line=4, end_line=4, column=8, end_column=13, occurrence=1) # jump if comparison it True self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_TRUE', diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 994bb370a575..cb7e22ff7b30 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -1561,12 +1561,12 @@ def _prepare_test_cases(): Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=58, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=60, starts_line=5, is_jump_target=False, positions=None), Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=62, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=107, arg=13, argval='<', argrepr='<', offset=64, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COMPARE_AND_BRANCH', opcode=141, arg=13, argval='<', argrepr='<', offset=64, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=72, argrepr='to 72', offset=68, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='JUMP_BACKWARD', opcode=140, arg=22, argval=28, argrepr='to 28', offset=70, starts_line=6, is_jump_target=False, positions=None), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=72, starts_line=7, is_jump_target=True, positions=None), Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=74, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=107, arg=68, argval='>', argrepr='>', offset=76, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COMPARE_AND_BRANCH', opcode=141, arg=68, argval='>', argrepr='>', offset=76, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=1, argval=84, argrepr='to 84', offset=80, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='JUMP_BACKWARD', opcode=140, arg=28, argval=28, argrepr='to 28', offset=82, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=84, starts_line=8, is_jump_target=True, positions=None), @@ -1588,12 +1588,12 @@ def _prepare_test_cases(): Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=154, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=156, starts_line=14, is_jump_target=False, positions=None), Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=158, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=107, arg=75, argval='>', argrepr='>', offset=160, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COMPARE_AND_BRANCH', opcode=141, arg=75, argval='>', argrepr='>', offset=160, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=168, argrepr='to 168', offset=164, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='JUMP_BACKWARD', opcode=140, arg=26, argval=116, argrepr='to 116', offset=166, starts_line=15, is_jump_target=False, positions=None), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=168, starts_line=16, is_jump_target=True, positions=None), Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=170, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=107, arg=13, argval='<', argrepr='<', offset=172, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='COMPARE_AND_BRANCH', opcode=141, arg=13, argval='<', argrepr='<', offset=172, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=180, argrepr='to 180', offset=176, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='JUMP_FORWARD', opcode=110, arg=16, argval=212, argrepr='to 212', offset=178, starts_line=17, is_jump_target=False, positions=None), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=180, starts_line=11, is_jump_target=True, positions=None), diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-12-13-46-49.gh-issue-100982.mJ234s.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-12-13-46-49.gh-issue-100982.mJ234s.rst new file mode 100644 index 000000000000..4f43e783cd6a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-12-13-46-49.gh-issue-100982.mJ234s.rst @@ -0,0 +1,4 @@ +Adds a new :opcode:`COMPARE_AND_BRANCH` instruction. This is a bit more +efficient when performing a comparison immediately followed by a branch, and +restores the design intent of PEP 659 that specializations are local to a +single instruction. diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 39ccca70f9cb..6bc04bc8e848 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -357,6 +357,14 @@ mark_stacks(PyCodeObject *code_obj, int len) assert(stacks[j] == UNINITIALIZED || stacks[j] == next_stack); stacks[j] = next_stack; break; + case COMPARE_AND_BRANCH: + next_stack = pop_value(pop_value(next_stack)); + i++; + j = get_arg(code, i) + i + 1; + assert(j < len); + assert(stacks[j] == UNINITIALIZED || stacks[j] == next_stack); + stacks[j] = next_stack; + break; case GET_ITER: case GET_AITER: next_stack = push_value(pop_value(next_stack), Iterator); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index faa6df6a0a65..18f9eabd842c 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -90,11 +90,6 @@ static size_t jump; // Dummy variables for cache effects static uint16_t invert, counter, index, hint; static uint32_t type_version; -// Dummy opcode names for 'op' opcodes -#define _COMPARE_OP_FLOAT 1003 -#define _COMPARE_OP_INT 1004 -#define _COMPARE_OP_STR 1005 -#define _JUMP_IF 1006 static PyObject * dummy_func( @@ -1829,64 +1824,76 @@ dummy_func( Py_DECREF(owner); } - family(compare_op) = { - COMPARE_OP, - _COMPARE_OP_FLOAT, - _COMPARE_OP_INT, - _COMPARE_OP_STR, + inst(COMPARE_OP, (unused/1, left, right -- res)) { + STAT_INC(COMPARE_OP, deferred); + assert((oparg >> 4) <= Py_GE); + res = PyObject_RichCompare(left, right, oparg>>4); + Py_DECREF(left); + Py_DECREF(right); + ERROR_IF(res == NULL, error); + } + + family(compare_and_branch) = { + COMPARE_AND_BRANCH, + COMPARE_AND_BRANCH_FLOAT, + COMPARE_AND_BRANCH_INT, + COMPARE_AND_BRANCH_STR, }; - inst(COMPARE_OP, (unused/1, left, right -- res)) { + inst(COMPARE_AND_BRANCH, (unused/2, left, right -- )) { _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); next_instr--; - _Py_Specialize_CompareOp(left, right, next_instr, oparg); + _Py_Specialize_CompareAndBranch(left, right, next_instr, oparg); DISPATCH_SAME_OPARG(); } - STAT_INC(COMPARE_OP, deferred); + STAT_INC(COMPARE_AND_BRANCH, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); assert((oparg >> 4) <= Py_GE); - res = PyObject_RichCompare(left, right, oparg>>4); + PyObject *cond = PyObject_RichCompare(left, right, oparg>>4); Py_DECREF(left); Py_DECREF(right); - ERROR_IF(res == NULL, error); + ERROR_IF(cond == NULL, error); + assert(_Py_OPCODE(next_instr[1]) == POP_JUMP_IF_FALSE || + _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE); + bool jump_on_true = _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE; + int offset = _Py_OPARG(next_instr[1]); + int err = PyObject_IsTrue(cond); + Py_DECREF(cond); + if (err < 0) { + goto error; + } + if (jump_on_true == (err != 0)) { + JUMPBY(offset); + } } - // The result is an int disguised as an object pointer. - op(_COMPARE_OP_FLOAT, (unused/1, left, right -- jump: size_t)) { + inst(COMPARE_AND_BRANCH_FLOAT, (unused/2, left, right -- )) { assert(cframe.use_tracing == 0); - // Combined: COMPARE_OP (float ? float) + POP_JUMP_IF_(true/false) - DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_OP); - DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_OP); - STAT_INC(COMPARE_OP, hit); + DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_AND_BRANCH); + DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_AND_BRANCH); + STAT_INC(COMPARE_AND_BRANCH, hit); double dleft = PyFloat_AS_DOUBLE(left); double dright = PyFloat_AS_DOUBLE(right); // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg int sign_ish = COMPARISON_BIT(dleft, dright); _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); - jump = sign_ish & oparg; - } - // The input is an int disguised as an object pointer! - op(_JUMP_IF, (jump: size_t --)) { - assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE); - if (jump) { - JUMPBY(oparg); + if (sign_ish & oparg) { + int offset = _Py_OPARG(next_instr[1]); + JUMPBY(offset); } } - // We're praying that the compiler optimizes the flags manipuations. - super(COMPARE_OP_FLOAT_JUMP) = _COMPARE_OP_FLOAT + _JUMP_IF; - // Similar to COMPARE_OP_FLOAT - op(_COMPARE_OP_INT, (unused/1, left, right -- jump: size_t)) { + // Similar to COMPARE_AND_BRANCH_FLOAT + inst(COMPARE_AND_BRANCH_INT, (unused/2, left, right -- )) { assert(cframe.use_tracing == 0); - // Combined: COMPARE_OP (int ? int) + POP_JUMP_IF_(true/false) - DEOPT_IF(!PyLong_CheckExact(left), COMPARE_OP); - DEOPT_IF(!PyLong_CheckExact(right), COMPARE_OP); - DEOPT_IF((size_t)(Py_SIZE(left) + 1) > 2, COMPARE_OP); - DEOPT_IF((size_t)(Py_SIZE(right) + 1) > 2, COMPARE_OP); - STAT_INC(COMPARE_OP, hit); + DEOPT_IF(!PyLong_CheckExact(left), COMPARE_AND_BRANCH); + DEOPT_IF(!PyLong_CheckExact(right), COMPARE_AND_BRANCH); + DEOPT_IF((size_t)(Py_SIZE(left) + 1) > 2, COMPARE_AND_BRANCH); + DEOPT_IF((size_t)(Py_SIZE(right) + 1) > 2, COMPARE_AND_BRANCH); + STAT_INC(COMPARE_AND_BRANCH, hit); assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1); Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0]; Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0]; @@ -1894,17 +1901,18 @@ dummy_func( int sign_ish = COMPARISON_BIT(ileft, iright); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); - jump = sign_ish & oparg; + if (sign_ish & oparg) { + int offset = _Py_OPARG(next_instr[1]); + JUMPBY(offset); + } } - super(COMPARE_OP_INT_JUMP) = _COMPARE_OP_INT + _JUMP_IF; - // Similar to COMPARE_OP_FLOAT, but for ==, != only - op(_COMPARE_OP_STR, (unused/1, left, right -- jump: size_t)) { + // Similar to COMPARE_AND_BRANCH_FLOAT, but for ==, != only + inst(COMPARE_AND_BRANCH_STR, (unused/2, left, right -- )) { assert(cframe.use_tracing == 0); - // Combined: COMPARE_OP (str == str or str != str) + POP_JUMP_IF_(true/false) - DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_OP); - DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_OP); - STAT_INC(COMPARE_OP, hit); + DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_AND_BRANCH); + DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_AND_BRANCH); + STAT_INC(COMPARE_AND_BRANCH, hit); int res = _PyUnicode_Equal(left, right); assert((oparg >>4) == Py_EQ || (oparg >>4) == Py_NE); _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); @@ -1912,11 +1920,12 @@ dummy_func( assert(res == 0 || res == 1); assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); - jump = (res + COMPARISON_NOT_EQUALS) & oparg; + if ((res + COMPARISON_NOT_EQUALS) & oparg) { + int offset = _Py_OPARG(next_instr[1]); + JUMPBY(offset); + } } - super(COMPARE_OP_STR_JUMP) = _COMPARE_OP_STR + _JUMP_IF; - inst(IS_OP, (left, right -- b)) { int res = Py_Is(left, right) ^ oparg; DECREF_INPUTS(); diff --git a/Python/compile.c b/Python/compile.c index c0177fbf3e33..f98892ec5fc6 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1195,6 +1195,9 @@ stack_effect(int opcode, int oparg, int jump) case POP_JUMP_IF_TRUE: return -1; + case COMPARE_AND_BRANCH: + return -2; + case LOAD_GLOBAL: return (oparg & 1) + 1; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 9874ddf206d8..e9819dfda997 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2077,19 +2077,10 @@ } TARGET(COMPARE_OP) { - PREDICTED(COMPARE_OP); PyObject *right = PEEK(1); PyObject *left = PEEK(2); PyObject *res; - _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; - if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { - assert(cframe.use_tracing == 0); - next_instr--; - _Py_Specialize_CompareOp(left, right, next_instr, oparg); - DISPATCH_SAME_OPARG(); - } STAT_INC(COMPARE_OP, deferred); - DECREMENT_ADAPTIVE_COUNTER(cache->counter); assert((oparg >> 4) <= Py_GE); res = PyObject_RichCompare(left, right, oparg>>4); Py_DECREF(left); @@ -2101,112 +2092,108 @@ DISPATCH(); } - TARGET(COMPARE_OP_FLOAT_JUMP) { - PyObject *_tmp_1 = PEEK(1); - PyObject *_tmp_2 = PEEK(2); - { - PyObject *right = _tmp_1; - PyObject *left = _tmp_2; - size_t jump; + TARGET(COMPARE_AND_BRANCH) { + PREDICTED(COMPARE_AND_BRANCH); + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; + if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); - // Combined: COMPARE_OP (float ? float) + POP_JUMP_IF_(true/false) - DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_OP); - DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_OP); - STAT_INC(COMPARE_OP, hit); - double dleft = PyFloat_AS_DOUBLE(left); - double dright = PyFloat_AS_DOUBLE(right); - // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg - int sign_ish = COMPARISON_BIT(dleft, dright); - _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); - _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); - jump = sign_ish & oparg; - _tmp_2 = (PyObject *)jump; + next_instr--; + _Py_Specialize_CompareAndBranch(left, right, next_instr, oparg); + DISPATCH_SAME_OPARG(); } - JUMPBY(1); - NEXTOPARG(); - JUMPBY(1); - { - size_t jump = (size_t)_tmp_2; - assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE); - if (jump) { - JUMPBY(oparg); - } + STAT_INC(COMPARE_AND_BRANCH, deferred); + DECREMENT_ADAPTIVE_COUNTER(cache->counter); + assert((oparg >> 4) <= Py_GE); + PyObject *cond = PyObject_RichCompare(left, right, oparg>>4); + Py_DECREF(left); + Py_DECREF(right); + if (cond == NULL) goto pop_2_error; + assert(_Py_OPCODE(next_instr[1]) == POP_JUMP_IF_FALSE || + _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE); + bool jump_on_true = _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE; + int offset = _Py_OPARG(next_instr[1]); + int err = PyObject_IsTrue(cond); + Py_DECREF(cond); + if (err < 0) { + goto error; + } + if (jump_on_true == (err != 0)) { + JUMPBY(offset); } STACK_SHRINK(2); + JUMPBY(2); DISPATCH(); } - TARGET(COMPARE_OP_INT_JUMP) { - PyObject *_tmp_1 = PEEK(1); - PyObject *_tmp_2 = PEEK(2); - { - PyObject *right = _tmp_1; - PyObject *left = _tmp_2; - size_t jump; - assert(cframe.use_tracing == 0); - // Combined: COMPARE_OP (int ? int) + POP_JUMP_IF_(true/false) - DEOPT_IF(!PyLong_CheckExact(left), COMPARE_OP); - DEOPT_IF(!PyLong_CheckExact(right), COMPARE_OP); - DEOPT_IF((size_t)(Py_SIZE(left) + 1) > 2, COMPARE_OP); - DEOPT_IF((size_t)(Py_SIZE(right) + 1) > 2, COMPARE_OP); - STAT_INC(COMPARE_OP, hit); - assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1); - Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0]; - Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0]; - // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg - int sign_ish = COMPARISON_BIT(ileft, iright); - _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); - jump = sign_ish & oparg; - _tmp_2 = (PyObject *)jump; - } - JUMPBY(1); - NEXTOPARG(); - JUMPBY(1); - { - size_t jump = (size_t)_tmp_2; - assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE); - if (jump) { - JUMPBY(oparg); - } + TARGET(COMPARE_AND_BRANCH_FLOAT) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + assert(cframe.use_tracing == 0); + DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_AND_BRANCH); + DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_AND_BRANCH); + STAT_INC(COMPARE_AND_BRANCH, hit); + double dleft = PyFloat_AS_DOUBLE(left); + double dright = PyFloat_AS_DOUBLE(right); + // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg + int sign_ish = COMPARISON_BIT(dleft, dright); + _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); + _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); + if (sign_ish & oparg) { + int offset = _Py_OPARG(next_instr[1]); + JUMPBY(offset); } STACK_SHRINK(2); + JUMPBY(2); DISPATCH(); } - TARGET(COMPARE_OP_STR_JUMP) { - PyObject *_tmp_1 = PEEK(1); - PyObject *_tmp_2 = PEEK(2); - { - PyObject *right = _tmp_1; - PyObject *left = _tmp_2; - size_t jump; - assert(cframe.use_tracing == 0); - // Combined: COMPARE_OP (str == str or str != str) + POP_JUMP_IF_(true/false) - DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_OP); - DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_OP); - STAT_INC(COMPARE_OP, hit); - int res = _PyUnicode_Equal(left, right); - assert((oparg >>4) == Py_EQ || (oparg >>4) == Py_NE); - _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); - _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); - assert(res == 0 || res == 1); - assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); - assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); - jump = (res + COMPARISON_NOT_EQUALS) & oparg; - _tmp_2 = (PyObject *)jump; + TARGET(COMPARE_AND_BRANCH_INT) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + assert(cframe.use_tracing == 0); + DEOPT_IF(!PyLong_CheckExact(left), COMPARE_AND_BRANCH); + DEOPT_IF(!PyLong_CheckExact(right), COMPARE_AND_BRANCH); + DEOPT_IF((size_t)(Py_SIZE(left) + 1) > 2, COMPARE_AND_BRANCH); + DEOPT_IF((size_t)(Py_SIZE(right) + 1) > 2, COMPARE_AND_BRANCH); + STAT_INC(COMPARE_AND_BRANCH, hit); + assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1); + Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0]; + Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0]; + // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg + int sign_ish = COMPARISON_BIT(ileft, iright); + _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); + _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); + if (sign_ish & oparg) { + int offset = _Py_OPARG(next_instr[1]); + JUMPBY(offset); } - JUMPBY(1); - NEXTOPARG(); - JUMPBY(1); - { - size_t jump = (size_t)_tmp_2; - assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE); - if (jump) { - JUMPBY(oparg); - } + STACK_SHRINK(2); + JUMPBY(2); + DISPATCH(); + } + + TARGET(COMPARE_AND_BRANCH_STR) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + assert(cframe.use_tracing == 0); + DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_AND_BRANCH); + DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_AND_BRANCH); + STAT_INC(COMPARE_AND_BRANCH, hit); + int res = _PyUnicode_Equal(left, right); + assert((oparg >>4) == Py_EQ || (oparg >>4) == Py_NE); + _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); + _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); + assert(res == 0 || res == 1); + assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); + assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); + if ((res + COMPARISON_NOT_EQUALS) & oparg) { + int offset = _Py_OPARG(next_instr[1]); + JUMPBY(offset); } STACK_SHRINK(2); + JUMPBY(2); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 4c8b9dde23a3..d293adafeeec 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -2,7 +2,7 @@ // from Python/bytecodes.c // Do not edit! enum Direction { DIR_NONE, DIR_READ, DIR_WRITE }; -enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC000, INSTR_FMT_IBCIB, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; +enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; static const struct { short n_popped; short n_pushed; @@ -113,9 +113,10 @@ static const struct { [STORE_ATTR_WITH_HINT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, [STORE_ATTR_SLOT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, [COMPARE_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [COMPARE_OP_FLOAT_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBCIB }, - [COMPARE_OP_INT_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBCIB }, - [COMPARE_OP_STR_JUMP] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBCIB }, + [COMPARE_AND_BRANCH] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, + [COMPARE_AND_BRANCH_FLOAT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, + [COMPARE_AND_BRANCH_INT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, + [COMPARE_AND_BRANCH_STR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, [IS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [CONTAINS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [CHECK_EG_MATCH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 83b3af7c0b1d..f1c3f3e0c4ee 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -47,7 +47,7 @@ static void *opcode_targets[256] = { &&TARGET_CALL_NO_KW_STR_1, &&TARGET_CALL_NO_KW_TUPLE_1, &&TARGET_CALL_NO_KW_TYPE_1, - &&TARGET_COMPARE_OP_FLOAT_JUMP, + &&TARGET_COMPARE_AND_BRANCH_FLOAT, &&TARGET_WITH_EXCEPT_START, &&TARGET_GET_AITER, &&TARGET_GET_ANEXT, @@ -55,8 +55,8 @@ static void *opcode_targets[256] = { &&TARGET_BEFORE_WITH, &&TARGET_END_ASYNC_FOR, &&TARGET_CLEANUP_THROW, - &&TARGET_COMPARE_OP_INT_JUMP, - &&TARGET_COMPARE_OP_STR_JUMP, + &&TARGET_COMPARE_AND_BRANCH_INT, + &&TARGET_COMPARE_AND_BRANCH_STR, &&TARGET_FOR_ITER_LIST, &&TARGET_FOR_ITER_TUPLE, &&TARGET_STORE_SUBSCR, @@ -140,9 +140,9 @@ static void *opcode_targets[256] = { &&TARGET_STORE_DEREF, &&TARGET_DELETE_DEREF, &&TARGET_JUMP_BACKWARD, - &&TARGET_STORE_FAST__STORE_FAST, + &&TARGET_COMPARE_AND_BRANCH, &&TARGET_CALL_FUNCTION_EX, - &&TARGET_STORE_SUBSCR_DICT, + &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD, @@ -152,15 +152,15 @@ static void *opcode_targets[256] = { &&TARGET_YIELD_VALUE, &&TARGET_RESUME, &&TARGET_MATCH_CLASS, + &&TARGET_STORE_SUBSCR_DICT, &&TARGET_STORE_SUBSCR_LIST_INT, - &&TARGET_UNPACK_SEQUENCE_LIST, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, + &&TARGET_UNPACK_SEQUENCE_LIST, &&TARGET_UNPACK_SEQUENCE_TUPLE, &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, &&_unknown_opcode, - &&_unknown_opcode, &&TARGET_LIST_EXTEND, &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, diff --git a/Python/specialize.c b/Python/specialize.c index 38bb151e7471..500fd714b38b 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -126,6 +126,7 @@ print_spec_stats(FILE *out, OpcodeStats *stats) /* Mark some opcodes as specializable for stats, * even though we don't specialize them yet. */ fprintf(out, "opcode[%d].specializable : 1\n", BINARY_SLICE); + fprintf(out, "opcode[%d].specializable : 1\n", COMPARE_OP); fprintf(out, "opcode[%d].specializable : 1\n", STORE_SLICE); for (int i = 0; i < 256; i++) { if (_PyOpcode_Caches[i]) { @@ -311,6 +312,7 @@ _PyCode_Quicken(PyCodeObject *code) if (opcode == POP_JUMP_IF_FALSE) { mask = mask ^ 0xf; } + instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].opcode = COMPARE_AND_BRANCH; instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].oparg = (oparg & 0xf0) | mask; break; } @@ -431,19 +433,19 @@ _PyCode_Quicken(PyCodeObject *code) #define SPEC_FAIL_CALL_OPERATOR_WRAPPER 29 /* COMPARE_OP */ -#define SPEC_FAIL_COMPARE_OP_DIFFERENT_TYPES 12 -#define SPEC_FAIL_COMPARE_OP_STRING 13 -#define SPEC_FAIL_COMPARE_OP_NOT_FOLLOWED_BY_COND_JUMP 14 -#define SPEC_FAIL_COMPARE_OP_BIG_INT 15 -#define SPEC_FAIL_COMPARE_OP_BYTES 16 -#define SPEC_FAIL_COMPARE_OP_TUPLE 17 -#define SPEC_FAIL_COMPARE_OP_LIST 18 -#define SPEC_FAIL_COMPARE_OP_SET 19 -#define SPEC_FAIL_COMPARE_OP_BOOL 20 -#define SPEC_FAIL_COMPARE_OP_BASEOBJECT 21 -#define SPEC_FAIL_COMPARE_OP_FLOAT_LONG 22 -#define SPEC_FAIL_COMPARE_OP_LONG_FLOAT 23 -#define SPEC_FAIL_COMPARE_OP_EXTENDED_ARG 24 +#define SPEC_FAIL_COMPARE_DIFFERENT_TYPES 12 +#define SPEC_FAIL_COMPARE_STRING 13 +#define SPEC_FAIL_COMPARE_NOT_FOLLOWED_BY_COND_JUMP 14 +#define SPEC_FAIL_COMPARE_BIG_INT 15 +#define SPEC_FAIL_COMPARE_BYTES 16 +#define SPEC_FAIL_COMPARE_TUPLE 17 +#define SPEC_FAIL_COMPARE_LIST 18 +#define SPEC_FAIL_COMPARE_SET 19 +#define SPEC_FAIL_COMPARE_BOOL 20 +#define SPEC_FAIL_COMPARE_BASEOBJECT 21 +#define SPEC_FAIL_COMPARE_FLOAT_LONG 22 +#define SPEC_FAIL_COMPARE_LONG_FLOAT 23 +#define SPEC_FAIL_COMPARE_EXTENDED_ARG 24 /* FOR_ITER */ #define SPEC_FAIL_FOR_ITER_GENERATOR 10 @@ -1968,87 +1970,82 @@ compare_op_fail_kind(PyObject *lhs, PyObject *rhs) { if (Py_TYPE(lhs) != Py_TYPE(rhs)) { if (PyFloat_CheckExact(lhs) && PyLong_CheckExact(rhs)) { - return SPEC_FAIL_COMPARE_OP_FLOAT_LONG; + return SPEC_FAIL_COMPARE_FLOAT_LONG; } if (PyLong_CheckExact(lhs) && PyFloat_CheckExact(rhs)) { - return SPEC_FAIL_COMPARE_OP_LONG_FLOAT; + return SPEC_FAIL_COMPARE_LONG_FLOAT; } - return SPEC_FAIL_COMPARE_OP_DIFFERENT_TYPES; + return SPEC_FAIL_COMPARE_DIFFERENT_TYPES; } if (PyBytes_CheckExact(lhs)) { - return SPEC_FAIL_COMPARE_OP_BYTES; + return SPEC_FAIL_COMPARE_BYTES; } if (PyTuple_CheckExact(lhs)) { - return SPEC_FAIL_COMPARE_OP_TUPLE; + return SPEC_FAIL_COMPARE_TUPLE; } if (PyList_CheckExact(lhs)) { - return SPEC_FAIL_COMPARE_OP_LIST; + return SPEC_FAIL_COMPARE_LIST; } if (PySet_CheckExact(lhs) || PyFrozenSet_CheckExact(lhs)) { - return SPEC_FAIL_COMPARE_OP_SET; + return SPEC_FAIL_COMPARE_SET; } if (PyBool_Check(lhs)) { - return SPEC_FAIL_COMPARE_OP_BOOL; + return SPEC_FAIL_COMPARE_BOOL; } if (Py_TYPE(lhs)->tp_richcompare == PyBaseObject_Type.tp_richcompare) { - return SPEC_FAIL_COMPARE_OP_BASEOBJECT; + return SPEC_FAIL_COMPARE_BASEOBJECT; } return SPEC_FAIL_OTHER; } #endif void -_Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, +_Py_Specialize_CompareAndBranch(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, int oparg) { - assert(_PyOpcode_Caches[COMPARE_OP] == INLINE_CACHE_ENTRIES_COMPARE_OP); + assert(_PyOpcode_Caches[COMPARE_AND_BRANCH] == INLINE_CACHE_ENTRIES_COMPARE_OP); _PyCompareOpCache *cache = (_PyCompareOpCache *)(instr + 1); +#ifndef NDEBUG int next_opcode = _Py_OPCODE(instr[INLINE_CACHE_ENTRIES_COMPARE_OP + 1]); - if (next_opcode != POP_JUMP_IF_FALSE && next_opcode != POP_JUMP_IF_TRUE) { - if (next_opcode == EXTENDED_ARG) { - SPECIALIZATION_FAIL(COMPARE_OP, SPEC_FAIL_COMPARE_OP_EXTENDED_ARG); - goto failure; - } - SPECIALIZATION_FAIL(COMPARE_OP, SPEC_FAIL_COMPARE_OP_NOT_FOLLOWED_BY_COND_JUMP); - goto failure; - } + assert(next_opcode == POP_JUMP_IF_FALSE || next_opcode == POP_JUMP_IF_TRUE); +#endif if (Py_TYPE(lhs) != Py_TYPE(rhs)) { - SPECIALIZATION_FAIL(COMPARE_OP, compare_op_fail_kind(lhs, rhs)); + SPECIALIZATION_FAIL(COMPARE_AND_BRANCH, compare_op_fail_kind(lhs, rhs)); goto failure; } if (PyFloat_CheckExact(lhs)) { - _py_set_opcode(instr, COMPARE_OP_FLOAT_JUMP); + _py_set_opcode(instr, COMPARE_AND_BRANCH_FLOAT); goto success; } if (PyLong_CheckExact(lhs)) { if (Py_ABS(Py_SIZE(lhs)) <= 1 && Py_ABS(Py_SIZE(rhs)) <= 1) { - _py_set_opcode(instr, COMPARE_OP_INT_JUMP); + _py_set_opcode(instr, COMPARE_AND_BRANCH_INT); goto success; } else { - SPECIALIZATION_FAIL(COMPARE_OP, SPEC_FAIL_COMPARE_OP_BIG_INT); + SPECIALIZATION_FAIL(COMPARE_AND_BRANCH, SPEC_FAIL_COMPARE_BIG_INT); goto failure; } } if (PyUnicode_CheckExact(lhs)) { int cmp = oparg >> 4; if (cmp != Py_EQ && cmp != Py_NE) { - SPECIALIZATION_FAIL(COMPARE_OP, SPEC_FAIL_COMPARE_OP_STRING); + SPECIALIZATION_FAIL(COMPARE_AND_BRANCH, SPEC_FAIL_COMPARE_STRING); goto failure; } else { - _py_set_opcode(instr, COMPARE_OP_STR_JUMP); + _py_set_opcode(instr, COMPARE_AND_BRANCH_STR); goto success; } } - SPECIALIZATION_FAIL(COMPARE_OP, compare_op_fail_kind(lhs, rhs)); + SPECIALIZATION_FAIL(COMPARE_AND_BRANCH, compare_op_fail_kind(lhs, rhs)); failure: - STAT_INC(COMPARE_OP, failure); - _py_set_opcode(instr, COMPARE_OP); + STAT_INC(COMPARE_AND_BRANCH, failure); + _py_set_opcode(instr, COMPARE_AND_BRANCH); cache->counter = adaptive_counter_backoff(cache->counter); return; success: - STAT_INC(COMPARE_OP, success); + STAT_INC(COMPARE_AND_BRANCH, success); cache->counter = adaptive_counter_cooldown(); } diff --git a/Tools/scripts/summarize_stats.py b/Tools/scripts/summarize_stats.py index ce25374f3a9a..7789c4d3a17d 100644 --- a/Tools/scripts/summarize_stats.py +++ b/Tools/scripts/summarize_stats.py @@ -228,6 +228,8 @@ def kind_to_text(kind, defines, opname): return pretty(defines[kind][0]) if opname.endswith("ATTR"): opname = "ATTR" + if opname in ("COMPARE_OP", "COMPARE_AND_BRANCH"): + opname = "COMPARE" if opname.endswith("SUBSCR"): opname = "SUBSCR" for name in defines[kind]: From webhook-mailer at python.org Mon Jan 16 11:05:46 2023 From: webhook-mailer at python.org (zooba) Date: Mon, 16 Jan 2023 16:05:46 -0000 Subject: [Python-checkins] gh-100320: Fix path calculations on Windows when python.exe is moved outside of the normal location (GH-100947) Message-ID: https://github.com/python/cpython/commit/df10571a130c46d67756239e76ad69a3d730a990 commit: df10571a130c46d67756239e76ad69a3d730a990 branch: main author: Steve Dower committer: zooba date: 2023-01-16T16:05:39Z summary: gh-100320: Fix path calculations on Windows when python.exe is moved outside of the normal location (GH-100947) files: A Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst M Lib/test/test_embed.py M Lib/test/test_getpath.py M Modules/getpath.py diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 2dda7ccf7bf8..acffe234ecff 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -714,8 +714,10 @@ def check_config(self, configs, expected): if MS_WINDOWS: value = config.get(key := 'program_name') if value and isinstance(value, str): - ext = '_d.exe' if debug_build(sys.executable) else '.exe' - config[key] = value[:len(value.lower().removesuffix(ext))] + value = value[:len(value.lower().removesuffix('.exe'))] + if debug_build(sys.executable): + value = value[:len(value.lower().removesuffix('_d'))] + config[key] = value for key, value in list(expected.items()): if value is self.IGNORE_CONFIG: config.pop(key, None) @@ -1292,7 +1294,7 @@ def test_init_setpythonhome(self): stdlib = os.path.join(home, "Lib") # Because we are specifying 'home', module search paths # are fairly static - expected_paths = [paths[0], stdlib, os.path.join(home, 'DLLs')] + expected_paths = [paths[0], os.path.join(home, 'DLLs'), stdlib] else: version = f'{sys.version_info.major}.{sys.version_info.minor}' stdlib = os.path.join(home, sys.platlibdir, f'python{version}') @@ -1333,7 +1335,7 @@ def test_init_is_python_build_with_home(self): stdlib = os.path.join(home, "Lib") # Because we are specifying 'home', module search paths # are fairly static - expected_paths = [paths[0], stdlib, os.path.join(home, 'DLLs')] + expected_paths = [paths[0], os.path.join(home, 'DLLs'), stdlib] else: version = f'{sys.version_info.major}.{sys.version_info.minor}' stdlib = os.path.join(home, sys.platlibdir, f'python{version}') @@ -1361,7 +1363,7 @@ def test_init_is_python_build_with_home(self): config['_is_python_build'] = 1 exedir = os.path.dirname(sys.executable) with open(os.path.join(exedir, 'pybuilddir.txt'), encoding='utf8') as f: - expected_paths[2] = os.path.normpath( + expected_paths[1 if MS_WINDOWS else 2] = os.path.normpath( os.path.join(exedir, f'{f.read()}\n$'.splitlines()[0])) if not MS_WINDOWS: # PREFIX (default) is set when running in build directory @@ -1438,8 +1440,8 @@ def test_init_pybuilddir_win32(self): module_search_paths = self.module_search_paths() module_search_paths[-3] = os.path.join(tmpdir, os.path.basename(module_search_paths[-3])) - module_search_paths[-2] = stdlibdir - module_search_paths[-1] = tmpdir + module_search_paths[-2] = tmpdir + module_search_paths[-1] = stdlibdir executable = self.test_exe config = { diff --git a/Lib/test/test_getpath.py b/Lib/test/test_getpath.py index bdcf4a371916..b9cbe1d92c43 100644 --- a/Lib/test/test_getpath.py +++ b/Lib/test/test_getpath.py @@ -37,8 +37,9 @@ def test_normal_win32(self): module_search_paths_set=1, module_search_paths=[ r"C:\Python\python98.zip", - r"C:\Python\Lib", r"C:\Python\DLLs", + r"C:\Python\Lib", + r"C:\Python", ], ) actual = getpath(ns, expected) @@ -63,8 +64,8 @@ def test_buildtree_win32(self): module_search_paths_set=1, module_search_paths=[ r"C:\CPython\PCbuild\amd64\python98.zip", - r"C:\CPython\Lib", r"C:\CPython\PCbuild\amd64", + r"C:\CPython\Lib", ], ) actual = getpath(ns, expected) @@ -133,8 +134,9 @@ def test_registry_win32(self): r"C:\Python\python98.zip", "path1-dir", # should not contain not-subdirs - r"C:\Python\Lib", r"C:\Python\DLLs", + r"C:\Python\Lib", + r"C:\Python", ], ) actual = getpath(ns, expected) @@ -147,8 +149,9 @@ def test_registry_win32(self): module_search_paths_set=1, module_search_paths=[ r"C:\Python\python98.zip", - r"C:\Python\Lib", r"C:\Python\DLLs", + r"C:\Python\Lib", + r"C:\Python", ], ) actual = getpath(ns, expected) @@ -173,8 +176,9 @@ def test_symlink_normal_win32(self): module_search_paths_set=1, module_search_paths=[ r"C:\Python\python98.zip", - r"C:\Python\Lib", r"C:\Python\DLLs", + r"C:\Python\Lib", + r"C:\Python", ], ) actual = getpath(ns, expected) @@ -201,8 +205,8 @@ def test_symlink_buildtree_win32(self): module_search_paths_set=1, module_search_paths=[ r"C:\CPython\PCbuild\amd64\python98.zip", - r"C:\CPython\Lib", r"C:\CPython\PCbuild\amd64", + r"C:\CPython\Lib", ], ) actual = getpath(ns, expected) @@ -231,8 +235,8 @@ def test_buildtree_pythonhome_win32(self): module_search_paths_set=1, module_search_paths=[ r"C:\Out\python98.zip", - r"C:\CPython\Lib", r"C:\Out", + r"C:\CPython\Lib", ], ) actual = getpath(ns, expected) @@ -254,8 +258,8 @@ def test_no_dlls_win32(self): module_search_paths_set=1, module_search_paths=[ r"C:\Python\python98.zip", - r"C:\Python\Lib", r"C:\Python", + r"C:\Python\Lib", ], ) actual = getpath(ns, expected) diff --git a/Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst b/Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst new file mode 100644 index 000000000000..c206fc8520a5 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst @@ -0,0 +1,3 @@ +Ensures the ``PythonPath`` registry key from an install is used when +launching from a different copy of Python that relies on an existing install +to provide a copy of its modules and standard library. diff --git a/Modules/getpath.py b/Modules/getpath.py index ab0d2dc0636a..6a0883878778 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -597,25 +597,27 @@ def search_up(prefix, *landmarks, test=isfile): # Detect exec_prefix by searching from executable for the platstdlib_dir if PLATSTDLIB_LANDMARK and not exec_prefix: - if executable_dir: - if os_name == 'nt': - # QUIRK: For compatibility and security, do not search for DLLs - # directory. The fallback below will cover it - exec_prefix = executable_dir - else: - exec_prefix = search_up(executable_dir, PLATSTDLIB_LANDMARK, test=isdir) + if os_name == 'nt': + # QUIRK: Windows always assumed these were the same + # gh-100320: Our PYDs are assumed to be relative to the Lib directory + # (that is, prefix) rather than the executable (that is, executable_dir) + exec_prefix = prefix + if not exec_prefix and executable_dir: + exec_prefix = search_up(executable_dir, PLATSTDLIB_LANDMARK, test=isdir) if not exec_prefix and EXEC_PREFIX: exec_prefix = EXEC_PREFIX if not exec_prefix or not isdir(joinpath(exec_prefix, PLATSTDLIB_LANDMARK)): if os_name == 'nt': # QUIRK: If DLLs is missing on Windows, don't warn, just assume - # that it's all the same as prefix. - # gh-98790: We set platstdlib_dir here to avoid adding "DLLs" into - # sys.path when it doesn't exist, which would give site-packages - # precedence over executable_dir, which is *probably* where our PYDs - # live. Ideally, whoever changes our layout will tell us what the - # layout is, but in the past this worked, so it should keep working. - platstdlib_dir = exec_prefix = prefix + # that they're in exec_prefix + if not platstdlib_dir: + # gh-98790: We set platstdlib_dir here to avoid adding "DLLs" into + # sys.path when it doesn't exist in the platstdlib place, which + # would give Lib packages precedence over executable_dir where our + # PYDs *probably* live. Ideally, whoever changes our layout will tell + # us what the layout is, but in the past this worked, so it should + # keep working. + platstdlib_dir = exec_prefix else: warn('Could not find platform dependent libraries ') @@ -701,8 +703,14 @@ def search_up(prefix, *landmarks, test=isfile): except OSError: break if isinstance(v, str): - pythonpath.append(v) + pythonpath.extend(v.split(DELIM)) i += 1 + # Paths from the core key get appended last, but only + # when home was not set and we aren't in a build dir + if not home_was_set and not venv_prefix and not build_prefix: + v = winreg.QueryValue(key, None) + if isinstance(v, str): + pythonpath.extend(v.split(DELIM)) finally: winreg.CloseKey(key) except OSError: @@ -714,13 +722,17 @@ def search_up(prefix, *landmarks, test=isfile): pythonpath.append(joinpath(prefix, p)) # Then add stdlib_dir and platstdlib_dir - if os_name == 'nt' and venv_prefix: - # QUIRK: Windows generates paths differently in a venv + if os_name == 'nt': + # QUIRK: Windows generates paths differently if platstdlib_dir: pythonpath.append(platstdlib_dir) if stdlib_dir: pythonpath.append(stdlib_dir) - if executable_dir not in pythonpath: + if executable_dir and executable_dir not in pythonpath: + # QUIRK: the executable directory is on sys.path + # We keep it low priority, so that properly installed modules are + # found first. It may be earlier in the order if we found some + # reason to put it there. pythonpath.append(executable_dir) else: if stdlib_dir: From webhook-mailer at python.org Mon Jan 16 12:01:11 2023 From: webhook-mailer at python.org (zooba) Date: Mon, 16 Jan 2023 17:01:11 -0000 Subject: [Python-checkins] gh-100247: Improve documentation for custom shebang commands in py.exe launcher (GH-101083) Message-ID: https://github.com/python/cpython/commit/30753c37c6d00929af6afb5e0bc20bed4656d6b6 commit: 30753c37c6d00929af6afb5e0bc20bed4656d6b6 branch: main author: Steve Dower committer: zooba date: 2023-01-16T17:01:04Z summary: gh-100247: Improve documentation for custom shebang commands in py.exe launcher (GH-101083) files: M Doc/using/windows.rst diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index 69bca4d7bd30..b2a0214026e4 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -880,14 +880,15 @@ The environment variable :envvar:`PYLAUNCHER_NO_SEARCH_PATH` may be set Shebang lines that do not match any of these patterns are looked up in the ``[commands]`` section of the launcher's :ref:`.INI file `. This may be used to handle certain commands in a way that makes sense for your -system. The name of the command must be a single argument (no spaces), -and the value substituted is the full path to the executable (no arguments -may be added). +system. The name of the command must be a single argument (no spaces in the +shebang executable), and the value substituted is the full path to the +executable (additional arguments specified in the .INI will be quoted as part +of the filename). .. code-block:: ini [commands] - /bin/sh=C:\Program Files\Bash\bash.exe + /bin/xpython=C:\Program Files\XPython\python.exe Any commands not found in the .INI file are treated as **Windows** executable paths that are absolute or relative to the directory containing the script file. From webhook-mailer at python.org Mon Jan 16 12:01:11 2023 From: webhook-mailer at python.org (zooba) Date: Mon, 16 Jan 2023 17:01:11 -0000 Subject: [Python-checkins] gh-100320: Fix path calculations on Windows when python.exe is moved outside of the normal location (GH-100947) Message-ID: https://github.com/python/cpython/commit/87ade7ebda2e04aa9fb1930894fd3cadb88eee0b commit: 87ade7ebda2e04aa9fb1930894fd3cadb88eee0b branch: 3.11 author: Steve Dower committer: zooba date: 2023-01-16T17:00:34Z summary: gh-100320: Fix path calculations on Windows when python.exe is moved outside of the normal location (GH-100947) files: A Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst M Lib/test/test_embed.py M Lib/test/test_getpath.py M Modules/getpath.py diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index be7980eebe15..2affa17e800c 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -709,8 +709,10 @@ def check_config(self, configs, expected): if MS_WINDOWS: value = config.get(key := 'program_name') if value and isinstance(value, str): - ext = '_d.exe' if debug_build(sys.executable) else '.exe' - config[key] = value[:len(value.lower().removesuffix(ext))] + value = value[:len(value.lower().removesuffix('.exe'))] + if debug_build(sys.executable): + value = value[:len(value.lower().removesuffix('_d'))] + config[key] = value for key, value in list(expected.items()): if value is self.IGNORE_CONFIG: config.pop(key, None) @@ -1283,7 +1285,7 @@ def test_init_setpythonhome(self): stdlib = os.path.join(home, "Lib") # Because we are specifying 'home', module search paths # are fairly static - expected_paths = [paths[0], stdlib, os.path.join(home, 'DLLs')] + expected_paths = [paths[0], os.path.join(home, 'DLLs'), stdlib] else: version = f'{sys.version_info.major}.{sys.version_info.minor}' stdlib = os.path.join(home, sys.platlibdir, f'python{version}') @@ -1324,7 +1326,7 @@ def test_init_is_python_build_with_home(self): stdlib = os.path.join(home, "Lib") # Because we are specifying 'home', module search paths # are fairly static - expected_paths = [paths[0], stdlib, os.path.join(home, 'DLLs')] + expected_paths = [paths[0], os.path.join(home, 'DLLs'), stdlib] else: version = f'{sys.version_info.major}.{sys.version_info.minor}' stdlib = os.path.join(home, sys.platlibdir, f'python{version}') @@ -1352,7 +1354,7 @@ def test_init_is_python_build_with_home(self): config['_is_python_build'] = 1 exedir = os.path.dirname(sys.executable) with open(os.path.join(exedir, 'pybuilddir.txt'), encoding='utf8') as f: - expected_paths[2] = os.path.normpath( + expected_paths[1 if MS_WINDOWS else 2] = os.path.normpath( os.path.join(exedir, f'{f.read()}\n$'.splitlines()[0])) if not MS_WINDOWS: # PREFIX (default) is set when running in build directory @@ -1429,8 +1431,8 @@ def test_init_pybuilddir_win32(self): module_search_paths = self.module_search_paths() module_search_paths[-3] = os.path.join(tmpdir, os.path.basename(module_search_paths[-3])) - module_search_paths[-2] = stdlibdir - module_search_paths[-1] = tmpdir + module_search_paths[-2] = tmpdir + module_search_paths[-1] = stdlibdir executable = self.test_exe config = { diff --git a/Lib/test/test_getpath.py b/Lib/test/test_getpath.py index 87932b891f80..8d5b4265f269 100644 --- a/Lib/test/test_getpath.py +++ b/Lib/test/test_getpath.py @@ -38,8 +38,9 @@ def test_normal_win32(self): module_search_paths_set=1, module_search_paths=[ r"C:\Python\python98.zip", - r"C:\Python\Lib", r"C:\Python\DLLs", + r"C:\Python\Lib", + r"C:\Python", ], ) actual = getpath(ns, expected) @@ -64,8 +65,8 @@ def test_buildtree_win32(self): module_search_paths_set=1, module_search_paths=[ r"C:\CPython\PCbuild\amd64\python98.zip", - r"C:\CPython\Lib", r"C:\CPython\PCbuild\amd64", + r"C:\CPython\Lib", ], ) actual = getpath(ns, expected) @@ -134,8 +135,9 @@ def test_registry_win32(self): r"C:\Python\python98.zip", "path1-dir", # should not contain not-subdirs - r"C:\Python\Lib", r"C:\Python\DLLs", + r"C:\Python\Lib", + r"C:\Python", ], ) actual = getpath(ns, expected) @@ -148,8 +150,9 @@ def test_registry_win32(self): module_search_paths_set=1, module_search_paths=[ r"C:\Python\python98.zip", - r"C:\Python\Lib", r"C:\Python\DLLs", + r"C:\Python\Lib", + r"C:\Python", ], ) actual = getpath(ns, expected) @@ -174,8 +177,9 @@ def test_symlink_normal_win32(self): module_search_paths_set=1, module_search_paths=[ r"C:\Python\python98.zip", - r"C:\Python\Lib", r"C:\Python\DLLs", + r"C:\Python\Lib", + r"C:\Python", ], ) actual = getpath(ns, expected) @@ -202,8 +206,8 @@ def test_symlink_buildtree_win32(self): module_search_paths_set=1, module_search_paths=[ r"C:\CPython\PCbuild\amd64\python98.zip", - r"C:\CPython\Lib", r"C:\CPython\PCbuild\amd64", + r"C:\CPython\Lib", ], ) actual = getpath(ns, expected) @@ -232,8 +236,8 @@ def test_buildtree_pythonhome_win32(self): module_search_paths_set=1, module_search_paths=[ r"C:\Out\python98.zip", - r"C:\CPython\Lib", r"C:\Out", + r"C:\CPython\Lib", ], ) actual = getpath(ns, expected) @@ -255,8 +259,8 @@ def test_no_dlls_win32(self): module_search_paths_set=1, module_search_paths=[ r"C:\Python\python98.zip", - r"C:\Python\Lib", r"C:\Python", + r"C:\Python\Lib", ], ) actual = getpath(ns, expected) diff --git a/Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst b/Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst new file mode 100644 index 000000000000..c206fc8520a5 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-11-16-28-09.gh-issue-100320.2DU2it.rst @@ -0,0 +1,3 @@ +Ensures the ``PythonPath`` registry key from an install is used when +launching from a different copy of Python that relies on an existing install +to provide a copy of its modules and standard library. diff --git a/Modules/getpath.py b/Modules/getpath.py index fc533a8f5f5e..e84714170732 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -597,25 +597,27 @@ def search_up(prefix, *landmarks, test=isfile): # Detect exec_prefix by searching from executable for the platstdlib_dir if PLATSTDLIB_LANDMARK and not exec_prefix: - if executable_dir: - if os_name == 'nt': - # QUIRK: For compatibility and security, do not search for DLLs - # directory. The fallback below will cover it - exec_prefix = executable_dir - else: - exec_prefix = search_up(executable_dir, PLATSTDLIB_LANDMARK, test=isdir) + if os_name == 'nt': + # QUIRK: Windows always assumed these were the same + # gh-100320: Our PYDs are assumed to be relative to the Lib directory + # (that is, prefix) rather than the executable (that is, executable_dir) + exec_prefix = prefix + if not exec_prefix and executable_dir: + exec_prefix = search_up(executable_dir, PLATSTDLIB_LANDMARK, test=isdir) if not exec_prefix and EXEC_PREFIX: exec_prefix = EXEC_PREFIX if not exec_prefix or not isdir(joinpath(exec_prefix, PLATSTDLIB_LANDMARK)): if os_name == 'nt': # QUIRK: If DLLs is missing on Windows, don't warn, just assume - # that it's all the same as prefix. - # gh-98790: We set platstdlib_dir here to avoid adding "DLLs" into - # sys.path when it doesn't exist, which would give site-packages - # precedence over executable_dir, which is *probably* where our PYDs - # live. Ideally, whoever changes our layout will tell us what the - # layout is, but in the past this worked, so it should keep working. - platstdlib_dir = exec_prefix = prefix + # that they're in exec_prefix + if not platstdlib_dir: + # gh-98790: We set platstdlib_dir here to avoid adding "DLLs" into + # sys.path when it doesn't exist in the platstdlib place, which + # would give Lib packages precedence over executable_dir where our + # PYDs *probably* live. Ideally, whoever changes our layout will tell + # us what the layout is, but in the past this worked, so it should + # keep working. + platstdlib_dir = exec_prefix else: warn('Could not find platform dependent libraries ') @@ -701,8 +703,14 @@ def search_up(prefix, *landmarks, test=isfile): except OSError: break if isinstance(v, str): - pythonpath.append(v) + pythonpath.extend(v.split(DELIM)) i += 1 + # Paths from the core key get appended last, but only + # when home was not set and we aren't in a build dir + if not home_was_set and not venv_prefix and not build_prefix: + v = winreg.QueryValue(key, None) + if isinstance(v, str): + pythonpath.extend(v.split(DELIM)) finally: winreg.CloseKey(key) except OSError: @@ -714,13 +722,17 @@ def search_up(prefix, *landmarks, test=isfile): pythonpath.append(joinpath(prefix, p)) # Then add stdlib_dir and platstdlib_dir - if os_name == 'nt' and venv_prefix: - # QUIRK: Windows generates paths differently in a venv + if os_name == 'nt': + # QUIRK: Windows generates paths differently if platstdlib_dir: pythonpath.append(platstdlib_dir) if stdlib_dir: pythonpath.append(stdlib_dir) - if executable_dir not in pythonpath: + if executable_dir and executable_dir not in pythonpath: + # QUIRK: the executable directory is on sys.path + # We keep it low priority, so that properly installed modules are + # found first. It may be earlier in the order if we found some + # reason to put it there. pythonpath.append(executable_dir) else: if stdlib_dir: From webhook-mailer at python.org Mon Jan 16 12:13:56 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 16 Jan 2023 17:13:56 -0000 Subject: [Python-checkins] gh-100247: Improve documentation for custom shebang commands in py.exe launcher (GH-101083) Message-ID: https://github.com/python/cpython/commit/a3510c3a4e7922987e083dffc19ecd93c85d153b commit: a3510c3a4e7922987e083dffc19ecd93c85d153b branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-16T09:13:47-08:00 summary: gh-100247: Improve documentation for custom shebang commands in py.exe launcher (GH-101083) (cherry picked from commit 30753c37c6d00929af6afb5e0bc20bed4656d6b6) Co-authored-by: Steve Dower files: M Doc/using/windows.rst diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index 7272f1b62c65..ce1d55e6e2e2 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -867,14 +867,15 @@ The environment variable :envvar:`PYLAUNCHER_NO_SEARCH_PATH` may be set Shebang lines that do not match any of these patterns are looked up in the ``[commands]`` section of the launcher's :ref:`.INI file `. This may be used to handle certain commands in a way that makes sense for your -system. The name of the command must be a single argument (no spaces), -and the value substituted is the full path to the executable (no arguments -may be added). +system. The name of the command must be a single argument (no spaces in the +shebang executable), and the value substituted is the full path to the +executable (additional arguments specified in the .INI will be quoted as part +of the filename). .. code-block:: ini [commands] - /bin/sh=C:\Program Files\Bash\bash.exe + /bin/xpython=C:\Program Files\XPython\python.exe Any commands not found in the .INI file are treated as **Windows** executable paths that are absolute or relative to the directory containing the script file. From webhook-mailer at python.org Mon Jan 16 13:45:43 2023 From: webhook-mailer at python.org (pablogsal) Date: Mon, 16 Jan 2023 18:45:43 -0000 Subject: [Python-checkins] gh-101046: Fix a potential memory leak in the parser when raising MemoryError (#101051) Message-ID: https://github.com/python/cpython/commit/1de4395f62bb140563761ef5cbdf46accef3c550 commit: 1de4395f62bb140563761ef5cbdf46accef3c550 branch: main author: Pablo Galindo Salgado committer: pablogsal date: 2023-01-16T18:45:37Z summary: gh-101046: Fix a potential memory leak in the parser when raising MemoryError (#101051) files: A Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst M Parser/parser.c M Tools/peg_generator/pegen/c_generator.py diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst new file mode 100644 index 000000000000..f600473620f1 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst @@ -0,0 +1,2 @@ +Fix a possible memory leak in the parser when raising :exc:`MemoryError`. +Patch by Pablo Galindo diff --git a/Parser/parser.c b/Parser/parser.c index d0bc25927bec..845c1739d63a 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -23803,6 +23803,7 @@ _loop0_1_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -23872,6 +23873,7 @@ _loop0_2_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -23941,6 +23943,7 @@ _loop1_3_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -24024,6 +24027,7 @@ _loop0_5_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -24607,6 +24611,7 @@ _loop1_14_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -24853,6 +24858,7 @@ _loop0_19_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -24973,6 +24979,7 @@ _loop0_21_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25189,6 +25196,7 @@ _loop0_24_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25258,6 +25266,7 @@ _loop1_25_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25341,6 +25350,7 @@ _loop0_27_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25508,6 +25518,7 @@ _loop0_30_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25666,6 +25677,7 @@ _loop1_32_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25884,6 +25896,7 @@ _loop0_36_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25953,6 +25966,7 @@ _loop0_37_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26022,6 +26036,7 @@ _loop0_38_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26091,6 +26106,7 @@ _loop1_39_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26165,6 +26181,7 @@ _loop0_40_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26234,6 +26251,7 @@ _loop1_41_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26308,6 +26326,7 @@ _loop1_42_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26382,6 +26401,7 @@ _loop1_43_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26456,6 +26476,7 @@ _loop0_44_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26525,6 +26546,7 @@ _loop1_45_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26599,6 +26621,7 @@ _loop0_46_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26668,6 +26691,7 @@ _loop1_47_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26742,6 +26766,7 @@ _loop0_48_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26811,6 +26836,7 @@ _loop0_49_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26880,6 +26906,7 @@ _loop1_50_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26963,6 +26990,7 @@ _loop0_52_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27083,6 +27111,7 @@ _loop0_54_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27203,6 +27232,7 @@ _loop0_56_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27323,6 +27353,7 @@ _loop0_58_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27511,6 +27542,7 @@ _loop1_60_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27585,6 +27617,7 @@ _loop1_61_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27753,6 +27786,7 @@ _loop1_64_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27836,6 +27870,7 @@ _loop0_66_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28226,6 +28261,7 @@ _loop0_72_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28346,6 +28382,7 @@ _loop0_74_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28524,6 +28561,7 @@ _loop0_77_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28644,6 +28682,7 @@ _loop0_79_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28755,6 +28794,7 @@ _loop1_80_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28829,6 +28869,7 @@ _loop1_81_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28912,6 +28953,7 @@ _loop0_83_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29023,6 +29065,7 @@ _loop1_84_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29097,6 +29140,7 @@ _loop1_85_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29171,6 +29215,7 @@ _loop1_86_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29298,6 +29343,7 @@ _loop0_89_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29745,6 +29791,7 @@ _loop0_95_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29814,6 +29861,7 @@ _loop0_96_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29883,6 +29931,7 @@ _loop0_97_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29952,6 +30001,7 @@ _loop1_98_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30026,6 +30076,7 @@ _loop0_99_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30095,6 +30146,7 @@ _loop1_100_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30169,6 +30221,7 @@ _loop1_101_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30243,6 +30296,7 @@ _loop1_102_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30317,6 +30371,7 @@ _loop0_103_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30386,6 +30441,7 @@ _loop1_104_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30460,6 +30516,7 @@ _loop0_105_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30529,6 +30586,7 @@ _loop1_106_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30603,6 +30661,7 @@ _loop0_107_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30672,6 +30731,7 @@ _loop1_108_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30746,6 +30806,7 @@ _loop1_109_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30879,6 +30940,7 @@ _loop0_112_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30990,6 +31052,7 @@ _loop1_113_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31064,6 +31127,7 @@ _loop0_114_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31133,6 +31197,7 @@ _loop0_115_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31271,6 +31336,7 @@ _loop0_118_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31439,6 +31505,7 @@ _loop0_121_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31559,6 +31626,7 @@ _loop0_123_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31679,6 +31747,7 @@ _loop0_125_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31799,6 +31868,7 @@ _loop0_127_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31910,6 +31980,7 @@ _loop0_128_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31988,6 +32059,7 @@ _loop0_130_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32099,6 +32171,7 @@ _loop1_131_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32223,6 +32296,7 @@ _loop0_134_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32343,6 +32417,7 @@ _loop0_136_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32463,6 +32538,7 @@ _loop0_138_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32583,6 +32659,7 @@ _loop0_140_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32703,6 +32780,7 @@ _loop0_142_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -33505,6 +33583,7 @@ _loop0_154_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -33574,6 +33653,7 @@ _loop0_155_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -33643,6 +33723,7 @@ _loop0_156_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34021,6 +34102,7 @@ _loop0_162_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34090,6 +34172,7 @@ _loop0_163_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34159,6 +34242,7 @@ _loop0_164_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34228,6 +34312,7 @@ _loop1_165_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34360,6 +34445,7 @@ _loop0_167_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34487,6 +34573,7 @@ _loop0_169_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34556,6 +34643,7 @@ _loop1_170_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34807,6 +34895,7 @@ _loop0_174_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35011,6 +35100,7 @@ _loop1_177_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35143,6 +35233,7 @@ _loop0_179_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35212,6 +35303,7 @@ _loop0_180_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35281,6 +35373,7 @@ _loop0_181_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35359,6 +35452,7 @@ _loop0_183_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35528,6 +35622,7 @@ _loop0_185_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35655,6 +35750,7 @@ _loop0_187_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35724,6 +35820,7 @@ _loop1_188_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35798,6 +35895,7 @@ _loop1_189_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35991,6 +36089,7 @@ _loop0_192_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -36281,6 +36380,7 @@ _loop0_197_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -36401,6 +36501,7 @@ _loop0_199_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -36521,6 +36622,7 @@ _loop0_201_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -36641,6 +36743,7 @@ _loop0_203_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -36810,6 +36913,7 @@ _loop0_205_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -36879,6 +36983,7 @@ _loop1_206_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -36995,6 +37100,7 @@ _loop0_208_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -37064,6 +37170,7 @@ _loop1_209_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -37592,6 +37699,7 @@ _loop0_221_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; diff --git a/Tools/peg_generator/pegen/c_generator.py b/Tools/peg_generator/pegen/c_generator.py index 31bb50598332..c41d87b74d1c 100644 --- a/Tools/peg_generator/pegen/c_generator.py +++ b/Tools/peg_generator/pegen/c_generator.py @@ -801,7 +801,7 @@ def handle_alt_loop(self, node: Alt, is_gather: bool, rulename: Optional[str]) - self.print( "void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));" ) - self.out_of_memory_return(f"!_new_children") + self.out_of_memory_return(f"!_new_children", cleanup_code="PyMem_Free(_children);") self.print("_children = _new_children;") self.print("}") self.print("_children[_n++] = _res;") From webhook-mailer at python.org Mon Jan 16 18:48:44 2023 From: webhook-mailer at python.org (pablogsal) Date: Mon, 16 Jan 2023 23:48:44 -0000 Subject: [Python-checkins] [3.10] gh-101046: Fix a potential memory leak in the parser when raising MemoryError (GH-101051). (#101086) Message-ID: https://github.com/python/cpython/commit/9345eea2568bc590e1c2d4c418e3238ca7948ceb commit: 9345eea2568bc590e1c2d4c418e3238ca7948ceb branch: 3.10 author: Pablo Galindo Salgado committer: pablogsal date: 2023-01-16T23:48:38Z summary: [3.10] gh-101046: Fix a potential memory leak in the parser when raising MemoryError (GH-101051). (#101086) Co-authored-by: Pablo Galindo Salgado files: A Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst M Parser/parser.c M Tools/peg_generator/pegen/c_generator.py diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst new file mode 100644 index 000000000000..f600473620f1 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst @@ -0,0 +1,2 @@ +Fix a possible memory leak in the parser when raising :exc:`MemoryError`. +Patch by Pablo Galindo diff --git a/Parser/parser.c b/Parser/parser.c index 458ada0e52b8..a2104afb41cb 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -22065,6 +22065,7 @@ _loop0_1_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -22134,6 +22135,7 @@ _loop0_2_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -22212,6 +22214,7 @@ _loop0_4_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -22332,6 +22335,7 @@ _loop0_6_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -22452,6 +22456,7 @@ _loop0_8_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -22572,6 +22577,7 @@ _loop0_10_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -22683,6 +22689,7 @@ _loop1_11_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -22766,6 +22773,7 @@ _loop0_13_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -23349,6 +23357,7 @@ _loop1_22_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -23548,6 +23557,7 @@ _loop0_26_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -23668,6 +23678,7 @@ _loop0_28_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -23884,6 +23895,7 @@ _loop0_31_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -23953,6 +23965,7 @@ _loop1_32_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -24036,6 +24049,7 @@ _loop0_34_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -24203,6 +24217,7 @@ _loop0_37_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -24370,6 +24385,7 @@ _loop0_40_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -24490,6 +24506,7 @@ _loop0_42_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -24610,6 +24627,7 @@ _loop0_44_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -24730,6 +24748,7 @@ _loop0_46_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -24918,6 +24937,7 @@ _loop1_48_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25039,6 +25059,7 @@ _loop1_50_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25122,6 +25143,7 @@ _loop0_52_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25512,6 +25534,7 @@ _loop0_58_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25632,6 +25655,7 @@ _loop0_60_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25810,6 +25834,7 @@ _loop0_63_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25930,6 +25955,7 @@ _loop0_65_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26224,6 +26250,7 @@ _loop0_70_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26293,6 +26320,7 @@ _loop0_71_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26362,6 +26390,7 @@ _loop0_72_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26431,6 +26460,7 @@ _loop1_73_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26505,6 +26535,7 @@ _loop0_74_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26574,6 +26605,7 @@ _loop1_75_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26648,6 +26680,7 @@ _loop1_76_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26722,6 +26755,7 @@ _loop1_77_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26796,6 +26830,7 @@ _loop0_78_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26865,6 +26900,7 @@ _loop1_79_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26939,6 +26975,7 @@ _loop0_80_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27008,6 +27045,7 @@ _loop1_81_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27082,6 +27120,7 @@ _loop0_82_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27151,6 +27190,7 @@ _loop1_83_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27225,6 +27265,7 @@ _loop1_84_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27349,6 +27390,7 @@ _loop1_86_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27432,6 +27474,7 @@ _loop0_88_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27543,6 +27586,7 @@ _loop1_89_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27617,6 +27661,7 @@ _loop0_90_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27686,6 +27731,7 @@ _loop0_91_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27755,6 +27801,7 @@ _loop0_92_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27824,6 +27871,7 @@ _loop1_93_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27898,6 +27946,7 @@ _loop0_94_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27967,6 +28016,7 @@ _loop1_95_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28041,6 +28091,7 @@ _loop1_96_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28115,6 +28166,7 @@ _loop1_97_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28189,6 +28241,7 @@ _loop0_98_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28258,6 +28311,7 @@ _loop1_99_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28332,6 +28386,7 @@ _loop0_100_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28401,6 +28456,7 @@ _loop1_101_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28475,6 +28531,7 @@ _loop0_102_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28544,6 +28601,7 @@ _loop1_103_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28618,6 +28676,7 @@ _loop1_104_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28692,6 +28751,7 @@ _loop1_105_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28766,6 +28826,7 @@ _loop1_106_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28893,6 +28954,7 @@ _loop0_109_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29282,6 +29344,7 @@ _loop1_114_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29533,6 +29596,7 @@ _loop0_119_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29644,6 +29708,7 @@ _loop1_120_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29718,6 +29783,7 @@ _loop0_121_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29787,6 +29853,7 @@ _loop0_122_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29865,6 +29932,7 @@ _loop0_124_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30033,6 +30101,7 @@ _loop0_127_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30153,6 +30222,7 @@ _loop0_129_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30273,6 +30343,7 @@ _loop0_131_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30393,6 +30464,7 @@ _loop0_133_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30504,6 +30576,7 @@ _loop0_134_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30582,6 +30655,7 @@ _loop0_136_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30693,6 +30767,7 @@ _loop1_137_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30817,6 +30892,7 @@ _loop0_140_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31477,6 +31553,7 @@ _loop0_149_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31546,6 +31623,7 @@ _loop0_150_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31615,6 +31693,7 @@ _loop0_151_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31935,6 +32014,7 @@ _loop0_156_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32004,6 +32084,7 @@ _loop1_157_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32078,6 +32159,7 @@ _loop0_158_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32147,6 +32229,7 @@ _loop1_159_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32429,6 +32512,7 @@ _loop0_164_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32549,6 +32633,7 @@ _loop0_166_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32669,6 +32754,7 @@ _loop0_168_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32789,6 +32875,7 @@ _loop0_170_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -33269,6 +33356,7 @@ _loop0_180_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; diff --git a/Tools/peg_generator/pegen/c_generator.py b/Tools/peg_generator/pegen/c_generator.py index 2f0d829d795f..4a93693e6143 100644 --- a/Tools/peg_generator/pegen/c_generator.py +++ b/Tools/peg_generator/pegen/c_generator.py @@ -783,7 +783,7 @@ def handle_alt_loop(self, node: Alt, is_gather: bool, rulename: Optional[str]) - self.print( "void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));" ) - self.out_of_memory_return(f"!_new_children") + self.out_of_memory_return(f"!_new_children", cleanup_code="PyMem_Free(_children);") self.print("_children = _new_children;") self.print("}") self.print("_children[_n++] = _res;") From webhook-mailer at python.org Mon Jan 16 18:48:56 2023 From: webhook-mailer at python.org (pablogsal) Date: Mon, 16 Jan 2023 23:48:56 -0000 Subject: [Python-checkins] [3.11] gh-101046: Fix a potential memory leak in the parser when raising MemoryError (GH-101051) (#101085) Message-ID: https://github.com/python/cpython/commit/31b82abb5c545f1e0b2ebba5fa28281c2c298173 commit: 31b82abb5c545f1e0b2ebba5fa28281c2c298173 branch: 3.11 author: Pablo Galindo Salgado committer: pablogsal date: 2023-01-16T23:48:51Z summary: [3.11] gh-101046: Fix a potential memory leak in the parser when raising MemoryError (GH-101051) (#101085) Co-authored-by: Pablo Galindo Salgado files: A Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst M Parser/parser.c M Tools/peg_generator/pegen/c_generator.py diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst new file mode 100644 index 000000000000..f600473620f1 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-15-03-26-04.gh-issue-101046.g2CM4S.rst @@ -0,0 +1,2 @@ +Fix a possible memory leak in the parser when raising :exc:`MemoryError`. +Patch by Pablo Galindo diff --git a/Parser/parser.c b/Parser/parser.c index bee94fe14666..f5aecbc42dcd 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -23575,6 +23575,7 @@ _loop0_1_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -23644,6 +23645,7 @@ _loop0_2_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -23713,6 +23715,7 @@ _loop1_3_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -23796,6 +23799,7 @@ _loop0_5_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -24379,6 +24383,7 @@ _loop1_14_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -24625,6 +24630,7 @@ _loop0_19_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -24745,6 +24751,7 @@ _loop0_21_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -24961,6 +24968,7 @@ _loop0_24_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25030,6 +25038,7 @@ _loop1_25_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25113,6 +25122,7 @@ _loop0_27_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25280,6 +25290,7 @@ _loop0_30_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25438,6 +25449,7 @@ _loop1_32_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25656,6 +25668,7 @@ _loop0_36_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25725,6 +25738,7 @@ _loop0_37_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25794,6 +25808,7 @@ _loop0_38_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25863,6 +25878,7 @@ _loop1_39_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -25937,6 +25953,7 @@ _loop0_40_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26006,6 +26023,7 @@ _loop1_41_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26080,6 +26098,7 @@ _loop1_42_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26154,6 +26173,7 @@ _loop1_43_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26228,6 +26248,7 @@ _loop0_44_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26297,6 +26318,7 @@ _loop1_45_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26371,6 +26393,7 @@ _loop0_46_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26440,6 +26463,7 @@ _loop1_47_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26514,6 +26538,7 @@ _loop0_48_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26583,6 +26608,7 @@ _loop0_49_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26652,6 +26678,7 @@ _loop1_50_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26735,6 +26762,7 @@ _loop0_52_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26855,6 +26883,7 @@ _loop0_54_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -26975,6 +27004,7 @@ _loop0_56_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27095,6 +27125,7 @@ _loop0_58_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27283,6 +27314,7 @@ _loop1_60_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27357,6 +27389,7 @@ _loop1_61_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27525,6 +27558,7 @@ _loop1_64_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27608,6 +27642,7 @@ _loop0_66_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -27998,6 +28033,7 @@ _loop0_72_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28118,6 +28154,7 @@ _loop0_74_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28296,6 +28333,7 @@ _loop0_77_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28416,6 +28454,7 @@ _loop0_79_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28527,6 +28566,7 @@ _loop1_80_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28601,6 +28641,7 @@ _loop1_81_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28684,6 +28725,7 @@ _loop0_83_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28795,6 +28837,7 @@ _loop1_84_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28869,6 +28912,7 @@ _loop1_85_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -28943,6 +28987,7 @@ _loop1_86_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29070,6 +29115,7 @@ _loop0_89_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29517,6 +29563,7 @@ _loop0_95_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29586,6 +29633,7 @@ _loop0_96_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29655,6 +29703,7 @@ _loop0_97_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29724,6 +29773,7 @@ _loop1_98_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29798,6 +29848,7 @@ _loop0_99_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29867,6 +29918,7 @@ _loop1_100_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -29941,6 +29993,7 @@ _loop1_101_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30015,6 +30068,7 @@ _loop1_102_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30089,6 +30143,7 @@ _loop0_103_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30158,6 +30213,7 @@ _loop1_104_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30232,6 +30288,7 @@ _loop0_105_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30301,6 +30358,7 @@ _loop1_106_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30375,6 +30433,7 @@ _loop0_107_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30444,6 +30503,7 @@ _loop1_108_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30518,6 +30578,7 @@ _loop1_109_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30651,6 +30712,7 @@ _loop0_112_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30762,6 +30824,7 @@ _loop1_113_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30836,6 +30899,7 @@ _loop0_114_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -30905,6 +30969,7 @@ _loop0_115_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31043,6 +31108,7 @@ _loop0_118_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31211,6 +31277,7 @@ _loop0_121_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31331,6 +31398,7 @@ _loop0_123_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31451,6 +31519,7 @@ _loop0_125_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31571,6 +31640,7 @@ _loop0_127_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31682,6 +31752,7 @@ _loop0_128_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31760,6 +31831,7 @@ _loop0_130_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31871,6 +31943,7 @@ _loop1_131_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -31995,6 +32068,7 @@ _loop0_134_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32115,6 +32189,7 @@ _loop0_136_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32235,6 +32310,7 @@ _loop0_138_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32355,6 +32431,7 @@ _loop0_140_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -32475,6 +32552,7 @@ _loop0_142_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -33177,6 +33255,7 @@ _loop0_152_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -33246,6 +33325,7 @@ _loop0_153_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -33315,6 +33395,7 @@ _loop0_154_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -33635,6 +33716,7 @@ _loop0_159_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -33704,6 +33786,7 @@ _loop0_160_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -33773,6 +33856,7 @@ _loop1_161_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -33905,6 +33989,7 @@ _loop0_163_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34032,6 +34117,7 @@ _loop0_165_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34159,6 +34245,7 @@ _loop0_167_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34228,6 +34315,7 @@ _loop1_168_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34479,6 +34567,7 @@ _loop0_172_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34683,6 +34772,7 @@ _loop1_175_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34757,6 +34847,7 @@ _loop0_176_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34826,6 +34917,7 @@ _loop0_177_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -34904,6 +34996,7 @@ _loop0_179_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35073,6 +35166,7 @@ _loop0_181_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35200,6 +35294,7 @@ _loop0_183_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35327,6 +35422,7 @@ _loop0_185_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35396,6 +35492,7 @@ _loop1_186_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35470,6 +35567,7 @@ _loop1_187_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35663,6 +35761,7 @@ _loop0_190_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -35953,6 +36052,7 @@ _loop0_195_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -36073,6 +36173,7 @@ _loop0_197_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -36193,6 +36294,7 @@ _loop0_199_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -36313,6 +36415,7 @@ _loop0_201_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -36482,6 +36585,7 @@ _loop0_203_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -36551,6 +36655,7 @@ _loop1_204_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -36667,6 +36772,7 @@ _loop0_206_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -36736,6 +36842,7 @@ _loop1_207_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; @@ -37264,6 +37371,7 @@ _loop0_219_rule(Parser *p) _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); if (!_new_children) { + PyMem_Free(_children); p->error_indicator = 1; PyErr_NoMemory(); p->level--; diff --git a/Tools/peg_generator/pegen/c_generator.py b/Tools/peg_generator/pegen/c_generator.py index 65bfd5900a69..295bc1f83e8d 100644 --- a/Tools/peg_generator/pegen/c_generator.py +++ b/Tools/peg_generator/pegen/c_generator.py @@ -801,7 +801,7 @@ def handle_alt_loop(self, node: Alt, is_gather: bool, rulename: Optional[str]) - self.print( "void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));" ) - self.out_of_memory_return(f"!_new_children") + self.out_of_memory_return(f"!_new_children", cleanup_code="PyMem_Free(_children);") self.print("_children = _new_children;") self.print("}") self.print("_children[_n++] = _res;") From webhook-mailer at python.org Tue Jan 17 05:52:07 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Tue, 17 Jan 2023 10:52:07 -0000 Subject: [Python-checkins] Fix typo in `ReprEnum` documentation (#101079) Message-ID: https://github.com/python/cpython/commit/c5660ae96f2ab5732c68c301ce9a63009f432d93 commit: c5660ae96f2ab5732c68c301ce9a63009f432d93 branch: main author: Viicos <65306057+Viicos at users.noreply.github.com> committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-17T16:21:56+05:30 summary: Fix typo in `ReprEnum` documentation (#101079) files: M Doc/library/enum.rst diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 9d1a297a903a..b9cc802e752e 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -617,7 +617,7 @@ Data Types .. class:: ReprEnum - :class:`!ReprEum` uses the :meth:`repr() ` of :class:`Enum`, + :class:`!ReprEnum` uses the :meth:`repr() ` of :class:`Enum`, but the :class:`str() ` of the mixed-in data type: * :meth:`!int.__str__` for :class:`IntEnum` and :class:`IntFlag` From webhook-mailer at python.org Tue Jan 17 05:59:41 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 17 Jan 2023 10:59:41 -0000 Subject: [Python-checkins] Fix typo in `ReprEnum` documentation (GH-101079) Message-ID: https://github.com/python/cpython/commit/5e52778b1a2a86fe89578b957b1898d165a350fe commit: 5e52778b1a2a86fe89578b957b1898d165a350fe branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-17T02:59:35-08:00 summary: Fix typo in `ReprEnum` documentation (GH-101079) (cherry picked from commit c5660ae96f2ab5732c68c301ce9a63009f432d93) Co-authored-by: Viicos <65306057+Viicos at users.noreply.github.com> files: M Doc/library/enum.rst diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 0768914c2a9e..0456e74748c3 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -613,7 +613,7 @@ Data Types .. class:: ReprEnum - :class:`!ReprEum` uses the :meth:`repr() ` of :class:`Enum`, + :class:`!ReprEnum` uses the :meth:`repr() ` of :class:`Enum`, but the :class:`str() ` of the mixed-in data type: * :meth:`!int.__str__` for :class:`IntEnum` and :class:`IntFlag` From webhook-mailer at python.org Tue Jan 17 14:23:30 2023 From: webhook-mailer at python.org (pfmoore) Date: Tue, 17 Jan 2023 19:23:30 -0000 Subject: [Python-checkins] gh-82052: Don't send partial UTF-8 sequences to the Windows API (GH-101103) Message-ID: https://github.com/python/cpython/commit/f34176b77f222726d901595968a4b44456186da4 commit: f34176b77f222726d901595968a4b44456186da4 branch: main author: Paul Moore committer: pfmoore date: 2023-01-17T19:23:06Z summary: gh-82052: Don't send partial UTF-8 sequences to the Windows API (GH-101103) Don't send partial UTF-8 sequences to the Windows API files: A Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst M Modules/_io/winconsoleio.c diff --git a/Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst b/Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst new file mode 100644 index 000000000000..4f7ab200b85c --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst @@ -0,0 +1 @@ +Fixed an issue where writing more than 32K of Unicode output to the console screen in one go can result in mojibake. diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index d5de64b4ac3d..4f41ab965e2e 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -954,7 +954,7 @@ _io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b) { BOOL res = TRUE; wchar_t *wbuf; - DWORD len, wlen, n = 0; + DWORD len, wlen, orig_len, n = 0; HANDLE handle; if (self->fd == -1) @@ -984,6 +984,21 @@ _io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b) have to reduce and recalculate. */ while (wlen > 32766 / sizeof(wchar_t)) { len /= 2; + orig_len = len; + /* Reduce the length until we hit the final byte of a UTF-8 sequence + * (top bit is unset). Fix for github issue 82052. + */ + while (len > 0 && (((char *)b->buf)[len-1] & 0x80) != 0) + --len; + /* If we hit a length of 0, something has gone wrong. This shouldn't + * be possible, as valid UTF-8 can have at most 3 non-final bytes + * before a final one, and our buffer is way longer than that. + * But to be on the safe side, if we hit this issue we just restore + * the original length and let the console API sort it out. + */ + if (len == 0) { + len = orig_len; + } wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0); } Py_END_ALLOW_THREADS From webhook-mailer at python.org Tue Jan 17 14:53:02 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 17 Jan 2023 19:53:02 -0000 Subject: [Python-checkins] gh-82052: Don't send partial UTF-8 sequences to the Windows API (GH-101103) Message-ID: https://github.com/python/cpython/commit/3ef9f6b508a8524f385cdc9fdd4b4afca0eac59b commit: 3ef9f6b508a8524f385cdc9fdd4b4afca0eac59b branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-17T11:52:50-08:00 summary: gh-82052: Don't send partial UTF-8 sequences to the Windows API (GH-101103) Don't send partial UTF-8 sequences to the Windows API (cherry picked from commit f34176b77f222726d901595968a4b44456186da4) Co-authored-by: Paul Moore files: A Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst M Modules/_io/winconsoleio.c diff --git a/Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst b/Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst new file mode 100644 index 000000000000..4f7ab200b85c --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst @@ -0,0 +1 @@ +Fixed an issue where writing more than 32K of Unicode output to the console screen in one go can result in mojibake. diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index 5c1a6dd86fc5..c8f3481e665a 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -954,7 +954,7 @@ _io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b) { BOOL res = TRUE; wchar_t *wbuf; - DWORD len, wlen, n = 0; + DWORD len, wlen, orig_len, n = 0; HANDLE handle; if (self->fd == -1) @@ -984,6 +984,21 @@ _io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b) have to reduce and recalculate. */ while (wlen > 32766 / sizeof(wchar_t)) { len /= 2; + orig_len = len; + /* Reduce the length until we hit the final byte of a UTF-8 sequence + * (top bit is unset). Fix for github issue 82052. + */ + while (len > 0 && (((char *)b->buf)[len-1] & 0x80) != 0) + --len; + /* If we hit a length of 0, something has gone wrong. This shouldn't + * be possible, as valid UTF-8 can have at most 3 non-final bytes + * before a final one, and our buffer is way longer than that. + * But to be on the safe side, if we hit this issue we just restore + * the original length and let the console API sort it out. + */ + if (len == 0) { + len = orig_len; + } wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0); } Py_END_ALLOW_THREADS From webhook-mailer at python.org Tue Jan 17 14:53:51 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 17 Jan 2023 19:53:51 -0000 Subject: [Python-checkins] gh-82052: Don't send partial UTF-8 sequences to the Windows API (GH-101103) Message-ID: https://github.com/python/cpython/commit/940763140f7519a125229782ca7a095af01edda4 commit: 940763140f7519a125229782ca7a095af01edda4 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-17T11:53:45-08:00 summary: gh-82052: Don't send partial UTF-8 sequences to the Windows API (GH-101103) Don't send partial UTF-8 sequences to the Windows API (cherry picked from commit f34176b77f222726d901595968a4b44456186da4) Co-authored-by: Paul Moore files: A Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst M Modules/_io/winconsoleio.c diff --git a/Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst b/Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst new file mode 100644 index 000000000000..4f7ab200b85c --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-17-18-17-58.gh-issue-82052.mWyysT.rst @@ -0,0 +1 @@ +Fixed an issue where writing more than 32K of Unicode output to the console screen in one go can result in mojibake. diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index 460f2d3fa071..7d605d9f9034 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -956,7 +956,7 @@ _io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b) { BOOL res = TRUE; wchar_t *wbuf; - DWORD len, wlen, n = 0; + DWORD len, wlen, orig_len, n = 0; HANDLE handle; if (self->fd == -1) @@ -986,6 +986,21 @@ _io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b) have to reduce and recalculate. */ while (wlen > 32766 / sizeof(wchar_t)) { len /= 2; + orig_len = len; + /* Reduce the length until we hit the final byte of a UTF-8 sequence + * (top bit is unset). Fix for github issue 82052. + */ + while (len > 0 && (((char *)b->buf)[len-1] & 0x80) != 0) + --len; + /* If we hit a length of 0, something has gone wrong. This shouldn't + * be possible, as valid UTF-8 can have at most 3 non-final bytes + * before a final one, and our buffer is way longer than that. + * But to be on the safe side, if we hit this issue we just restore + * the original length and let the console API sort it out. + */ + if (len == 0) { + len = orig_len; + } wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0); } Py_END_ALLOW_THREADS From webhook-mailer at python.org Tue Jan 17 18:59:34 2023 From: webhook-mailer at python.org (gvanrossum) Date: Tue, 17 Jan 2023 23:59:34 -0000 Subject: [Python-checkins] GH-98831: Implement array support in cases generator (#100912) Message-ID: https://github.com/python/cpython/commit/80e3e3423cb456ba341f95d6a07e94cf3b16c0be commit: 80e3e3423cb456ba341f95d6a07e94cf3b16c0be branch: main author: Guido van Rossum committer: gvanrossum date: 2023-01-17T15:59:19-08:00 summary: GH-98831: Implement array support in cases generator (#100912) You can now write things like this: ``` inst(BUILD_STRING, (pieces[oparg] -- str)) { ... } inst(LIST_APPEND, (list, unused[oparg-1], v -- list, unused[oparg-1])) { ... } ``` Note that array output effects are only partially supported (they must be named `unused` or correspond to an input effect). files: M Python/bytecodes.c M Python/generated_cases.c.h M Python/opcode_metadata.h M Tools/cases_generator/generate_cases.py M Tools/cases_generator/parser.py M Tools/cases_generator/test_generator.py diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 18f9eabd842c..344e9bb80aae 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -451,16 +451,12 @@ dummy_func( DISPATCH_INLINED(new_frame); } - // Alternative: (list, unused[oparg], v -- list, unused[oparg]) - inst(LIST_APPEND, (v --)) { - PyObject *list = PEEK(oparg + 1); // +1 to account for v staying on stack + inst(LIST_APPEND, (list, unused[oparg-1], v -- list, unused[oparg-1])) { ERROR_IF(_PyList_AppendTakeRef((PyListObject *)list, v) < 0, error); PREDICT(JUMP_BACKWARD); } - // Alternative: (set, unused[oparg], v -- set, unused[oparg]) - inst(SET_ADD, (v --)) { - PyObject *set = PEEK(oparg + 1); // +1 to account for v staying on stack + inst(SET_ADD, (set, unused[oparg-1], v -- set, unused[oparg-1])) { int err = PySet_Add(set, v); Py_DECREF(v); ERROR_IF(err, error); @@ -532,7 +528,7 @@ dummy_func( ERROR_IF(res == NULL, error); } - // stack effect: (__array[oparg] -- ) + // This should remain a legacy instruction. inst(RAISE_VARARGS) { PyObject *cause = NULL, *exc = NULL; switch (oparg) { @@ -1295,40 +1291,25 @@ dummy_func( } } - // stack effect: (__array[oparg] -- __0) - inst(BUILD_STRING) { - PyObject *str; - str = _PyUnicode_JoinArray(&_Py_STR(empty), - stack_pointer - oparg, oparg); - if (str == NULL) - goto error; - while (--oparg >= 0) { - PyObject *item = POP(); - Py_DECREF(item); + inst(BUILD_STRING, (pieces[oparg] -- str)) { + str = _PyUnicode_JoinArray(&_Py_STR(empty), pieces, oparg); + for (int i = 0; i < oparg; i++) { + Py_DECREF(pieces[i]); } - PUSH(str); + ERROR_IF(str == NULL, error); } - // stack effect: (__array[oparg] -- __0) - inst(BUILD_TUPLE) { - STACK_SHRINK(oparg); - PyObject *tup = _PyTuple_FromArraySteal(stack_pointer, oparg); - if (tup == NULL) - goto error; - PUSH(tup); + inst(BUILD_TUPLE, (values[oparg] -- tup)) { + tup = _PyTuple_FromArraySteal(values, oparg); + ERROR_IF(tup == NULL, error); } - // stack effect: (__array[oparg] -- __0) - inst(BUILD_LIST) { - STACK_SHRINK(oparg); - PyObject *list = _PyList_FromArraySteal(stack_pointer, oparg); - if (list == NULL) - goto error; - PUSH(list); + inst(BUILD_LIST, (values[oparg] -- list)) { + list = _PyList_FromArraySteal(values, oparg); + ERROR_IF(list == NULL, error); } - inst(LIST_EXTEND, (iterable -- )) { - PyObject *list = PEEK(oparg + 1); // iterable is still on the stack + inst(LIST_EXTEND, (list, unused[oparg-1], iterable -- list, unused[oparg-1])) { PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable); if (none_val == NULL) { if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError) && @@ -1346,48 +1327,40 @@ dummy_func( DECREF_INPUTS(); } - inst(SET_UPDATE, (iterable --)) { - PyObject *set = PEEK(oparg + 1); // iterable is still on the stack + inst(SET_UPDATE, (set, unused[oparg-1], iterable -- set, unused[oparg-1])) { int err = _PySet_Update(set, iterable); DECREF_INPUTS(); ERROR_IF(err < 0, error); } - // stack effect: (__array[oparg] -- __0) - inst(BUILD_SET) { - PyObject *set = PySet_New(NULL); + inst(BUILD_SET, (values[oparg] -- set)) { + set = PySet_New(NULL); int err = 0; - int i; - if (set == NULL) - goto error; - for (i = oparg; i > 0; i--) { - PyObject *item = PEEK(i); + for (int i = 0; i < oparg; i++) { + PyObject *item = values[i]; if (err == 0) err = PySet_Add(set, item); Py_DECREF(item); } - STACK_SHRINK(oparg); if (err != 0) { Py_DECREF(set); - goto error; + ERROR_IF(true, error); } - PUSH(set); } - // stack effect: (__array[oparg*2] -- __0) - inst(BUILD_MAP) { - PyObject *map = _PyDict_FromItems( - &PEEK(2*oparg), 2, - &PEEK(2*oparg - 1), 2, + inst(BUILD_MAP, (values[oparg*2] -- map)) { + map = _PyDict_FromItems( + values, 2, + values+1, 2, oparg); if (map == NULL) goto error; - while (oparg--) { - Py_DECREF(POP()); - Py_DECREF(POP()); + for (int i = 0; i < oparg; i++) { + Py_DECREF(values[i*2]); + Py_DECREF(values[i*2+1]); } - PUSH(map); + ERROR_IF(map == NULL, error); } inst(SETUP_ANNOTATIONS, (--)) { @@ -1432,28 +1405,21 @@ dummy_func( } } - // stack effect: (__array[oparg] -- ) - inst(BUILD_CONST_KEY_MAP) { - PyObject *map; - PyObject *keys = TOP(); + inst(BUILD_CONST_KEY_MAP, (values[oparg], keys -- map)) { if (!PyTuple_CheckExact(keys) || PyTuple_GET_SIZE(keys) != (Py_ssize_t)oparg) { _PyErr_SetString(tstate, PyExc_SystemError, "bad BUILD_CONST_KEY_MAP keys argument"); - goto error; + goto error; // Pop the keys and values. } map = _PyDict_FromItems( &PyTuple_GET_ITEM(keys, 0), 1, - &PEEK(oparg + 1), 1, oparg); - if (map == NULL) { - goto error; - } - - Py_DECREF(POP()); - while (oparg--) { - Py_DECREF(POP()); + values, 1, oparg); + Py_DECREF(keys); + for (int i = 0; i < oparg; i++) { + Py_DECREF(values[i]); } - PUSH(map); + ERROR_IF(map == NULL, error); } inst(DICT_UPDATE, (update --)) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index e9819dfda997..445511df2a3a 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -574,7 +574,7 @@ TARGET(LIST_APPEND) { PyObject *v = PEEK(1); - PyObject *list = PEEK(oparg + 1); // +1 to account for v staying on stack + PyObject *list = PEEK(2 + (oparg-1)); if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0) goto pop_1_error; STACK_SHRINK(1); PREDICT(JUMP_BACKWARD); @@ -583,7 +583,7 @@ TARGET(SET_ADD) { PyObject *v = PEEK(1); - PyObject *set = PEEK(oparg + 1); // +1 to account for v staying on stack + PyObject *set = PEEK(2 + (oparg-1)); int err = PySet_Add(set, v); Py_DECREF(v); if (err) goto pop_1_error; @@ -1511,40 +1511,44 @@ } TARGET(BUILD_STRING) { + PyObject **pieces = &PEEK(oparg); PyObject *str; - str = _PyUnicode_JoinArray(&_Py_STR(empty), - stack_pointer - oparg, oparg); - if (str == NULL) - goto error; - while (--oparg >= 0) { - PyObject *item = POP(); - Py_DECREF(item); + str = _PyUnicode_JoinArray(&_Py_STR(empty), pieces, oparg); + for (int i = 0; i < oparg; i++) { + Py_DECREF(pieces[i]); } - PUSH(str); + if (str == NULL) { STACK_SHRINK(oparg); goto error; } + STACK_SHRINK(oparg); + STACK_GROW(1); + POKE(1, str); DISPATCH(); } TARGET(BUILD_TUPLE) { + PyObject **values = &PEEK(oparg); + PyObject *tup; + tup = _PyTuple_FromArraySteal(values, oparg); + if (tup == NULL) { STACK_SHRINK(oparg); goto error; } STACK_SHRINK(oparg); - PyObject *tup = _PyTuple_FromArraySteal(stack_pointer, oparg); - if (tup == NULL) - goto error; - PUSH(tup); + STACK_GROW(1); + POKE(1, tup); DISPATCH(); } TARGET(BUILD_LIST) { + PyObject **values = &PEEK(oparg); + PyObject *list; + list = _PyList_FromArraySteal(values, oparg); + if (list == NULL) { STACK_SHRINK(oparg); goto error; } STACK_SHRINK(oparg); - PyObject *list = _PyList_FromArraySteal(stack_pointer, oparg); - if (list == NULL) - goto error; - PUSH(list); + STACK_GROW(1); + POKE(1, list); DISPATCH(); } TARGET(LIST_EXTEND) { PyObject *iterable = PEEK(1); - PyObject *list = PEEK(oparg + 1); // iterable is still on the stack + PyObject *list = PEEK(2 + (oparg-1)); PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable); if (none_val == NULL) { if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError) && @@ -1566,7 +1570,7 @@ TARGET(SET_UPDATE) { PyObject *iterable = PEEK(1); - PyObject *set = PEEK(oparg + 1); // iterable is still on the stack + PyObject *set = PEEK(2 + (oparg-1)); int err = _PySet_Update(set, iterable); Py_DECREF(iterable); if (err < 0) goto pop_1_error; @@ -1575,39 +1579,44 @@ } TARGET(BUILD_SET) { - PyObject *set = PySet_New(NULL); + PyObject **values = &PEEK(oparg); + PyObject *set; + set = PySet_New(NULL); int err = 0; - int i; - if (set == NULL) - goto error; - for (i = oparg; i > 0; i--) { - PyObject *item = PEEK(i); + for (int i = 0; i < oparg; i++) { + PyObject *item = values[i]; if (err == 0) err = PySet_Add(set, item); Py_DECREF(item); } - STACK_SHRINK(oparg); if (err != 0) { Py_DECREF(set); - goto error; + if (true) { STACK_SHRINK(oparg); goto error; } } - PUSH(set); + STACK_SHRINK(oparg); + STACK_GROW(1); + POKE(1, set); DISPATCH(); } TARGET(BUILD_MAP) { - PyObject *map = _PyDict_FromItems( - &PEEK(2*oparg), 2, - &PEEK(2*oparg - 1), 2, + PyObject **values = &PEEK(oparg*2); + PyObject *map; + map = _PyDict_FromItems( + values, 2, + values+1, 2, oparg); if (map == NULL) goto error; - while (oparg--) { - Py_DECREF(POP()); - Py_DECREF(POP()); + for (int i = 0; i < oparg; i++) { + Py_DECREF(values[i*2]); + Py_DECREF(values[i*2+1]); } - PUSH(map); + if (map == NULL) { STACK_SHRINK(oparg*2); goto error; } + STACK_SHRINK(oparg*2); + STACK_GROW(1); + POKE(1, map); DISPATCH(); } @@ -1655,26 +1664,25 @@ } TARGET(BUILD_CONST_KEY_MAP) { + PyObject *keys = PEEK(1); + PyObject **values = &PEEK(1 + oparg); PyObject *map; - PyObject *keys = TOP(); if (!PyTuple_CheckExact(keys) || PyTuple_GET_SIZE(keys) != (Py_ssize_t)oparg) { _PyErr_SetString(tstate, PyExc_SystemError, "bad BUILD_CONST_KEY_MAP keys argument"); - goto error; + goto error; // Pop the keys and values. } map = _PyDict_FromItems( &PyTuple_GET_ITEM(keys, 0), 1, - &PEEK(oparg + 1), 1, oparg); - if (map == NULL) { - goto error; - } - - Py_DECREF(POP()); - while (oparg--) { - Py_DECREF(POP()); + values, 1, oparg); + Py_DECREF(keys); + for (int i = 0; i < oparg; i++) { + Py_DECREF(values[i]); } - PUSH(map); + if (map == NULL) { STACK_SHRINK(oparg); goto pop_1_error; } + STACK_SHRINK(oparg); + POKE(1, map); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index d293adafeeec..e3d8ed340e44 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -45,8 +45,8 @@ static const struct { [BINARY_SUBSCR_TUPLE_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, [BINARY_SUBSCR_DICT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, [BINARY_SUBSCR_GETITEM] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, - [LIST_APPEND] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [SET_ADD] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LIST_APPEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SET_ADD] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [STORE_SUBSCR] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, [STORE_SUBSCR_LIST_INT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, [STORE_SUBSCR_DICT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, @@ -92,8 +92,8 @@ static const struct { [BUILD_STRING] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [BUILD_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [BUILD_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LIST_EXTEND] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [SET_UPDATE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LIST_EXTEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SET_UPDATE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [BUILD_SET] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [BUILD_MAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [SETUP_ANNOTATIONS] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 4bd99b78f811..59e249930b45 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -15,14 +15,14 @@ import parser from parser import StackEffect -DEFAULT_INPUT = os.path.relpath( - os.path.join(os.path.dirname(__file__), "../../Python/bytecodes.c") -) -DEFAULT_OUTPUT = os.path.relpath( - os.path.join(os.path.dirname(__file__), "../../Python/generated_cases.c.h") -) +HERE = os.path.dirname(__file__) +ROOT = os.path.join(HERE, "../..") +THIS = os.path.relpath(__file__, ROOT) + +DEFAULT_INPUT = os.path.relpath(os.path.join(ROOT, "Python/bytecodes.c")) +DEFAULT_OUTPUT = os.path.relpath(os.path.join(ROOT, "Python/generated_cases.c.h")) DEFAULT_METADATA_OUTPUT = os.path.relpath( - os.path.join(os.path.dirname(__file__), "../../Python/opcode_metadata.h") + os.path.join(ROOT, "Python/opcode_metadata.h") ) BEGIN_MARKER = "// BEGIN BYTECODES //" END_MARKER = "// END BYTECODES //" @@ -48,6 +48,55 @@ ) +def effect_size(effect: StackEffect) -> tuple[int, str]: + """Return the 'size' impact of a stack effect. + + Returns a tuple (numeric, symbolic) where: + + - numeric is an int giving the statically analyzable size of the effect + - symbolic is a string representing a variable effect (e.g. 'oparg*2') + + At most one of these will be non-zero / non-empty. + """ + if effect.size: + return 0, effect.size + else: + return 1, "" + + +def maybe_parenthesize(sym: str) -> str: + """Add parentheses around a string if it contains an operator. + + An exception is made for '*' which is common and harmless + in the context where the symbolic size is used. + """ + if re.match(r"^[\s\w*]+$", sym): + return sym + else: + return f"({sym})" + + +def list_effect_size(effects: list[StackEffect]) -> tuple[int, str]: + numeric = 0 + symbolic: list[str] = [] + for effect in effects: + diff, sym = effect_size(effect) + numeric += diff + if sym: + symbolic.append(maybe_parenthesize(sym)) + return numeric, " + ".join(symbolic) + + +def string_effect_size(arg: tuple[int, str]) -> str: + numeric, symbolic = arg + if numeric and symbolic: + return f"{numeric} + {symbolic}" + elif symbolic: + return symbolic + else: + return str(numeric) + + class Formatter: """Wraps an output stream with the ability to indent etc.""" @@ -83,28 +132,43 @@ def block(self, head: str): yield self.emit("}") - def stack_adjust(self, diff: int): + def stack_adjust(self, diff: int, input_effects: list[StackEffect], output_effects: list[StackEffect]): + # TODO: Get rid of 'diff' parameter + shrink, isym = list_effect_size(input_effects) + grow, osym = list_effect_size(output_effects) + diff += grow - shrink + if isym and isym != osym: + self.emit(f"STACK_SHRINK({isym});") + if diff < 0: + self.emit(f"STACK_SHRINK({-diff});") if diff > 0: self.emit(f"STACK_GROW({diff});") - elif diff < 0: - self.emit(f"STACK_SHRINK({-diff});") + if osym and osym != isym: + self.emit(f"STACK_GROW({osym});") def declare(self, dst: StackEffect, src: StackEffect | None): if dst.name == UNUSED: return - typ = f"{dst.type} " if dst.type else "PyObject *" + typ = f"{dst.type}" if dst.type else "PyObject *" init = "" if src: cast = self.cast(dst, src) init = f" = {cast}{src.name}" - self.emit(f"{typ}{dst.name}{init};") + sepa = "" if typ.endswith("*") else " " + self.emit(f"{typ}{sepa}{dst.name}{init};") def assign(self, dst: StackEffect, src: StackEffect): if src.name == UNUSED: return cast = self.cast(dst, src) - if m := re.match(r"^PEEK\((\d+)\)$", dst.name): + if m := re.match(r"^PEEK\((.*)\)$", dst.name): self.emit(f"POKE({m.group(1)}, {cast}{src.name});") + elif m := re.match(r"^&PEEK\(.*\)$", dst.name): + # NOTE: MOVE_ITEMS() does not actually exist. + # The only supported output array forms are: + # - unused[...] + # - X[...] where X[...] matches an input array exactly + self.emit(f"MOVE_ITEMS({dst.name}, {src.name}, {src.size});") elif m := re.match(r"^REG\(oparg(\d+)\)$", dst.name): self.emit(f"Py_XSETREF({dst.name}, {cast}{src.name});") else: @@ -172,7 +236,7 @@ def __init__(self, inst: parser.InstDef): num_dummies = (num_regs // 2) * 2 + 1 - num_regs fmt = "I" + "B"*num_regs + "X"*num_dummies else: - if variable_used(inst.block, "oparg"): + if variable_used(inst, "oparg"): fmt = "IB" else: fmt = "IX" @@ -200,7 +264,6 @@ def analyze_registers(self, a: "Analyzer") -> None: def write(self, out: Formatter) -> None: """Write one instruction, sans prologue and epilogue.""" # Write a static assertion that a family's cache size is correct - if family := self.family: if self.name == family.members[0]: if cache_size := family.size: @@ -211,8 +274,13 @@ def write(self, out: Formatter) -> None: if not self.register: # Write input stack effect variable declarations and initializations - for i, ieffect in enumerate(reversed(self.input_effects), 1): - src = StackEffect(f"PEEK({i})", "") + ieffects = list(reversed(self.input_effects)) + for i, ieffect in enumerate(ieffects): + isize = string_effect_size(list_effect_size(ieffects[:i+1])) + if ieffect.size: + src = StackEffect(f"&PEEK({isize})", "PyObject **") + else: + src = StackEffect(f"PEEK({isize})", "") out.declare(ieffect, src) else: # Write input register variable declarations and initializations @@ -236,14 +304,19 @@ def write(self, out: Formatter) -> None: if not self.register: # Write net stack growth/shrinkage - diff = len(self.output_effects) - len(self.input_effects) - out.stack_adjust(diff) + out.stack_adjust(0, self.input_effects, self.output_effects) # Write output stack effect assignments - for i, oeffect in enumerate(reversed(self.output_effects), 1): - if oeffect.name not in self.unmoved_names: - dst = StackEffect(f"PEEK({i})", "") - out.assign(dst, oeffect) + oeffects = list(reversed(self.output_effects)) + for i, oeffect in enumerate(oeffects): + if oeffect.name in self.unmoved_names: + continue + osize = string_effect_size(list_effect_size(oeffects[:i+1])) + if oeffect.size: + dst = StackEffect(f"&PEEK({osize})", "PyObject **") + else: + dst = StackEffect(f"PEEK({osize})", "") + out.assign(dst, oeffect) else: # Write output register assignments for oeffect, reg in zip(self.output_effects, self.output_registers): @@ -287,19 +360,21 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None # The code block is responsible for DECREF()ing them. # NOTE: If the label doesn't exist, just add it to ceval.c. if not self.register: - ninputs = len(self.input_effects) # Don't pop common input/output effects at the bottom! # These aren't DECREF'ed so they can stay. - for ieff, oeff in zip(self.input_effects, self.output_effects): - if ieff.name == oeff.name: - ninputs -= 1 - else: - break + ieffs = list(self.input_effects) + oeffs = list(self.output_effects) + while ieffs and oeffs and ieffs[0] == oeffs[0]: + ieffs.pop(0) + oeffs.pop(0) + ninputs, symbolic = list_effect_size(ieffs) + if ninputs: + label = f"pop_{ninputs}_{label}" else: - ninputs = 0 - if ninputs: + symbolic = "" + if symbolic: out.write_raw( - f"{extra}{space}if ({cond}) goto pop_{ninputs}_{label};\n" + f"{extra}{space}if ({cond}) {{ STACK_SHRINK({symbolic}); goto {label}; }}\n" ) else: out.write_raw(f"{extra}{space}if ({cond}) goto {label};\n") @@ -635,6 +710,13 @@ def stack_analysis( for thing in components: match thing: case Instruction() as instr: + if any(eff.size for eff in instr.input_effects + instr.output_effects): + # TODO: Eventually this will be needed, at least for macros. + self.error( + f"Instruction {instr.name!r} has variable-sized stack effect, " + "which are not supported in super- or macro instructions", + instr.inst, # TODO: Pass name+location of super/macro + ) current -= len(instr.input_effects) lowest = min(lowest, current) current += len(instr.output_effects) @@ -673,10 +755,8 @@ def write_metadata(self) -> None: with open(self.output_filename, "w") as f: # Write provenance header - f.write( - f"// This file is generated by {os.path.relpath(__file__)} --metadata\n" - ) - f.write(f"// from {os.path.relpath(self.filename)}\n") + f.write(f"// This file is generated by {THIS} --metadata\n") + f.write(f"// from {os.path.relpath(self.filename, ROOT)}\n") f.write(f"// Do not edit!\n") # Create formatter; the rest of the code uses this @@ -719,8 +799,11 @@ def write_metadata_for_inst(self, instr: Instruction) -> None: n_popped = n_pushed = -1 assert not instr.register else: - n_popped = len(instr.input_effects) - n_pushed = len(instr.output_effects) + n_popped, sym_popped = list_effect_size(instr.input_effects) + n_pushed, sym_pushed = list_effect_size(instr.output_effects) + if sym_popped or sym_pushed: + # TODO: Record symbolic effects (how?) + n_popped = n_pushed = -1 if instr.register: directions: list[str] = [] directions.extend("DIR_READ" for _ in instr.input_effects) @@ -754,8 +837,8 @@ def write_instructions(self) -> None: """Write instructions to output file.""" with open(self.output_filename, "w") as f: # Write provenance header - f.write(f"// This file is generated by {os.path.relpath(__file__)}\n") - f.write(f"// from {os.path.relpath(self.filename)}\n") + f.write(f"// This file is generated by {THIS}\n") + f.write(f"// from {os.path.relpath(self.filename, ROOT)}\n") f.write(f"// Do not edit!\n") # Create formatter; the rest of the code uses this @@ -848,7 +931,9 @@ def wrap_super_or_macro(self, up: SuperOrMacroInstruction): yield - self.out.stack_adjust(up.final_sp - up.initial_sp) + # TODO: Use slices of up.stack instead of numeric values + self.out.stack_adjust(up.final_sp - up.initial_sp, [], []) + for i, var in enumerate(reversed(up.stack[: up.final_sp]), 1): dst = StackEffect(f"PEEK({i})", "") self.out.assign(dst, var) @@ -899,9 +984,9 @@ def always_exits(lines: list[str]) -> bool: ) -def variable_used(block: parser.Block, name: str) -> bool: - """Determine whether a variable with a given name is used in a block.""" - return any(token.kind == "IDENTIFIER" and token.text == name for token in block.tokens) +def variable_used(node: parser.Node, name: str) -> bool: + """Determine whether a variable with a given name is used in a node.""" + return any(token.kind == "IDENTIFIER" and token.text == name for token in node.tokens) def main(): diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py index fad0cb90c69e..c2cebe96ccd6 100644 --- a/Tools/cases_generator/parser.py +++ b/Tools/cases_generator/parser.py @@ -38,7 +38,7 @@ def __repr__(self): @dataclass class Node: - context: Context | None = field(init=False, default=None) + context: Context | None = field(init=False, compare=False, default=None) @property def text(self) -> str: @@ -51,19 +51,37 @@ def to_text(self, dedent: int = 0) -> str: tokens = context.owner.tokens begin = context.begin end = context.end - return lx.to_text(tokens[begin:end], dedent) + return lx.to_text(self.tokens, dedent) + + @property + def tokens(self) -> list[lx.Token]: + context = self.context + if not context: + return [] + tokens = context.owner.tokens + begin = context.begin + end = context.end + return tokens[begin:end] @dataclass class Block(Node): - tokens: list[lx.Token] + # This just holds a context which has the list of tokens. + pass @dataclass class StackEffect(Node): name: str - type: str = "" - # TODO: array, condition + type: str = "" # Optional `:type` + size: str = "" # Optional `[size]` + # Note: we can have type or size but not both + # TODO: condition (can be combined with type but not size) + + + at dataclass +class Dimension(Node): + size: str @dataclass @@ -221,13 +239,31 @@ def cache_effect(self) -> CacheEffect | None: @contextual def stack_effect(self) -> StackEffect | None: - # IDENTIFIER [':' IDENTIFIER] - # TODO: Arrays, conditions + # IDENTIFIER + # | IDENTIFIER ':' IDENTIFIER + # | IDENTIFIER '[' dimension ']' + # TODO: Conditions if tkn := self.expect(lx.IDENTIFIER): - type = "" if self.expect(lx.COLON): - type = self.require(lx.IDENTIFIER).text - return StackEffect(tkn.text, type) + typ = self.require(lx.IDENTIFIER) + return StackEffect(tkn.text, typ.text) + elif self.expect(lx.LBRACKET): + if not (dim := self.dimension()): + raise self.make_syntax_error("Expected dimension") + self.require(lx.RBRACKET) + return StackEffect(tkn.text, "PyObject **", dim.text.strip()) + else: + return StackEffect(tkn.text) + + @contextual + def dimension(self) -> Dimension | None: + tokens: list[lx.Token] = [] + while (tkn := self.peek()) and tkn.kind != lx.RBRACKET: + tokens.append(tkn) + self.next() + if not tokens: + return None + return Dimension(lx.to_text(tokens).strip()) @contextual def super_def(self) -> Super | None: @@ -331,8 +367,8 @@ def members(self) -> list[str] | None: @contextual def block(self) -> Block: - tokens = self.c_blob() - return Block(tokens) + if self.c_blob(): + return Block() def c_blob(self) -> list[lx.Token]: tokens: list[lx.Token] = [] diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index e707a5334268..ae0c1e2f97c3 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -4,6 +4,36 @@ import tempfile import generate_cases +from parser import StackEffect + + +def test_effect_sizes(): + input_effects = [ + x := StackEffect("x", "", ""), + y := StackEffect("y", "", "oparg"), + z := StackEffect("z", "", "oparg*2"), + ] + output_effects = [ + a := StackEffect("a", "", ""), + b := StackEffect("b", "", "oparg*4"), + c := StackEffect("c", "", ""), + ] + other_effects = [ + p := StackEffect("p", "", "oparg<<1"), + q := StackEffect("q", "", ""), + r := StackEffect("r", "", ""), + ] + assert generate_cases.effect_size(x) == (1, "") + assert generate_cases.effect_size(y) == (0, "oparg") + assert generate_cases.effect_size(z) == (0, "oparg*2") + + assert generate_cases.list_effect_size(input_effects) == (1, "oparg + oparg*2") + assert generate_cases.list_effect_size(output_effects) == (2, "oparg*4") + assert generate_cases.list_effect_size(other_effects) == (2, "(oparg<<1)") + + assert generate_cases.string_effect_size(generate_cases.list_effect_size(input_effects)) == "1 + oparg + oparg*2" + assert generate_cases.string_effect_size(generate_cases.list_effect_size(output_effects)) == "2 + oparg*4" + assert generate_cases.string_effect_size(generate_cases.list_effect_size(other_effects)) == "2 + (oparg<<1)" def run_cases_test(input: str, expected: str): @@ -123,6 +153,24 @@ def test_binary_op(): """ run_cases_test(input, output) +def test_overlap(): + input = """ + inst(OP, (left, right -- left, result)) { + spam(); + } + """ + output = """ + TARGET(OP) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + PyObject *result; + spam(); + POKE(1, result); + DISPATCH(); + } + """ + run_cases_test(input, output) + def test_predictions(): input = """ inst(OP1, (--)) { @@ -308,3 +356,81 @@ def test_macro_instruction(): } """ run_cases_test(input, output) + +def test_array_input(): + input = """ + inst(OP, (below, values[oparg*2], above --)) { + spam(); + } + """ + output = """ + TARGET(OP) { + PyObject *above = PEEK(1); + PyObject **values = &PEEK(1 + oparg*2); + PyObject *below = PEEK(2 + oparg*2); + spam(); + STACK_SHRINK(oparg*2); + STACK_SHRINK(2); + DISPATCH(); + } + """ + run_cases_test(input, output) + +def test_array_output(): + input = """ + inst(OP, (-- below, values[oparg*3], above)) { + spam(); + } + """ + output = """ + TARGET(OP) { + PyObject *below; + PyObject **values; + PyObject *above; + spam(); + STACK_GROW(2); + STACK_GROW(oparg*3); + POKE(1, above); + MOVE_ITEMS(&PEEK(1 + oparg*3), values, oparg*3); + POKE(2 + oparg*3, below); + DISPATCH(); + } + """ + run_cases_test(input, output) + +def test_array_input_output(): + input = """ + inst(OP, (below, values[oparg] -- values[oparg], above)) { + spam(); + } + """ + output = """ + TARGET(OP) { + PyObject **values = &PEEK(oparg); + PyObject *below = PEEK(1 + oparg); + PyObject *above; + spam(); + POKE(1, above); + MOVE_ITEMS(&PEEK(1 + oparg), values, oparg); + DISPATCH(); + } + """ + run_cases_test(input, output) + +def test_array_error_if(): + input = """ + inst(OP, (extra, values[oparg] --)) { + ERROR_IF(oparg == 0, somewhere); + } + """ + output = """ + TARGET(OP) { + PyObject **values = &PEEK(oparg); + PyObject *extra = PEEK(1 + oparg); + if (oparg == 0) { STACK_SHRINK(oparg); goto pop_1_somewhere; } + STACK_SHRINK(oparg); + STACK_SHRINK(1); + DISPATCH(); + } + """ + run_cases_test(input, output) From webhook-mailer at python.org Tue Jan 17 19:06:46 2023 From: webhook-mailer at python.org (hauntsaninja) Date: Wed, 18 Jan 2023 00:06:46 -0000 Subject: [Python-checkins] Fix typo and old link in wasm readme (#101096) Message-ID: https://github.com/python/cpython/commit/48ec678287a3be1539823fa3fc0ef457ece7e1c6 commit: 48ec678287a3be1539823fa3fc0ef457ece7e1c6 branch: main author: Zac Hatfield-Dodds committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com> date: 2023-01-17T16:06:38-08:00 summary: Fix typo and old link in wasm readme (#101096) files: M Tools/wasm/README.md diff --git a/Tools/wasm/README.md b/Tools/wasm/README.md index f8dd6e675ea7..8874f9b7e35e 100644 --- a/Tools/wasm/README.md +++ b/Tools/wasm/README.md @@ -168,7 +168,7 @@ functions. - Most user, group, and permission related function and modules are not supported or don't work as expected, e.g.``pwd`` module, ``grp`` module, - ``os.setgroups``, ``os.chown``, and so on. ``lchown`` and `lchmod`` are + ``os.setgroups``, ``os.chown``, and so on. ``lchown`` and ``lchmod`` are not available. - ``umask`` is a no-op. - hard links (``os.link``) are not supported. @@ -192,7 +192,7 @@ functions. supports. It's currently known to crash in combination with threading. - glibc extensions for date and time formatting are not available. - ``locales`` module is affected by musl libc issues, - [bpo-46390](https://bugs.python.org/issue46390). + [gh-90548](https://github.com/python/cpython/issues/90548). - Python's object allocator ``obmalloc`` is disabled by default. - ``ensurepip`` is not available. - Some ``ctypes`` features like ``c_longlong`` and ``c_longdouble`` may need From webhook-mailer at python.org Wed Jan 18 04:36:48 2023 From: webhook-mailer at python.org (erlend-aasland) Date: Wed, 18 Jan 2023 09:36:48 -0000 Subject: [Python-checkins] Docs: improve sqlite3 placeholders example (#101092) Message-ID: https://github.com/python/cpython/commit/b84be8d9c0e6eca37be14c38250580251a3ef908 commit: b84be8d9c0e6eca37be14c38250580251a3ef908 branch: main author: Erlend E. Aasland committer: erlend-aasland date: 2023-01-18T10:36:17+01:00 summary: Docs: improve sqlite3 placeholders example (#101092) files: M Doc/library/sqlite3.rst diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index ba72b96c6741..8796bdfc0b6f 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1958,19 +1958,18 @@ Here's an example of both styles: con = sqlite3.connect(":memory:") cur = con.execute("CREATE TABLE lang(name, first_appeared)") - # This is the qmark style: - cur.execute("INSERT INTO lang VALUES(?, ?)", ("C", 1972)) - - # The qmark style used with executemany(): - lang_list = [ - ("Fortran", 1957), - ("Python", 1991), - ("Go", 2009), - ] - cur.executemany("INSERT INTO lang VALUES(?, ?)", lang_list) - - # And this is the named style: - cur.execute("SELECT * FROM lang WHERE first_appeared = :year", {"year": 1972}) + # This is the named style used with executemany(): + data = ( + {"name": "C", "year": 1972}, + {"name": "Fortran", "year": 1957}, + {"name": "Python", "year": 1991}, + {"name": "Go", "year": 2009}, + ) + cur.executemany("INSERT INTO lang VALUES(:name, :year)", data) + + # This is the qmark style used in a SELECT query: + params = (1972,) + cur.execute("SELECT * FROM lang WHERE first_appeared = ?", params) print(cur.fetchall()) .. testoutput:: From webhook-mailer at python.org Wed Jan 18 04:43:01 2023 From: webhook-mailer at python.org (miss-islington) Date: Wed, 18 Jan 2023 09:43:01 -0000 Subject: [Python-checkins] Docs: improve sqlite3 placeholders example (GH-101092) Message-ID: https://github.com/python/cpython/commit/2c1eeb508cf202d744d06b7d76d002d76f7f45bf commit: 2c1eeb508cf202d744d06b7d76d002d76f7f45bf branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-18T01:42:55-08:00 summary: Docs: improve sqlite3 placeholders example (GH-101092) (cherry picked from commit b84be8d9c0e6eca37be14c38250580251a3ef908) Co-authored-by: Erlend E. Aasland files: M Doc/library/sqlite3.rst diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 2cc0d8a4b4c3..065243c7eaad 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1455,19 +1455,18 @@ Here's an example of both styles: con = sqlite3.connect(":memory:") cur = con.execute("CREATE TABLE lang(name, first_appeared)") - # This is the qmark style: - cur.execute("INSERT INTO lang VALUES(?, ?)", ("C", 1972)) - - # The qmark style used with executemany(): - lang_list = [ - ("Fortran", 1957), - ("Python", 1991), - ("Go", 2009), - ] - cur.executemany("INSERT INTO lang VALUES(?, ?)", lang_list) - - # And this is the named style: - cur.execute("SELECT * FROM lang WHERE first_appeared = :year", {"year": 1972}) + # This is the named style used with executemany(): + data = ( + {"name": "C", "year": 1972}, + {"name": "Fortran", "year": 1957}, + {"name": "Python", "year": 1991}, + {"name": "Go", "year": 2009}, + ) + cur.executemany("INSERT INTO lang VALUES(:name, :year)", data) + + # This is the qmark style used in a SELECT query: + params = (1972,) + cur.execute("SELECT * FROM lang WHERE first_appeared = ?", params) print(cur.fetchall()) .. testoutput:: From webhook-mailer at python.org Wed Jan 18 04:43:50 2023 From: webhook-mailer at python.org (miss-islington) Date: Wed, 18 Jan 2023 09:43:50 -0000 Subject: [Python-checkins] Docs: improve sqlite3 placeholders example (GH-101092) Message-ID: https://github.com/python/cpython/commit/db65a326a4022fbd43648858b460f52734faf1b5 commit: db65a326a4022fbd43648858b460f52734faf1b5 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-18T01:43:44-08:00 summary: Docs: improve sqlite3 placeholders example (GH-101092) (cherry picked from commit b84be8d9c0e6eca37be14c38250580251a3ef908) Co-authored-by: Erlend E. Aasland files: M Doc/library/sqlite3.rst diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index c52bdd7ca4ac..ad5d063981f3 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1841,19 +1841,18 @@ Here's an example of both styles: con = sqlite3.connect(":memory:") cur = con.execute("CREATE TABLE lang(name, first_appeared)") - # This is the qmark style: - cur.execute("INSERT INTO lang VALUES(?, ?)", ("C", 1972)) - - # The qmark style used with executemany(): - lang_list = [ - ("Fortran", 1957), - ("Python", 1991), - ("Go", 2009), - ] - cur.executemany("INSERT INTO lang VALUES(?, ?)", lang_list) - - # And this is the named style: - cur.execute("SELECT * FROM lang WHERE first_appeared = :year", {"year": 1972}) + # This is the named style used with executemany(): + data = ( + {"name": "C", "year": 1972}, + {"name": "Fortran", "year": 1957}, + {"name": "Python", "year": 1991}, + {"name": "Go", "year": 2009}, + ) + cur.executemany("INSERT INTO lang VALUES(:name, :year)", data) + + # This is the qmark style used in a SELECT query: + params = (1972,) + cur.execute("SELECT * FROM lang WHERE first_appeared = ?", params) print(cur.fetchall()) .. testoutput:: From webhook-mailer at python.org Wed Jan 18 11:25:36 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Wed, 18 Jan 2023 16:25:36 -0000 Subject: [Python-checkins] fix typo in 3.12 What's New (#101105) Message-ID: https://github.com/python/cpython/commit/d65f48507045c87000c65dc2c4fa727f483caad6 commit: d65f48507045c87000c65dc2c4fa727f483caad6 branch: main author: Tushar Sadhwani committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-18T21:54:56+05:30 summary: fix typo in 3.12 What's New (#101105) files: M Doc/whatsnew/3.12.rst diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 72a3ad9f5c8c..0c85aef007e0 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -157,7 +157,7 @@ Other Language Changes in :gh:`96670`.) * The Garbage Collector now runs only on the eval breaker mechanism of the - Python bytecode evaluation loop instead on object allocations. The GC can + Python bytecode evaluation loop instead of object allocations. The GC can also run when :c:func:`PyErr_CheckSignals` is called so C extensions that need to run for a long time without executing any Python code also have a chance to execute the GC periodically. (Contributed by Pablo Galindo in From webhook-mailer at python.org Wed Jan 18 13:41:14 2023 From: webhook-mailer at python.org (gvanrossum) Date: Wed, 18 Jan 2023 18:41:14 -0000 Subject: [Python-checkins] GH-98831: Move assorted macros from ceval.h to a new header (#101116) Message-ID: https://github.com/python/cpython/commit/1f0d0a432cf431882b432eeba8315f84f818da6b commit: 1f0d0a432cf431882b432eeba8315f84f818da6b branch: main author: Guido van Rossum committer: gvanrossum date: 2023-01-18T10:41:07-08:00 summary: GH-98831: Move assorted macros from ceval.h to a new header (#101116) files: A Python/ceval_macros.h M Makefile.pre.in M Python/bytecodes.c M Python/ceval.c diff --git a/Makefile.pre.in b/Makefile.pre.in index 8c7a17be9e16..d98f98634b7d 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1466,8 +1466,12 @@ regen-cases: -o $(srcdir)/Python/opcode_metadata.h.new $(UPDATE_FILE) $(srcdir)/Python/opcode_metadata.h $(srcdir)/Python/opcode_metadata.h.new -Python/ceval.o: $(srcdir)/Python/opcode_targets.h $(srcdir)/Python/condvar.h $(srcdir)/Python/generated_cases.c.h - +Python/ceval.o: \ + $(srcdir)/Python/ceval_macros.h \ + $(srcdir)/Python/condvar.h \ + $(srcdir)/Python/generated_cases.c.h \ + $(srcdir)/Python/opcode_metadata.h \ + $(srcdir)/Python/opcode_targets.h Python/frozen.o: $(FROZEN_FILES_OUT) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 344e9bb80aae..5d5929fb2f68 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -34,41 +34,14 @@ #include "setobject.h" #include "structmember.h" // struct PyMemberDef, T_OFFSET_EX -void _PyFloat_ExactDealloc(PyObject *); -void _PyUnicode_ExactDealloc(PyObject *); - -/* Stack effect macros - * These will be mostly replaced by stack effect descriptions, - * but the tooling need to recognize them. - */ -#define SET_TOP(v) (stack_pointer[-1] = (v)) -#define SET_SECOND(v) (stack_pointer[-2] = (v)) -#define PEEK(n) (stack_pointer[-(n)]) -#define POKE(n, v) (stack_pointer[-(n)] = (v)) -#define PUSH(val) (*(stack_pointer++) = (val)) -#define POP() (*(--stack_pointer)) -#define TOP() PEEK(1) -#define SECOND() PEEK(2) -#define STACK_GROW(n) (stack_pointer += (n)) -#define STACK_SHRINK(n) (stack_pointer -= (n)) -#define EMPTY() 1 -#define STACK_LEVEL() 2 - -/* Local variable macros */ -#define GETLOCAL(i) (frame->localsplus[i]) -#define SETLOCAL(i, val) \ -do { \ - PyObject *_tmp = frame->localsplus[i]; \ - frame->localsplus[i] = (val); \ - Py_XDECREF(_tmp); \ -} while (0) +#define USE_COMPUTED_GOTOS 0 +#include "ceval_macros.h" /* Flow control macros */ #define DEOPT_IF(cond, instname) ((void)0) #define ERROR_IF(cond, labelname) ((void)0) -#define JUMPBY(offset) ((void)0) #define GO_TO_INSTRUCTION(instname) ((void)0) -#define DISPATCH_SAME_OPARG() ((void)0) +#define PREDICT(opname) ((void)0) #define inst(name, ...) case name: #define op(name, ...) /* NAME is ignored */ @@ -76,16 +49,14 @@ do { \ #define super(name) static int SUPER_##name #define family(name, ...) static int family_##name -#define NAME_ERROR_MSG \ - "name '%.200s' is not defined" - // Dummy variables for stack effects. static PyObject *value, *value1, *value2, *left, *right, *res, *sum, *prod, *sub; static PyObject *container, *start, *stop, *v, *lhs, *rhs; -static PyObject *list, *tuple, *dict, *owner; +static PyObject *list, *tuple, *dict, *owner, *set, *str, *tup, *map, *keys; static PyObject *exit_func, *lasti, *val, *retval, *obj, *iter; static PyObject *aiter, *awaitable, *iterable, *w, *exc_value, *bc; static PyObject *orig, *excs, *update, *b, *fromlist, *level, *from; +static PyObject **pieces, **values; static size_t jump; // Dummy variables for cache effects static uint16_t invert, counter, index, hint; @@ -456,7 +427,7 @@ dummy_func( PREDICT(JUMP_BACKWARD); } - inst(SET_ADD, (set, unused[oparg-1], v -- set, unused[oparg-1])) { + inst(SET_ADD, (set, unused[oparg-1], v -- set, unused[oparg-1])) { int err = PySet_Add(set, v); Py_DECREF(v); ERROR_IF(err, error); @@ -3336,8 +3307,10 @@ dummy_func( // END BYTECODES // } + dispatch_opcode: error: exception_unwind: + exit_unwind: handle_eval_breaker: resume_frame: resume_with_error: diff --git a/Python/ceval.c b/Python/ceval.c index ecbe2f9d5136..a97313c773ee 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -215,8 +215,6 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, static void _PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame); -#define NAME_ERROR_MSG \ - "name '%.200s' is not defined" #define UNBOUNDLOCAL_ERROR_MSG \ "cannot access local variable '%s' where it is not associated with a value" #define UNBOUNDFREE_ERROR_MSG \ @@ -600,352 +598,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) return _PyEval_EvalFrame(tstate, f->f_frame, throwflag); } - -/* Computed GOTOs, or - the-optimization-commonly-but-improperly-known-as-"threaded code" - using gcc's labels-as-values extension - (http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html). - - The traditional bytecode evaluation loop uses a "switch" statement, which - decent compilers will optimize as a single indirect branch instruction - combined with a lookup table of jump addresses. However, since the - indirect jump instruction is shared by all opcodes, the CPU will have a - hard time making the right prediction for where to jump next (actually, - it will be always wrong except in the uncommon case of a sequence of - several identical opcodes). - - "Threaded code" in contrast, uses an explicit jump table and an explicit - indirect jump instruction at the end of each opcode. Since the jump - instruction is at a different address for each opcode, the CPU will make a - separate prediction for each of these instructions, which is equivalent to - predicting the second opcode of each opcode pair. These predictions have - a much better chance to turn out valid, especially in small bytecode loops. - - A mispredicted branch on a modern CPU flushes the whole pipeline and - can cost several CPU cycles (depending on the pipeline depth), - and potentially many more instructions (depending on the pipeline width). - A correctly predicted branch, however, is nearly free. - - At the time of this writing, the "threaded code" version is up to 15-20% - faster than the normal "switch" version, depending on the compiler and the - CPU architecture. - - NOTE: care must be taken that the compiler doesn't try to "optimize" the - indirect jumps by sharing them between all opcodes. Such optimizations - can be disabled on gcc by using the -fno-gcse flag (or possibly - -fno-crossjumping). -*/ - -/* Use macros rather than inline functions, to make it as clear as possible - * to the C compiler that the tracing check is a simple test then branch. - * We want to be sure that the compiler knows this before it generates - * the CFG. - */ - -#ifdef WITH_DTRACE -#define OR_DTRACE_LINE | (PyDTrace_LINE_ENABLED() ? 255 : 0) -#else -#define OR_DTRACE_LINE -#endif - -#ifdef HAVE_COMPUTED_GOTOS - #ifndef USE_COMPUTED_GOTOS - #define USE_COMPUTED_GOTOS 1 - #endif -#else - #if defined(USE_COMPUTED_GOTOS) && USE_COMPUTED_GOTOS - #error "Computed gotos are not supported on this compiler." - #endif - #undef USE_COMPUTED_GOTOS - #define USE_COMPUTED_GOTOS 0 -#endif - -#ifdef Py_STATS -#define INSTRUCTION_START(op) \ - do { \ - frame->prev_instr = next_instr++; \ - OPCODE_EXE_INC(op); \ - if (_py_stats) _py_stats->opcode_stats[lastopcode].pair_count[op]++; \ - lastopcode = op; \ - } while (0) -#else -#define INSTRUCTION_START(op) (frame->prev_instr = next_instr++) -#endif - -#if USE_COMPUTED_GOTOS -# define TARGET(op) TARGET_##op: INSTRUCTION_START(op); -# define DISPATCH_GOTO() goto *opcode_targets[opcode] -#else -# define TARGET(op) case op: TARGET_##op: INSTRUCTION_START(op); -# define DISPATCH_GOTO() goto dispatch_opcode -#endif - -/* PRE_DISPATCH_GOTO() does lltrace if enabled. Normally a no-op */ -#ifdef LLTRACE -#define PRE_DISPATCH_GOTO() if (lltrace) { \ - lltrace_instruction(frame, stack_pointer, next_instr); } -#else -#define PRE_DISPATCH_GOTO() ((void)0) -#endif - - -/* Do interpreter dispatch accounting for tracing and instrumentation */ -#define DISPATCH() \ - { \ - NEXTOPARG(); \ - PRE_DISPATCH_GOTO(); \ - assert(cframe.use_tracing == 0 || cframe.use_tracing == 255); \ - opcode |= cframe.use_tracing OR_DTRACE_LINE; \ - DISPATCH_GOTO(); \ - } - -#define DISPATCH_SAME_OPARG() \ - { \ - opcode = _Py_OPCODE(*next_instr); \ - PRE_DISPATCH_GOTO(); \ - opcode |= cframe.use_tracing OR_DTRACE_LINE; \ - DISPATCH_GOTO(); \ - } - -#define DISPATCH_INLINED(NEW_FRAME) \ - do { \ - _PyFrame_SetStackPointer(frame, stack_pointer); \ - frame->prev_instr = next_instr - 1; \ - (NEW_FRAME)->previous = frame; \ - frame = cframe.current_frame = (NEW_FRAME); \ - CALL_STAT_INC(inlined_py_calls); \ - goto start_frame; \ - } while (0) - -#define CHECK_EVAL_BREAKER() \ - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); \ - if (_Py_atomic_load_relaxed_int32(eval_breaker)) { \ - goto handle_eval_breaker; \ - } - - -/* Tuple access macros */ - -#ifndef Py_DEBUG -#define GETITEM(v, i) PyTuple_GET_ITEM((v), (i)) -#else -static inline PyObject * -GETITEM(PyObject *v, Py_ssize_t i) { - assert(PyTuple_Check(v)); - assert(i >= 0); - assert(i < PyTuple_GET_SIZE(v)); - return PyTuple_GET_ITEM(v, i); -} -#endif - -/* Code access macros */ - -/* The integer overflow is checked by an assertion below. */ -#define INSTR_OFFSET() ((int)(next_instr - _PyCode_CODE(frame->f_code))) -#define NEXTOPARG() do { \ - _Py_CODEUNIT word = *next_instr; \ - opcode = _Py_OPCODE(word); \ - oparg = _Py_OPARG(word); \ - } while (0) -#define JUMPTO(x) (next_instr = _PyCode_CODE(frame->f_code) + (x)) -#define JUMPBY(x) (next_instr += (x)) - -/* OpCode prediction macros - Some opcodes tend to come in pairs thus making it possible to - predict the second code when the first is run. For example, - COMPARE_OP is often followed by POP_JUMP_IF_FALSE or POP_JUMP_IF_TRUE. - - Verifying the prediction costs a single high-speed test of a register - variable against a constant. If the pairing was good, then the - processor's own internal branch predication has a high likelihood of - success, resulting in a nearly zero-overhead transition to the - next opcode. A successful prediction saves a trip through the eval-loop - including its unpredictable switch-case branch. Combined with the - processor's internal branch prediction, a successful PREDICT has the - effect of making the two opcodes run as if they were a single new opcode - with the bodies combined. - - If collecting opcode statistics, your choices are to either keep the - predictions turned-on and interpret the results as if some opcodes - had been combined or turn-off predictions so that the opcode frequency - counter updates for both opcodes. - - Opcode prediction is disabled with threaded code, since the latter allows - the CPU to record separate branch prediction information for each - opcode. - -*/ - -#define PREDICT_ID(op) PRED_##op - -#if USE_COMPUTED_GOTOS -#define PREDICT(op) if (0) goto PREDICT_ID(op) -#else -#define PREDICT(op) \ - do { \ - _Py_CODEUNIT word = *next_instr; \ - opcode = _Py_OPCODE(word) | cframe.use_tracing OR_DTRACE_LINE; \ - if (opcode == op) { \ - oparg = _Py_OPARG(word); \ - INSTRUCTION_START(op); \ - goto PREDICT_ID(op); \ - } \ - } while(0) -#endif -#define PREDICTED(op) PREDICT_ID(op): - - -/* Stack manipulation macros */ - -/* The stack can grow at most MAXINT deep, as co_nlocals and - co_stacksize are ints. */ -#define STACK_LEVEL() ((int)(stack_pointer - _PyFrame_Stackbase(frame))) -#define STACK_SIZE() (frame->f_code->co_stacksize) -#define EMPTY() (STACK_LEVEL() == 0) -#define TOP() (stack_pointer[-1]) -#define SECOND() (stack_pointer[-2]) -#define THIRD() (stack_pointer[-3]) -#define FOURTH() (stack_pointer[-4]) -#define PEEK(n) (stack_pointer[-(n)]) -#define POKE(n, v) (stack_pointer[-(n)] = (v)) -#define SET_TOP(v) (stack_pointer[-1] = (v)) -#define SET_SECOND(v) (stack_pointer[-2] = (v)) -#define BASIC_STACKADJ(n) (stack_pointer += n) -#define BASIC_PUSH(v) (*stack_pointer++ = (v)) -#define BASIC_POP() (*--stack_pointer) - -#ifdef Py_DEBUG -#define PUSH(v) do { \ - BASIC_PUSH(v); \ - assert(STACK_LEVEL() <= STACK_SIZE()); \ - } while (0) -#define POP() (assert(STACK_LEVEL() > 0), BASIC_POP()) -#define STACK_GROW(n) do { \ - assert(n >= 0); \ - BASIC_STACKADJ(n); \ - assert(STACK_LEVEL() <= STACK_SIZE()); \ - } while (0) -#define STACK_SHRINK(n) do { \ - assert(n >= 0); \ - assert(STACK_LEVEL() >= n); \ - BASIC_STACKADJ(-(n)); \ - } while (0) -#else -#define PUSH(v) BASIC_PUSH(v) -#define POP() BASIC_POP() -#define STACK_GROW(n) BASIC_STACKADJ(n) -#define STACK_SHRINK(n) BASIC_STACKADJ(-(n)) -#endif - -/* Local variable macros */ - -#define GETLOCAL(i) (frame->localsplus[i]) - -/* The SETLOCAL() macro must not DECREF the local variable in-place and - then store the new value; it must copy the old value to a temporary - value, then store the new value, and then DECREF the temporary value. - This is because it is possible that during the DECREF the frame is - accessed by other code (e.g. a __del__ method or gc.collect()) and the - variable would be pointing to already-freed memory. */ -#define SETLOCAL(i, value) do { PyObject *tmp = GETLOCAL(i); \ - GETLOCAL(i) = value; \ - Py_XDECREF(tmp); } while (0) - -#define GO_TO_INSTRUCTION(op) goto PREDICT_ID(op) - -#ifdef Py_STATS -#define UPDATE_MISS_STATS(INSTNAME) \ - do { \ - STAT_INC(opcode, miss); \ - STAT_INC((INSTNAME), miss); \ - /* The counter is always the first cache entry: */ \ - if (ADAPTIVE_COUNTER_IS_ZERO(next_instr->cache)) { \ - STAT_INC((INSTNAME), deopt); \ - } \ - else { \ - /* This is about to be (incorrectly) incremented: */ \ - STAT_DEC((INSTNAME), deferred); \ - } \ - } while (0) -#else -#define UPDATE_MISS_STATS(INSTNAME) ((void)0) -#endif - -#define DEOPT_IF(COND, INSTNAME) \ - if ((COND)) { \ - /* This is only a single jump on release builds! */ \ - UPDATE_MISS_STATS((INSTNAME)); \ - assert(_PyOpcode_Deopt[opcode] == (INSTNAME)); \ - GO_TO_INSTRUCTION(INSTNAME); \ - } - - -#define GLOBALS() frame->f_globals -#define BUILTINS() frame->f_builtins -#define LOCALS() frame->f_locals - -/* Shared opcode macros */ - -#define TRACE_FUNCTION_EXIT() \ - if (cframe.use_tracing) { \ - if (trace_function_exit(tstate, frame, retval)) { \ - Py_DECREF(retval); \ - goto exit_unwind; \ - } \ - } - -#define DTRACE_FUNCTION_EXIT() \ - if (PyDTrace_FUNCTION_RETURN_ENABLED()) { \ - dtrace_function_return(frame); \ - } - -#define TRACE_FUNCTION_UNWIND() \ - if (cframe.use_tracing) { \ - /* Since we are already unwinding, \ - * we don't care if this raises */ \ - trace_function_exit(tstate, frame, NULL); \ - } - -#define TRACE_FUNCTION_ENTRY() \ - if (cframe.use_tracing) { \ - _PyFrame_SetStackPointer(frame, stack_pointer); \ - int err = trace_function_entry(tstate, frame); \ - stack_pointer = _PyFrame_GetStackPointer(frame); \ - if (err) { \ - goto error; \ - } \ - } - -#define TRACE_FUNCTION_THROW_ENTRY() \ - if (cframe.use_tracing) { \ - assert(frame->stacktop >= 0); \ - if (trace_function_entry(tstate, frame)) { \ - goto exit_unwind; \ - } \ - } - -#define DTRACE_FUNCTION_ENTRY() \ - if (PyDTrace_FUNCTION_ENTRY_ENABLED()) { \ - dtrace_function_entry(frame); \ - } - -#define ADAPTIVE_COUNTER_IS_ZERO(COUNTER) \ - (((COUNTER) >> ADAPTIVE_BACKOFF_BITS) == 0) - -#define ADAPTIVE_COUNTER_IS_MAX(COUNTER) \ - (((COUNTER) >> ADAPTIVE_BACKOFF_BITS) == ((1 << MAX_BACKOFF_VALUE) - 1)) - -#define DECREMENT_ADAPTIVE_COUNTER(COUNTER) \ - do { \ - assert(!ADAPTIVE_COUNTER_IS_ZERO((COUNTER))); \ - (COUNTER) -= (1 << ADAPTIVE_BACKOFF_BITS); \ - } while (0); - -#define INCREMENT_ADAPTIVE_COUNTER(COUNTER) \ - do { \ - assert(!ADAPTIVE_COUNTER_IS_MAX((COUNTER))); \ - (COUNTER) += (1 << ADAPTIVE_BACKOFF_BITS); \ - } while (0); +#include "ceval_macros.h" static int trace_function_entry(PyThreadState *tstate, _PyInterpreterFrame *frame) diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h new file mode 100644 index 000000000000..d7a8f0beeec8 --- /dev/null +++ b/Python/ceval_macros.h @@ -0,0 +1,349 @@ +// Macros needed by ceval.c and bytecodes.c + +/* Computed GOTOs, or + the-optimization-commonly-but-improperly-known-as-"threaded code" + using gcc's labels-as-values extension + (http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html). + + The traditional bytecode evaluation loop uses a "switch" statement, which + decent compilers will optimize as a single indirect branch instruction + combined with a lookup table of jump addresses. However, since the + indirect jump instruction is shared by all opcodes, the CPU will have a + hard time making the right prediction for where to jump next (actually, + it will be always wrong except in the uncommon case of a sequence of + several identical opcodes). + + "Threaded code" in contrast, uses an explicit jump table and an explicit + indirect jump instruction at the end of each opcode. Since the jump + instruction is at a different address for each opcode, the CPU will make a + separate prediction for each of these instructions, which is equivalent to + predicting the second opcode of each opcode pair. These predictions have + a much better chance to turn out valid, especially in small bytecode loops. + + A mispredicted branch on a modern CPU flushes the whole pipeline and + can cost several CPU cycles (depending on the pipeline depth), + and potentially many more instructions (depending on the pipeline width). + A correctly predicted branch, however, is nearly free. + + At the time of this writing, the "threaded code" version is up to 15-20% + faster than the normal "switch" version, depending on the compiler and the + CPU architecture. + + NOTE: care must be taken that the compiler doesn't try to "optimize" the + indirect jumps by sharing them between all opcodes. Such optimizations + can be disabled on gcc by using the -fno-gcse flag (or possibly + -fno-crossjumping). +*/ + +/* Use macros rather than inline functions, to make it as clear as possible + * to the C compiler that the tracing check is a simple test then branch. + * We want to be sure that the compiler knows this before it generates + * the CFG. + */ + +#ifdef WITH_DTRACE +#define OR_DTRACE_LINE | (PyDTrace_LINE_ENABLED() ? 255 : 0) +#else +#define OR_DTRACE_LINE +#endif + +#ifdef HAVE_COMPUTED_GOTOS + #ifndef USE_COMPUTED_GOTOS + #define USE_COMPUTED_GOTOS 1 + #endif +#else + #if defined(USE_COMPUTED_GOTOS) && USE_COMPUTED_GOTOS + #error "Computed gotos are not supported on this compiler." + #endif + #undef USE_COMPUTED_GOTOS + #define USE_COMPUTED_GOTOS 0 +#endif + +#ifdef Py_STATS +#define INSTRUCTION_START(op) \ + do { \ + frame->prev_instr = next_instr++; \ + OPCODE_EXE_INC(op); \ + if (_py_stats) _py_stats->opcode_stats[lastopcode].pair_count[op]++; \ + lastopcode = op; \ + } while (0) +#else +#define INSTRUCTION_START(op) (frame->prev_instr = next_instr++) +#endif + +#if USE_COMPUTED_GOTOS +# define TARGET(op) TARGET_##op: INSTRUCTION_START(op); +# define DISPATCH_GOTO() goto *opcode_targets[opcode] +#else +# define TARGET(op) case op: TARGET_##op: INSTRUCTION_START(op); +# define DISPATCH_GOTO() goto dispatch_opcode +#endif + +/* PRE_DISPATCH_GOTO() does lltrace if enabled. Normally a no-op */ +#ifdef LLTRACE +#define PRE_DISPATCH_GOTO() if (lltrace) { \ + lltrace_instruction(frame, stack_pointer, next_instr); } +#else +#define PRE_DISPATCH_GOTO() ((void)0) +#endif + + +/* Do interpreter dispatch accounting for tracing and instrumentation */ +#define DISPATCH() \ + { \ + NEXTOPARG(); \ + PRE_DISPATCH_GOTO(); \ + assert(cframe.use_tracing == 0 || cframe.use_tracing == 255); \ + opcode |= cframe.use_tracing OR_DTRACE_LINE; \ + DISPATCH_GOTO(); \ + } + +#define DISPATCH_SAME_OPARG() \ + { \ + opcode = _Py_OPCODE(*next_instr); \ + PRE_DISPATCH_GOTO(); \ + opcode |= cframe.use_tracing OR_DTRACE_LINE; \ + DISPATCH_GOTO(); \ + } + +#define DISPATCH_INLINED(NEW_FRAME) \ + do { \ + _PyFrame_SetStackPointer(frame, stack_pointer); \ + frame->prev_instr = next_instr - 1; \ + (NEW_FRAME)->previous = frame; \ + frame = cframe.current_frame = (NEW_FRAME); \ + CALL_STAT_INC(inlined_py_calls); \ + goto start_frame; \ + } while (0) + +#define CHECK_EVAL_BREAKER() \ + _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); \ + if (_Py_atomic_load_relaxed_int32(eval_breaker)) { \ + goto handle_eval_breaker; \ + } + + +/* Tuple access macros */ + +#ifndef Py_DEBUG +#define GETITEM(v, i) PyTuple_GET_ITEM((v), (i)) +#else +static inline PyObject * +GETITEM(PyObject *v, Py_ssize_t i) { + assert(PyTuple_Check(v)); + assert(i >= 0); + assert(i < PyTuple_GET_SIZE(v)); + return PyTuple_GET_ITEM(v, i); +} +#endif + +/* Code access macros */ + +/* The integer overflow is checked by an assertion below. */ +#define INSTR_OFFSET() ((int)(next_instr - _PyCode_CODE(frame->f_code))) +#define NEXTOPARG() do { \ + _Py_CODEUNIT word = *next_instr; \ + opcode = _Py_OPCODE(word); \ + oparg = _Py_OPARG(word); \ + } while (0) +#define JUMPTO(x) (next_instr = _PyCode_CODE(frame->f_code) + (x)) +#define JUMPBY(x) (next_instr += (x)) + +/* OpCode prediction macros + Some opcodes tend to come in pairs thus making it possible to + predict the second code when the first is run. For example, + COMPARE_OP is often followed by POP_JUMP_IF_FALSE or POP_JUMP_IF_TRUE. + + Verifying the prediction costs a single high-speed test of a register + variable against a constant. If the pairing was good, then the + processor's own internal branch predication has a high likelihood of + success, resulting in a nearly zero-overhead transition to the + next opcode. A successful prediction saves a trip through the eval-loop + including its unpredictable switch-case branch. Combined with the + processor's internal branch prediction, a successful PREDICT has the + effect of making the two opcodes run as if they were a single new opcode + with the bodies combined. + + If collecting opcode statistics, your choices are to either keep the + predictions turned-on and interpret the results as if some opcodes + had been combined or turn-off predictions so that the opcode frequency + counter updates for both opcodes. + + Opcode prediction is disabled with threaded code, since the latter allows + the CPU to record separate branch prediction information for each + opcode. + +*/ + +#define PREDICT_ID(op) PRED_##op + +#if USE_COMPUTED_GOTOS +#define PREDICT(op) if (0) goto PREDICT_ID(op) +#else +#define PREDICT(op) \ + do { \ + _Py_CODEUNIT word = *next_instr; \ + opcode = _Py_OPCODE(word) | cframe.use_tracing OR_DTRACE_LINE; \ + if (opcode == op) { \ + oparg = _Py_OPARG(word); \ + INSTRUCTION_START(op); \ + goto PREDICT_ID(op); \ + } \ + } while(0) +#endif +#define PREDICTED(op) PREDICT_ID(op): + + +/* Stack manipulation macros */ + +/* The stack can grow at most MAXINT deep, as co_nlocals and + co_stacksize are ints. */ +#define STACK_LEVEL() ((int)(stack_pointer - _PyFrame_Stackbase(frame))) +#define STACK_SIZE() (frame->f_code->co_stacksize) +#define EMPTY() (STACK_LEVEL() == 0) +#define TOP() (stack_pointer[-1]) +#define SECOND() (stack_pointer[-2]) +#define THIRD() (stack_pointer[-3]) +#define FOURTH() (stack_pointer[-4]) +#define PEEK(n) (stack_pointer[-(n)]) +#define POKE(n, v) (stack_pointer[-(n)] = (v)) +#define SET_TOP(v) (stack_pointer[-1] = (v)) +#define SET_SECOND(v) (stack_pointer[-2] = (v)) +#define BASIC_STACKADJ(n) (stack_pointer += n) +#define BASIC_PUSH(v) (*stack_pointer++ = (v)) +#define BASIC_POP() (*--stack_pointer) + +#ifdef Py_DEBUG +#define PUSH(v) do { \ + BASIC_PUSH(v); \ + assert(STACK_LEVEL() <= STACK_SIZE()); \ + } while (0) +#define POP() (assert(STACK_LEVEL() > 0), BASIC_POP()) +#define STACK_GROW(n) do { \ + assert(n >= 0); \ + BASIC_STACKADJ(n); \ + assert(STACK_LEVEL() <= STACK_SIZE()); \ + } while (0) +#define STACK_SHRINK(n) do { \ + assert(n >= 0); \ + assert(STACK_LEVEL() >= n); \ + BASIC_STACKADJ(-(n)); \ + } while (0) +#else +#define PUSH(v) BASIC_PUSH(v) +#define POP() BASIC_POP() +#define STACK_GROW(n) BASIC_STACKADJ(n) +#define STACK_SHRINK(n) BASIC_STACKADJ(-(n)) +#endif + +/* Local variable macros */ + +#define GETLOCAL(i) (frame->localsplus[i]) + +/* The SETLOCAL() macro must not DECREF the local variable in-place and + then store the new value; it must copy the old value to a temporary + value, then store the new value, and then DECREF the temporary value. + This is because it is possible that during the DECREF the frame is + accessed by other code (e.g. a __del__ method or gc.collect()) and the + variable would be pointing to already-freed memory. */ +#define SETLOCAL(i, value) do { PyObject *tmp = GETLOCAL(i); \ + GETLOCAL(i) = value; \ + Py_XDECREF(tmp); } while (0) + +#define GO_TO_INSTRUCTION(op) goto PREDICT_ID(op) + +#ifdef Py_STATS +#define UPDATE_MISS_STATS(INSTNAME) \ + do { \ + STAT_INC(opcode, miss); \ + STAT_INC((INSTNAME), miss); \ + /* The counter is always the first cache entry: */ \ + if (ADAPTIVE_COUNTER_IS_ZERO(next_instr->cache)) { \ + STAT_INC((INSTNAME), deopt); \ + } \ + else { \ + /* This is about to be (incorrectly) incremented: */ \ + STAT_DEC((INSTNAME), deferred); \ + } \ + } while (0) +#else +#define UPDATE_MISS_STATS(INSTNAME) ((void)0) +#endif + +#define DEOPT_IF(COND, INSTNAME) \ + if ((COND)) { \ + /* This is only a single jump on release builds! */ \ + UPDATE_MISS_STATS((INSTNAME)); \ + assert(_PyOpcode_Deopt[opcode] == (INSTNAME)); \ + GO_TO_INSTRUCTION(INSTNAME); \ + } + + +#define GLOBALS() frame->f_globals +#define BUILTINS() frame->f_builtins +#define LOCALS() frame->f_locals + +/* Shared opcode macros */ + +#define TRACE_FUNCTION_EXIT() \ + if (cframe.use_tracing) { \ + if (trace_function_exit(tstate, frame, retval)) { \ + Py_DECREF(retval); \ + goto exit_unwind; \ + } \ + } + +#define DTRACE_FUNCTION_EXIT() \ + if (PyDTrace_FUNCTION_RETURN_ENABLED()) { \ + dtrace_function_return(frame); \ + } + +#define TRACE_FUNCTION_UNWIND() \ + if (cframe.use_tracing) { \ + /* Since we are already unwinding, \ + * we don't care if this raises */ \ + trace_function_exit(tstate, frame, NULL); \ + } + +#define TRACE_FUNCTION_ENTRY() \ + if (cframe.use_tracing) { \ + _PyFrame_SetStackPointer(frame, stack_pointer); \ + int err = trace_function_entry(tstate, frame); \ + stack_pointer = _PyFrame_GetStackPointer(frame); \ + if (err) { \ + goto error; \ + } \ + } + +#define TRACE_FUNCTION_THROW_ENTRY() \ + if (cframe.use_tracing) { \ + assert(frame->stacktop >= 0); \ + if (trace_function_entry(tstate, frame)) { \ + goto exit_unwind; \ + } \ + } + +#define DTRACE_FUNCTION_ENTRY() \ + if (PyDTrace_FUNCTION_ENTRY_ENABLED()) { \ + dtrace_function_entry(frame); \ + } + +#define ADAPTIVE_COUNTER_IS_ZERO(COUNTER) \ + (((COUNTER) >> ADAPTIVE_BACKOFF_BITS) == 0) + +#define ADAPTIVE_COUNTER_IS_MAX(COUNTER) \ + (((COUNTER) >> ADAPTIVE_BACKOFF_BITS) == ((1 << MAX_BACKOFF_VALUE) - 1)) + +#define DECREMENT_ADAPTIVE_COUNTER(COUNTER) \ + do { \ + assert(!ADAPTIVE_COUNTER_IS_ZERO((COUNTER))); \ + (COUNTER) -= (1 << ADAPTIVE_BACKOFF_BITS); \ + } while (0); + +#define INCREMENT_ADAPTIVE_COUNTER(COUNTER) \ + do { \ + assert(!ADAPTIVE_COUNTER_IS_MAX((COUNTER))); \ + (COUNTER) += (1 << ADAPTIVE_BACKOFF_BITS); \ + } while (0); + +#define NAME_ERROR_MSG "name '%.200s' is not defined" From webhook-mailer at python.org Wed Jan 18 13:43:55 2023 From: webhook-mailer at python.org (kushaldas) Date: Wed, 18 Jan 2023 18:43:55 -0000 Subject: [Python-checkins] gh-100340: Allows -Wno-int-conversion for wasm (#100341) Message-ID: https://github.com/python/cpython/commit/75c8133efec035ec1083ebd8e7d43ef340c2e581 commit: 75c8133efec035ec1083ebd8e7d43ef340c2e581 branch: main author: Kushal Das committer: kushaldas date: 2023-01-18T19:43:49+01:00 summary: gh-100340: Allows -Wno-int-conversion for wasm (#100341) Fixes #100340 allows -Wno-int-conversion for wasm files: A Misc/NEWS.d/next/Build/2023-01-17-21-32-51.gh-issue-100340.i9zRGM.rst M Tools/wasm/config.site-wasm32-wasi M configure M configure.ac diff --git a/Misc/NEWS.d/next/Build/2023-01-17-21-32-51.gh-issue-100340.i9zRGM.rst b/Misc/NEWS.d/next/Build/2023-01-17-21-32-51.gh-issue-100340.i9zRGM.rst new file mode 100644 index 000000000000..3a37f798dc6c --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-01-17-21-32-51.gh-issue-100340.i9zRGM.rst @@ -0,0 +1,2 @@ +Allows -Wno-int-conversion for wasm-sdk 17 and onwards, thus enables +building WASI builds once against the latest sdk. diff --git a/Tools/wasm/config.site-wasm32-wasi b/Tools/wasm/config.site-wasm32-wasi index 4b8df2229915..5e98775400f6 100644 --- a/Tools/wasm/config.site-wasm32-wasi +++ b/Tools/wasm/config.site-wasm32-wasi @@ -37,3 +37,6 @@ ac_cv_header_netpacket_packet_h=no # disable accept for WASM runtimes without sock_accept #ac_cv_func_accept=no #ac_cv_func_accept4=no + +# Disable int-conversion for wask-sdk as it triggers an error from version 17. +ac_cv_disable_int_conversion=yes diff --git a/configure b/configure index 5b6750aaa8db..144b35d3c729 100755 --- a/configure +++ b/configure @@ -8743,6 +8743,44 @@ fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can disable $CC int-conversion warning" >&5 +$as_echo_n "checking if we can disable $CC int-conversion warning... " >&6; } +if ${ac_cv_disable_int_conversion_warning+:} false; then : + $as_echo_n "(cached) " >&6 +else + + py_cflags=$CFLAGS + as_fn_append CFLAGS "-Wint-conversion -Werror" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_disable_int_conversion_warning=yes +else + ac_cv_disable_int_conversion_warning=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$py_cflags + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_disable_int_conversion_warning" >&5 +$as_echo "$ac_cv_disable_int_conversion_warning" >&6; } + + + if test "x$ac_cv_disable_int_conversion" = xyes; then : + CFLAGS_NODIST="$CFLAGS_NODIST -Wno-int-conversion" +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can disable $CC missing-field-initializers warning" >&5 $as_echo_n "checking if we can disable $CC missing-field-initializers warning... " >&6; } if ${ac_cv_disable_missing_field_initializers_warning+:} false; then : diff --git a/configure.ac b/configure.ac index 956334a865f7..595adc67cf84 100644 --- a/configure.ac +++ b/configure.ac @@ -2288,6 +2288,10 @@ yes) AS_VAR_IF([ac_cv_disable_unused_parameter_warning], [yes], [CFLAGS_NODIST="$CFLAGS_NODIST -Wno-unused-parameter"]) + PY_CHECK_CC_WARNING([disable], [int-conversion]) + AS_VAR_IF([ac_cv_disable_int_conversion], [yes], + [CFLAGS_NODIST="$CFLAGS_NODIST -Wno-int-conversion"]) + PY_CHECK_CC_WARNING([disable], [missing-field-initializers]) AS_VAR_IF([ac_cv_disable_missing_field_initializers_warning], [yes], [CFLAGS_NODIST="$CFLAGS_NODIST -Wno-missing-field-initializers"]) From webhook-mailer at python.org Wed Jan 18 16:02:54 2023 From: webhook-mailer at python.org (pablogsal) Date: Wed, 18 Jan 2023 21:02:54 -0000 Subject: [Python-checkins] gh-100940: Change "char *str" to "const char *str" in KeywordToken: It is an immutable string. (#100936) Message-ID: https://github.com/python/cpython/commit/a1e051a23736fdf3da812363bcaf32e53a294f03 commit: a1e051a23736fdf3da812363bcaf32e53a294f03 branch: main author: Stepfen Shawn committer: pablogsal date: 2023-01-18T21:02:48Z summary: gh-100940: Change "char *str" to "const char *str" in KeywordToken: It is an immutable string. (#100936) files: M Parser/pegen.h diff --git a/Parser/pegen.h b/Parser/pegen.h index d8ac7e8cb918..ad5c97f5f7e5 100644 --- a/Parser/pegen.h +++ b/Parser/pegen.h @@ -42,7 +42,7 @@ typedef struct { } Token; typedef struct { - char *str; + const char *str; int type; } KeywordToken; From webhook-mailer at python.org Thu Jan 19 13:15:05 2023 From: webhook-mailer at python.org (iritkatriel) Date: Thu, 19 Jan 2023 18:15:05 -0000 Subject: [Python-checkins] gh-100712: make it possible to disable specialization (for debugging) (#100713) Message-ID: https://github.com/python/cpython/commit/e9ccfe4a636d5fe33f65cea2605c3621ffa55f19 commit: e9ccfe4a636d5fe33f65cea2605c3621ffa55f19 branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-19T18:14:55Z summary: gh-100712: make it possible to disable specialization (for debugging) (#100713) files: A Misc/NEWS.d/next/Core and Builtins/2023-01-03-14-33-23.gh-issue-100712.po6xyB.rst M Include/opcode.h M Lib/opcode.py M Lib/test/support/__init__.py M Lib/test/test_compile.py M Lib/test/test_dis.py M Lib/test/test_embed.py M Python/bytecodes.c M Python/generated_cases.c.h M Python/specialize.c M Tools/build/generate_opcode_h.py diff --git a/Include/opcode.h b/Include/opcode.h index e057a44d1fd8..827f9931beb3 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -226,6 +226,8 @@ extern "C" { #define NB_INPLACE_TRUE_DIVIDE 24 #define NB_INPLACE_XOR 25 +/* Defined in Lib/opcode.py */ +#define ENABLE_SPECIALIZATION 1 #define IS_PSEUDO_OPCODE(op) (((op) >= MIN_PSEUDO_OPCODE) && ((op) <= MAX_PSEUDO_OPCODE)) diff --git a/Lib/opcode.py b/Lib/opcode.py index 46051b28a0f1..c317e23beae6 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -33,6 +33,9 @@ hasfree = [] hasexc = [] + +ENABLE_SPECIALIZATION = True + def is_pseudo(op): return op >= MIN_PSEUDO_OPCODE and op <= MAX_PSEUDO_OPCODE diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index ff736f1c2db8..37f90cfed212 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -6,6 +6,7 @@ import contextlib import functools import getpass +import opcode import os import re import stat @@ -46,7 +47,7 @@ "anticipate_failure", "load_package_tests", "detect_api_mismatch", "check__all__", "skip_if_buggy_ucrt_strfptime", "check_disallow_instantiation", "check_sanitizer", "skip_if_sanitizer", - "requires_limited_api", + "requires_limited_api", "requires_specialization", # sys "is_jython", "is_android", "is_emscripten", "is_wasi", "check_impl_detail", "unix_shell", "setswitchinterval", @@ -1077,6 +1078,8 @@ def requires_limited_api(test): return unittest.skipUnless( _testcapi.LIMITED_API_AVAILABLE, 'needs Limited API support')(test) +def requires_specialization(test): + return unittest.skipUnless(opcode.ENABLE_SPECIALIZATION, "requires specialization") def _filter_suite(suite, pred): """Recursively filter test cases in a suite based on a predicate.""" diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 1606c9cfda32..05a5ed1fa9a6 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -10,7 +10,8 @@ import textwrap import warnings from test import support -from test.support import script_helper, requires_debug_ranges +from test.support import (script_helper, requires_debug_ranges, + requires_specialization) from test.support.os_helper import FakePath @@ -1251,6 +1252,7 @@ def test_multiline_expression(self): self.assertOpcodeSourcePositionIs(compiled_code, 'CALL', line=1, end_line=3, column=0, end_column=1) + @requires_specialization def test_multiline_boolean_expression(self): snippet = """\ if (a or diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index cb7e22ff7b30..27487de8fb3d 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -7,7 +7,8 @@ import sys import types import unittest -from test.support import captured_stdout, requires_debug_ranges, cpython_only +from test.support import (captured_stdout, requires_debug_ranges, + requires_specialization, cpython_only) from test.support.bytecode_helper import BytecodeTestCase import opcode @@ -1086,12 +1087,14 @@ def code_quicken(f, times=ADAPTIVE_WARMUP_DELAY): f() @cpython_only + @requires_specialization def test_super_instructions(self): self.code_quicken(lambda: load_test(0, 0)) got = self.get_disassembly(load_test, adaptive=True) self.do_disassembly_compare(got, dis_load_test_quickened_code, True) @cpython_only + @requires_specialization def test_binary_specialize(self): binary_op_quicken = """\ 0 0 RESUME 0 @@ -1130,6 +1133,7 @@ def test_binary_specialize(self): self.do_disassembly_compare(got, binary_subscr_quicken % "BINARY_SUBSCR_DICT", True) @cpython_only + @requires_specialization def test_load_attr_specialize(self): load_attr_quicken = """\ 0 0 RESUME 0 @@ -1144,6 +1148,7 @@ def test_load_attr_specialize(self): self.do_disassembly_compare(got, load_attr_quicken, True) @cpython_only + @requires_specialization def test_call_specialize(self): call_quicken = """\ 0 RESUME 0 @@ -1160,6 +1165,7 @@ def test_call_specialize(self): self.do_disassembly_compare(got, call_quicken) @cpython_only + @requires_specialization def test_loop_quicken(self): # Loop can trigger a quicken where the loop is located self.code_quicken(loop_test, 1) diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index acffe234ecff..4d422da5b99f 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -2,6 +2,7 @@ from test import support from test.support import import_helper from test.support import os_helper +from test.support import requires_specialization import unittest from collections import namedtuple @@ -346,6 +347,7 @@ def test_simple_initialization_api(self): out, err = self.run_embedded_interpreter("test_repeated_simple_init") self.assertEqual(out, 'Finalized\n' * INIT_LOOPS) + @requires_specialization def test_specialized_static_code_gets_unspecialized_at_Py_FINALIZE(self): # https://github.com/python/cpython/issues/92031 diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-14-33-23.gh-issue-100712.po6xyB.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-14-33-23.gh-issue-100712.po6xyB.rst new file mode 100644 index 000000000000..3ebee0dd2aa4 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-14-33-23.gh-issue-100712.po6xyB.rst @@ -0,0 +1 @@ +Added option to build cpython with specialization disabled, by setting ``ENABLE_SPECIALIZATION=False`` in :mod:`opcode`, followed by ``make regen-all``. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 5d5929fb2f68..6088fa45ac64 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -304,6 +304,7 @@ dummy_func( }; inst(BINARY_SUBSCR, (unused/4, container, sub -- res)) { + #if ENABLE_SPECIALIZATION _PyBinarySubscrCache *cache = (_PyBinarySubscrCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -313,6 +314,7 @@ dummy_func( } STAT_INC(BINARY_SUBSCR, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ res = PyObject_GetItem(container, sub); DECREF_INPUTS(); ERROR_IF(res == NULL, error); @@ -441,6 +443,7 @@ dummy_func( }; inst(STORE_SUBSCR, (counter/1, v, container, sub -- )) { + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { assert(cframe.use_tracing == 0); next_instr--; @@ -450,6 +453,9 @@ dummy_func( STAT_INC(STORE_SUBSCR, deferred); _PyStoreSubscrCache *cache = (_PyStoreSubscrCache *)next_instr; DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #else + (void)counter; // Unused. + #endif /* ENABLE_SPECIALIZATION */ /* container[sub] = v */ int err = PyObject_SetItem(container, sub, v); DECREF_INPUTS(); @@ -872,6 +878,7 @@ dummy_func( // stack effect: (__0 -- __array[oparg]) inst(UNPACK_SEQUENCE) { + #if ENABLE_SPECIALIZATION _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -882,6 +889,7 @@ dummy_func( } STAT_INC(UNPACK_SEQUENCE, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ PyObject *seq = POP(); PyObject **top = stack_pointer + oparg; if (!unpack_iterable(tstate, seq, oparg, -1, top)) { @@ -956,6 +964,7 @@ dummy_func( }; inst(STORE_ATTR, (counter/1, unused/3, v, owner --)) { + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { assert(cframe.use_tracing == 0); PyObject *name = GETITEM(names, oparg); @@ -966,6 +975,9 @@ dummy_func( STAT_INC(STORE_ATTR, deferred); _PyAttrCache *cache = (_PyAttrCache *)next_instr; DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #else + (void)counter; // Unused. + #endif /* ENABLE_SPECIALIZATION */ PyObject *name = GETITEM(names, oparg); int err = PyObject_SetAttr(owner, name, v); Py_DECREF(v); @@ -1064,6 +1076,7 @@ dummy_func( // error: LOAD_GLOBAL has irregular stack effect inst(LOAD_GLOBAL) { + #if ENABLE_SPECIALIZATION _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -1074,6 +1087,7 @@ dummy_func( } STAT_INC(LOAD_GLOBAL, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ int push_null = oparg & 1; PEEK(0) = NULL; PyObject *name = GETITEM(names, oparg>>1); @@ -1430,6 +1444,7 @@ dummy_func( // error: LOAD_ATTR has irregular stack effect inst(LOAD_ATTR) { + #if ENABLE_SPECIALIZATION _PyAttrCache *cache = (_PyAttrCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -1441,6 +1456,7 @@ dummy_func( } STAT_INC(LOAD_ATTR, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ PyObject *name = GETITEM(names, oparg >> 1); PyObject *owner = TOP(); if (oparg & 1) { @@ -1778,6 +1794,7 @@ dummy_func( }; inst(COMPARE_AND_BRANCH, (unused/2, left, right -- )) { + #if ENABLE_SPECIALIZATION _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -1787,6 +1804,7 @@ dummy_func( } STAT_INC(COMPARE_AND_BRANCH, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ assert((oparg >> 4) <= Py_GE); PyObject *cond = PyObject_RichCompare(left, right, oparg>>4); Py_DECREF(left); @@ -2194,6 +2212,7 @@ dummy_func( // stack effect: ( -- __0) inst(FOR_ITER) { + #if ENABLE_SPECIALIZATION _PyForIterCache *cache = (_PyForIterCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -2203,6 +2222,7 @@ dummy_func( } STAT_INC(FOR_ITER, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ /* before: [iter]; after: [iter, iter()] *or* [] */ PyObject *iter = TOP(); PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); @@ -2518,6 +2538,7 @@ dummy_func( // stack effect: (__0, __array[oparg] -- ) inst(CALL) { + #if ENABLE_SPECIALIZATION _PyCallCache *cache = (_PyCallCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -2530,6 +2551,7 @@ dummy_func( } STAT_INC(CALL, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ int total_args, is_meth; is_meth = is_method(stack_pointer, oparg); PyObject *function = PEEK(oparg + 1); @@ -3262,6 +3284,7 @@ dummy_func( } inst(BINARY_OP, (unused/1, lhs, rhs -- res)) { + #if ENABLE_SPECIALIZATION _PyBinaryOpCache *cache = (_PyBinaryOpCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -3271,6 +3294,7 @@ dummy_func( } STAT_INC(BINARY_OP, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ assert(0 <= oparg); assert((unsigned)oparg < Py_ARRAY_LENGTH(binary_ops)); assert(binary_ops[oparg]); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 445511df2a3a..46d421f98ac7 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -410,6 +410,7 @@ PyObject *sub = PEEK(1); PyObject *container = PEEK(2); PyObject *res; + #if ENABLE_SPECIALIZATION _PyBinarySubscrCache *cache = (_PyBinarySubscrCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -419,6 +420,7 @@ } STAT_INC(BINARY_SUBSCR, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ res = PyObject_GetItem(container, sub); Py_DECREF(container); Py_DECREF(sub); @@ -598,6 +600,7 @@ PyObject *container = PEEK(2); PyObject *v = PEEK(3); uint16_t counter = read_u16(&next_instr[0].cache); + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { assert(cframe.use_tracing == 0); next_instr--; @@ -607,6 +610,9 @@ STAT_INC(STORE_SUBSCR, deferred); _PyStoreSubscrCache *cache = (_PyStoreSubscrCache *)next_instr; DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #else + (void)counter; // Unused. + #endif /* ENABLE_SPECIALIZATION */ /* container[sub] = v */ int err = PyObject_SetItem(container, sub, v); Py_DECREF(v); @@ -1092,6 +1098,7 @@ TARGET(UNPACK_SEQUENCE) { PREDICTED(UNPACK_SEQUENCE); + #if ENABLE_SPECIALIZATION _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -1102,6 +1109,7 @@ } STAT_INC(UNPACK_SEQUENCE, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ PyObject *seq = POP(); PyObject **top = stack_pointer + oparg; if (!unpack_iterable(tstate, seq, oparg, -1, top)) { @@ -1174,6 +1182,7 @@ PyObject *owner = PEEK(1); PyObject *v = PEEK(2); uint16_t counter = read_u16(&next_instr[0].cache); + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { assert(cframe.use_tracing == 0); PyObject *name = GETITEM(names, oparg); @@ -1184,6 +1193,9 @@ STAT_INC(STORE_ATTR, deferred); _PyAttrCache *cache = (_PyAttrCache *)next_instr; DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #else + (void)counter; // Unused. + #endif /* ENABLE_SPECIALIZATION */ PyObject *name = GETITEM(names, oparg); int err = PyObject_SetAttr(owner, name, v); Py_DECREF(v); @@ -1296,6 +1308,7 @@ TARGET(LOAD_GLOBAL) { PREDICTED(LOAD_GLOBAL); + #if ENABLE_SPECIALIZATION _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -1306,6 +1319,7 @@ } STAT_INC(LOAD_GLOBAL, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ int push_null = oparg & 1; PEEK(0) = NULL; PyObject *name = GETITEM(names, oparg>>1); @@ -1733,6 +1747,7 @@ TARGET(LOAD_ATTR) { PREDICTED(LOAD_ATTR); + #if ENABLE_SPECIALIZATION _PyAttrCache *cache = (_PyAttrCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -1744,6 +1759,7 @@ } STAT_INC(LOAD_ATTR, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ PyObject *name = GETITEM(names, oparg >> 1); PyObject *owner = TOP(); if (oparg & 1) { @@ -2104,6 +2120,7 @@ PREDICTED(COMPARE_AND_BRANCH); PyObject *right = PEEK(1); PyObject *left = PEEK(2); + #if ENABLE_SPECIALIZATION _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -2113,6 +2130,7 @@ } STAT_INC(COMPARE_AND_BRANCH, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ assert((oparg >> 4) <= Py_GE); PyObject *cond = PyObject_RichCompare(left, right, oparg>>4); Py_DECREF(left); @@ -2571,6 +2589,7 @@ TARGET(FOR_ITER) { PREDICTED(FOR_ITER); + #if ENABLE_SPECIALIZATION _PyForIterCache *cache = (_PyForIterCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -2580,6 +2599,7 @@ } STAT_INC(FOR_ITER, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ /* before: [iter]; after: [iter, iter()] *or* [] */ PyObject *iter = TOP(); PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); @@ -2901,6 +2921,7 @@ TARGET(CALL) { PREDICTED(CALL); + #if ENABLE_SPECIALIZATION _PyCallCache *cache = (_PyCallCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -2913,6 +2934,7 @@ } STAT_INC(CALL, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ int total_args, is_meth; is_meth = is_method(stack_pointer, oparg); PyObject *function = PEEK(oparg + 1); @@ -3650,6 +3672,7 @@ PyObject *rhs = PEEK(1); PyObject *lhs = PEEK(2); PyObject *res; + #if ENABLE_SPECIALIZATION _PyBinaryOpCache *cache = (_PyBinaryOpCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); @@ -3659,6 +3682,7 @@ } STAT_INC(BINARY_OP, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); + #endif /* ENABLE_SPECIALIZATION */ assert(0 <= oparg); assert((unsigned)oparg < Py_ARRAY_LENGTH(binary_ops)); assert(binary_ops[oparg]); diff --git a/Python/specialize.c b/Python/specialize.c index 500fd714b38b..84784b2d149e 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -276,6 +276,7 @@ static int compare_masks[] = { void _PyCode_Quicken(PyCodeObject *code) { + #if ENABLE_SPECIALIZATION int opcode = 0; _Py_CODEUNIT *instructions = _PyCode_CODE(code); for (int i = 0; i < Py_SIZE(code); i++) { @@ -318,6 +319,7 @@ _PyCode_Quicken(PyCodeObject *code) } } } + #endif /* ENABLE_SPECIALIZATION */ } #define SIMPLE_FUNCTION 0 @@ -703,6 +705,7 @@ static int specialize_class_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyOb void _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) { + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[LOAD_ATTR] == INLINE_CACHE_ENTRIES_LOAD_ATTR); _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); PyTypeObject *type = Py_TYPE(owner); @@ -875,6 +878,7 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) void _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) { + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[STORE_ATTR] == INLINE_CACHE_ENTRIES_STORE_ATTR); _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); PyTypeObject *type = Py_TYPE(owner); @@ -1146,6 +1150,7 @@ _Py_Specialize_LoadGlobal( PyObject *globals, PyObject *builtins, _Py_CODEUNIT *instr, PyObject *name) { + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[LOAD_GLOBAL] == INLINE_CACHE_ENTRIES_LOAD_GLOBAL); /* Use inline cache */ _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)(instr + 1); @@ -1317,6 +1322,7 @@ void _Py_Specialize_BinarySubscr( PyObject *container, PyObject *sub, _Py_CODEUNIT *instr) { + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[BINARY_SUBSCR] == INLINE_CACHE_ENTRIES_BINARY_SUBSCR); _PyBinarySubscrCache *cache = (_PyBinarySubscrCache *)(instr + 1); @@ -1399,6 +1405,7 @@ _Py_Specialize_BinarySubscr( void _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *instr) { + assert(ENABLE_SPECIALIZATION); _PyStoreSubscrCache *cache = (_PyStoreSubscrCache *)(instr + 1); PyTypeObject *container_type = Py_TYPE(container); if (container_type == &PyList_Type) { @@ -1778,6 +1785,7 @@ void _Py_Specialize_Call(PyObject *callable, _Py_CODEUNIT *instr, int nargs, PyObject *kwnames) { + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[CALL] == INLINE_CACHE_ENTRIES_CALL); _PyCallCache *cache = (_PyCallCache *)(instr + 1); int fail; @@ -1896,6 +1904,7 @@ void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, int oparg, PyObject **locals) { + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[BINARY_OP] == INLINE_CACHE_ENTRIES_BINARY_OP); _PyBinaryOpCache *cache = (_PyBinaryOpCache *)(instr + 1); switch (oparg) { @@ -2003,6 +2012,7 @@ void _Py_Specialize_CompareAndBranch(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, int oparg) { + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[COMPARE_AND_BRANCH] == INLINE_CACHE_ENTRIES_COMPARE_OP); _PyCompareOpCache *cache = (_PyCompareOpCache *)(instr + 1); #ifndef NDEBUG @@ -2066,6 +2076,7 @@ unpack_sequence_fail_kind(PyObject *seq) void _Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr, int oparg) { + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[UNPACK_SEQUENCE] == INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)(instr + 1); @@ -2175,6 +2186,7 @@ int void _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg) { + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[FOR_ITER] == INLINE_CACHE_ENTRIES_FOR_ITER); _PyForIterCache *cache = (_PyForIterCache *)(instr + 1); PyTypeObject *tp = Py_TYPE(iter); diff --git a/Tools/build/generate_opcode_h.py b/Tools/build/generate_opcode_h.py index 174573a3c64b..9b2112f7f5f3 100644 --- a/Tools/build/generate_opcode_h.py +++ b/Tools/build/generate_opcode_h.py @@ -85,6 +85,7 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna is_pseudo = opcode['is_pseudo'] _pseudo_ops = opcode['_pseudo_ops'] + ENABLE_SPECIALIZATION = opcode["ENABLE_SPECIALIZATION"] HAVE_ARGUMENT = opcode["HAVE_ARGUMENT"] MIN_PSEUDO_OPCODE = opcode["MIN_PSEUDO_OPCODE"] MAX_PSEUDO_OPCODE = opcode["MAX_PSEUDO_OPCODE"] @@ -171,6 +172,10 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna for i, (op, _) in enumerate(opcode["_nb_ops"]): fobj.write(DEFINE.format(op, i)) + fobj.write("\n") + fobj.write("/* Defined in Lib/opcode.py */\n") + fobj.write(f"#define ENABLE_SPECIALIZATION {int(ENABLE_SPECIALIZATION)}") + iobj.write("\n") iobj.write("#ifdef Py_DEBUG\n") iobj.write(f"static const char *const _PyOpcode_OpName[{NUM_OPCODES}] = {{\n") From webhook-mailer at python.org Thu Jan 19 16:03:12 2023 From: webhook-mailer at python.org (iritkatriel) Date: Thu, 19 Jan 2023 21:03:12 -0000 Subject: [Python-checkins] gh-98831: register instructions have 0 pushes and pops (#101163) Message-ID: https://github.com/python/cpython/commit/8a2d4f4e8eea86352de37d2ce28117e13b3dfaed commit: 8a2d4f4e8eea86352de37d2ce28117e13b3dfaed branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-19T21:02:56Z summary: gh-98831: register instructions have 0 pushes and pops (#101163) files: M Tools/cases_generator/generate_cases.py diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 59e249930b45..90f101adceeb 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -810,6 +810,7 @@ def write_metadata_for_inst(self, instr: Instruction) -> None: directions.extend("DIR_WRITE" for _ in instr.output_effects) directions.extend("DIR_NONE" for _ in range(3)) dir_op1, dir_op2, dir_op3 = directions[:3] + n_popped = n_pushed = 0 self.out.emit( f' [{instr.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{instr.instr_fmt} }},' ) From webhook-mailer at python.org Thu Jan 19 18:04:20 2023 From: webhook-mailer at python.org (ericsnowcurrently) Date: Thu, 19 Jan 2023 23:04:20 -0000 Subject: [Python-checkins] gh-59956: Clarify GILState-related Code (gh-101161) Message-ID: https://github.com/python/cpython/commit/6036c3e856f033bf13e929536e7bf127fdd921c9 commit: 6036c3e856f033bf13e929536e7bf127fdd921c9 branch: main author: Eric Snow committer: ericsnowcurrently date: 2023-01-19T16:04:14-07:00 summary: gh-59956: Clarify GILState-related Code (gh-101161) The objective of this change is to help make the GILState-related code easier to understand. This mostly involves moving code around and some semantically equivalent refactors. However, there are a also a small number of slight changes in structure and behavior: * tstate_current is moved out of _PyRuntimeState.gilstate * autoTSSkey is moved out of _PyRuntimeState.gilstate * autoTSSkey is initialized earlier * autoTSSkey is re-initialized (after fork) earlier https://github.com/python/cpython/issues/59956 files: M Include/cpython/pystate.h M Include/internal/pycore_pylifecycle.h M Include/internal/pycore_pystate.h M Include/internal/pycore_runtime.h M Include/internal/pycore_runtime_init.h M Modules/_threadmodule.c M Modules/posixmodule.c M Python/ceval_gil.c M Python/pylifecycle.c M Python/pystate.c diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 0117c23f518c..81944a50c47f 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -114,11 +114,7 @@ struct _ts { PyThreadState *next; PyInterpreterState *interp; - /* Has been initialized to a safe state. - - In order to be effective, this must be set to 0 during or right - after allocation. */ - int _initialized; + int _status; int py_recursion_remaining; int py_recursion_limit; diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index 370e4cbd59f9..2d431befd74f 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -69,7 +69,7 @@ extern void _PyThread_FiniType(PyInterpreterState *interp); extern void _Py_Deepfreeze_Fini(void); extern void _PyArg_Fini(void); -extern PyStatus _PyGILState_Init(_PyRuntimeState *runtime); +extern PyStatus _PyGILState_Init(PyInterpreterState *interp); extern PyStatus _PyGILState_SetTstate(PyThreadState *tstate); extern void _PyGILState_Fini(PyInterpreterState *interp); diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 736c2b30d8e0..0e46693c1f12 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -67,12 +67,12 @@ _Py_ThreadCanHandlePendingCalls(void) static inline PyThreadState* _PyRuntimeState_GetThreadState(_PyRuntimeState *runtime) { - return (PyThreadState*)_Py_atomic_load_relaxed(&runtime->gilstate.tstate_current); + return (PyThreadState*)_Py_atomic_load_relaxed(&runtime->tstate_current); } /* Get the current Python thread state. - Efficient macro reading directly the 'gilstate.tstate_current' atomic + Efficient macro reading directly the 'tstate_current' atomic variable. The macro is unsafe: it does not check for error and it can return NULL. @@ -120,7 +120,7 @@ static inline PyInterpreterState* _PyInterpreterState_GET(void) { // PyThreadState functions -PyAPI_FUNC(void) _PyThreadState_SetCurrent(PyThreadState *tstate); +PyAPI_FUNC(void) _PyThreadState_Bind(PyThreadState *tstate); // We keep this around exclusively for stable ABI compatibility. PyAPI_FUNC(void) _PyThreadState_Init( PyThreadState *tstate); @@ -139,17 +139,28 @@ _PyThreadState_UpdateTracingState(PyThreadState *tstate) } +/* PyThreadState status */ + +#define PyThreadState_UNINITIALIZED 0 +/* Has been initialized to a safe state. + + In order to be effective, this must be set to 0 during or right + after allocation. */ +#define PyThreadState_INITIALIZED 1 +#define PyThreadState_BOUND 2 +#define PyThreadState_UNBOUND 3 + + /* Other */ PyAPI_FUNC(PyThreadState *) _PyThreadState_Swap( - struct _gilstate_runtime_state *gilstate, + _PyRuntimeState *runtime, PyThreadState *newts); PyAPI_FUNC(PyStatus) _PyInterpreterState_Enable(_PyRuntimeState *runtime); #ifdef HAVE_FORK extern PyStatus _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime); -extern PyStatus _PyGILState_Reinit(_PyRuntimeState *runtime); extern void _PySignal_AfterFork(void); #endif diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index d100e836c7d1..41e1b2cffb81 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -41,15 +41,11 @@ struct _gilstate_runtime_state { /* bpo-26558: Flag to disable PyGILState_Check(). If set to non-zero, PyGILState_Check() always return 1. */ int check_enabled; - /* Assuming the current thread holds the GIL, this is the - PyThreadState for the current thread. */ - _Py_atomic_address tstate_current; /* The single PyInterpreterState used by this process' GILState implementation */ /* TODO: Given interp_main, it may be possible to kill this ref */ PyInterpreterState *autoInterpreterState; - Py_tss_t autoTSSkey; }; /* Runtime audit hook state */ @@ -124,6 +120,12 @@ typedef struct pyruntimestate { unsigned long main_thread; + /* Assuming the current thread holds the GIL, this is the + PyThreadState for the current thread. */ + _Py_atomic_address tstate_current; + /* Used for the thread state bound to the current thread. */ + Py_tss_t autoTSSkey; + PyWideStringList orig_argv; struct _parser_runtime_state parser; diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index cb3fce3732c7..f10dccc01580 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -33,6 +33,9 @@ extern "C" { until _PyInterpreterState_Enable() is called. */ \ .next_id = -1, \ }, \ + /* A TSS key must be initialized with Py_tss_NEEDS_INIT \ + in accordance with the specification. */ \ + .autoTSSkey = Py_tss_NEEDS_INIT, \ .parser = _parser_runtime_state_INIT, \ .imports = { \ .lock = { \ @@ -49,9 +52,6 @@ extern "C" { }, \ .gilstate = { \ .check_enabled = 1, \ - /* A TSS key must be initialized with Py_tss_NEEDS_INIT \ - in accordance with the specification. */ \ - .autoTSSkey = Py_tss_NEEDS_INIT, \ }, \ .dtoa = _dtoa_runtime_state_INIT(runtime), \ .fileutils = { \ diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 80f467a8e21d..bf4b6ec00e3e 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1074,13 +1074,7 @@ thread_run(void *boot_raw) PyThreadState *tstate; tstate = boot->tstate; - tstate->thread_id = PyThread_get_thread_ident(); -#ifdef PY_HAVE_THREAD_NATIVE_ID - tstate->native_thread_id = PyThread_get_thread_native_id(); -#else - tstate->native_thread_id = 0; -#endif - _PyThreadState_SetCurrent(tstate); + _PyThreadState_Bind(tstate); PyEval_AcquireThread(tstate); tstate->interp->threads.count++; diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 0d4c179368ce..b84fb0d280f4 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -587,7 +587,7 @@ PyOS_AfterFork_Child(void) PyStatus status; _PyRuntimeState *runtime = &_PyRuntime; - status = _PyGILState_Reinit(runtime); + status = _PyRuntimeState_ReInitThreads(runtime); if (_PyStatus_EXCEPTION(status)) { goto fatal_error; } @@ -611,11 +611,6 @@ PyOS_AfterFork_Child(void) _PySignal_AfterFork(); - status = _PyRuntimeState_ReInitThreads(runtime); - if (_PyStatus_EXCEPTION(status)) { - goto fatal_error; - } - status = _PyInterpreterState_DeleteExceptMain(runtime); if (_PyStatus_EXCEPTION(status)) { goto fatal_error; diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index 83f4e91e5457..73d412ba4d71 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -581,8 +581,7 @@ PyEval_AcquireThread(PyThreadState *tstate) take_gil(tstate); - struct _gilstate_runtime_state *gilstate = &tstate->interp->runtime->gilstate; - if (_PyThreadState_Swap(gilstate, tstate) != NULL) { + if (_PyThreadState_Swap(tstate->interp->runtime, tstate) != NULL) { Py_FatalError("non-NULL old thread state"); } } @@ -593,7 +592,7 @@ PyEval_ReleaseThread(PyThreadState *tstate) assert(is_tstate_valid(tstate)); _PyRuntimeState *runtime = tstate->interp->runtime; - PyThreadState *new_tstate = _PyThreadState_Swap(&runtime->gilstate, NULL); + PyThreadState *new_tstate = _PyThreadState_Swap(runtime, NULL); if (new_tstate != tstate) { Py_FatalError("wrong thread state"); } @@ -643,7 +642,7 @@ PyThreadState * PyEval_SaveThread(void) { _PyRuntimeState *runtime = &_PyRuntime; - PyThreadState *tstate = _PyThreadState_Swap(&runtime->gilstate, NULL); + PyThreadState *tstate = _PyThreadState_Swap(runtime, NULL); _Py_EnsureTstateNotNULL(tstate); struct _ceval_runtime_state *ceval = &runtime->ceval; @@ -660,8 +659,7 @@ PyEval_RestoreThread(PyThreadState *tstate) take_gil(tstate); - struct _gilstate_runtime_state *gilstate = &tstate->interp->runtime->gilstate; - _PyThreadState_Swap(gilstate, tstate); + _PyThreadState_Swap(tstate->interp->runtime, tstate); } @@ -965,7 +963,7 @@ _Py_HandlePending(PyThreadState *tstate) /* GIL drop request */ if (_Py_atomic_load_relaxed_int32(&interp_ceval_state->gil_drop_request)) { /* Give another thread a chance */ - if (_PyThreadState_Swap(&runtime->gilstate, NULL) != tstate) { + if (_PyThreadState_Swap(runtime, NULL) != tstate) { Py_FatalError("tstate mix-up"); } drop_gil(ceval, interp_ceval_state, tstate); @@ -974,7 +972,7 @@ _Py_HandlePending(PyThreadState *tstate) take_gil(tstate); - if (_PyThreadState_Swap(&runtime->gilstate, tstate) != NULL) { + if (_PyThreadState_Swap(runtime, tstate) != NULL) { Py_FatalError("orphan tstate"); } } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 1cb0e4d747e1..5ef2d3f6aa72 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -675,12 +675,7 @@ pycore_create_interpreter(_PyRuntimeState *runtime, const PyConfig *src_config, PyThreadState **tstate_p) { - /* Auto-thread-state API */ - PyStatus status = _PyGILState_Init(runtime); - if (_PyStatus_EXCEPTION(status)) { - return status; - } - + PyStatus status; PyInterpreterState *interp = PyInterpreterState_New(); if (interp == NULL) { return _PyStatus_ERR("can't make main interpreter"); @@ -692,6 +687,12 @@ pycore_create_interpreter(_PyRuntimeState *runtime, return status; } + /* Auto-thread-state API */ + status = _PyGILState_Init(interp); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + const _PyInterpreterConfig config = _PyInterpreterConfig_LEGACY_INIT; init_interp_settings(interp, &config); @@ -1795,10 +1796,8 @@ finalize_interp_clear(PyThreadState *tstate) static void finalize_interp_delete(PyInterpreterState *interp) { - if (_Py_IsMainInterpreter(interp)) { - /* Cleanup auto-thread-state */ - _PyGILState_Fini(interp); - } + /* Cleanup auto-thread-state */ + _PyGILState_Fini(interp); /* We can't call _PyEval_FiniGIL() here because destroying the GIL lock can fail when it is being awaited by another running daemon thread (see diff --git a/Python/pystate.c b/Python/pystate.c index bf9b8db9a7ed..756c1c73330d 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -10,7 +10,7 @@ #include "pycore_pyerrors.h" #include "pycore_pylifecycle.h" #include "pycore_pymem.h" // _PyMem_SetDefaultAllocator() -#include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_pystate.h" #include "pycore_runtime_init.h" // _PyRuntimeState_INIT #include "pycore_sysmodule.h" @@ -37,16 +37,205 @@ to avoid the expense of doing their own locking). extern "C" { #endif -#define _PyRuntimeGILState_GetThreadState(gilstate) \ - ((PyThreadState*)_Py_atomic_load_relaxed(&(gilstate)->tstate_current)) -#define _PyRuntimeGILState_SetThreadState(gilstate, value) \ - _Py_atomic_store_relaxed(&(gilstate)->tstate_current, \ - (uintptr_t)(value)) - /* Forward declarations */ -static PyThreadState *_PyGILState_GetThisThreadState(struct _gilstate_runtime_state *gilstate); static void _PyThreadState_Delete(PyThreadState *tstate, int check_current); + +/* the current thread state */ + +static inline PyThreadState * +current_fast_get(_PyRuntimeState *runtime) +{ + // The GIL must be held by the current thread. + return (PyThreadState*)_Py_atomic_load_relaxed(&runtime->tstate_current); +} + +static inline void +current_fast_set(_PyRuntimeState *runtime, PyThreadState *tstate) +{ + // The GIL must be held by the current thread. + assert(tstate != NULL); + _Py_atomic_store_relaxed(&runtime->tstate_current, (uintptr_t)tstate); +} + +static inline void +current_fast_clear(_PyRuntimeState *runtime) +{ + // The GIL must be held by the current thread. + _Py_atomic_store_relaxed(&runtime->tstate_current, (uintptr_t)NULL); +} + + +static int +current_tss_initialized(_PyRuntimeState *runtime) +{ + return PyThread_tss_is_created(&runtime->autoTSSkey); +} + +static PyStatus +current_tss_init(_PyRuntimeState *runtime) +{ + assert(!current_tss_initialized(runtime)); + if (PyThread_tss_create(&runtime->autoTSSkey) != 0) { + return _PyStatus_NO_MEMORY(); + } + return _PyStatus_OK(); +} + +static void +current_tss_fini(_PyRuntimeState *runtime) +{ + assert(current_tss_initialized(runtime)); + PyThread_tss_delete(&runtime->autoTSSkey); +} + +static inline PyThreadState * +current_tss_get(_PyRuntimeState *runtime) +{ + assert(current_tss_initialized(runtime)); + return (PyThreadState *)PyThread_tss_get(&runtime->autoTSSkey); +} + +static inline int +_current_tss_set(_PyRuntimeState *runtime, PyThreadState *tstate) +{ + assert(tstate != NULL); + assert(current_tss_initialized(runtime)); + return PyThread_tss_set(&runtime->autoTSSkey, (void *)tstate); +} +static inline void +current_tss_set(_PyRuntimeState *runtime, PyThreadState *tstate) +{ + if (_current_tss_set(runtime, tstate) != 0) { + Py_FatalError("failed to set current tstate (TSS)"); + } +} + +static inline void +current_tss_clear(_PyRuntimeState *runtime) +{ + assert(current_tss_initialized(runtime)); + if (PyThread_tss_set(&runtime->autoTSSkey, NULL) != 0) { + Py_FatalError("failed to clear current tstate (TSS)"); + } +} + +#ifdef HAVE_FORK +/* Reset the TSS key - called by PyOS_AfterFork_Child(). + * This should not be necessary, but some - buggy - pthread implementations + * don't reset TSS upon fork(), see issue #10517. + */ +static PyStatus +current_tss_reinit(_PyRuntimeState *runtime) +{ + if (!current_tss_initialized(runtime)) { + return _PyStatus_OK(); + } + PyThreadState *tstate = current_tss_get(runtime); + + current_tss_fini(runtime); + PyStatus status = current_tss_init(runtime); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + + /* If the thread had an associated auto thread state, reassociate it with + * the new key. */ + if (tstate && _current_tss_set(runtime, tstate) != 0) { + return _PyStatus_ERR("failed to set autoTSSkey"); + } + return _PyStatus_OK(); +} +#endif + +static void +bind_tstate(PyThreadState *tstate) +{ + assert(tstate != NULL); + assert(tstate->_status == PyThreadState_INITIALIZED); + assert(tstate->thread_id == 0); + assert(tstate->native_thread_id == 0); + _PyRuntimeState *runtime = tstate->interp->runtime; + + /* Stick the thread state for this thread in thread specific storage. + + The only situation where you can legitimately have more than one + thread state for an OS level thread is when there are multiple + interpreters. + + You shouldn't really be using the PyGILState_ APIs anyway (see issues + #10915 and #15751). + + The first thread state created for that given OS level thread will + "win", which seems reasonable behaviour. + */ + /* When a thread state is created for a thread by some mechanism + other than PyGILState_Ensure(), it's important that the GILState + machinery knows about it so it doesn't try to create another + thread state for the thread. + (This is a better fix for SF bug #1010677 than the first one attempted.) + */ + // XXX Skipping like this does not play nice with multiple interpreters. + if (current_tss_get(runtime) == NULL) { + current_tss_set(runtime, tstate); + } + + tstate->thread_id = PyThread_get_thread_ident(); +#ifdef PY_HAVE_THREAD_NATIVE_ID + tstate->native_thread_id = PyThread_get_thread_native_id(); +#endif + + tstate->_status = PyThreadState_BOUND; +} + +static void +unbind_tstate(PyThreadState *tstate) +{ + assert(tstate != NULL); + assert(tstate->_status == PyThreadState_BOUND); + assert(tstate->thread_id > 0); +#ifdef PY_HAVE_THREAD_NATIVE_ID + assert(tstate->native_thread_id > 0); +#endif + _PyRuntimeState *runtime = tstate->interp->runtime; + + if (current_tss_initialized(runtime) && + tstate == current_tss_get(runtime)) + { + current_tss_clear(runtime); + } + + // We leave thread_id and native_thraed_id alone + // since they can be useful for debugging. + // Check the `_status` field to know if these values + // are still valid. + + tstate->_status = PyThreadState_UNBOUND; +} + +/* This is not exported, as it is not reliable! It can only + ever be compared to the state for the *current* thread. + * If not equal, then it doesn't matter that the actual + value may change immediately after comparison, as it can't + possibly change to the current thread's state. + * If equal, then the current thread holds the lock, so the value can't + change until we yield the lock. +*/ +static int +holds_gil(PyThreadState *tstate) +{ + // XXX Fall back to tstate->interp->runtime->ceval.gil.last_holder + // (and tstate->interp->runtime->ceval.gil.locked). + assert(tstate != NULL); + _PyRuntimeState *runtime = tstate->interp->runtime; + /* Must be the tstate for this thread */ + assert(tstate == current_tss_get(runtime)); + return tstate == current_fast_get(runtime); +} + + +/* the global runtime */ + /* Suppress deprecation warning for PyBytesObject.ob_shash */ _Py_COMP_DIAG_PUSH _Py_COMP_DIAG_IGNORE_DEPR_DECLS @@ -166,6 +355,13 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) // Reset to _PyRuntimeState_INIT. memcpy(runtime, &initial, sizeof(*runtime)); } + + PyStatus status = current_tss_init(runtime); + if (_PyStatus_EXCEPTION(status)) { + _PyRuntimeState_Fini(runtime); + return status; + } + init_runtime(runtime, open_code_hook, open_code_userdata, audit_hook_head, unicode_next_index, lock1, lock2, lock3, lock4); @@ -175,6 +371,10 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) void _PyRuntimeState_Fini(_PyRuntimeState *runtime) { + if (current_tss_initialized(runtime)) { + current_tss_fini(runtime); + } + /* Force the allocator used by _PyRuntimeState_Init(). */ PyMemAllocatorEx old_alloc; _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); @@ -227,15 +427,17 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime) return _PyStatus_ERR("Failed to reinitialize runtime locks"); } + + PyStatus status = current_tss_reinit(runtime); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + return _PyStatus_OK(); } #endif -/* Forward declaration */ -static void _PyGILState_NoteThreadState( - struct _gilstate_runtime_state *gilstate, PyThreadState* tstate); - PyStatus _PyInterpreterState_Enable(_PyRuntimeState *runtime) { @@ -318,7 +520,8 @@ PyInterpreterState * PyInterpreterState_New(void) { PyInterpreterState *interp; - PyThreadState *tstate = _PyThreadState_GET(); + _PyRuntimeState *runtime = &_PyRuntime; + PyThreadState *tstate = current_fast_get(runtime); /* tstate is NULL when Py_InitializeFromConfig() calls PyInterpreterState_New() to create the main interpreter. */ @@ -335,7 +538,6 @@ PyInterpreterState_New(void) } /* Don't get runtime from tstate since tstate can be NULL. */ - _PyRuntimeState *runtime = &_PyRuntime; struct pyinterpreters *interpreters = &runtime->interpreters; /* We completely serialize creation of multiple interpreters, since @@ -483,7 +685,7 @@ PyInterpreterState_Clear(PyInterpreterState *interp) // Use the current Python thread state to call audit hooks and to collect // garbage. It can be different than the current Python thread state // of 'interp'. - PyThreadState *current_tstate = _PyThreadState_GET(); + PyThreadState *current_tstate = current_fast_get(interp->runtime); interpreter_clear(interp, current_tstate); } @@ -518,7 +720,7 @@ PyInterpreterState_Delete(PyInterpreterState *interp) _PyEval_FiniState(&interp->ceval); /* Delete current thread. After this, many C API calls become crashy. */ - _PyThreadState_Swap(&runtime->gilstate, NULL); + _PyThreadState_Swap(runtime, NULL); HEAD_LOCK(runtime); PyInterpreterState **p; @@ -558,10 +760,9 @@ PyInterpreterState_Delete(PyInterpreterState *interp) PyStatus _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime) { - struct _gilstate_runtime_state *gilstate = &runtime->gilstate; struct pyinterpreters *interpreters = &runtime->interpreters; - PyThreadState *tstate = _PyThreadState_Swap(gilstate, NULL); + PyThreadState *tstate = _PyThreadState_Swap(runtime, NULL); if (tstate != NULL && tstate->interp != interpreters->main) { return _PyStatus_ERR("not main interpreter"); } @@ -591,7 +792,7 @@ _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime) if (interpreters->head == NULL) { return _PyStatus_ERR("missing main interpreter"); } - _PyThreadState_Swap(gilstate, tstate); + _PyThreadState_Swap(runtime, tstate); return _PyStatus_OK(); } #endif @@ -600,7 +801,7 @@ _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime) PyInterpreterState * PyInterpreterState_Get(void) { - PyThreadState *tstate = _PyThreadState_GET(); + PyThreadState *tstate = current_fast_get(&_PyRuntime); _Py_EnsureTstateNotNULL(tstate); PyInterpreterState *interp = tstate->interp; if (interp == NULL) { @@ -691,8 +892,8 @@ void _PyInterpreterState_IDDecref(PyInterpreterState *interp) { assert(interp->id_mutex != NULL); + _PyRuntimeState *runtime = interp->runtime; - struct _gilstate_runtime_state *gilstate = &_PyRuntime.gilstate; PyThread_acquire_lock(interp->id_mutex, WAIT_LOCK); assert(interp->id_refcount != 0); interp->id_refcount -= 1; @@ -703,9 +904,9 @@ _PyInterpreterState_IDDecref(PyInterpreterState *interp) // XXX Using the "head" thread isn't strictly correct. PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); // XXX Possible GILState issues? - PyThreadState *save_tstate = _PyThreadState_Swap(gilstate, tstate); + PyThreadState *save_tstate = _PyThreadState_Swap(runtime, tstate); Py_EndInterpreter(tstate); - _PyThreadState_Swap(gilstate, save_tstate); + _PyThreadState_Swap(runtime, save_tstate); } } @@ -790,7 +991,7 @@ init_threadstate(PyThreadState *tstate, PyInterpreterState *interp, uint64_t id, PyThreadState *next) { - if (tstate->_initialized) { + if (tstate->_status != PyThreadState_UNINITIALIZED) { Py_FatalError("thread state already initialized"); } @@ -809,10 +1010,7 @@ init_threadstate(PyThreadState *tstate, tstate->next = next; assert(tstate->prev == NULL); - tstate->thread_id = PyThread_get_thread_ident(); -#ifdef PY_HAVE_THREAD_NATIVE_ID - tstate->native_thread_id = PyThread_get_thread_native_id(); -#endif + // thread_id and native_thread_id are set in bind_tstate(). tstate->py_recursion_limit = interp->ceval.recursion_limit, tstate->py_recursion_remaining = interp->ceval.recursion_limit, @@ -820,12 +1018,16 @@ init_threadstate(PyThreadState *tstate, tstate->exc_info = &tstate->exc_state; + // PyGILState_Release must not try to delete this thread state. + // This is cleared when PyGILState_Ensure() creates the thread sate. + tstate->gilstate_counter = 1; + tstate->cframe = &tstate->root_cframe; tstate->datastack_chunk = NULL; tstate->datastack_top = NULL; tstate->datastack_limit = NULL; - tstate->_initialized = 1; + tstate->_status = PyThreadState_INITIALIZED; } static PyThreadState * @@ -884,11 +1086,12 @@ PyThreadState_New(PyInterpreterState *interp) { PyThreadState *tstate = new_threadstate(interp); if (tstate) { - _PyThreadState_SetCurrent(tstate); + bind_tstate(tstate); } return tstate; } +// This must be followed by a call to _PyThreadState_Bind(); PyThreadState * _PyThreadState_Prealloc(PyInterpreterState *interp) { @@ -904,9 +1107,9 @@ _PyThreadState_Init(PyThreadState *tstate) } void -_PyThreadState_SetCurrent(PyThreadState *tstate) +_PyThreadState_Bind(PyThreadState *tstate) { - _PyGILState_NoteThreadState(&tstate->interp->runtime->gilstate, tstate); + bind_tstate(tstate); } PyObject* @@ -968,7 +1171,7 @@ PyState_AddModule(PyObject* module, PyModuleDef* def) return -1; } - PyThreadState *tstate = _PyThreadState_GET(); + PyThreadState *tstate = current_fast_get(&_PyRuntime); PyInterpreterState *interp = tstate->interp; Py_ssize_t index = def->m_base.m_index; if (interp->modules_by_index && @@ -984,7 +1187,7 @@ PyState_AddModule(PyObject* module, PyModuleDef* def) int PyState_RemoveModule(PyModuleDef* def) { - PyThreadState *tstate = _PyThreadState_GET(); + PyThreadState *tstate = current_fast_get(&_PyRuntime); PyInterpreterState *interp = tstate->interp; if (def->m_slots) { @@ -1089,8 +1292,7 @@ PyThreadState_Clear(PyThreadState *tstate) /* Common code for PyThreadState_Delete() and PyThreadState_DeleteCurrent() */ static void -tstate_delete_common(PyThreadState *tstate, - struct _gilstate_runtime_state *gilstate) +tstate_delete_common(PyThreadState *tstate) { _Py_EnsureTstateNotNULL(tstate); PyInterpreterState *interp = tstate->interp; @@ -1111,11 +1313,10 @@ tstate_delete_common(PyThreadState *tstate, } HEAD_UNLOCK(runtime); - if (gilstate->autoInterpreterState && - PyThread_tss_get(&gilstate->autoTSSkey) == tstate) - { - PyThread_tss_set(&gilstate->autoTSSkey, NULL); - } + // XXX Do this in PyThreadState_Swap() (and assert not-equal here)? + unbind_tstate(tstate); + + // XXX Move to PyThreadState_Clear()? _PyStackChunk *chunk = tstate->datastack_chunk; tstate->datastack_chunk = NULL; while (chunk != NULL) { @@ -1128,13 +1329,12 @@ tstate_delete_common(PyThreadState *tstate, static void _PyThreadState_Delete(PyThreadState *tstate, int check_current) { - struct _gilstate_runtime_state *gilstate = &tstate->interp->runtime->gilstate; if (check_current) { - if (tstate == _PyRuntimeGILState_GetThreadState(gilstate)) { + if (tstate == current_fast_get(tstate->interp->runtime)) { _Py_FatalErrorFormat(__func__, "tstate %p is still current", tstate); } } - tstate_delete_common(tstate, gilstate); + tstate_delete_common(tstate); free_threadstate(tstate); } @@ -1150,9 +1350,8 @@ void _PyThreadState_DeleteCurrent(PyThreadState *tstate) { _Py_EnsureTstateNotNULL(tstate); - struct _gilstate_runtime_state *gilstate = &tstate->interp->runtime->gilstate; - tstate_delete_common(tstate, gilstate); - _PyRuntimeGILState_SetThreadState(gilstate, NULL); + tstate_delete_common(tstate); + current_fast_clear(tstate->interp->runtime); _PyEval_ReleaseLock(tstate); free_threadstate(tstate); } @@ -1160,8 +1359,7 @@ _PyThreadState_DeleteCurrent(PyThreadState *tstate) void PyThreadState_DeleteCurrent(void) { - struct _gilstate_runtime_state *gilstate = &_PyRuntime.gilstate; - PyThreadState *tstate = _PyRuntimeGILState_GetThreadState(gilstate); + PyThreadState *tstate = current_fast_get(&_PyRuntime); _PyThreadState_DeleteCurrent(tstate); } @@ -1211,38 +1409,45 @@ _PyThreadState_DeleteExcept(_PyRuntimeState *runtime, PyThreadState *tstate) PyThreadState * _PyThreadState_UncheckedGet(void) { - return _PyThreadState_GET(); + return current_fast_get(&_PyRuntime); } PyThreadState * PyThreadState_Get(void) { - PyThreadState *tstate = _PyThreadState_GET(); + PyThreadState *tstate = current_fast_get(&_PyRuntime); _Py_EnsureTstateNotNULL(tstate); return tstate; } PyThreadState * -_PyThreadState_Swap(struct _gilstate_runtime_state *gilstate, PyThreadState *newts) +_PyThreadState_Swap(_PyRuntimeState *runtime, PyThreadState *newts) { - PyThreadState *oldts = _PyRuntimeGILState_GetThreadState(gilstate); + PyThreadState *oldts = current_fast_get(runtime); - _PyRuntimeGILState_SetThreadState(gilstate, newts); + if (newts == NULL) { + current_fast_clear(runtime); + } + else { + current_fast_set(runtime, newts); + } /* It should not be possible for more than one thread state to be used for a thread. Check this the best we can in debug builds. */ + // XXX The above isn't true when multiple interpreters are involved. #if defined(Py_DEBUG) - if (newts) { + if (newts && current_tss_initialized(runtime)) { /* This can be called from PyEval_RestoreThread(). Similar to it, we need to ensure errno doesn't change. */ int err = errno; - PyThreadState *check = _PyGILState_GetThisThreadState(gilstate); - if (check && check->interp == newts->interp && check != newts) + PyThreadState *check = current_tss_get(runtime); + if (check && check->interp == newts->interp && check != newts) { Py_FatalError("Invalid thread state for this thread"); + } errno = err; } #endif @@ -1252,7 +1457,7 @@ _PyThreadState_Swap(struct _gilstate_runtime_state *gilstate, PyThreadState *new PyThreadState * PyThreadState_Swap(PyThreadState *newts) { - return _PyThreadState_Swap(&_PyRuntime.gilstate, newts); + return _PyThreadState_Swap(&_PyRuntime, newts); } /* An extension mechanism to store arbitrary additional per-thread state. @@ -1278,7 +1483,7 @@ _PyThreadState_GetDict(PyThreadState *tstate) PyObject * PyThreadState_GetDict(void) { - PyThreadState *tstate = _PyThreadState_GET(); + PyThreadState *tstate = current_fast_get(&_PyRuntime); if (tstate == NULL) { return NULL; } @@ -1401,7 +1606,8 @@ PyThreadState_Next(PyThreadState *tstate) { PyObject * _PyThread_CurrentFrames(void) { - PyThreadState *tstate = _PyThreadState_GET(); + _PyRuntimeState *runtime = &_PyRuntime; + PyThreadState *tstate = current_fast_get(runtime); if (_PySys_Audit(tstate, "sys._current_frames", NULL) < 0) { return NULL; } @@ -1417,7 +1623,6 @@ _PyThread_CurrentFrames(void) * Because these lists can mutate even when the GIL is held, we * need to grab head_mutex for the duration. */ - _PyRuntimeState *runtime = tstate->interp->runtime; HEAD_LOCK(runtime); PyInterpreterState *i; for (i = runtime->interpreters.head; i != NULL; i = i->next) { @@ -1457,7 +1662,8 @@ _PyThread_CurrentFrames(void) PyObject * _PyThread_CurrentExceptions(void) { - PyThreadState *tstate = _PyThreadState_GET(); + _PyRuntimeState *runtime = &_PyRuntime; + PyThreadState *tstate = current_fast_get(runtime); _Py_EnsureTstateNotNULL(tstate); @@ -1476,7 +1682,6 @@ _PyThread_CurrentExceptions(void) * Because these lists can mutate even when the GIL is held, we * need to grab head_mutex for the duration. */ - _PyRuntimeState *runtime = tstate->interp->runtime; HEAD_LOCK(runtime); PyInterpreterState *i; for (i = runtime->interpreters.head; i != NULL; i = i->next) { @@ -1515,40 +1720,37 @@ _PyThread_CurrentExceptions(void) /* Python "auto thread state" API. */ -/* Keep this as a static, as it is not reliable! It can only - ever be compared to the state for the *current* thread. - * If not equal, then it doesn't matter that the actual - value may change immediately after comparison, as it can't - possibly change to the current thread's state. - * If equal, then the current thread holds the lock, so the value can't - change until we yield the lock. -*/ -static int -PyThreadState_IsCurrent(PyThreadState *tstate) -{ - /* Must be the tstate for this thread */ - struct _gilstate_runtime_state *gilstate = &_PyRuntime.gilstate; - assert(_PyGILState_GetThisThreadState(gilstate) == tstate); - return tstate == _PyRuntimeGILState_GetThreadState(gilstate); -} - /* Internal initialization/finalization functions called by Py_Initialize/Py_FinalizeEx */ PyStatus -_PyGILState_Init(_PyRuntimeState *runtime) +_PyGILState_Init(PyInterpreterState *interp) { - struct _gilstate_runtime_state *gilstate = &runtime->gilstate; - if (PyThread_tss_create(&gilstate->autoTSSkey) != 0) { - return _PyStatus_NO_MEMORY(); + if (!_Py_IsMainInterpreter(interp)) { + /* Currently, PyGILState is shared by all interpreters. The main + * interpreter is responsible to initialize it. */ + return _PyStatus_OK(); } - // PyThreadState_New() calls _PyGILState_NoteThreadState() which does - // nothing before autoInterpreterState is set. - assert(gilstate->autoInterpreterState == NULL); + _PyRuntimeState *runtime = interp->runtime; + assert(current_tss_get(runtime) == NULL); + assert(runtime->gilstate.autoInterpreterState == NULL); + runtime->gilstate.autoInterpreterState = interp; return _PyStatus_OK(); } +void +_PyGILState_Fini(PyInterpreterState *interp) +{ + if (!_Py_IsMainInterpreter(interp)) { + /* Currently, PyGILState is shared by all interpreters. The main + * interpreter is responsible to initialize it. */ + return; + } + interp->runtime->gilstate.autoInterpreterState = NULL; +} + +// XXX Drop this. PyStatus _PyGILState_SetTstate(PyThreadState *tstate) { @@ -1561,14 +1763,12 @@ _PyGILState_SetTstate(PyThreadState *tstate) * interpreter is responsible to initialize it. */ return _PyStatus_OK(); } + _PyRuntimeState *runtime = tstate->interp->runtime; - struct _gilstate_runtime_state *gilstate = &tstate->interp->runtime->gilstate; - - gilstate->autoInterpreterState = tstate->interp; - assert(PyThread_tss_get(&gilstate->autoTSSkey) == NULL); - assert(tstate->gilstate_counter == 0); + assert(runtime->gilstate.autoInterpreterState == tstate->interp); + assert(current_tss_get(runtime) == tstate); + assert(tstate->gilstate_counter == 1); - _PyGILState_NoteThreadState(gilstate, tstate); return _PyStatus_OK(); } @@ -1578,118 +1778,42 @@ _PyGILState_GetInterpreterStateUnsafe(void) return _PyRuntime.gilstate.autoInterpreterState; } -void -_PyGILState_Fini(PyInterpreterState *interp) -{ - struct _gilstate_runtime_state *gilstate = &interp->runtime->gilstate; - PyThread_tss_delete(&gilstate->autoTSSkey); - gilstate->autoInterpreterState = NULL; -} - -#ifdef HAVE_FORK -/* Reset the TSS key - called by PyOS_AfterFork_Child(). - * This should not be necessary, but some - buggy - pthread implementations - * don't reset TSS upon fork(), see issue #10517. - */ -PyStatus -_PyGILState_Reinit(_PyRuntimeState *runtime) -{ - struct _gilstate_runtime_state *gilstate = &runtime->gilstate; - PyThreadState *tstate = _PyGILState_GetThisThreadState(gilstate); - - PyThread_tss_delete(&gilstate->autoTSSkey); - if (PyThread_tss_create(&gilstate->autoTSSkey) != 0) { - return _PyStatus_NO_MEMORY(); - } - - /* If the thread had an associated auto thread state, reassociate it with - * the new key. */ - if (tstate && - PyThread_tss_set(&gilstate->autoTSSkey, (void *)tstate) != 0) - { - return _PyStatus_ERR("failed to set autoTSSkey"); - } - return _PyStatus_OK(); -} -#endif - -/* When a thread state is created for a thread by some mechanism other than - PyGILState_Ensure, it's important that the GILState machinery knows about - it so it doesn't try to create another thread state for the thread (this is - a better fix for SF bug #1010677 than the first one attempted). -*/ -static void -_PyGILState_NoteThreadState(struct _gilstate_runtime_state *gilstate, PyThreadState* tstate) -{ - /* If autoTSSkey isn't initialized, this must be the very first - threadstate created in Py_Initialize(). Don't do anything for now - (we'll be back here when _PyGILState_Init is called). */ - if (!gilstate->autoInterpreterState) { - return; - } - - /* Stick the thread state for this thread in thread specific storage. - - The only situation where you can legitimately have more than one - thread state for an OS level thread is when there are multiple - interpreters. - - You shouldn't really be using the PyGILState_ APIs anyway (see issues - #10915 and #15751). - - The first thread state created for that given OS level thread will - "win", which seems reasonable behaviour. - */ - if (PyThread_tss_get(&gilstate->autoTSSkey) == NULL) { - if ((PyThread_tss_set(&gilstate->autoTSSkey, (void *)tstate)) != 0) { - Py_FatalError("Couldn't create autoTSSkey mapping"); - } - } - - /* PyGILState_Release must not try to delete this thread state. */ - tstate->gilstate_counter = 1; -} - /* The public functions */ -static PyThreadState * -_PyGILState_GetThisThreadState(struct _gilstate_runtime_state *gilstate) -{ - if (gilstate->autoInterpreterState == NULL) - return NULL; - return (PyThreadState *)PyThread_tss_get(&gilstate->autoTSSkey); -} PyThreadState * PyGILState_GetThisThreadState(void) { - return _PyGILState_GetThisThreadState(&_PyRuntime.gilstate); + _PyRuntimeState *runtime = &_PyRuntime; + if (!current_tss_initialized(runtime)) { + return NULL; + } + return current_tss_get(runtime); } int PyGILState_Check(void) { - struct _gilstate_runtime_state *gilstate = &_PyRuntime.gilstate; - if (!gilstate->check_enabled) { + _PyRuntimeState *runtime = &_PyRuntime; + if (!runtime->gilstate.check_enabled) { return 1; } - if (!PyThread_tss_is_created(&gilstate->autoTSSkey)) { + if (!current_tss_initialized(runtime)) { return 1; } - PyThreadState *tstate = _PyRuntimeGILState_GetThreadState(gilstate); + PyThreadState *tstate = current_fast_get(runtime); if (tstate == NULL) { return 0; } - return (tstate == _PyGILState_GetThisThreadState(gilstate)); + return (tstate == current_tss_get(runtime)); } PyGILState_STATE PyGILState_Ensure(void) { _PyRuntimeState *runtime = &_PyRuntime; - struct _gilstate_runtime_state *gilstate = &runtime->gilstate; /* Note that we do not auto-init Python here - apart from potential races with 2 threads auto-initializing, pep-311 @@ -1699,27 +1823,29 @@ PyGILState_Ensure(void) /* Ensure that _PyEval_InitThreads() and _PyGILState_Init() have been called by Py_Initialize() */ assert(_PyEval_ThreadsInitialized(runtime)); - assert(gilstate->autoInterpreterState); + assert(current_tss_initialized(runtime)); + assert(runtime->gilstate.autoInterpreterState != NULL); - PyThreadState *tcur = (PyThreadState *)PyThread_tss_get(&gilstate->autoTSSkey); - int current; + PyThreadState *tcur = current_tss_get(runtime); + int has_gil; if (tcur == NULL) { /* Create a new Python thread state for this thread */ - tcur = PyThreadState_New(gilstate->autoInterpreterState); + tcur = PyThreadState_New(runtime->gilstate.autoInterpreterState); if (tcur == NULL) { Py_FatalError("Couldn't create thread-state for new thread"); } /* This is our thread state! We'll need to delete it in the matching call to PyGILState_Release(). */ + assert(tcur->gilstate_counter == 1); tcur->gilstate_counter = 0; - current = 0; /* new thread state is never current */ + has_gil = 0; /* new thread state is never current */ } else { - current = PyThreadState_IsCurrent(tcur); + has_gil = holds_gil(tcur); } - if (current == 0) { + if (!has_gil) { PyEval_RestoreThread(tcur); } @@ -1730,14 +1856,14 @@ PyGILState_Ensure(void) */ ++tcur->gilstate_counter; - return current ? PyGILState_LOCKED : PyGILState_UNLOCKED; + return has_gil ? PyGILState_LOCKED : PyGILState_UNLOCKED; } void PyGILState_Release(PyGILState_STATE oldstate) { _PyRuntimeState *runtime = &_PyRuntime; - PyThreadState *tstate = PyThread_tss_get(&runtime->gilstate.autoTSSkey); + PyThreadState *tstate = current_tss_get(runtime); if (tstate == NULL) { Py_FatalError("auto-releasing thread-state, " "but no thread-state for this thread"); @@ -1748,12 +1874,12 @@ PyGILState_Release(PyGILState_STATE oldstate) but while this is very new (April 2003), the extra check by release-only users can't hurt. */ - if (!PyThreadState_IsCurrent(tstate)) { + if (!holds_gil(tstate)) { _Py_FatalErrorFormat(__func__, "thread state %p must be current when releasing", tstate); } - assert(PyThreadState_IsCurrent(tstate)); + assert(holds_gil(tstate)); --tstate->gilstate_counter; assert(tstate->gilstate_counter >= 0); /* illegal counter value */ @@ -1769,12 +1895,13 @@ PyGILState_Release(PyGILState_STATE oldstate) * races; see bugs 225673 and 1061968 (that nasty bug has a * habit of coming back). */ - assert(_PyRuntimeGILState_GetThreadState(&runtime->gilstate) == tstate); + assert(current_fast_get(runtime) == tstate); _PyThreadState_DeleteCurrent(tstate); } /* Release the lock if necessary */ - else if (oldstate == PyGILState_UNLOCKED) + else if (oldstate == PyGILState_UNLOCKED) { PyEval_SaveThread(); + } } @@ -1905,7 +2032,8 @@ _PyObject_CheckCrossInterpreterData(PyObject *obj) int _PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data) { - PyThreadState *tstate = _PyThreadState_GET(); + _PyRuntimeState *runtime = &_PyRuntime; + PyThreadState *tstate = current_fast_get(runtime); #ifdef Py_DEBUG // The caller must hold the GIL _Py_EnsureTstateNotNULL(tstate); @@ -1948,19 +2076,19 @@ _PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *data) typedef void (*releasefunc)(PyInterpreterState *, void *); static void -_call_in_interpreter(struct _gilstate_runtime_state *gilstate, - PyInterpreterState *interp, releasefunc func, void *arg) +_call_in_interpreter(PyInterpreterState *interp, releasefunc func, void *arg) { /* We would use Py_AddPendingCall() if it weren't specific to the * main interpreter (see bpo-33608). In the meantime we take a * naive approach. */ + _PyRuntimeState *runtime = interp->runtime; PyThreadState *save_tstate = NULL; - if (interp != _PyRuntimeGILState_GetThreadState(gilstate)->interp) { + if (interp != current_fast_get(runtime)->interp) { // XXX Using the "head" thread isn't strictly correct. PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); // XXX Possible GILState issues? - save_tstate = _PyThreadState_Swap(gilstate, tstate); + save_tstate = _PyThreadState_Swap(runtime, tstate); } // XXX Once the GIL is per-interpreter, this should be called with the @@ -1969,7 +2097,7 @@ _call_in_interpreter(struct _gilstate_runtime_state *gilstate, // Switch back. if (save_tstate != NULL) { - _PyThreadState_Swap(gilstate, save_tstate); + _PyThreadState_Swap(runtime, save_tstate); } } @@ -1993,8 +2121,7 @@ _PyCrossInterpreterData_Release(_PyCrossInterpreterData *data) } // "Release" the data and/or the object. - struct _gilstate_runtime_state *gilstate = &_PyRuntime.gilstate; - _call_in_interpreter(gilstate, interp, + _call_in_interpreter(interp, (releasefunc)_PyCrossInterpreterData_Clear, data); return 0; } @@ -2314,8 +2441,9 @@ _PyInterpreterState_GetConfigCopy(PyConfig *config) const PyConfig* _Py_GetConfig(void) { + _PyRuntimeState *runtime = &_PyRuntime; assert(PyGILState_Check()); - PyThreadState *tstate = _PyThreadState_GET(); + PyThreadState *tstate = current_fast_get(runtime); _Py_EnsureTstateNotNULL(tstate); return _PyInterpreterState_GetConfig(tstate->interp); } From webhook-mailer at python.org Thu Jan 19 18:30:55 2023 From: webhook-mailer at python.org (iritkatriel) Date: Thu, 19 Jan 2023 23:30:55 -0000 Subject: [Python-checkins] gh-101169: reduce the implementation of except* by one bytecode instruction (#101170) Message-ID: https://github.com/python/cpython/commit/9ec9b203eaa219ed13ad9deb21740924bd22b10d commit: 9ec9b203eaa219ed13ad9deb21740924bd22b10d branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-19T23:30:49Z summary: gh-101169: reduce the implementation of except* by one bytecode instruction (#101170) files: M Python/compile.c diff --git a/Python/compile.c b/Python/compile.c index f98892ec5fc6..bfc6e3499001 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -3586,16 +3586,15 @@ compiler_try_except(struct compiler *c, stmt_ty s) [] POP_BLOCK [] JUMP L0 - [exc] L1: COPY 1 ) save copy of the original exception - [orig, exc] BUILD_LIST ) list for raised/reraised excs ("result") - [orig, exc, res] SWAP 2 + [exc] L1: BUILD_LIST ) list for raised/reraised excs ("result") + [orig, res] COPY 2 ) make a copy of the original EG [orig, res, exc] [orig, res, exc, E1] CHECK_EG_MATCH - [orig, red, rest/exc, match?] COPY 1 - [orig, red, rest/exc, match?, match?] POP_JUMP_IF_NOT_NONE H1 - [orig, red, exc, None] POP_TOP - [orig, red, exc] JUMP L2 + [orig, res, rest/exc, match?] COPY 1 + [orig, res, rest/exc, match?, match?] POP_JUMP_IF_NOT_NONE H1 + [orig, res, exc, None] POP_TOP + [orig, res, exc] JUMP L2 [orig, res, rest, match] H1: (or POP if no V1) @@ -3664,21 +3663,17 @@ compiler_try_star_except(struct compiler *c, stmt_ty s) except = next_except; NEW_JUMP_TARGET_LABEL(c, handle_match); if (i == 0) { - /* Push the original EG into the stack */ + /* create empty list for exceptions raised/reraise in the except* blocks */ /* - [exc] COPY 1 - [orig, exc] + [orig] BUILD_LIST */ - ADDOP_I(c, loc, COPY, 1); - - /* create empty list for exceptions raised/reraise in the except* blocks */ + /* Creat a copy of the original EG */ /* - [orig, exc] BUILD_LIST - [orig, exc, []] SWAP 2 + [orig, []] COPY 2 [orig, [], exc] */ ADDOP_I(c, loc, BUILD_LIST, 0); - ADDOP_I(c, loc, SWAP, 2); + ADDOP_I(c, loc, COPY, 2); } if (handler->v.ExceptHandler.type) { VISIT(c, expr, handler->v.ExceptHandler.type); From webhook-mailer at python.org Thu Jan 19 18:45:59 2023 From: webhook-mailer at python.org (miss-islington) Date: Thu, 19 Jan 2023 23:45:59 -0000 Subject: [Python-checkins] gh-101167: fix bug in the new test.support.requires_specialization decorator (GH-101171) Message-ID: https://github.com/python/cpython/commit/0c5db2a60701a939288eb4c7704382631a598398 commit: 0c5db2a60701a939288eb4c7704382631a598398 branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-19T15:45:53-08:00 summary: gh-101167: fix bug in the new test.support.requires_specialization decorator (GH-101171) Fixes #101167. Automerge-Triggered-By: GH:iritkatriel files: M Lib/test/support/__init__.py diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 37f90cfed212..e4e4de896dff 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1079,7 +1079,8 @@ def requires_limited_api(test): _testcapi.LIMITED_API_AVAILABLE, 'needs Limited API support')(test) def requires_specialization(test): - return unittest.skipUnless(opcode.ENABLE_SPECIALIZATION, "requires specialization") + return unittest.skipUnless( + opcode.ENABLE_SPECIALIZATION, "requires specialization")(test) def _filter_suite(suite, pred): """Recursively filter test cases in a suite based on a predicate.""" From webhook-mailer at python.org Thu Jan 19 19:23:59 2023 From: webhook-mailer at python.org (ericsnowcurrently) Date: Fri, 20 Jan 2023 00:23:59 -0000 Subject: [Python-checkins] gh-59956: Fix Function Groupings in pystate.c (gh-101172) Message-ID: https://github.com/python/cpython/commit/f30c94024f305d7d0ebb34fdd0f422442cfb0047 commit: f30c94024f305d7d0ebb34fdd0f422442cfb0047 branch: main author: Eric Snow committer: ericsnowcurrently date: 2023-01-19T17:23:53-07:00 summary: gh-59956: Fix Function Groupings in pystate.c (gh-101172) This is a follow-up to gh-101161. The objective is to make it easier to read Python/pystate.c by grouping the functions there in a consistent way. This exclusively involves moving code around and adding various kinds of comments. https://github.com/python/cpython/issues/59956 files: M Python/pystate.c diff --git a/Python/pystate.c b/Python/pystate.c index 756c1c73330d..464e5d63e4c4 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -41,19 +41,36 @@ extern "C" { static void _PyThreadState_Delete(PyThreadState *tstate, int check_current); -/* the current thread state */ +/****************************************/ +/* helpers for the current thread state */ +/****************************************/ + +// API for the current thread state is further down. + +/* "current" means one of: + - bound to the current OS thread + - holds the GIL + */ + +//------------------------------------------------- +// a highly efficient lookup for the current thread +//------------------------------------------------- + +/* + The stored thread state is set by PyThraedState_Swap(). + + For each of these functions, the GIL mus be held by the current thread. + */ static inline PyThreadState * current_fast_get(_PyRuntimeState *runtime) { - // The GIL must be held by the current thread. return (PyThreadState*)_Py_atomic_load_relaxed(&runtime->tstate_current); } static inline void current_fast_set(_PyRuntimeState *runtime, PyThreadState *tstate) { - // The GIL must be held by the current thread. assert(tstate != NULL); _Py_atomic_store_relaxed(&runtime->tstate_current, (uintptr_t)tstate); } @@ -61,11 +78,20 @@ current_fast_set(_PyRuntimeState *runtime, PyThreadState *tstate) static inline void current_fast_clear(_PyRuntimeState *runtime) { - // The GIL must be held by the current thread. _Py_atomic_store_relaxed(&runtime->tstate_current, (uintptr_t)NULL); } +//------------------------------------------------ +// the thread state bound to the current OS thread +//------------------------------------------------ + +/* + The stored thread state is set by bind_tstate() (AKA PyThreadState_Bind(). + + The GIL does no need to be held for these. + */ + static int current_tss_initialized(_PyRuntimeState *runtime) { @@ -148,6 +174,7 @@ current_tss_reinit(_PyRuntimeState *runtime) } #endif + static void bind_tstate(PyThreadState *tstate) { @@ -213,6 +240,11 @@ unbind_tstate(PyThreadState *tstate) tstate->_status = PyThreadState_UNBOUND; } + +//---------------------------------------------- +// the thread state that currently holds the GIL +//---------------------------------------------- + /* This is not exported, as it is not reliable! It can only ever be compared to the state for the *current* thread. * If not equal, then it doesn't matter that the actual @@ -234,7 +266,13 @@ holds_gil(PyThreadState *tstate) } -/* the global runtime */ +/****************************/ +/* the global runtime state */ +/****************************/ + +//---------- +// lifecycle +//---------- /* Suppress deprecation warning for PyBytesObject.ob_shash */ _Py_COMP_DIAG_PUSH @@ -438,6 +476,16 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime) #endif +/*************************************/ +/* the per-interpreter runtime state */ +/*************************************/ + +//---------- +// lifecycle +//---------- + +/* Calling this indicates that the runtime is ready to create interpreters. */ + PyStatus _PyInterpreterState_Enable(_PyRuntimeState *runtime) { @@ -464,6 +512,7 @@ _PyInterpreterState_Enable(_PyRuntimeState *runtime) return _PyStatus_OK(); } + static PyInterpreterState * alloc_interpreter(void) { @@ -798,19 +847,40 @@ _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime) #endif -PyInterpreterState * -PyInterpreterState_Get(void) +// Used by finalize_modules() +void +_PyInterpreterState_ClearModules(PyInterpreterState *interp) { - PyThreadState *tstate = current_fast_get(&_PyRuntime); - _Py_EnsureTstateNotNULL(tstate); - PyInterpreterState *interp = tstate->interp; - if (interp == NULL) { - Py_FatalError("no current interpreter"); + if (!interp->modules_by_index) { + return; + } + + Py_ssize_t i; + for (i = 0; i < PyList_GET_SIZE(interp->modules_by_index); i++) { + PyObject *m = PyList_GET_ITEM(interp->modules_by_index, i); + if (PyModule_Check(m)) { + /* cleanup the saved copy of module dicts */ + PyModuleDef *md = PyModule_GetDef(m); + if (md) { + Py_CLEAR(md->m_base.m_copy); + } + } + } + + /* Setting modules_by_index to NULL could be dangerous, so we + clear the list instead. */ + if (PyList_SetSlice(interp->modules_by_index, + 0, PyList_GET_SIZE(interp->modules_by_index), + NULL)) { + PyErr_WriteUnraisable(interp->modules_by_index); } - return interp; } +//---------- +// accessors +//---------- + int64_t PyInterpreterState_GetID(PyInterpreterState *interp) { @@ -822,41 +892,6 @@ PyInterpreterState_GetID(PyInterpreterState *interp) } -static PyInterpreterState * -interp_look_up_id(_PyRuntimeState *runtime, int64_t requested_id) -{ - PyInterpreterState *interp = runtime->interpreters.head; - while (interp != NULL) { - int64_t id = PyInterpreterState_GetID(interp); - if (id < 0) { - return NULL; - } - if (requested_id == id) { - return interp; - } - interp = PyInterpreterState_Next(interp); - } - return NULL; -} - -PyInterpreterState * -_PyInterpreterState_LookUpID(int64_t requested_id) -{ - PyInterpreterState *interp = NULL; - if (requested_id >= 0) { - _PyRuntimeState *runtime = &_PyRuntime; - HEAD_LOCK(runtime); - interp = interp_look_up_id(runtime, requested_id); - HEAD_UNLOCK(runtime); - } - if (interp == NULL && !PyErr_Occurred()) { - PyErr_Format(PyExc_RuntimeError, - "unrecognized interpreter ID %lld", requested_id); - } - return interp; -} - - int _PyInterpreterState_IDInitref(PyInterpreterState *interp) { @@ -945,6 +980,76 @@ PyInterpreterState_GetDict(PyInterpreterState *interp) return interp->dict; } + +//----------------------------- +// look up an interpreter state +//----------------------------- + +/* Return the interpreter associated with the current OS thread. + + The GIL must be held. + */ + +PyInterpreterState * +PyInterpreterState_Get(void) +{ + PyThreadState *tstate = current_fast_get(&_PyRuntime); + _Py_EnsureTstateNotNULL(tstate); + PyInterpreterState *interp = tstate->interp; + if (interp == NULL) { + Py_FatalError("no current interpreter"); + } + return interp; +} + + +static PyInterpreterState * +interp_look_up_id(_PyRuntimeState *runtime, int64_t requested_id) +{ + PyInterpreterState *interp = runtime->interpreters.head; + while (interp != NULL) { + int64_t id = PyInterpreterState_GetID(interp); + if (id < 0) { + return NULL; + } + if (requested_id == id) { + return interp; + } + interp = PyInterpreterState_Next(interp); + } + return NULL; +} + +/* Return the interpreter state with the given ID. + + Fail with RuntimeError if the interpreter is not found. */ + +PyInterpreterState * +_PyInterpreterState_LookUpID(int64_t requested_id) +{ + PyInterpreterState *interp = NULL; + if (requested_id >= 0) { + _PyRuntimeState *runtime = &_PyRuntime; + HEAD_LOCK(runtime); + interp = interp_look_up_id(runtime, requested_id); + HEAD_UNLOCK(runtime); + } + if (interp == NULL && !PyErr_Occurred()) { + PyErr_Format(PyExc_RuntimeError, + "unrecognized interpreter ID %lld", requested_id); + } + return interp; +} + + +/********************************/ +/* the per-thread runtime state */ +/********************************/ + +//---------- +// lifecycle +//---------- + /* Minimum size of data stack chunk */ #define DATA_STACK_CHUNK_SIZE (16*1024) @@ -1106,140 +1211,6 @@ _PyThreadState_Init(PyThreadState *tstate) Py_FatalError("_PyThreadState_Init() is for internal use only"); } -void -_PyThreadState_Bind(PyThreadState *tstate) -{ - bind_tstate(tstate); -} - -PyObject* -PyState_FindModule(PyModuleDef* module) -{ - Py_ssize_t index = module->m_base.m_index; - PyInterpreterState *state = _PyInterpreterState_GET(); - PyObject *res; - if (module->m_slots) { - return NULL; - } - if (index == 0) - return NULL; - if (state->modules_by_index == NULL) - return NULL; - if (index >= PyList_GET_SIZE(state->modules_by_index)) - return NULL; - res = PyList_GET_ITEM(state->modules_by_index, index); - return res==Py_None ? NULL : res; -} - -int -_PyState_AddModule(PyThreadState *tstate, PyObject* module, PyModuleDef* def) -{ - if (!def) { - assert(_PyErr_Occurred(tstate)); - return -1; - } - if (def->m_slots) { - _PyErr_SetString(tstate, - PyExc_SystemError, - "PyState_AddModule called on module with slots"); - return -1; - } - - PyInterpreterState *interp = tstate->interp; - if (!interp->modules_by_index) { - interp->modules_by_index = PyList_New(0); - if (!interp->modules_by_index) { - return -1; - } - } - - while (PyList_GET_SIZE(interp->modules_by_index) <= def->m_base.m_index) { - if (PyList_Append(interp->modules_by_index, Py_None) < 0) { - return -1; - } - } - - return PyList_SetItem(interp->modules_by_index, - def->m_base.m_index, Py_NewRef(module)); -} - -int -PyState_AddModule(PyObject* module, PyModuleDef* def) -{ - if (!def) { - Py_FatalError("module definition is NULL"); - return -1; - } - - PyThreadState *tstate = current_fast_get(&_PyRuntime); - PyInterpreterState *interp = tstate->interp; - Py_ssize_t index = def->m_base.m_index; - if (interp->modules_by_index && - index < PyList_GET_SIZE(interp->modules_by_index) && - module == PyList_GET_ITEM(interp->modules_by_index, index)) - { - _Py_FatalErrorFormat(__func__, "module %p already added", module); - return -1; - } - return _PyState_AddModule(tstate, module, def); -} - -int -PyState_RemoveModule(PyModuleDef* def) -{ - PyThreadState *tstate = current_fast_get(&_PyRuntime); - PyInterpreterState *interp = tstate->interp; - - if (def->m_slots) { - _PyErr_SetString(tstate, - PyExc_SystemError, - "PyState_RemoveModule called on module with slots"); - return -1; - } - - Py_ssize_t index = def->m_base.m_index; - if (index == 0) { - Py_FatalError("invalid module index"); - } - if (interp->modules_by_index == NULL) { - Py_FatalError("Interpreters module-list not accessible."); - } - if (index > PyList_GET_SIZE(interp->modules_by_index)) { - Py_FatalError("Module index out of bounds."); - } - - return PyList_SetItem(interp->modules_by_index, index, Py_NewRef(Py_None)); -} - -// Used by finalize_modules() -void -_PyInterpreterState_ClearModules(PyInterpreterState *interp) -{ - if (!interp->modules_by_index) { - return; - } - - Py_ssize_t i; - for (i = 0; i < PyList_GET_SIZE(interp->modules_by_index); i++) { - PyObject *m = PyList_GET_ITEM(interp->modules_by_index, i); - if (PyModule_Check(m)) { - /* cleanup the saved copy of module dicts */ - PyModuleDef *md = PyModule_GetDef(m); - if (md) { - Py_CLEAR(md->m_base.m_copy); - } - } - } - - /* Setting modules_by_index to NULL could be dangerous, so we - clear the list instead. */ - if (PyList_SetSlice(interp->modules_by_index, - 0, PyList_GET_SIZE(interp->modules_by_index), - NULL)) { - PyErr_WriteUnraisable(interp->modules_by_index); - } -} - void PyThreadState_Clear(PyThreadState *tstate) { @@ -1406,59 +1377,9 @@ _PyThreadState_DeleteExcept(_PyRuntimeState *runtime, PyThreadState *tstate) } -PyThreadState * -_PyThreadState_UncheckedGet(void) -{ - return current_fast_get(&_PyRuntime); -} - - -PyThreadState * -PyThreadState_Get(void) -{ - PyThreadState *tstate = current_fast_get(&_PyRuntime); - _Py_EnsureTstateNotNULL(tstate); - return tstate; -} - - -PyThreadState * -_PyThreadState_Swap(_PyRuntimeState *runtime, PyThreadState *newts) -{ - PyThreadState *oldts = current_fast_get(runtime); - - if (newts == NULL) { - current_fast_clear(runtime); - } - else { - current_fast_set(runtime, newts); - } - /* It should not be possible for more than one thread state - to be used for a thread. Check this the best we can in debug - builds. - */ - // XXX The above isn't true when multiple interpreters are involved. -#if defined(Py_DEBUG) - if (newts && current_tss_initialized(runtime)) { - /* This can be called from PyEval_RestoreThread(). Similar - to it, we need to ensure errno doesn't change. - */ - int err = errno; - PyThreadState *check = current_tss_get(runtime); - if (check && check->interp == newts->interp && check != newts) { - Py_FatalError("Invalid thread state for this thread"); - } - errno = err; - } -#endif - return oldts; -} - -PyThreadState * -PyThreadState_Swap(PyThreadState *newts) -{ - return _PyThreadState_Swap(&_PyRuntime, newts); -} +//---------- +// accessors +//---------- /* An extension mechanism to store arbitrary additional per-thread state. PyThreadState_GetDict() returns a dictionary that can be used to hold such @@ -1523,6 +1444,10 @@ PyThreadState_GetID(PyThreadState *tstate) } +//---------- +// other API +//---------- + /* Asynchronously raise an exception in a thread. Requested by Just van Rossum and Alex Martelli. To prevent naive misuse, you must write your own extension @@ -1531,6 +1456,8 @@ PyThreadState_GetID(PyThreadState *tstate) match any known thread id). Can be called with exc=NULL to clear an existing async exception. This raises no exceptions. */ +// XXX Move this to Python/ceval_gil.c? +// XXX Deprecate this. int PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc) { @@ -1568,8 +1495,79 @@ PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc) return 0; } -/* Routines for advanced debuggers, requested by David Beazley. - Don't use unless you know what you are doing! */ + +//--------------------------------- +// API for the current thread state +//--------------------------------- + +PyThreadState * +_PyThreadState_UncheckedGet(void) +{ + return current_fast_get(&_PyRuntime); +} + + +PyThreadState * +PyThreadState_Get(void) +{ + PyThreadState *tstate = current_fast_get(&_PyRuntime); + _Py_EnsureTstateNotNULL(tstate); + return tstate; +} + + +PyThreadState * +_PyThreadState_Swap(_PyRuntimeState *runtime, PyThreadState *newts) +{ + PyThreadState *oldts = current_fast_get(runtime); + + if (newts == NULL) { + current_fast_clear(runtime); + } + else { + current_fast_set(runtime, newts); + } + /* It should not be possible for more than one thread state + to be used for a thread. Check this the best we can in debug + builds. + */ + // XXX The above isn't true when multiple interpreters are involved. +#if defined(Py_DEBUG) + if (newts && current_tss_initialized(runtime)) { + /* This can be called from PyEval_RestoreThread(). Similar + to it, we need to ensure errno doesn't change. + */ + int err = errno; + PyThreadState *check = current_tss_get(runtime); + if (check && check->interp == newts->interp && check != newts) { + Py_FatalError("Invalid thread state for this thread"); + } + errno = err; + } +#endif + return oldts; +} + +PyThreadState * +PyThreadState_Swap(PyThreadState *newts) +{ + return _PyThreadState_Swap(&_PyRuntime, newts); +} + + +void +_PyThreadState_Bind(PyThreadState *tstate) +{ + bind_tstate(tstate); +} + + +/***********************************/ +/* routines for advanced debuggers */ +/***********************************/ + +// (requested by David Beazley) +// Don't use unless you know what you are doing! PyInterpreterState * PyInterpreterState_Head(void) @@ -1598,6 +1596,11 @@ PyThreadState_Next(PyThreadState *tstate) { return tstate->next; } + +/********************************************/ +/* reporting execution state of all threads */ +/********************************************/ + /* The implementation of sys._current_frames(). This is intended to be called with the GIL held, as it will be when called via sys._current_frames(). It's possible it would work fine even without @@ -1659,6 +1662,11 @@ _PyThread_CurrentFrames(void) return result; } +/* The implementation of sys._current_exceptions(). This is intended to be + called with the GIL held, as it will be when called via + sys._current_exceptions(). It's possible it would work fine even without + the GIL held, but haven't thought enough about that. +*/ PyObject * _PyThread_CurrentExceptions(void) { @@ -1718,7 +1726,114 @@ _PyThread_CurrentExceptions(void) return result; } + +/****************/ +/* module state */ +/****************/ + +PyObject* +PyState_FindModule(PyModuleDef* module) +{ + Py_ssize_t index = module->m_base.m_index; + PyInterpreterState *state = _PyInterpreterState_GET(); + PyObject *res; + if (module->m_slots) { + return NULL; + } + if (index == 0) + return NULL; + if (state->modules_by_index == NULL) + return NULL; + if (index >= PyList_GET_SIZE(state->modules_by_index)) + return NULL; + res = PyList_GET_ITEM(state->modules_by_index, index); + return res==Py_None ? NULL : res; +} + +int +_PyState_AddModule(PyThreadState *tstate, PyObject* module, PyModuleDef* def) +{ + if (!def) { + assert(_PyErr_Occurred(tstate)); + return -1; + } + if (def->m_slots) { + _PyErr_SetString(tstate, + PyExc_SystemError, + "PyState_AddModule called on module with slots"); + return -1; + } + + PyInterpreterState *interp = tstate->interp; + if (!interp->modules_by_index) { + interp->modules_by_index = PyList_New(0); + if (!interp->modules_by_index) { + return -1; + } + } + + while (PyList_GET_SIZE(interp->modules_by_index) <= def->m_base.m_index) { + if (PyList_Append(interp->modules_by_index, Py_None) < 0) { + return -1; + } + } + + return PyList_SetItem(interp->modules_by_index, + def->m_base.m_index, Py_NewRef(module)); +} + +int +PyState_AddModule(PyObject* module, PyModuleDef* def) +{ + if (!def) { + Py_FatalError("module definition is NULL"); + return -1; + } + + PyThreadState *tstate = current_fast_get(&_PyRuntime); + PyInterpreterState *interp = tstate->interp; + Py_ssize_t index = def->m_base.m_index; + if (interp->modules_by_index && + index < PyList_GET_SIZE(interp->modules_by_index) && + module == PyList_GET_ITEM(interp->modules_by_index, index)) + { + _Py_FatalErrorFormat(__func__, "module %p already added", module); + return -1; + } + return _PyState_AddModule(tstate, module, def); +} + +int +PyState_RemoveModule(PyModuleDef* def) +{ + PyThreadState *tstate = current_fast_get(&_PyRuntime); + PyInterpreterState *interp = tstate->interp; + + if (def->m_slots) { + _PyErr_SetString(tstate, + PyExc_SystemError, + "PyState_RemoveModule called on module with slots"); + return -1; + } + + Py_ssize_t index = def->m_base.m_index; + if (index == 0) { + Py_FatalError("invalid module index"); + } + if (interp->modules_by_index == NULL) { + Py_FatalError("Interpreters module-list not accessible."); + } + if (index > PyList_GET_SIZE(interp->modules_by_index)) { + Py_FatalError("Module index out of bounds."); + } + + return PyList_SetItem(interp->modules_by_index, index, Py_NewRef(Py_None)); +} + + +/***********************************/ /* Python "auto thread state" API. */ +/***********************************/ /* Internal initialization/finalization functions called by Py_Initialize/Py_FinalizeEx From webhook-mailer at python.org Thu Jan 19 21:16:27 2023 From: webhook-mailer at python.org (ethanfurman) Date: Fri, 20 Jan 2023 02:16:27 -0000 Subject: [Python-checkins] gh-101100: [Enum] Fix sphinx warnings in docs (GH-101122) Message-ID: https://github.com/python/cpython/commit/9e025d305f159aebf01775ad1dc2817679f01aa9 commit: 9e025d305f159aebf01775ad1dc2817679f01aa9 branch: main author: Nikita Sobolev committer: ethanfurman date: 2023-01-19T18:16:21-08:00 summary: gh-101100: [Enum] Fix sphinx warnings in docs (GH-101122) * gh-101100: [Enum] Fix sphinx warnings in Co-authored-by: C.A.M. Gerlach files: M Doc/library/enum.rst diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index b9cc802e752e..cfe4fbeb0698 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -52,11 +52,11 @@ are not normal Python classes. See .. note:: Nomenclature - - The class :class:`Color` is an *enumeration* (or *enum*) - - The attributes :attr:`Color.RED`, :attr:`Color.GREEN`, etc., are + - The class :class:`!Color` is an *enumeration* (or *enum*) + - The attributes :attr:`!Color.RED`, :attr:`!Color.GREEN`, etc., are *enumeration members* (or *members*) and are functionally constants. - The enum members have *names* and *values* (the name of - :attr:`Color.RED` is ``RED``, the value of :attr:`Color.BLUE` is + :attr:`!Color.RED` is ``RED``, the value of :attr:`!Color.BLUE` is ``3``, etc.) --------------- @@ -165,8 +165,8 @@ Data Types to subclass *EnumType* -- see :ref:`Subclassing EnumType ` for details. - *EnumType* is responsible for setting the correct :meth:`__repr__`, - :meth:`__str__`, :meth:`__format__`, and :meth:`__reduce__` methods on the + *EnumType* is responsible for setting the correct :meth:`!__repr__`, + :meth:`!__str__`, :meth:`!__format__`, and :meth:`!__reduce__` methods on the final *enum*, as well as creating the enum members, properly handling duplicates, providing iteration over the enum class, etc. @@ -424,9 +424,9 @@ Data Types Using :class:`auto` with :class:`IntEnum` results in integers of increasing value, starting with ``1``. - .. versionchanged:: 3.11 :meth:`__str__` is now :func:`int.__str__` to + .. versionchanged:: 3.11 :meth:`~object.__str__` is now :meth:`!int.__str__` to better support the *replacement of existing constants* use-case. - :meth:`__format__` was already :func:`int.__format__` for that same reason. + :meth:`~object.__format__` was already :meth:`!int.__format__` for that same reason. .. class:: StrEnum @@ -761,11 +761,11 @@ Data Types Supported ``__dunder__`` names """""""""""""""""""""""""""""" -:attr:`__members__` is a read-only ordered mapping of ``member_name``:``member`` +:attr:`~EnumType.__members__` is a read-only ordered mapping of ``member_name``:``member`` items. It is only available on the class. -:meth:`__new__`, if specified, must create and return the enum members; it is -also a very good idea to set the member's :attr:`_value_` appropriately. Once +:meth:`~object.__new__`, if specified, must create and return the enum members; it is +also a very good idea to set the member's :attr:`!_value_` appropriately. Once all the members are created it is no longer used. @@ -804,7 +804,7 @@ Utilities and Decorators .. class:: auto *auto* can be used in place of a value. If used, the *Enum* machinery will - call an *Enum*'s :meth:`_generate_next_value_` to get an appropriate value. + call an *Enum*'s :meth:`~Enum._generate_next_value_` to get an appropriate value. For *Enum* and *IntEnum* that appropriate value will be the last value plus one; for *Flag* and *IntFlag* it will be the first power-of-two greater than the highest value; for *StrEnum* it will be the lower-cased version of @@ -847,7 +847,7 @@ Utilities and Decorators .. decorator:: unique A :keyword:`class` decorator specifically for enumerations. It searches an - enumeration's :attr:`__members__`, gathering any aliases it finds; if any are + enumeration's :attr:`~EnumType.__members__`, gathering any aliases it finds; if any are found :exc:`ValueError` is raised with the details:: >>> from enum import Enum, unique From webhook-mailer at python.org Fri Jan 20 00:06:11 2023 From: webhook-mailer at python.org (miss-islington) Date: Fri, 20 Jan 2023 05:06:11 -0000 Subject: [Python-checkins] gh-101041: Fix a misspelled name of `utctimetuple` in a doc warning (GH-101042) Message-ID: https://github.com/python/cpython/commit/10c61301240374701279aa8f87bf9f1caf1e9c9a commit: 10c61301240374701279aa8f87bf9f1caf1e9c9a branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-19T21:06:04-08:00 summary: gh-101041: Fix a misspelled name of `utctimetuple` in a doc warning (GH-101042) (cherry picked from commit 8e9d08b062bbabfe439bc73f82e3d7bb3800189e) Co-authored-by: Oleg Iarygin files: M Doc/library/datetime.rst diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index f222ec9bb771..e23d2a96cc26 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -1342,7 +1342,7 @@ Instance methods: Because naive ``datetime`` objects are treated by many ``datetime`` methods as local times, it is preferred to use aware datetimes to represent times - in UTC; as a result, using ``utcfromtimetuple`` may give misleading + in UTC; as a result, using :meth:`datetime.utctimetuple` may give misleading results. If you have a naive ``datetime`` representing UTC, use ``datetime.replace(tzinfo=timezone.utc)`` to make it aware, at which point you can use :meth:`.datetime.timetuple`. From webhook-mailer at python.org Fri Jan 20 00:06:54 2023 From: webhook-mailer at python.org (miss-islington) Date: Fri, 20 Jan 2023 05:06:54 -0000 Subject: [Python-checkins] gh-101041: Fix a misspelled name of `utctimetuple` in a doc warning (GH-101042) Message-ID: https://github.com/python/cpython/commit/1998ea69c7739bc833baca47849f33e84fa337df commit: 1998ea69c7739bc833baca47849f33e84fa337df branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-19T21:06:47-08:00 summary: gh-101041: Fix a misspelled name of `utctimetuple` in a doc warning (GH-101042) (cherry picked from commit 8e9d08b062bbabfe439bc73f82e3d7bb3800189e) Co-authored-by: Oleg Iarygin files: M Doc/library/datetime.rst diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index e700b0a1347a..c7b96671eff7 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -1350,7 +1350,7 @@ Instance methods: Because naive ``datetime`` objects are treated by many ``datetime`` methods as local times, it is preferred to use aware datetimes to represent times - in UTC; as a result, using ``utcfromtimetuple`` may give misleading + in UTC; as a result, using :meth:`datetime.utctimetuple` may give misleading results. If you have a naive ``datetime`` representing UTC, use ``datetime.replace(tzinfo=timezone.utc)`` to make it aware, at which point you can use :meth:`.datetime.timetuple`. From webhook-mailer at python.org Fri Jan 20 02:04:38 2023 From: webhook-mailer at python.org (gpshead) Date: Fri, 20 Jan 2023 07:04:38 -0000 Subject: [Python-checkins] gh-101144: Allow open and read_text encoding to be positional. (#101145) Message-ID: https://github.com/python/cpython/commit/5927013e47a8c63b70e104152351f3447baa819c commit: 5927013e47a8c63b70e104152351f3447baa819c branch: main author: Gregory P. Smith committer: gpshead date: 2023-01-19T23:04:30-08:00 summary: gh-101144: Allow open and read_text encoding to be positional. (#101145) The zipfile.Path open() and read_text() encoding parameter can be supplied as a positional argument without causing a TypeError again. 3.10.0b1 included a regression that made it keyword only. Documentation update included as users writing code to be compatible with a wide range of versions will need to consider this for some time. files: A Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst M Doc/library/zipfile.rst M Lib/test/test_zipfile/test_path.py M Lib/zipfile/_path.py diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst index ef6934483bcd..1c516723f04a 100644 --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -551,6 +551,12 @@ Path objects are traversable using the ``/`` operator or ``joinpath``. Added support for text and binary modes for open. Default mode is now text. + .. versionchanged:: 3.11.2 + The ``encoding`` parameter can be supplied as a positional argument + without causing a :exc:`TypeError`. As it could in 3.9. Code needing to + be compatible with unpatched 3.10 and 3.11 versions must pass all + :class:`io.TextIOWrapper` arguments, ``encoding`` included, as keywords. + .. method:: Path.iterdir() Enumerate the children of the current directory. @@ -596,6 +602,12 @@ Path objects are traversable using the ``/`` operator or ``joinpath``. :class:`io.TextIOWrapper` (except ``buffer``, which is implied by the context). + .. versionchanged:: 3.11.2 + The ``encoding`` parameter can be supplied as a positional argument + without causing a :exc:`TypeError`. As it could in 3.9. Code needing to + be compatible with unpatched 3.10 and 3.11 versions must pass all + :class:`io.TextIOWrapper` arguments, ``encoding`` included, as keywords. + .. method:: Path.read_bytes() Read the current file as bytes. diff --git a/Lib/test/test_zipfile/test_path.py b/Lib/test/test_zipfile/test_path.py index 02253c59e959..3086fd2080a9 100644 --- a/Lib/test/test_zipfile/test_path.py +++ b/Lib/test/test_zipfile/test_path.py @@ -1,11 +1,12 @@ import io -import zipfile +import itertools import contextlib import pathlib -import unittest -import string import pickle -import itertools +import string +from test.support.script_helper import assert_python_ok +import unittest +import zipfile from ._test_params import parameterize, Invoked from ._functools import compose @@ -145,7 +146,69 @@ def test_open(self, alpharep): a, b, g = root.iterdir() with a.open(encoding="utf-8") as strm: data = strm.read() - assert data == "content of a" + self.assertEqual(data, "content of a") + with a.open('r', "utf-8") as strm: # not a kw, no gh-101144 TypeError + data = strm.read() + self.assertEqual(data, "content of a") + + def test_open_encoding_utf16(self): + in_memory_file = io.BytesIO() + zf = zipfile.ZipFile(in_memory_file, "w") + zf.writestr("path/16.txt", "This was utf-16".encode("utf-16")) + zf.filename = "test_open_utf16.zip" + root = zipfile.Path(zf) + (path,) = root.iterdir() + u16 = path.joinpath("16.txt") + with u16.open('r', "utf-16") as strm: + data = strm.read() + self.assertEqual(data, "This was utf-16") + with u16.open(encoding="utf-16") as strm: + data = strm.read() + self.assertEqual(data, "This was utf-16") + + def test_open_encoding_errors(self): + in_memory_file = io.BytesIO() + zf = zipfile.ZipFile(in_memory_file, "w") + zf.writestr("path/bad-utf8.bin", b"invalid utf-8: \xff\xff.") + zf.filename = "test_read_text_encoding_errors.zip" + root = zipfile.Path(zf) + (path,) = root.iterdir() + u16 = path.joinpath("bad-utf8.bin") + + # encoding= as a positional argument for gh-101144. + data = u16.read_text("utf-8", errors="ignore") + self.assertEqual(data, "invalid utf-8: .") + with u16.open("r", "utf-8", errors="surrogateescape") as f: + self.assertEqual(f.read(), "invalid utf-8: \udcff\udcff.") + + # encoding= both positional and keyword is an error; gh-101144. + with self.assertRaisesRegex(TypeError, "encoding"): + data = u16.read_text("utf-8", encoding="utf-8") + + # both keyword arguments work. + with u16.open("r", encoding="utf-8", errors="strict") as f: + # error during decoding with wrong codec. + with self.assertRaises(UnicodeDecodeError): + f.read() + + def test_encoding_warnings(self): + """EncodingWarning must blame the read_text and open calls.""" + code = '''\ +import io, zipfile +with zipfile.ZipFile(io.BytesIO(), "w") as zf: + zf.filename = '' + zf.writestr("path/file.txt", b"Spanish Inquisition") + root = zipfile.Path(zf) + (path,) = root.iterdir() + file_path = path.joinpath("file.txt") + unused = file_path.read_text() # should warn + file_path.open("r").close() # should warn +''' + proc = assert_python_ok('-X', 'warn_default_encoding', '-c', code) + warnings = proc.err.splitlines() + self.assertEqual(len(warnings), 2, proc.err) + self.assertRegex(warnings[0], rb"^:8: EncodingWarning:") + self.assertRegex(warnings[1], rb"^:9: EncodingWarning:") def test_open_write(self): """ @@ -187,6 +250,7 @@ def test_read(self, alpharep): root = zipfile.Path(alpharep) a, b, g = root.iterdir() assert a.read_text(encoding="utf-8") == "content of a" + a.read_text("utf-8") # No positional arg TypeError per gh-101144. assert a.read_bytes() == b"content of a" @pass_alpharep diff --git a/Lib/zipfile/_path.py b/Lib/zipfile/_path.py index aea17b65b6aa..7c7a6a0e2c0d 100644 --- a/Lib/zipfile/_path.py +++ b/Lib/zipfile/_path.py @@ -148,6 +148,11 @@ def _name_set(self): return self.__lookup +def _extract_text_encoding(encoding=None, *args, **kwargs): + # stacklevel=3 so that the caller of the caller see any warning. + return io.text_encoding(encoding, 3), args, kwargs + + class Path: """ A pathlib-compatible interface for zip files. @@ -257,9 +262,9 @@ def open(self, mode='r', *args, pwd=None, **kwargs): if args or kwargs: raise ValueError("encoding args invalid for binary operation") return stream - else: - kwargs["encoding"] = io.text_encoding(kwargs.get("encoding")) - return io.TextIOWrapper(stream, *args, **kwargs) + # Text mode: + encoding, args, kwargs = _extract_text_encoding(*args, **kwargs) + return io.TextIOWrapper(stream, encoding, *args, **kwargs) @property def name(self): @@ -282,8 +287,8 @@ def filename(self): return pathlib.Path(self.root.filename).joinpath(self.at) def read_text(self, *args, **kwargs): - kwargs["encoding"] = io.text_encoding(kwargs.get("encoding")) - with self.open('r', *args, **kwargs) as strm: + encoding, args, kwargs = _extract_text_encoding(*args, **kwargs) + with self.open('r', encoding, *args, **kwargs) as strm: return strm.read() def read_bytes(self): diff --git a/Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst b/Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst new file mode 100644 index 000000000000..297652259949 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst @@ -0,0 +1,4 @@ +Make :func:`zipfile.Path.open` and :func:`zipfile.Path.read_text` also accept +``encoding`` as a positional argument. This was the behavior in Python 3.9 and +earlier. 3.10 introduced a regression where supplying it as a positional +argument would lead to a :exc:`TypeError`. From webhook-mailer at python.org Fri Jan 20 02:56:25 2023 From: webhook-mailer at python.org (gpshead) Date: Fri, 20 Jan 2023 07:56:25 -0000 Subject: [Python-checkins] gh-88324: Clarify documentation for redirected stdout/stderr when using subprocess in Linux (#94035) Message-ID: https://github.com/python/cpython/commit/3fa8fe7177bb941aa60ecaf14d1fa1750a26f674 commit: 3fa8fe7177bb941aa60ecaf14d1fa1750a26f674 branch: main author: richardhob committer: gpshead date: 2023-01-19T23:56:13-08:00 summary: gh-88324: Clarify documentation for redirected stdout/stderr when using subprocess in Linux (#94035) * Update description of stdout, stderr, and stdin. Changes: - Move the ``None`` option (which is default) to the front of the list of input options - Move the ``None`` option description up to make the default behavior more clear (No redirection) - Remove mention of Child File Descriptors from ``None`` option description files: A Misc/NEWS.d/next/Documentation/2022-06-19-22-04-47.gh-issue-88324.GHhSQ1.rst M Doc/library/subprocess.rst M Misc/ACKS diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index e4e38e933681..785251afdf26 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -270,15 +270,14 @@ default values. The arguments that are most commonly needed are: *stdin*, *stdout* and *stderr* specify the executed program's standard input, standard output and standard error file handles, respectively. Valid values - are :data:`PIPE`, :data:`DEVNULL`, an existing file descriptor (a positive - integer), an existing file object with a valid file descriptor, and ``None``. - :data:`PIPE` indicates that a new pipe to the child should be created. - :data:`DEVNULL` indicates that the special file :data:`os.devnull` will - be used. With the default settings of ``None``, no redirection will occur; - the child's file handles will be inherited from the parent. - Additionally, *stderr* can be :data:`STDOUT`, which indicates that the - stderr data from the child process should be captured into the same file - handle as for *stdout*. + are ``None``, :data:`PIPE`, :data:`DEVNULL`, an existing file descriptor (a + positive integer), and an existing :term:`file object` with a valid file + descriptor. With the default settings of ``None``, no redirection will + occur. :data:`PIPE` indicates that a new pipe to the child should be + created. :data:`DEVNULL` indicates that the special file :data:`os.devnull` + will be used. Additionally, *stderr* can be :data:`STDOUT`, which indicates + that the stderr data from the child process should be captured into the same + file handle as for *stdout*. .. index:: single: universal newlines; subprocess module @@ -490,15 +489,14 @@ functions. *stdin*, *stdout* and *stderr* specify the executed program's standard input, standard output and standard error file handles, respectively. Valid values - are :data:`PIPE`, :data:`DEVNULL`, an existing file descriptor (a positive - integer), an existing :term:`file object` with a valid file descriptor, - and ``None``. :data:`PIPE` indicates that a new pipe to the child should - be created. :data:`DEVNULL` indicates that the special file - :data:`os.devnull` will be used. With the default settings of ``None``, - no redirection will occur; the child's file handles will be inherited from - the parent. Additionally, *stderr* can be :data:`STDOUT`, which indicates + are ``None``, :data:`PIPE`, :data:`DEVNULL`, an existing file descriptor (a + positive integer), and an existing :term:`file object` with a valid file + descriptor. With the default settings of ``None``, no redirection will + occur. :data:`PIPE` indicates that a new pipe to the child should be + created. :data:`DEVNULL` indicates that the special file :data:`os.devnull` + will be used. Additionally, *stderr* can be :data:`STDOUT`, which indicates that the stderr data from the applications should be captured into the same - file handle as for stdout. + file handle as for *stdout*. If *preexec_fn* is set to a callable object, this object will be called in the child process just before the child is executed. diff --git a/Misc/ACKS b/Misc/ACKS index a51658d79fa1..768034516d32 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -747,6 +747,7 @@ Aaron Hill Joel Hillacre Richie Hindle Konrad Hinsen +Richard Hoberecht David Hobley Tim Hochberg Benjamin Hodgson diff --git a/Misc/NEWS.d/next/Documentation/2022-06-19-22-04-47.gh-issue-88324.GHhSQ1.rst b/Misc/NEWS.d/next/Documentation/2022-06-19-22-04-47.gh-issue-88324.GHhSQ1.rst new file mode 100644 index 000000000000..6c8d192daa79 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2022-06-19-22-04-47.gh-issue-88324.GHhSQ1.rst @@ -0,0 +1,3 @@ +Reword :mod:`subprocess` to emphasize default behavior of *stdin*, *stdout*, +and *stderr* arguments. Remove inaccurate statement about child file handle +inheritance. From webhook-mailer at python.org Fri Jan 20 03:06:25 2023 From: webhook-mailer at python.org (gpshead) Date: Fri, 20 Jan 2023 08:06:25 -0000 Subject: [Python-checkins] [3.11] gh-101144: Allow zipfile.Path .open & .read_text encoding to be positional (#101179) Message-ID: https://github.com/python/cpython/commit/efe3a389cabd7295e6e0938767cdc4055c871e3c commit: efe3a389cabd7295e6e0938767cdc4055c871e3c branch: 3.11 author: Gregory P. Smith committer: gpshead date: 2023-01-20T00:06:18-08:00 summary: [3.11] gh-101144: Allow zipfile.Path .open & .read_text encoding to be positional (#101179) The zipfile.Path open() and read_text() encoding parameter can be supplied as a positional argument without causing a TypeError again. 3.10.0b1 included a regression that made it keyword only. Documentation update included as users writing code to be compatible with a wide range of versions will need to consider this for some time.. (cherry picked from commit 5927013e47a8c63b70e104152351f3447baa819c) Co-authored-by: Gregory P. Smith [Google] files: A Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst M Doc/library/zipfile.rst M Lib/test/test_zipfile.py M Lib/zipfile.py diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst index d02e32d320bc..af1f26385553 100644 --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -551,6 +551,12 @@ Path objects are traversable using the ``/`` operator or ``joinpath``. Added support for text and binary modes for open. Default mode is now text. + .. versionchanged:: 3.11.2 + The ``encoding`` parameter can be supplied as a positional argument + without causing a :exc:`TypeError`. As it could in 3.9. Code needing to + be compatible with unpatched 3.10 and 3.11 versions must pass all + :class:`io.TextIOWrapper` arguments, ``encoding`` included, as keywords. + .. method:: Path.iterdir() Enumerate the children of the current directory. @@ -596,6 +602,12 @@ Path objects are traversable using the ``/`` operator or ``joinpath``. :class:`io.TextIOWrapper` (except ``buffer``, which is implied by the context). + .. versionchanged:: 3.11.2 + The ``encoding`` parameter can be supplied as a positional argument + without causing a :exc:`TypeError`. As it could in 3.9. Code needing to + be compatible with unpatched 3.10 and 3.11 versions must pass all + :class:`io.TextIOWrapper` arguments, ``encoding`` included, as keywords. + .. method:: Path.read_bytes() Read the current file as bytes. diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index fd25e5a800d7..35d78d96a68e 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -10,6 +10,7 @@ import struct import subprocess import sys +from test.support.script_helper import assert_python_ok import time import unittest import unittest.mock as mock @@ -3005,7 +3006,69 @@ def test_open(self, alpharep): a, b, g = root.iterdir() with a.open(encoding="utf-8") as strm: data = strm.read() - assert data == "content of a" + self.assertEqual(data, "content of a") + with a.open('r', "utf-8") as strm: # not a kw, no gh-101144 TypeError + data = strm.read() + self.assertEqual(data, "content of a") + + def test_open_encoding_utf16(self): + in_memory_file = io.BytesIO() + zf = zipfile.ZipFile(in_memory_file, "w") + zf.writestr("path/16.txt", "This was utf-16".encode("utf-16")) + zf.filename = "test_open_utf16.zip" + root = zipfile.Path(zf) + (path,) = root.iterdir() + u16 = path.joinpath("16.txt") + with u16.open('r', "utf-16") as strm: + data = strm.read() + self.assertEqual(data, "This was utf-16") + with u16.open(encoding="utf-16") as strm: + data = strm.read() + self.assertEqual(data, "This was utf-16") + + def test_open_encoding_errors(self): + in_memory_file = io.BytesIO() + zf = zipfile.ZipFile(in_memory_file, "w") + zf.writestr("path/bad-utf8.bin", b"invalid utf-8: \xff\xff.") + zf.filename = "test_read_text_encoding_errors.zip" + root = zipfile.Path(zf) + (path,) = root.iterdir() + u16 = path.joinpath("bad-utf8.bin") + + # encoding= as a positional argument for gh-101144. + data = u16.read_text("utf-8", errors="ignore") + self.assertEqual(data, "invalid utf-8: .") + with u16.open("r", "utf-8", errors="surrogateescape") as f: + self.assertEqual(f.read(), "invalid utf-8: \udcff\udcff.") + + # encoding= both positional and keyword is an error; gh-101144. + with self.assertRaisesRegex(TypeError, "encoding"): + data = u16.read_text("utf-8", encoding="utf-8") + + # both keyword arguments work. + with u16.open("r", encoding="utf-8", errors="strict") as f: + # error during decoding with wrong codec. + with self.assertRaises(UnicodeDecodeError): + f.read() + + def test_encoding_warnings(self): + """EncodingWarning must blame the read_text and open calls.""" + code = '''\ +import io, zipfile +with zipfile.ZipFile(io.BytesIO(), "w") as zf: + zf.filename = '' + zf.writestr("path/file.txt", b"Spanish Inquisition") + root = zipfile.Path(zf) + (path,) = root.iterdir() + file_path = path.joinpath("file.txt") + unused = file_path.read_text() # should warn + file_path.open("r").close() # should warn +''' + proc = assert_python_ok('-X', 'warn_default_encoding', '-c', code) + warnings = proc.err.splitlines() + self.assertEqual(len(warnings), 2, proc.err) + self.assertRegex(warnings[0], rb"^:8: EncodingWarning:") + self.assertRegex(warnings[1], rb"^:9: EncodingWarning:") def test_open_write(self): """ @@ -3047,6 +3110,7 @@ def test_read(self, alpharep): root = zipfile.Path(alpharep) a, b, g = root.iterdir() assert a.read_text(encoding="utf-8") == "content of a" + a.read_text("utf-8") # No positional arg TypeError per gh-101144. assert a.read_bytes() == b"content of a" @pass_alpharep diff --git a/Lib/zipfile.py b/Lib/zipfile.py index de667a7efeeb..f69a43b764ef 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -2287,6 +2287,11 @@ def _name_set(self): return self.__lookup +def _extract_text_encoding(encoding=None, *args, **kwargs): + # stacklevel=3 so that the caller of the caller see any warning. + return io.text_encoding(encoding, 3), args, kwargs + + class Path: """ A pathlib-compatible interface for zip files. @@ -2396,9 +2401,9 @@ def open(self, mode='r', *args, pwd=None, **kwargs): if args or kwargs: raise ValueError("encoding args invalid for binary operation") return stream - else: - kwargs["encoding"] = io.text_encoding(kwargs.get("encoding")) - return io.TextIOWrapper(stream, *args, **kwargs) + # Text mode: + encoding, args, kwargs = _extract_text_encoding(*args, **kwargs) + return io.TextIOWrapper(stream, encoding, *args, **kwargs) @property def name(self): @@ -2421,8 +2426,8 @@ def filename(self): return pathlib.Path(self.root.filename).joinpath(self.at) def read_text(self, *args, **kwargs): - kwargs["encoding"] = io.text_encoding(kwargs.get("encoding")) - with self.open('r', *args, **kwargs) as strm: + encoding, args, kwargs = _extract_text_encoding(*args, **kwargs) + with self.open('r', encoding, *args, **kwargs) as strm: return strm.read() def read_bytes(self): diff --git a/Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst b/Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst new file mode 100644 index 000000000000..297652259949 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst @@ -0,0 +1,4 @@ +Make :func:`zipfile.Path.open` and :func:`zipfile.Path.read_text` also accept +``encoding`` as a positional argument. This was the behavior in Python 3.9 and +earlier. 3.10 introduced a regression where supplying it as a positional +argument would lead to a :exc:`TypeError`. From webhook-mailer at python.org Fri Jan 20 03:31:46 2023 From: webhook-mailer at python.org (miss-islington) Date: Fri, 20 Jan 2023 08:31:46 -0000 Subject: [Python-checkins] [3.10] gh-101144: Allow zipfile.Path .open & .read_text encoding to be positional (GH-101179) (GH-101182) Message-ID: https://github.com/python/cpython/commit/b2a662fc6b391f176bbf9da45657d21ef5b363ac commit: b2a662fc6b391f176bbf9da45657d21ef5b363ac branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-20T00:31:15-08:00 summary: [3.10] gh-101144: Allow zipfile.Path .open & .read_text encoding to be positional (GH-101179) (GH-101182) The zipfile.Path open() and read_text() encoding parameter can be supplied as a positional argument without causing a TypeError again. 3.10.0b1 included a regression that made it keyword only. Documentation update included as users writing code to be compatible with a wide range of versions will need to consider this for some time.. (cherry picked from commit 5927013e47a8c63b70e104152351f3447baa819c) (cherry picked from commit efe3a389cabd7295e6e0938767cdc4055c871e3c) Co-authored-by: Gregory P. Smith [Google] Automerge-Triggered-By: GH:gpshead files: A Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst M Doc/library/zipfile.rst M Lib/test/test_zipfile.py M Lib/zipfile.py diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst index 10fffb70dbdb..eab1eaf4312d 100644 --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -509,6 +509,12 @@ Path objects are traversable using the ``/`` operator or ``joinpath``. Added support for text and binary modes for open. Default mode is now text. + .. versionchanged:: 3.10.10 + The ``encoding`` parameter can be supplied as a positional argument + without causing a :exc:`TypeError`. As it could in 3.9. Code needing to + be compatible with unpatched 3.10 and 3.11 versions must pass all + :class:`io.TextIOWrapper` arguments, ``encoding`` included, as keywords. + .. method:: Path.iterdir() Enumerate the children of the current directory. @@ -533,6 +539,12 @@ Path objects are traversable using the ``/`` operator or ``joinpath``. :class:`io.TextIOWrapper` (except ``buffer``, which is implied by the context). + .. versionchanged:: 3.10.10 + The ``encoding`` parameter can be supplied as a positional argument + without causing a :exc:`TypeError`. As it could in 3.9. Code needing to + be compatible with unpatched 3.10 and 3.11 versions must pass all + :class:`io.TextIOWrapper` arguments, ``encoding`` included, as keywords. + .. method:: Path.read_bytes() Read the current file as bytes. diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index e557d569a119..cf40412f8526 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -10,6 +10,7 @@ import struct import subprocess import sys +from test.support.script_helper import assert_python_ok import time import unittest import unittest.mock as mock @@ -2933,7 +2934,69 @@ def test_open(self, alpharep): a, b, g = root.iterdir() with a.open(encoding="utf-8") as strm: data = strm.read() - assert data == "content of a" + self.assertEqual(data, "content of a") + with a.open('r', "utf-8") as strm: # not a kw, no gh-101144 TypeError + data = strm.read() + self.assertEqual(data, "content of a") + + def test_open_encoding_utf16(self): + in_memory_file = io.BytesIO() + zf = zipfile.ZipFile(in_memory_file, "w") + zf.writestr("path/16.txt", "This was utf-16".encode("utf-16")) + zf.filename = "test_open_utf16.zip" + root = zipfile.Path(zf) + (path,) = root.iterdir() + u16 = path.joinpath("16.txt") + with u16.open('r', "utf-16") as strm: + data = strm.read() + self.assertEqual(data, "This was utf-16") + with u16.open(encoding="utf-16") as strm: + data = strm.read() + self.assertEqual(data, "This was utf-16") + + def test_open_encoding_errors(self): + in_memory_file = io.BytesIO() + zf = zipfile.ZipFile(in_memory_file, "w") + zf.writestr("path/bad-utf8.bin", b"invalid utf-8: \xff\xff.") + zf.filename = "test_read_text_encoding_errors.zip" + root = zipfile.Path(zf) + (path,) = root.iterdir() + u16 = path.joinpath("bad-utf8.bin") + + # encoding= as a positional argument for gh-101144. + data = u16.read_text("utf-8", errors="ignore") + self.assertEqual(data, "invalid utf-8: .") + with u16.open("r", "utf-8", errors="surrogateescape") as f: + self.assertEqual(f.read(), "invalid utf-8: \udcff\udcff.") + + # encoding= both positional and keyword is an error; gh-101144. + with self.assertRaisesRegex(TypeError, "encoding"): + data = u16.read_text("utf-8", encoding="utf-8") + + # both keyword arguments work. + with u16.open("r", encoding="utf-8", errors="strict") as f: + # error during decoding with wrong codec. + with self.assertRaises(UnicodeDecodeError): + f.read() + + def test_encoding_warnings(self): + """EncodingWarning must blame the read_text and open calls.""" + code = '''\ +import io, zipfile +with zipfile.ZipFile(io.BytesIO(), "w") as zf: + zf.filename = '' + zf.writestr("path/file.txt", b"Spanish Inquisition") + root = zipfile.Path(zf) + (path,) = root.iterdir() + file_path = path.joinpath("file.txt") + unused = file_path.read_text() # should warn + file_path.open("r").close() # should warn +''' + proc = assert_python_ok('-X', 'warn_default_encoding', '-c', code) + warnings = proc.err.splitlines() + self.assertEqual(len(warnings), 2, proc.err) + self.assertRegex(warnings[0], rb"^:8: EncodingWarning:") + self.assertRegex(warnings[1], rb"^:9: EncodingWarning:") def test_open_write(self): """ @@ -2975,6 +3038,7 @@ def test_read(self, alpharep): root = zipfile.Path(alpharep) a, b, g = root.iterdir() assert a.read_text(encoding="utf-8") == "content of a" + a.read_text("utf-8") # No positional arg TypeError per gh-101144. assert a.read_bytes() == b"content of a" @pass_alpharep diff --git a/Lib/zipfile.py b/Lib/zipfile.py index eee1f47ed097..dc4eb38e3a90 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -2236,6 +2236,11 @@ def _name_set(self): return self.__lookup +def _extract_text_encoding(encoding=None, *args, **kwargs): + # stacklevel=3 so that the caller of the caller see any warning. + return io.text_encoding(encoding, 3), args, kwargs + + class Path: """ A pathlib-compatible interface for zip files. @@ -2345,9 +2350,9 @@ def open(self, mode='r', *args, pwd=None, **kwargs): if args or kwargs: raise ValueError("encoding args invalid for binary operation") return stream - else: - kwargs["encoding"] = io.text_encoding(kwargs.get("encoding")) - return io.TextIOWrapper(stream, *args, **kwargs) + # Text mode: + encoding, args, kwargs = _extract_text_encoding(*args, **kwargs) + return io.TextIOWrapper(stream, encoding, *args, **kwargs) @property def name(self): @@ -2358,8 +2363,8 @@ def filename(self): return pathlib.Path(self.root.filename).joinpath(self.at) def read_text(self, *args, **kwargs): - kwargs["encoding"] = io.text_encoding(kwargs.get("encoding")) - with self.open('r', *args, **kwargs) as strm: + encoding, args, kwargs = _extract_text_encoding(*args, **kwargs) + with self.open('r', encoding, *args, **kwargs) as strm: return strm.read() def read_bytes(self): diff --git a/Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst b/Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst new file mode 100644 index 000000000000..2454ea83ef00 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-18-17-58-50.gh-issue-101144.FHd8Un.rst @@ -0,0 +1,4 @@ +Make :func:`zipfile.Path.open` and :func:`zipfile.Path.read_text` also accept +``encoding`` as a positional argument. This was the behavior in Python 3.9 and +earlier. Earlier 3.10 versions had a regression where supplying it as a positional +argument would lead to a :exc:`TypeError`. From webhook-mailer at python.org Fri Jan 20 03:55:09 2023 From: webhook-mailer at python.org (vsajip) Date: Fri, 20 Jan 2023 08:55:09 -0000 Subject: [Python-checkins] Provided better example for logging cookbook (GH-101164) Message-ID: https://github.com/python/cpython/commit/8cdbc4609022c9c8dfb66da328a149c2bea75f89 commit: 8cdbc4609022c9c8dfb66da328a149c2bea75f89 branch: main author: Vladimir Malinovskii committer: vsajip date: 2023-01-20T08:54:48Z summary: Provided better example for logging cookbook (GH-101164) Co-authored-by: Vinay Sajip files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 8aec8e5e5ee0..2ba5b5c13681 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -1982,26 +1982,47 @@ Using a rotator and namer to customize log rotation processing -------------------------------------------------------------- An example of how you can define a namer and rotator is given in the following -snippet, which shows zlib-based compression of the log file:: +runnable script, which shows gzip compression of the log file:: + + import gzip + import logging + import logging.handlers + import os + import shutil def namer(name): return name + ".gz" def rotator(source, dest): - with open(source, "rb") as sf: - data = sf.read() - compressed = zlib.compress(data, 9) - with open(dest, "wb") as df: - df.write(compressed) + with open(source, 'rb') as f_in: + with gzip.open(dest, 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) os.remove(source) - rh = logging.handlers.RotatingFileHandler(...) + + rh = logging.handlers.RotatingFileHandler('rotated.log', maxBytes=128, backupCount=5) rh.rotator = rotator rh.namer = namer -These are not "true" .gz files, as they are bare compressed data, with no -"container" such as you?d find in an actual gzip file. This snippet is just -for illustration purposes. + root = logging.getLogger() + root.setLevel(logging.INFO) + root.addHandler(rh) + f = logging.Formatter('%(asctime)s %(message)s') + rh.setFormatter(f) + for i in range(1000): + root.info(f'Message no. {i + 1}') + +After running this, you will see six new files, five of which are compressed: + +.. code-block:: shell-session + + $ ls rotated.log* + rotated.log rotated.log.2.gz rotated.log.4.gz + rotated.log.1.gz rotated.log.3.gz rotated.log.5.gz + $ zcat rotated.log.1.gz + 2023-01-20 02:28:17,767 Message no. 996 + 2023-01-20 02:28:17,767 Message no. 997 + 2023-01-20 02:28:17,767 Message no. 998 A more elaborate multiprocessing example ---------------------------------------- From webhook-mailer at python.org Fri Jan 20 06:05:04 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Fri, 20 Jan 2023 11:05:04 -0000 Subject: [Python-checkins] GH-90699: Clear interned strings in `_elementtree` (#101185) Message-ID: https://github.com/python/cpython/commit/9109d460511a317f5598a26658ba495e77ea8686 commit: 9109d460511a317f5598a26658ba495e77ea8686 branch: main author: Erlend E. Aasland committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-20T16:34:14+05:30 summary: GH-90699: Clear interned strings in `_elementtree` (#101185) files: M Modules/_elementtree.c diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 555c22f88b36..e0ab79103f06 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -122,6 +122,16 @@ elementtree_clear(PyObject *m) Py_CLEAR(st->elementpath_obj); Py_CLEAR(st->comment_factory); Py_CLEAR(st->pi_factory); + + // Interned strings + Py_CLEAR(st->str_append); + Py_CLEAR(st->str_find); + Py_CLEAR(st->str_findall); + Py_CLEAR(st->str_findtext); + Py_CLEAR(st->str_iterfind); + Py_CLEAR(st->str_tail); + Py_CLEAR(st->str_text); + Py_CLEAR(st->str_doctype); return 0; } From webhook-mailer at python.org Fri Jan 20 06:40:17 2023 From: webhook-mailer at python.org (erlend-aasland) Date: Fri, 20 Jan 2023 11:40:17 -0000 Subject: [Python-checkins] gh-92123: Convert `_elementtree` types to heap types (#99221) Message-ID: https://github.com/python/cpython/commit/3847a6c64b96bb2cb93be394a590d4df2c35e876 commit: 3847a6c64b96bb2cb93be394a590d4df2c35e876 branch: main author: Erlend E. Aasland committer: erlend-aasland date: 2023-01-20T12:40:06+01:00 summary: gh-92123: Convert `_elementtree` types to heap types (#99221) files: A Misc/NEWS.d/next/Library/2020-11-20-21-06-08.bpo-40077.M-iZq3.rst M Lib/test/test_xml_etree_c.py M Modules/_elementtree.c M Modules/clinic/_elementtree.c.h diff --git a/Lib/test/test_xml_etree_c.py b/Lib/test/test_xml_etree_c.py index bec820857190..fd27b575ec8d 100644 --- a/Lib/test/test_xml_etree_c.py +++ b/Lib/test/test_xml_etree_c.py @@ -181,6 +181,26 @@ def __hash__(self): r = e.get(X()) self.assertIsNone(r) + @support.cpython_only + def test_immutable_types(self): + root = cET.fromstring('') + dataset = ( + cET.Element, + cET.TreeBuilder, + cET.XMLParser, + type(root.iter()), + ) + for tp in dataset: + with self.subTest(tp=tp): + with self.assertRaisesRegex(TypeError, "immutable"): + tp.foo = 1 + + @support.cpython_only + def test_disallow_instantiation(self): + root = cET.fromstring('') + iter_type = type(root.iter()) + support.check_disallow_instantiation(self, iter_type) + @unittest.skipUnless(cET, 'requires _elementtree') class TestAliasWorking(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2020-11-20-21-06-08.bpo-40077.M-iZq3.rst b/Misc/NEWS.d/next/Library/2020-11-20-21-06-08.bpo-40077.M-iZq3.rst new file mode 100644 index 000000000000..8a74477a4b35 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-11-20-21-06-08.bpo-40077.M-iZq3.rst @@ -0,0 +1 @@ +Convert :mod:`elementtree` types to heap types. Patch by Erlend E. Aasland. diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index e0ab79103f06..93a9d609d7a0 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -70,10 +70,10 @@ static void _clear_joined_ptr(PyObject **p) } /* Types defined by this extension */ -static PyTypeObject Element_Type; -static PyTypeObject ElementIter_Type; -static PyTypeObject TreeBuilder_Type; -static PyTypeObject XMLParser_Type; +static PyTypeObject *Element_Type; +static PyTypeObject *ElementIter_Type; +static PyTypeObject *TreeBuilder_Type; +static PyTypeObject *XMLParser_Type; /* Per-module state; PEP 3121 */ @@ -223,8 +223,8 @@ typedef struct { } ElementObject; -#define Element_CheckExact(op) Py_IS_TYPE(op, &Element_Type) -#define Element_Check(op) PyObject_TypeCheck(op, &Element_Type) +#define Element_CheckExact(op) Py_IS_TYPE(op, Element_Type) +#define Element_Check(op) PyObject_TypeCheck(op, Element_Type) /* -------------------------------------------------------------------- */ @@ -291,7 +291,7 @@ create_new_element(PyObject* tag, PyObject* attrib) { ElementObject* self; - self = PyObject_GC_New(ElementObject, &Element_Type); + self = PyObject_GC_New(ElementObject, Element_Type); if (self == NULL) return NULL; self->extra = NULL; @@ -373,11 +373,11 @@ get_attrib_from_keywords(PyObject *kwds) /*[clinic input] module _elementtree -class _elementtree.Element "ElementObject *" "&Element_Type" -class _elementtree.TreeBuilder "TreeBuilderObject *" "&TreeBuilder_Type" -class _elementtree.XMLParser "XMLParserObject *" "&XMLParser_Type" +class _elementtree.Element "ElementObject *" "Element_Type" +class _elementtree.TreeBuilder "TreeBuilderObject *" "TreeBuilder_Type" +class _elementtree.XMLParser "XMLParserObject *" "XMLParser_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=159aa50a54061c22]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=1ecdb32b55d9d5de]*/ static int element_init(PyObject *self, PyObject *args, PyObject *kwds) @@ -579,7 +579,7 @@ subelement(PyObject *self, PyObject *args, PyObject *kwds) PyObject* tag; PyObject* attrib = NULL; if (!PyArg_ParseTuple(args, "O!O|O!:SubElement", - &Element_Type, &parent, &tag, + Element_Type, &parent, &tag, &PyDict_Type, &attrib)) { return NULL; } @@ -618,6 +618,7 @@ subelement(PyObject *self, PyObject *args, PyObject *kwds) static int element_gc_traverse(ElementObject *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->tag); Py_VISIT(JOIN_OBJ(self->text)); Py_VISIT(JOIN_OBJ(self->tail)); @@ -649,6 +650,8 @@ element_gc_clear(ElementObject *self) static void element_dealloc(ElementObject* self) { + PyTypeObject *tp = Py_TYPE(self); + /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(self); Py_TRASHCAN_BEGIN(self, element_dealloc) @@ -660,7 +663,8 @@ element_dealloc(ElementObject* self) */ element_gc_clear(self); - Py_TYPE(self)->tp_free((PyObject *)self); + tp->tp_free((PyObject *)self); + Py_DECREF(tp); Py_TRASHCAN_END } @@ -669,14 +673,14 @@ element_dealloc(ElementObject* self) /*[clinic input] _elementtree.Element.append - subelement: object(subclass_of='&Element_Type') + subelement: object(subclass_of='Element_Type') / [clinic start generated code]*/ static PyObject * _elementtree_Element_append_impl(ElementObject *self, PyObject *subelement) -/*[clinic end generated code: output=54a884b7cf2295f4 input=3ed648beb5bfa22a]*/ +/*[clinic end generated code: output=54a884b7cf2295f4 input=59866b732e6e2891]*/ { if (element_add_subelement(self, subelement) < 0) return NULL; @@ -1436,7 +1440,7 @@ element_getitem(PyObject* self_, Py_ssize_t index) _elementtree.Element.insert index: Py_ssize_t - subelement: object(subclass_of='&Element_Type') + subelement: object(subclass_of='Element_Type') / [clinic start generated code]*/ @@ -1444,7 +1448,7 @@ _elementtree.Element.insert static PyObject * _elementtree_Element_insert_impl(ElementObject *self, Py_ssize_t index, PyObject *subelement) -/*[clinic end generated code: output=990adfef4d424c0b input=cd6fbfcdab52d7a8]*/ +/*[clinic end generated code: output=990adfef4d424c0b input=4382c42ab2659f9b]*/ { Py_ssize_t i; @@ -1543,14 +1547,14 @@ _elementtree_Element_makeelement_impl(ElementObject *self, PyObject *tag, /*[clinic input] _elementtree.Element.remove - subelement: object(subclass_of='&Element_Type') + subelement: object(subclass_of='Element_Type') / [clinic start generated code]*/ static PyObject * _elementtree_Element_remove_impl(ElementObject *self, PyObject *subelement) -/*[clinic end generated code: output=38fe6c07d6d87d1f input=d52fc28ededc0bd8]*/ +/*[clinic end generated code: output=38fe6c07d6d87d1f input=cbdf9f2ab34d93b0]*/ { Py_ssize_t i; int rc; @@ -2001,16 +2005,6 @@ element_attrib_setter(ElementObject *self, PyObject *value, void *closure) return 0; } -static PySequenceMethods element_as_sequence = { - (lenfunc) element_length, - 0, /* sq_concat */ - 0, /* sq_repeat */ - element_getitem, - 0, - element_setitem, - 0, -}; - /******************************* Element iterator ****************************/ /* ElementIterObject represents the iteration state over an XML element in @@ -2040,6 +2034,7 @@ typedef struct { static void elementiter_dealloc(ElementIterObject *it) { + PyTypeObject *tp = Py_TYPE(it); Py_ssize_t i = it->parent_stack_used; it->parent_stack_used = 0; /* bpo-31095: UnTrack is needed before calling any callbacks */ @@ -2051,7 +2046,8 @@ elementiter_dealloc(ElementIterObject *it) Py_XDECREF(it->sought_tag); Py_XDECREF(it->root_element); - PyObject_GC_Del(it); + tp->tp_free(it); + Py_DECREF(tp); } static int @@ -2063,6 +2059,7 @@ elementiter_traverse(ElementIterObject *it, visitproc visit, void *arg) Py_VISIT(it->root_element); Py_VISIT(it->sought_tag); + Py_VISIT(Py_TYPE(it)); return 0; } @@ -2193,49 +2190,22 @@ elementiter_next(ElementIterObject *it) return NULL; } +static PyType_Slot elementiter_slots[] = { + {Py_tp_dealloc, elementiter_dealloc}, + {Py_tp_traverse, elementiter_traverse}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, elementiter_next}, + {0, NULL}, +}; -static PyTypeObject ElementIter_Type = { - PyVarObject_HEAD_INIT(NULL, 0) +static PyType_Spec elementiter_spec = { /* Using the module's name since the pure-Python implementation does not have such a type. */ - "_elementtree._element_iterator", /* tp_name */ - sizeof(ElementIterObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)elementiter_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)elementiter_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)elementiter_next, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ + .name = "_elementtree._element_iterator", + .basicsize = sizeof(ElementIterObject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION), + .slots = elementiter_slots, }; #define INIT_PARENT_STACK_SIZE 8 @@ -2245,7 +2215,7 @@ create_elementiter(ElementObject *self, PyObject *tag, int gettext) { ElementIterObject *it; - it = PyObject_GC_New(ElementIterObject, &ElementIter_Type); + it = PyObject_GC_New(ElementIterObject, ElementIter_Type); if (!it) return NULL; @@ -2302,7 +2272,7 @@ typedef struct { char insert_pis; } TreeBuilderObject; -#define TreeBuilder_CheckExact(op) Py_IS_TYPE((op), &TreeBuilder_Type) +#define TreeBuilder_CheckExact(op) Py_IS_TYPE((op), TreeBuilder_Type) /* -------------------------------------------------------------------- */ /* constructor and destructor */ @@ -2393,6 +2363,7 @@ _elementtree_TreeBuilder___init___impl(TreeBuilderObject *self, static int treebuilder_gc_traverse(TreeBuilderObject *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->pi_event_obj); Py_VISIT(self->comment_event_obj); Py_VISIT(self->end_ns_event_obj); @@ -2437,9 +2408,11 @@ treebuilder_gc_clear(TreeBuilderObject *self) static void treebuilder_dealloc(TreeBuilderObject *self) { + PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); treebuilder_gc_clear(self); - Py_TYPE(self)->tp_free((PyObject *)self); + tp->tp_free(self); + Py_DECREF(tp); } /* -------------------------------------------------------------------- */ @@ -3593,7 +3566,7 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *target, if (target != Py_None) { Py_INCREF(target); } else { - target = treebuilder_new(&TreeBuilder_Type, NULL, NULL); + target = treebuilder_new(TreeBuilder_Type, NULL, NULL); if (!target) { Py_CLEAR(self->entity); Py_CLEAR(self->names); @@ -3685,6 +3658,7 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *target, static int xmlparser_gc_traverse(XMLParserObject *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->handle_close); Py_VISIT(self->handle_pi); Py_VISIT(self->handle_comment); @@ -3731,9 +3705,11 @@ xmlparser_gc_clear(XMLParserObject *self) static void xmlparser_dealloc(XMLParserObject* self) { + PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); xmlparser_gc_clear(self); - Py_TYPE(self)->tp_free((PyObject *)self); + tp->tp_free(self); + Py_DECREF(tp); } Py_LOCAL_INLINE(int) @@ -4106,10 +4082,9 @@ static PyMethodDef element_methods[] = { {NULL, NULL} }; -static PyMappingMethods element_as_mapping = { - (lenfunc) element_length, - (binaryfunc) element_subscr, - (objobjargproc) element_ass_subscr, +static struct PyMemberDef element_members[] = { + {"__weaklistoffset__", T_PYSSIZET, offsetof(ElementObject, weakreflist), READONLY}, + {NULL}, }; static PyGetSetDef element_getsetlist[] = { @@ -4132,46 +4107,33 @@ static PyGetSetDef element_getsetlist[] = { {NULL}, }; -static PyTypeObject Element_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "xml.etree.ElementTree.Element", sizeof(ElementObject), 0, - /* methods */ - (destructor)element_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)element_repr, /* tp_repr */ - 0, /* tp_as_number */ - &element_as_sequence, /* tp_as_sequence */ - &element_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, - /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)element_gc_traverse, /* tp_traverse */ - (inquiry)element_gc_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(ElementObject, weakreflist), /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - element_methods, /* tp_methods */ - 0, /* tp_members */ - element_getsetlist, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)element_init, /* tp_init */ - PyType_GenericAlloc, /* tp_alloc */ - element_new, /* tp_new */ - 0, /* tp_free */ +static PyType_Slot element_slots[] = { + {Py_tp_dealloc, element_dealloc}, + {Py_tp_repr, element_repr}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_traverse, element_gc_traverse}, + {Py_tp_clear, element_gc_clear}, + {Py_tp_methods, element_methods}, + {Py_tp_members, element_members}, + {Py_tp_getset, element_getsetlist}, + {Py_tp_init, element_init}, + {Py_tp_alloc, PyType_GenericAlloc}, + {Py_tp_new, element_new}, + {Py_sq_length, element_length}, + {Py_sq_item, element_getitem}, + {Py_sq_ass_item, element_setitem}, + {Py_mp_length, element_length}, + {Py_mp_subscript, element_subscr}, + {Py_mp_ass_subscript, element_ass_subscr}, + {0, NULL}, +}; + +static PyType_Spec element_spec = { + .name = "xml.etree.ElementTree.Element", + .basicsize = sizeof(ElementObject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = element_slots, }; static PyMethodDef treebuilder_methods[] = { @@ -4184,46 +4146,22 @@ static PyMethodDef treebuilder_methods[] = { {NULL, NULL} }; -static PyTypeObject TreeBuilder_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "xml.etree.ElementTree.TreeBuilder", sizeof(TreeBuilderObject), 0, - /* methods */ - (destructor)treebuilder_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, - /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)treebuilder_gc_traverse, /* tp_traverse */ - (inquiry)treebuilder_gc_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - treebuilder_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - _elementtree_TreeBuilder___init__, /* tp_init */ - PyType_GenericAlloc, /* tp_alloc */ - treebuilder_new, /* tp_new */ - 0, /* tp_free */ +static PyType_Slot treebuilder_slots[] = { + {Py_tp_dealloc, treebuilder_dealloc}, + {Py_tp_traverse, treebuilder_gc_traverse}, + {Py_tp_clear, treebuilder_gc_clear}, + {Py_tp_methods, treebuilder_methods}, + {Py_tp_init, _elementtree_TreeBuilder___init__}, + {Py_tp_alloc, PyType_GenericAlloc}, + {Py_tp_new, treebuilder_new}, + {0, NULL}, +}; + +static PyType_Spec treebuilder_spec = { + .name = "xml.etree.ElementTree.TreeBuilder", + .basicsize = sizeof(TreeBuilderObject), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE, + .slots = treebuilder_slots, }; static PyMethodDef xmlparser_methods[] = { @@ -4234,46 +4172,25 @@ static PyMethodDef xmlparser_methods[] = { {NULL, NULL} }; -static PyTypeObject XMLParser_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "xml.etree.ElementTree.XMLParser", sizeof(XMLParserObject), 0, - /* methods */ - (destructor)xmlparser_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, - /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)xmlparser_gc_traverse, /* tp_traverse */ - (inquiry)xmlparser_gc_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - xmlparser_methods, /* tp_methods */ - xmlparser_members, /* tp_members */ - xmlparser_getsetlist, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - _elementtree_XMLParser___init__, /* tp_init */ - PyType_GenericAlloc, /* tp_alloc */ - xmlparser_new, /* tp_new */ - 0, /* tp_free */ +static PyType_Slot xmlparser_slots[] = { + {Py_tp_dealloc, xmlparser_dealloc}, + {Py_tp_traverse, xmlparser_gc_traverse}, + {Py_tp_clear, xmlparser_gc_clear}, + {Py_tp_methods, xmlparser_methods}, + {Py_tp_members, xmlparser_members}, + {Py_tp_getset, xmlparser_getsetlist}, + {Py_tp_init, _elementtree_XMLParser___init__}, + {Py_tp_alloc, PyType_GenericAlloc}, + {Py_tp_new, xmlparser_new}, + {0, NULL}, +}; + +static PyType_Spec xmlparser_spec = { + .name = "xml.etree.ElementTree.XMLParser", + .basicsize = sizeof(XMLParserObject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = xmlparser_slots, }; /* ==================================================================== */ @@ -4298,40 +4215,47 @@ static struct PyModuleDef elementtreemodule = { elementtree_free }; +#define CREATE_TYPE(module, type, spec) \ +do { \ + if (type != NULL) { \ + break; \ + } \ + type = (PyTypeObject *)PyType_FromModuleAndSpec(module, spec, NULL); \ + if (type == NULL) { \ + goto error; \ + } \ +} while (0) + PyMODINIT_FUNC PyInit__elementtree(void) { - PyObject *m; - elementtreestate *st; + PyObject *m = NULL; + elementtreestate *st = NULL; m = PyState_FindModule(&elementtreemodule); if (m) { return Py_NewRef(m); } - /* Initialize object types */ - if (PyType_Ready(&ElementIter_Type) < 0) - return NULL; - if (PyType_Ready(&TreeBuilder_Type) < 0) - return NULL; - if (PyType_Ready(&Element_Type) < 0) - return NULL; - if (PyType_Ready(&XMLParser_Type) < 0) - return NULL; - m = PyModule_Create(&elementtreemodule); if (!m) - return NULL; + goto error; st = get_elementtree_state(m); + /* Initialize object types */ + CREATE_TYPE(m, ElementIter_Type, &elementiter_spec); + CREATE_TYPE(m, TreeBuilder_Type, &treebuilder_spec); + CREATE_TYPE(m, Element_Type, &element_spec); + CREATE_TYPE(m, XMLParser_Type, &xmlparser_spec); + st->deepcopy_obj = _PyImport_GetModuleAttrString("copy", "deepcopy"); if (st->deepcopy_obj == NULL) { - return NULL; + goto error; } assert(!PyErr_Occurred()); if (!(st->elementpath_obj = PyImport_ImportModule("xml.etree.ElementPath"))) - return NULL; + goto error; /* link against pyexpat */ expat_capi = PyCapsule_Import(PyExpat_CAPSULE_NAME, 0); @@ -4344,62 +4268,66 @@ PyInit__elementtree(void) expat_capi->MICRO_VERSION != XML_MICRO_VERSION) { PyErr_SetString(PyExc_ImportError, "pyexpat version is incompatible"); - return NULL; + goto error; } } else { - return NULL; + goto error; } st->str_append = PyUnicode_InternFromString("append"); if (st->str_append == NULL) { - return NULL; + goto error; } st->str_find = PyUnicode_InternFromString("find"); if (st->str_find == NULL) { - return NULL; + goto error; } st->str_findall = PyUnicode_InternFromString("findall"); if (st->str_findall == NULL) { - return NULL; + goto error; } st->str_findtext = PyUnicode_InternFromString("findtext"); if (st->str_findtext == NULL) { - return NULL; + goto error; } st->str_iterfind = PyUnicode_InternFromString("iterfind"); if (st->str_iterfind == NULL) { - return NULL; + goto error; } st->str_tail = PyUnicode_InternFromString("tail"); if (st->str_tail == NULL) { - return NULL; + goto error; } st->str_text = PyUnicode_InternFromString("text"); if (st->str_text == NULL) { - return NULL; + goto error; } st->str_doctype = PyUnicode_InternFromString("doctype"); if (st->str_doctype == NULL) { - return NULL; + goto error; } st->parseerror_obj = PyErr_NewException( "xml.etree.ElementTree.ParseError", PyExc_SyntaxError, NULL ); if (PyModule_AddObjectRef(m, "ParseError", st->parseerror_obj) < 0) { - return NULL; + goto error; } PyTypeObject *types[] = { - &Element_Type, - &TreeBuilder_Type, - &XMLParser_Type + Element_Type, + TreeBuilder_Type, + XMLParser_Type }; for (size_t i = 0; i < Py_ARRAY_LENGTH(types); i++) { if (PyModule_AddType(m, types[i]) < 0) { - return NULL; + goto error; } } return m; + +error: + Py_XDECREF(m); + return NULL; } diff --git a/Modules/clinic/_elementtree.c.h b/Modules/clinic/_elementtree.c.h index 33ccaf7e7c7e..b816e1bd8559 100644 --- a/Modules/clinic/_elementtree.c.h +++ b/Modules/clinic/_elementtree.c.h @@ -25,8 +25,8 @@ _elementtree_Element_append(ElementObject *self, PyObject *arg) PyObject *return_value = NULL; PyObject *subelement; - if (!PyObject_TypeCheck(arg, &Element_Type)) { - _PyArg_BadArgument("append", "argument", (&Element_Type)->tp_name, arg); + if (!PyObject_TypeCheck(arg, Element_Type)) { + _PyArg_BadArgument("append", "argument", (Element_Type)->tp_name, arg); goto exit; } subelement = arg; @@ -586,8 +586,8 @@ _elementtree_Element_insert(ElementObject *self, PyObject *const *args, Py_ssize } index = ival; } - if (!PyObject_TypeCheck(args[1], &Element_Type)) { - _PyArg_BadArgument("insert", "argument 2", (&Element_Type)->tp_name, args[1]); + if (!PyObject_TypeCheck(args[1], Element_Type)) { + _PyArg_BadArgument("insert", "argument 2", (Element_Type)->tp_name, args[1]); goto exit; } subelement = args[1]; @@ -682,8 +682,8 @@ _elementtree_Element_remove(ElementObject *self, PyObject *arg) PyObject *return_value = NULL; PyObject *subelement; - if (!PyObject_TypeCheck(arg, &Element_Type)) { - _PyArg_BadArgument("remove", "argument", (&Element_Type)->tp_name, arg); + if (!PyObject_TypeCheck(arg, Element_Type)) { + _PyArg_BadArgument("remove", "argument", (Element_Type)->tp_name, arg); goto exit; } subelement = arg; @@ -1105,4 +1105,4 @@ _elementtree_XMLParser__setevents(XMLParserObject *self, PyObject *const *args, exit: return return_value; } -/*[clinic end generated code: output=4ad006cadce01571 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=db3d6654de9f8013 input=a9049054013a1b77]*/ From webhook-mailer at python.org Fri Jan 20 12:42:47 2023 From: webhook-mailer at python.org (vsajip) Date: Fri, 20 Jan 2023 17:42:47 -0000 Subject: [Python-checkins] [3.10] Provided better example for logging cookbook (GH-101164) (GH-101183) Message-ID: https://github.com/python/cpython/commit/2fe0404deb96999e56b205a72fa6e6056a048935 commit: 2fe0404deb96999e56b205a72fa6e6056a048935 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: vsajip date: 2023-01-20T17:42:40Z summary: [3.10] Provided better example for logging cookbook (GH-101164) (GH-101183) Co-authored-by: Vladimir Malinovskii Co-authored-by: Vinay Sajip files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 1fa8dc10ad3c..66945bdf63eb 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -1982,26 +1982,47 @@ Using a rotator and namer to customize log rotation processing -------------------------------------------------------------- An example of how you can define a namer and rotator is given in the following -snippet, which shows zlib-based compression of the log file:: +runnable script, which shows gzip compression of the log file:: + + import gzip + import logging + import logging.handlers + import os + import shutil def namer(name): return name + ".gz" def rotator(source, dest): - with open(source, "rb") as sf: - data = sf.read() - compressed = zlib.compress(data, 9) - with open(dest, "wb") as df: - df.write(compressed) + with open(source, 'rb') as f_in: + with gzip.open(dest, 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) os.remove(source) - rh = logging.handlers.RotatingFileHandler(...) + + rh = logging.handlers.RotatingFileHandler('rotated.log', maxBytes=128, backupCount=5) rh.rotator = rotator rh.namer = namer -These are not "true" .gz files, as they are bare compressed data, with no -"container" such as you?d find in an actual gzip file. This snippet is just -for illustration purposes. + root = logging.getLogger() + root.setLevel(logging.INFO) + root.addHandler(rh) + f = logging.Formatter('%(asctime)s %(message)s') + rh.setFormatter(f) + for i in range(1000): + root.info(f'Message no. {i + 1}') + +After running this, you will see six new files, five of which are compressed: + +.. code-block:: shell-session + + $ ls rotated.log* + rotated.log rotated.log.2.gz rotated.log.4.gz + rotated.log.1.gz rotated.log.3.gz rotated.log.5.gz + $ zcat rotated.log.1.gz + 2023-01-20 02:28:17,767 Message no. 996 + 2023-01-20 02:28:17,767 Message no. 997 + 2023-01-20 02:28:17,767 Message no. 998 A more elaborate multiprocessing example ---------------------------------------- From webhook-mailer at python.org Fri Jan 20 12:43:15 2023 From: webhook-mailer at python.org (vsajip) Date: Fri, 20 Jan 2023 17:43:15 -0000 Subject: [Python-checkins] [3.11] Provided better example for logging cookbook (GH-101164) (GH-101184) Message-ID: https://github.com/python/cpython/commit/5e1c4ac85e68c4d0034ff0d10cb8e3e88b7e06c9 commit: 5e1c4ac85e68c4d0034ff0d10cb8e3e88b7e06c9 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: vsajip date: 2023-01-20T17:43:08Z summary: [3.11] Provided better example for logging cookbook (GH-101164) (GH-101184) Co-authored-by: Vladimir Malinovskii Co-authored-by: Vinay Sajip files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 8aec8e5e5ee0..2ba5b5c13681 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -1982,26 +1982,47 @@ Using a rotator and namer to customize log rotation processing -------------------------------------------------------------- An example of how you can define a namer and rotator is given in the following -snippet, which shows zlib-based compression of the log file:: +runnable script, which shows gzip compression of the log file:: + + import gzip + import logging + import logging.handlers + import os + import shutil def namer(name): return name + ".gz" def rotator(source, dest): - with open(source, "rb") as sf: - data = sf.read() - compressed = zlib.compress(data, 9) - with open(dest, "wb") as df: - df.write(compressed) + with open(source, 'rb') as f_in: + with gzip.open(dest, 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) os.remove(source) - rh = logging.handlers.RotatingFileHandler(...) + + rh = logging.handlers.RotatingFileHandler('rotated.log', maxBytes=128, backupCount=5) rh.rotator = rotator rh.namer = namer -These are not "true" .gz files, as they are bare compressed data, with no -"container" such as you?d find in an actual gzip file. This snippet is just -for illustration purposes. + root = logging.getLogger() + root.setLevel(logging.INFO) + root.addHandler(rh) + f = logging.Formatter('%(asctime)s %(message)s') + rh.setFormatter(f) + for i in range(1000): + root.info(f'Message no. {i + 1}') + +After running this, you will see six new files, five of which are compressed: + +.. code-block:: shell-session + + $ ls rotated.log* + rotated.log rotated.log.2.gz rotated.log.4.gz + rotated.log.1.gz rotated.log.3.gz rotated.log.5.gz + $ zcat rotated.log.1.gz + 2023-01-20 02:28:17,767 Message no. 996 + 2023-01-20 02:28:17,767 Message no. 997 + 2023-01-20 02:28:17,767 Message no. 998 A more elaborate multiprocessing example ---------------------------------------- From webhook-mailer at python.org Fri Jan 20 13:01:44 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Fri, 20 Jan 2023 18:01:44 -0000 Subject: [Python-checkins] gh-101181: Fix `unused-variable` warning in `pystate.c` (#101188) Message-ID: https://github.com/python/cpython/commit/8be6992620db18bea31c7f75a33c7dcc3782e95a commit: 8be6992620db18bea31c7f75a33c7dcc3782e95a branch: main author: Nikita Sobolev committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-20T23:31:30+05:30 summary: gh-101181: Fix `unused-variable` warning in `pystate.c` (#101188) Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> files: M Python/pystate.c diff --git a/Python/pystate.c b/Python/pystate.c index 464e5d63e4c4..5c1636a8dc37 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1878,11 +1878,14 @@ _PyGILState_SetTstate(PyThreadState *tstate) * interpreter is responsible to initialize it. */ return _PyStatus_OK(); } + +#ifndef NDEBUG _PyRuntimeState *runtime = tstate->interp->runtime; assert(runtime->gilstate.autoInterpreterState == tstate->interp); assert(current_tss_get(runtime) == tstate); assert(tstate->gilstate_counter == 1); +#endif return _PyStatus_OK(); } From webhook-mailer at python.org Fri Jan 20 13:35:16 2023 From: webhook-mailer at python.org (iritkatriel) Date: Fri, 20 Jan 2023 18:35:16 -0000 Subject: [Python-checkins] Fix typo in comment in compile.c (#101194) Message-ID: https://github.com/python/cpython/commit/4510fbb2163a5bdc4250752eab5a4bb9034dc76d commit: 4510fbb2163a5bdc4250752eab5a4bb9034dc76d branch: main author: Ikko Eltociear Ashimine committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-20T18:34:36Z summary: Fix typo in comment in compile.c (#101194) files: M Python/compile.c diff --git a/Python/compile.c b/Python/compile.c index bfc6e3499001..ce714dce6edf 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -3667,7 +3667,7 @@ compiler_try_star_except(struct compiler *c, stmt_ty s) /* [orig] BUILD_LIST */ - /* Creat a copy of the original EG */ + /* Create a copy of the original EG */ /* [orig, []] COPY 2 [orig, [], exc] From webhook-mailer at python.org Fri Jan 20 17:06:30 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:06:30 -0000 Subject: [Python-checkins] gh-91485: Avoid unnecessary use of non-Python syntax in io docs (#101177) Message-ID: https://github.com/python/cpython/commit/783d1bc51b886b6135e6a4effb91be207df5f50a commit: 783d1bc51b886b6135e6a4effb91be207df5f50a branch: main author: Shantanu <12621235+hauntsaninja at users.noreply.github.com> committer: ambv date: 2023-01-20T23:06:10+01:00 summary: gh-91485: Avoid unnecessary use of non-Python syntax in io docs (#101177) files: M Doc/library/io.rst diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 0968509fbafe..c9249da1c3c3 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -1021,8 +1021,8 @@ Text I/O .. versionadded:: 3.7 - .. method:: reconfigure(*[, encoding][, errors][, newline][, \ - line_buffering][, write_through]) + .. method:: reconfigure(*, encoding=None, errors=None, newline=None, \ + line_buffering=None, write_through=None) Reconfigure this text stream using new settings for *encoding*, *errors*, *newline*, *line_buffering* and *write_through*. From webhook-mailer at python.org Fri Jan 20 17:07:29 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:07:29 -0000 Subject: [Python-checkins] [3.11] gh-101100: [Enum] Fix sphinx warnings in docs (GH-101122) (#101173) Message-ID: https://github.com/python/cpython/commit/e039d53dae74bf7fd2bd14d4a271052ffa1f523b commit: e039d53dae74bf7fd2bd14d4a271052ffa1f523b branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: ambv date: 2023-01-20T23:07:23+01:00 summary: [3.11] gh-101100: [Enum] Fix sphinx warnings in docs (GH-101122) (#101173) (cherry picked from commit 9e025d305f159aebf01775ad1dc2817679f01aa9) Co-authored-by: Nikita Sobolev Co-authored-by: C.A.M. Gerlach files: M Doc/library/enum.rst diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 0456e74748c3..b58f3dba0310 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -52,11 +52,11 @@ are not normal Python classes. See .. note:: Nomenclature - - The class :class:`Color` is an *enumeration* (or *enum*) - - The attributes :attr:`Color.RED`, :attr:`Color.GREEN`, etc., are + - The class :class:`!Color` is an *enumeration* (or *enum*) + - The attributes :attr:`!Color.RED`, :attr:`!Color.GREEN`, etc., are *enumeration members* (or *members*) and are functionally constants. - The enum members have *names* and *values* (the name of - :attr:`Color.RED` is ``RED``, the value of :attr:`Color.BLUE` is + :attr:`!Color.RED` is ``RED``, the value of :attr:`!Color.BLUE` is ``3``, etc.) --------------- @@ -165,8 +165,8 @@ Data Types to subclass *EnumType* -- see :ref:`Subclassing EnumType ` for details. - *EnumType* is responsible for setting the correct :meth:`__repr__`, - :meth:`__str__`, :meth:`__format__`, and :meth:`__reduce__` methods on the + *EnumType* is responsible for setting the correct :meth:`!__repr__`, + :meth:`!__str__`, :meth:`!__format__`, and :meth:`!__reduce__` methods on the final *enum*, as well as creating the enum members, properly handling duplicates, providing iteration over the enum class, etc. @@ -422,9 +422,9 @@ Data Types Using :class:`auto` with :class:`IntEnum` results in integers of increasing value, starting with ``1``. - .. versionchanged:: 3.11 :meth:`__str__` is now :func:`int.__str__` to + .. versionchanged:: 3.11 :meth:`~object.__str__` is now :meth:`!int.__str__` to better support the *replacement of existing constants* use-case. - :meth:`__format__` was already :func:`int.__format__` for that same reason. + :meth:`~object.__format__` was already :meth:`!int.__format__` for that same reason. .. class:: StrEnum @@ -753,11 +753,11 @@ Data Types Supported ``__dunder__`` names """""""""""""""""""""""""""""" -:attr:`__members__` is a read-only ordered mapping of ``member_name``:``member`` +:attr:`~EnumType.__members__` is a read-only ordered mapping of ``member_name``:``member`` items. It is only available on the class. -:meth:`__new__`, if specified, must create and return the enum members; it is -also a very good idea to set the member's :attr:`_value_` appropriately. Once +:meth:`~object.__new__`, if specified, must create and return the enum members; it is +also a very good idea to set the member's :attr:`!_value_` appropriately. Once all the members are created it is no longer used. @@ -796,7 +796,7 @@ Utilities and Decorators .. class:: auto *auto* can be used in place of a value. If used, the *Enum* machinery will - call an *Enum*'s :meth:`_generate_next_value_` to get an appropriate value. + call an *Enum*'s :meth:`~Enum._generate_next_value_` to get an appropriate value. For *Enum* and *IntEnum* that appropriate value will be the last value plus one; for *Flag* and *IntFlag* it will be the first power-of-two greater than the highest value; for *StrEnum* it will be the lower-cased version of @@ -839,7 +839,7 @@ Utilities and Decorators .. decorator:: unique A :keyword:`class` decorator specifically for enumerations. It searches an - enumeration's :attr:`__members__`, gathering any aliases it finds; if any are + enumeration's :attr:`~EnumType.__members__`, gathering any aliases it finds; if any are found :exc:`ValueError` is raised with the details:: >>> from enum import Enum, unique From webhook-mailer at python.org Fri Jan 20 17:08:51 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:08:51 -0000 Subject: [Python-checkins] Fix minor typos in SSL documentation (#101158) Message-ID: https://github.com/python/cpython/commit/73497690b5debe01ba94ffefbecee034e39e8627 commit: 73497690b5debe01ba94ffefbecee034e39e8627 branch: main author: Simon Robinson committer: ambv date: 2023-01-20T23:08:45+01:00 summary: Fix minor typos in SSL documentation (#101158) files: M Doc/library/ssl.rst diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 78d44a23a83b..30f2a0765cc9 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1054,7 +1054,7 @@ SSL Sockets .. versionchanged:: 3.5 The :meth:`shutdown` does not reset the socket timeout each time bytes - are received or sent. The socket timeout is now to maximum total duration + are received or sent. The socket timeout is now the maximum total duration of the shutdown. .. deprecated:: 3.6 @@ -1087,8 +1087,8 @@ SSL sockets also have the following additional methods and attributes: cause write operations. .. versionchanged:: 3.5 - The socket timeout is no more reset each time bytes are received or sent. - The socket timeout is now to maximum total duration to read up to *len* + The socket timeout is no longer reset each time bytes are received or sent. + The socket timeout is now the maximum total duration to read up to *len* bytes. .. deprecated:: 3.6 @@ -1106,8 +1106,8 @@ SSL sockets also have the following additional methods and attributes: also cause read operations. .. versionchanged:: 3.5 - The socket timeout is no more reset each time bytes are received or sent. - The socket timeout is now to maximum total duration to write *buf*. + The socket timeout is no longer reset each time bytes are received or sent. + The socket timeout is now the maximum total duration to write *buf*. .. deprecated:: 3.6 Use :meth:`~SSLSocket.send` instead of :meth:`~SSLSocket.write`. @@ -1134,14 +1134,14 @@ SSL sockets also have the following additional methods and attributes: :attr:`~SSLSocket.context` is true. .. versionchanged:: 3.5 - The socket timeout is no more reset each time bytes are received or sent. - The socket timeout is now to maximum total duration of the handshake. + The socket timeout is no longer reset each time bytes are received or sent. + The socket timeout is now the maximum total duration of the handshake. .. versionchanged:: 3.7 Hostname or IP address is matched by OpenSSL during handshake. The function :func:`match_hostname` is no longer used. In case OpenSSL refuses a hostname or IP address, the handshake is aborted early and - a TLS alert message is send to the peer. + a TLS alert message is sent to the peer. .. method:: SSLSocket.getpeercert(binary_form=False) From webhook-mailer at python.org Fri Jan 20 17:10:14 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:10:14 -0000 Subject: [Python-checkins] Fix a typo in whatsnew/3.12.rst (#101150) Message-ID: https://github.com/python/cpython/commit/f1d0711dd33bdd6485082116ddaca004c225b62f commit: f1d0711dd33bdd6485082116ddaca004c225b62f branch: main author: caozhanhao <64004730+caozhanhao at users.noreply.github.com> committer: ambv date: 2023-01-20T23:10:08+01:00 summary: Fix a typo in whatsnew/3.12.rst (#101150) files: M Doc/whatsnew/3.12.rst diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 0c85aef007e0..442a3421f711 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -946,7 +946,7 @@ Removed internals. (Contributed by Victor Stinner in :gh:`92651`.) -* Legacy Unicode APIs has been removed. See :pep:`623` for detail. +* Legacy Unicode APIs have been removed. See :pep:`623` for detail. * :c:macro:`PyUnicode_WCHAR_KIND` * :c:func:`PyUnicode_AS_UNICODE` From webhook-mailer at python.org Fri Jan 20 17:11:37 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:11:37 -0000 Subject: [Python-checkins] GH-101112: Specify type of pattern for Path.rglob (#101132) Message-ID: https://github.com/python/cpython/commit/61f338a005aa9f36b2a0a8d6924857e703bb6140 commit: 61f338a005aa9f36b2a0a8d6924857e703bb6140 branch: main author: J?rgen Gmach committer: ambv date: 2023-01-20T23:11:31+01:00 summary: GH-101112: Specify type of pattern for Path.rglob (#101132) The documentation for `rglob` did not mention what `pattern` actually is. Mentioning and linking to `fnmatch` makes this explicit, as the documentation for `fnmatch` both shows the syntax and some explanation. files: M Doc/library/pathlib.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index c5cf409f5f1f..42d63b1a8f09 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1270,7 +1270,8 @@ call fails (for example because the path doesn't exist). .. method:: Path.rglob(pattern) Glob the given relative *pattern* recursively. This is like calling - :func:`Path.glob` with "``**/``" added in front of the *pattern*:: + :func:`Path.glob` with "``**/``" added in front of the *pattern*, where + *patterns* are the same as for :mod:`fnmatch`:: >>> sorted(Path().rglob("*.py")) [PosixPath('build/lib/pathlib.py'), From webhook-mailer at python.org Fri Jan 20 17:12:33 2023 From: webhook-mailer at python.org (miss-islington) Date: Fri, 20 Jan 2023 22:12:33 -0000 Subject: [Python-checkins] gh-91485: Avoid unnecessary use of non-Python syntax in io docs (GH-101177) Message-ID: https://github.com/python/cpython/commit/0dc35e4bbb33adc730fb43e4ef973737cf1c02d8 commit: 0dc35e4bbb33adc730fb43e4ef973737cf1c02d8 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-20T14:12:26-08:00 summary: gh-91485: Avoid unnecessary use of non-Python syntax in io docs (GH-101177) (cherry picked from commit 783d1bc51b886b6135e6a4effb91be207df5f50a) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> files: M Doc/library/io.rst diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 0968509fbafe..c9249da1c3c3 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -1021,8 +1021,8 @@ Text I/O .. versionadded:: 3.7 - .. method:: reconfigure(*[, encoding][, errors][, newline][, \ - line_buffering][, write_through]) + .. method:: reconfigure(*, encoding=None, errors=None, newline=None, \ + line_buffering=None, write_through=None) Reconfigure this text stream using new settings for *encoding*, *errors*, *newline*, *line_buffering* and *write_through*. From webhook-mailer at python.org Fri Jan 20 17:13:14 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:13:14 -0000 Subject: [Python-checkins] GH-101111: Disambigaute origin of const for store_const and append_const (#101121) Message-ID: https://github.com/python/cpython/commit/9a155138c58cad409e28e34359ba87ec0025b6b7 commit: 9a155138c58cad409e28e34359ba87ec0025b6b7 branch: main author: J?rgen Gmach committer: ambv date: 2023-01-20T23:13:07+01:00 summary: GH-101111: Disambigaute origin of const for store_const and append_const (#101121) While the documentation for `optparse` mentioned that both `store_const` and `append_const` store a constant value, it was not clear where this value was coming from. A link to `Option.const` makes this explicit. files: M Doc/library/optparse.rst diff --git a/Doc/library/optparse.rst b/Doc/library/optparse.rst index 47e62553fb03..3e29fed0175e 100644 --- a/Doc/library/optparse.rst +++ b/Doc/library/optparse.rst @@ -404,7 +404,7 @@ Other actions Some other actions supported by :mod:`optparse` are: ``"store_const"`` - store a constant value + store a constant value, pre-set via :attr:`Option.const` ``"append"`` append this option's argument to a list @@ -925,7 +925,7 @@ The canonical way to create an :class:`Option` instance is with the store this option's argument (default) ``"store_const"`` - store a constant value + store a constant value, pre-set via :attr:`Option.const` ``"store_true"`` store ``True`` @@ -937,7 +937,7 @@ The canonical way to create an :class:`Option` instance is with the append this option's argument to a list ``"append_const"`` - append a constant value to a list + append a constant value to a list, pre-set via :attr:`Option.const` ``"count"`` increment a counter by one From webhook-mailer at python.org Fri Jan 20 17:14:05 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:14:05 -0000 Subject: [Python-checkins] gh-86610: Use attribute directive in docs for pathlib.PurePath (#101114) Message-ID: https://github.com/python/cpython/commit/01093b82037fbae83623581294a0f1cf5b4a44b0 commit: 01093b82037fbae83623581294a0f1cf5b4a44b0 branch: main author: Barney Gale committer: ambv date: 2023-01-20T23:13:58+01:00 summary: gh-86610: Use attribute directive in docs for pathlib.PurePath (#101114) files: M Doc/library/pathlib.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 42d63b1a8f09..f222745a2c56 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -266,7 +266,7 @@ Accessing individual parts To access the individual "parts" (components) of a path, use the following property: -.. data:: PurePath.parts +.. attribute:: PurePath.parts A tuple giving access to the path's various components:: @@ -290,7 +290,7 @@ Methods and properties Pure paths provide the following methods and properties: -.. data:: PurePath.drive +.. attribute:: PurePath.drive A string representing the drive letter or name, if any:: @@ -306,7 +306,7 @@ Pure paths provide the following methods and properties: >>> PureWindowsPath('//host/share/foo.txt').drive '\\\\host\\share' -.. data:: PurePath.root +.. attribute:: PurePath.root A string representing the (local or global) root, if any:: @@ -342,7 +342,7 @@ Pure paths provide the following methods and properties: an implementation-defined manner, although more than two leading slashes shall be treated as a single slash."* -.. data:: PurePath.anchor +.. attribute:: PurePath.anchor The concatenation of the drive and root:: @@ -356,7 +356,7 @@ Pure paths provide the following methods and properties: '\\\\host\\share\\' -.. data:: PurePath.parents +.. attribute:: PurePath.parents An immutable sequence providing access to the logical ancestors of the path:: @@ -372,7 +372,7 @@ Pure paths provide the following methods and properties: .. versionchanged:: 3.10 The parents sequence now supports :term:`slices ` and negative index values. -.. data:: PurePath.parent +.. attribute:: PurePath.parent The logical parent of the path:: @@ -401,7 +401,7 @@ Pure paths provide the following methods and properties: symlinks and eliminate ``".."`` components. -.. data:: PurePath.name +.. attribute:: PurePath.name A string representing the final path component, excluding the drive and root, if any:: @@ -417,7 +417,7 @@ Pure paths provide the following methods and properties: '' -.. data:: PurePath.suffix +.. attribute:: PurePath.suffix The file extension of the final component, if any:: @@ -429,7 +429,7 @@ Pure paths provide the following methods and properties: '' -.. data:: PurePath.suffixes +.. attribute:: PurePath.suffixes A list of the path's file extensions:: @@ -441,7 +441,7 @@ Pure paths provide the following methods and properties: [] -.. data:: PurePath.stem +.. attribute:: PurePath.stem The final path component, without its suffix:: @@ -1446,11 +1446,11 @@ Below is a table mapping various :mod:`os` functions to their corresponding :meth:`Path.group` :func:`os.path.isabs` :meth:`PurePath.is_absolute` :func:`os.path.join` :func:`PurePath.joinpath` -:func:`os.path.basename` :data:`PurePath.name` -:func:`os.path.dirname` :data:`PurePath.parent` +:func:`os.path.basename` :attr:`PurePath.name` +:func:`os.path.dirname` :attr:`PurePath.parent` :func:`os.path.samefile` :meth:`Path.samefile` -:func:`os.path.splitext` :data:`PurePath.stem` and - :data:`PurePath.suffix` +:func:`os.path.splitext` :attr:`PurePath.stem` and + :attr:`PurePath.suffix` ==================================== ============================== .. rubric:: Footnotes From webhook-mailer at python.org Fri Jan 20 17:16:17 2023 From: webhook-mailer at python.org (miss-islington) Date: Fri, 20 Jan 2023 22:16:17 -0000 Subject: [Python-checkins] gh-91485: Avoid unnecessary use of non-Python syntax in io docs (GH-101177) Message-ID: https://github.com/python/cpython/commit/23bb5f35f57b9bd2b31830c77e196422c4ae09cb commit: 23bb5f35f57b9bd2b31830c77e196422c4ae09cb branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-20T14:16:10-08:00 summary: gh-91485: Avoid unnecessary use of non-Python syntax in io docs (GH-101177) (cherry picked from commit 783d1bc51b886b6135e6a4effb91be207df5f50a) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> files: M Doc/library/io.rst diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 5cf692e060cd..06716b7a7fc5 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -1015,8 +1015,8 @@ Text I/O .. versionadded:: 3.7 - .. method:: reconfigure(*[, encoding][, errors][, newline][, \ - line_buffering][, write_through]) + .. method:: reconfigure(*, encoding=None, errors=None, newline=None, \ + line_buffering=None, write_through=None) Reconfigure this text stream using new settings for *encoding*, *errors*, *newline*, *line_buffering* and *write_through*. From webhook-mailer at python.org Fri Jan 20 17:18:04 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:18:04 -0000 Subject: [Python-checkins] [3.10] Fix minor typos in SSL documentation (GH-101158) (#101202) Message-ID: https://github.com/python/cpython/commit/8fd481e62e4ee1487cbbd003b3bc9104bf39e0ce commit: 8fd481e62e4ee1487cbbd003b3bc9104bf39e0ce branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: ambv date: 2023-01-20T23:17:58+01:00 summary: [3.10] Fix minor typos in SSL documentation (GH-101158) (#101202) (cherry picked from commit 73497690b5debe01ba94ffefbecee034e39e8627) Co-authored-by: Simon Robinson files: M Doc/library/ssl.rst diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index a227df76d5d6..de9edcb26210 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1144,7 +1144,7 @@ SSL Sockets .. versionchanged:: 3.5 The :meth:`shutdown` does not reset the socket timeout each time bytes - are received or sent. The socket timeout is now to maximum total duration + are received or sent. The socket timeout is now the maximum total duration of the shutdown. .. deprecated:: 3.6 @@ -1177,8 +1177,8 @@ SSL sockets also have the following additional methods and attributes: cause write operations. .. versionchanged:: 3.5 - The socket timeout is no more reset each time bytes are received or sent. - The socket timeout is now to maximum total duration to read up to *len* + The socket timeout is no longer reset each time bytes are received or sent. + The socket timeout is now the maximum total duration to read up to *len* bytes. .. deprecated:: 3.6 @@ -1196,8 +1196,8 @@ SSL sockets also have the following additional methods and attributes: also cause read operations. .. versionchanged:: 3.5 - The socket timeout is no more reset each time bytes are received or sent. - The socket timeout is now to maximum total duration to write *buf*. + The socket timeout is no longer reset each time bytes are received or sent. + The socket timeout is now the maximum total duration to write *buf*. .. deprecated:: 3.6 Use :meth:`~SSLSocket.send` instead of :meth:`~SSLSocket.write`. @@ -1224,14 +1224,14 @@ SSL sockets also have the following additional methods and attributes: :attr:`~SSLSocket.context` is true. .. versionchanged:: 3.5 - The socket timeout is no more reset each time bytes are received or sent. - The socket timeout is now to maximum total duration of the handshake. + The socket timeout is no longer reset each time bytes are received or sent. + The socket timeout is now the maximum total duration of the handshake. .. versionchanged:: 3.7 Hostname or IP address is matched by OpenSSL during handshake. The function :func:`match_hostname` is no longer used. In case OpenSSL refuses a hostname or IP address, the handshake is aborted early and - a TLS alert message is send to the peer. + a TLS alert message is sent to the peer. .. method:: SSLSocket.getpeercert(binary_form=False) From webhook-mailer at python.org Fri Jan 20 17:18:31 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:18:31 -0000 Subject: [Python-checkins] [3.11] Fix minor typos in SSL documentation (GH-101158) (#101201) Message-ID: https://github.com/python/cpython/commit/1798df2b40e4f5aa91d0f67a1407e9153e258f22 commit: 1798df2b40e4f5aa91d0f67a1407e9153e258f22 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: ambv date: 2023-01-20T23:18:25+01:00 summary: [3.11] Fix minor typos in SSL documentation (GH-101158) (#101201) (cherry picked from commit 73497690b5debe01ba94ffefbecee034e39e8627) Co-authored-by: Simon Robinson files: M Doc/library/ssl.rst diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index a0aa45e9008a..372b1243dd75 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1142,7 +1142,7 @@ SSL Sockets .. versionchanged:: 3.5 The :meth:`shutdown` does not reset the socket timeout each time bytes - are received or sent. The socket timeout is now to maximum total duration + are received or sent. The socket timeout is now the maximum total duration of the shutdown. .. deprecated:: 3.6 @@ -1175,8 +1175,8 @@ SSL sockets also have the following additional methods and attributes: cause write operations. .. versionchanged:: 3.5 - The socket timeout is no more reset each time bytes are received or sent. - The socket timeout is now to maximum total duration to read up to *len* + The socket timeout is no longer reset each time bytes are received or sent. + The socket timeout is now the maximum total duration to read up to *len* bytes. .. deprecated:: 3.6 @@ -1194,8 +1194,8 @@ SSL sockets also have the following additional methods and attributes: also cause read operations. .. versionchanged:: 3.5 - The socket timeout is no more reset each time bytes are received or sent. - The socket timeout is now to maximum total duration to write *buf*. + The socket timeout is no longer reset each time bytes are received or sent. + The socket timeout is now the maximum total duration to write *buf*. .. deprecated:: 3.6 Use :meth:`~SSLSocket.send` instead of :meth:`~SSLSocket.write`. @@ -1222,14 +1222,14 @@ SSL sockets also have the following additional methods and attributes: :attr:`~SSLSocket.context` is true. .. versionchanged:: 3.5 - The socket timeout is no more reset each time bytes are received or sent. - The socket timeout is now to maximum total duration of the handshake. + The socket timeout is no longer reset each time bytes are received or sent. + The socket timeout is now the maximum total duration of the handshake. .. versionchanged:: 3.7 Hostname or IP address is matched by OpenSSL during handshake. The function :func:`match_hostname` is no longer used. In case OpenSSL refuses a hostname or IP address, the handshake is aborted early and - a TLS alert message is send to the peer. + a TLS alert message is sent to the peer. .. method:: SSLSocket.getpeercert(binary_form=False) From webhook-mailer at python.org Fri Jan 20 17:20:15 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:20:15 -0000 Subject: [Python-checkins] [3.8] Correct CVE-2020-10735 documentation (GH-100306) (#100698) Message-ID: https://github.com/python/cpython/commit/594ba1901cd0ad49e353bc10a031be439856ccbf commit: 594ba1901cd0ad49e353bc10a031be439856ccbf branch: 3.8 author: Gregory P. Smith committer: ambv date: 2023-01-20T23:20:09+01:00 summary: [3.8] Correct CVE-2020-10735 documentation (GH-100306) (#100698) (cherry picked from commit 1cf3d78c92eb07dc09d15cc2e773b0b1b9436825) (cherry picked from commit 88fe8d701af3316c8869ea18ea1c7acec6f68c04) Co-authored-by: Jeremy Paige Co-authored-by: Gregory P. Smith files: M Doc/library/stdtypes.rst M Python/clinic/sysmodule.c.h M Python/sysmodule.c diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 45d648bca0c5..3585f2014266 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4880,7 +4880,7 @@ to mitigate denial of service attacks. This limit *only* applies to decimal or other non-power-of-two number bases. Hexadecimal, octal, and binary conversions are unlimited. The limit can be configured. -The :class:`int` type in CPython is an abitrary length number stored in binary +The :class:`int` type in CPython is an arbitrary length number stored in binary form (commonly known as a "bignum"). There exists no algorithm that can convert a string to a binary integer or a binary integer to a string in linear time, *unless* the base is a power of 2. Even the best known algorithms for base 10 @@ -4944,7 +4944,7 @@ and :class:`str` or :class:`bytes`: * ``int(string)`` with default base 10. * ``int(string, base)`` for all bases that are not a power of 2. * ``str(integer)``. -* ``repr(integer)`` +* ``repr(integer)``. * any other string conversion to base 10, for example ``f"{integer}"``, ``"{}".format(integer)``, or ``b"%d" % integer``. @@ -4972,7 +4972,7 @@ command line flag to configure the limit: :envvar:`PYTHONINTMAXSTRDIGITS` or :option:`-X int_max_str_digits <-X>`. If both the env var and the ``-X`` option are set, the ``-X`` option takes precedence. A value of *-1* indicates that both were unset, thus a value of - :data:`sys.int_info.default_max_str_digits` was used during initilization. + :data:`sys.int_info.default_max_str_digits` was used during initialization. From code, you can inspect the current limit and set a new one using these :mod:`sys` APIs: diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index e41a9f3067fa..f6d012242fa2 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -727,7 +727,7 @@ PyDoc_STRVAR(sys_get_int_max_str_digits__doc__, "get_int_max_str_digits($module, /)\n" "--\n" "\n" -"Set the maximum string digits limit for non-binary int<->str conversions."); +"Return the maximum string digits limit for non-binary int<->str conversions."); #define SYS_GET_INT_MAX_STR_DIGITS_METHODDEF \ {"get_int_max_str_digits", (PyCFunction)sys_get_int_max_str_digits, METH_NOARGS, sys_get_int_max_str_digits__doc__}, @@ -1146,4 +1146,4 @@ sys_getandroidapilevel(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=c41f7fa36ead9409 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c55dafde3ed70944 input=a9049054013a1b77]*/ diff --git a/Python/sysmodule.c b/Python/sysmodule.c index eb3245a33298..a47926727a36 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1615,12 +1615,12 @@ sys_mdebug_impl(PyObject *module, int flag) /*[clinic input] sys.get_int_max_str_digits -Set the maximum string digits limit for non-binary int<->str conversions. +Return the maximum string digits limit for non-binary int<->str conversions. [clinic start generated code]*/ static PyObject * sys_get_int_max_str_digits_impl(PyObject *module) -/*[clinic end generated code: output=0042f5e8ae0e8631 input=8dab13e2023e60d5]*/ +/*[clinic end generated code: output=0042f5e8ae0e8631 input=61bf9f99bc8b112d]*/ { PyInterpreterState *interp = _PyInterpreterState_Get(); return PyLong_FromSsize_t(interp->int_max_str_digits); From webhook-mailer at python.org Fri Jan 20 17:20:38 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:20:38 -0000 Subject: [Python-checkins] [3.9] Correct CVE-2020-10735 documentation (GH-100306). (#100697) Message-ID: https://github.com/python/cpython/commit/cf71e19297de5e94d534832f179f585c1a1570bb commit: cf71e19297de5e94d534832f179f585c1a1570bb branch: 3.9 author: Gregory P. Smith committer: ambv date: 2023-01-20T23:20:32+01:00 summary: [3.9] Correct CVE-2020-10735 documentation (GH-100306). (#100697) (cherry picked from commit 1cf3d78c92eb07dc09d15cc2e773b0b1b9436825) (cherry picked from commit 88fe8d701af3316c8869ea18ea1c7acec6f68c04) Co-authored-by: Jeremy Paige Co-authored-by: Gregory P. Smith files: M Doc/library/stdtypes.rst M Python/clinic/sysmodule.c.h M Python/sysmodule.c diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index d6b270d144a7..8613820fbcb9 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -5254,7 +5254,7 @@ to mitigate denial of service attacks. This limit *only* applies to decimal or other non-power-of-two number bases. Hexadecimal, octal, and binary conversions are unlimited. The limit can be configured. -The :class:`int` type in CPython is an abitrary length number stored in binary +The :class:`int` type in CPython is an arbitrary length number stored in binary form (commonly known as a "bignum"). There exists no algorithm that can convert a string to a binary integer or a binary integer to a string in linear time, *unless* the base is a power of 2. Even the best known algorithms for base 10 @@ -5318,7 +5318,7 @@ and :class:`str` or :class:`bytes`: * ``int(string)`` with default base 10. * ``int(string, base)`` for all bases that are not a power of 2. * ``str(integer)``. -* ``repr(integer)`` +* ``repr(integer)``. * any other string conversion to base 10, for example ``f"{integer}"``, ``"{}".format(integer)``, or ``b"%d" % integer``. @@ -5346,7 +5346,7 @@ command line flag to configure the limit: :envvar:`PYTHONINTMAXSTRDIGITS` or :option:`-X int_max_str_digits <-X>`. If both the env var and the ``-X`` option are set, the ``-X`` option takes precedence. A value of *-1* indicates that both were unset, thus a value of - :data:`sys.int_info.default_max_str_digits` was used during initilization. + :data:`sys.int_info.default_max_str_digits` was used during initialization. From code, you can inspect the current limit and set a new one using these :mod:`sys` APIs: diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 41444080b5bf..bbb990ce2c4a 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -671,7 +671,7 @@ PyDoc_STRVAR(sys_get_int_max_str_digits__doc__, "get_int_max_str_digits($module, /)\n" "--\n" "\n" -"Set the maximum string digits limit for non-binary int<->str conversions."); +"Return the maximum string digits limit for non-binary int<->str conversions."); #define SYS_GET_INT_MAX_STR_DIGITS_METHODDEF \ {"get_int_max_str_digits", (PyCFunction)sys_get_int_max_str_digits, METH_NOARGS, sys_get_int_max_str_digits__doc__}, @@ -1028,4 +1028,4 @@ sys_getandroidapilevel(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=401254a595859ac6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=97a4176745dbbe79 input=a9049054013a1b77]*/ diff --git a/Python/sysmodule.c b/Python/sysmodule.c index a8f2f021c326..67df01699c8d 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1643,12 +1643,12 @@ sys_mdebug_impl(PyObject *module, int flag) /*[clinic input] sys.get_int_max_str_digits -Set the maximum string digits limit for non-binary int<->str conversions. +Return the maximum string digits limit for non-binary int<->str conversions. [clinic start generated code]*/ static PyObject * sys_get_int_max_str_digits_impl(PyObject *module) -/*[clinic end generated code: output=0042f5e8ae0e8631 input=8dab13e2023e60d5]*/ +/*[clinic end generated code: output=0042f5e8ae0e8631 input=61bf9f99bc8b112d]*/ { PyInterpreterState *interp = _PyInterpreterState_GET(); return PyLong_FromSsize_t(interp->int_max_str_digits); From webhook-mailer at python.org Fri Jan 20 17:21:06 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:21:06 -0000 Subject: [Python-checkins] [3.9] gh-95778: add doc missing in some places (GH-100627). (#101066) Message-ID: https://github.com/python/cpython/commit/6be2e0e24714fcb69417dad91a1643b0d4063565 commit: 6be2e0e24714fcb69417dad91a1643b0d4063565 branch: 3.9 author: ?ric committer: ambv date: 2023-01-20T23:21:00+01:00 summary: [3.9] gh-95778: add doc missing in some places (GH-100627). (#101066) (cherry picked from commit 46521826cb1883e29e4640f94089dd92c57efc5b) Co-authored-by: ?ric files: M Misc/python.man diff --git a/Misc/python.man b/Misc/python.man index 225376574a26..fce2bdf49304 100644 --- a/Misc/python.man +++ b/Misc/python.man @@ -308,6 +308,11 @@ Set implementation specific option. The following options are available: -X pycache_prefix=PATH: enable writing .pyc files to a parallel tree rooted at the given directory instead of to the code tree. + + -X int_max_str_digits=number: limit the size of int<->str conversions. + This helps avoid denial of service attacks when parsing untrusted data. + The default is sys.int_info.default_max_str_digits. 0 disables. + .TP .B \-x Skip the first line of the source. This is intended for a DOS @@ -477,6 +482,11 @@ values. The integer must be a decimal number in the range [0,4294967295]. Specifying the value 0 will disable hash randomization. +.IP PYTHONINTMAXSTRDIGITS +Limit the maximum digit characters in an int value +when converting from a string and when converting an int back to a str. +A value of 0 disables the limit. Conversions to or from bases 2, 4, 8, +16, and 32 are never limited. .IP PYTHONMALLOC Set the Python memory allocators and/or install debug hooks. The available memory allocators are From webhook-mailer at python.org Fri Jan 20 17:21:46 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:21:46 -0000 Subject: [Python-checkins] [3.9] GH-100892: Fix race in clearing `threading.local` (GH-100922) (#100939) Message-ID: https://github.com/python/cpython/commit/6954203c9fb7166d3cff8e59ac7e44ddb5bf2fc0 commit: 6954203c9fb7166d3cff8e59ac7e44ddb5bf2fc0 branch: 3.9 author: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> committer: ambv date: 2023-01-20T23:21:40+01:00 summary: [3.9] GH-100892: Fix race in clearing `threading.local` (GH-100922) (#100939) [3.9] [3.10] GH-100892: Fix race in clearing `threading.local` (GH-100922). (cherry picked from commit 762745a124cbc297cf2fe6f3ec9ca1840bb2e873) Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com>. (cherry picked from commit 683e9fe30ecd024f5508b2a33316752870100a96) Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> files: A Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst M Lib/test/test_threading_local.py M Modules/_testcapimodule.c M Modules/_threadmodule.c diff --git a/Lib/test/test_threading_local.py b/Lib/test/test_threading_local.py index 5ddb8c41c5f3..8b24e3a28bf0 100644 --- a/Lib/test/test_threading_local.py +++ b/Lib/test/test_threading_local.py @@ -193,6 +193,22 @@ class X: self.assertIsNone(wr()) + def test_threading_local_clear_race(self): + # See https://github.com/python/cpython/issues/100892 + + try: + import _testcapi + except ImportError: + unittest.skip("requires _testcapi") + + _testcapi.call_in_temporary_c_thread(lambda: None, False) + + for _ in range(1000): + _ = threading.local() + + _testcapi.join_temporary_c_thread() + + class ThreadLocalTest(unittest.TestCase, BaseLocalTest): _local = _thread._local diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst new file mode 100644 index 000000000000..f2576becc2fc --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-10-14-11-17.gh-issue-100892.qfBVYI.rst @@ -0,0 +1 @@ +Fix race while iterating over thread states in clearing :class:`threading.local`. Patch by Kumar Aditya. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 90841270e473..361ce7b55a26 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -4239,12 +4239,19 @@ temporary_c_thread(void *data) PyThread_exit_thread(); } +static test_c_thread_t test_c_thread; + static PyObject * -call_in_temporary_c_thread(PyObject *self, PyObject *callback) +call_in_temporary_c_thread(PyObject *self, PyObject *args) { PyObject *res = NULL; - test_c_thread_t test_c_thread; + PyObject *callback = NULL; long thread; + int wait = 1; + if (!PyArg_ParseTuple(args, "O|i", &callback, &wait)) + { + return NULL; + } test_c_thread.start_event = PyThread_allocate_lock(); test_c_thread.exit_event = PyThread_allocate_lock(); @@ -4271,6 +4278,10 @@ call_in_temporary_c_thread(PyObject *self, PyObject *callback) PyThread_acquire_lock(test_c_thread.start_event, 1); PyThread_release_lock(test_c_thread.start_event); + if (!wait) { + Py_RETURN_NONE; + } + Py_BEGIN_ALLOW_THREADS PyThread_acquire_lock(test_c_thread.exit_event, 1); PyThread_release_lock(test_c_thread.exit_event); @@ -4281,13 +4292,32 @@ call_in_temporary_c_thread(PyObject *self, PyObject *callback) exit: Py_CLEAR(test_c_thread.callback); - if (test_c_thread.start_event) + if (test_c_thread.start_event) { PyThread_free_lock(test_c_thread.start_event); - if (test_c_thread.exit_event) + test_c_thread.start_event = NULL; + } + if (test_c_thread.exit_event) { PyThread_free_lock(test_c_thread.exit_event); + test_c_thread.exit_event = NULL; + } return res; } +static PyObject * +join_temporary_c_thread(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + Py_BEGIN_ALLOW_THREADS + PyThread_acquire_lock(test_c_thread.exit_event, 1); + PyThread_release_lock(test_c_thread.exit_event); + Py_END_ALLOW_THREADS + Py_CLEAR(test_c_thread.callback); + PyThread_free_lock(test_c_thread.start_event); + test_c_thread.start_event = NULL; + PyThread_free_lock(test_c_thread.exit_event); + test_c_thread.exit_event = NULL; + Py_RETURN_NONE; +} + /* marshal */ static PyObject* @@ -5532,8 +5562,9 @@ static PyMethodDef TestMethods[] = { {"docstring_with_signature_with_defaults", (PyCFunction)test_with_docstring, METH_NOARGS, docstring_with_signature_with_defaults}, - {"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_O, + {"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_VARARGS, PyDoc_STR("set_error_class(error_class) -> None")}, + {"join_temporary_c_thread", join_temporary_c_thread, METH_NOARGS}, {"pymarshal_write_long_to_file", pymarshal_write_long_to_file, METH_VARARGS}, {"pymarshal_write_object_to_file", diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index a370352238d3..1ff31a452cac 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -801,6 +801,11 @@ local_traverse(localobject *self, visitproc visit, void *arg) return 0; } +#define HEAD_LOCK(runtime) \ + PyThread_acquire_lock((runtime)->interpreters.mutex, WAIT_LOCK) +#define HEAD_UNLOCK(runtime) \ + PyThread_release_lock((runtime)->interpreters.mutex) + static int local_clear(localobject *self) { @@ -810,17 +815,26 @@ local_clear(localobject *self) Py_CLEAR(self->dummies); Py_CLEAR(self->wr_callback); /* Remove all strong references to dummies from the thread states */ - if (self->key - && (tstate = PyThreadState_Get()) - && tstate->interp) { - for(tstate = PyInterpreterState_ThreadHead(tstate->interp); - tstate; - tstate = PyThreadState_Next(tstate)) - if (tstate->dict && PyDict_GetItem(tstate->dict, self->key)) { - if (PyDict_DelItem(tstate->dict, self->key)) { + if (self->key) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyRuntimeState *runtime = &_PyRuntime; + HEAD_LOCK(runtime); + PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); + HEAD_UNLOCK(runtime); + while (tstate) { + if (tstate->dict) { + PyObject *v = _PyDict_Pop(tstate->dict, self->key, Py_None); + if (v != NULL) { + Py_DECREF(v); + } + else { PyErr_Clear(); } } + HEAD_LOCK(runtime); + tstate = PyThreadState_Next(tstate); + HEAD_UNLOCK(runtime); + } } return 0; } From webhook-mailer at python.org Fri Jan 20 17:22:11 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:22:11 -0000 Subject: [Python-checkins] [3.11] GH-101111: Disambigaute origin of const for store_const and append_const (GH-101121) (#101203) Message-ID: https://github.com/python/cpython/commit/efc89f3454d694c003934a29f294ff9f27ed9045 commit: efc89f3454d694c003934a29f294ff9f27ed9045 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: ambv date: 2023-01-20T23:22:04+01:00 summary: [3.11] GH-101111: Disambigaute origin of const for store_const and append_const (GH-101121) (#101203) (cherry picked from commit 9a155138c58cad409e28e34359ba87ec0025b6b7) While the documentation for `optparse` mentioned that both `store_const` and `append_const` store a constant value, it was not clear where this value was coming from. A link to `Option.const` makes this explicit. Co-authored-by: J?rgen Gmach files: M Doc/library/optparse.rst diff --git a/Doc/library/optparse.rst b/Doc/library/optparse.rst index 47e62553fb03..3e29fed0175e 100644 --- a/Doc/library/optparse.rst +++ b/Doc/library/optparse.rst @@ -404,7 +404,7 @@ Other actions Some other actions supported by :mod:`optparse` are: ``"store_const"`` - store a constant value + store a constant value, pre-set via :attr:`Option.const` ``"append"`` append this option's argument to a list @@ -925,7 +925,7 @@ The canonical way to create an :class:`Option` instance is with the store this option's argument (default) ``"store_const"`` - store a constant value + store a constant value, pre-set via :attr:`Option.const` ``"store_true"`` store ``True`` @@ -937,7 +937,7 @@ The canonical way to create an :class:`Option` instance is with the append this option's argument to a list ``"append_const"`` - append a constant value to a list + append a constant value to a list, pre-set via :attr:`Option.const` ``"count"`` increment a counter by one From webhook-mailer at python.org Fri Jan 20 17:22:27 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:22:27 -0000 Subject: [Python-checkins] [3.10] GH-101111: Disambigaute origin of const for store_const and append_const (GH-101121) (#101204) Message-ID: https://github.com/python/cpython/commit/f1313ab97932ffdda2868f59e54c98483d37e489 commit: f1313ab97932ffdda2868f59e54c98483d37e489 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: ambv date: 2023-01-20T23:22:21+01:00 summary: [3.10] GH-101111: Disambigaute origin of const for store_const and append_const (GH-101121) (#101204) (cherry picked from commit 9a155138c58cad409e28e34359ba87ec0025b6b7) While the documentation for `optparse` mentioned that both `store_const` and `append_const` store a constant value, it was not clear where this value was coming from. A link to `Option.const` makes this explicit. Co-authored-by: J?rgen Gmach files: M Doc/library/optparse.rst diff --git a/Doc/library/optparse.rst b/Doc/library/optparse.rst index 47e62553fb03..3e29fed0175e 100644 --- a/Doc/library/optparse.rst +++ b/Doc/library/optparse.rst @@ -404,7 +404,7 @@ Other actions Some other actions supported by :mod:`optparse` are: ``"store_const"`` - store a constant value + store a constant value, pre-set via :attr:`Option.const` ``"append"`` append this option's argument to a list @@ -925,7 +925,7 @@ The canonical way to create an :class:`Option` instance is with the store this option's argument (default) ``"store_const"`` - store a constant value + store a constant value, pre-set via :attr:`Option.const` ``"store_true"`` store ``True`` @@ -937,7 +937,7 @@ The canonical way to create an :class:`Option` instance is with the append this option's argument to a list ``"append_const"`` - append a constant value to a list + append a constant value to a list, pre-set via :attr:`Option.const` ``"count"`` increment a counter by one From webhook-mailer at python.org Fri Jan 20 17:22:57 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:22:57 -0000 Subject: [Python-checkins] [3.9] gh-100180: Update Windows installer to OpenSSL 1.1.1s (GH-100903) (#100904) Message-ID: https://github.com/python/cpython/commit/fcfa505b80bcb7600e3b7605cab27627df07faf1 commit: fcfa505b80bcb7600e3b7605cab27627df07faf1 branch: 3.9 author: Steve Dower committer: ambv date: 2023-01-20T23:22:50+01:00 summary: [3.9] gh-100180: Update Windows installer to OpenSSL 1.1.1s (GH-100903) (#100904) files: A Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst M PCbuild/get_externals.bat M PCbuild/python.props diff --git a/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst b/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst new file mode 100644 index 000000000000..5b0f42568d92 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst @@ -0,0 +1 @@ +Update Windows installer to OpenSSL 1.1.1s diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index ee79addd44d0..b17f37c4499f 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -53,7 +53,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.3.0 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1n +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1s set libraries=%libraries% sqlite-3.37.2.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.12.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.12.0 @@ -77,7 +77,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.3.0 -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1n +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1s if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.12.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/python.props b/PCbuild/python.props index c451429da2c8..eeef39af637b 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -63,8 +63,8 @@ $(ExternalsDir)libffi-3.3.0\ $(ExternalsDir)libffi-3.3.0\$(ArchName)\ $(libffiOutDir)include - $(ExternalsDir)openssl-1.1.1n\ - $(ExternalsDir)openssl-bin-1.1.1n\$(ArchName)\ + $(ExternalsDir)openssl-1.1.1s\ + $(ExternalsDir)openssl-bin-1.1.1s\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.12\ From webhook-mailer at python.org Fri Jan 20 17:25:45 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:25:45 -0000 Subject: [Python-checkins] gh-100750: pass encoding kwarg in lib/platform.py (#100751) Message-ID: https://github.com/python/cpython/commit/6b3993c556eb3bb36d1754a17643cddd3f6ade92 commit: 6b3993c556eb3bb36d1754a17643cddd3f6ade92 branch: main author: Thomas Grainger committer: ambv date: 2023-01-20T23:25:38+01:00 summary: gh-100750: pass encoding kwarg in lib/platform.py (#100751) Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> files: A Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst M Lib/platform.py diff --git a/Lib/platform.py b/Lib/platform.py index b018046f5268..2dfaf76252db 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -285,6 +285,7 @@ def _syscmd_ver(system='', release='', version='', stdin=subprocess.DEVNULL, stderr=subprocess.DEVNULL, text=True, + encoding="locale", shell=True) except (OSError, subprocess.CalledProcessError) as why: #print('Command %s failed: %s' % (cmd, why)) @@ -824,6 +825,7 @@ def from_subprocess(): ['uname', '-p'], stderr=subprocess.DEVNULL, text=True, + encoding="utf8", ).strip() except (OSError, subprocess.CalledProcessError): pass diff --git a/Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst b/Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst new file mode 100644 index 000000000000..be351532822c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst @@ -0,0 +1 @@ +pass encoding kwarg to subprocess in platform From webhook-mailer at python.org Fri Jan 20 17:28:29 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:28:29 -0000 Subject: [Python-checkins] [3.11] gh-86610: Use attribute directive in docs for pathlib.PurePath (GH-101114) (#101205) Message-ID: https://github.com/python/cpython/commit/db0eeb381e5a5a3d9f3ccbdc9eb49b49dcf4f006 commit: db0eeb381e5a5a3d9f3ccbdc9eb49b49dcf4f006 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: ambv date: 2023-01-20T23:28:21+01:00 summary: [3.11] gh-86610: Use attribute directive in docs for pathlib.PurePath (GH-101114) (#101205) (cherry picked from commit 01093b82037fbae83623581294a0f1cf5b4a44b0) Co-authored-by: Barney Gale files: M Doc/library/pathlib.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 9321c1d50e9c..e6db8caa16bb 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -266,7 +266,7 @@ Accessing individual parts To access the individual "parts" (components) of a path, use the following property: -.. data:: PurePath.parts +.. attribute:: PurePath.parts A tuple giving access to the path's various components:: @@ -290,7 +290,7 @@ Methods and properties Pure paths provide the following methods and properties: -.. data:: PurePath.drive +.. attribute:: PurePath.drive A string representing the drive letter or name, if any:: @@ -306,7 +306,7 @@ Pure paths provide the following methods and properties: >>> PureWindowsPath('//host/share/foo.txt').drive '\\\\host\\share' -.. data:: PurePath.root +.. attribute:: PurePath.root A string representing the (local or global) root, if any:: @@ -342,7 +342,7 @@ Pure paths provide the following methods and properties: an implementation-defined manner, although more than two leading slashes shall be treated as a single slash."* -.. data:: PurePath.anchor +.. attribute:: PurePath.anchor The concatenation of the drive and root:: @@ -356,7 +356,7 @@ Pure paths provide the following methods and properties: '\\\\host\\share\\' -.. data:: PurePath.parents +.. attribute:: PurePath.parents An immutable sequence providing access to the logical ancestors of the path:: @@ -372,7 +372,7 @@ Pure paths provide the following methods and properties: .. versionchanged:: 3.10 The parents sequence now supports :term:`slices ` and negative index values. -.. data:: PurePath.parent +.. attribute:: PurePath.parent The logical parent of the path:: @@ -401,7 +401,7 @@ Pure paths provide the following methods and properties: symlinks and eliminate ``".."`` components. -.. data:: PurePath.name +.. attribute:: PurePath.name A string representing the final path component, excluding the drive and root, if any:: @@ -417,7 +417,7 @@ Pure paths provide the following methods and properties: '' -.. data:: PurePath.suffix +.. attribute:: PurePath.suffix The file extension of the final component, if any:: @@ -429,7 +429,7 @@ Pure paths provide the following methods and properties: '' -.. data:: PurePath.suffixes +.. attribute:: PurePath.suffixes A list of the path's file extensions:: @@ -441,7 +441,7 @@ Pure paths provide the following methods and properties: [] -.. data:: PurePath.stem +.. attribute:: PurePath.stem The final path component, without its suffix:: @@ -1323,11 +1323,11 @@ Below is a table mapping various :mod:`os` functions to their corresponding :meth:`Path.group` :func:`os.path.isabs` :meth:`PurePath.is_absolute` :func:`os.path.join` :func:`PurePath.joinpath` -:func:`os.path.basename` :data:`PurePath.name` -:func:`os.path.dirname` :data:`PurePath.parent` +:func:`os.path.basename` :attr:`PurePath.name` +:func:`os.path.dirname` :attr:`PurePath.parent` :func:`os.path.samefile` :meth:`Path.samefile` -:func:`os.path.splitext` :data:`PurePath.stem` and - :data:`PurePath.suffix` +:func:`os.path.splitext` :attr:`PurePath.stem` and + :attr:`PurePath.suffix` ==================================== ============================== .. rubric:: Footnotes From webhook-mailer at python.org Fri Jan 20 17:28:41 2023 From: webhook-mailer at python.org (ambv) Date: Fri, 20 Jan 2023 22:28:41 -0000 Subject: [Python-checkins] [3.10] gh-86610: Use attribute directive in docs for pathlib.PurePath (GH-101114) (#101206) Message-ID: https://github.com/python/cpython/commit/6919c36541a141ee6d98d36fda2aa9f2c3b3ac5f commit: 6919c36541a141ee6d98d36fda2aa9f2c3b3ac5f branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: ambv date: 2023-01-20T23:28:35+01:00 summary: [3.10] gh-86610: Use attribute directive in docs for pathlib.PurePath (GH-101114) (#101206) (cherry picked from commit 01093b82037fbae83623581294a0f1cf5b4a44b0) Co-authored-by: Barney Gale files: M Doc/library/pathlib.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 881f8d6bee61..ee140e871cb4 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -266,7 +266,7 @@ Accessing individual parts To access the individual "parts" (components) of a path, use the following property: -.. data:: PurePath.parts +.. attribute:: PurePath.parts A tuple giving access to the path's various components:: @@ -290,7 +290,7 @@ Methods and properties Pure paths provide the following methods and properties: -.. data:: PurePath.drive +.. attribute:: PurePath.drive A string representing the drive letter or name, if any:: @@ -306,7 +306,7 @@ Pure paths provide the following methods and properties: >>> PureWindowsPath('//host/share/foo.txt').drive '\\\\host\\share' -.. data:: PurePath.root +.. attribute:: PurePath.root A string representing the (local or global) root, if any:: @@ -342,7 +342,7 @@ Pure paths provide the following methods and properties: an implementation-defined manner, although more than two leading slashes shall be treated as a single slash."* -.. data:: PurePath.anchor +.. attribute:: PurePath.anchor The concatenation of the drive and root:: @@ -356,7 +356,7 @@ Pure paths provide the following methods and properties: '\\\\host\\share\\' -.. data:: PurePath.parents +.. attribute:: PurePath.parents An immutable sequence providing access to the logical ancestors of the path:: @@ -372,7 +372,7 @@ Pure paths provide the following methods and properties: .. versionchanged:: 3.10 The parents sequence now supports :term:`slices ` and negative index values. -.. data:: PurePath.parent +.. attribute:: PurePath.parent The logical parent of the path:: @@ -401,7 +401,7 @@ Pure paths provide the following methods and properties: symlinks and eliminate `".."` components. -.. data:: PurePath.name +.. attribute:: PurePath.name A string representing the final path component, excluding the drive and root, if any:: @@ -417,7 +417,7 @@ Pure paths provide the following methods and properties: '' -.. data:: PurePath.suffix +.. attribute:: PurePath.suffix The file extension of the final component, if any:: @@ -429,7 +429,7 @@ Pure paths provide the following methods and properties: '' -.. data:: PurePath.suffixes +.. attribute:: PurePath.suffixes A list of the path's file extensions:: @@ -441,7 +441,7 @@ Pure paths provide the following methods and properties: [] -.. data:: PurePath.stem +.. attribute:: PurePath.stem The final path component, without its suffix:: @@ -1304,11 +1304,11 @@ Below is a table mapping various :mod:`os` functions to their corresponding :meth:`Path.group` :func:`os.path.isabs` :meth:`PurePath.is_absolute` :func:`os.path.join` :func:`PurePath.joinpath` -:func:`os.path.basename` :data:`PurePath.name` -:func:`os.path.dirname` :data:`PurePath.parent` +:func:`os.path.basename` :attr:`PurePath.name` +:func:`os.path.dirname` :attr:`PurePath.parent` :func:`os.path.samefile` :meth:`Path.samefile` -:func:`os.path.splitext` :data:`PurePath.stem` and - :data:`PurePath.suffix` +:func:`os.path.splitext` :attr:`PurePath.stem` and + :attr:`PurePath.suffix` ==================================== ============================== .. rubric:: Footnotes From webhook-mailer at python.org Fri Jan 20 19:01:07 2023 From: webhook-mailer at python.org (brettcannon) Date: Sat, 21 Jan 2023 00:01:07 -0000 Subject: [Python-checkins] gh-91351: Fix some bugs in importlib handling of re-entrant imports (GH-94504) Message-ID: https://github.com/python/cpython/commit/3325f054e33b318aa56b74472f76a56b8afc0510 commit: 3325f054e33b318aa56b74472f76a56b8afc0510 branch: main author: Jean-Paul Calderone committer: brettcannon date: 2023-01-20T16:00:39-08:00 summary: gh-91351: Fix some bugs in importlib handling of re-entrant imports (GH-94504) Co-authored-by: Brett Cannon files: A Misc/NEWS.d/next/Core and Builtins/2023-01-06-09-22-21.gh-issue-91351.iq2vZ_.rst M Lib/importlib/_bootstrap.py diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 1a1d5cf7c9c3..bebe7e15cbce 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -54,14 +54,87 @@ def _new_module(name): # A dict mapping module names to weakrefs of _ModuleLock instances # Dictionary protected by the global import lock _module_locks = {} -# A dict mapping thread ids to _ModuleLock instances + +# A dict mapping thread IDs to lists of _ModuleLock instances. This maps a +# thread to the module locks it is blocking on acquiring. The values are +# lists because a single thread could perform a re-entrant import and be "in +# the process" of blocking on locks for more than one module. A thread can +# be "in the process" because a thread cannot actually block on acquiring +# more than one lock but it can have set up bookkeeping that reflects that +# it intends to block on acquiring more than one lock. _blocking_on = {} +class _BlockingOnManager: + """A context manager responsible to updating ``_blocking_on``.""" + def __init__(self, thread_id, lock): + self.thread_id = thread_id + self.lock = lock + + def __enter__(self): + """Mark the running thread as waiting for self.lock. via _blocking_on.""" + # Interactions with _blocking_on are *not* protected by the global + # import lock here because each thread only touches the state that it + # owns (state keyed on its thread id). The global import lock is + # re-entrant (i.e., a single thread may take it more than once) so it + # wouldn't help us be correct in the face of re-entrancy either. + + self.blocked_on = _blocking_on.setdefault(self.thread_id, []) + self.blocked_on.append(self.lock) + + def __exit__(self, *args, **kwargs): + """Remove self.lock from this thread's _blocking_on list.""" + self.blocked_on.remove(self.lock) + + class _DeadlockError(RuntimeError): pass + +def _has_deadlocked(target_id, *, seen_ids, candidate_ids, blocking_on): + """Check if 'target_id' is holding the same lock as another thread(s). + + The search within 'blocking_on' starts with the threads listed in + 'candidate_ids'. 'seen_ids' contains any threads that are considered + already traversed in the search. + + Keyword arguments: + target_id -- The thread id to try to reach. + seen_ids -- A set of threads that have already been visited. + candidate_ids -- The thread ids from which to begin. + blocking_on -- A dict representing the thread/blocking-on graph. This may + be the same object as the global '_blocking_on' but it is + a parameter to reduce the impact that global mutable + state has on the result of this function. + """ + if target_id in candidate_ids: + # If we have already reached the target_id, we're done - signal that it + # is reachable. + return True + + # Otherwise, try to reach the target_id from each of the given candidate_ids. + for tid in candidate_ids: + if not (candidate_blocking_on := blocking_on.get(tid)): + # There are no edges out from this node, skip it. + continue + elif tid in seen_ids: + # bpo 38091: the chain of tid's we encounter here eventually leads + # to a fixed point or a cycle, but does not reach target_id. + # This means we would not actually deadlock. This can happen if + # other threads are at the beginning of acquire() below. + return False + seen_ids.add(tid) + + # Follow the edges out from this thread. + edges = [lock.owner for lock in candidate_blocking_on] + if _has_deadlocked(target_id, seen_ids=seen_ids, candidate_ids=edges, + blocking_on=blocking_on): + return True + + return False + + class _ModuleLock: """A recursive lock implementation which is able to detect deadlocks (e.g. thread 1 trying to take locks A then B, and thread 2 trying to @@ -69,33 +142,76 @@ class _ModuleLock: """ def __init__(self, name): - self.lock = _thread.allocate_lock() + # Create an RLock for protecting the import process for the + # corresponding module. Since it is an RLock, a single thread will be + # able to take it more than once. This is necessary to support + # re-entrancy in the import system that arises from (at least) signal + # handlers and the garbage collector. Consider the case of: + # + # import foo + # -> ... + # -> importlib._bootstrap._ModuleLock.acquire + # -> ... + # -> + # -> __del__ + # -> import foo + # -> ... + # -> importlib._bootstrap._ModuleLock.acquire + # -> _BlockingOnManager.__enter__ + # + # If a different thread than the running one holds the lock then the + # thread will have to block on taking the lock, which is what we want + # for thread safety. + self.lock = _thread.RLock() self.wakeup = _thread.allocate_lock() + + # The name of the module for which this is a lock. self.name = name + + # Can end up being set to None if this lock is not owned by any thread + # or the thread identifier for the owning thread. self.owner = None - self.count = 0 - self.waiters = 0 + + # Represent the number of times the owning thread has acquired this lock + # via a list of True. This supports RLock-like ("re-entrant lock") + # behavior, necessary in case a single thread is following a circular + # import dependency and needs to take the lock for a single module + # more than once. + # + # Counts are represented as a list of True because list.append(True) + # and list.pop() are both atomic and thread-safe in CPython and it's hard + # to find another primitive with the same properties. + self.count = [] + + # This is a count of the number of threads that are blocking on + # self.wakeup.acquire() awaiting to get their turn holding this module + # lock. When the module lock is released, if this is greater than + # zero, it is decremented and `self.wakeup` is released one time. The + # intent is that this will let one other thread make more progress on + # acquiring this module lock. This repeats until all the threads have + # gotten a turn. + # + # This is incremented in self.acquire() when a thread notices it is + # going to have to wait for another thread to finish. + # + # See the comment above count for explanation of the representation. + self.waiters = [] def has_deadlock(self): - # Deadlock avoidance for concurrent circular imports. - me = _thread.get_ident() - tid = self.owner - seen = set() - while True: - lock = _blocking_on.get(tid) - if lock is None: - return False - tid = lock.owner - if tid == me: - return True - if tid in seen: - # bpo 38091: the chain of tid's we encounter here - # eventually leads to a fixpoint or a cycle, but - # does not reach 'me'. This means we would not - # actually deadlock. This can happen if other - # threads are at the beginning of acquire() below. - return False - seen.add(tid) + # To avoid deadlocks for concurrent or re-entrant circular imports, + # look at _blocking_on to see if any threads are blocking + # on getting the import lock for any module for which the import lock + # is held by this thread. + return _has_deadlocked( + # Try to find this thread. + target_id=_thread.get_ident(), + seen_ids=set(), + # Start from the thread that holds the import lock for this + # module. + candidate_ids=[self.owner], + # Use the global "blocking on" state. + blocking_on=_blocking_on, + ) def acquire(self): """ @@ -104,35 +220,78 @@ def acquire(self): Otherwise, the lock is always acquired and True is returned. """ tid = _thread.get_ident() - _blocking_on[tid] = self - try: + with _BlockingOnManager(tid, self): while True: + # Protect interaction with state on self with a per-module + # lock. This makes it safe for more than one thread to try to + # acquire the lock for a single module at the same time. with self.lock: - if self.count == 0 or self.owner == tid: + if self.count == [] or self.owner == tid: + # If the lock for this module is unowned then we can + # take the lock immediately and succeed. If the lock + # for this module is owned by the running thread then + # we can also allow the acquire to succeed. This + # supports circular imports (thread T imports module A + # which imports module B which imports module A). self.owner = tid - self.count += 1 + self.count.append(True) return True + + # At this point we know the lock is held (because count != + # 0) by another thread (because owner != tid). We'll have + # to get in line to take the module lock. + + # But first, check to see if this thread would create a + # deadlock by acquiring this module lock. If it would + # then just stop with an error. + # + # It's not clear who is expected to handle this error. + # There is one handler in _lock_unlock_module but many + # times this method is called when entering the context + # manager _ModuleLockManager instead - so _DeadlockError + # will just propagate up to application code. + # + # This seems to be more than just a hypothetical - + # https://stackoverflow.com/questions/59509154 + # https://github.com/encode/django-rest-framework/issues/7078 if self.has_deadlock(): - raise _DeadlockError('deadlock detected by %r' % self) + raise _DeadlockError(f'deadlock detected by {self!r}') + + # Check to see if we're going to be able to acquire the + # lock. If we are going to have to wait then increment + # the waiters so `self.release` will know to unblock us + # later on. We do this part non-blockingly so we don't + # get stuck here before we increment waiters. We have + # this extra acquire call (in addition to the one below, + # outside the self.lock context manager) to make sure + # self.wakeup is held when the next acquire is called (so + # we block). This is probably needlessly complex and we + # should just take self.wakeup in the return codepath + # above. if self.wakeup.acquire(False): - self.waiters += 1 - # Wait for a release() call + self.waiters.append(None) + + # Now take the lock in a blocking fashion. This won't + # complete until the thread holding this lock + # (self.owner) calls self.release. self.wakeup.acquire() + + # Taking the lock has served its purpose (making us wait), so we can + # give it up now. We'll take it w/o blocking again on the + # next iteration around this 'while' loop. self.wakeup.release() - finally: - del _blocking_on[tid] def release(self): tid = _thread.get_ident() with self.lock: if self.owner != tid: raise RuntimeError('cannot release un-acquired lock') - assert self.count > 0 - self.count -= 1 - if self.count == 0: + assert len(self.count) > 0 + self.count.pop() + if not len(self.count): self.owner = None - if self.waiters: - self.waiters -= 1 + if len(self.waiters) > 0: + self.waiters.pop() self.wakeup.release() def __repr__(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-06-09-22-21.gh-issue-91351.iq2vZ_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-06-09-22-21.gh-issue-91351.iq2vZ_.rst new file mode 100644 index 000000000000..19de1f8d0fb3 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-06-09-22-21.gh-issue-91351.iq2vZ_.rst @@ -0,0 +1,5 @@ +Fix a case where re-entrant imports could corrupt the import deadlock +detection code and cause a :exc:`KeyError` to be raised out of +:mod:`importlib/_bootstrap`. In addition to the straightforward cases, this +could also happen when garbage collection leads to a warning being emitted -- +as happens when it collects an open socket or file) From webhook-mailer at python.org Sat Jan 21 00:08:03 2023 From: webhook-mailer at python.org (miss-islington) Date: Sat, 21 Jan 2023 05:08:03 -0000 Subject: [Python-checkins] gh-100750: pass encoding kwarg in lib/platform.py (GH-100751) Message-ID: https://github.com/python/cpython/commit/c6cfde6fb135d45a61670300b91e68805e61042a commit: c6cfde6fb135d45a61670300b91e68805e61042a branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-20T21:07:56-08:00 summary: gh-100750: pass encoding kwarg in lib/platform.py (GH-100751) (cherry picked from commit 6b3993c556eb3bb36d1754a17643cddd3f6ade92) Co-authored-by: Thomas Grainger Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> files: A Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst M Lib/platform.py diff --git a/Lib/platform.py b/Lib/platform.py index 9e9f3af3ce4b..9b9d88bf5844 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -285,6 +285,7 @@ def _syscmd_ver(system='', release='', version='', stdin=subprocess.DEVNULL, stderr=subprocess.DEVNULL, text=True, + encoding="locale", shell=True) except (OSError, subprocess.CalledProcessError) as why: #print('Command %s failed: %s' % (cmd, why)) @@ -762,6 +763,7 @@ def from_subprocess(): ['uname', '-p'], stderr=subprocess.DEVNULL, text=True, + encoding="utf8", ).strip() except (OSError, subprocess.CalledProcessError): pass diff --git a/Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst b/Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst new file mode 100644 index 000000000000..be351532822c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-04-14-42-59.gh-issue-100750.iFJs5Y.rst @@ -0,0 +1 @@ +pass encoding kwarg to subprocess in platform From webhook-mailer at python.org Sat Jan 21 03:54:00 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sat, 21 Jan 2023 08:54:00 -0000 Subject: [Python-checkins] Fix rst formatting in 3.12 What's New (#101110) Message-ID: https://github.com/python/cpython/commit/120cb18c72677be5568e65d5441b08ce30915699 commit: 120cb18c72677be5568e65d5441b08ce30915699 branch: main author: scrazzz committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-21T14:23:54+05:30 summary: Fix rst formatting in 3.12 What's New (#101110) files: M Doc/whatsnew/3.12.rst diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 442a3421f711..4d6d4669d0c7 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -677,13 +677,13 @@ Changes in the Python API contain ASCII letters and digits and underscore. (Contributed by Serhiy Storchaka in :gh:`91760`.) -* Removed randrange() functionality deprecated since Python 3.10. Formerly, - randrange(10.0) losslessly converted to randrange(10). Now, it raises a - TypeError. Also, the exception raised for non-integral values such as - randrange(10.5) or randrange('10') has been changed from ValueError to - TypeError. This also prevents bugs where ``randrange(1e25)`` would silently +* Removed ``randrange()`` functionality deprecated since Python 3.10. Formerly, + ``randrange(10.0)`` losslessly converted to ``randrange(10)``. Now, it raises a + :exc:`TypeError`. Also, the exception raised for non-integral values such as + ``randrange(10.5)`` or ``randrange('10')`` has been changed from :exc:`ValueError` to + :exc:`TypeError`. This also prevents bugs where ``randrange(1e25)`` would silently select from a larger range than ``randrange(10**25)``. - (Originally suggested by Serhiy Storchaka gh-86388.) + (Originally suggested by Serhiy Storchaka :gh:`86388`.) * :class:`argparse.ArgumentParser` changed encoding and error handler for reading arguments from file (e.g. ``fromfile_prefix_chars`` option) From webhook-mailer at python.org Sat Jan 21 04:01:36 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sat, 21 Jan 2023 09:01:36 -0000 Subject: [Python-checkins] Bump Azure Pipelines to ubuntu-22.04 (#101089) Message-ID: https://github.com/python/cpython/commit/c22a55c8b4f142ff679880ec954691d5920b7845 commit: c22a55c8b4f142ff679880ec954691d5920b7845 branch: main author: Hugo van Kemenade committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-21T14:31:31+05:30 summary: Bump Azure Pipelines to ubuntu-22.04 (#101089) files: M .azure-pipelines/ci.yml M .azure-pipelines/pr.yml diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index bf164d19ef22..e45dc2d43659 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -8,7 +8,7 @@ jobs: displayName: Pre-build checks pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./prebuild-checks.yml @@ -20,7 +20,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['docs.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./docs-steps.yml @@ -52,7 +52,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(build.sourceBranchName)-linux' @@ -78,7 +78,7 @@ jobs: ) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' diff --git a/.azure-pipelines/pr.yml b/.azure-pipelines/pr.yml index 3cbd19fda982..af94ebf78c84 100644 --- a/.azure-pipelines/pr.yml +++ b/.azure-pipelines/pr.yml @@ -8,7 +8,7 @@ jobs: displayName: Pre-build checks pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./prebuild-checks.yml @@ -20,7 +20,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['docs.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./docs-steps.yml @@ -52,7 +52,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(system.pullRequest.TargetBranch)-linux' @@ -78,7 +78,7 @@ jobs: ) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' From webhook-mailer at python.org Sat Jan 21 04:11:03 2023 From: webhook-mailer at python.org (ambv) Date: Sat, 21 Jan 2023 09:11:03 -0000 Subject: [Python-checkins] [3.10] gh-95778: add doc missing in some places (GH-100627). (#101065) Message-ID: https://github.com/python/cpython/commit/c77b0ce186d86ceff352795a6cfdb82721e6f0fc commit: c77b0ce186d86ceff352795a6cfdb82721e6f0fc branch: 3.10 author: ?ric committer: ambv date: 2023-01-21T10:10:57+01:00 summary: [3.10] gh-95778: add doc missing in some places (GH-100627). (#101065) (cherry picked from commit 46521826cb1883e29e4640f94089dd92c57efc5b) Co-authored-by: ?ric files: M Misc/python.man diff --git a/Misc/python.man b/Misc/python.man index 6b21fdc8790b..af90747cb671 100644 --- a/Misc/python.man +++ b/Misc/python.man @@ -318,6 +318,10 @@ Set implementation specific option. The following options are available: -X warn_default_encoding: enable opt-in EncodingWarning for 'encoding=None' + -X int_max_str_digits=number: limit the size of int<->str conversions. + This helps avoid denial of service attacks when parsing untrusted data. + The default is sys.int_info.default_max_str_digits. 0 disables. + .TP .B \-x Skip the first line of the source. This is intended for a DOS @@ -487,6 +491,11 @@ values. The integer must be a decimal number in the range [0,4294967295]. Specifying the value 0 will disable hash randomization. +.IP PYTHONINTMAXSTRDIGITS +Limit the maximum digit characters in an int value +when converting from a string and when converting an int back to a str. +A value of 0 disables the limit. Conversions to or from bases 2, 4, 8, +16, and 32 are never limited. .IP PYTHONMALLOC Set the Python memory allocators and/or install debug hooks. The available memory allocators are From webhook-mailer at python.org Sat Jan 21 04:16:13 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sat, 21 Jan 2023 09:16:13 -0000 Subject: [Python-checkins] gh-101143: Remove references to `TimerHandle` from `asyncio.base_events.BaseEventLoop._add_callback` (#101197) Message-ID: https://github.com/python/cpython/commit/9e947675ae3dc32f5863e5ed3022301cf7fd79b4 commit: 9e947675ae3dc32f5863e5ed3022301cf7fd79b4 branch: main author: J. Nick Koston committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-21T14:46:07+05:30 summary: gh-101143: Remove references to `TimerHandle` from `asyncio.base_events.BaseEventLoop._add_callback` (#101197) Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> files: A Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst M Lib/asyncio/base_events.py diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index cbabb43ae060..32d7e1c481ec 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -1857,12 +1857,9 @@ def call_exception_handler(self, context): exc_info=True) def _add_callback(self, handle): - """Add a Handle to _scheduled (TimerHandle) or _ready.""" - assert isinstance(handle, events.Handle), 'A Handle is required here' - if handle._cancelled: - return - assert not isinstance(handle, events.TimerHandle) - self._ready.append(handle) + """Add a Handle to _ready.""" + if not handle._cancelled: + self._ready.append(handle) def _add_callback_signalsafe(self, handle): """Like _add_callback() but called from a signal handler.""" diff --git a/Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst b/Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst new file mode 100644 index 000000000000..d14b9e25a691 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst @@ -0,0 +1,2 @@ +Remove unused references to :class:`~asyncio.TimerHandle` in +``asyncio.base_events.BaseEventLoop._add_callback``. From webhook-mailer at python.org Sat Jan 21 05:24:05 2023 From: webhook-mailer at python.org (mdickinson) Date: Sat, 21 Jan 2023 10:24:05 -0000 Subject: [Python-checkins] gh-101037: Fix potential memory underallocation for zeros of int subtypes (#101038) Message-ID: https://github.com/python/cpython/commit/401fdf9c851eb61229250ebffa942adca99b36d1 commit: 401fdf9c851eb61229250ebffa942adca99b36d1 branch: main author: Mark Dickinson committer: mdickinson date: 2023-01-21T10:23:59Z summary: gh-101037: Fix potential memory underallocation for zeros of int subtypes (#101038) This PR fixes object allocation in long_subtype_new to ensure that there's at least one digit in all cases, and makes sure that the value of that digit is copied over from the source long. Needs backport to 3.11, but not any further: the change to require at least one digit was only introduced for Python 3.11. Fixes #101037. files: A Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst M Include/cpython/longintrepr.h M Objects/longobject.c diff --git a/Include/cpython/longintrepr.h b/Include/cpython/longintrepr.h index 68dbf9c4382d..6d52427508b5 100644 --- a/Include/cpython/longintrepr.h +++ b/Include/cpython/longintrepr.h @@ -71,6 +71,9 @@ typedef long stwodigits; /* signed variant of twodigits */ 0 <= ob_digit[i] <= MASK. The allocation function takes care of allocating extra memory so that ob_digit[0] ... ob_digit[abs(ob_size)-1] are actually available. + We always allocate memory for at least one digit, so accessing ob_digit[0] + is always safe. However, in the case ob_size == 0, the contents of + ob_digit[0] may be undefined. CAUTION: Generic code manipulating subtypes of PyVarObject has to aware that ints abuse ob_size's sign bit. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst new file mode 100644 index 000000000000..a48756657a29 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst @@ -0,0 +1,2 @@ +Fix potential memory underallocation issue for instances of :class:`int` +subclasses with value zero. diff --git a/Objects/longobject.c b/Objects/longobject.c index 1db4ca418e06..51ac86961c99 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5638,6 +5638,11 @@ long_subtype_new(PyTypeObject *type, PyObject *x, PyObject *obase) n = Py_SIZE(tmp); if (n < 0) n = -n; + /* Fast operations for single digit integers (including zero) + * assume that there is always at least one digit present. */ + if (n == 0) { + n = 1; + } newobj = (PyLongObject *)type->tp_alloc(type, n); if (newobj == NULL) { Py_DECREF(tmp); From webhook-mailer at python.org Sat Jan 21 05:28:14 2023 From: webhook-mailer at python.org (miss-islington) Date: Sat, 21 Jan 2023 10:28:14 -0000 Subject: [Python-checkins] gh-101143: Remove references to `TimerHandle` from `asyncio.base_events.BaseEventLoop._add_callback` (GH-101197) Message-ID: https://github.com/python/cpython/commit/99da75e770a7108cd046b103ada27cf31427fb0b commit: 99da75e770a7108cd046b103ada27cf31427fb0b branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-21T02:28:09-08:00 summary: gh-101143: Remove references to `TimerHandle` from `asyncio.base_events.BaseEventLoop._add_callback` (GH-101197) (cherry picked from commit 9e947675ae3dc32f5863e5ed3022301cf7fd79b4) Co-authored-by: J. Nick Koston Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> files: A Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst M Lib/asyncio/base_events.py diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 4692f95b80eb..c5c7bd46ff8b 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -1828,12 +1828,9 @@ def call_exception_handler(self, context): exc_info=True) def _add_callback(self, handle): - """Add a Handle to _scheduled (TimerHandle) or _ready.""" - assert isinstance(handle, events.Handle), 'A Handle is required here' - if handle._cancelled: - return - assert not isinstance(handle, events.TimerHandle) - self._ready.append(handle) + """Add a Handle to _ready.""" + if not handle._cancelled: + self._ready.append(handle) def _add_callback_signalsafe(self, handle): """Like _add_callback() but called from a signal handler.""" diff --git a/Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst b/Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst new file mode 100644 index 000000000000..d14b9e25a691 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst @@ -0,0 +1,2 @@ +Remove unused references to :class:`~asyncio.TimerHandle` in +``asyncio.base_events.BaseEventLoop._add_callback``. From webhook-mailer at python.org Sat Jan 21 05:54:15 2023 From: webhook-mailer at python.org (mdickinson) Date: Sat, 21 Jan 2023 10:54:15 -0000 Subject: [Python-checkins] [3.11] gh-101037: Fix potential memory underallocation for zeros of int subtypes (GH-101038) (#101219) Message-ID: https://github.com/python/cpython/commit/d2aaf818aefb217029371cf660cc10bd63726730 commit: d2aaf818aefb217029371cf660cc10bd63726730 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: mdickinson date: 2023-01-21T10:54:09Z summary: [3.11] gh-101037: Fix potential memory underallocation for zeros of int subtypes (GH-101038) (#101219) gh-101037: Fix potential memory underallocation for zeros of int subtypes (GH-101038) This PR fixes object allocation in long_subtype_new to ensure that there's at least one digit in all cases, and makes sure that the value of that digit is copied over from the source long. Needs backport to 3.11, but not any further: the change to require at least one digit was only introduced for Python 3.11. Fixes GH-101037. (cherry picked from commit 401fdf9c851eb61229250ebffa942adca99b36d1) Co-authored-by: Mark Dickinson files: A Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst M Include/cpython/longintrepr.h M Objects/longobject.c diff --git a/Include/cpython/longintrepr.h b/Include/cpython/longintrepr.h index 68dbf9c4382d..6d52427508b5 100644 --- a/Include/cpython/longintrepr.h +++ b/Include/cpython/longintrepr.h @@ -71,6 +71,9 @@ typedef long stwodigits; /* signed variant of twodigits */ 0 <= ob_digit[i] <= MASK. The allocation function takes care of allocating extra memory so that ob_digit[0] ... ob_digit[abs(ob_size)-1] are actually available. + We always allocate memory for at least one digit, so accessing ob_digit[0] + is always safe. However, in the case ob_size == 0, the contents of + ob_digit[0] may be undefined. CAUTION: Generic code manipulating subtypes of PyVarObject has to aware that ints abuse ob_size's sign bit. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst new file mode 100644 index 000000000000..a48756657a29 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-14-17-03-08.gh-issue-101037.9ATNuf.rst @@ -0,0 +1,2 @@ +Fix potential memory underallocation issue for instances of :class:`int` +subclasses with value zero. diff --git a/Objects/longobject.c b/Objects/longobject.c index c8dd5761a3ce..84c05e8aabdf 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5415,6 +5415,11 @@ long_subtype_new(PyTypeObject *type, PyObject *x, PyObject *obase) n = Py_SIZE(tmp); if (n < 0) n = -n; + /* Fast operations for single digit integers (including zero) + * assume that there is always at least one digit present. */ + if (n == 0) { + n = 1; + } newobj = (PyLongObject *)type->tp_alloc(type, n); if (newobj == NULL) { Py_DECREF(tmp); From webhook-mailer at python.org Sat Jan 21 06:01:21 2023 From: webhook-mailer at python.org (erlend-aasland) Date: Sat, 21 Jan 2023 11:01:21 -0000 Subject: [Python-checkins] GH-92123: Move _elementtree heap types to module state (#101187) Message-ID: https://github.com/python/cpython/commit/13566a37c2f27b7f3a5ef1ecf4288230171b7daf commit: 13566a37c2f27b7f3a5ef1ecf4288230171b7daf branch: main author: Erlend E. Aasland committer: erlend-aasland date: 2023-01-21T12:01:15+01:00 summary: GH-92123: Move _elementtree heap types to module state (#101187) files: M Modules/_elementtree.c M Modules/clinic/_elementtree.c.h M Tools/c-analyzer/cpython/globals-to-fix.tsv diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 93a9d609d7a0..3be098a7dbf6 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -69,13 +69,6 @@ static void _clear_joined_ptr(PyObject **p) } } -/* Types defined by this extension */ -static PyTypeObject *Element_Type; -static PyTypeObject *ElementIter_Type; -static PyTypeObject *TreeBuilder_Type; -static PyTypeObject *XMLParser_Type; - - /* Per-module state; PEP 3121 */ typedef struct { PyObject *parseerror_obj; @@ -92,6 +85,11 @@ typedef struct { PyObject *str_findall; PyObject *str_iterfind; PyObject *str_doctype; + /* Types defined by this extension */ + PyTypeObject *Element_Type; + PyTypeObject *ElementIter_Type; + PyTypeObject *TreeBuilder_Type; + PyTypeObject *XMLParser_Type; } elementtreestate; static struct PyModuleDef elementtreemodule; @@ -132,6 +130,12 @@ elementtree_clear(PyObject *m) Py_CLEAR(st->str_tail); Py_CLEAR(st->str_text); Py_CLEAR(st->str_doctype); + + // Heap types + Py_CLEAR(st->Element_Type); + Py_CLEAR(st->ElementIter_Type); + Py_CLEAR(st->TreeBuilder_Type); + Py_CLEAR(st->XMLParser_Type); return 0; } @@ -144,6 +148,12 @@ elementtree_traverse(PyObject *m, visitproc visit, void *arg) Py_VISIT(st->elementpath_obj); Py_VISIT(st->comment_factory); Py_VISIT(st->pi_factory); + + // Heap types + Py_VISIT(st->Element_Type); + Py_VISIT(st->ElementIter_Type); + Py_VISIT(st->TreeBuilder_Type); + Py_VISIT(st->XMLParser_Type); return 0; } @@ -223,8 +233,8 @@ typedef struct { } ElementObject; -#define Element_CheckExact(op) Py_IS_TYPE(op, Element_Type) -#define Element_Check(op) PyObject_TypeCheck(op, Element_Type) +#define Element_CheckExact(st, op) Py_IS_TYPE(op, (st)->Element_Type) +#define Element_Check(st, op) PyObject_TypeCheck(op, (st)->Element_Type) /* -------------------------------------------------------------------- */ @@ -291,7 +301,8 @@ create_new_element(PyObject* tag, PyObject* attrib) { ElementObject* self; - self = PyObject_GC_New(ElementObject, Element_Type); + elementtreestate *st = ET_STATE_GLOBAL; + self = PyObject_GC_New(ElementObject, st->Element_Type); if (self == NULL) return NULL; self->extra = NULL; @@ -373,11 +384,11 @@ get_attrib_from_keywords(PyObject *kwds) /*[clinic input] module _elementtree -class _elementtree.Element "ElementObject *" "Element_Type" -class _elementtree.TreeBuilder "TreeBuilderObject *" "TreeBuilder_Type" -class _elementtree.XMLParser "XMLParserObject *" "XMLParser_Type" +class _elementtree.Element "ElementObject *" "clinic_state()->Element_Type" +class _elementtree.TreeBuilder "TreeBuilderObject *" "clinic_state()->TreeBuilder_Type" +class _elementtree.XMLParser "XMLParserObject *" "clinic_state()->XMLParser_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=1ecdb32b55d9d5de]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=6c83ea832d2b0ef1]*/ static int element_init(PyObject *self, PyObject *args, PyObject *kwds) @@ -496,8 +507,8 @@ LOCAL(int) element_add_subelement(ElementObject* self, PyObject* element) { /* add a child element to a parent */ - - if (!Element_Check(element)) { + elementtreestate *st = ET_STATE_GLOBAL; + if (!Element_Check(st, element)) { raise_type_error(element); return -1; } @@ -575,11 +586,12 @@ subelement(PyObject *self, PyObject *args, PyObject *kwds) { PyObject* elem; + elementtreestate *st = ET_STATE_GLOBAL; ElementObject* parent; PyObject* tag; PyObject* attrib = NULL; if (!PyArg_ParseTuple(args, "O!O|O!:SubElement", - Element_Type, &parent, &tag, + st->Element_Type, &parent, &tag, &PyDict_Type, &attrib)) { return NULL; } @@ -673,14 +685,14 @@ element_dealloc(ElementObject* self) /*[clinic input] _elementtree.Element.append - subelement: object(subclass_of='Element_Type') + subelement: object(subclass_of='clinic_state()->Element_Type') / [clinic start generated code]*/ static PyObject * _elementtree_Element_append_impl(ElementObject *self, PyObject *subelement) -/*[clinic end generated code: output=54a884b7cf2295f4 input=59866b732e6e2891]*/ +/*[clinic end generated code: output=54a884b7cf2295f4 input=439f2bd777288fb6]*/ { if (element_add_subelement(self, subelement) < 0) return NULL; @@ -806,9 +818,10 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo) if (element_resize(element, self->extra->length) < 0) goto error; + elementtreestate *st = ET_STATE_GLOBAL; for (i = 0; i < self->extra->length; i++) { PyObject* child = deepcopy(self->extra->children[i], memo); - if (!child || !Element_Check(child)) { + if (!child || !Element_Check(st, child)) { if (child) { raise_type_error(child); Py_DECREF(child); @@ -846,7 +859,7 @@ LOCAL(PyObject *) deepcopy(PyObject *object, PyObject *memo) { /* do a deep copy of the given object */ - elementtreestate *st; + elementtreestate *st = ET_STATE_GLOBAL; PyObject *stack[2]; /* Fast paths */ @@ -869,14 +882,13 @@ deepcopy(PyObject *object, PyObject *memo) return PyDict_Copy(object); /* Fall through to general case */ } - else if (Element_CheckExact(object)) { + else if (Element_CheckExact(st, object)) { return _elementtree_Element___deepcopy___impl( (ElementObject *)object, memo); } } /* General case */ - st = ET_STATE_GLOBAL; if (!st->deepcopy_obj) { PyErr_SetString(PyExc_RuntimeError, "deepcopy helper not found"); @@ -1020,9 +1032,10 @@ element_setstate_from_attributes(ElementObject *self, } /* Copy children */ + elementtreestate *st = ET_STATE_GLOBAL; for (i = 0; i < nchildren; i++) { PyObject *child = PyList_GET_ITEM(children, i); - if (!Element_Check(child)) { + if (!Element_Check(st, child)) { raise_type_error(child); self->extra->length = i; dealloc_extra(oldextra); @@ -1220,7 +1233,7 @@ _elementtree_Element_find_impl(ElementObject *self, PyObject *path, for (i = 0; i < self->extra->length; i++) { PyObject* item = self->extra->children[i]; int rc; - assert(Element_Check(item)); + assert(Element_Check(st, item)); Py_INCREF(item); rc = PyObject_RichCompareBool(((ElementObject*)item)->tag, path, Py_EQ); if (rc > 0) @@ -1264,7 +1277,7 @@ _elementtree_Element_findtext_impl(ElementObject *self, PyObject *path, for (i = 0; i < self->extra->length; i++) { PyObject *item = self->extra->children[i]; int rc; - assert(Element_Check(item)); + assert(Element_Check(st, item)); Py_INCREF(item); rc = PyObject_RichCompareBool(((ElementObject*)item)->tag, path, Py_EQ); if (rc > 0) { @@ -1318,7 +1331,7 @@ _elementtree_Element_findall_impl(ElementObject *self, PyObject *path, for (i = 0; i < self->extra->length; i++) { PyObject* item = self->extra->children[i]; int rc; - assert(Element_Check(item)); + assert(Element_Check(st, item)); Py_INCREF(item); rc = PyObject_RichCompareBool(((ElementObject*)item)->tag, path, Py_EQ); if (rc != 0 && (rc < 0 || PyList_Append(out, item) < 0)) { @@ -1440,7 +1453,7 @@ element_getitem(PyObject* self_, Py_ssize_t index) _elementtree.Element.insert index: Py_ssize_t - subelement: object(subclass_of='Element_Type') + subelement: object(subclass_of='clinic_state()->Element_Type') / [clinic start generated code]*/ @@ -1448,7 +1461,7 @@ _elementtree.Element.insert static PyObject * _elementtree_Element_insert_impl(ElementObject *self, Py_ssize_t index, PyObject *subelement) -/*[clinic end generated code: output=990adfef4d424c0b input=4382c42ab2659f9b]*/ +/*[clinic end generated code: output=990adfef4d424c0b input=9530f4905aa401ca]*/ { Py_ssize_t i; @@ -1547,14 +1560,14 @@ _elementtree_Element_makeelement_impl(ElementObject *self, PyObject *tag, /*[clinic input] _elementtree.Element.remove - subelement: object(subclass_of='Element_Type') + subelement: object(subclass_of='clinic_state()->Element_Type') / [clinic start generated code]*/ static PyObject * _elementtree_Element_remove_impl(ElementObject *self, PyObject *subelement) -/*[clinic end generated code: output=38fe6c07d6d87d1f input=cbdf9f2ab34d93b0]*/ +/*[clinic end generated code: output=38fe6c07d6d87d1f input=6133e1d05597d5ee]*/ { Py_ssize_t i; int rc; @@ -1668,7 +1681,8 @@ element_setitem(PyObject* self_, Py_ssize_t index, PyObject* item) old = self->extra->children[index]; if (item) { - if (!Element_Check(item)) { + elementtreestate *st = ET_STATE_GLOBAL; + if (!Element_Check(st, item)) { raise_type_error(item); return -1; } @@ -1865,9 +1879,10 @@ element_ass_subscr(PyObject* self_, PyObject* item, PyObject* value) } } + elementtreestate *st = ET_STATE_GLOBAL; for (i = 0; i < newlen; i++) { PyObject *element = PySequence_Fast_GET_ITEM(seq, i); - if (!Element_Check(element)) { + if (!Element_Check(st, element)) { raise_type_error(element); Py_DECREF(seq); return -1; @@ -2140,7 +2155,10 @@ elementiter_next(ElementIterObject *it) continue; } - assert(Element_Check(extra->children[child_index])); +#ifndef NDEBUG + elementtreestate *st = ET_STATE_GLOBAL; + assert(Element_Check(st, extra->children[child_index])); +#endif elem = (ElementObject *)Py_NewRef(extra->children[child_index]); item->child_index++; } @@ -2215,7 +2233,8 @@ create_elementiter(ElementObject *self, PyObject *tag, int gettext) { ElementIterObject *it; - it = PyObject_GC_New(ElementIterObject, ElementIter_Type); + elementtreestate *st = ET_STATE_GLOBAL; + it = PyObject_GC_New(ElementIterObject, st->ElementIter_Type); if (!it) return NULL; @@ -2272,7 +2291,7 @@ typedef struct { char insert_pis; } TreeBuilderObject; -#define TreeBuilder_CheckExact(op) Py_IS_TYPE((op), TreeBuilder_Type) +#define TreeBuilder_CheckExact(st, op) Py_IS_TYPE((op), (st)->TreeBuilder_Type) /* -------------------------------------------------------------------- */ /* constructor and destructor */ @@ -2472,7 +2491,8 @@ treebuilder_extend_element_text_or_tail(PyObject *element, PyObject **data, PyObject **dest, PyObject *name) { /* Fast paths for the "almost always" cases. */ - if (Element_CheckExact(element)) { + elementtreestate *st = ET_STATE_GLOBAL; + if (Element_CheckExact(st, element)) { PyObject *dest_obj = JOIN_OBJ(*dest); if (dest_obj == Py_None) { *dest = JOIN_SET(*data, PyList_CheckExact(*data)); @@ -2546,7 +2566,7 @@ static int treebuilder_add_subelement(PyObject *element, PyObject *child) { elementtreestate *st = ET_STATE_GLOBAL; - if (Element_CheckExact(element)) { + if (Element_CheckExact(st, element)) { ElementObject *elem = (ElementObject *) element; return element_add_subelement(elem, child); } @@ -3121,7 +3141,8 @@ expat_default_handler(XMLParserObject* self, const XML_Char* data_in, value = PyDict_GetItemWithError(self->entity, key); if (value) { - if (TreeBuilder_CheckExact(self->target)) + elementtreestate *st = ET_STATE_GLOBAL; + if (TreeBuilder_CheckExact(st, self->target)) res = treebuilder_handle_data( (TreeBuilderObject*) self->target, value ); @@ -3193,7 +3214,8 @@ expat_start_handler(XMLParserObject* self, const XML_Char* tag_in, attrib = NULL; } - if (TreeBuilder_CheckExact(self->target)) { + elementtreestate *st = ET_STATE_GLOBAL; + if (TreeBuilder_CheckExact(st, self->target)) { /* shortcut */ res = treebuilder_handle_start((TreeBuilderObject*) self->target, tag, attrib); @@ -3231,7 +3253,8 @@ expat_data_handler(XMLParserObject* self, const XML_Char* data_in, if (!data) return; /* parser will look for errors */ - if (TreeBuilder_CheckExact(self->target)) + elementtreestate *st = ET_STATE_GLOBAL; + if (TreeBuilder_CheckExact(st, self->target)) /* shortcut */ res = treebuilder_handle_data((TreeBuilderObject*) self->target, data); else if (self->handle_data) @@ -3253,7 +3276,8 @@ expat_end_handler(XMLParserObject* self, const XML_Char* tag_in) if (PyErr_Occurred()) return; - if (TreeBuilder_CheckExact(self->target)) + elementtreestate *st = ET_STATE_GLOBAL; + if (TreeBuilder_CheckExact(st, self->target)) /* shortcut */ /* the standard tree builder doesn't look at the end tag */ res = treebuilder_handle_end( @@ -3287,7 +3311,8 @@ expat_start_ns_handler(XMLParserObject* self, const XML_Char* prefix_in, if (!prefix_in) prefix_in = ""; - if (TreeBuilder_CheckExact(self->target)) { + elementtreestate *st = ET_STATE_GLOBAL; + if (TreeBuilder_CheckExact(st, self->target)) { /* shortcut - TreeBuilder does not actually implement .start_ns() */ TreeBuilderObject *target = (TreeBuilderObject*) self->target; @@ -3337,7 +3362,8 @@ expat_end_ns_handler(XMLParserObject* self, const XML_Char* prefix_in) if (!prefix_in) prefix_in = ""; - if (TreeBuilder_CheckExact(self->target)) { + elementtreestate *st = ET_STATE_GLOBAL; + if (TreeBuilder_CheckExact(st, self->target)) { /* shortcut - TreeBuilder does not actually implement .end_ns() */ TreeBuilderObject *target = (TreeBuilderObject*) self->target; @@ -3365,7 +3391,8 @@ expat_comment_handler(XMLParserObject* self, const XML_Char* comment_in) if (PyErr_Occurred()) return; - if (TreeBuilder_CheckExact(self->target)) { + elementtreestate *st = ET_STATE_GLOBAL; + if (TreeBuilder_CheckExact(st, self->target)) { /* shortcut */ TreeBuilderObject *target = (TreeBuilderObject*) self->target; @@ -3458,7 +3485,8 @@ expat_pi_handler(XMLParserObject* self, const XML_Char* target_in, if (PyErr_Occurred()) return; - if (TreeBuilder_CheckExact(self->target)) { + elementtreestate *st = ET_STATE_GLOBAL; + if (TreeBuilder_CheckExact(st, self->target)) { /* shortcut */ TreeBuilderObject *target = (TreeBuilderObject*) self->target; @@ -3566,7 +3594,8 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *target, if (target != Py_None) { Py_INCREF(target); } else { - target = treebuilder_new(TreeBuilder_Type, NULL, NULL); + elementtreestate *st = ET_STATE_GLOBAL; + target = treebuilder_new(st->TreeBuilder_Type, NULL, NULL); if (!target) { Py_CLEAR(self->entity); Py_CLEAR(self->names); @@ -3767,7 +3796,8 @@ _elementtree_XMLParser_close_impl(XMLParserObject *self) if (!res) return NULL; - if (TreeBuilder_CheckExact(self->target)) { + elementtreestate *st = ET_STATE_GLOBAL; + if (TreeBuilder_CheckExact(st, self->target)) { Py_DECREF(res); return treebuilder_done((TreeBuilderObject*) self->target); } @@ -3906,7 +3936,8 @@ _elementtree_XMLParser__parse_whole(XMLParserObject *self, PyObject *file) res = expat_parse(self, "", 0, 1); - if (res && TreeBuilder_CheckExact(self->target)) { + elementtreestate *st = ET_STATE_GLOBAL; + if (res && TreeBuilder_CheckExact(st, self->target)) { Py_DECREF(res); return treebuilder_done((TreeBuilderObject*) self->target); } @@ -3937,7 +3968,8 @@ _elementtree_XMLParser__setevents_impl(XMLParserObject *self, if (!_check_xmlparser(self)) { return NULL; } - if (!TreeBuilder_CheckExact(self->target)) { + elementtreestate *st = ET_STATE_GLOBAL; + if (!TreeBuilder_CheckExact(st, self->target)) { PyErr_SetString( PyExc_TypeError, "event handling only supported for ElementTree.TreeBuilder " @@ -4046,7 +4078,9 @@ static PyGetSetDef xmlparser_getsetlist[] = { {NULL}, }; +#define clinic_state() (ET_STATE_GLOBAL) #include "clinic/_elementtree.c.h" +#undef clinic_state static PyMethodDef element_methods[] = { @@ -4243,10 +4277,10 @@ PyInit__elementtree(void) st = get_elementtree_state(m); /* Initialize object types */ - CREATE_TYPE(m, ElementIter_Type, &elementiter_spec); - CREATE_TYPE(m, TreeBuilder_Type, &treebuilder_spec); - CREATE_TYPE(m, Element_Type, &element_spec); - CREATE_TYPE(m, XMLParser_Type, &xmlparser_spec); + CREATE_TYPE(m, st->ElementIter_Type, &elementiter_spec); + CREATE_TYPE(m, st->TreeBuilder_Type, &treebuilder_spec); + CREATE_TYPE(m, st->Element_Type, &element_spec); + CREATE_TYPE(m, st->XMLParser_Type, &xmlparser_spec); st->deepcopy_obj = _PyImport_GetModuleAttrString("copy", "deepcopy"); if (st->deepcopy_obj == NULL) { @@ -4314,9 +4348,9 @@ PyInit__elementtree(void) } PyTypeObject *types[] = { - Element_Type, - TreeBuilder_Type, - XMLParser_Type + st->Element_Type, + st->TreeBuilder_Type, + st->XMLParser_Type }; for (size_t i = 0; i < Py_ARRAY_LENGTH(types); i++) { diff --git a/Modules/clinic/_elementtree.c.h b/Modules/clinic/_elementtree.c.h index b816e1bd8559..4bf342c8b60d 100644 --- a/Modules/clinic/_elementtree.c.h +++ b/Modules/clinic/_elementtree.c.h @@ -25,8 +25,8 @@ _elementtree_Element_append(ElementObject *self, PyObject *arg) PyObject *return_value = NULL; PyObject *subelement; - if (!PyObject_TypeCheck(arg, Element_Type)) { - _PyArg_BadArgument("append", "argument", (Element_Type)->tp_name, arg); + if (!PyObject_TypeCheck(arg, clinic_state()->Element_Type)) { + _PyArg_BadArgument("append", "argument", (clinic_state()->Element_Type)->tp_name, arg); goto exit; } subelement = arg; @@ -586,8 +586,8 @@ _elementtree_Element_insert(ElementObject *self, PyObject *const *args, Py_ssize } index = ival; } - if (!PyObject_TypeCheck(args[1], Element_Type)) { - _PyArg_BadArgument("insert", "argument 2", (Element_Type)->tp_name, args[1]); + if (!PyObject_TypeCheck(args[1], clinic_state()->Element_Type)) { + _PyArg_BadArgument("insert", "argument 2", (clinic_state()->Element_Type)->tp_name, args[1]); goto exit; } subelement = args[1]; @@ -682,8 +682,8 @@ _elementtree_Element_remove(ElementObject *self, PyObject *arg) PyObject *return_value = NULL; PyObject *subelement; - if (!PyObject_TypeCheck(arg, Element_Type)) { - _PyArg_BadArgument("remove", "argument", (Element_Type)->tp_name, arg); + if (!PyObject_TypeCheck(arg, clinic_state()->Element_Type)) { + _PyArg_BadArgument("remove", "argument", (clinic_state()->Element_Type)->tp_name, arg); goto exit; } subelement = arg; @@ -1105,4 +1105,4 @@ _elementtree_XMLParser__setevents(XMLParserObject *self, PyObject *const *args, exit: return return_value; } -/*[clinic end generated code: output=db3d6654de9f8013 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d380adb43d8f4a62 input=a9049054013a1b77]*/ diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index cd08782edce4..761a2ab43e6c 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -425,10 +425,6 @@ Modules/_decimal/_decimal.c - PyDecContextManager_Type - Modules/_decimal/_decimal.c - PyDecContext_Type - Modules/_decimal/_decimal.c - PyDecSignalDictMixin_Type - Modules/_decimal/_decimal.c - PyDec_Type - -Modules/_elementtree.c - ElementIter_Type - -Modules/_elementtree.c - Element_Type - -Modules/_elementtree.c - TreeBuilder_Type - -Modules/_elementtree.c - XMLParser_Type - Modules/_pickle.c - Pdata_Type - Modules/_pickle.c - PicklerMemoProxyType - Modules/_pickle.c - Pickler_Type - From webhook-mailer at python.org Sat Jan 21 06:25:28 2023 From: webhook-mailer at python.org (miss-islington) Date: Sat, 21 Jan 2023 11:25:28 -0000 Subject: [Python-checkins] gh-101143: Remove references to `TimerHandle` from `asyncio.base_events.BaseEventLoop._add_callback` (GH-101197) Message-ID: https://github.com/python/cpython/commit/9532c04151a088c3da4757272fa9bee715bb9388 commit: 9532c04151a088c3da4757272fa9bee715bb9388 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-21T03:25:22-08:00 summary: gh-101143: Remove references to `TimerHandle` from `asyncio.base_events.BaseEventLoop._add_callback` (GH-101197) (cherry picked from commit 9e947675ae3dc32f5863e5ed3022301cf7fd79b4) Co-authored-by: J. Nick Koston Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> files: A Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst M Lib/asyncio/base_events.py diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index f4c30c1551a3..0890e9e1da69 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -1815,12 +1815,9 @@ def call_exception_handler(self, context): exc_info=True) def _add_callback(self, handle): - """Add a Handle to _scheduled (TimerHandle) or _ready.""" - assert isinstance(handle, events.Handle), 'A Handle is required here' - if handle._cancelled: - return - assert not isinstance(handle, events.TimerHandle) - self._ready.append(handle) + """Add a Handle to _ready.""" + if not handle._cancelled: + self._ready.append(handle) def _add_callback_signalsafe(self, handle): """Like _add_callback() but called from a signal handler.""" diff --git a/Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst b/Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst new file mode 100644 index 000000000000..d14b9e25a691 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-20-10-46-59.gh-issue-101143.hJo8hu.rst @@ -0,0 +1,2 @@ +Remove unused references to :class:`~asyncio.TimerHandle` in +``asyncio.base_events.BaseEventLoop._add_callback``. From webhook-mailer at python.org Sat Jan 21 08:44:48 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sat, 21 Jan 2023 13:44:48 -0000 Subject: [Python-checkins] gh-99266: ctypes: Preserve more detailed exception in `ArgumentError` Message-ID: https://github.com/python/cpython/commit/b4e11a7985a3bc116596c63d1e5f8bbd653041b9 commit: b4e11a7985a3bc116596c63d1e5f8bbd653041b9 branch: main author: Kamil Turek committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-21T19:14:43+05:30 summary: gh-99266: ctypes: Preserve more detailed exception in `ArgumentError` Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> files: A Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst M Doc/library/ctypes.rst M Lib/test/test_ctypes/test_functions.py M Lib/test/test_ctypes/test_parameters.py M Modules/_ctypes/_ctypes.c diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 50ab29375623..4de5c820f2c6 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -466,6 +466,14 @@ integer, string, bytes, a :mod:`ctypes` instance, or an object with an Return types ^^^^^^^^^^^^ +.. testsetup:: + + from ctypes import CDLL, c_char, c_char_p + from ctypes.util import find_library + libc = CDLL(find_library('c')) + strchr = libc.strchr + + By default functions are assumed to return the C :c:expr:`int` type. Other return types can be specified by setting the :attr:`restype` attribute of the function object. @@ -502,18 +510,19 @@ If you want to avoid the ``ord("x")`` calls above, you can set the :attr:`argtypes` attribute, and the second argument will be converted from a single character Python bytes object into a C char:: +.. doctest:: + >>> strchr.restype = c_char_p >>> strchr.argtypes = [c_char_p, c_char] >>> strchr(b"abcdef", b"d") - 'def' + b'def' >>> strchr(b"abcdef", b"def") Traceback (most recent call last): - File "", line 1, in - ArgumentError: argument 2: TypeError: one character string expected + ctypes.ArgumentError: argument 2: TypeError: one character bytes, bytearray or integer expected >>> print(strchr(b"abcdef", b"x")) None >>> strchr(b"abcdef", b"d") - 'def' + b'def' >>> You can also use a callable Python object (a function or a class for example) as diff --git a/Lib/test/test_ctypes/test_functions.py b/Lib/test/test_ctypes/test_functions.py index 95633dfa8b38..703bd2c601cc 100644 --- a/Lib/test/test_ctypes/test_functions.py +++ b/Lib/test/test_ctypes/test_functions.py @@ -54,6 +54,23 @@ class X(object, _SimpleCData): class X(object, Structure): _fields_ = [] + def test_c_char_parm(self): + proto = CFUNCTYPE(c_int, c_char) + def callback(*args): + return 0 + + callback = proto(callback) + + self.assertEqual(callback(b"a"), 0) + + with self.assertRaises(ArgumentError) as cm: + callback(b"abc") + + self.assertEqual(str(cm.exception), + "argument 1: TypeError: one character bytes, " + "bytearray or integer expected") + + @need_symbol('c_wchar') def test_wchar_parm(self): f = dll._testfunc_i_bhilfd @@ -62,6 +79,18 @@ def test_wchar_parm(self): self.assertEqual(result, 139) self.assertEqual(type(result), int) + with self.assertRaises(ArgumentError) as cm: + f(1, 2, 3, 4, 5.0, 6.0) + self.assertEqual(str(cm.exception), + "argument 2: TypeError: unicode string expected " + "instead of int instance") + + with self.assertRaises(ArgumentError) as cm: + f(1, "abc", 3, 4, 5.0, 6.0) + self.assertEqual(str(cm.exception), + "argument 2: TypeError: one character unicode string " + "expected") + @need_symbol('c_wchar') def test_wchar_result(self): f = dll._testfunc_i_bhilfd diff --git a/Lib/test/test_ctypes/test_parameters.py b/Lib/test/test_ctypes/test_parameters.py index 84839d9c6a96..22d290db1bcb 100644 --- a/Lib/test/test_ctypes/test_parameters.py +++ b/Lib/test/test_ctypes/test_parameters.py @@ -78,6 +78,29 @@ def test_cw_strings(self): pa = c_wchar_p.from_param(c_wchar_p("123")) self.assertEqual(type(pa), c_wchar_p) + def test_c_char(self): + from ctypes import c_char + + with self.assertRaises(TypeError) as cm: + c_char.from_param(b"abc") + self.assertEqual(str(cm.exception), + "one character bytes, bytearray or integer expected") + + @need_symbol('c_wchar') + def test_c_wchar(self): + from ctypes import c_wchar + + with self.assertRaises(TypeError) as cm: + c_wchar.from_param("abc") + self.assertEqual(str(cm.exception), + "one character unicode string expected") + + + with self.assertRaises(TypeError) as cm: + c_wchar.from_param(123) + self.assertEqual(str(cm.exception), + "unicode string expected instead of int instance") + def test_int_pointers(self): from ctypes import c_short, c_uint, c_int, c_long, POINTER, pointer LPINT = POINTER(c_int) diff --git a/Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst b/Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst new file mode 100644 index 000000000000..97e9569e40a9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-11-24-21-52-31.gh-issue-99266.88GcV9.rst @@ -0,0 +1 @@ +Preserve more detailed error messages in :mod:`ctypes`. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 4ce6433a2e45..272cafb5a9a3 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -2197,6 +2197,7 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value) struct fielddesc *fd; PyObject *as_parameter; int res; + PyObject *exc, *val, *tb; /* If the value is already an instance of the requested type, we can use it as is */ @@ -2230,24 +2231,37 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value) parg->obj = fd->setfunc(&parg->value, value, 0); if (parg->obj) return (PyObject *)parg; - PyErr_Clear(); + PyErr_Fetch(&exc, &val, &tb); Py_DECREF(parg); if (_PyObject_LookupAttr(value, &_Py_ID(_as_parameter_), &as_parameter) < 0) { + Py_XDECREF(exc); + Py_XDECREF(val); + Py_XDECREF(tb); return NULL; } if (as_parameter) { if (_Py_EnterRecursiveCall("while processing _as_parameter_")) { Py_DECREF(as_parameter); + Py_XDECREF(exc); + Py_XDECREF(val); + Py_XDECREF(tb); return NULL; } value = PyCSimpleType_from_param(type, as_parameter); _Py_LeaveRecursiveCall(); Py_DECREF(as_parameter); + Py_XDECREF(exc); + Py_XDECREF(val); + Py_XDECREF(tb); return value; } - PyErr_SetString(PyExc_TypeError, - "wrong type"); + if (exc) { + PyErr_Restore(exc, val, tb); + } + else { + PyErr_SetString(PyExc_TypeError, "wrong type"); + } return NULL; } From webhook-mailer at python.org Sat Jan 21 09:39:43 2023 From: webhook-mailer at python.org (miss-islington) Date: Sat, 21 Jan 2023 14:39:43 -0000 Subject: [Python-checkins] Bump Azure Pipelines to ubuntu-22.04 (GH-101089) Message-ID: https://github.com/python/cpython/commit/8cf8a6820e3fa885ac6159b8002f5e8d7834ca83 commit: 8cf8a6820e3fa885ac6159b8002f5e8d7834ca83 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-21T06:39:37-08:00 summary: Bump Azure Pipelines to ubuntu-22.04 (GH-101089) (cherry picked from commit c22a55c8b4f142ff679880ec954691d5920b7845) Co-authored-by: Hugo van Kemenade files: M .azure-pipelines/ci.yml M .azure-pipelines/pr.yml diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index bf164d19ef22..e45dc2d43659 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -8,7 +8,7 @@ jobs: displayName: Pre-build checks pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./prebuild-checks.yml @@ -20,7 +20,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['docs.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./docs-steps.yml @@ -52,7 +52,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(build.sourceBranchName)-linux' @@ -78,7 +78,7 @@ jobs: ) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' diff --git a/.azure-pipelines/pr.yml b/.azure-pipelines/pr.yml index 3cbd19fda982..af94ebf78c84 100644 --- a/.azure-pipelines/pr.yml +++ b/.azure-pipelines/pr.yml @@ -8,7 +8,7 @@ jobs: displayName: Pre-build checks pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./prebuild-checks.yml @@ -20,7 +20,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['docs.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./docs-steps.yml @@ -52,7 +52,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(system.pullRequest.TargetBranch)-linux' @@ -78,7 +78,7 @@ jobs: ) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' From webhook-mailer at python.org Sat Jan 21 14:33:15 2023 From: webhook-mailer at python.org (mdickinson) Date: Sat, 21 Jan 2023 19:33:15 -0000 Subject: [Python-checkins] gh-100726: Optimize construction of range object for medium sized integers (#100810) Message-ID: https://github.com/python/cpython/commit/f63f525e161204970418ebc132efc542daaa24ed commit: f63f525e161204970418ebc132efc542daaa24ed branch: main author: Pieter Eendebak committer: mdickinson date: 2023-01-21T19:33:08Z summary: gh-100726: Optimize construction of range object for medium sized integers (#100810) Use C long arithmetic instead of PyLong arithmetic to compute the range length, where possible. Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Mark Dickinson files: A Misc/NEWS.d/next/Core and Builtins/2023-01-03-20-59-20.gh-issue-100726.W9huFl.rst M Lib/test/test_range.py M Objects/rangeobject.c diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index 7be76b32ac29..3870b153688b 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -542,6 +542,7 @@ def test_range_iterators(self): for start in limits for end in limits for step in (-2**63, -2**31, -2, -1, 1, 2)] + test_ranges += [(-2**63, 2**63-2, 1)] # regression test for gh-100810 for start, end, step in test_ranges: iter1 = range(start, end, step) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-03-20-59-20.gh-issue-100726.W9huFl.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-20-59-20.gh-issue-100726.W9huFl.rst new file mode 100644 index 000000000000..2c93098b347a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-03-20-59-20.gh-issue-100726.W9huFl.rst @@ -0,0 +1 @@ +Optimize construction of ``range`` object for medium size integers. diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index 992e7c079ded..b4d0bbf32c84 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -171,6 +171,49 @@ range_dealloc(rangeobject *r) PyObject_Free(r); } +static unsigned long +get_len_of_range(long lo, long hi, long step); + +/* Return the length as a long, -2 for an overflow and -1 for any other type of error + * + * In case of an overflow no error is set + */ +static long compute_range_length_long(PyObject *start, + PyObject *stop, PyObject *step) { + int overflow = 0; + + long long_start = PyLong_AsLongAndOverflow(start, &overflow); + if (overflow) { + return -2; + } + if (long_start == -1 && PyErr_Occurred()) { + return -1; + } + long long_stop = PyLong_AsLongAndOverflow(stop, &overflow); + if (overflow) { + return -2; + } + if (long_stop == -1 && PyErr_Occurred()) { + return -1; + } + long long_step = PyLong_AsLongAndOverflow(step, &overflow); + if (overflow) { + return -2; + } + if (long_step == -1 && PyErr_Occurred()) { + return -1; + } + + unsigned long ulen = get_len_of_range(long_start, long_stop, long_step); + if (ulen > (unsigned long)LONG_MAX) { + /* length too large for a long */ + return -2; + } + else { + return (long)ulen; + } +} + /* Return number of items in range (lo, hi, step) as a PyLong object, * when arguments are PyLong objects. Arguments MUST return 1 with * PyLong_Check(). Return NULL when there is an error. @@ -191,6 +234,21 @@ compute_range_length(PyObject *start, PyObject *stop, PyObject *step) PyObject *zero = _PyLong_GetZero(); // borrowed reference PyObject *one = _PyLong_GetOne(); // borrowed reference + assert(PyLong_Check(start)); + assert(PyLong_Check(stop)); + assert(PyLong_Check(step)); + + /* fast path when all arguments fit into a long integer */ + long len = compute_range_length_long(start, stop, step); + if (len >= 0) { + return PyLong_FromLong(len); + } + else if (len == -1) { + /* unexpected error from compute_range_length_long, we propagate to the caller */ + return NULL; + } + assert(len == -2); + cmp_result = PyObject_RichCompareBool(step, zero, Py_GT); if (cmp_result == -1) return NULL; From webhook-mailer at python.org Sat Jan 21 14:38:42 2023 From: webhook-mailer at python.org (ambv) Date: Sat, 21 Jan 2023 19:38:42 -0000 Subject: [Python-checkins] [3.10] Bump Azure Pipelines to ubuntu-22.04 (GH-101089) (#101213) Message-ID: https://github.com/python/cpython/commit/caa6bcfb8c85e79df1ed55e2de971ec15a2e3886 commit: caa6bcfb8c85e79df1ed55e2de971ec15a2e3886 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: ambv date: 2023-01-21T20:38:36+01:00 summary: [3.10] Bump Azure Pipelines to ubuntu-22.04 (GH-101089) (#101213) Bump Azure Pipelines to ubuntu-22.04 (GH-101089) (cherry picked from commit c22a55c8b4f142ff679880ec954691d5920b7845) Co-authored-by: Hugo van Kemenade files: M .azure-pipelines/ci.yml M .azure-pipelines/pr.yml diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index 8dd79eeccdb0..133699d46535 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -8,7 +8,7 @@ jobs: displayName: Pre-build checks pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./prebuild-checks.yml @@ -20,7 +20,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['docs.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./docs-steps.yml @@ -52,7 +52,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(build.sourceBranchName)-linux' @@ -78,7 +78,7 @@ jobs: ) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' diff --git a/.azure-pipelines/pr.yml b/.azure-pipelines/pr.yml index e1b282be129d..d84684e4c030 100644 --- a/.azure-pipelines/pr.yml +++ b/.azure-pipelines/pr.yml @@ -8,7 +8,7 @@ jobs: displayName: Pre-build checks pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./prebuild-checks.yml @@ -20,7 +20,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['docs.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./docs-steps.yml @@ -52,7 +52,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(system.pullRequest.TargetBranch)-linux' @@ -78,7 +78,7 @@ jobs: ) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' From webhook-mailer at python.org Sat Jan 21 14:38:57 2023 From: webhook-mailer at python.org (ambv) Date: Sat, 21 Jan 2023 19:38:57 -0000 Subject: [Python-checkins] [3.9] Bump Azure Pipelines to ubuntu-22.04 (GH-101089) (#101214) Message-ID: https://github.com/python/cpython/commit/044fb4fb53594b37de8188cb36f3ba33ce2d617e commit: 044fb4fb53594b37de8188cb36f3ba33ce2d617e branch: 3.9 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: ambv date: 2023-01-21T20:38:52+01:00 summary: [3.9] Bump Azure Pipelines to ubuntu-22.04 (GH-101089) (#101214) (cherry picked from commit c22a55c8b4f142ff679880ec954691d5920b7845) Co-authored-by: Hugo van Kemenade files: M .azure-pipelines/ci.yml M .azure-pipelines/pr.yml diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index d0efa77f9305..39ad0e6b3ff7 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -8,7 +8,7 @@ jobs: displayName: Pre-build checks pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./prebuild-checks.yml @@ -20,7 +20,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['docs.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./docs-steps.yml @@ -52,7 +52,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(build.sourceBranchName)-linux' @@ -78,7 +78,7 @@ jobs: ) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' diff --git a/.azure-pipelines/pr.yml b/.azure-pipelines/pr.yml index a4f32460c7ea..84d01806a452 100644 --- a/.azure-pipelines/pr.yml +++ b/.azure-pipelines/pr.yml @@ -8,7 +8,7 @@ jobs: displayName: Pre-build checks pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./prebuild-checks.yml @@ -20,7 +20,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['docs.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./docs-steps.yml @@ -52,7 +52,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(system.pullRequest.TargetBranch)-linux' @@ -78,7 +78,7 @@ jobs: ) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' From webhook-mailer at python.org Sat Jan 21 14:41:04 2023 From: webhook-mailer at python.org (ambv) Date: Sat, 21 Jan 2023 19:41:04 -0000 Subject: [Python-checkins] [3.8] Bump Azure Pipelines to ubuntu-22.04 (GH-101089) (#101215) Message-ID: https://github.com/python/cpython/commit/e57a3c11152210bf76c747e378a6d46e074e80cf commit: e57a3c11152210bf76c747e378a6d46e074e80cf branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: ambv date: 2023-01-21T20:40:58+01:00 summary: [3.8] Bump Azure Pipelines to ubuntu-22.04 (GH-101089) (#101215) (cherry picked from commit c22a55c8b4f142ff679880ec954691d5920b7845) Co-authored-by: Hugo van Kemenade files: M .azure-pipelines/ci.yml M .azure-pipelines/pr.yml diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index d0efa77f9305..39ad0e6b3ff7 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -8,7 +8,7 @@ jobs: displayName: Pre-build checks pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./prebuild-checks.yml @@ -20,7 +20,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['docs.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./docs-steps.yml @@ -52,7 +52,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(build.sourceBranchName)-linux' @@ -78,7 +78,7 @@ jobs: ) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' diff --git a/.azure-pipelines/pr.yml b/.azure-pipelines/pr.yml index a4f32460c7ea..84d01806a452 100644 --- a/.azure-pipelines/pr.yml +++ b/.azure-pipelines/pr.yml @@ -8,7 +8,7 @@ jobs: displayName: Pre-build checks pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./prebuild-checks.yml @@ -20,7 +20,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['docs.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 steps: - template: ./docs-steps.yml @@ -52,7 +52,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(system.pullRequest.TargetBranch)-linux' @@ -78,7 +78,7 @@ jobs: ) pool: - vmImage: ubuntu-20.04 + vmImage: ubuntu-22.04 variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' From webhook-mailer at python.org Sat Jan 21 16:44:49 2023 From: webhook-mailer at python.org (pablogsal) Date: Sat, 21 Jan 2023 21:44:49 -0000 Subject: [Python-checkins] gh-100518: Add tests for `ast.NodeTransformer` (#100521) Message-ID: https://github.com/python/cpython/commit/c1c5882359a2899b74c1685a0d4e61d6e232161f commit: c1c5882359a2899b74c1685a0d4e61d6e232161f branch: main author: Nikita Sobolev committer: pablogsal date: 2023-01-21T21:44:41Z summary: gh-100518: Add tests for `ast.NodeTransformer` (#100521) files: A Lib/test/support/ast_helper.py M Lib/test/test_ast.py M Lib/test/test_unparse.py diff --git a/Lib/test/support/ast_helper.py b/Lib/test/support/ast_helper.py new file mode 100644 index 000000000000..8a0415b6aae3 --- /dev/null +++ b/Lib/test/support/ast_helper.py @@ -0,0 +1,43 @@ +import ast + +class ASTTestMixin: + """Test mixing to have basic assertions for AST nodes.""" + + def assertASTEqual(self, ast1, ast2): + # Ensure the comparisons start at an AST node + self.assertIsInstance(ast1, ast.AST) + self.assertIsInstance(ast2, ast.AST) + + # An AST comparison routine modeled after ast.dump(), but + # instead of string building, it traverses the two trees + # in lock-step. + def traverse_compare(a, b, missing=object()): + if type(a) is not type(b): + self.fail(f"{type(a)!r} is not {type(b)!r}") + if isinstance(a, ast.AST): + for field in a._fields: + value1 = getattr(a, field, missing) + value2 = getattr(b, field, missing) + # Singletons are equal by definition, so further + # testing can be skipped. + if value1 is not value2: + traverse_compare(value1, value2) + elif isinstance(a, list): + try: + for node1, node2 in zip(a, b, strict=True): + traverse_compare(node1, node2) + except ValueError: + # Attempt a "pretty" error ala assertSequenceEqual() + len1 = len(a) + len2 = len(b) + if len1 > len2: + what = "First" + diff = len1 - len2 + else: + what = "Second" + diff = len2 - len1 + msg = f"{what} list contains {diff} additional elements." + raise self.failureException(msg) from None + elif a != b: + self.fail(f"{a!r} != {b!r}") + traverse_compare(ast1, ast2) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 53a6418329e5..c728d2b55e42 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -11,6 +11,7 @@ from textwrap import dedent from test import support +from test.support.ast_helper import ASTTestMixin def to_tuple(t): if t is None or isinstance(t, (str, int, complex)) or t is Ellipsis: @@ -2290,9 +2291,10 @@ def test_source_segment_missing_info(self): self.assertIsNone(ast.get_source_segment(s, x)) self.assertIsNone(ast.get_source_segment(s, y)) -class NodeVisitorTests(unittest.TestCase): +class BaseNodeVisitorCases: + # Both `NodeVisitor` and `NodeTranformer` must raise these warnings: def test_old_constant_nodes(self): - class Visitor(ast.NodeVisitor): + class Visitor(self.visitor_class): def visit_Num(self, node): log.append((node.lineno, 'Num', node.n)) def visit_Str(self, node): @@ -2340,6 +2342,128 @@ def visit_Ellipsis(self, node): ]) +class NodeVisitorTests(BaseNodeVisitorCases, unittest.TestCase): + visitor_class = ast.NodeVisitor + + +class NodeTransformerTests(ASTTestMixin, BaseNodeVisitorCases, unittest.TestCase): + visitor_class = ast.NodeTransformer + + def assertASTTransformation(self, tranformer_class, + initial_code, expected_code): + initial_ast = ast.parse(dedent(initial_code)) + expected_ast = ast.parse(dedent(expected_code)) + + tranformer = tranformer_class() + result_ast = ast.fix_missing_locations(tranformer.visit(initial_ast)) + + self.assertASTEqual(result_ast, expected_ast) + + def test_node_remove_single(self): + code = 'def func(arg) -> SomeType: ...' + expected = 'def func(arg): ...' + + # Since `FunctionDef.returns` is defined as a single value, we test + # the `if isinstance(old_value, AST):` branch here. + class SomeTypeRemover(ast.NodeTransformer): + def visit_Name(self, node: ast.Name): + self.generic_visit(node) + if node.id == 'SomeType': + return None + return node + + self.assertASTTransformation(SomeTypeRemover, code, expected) + + def test_node_remove_from_list(self): + code = """ + def func(arg): + print(arg) + yield arg + """ + expected = """ + def func(arg): + print(arg) + """ + + # Since `FunctionDef.body` is defined as a list, we test + # the `if isinstance(old_value, list):` branch here. + class YieldRemover(ast.NodeTransformer): + def visit_Expr(self, node: ast.Expr): + self.generic_visit(node) + if isinstance(node.value, ast.Yield): + return None # Remove `yield` from a function + return node + + self.assertASTTransformation(YieldRemover, code, expected) + + def test_node_return_list(self): + code = """ + class DSL(Base, kw1=True): ... + """ + expected = """ + class DSL(Base, kw1=True, kw2=True, kw3=False): ... + """ + + class ExtendKeywords(ast.NodeTransformer): + def visit_keyword(self, node: ast.keyword): + self.generic_visit(node) + if node.arg == 'kw1': + return [ + node, + ast.keyword('kw2', ast.Constant(True)), + ast.keyword('kw3', ast.Constant(False)), + ] + return node + + self.assertASTTransformation(ExtendKeywords, code, expected) + + def test_node_mutate(self): + code = """ + def func(arg): + print(arg) + """ + expected = """ + def func(arg): + log(arg) + """ + + class PrintToLog(ast.NodeTransformer): + def visit_Call(self, node: ast.Call): + self.generic_visit(node) + if isinstance(node.func, ast.Name) and node.func.id == 'print': + node.func.id = 'log' + return node + + self.assertASTTransformation(PrintToLog, code, expected) + + def test_node_replace(self): + code = """ + def func(arg): + print(arg) + """ + expected = """ + def func(arg): + logger.log(arg, debug=True) + """ + + class PrintToLog(ast.NodeTransformer): + def visit_Call(self, node: ast.Call): + self.generic_visit(node) + if isinstance(node.func, ast.Name) and node.func.id == 'print': + return ast.Call( + func=ast.Attribute( + ast.Name('logger', ctx=ast.Load()), + attr='log', + ctx=ast.Load(), + ), + args=node.args, + keywords=[ast.keyword('debug', ast.Constant(True))], + ) + return node + + self.assertASTTransformation(PrintToLog, code, expected) + + @support.cpython_only class ModuleStateTests(unittest.TestCase): # bpo-41194, bpo-41261, bpo-41631: The _ast module uses a global state. diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py index f1f1dd5dc26b..88c7c3a0af87 100644 --- a/Lib/test/test_unparse.py +++ b/Lib/test/test_unparse.py @@ -6,6 +6,7 @@ import random import tokenize import ast +from test.support.ast_helper import ASTTestMixin def read_pyfile(filename): @@ -128,46 +129,7 @@ class Foo: pass "async def foo():\n ", ) -class ASTTestCase(unittest.TestCase): - def assertASTEqual(self, ast1, ast2): - # Ensure the comparisons start at an AST node - self.assertIsInstance(ast1, ast.AST) - self.assertIsInstance(ast2, ast.AST) - - # An AST comparison routine modeled after ast.dump(), but - # instead of string building, it traverses the two trees - # in lock-step. - def traverse_compare(a, b, missing=object()): - if type(a) is not type(b): - self.fail(f"{type(a)!r} is not {type(b)!r}") - if isinstance(a, ast.AST): - for field in a._fields: - value1 = getattr(a, field, missing) - value2 = getattr(b, field, missing) - # Singletons are equal by definition, so further - # testing can be skipped. - if value1 is not value2: - traverse_compare(value1, value2) - elif isinstance(a, list): - try: - for node1, node2 in zip(a, b, strict=True): - traverse_compare(node1, node2) - except ValueError: - # Attempt a "pretty" error ala assertSequenceEqual() - len1 = len(a) - len2 = len(b) - if len1 > len2: - what = "First" - diff = len1 - len2 - else: - what = "Second" - diff = len2 - len1 - msg = f"{what} list contains {diff} additional elements." - raise self.failureException(msg) from None - elif a != b: - self.fail(f"{a!r} != {b!r}") - traverse_compare(ast1, ast2) - +class ASTTestCase(ASTTestMixin, unittest.TestCase): def check_ast_roundtrip(self, code1, **kwargs): with self.subTest(code1=code1, ast_parse_kwargs=kwargs): ast1 = ast.parse(code1, **kwargs) From webhook-mailer at python.org Sat Jan 21 18:49:33 2023 From: webhook-mailer at python.org (ned-deily) Date: Sat, 21 Jan 2023 23:49:33 -0000 Subject: [Python-checkins] [3.7] Bump Azure Pipelines to ubuntu-20.04 (GH-101089). (GH-101226) Message-ID: https://github.com/python/cpython/commit/1c338916f1420fe5f7074690605aad900f306841 commit: 1c338916f1420fe5f7074690605aad900f306841 branch: 3.7 author: Hugo van Kemenade committer: ned-deily date: 2023-01-21T18:49:15-05:00 summary: [3.7] Bump Azure Pipelines to ubuntu-20.04 (GH-101089). (GH-101226) Co-authored-by: Hugo van Kemenade files: M .azure-pipelines/ci.yml M .azure-pipelines/pr.yml diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index eaaa88c9634f..3a12fe4bfa43 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -12,7 +12,7 @@ jobs: displayName: Pre-build checks pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-20.04 steps: - template: ./prebuild-checks.yml @@ -24,7 +24,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['docs.run'], 'true')) pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-20.04 steps: - template: ./docs-steps.yml @@ -56,7 +56,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-20.04 variables: testRunTitle: '$(build.sourceBranchName)-linux' @@ -82,7 +82,7 @@ jobs: ) pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-20.04 container: manylinux1 @@ -113,7 +113,7 @@ jobs: ) pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-20.04 variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' diff --git a/.azure-pipelines/pr.yml b/.azure-pipelines/pr.yml index 7f01f8b25c2c..91607b931a36 100644 --- a/.azure-pipelines/pr.yml +++ b/.azure-pipelines/pr.yml @@ -12,7 +12,7 @@ jobs: displayName: Pre-build checks pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-20.04 steps: - template: ./prebuild-checks.yml @@ -24,7 +24,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['docs.run'], 'true')) pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-20.04 steps: - template: ./docs-steps.yml @@ -56,7 +56,7 @@ jobs: condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-20.04 variables: testRunTitle: '$(system.pullRequest.TargetBranch)-linux' @@ -82,7 +82,7 @@ jobs: ) pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-20.04 container: manylinux1 @@ -113,7 +113,7 @@ jobs: ) pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-20.04 variables: testRunTitle: '$(Build.SourceBranchName)-linux-coverage' From webhook-mailer at python.org Sun Jan 22 01:59:58 2023 From: webhook-mailer at python.org (gpshead) Date: Sun, 22 Jan 2023 06:59:58 -0000 Subject: [Python-checkins] GH-88597: Added command line interface to UUID module. (#99463) Message-ID: https://github.com/python/cpython/commit/95f5b05a8cad61b296807c14e50896075c4dc1de commit: 95f5b05a8cad61b296807c14e50896075c4dc1de branch: main author: achhina committer: gpshead date: 2023-01-21T22:59:31-08:00 summary: GH-88597: Added command line interface to UUID module. (#99463) The `uuid` module now supports command line usage. ```python ? ./python.exe -m uuid 5f2d57b1-90e8-417c-ba5d-69b9b6f74289 ? ./python.exe -m uuid -h usage: uuid.py [-h] [-u {uuid1,uuid3,uuid4,uuid5}] [-ns NAMESPACE] [-n NAME] ... ``` files: A Misc/NEWS.d/next/Library/2022-11-14-03-06-03.gh-issue-88597.EYJA-Q.rst M Doc/library/uuid.rst M Doc/whatsnew/3.12.rst M Lib/test/test_uuid.py M Lib/uuid.py M Misc/ACKS diff --git a/Doc/library/uuid.rst b/Doc/library/uuid.rst index a71fe7abf5b5..804884dee0a2 100644 --- a/Doc/library/uuid.rst +++ b/Doc/library/uuid.rst @@ -261,6 +261,46 @@ of the :attr:`variant` attribute: internal format of UUIDs, and methods of generating UUIDs. +.. _uuid-cli: + +Command-Line Usage +------------------ + +.. versionadded:: 3.12 + +The :mod:`uuid` module can be executed as a script from the command line. + +.. code-block:: sh + + python -m uuid [-h] [-u {uuid1,uuid3,uuid4,uuid5}] [-ns NAMESPACE] [-n NAME] + +The following options are accepted: + +.. program:: uuid + +.. cmdoption:: -h, --help + + Show the help message and exit. + +.. cmdoption:: -u + --uuid + + Specify the function name to use to generate the uuid. By default :func:`uuid4` + is used. + +.. cmdoption:: -ns + --namespace + + The namespace used as part of generating the uuid. Only required for + :func:`uuid3` / :func:`uuid5` functions. + +.. cmdoption:: -n + --name + + The name used as part of generating the uuid. Only required for + :func:`uuid3` / :func:`uuid5` functions. + + .. _uuid-example: Example @@ -301,3 +341,22 @@ Here are some examples of typical usage of the :mod:`uuid` module:: >>> uuid.UUID(bytes=x.bytes) UUID('00010203-0405-0607-0809-0a0b0c0d0e0f') + +.. _uuid-cli-example: + +Command-Line Example +-------------------- + +Here are some examples of typical usage of the :mod:`uuid` command line interface: + +.. code-block:: shell + + # generate a random uuid - by default uuid4() is used + $ python -m uuid + + # generate a uuid using uuid1() + $ python -m uuid -u uuid1 + + # generate a uuid using uuid5 + $ python -m uuid -u uuid5 -ns NAMESPACE_URL -n example.com + diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 4d6d4669d0c7..851105f004ba 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -327,6 +327,12 @@ unicodedata * The Unicode database has been updated to version 15.0.0. (Contributed by Benjamin Peterson in :gh:`96734`). +uuid +---- + +* Add a :ref:`command-line interface `. + (Contributed by Adam Chhina in :gh:`88597`.) + tempfile -------- diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index 411eec0f4062..61ae2567c90d 100755 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -675,6 +675,64 @@ def test_uuid_weakref(self): weak = weakref.ref(strong) self.assertIs(strong, weak()) + @mock.patch.object(sys, "argv", ["", "-u", "uuid3", "-ns", "NAMESPACE_DNS"]) + def test_cli_namespace_required_for_uuid3(self): + with self.assertRaises(SystemExit) as cm: + self.uuid.main() + + # Check that exception code is the same as argparse.ArgumentParser.error + self.assertEqual(cm.exception.code, 2) + + @mock.patch.object(sys, "argv", ["", "-u", "uuid3", "-n", "python.org"]) + def test_cli_name_required_for_uuid3(self): + with self.assertRaises(SystemExit) as cm: + self.uuid.main() + + # Check that exception code is the same as argparse.ArgumentParser.error + self.assertEqual(cm.exception.code, 2) + + @mock.patch.object(sys, "argv", [""]) + def test_cli_uuid4_outputted_with_no_args(self): + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + self.uuid.main() + + output = stdout.getvalue().strip() + uuid_output = self.uuid.UUID(output) + + # Output uuid should be in the format of uuid4 + self.assertEqual(output, str(uuid_output)) + self.assertEqual(uuid_output.version, 4) + + @mock.patch.object(sys, "argv", + ["", "-u", "uuid3", "-ns", "NAMESPACE_DNS", "-n", "python.org"]) + def test_cli_uuid3_ouputted_with_valid_namespace_and_name(self): + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + self.uuid.main() + + output = stdout.getvalue().strip() + uuid_output = self.uuid.UUID(output) + + # Output should be in the form of uuid5 + self.assertEqual(output, str(uuid_output)) + self.assertEqual(uuid_output.version, 3) + + @mock.patch.object(sys, "argv", + ["", "-u", "uuid5", "-ns", "NAMESPACE_DNS", "-n", "python.org"]) + def test_cli_uuid5_ouputted_with_valid_namespace_and_name(self): + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + self.uuid.main() + + output = stdout.getvalue().strip() + uuid_output = self.uuid.UUID(output) + + # Output should be in the form of uuid5 + self.assertEqual(output, str(uuid_output)) + self.assertEqual(uuid_output.version, 5) + + class TestUUIDWithoutExtModule(BaseTestUUID, unittest.TestCase): uuid = py_uuid diff --git a/Lib/uuid.py b/Lib/uuid.py index e863b631877f..2904b9c4af64 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -728,9 +728,58 @@ def uuid5(namespace, name): hash = sha1(namespace.bytes + bytes(name, "utf-8")).digest() return UUID(bytes=hash[:16], version=5) + +def main(): + """Run the uuid command line interface.""" + uuid_funcs = {"uuid1": uuid1, + "uuid3": uuid3, + "uuid4": uuid4, + "uuid5": uuid5} + uuid_namespace_funcs = ("uuid3", "uuid5") + namespaces = { + "NAMESPACE_DNS": NAMESPACE_DNS, + "NAMESPACE_URL": NAMESPACE_URL, + "NAMESPACE_OID": NAMESPACE_OID, + "NAMESPACE_X500": NAMESPACE_X500 + } + + import argparse + parser = argparse.ArgumentParser( + description="Generates a uuid using the selected uuid function.") + parser.add_argument("-u", "--uuid", choices=uuid_funcs.keys(), default="uuid4", + help="The function to use to generate the uuid. " + "By default uuid4 function is used.") + parser.add_argument("-ns", "--namespace", + help="The namespace used as part of generating the uuid. " + "Only required for uuid3/uuid5 functions.") + parser.add_argument("-n", "--name", + help="The name used as part of generating the uuid. " + "Only required for uuid3/uuid5 functions.") + + args = parser.parse_args() + uuid_func = uuid_funcs[args.uuid] + namespace = args.namespace + name = args.name + + if args.uuid in uuid_namespace_funcs: + if not namespace or not name: + parser.error( + "Incorrect number of arguments. " + f"{args.uuid} requires a namespace and a name. " + "Run 'python -m uuid -h' for more information." + ) + namespace = namespaces[namespace] if namespace in namespaces else UUID(namespace) + print(uuid_func(namespace, name)) + else: + print(uuid_func()) + + # The following standard UUIDs are for use with uuid3() or uuid5(). NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8') NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8') NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8') NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8') + +if __name__ == "__main__": + main() diff --git a/Misc/ACKS b/Misc/ACKS index 768034516d32..74abcebe21ea 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -315,6 +315,7 @@ Nicolas Chauvat Jerry Chen Michael Chermside Ingrid Cheung +Adam Chhina Terry Chia Albert Chin-A-Young Adal Chiriliuc diff --git a/Misc/NEWS.d/next/Library/2022-11-14-03-06-03.gh-issue-88597.EYJA-Q.rst b/Misc/NEWS.d/next/Library/2022-11-14-03-06-03.gh-issue-88597.EYJA-Q.rst new file mode 100644 index 000000000000..a98e1ab4d157 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-11-14-03-06-03.gh-issue-88597.EYJA-Q.rst @@ -0,0 +1 @@ +:mod:`uuid` now has a command line interface. Try ``python -m uuid -h``. From webhook-mailer at python.org Sun Jan 22 03:10:11 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sun, 22 Jan 2023 08:10:11 -0000 Subject: [Python-checkins] gh-100795: avoid unexpected `freeaddrinfo` after failed `getaddrinfo` (#101220) Message-ID: https://github.com/python/cpython/commit/5f08fe4a2c055880c23c6f9b57ff03005d193bfc commit: 5f08fe4a2c055880c23c6f9b57ff03005d193bfc branch: main author: Sergey G. Brester committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-22T13:40:00+05:30 summary: gh-100795: avoid unexpected `freeaddrinfo` after failed `getaddrinfo` (#101220) Co-authored-by: Oleg Iarygin files: A Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst M Modules/socketmodule.c diff --git a/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst b/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst new file mode 100644 index 000000000000..beec5c9e57f6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst @@ -0,0 +1,2 @@ +Avoid unexpected ``freeaddrinfo`` when :meth:`socket.socket.getaddrinfo` +fails. Patch by Sergey G. Brester. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 4747a23e8317..8659d725f2e3 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -6719,6 +6719,7 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs) error = getaddrinfo(hptr, pptr, &hints, &res0); Py_END_ALLOW_THREADS if (error) { + res0 = NULL; /* avoid unexpected free if res0 becomes not NULL */ set_gaierror(error); goto err; } @@ -6815,6 +6816,7 @@ socket_getnameinfo(PyObject *self, PyObject *args) error = getaddrinfo(hostp, pbuf, &hints, &res); Py_END_ALLOW_THREADS if (error) { + res = NULL; /* avoid unexpected free if res becomes not NULL */ set_gaierror(error); goto fail; } From webhook-mailer at python.org Sun Jan 22 03:47:07 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Sun, 22 Jan 2023 08:47:07 -0000 Subject: [Python-checkins] Revert "gh-100795: avoid unexpected `freeaddrinfo` after failed `getaddrinfo` (#101220)" (#101238) Message-ID: https://github.com/python/cpython/commit/b53bad6dd08db78f5b5e2f41bea69c4b657fca13 commit: b53bad6dd08db78f5b5e2f41bea69c4b657fca13 branch: main author: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-22T14:17:01+05:30 summary: Revert "gh-100795: avoid unexpected `freeaddrinfo` after failed `getaddrinfo` (#101220)" (#101238) Revert "gh-100795: avoid unexpected `freeaddrinfo` after failed `getaddrinfo` (#101220)" This reverts commit 5f08fe4a2c055880c23c6f9b57ff03005d193bfc. files: D Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst M Modules/socketmodule.c diff --git a/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst b/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst deleted file mode 100644 index beec5c9e57f6..000000000000 --- a/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst +++ /dev/null @@ -1,2 +0,0 @@ -Avoid unexpected ``freeaddrinfo`` when :meth:`socket.socket.getaddrinfo` -fails. Patch by Sergey G. Brester. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 8659d725f2e3..4747a23e8317 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -6719,7 +6719,6 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs) error = getaddrinfo(hptr, pptr, &hints, &res0); Py_END_ALLOW_THREADS if (error) { - res0 = NULL; /* avoid unexpected free if res0 becomes not NULL */ set_gaierror(error); goto err; } @@ -6816,7 +6815,6 @@ socket_getnameinfo(PyObject *self, PyObject *args) error = getaddrinfo(hostp, pbuf, &hints, &res); Py_END_ALLOW_THREADS if (error) { - res = NULL; /* avoid unexpected free if res becomes not NULL */ set_gaierror(error); goto fail; } From webhook-mailer at python.org Sun Jan 22 08:19:16 2023 From: webhook-mailer at python.org (JelleZijlstra) Date: Sun, 22 Jan 2023 13:19:16 -0000 Subject: [Python-checkins] [3.10] gh-96192: fix os.ismount() to use a path that is str or bytes (GH-96194) (#99456) Message-ID: https://github.com/python/cpython/commit/5717ab3ac50aa91d19c1f44f970e7db92619f2c0 commit: 5717ab3ac50aa91d19c1f44f970e7db92619f2c0 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: JelleZijlstra date: 2023-01-22T05:19:10-08:00 summary: [3.10] gh-96192: fix os.ismount() to use a path that is str or bytes (GH-96194) (#99456) gh-96192: fix os.ismount() to use a path that is str or bytes (GH-96194) (cherry picked from commit 367f552129341796d75fc4cc40edb49405235a2b) Signed-off-by: Christoph Anton Mitterer Co-authored-by: Christoph Anton Mitterer Co-authored-by: Eryk Sun Co-authored-by: Jelle Zijlstra files: A Misc/NEWS.d/next/Library/2022-08-23-03-13-18.gh-issue-96192.TJywOF.rst M Lib/posixpath.py M Lib/test/test_posixpath.py diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 526f017acbd5..e550b470da5b 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -195,6 +195,7 @@ def ismount(path): if stat.S_ISLNK(s1.st_mode): return False + path = os.fspath(path) if isinstance(path, bytes): parent = join(path, b'..') else: diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 8d398ec01035..50cba256cef0 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -178,6 +178,8 @@ def test_islink(self): def test_ismount(self): self.assertIs(posixpath.ismount("/"), True) self.assertIs(posixpath.ismount(b"/"), True) + self.assertIs(posixpath.ismount(FakePath("/")), True) + self.assertIs(posixpath.ismount(FakePath(b"/")), True) def test_ismount_non_existent(self): # Non-existent mountpoint. diff --git a/Misc/NEWS.d/next/Library/2022-08-23-03-13-18.gh-issue-96192.TJywOF.rst b/Misc/NEWS.d/next/Library/2022-08-23-03-13-18.gh-issue-96192.TJywOF.rst new file mode 100644 index 000000000000..58e51da6ba39 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-08-23-03-13-18.gh-issue-96192.TJywOF.rst @@ -0,0 +1 @@ +Fix handling of ``bytes`` :term:`path-like objects ` in :func:`os.ismount()`. From webhook-mailer at python.org Sun Jan 22 13:44:58 2023 From: webhook-mailer at python.org (mdickinson) Date: Sun, 22 Jan 2023 18:44:58 -0000 Subject: [Python-checkins] gh-67790: Support float-style formatting for Fraction instances (#100161) Message-ID: https://github.com/python/cpython/commit/3e09f3152e518cdc8779b52943b86812114ce071 commit: 3e09f3152e518cdc8779b52943b86812114ce071 branch: main author: Mark Dickinson committer: mdickinson date: 2023-01-22T18:44:49Z summary: gh-67790: Support float-style formatting for Fraction instances (#100161) This PR adds support for float-style formatting for `Fraction` objects: it supports the `"e"`, `"E"`, `"f"`, `"F"`, `"g"`, `"G"` and `"%"` presentation types, and all the various bells and whistles of the formatting mini-language for those presentation types. The behaviour almost exactly matches that of `float`, but the implementation works with the exact `Fraction` value and does not do an intermediate conversion to `float`, and so avoids loss of precision or issues with numbers that are outside the dynamic range of the `float` type. Note that the `"n"` presentation type is _not_ supported. That support could be added later if people have a need for it. There's one corner-case where the behaviour differs from that of float: for the `float` type, if explicit alignment is specified with a fill character of `'0'` and alignment type `'='`, then thousands separators (if specified) are inserted into the padding string: ```python >>> format(3.14, '0=11,.2f') '0,000,003.14' ``` The exact same effect can be achieved by using the `'0'` flag: ```python >>> format(3.14, '011,.2f') '0,000,003.14' ``` For `Fraction`, only the `'0'` flag has the above behaviour with respect to thousands separators: there's no special-casing of the particular `'0='` fill-character/alignment combination. Instead, we treat the fill character `'0'` just like any other: ```python >>> format(Fraction('3.14'), '0=11,.2f') '00000003.14' >>> format(Fraction('3.14'), '011,.2f') '0,000,003.14' ``` The `Fraction` formatter is also stricter about combining these two things: it's not permitted to use both the `'0'` flag _and_ explicit alignment, on the basis that we should refuse the temptation to guess in the face of ambiguity. `float` is less picky: ```python >>> format(3.14, '0<011,.2f') '3.140000000' >>> format(Fraction('3.14'), '0<011,.2f') Traceback (most recent call last): File "", line 1, in File "/Users/mdickinson/Repositories/python/cpython/Lib/fractions.py", line 414, in __format__ raise ValueError( ValueError: Invalid format specifier '0<011,.2f' for object of type 'Fraction'; can't use explicit alignment when zero-padding ``` files: A Misc/NEWS.d/next/Library/2022-12-10-15-30-17.gh-issue-67790.P9YUZM.rst M Doc/library/fractions.rst M Doc/whatsnew/3.12.rst M Lib/fractions.py M Lib/test/test_fractions.py diff --git a/Doc/library/fractions.rst b/Doc/library/fractions.rst index dc9d5fc18215..06b0e038c89b 100644 --- a/Doc/library/fractions.rst +++ b/Doc/library/fractions.rst @@ -101,6 +101,11 @@ another rational number, or from a string. .. versionchanged:: 3.12 Space is allowed around the slash for string inputs: ``Fraction('2 / 3')``. + .. versionchanged:: 3.12 + :class:`Fraction` instances now support float-style formatting, with + presentation types ``"e"``, ``"E"``, ``"f"``, ``"F"``, ``"g"``, ``"G"`` + and ``"%""``. + .. attribute:: numerator Numerator of the Fraction in lowest term. @@ -193,6 +198,29 @@ another rational number, or from a string. ``ndigits`` is negative), again rounding half toward even. This method can also be accessed through the :func:`round` function. + .. method:: __format__(format_spec, /) + + Provides support for float-style formatting of :class:`Fraction` + instances via the :meth:`str.format` method, the :func:`format` built-in + function, or :ref:`Formatted string literals `. The + presentation types ``"e"``, ``"E"``, ``"f"``, ``"F"``, ``"g"``, ``"G"`` + and ``"%"`` are supported. For these presentation types, formatting for a + :class:`Fraction` object ``x`` follows the rules outlined for + the :class:`float` type in the :ref:`formatspec` section. + + Here are some examples:: + + >>> from fractions import Fraction + >>> format(Fraction(1, 7), '.40g') + '0.1428571428571428571428571428571428571429' + >>> format(Fraction('1234567.855'), '_.2f') + '1_234_567.86' + >>> f"{Fraction(355, 113):*>20.6e}" + '********3.141593e+00' + >>> old_price, new_price = 499, 672 + >>> "{:.2%} price increase".format(Fraction(new_price, old_price) - 1) + '34.67% price increase' + .. seealso:: diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 851105f004ba..164cd89522d2 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -269,6 +269,12 @@ dis :data:`~dis.hasarg` collection instead. (Contributed by Irit Katriel in :gh:`94216`.) +fractions +--------- + +* Objects of type :class:`fractions.Fraction` now support float-style + formatting. (Contributed by Mark Dickinson in :gh:`100161`.) + math ---- diff --git a/Lib/fractions.py b/Lib/fractions.py index bdba6c339593..49a3f2841a2e 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -69,6 +69,96 @@ def _hash_algorithm(numerator, denominator): """, re.VERBOSE | re.IGNORECASE) +# Helpers for formatting + +def _round_to_exponent(n, d, exponent, no_neg_zero=False): + """Round a rational number to the nearest multiple of a given power of 10. + + Rounds the rational number n/d to the nearest integer multiple of + 10**exponent, rounding to the nearest even integer multiple in the case of + a tie. Returns a pair (sign: bool, significand: int) representing the + rounded value (-1)**sign * significand * 10**exponent. + + If no_neg_zero is true, then the returned sign will always be False when + the significand is zero. Otherwise, the sign reflects the sign of the + input. + + d must be positive, but n and d need not be relatively prime. + """ + if exponent >= 0: + d *= 10**exponent + else: + n *= 10**-exponent + + # The divmod quotient is correct for round-ties-towards-positive-infinity; + # In the case of a tie, we zero out the least significant bit of q. + q, r = divmod(n + (d >> 1), d) + if r == 0 and d & 1 == 0: + q &= -2 + + sign = q < 0 if no_neg_zero else n < 0 + return sign, abs(q) + + +def _round_to_figures(n, d, figures): + """Round a rational number to a given number of significant figures. + + Rounds the rational number n/d to the given number of significant figures + using the round-ties-to-even rule, and returns a triple + (sign: bool, significand: int, exponent: int) representing the rounded + value (-1)**sign * significand * 10**exponent. + + In the special case where n = 0, returns a significand of zero and + an exponent of 1 - figures, for compatibility with formatting. + Otherwise, the returned significand satisfies + 10**(figures - 1) <= significand < 10**figures. + + d must be positive, but n and d need not be relatively prime. + figures must be positive. + """ + # Special case for n == 0. + if n == 0: + return False, 0, 1 - figures + + # Find integer m satisfying 10**(m - 1) <= abs(n)/d <= 10**m. (If abs(n)/d + # is a power of 10, either of the two possible values for m is fine.) + str_n, str_d = str(abs(n)), str(d) + m = len(str_n) - len(str_d) + (str_d <= str_n) + + # Round to a multiple of 10**(m - figures). The significand we get + # satisfies 10**(figures - 1) <= significand <= 10**figures. + exponent = m - figures + sign, significand = _round_to_exponent(n, d, exponent) + + # Adjust in the case where significand == 10**figures, to ensure that + # 10**(figures - 1) <= significand < 10**figures. + if len(str(significand)) == figures + 1: + significand //= 10 + exponent += 1 + + return sign, significand, exponent + + +# Pattern for matching float-style format specifications; +# supports 'e', 'E', 'f', 'F', 'g', 'G' and '%' presentation types. +_FLOAT_FORMAT_SPECIFICATION_MATCHER = re.compile(r""" + (?: + (?P.)? + (?P[<>=^]) + )? + (?P[-+ ]?) + (?Pz)? + (?P\#)? + # A '0' that's *not* followed by another digit is parsed as a minimum width + # rather than a zeropad flag. + (?P0(?=[0-9]))? + (?P0|[1-9][0-9]*)? + (?P[,_])? + (?:\.(?P0|[1-9][0-9]*))? + (?P[eEfFgG%]) +""", re.DOTALL | re.VERBOSE).fullmatch + + class Fraction(numbers.Rational): """This class implements rational numbers. @@ -314,6 +404,122 @@ def __str__(self): else: return '%s/%s' % (self._numerator, self._denominator) + def __format__(self, format_spec, /): + """Format this fraction according to the given format specification.""" + + # Backwards compatiblility with existing formatting. + if not format_spec: + return str(self) + + # Validate and parse the format specifier. + match = _FLOAT_FORMAT_SPECIFICATION_MATCHER(format_spec) + if match is None: + raise ValueError( + f"Invalid format specifier {format_spec!r} " + f"for object of type {type(self).__name__!r}" + ) + elif match["align"] is not None and match["zeropad"] is not None: + # Avoid the temptation to guess. + raise ValueError( + f"Invalid format specifier {format_spec!r} " + f"for object of type {type(self).__name__!r}; " + "can't use explicit alignment when zero-padding" + ) + fill = match["fill"] or " " + align = match["align"] or ">" + pos_sign = "" if match["sign"] == "-" else match["sign"] + no_neg_zero = bool(match["no_neg_zero"]) + alternate_form = bool(match["alt"]) + zeropad = bool(match["zeropad"]) + minimumwidth = int(match["minimumwidth"] or "0") + thousands_sep = match["thousands_sep"] + precision = int(match["precision"] or "6") + presentation_type = match["presentation_type"] + trim_zeros = presentation_type in "gG" and not alternate_form + trim_point = not alternate_form + exponent_indicator = "E" if presentation_type in "EFG" else "e" + + # Round to get the digits we need, figure out where to place the point, + # and decide whether to use scientific notation. 'point_pos' is the + # relative to the _end_ of the digit string: that is, it's the number + # of digits that should follow the point. + if presentation_type in "fF%": + exponent = -precision + if presentation_type == "%": + exponent -= 2 + negative, significand = _round_to_exponent( + self._numerator, self._denominator, exponent, no_neg_zero) + scientific = False + point_pos = precision + else: # presentation_type in "eEgG" + figures = ( + max(precision, 1) + if presentation_type in "gG" + else precision + 1 + ) + negative, significand, exponent = _round_to_figures( + self._numerator, self._denominator, figures) + scientific = ( + presentation_type in "eE" + or exponent > 0 + or exponent + figures <= -4 + ) + point_pos = figures - 1 if scientific else -exponent + + # Get the suffix - the part following the digits, if any. + if presentation_type == "%": + suffix = "%" + elif scientific: + suffix = f"{exponent_indicator}{exponent + point_pos:+03d}" + else: + suffix = "" + + # String of output digits, padded sufficiently with zeros on the left + # so that we'll have at least one digit before the decimal point. + digits = f"{significand:0{point_pos + 1}d}" + + # Before padding, the output has the form f"{sign}{leading}{trailing}", + # where `leading` includes thousands separators if necessary and + # `trailing` includes the decimal separator where appropriate. + sign = "-" if negative else pos_sign + leading = digits[: len(digits) - point_pos] + frac_part = digits[len(digits) - point_pos :] + if trim_zeros: + frac_part = frac_part.rstrip("0") + separator = "" if trim_point and not frac_part else "." + trailing = separator + frac_part + suffix + + # Do zero padding if required. + if zeropad: + min_leading = minimumwidth - len(sign) - len(trailing) + # When adding thousands separators, they'll be added to the + # zero-padded portion too, so we need to compensate. + leading = leading.zfill( + 3 * min_leading // 4 + 1 if thousands_sep else min_leading + ) + + # Insert thousands separators if required. + if thousands_sep: + first_pos = 1 + (len(leading) - 1) % 3 + leading = leading[:first_pos] + "".join( + thousands_sep + leading[pos : pos + 3] + for pos in range(first_pos, len(leading), 3) + ) + + # We now have a sign and a body. Pad with fill character if necessary + # and return. + body = leading + trailing + padding = fill * (minimumwidth - len(sign) - len(body)) + if align == ">": + return padding + sign + body + elif align == "<": + return sign + body + padding + elif align == "^": + half = len(padding) // 2 + return padding[:half] + sign + body + padding[half:] + else: # align == "=" + return sign + padding + body + def _operator_fallbacks(monomorphic_operator, fallback_operator): """Generates forward and reverse operators given a purely-rational operator and a function from the operator module. diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py index 819d680fd88e..3bc6b409e05d 100644 --- a/Lib/test/test_fractions.py +++ b/Lib/test/test_fractions.py @@ -843,6 +843,382 @@ def denominator(self): self.assertEqual(type(f.numerator), myint) self.assertEqual(type(f.denominator), myint) + def test_format_no_presentation_type(self): + # Triples (fraction, specification, expected_result) + testcases = [ + (F(1, 3), '', '1/3'), + (F(-1, 3), '', '-1/3'), + (F(3), '', '3'), + (F(-3), '', '-3'), + ] + for fraction, spec, expected in testcases: + with self.subTest(fraction=fraction, spec=spec): + self.assertEqual(format(fraction, spec), expected) + + def test_format_e_presentation_type(self): + # Triples (fraction, specification, expected_result) + testcases = [ + (F(2, 3), '.6e', '6.666667e-01'), + (F(3, 2), '.6e', '1.500000e+00'), + (F(2, 13), '.6e', '1.538462e-01'), + (F(2, 23), '.6e', '8.695652e-02'), + (F(2, 33), '.6e', '6.060606e-02'), + (F(13, 2), '.6e', '6.500000e+00'), + (F(20, 2), '.6e', '1.000000e+01'), + (F(23, 2), '.6e', '1.150000e+01'), + (F(33, 2), '.6e', '1.650000e+01'), + (F(2, 3), '.6e', '6.666667e-01'), + (F(3, 2), '.6e', '1.500000e+00'), + # Zero + (F(0), '.3e', '0.000e+00'), + # Powers of 10, to exercise the log10 boundary logic + (F(1, 1000), '.3e', '1.000e-03'), + (F(1, 100), '.3e', '1.000e-02'), + (F(1, 10), '.3e', '1.000e-01'), + (F(1, 1), '.3e', '1.000e+00'), + (F(10), '.3e', '1.000e+01'), + (F(100), '.3e', '1.000e+02'), + (F(1000), '.3e', '1.000e+03'), + # Boundary where we round up to the next power of 10 + (F('99.999994999999'), '.6e', '9.999999e+01'), + (F('99.999995'), '.6e', '1.000000e+02'), + (F('99.999995000001'), '.6e', '1.000000e+02'), + # Negatives + (F(-2, 3), '.6e', '-6.666667e-01'), + (F(-3, 2), '.6e', '-1.500000e+00'), + (F(-100), '.6e', '-1.000000e+02'), + # Large and small + (F('1e1000'), '.3e', '1.000e+1000'), + (F('1e-1000'), '.3e', '1.000e-1000'), + # Using 'E' instead of 'e' should give us a capital 'E' + (F(2, 3), '.6E', '6.666667E-01'), + # Tiny precision + (F(2, 3), '.1e', '6.7e-01'), + (F('0.995'), '.0e', '1e+00'), + # Default precision is 6 + (F(22, 7), 'e', '3.142857e+00'), + # Alternate form forces a decimal point + (F('0.995'), '#.0e', '1.e+00'), + # Check that padding takes the exponent into account. + (F(22, 7), '11.6e', '3.142857e+00'), + (F(22, 7), '12.6e', '3.142857e+00'), + (F(22, 7), '13.6e', ' 3.142857e+00'), + # Thousands separators + (F('1234567.123456'), ',.5e', '1.23457e+06'), + (F('123.123456'), '012_.2e', '0_001.23e+02'), + # z flag is legal, but never makes a difference to the output + (F(-1, 7**100), 'z.6e', '-3.091690e-85'), + ] + for fraction, spec, expected in testcases: + with self.subTest(fraction=fraction, spec=spec): + self.assertEqual(format(fraction, spec), expected) + + def test_format_f_presentation_type(self): + # Triples (fraction, specification, expected_result) + testcases = [ + # Simple .f formatting + (F(0, 1), '.2f', '0.00'), + (F(1, 3), '.2f', '0.33'), + (F(2, 3), '.2f', '0.67'), + (F(4, 3), '.2f', '1.33'), + (F(1, 8), '.2f', '0.12'), + (F(3, 8), '.2f', '0.38'), + (F(1, 13), '.2f', '0.08'), + (F(1, 199), '.2f', '0.01'), + (F(1, 200), '.2f', '0.00'), + (F(22, 7), '.5f', '3.14286'), + (F('399024789'), '.2f', '399024789.00'), + # Large precision (more than float can provide) + (F(104348, 33215), '.50f', + '3.14159265392142104470871594159265392142104470871594'), + # Precision defaults to 6 if not given + (F(22, 7), 'f', '3.142857'), + (F(0), 'f', '0.000000'), + (F(-22, 7), 'f', '-3.142857'), + # Round-ties-to-even checks + (F('1.225'), '.2f', '1.22'), + (F('1.2250000001'), '.2f', '1.23'), + (F('1.2349999999'), '.2f', '1.23'), + (F('1.235'), '.2f', '1.24'), + (F('1.245'), '.2f', '1.24'), + (F('1.2450000001'), '.2f', '1.25'), + (F('1.2549999999'), '.2f', '1.25'), + (F('1.255'), '.2f', '1.26'), + (F('-1.225'), '.2f', '-1.22'), + (F('-1.2250000001'), '.2f', '-1.23'), + (F('-1.2349999999'), '.2f', '-1.23'), + (F('-1.235'), '.2f', '-1.24'), + (F('-1.245'), '.2f', '-1.24'), + (F('-1.2450000001'), '.2f', '-1.25'), + (F('-1.2549999999'), '.2f', '-1.25'), + (F('-1.255'), '.2f', '-1.26'), + # Negatives and sign handling + (F(2, 3), '.2f', '0.67'), + (F(2, 3), '-.2f', '0.67'), + (F(2, 3), '+.2f', '+0.67'), + (F(2, 3), ' .2f', ' 0.67'), + (F(-2, 3), '.2f', '-0.67'), + (F(-2, 3), '-.2f', '-0.67'), + (F(-2, 3), '+.2f', '-0.67'), + (F(-2, 3), ' .2f', '-0.67'), + # Formatting to zero places + (F(1, 2), '.0f', '0'), + (F(-1, 2), '.0f', '-0'), + (F(22, 7), '.0f', '3'), + (F(-22, 7), '.0f', '-3'), + # Formatting to zero places, alternate form + (F(1, 2), '#.0f', '0.'), + (F(-1, 2), '#.0f', '-0.'), + (F(22, 7), '#.0f', '3.'), + (F(-22, 7), '#.0f', '-3.'), + # z flag for suppressing negative zeros + (F('-0.001'), 'z.2f', '0.00'), + (F('-0.001'), '-z.2f', '0.00'), + (F('-0.001'), '+z.2f', '+0.00'), + (F('-0.001'), ' z.2f', ' 0.00'), + (F('0.001'), 'z.2f', '0.00'), + (F('0.001'), '-z.2f', '0.00'), + (F('0.001'), '+z.2f', '+0.00'), + (F('0.001'), ' z.2f', ' 0.00'), + # Specifying a minimum width + (F(2, 3), '6.2f', ' 0.67'), + (F(12345), '6.2f', '12345.00'), + (F(12345), '12f', '12345.000000'), + # Fill and alignment + (F(2, 3), '>6.2f', ' 0.67'), + (F(2, 3), '<6.2f', '0.67 '), + (F(2, 3), '^3.2f', '0.67'), + (F(2, 3), '^4.2f', '0.67'), + (F(2, 3), '^5.2f', '0.67 '), + (F(2, 3), '^6.2f', ' 0.67 '), + (F(2, 3), '^7.2f', ' 0.67 '), + (F(2, 3), '^8.2f', ' 0.67 '), + # '=' alignment + (F(-2, 3), '=+8.2f', '- 0.67'), + (F(2, 3), '=+8.2f', '+ 0.67'), + # Fill character + (F(-2, 3), 'X>3.2f', '-0.67'), + (F(-2, 3), 'X>7.2f', 'XX-0.67'), + (F(-2, 3), 'X<7.2f', '-0.67XX'), + (F(-2, 3), 'X^7.2f', 'X-0.67X'), + (F(-2, 3), 'X=7.2f', '-XX0.67'), + (F(-2, 3), ' >7.2f', ' -0.67'), + # Corner cases: weird fill characters + (F(-2, 3), '\x00>7.2f', '\x00\x00-0.67'), + (F(-2, 3), '\n>7.2f', '\n\n-0.67'), + (F(-2, 3), '\t>7.2f', '\t\t-0.67'), + (F(-2, 3), '>>7.2f', '>>-0.67'), + (F(-2, 3), '<>7.2f', '<<-0.67'), + (F(-2, 3), '?>7.2f', '??-0.67'), + # Zero-padding + (F(-2, 3), '07.2f', '-000.67'), + (F(-2, 3), '-07.2f', '-000.67'), + (F(2, 3), '+07.2f', '+000.67'), + (F(2, 3), ' 07.2f', ' 000.67'), + # An isolated zero is a minimum width, not a zero-pad flag. + # So unlike zero-padding, it's legal in combination with alignment. + (F(2, 3), '0.2f', '0.67'), + (F(2, 3), '>0.2f', '0.67'), + (F(2, 3), '<0.2f', '0.67'), + (F(2, 3), '^0.2f', '0.67'), + (F(2, 3), '=0.2f', '0.67'), + # Corner case: zero-padding _and_ a zero minimum width. + (F(2, 3), '00.2f', '0.67'), + # Thousands separator (only affects portion before the point) + (F(2, 3), ',.2f', '0.67'), + (F(2, 3), ',.7f', '0.6666667'), + (F('123456.789'), ',.2f', '123,456.79'), + (F('1234567'), ',.2f', '1,234,567.00'), + (F('12345678'), ',.2f', '12,345,678.00'), + (F('12345678'), ',f', '12,345,678.000000'), + # Underscore as thousands separator + (F(2, 3), '_.2f', '0.67'), + (F(2, 3), '_.7f', '0.6666667'), + (F('123456.789'), '_.2f', '123_456.79'), + (F('1234567'), '_.2f', '1_234_567.00'), + (F('12345678'), '_.2f', '12_345_678.00'), + # Thousands and zero-padding + (F('1234.5678'), '07,.2f', '1,234.57'), + (F('1234.5678'), '08,.2f', '1,234.57'), + (F('1234.5678'), '09,.2f', '01,234.57'), + (F('1234.5678'), '010,.2f', '001,234.57'), + (F('1234.5678'), '011,.2f', '0,001,234.57'), + (F('1234.5678'), '012,.2f', '0,001,234.57'), + (F('1234.5678'), '013,.2f', '00,001,234.57'), + (F('1234.5678'), '014,.2f', '000,001,234.57'), + (F('1234.5678'), '015,.2f', '0,000,001,234.57'), + (F('1234.5678'), '016,.2f', '0,000,001,234.57'), + (F('-1234.5678'), '07,.2f', '-1,234.57'), + (F('-1234.5678'), '08,.2f', '-1,234.57'), + (F('-1234.5678'), '09,.2f', '-1,234.57'), + (F('-1234.5678'), '010,.2f', '-01,234.57'), + (F('-1234.5678'), '011,.2f', '-001,234.57'), + (F('-1234.5678'), '012,.2f', '-0,001,234.57'), + (F('-1234.5678'), '013,.2f', '-0,001,234.57'), + (F('-1234.5678'), '014,.2f', '-00,001,234.57'), + (F('-1234.5678'), '015,.2f', '-000,001,234.57'), + (F('-1234.5678'), '016,.2f', '-0,000,001,234.57'), + # Corner case: no decimal point + (F('-1234.5678'), '06,.0f', '-1,235'), + (F('-1234.5678'), '07,.0f', '-01,235'), + (F('-1234.5678'), '08,.0f', '-001,235'), + (F('-1234.5678'), '09,.0f', '-0,001,235'), + # Corner-case - zero-padding specified through fill and align + # instead of the zero-pad character - in this case, treat '0' as a + # regular fill character and don't attempt to insert commas into + # the filled portion. This differs from the int and float + # behaviour. + (F('1234.5678'), '0=12,.2f', '00001,234.57'), + # Corner case where it's not clear whether the '0' indicates zero + # padding or gives the minimum width, but there's still an obvious + # answer to give. We want this to work in case the minimum width + # is being inserted programmatically: spec = f'{width}.2f'. + (F('12.34'), '0.2f', '12.34'), + (F('12.34'), 'X>0.2f', '12.34'), + # 'F' should work identically to 'f' + (F(22, 7), '.5F', '3.14286'), + # %-specifier + (F(22, 7), '.2%', '314.29%'), + (F(1, 7), '.2%', '14.29%'), + (F(1, 70), '.2%', '1.43%'), + (F(1, 700), '.2%', '0.14%'), + (F(1, 7000), '.2%', '0.01%'), + (F(1, 70000), '.2%', '0.00%'), + (F(1, 7), '.0%', '14%'), + (F(1, 7), '#.0%', '14.%'), + (F(100, 7), ',.2%', '1,428.57%'), + (F(22, 7), '7.2%', '314.29%'), + (F(22, 7), '8.2%', ' 314.29%'), + (F(22, 7), '08.2%', '0314.29%'), + # Test cases from #67790 and discuss.python.org Ideas thread. + (F(1, 3), '.2f', '0.33'), + (F(1, 8), '.2f', '0.12'), + (F(3, 8), '.2f', '0.38'), + (F(2545, 1000), '.2f', '2.54'), + (F(2549, 1000), '.2f', '2.55'), + (F(2635, 1000), '.2f', '2.64'), + (F(1, 100), '.1f', '0.0'), + (F(49, 1000), '.1f', '0.0'), + (F(51, 1000), '.1f', '0.1'), + (F(149, 1000), '.1f', '0.1'), + (F(151, 1000), '.1f', '0.2'), + ] + for fraction, spec, expected in testcases: + with self.subTest(fraction=fraction, spec=spec): + self.assertEqual(format(fraction, spec), expected) + + def test_format_g_presentation_type(self): + # Triples (fraction, specification, expected_result) + testcases = [ + (F('0.000012345678'), '.6g', '1.23457e-05'), + (F('0.00012345678'), '.6g', '0.000123457'), + (F('0.0012345678'), '.6g', '0.00123457'), + (F('0.012345678'), '.6g', '0.0123457'), + (F('0.12345678'), '.6g', '0.123457'), + (F('1.2345678'), '.6g', '1.23457'), + (F('12.345678'), '.6g', '12.3457'), + (F('123.45678'), '.6g', '123.457'), + (F('1234.5678'), '.6g', '1234.57'), + (F('12345.678'), '.6g', '12345.7'), + (F('123456.78'), '.6g', '123457'), + (F('1234567.8'), '.6g', '1.23457e+06'), + # Rounding up cases + (F('9.99999e+2'), '.4g', '1000'), + (F('9.99999e-8'), '.4g', '1e-07'), + (F('9.99999e+8'), '.4g', '1e+09'), + # Check round-ties-to-even behaviour + (F('-0.115'), '.2g', '-0.12'), + (F('-0.125'), '.2g', '-0.12'), + (F('-0.135'), '.2g', '-0.14'), + (F('-0.145'), '.2g', '-0.14'), + (F('0.115'), '.2g', '0.12'), + (F('0.125'), '.2g', '0.12'), + (F('0.135'), '.2g', '0.14'), + (F('0.145'), '.2g', '0.14'), + # Trailing zeros and decimal point suppressed by default ... + (F(0), '.6g', '0'), + (F('123.400'), '.6g', '123.4'), + (F('123.000'), '.6g', '123'), + (F('120.000'), '.6g', '120'), + (F('12000000'), '.6g', '1.2e+07'), + # ... but not when alternate form is in effect + (F(0), '#.6g', '0.00000'), + (F('123.400'), '#.6g', '123.400'), + (F('123.000'), '#.6g', '123.000'), + (F('120.000'), '#.6g', '120.000'), + (F('12000000'), '#.6g', '1.20000e+07'), + # 'G' format (uses 'E' instead of 'e' for the exponent indicator) + (F('123.45678'), '.6G', '123.457'), + (F('1234567.8'), '.6G', '1.23457E+06'), + # Default precision is 6 significant figures + (F('3.1415926535'), 'g', '3.14159'), + # Precision 0 is treated the same as precision 1. + (F('0.000031415'), '.0g', '3e-05'), + (F('0.00031415'), '.0g', '0.0003'), + (F('0.31415'), '.0g', '0.3'), + (F('3.1415'), '.0g', '3'), + (F('3.1415'), '#.0g', '3.'), + (F('31.415'), '.0g', '3e+01'), + (F('31.415'), '#.0g', '3.e+01'), + (F('0.000031415'), '.1g', '3e-05'), + (F('0.00031415'), '.1g', '0.0003'), + (F('0.31415'), '.1g', '0.3'), + (F('3.1415'), '.1g', '3'), + (F('3.1415'), '#.1g', '3.'), + (F('31.415'), '.1g', '3e+01'), + # Thousands separator + (F(2**64), '_.25g', '18_446_744_073_709_551_616'), + # As with 'e' format, z flag is legal, but has no effect + (F(-1, 7**100), 'zg', '-3.09169e-85'), + ] + for fraction, spec, expected in testcases: + with self.subTest(fraction=fraction, spec=spec): + self.assertEqual(format(fraction, spec), expected) + + def test_invalid_formats(self): + fraction = F(2, 3) + with self.assertRaises(TypeError): + format(fraction, None) + + invalid_specs = [ + 'Q6f', # regression test + # illegal to use fill or alignment when zero padding + 'X>010f', + 'X<010f', + 'X^010f', + 'X=010f', + '0>010f', + '0<010f', + '0^010f', + '0=010f', + '>010f', + '<010f', + '^010f', + '=010e', + '=010f', + '=010g', + '=010%', + '>00.2f', + '>00f', + # Too many zeros - minimum width should not have leading zeros + '006f', + # Leading zeros in precision + '.010f', + '.02f', + '.000f', + # Missing precision + '.e', + '.f', + '.g', + '.%', + # Z instead of z for negative zero suppression + 'Z.2f' + ] + for spec in invalid_specs: + with self.subTest(spec=spec): + with self.assertRaises(ValueError): + format(fraction, spec) + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2022-12-10-15-30-17.gh-issue-67790.P9YUZM.rst b/Misc/NEWS.d/next/Library/2022-12-10-15-30-17.gh-issue-67790.P9YUZM.rst new file mode 100644 index 000000000000..ba0db774f8b3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-10-15-30-17.gh-issue-67790.P9YUZM.rst @@ -0,0 +1,2 @@ +Add float-style formatting support for :class:`fractions.Fraction` +instances. From webhook-mailer at python.org Sun Jan 22 15:58:42 2023 From: webhook-mailer at python.org (rhettinger) Date: Sun, 22 Jan 2023 20:58:42 -0000 Subject: [Python-checkins] GH-101097: Switch from standard interval notation to greater or less than signs for random.random()'s documentation (#101119) Message-ID: https://github.com/python/cpython/commit/8bcd4a6ec7f951d952c174c6a8d937cc62444738 commit: 8bcd4a6ec7f951d952c174c6a8d937cc62444738 branch: main author: Andrew Hong committer: rhettinger date: 2023-01-22T14:58:36-06:00 summary: GH-101097: Switch from standard interval notation to greater or less than signs for random.random()'s documentation (#101119) files: M Doc/library/random.rst M Lib/random.py diff --git a/Doc/library/random.rst b/Doc/library/random.rst index d944518a7909..4522f5a8d26b 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -21,8 +21,8 @@ lognormal, negative exponential, gamma, and beta distributions. For generating distributions of angles, the von Mises distribution is available. Almost all module functions depend on the basic function :func:`.random`, which -generates a random float uniformly in the semi-open range [0.0, 1.0). Python -uses the Mersenne Twister as the core generator. It produces 53-bit precision +generates a random float uniformly in the half-open range ``0.0 <= X < 1.0``. +Python uses the Mersenne Twister as the core generator. It produces 53-bit precision floats and has a period of 2\*\*19937-1. The underlying implementation in C is both fast and threadsafe. The Mersenne Twister is one of the most extensively tested random number generators in existence. However, being completely @@ -294,7 +294,7 @@ be found in any statistics text. .. function:: random() - Return the next random floating point number in the range [0.0, 1.0). + Return the next random floating point number in the range ``0.0 <= X < 1.0`` .. function:: uniform(a, b) diff --git a/Lib/random.py b/Lib/random.py index 1c9e1a48b662..3c4291f6a652 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -849,7 +849,7 @@ class SystemRandom(Random): """ def random(self): - """Get the next random number in the range [0.0, 1.0).""" + """Get the next random number in the range 0.0 <= X < 1.0.""" return (int.from_bytes(_urandom(7)) >> 3) * RECIP_BPF def getrandbits(self, k): From webhook-mailer at python.org Sun Jan 22 16:46:01 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 22 Jan 2023 21:46:01 -0000 Subject: [Python-checkins] [3.11] GH-101097: Switch from standard interval notation to greater or less than signs for random.random()'s documentation (GH-101119) (GH-101246) Message-ID: https://github.com/python/cpython/commit/69d12d868ed551b45ed449e73b30db96dbc3e45b commit: 69d12d868ed551b45ed449e73b30db96dbc3e45b branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-22T13:45:55-08:00 summary: [3.11] GH-101097: Switch from standard interval notation to greater or less than signs for random.random()'s documentation (GH-101119) (GH-101246) (cherry picked from commit 8bcd4a6ec7f951d952c174c6a8d937cc62444738) Co-authored-by: Andrew Hong Automerge-Triggered-By: GH:rhettinger files: M Doc/library/random.rst M Lib/random.py diff --git a/Doc/library/random.rst b/Doc/library/random.rst index 2b87a36f7c52..c7d94e0680f0 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -21,8 +21,8 @@ lognormal, negative exponential, gamma, and beta distributions. For generating distributions of angles, the von Mises distribution is available. Almost all module functions depend on the basic function :func:`.random`, which -generates a random float uniformly in the semi-open range [0.0, 1.0). Python -uses the Mersenne Twister as the core generator. It produces 53-bit precision +generates a random float uniformly in the half-open range ``0.0 <= X < 1.0``. +Python uses the Mersenne Twister as the core generator. It produces 53-bit precision floats and has a period of 2\*\*19937-1. The underlying implementation in C is both fast and threadsafe. The Mersenne Twister is one of the most extensively tested random number generators in existence. However, being completely @@ -273,7 +273,7 @@ be found in any statistics text. .. function:: random() - Return the next random floating point number in the range [0.0, 1.0). + Return the next random floating point number in the range ``0.0 <= X < 1.0`` .. function:: uniform(a, b) diff --git a/Lib/random.py b/Lib/random.py index 22dcb4d3991c..d07fffba6094 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -794,7 +794,7 @@ class SystemRandom(Random): """ def random(self): - """Get the next random number in the range [0.0, 1.0).""" + """Get the next random number in the range 0.0 <= X < 1.0.""" return (int.from_bytes(_urandom(7)) >> 3) * RECIP_BPF def getrandbits(self, k): From webhook-mailer at python.org Sun Jan 22 17:02:22 2023 From: webhook-mailer at python.org (erlend-aasland) Date: Sun, 22 Jan 2023 22:02:22 -0000 Subject: [Python-checkins] Add Erlend Aasland as autoconf codeowner (#101247) Message-ID: https://github.com/python/cpython/commit/4db64529aea775aa23b24f35d08611f427ec2f6f commit: 4db64529aea775aa23b24f35d08611f427ec2f6f branch: main author: Erlend E. Aasland committer: erlend-aasland date: 2023-01-22T23:02:15+01:00 summary: Add Erlend Aasland as autoconf codeowner (#101247) files: M .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d68f1eb328d0..f8dd9170228c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -7,6 +7,9 @@ # GitHub .github/** @ezio-melotti +# Build system +configure* @erlend-aasland + # asyncio **/*asyncio* @1st1 @asvetlov @gvanrossum @kumaraditya303 From webhook-mailer at python.org Sun Jan 22 17:26:48 2023 From: webhook-mailer at python.org (ned-deily) Date: Sun, 22 Jan 2023 22:26:48 -0000 Subject: [Python-checkins] gh-101047: Remove vestigial references to macOS libtool options (gh-101048) Message-ID: https://github.com/python/cpython/commit/79af40a40384ff57238185017970c3f60e351ee1 commit: 79af40a40384ff57238185017970c3f60e351ee1 branch: main author: Gregory Szorc committer: ned-deily date: 2023-01-22T17:26:42-05:00 summary: gh-101047: Remove vestigial references to macOS libtool options (gh-101048) LIBTOOL_CRUFT and OTHER_LIBTOOL_OPT variables have been unused since commit 19199830f7f880b1e9cff5d92d30706fd8bb4700 in 2011. files: M Makefile.pre.in M configure M configure.ac diff --git a/Makefile.pre.in b/Makefile.pre.in index d98f98634b7d..618bb7b5f730 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -198,9 +198,6 @@ LIPO_32BIT_FLAGS=@LIPO_32BIT_FLAGS@ # Flags to lipo to produce an intel-64-only universal executable LIPO_INTEL64_FLAGS=@LIPO_INTEL64_FLAGS@ -# Options to enable prebinding (for fast startup prior to Mac OS X 10.3) -OTHER_LIBTOOL_OPT=@OTHER_LIBTOOL_OPT@ - # Environment to run shared python without installed libraries RUNSHARED= @RUNSHARED@ diff --git a/configure b/configure index 144b35d3c729..e4ec5c40fd7a 100755 --- a/configure +++ b/configure @@ -874,8 +874,6 @@ LDSHARED SHLIB_SUFFIX DSYMUTIL_PATH DSYMUTIL -LIBTOOL_CRUFT -OTHER_LIBTOOL_OPT UNIVERSAL_ARCH_FLAGS WASM_STDLIB WASM_ASSETS_DIR @@ -10910,97 +10908,6 @@ fi CC="$ac_save_cc" - -case $ac_sys_system/$ac_sys_release in - Darwin/[01567]\..*) - OTHER_LIBTOOL_OPT="-prebind -seg1addr 0x10000000" - ;; - Darwin/*) - OTHER_LIBTOOL_OPT="" - ;; -esac - - - -case $ac_sys_system/$ac_sys_release in - Darwin/[01567]\..*) - LIBTOOL_CRUFT="-framework System -lcc_dynamic" - if test "${enable_universalsdk}"; then - : - else - LIBTOOL_CRUFT="${LIBTOOL_CRUFT} -arch_only `/usr/bin/arch`" - fi - LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -install_name $(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' - LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -compatibility_version $(VERSION) -current_version $(VERSION)';; - Darwin/*) - gcc_version=`gcc -dumpversion` - if test ${gcc_version} '<' 4.0 - then - LIBTOOL_CRUFT="-lcc_dynamic" - else - LIBTOOL_CRUFT="" - fi - if test "$cross_compiling" = yes; then : - ac_osx_32bit=yes -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - #include - int main(int argc, char*argv[]) - { - if (sizeof(long) == 4) { - return 0; - } else { - return 1; - } - } - -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - ac_osx_32bit=yes -else - ac_osx_32bit=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - - - if test "${ac_osx_32bit}" = "yes"; then - case `/usr/bin/arch` in - i386) - MACOSX_DEFAULT_ARCH="i386" - ;; - ppc) - MACOSX_DEFAULT_ARCH="ppc" - ;; - *) - as_fn_error $? "Unexpected output of 'arch' on macOS" "$LINENO" 5 - ;; - esac - else - case `/usr/bin/arch` in - i386) - MACOSX_DEFAULT_ARCH="x86_64" - ;; - ppc) - MACOSX_DEFAULT_ARCH="ppc64" - ;; - arm64) - MACOSX_DEFAULT_ARCH="arm64" - ;; - *) - as_fn_error $? "Unexpected output of 'arch' on macOS" "$LINENO" 5 - ;; - esac - - fi - - LIBTOOL_CRUFT=$LIBTOOL_CRUFT" -lSystem -lSystemStubs -arch_only ${MACOSX_DEFAULT_ARCH}" - LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -install_name $(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' - LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -compatibility_version $(VERSION) -current_version $(VERSION)';; -esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for --enable-framework" >&5 $as_echo_n "checking for --enable-framework... " >&6; } if test "$enable_framework" diff --git a/configure.ac b/configure.ac index 595adc67cf84..15412054856f 100644 --- a/configure.ac +++ b/configure.ac @@ -2964,82 +2964,6 @@ AS_VAR_IF([ac_cv_pthread_key_t_is_arithmetic_type], [yes], [ CC="$ac_save_cc" -AC_SUBST(OTHER_LIBTOOL_OPT) -case $ac_sys_system/$ac_sys_release in - Darwin/@<:@01567@:>@\..*) - OTHER_LIBTOOL_OPT="-prebind -seg1addr 0x10000000" - ;; - Darwin/*) - OTHER_LIBTOOL_OPT="" - ;; -esac - - -AC_SUBST(LIBTOOL_CRUFT) -case $ac_sys_system/$ac_sys_release in - Darwin/@<:@01567@:>@\..*) - LIBTOOL_CRUFT="-framework System -lcc_dynamic" - if test "${enable_universalsdk}"; then - : - else - LIBTOOL_CRUFT="${LIBTOOL_CRUFT} -arch_only `/usr/bin/arch`" - fi - LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -install_name $(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' - LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -compatibility_version $(VERSION) -current_version $(VERSION)';; - Darwin/*) - gcc_version=`gcc -dumpversion` - if test ${gcc_version} '<' 4.0 - then - LIBTOOL_CRUFT="-lcc_dynamic" - else - LIBTOOL_CRUFT="" - fi - AC_RUN_IFELSE([AC_LANG_SOURCE([[ - #include - int main(int argc, char*argv[]) - { - if (sizeof(long) == 4) { - return 0; - } else { - return 1; - } - } - ]])],[ac_osx_32bit=yes],[ac_osx_32bit=no],[ac_osx_32bit=yes]) - - if test "${ac_osx_32bit}" = "yes"; then - case `/usr/bin/arch` in - i386) - MACOSX_DEFAULT_ARCH="i386" - ;; - ppc) - MACOSX_DEFAULT_ARCH="ppc" - ;; - *) - AC_MSG_ERROR([Unexpected output of 'arch' on macOS]) - ;; - esac - else - case `/usr/bin/arch` in - i386) - MACOSX_DEFAULT_ARCH="x86_64" - ;; - ppc) - MACOSX_DEFAULT_ARCH="ppc64" - ;; - arm64) - MACOSX_DEFAULT_ARCH="arm64" - ;; - *) - AC_MSG_ERROR([Unexpected output of 'arch' on macOS]) - ;; - esac - - fi - - LIBTOOL_CRUFT=$LIBTOOL_CRUFT" -lSystem -lSystemStubs -arch_only ${MACOSX_DEFAULT_ARCH}" - LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -install_name $(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' - LIBTOOL_CRUFT=$LIBTOOL_CRUFT' -compatibility_version $(VERSION) -current_version $(VERSION)';; -esac AC_MSG_CHECKING(for --enable-framework) if test "$enable_framework" then From webhook-mailer at python.org Sun Jan 22 18:07:58 2023 From: webhook-mailer at python.org (rhettinger) Date: Sun, 22 Jan 2023 23:07:58 -0000 Subject: [Python-checkins] Sumprod(): Update citation. Reorder functions. Add final twosum() call. Improve comments. (#101249) Message-ID: https://github.com/python/cpython/commit/997073c28b2f8d199ff97759775208bc9a99b2b3 commit: 997073c28b2f8d199ff97759775208bc9a99b2b3 branch: main author: Raymond Hettinger committer: rhettinger date: 2023-01-22T17:07:52-06:00 summary: Sumprod(): Update citation. Reorder functions. Add final twosum() call. Improve comments. (#101249) files: M Modules/mathmodule.c diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 1342162fa74b..69907ea04ec8 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2833,34 +2833,20 @@ long_add_would_overflow(long a, long b) /* Double and triple length extended precision floating point arithmetic -based on ideas from three sources: - - Improved Kahan?Babu?ka algorithm by Arnold Neumaier - https://www.mat.univie.ac.at/~neum/scan/01.pdf +based on: A Floating-Point Technique for Extending the Available Precision by T. J. Dekker https://csclub.uwaterloo.ca/~pbarfuss/dekker1971.pdf - Ultimately Fast Accurate Summation by Siegfried M. Rump - https://www.tuhh.de/ti3/paper/rump/Ru08b.pdf - -Double length functions: -* dl_split() exact split of a C double into two half precision components. -* dl_mul() exact multiplication of two C doubles. - -Triple length functions and constant: -* tl_zero is a triple length zero for starting or resetting an accumulation. -* tl_add() compensated addition of a C double to a triple length number. -* tl_fma() performs a triple length fused-multiply-add. -* tl_to_d() converts from triple length number back to a C double. + Accurate Sum and Dot Product + by Takeshi Ogita, Siegfried M. Rump, and Shin?Ichi Oishi + https://doi.org/10.1137/030601818 + https://www.tuhh.de/ti3/paper/rump/OgRuOi05.pdf */ typedef struct{ double hi; double lo; } DoubleLength; -typedef struct{ double hi; double lo; double tiny; } TripleLength; - -static const TripleLength tl_zero = {0.0, 0.0, 0.0}; static inline DoubleLength twosum(double a, double b) @@ -2874,25 +2860,9 @@ twosum(double a, double b) return (DoubleLength) {s, t}; } -static inline TripleLength -tl_add(TripleLength total, double x) -{ - /* Input: x total.hi total.lo total.tiny - |--- twosum ---| - s.hi s.lo - |--- twosum ---| - t.hi t.lo - |--- single sum ---| - Output: s.hi t.hi tiny - */ - DoubleLength s = twosum(x, total.hi); - DoubleLength t = twosum(s.lo, total.lo); - return (TripleLength) {s.hi, t.hi, t.lo + total.tiny}; -} - static inline DoubleLength dl_split(double x) { - double t = x * 134217729.0; /* Veltkamp constant = float(0x8000001) */ + double t = x * 134217729.0; // Veltkamp constant = 2.0 ** 27 + 1 double hi = t - (t - x); double lo = x - hi; return (DoubleLength) {hi, lo}; @@ -2911,6 +2881,18 @@ dl_mul(double x, double y) return (DoubleLength) {z, zz}; } +typedef struct{ double hi; double lo; double tiny; } TripleLength; + +static const TripleLength tl_zero = {0.0, 0.0, 0.0}; + +static inline TripleLength +tl_add(TripleLength total, double x) +{ + DoubleLength s = twosum(x, total.hi); + DoubleLength t = twosum(s.lo, total.lo); + return (TripleLength) {s.hi, t.hi, t.lo + total.tiny}; +} + static inline TripleLength tl_fma(TripleLength total, double p, double q) { @@ -2922,7 +2904,8 @@ tl_fma(TripleLength total, double p, double q) static inline double tl_to_d(TripleLength total) { - return total.tiny + total.lo + total.hi; + DoubleLength last = twosum(total.lo, total.hi); + return total.tiny + last.lo + last.hi; } /*[clinic input] @@ -3039,7 +3022,7 @@ math_sumprod_impl(PyObject *module, PyObject *p, PyObject *q) } finalize_int_path: - // # We're finished, overflowed, or have a non-int + // We're finished, overflowed, or have a non-int int_path_enabled = false; if (int_total_in_use) { term_i = PyLong_FromLong(int_total); From webhook-mailer at python.org Sun Jan 22 20:16:56 2023 From: webhook-mailer at python.org (gpshead) Date: Mon, 23 Jan 2023 01:16:56 -0000 Subject: [Python-checkins] gh-83122: Deprecate testing element truth values in `ElementTree` (#31149) Message-ID: https://github.com/python/cpython/commit/d717be04dc7876696cb21ce7901bda0214c4b2e0 commit: d717be04dc7876696cb21ce7901bda0214c4b2e0 branch: main author: Jacob Walls committer: gpshead date: 2023-01-22T17:16:48-08:00 summary: gh-83122: Deprecate testing element truth values in `ElementTree` (#31149) When testing element truth values, emit a DeprecationWarning in all implementations. This had emitted a FutureWarning in the rarely used python-only implementation since ~2.7 and has always been documented as a behavior not to rely on. Matching an element in a tree search but having it test False can be unexpected. Raising the warning enables making the choice to finally raise an exception for this ambiguous behavior in the future. files: A Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst M Doc/library/xml.etree.elementtree.rst M Doc/whatsnew/3.12.rst M Lib/test/test_xml_etree.py M Lib/xml/etree/ElementTree.py M Modules/_elementtree.c diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst index 876de29b17ca..f9290f528e55 100644 --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -1045,9 +1045,9 @@ Element Objects :meth:`~object.__getitem__`, :meth:`~object.__setitem__`, :meth:`~object.__len__`. - Caution: Elements with no subelements will test as ``False``. This behavior - will change in future versions. Use specific ``len(elem)`` or ``elem is - None`` test instead. :: + Caution: Elements with no subelements will test as ``False``. Testing the + truth value of an Element is deprecated and will raise an exception in + Python 3.14. Use specific ``len(elem)`` or ``elem is None`` test instead.:: element = root.find('foo') @@ -1057,6 +1057,9 @@ Element Objects if element is None: print("element not found") + .. versionchanged:: 3.12 + Testing the truth value of an Element emits :exc:`DeprecationWarning`. + Prior to Python 3.8, the serialisation order of the XML attributes of elements was artificially made predictable by sorting the attributes by their name. Based on the now guaranteed ordering of dicts, this arbitrary diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 164cd89522d2..3cab89712451 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -425,6 +425,11 @@ Deprecated is no current event loop set and it decides to create one. (Contributed by Serhiy Storchaka and Guido van Rossum in :gh:`100160`.) +* The :mod:`xml.etree.ElementTree` module now emits :exc:`DeprecationWarning` + when testing the truth value of an :class:`xml.etree.ElementTree.Element`. + Before, the Python implementation emitted :exc:`FutureWarning`, and the C + implementation emitted nothing. + Pending Removal in Python 3.13 ------------------------------ @@ -487,6 +492,9 @@ Pending Removal in Python 3.14 * ``__package__`` and ``__cached__`` will cease to be set or taken into consideration by the import system (:gh:`97879`). +* Testing the truth value of an :class:`xml.etree.ElementTree.Element` + is deprecated and will raise an exception in Python 3.14. + Pending Removal in Future Versions ---------------------------------- diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index ee3154e99f32..ca5bb562996b 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -3957,6 +3957,25 @@ def test_correct_import_pyET(self): self.assertIsInstance(pyET.Element.__init__, types.FunctionType) self.assertIsInstance(pyET.XMLParser.__init__, types.FunctionType) +# -------------------------------------------------------------------- + +class BoolTest(unittest.TestCase): + def test_warning(self): + e = ET.fromstring('') + msg = ( + r"Testing an element's truth value will raise an exception in " + r"future versions. " + r"Use specific 'len\(elem\)' or 'elem is not None' test instead.") + with self.assertWarnsRegex(DeprecationWarning, msg): + result = bool(e) + # Emulate prior behavior for now + self.assertIs(result, False) + + # Element with children + ET.SubElement(e, 'b') + with self.assertWarnsRegex(DeprecationWarning, msg): + new_result = bool(e) + self.assertIs(new_result, True) # -------------------------------------------------------------------- @@ -4223,6 +4242,7 @@ def test_main(module=None): XMLPullParserTest, BugsTest, KeywordArgsTest, + BoolTest, C14NTest, ] diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py index df5d5191126a..42574eefd81b 100644 --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -200,9 +200,10 @@ def __len__(self): def __bool__(self): warnings.warn( - "The behavior of this method will change in future versions. " + "Testing an element's truth value will raise an exception in " + "future versions. " "Use specific 'len(elem)' or 'elem is not None' test instead.", - FutureWarning, stacklevel=2 + DeprecationWarning, stacklevel=2 ) return len(self._children) != 0 # emulate old behaviour, for now diff --git a/Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst b/Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst new file mode 100644 index 000000000000..5f996042260d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-02-05-12-01-58.bpo-38941.8IhvyG.rst @@ -0,0 +1,4 @@ +The :mod:`xml.etree.ElementTree` module now emits :exc:`DeprecationWarning` +when testing the truth value of an :class:`xml.etree.ElementTree.Element`. +Before, the Python implementation emitted :exc:`FutureWarning`, and the C +implementation emitted nothing. diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 3be098a7dbf6..df1ebc3cfc3b 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -1449,6 +1449,23 @@ element_getitem(PyObject* self_, Py_ssize_t index) return Py_NewRef(self->extra->children[index]); } +static int +element_bool(PyObject* self_) +{ + ElementObject* self = (ElementObject*) self_; + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "Testing an element's truth value will raise an exception " + "in future versions. Use specific 'len(elem)' or " + "'elem is not None' test instead.", + 1) < 0) { + return -1; + }; + if (self->extra ? self->extra->length : 0) { + return 1; + } + return 0; +} + /*[clinic input] _elementtree.Element.insert @@ -4156,6 +4173,7 @@ static PyType_Slot element_slots[] = { {Py_sq_length, element_length}, {Py_sq_item, element_getitem}, {Py_sq_ass_item, element_setitem}, + {Py_nb_bool, element_bool}, {Py_mp_length, element_length}, {Py_mp_subscript, element_subscr}, {Py_mp_ass_subscript, element_ass_subscr}, From webhook-mailer at python.org Mon Jan 23 02:53:20 2023 From: webhook-mailer at python.org (AlexWaygood) Date: Mon, 23 Jan 2023 07:53:20 -0000 Subject: [Python-checkins] gh-101015: Fix `typing.get_type_hints` with unpacked `*tuple` (PEP 646) (#101031) Message-ID: https://github.com/python/cpython/commit/807d6b576fa37f3ab7eb951297cb365c0c198595 commit: 807d6b576fa37f3ab7eb951297cb365c0c198595 branch: main author: Nikita Sobolev committer: AlexWaygood date: 2023-01-23T07:52:58Z summary: gh-101015: Fix `typing.get_type_hints` with unpacked `*tuple` (PEP 646) (#101031) files: A Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst M Lib/test/test_typing.py M Lib/typing.py diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 1cae1b0de714..5aa49bb0e245 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -1224,6 +1224,36 @@ class D(Generic[Unpack[Ts]]): pass self.assertIs(D[T].__origin__, D) self.assertIs(D[Unpack[Ts]].__origin__, D) + def test_get_type_hints_on_unpack_args(self): + Ts = TypeVarTuple('Ts') + + def func1(*args: *Ts): pass + self.assertEqual(gth(func1), {'args': Unpack[Ts]}) + + def func2(*args: *tuple[int, str]): pass + self.assertEqual(gth(func2), {'args': Unpack[tuple[int, str]]}) + + class CustomVariadic(Generic[*Ts]): pass + + def func3(*args: *CustomVariadic[int, str]): pass + self.assertEqual(gth(func3), {'args': Unpack[CustomVariadic[int, str]]}) + + def test_get_type_hints_on_unpack_args_string(self): + Ts = TypeVarTuple('Ts') + + def func1(*args: '*Ts'): pass + self.assertEqual(gth(func1, localns={'Ts': Ts}), + {'args': Unpack[Ts]}) + + def func2(*args: '*tuple[int, str]'): pass + self.assertEqual(gth(func2), {'args': Unpack[tuple[int, str]]}) + + class CustomVariadic(Generic[*Ts]): pass + + def func3(*args: '*CustomVariadic[int, str]'): pass + self.assertEqual(gth(func3, localns={'CustomVariadic': CustomVariadic}), + {'args': Unpack[CustomVariadic[int, str]]}) + def test_tuple_args_are_correct(self): Ts = TypeVarTuple('Ts') diff --git a/Lib/typing.py b/Lib/typing.py index 4675af12d087..bdf51bb5f415 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -371,10 +371,13 @@ def _eval_type(t, globalns, localns, recursive_guard=frozenset()): ForwardRef(arg) if isinstance(arg, str) else arg for arg in t.__args__ ) + is_unpacked = t.__unpacked__ if _should_unflatten_callable_args(t, args): t = t.__origin__[(args[:-1], args[-1])] else: t = t.__origin__[args] + if is_unpacked: + t = Unpack[t] ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__) if ev_args == t.__args__: return t @@ -828,7 +831,7 @@ def __init__(self, arg, is_argument=True, module=None, *, is_class=False): # Unfortunately, this isn't a valid expression on its own, so we # do the unpacking manually. if arg[0] == '*': - arg_to_compile = f'({arg},)[0]' # E.g. (*Ts,)[0] + arg_to_compile = f'({arg},)[0]' # E.g. (*Ts,)[0] or (*tuple[int, int],)[0] else: arg_to_compile = arg try: diff --git a/Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst b/Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst new file mode 100644 index 000000000000..b9d73ff98552 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst @@ -0,0 +1,2 @@ +Fix :func:`typing.get_type_hints` on ``'*tuple[...]'`` and ``*tuple[...]``. +It must not drop the ``Unpack`` part. From webhook-mailer at python.org Mon Jan 23 03:20:29 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 23 Jan 2023 08:20:29 -0000 Subject: [Python-checkins] gh-101015: Fix `typing.get_type_hints` with unpacked `*tuple` (PEP 646) (GH-101031) Message-ID: https://github.com/python/cpython/commit/29ff9daf823ec7af7875c6642f1e191ed48e3b73 commit: 29ff9daf823ec7af7875c6642f1e191ed48e3b73 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-23T00:20:15-08:00 summary: gh-101015: Fix `typing.get_type_hints` with unpacked `*tuple` (PEP 646) (GH-101031) (cherry picked from commit 807d6b576fa37f3ab7eb951297cb365c0c198595) Co-authored-by: Nikita Sobolev files: A Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst M Lib/test/test_typing.py M Lib/typing.py diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index efa4cb3d4e2a..10023af4f788 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -1222,6 +1222,36 @@ class D(Generic[Unpack[Ts]]): pass self.assertIs(D[T].__origin__, D) self.assertIs(D[Unpack[Ts]].__origin__, D) + def test_get_type_hints_on_unpack_args(self): + Ts = TypeVarTuple('Ts') + + def func1(*args: *Ts): pass + self.assertEqual(gth(func1), {'args': Unpack[Ts]}) + + def func2(*args: *tuple[int, str]): pass + self.assertEqual(gth(func2), {'args': Unpack[tuple[int, str]]}) + + class CustomVariadic(Generic[*Ts]): pass + + def func3(*args: *CustomVariadic[int, str]): pass + self.assertEqual(gth(func3), {'args': Unpack[CustomVariadic[int, str]]}) + + def test_get_type_hints_on_unpack_args_string(self): + Ts = TypeVarTuple('Ts') + + def func1(*args: '*Ts'): pass + self.assertEqual(gth(func1, localns={'Ts': Ts}), + {'args': Unpack[Ts]}) + + def func2(*args: '*tuple[int, str]'): pass + self.assertEqual(gth(func2), {'args': Unpack[tuple[int, str]]}) + + class CustomVariadic(Generic[*Ts]): pass + + def func3(*args: '*CustomVariadic[int, str]'): pass + self.assertEqual(gth(func3, localns={'CustomVariadic': CustomVariadic}), + {'args': Unpack[CustomVariadic[int, str]]}) + def test_tuple_args_are_correct(self): Ts = TypeVarTuple('Ts') diff --git a/Lib/typing.py b/Lib/typing.py index b287446169c2..9f5db1a1faab 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -363,10 +363,13 @@ def _eval_type(t, globalns, localns, recursive_guard=frozenset()): ForwardRef(arg) if isinstance(arg, str) else arg for arg in t.__args__ ) + is_unpacked = t.__unpacked__ if _should_unflatten_callable_args(t, args): t = t.__origin__[(args[:-1], args[-1])] else: t = t.__origin__[args] + if is_unpacked: + t = Unpack[t] ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__) if ev_args == t.__args__: return t @@ -820,7 +823,7 @@ def __init__(self, arg, is_argument=True, module=None, *, is_class=False): # Unfortunately, this isn't a valid expression on its own, so we # do the unpacking manually. if arg[0] == '*': - arg_to_compile = f'({arg},)[0]' # E.g. (*Ts,)[0] + arg_to_compile = f'({arg},)[0]' # E.g. (*Ts,)[0] or (*tuple[int, int],)[0] else: arg_to_compile = arg try: diff --git a/Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst b/Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst new file mode 100644 index 000000000000..b9d73ff98552 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-14-12-58-21.gh-issue-101015.stWFid.rst @@ -0,0 +1,2 @@ +Fix :func:`typing.get_type_hints` on ``'*tuple[...]'`` and ``*tuple[...]``. +It must not drop the ``Unpack`` part. From webhook-mailer at python.org Mon Jan 23 06:31:22 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Mon, 23 Jan 2023 11:31:22 -0000 Subject: [Python-checkins] Added asyncio REPL example to docs. (#101243) Message-ID: https://github.com/python/cpython/commit/5d868efde9ea08b39357b962a02a0294aa97689c commit: 5d868efde9ea08b39357b962a02a0294aa97689c branch: main author: Carlton Gibson committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-23T17:01:13+05:30 summary: Added asyncio REPL example to docs. (#101243) Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> files: M Doc/library/asyncio.rst diff --git a/Doc/library/asyncio.rst b/Doc/library/asyncio.rst index b71006e32b2b..c6a046f534e9 100644 --- a/Doc/library/asyncio.rst +++ b/Doc/library/asyncio.rst @@ -56,6 +56,18 @@ Additionally, there are **low-level** APIs for * :ref:`bridge ` callback-based libraries and code with async/await syntax. +You can experiment with an ``asyncio`` concurrent context in the REPL: + +.. code-block:: pycon + + $ python -m asyncio + asyncio REPL ... + Use "await" directly instead of "asyncio.run()". + Type "help", "copyright", "credits" or "license" for more information. + >>> import asyncio + >>> await asyncio.sleep(10, result='hello') + 'hello' + .. include:: ../includes/wasm-notavail.rst .. We use the "rubric" directive here to avoid creating From webhook-mailer at python.org Mon Jan 23 06:39:07 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 23 Jan 2023 11:39:07 -0000 Subject: [Python-checkins] Added asyncio REPL example to docs. (GH-101243) Message-ID: https://github.com/python/cpython/commit/4cf41693508f2c670189c0231233e6b4d5109714 commit: 4cf41693508f2c670189c0231233e6b4d5109714 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-23T03:39:01-08:00 summary: Added asyncio REPL example to docs. (GH-101243) (cherry picked from commit 5d868efde9ea08b39357b962a02a0294aa97689c) Co-authored-by: Carlton Gibson Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> files: M Doc/library/asyncio.rst diff --git a/Doc/library/asyncio.rst b/Doc/library/asyncio.rst index b71006e32b2b..c6a046f534e9 100644 --- a/Doc/library/asyncio.rst +++ b/Doc/library/asyncio.rst @@ -56,6 +56,18 @@ Additionally, there are **low-level** APIs for * :ref:`bridge ` callback-based libraries and code with async/await syntax. +You can experiment with an ``asyncio`` concurrent context in the REPL: + +.. code-block:: pycon + + $ python -m asyncio + asyncio REPL ... + Use "await" directly instead of "asyncio.run()". + Type "help", "copyright", "credits" or "license" for more information. + >>> import asyncio + >>> await asyncio.sleep(10, result='hello') + 'hello' + .. include:: ../includes/wasm-notavail.rst .. We use the "rubric" directive here to avoid creating From webhook-mailer at python.org Mon Jan 23 06:52:46 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Mon, 23 Jan 2023 11:52:46 -0000 Subject: [Python-checkins] [3.10] Added asyncio REPL example to docs. (GH-101243) (#101257) Message-ID: https://github.com/python/cpython/commit/e24c73e71e952028bc702a741241ff79c23eb30e commit: e24c73e71e952028bc702a741241ff79c23eb30e branch: 3.10 author: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-23T17:22:38+05:30 summary: [3.10] Added asyncio REPL example to docs. (GH-101243) (#101257) (cherry picked from commit 5d868efde9ea08b39357b962a02a0294aa97689c) Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> Co-authored-by: Carlton Gibson files: M Doc/library/asyncio.rst diff --git a/Doc/library/asyncio.rst b/Doc/library/asyncio.rst index a6429394389b..8a6526ed9022 100644 --- a/Doc/library/asyncio.rst +++ b/Doc/library/asyncio.rst @@ -56,6 +56,17 @@ Additionally, there are **low-level** APIs for * :ref:`bridge ` callback-based libraries and code with async/await syntax. +You can experiment with an ``asyncio`` concurrent context in the REPL: + +.. code-block:: pycon + + $ python -m asyncio + asyncio REPL ... + Use "await" directly instead of "asyncio.run()". + Type "help", "copyright", "credits" or "license" for more information. + >>> import asyncio + >>> await asyncio.sleep(10, result='hello') + 'hello' .. We use the "rubric" directive here to avoid creating the "Reference" subsection in the TOC. From webhook-mailer at python.org Mon Jan 23 07:20:45 2023 From: webhook-mailer at python.org (erlend-aasland) Date: Mon, 23 Jan 2023 12:20:45 -0000 Subject: [Python-checkins] GH-92123: Store _elementtree state in type contexts (#101190) Message-ID: https://github.com/python/cpython/commit/984387f39a3385ed5699bd7e21797c348e543b19 commit: 984387f39a3385ed5699bd7e21797c348e543b19 branch: main author: Erlend E. Aasland committer: erlend-aasland date: 2023-01-23T13:20:36+01:00 summary: GH-92123: Store _elementtree state in type contexts (#101190) - add state pointer to TreeBuilderObject - add state pointer to XMLParserObject files: M Modules/_elementtree.c diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index df1ebc3cfc3b..8c94cf016106 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -2306,6 +2306,7 @@ typedef struct { char insert_comments; char insert_pis; + elementtreestate *state; } TreeBuilderObject; #define TreeBuilder_CheckExact(st, op) Py_IS_TYPE((op), (st)->TreeBuilder_Type) @@ -2339,6 +2340,7 @@ treebuilder_new(PyTypeObject *type, PyObject *args, PyObject *kwds) t->start_ns_event_obj = t->end_ns_event_obj = NULL; t->comment_event_obj = t->pi_event_obj = NULL; t->insert_comments = t->insert_pis = 0; + t->state = ET_STATE_GLOBAL; } return (PyObject *)t; } @@ -2370,7 +2372,7 @@ _elementtree_TreeBuilder___init___impl(TreeBuilderObject *self, } if (comment_factory == Py_None) { - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; comment_factory = st->comment_factory; } if (comment_factory) { @@ -2382,7 +2384,7 @@ _elementtree_TreeBuilder___init___impl(TreeBuilderObject *self, } if (pi_factory == Py_None) { - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; pi_factory = st->pi_factory; } if (pi_factory) { @@ -2564,7 +2566,7 @@ treebuilder_flush_data(TreeBuilderObject* self) if (!self->data) { return 0; } - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; if (!self->last_for_tail) { PyObject *element = self->last; return treebuilder_extend_element_text_or_tail( @@ -2624,7 +2626,7 @@ treebuilder_handle_start(TreeBuilderObject* self, PyObject* tag, { PyObject* node; PyObject* this; - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; if (treebuilder_flush_data(self) < 0) { return NULL; @@ -3013,6 +3015,7 @@ typedef struct { PyObject *handle_close; + elementtreestate *state; } XMLParserObject; /* helpers */ @@ -3158,7 +3161,7 @@ expat_default_handler(XMLParserObject* self, const XML_Char* data_in, value = PyDict_GetItemWithError(self->entity, key); if (value) { - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; if (TreeBuilder_CheckExact(st, self->target)) res = treebuilder_handle_data( (TreeBuilderObject*) self->target, value @@ -3231,7 +3234,7 @@ expat_start_handler(XMLParserObject* self, const XML_Char* tag_in, attrib = NULL; } - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; if (TreeBuilder_CheckExact(st, self->target)) { /* shortcut */ res = treebuilder_handle_start((TreeBuilderObject*) self->target, @@ -3270,7 +3273,7 @@ expat_data_handler(XMLParserObject* self, const XML_Char* data_in, if (!data) return; /* parser will look for errors */ - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; if (TreeBuilder_CheckExact(st, self->target)) /* shortcut */ res = treebuilder_handle_data((TreeBuilderObject*) self->target, data); @@ -3293,7 +3296,7 @@ expat_end_handler(XMLParserObject* self, const XML_Char* tag_in) if (PyErr_Occurred()) return; - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; if (TreeBuilder_CheckExact(st, self->target)) /* shortcut */ /* the standard tree builder doesn't look at the end tag */ @@ -3328,7 +3331,7 @@ expat_start_ns_handler(XMLParserObject* self, const XML_Char* prefix_in, if (!prefix_in) prefix_in = ""; - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; if (TreeBuilder_CheckExact(st, self->target)) { /* shortcut - TreeBuilder does not actually implement .start_ns() */ TreeBuilderObject *target = (TreeBuilderObject*) self->target; @@ -3379,7 +3382,7 @@ expat_end_ns_handler(XMLParserObject* self, const XML_Char* prefix_in) if (!prefix_in) prefix_in = ""; - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; if (TreeBuilder_CheckExact(st, self->target)) { /* shortcut - TreeBuilder does not actually implement .end_ns() */ TreeBuilderObject *target = (TreeBuilderObject*) self->target; @@ -3408,7 +3411,7 @@ expat_comment_handler(XMLParserObject* self, const XML_Char* comment_in) if (PyErr_Occurred()) return; - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; if (TreeBuilder_CheckExact(st, self->target)) { /* shortcut */ TreeBuilderObject *target = (TreeBuilderObject*) self->target; @@ -3469,7 +3472,7 @@ expat_start_doctype_handler(XMLParserObject *self, pubid_obj = Py_NewRef(Py_None); } - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; /* If the target has a handler for doctype, call it. */ if (self->handle_doctype) { res = PyObject_CallFunctionObjArgs(self->handle_doctype, @@ -3502,7 +3505,7 @@ expat_pi_handler(XMLParserObject* self, const XML_Char* target_in, if (PyErr_Occurred()) return; - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; if (TreeBuilder_CheckExact(st, self->target)) { /* shortcut */ TreeBuilderObject *target = (TreeBuilderObject*) self->target; @@ -3555,6 +3558,7 @@ xmlparser_new(PyTypeObject *type, PyObject *args, PyObject *kwds) self->handle_start = self->handle_data = self->handle_end = NULL; self->handle_comment = self->handle_pi = self->handle_close = NULL; self->handle_doctype = NULL; + self->state = ET_STATE_GLOBAL; } return (PyObject *)self; } @@ -3611,7 +3615,7 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *target, if (target != Py_None) { Py_INCREF(target); } else { - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; target = treebuilder_new(st->TreeBuilder_Type, NULL, NULL); if (!target) { Py_CLEAR(self->entity); @@ -3813,7 +3817,7 @@ _elementtree_XMLParser_close_impl(XMLParserObject *self) if (!res) return NULL; - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; if (TreeBuilder_CheckExact(st, self->target)) { Py_DECREF(res); return treebuilder_done((TreeBuilderObject*) self->target); @@ -3953,7 +3957,7 @@ _elementtree_XMLParser__parse_whole(XMLParserObject *self, PyObject *file) res = expat_parse(self, "", 0, 1); - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; if (res && TreeBuilder_CheckExact(st, self->target)) { Py_DECREF(res); return treebuilder_done((TreeBuilderObject*) self->target); @@ -3985,7 +3989,7 @@ _elementtree_XMLParser__setevents_impl(XMLParserObject *self, if (!_check_xmlparser(self)) { return NULL; } - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = self->state; if (!TreeBuilder_CheckExact(st, self->target)) { PyErr_SetString( PyExc_TypeError, From webhook-mailer at python.org Mon Jan 23 10:30:50 2023 From: webhook-mailer at python.org (ericsnowcurrently) Date: Mon, 23 Jan 2023 15:30:50 -0000 Subject: [Python-checkins] gh-59956: Allow the "Trashcan" Mechanism to Work Without a Thread State (gh-101209) Message-ID: https://github.com/python/cpython/commit/7b20a0f55a16b3e2d274cc478e4d04bd8a836a9f commit: 7b20a0f55a16b3e2d274cc478e4d04bd8a836a9f branch: main author: Eric Snow committer: ericsnowcurrently date: 2023-01-23T08:30:20-07:00 summary: gh-59956: Allow the "Trashcan" Mechanism to Work Without a Thread State (gh-101209) We've factored out a struct from the two PyThreadState fields. This accomplishes two things: * make it clear that the trashcan-related code doesn't need any other parts of PyThreadState * allows us to use the trashcan mechanism even when there isn't a "current" thread state We still expect the caller to hold the GIL. https://github.com/python/cpython/issues/59956 files: M Include/cpython/object.h M Include/cpython/pystate.h M Include/internal/pycore_runtime.h M Objects/object.c M Python/pystate.c diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 426337086130..3f26f2487d70 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -507,7 +507,7 @@ PyAPI_FUNC(int) _PyTrash_cond(PyObject *op, destructor dealloc); /* If "cond" is false, then _tstate remains NULL and the deallocator \ * is run normally without involving the trashcan */ \ if (cond) { \ - _tstate = PyThreadState_Get(); \ + _tstate = _PyThreadState_UncheckedGet(); \ if (_PyTrash_begin(_tstate, _PyObject_CAST(op))) { \ break; \ } \ diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 81944a50c47f..3c1e70de3e48 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -107,6 +107,11 @@ typedef struct _stack_chunk { PyObject * data[1]; /* Variable sized */ } _PyStackChunk; +struct _py_trashcan { + int delete_nesting; + PyObject *delete_later; +}; + struct _ts { /* See Python/ceval.c for comments explaining most fields */ @@ -160,8 +165,7 @@ struct _ts { */ unsigned long native_thread_id; - int trash_delete_nesting; - PyObject *trash_delete_later; + struct _py_trashcan trash; /* Called when a thread state is deleted normally, but not when it * is destroyed after fork(). diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 41e1b2cffb81..9ef270791576 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -126,6 +126,9 @@ typedef struct pyruntimestate { /* Used for the thread state bound to the current thread. */ Py_tss_t autoTSSkey; + /* Used instead of PyThreadState.trash when there is not current tstate. */ + Py_tss_t trashTSSkey; + PyWideStringList orig_argv; struct _parser_runtime_state parser; diff --git a/Objects/object.c b/Objects/object.c index fae508cae3d6..e940222c657e 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2225,22 +2225,20 @@ Py_ReprLeave(PyObject *obj) * object, with refcount 0. Py_DECREF must already have been called on it. */ static void -_PyTrash_thread_deposit_object(PyObject *op) +_PyTrash_thread_deposit_object(struct _py_trashcan *trash, PyObject *op) { - PyThreadState *tstate = _PyThreadState_GET(); _PyObject_ASSERT(op, _PyObject_IS_GC(op)); _PyObject_ASSERT(op, !_PyObject_GC_IS_TRACKED(op)); _PyObject_ASSERT(op, Py_REFCNT(op) == 0); - _PyGCHead_SET_PREV(_Py_AS_GC(op), (PyGC_Head*)tstate->trash_delete_later); - tstate->trash_delete_later = op; + _PyGCHead_SET_PREV(_Py_AS_GC(op), (PyGC_Head*)trash->delete_later); + trash->delete_later = op; } /* Deallocate all the objects in the gcstate->trash_delete_later list. * Called when the call-stack unwinds again. */ static void -_PyTrash_thread_destroy_chain(void) +_PyTrash_thread_destroy_chain(struct _py_trashcan *trash) { - PyThreadState *tstate = _PyThreadState_GET(); /* We need to increase trash_delete_nesting here, otherwise, _PyTrash_thread_destroy_chain will be called recursively and then possibly crash. An example that may crash without @@ -2252,13 +2250,13 @@ _PyTrash_thread_destroy_chain(void) tups = [(tup,) for tup in tups] del tups */ - assert(tstate->trash_delete_nesting == 0); - ++tstate->trash_delete_nesting; - while (tstate->trash_delete_later) { - PyObject *op = tstate->trash_delete_later; + assert(trash->delete_nesting == 0); + ++trash->delete_nesting; + while (trash->delete_later) { + PyObject *op = trash->delete_later; destructor dealloc = Py_TYPE(op)->tp_dealloc; - tstate->trash_delete_later = + trash->delete_later = (PyObject*) _PyGCHead_PREV(_Py_AS_GC(op)); /* Call the deallocator directly. This used to try to @@ -2269,22 +2267,64 @@ _PyTrash_thread_destroy_chain(void) */ _PyObject_ASSERT(op, Py_REFCNT(op) == 0); (*dealloc)(op); - assert(tstate->trash_delete_nesting == 1); + assert(trash->delete_nesting == 1); + } + --trash->delete_nesting; +} + + +static struct _py_trashcan * +_PyTrash_get_state(PyThreadState *tstate) +{ + if (tstate != NULL) { + return &tstate->trash; + } + // The current thread must be finalizing. + // Fall back to using thread-local state. + // XXX Use thread-local variable syntax? + assert(PyThread_tss_is_created(&_PyRuntime.trashTSSkey)); + struct _py_trashcan *trash = + (struct _py_trashcan *)PyThread_tss_get(&_PyRuntime.trashTSSkey); + if (trash == NULL) { + trash = PyMem_RawMalloc(sizeof(struct _py_trashcan)); + if (trash == NULL) { + Py_FatalError("Out of memory"); + } + PyThread_tss_set(&_PyRuntime.trashTSSkey, (void *)trash); + } + return trash; +} + +static void +_PyTrash_clear_state(PyThreadState *tstate) +{ + if (tstate != NULL) { + assert(tstate->trash.delete_later == NULL); + return; + } + if (PyThread_tss_is_created(&_PyRuntime.trashTSSkey)) { + struct _py_trashcan *trash = + (struct _py_trashcan *)PyThread_tss_get(&_PyRuntime.trashTSSkey); + if (trash != NULL) { + PyThread_tss_set(&_PyRuntime.trashTSSkey, (void *)NULL); + PyMem_RawFree(trash); + } } - --tstate->trash_delete_nesting; } int _PyTrash_begin(PyThreadState *tstate, PyObject *op) { - if (tstate->trash_delete_nesting >= _PyTrash_UNWIND_LEVEL) { + // XXX Make sure the GIL is held. + struct _py_trashcan *trash = _PyTrash_get_state(tstate); + if (trash->delete_nesting >= _PyTrash_UNWIND_LEVEL) { /* Store the object (to be deallocated later) and jump past * Py_TRASHCAN_END, skipping the body of the deallocator */ - _PyTrash_thread_deposit_object(op); + _PyTrash_thread_deposit_object(trash, op); return 1; } - ++tstate->trash_delete_nesting; + ++trash->delete_nesting; return 0; } @@ -2292,9 +2332,14 @@ _PyTrash_begin(PyThreadState *tstate, PyObject *op) void _PyTrash_end(PyThreadState *tstate) { - --tstate->trash_delete_nesting; - if (tstate->trash_delete_later && tstate->trash_delete_nesting <= 0) { - _PyTrash_thread_destroy_chain(); + // XXX Make sure the GIL is held. + struct _py_trashcan *trash = _PyTrash_get_state(tstate); + --trash->delete_nesting; + if (trash->delete_nesting <= 0) { + if (trash->delete_later != NULL) { + _PyTrash_thread_destroy_chain(trash); + } + _PyTrash_clear_state(tstate); } } @@ -2371,7 +2416,7 @@ _Py_Dealloc(PyObject *op) destructor dealloc = type->tp_dealloc; #ifdef Py_DEBUG PyThreadState *tstate = _PyThreadState_GET(); - PyObject *old_exc_type = tstate->curexc_type; + PyObject *old_exc_type = tstate != NULL ? tstate->curexc_type : NULL; // Keep the old exception type alive to prevent undefined behavior // on (tstate->curexc_type != old_exc_type) below Py_XINCREF(old_exc_type); @@ -2387,7 +2432,7 @@ _Py_Dealloc(PyObject *op) #ifdef Py_DEBUG // gh-89373: The tp_dealloc function must leave the current exception // unchanged. - if (tstate->curexc_type != old_exc_type) { + if (tstate != NULL && tstate->curexc_type != old_exc_type) { const char *err; if (old_exc_type == NULL) { err = "Deallocator of type '%s' raised an exception"; diff --git a/Python/pystate.c b/Python/pystate.c index 5c1636a8dc37..d31c1f166f22 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -400,6 +400,11 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) return status; } + if (PyThread_tss_create(&runtime->trashTSSkey) != 0) { + _PyRuntimeState_Fini(runtime); + return _PyStatus_NO_MEMORY(); + } + init_runtime(runtime, open_code_hook, open_code_userdata, audit_hook_head, unicode_next_index, lock1, lock2, lock3, lock4); @@ -413,6 +418,10 @@ _PyRuntimeState_Fini(_PyRuntimeState *runtime) current_tss_fini(runtime); } + if (PyThread_tss_is_created(&runtime->trashTSSkey)) { + PyThread_tss_delete(&runtime->trashTSSkey); + } + /* Force the allocator used by _PyRuntimeState_Init(). */ PyMemAllocatorEx old_alloc; _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); @@ -471,6 +480,13 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime) return status; } + if (PyThread_tss_is_created(&runtime->trashTSSkey)) { + PyThread_tss_delete(&runtime->trashTSSkey); + } + if (PyThread_tss_create(&runtime->trashTSSkey) != 0) { + return _PyStatus_NO_MEMORY(); + } + return _PyStatus_OK(); } #endif From webhook-mailer at python.org Mon Jan 23 12:54:33 2023 From: webhook-mailer at python.org (ambv) Date: Mon, 23 Jan 2023 17:54:33 -0000 Subject: [Python-checkins] [3.8] gh-100180: Update Windows installer to OpenSSL 1.1.1s (GH-100903) (#101258) Message-ID: https://github.com/python/cpython/commit/be3b5f7c78d1443a24af50aa6d6c6b90fb09bd70 commit: be3b5f7c78d1443a24af50aa6d6c6b90fb09bd70 branch: 3.8 author: Steve Dower committer: ambv date: 2023-01-23T18:53:56+01:00 summary: [3.8] gh-100180: Update Windows installer to OpenSSL 1.1.1s (GH-100903) (#101258) files: A Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst M PCbuild/get_externals.bat M PCbuild/python.props diff --git a/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst b/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst new file mode 100644 index 000000000000..5b0f42568d92 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst @@ -0,0 +1 @@ +Update Windows installer to OpenSSL 1.1.1s diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 9e2d70cd5d86..59ca66aa3601 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -53,7 +53,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.3.0 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1n +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1s set libraries=%libraries% sqlite-3.35.5.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.9.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.9.0 @@ -77,7 +77,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.3.0 -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1n +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1s if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.9.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/python.props b/PCbuild/python.props index a2d541bd76ae..e9b491cac3ba 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -62,8 +62,8 @@ $(ExternalsDir)libffi-3.3.0\ $(ExternalsDir)libffi-3.3.0\$(ArchName)\ $(libffiOutDir)include - $(ExternalsDir)openssl-1.1.1n\ - $(ExternalsDir)openssl-bin-1.1.1n\$(ArchName)\ + $(ExternalsDir)openssl-1.1.1s\ + $(ExternalsDir)openssl-bin-1.1.1s\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.12\ From webhook-mailer at python.org Mon Jan 23 15:10:16 2023 From: webhook-mailer at python.org (iritkatriel) Date: Mon, 23 Jan 2023 20:10:16 -0000 Subject: [Python-checkins] gh-101261: add test for function with > 255 args (#101262) Message-ID: https://github.com/python/cpython/commit/bd7903967cd2a19ebc842dd1cce75f60a18aef02 commit: bd7903967cd2a19ebc842dd1cce75f60a18aef02 branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-23T20:10:10Z summary: gh-101261: add test for function with > 255 args (#101262) files: M Lib/test/test_call.py diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index c17528be97b4..aab7b1580eaf 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -934,6 +934,16 @@ def c_py_recurse(m): finally: sys.setrecursionlimit(depth) +class TestFunctionWithManyArgs(unittest.TestCase): + def test_function_with_many_args(self): + for N in (10, 500, 1000): + with self.subTest(N=N): + args = ",".join([f"a{i}" for i in range(N)]) + src = f"def f({args}) : return a{N//2}" + l = {} + exec(src, {}, l) + self.assertEqual(l['f'](*range(N)), N//2) + if __name__ == "__main__": unittest.main() From webhook-mailer at python.org Mon Jan 23 18:03:34 2023 From: webhook-mailer at python.org (gpshead) Date: Mon, 23 Jan 2023 23:03:34 -0000 Subject: [Python-checkins] gh-100795: Don't call freeaddrinfo on failure. (#101252) Message-ID: https://github.com/python/cpython/commit/b724ac2fe7fbb5a7a33d639cad8e748f17b325e0 commit: b724ac2fe7fbb5a7a33d639cad8e748f17b325e0 branch: main author: Gregory P. Smith committer: gpshead date: 2023-01-23T15:03:26-08:00 summary: gh-100795: Don't call freeaddrinfo on failure. (#101252) When getaddrinfo returns an error, the output pointer is in an unknown state Don't call freeaddrinfo on it. See the issue for discussion and details with links to reasoning. _Most_ libc getaddrinfo implementations never modify the output pointer unless they are returning success. Co-authored-by: Sergey G. Brester Co-authored-by: Oleg Iarygin files: A Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst M Modules/socketmodule.c diff --git a/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst b/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst new file mode 100644 index 000000000000..4cb56ea0f0e5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst @@ -0,0 +1,3 @@ +Avoid potential unexpected ``freeaddrinfo`` call (double free) in :mod:`socket` +when when a libc ``getaddrinfo()`` implementation leaves garbage in an output +pointer when returning an error. Original patch by Sergey G. Brester. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 4747a23e8317..0a9e46512b15 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1085,6 +1085,7 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int subsequent call to getaddrinfo() does not destroy the outcome of the first call. */ if (error) { + res = NULL; // no-op, remind us that it is invalid; gh-100795 set_gaierror(error); return -1; } @@ -1195,6 +1196,7 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int #endif Py_END_ALLOW_THREADS if (error) { + res = NULL; // no-op, remind us that it is invalid; gh-100795 set_gaierror(error); return -1; } @@ -6719,6 +6721,7 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs) error = getaddrinfo(hptr, pptr, &hints, &res0); Py_END_ALLOW_THREADS if (error) { + res0 = NULL; // gh-100795 set_gaierror(error); goto err; } @@ -6815,6 +6818,7 @@ socket_getnameinfo(PyObject *self, PyObject *args) error = getaddrinfo(hostp, pbuf, &hints, &res); Py_END_ALLOW_THREADS if (error) { + res = NULL; // gh-100795 set_gaierror(error); goto fail; } From webhook-mailer at python.org Mon Jan 23 18:27:47 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 23 Jan 2023 23:27:47 -0000 Subject: [Python-checkins] gh-100795: Don't call freeaddrinfo on failure. (GH-101252) Message-ID: https://github.com/python/cpython/commit/81266281073f498349d6e98563a6519e2b94a1c4 commit: 81266281073f498349d6e98563a6519e2b94a1c4 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-23T15:27:41-08:00 summary: gh-100795: Don't call freeaddrinfo on failure. (GH-101252) When getaddrinfo returns an error, the output pointer is in an unknown state Don't call freeaddrinfo on it. See the issue for discussion and details with links to reasoning. _Most_ libc getaddrinfo implementations never modify the output pointer unless they are returning success. (cherry picked from commit b724ac2fe7fbb5a7a33d639cad8e748f17b325e0) Co-authored-by: Gregory P. Smith Co-authored-by: Sergey G. Brester Co-authored-by: Oleg Iarygin files: A Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst M Modules/socketmodule.c diff --git a/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst b/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst new file mode 100644 index 000000000000..4cb56ea0f0e5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst @@ -0,0 +1,3 @@ +Avoid potential unexpected ``freeaddrinfo`` call (double free) in :mod:`socket` +when when a libc ``getaddrinfo()`` implementation leaves garbage in an output +pointer when returning an error. Original patch by Sergey G. Brester. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 6a9ac2ceb734..0762a8df8663 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1053,6 +1053,7 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int subsequent call to getaddrinfo() does not destroy the outcome of the first call. */ if (error) { + res = NULL; // no-op, remind us that it is invalid; gh-100795 set_gaierror(error); return -1; } @@ -1163,6 +1164,7 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int #endif Py_END_ALLOW_THREADS if (error) { + res = NULL; // no-op, remind us that it is invalid; gh-100795 set_gaierror(error); return -1; } @@ -6514,6 +6516,7 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs) error = getaddrinfo(hptr, pptr, &hints, &res0); Py_END_ALLOW_THREADS if (error) { + res0 = NULL; // gh-100795 set_gaierror(error); goto err; } @@ -6608,6 +6611,7 @@ socket_getnameinfo(PyObject *self, PyObject *args) error = getaddrinfo(hostp, pbuf, &hints, &res); Py_END_ALLOW_THREADS if (error) { + res = NULL; // gh-100795 set_gaierror(error); goto fail; } From webhook-mailer at python.org Mon Jan 23 18:30:25 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 23 Jan 2023 23:30:25 -0000 Subject: [Python-checkins] gh-100795: Don't call freeaddrinfo on failure. (GH-101252) Message-ID: https://github.com/python/cpython/commit/5964b1282919b4f82fbe7f4afd28624a4564dc04 commit: 5964b1282919b4f82fbe7f4afd28624a4564dc04 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-23T15:30:19-08:00 summary: gh-100795: Don't call freeaddrinfo on failure. (GH-101252) When getaddrinfo returns an error, the output pointer is in an unknown state Don't call freeaddrinfo on it. See the issue for discussion and details with links to reasoning. _Most_ libc getaddrinfo implementations never modify the output pointer unless they are returning success. (cherry picked from commit b724ac2fe7fbb5a7a33d639cad8e748f17b325e0) Co-authored-by: Gregory P. Smith Co-authored-by: Sergey G. Brester Co-authored-by: Oleg Iarygin files: A Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst M Modules/socketmodule.c diff --git a/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst b/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst new file mode 100644 index 000000000000..4cb56ea0f0e5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-21-16-50-22.gh-issue-100795.NPMZf7.rst @@ -0,0 +1,3 @@ +Avoid potential unexpected ``freeaddrinfo`` call (double free) in :mod:`socket` +when when a libc ``getaddrinfo()`` implementation leaves garbage in an output +pointer when returning an error. Original patch by Sergey G. Brester. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 14af496a45f4..0e319a4cfd38 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1078,6 +1078,7 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int subsequent call to getaddrinfo() does not destroy the outcome of the first call. */ if (error) { + res = NULL; // no-op, remind us that it is invalid; gh-100795 set_gaierror(error); return -1; } @@ -1188,6 +1189,7 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int #endif Py_END_ALLOW_THREADS if (error) { + res = NULL; // no-op, remind us that it is invalid; gh-100795 set_gaierror(error); return -1; } @@ -6608,6 +6610,7 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs) error = getaddrinfo(hptr, pptr, &hints, &res0); Py_END_ALLOW_THREADS if (error) { + res0 = NULL; // gh-100795 set_gaierror(error); goto err; } @@ -6704,6 +6707,7 @@ socket_getnameinfo(PyObject *self, PyObject *args) error = getaddrinfo(hostp, pbuf, &hints, &res); Py_END_ALLOW_THREADS if (error) { + res = NULL; // gh-100795 set_gaierror(error); goto fail; } From webhook-mailer at python.org Tue Jan 24 00:21:00 2023 From: webhook-mailer at python.org (gpshead) Date: Tue, 24 Jan 2023 05:21:00 -0000 Subject: [Python-checkins] [docs] Mention how to get/set a bigint PyLong via the C API (#101270) Message-ID: https://github.com/python/cpython/commit/e244401ce508ad391295beb636e499fcc6797a2a commit: e244401ce508ad391295beb636e499fcc6797a2a branch: main author: Gregory P. Smith committer: gpshead date: 2023-01-23T21:20:53-08:00 summary: [docs] Mention how to get/set a bigint PyLong via the C API (#101270) We don't need direct C APIs to get at a bigint representation of PyLong but we do want the few people who need to understand how. Additional Author: CAM-Gerlach files: M Doc/c-api/long.rst diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index 4f6f865db8be..41b5632d2300 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -94,6 +94,10 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. ignored. If there are no digits or *str* is not NULL-terminated following the digits and trailing whitespace, :exc:`ValueError` will be raised. + .. seealso:: Python methods :meth:`int.to_bytes` and :meth:`int.from_bytes` + to convert a :c:type:`PyLongObject` to/from an array of bytes in base + ``256``. You can call those from C using :c:func:`PyObject_CallMethod`. + .. c:function:: PyObject* PyLong_FromUnicodeObject(PyObject *u, int base) From webhook-mailer at python.org Tue Jan 24 00:30:35 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 24 Jan 2023 05:30:35 -0000 Subject: [Python-checkins] [docs] Mention how to get/set a bigint PyLong via the C API (GH-101270) Message-ID: https://github.com/python/cpython/commit/a259efc63fb784634b66b5f8c8342d60672aaa23 commit: a259efc63fb784634b66b5f8c8342d60672aaa23 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-23T21:30:29-08:00 summary: [docs] Mention how to get/set a bigint PyLong via the C API (GH-101270) We don't need direct C APIs to get at a bigint representation of PyLong but we do want the few people who need to understand how. Additional Author: CAM-Gerlach (cherry picked from commit e244401ce508ad391295beb636e499fcc6797a2a) Co-authored-by: Gregory P. Smith files: M Doc/c-api/long.rst diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index bd3d731d8464..b3e455d0a89c 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -93,6 +93,10 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. underscores after a base specifier and between digits are ignored. If there are no digits, :exc:`ValueError` will be raised. + .. seealso:: Python methods :meth:`int.to_bytes` and :meth:`int.from_bytes` + to convert a :c:type:`PyLongObject` to/from an array of bytes in base + ``256``. You can call those from C using :c:func:`PyObject_CallMethod`. + .. c:function:: PyObject* PyLong_FromUnicodeObject(PyObject *u, int base) From webhook-mailer at python.org Tue Jan 24 00:31:12 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 24 Jan 2023 05:31:12 -0000 Subject: [Python-checkins] [docs] Mention how to get/set a bigint PyLong via the C API (GH-101270) Message-ID: https://github.com/python/cpython/commit/bab79942c46436e0034c8a033a3c22974ddf6645 commit: bab79942c46436e0034c8a033a3c22974ddf6645 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-23T21:31:06-08:00 summary: [docs] Mention how to get/set a bigint PyLong via the C API (GH-101270) We don't need direct C APIs to get at a bigint representation of PyLong but we do want the few people who need to understand how. Additional Author: CAM-Gerlach (cherry picked from commit e244401ce508ad391295beb636e499fcc6797a2a) Co-authored-by: Gregory P. Smith files: M Doc/c-api/long.rst diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index bd3d731d8464..b3e455d0a89c 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -93,6 +93,10 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. underscores after a base specifier and between digits are ignored. If there are no digits, :exc:`ValueError` will be raised. + .. seealso:: Python methods :meth:`int.to_bytes` and :meth:`int.from_bytes` + to convert a :c:type:`PyLongObject` to/from an array of bytes in base + ``256``. You can call those from C using :c:func:`PyObject_CallMethod`. + .. c:function:: PyObject* PyLong_FromUnicodeObject(PyObject *u, int base) From webhook-mailer at python.org Tue Jan 24 04:29:28 2023 From: webhook-mailer at python.org (Fidget-Spinner) Date: Tue, 24 Jan 2023 09:29:28 -0000 Subject: [Python-checkins] gh-101152: Implement PEP 699 (GH-101193) Message-ID: https://github.com/python/cpython/commit/7f95ec3e7405ea5f44adc3584e297a3191118f32 commit: 7f95ec3e7405ea5f44adc3584e297a3191118f32 branch: main author: ram vikram singh committer: Fidget-Spinner date: 2023-01-24T17:29:22+08:00 summary: gh-101152: Implement PEP 699 (GH-101193) Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com> Co-authored-by: Ken Jin files: A Misc/NEWS.d/next/Build/2023-01-21-10-31-35.gh-issue-101152.xvM8pL.rst M Doc/whatsnew/3.12.rst M Include/cpython/dictobject.h M Modules/_testcapimodule.c diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 3cab89712451..2f9ca1102d3d 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -430,6 +430,11 @@ Deprecated Before, the Python implementation emitted :exc:`FutureWarning`, and the C implementation emitted nothing. +* In accordance with :pep:`699`, the ``ma_version_tag`` field in :c:type:`PyDictObject` + is deprecated for extension modules. Accessing this field will generate a compiler + warning at compile time. This field will be removed in Python 3.14. + (Contributed by Ramvikrams and Kumar Aditya in :gh:`101193`. PEP by Ken Jin.) + Pending Removal in Python 3.13 ------------------------------ diff --git a/Include/cpython/dictobject.h b/Include/cpython/dictobject.h index 2dff59ef0b8a..5001f3565447 100644 --- a/Include/cpython/dictobject.h +++ b/Include/cpython/dictobject.h @@ -16,7 +16,11 @@ typedef struct { /* Dictionary version: globally unique, value change each time the dictionary is modified */ +#ifdef Py_BUILD_CORE uint64_t ma_version_tag; +#else + Py_DEPRECATED(3.12) uint64_t ma_version_tag; +#endif PyDictKeysObject *ma_keys; diff --git a/Misc/NEWS.d/next/Build/2023-01-21-10-31-35.gh-issue-101152.xvM8pL.rst b/Misc/NEWS.d/next/Build/2023-01-21-10-31-35.gh-issue-101152.xvM8pL.rst new file mode 100644 index 000000000000..e35b6178aa4c --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-01-21-10-31-35.gh-issue-101152.xvM8pL.rst @@ -0,0 +1,3 @@ +In accordance with :PEP:`699`, the ``ma_version_tag`` field in :c:type:`PyDictObject` +is deprecated for extension modules. Accessing this field will generate a compiler +warning at compile time. This field will be removed in Python 3.14. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 486988b4e34c..46c025bf5340 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2148,7 +2148,10 @@ dict_get_version(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "O!", &PyDict_Type, &dict)) return NULL; + _Py_COMP_DIAG_PUSH + _Py_COMP_DIAG_IGNORE_DEPR_DECLS version = dict->ma_version_tag; + _Py_COMP_DIAG_POP static_assert(sizeof(unsigned long long) >= sizeof(version), "version is larger than unsigned long long"); From webhook-mailer at python.org Tue Jan 24 04:34:50 2023 From: webhook-mailer at python.org (corona10) Date: Tue, 24 Jan 2023 09:34:50 -0000 Subject: [Python-checkins] gh-101060: conditionally add `-fno-reorder-blocks-and-partition` (gh-101061) Message-ID: https://github.com/python/cpython/commit/7589d713a14a1efbb0cbb7caa9fdbad2b081cf18 commit: 7589d713a14a1efbb0cbb7caa9fdbad2b081cf18 branch: main author: Gregory Szorc committer: corona10 date: 2023-01-24T18:34:44+09:00 summary: gh-101060: conditionally add `-fno-reorder-blocks-and-partition` (gh-101061) files: A Misc/NEWS.d/next/Build/2023-01-15-11-22-15.gh-issue-101060.0mYk9E.rst M configure M configure.ac diff --git a/Misc/NEWS.d/next/Build/2023-01-15-11-22-15.gh-issue-101060.0mYk9E.rst b/Misc/NEWS.d/next/Build/2023-01-15-11-22-15.gh-issue-101060.0mYk9E.rst new file mode 100644 index 000000000000..bebbf8c898d5 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-01-15-11-22-15.gh-issue-101060.0mYk9E.rst @@ -0,0 +1,3 @@ +Conditionally add ``-fno-reorder-blocks-and-partition`` in configure. +Effectively fixes ``--enable-bolt`` when using Clang, as this appears to be +a GCC-only flag. diff --git a/configure b/configure index e4ec5c40fd7a..709913dee831 100755 --- a/configure +++ b/configure @@ -7914,8 +7914,47 @@ if test "$Py_BOLT" = 'true' ; then DEF_MAKE_ALL_RULE="bolt-opt" DEF_MAKE_RULE="build_all" + # -fno-reorder-blocks-and-partition is required for bolt to work. + # Possibly GCC only. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fno-reorder-blocks-and-partition" >&5 +$as_echo_n "checking whether C compiler accepts -fno-reorder-blocks-and-partition... " >&6; } +if ${ax_cv_check_cflags___fno_reorder_blocks_and_partition+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -fno-reorder-blocks-and-partition" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___fno_reorder_blocks_and_partition=yes +else + ax_cv_check_cflags___fno_reorder_blocks_and_partition=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___fno_reorder_blocks_and_partition" >&5 +$as_echo "$ax_cv_check_cflags___fno_reorder_blocks_and_partition" >&6; } +if test "x$ax_cv_check_cflags___fno_reorder_blocks_and_partition" = xyes; then : + + CFLAGS_NODIST="$CFLAGS_NODIST -fno-reorder-blocks-and-partition" + +else + : +fi + + # These flags are required for bolt to work: - CFLAGS_NODIST="$CFLAGS_NODIST -fno-reorder-blocks-and-partition" LDFLAGS_NODIST="$LDFLAGS_NODIST -Wl,--emit-relocs" # These flags are required to get good performance from bolt: diff --git a/configure.ac b/configure.ac index 15412054856f..12249b2d4729 100644 --- a/configure.ac +++ b/configure.ac @@ -1939,8 +1939,13 @@ if test "$Py_BOLT" = 'true' ; then DEF_MAKE_ALL_RULE="bolt-opt" DEF_MAKE_RULE="build_all" + # -fno-reorder-blocks-and-partition is required for bolt to work. + # Possibly GCC only. + AX_CHECK_COMPILE_FLAG([-fno-reorder-blocks-and-partition],[ + CFLAGS_NODIST="$CFLAGS_NODIST -fno-reorder-blocks-and-partition" + ]) + # These flags are required for bolt to work: - CFLAGS_NODIST="$CFLAGS_NODIST -fno-reorder-blocks-and-partition" LDFLAGS_NODIST="$LDFLAGS_NODIST -Wl,--emit-relocs" # These flags are required to get good performance from bolt: From webhook-mailer at python.org Tue Jan 24 04:43:22 2023 From: webhook-mailer at python.org (iritkatriel) Date: Tue, 24 Jan 2023 09:43:22 -0000 Subject: [Python-checkins] gh-98831: rewrite CHECK_EG_MATCH opcode in the instruction definition DSL (#101269) Message-ID: https://github.com/python/cpython/commit/8c183cddd3d0031c6d397738f73c20ee6bf61ce8 commit: 8c183cddd3d0031c6d397738f73c20ee6bf61ce8 branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-24T09:43:16Z summary: gh-98831: rewrite CHECK_EG_MATCH opcode in the instruction definition DSL (#101269) files: M Python/bytecodes.c M Python/ceval.c M Python/generated_cases.c.h M Python/opcode_metadata.h diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 6088fa45ac64..47bbe1a99e3b 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1894,44 +1894,24 @@ dummy_func( b = Py_NewRef((res^oparg) ? Py_True : Py_False); } - // stack effect: ( -- ) - inst(CHECK_EG_MATCH) { - PyObject *match_type = POP(); + inst(CHECK_EG_MATCH, (exc_value, match_type -- rest, match)) { if (check_except_star_type_valid(tstate, match_type) < 0) { - Py_DECREF(match_type); - goto error; + DECREF_INPUTS(); + ERROR_IF(true, error); } - PyObject *exc_value = TOP(); - PyObject *match = NULL, *rest = NULL; + match = NULL; + rest = NULL; int res = exception_group_match(exc_value, match_type, &match, &rest); - Py_DECREF(match_type); - if (res < 0) { - goto error; - } + DECREF_INPUTS(); + ERROR_IF(res < 0, error); - if (match == NULL || rest == NULL) { - assert(match == NULL); - assert(rest == NULL); - goto error; - } - if (Py_IsNone(match)) { - PUSH(match); - Py_XDECREF(rest); - } - else { - /* Total or partial match - update the stack from - * [val] - * to - * [rest, match] - * (rest can be Py_None) - */ - - SET_TOP(rest); - PUSH(match); + assert((match == NULL) == (rest == NULL)); + ERROR_IF(match == NULL, error); + + if (!Py_IsNone(match)) { PyErr_SetExcInfo(NULL, Py_NewRef(match), NULL); - Py_DECREF(exc_value); } } diff --git a/Python/ceval.c b/Python/ceval.c index a97313c773ee..95eb99b45334 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1946,7 +1946,7 @@ exception_group_match(PyObject* exc_value, PyObject *match_type, } /* no match */ *match = Py_NewRef(Py_None); - *rest = Py_NewRef(Py_None); + *rest = Py_NewRef(exc_value); return 0; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 46d421f98ac7..c1eb4000883d 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2251,43 +2251,32 @@ } TARGET(CHECK_EG_MATCH) { - PyObject *match_type = POP(); + PyObject *match_type = PEEK(1); + PyObject *exc_value = PEEK(2); + PyObject *rest; + PyObject *match; if (check_except_star_type_valid(tstate, match_type) < 0) { + Py_DECREF(exc_value); Py_DECREF(match_type); - goto error; + if (true) goto pop_2_error; } - PyObject *exc_value = TOP(); - PyObject *match = NULL, *rest = NULL; + match = NULL; + rest = NULL; int res = exception_group_match(exc_value, match_type, &match, &rest); + Py_DECREF(exc_value); Py_DECREF(match_type); - if (res < 0) { - goto error; - } + if (res < 0) goto pop_2_error; - if (match == NULL || rest == NULL) { - assert(match == NULL); - assert(rest == NULL); - goto error; - } - if (Py_IsNone(match)) { - PUSH(match); - Py_XDECREF(rest); - } - else { - /* Total or partial match - update the stack from - * [val] - * to - * [rest, match] - * (rest can be Py_None) - */ - - SET_TOP(rest); - PUSH(match); + assert((match == NULL) == (rest == NULL)); + if (match == NULL) goto pop_2_error; + + if (!Py_IsNone(match)) { PyErr_SetExcInfo(NULL, Py_NewRef(match), NULL); - Py_DECREF(exc_value); } + POKE(1, match); + POKE(2, rest); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index e3d8ed340e44..3ceaca8c397f 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -119,7 +119,7 @@ static const struct { [COMPARE_AND_BRANCH_STR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, [IS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [CONTAINS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CHECK_EG_MATCH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [CHECK_EG_MATCH] = { 2, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [CHECK_EXC_MATCH] = { 2, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [IMPORT_NAME] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [IMPORT_FROM] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, From webhook-mailer at python.org Tue Jan 24 05:22:07 2023 From: webhook-mailer at python.org (corona10) Date: Tue, 24 Jan 2023 10:22:07 -0000 Subject: [Python-checkins] gh-101278: Drop -gdwarf-4 flag from the BOLT build (gh-101280) Message-ID: https://github.com/python/cpython/commit/a958e7d35af191c354a3e89a1236e830b1e46029 commit: a958e7d35af191c354a3e89a1236e830b1e46029 branch: main author: Dong-hee Na committer: corona10 date: 2023-01-24T19:22:00+09:00 summary: gh-101278: Drop -gdwarf-4 flag from the BOLT build (gh-101280) files: M configure M configure.ac diff --git a/configure b/configure index 709913dee831..8b707cda6212 100755 --- a/configure +++ b/configure @@ -7961,9 +7961,6 @@ fi CFLAGS_NODIST="$CFLAGS_NODIST -fno-pie" # We want to add these no-pie flags to linking executables but not shared libraries: LINKCC="$LINKCC -fno-pie -no-pie" - # Designate the DWARF version into 4 since the LLVM-BOLT does not support DWARF5 yet. - CFLAGS="$CFLAGS -gdwarf-4" - LDFLAGS="$LDFLAGS -gdwarf-4" if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}llvm-bolt", so it can be a program name with args. diff --git a/configure.ac b/configure.ac index 12249b2d4729..5eee4586680d 100644 --- a/configure.ac +++ b/configure.ac @@ -1952,9 +1952,6 @@ if test "$Py_BOLT" = 'true' ; then CFLAGS_NODIST="$CFLAGS_NODIST -fno-pie" # We want to add these no-pie flags to linking executables but not shared libraries: LINKCC="$LINKCC -fno-pie -no-pie" - # Designate the DWARF version into 4 since the LLVM-BOLT does not support DWARF5 yet. - CFLAGS="$CFLAGS -gdwarf-4" - LDFLAGS="$LDFLAGS -gdwarf-4" AC_SUBST(LLVM_BOLT) AC_PATH_TOOL(LLVM_BOLT, llvm-bolt, '', ${llvm_path}) if test -n "${LLVM_BOLT}" -a -x "${LLVM_BOLT}" From webhook-mailer at python.org Tue Jan 24 05:22:29 2023 From: webhook-mailer at python.org (corona10) Date: Tue, 24 Jan 2023 10:22:29 -0000 Subject: [Python-checkins] no-issue: Add Dong-hee Na as autoconf codeowner (gh-101281) Message-ID: https://github.com/python/cpython/commit/38cc24f119346e2947e316478b58e58f0dde307c commit: 38cc24f119346e2947e316478b58e58f0dde307c branch: main author: Dong-hee Na committer: corona10 date: 2023-01-24T19:22:22+09:00 summary: no-issue: Add Dong-hee Na as autoconf codeowner (gh-101281) files: M .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f8dd9170228c..8dd07d911f5b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,7 +8,7 @@ .github/** @ezio-melotti # Build system -configure* @erlend-aasland +configure* @erlend-aasland @corona10 # asyncio **/*asyncio* @1st1 @asvetlov @gvanrossum @kumaraditya303 From webhook-mailer at python.org Tue Jan 24 06:46:01 2023 From: webhook-mailer at python.org (erlend-aasland) Date: Tue, 24 Jan 2023 11:46:01 -0000 Subject: [Python-checkins] GH-92123: Pass _elementtree state as parameter (#101189) Message-ID: https://github.com/python/cpython/commit/b2ac39626a13ae2fb14ec58ea0f0807e47f9910a commit: b2ac39626a13ae2fb14ec58ea0f0807e47f9910a branch: main author: Erlend E. Aasland committer: erlend-aasland date: 2023-01-24T12:45:55+01:00 summary: GH-92123: Pass _elementtree state as parameter (#101189) files: M Modules/_elementtree.c diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 8c94cf016106..f7ab8881723f 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -297,11 +297,10 @@ clear_extra(ElementObject* self) * tag and attributes. */ LOCAL(PyObject*) -create_new_element(PyObject* tag, PyObject* attrib) +create_new_element(elementtreestate *st, PyObject *tag, PyObject *attrib) { ElementObject* self; - elementtreestate *st = ET_STATE_GLOBAL; self = PyObject_GC_New(ElementObject, st->Element_Type); if (self == NULL) return NULL; @@ -504,10 +503,10 @@ raise_type_error(PyObject *element) } LOCAL(int) -element_add_subelement(ElementObject* self, PyObject* element) +element_add_subelement(elementtreestate *st, ElementObject *self, + PyObject *element) { /* add a child element to a parent */ - elementtreestate *st = ET_STATE_GLOBAL; if (!Element_Check(st, element)) { raise_type_error(element); return -1; @@ -614,12 +613,12 @@ subelement(PyObject *self, PyObject *args, PyObject *kwds) /* no attrib arg, no kwds, so no attribute */ } - elem = create_new_element(tag, attrib); + elem = create_new_element(st, tag, attrib); Py_XDECREF(attrib); if (elem == NULL) return NULL; - if (element_add_subelement(parent, elem) < 0) { + if (element_add_subelement(st, parent, elem) < 0) { Py_DECREF(elem); return NULL; } @@ -694,7 +693,8 @@ static PyObject * _elementtree_Element_append_impl(ElementObject *self, PyObject *subelement) /*[clinic end generated code: output=54a884b7cf2295f4 input=439f2bd777288fb6]*/ { - if (element_add_subelement(self, subelement) < 0) + elementtreestate *st = ET_STATE_GLOBAL; + if (element_add_subelement(st, self, subelement) < 0) return NULL; Py_RETURN_NONE; @@ -728,9 +728,10 @@ _elementtree_Element___copy___impl(ElementObject *self) { Py_ssize_t i; ElementObject* element; + elementtreestate *st = ET_STATE_GLOBAL; element = (ElementObject*) create_new_element( - self->tag, self->extra ? self->extra->attrib : NULL); + st, self->tag, self->extra ? self->extra->attrib : NULL); if (!element) return NULL; @@ -759,7 +760,7 @@ _elementtree_Element___copy___impl(ElementObject *self) } /* Helper for a deep copy. */ -LOCAL(PyObject *) deepcopy(PyObject *, PyObject *); +LOCAL(PyObject *) deepcopy(elementtreestate *, PyObject *, PyObject *); /*[clinic input] _elementtree.Element.__deepcopy__ @@ -781,12 +782,13 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo) PyObject* tail; PyObject* id; - tag = deepcopy(self->tag, memo); + elementtreestate *st = ET_STATE_GLOBAL; + tag = deepcopy(st, self->tag, memo); if (!tag) return NULL; if (self->extra && self->extra->attrib) { - attrib = deepcopy(self->extra->attrib, memo); + attrib = deepcopy(st, self->extra->attrib, memo); if (!attrib) { Py_DECREF(tag); return NULL; @@ -795,7 +797,7 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo) attrib = NULL; } - element = (ElementObject*) create_new_element(tag, attrib); + element = (ElementObject*) create_new_element(st, tag, attrib); Py_DECREF(tag); Py_XDECREF(attrib); @@ -803,12 +805,12 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo) if (!element) return NULL; - text = deepcopy(JOIN_OBJ(self->text), memo); + text = deepcopy(st, JOIN_OBJ(self->text), memo); if (!text) goto error; _set_joined_ptr(&element->text, JOIN_SET(text, JOIN_GET(self->text))); - tail = deepcopy(JOIN_OBJ(self->tail), memo); + tail = deepcopy(st, JOIN_OBJ(self->tail), memo); if (!tail) goto error; _set_joined_ptr(&element->tail, JOIN_SET(tail, JOIN_GET(self->tail))); @@ -818,9 +820,8 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo) if (element_resize(element, self->extra->length) < 0) goto error; - elementtreestate *st = ET_STATE_GLOBAL; for (i = 0; i < self->extra->length; i++) { - PyObject* child = deepcopy(self->extra->children[i], memo); + PyObject* child = deepcopy(st, self->extra->children[i], memo); if (!child || !Element_Check(st, child)) { if (child) { raise_type_error(child); @@ -856,10 +857,9 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo) } LOCAL(PyObject *) -deepcopy(PyObject *object, PyObject *memo) +deepcopy(elementtreestate *st, PyObject *object, PyObject *memo) { /* do a deep copy of the given object */ - elementtreestate *st = ET_STATE_GLOBAL; PyObject *stack[2]; /* Fast paths */ @@ -974,7 +974,8 @@ _elementtree_Element___getstate___impl(ElementObject *self) } static PyObject * -element_setstate_from_attributes(ElementObject *self, +element_setstate_from_attributes(elementtreestate *st, + ElementObject *self, PyObject *tag, PyObject *attrib, PyObject *text, @@ -1032,7 +1033,6 @@ element_setstate_from_attributes(ElementObject *self, } /* Copy children */ - elementtreestate *st = ET_STATE_GLOBAL; for (i = 0; i < nchildren; i++) { PyObject *child = PyList_GET_ITEM(children, i); if (!Element_Check(st, child)) { @@ -1065,7 +1065,8 @@ element_setstate_from_attributes(ElementObject *self, */ static PyObject * -element_setstate_from_Python(ElementObject *self, PyObject *state) +element_setstate_from_Python(elementtreestate *st, ElementObject *self, + PyObject *state) { static char *kwlist[] = {PICKLED_TAG, PICKLED_ATTRIB, PICKLED_TEXT, PICKLED_TAIL, PICKLED_CHILDREN, 0}; @@ -1080,7 +1081,7 @@ element_setstate_from_Python(ElementObject *self, PyObject *state) if (PyArg_ParseTupleAndKeywords(args, state, "|$OOOOO", kwlist, &tag, &attrib, &text, &tail, &children)) - retval = element_setstate_from_attributes(self, tag, attrib, text, + retval = element_setstate_from_attributes(st, self, tag, attrib, text, tail, children); else retval = NULL; @@ -1107,8 +1108,10 @@ _elementtree_Element___setstate__(ElementObject *self, PyObject *state) state); return NULL; } - else - return element_setstate_from_Python(self, state); + else { + elementtreestate *st = ET_STATE_GLOBAL; + return element_setstate_from_Python(st, self, state); + } } LOCAL(int) @@ -1190,9 +1193,10 @@ _elementtree_Element_extend(ElementObject *self, PyObject *elements) return NULL; } + elementtreestate *st = ET_STATE_GLOBAL; for (i = 0; i < PySequence_Fast_GET_SIZE(seq); i++) { PyObject* element = Py_NewRef(PySequence_Fast_GET_ITEM(seq, i)); - if (element_add_subelement(self, element) < 0) { + if (element_add_subelement(st, self, element) < 0) { Py_DECREF(seq); Py_DECREF(element); return NULL; @@ -1391,7 +1395,8 @@ _elementtree_Element_get_impl(ElementObject *self, PyObject *key, } static PyObject * -create_elementiter(ElementObject *self, PyObject *tag, int gettext); +create_elementiter(elementtreestate *st, ElementObject *self, PyObject *tag, + int gettext); /*[clinic input] @@ -1416,7 +1421,8 @@ _elementtree_Element_iter_impl(ElementObject *self, PyObject *tag) tag = Py_None; } - return create_elementiter(self, tag, 0); + elementtreestate *st = ET_STATE_GLOBAL; + return create_elementiter(st, self, tag, 0); } @@ -1429,7 +1435,8 @@ static PyObject * _elementtree_Element_itertext_impl(ElementObject *self) /*[clinic end generated code: output=5fa34b2fbcb65df6 input=af8f0e42cb239c89]*/ { - return create_elementiter(self, Py_None, 1); + elementtreestate *st = ET_STATE_GLOBAL; + return create_elementiter(st, self, Py_None, 1); } @@ -1567,7 +1574,8 @@ _elementtree_Element_makeelement_impl(ElementObject *self, PyObject *tag, if (!attrib) return NULL; - elem = create_new_element(tag, attrib); + elementtreestate *st = ET_STATE_GLOBAL; + elem = create_new_element(st, tag, attrib); Py_DECREF(attrib); @@ -2246,11 +2254,11 @@ static PyType_Spec elementiter_spec = { #define INIT_PARENT_STACK_SIZE 8 static PyObject * -create_elementiter(ElementObject *self, PyObject *tag, int gettext) +create_elementiter(elementtreestate *st, ElementObject *self, PyObject *tag, + int gettext) { ElementIterObject *it; - elementtreestate *st = ET_STATE_GLOBAL; it = PyObject_GC_New(ElementIterObject, st->ElementIter_Type); if (!it) return NULL; @@ -2506,11 +2514,11 @@ _elementtree__set_factories_impl(PyObject *module, PyObject *comment_factory, } static int -treebuilder_extend_element_text_or_tail(PyObject *element, PyObject **data, - PyObject **dest, PyObject *name) +treebuilder_extend_element_text_or_tail(elementtreestate *st, PyObject *element, + PyObject **data, PyObject **dest, + PyObject *name) { /* Fast paths for the "almost always" cases. */ - elementtreestate *st = ET_STATE_GLOBAL; if (Element_CheckExact(st, element)) { PyObject *dest_obj = JOIN_OBJ(*dest); if (dest_obj == Py_None) { @@ -2570,24 +2578,24 @@ treebuilder_flush_data(TreeBuilderObject* self) if (!self->last_for_tail) { PyObject *element = self->last; return treebuilder_extend_element_text_or_tail( - element, &self->data, + st, element, &self->data, &((ElementObject *) element)->text, st->str_text); } else { PyObject *element = self->last_for_tail; return treebuilder_extend_element_text_or_tail( - element, &self->data, + st, element, &self->data, &((ElementObject *) element)->tail, st->str_tail); } } static int -treebuilder_add_subelement(PyObject *element, PyObject *child) +treebuilder_add_subelement(elementtreestate *st, PyObject *element, + PyObject *child) { - elementtreestate *st = ET_STATE_GLOBAL; if (Element_CheckExact(st, element)) { ElementObject *elem = (ElementObject *) element; - return element_add_subelement(elem, child); + return element_add_subelement(st, elem, child); } else { PyObject *res; @@ -2633,8 +2641,9 @@ treebuilder_handle_start(TreeBuilderObject* self, PyObject* tag, } if (!self->element_factory) { - node = create_new_element(tag, attrib); - } else if (attrib == NULL) { + node = create_new_element(st, tag, attrib); + } + else if (attrib == NULL) { attrib = PyDict_New(); if (!attrib) return NULL; @@ -2654,8 +2663,9 @@ treebuilder_handle_start(TreeBuilderObject* self, PyObject* tag, Py_CLEAR(self->last_for_tail); if (this != Py_None) { - if (treebuilder_add_subelement(this, node) < 0) + if (treebuilder_add_subelement(st, this, node) < 0) { goto error; + } } else { if (self->root) { PyErr_SetString( @@ -2774,8 +2784,9 @@ treebuilder_handle_comment(TreeBuilderObject* self, PyObject* text) this = self->this; if (self->insert_comments && this != Py_None) { - if (treebuilder_add_subelement(this, comment) < 0) + if (treebuilder_add_subelement(self->state, this, comment) < 0) { goto error; + } Py_XSETREF(self->last_for_tail, Py_NewRef(comment)); } } else { @@ -2813,8 +2824,9 @@ treebuilder_handle_pi(TreeBuilderObject* self, PyObject* target, PyObject* text) this = self->this; if (self->insert_pis && this != Py_None) { - if (treebuilder_add_subelement(this, pi) < 0) + if (treebuilder_add_subelement(self->state, this, pi) < 0) { goto error; + } Py_XSETREF(self->last_for_tail, Py_NewRef(pi)); } } else { @@ -3091,11 +3103,10 @@ makeuniversal(XMLParserObject* self, const char* string) * message string is the default for the given error_code. */ static void -expat_set_error(enum XML_Error error_code, Py_ssize_t line, Py_ssize_t column, - const char *message) +expat_set_error(elementtreestate *st, enum XML_Error error_code, + Py_ssize_t line, Py_ssize_t column, const char *message) { PyObject *errmsg, *error, *position, *code; - elementtreestate *st = ET_STATE_GLOBAL; errmsg = PyUnicode_FromFormat("%s: line %zd, column %zd", message ? message : EXPAT(ErrorString)(error_code), @@ -3160,8 +3171,8 @@ expat_default_handler(XMLParserObject* self, const XML_Char* data_in, value = PyDict_GetItemWithError(self->entity, key); + elementtreestate *st = self->state; if (value) { - elementtreestate *st = self->state; if (TreeBuilder_CheckExact(st, self->target)) res = treebuilder_handle_data( (TreeBuilderObject*) self->target, value @@ -3176,6 +3187,7 @@ expat_default_handler(XMLParserObject* self, const XML_Char* data_in, char message[128] = "undefined entity "; strncat(message, data_in, data_len < 100?data_len:100); expat_set_error( + st, XML_ERROR_UNDEFINED_ENTITY, EXPAT(GetErrorLineNumber)(self->parser), EXPAT(GetErrorColumnNumber)(self->parser), @@ -3774,7 +3786,8 @@ _check_xmlparser(XMLParserObject* self) } LOCAL(PyObject*) -expat_parse(XMLParserObject* self, const char* data, int data_len, int final) +expat_parse(elementtreestate *st, XMLParserObject *self, const char *data, + int data_len, int final) { int ok; @@ -3786,6 +3799,7 @@ expat_parse(XMLParserObject* self, const char* data, int data_len, int final) if (!ok) { expat_set_error( + st, EXPAT(GetErrorCode)(self->parser), EXPAT(GetErrorLineNumber)(self->parser), EXPAT(GetErrorColumnNumber)(self->parser), @@ -3813,11 +3827,11 @@ _elementtree_XMLParser_close_impl(XMLParserObject *self) if (!_check_xmlparser(self)) { return NULL; } - res = expat_parse(self, "", 0, 1); + elementtreestate *st = self->state; + res = expat_parse(st, self, "", 0, 1); if (!res) return NULL; - elementtreestate *st = self->state; if (TreeBuilder_CheckExact(st, self->target)) { Py_DECREF(res); return treebuilder_done((TreeBuilderObject*) self->target); @@ -3848,6 +3862,7 @@ _elementtree_XMLParser_feed(XMLParserObject *self, PyObject *data) if (!_check_xmlparser(self)) { return NULL; } + elementtreestate *st = self->state; if (PyUnicode_Check(data)) { Py_ssize_t data_len; const char *data_ptr = PyUnicode_AsUTF8AndSize(data, &data_len); @@ -3859,7 +3874,8 @@ _elementtree_XMLParser_feed(XMLParserObject *self, PyObject *data) } /* Explicitly set UTF-8 encoding. Return code ignored. */ (void)EXPAT(SetEncoding)(self->parser, "utf-8"); - return expat_parse(self, data_ptr, (int)data_len, 0); + + return expat_parse(st, self, data_ptr, (int)data_len, 0); } else { Py_buffer view; @@ -3871,7 +3887,7 @@ _elementtree_XMLParser_feed(XMLParserObject *self, PyObject *data) PyErr_SetString(PyExc_OverflowError, "size does not fit in an int"); return NULL; } - res = expat_parse(self, view.buf, (int)view.len, 0); + res = expat_parse(st, self, view.buf, (int)view.len, 0); PyBuffer_Release(&view); return res; } @@ -3903,6 +3919,7 @@ _elementtree_XMLParser__parse_whole(XMLParserObject *self, PyObject *file) return NULL; /* read from open file object */ + elementtreestate *st = self->state; for (;;) { buffer = PyObject_CallFunction(reader, "i", 64*1024); @@ -3940,8 +3957,8 @@ _elementtree_XMLParser__parse_whole(XMLParserObject *self, PyObject *file) return NULL; } res = expat_parse( - self, PyBytes_AS_STRING(buffer), (int)PyBytes_GET_SIZE(buffer), 0 - ); + st, self, PyBytes_AS_STRING(buffer), (int)PyBytes_GET_SIZE(buffer), + 0); Py_DECREF(buffer); @@ -3955,9 +3972,8 @@ _elementtree_XMLParser__parse_whole(XMLParserObject *self, PyObject *file) Py_DECREF(reader); - res = expat_parse(self, "", 0, 1); + res = expat_parse(st, self, "", 0, 1); - elementtreestate *st = self->state; if (res && TreeBuilder_CheckExact(st, self->target)) { Py_DECREF(res); return treebuilder_done((TreeBuilderObject*) self->target); From webhook-mailer at python.org Tue Jan 24 10:07:37 2023 From: webhook-mailer at python.org (kumaraditya303) Date: Tue, 24 Jan 2023 15:07:37 -0000 Subject: [Python-checkins] GH-91375: Purge `asyncio` static variables from globals-to-fix list (#101288) Message-ID: https://github.com/python/cpython/commit/00d092caa8c6d6e6db349bd532533b8fc096f1f4 commit: 00d092caa8c6d6e6db349bd532533b8fc096f1f4 branch: main author: Erlend E. Aasland committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com> date: 2023-01-24T20:37:27+05:30 summary: GH-91375: Purge `asyncio` static variables from globals-to-fix list (#101288) files: M Tools/c-analyzer/cpython/globals-to-fix.tsv diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 761a2ab43e6c..cf2d5c368f1b 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -371,11 +371,6 @@ Modules/itertoolsmodule.c - ziplongest_type - ##----------------------- ## static types -Modules/_asynciomodule.c - FutureIterType - -Modules/_asynciomodule.c - FutureType - -Modules/_asynciomodule.c - PyRunningLoopHolder_Type - -Modules/_asynciomodule.c - TaskStepMethWrapper_Type - -Modules/_asynciomodule.c - TaskType - Modules/_ctypes/_ctypes.c - DictRemover_Type - Modules/_ctypes/_ctypes.c - PyCArrayType_Type - Modules/_ctypes/_ctypes.c - PyCArray_Type - @@ -466,21 +461,10 @@ Modules/xxmodule.c - ErrorObject - ## cached - initialized once ## manually cached PyUnicodeOjbect -Modules/_asynciomodule.c - context_kwname - Modules/_ctypes/callproc.c _ctypes_get_errobj error_object_name - Modules/_ctypes/_ctypes.c CreateSwappedType suffix - ## other - during module init -Modules/_asynciomodule.c - asyncio_mod - -Modules/_asynciomodule.c - traceback_extract_stack - -Modules/_asynciomodule.c - asyncio_future_repr_func - -Modules/_asynciomodule.c - asyncio_get_event_loop_policy - -Modules/_asynciomodule.c - asyncio_iscoroutine_func - -Modules/_asynciomodule.c - asyncio_task_get_stack_func - -Modules/_asynciomodule.c - asyncio_task_print_stack_func - -Modules/_asynciomodule.c - asyncio_task_repr_func - -Modules/_asynciomodule.c - asyncio_InvalidStateError - -Modules/_asynciomodule.c - asyncio_CancelledError - Modules/_zoneinfo.c - io_open - Modules/_zoneinfo.c - _tzpath_find_tzfile - Modules/_zoneinfo.c - _common_mod - @@ -512,12 +496,8 @@ Modules/_decimal/_decimal.c - SignalTuple - Modules/arraymodule.c array_array___reduce_ex___impl array_reconstructor - ## state -Modules/_asynciomodule.c - cached_running_holder - Modules/_asynciomodule.c - fi_freelist - Modules/_asynciomodule.c - fi_freelist_len - -Modules/_asynciomodule.c - all_tasks - -Modules/_asynciomodule.c - current_tasks - -Modules/_asynciomodule.c - iscoroutine_typecache - Modules/_ctypes/_ctypes.c - _ctypes_ptrtype_cache - Modules/_tkinter.c - tcl_lock - Modules/_tkinter.c - excInCmd - @@ -539,7 +519,6 @@ Modules/pyexpat.c PyUnknownEncodingHandler template_buffer - ## other Include/datetime.h - PyDateTimeAPI - -Modules/_asynciomodule.c - module_initialized - Modules/_ctypes/cfield.c _ctypes_get_fielddesc initialized - Modules/_ctypes/malloc_closure.c - _pagesize - Modules/_cursesmodule.c - initialised - @@ -585,8 +564,6 @@ Modules/socketmodule.c - sock_cloexec_works - ##----------------------- ## state -Modules/_asynciomodule.c - cached_running_holder_tsid - -Modules/_asynciomodule.c - task_name_counter - Modules/_ctypes/cfield.c - formattable - Modules/_ctypes/malloc_closure.c - free_list - Modules/_curses_panel.c - lop - From webhook-mailer at python.org Tue Jan 24 11:00:33 2023 From: webhook-mailer at python.org (erlend-aasland) Date: Tue, 24 Jan 2023 16:00:33 -0000 Subject: [Python-checkins] gh-92123: Adapt _elementtree to multi-phase init (PEP 489) (#101285) Message-ID: https://github.com/python/cpython/commit/fee7a995a18589001a432cea365fd04bf8efb7c1 commit: fee7a995a18589001a432cea365fd04bf8efb7c1 branch: main author: Erlend E. Aasland committer: erlend-aasland date: 2023-01-24T17:00:24+01:00 summary: gh-92123: Adapt _elementtree to multi-phase init (PEP 489) (#101285) files: A Misc/NEWS.d/next/Library/2023-01-24-12-53-59.gh-issue-92123.jf6TO5.rst M Modules/_elementtree.c M Modules/clinic/_elementtree.c.h diff --git a/Misc/NEWS.d/next/Library/2023-01-24-12-53-59.gh-issue-92123.jf6TO5.rst b/Misc/NEWS.d/next/Library/2023-01-24-12-53-59.gh-issue-92123.jf6TO5.rst new file mode 100644 index 000000000000..4b4443a55fdb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-24-12-53-59.gh-issue-92123.jf6TO5.rst @@ -0,0 +1,2 @@ +Adapt the ``_elementtree`` extension module to multi-phase init (:pep:`489`). +Patches by Erlend E. Aasland. diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index f7ab8881723f..97be89a16710 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -105,11 +105,21 @@ get_elementtree_state(PyObject *module) return (elementtreestate *)state; } -/* Find the module instance imported in the currently running sub-interpreter - * and get its state. - */ -#define ET_STATE_GLOBAL \ - ((elementtreestate *) PyModule_GetState(PyState_FindModule(&elementtreemodule))) +static inline elementtreestate * +get_elementtree_state_by_cls(PyTypeObject *cls) +{ + void *state = PyType_GetModuleState(cls); + assert(state != NULL); + return (elementtreestate *)state; +} + +static inline elementtreestate * +get_elementtree_state_by_type(PyTypeObject *tp) +{ + PyObject *mod = PyType_GetModuleByDef(tp, &elementtreemodule); + assert(mod != NULL); + return get_elementtree_state(mod); +} static int elementtree_clear(PyObject *m) @@ -585,7 +595,7 @@ subelement(PyObject *self, PyObject *args, PyObject *kwds) { PyObject* elem; - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = get_elementtree_state(self); ElementObject* parent; PyObject* tag; PyObject* attrib = NULL; @@ -684,16 +694,18 @@ element_dealloc(ElementObject* self) /*[clinic input] _elementtree.Element.append + cls: defining_class subelement: object(subclass_of='clinic_state()->Element_Type') / [clinic start generated code]*/ static PyObject * -_elementtree_Element_append_impl(ElementObject *self, PyObject *subelement) -/*[clinic end generated code: output=54a884b7cf2295f4 input=439f2bd777288fb6]*/ +_elementtree_Element_append_impl(ElementObject *self, PyTypeObject *cls, + PyObject *subelement) +/*[clinic end generated code: output=d00923711ea317fc input=8baf92679f9717b8]*/ { - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = get_elementtree_state_by_cls(cls); if (element_add_subelement(st, self, subelement) < 0) return NULL; @@ -720,15 +732,18 @@ _elementtree_Element_clear_impl(ElementObject *self) /*[clinic input] _elementtree.Element.__copy__ + cls: defining_class + / + [clinic start generated code]*/ static PyObject * -_elementtree_Element___copy___impl(ElementObject *self) -/*[clinic end generated code: output=2c701ebff7247781 input=ad87aaebe95675bf]*/ +_elementtree_Element___copy___impl(ElementObject *self, PyTypeObject *cls) +/*[clinic end generated code: output=da22894421ff2b36 input=91edb92d9f441213]*/ { Py_ssize_t i; ElementObject* element; - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = get_elementtree_state_by_cls(cls); element = (ElementObject*) create_new_element( st, self->tag, self->extra ? self->extra->attrib : NULL); @@ -782,7 +797,8 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo) PyObject* tail; PyObject* id; - elementtreestate *st = ET_STATE_GLOBAL; + PyTypeObject *tp = Py_TYPE(self); + elementtreestate *st = get_elementtree_state_by_type(tp); tag = deepcopy(st, self->tag, memo); if (!tag) return NULL; @@ -1093,14 +1109,16 @@ element_setstate_from_Python(elementtreestate *st, ElementObject *self, /*[clinic input] _elementtree.Element.__setstate__ + cls: defining_class state: object / [clinic start generated code]*/ static PyObject * -_elementtree_Element___setstate__(ElementObject *self, PyObject *state) -/*[clinic end generated code: output=ea28bf3491b1f75e input=aaf80abea7c1e3b9]*/ +_elementtree_Element___setstate___impl(ElementObject *self, + PyTypeObject *cls, PyObject *state) +/*[clinic end generated code: output=598bfb5730f71509 input=13830488d35d51f7]*/ { if (!PyDict_CheckExact(state)) { PyErr_Format(PyExc_TypeError, @@ -1109,7 +1127,7 @@ _elementtree_Element___setstate__(ElementObject *self, PyObject *state) return NULL; } else { - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = get_elementtree_state_by_cls(cls); return element_setstate_from_Python(st, self, state); } } @@ -1172,14 +1190,16 @@ checkpath(PyObject* tag) /*[clinic input] _elementtree.Element.extend + cls: defining_class elements: object / [clinic start generated code]*/ static PyObject * -_elementtree_Element_extend(ElementObject *self, PyObject *elements) -/*[clinic end generated code: output=f6e67fc2ff529191 input=807bc4f31c69f7c0]*/ +_elementtree_Element_extend_impl(ElementObject *self, PyTypeObject *cls, + PyObject *elements) +/*[clinic end generated code: output=3e86d37fac542216 input=6479b1b5379d09ae]*/ { PyObject* seq; Py_ssize_t i; @@ -1193,7 +1213,7 @@ _elementtree_Element_extend(ElementObject *self, PyObject *elements) return NULL; } - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = get_elementtree_state_by_cls(cls); for (i = 0; i < PySequence_Fast_GET_SIZE(seq); i++) { PyObject* element = Py_NewRef(PySequence_Fast_GET_ITEM(seq, i)); if (element_add_subelement(st, self, element) < 0) { @@ -1212,18 +1232,20 @@ _elementtree_Element_extend(ElementObject *self, PyObject *elements) /*[clinic input] _elementtree.Element.find + cls: defining_class + / path: object namespaces: object = None [clinic start generated code]*/ static PyObject * -_elementtree_Element_find_impl(ElementObject *self, PyObject *path, - PyObject *namespaces) -/*[clinic end generated code: output=41b43f0f0becafae input=359b6985f6489d2e]*/ +_elementtree_Element_find_impl(ElementObject *self, PyTypeObject *cls, + PyObject *path, PyObject *namespaces) +/*[clinic end generated code: output=18f77d393c9fef1b input=94df8a83f956acc6]*/ { Py_ssize_t i; - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = get_elementtree_state_by_cls(cls); if (checkpath(path) || namespaces != Py_None) { return PyObject_CallMethodObjArgs( @@ -1253,6 +1275,8 @@ _elementtree_Element_find_impl(ElementObject *self, PyObject *path, /*[clinic input] _elementtree.Element.findtext + cls: defining_class + / path: object default: object = None namespaces: object = None @@ -1260,13 +1284,13 @@ _elementtree.Element.findtext [clinic start generated code]*/ static PyObject * -_elementtree_Element_findtext_impl(ElementObject *self, PyObject *path, - PyObject *default_value, +_elementtree_Element_findtext_impl(ElementObject *self, PyTypeObject *cls, + PyObject *path, PyObject *default_value, PyObject *namespaces) -/*[clinic end generated code: output=83b3ba4535d308d2 input=b53a85aa5aa2a916]*/ +/*[clinic end generated code: output=6af7a2d96aac32cb input=32f252099f62a3d2]*/ { Py_ssize_t i; - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = get_elementtree_state_by_cls(cls); if (checkpath(path) || namespaces != Py_None) return PyObject_CallMethodObjArgs( @@ -1305,19 +1329,21 @@ _elementtree_Element_findtext_impl(ElementObject *self, PyObject *path, /*[clinic input] _elementtree.Element.findall + cls: defining_class + / path: object namespaces: object = None [clinic start generated code]*/ static PyObject * -_elementtree_Element_findall_impl(ElementObject *self, PyObject *path, - PyObject *namespaces) -/*[clinic end generated code: output=1a0bd9f5541b711d input=4d9e6505a638550c]*/ +_elementtree_Element_findall_impl(ElementObject *self, PyTypeObject *cls, + PyObject *path, PyObject *namespaces) +/*[clinic end generated code: output=65e39a1208f3b59e input=7aa0db45673fc9a5]*/ { Py_ssize_t i; PyObject* out; - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = get_elementtree_state_by_cls(cls); if (checkpath(path) || namespaces != Py_None) { return PyObject_CallMethodObjArgs( @@ -1352,18 +1378,20 @@ _elementtree_Element_findall_impl(ElementObject *self, PyObject *path, /*[clinic input] _elementtree.Element.iterfind + cls: defining_class + / path: object namespaces: object = None [clinic start generated code]*/ static PyObject * -_elementtree_Element_iterfind_impl(ElementObject *self, PyObject *path, - PyObject *namespaces) -/*[clinic end generated code: output=ecdd56d63b19d40f input=abb974e350fb65c7]*/ +_elementtree_Element_iterfind_impl(ElementObject *self, PyTypeObject *cls, + PyObject *path, PyObject *namespaces) +/*[clinic end generated code: output=be5c3f697a14e676 input=88766875a5c9a88b]*/ { PyObject* tag = path; - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = get_elementtree_state_by_cls(cls); return PyObject_CallMethodObjArgs( st->elementpath_obj, st->str_iterfind, self, tag, namespaces, NULL); @@ -1402,13 +1430,16 @@ create_elementiter(elementtreestate *st, ElementObject *self, PyObject *tag, /*[clinic input] _elementtree.Element.iter + cls: defining_class + / tag: object = None [clinic start generated code]*/ static PyObject * -_elementtree_Element_iter_impl(ElementObject *self, PyObject *tag) -/*[clinic end generated code: output=3f49f9a862941cc5 input=774d5b12e573aedd]*/ +_elementtree_Element_iter_impl(ElementObject *self, PyTypeObject *cls, + PyObject *tag) +/*[clinic end generated code: output=bff29dc5d4566c68 input=f6944c48d3f84c58]*/ { if (PyUnicode_Check(tag)) { if (PyUnicode_READY(tag) < 0) @@ -1421,7 +1452,7 @@ _elementtree_Element_iter_impl(ElementObject *self, PyObject *tag) tag = Py_None; } - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = get_elementtree_state_by_cls(cls); return create_elementiter(st, self, tag, 0); } @@ -1429,13 +1460,16 @@ _elementtree_Element_iter_impl(ElementObject *self, PyObject *tag) /*[clinic input] _elementtree.Element.itertext + cls: defining_class + / + [clinic start generated code]*/ static PyObject * -_elementtree_Element_itertext_impl(ElementObject *self) -/*[clinic end generated code: output=5fa34b2fbcb65df6 input=af8f0e42cb239c89]*/ +_elementtree_Element_itertext_impl(ElementObject *self, PyTypeObject *cls) +/*[clinic end generated code: output=fdeb2a3bca0ae063 input=a1ef1f0fc872a586]*/ { - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = get_elementtree_state_by_cls(cls); return create_elementiter(st, self, Py_None, 1); } @@ -1557,6 +1591,7 @@ element_length(ElementObject* self) /*[clinic input] _elementtree.Element.makeelement + cls: defining_class tag: object attrib: object(subclass_of='&PyDict_Type') / @@ -1564,9 +1599,9 @@ _elementtree.Element.makeelement [clinic start generated code]*/ static PyObject * -_elementtree_Element_makeelement_impl(ElementObject *self, PyObject *tag, - PyObject *attrib) -/*[clinic end generated code: output=4109832d5bb789ef input=2279d974529c3861]*/ +_elementtree_Element_makeelement_impl(ElementObject *self, PyTypeObject *cls, + PyObject *tag, PyObject *attrib) +/*[clinic end generated code: output=d50bb17a47077d47 input=589829dab92f26e8]*/ { PyObject* elem; @@ -1574,7 +1609,7 @@ _elementtree_Element_makeelement_impl(ElementObject *self, PyObject *tag, if (!attrib) return NULL; - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = get_elementtree_state_by_cls(cls); elem = create_new_element(st, tag, attrib); Py_DECREF(attrib); @@ -1706,7 +1741,8 @@ element_setitem(PyObject* self_, Py_ssize_t index, PyObject* item) old = self->extra->children[index]; if (item) { - elementtreestate *st = ET_STATE_GLOBAL; + PyTypeObject *tp = Py_TYPE(self); + elementtreestate *st = get_elementtree_state_by_type(tp); if (!Element_Check(st, item)) { raise_type_error(item); return -1; @@ -1904,7 +1940,8 @@ element_ass_subscr(PyObject* self_, PyObject* item, PyObject* value) } } - elementtreestate *st = ET_STATE_GLOBAL; + PyTypeObject *tp = Py_TYPE(self); + elementtreestate *st = get_elementtree_state_by_type(tp); for (i = 0; i < newlen; i++) { PyObject *element = PySequence_Fast_GET_ITEM(seq, i); if (!Element_Check(st, element)) { @@ -2181,7 +2218,8 @@ elementiter_next(ElementIterObject *it) } #ifndef NDEBUG - elementtreestate *st = ET_STATE_GLOBAL; + PyTypeObject *tp = Py_TYPE(it); + elementtreestate *st = get_elementtree_state_by_type(tp); assert(Element_Check(st, extra->children[child_index])); #endif elem = (ElementObject *)Py_NewRef(extra->children[child_index]); @@ -2348,7 +2386,7 @@ treebuilder_new(PyTypeObject *type, PyObject *args, PyObject *kwds) t->start_ns_event_obj = t->end_ns_event_obj = NULL; t->comment_event_obj = t->pi_event_obj = NULL; t->insert_comments = t->insert_pis = 0; - t->state = ET_STATE_GLOBAL; + t->state = get_elementtree_state_by_type(type); } return (PyObject *)t; } @@ -2481,7 +2519,7 @@ _elementtree__set_factories_impl(PyObject *module, PyObject *comment_factory, PyObject *pi_factory) /*[clinic end generated code: output=813b408adee26535 input=99d17627aea7fb3b]*/ { - elementtreestate *st = ET_STATE_GLOBAL; + elementtreestate *st = get_elementtree_state(module); PyObject *old; if (!PyCallable_Check(comment_factory) && comment_factory != Py_None) { @@ -3570,7 +3608,7 @@ xmlparser_new(PyTypeObject *type, PyObject *args, PyObject *kwds) self->handle_start = self->handle_data = self->handle_end = NULL; self->handle_comment = self->handle_pi = self->handle_close = NULL; self->handle_doctype = NULL; - self->state = ET_STATE_GLOBAL; + self->state = get_elementtree_state_by_type(type); } return (PyObject *)self; } @@ -4115,7 +4153,7 @@ static PyGetSetDef xmlparser_getsetlist[] = { {NULL}, }; -#define clinic_state() (ET_STATE_GLOBAL) +#define clinic_state() (get_elementtree_state_by_type(Py_TYPE(self))) #include "clinic/_elementtree.c.h" #undef clinic_state @@ -4274,19 +4312,6 @@ static PyMethodDef _functions[] = { {NULL, NULL} }; - -static struct PyModuleDef elementtreemodule = { - PyModuleDef_HEAD_INIT, - "_elementtree", - NULL, - sizeof(elementtreestate), - _functions, - NULL, - elementtree_traverse, - elementtree_clear, - elementtree_free -}; - #define CREATE_TYPE(module, type, spec) \ do { \ if (type != NULL) { \ @@ -4298,21 +4323,10 @@ do { \ } \ } while (0) -PyMODINIT_FUNC -PyInit__elementtree(void) +static int +module_exec(PyObject *m) { - PyObject *m = NULL; - elementtreestate *st = NULL; - - m = PyState_FindModule(&elementtreemodule); - if (m) { - return Py_NewRef(m); - } - - m = PyModule_Create(&elementtreemodule); - if (!m) - goto error; - st = get_elementtree_state(m); + elementtreestate *st = get_elementtree_state(m); /* Initialize object types */ CREATE_TYPE(m, st->ElementIter_Type, &elementiter_spec); @@ -4397,9 +4411,30 @@ PyInit__elementtree(void) } } - return m; + return 0; error: - Py_XDECREF(m); - return NULL; + return -1; +} + +static struct PyModuleDef_Slot elementtree_slots[] = { + {Py_mod_exec, module_exec}, + {0, NULL}, +}; + +static struct PyModuleDef elementtreemodule = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = "_elementtree", + .m_size = sizeof(elementtreestate), + .m_methods = _functions, + .m_slots = elementtree_slots, + .m_traverse = elementtree_traverse, + .m_clear = elementtree_clear, + .m_free = elementtree_free, +}; + +PyMODINIT_FUNC +PyInit__elementtree(void) +{ + return PyModuleDef_Init(&elementtreemodule); } diff --git a/Modules/clinic/_elementtree.c.h b/Modules/clinic/_elementtree.c.h index 4bf342c8b60d..a0bc751ca21e 100644 --- a/Modules/clinic/_elementtree.c.h +++ b/Modules/clinic/_elementtree.c.h @@ -14,23 +14,42 @@ PyDoc_STRVAR(_elementtree_Element_append__doc__, "\n"); #define _ELEMENTTREE_ELEMENT_APPEND_METHODDEF \ - {"append", (PyCFunction)_elementtree_Element_append, METH_O, _elementtree_Element_append__doc__}, + {"append", _PyCFunction_CAST(_elementtree_Element_append), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _elementtree_Element_append__doc__}, static PyObject * -_elementtree_Element_append_impl(ElementObject *self, PyObject *subelement); +_elementtree_Element_append_impl(ElementObject *self, PyTypeObject *cls, + PyObject *subelement); static PyObject * -_elementtree_Element_append(ElementObject *self, PyObject *arg) +_elementtree_Element_append(ElementObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "append", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; PyObject *subelement; - if (!PyObject_TypeCheck(arg, clinic_state()->Element_Type)) { - _PyArg_BadArgument("append", "argument", (clinic_state()->Element_Type)->tp_name, arg); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { goto exit; } - subelement = arg; - return_value = _elementtree_Element_append_impl(self, subelement); + if (!PyObject_TypeCheck(args[0], clinic_state()->Element_Type)) { + _PyArg_BadArgument("append", "argument 1", (clinic_state()->Element_Type)->tp_name, args[0]); + goto exit; + } + subelement = args[0]; + return_value = _elementtree_Element_append_impl(self, cls, subelement); exit: return return_value; @@ -59,15 +78,19 @@ PyDoc_STRVAR(_elementtree_Element___copy____doc__, "\n"); #define _ELEMENTTREE_ELEMENT___COPY___METHODDEF \ - {"__copy__", (PyCFunction)_elementtree_Element___copy__, METH_NOARGS, _elementtree_Element___copy____doc__}, + {"__copy__", _PyCFunction_CAST(_elementtree_Element___copy__), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _elementtree_Element___copy____doc__}, static PyObject * -_elementtree_Element___copy___impl(ElementObject *self); +_elementtree_Element___copy___impl(ElementObject *self, PyTypeObject *cls); static PyObject * -_elementtree_Element___copy__(ElementObject *self, PyObject *Py_UNUSED(ignored)) +_elementtree_Element___copy__(ElementObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { - return _elementtree_Element___copy___impl(self); + if (nargs) { + PyErr_SetString(PyExc_TypeError, "__copy__() takes no arguments"); + return NULL; + } + return _elementtree_Element___copy___impl(self, cls); } PyDoc_STRVAR(_elementtree_Element___deepcopy____doc__, @@ -148,7 +171,42 @@ PyDoc_STRVAR(_elementtree_Element___setstate____doc__, "\n"); #define _ELEMENTTREE_ELEMENT___SETSTATE___METHODDEF \ - {"__setstate__", (PyCFunction)_elementtree_Element___setstate__, METH_O, _elementtree_Element___setstate____doc__}, + {"__setstate__", _PyCFunction_CAST(_elementtree_Element___setstate__), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _elementtree_Element___setstate____doc__}, + +static PyObject * +_elementtree_Element___setstate___impl(ElementObject *self, + PyTypeObject *cls, PyObject *state); + +static PyObject * +_elementtree_Element___setstate__(ElementObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "__setstate__", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *state; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + state = args[0]; + return_value = _elementtree_Element___setstate___impl(self, cls, state); + +exit: + return return_value; +} PyDoc_STRVAR(_elementtree_Element_extend__doc__, "extend($self, elements, /)\n" @@ -156,7 +214,42 @@ PyDoc_STRVAR(_elementtree_Element_extend__doc__, "\n"); #define _ELEMENTTREE_ELEMENT_EXTEND_METHODDEF \ - {"extend", (PyCFunction)_elementtree_Element_extend, METH_O, _elementtree_Element_extend__doc__}, + {"extend", _PyCFunction_CAST(_elementtree_Element_extend), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _elementtree_Element_extend__doc__}, + +static PyObject * +_elementtree_Element_extend_impl(ElementObject *self, PyTypeObject *cls, + PyObject *elements); + +static PyObject * +_elementtree_Element_extend(ElementObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "extend", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *elements; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + elements = args[0]; + return_value = _elementtree_Element_extend_impl(self, cls, elements); + +exit: + return return_value; +} PyDoc_STRVAR(_elementtree_Element_find__doc__, "find($self, /, path, namespaces=None)\n" @@ -164,14 +257,14 @@ PyDoc_STRVAR(_elementtree_Element_find__doc__, "\n"); #define _ELEMENTTREE_ELEMENT_FIND_METHODDEF \ - {"find", _PyCFunction_CAST(_elementtree_Element_find), METH_FASTCALL|METH_KEYWORDS, _elementtree_Element_find__doc__}, + {"find", _PyCFunction_CAST(_elementtree_Element_find), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _elementtree_Element_find__doc__}, static PyObject * -_elementtree_Element_find_impl(ElementObject *self, PyObject *path, - PyObject *namespaces); +_elementtree_Element_find_impl(ElementObject *self, PyTypeObject *cls, + PyObject *path, PyObject *namespaces); static PyObject * -_elementtree_Element_find(ElementObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +_elementtree_Element_find(ElementObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -214,7 +307,7 @@ _elementtree_Element_find(ElementObject *self, PyObject *const *args, Py_ssize_t } namespaces = args[1]; skip_optional_pos: - return_value = _elementtree_Element_find_impl(self, path, namespaces); + return_value = _elementtree_Element_find_impl(self, cls, path, namespaces); exit: return return_value; @@ -226,15 +319,15 @@ PyDoc_STRVAR(_elementtree_Element_findtext__doc__, "\n"); #define _ELEMENTTREE_ELEMENT_FINDTEXT_METHODDEF \ - {"findtext", _PyCFunction_CAST(_elementtree_Element_findtext), METH_FASTCALL|METH_KEYWORDS, _elementtree_Element_findtext__doc__}, + {"findtext", _PyCFunction_CAST(_elementtree_Element_findtext), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _elementtree_Element_findtext__doc__}, static PyObject * -_elementtree_Element_findtext_impl(ElementObject *self, PyObject *path, - PyObject *default_value, +_elementtree_Element_findtext_impl(ElementObject *self, PyTypeObject *cls, + PyObject *path, PyObject *default_value, PyObject *namespaces); static PyObject * -_elementtree_Element_findtext(ElementObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +_elementtree_Element_findtext(ElementObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -284,7 +377,7 @@ _elementtree_Element_findtext(ElementObject *self, PyObject *const *args, Py_ssi } namespaces = args[2]; skip_optional_pos: - return_value = _elementtree_Element_findtext_impl(self, path, default_value, namespaces); + return_value = _elementtree_Element_findtext_impl(self, cls, path, default_value, namespaces); exit: return return_value; @@ -296,14 +389,14 @@ PyDoc_STRVAR(_elementtree_Element_findall__doc__, "\n"); #define _ELEMENTTREE_ELEMENT_FINDALL_METHODDEF \ - {"findall", _PyCFunction_CAST(_elementtree_Element_findall), METH_FASTCALL|METH_KEYWORDS, _elementtree_Element_findall__doc__}, + {"findall", _PyCFunction_CAST(_elementtree_Element_findall), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _elementtree_Element_findall__doc__}, static PyObject * -_elementtree_Element_findall_impl(ElementObject *self, PyObject *path, - PyObject *namespaces); +_elementtree_Element_findall_impl(ElementObject *self, PyTypeObject *cls, + PyObject *path, PyObject *namespaces); static PyObject * -_elementtree_Element_findall(ElementObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +_elementtree_Element_findall(ElementObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -346,7 +439,7 @@ _elementtree_Element_findall(ElementObject *self, PyObject *const *args, Py_ssiz } namespaces = args[1]; skip_optional_pos: - return_value = _elementtree_Element_findall_impl(self, path, namespaces); + return_value = _elementtree_Element_findall_impl(self, cls, path, namespaces); exit: return return_value; @@ -358,14 +451,14 @@ PyDoc_STRVAR(_elementtree_Element_iterfind__doc__, "\n"); #define _ELEMENTTREE_ELEMENT_ITERFIND_METHODDEF \ - {"iterfind", _PyCFunction_CAST(_elementtree_Element_iterfind), METH_FASTCALL|METH_KEYWORDS, _elementtree_Element_iterfind__doc__}, + {"iterfind", _PyCFunction_CAST(_elementtree_Element_iterfind), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _elementtree_Element_iterfind__doc__}, static PyObject * -_elementtree_Element_iterfind_impl(ElementObject *self, PyObject *path, - PyObject *namespaces); +_elementtree_Element_iterfind_impl(ElementObject *self, PyTypeObject *cls, + PyObject *path, PyObject *namespaces); static PyObject * -_elementtree_Element_iterfind(ElementObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +_elementtree_Element_iterfind(ElementObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -408,7 +501,7 @@ _elementtree_Element_iterfind(ElementObject *self, PyObject *const *args, Py_ssi } namespaces = args[1]; skip_optional_pos: - return_value = _elementtree_Element_iterfind_impl(self, path, namespaces); + return_value = _elementtree_Element_iterfind_impl(self, cls, path, namespaces); exit: return return_value; @@ -482,13 +575,14 @@ PyDoc_STRVAR(_elementtree_Element_iter__doc__, "\n"); #define _ELEMENTTREE_ELEMENT_ITER_METHODDEF \ - {"iter", _PyCFunction_CAST(_elementtree_Element_iter), METH_FASTCALL|METH_KEYWORDS, _elementtree_Element_iter__doc__}, + {"iter", _PyCFunction_CAST(_elementtree_Element_iter), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _elementtree_Element_iter__doc__}, static PyObject * -_elementtree_Element_iter_impl(ElementObject *self, PyObject *tag); +_elementtree_Element_iter_impl(ElementObject *self, PyTypeObject *cls, + PyObject *tag); static PyObject * -_elementtree_Element_iter(ElementObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +_elementtree_Element_iter(ElementObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -529,7 +623,7 @@ _elementtree_Element_iter(ElementObject *self, PyObject *const *args, Py_ssize_t } tag = args[0]; skip_optional_pos: - return_value = _elementtree_Element_iter_impl(self, tag); + return_value = _elementtree_Element_iter_impl(self, cls, tag); exit: return return_value; @@ -541,15 +635,19 @@ PyDoc_STRVAR(_elementtree_Element_itertext__doc__, "\n"); #define _ELEMENTTREE_ELEMENT_ITERTEXT_METHODDEF \ - {"itertext", (PyCFunction)_elementtree_Element_itertext, METH_NOARGS, _elementtree_Element_itertext__doc__}, + {"itertext", _PyCFunction_CAST(_elementtree_Element_itertext), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _elementtree_Element_itertext__doc__}, static PyObject * -_elementtree_Element_itertext_impl(ElementObject *self); +_elementtree_Element_itertext_impl(ElementObject *self, PyTypeObject *cls); static PyObject * -_elementtree_Element_itertext(ElementObject *self, PyObject *Py_UNUSED(ignored)) +_elementtree_Element_itertext(ElementObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { - return _elementtree_Element_itertext_impl(self); + if (nargs) { + PyErr_SetString(PyExc_TypeError, "itertext() takes no arguments"); + return NULL; + } + return _elementtree_Element_itertext_impl(self, cls); } PyDoc_STRVAR(_elementtree_Element_insert__doc__, @@ -637,20 +735,35 @@ PyDoc_STRVAR(_elementtree_Element_makeelement__doc__, "\n"); #define _ELEMENTTREE_ELEMENT_MAKEELEMENT_METHODDEF \ - {"makeelement", _PyCFunction_CAST(_elementtree_Element_makeelement), METH_FASTCALL, _elementtree_Element_makeelement__doc__}, + {"makeelement", _PyCFunction_CAST(_elementtree_Element_makeelement), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _elementtree_Element_makeelement__doc__}, static PyObject * -_elementtree_Element_makeelement_impl(ElementObject *self, PyObject *tag, - PyObject *attrib); +_elementtree_Element_makeelement_impl(ElementObject *self, PyTypeObject *cls, + PyObject *tag, PyObject *attrib); static PyObject * -_elementtree_Element_makeelement(ElementObject *self, PyObject *const *args, Py_ssize_t nargs) +_elementtree_Element_makeelement(ElementObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "makeelement", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; PyObject *tag; PyObject *attrib; - if (!_PyArg_CheckPositional("makeelement", nargs, 2, 2)) { + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf); + if (!args) { goto exit; } tag = args[0]; @@ -659,7 +772,7 @@ _elementtree_Element_makeelement(ElementObject *self, PyObject *const *args, Py_ goto exit; } attrib = args[1]; - return_value = _elementtree_Element_makeelement_impl(self, tag, attrib); + return_value = _elementtree_Element_makeelement_impl(self, cls, tag, attrib); exit: return return_value; @@ -1105,4 +1218,4 @@ _elementtree_XMLParser__setevents(XMLParserObject *self, PyObject *const *args, exit: return return_value; } -/*[clinic end generated code: output=d380adb43d8f4a62 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=40767b1a98e54b60 input=a9049054013a1b77]*/ From webhook-mailer at python.org Tue Jan 24 11:35:23 2023 From: webhook-mailer at python.org (zooba) Date: Tue, 24 Jan 2023 16:35:23 -0000 Subject: [Python-checkins] gh-101135: Add backwards compatibility to Windows launcher for older 32-bit versions (GH-101138) Message-ID: https://github.com/python/cpython/commit/daec3a463c747c852d7ee91e82770fb1763d7d31 commit: daec3a463c747c852d7ee91e82770fb1763d7d31 branch: main author: Martin Boisvert committer: zooba date: 2023-01-24T16:35:16Z summary: gh-101135: Add backwards compatibility to Windows launcher for older 32-bit versions (GH-101138) Python 2.x and up to 3.4 did not contain the "-32" in their registry name, so the 32 and 64-bit installs were treated equal. Since 3.5/PEP 514 this is no longer true, but we still want to detect the EOL versions correctly in case people are still using them. Additionally, the code to replace a node with one with a lower sort key was buggy (wrong node chosen, replace never happened since parent was always NULL, replaced node never freed, etc) files: A Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst M PC/launcher2.c diff --git a/Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst b/Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst new file mode 100644 index 000000000000..2e6d6371340d --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst @@ -0,0 +1,3 @@ +Restore ability to launch older 32-bit versions from the :file:`py.exe` +launcher when both 32-bit and 64-bit installs of the same version are +available. diff --git a/PC/launcher2.c b/PC/launcher2.c index 8371c6014cd9..4c77ec0be439 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -1294,34 +1294,34 @@ _compareTag(const wchar_t *x, const wchar_t *y) int -addEnvironmentInfo(EnvironmentInfo **root, EnvironmentInfo *node) +addEnvironmentInfo(EnvironmentInfo **root, EnvironmentInfo* parent, EnvironmentInfo *node) { EnvironmentInfo *r = *root; if (!r) { *root = node; - node->parent = NULL; + node->parent = parent; return 0; } // Sort by company name switch (_compareCompany(node->company, r->company)) { case -1: - return addEnvironmentInfo(&r->prev, node); + return addEnvironmentInfo(&r->prev, r, node); case 1: - return addEnvironmentInfo(&r->next, node); + return addEnvironmentInfo(&r->next, r, node); case 0: break; } // Then by tag (descending) switch (_compareTag(node->tag, r->tag)) { case -1: - return addEnvironmentInfo(&r->next, node); + return addEnvironmentInfo(&r->next, r, node); case 1: - return addEnvironmentInfo(&r->prev, node); + return addEnvironmentInfo(&r->prev, r, node); case 0: break; } // Then keep the one with the lowest internal sort key - if (r->internalSortKey < node->internalSortKey) { + if (node->internalSortKey < r->internalSortKey) { // Replace the current node node->parent = r->parent; if (node->parent) { @@ -1334,9 +1334,16 @@ addEnvironmentInfo(EnvironmentInfo **root, EnvironmentInfo *node) freeEnvironmentInfo(node); return RC_INTERNAL_ERROR; } + } else { + // If node has no parent, then it is the root. + *root = node; } + node->next = r->next; node->prev = r->prev; + + debug(L"# replaced %s/%s/%i in tree\n", node->company, node->tag, node->internalSortKey); + freeEnvironmentInfo(r); } else { debug(L"# not adding %s/%s/%i to tree\n", node->company, node->tag, node->internalSortKey); return RC_DUPLICATE_ITEM; @@ -1392,6 +1399,100 @@ _combineWithInstallDir(const wchar_t **dest, const wchar_t *installDir, const wc } +bool +_isLegacyVersion(EnvironmentInfo *env) +{ + // Check if backwards-compatibility is required. + // Specifically PythonCore versions 2.X and 3.0 - 3.5 do not implement PEP 514. + if (0 != _compare(env->company, -1, L"PythonCore", -1)) { + return false; + } + + int versionMajor, versionMinor; + int n = swscanf_s(env->tag, L"%d.%d", &versionMajor, &versionMinor); + if (n != 2) { + debug(L"# %s/%s has an invalid version tag\n", env->company, env->tag); + return false; + } + + return versionMajor == 2 + || (versionMajor == 3 && versionMinor >= 0 && versionMinor <= 5); +} + +int +_registryReadLegacyEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *env, const wchar_t *fallbackArch) +{ + // Backwards-compatibility for PythonCore versions which do not implement PEP 514. + int exitCode = _combineWithInstallDir( + &env->executablePath, + env->installDir, + search->executable, + search->executableLength + ); + if (exitCode) { + return exitCode; + } + + if (search->windowed) { + exitCode = _registryReadString(&env->executableArgs, root, L"InstallPath", L"WindowedExecutableArguments"); + } + else { + exitCode = _registryReadString(&env->executableArgs, root, L"InstallPath", L"ExecutableArguments"); + } + if (exitCode) { + return exitCode; + } + + if (fallbackArch) { + copyWstr(&env->architecture, fallbackArch); + } else { + DWORD binaryType; + BOOL success = GetBinaryTypeW(env->executablePath, &binaryType); + if (!success) { + return RC_NO_PYTHON; + } + + switch (binaryType) { + case SCS_32BIT_BINARY: + copyWstr(&env->architecture, L"32bit"); + break; + case SCS_64BIT_BINARY: + copyWstr(&env->architecture, L"64bit"); + break; + default: + return RC_NO_PYTHON; + } + } + + if (0 == _compare(env->architecture, -1, L"32bit", -1)) { + size_t tagLength = wcslen(env->tag); + if (tagLength <= 3 || 0 != _compare(&env->tag[tagLength - 3], 3, L"-32", 3)) { + const wchar_t *rawTag = env->tag; + wchar_t *realTag = (wchar_t*) malloc(sizeof(wchar_t) * (tagLength + 4)); + if (!realTag) { + return RC_NO_MEMORY; + } + + int count = swprintf_s(realTag, tagLength + 4, L"%s-32", env->tag); + if (count == -1) { + free(realTag); + return RC_INTERNAL_ERROR; + } + + env->tag = realTag; + free((void*)rawTag); + } + } + + wchar_t buffer[MAXLEN]; + if (swprintf_s(buffer, MAXLEN, L"Python %s", env->tag)) { + copyWstr(&env->displayName, buffer); + } + + return 0; +} + + int _registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *env, const wchar_t *fallbackArch) { @@ -1403,6 +1504,10 @@ _registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *e return RC_NO_PYTHON; } + if (_isLegacyVersion(env)) { + return _registryReadLegacyEnvironment(search, root, env, fallbackArch); + } + // If pythonw.exe requested, check specific value if (search->windowed) { exitCode = _registryReadString(&env->executablePath, root, L"InstallPath", L"WindowedExecutablePath"); @@ -1425,6 +1530,11 @@ _registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *e return exitCode; } + if (!env->executablePath) { + debug(L"# %s/%s has no executable path\n", env->company, env->tag); + return RC_NO_PYTHON; + } + exitCode = _registryReadString(&env->architecture, root, NULL, L"SysArchitecture"); if (exitCode) { return exitCode; @@ -1435,29 +1545,6 @@ _registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *e return exitCode; } - // Only PythonCore entries will infer executablePath from installDir and architecture from the binary - if (0 == _compare(env->company, -1, L"PythonCore", -1)) { - if (!env->executablePath) { - exitCode = _combineWithInstallDir( - &env->executablePath, - env->installDir, - search->executable, - search->executableLength - ); - if (exitCode) { - return exitCode; - } - } - if (!env->architecture && env->executablePath && fallbackArch) { - copyWstr(&env->architecture, fallbackArch); - } - } - - if (!env->executablePath) { - debug(L"# %s/%s has no executable path\n", env->company, env->tag); - return RC_NO_PYTHON; - } - return 0; } @@ -1486,7 +1573,7 @@ _registrySearchTags(const SearchInfo *search, EnvironmentInfo **result, HKEY roo freeEnvironmentInfo(env); exitCode = 0; } else if (!exitCode) { - exitCode = addEnvironmentInfo(result, env); + exitCode = addEnvironmentInfo(result, NULL, env); if (exitCode) { freeEnvironmentInfo(env); if (exitCode == RC_DUPLICATE_ITEM) { @@ -1574,7 +1661,7 @@ appxSearch(const SearchInfo *search, EnvironmentInfo **result, const wchar_t *pa copyWstr(&env->displayName, buffer); } - int exitCode = addEnvironmentInfo(result, env); + int exitCode = addEnvironmentInfo(result, NULL, env); if (exitCode) { freeEnvironmentInfo(env); if (exitCode == RC_DUPLICATE_ITEM) { @@ -1612,7 +1699,7 @@ explicitOverrideSearch(const SearchInfo *search, EnvironmentInfo **result) if (exitCode) { goto abort; } - exitCode = addEnvironmentInfo(result, env); + exitCode = addEnvironmentInfo(result, NULL, env); if (exitCode) { goto abort; } @@ -1661,7 +1748,7 @@ virtualenvSearch(const SearchInfo *search, EnvironmentInfo **result) if (exitCode) { goto abort; } - exitCode = addEnvironmentInfo(result, env); + exitCode = addEnvironmentInfo(result, NULL, env); if (exitCode) { goto abort; } From webhook-mailer at python.org Tue Jan 24 12:04:09 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 24 Jan 2023 17:04:09 -0000 Subject: [Python-checkins] gh-101135: Add backwards compatibility to Windows launcher for older 32-bit versions (GH-101138) Message-ID: https://github.com/python/cpython/commit/5a8ed019f9b826d2ba5766688d51005624cd0699 commit: 5a8ed019f9b826d2ba5766688d51005624cd0699 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-24T09:04:00-08:00 summary: gh-101135: Add backwards compatibility to Windows launcher for older 32-bit versions (GH-101138) Python 2.x and up to 3.4 did not contain the "-32" in their registry name, so the 32 and 64-bit installs were treated equal. Since 3.5/PEP 514 this is no longer true, but we still want to detect the EOL versions correctly in case people are still using them. Additionally, the code to replace a node with one with a lower sort key was buggy (wrong node chosen, replace never happened since parent was always NULL, replaced node never freed, etc) (cherry picked from commit daec3a463c747c852d7ee91e82770fb1763d7d31) Co-authored-by: Martin Boisvert files: A Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst M PC/launcher2.c diff --git a/Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst b/Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst new file mode 100644 index 000000000000..2e6d6371340d --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-18-18-25-18.gh-issue-101135.HF9VlG.rst @@ -0,0 +1,3 @@ +Restore ability to launch older 32-bit versions from the :file:`py.exe` +launcher when both 32-bit and 64-bit installs of the same version are +available. diff --git a/PC/launcher2.c b/PC/launcher2.c index 8371c6014cd9..4c77ec0be439 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -1294,34 +1294,34 @@ _compareTag(const wchar_t *x, const wchar_t *y) int -addEnvironmentInfo(EnvironmentInfo **root, EnvironmentInfo *node) +addEnvironmentInfo(EnvironmentInfo **root, EnvironmentInfo* parent, EnvironmentInfo *node) { EnvironmentInfo *r = *root; if (!r) { *root = node; - node->parent = NULL; + node->parent = parent; return 0; } // Sort by company name switch (_compareCompany(node->company, r->company)) { case -1: - return addEnvironmentInfo(&r->prev, node); + return addEnvironmentInfo(&r->prev, r, node); case 1: - return addEnvironmentInfo(&r->next, node); + return addEnvironmentInfo(&r->next, r, node); case 0: break; } // Then by tag (descending) switch (_compareTag(node->tag, r->tag)) { case -1: - return addEnvironmentInfo(&r->next, node); + return addEnvironmentInfo(&r->next, r, node); case 1: - return addEnvironmentInfo(&r->prev, node); + return addEnvironmentInfo(&r->prev, r, node); case 0: break; } // Then keep the one with the lowest internal sort key - if (r->internalSortKey < node->internalSortKey) { + if (node->internalSortKey < r->internalSortKey) { // Replace the current node node->parent = r->parent; if (node->parent) { @@ -1334,9 +1334,16 @@ addEnvironmentInfo(EnvironmentInfo **root, EnvironmentInfo *node) freeEnvironmentInfo(node); return RC_INTERNAL_ERROR; } + } else { + // If node has no parent, then it is the root. + *root = node; } + node->next = r->next; node->prev = r->prev; + + debug(L"# replaced %s/%s/%i in tree\n", node->company, node->tag, node->internalSortKey); + freeEnvironmentInfo(r); } else { debug(L"# not adding %s/%s/%i to tree\n", node->company, node->tag, node->internalSortKey); return RC_DUPLICATE_ITEM; @@ -1392,6 +1399,100 @@ _combineWithInstallDir(const wchar_t **dest, const wchar_t *installDir, const wc } +bool +_isLegacyVersion(EnvironmentInfo *env) +{ + // Check if backwards-compatibility is required. + // Specifically PythonCore versions 2.X and 3.0 - 3.5 do not implement PEP 514. + if (0 != _compare(env->company, -1, L"PythonCore", -1)) { + return false; + } + + int versionMajor, versionMinor; + int n = swscanf_s(env->tag, L"%d.%d", &versionMajor, &versionMinor); + if (n != 2) { + debug(L"# %s/%s has an invalid version tag\n", env->company, env->tag); + return false; + } + + return versionMajor == 2 + || (versionMajor == 3 && versionMinor >= 0 && versionMinor <= 5); +} + +int +_registryReadLegacyEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *env, const wchar_t *fallbackArch) +{ + // Backwards-compatibility for PythonCore versions which do not implement PEP 514. + int exitCode = _combineWithInstallDir( + &env->executablePath, + env->installDir, + search->executable, + search->executableLength + ); + if (exitCode) { + return exitCode; + } + + if (search->windowed) { + exitCode = _registryReadString(&env->executableArgs, root, L"InstallPath", L"WindowedExecutableArguments"); + } + else { + exitCode = _registryReadString(&env->executableArgs, root, L"InstallPath", L"ExecutableArguments"); + } + if (exitCode) { + return exitCode; + } + + if (fallbackArch) { + copyWstr(&env->architecture, fallbackArch); + } else { + DWORD binaryType; + BOOL success = GetBinaryTypeW(env->executablePath, &binaryType); + if (!success) { + return RC_NO_PYTHON; + } + + switch (binaryType) { + case SCS_32BIT_BINARY: + copyWstr(&env->architecture, L"32bit"); + break; + case SCS_64BIT_BINARY: + copyWstr(&env->architecture, L"64bit"); + break; + default: + return RC_NO_PYTHON; + } + } + + if (0 == _compare(env->architecture, -1, L"32bit", -1)) { + size_t tagLength = wcslen(env->tag); + if (tagLength <= 3 || 0 != _compare(&env->tag[tagLength - 3], 3, L"-32", 3)) { + const wchar_t *rawTag = env->tag; + wchar_t *realTag = (wchar_t*) malloc(sizeof(wchar_t) * (tagLength + 4)); + if (!realTag) { + return RC_NO_MEMORY; + } + + int count = swprintf_s(realTag, tagLength + 4, L"%s-32", env->tag); + if (count == -1) { + free(realTag); + return RC_INTERNAL_ERROR; + } + + env->tag = realTag; + free((void*)rawTag); + } + } + + wchar_t buffer[MAXLEN]; + if (swprintf_s(buffer, MAXLEN, L"Python %s", env->tag)) { + copyWstr(&env->displayName, buffer); + } + + return 0; +} + + int _registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *env, const wchar_t *fallbackArch) { @@ -1403,6 +1504,10 @@ _registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *e return RC_NO_PYTHON; } + if (_isLegacyVersion(env)) { + return _registryReadLegacyEnvironment(search, root, env, fallbackArch); + } + // If pythonw.exe requested, check specific value if (search->windowed) { exitCode = _registryReadString(&env->executablePath, root, L"InstallPath", L"WindowedExecutablePath"); @@ -1425,6 +1530,11 @@ _registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *e return exitCode; } + if (!env->executablePath) { + debug(L"# %s/%s has no executable path\n", env->company, env->tag); + return RC_NO_PYTHON; + } + exitCode = _registryReadString(&env->architecture, root, NULL, L"SysArchitecture"); if (exitCode) { return exitCode; @@ -1435,29 +1545,6 @@ _registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *e return exitCode; } - // Only PythonCore entries will infer executablePath from installDir and architecture from the binary - if (0 == _compare(env->company, -1, L"PythonCore", -1)) { - if (!env->executablePath) { - exitCode = _combineWithInstallDir( - &env->executablePath, - env->installDir, - search->executable, - search->executableLength - ); - if (exitCode) { - return exitCode; - } - } - if (!env->architecture && env->executablePath && fallbackArch) { - copyWstr(&env->architecture, fallbackArch); - } - } - - if (!env->executablePath) { - debug(L"# %s/%s has no executable path\n", env->company, env->tag); - return RC_NO_PYTHON; - } - return 0; } @@ -1486,7 +1573,7 @@ _registrySearchTags(const SearchInfo *search, EnvironmentInfo **result, HKEY roo freeEnvironmentInfo(env); exitCode = 0; } else if (!exitCode) { - exitCode = addEnvironmentInfo(result, env); + exitCode = addEnvironmentInfo(result, NULL, env); if (exitCode) { freeEnvironmentInfo(env); if (exitCode == RC_DUPLICATE_ITEM) { @@ -1574,7 +1661,7 @@ appxSearch(const SearchInfo *search, EnvironmentInfo **result, const wchar_t *pa copyWstr(&env->displayName, buffer); } - int exitCode = addEnvironmentInfo(result, env); + int exitCode = addEnvironmentInfo(result, NULL, env); if (exitCode) { freeEnvironmentInfo(env); if (exitCode == RC_DUPLICATE_ITEM) { @@ -1612,7 +1699,7 @@ explicitOverrideSearch(const SearchInfo *search, EnvironmentInfo **result) if (exitCode) { goto abort; } - exitCode = addEnvironmentInfo(result, env); + exitCode = addEnvironmentInfo(result, NULL, env); if (exitCode) { goto abort; } @@ -1661,7 +1748,7 @@ virtualenvSearch(const SearchInfo *search, EnvironmentInfo **result) if (exitCode) { goto abort; } - exitCode = addEnvironmentInfo(result, env); + exitCode = addEnvironmentInfo(result, NULL, env); if (exitCode) { goto abort; } From webhook-mailer at python.org Tue Jan 24 12:25:47 2023 From: webhook-mailer at python.org (markshannon) Date: Tue, 24 Jan 2023 17:25:47 -0000 Subject: [Python-checkins] GH-100762: Don't call `gen.throw()` in `gen.close()`, unless necessary. (GH-101013) Message-ID: https://github.com/python/cpython/commit/f02fa64bf2d03ef7a28650c164e17a5fb5d8543d commit: f02fa64bf2d03ef7a28650c164e17a5fb5d8543d branch: main author: Mark Shannon committer: markshannon date: 2023-01-24T17:25:37Z summary: GH-100762: Don't call `gen.throw()` in `gen.close()`, unless necessary. (GH-101013) * Store exception stack depth in YIELD_VALUE's oparg and use it avoid expensive gen.throw() in gen.close() where possible. files: A Misc/NEWS.d/next/Core and Builtins/2023-01-13-12-56-20.gh-issue-100762.YvHaQJ.rst M Doc/library/dis.rst M Lib/importlib/_bootstrap_external.py M Lib/test/test_dis.py M Objects/genobject.c M Python/bytecodes.c M Python/compile.c M Python/generated_cases.c.h M Python/opcode_metadata.h diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 7f309ce56dde..6a68ec4b14be 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -700,7 +700,10 @@ iterations of the loop. Yields ``STACK.pop()`` from a :term:`generator`. .. versionchanged:: 3.11 - oparg set to be the stack depth, for efficient handling on frames. + oparg set to be the stack depth. + + .. versionchanged:: 3.12 + oparg set to be the exception block depth, for efficient closing of generators. .. opcode:: SETUP_ANNOTATIONS diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 5a8cbb98ed3f..cd44d0050ede 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -429,7 +429,8 @@ def _write_atomic(path, data, mode=0o666): # Python 3.12a1 3513 (Add CALL_INTRINSIC_1 instruction, removed STOPITERATION_ERROR, PRINT_EXPR, IMPORT_STAR) # Python 3.12a1 3514 (Remove ASYNC_GEN_WRAP, LIST_TO_TUPLE, and UNARY_POSITIVE) # Python 3.12a1 3515 (Embed jump mask in COMPARE_OP oparg) -# Python 3.12a1 3516 (Add COMAPRE_AND_BRANCH instruction) +# Python 3.12a1 3516 (Add COMPARE_AND_BRANCH instruction) +# Python 3.12a1 3517 (Change YIELD_VALUE oparg to exception block depth) # Python 3.13 will start with 3550 @@ -442,7 +443,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3516).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3517).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 27487de8fb3d..bdf48c153092 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -492,7 +492,7 @@ async def _asyncwith(c): GET_AWAITABLE 1 LOAD_CONST 0 (None) >> SEND 3 (to 22) - YIELD_VALUE 3 + YIELD_VALUE 2 RESUME 3 JUMP_BACKWARD_NO_INTERRUPT 4 (to 14) >> POP_TOP @@ -526,7 +526,7 @@ async def _asyncwith(c): GET_AWAITABLE 2 LOAD_CONST 0 (None) >> SEND 4 (to 92) - YIELD_VALUE 6 + YIELD_VALUE 3 RESUME 3 JUMP_BACKWARD_NO_INTERRUPT 4 (to 82) >> CLEANUP_THROW diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-13-12-56-20.gh-issue-100762.YvHaQJ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-13-12-56-20.gh-issue-100762.YvHaQJ.rst new file mode 100644 index 000000000000..2f6b121439a9 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-13-12-56-20.gh-issue-100762.YvHaQJ.rst @@ -0,0 +1,3 @@ +Record the (virtual) exception block depth in the oparg of +:opcode:`YIELD_VALUE`. Use this to avoid the expensive ``throw()`` when +closing generators (and coroutines) that can be closed trivially. diff --git a/Objects/genobject.c b/Objects/genobject.c index 2adb1e4bf308..35246653c453 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -354,6 +354,13 @@ gen_close(PyGenObject *gen, PyObject *args) PyObject *yf = _PyGen_yf(gen); int err = 0; + if (gen->gi_frame_state == FRAME_CREATED) { + gen->gi_frame_state = FRAME_COMPLETED; + Py_RETURN_NONE; + } + if (gen->gi_frame_state >= FRAME_COMPLETED) { + Py_RETURN_NONE; + } if (yf) { PyFrameState state = gen->gi_frame_state; gen->gi_frame_state = FRAME_EXECUTING; @@ -361,8 +368,23 @@ gen_close(PyGenObject *gen, PyObject *args) gen->gi_frame_state = state; Py_DECREF(yf); } - if (err == 0) + _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe; + /* It is possible for the previous instruction to not be a + * YIELD_VALUE if the debugger has changed the lineno. */ + if (err == 0 && frame->prev_instr->opcode == YIELD_VALUE) { + assert(frame->prev_instr[1].opcode == RESUME); + int exception_handler_depth = frame->prev_instr->oparg; + assert(exception_handler_depth > 0); + /* We can safely ignore the outermost try block + * as it automatically generated to handle + * StopIteration. */ + if (exception_handler_depth == 1) { + Py_RETURN_NONE; + } + } + if (err == 0) { PyErr_SetNone(PyExc_GeneratorExit); + } retval = gen_send_ex(gen, Py_None, 1, 1); if (retval) { const char *msg = "generator ignored GeneratorExit"; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 47bbe1a99e3b..ac54791d67e4 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -720,7 +720,6 @@ dummy_func( // NOTE: It's important that YIELD_VALUE never raises an exception! // The compiler treats any exception raised here as a failed close() // or throw() call. - assert(oparg == STACK_LEVEL()); assert(frame != &entry_frame); PyGenObject *gen = _PyFrame_GetGenerator(frame); gen->gi_frame_state = FRAME_SUSPENDED; diff --git a/Python/compile.c b/Python/compile.c index ce714dce6edf..9fc997cdf525 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -7162,9 +7162,6 @@ stackdepth(basicblock *entryblock, int code_flags) next = NULL; break; } - if (instr->i_opcode == YIELD_VALUE) { - instr->i_oparg = depth; - } } if (next != NULL) { assert(BB_HAS_FALLTHROUGH(b)); @@ -7332,6 +7329,9 @@ label_exception_targets(basicblock *entryblock) { } } else { + if (instr->i_opcode == YIELD_VALUE) { + instr->i_oparg = except_stack->depth; + } instr->i_except = handler; } } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index c1eb4000883d..5dcc8eeec19f 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -919,7 +919,6 @@ // NOTE: It's important that YIELD_VALUE never raises an exception! // The compiler treats any exception raised here as a failed close() // or throw() call. - assert(oparg == STACK_LEVEL()); assert(frame != &entry_frame); PyGenObject *gen = _PyFrame_GetGenerator(frame); gen->gi_frame_state = FRAME_SUSPENDED; diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 3ceaca8c397f..1fb0acceb511 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -59,7 +59,7 @@ static const struct { [GET_ANEXT] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_AWAITABLE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [SEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [YIELD_VALUE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [YIELD_VALUE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [POP_EXCEPT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [RERAISE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [PREP_RERAISE_STAR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, From webhook-mailer at python.org Tue Jan 24 17:39:20 2023 From: webhook-mailer at python.org (iritkatriel) Date: Tue, 24 Jan 2023 22:39:20 -0000 Subject: [Python-checkins] gh-98831: rewrite pattern matching opcodes in the instruction definition DSL (#101287) Message-ID: https://github.com/python/cpython/commit/1a9d8c750be83e6abb65769d312361fe9742de02 commit: 1a9d8c750be83e6abb65769d312361fe9742de02 branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-24T22:39:13Z summary: gh-98831: rewrite pattern matching opcodes in the instruction definition DSL (#101287) files: M Python/bytecodes.c M Python/generated_cases.c.h M Python/opcode_metadata.h M Tools/cases_generator/generate_cases.py M Tools/cases_generator/test_generator.py diff --git a/Python/bytecodes.c b/Python/bytecodes.c index ac54791d67e4..d3e242b81e60 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2092,61 +2092,37 @@ dummy_func( PUSH(len_o); } - // stack effect: (__0, __1 -- ) - inst(MATCH_CLASS) { + inst(MATCH_CLASS, (subject, type, names -- attrs)) { // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or // None on failure. - PyObject *names = POP(); - PyObject *type = POP(); - PyObject *subject = TOP(); assert(PyTuple_CheckExact(names)); - PyObject *attrs = match_class(tstate, subject, type, oparg, names); - Py_DECREF(names); - Py_DECREF(type); + attrs = match_class(tstate, subject, type, oparg, names); + DECREF_INPUTS(); if (attrs) { - // Success! - assert(PyTuple_CheckExact(attrs)); - SET_TOP(attrs); - } - else if (_PyErr_Occurred(tstate)) { - // Error! - goto error; + assert(PyTuple_CheckExact(attrs)); // Success! } else { - // Failure! - SET_TOP(Py_NewRef(Py_None)); + ERROR_IF(_PyErr_Occurred(tstate), error); // Error! + attrs = Py_NewRef(Py_None); // Failure! } - Py_DECREF(subject); } - // stack effect: ( -- __0) - inst(MATCH_MAPPING) { - PyObject *subject = TOP(); + inst(MATCH_MAPPING, (subject -- subject, res)) { int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; - PyObject *res = match ? Py_True : Py_False; - PUSH(Py_NewRef(res)); + res = Py_NewRef(match ? Py_True : Py_False); PREDICT(POP_JUMP_IF_FALSE); } - // stack effect: ( -- __0) - inst(MATCH_SEQUENCE) { - PyObject *subject = TOP(); + inst(MATCH_SEQUENCE, (subject -- subject, res)) { int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; - PyObject *res = match ? Py_True : Py_False; - PUSH(Py_NewRef(res)); + res = Py_NewRef(match ? Py_True : Py_False); PREDICT(POP_JUMP_IF_FALSE); } - // stack effect: ( -- __0) - inst(MATCH_KEYS) { + inst(MATCH_KEYS, (subject, keys -- subject, keys, values_or_none)) { // On successful match, PUSH(values). Otherwise, PUSH(None). - PyObject *keys = TOP(); - PyObject *subject = SECOND(); - PyObject *values_or_none = match_keys(tstate, subject, keys); - if (values_or_none == NULL) { - goto error; - } - PUSH(values_or_none); + values_or_none = match_keys(tstate, subject, keys); + ERROR_IF(values_or_none == NULL, error); } // stack effect: ( -- ) diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 5dcc8eeec19f..7d3396ad6bde 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2479,59 +2479,60 @@ } TARGET(MATCH_CLASS) { + PyObject *names = PEEK(1); + PyObject *type = PEEK(2); + PyObject *subject = PEEK(3); + PyObject *attrs; // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or // None on failure. - PyObject *names = POP(); - PyObject *type = POP(); - PyObject *subject = TOP(); assert(PyTuple_CheckExact(names)); - PyObject *attrs = match_class(tstate, subject, type, oparg, names); - Py_DECREF(names); + attrs = match_class(tstate, subject, type, oparg, names); + Py_DECREF(subject); Py_DECREF(type); + Py_DECREF(names); if (attrs) { - // Success! - assert(PyTuple_CheckExact(attrs)); - SET_TOP(attrs); - } - else if (_PyErr_Occurred(tstate)) { - // Error! - goto error; + assert(PyTuple_CheckExact(attrs)); // Success! } else { - // Failure! - SET_TOP(Py_NewRef(Py_None)); + if (_PyErr_Occurred(tstate)) goto pop_3_error; + attrs = Py_NewRef(Py_None); // Failure! } - Py_DECREF(subject); + STACK_SHRINK(2); + POKE(1, attrs); DISPATCH(); } TARGET(MATCH_MAPPING) { - PyObject *subject = TOP(); + PyObject *subject = PEEK(1); + PyObject *res; int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; - PyObject *res = match ? Py_True : Py_False; - PUSH(Py_NewRef(res)); + res = Py_NewRef(match ? Py_True : Py_False); + STACK_GROW(1); + POKE(1, res); PREDICT(POP_JUMP_IF_FALSE); DISPATCH(); } TARGET(MATCH_SEQUENCE) { - PyObject *subject = TOP(); + PyObject *subject = PEEK(1); + PyObject *res; int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; - PyObject *res = match ? Py_True : Py_False; - PUSH(Py_NewRef(res)); + res = Py_NewRef(match ? Py_True : Py_False); + STACK_GROW(1); + POKE(1, res); PREDICT(POP_JUMP_IF_FALSE); DISPATCH(); } TARGET(MATCH_KEYS) { + PyObject *keys = PEEK(1); + PyObject *subject = PEEK(2); + PyObject *values_or_none; // On successful match, PUSH(values). Otherwise, PUSH(None). - PyObject *keys = TOP(); - PyObject *subject = SECOND(); - PyObject *values_or_none = match_keys(tstate, subject, keys); - if (values_or_none == NULL) { - goto error; - } - PUSH(values_or_none); + values_or_none = match_keys(tstate, subject, keys); + if (values_or_none == NULL) goto error; + STACK_GROW(1); + POKE(1, values_or_none); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 1fb0acceb511..f9c640187afe 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -133,10 +133,10 @@ static const struct { [JUMP_IF_TRUE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [JUMP_BACKWARD_NO_INTERRUPT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [GET_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [MATCH_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [MATCH_MAPPING] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [MATCH_SEQUENCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [MATCH_KEYS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [MATCH_CLASS] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MATCH_MAPPING] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [MATCH_SEQUENCE] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [MATCH_KEYS] = { 2, 3, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_YIELD_FROM_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [FOR_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 90f101adceeb..429c9d34d606 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -26,7 +26,7 @@ ) BEGIN_MARKER = "// BEGIN BYTECODES //" END_MARKER = "// END BYTECODES //" -RE_PREDICTED = r"^\s*(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);\s*$" +RE_PREDICTED = r"^\s*(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);\s*(?://.*)?$" UNUSED = "unused" BITS_PER_CODE_UNIT = 16 @@ -354,7 +354,7 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None assert dedent <= 0 extra = " " * -dedent for line in self.block_text: - if m := re.match(r"(\s*)ERROR_IF\((.+), (\w+)\);\s*$", line): + if m := re.match(r"(\s*)ERROR_IF\((.+), (\w+)\);\s*(?://.*)?$", line): space, cond, label = m.groups() # ERROR_IF() must pop the inputs from the stack. # The code block is responsible for DECREF()ing them. @@ -378,7 +378,7 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None ) else: out.write_raw(f"{extra}{space}if ({cond}) goto {label};\n") - elif m := re.match(r"(\s*)DECREF_INPUTS\(\);\s*$", line): + elif m := re.match(r"(\s*)DECREF_INPUTS\(\);\s*(?://.*)?$", line): if not self.register: space = m.group(1) for ieff in self.input_effects: @@ -964,7 +964,7 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]: # Separate PREDICT(...) macros from end predictions: list[str] = [] - while blocklines and (m := re.match(r"^\s*PREDICT\((\w+)\);\s*$", blocklines[-1])): + while blocklines and (m := re.match(r"^\s*PREDICT\((\w+)\);\s*(?://.*)?$", blocklines[-1])): predictions.insert(0, m.group(1)) blocklines.pop() diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index ae0c1e2f97c3..c59aae878cfa 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -215,6 +215,20 @@ def test_error_if_plain(): """ run_cases_test(input, output) +def test_error_if_plain_with_comment(): + input = """ + inst(OP, (--)) { + ERROR_IF(cond, label); // Comment is ok + } + """ + output = """ + TARGET(OP) { + if (cond) goto label; + DISPATCH(); + } + """ + run_cases_test(input, output) + def test_error_if_pop(): input = """ inst(OP, (left, right -- res)) { From webhook-mailer at python.org Tue Jan 24 17:58:24 2023 From: webhook-mailer at python.org (gvanrossum) Date: Tue, 24 Jan 2023 22:58:24 -0000 Subject: [Python-checkins] Fix some comments in ceval.c and fix lltrace output (#101297) Message-ID: https://github.com/python/cpython/commit/498598e8c2d64232d26c075de87c513415176bbf commit: 498598e8c2d64232d26c075de87c513415176bbf branch: main author: Guido van Rossum committer: gvanrossum date: 2023-01-24T14:58:18-08:00 summary: Fix some comments in ceval.c and fix lltrace output (#101297) The comment at the top was rather outdated. :-) Also added a note about the dangers of dump_stack(). files: M Python/ceval.c diff --git a/Python/ceval.c b/Python/ceval.c index 95eb99b45334..2e6fed580ded 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1,10 +1,5 @@ /* Execute compiled code */ -/* XXX TO DO: - XXX speed up searching for keywords by using a dictionary - XXX document it! - */ - #define _PY_INTERPRETER #include "Python.h" @@ -133,6 +128,9 @@ lltrace_instruction(_PyInterpreterFrame *frame, PyObject **stack_pointer, _Py_CODEUNIT *next_instr) { + /* This dump_stack() operation is risky, since the repr() of some + objects enters the interpreter recursively. It is also slow. + So you might want to comment it out. */ dump_stack(frame, stack_pointer); int oparg = _Py_OPARG(*next_instr); int opcode = _Py_OPCODE(*next_instr); @@ -155,7 +153,7 @@ lltrace_resume_frame(_PyInterpreterFrame *frame) fobj == NULL || !PyFunction_Check(fobj) ) { - printf("\nResuming frame."); + printf("\nResuming frame.\n"); return; } PyFunctionObject *f = (PyFunctionObject *)fobj; From webhook-mailer at python.org Tue Jan 24 23:00:23 2023 From: webhook-mailer at python.org (ned-deily) Date: Wed, 25 Jan 2023 04:00:23 -0000 Subject: [Python-checkins] gh-100180: Update Windows installer to OpenSSL 1.1.1s (GH-100903) (GH-101259) Message-ID: https://github.com/python/cpython/commit/ae3dea385ef382ebecb31a4eeef25756b5f54112 commit: ae3dea385ef382ebecb31a4eeef25756b5f54112 branch: 3.7 author: Steve Dower committer: ned-deily date: 2023-01-24T23:00:14-05:00 summary: gh-100180: Update Windows installer to OpenSSL 1.1.1s (GH-100903) (GH-101259) files: A Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst M PCbuild/get_externals.bat M PCbuild/python.props diff --git a/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst b/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst new file mode 100644 index 000000000000..5b0f42568d92 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-09-23-03-57.gh-issue-100180.b5phrg.rst @@ -0,0 +1 @@ +Update Windows installer to OpenSSL 1.1.1s diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 57628396ed06..8f7a6aae7833 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -49,7 +49,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1n +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1s set libraries=%libraries% sqlite-3.31.1.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.9.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.9.0 @@ -72,7 +72,7 @@ for %%e in (%libraries%) do ( echo.Fetching external binaries... set binaries= -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1n +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1s if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.9.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/python.props b/PCbuild/python.props index 1226d623831b..53d106935b5d 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -49,8 +49,8 @@ $(ExternalsDir)sqlite-3.31.1.0\ $(ExternalsDir)bzip2-1.0.8\ $(ExternalsDir)xz-5.2.2\ - $(ExternalsDir)openssl-1.1.1n\ - $(ExternalsDir)openssl-bin-1.1.1n\$(ArchName)\ + $(ExternalsDir)openssl-1.1.1s\ + $(ExternalsDir)openssl-bin-1.1.1s\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.12\ From webhook-mailer at python.org Wed Jan 25 11:55:53 2023 From: webhook-mailer at python.org (gvanrossum) Date: Wed, 25 Jan 2023 16:55:53 -0000 Subject: [Python-checkins] GH-98831: Elaborate some cases_generator tests (#101299) Message-ID: https://github.com/python/cpython/commit/395871e511d6d9634399598cae8f0c35fe72d79b commit: 395871e511d6d9634399598cae8f0c35fe72d79b branch: main author: Guido van Rossum committer: gvanrossum date: 2023-01-25T08:55:46-08:00 summary: GH-98831: Elaborate some cases_generator tests (#101299) * Make macro test more elaborate * Add test for 'register inst()' files: M Tools/cases_generator/test_generator.py diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index c59aae878cfa..cf58e6aaf2b3 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -333,20 +333,21 @@ def test_super_instruction(): def test_macro_instruction(): input = """ - inst(OP1, (counter/1, arg --)) { - op1(); + inst(OP1, (counter/1, arg1 -- interim)) { + interim = op1(arg1); } - op(OP2, (extra/2, arg --)) { - op2(); + op(OP2, (extra/2, arg2, interim -- res)) { + res = op2(arg2, interim); } macro(OP) = OP1 + cache/2 + OP2; """ output = """ TARGET(OP1) { - PyObject *arg = PEEK(1); + PyObject *arg1 = PEEK(1); + PyObject *interim; uint16_t counter = read_u16(&next_instr[0].cache); - op1(); - STACK_SHRINK(1); + interim = op1(arg1); + POKE(1, interim); JUMPBY(1); DISPATCH(); } @@ -355,17 +356,24 @@ def test_macro_instruction(): PyObject *_tmp_1 = PEEK(1); PyObject *_tmp_2 = PEEK(2); { - PyObject *arg = _tmp_1; - uint16_t counter = read_u16(&next_instr[0].cache); - op1(); + PyObject *arg1 = _tmp_1; + PyObject *interim; + uint16_t counter = re + ad_u16(&next_instr[0].cache); + interim = op1(arg1); + _tmp_1 = interim; } { - PyObject *arg = _tmp_2; + PyObject *interim = _tmp_1; + PyObject *arg2 = _tmp_2; + PyObject *res; uint32_t extra = read_u32(&next_instr[3].cache); - op2(); + res = op2(arg2, interim); + _tmp_2 = res; } JUMPBY(5); - STACK_SHRINK(2); + STACK_SHRINK(1); + POKE(1, _tmp_2); DISPATCH(); } """ @@ -448,3 +456,23 @@ def test_array_error_if(): } """ run_cases_test(input, output) + +def test_register(): + input = """ + register inst(OP, (counter/1, left, right -- result)) { + result = op(left, right); + } + """ + output = """ + TARGET(OP) { + PyObject *left = REG(oparg1); + PyObject *right = REG(oparg2); + PyObject *result; + uint16_t counter = read_u16(&next_instr[0].cache); + result = op(left, right); + Py_XSETREF(REG(oparg3), result); + JUMPBY(1); + DISPATCH(); + } + """ + run_cases_test(input, output) From webhook-mailer at python.org Wed Jan 25 12:28:21 2023 From: webhook-mailer at python.org (gvanrossum) Date: Wed, 25 Jan 2023 17:28:21 -0000 Subject: [Python-checkins] Add advice how to freeze fewer modules (#101298) Message-ID: https://github.com/python/cpython/commit/14177128126f688856099c8ec138ac4693a8cf85 commit: 14177128126f688856099c8ec138ac4693a8cf85 branch: main author: Guido van Rossum committer: gvanrossum date: 2023-01-25T09:28:14-08:00 summary: Add advice how to freeze fewer modules (#101298) (And fix a bug that only occurs when you follow the advice.) files: M Tools/build/freeze_modules.py diff --git a/Tools/build/freeze_modules.py b/Tools/build/freeze_modules.py index 810224b28f2f..ee4dd2f8682b 100644 --- a/Tools/build/freeze_modules.py +++ b/Tools/build/freeze_modules.py @@ -32,6 +32,9 @@ OS_PATH = 'ntpath' if os.name == 'nt' else 'posixpath' # These are modules that get frozen. +# If you're debugging new bytecode instructions, +# you can delete all sections except 'import system'. +# This also speeds up building somewhat. TESTS_SECTION = 'Test module' FROZEN = [ # See parse_frozen_spec() for the format. @@ -45,6 +48,7 @@ # on a builtin zip file instead of a filesystem. 'zipimport', ]), + # (You can delete entries from here down to the end of the list.) ('stdlib - startup, without site (python -S)', [ 'abc', 'codecs', @@ -80,6 +84,7 @@ '<__phello__.**.*>', f'frozen_only : __hello_only__ = {FROZEN_ONLY}', ]), + # (End of stuff you could delete.) ] BOOTSTRAP = { 'importlib._bootstrap', @@ -520,7 +525,7 @@ def regen_frozen(modules, frozen_modules: bool): for lines in (bootstraplines, stdliblines, testlines): # TODO: Is this necessary any more? - if not lines[0]: + if lines and not lines[0]: del lines[0] for i, line in enumerate(lines): if line: From webhook-mailer at python.org Wed Jan 25 12:39:50 2023 From: webhook-mailer at python.org (gpshead) Date: Wed, 25 Jan 2023 17:39:50 -0000 Subject: [Python-checkins] GH-88597: Rename uuid's new CLI args to be in line with uuidgen. (#101248) Message-ID: https://github.com/python/cpython/commit/952a1d9cc970508b280af475c0be1809692f0c76 commit: 952a1d9cc970508b280af475c0be1809692f0c76 branch: main author: achhina committer: gpshead date: 2023-01-25T09:39:42-08:00 summary: GH-88597: Rename uuid's new CLI args to be in line with uuidgen. (#101248) this way they match an existing uuidgen command line tool. files: M Doc/library/uuid.rst M Lib/test/test_uuid.py M Lib/uuid.py diff --git a/Doc/library/uuid.rst b/Doc/library/uuid.rst index 804884dee0a2..38b6434f467f 100644 --- a/Doc/library/uuid.rst +++ b/Doc/library/uuid.rst @@ -272,7 +272,7 @@ The :mod:`uuid` module can be executed as a script from the command line. .. code-block:: sh - python -m uuid [-h] [-u {uuid1,uuid3,uuid4,uuid5}] [-ns NAMESPACE] [-n NAME] + python -m uuid [-h] [-u {uuid1,uuid3,uuid4,uuid5}] [-n NAMESPACE] [-N NAME] The following options are accepted: @@ -288,13 +288,14 @@ The following options are accepted: Specify the function name to use to generate the uuid. By default :func:`uuid4` is used. -.. cmdoption:: -ns +.. cmdoption:: -n --namespace - The namespace used as part of generating the uuid. Only required for - :func:`uuid3` / :func:`uuid5` functions. + The namespace is a ``UUID``, or ``@ns`` where ``ns`` is a well-known predefined UUID + addressed by namespace name. Such as ``@dns``, ``@url``, ``@oid``, and ``@x500``. + Only required for :func:`uuid3` / :func:`uuid5` functions. -.. cmdoption:: -n +.. cmdoption:: -N --name The name used as part of generating the uuid. Only required for @@ -351,12 +352,12 @@ Here are some examples of typical usage of the :mod:`uuid` command line interfac .. code-block:: shell - # generate a random uuid - by default uuid4() is used - $ python -m uuid + # generate a random uuid - by default uuid4() is used + $ python -m uuid - # generate a uuid using uuid1() - $ python -m uuid -u uuid1 + # generate a uuid using uuid1() + $ python -m uuid -u uuid1 - # generate a uuid using uuid5 - $ python -m uuid -u uuid5 -ns NAMESPACE_URL -n example.com + # generate a uuid using uuid5 + $ python -m uuid -u uuid5 -n @url -N example.com diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index 61ae2567c90d..b2c229cd634e 100755 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -675,7 +675,7 @@ def test_uuid_weakref(self): weak = weakref.ref(strong) self.assertIs(strong, weak()) - @mock.patch.object(sys, "argv", ["", "-u", "uuid3", "-ns", "NAMESPACE_DNS"]) + @mock.patch.object(sys, "argv", ["", "-u", "uuid3", "-n", "@dns"]) def test_cli_namespace_required_for_uuid3(self): with self.assertRaises(SystemExit) as cm: self.uuid.main() @@ -683,7 +683,7 @@ def test_cli_namespace_required_for_uuid3(self): # Check that exception code is the same as argparse.ArgumentParser.error self.assertEqual(cm.exception.code, 2) - @mock.patch.object(sys, "argv", ["", "-u", "uuid3", "-n", "python.org"]) + @mock.patch.object(sys, "argv", ["", "-u", "uuid3", "-N", "python.org"]) def test_cli_name_required_for_uuid3(self): with self.assertRaises(SystemExit) as cm: self.uuid.main() @@ -705,7 +705,7 @@ def test_cli_uuid4_outputted_with_no_args(self): self.assertEqual(uuid_output.version, 4) @mock.patch.object(sys, "argv", - ["", "-u", "uuid3", "-ns", "NAMESPACE_DNS", "-n", "python.org"]) + ["", "-u", "uuid3", "-n", "@dns", "-N", "python.org"]) def test_cli_uuid3_ouputted_with_valid_namespace_and_name(self): stdout = io.StringIO() with contextlib.redirect_stdout(stdout): @@ -719,7 +719,7 @@ def test_cli_uuid3_ouputted_with_valid_namespace_and_name(self): self.assertEqual(uuid_output.version, 3) @mock.patch.object(sys, "argv", - ["", "-u", "uuid5", "-ns", "NAMESPACE_DNS", "-n", "python.org"]) + ["", "-u", "uuid5", "-n", "@dns", "-N", "python.org"]) def test_cli_uuid5_ouputted_with_valid_namespace_and_name(self): stdout = io.StringIO() with contextlib.redirect_stdout(stdout): diff --git a/Lib/uuid.py b/Lib/uuid.py index 2904b9c4af64..1c5578bf1f05 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -731,16 +731,18 @@ def uuid5(namespace, name): def main(): """Run the uuid command line interface.""" - uuid_funcs = {"uuid1": uuid1, - "uuid3": uuid3, - "uuid4": uuid4, - "uuid5": uuid5} + uuid_funcs = { + "uuid1": uuid1, + "uuid3": uuid3, + "uuid4": uuid4, + "uuid5": uuid5 + } uuid_namespace_funcs = ("uuid3", "uuid5") namespaces = { - "NAMESPACE_DNS": NAMESPACE_DNS, - "NAMESPACE_URL": NAMESPACE_URL, - "NAMESPACE_OID": NAMESPACE_OID, - "NAMESPACE_X500": NAMESPACE_X500 + "@dns": NAMESPACE_DNS, + "@url": NAMESPACE_URL, + "@oid": NAMESPACE_OID, + "@x500": NAMESPACE_X500 } import argparse @@ -748,11 +750,13 @@ def main(): description="Generates a uuid using the selected uuid function.") parser.add_argument("-u", "--uuid", choices=uuid_funcs.keys(), default="uuid4", help="The function to use to generate the uuid. " - "By default uuid4 function is used.") - parser.add_argument("-ns", "--namespace", - help="The namespace used as part of generating the uuid. " + "By default uuid4 function is used.") + parser.add_argument("-n", "--namespace", + help="The namespace is a UUID, or '@ns' where 'ns' is a " + "well-known predefined UUID addressed by namespace name. " + "Such as @dns, @url, @oid, and @x500. " "Only required for uuid3/uuid5 functions.") - parser.add_argument("-n", "--name", + parser.add_argument("-N", "--name", help="The name used as part of generating the uuid. " "Only required for uuid3/uuid5 functions.") From webhook-mailer at python.org Wed Jan 25 15:01:11 2023 From: webhook-mailer at python.org (hauntsaninja) Date: Wed, 25 Jan 2023 20:01:11 -0000 Subject: [Python-checkins] gh-101326: Fix regression when passing None to FutureIter.throw (#101327) Message-ID: https://github.com/python/cpython/commit/a178ba82bfe2f2fb6f6ff0e67cb734fd7c4321e3 commit: a178ba82bfe2f2fb6f6ff0e67cb734fd7c4321e3 branch: main author: Shantanu <12621235+hauntsaninja at users.noreply.github.com> committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com> date: 2023-01-25T12:01:01-08:00 summary: gh-101326: Fix regression when passing None to FutureIter.throw (#101327) files: A Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst M Lib/test/test_asyncio/test_futures.py M Modules/_asynciomodule.c diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 56b0b864de2d..2184b2091f84 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -612,6 +612,8 @@ def test_future_iter_throw(self): Exception, Exception("elephant"), 32) self.assertRaises(TypeError, fi.throw, Exception("elephant"), Exception("elephant")) + # https://github.com/python/cpython/issues/101326 + self.assertRaises(ValueError, fi.throw, ValueError, None, None) self.assertRaises(TypeError, fi.throw, list) def test_future_del_collect(self): diff --git a/Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst b/Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst new file mode 100644 index 000000000000..54b69b943091 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst @@ -0,0 +1 @@ +Fix regression when passing ``None`` as second or third argument to ``FutureIter.throw``. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 6fe4ca469475..055dded05431 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1694,7 +1694,12 @@ FutureIter_throw(futureiterobject *self, PyObject *const *args, Py_ssize_t nargs val = args[1]; } - if (tb != NULL && !PyTraceBack_Check(tb)) { + if (val == Py_None) { + val = NULL; + } + if (tb == Py_None ) { + tb = NULL; + } else if (tb != NULL && !PyTraceBack_Check(tb)) { PyErr_SetString(PyExc_TypeError, "throw() third argument must be a traceback"); return NULL; } From webhook-mailer at python.org Wed Jan 25 15:41:39 2023 From: webhook-mailer at python.org (iritkatriel) Date: Wed, 25 Jan 2023 20:41:39 -0000 Subject: [Python-checkins] gh-98831: add variable stack effect support to cases generator (#101309) Message-ID: https://github.com/python/cpython/commit/19f90d6b97120eafe6dc6689d5a946f50c0c8ef8 commit: 19f90d6b97120eafe6dc6689d5a946f50c0c8ef8 branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-25T20:41:03Z summary: gh-98831: add variable stack effect support to cases generator (#101309) files: M Python/compile.c M Python/opcode_metadata.h M Tools/cases_generator/generate_cases.py diff --git a/Python/compile.c b/Python/compile.c index 9fc997cdf525..c31f08c0a179 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -36,7 +36,7 @@ #include "pycore_pymem.h" // _PyMem_IsPtrFreed() #include "pycore_symtable.h" // PySTEntryObject -#include "opcode_metadata.h" // _PyOpcode_opcode_metadata +#include "opcode_metadata.h" // _PyOpcode_opcode_metadata, _PyOpcode_num_popped/pushed #define DEFAULT_BLOCK_SIZE 16 @@ -8651,13 +8651,15 @@ no_redundant_jumps(cfg_builder *g) { static bool opcode_metadata_is_sane(cfg_builder *g) { + bool result = true; for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { for (int i = 0; i < b->b_iused; i++) { struct instr *instr = &b->b_instr[i]; int opcode = instr->i_opcode; + int oparg = instr->i_oparg; assert(opcode <= MAX_REAL_OPCODE); - int pushed = _PyOpcode_opcode_metadata[opcode].n_pushed; - int popped = _PyOpcode_opcode_metadata[opcode].n_popped; + int popped = _PyOpcode_num_popped(opcode, oparg); + int pushed = _PyOpcode_num_pushed(opcode, oparg); assert((pushed < 0) == (popped < 0)); if (pushed >= 0) { assert(_PyOpcode_opcode_metadata[opcode].valid_entry); @@ -8666,12 +8668,12 @@ opcode_metadata_is_sane(cfg_builder *g) { fprintf(stderr, "op=%d: stack_effect (%d) != pushed (%d) - popped (%d)\n", opcode, effect, pushed, popped); - return false; + result = false; } } } } - return true; + return result; } static bool diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index f9c640187afe..46fd9673e8fb 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -1,183 +1,869 @@ // This file is generated by Tools/cases_generator/generate_cases.py --metadata // from Python/bytecodes.c // Do not edit! + +static int +_PyOpcode_num_popped(int opcode, int oparg) { + switch(opcode) { + case NOP: + return 0; + case RESUME: + return 0; + case LOAD_CLOSURE: + return 0; + case LOAD_FAST_CHECK: + return 0; + case LOAD_FAST: + return 0; + case LOAD_CONST: + return 0; + case STORE_FAST: + return 1; + case LOAD_FAST__LOAD_FAST: + return 0+0; + case LOAD_FAST__LOAD_CONST: + return 0+0; + case STORE_FAST__LOAD_FAST: + return 1+0; + case STORE_FAST__STORE_FAST: + return 1+1; + case LOAD_CONST__LOAD_FAST: + return 0+0; + case POP_TOP: + return 1; + case PUSH_NULL: + return 0; + case END_FOR: + return 1+1; + case UNARY_NEGATIVE: + return 1; + case UNARY_NOT: + return 1; + case UNARY_INVERT: + return 1; + case BINARY_OP_MULTIPLY_INT: + return 2; + case BINARY_OP_MULTIPLY_FLOAT: + return 2; + case BINARY_OP_SUBTRACT_INT: + return 2; + case BINARY_OP_SUBTRACT_FLOAT: + return 2; + case BINARY_OP_ADD_UNICODE: + return 2; + case BINARY_OP_INPLACE_ADD_UNICODE: + return 2; + case BINARY_OP_ADD_FLOAT: + return 2; + case BINARY_OP_ADD_INT: + return 2; + case BINARY_SUBSCR: + return 2; + case BINARY_SLICE: + return 3; + case STORE_SLICE: + return 4; + case BINARY_SUBSCR_LIST_INT: + return 2; + case BINARY_SUBSCR_TUPLE_INT: + return 2; + case BINARY_SUBSCR_DICT: + return 2; + case BINARY_SUBSCR_GETITEM: + return 2; + case LIST_APPEND: + return (oparg-1) + 2; + case SET_ADD: + return (oparg-1) + 2; + case STORE_SUBSCR: + return 3; + case STORE_SUBSCR_LIST_INT: + return 3; + case STORE_SUBSCR_DICT: + return 3; + case DELETE_SUBSCR: + return 2; + case CALL_INTRINSIC_1: + return 1; + case RAISE_VARARGS: + return -1; + case INTERPRETER_EXIT: + return 1; + case RETURN_VALUE: + return 1; + case GET_AITER: + return 1; + case GET_ANEXT: + return 1; + case GET_AWAITABLE: + return 1; + case SEND: + return -1; + case YIELD_VALUE: + return 1; + case POP_EXCEPT: + return 1; + case RERAISE: + return -1; + case PREP_RERAISE_STAR: + return 2; + case END_ASYNC_FOR: + return -1; + case CLEANUP_THROW: + return -1; + case LOAD_ASSERTION_ERROR: + return 0; + case LOAD_BUILD_CLASS: + return 0; + case STORE_NAME: + return 1; + case DELETE_NAME: + return 0; + case UNPACK_SEQUENCE: + return -1; + case UNPACK_SEQUENCE_TWO_TUPLE: + return -1; + case UNPACK_SEQUENCE_TUPLE: + return -1; + case UNPACK_SEQUENCE_LIST: + return -1; + case UNPACK_EX: + return -1; + case STORE_ATTR: + return 2; + case DELETE_ATTR: + return 1; + case STORE_GLOBAL: + return 1; + case DELETE_GLOBAL: + return 0; + case LOAD_NAME: + return 0; + case LOAD_GLOBAL: + return -1; + case LOAD_GLOBAL_MODULE: + return -1; + case LOAD_GLOBAL_BUILTIN: + return -1; + case DELETE_FAST: + return 0; + case MAKE_CELL: + return 0; + case DELETE_DEREF: + return 0; + case LOAD_CLASSDEREF: + return 0; + case LOAD_DEREF: + return 0; + case STORE_DEREF: + return 1; + case COPY_FREE_VARS: + return 0; + case BUILD_STRING: + return oparg; + case BUILD_TUPLE: + return oparg; + case BUILD_LIST: + return oparg; + case LIST_EXTEND: + return (oparg-1) + 2; + case SET_UPDATE: + return (oparg-1) + 2; + case BUILD_SET: + return oparg; + case BUILD_MAP: + return oparg*2; + case SETUP_ANNOTATIONS: + return 0; + case BUILD_CONST_KEY_MAP: + return oparg + 1; + case DICT_UPDATE: + return 1; + case DICT_MERGE: + return 1; + case MAP_ADD: + return 2; + case LOAD_ATTR: + return -1; + case LOAD_ATTR_INSTANCE_VALUE: + return -1; + case LOAD_ATTR_MODULE: + return -1; + case LOAD_ATTR_WITH_HINT: + return -1; + case LOAD_ATTR_SLOT: + return -1; + case LOAD_ATTR_CLASS: + return -1; + case LOAD_ATTR_PROPERTY: + return -1; + case LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN: + return -1; + case STORE_ATTR_INSTANCE_VALUE: + return 2; + case STORE_ATTR_WITH_HINT: + return 2; + case STORE_ATTR_SLOT: + return 2; + case COMPARE_OP: + return 2; + case COMPARE_AND_BRANCH: + return 2; + case COMPARE_AND_BRANCH_FLOAT: + return 2; + case COMPARE_AND_BRANCH_INT: + return 2; + case COMPARE_AND_BRANCH_STR: + return 2; + case IS_OP: + return 2; + case CONTAINS_OP: + return 2; + case CHECK_EG_MATCH: + return 2; + case CHECK_EXC_MATCH: + return 2; + case IMPORT_NAME: + return 2; + case IMPORT_FROM: + return 1; + case JUMP_FORWARD: + return 0; + case JUMP_BACKWARD: + return 0; + case POP_JUMP_IF_FALSE: + return -1; + case POP_JUMP_IF_TRUE: + return -1; + case POP_JUMP_IF_NOT_NONE: + return -1; + case POP_JUMP_IF_NONE: + return -1; + case JUMP_IF_FALSE_OR_POP: + return -1; + case JUMP_IF_TRUE_OR_POP: + return -1; + case JUMP_BACKWARD_NO_INTERRUPT: + return -1; + case GET_LEN: + return -1; + case MATCH_CLASS: + return 3; + case MATCH_MAPPING: + return 1; + case MATCH_SEQUENCE: + return 1; + case MATCH_KEYS: + return 2; + case GET_ITER: + return -1; + case GET_YIELD_FROM_ITER: + return -1; + case FOR_ITER: + return -1; + case FOR_ITER_LIST: + return -1; + case FOR_ITER_TUPLE: + return -1; + case FOR_ITER_RANGE: + return -1; + case FOR_ITER_GEN: + return -1; + case BEFORE_ASYNC_WITH: + return -1; + case BEFORE_WITH: + return -1; + case WITH_EXCEPT_START: + return 4; + case PUSH_EXC_INFO: + return -1; + case LOAD_ATTR_METHOD_WITH_VALUES: + return -1; + case LOAD_ATTR_METHOD_NO_DICT: + return -1; + case LOAD_ATTR_METHOD_LAZY_DICT: + return -1; + case CALL_BOUND_METHOD_EXACT_ARGS: + return -1; + case KW_NAMES: + return -1; + case CALL: + return -1; + case CALL_PY_EXACT_ARGS: + return -1; + case CALL_PY_WITH_DEFAULTS: + return -1; + case CALL_NO_KW_TYPE_1: + return -1; + case CALL_NO_KW_STR_1: + return -1; + case CALL_NO_KW_TUPLE_1: + return -1; + case CALL_BUILTIN_CLASS: + return -1; + case CALL_NO_KW_BUILTIN_O: + return -1; + case CALL_NO_KW_BUILTIN_FAST: + return -1; + case CALL_BUILTIN_FAST_WITH_KEYWORDS: + return -1; + case CALL_NO_KW_LEN: + return -1; + case CALL_NO_KW_ISINSTANCE: + return -1; + case CALL_NO_KW_LIST_APPEND: + return -1; + case CALL_NO_KW_METHOD_DESCRIPTOR_O: + return -1; + case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: + return -1; + case CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS: + return -1; + case CALL_NO_KW_METHOD_DESCRIPTOR_FAST: + return -1; + case CALL_FUNCTION_EX: + return -1; + case MAKE_FUNCTION: + return -1; + case RETURN_GENERATOR: + return -1; + case BUILD_SLICE: + return -1; + case FORMAT_VALUE: + return -1; + case COPY: + return -1; + case BINARY_OP: + return 2; + case SWAP: + return -1; + case EXTENDED_ARG: + return -1; + case CACHE: + return -1; + default: + Py_UNREACHABLE(); + } +} + +static int +_PyOpcode_num_pushed(int opcode, int oparg) { + switch(opcode) { + case NOP: + return 0; + case RESUME: + return 0; + case LOAD_CLOSURE: + return 1; + case LOAD_FAST_CHECK: + return 1; + case LOAD_FAST: + return 1; + case LOAD_CONST: + return 1; + case STORE_FAST: + return 0; + case LOAD_FAST__LOAD_FAST: + return 1+1; + case LOAD_FAST__LOAD_CONST: + return 1+1; + case STORE_FAST__LOAD_FAST: + return 0+1; + case STORE_FAST__STORE_FAST: + return 0+0; + case LOAD_CONST__LOAD_FAST: + return 1+1; + case POP_TOP: + return 0; + case PUSH_NULL: + return 1; + case END_FOR: + return 0+0; + case UNARY_NEGATIVE: + return 1; + case UNARY_NOT: + return 1; + case UNARY_INVERT: + return 1; + case BINARY_OP_MULTIPLY_INT: + return 1; + case BINARY_OP_MULTIPLY_FLOAT: + return 1; + case BINARY_OP_SUBTRACT_INT: + return 1; + case BINARY_OP_SUBTRACT_FLOAT: + return 1; + case BINARY_OP_ADD_UNICODE: + return 1; + case BINARY_OP_INPLACE_ADD_UNICODE: + return 0; + case BINARY_OP_ADD_FLOAT: + return 1; + case BINARY_OP_ADD_INT: + return 1; + case BINARY_SUBSCR: + return 1; + case BINARY_SLICE: + return 1; + case STORE_SLICE: + return 0; + case BINARY_SUBSCR_LIST_INT: + return 1; + case BINARY_SUBSCR_TUPLE_INT: + return 1; + case BINARY_SUBSCR_DICT: + return 1; + case BINARY_SUBSCR_GETITEM: + return 1; + case LIST_APPEND: + return (oparg-1) + 1; + case SET_ADD: + return (oparg-1) + 1; + case STORE_SUBSCR: + return 0; + case STORE_SUBSCR_LIST_INT: + return 0; + case STORE_SUBSCR_DICT: + return 0; + case DELETE_SUBSCR: + return 0; + case CALL_INTRINSIC_1: + return 1; + case RAISE_VARARGS: + return -1; + case INTERPRETER_EXIT: + return 0; + case RETURN_VALUE: + return 0; + case GET_AITER: + return 1; + case GET_ANEXT: + return 2; + case GET_AWAITABLE: + return 1; + case SEND: + return -1; + case YIELD_VALUE: + return 1; + case POP_EXCEPT: + return 0; + case RERAISE: + return -1; + case PREP_RERAISE_STAR: + return 1; + case END_ASYNC_FOR: + return -1; + case CLEANUP_THROW: + return -1; + case LOAD_ASSERTION_ERROR: + return 1; + case LOAD_BUILD_CLASS: + return 1; + case STORE_NAME: + return 0; + case DELETE_NAME: + return 0; + case UNPACK_SEQUENCE: + return -1; + case UNPACK_SEQUENCE_TWO_TUPLE: + return -1; + case UNPACK_SEQUENCE_TUPLE: + return -1; + case UNPACK_SEQUENCE_LIST: + return -1; + case UNPACK_EX: + return -1; + case STORE_ATTR: + return 0; + case DELETE_ATTR: + return 0; + case STORE_GLOBAL: + return 0; + case DELETE_GLOBAL: + return 0; + case LOAD_NAME: + return 1; + case LOAD_GLOBAL: + return -1; + case LOAD_GLOBAL_MODULE: + return -1; + case LOAD_GLOBAL_BUILTIN: + return -1; + case DELETE_FAST: + return 0; + case MAKE_CELL: + return 0; + case DELETE_DEREF: + return 0; + case LOAD_CLASSDEREF: + return 1; + case LOAD_DEREF: + return 1; + case STORE_DEREF: + return 0; + case COPY_FREE_VARS: + return 0; + case BUILD_STRING: + return 1; + case BUILD_TUPLE: + return 1; + case BUILD_LIST: + return 1; + case LIST_EXTEND: + return (oparg-1) + 1; + case SET_UPDATE: + return (oparg-1) + 1; + case BUILD_SET: + return 1; + case BUILD_MAP: + return 1; + case SETUP_ANNOTATIONS: + return 0; + case BUILD_CONST_KEY_MAP: + return 1; + case DICT_UPDATE: + return 0; + case DICT_MERGE: + return 0; + case MAP_ADD: + return 0; + case LOAD_ATTR: + return -1; + case LOAD_ATTR_INSTANCE_VALUE: + return -1; + case LOAD_ATTR_MODULE: + return -1; + case LOAD_ATTR_WITH_HINT: + return -1; + case LOAD_ATTR_SLOT: + return -1; + case LOAD_ATTR_CLASS: + return -1; + case LOAD_ATTR_PROPERTY: + return -1; + case LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN: + return -1; + case STORE_ATTR_INSTANCE_VALUE: + return 0; + case STORE_ATTR_WITH_HINT: + return 0; + case STORE_ATTR_SLOT: + return 0; + case COMPARE_OP: + return 1; + case COMPARE_AND_BRANCH: + return 0; + case COMPARE_AND_BRANCH_FLOAT: + return 0; + case COMPARE_AND_BRANCH_INT: + return 0; + case COMPARE_AND_BRANCH_STR: + return 0; + case IS_OP: + return 1; + case CONTAINS_OP: + return 1; + case CHECK_EG_MATCH: + return 2; + case CHECK_EXC_MATCH: + return 2; + case IMPORT_NAME: + return 1; + case IMPORT_FROM: + return 2; + case JUMP_FORWARD: + return 0; + case JUMP_BACKWARD: + return 0; + case POP_JUMP_IF_FALSE: + return -1; + case POP_JUMP_IF_TRUE: + return -1; + case POP_JUMP_IF_NOT_NONE: + return -1; + case POP_JUMP_IF_NONE: + return -1; + case JUMP_IF_FALSE_OR_POP: + return -1; + case JUMP_IF_TRUE_OR_POP: + return -1; + case JUMP_BACKWARD_NO_INTERRUPT: + return -1; + case GET_LEN: + return -1; + case MATCH_CLASS: + return 1; + case MATCH_MAPPING: + return 2; + case MATCH_SEQUENCE: + return 2; + case MATCH_KEYS: + return 3; + case GET_ITER: + return -1; + case GET_YIELD_FROM_ITER: + return -1; + case FOR_ITER: + return -1; + case FOR_ITER_LIST: + return -1; + case FOR_ITER_TUPLE: + return -1; + case FOR_ITER_RANGE: + return -1; + case FOR_ITER_GEN: + return -1; + case BEFORE_ASYNC_WITH: + return -1; + case BEFORE_WITH: + return -1; + case WITH_EXCEPT_START: + return 5; + case PUSH_EXC_INFO: + return -1; + case LOAD_ATTR_METHOD_WITH_VALUES: + return -1; + case LOAD_ATTR_METHOD_NO_DICT: + return -1; + case LOAD_ATTR_METHOD_LAZY_DICT: + return -1; + case CALL_BOUND_METHOD_EXACT_ARGS: + return -1; + case KW_NAMES: + return -1; + case CALL: + return -1; + case CALL_PY_EXACT_ARGS: + return -1; + case CALL_PY_WITH_DEFAULTS: + return -1; + case CALL_NO_KW_TYPE_1: + return -1; + case CALL_NO_KW_STR_1: + return -1; + case CALL_NO_KW_TUPLE_1: + return -1; + case CALL_BUILTIN_CLASS: + return -1; + case CALL_NO_KW_BUILTIN_O: + return -1; + case CALL_NO_KW_BUILTIN_FAST: + return -1; + case CALL_BUILTIN_FAST_WITH_KEYWORDS: + return -1; + case CALL_NO_KW_LEN: + return -1; + case CALL_NO_KW_ISINSTANCE: + return -1; + case CALL_NO_KW_LIST_APPEND: + return -1; + case CALL_NO_KW_METHOD_DESCRIPTOR_O: + return -1; + case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: + return -1; + case CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS: + return -1; + case CALL_NO_KW_METHOD_DESCRIPTOR_FAST: + return -1; + case CALL_FUNCTION_EX: + return -1; + case MAKE_FUNCTION: + return -1; + case RETURN_GENERATOR: + return -1; + case BUILD_SLICE: + return -1; + case FORMAT_VALUE: + return -1; + case COPY: + return -1; + case BINARY_OP: + return 1; + case SWAP: + return -1; + case EXTENDED_ARG: + return -1; + case CACHE: + return -1; + default: + Py_UNREACHABLE(); + } +} enum Direction { DIR_NONE, DIR_READ, DIR_WRITE }; enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; -static const struct { - short n_popped; - short n_pushed; +struct opcode_metadata { enum Direction dir_op1; enum Direction dir_op2; enum Direction dir_op3; bool valid_entry; enum InstructionFormat instr_format; } _PyOpcode_opcode_metadata[256] = { - [NOP] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [RESUME] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_CLOSURE] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_FAST_CHECK] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_FAST] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_CONST] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [STORE_FAST] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_FAST__LOAD_FAST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, - [LOAD_FAST__LOAD_CONST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, - [STORE_FAST__LOAD_FAST] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, - [STORE_FAST__STORE_FAST] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, - [LOAD_CONST__LOAD_FAST] = { 0, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, - [POP_TOP] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [PUSH_NULL] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [END_FOR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNARY_NEGATIVE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [UNARY_NOT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [UNARY_INVERT] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [BINARY_OP_MULTIPLY_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [BINARY_OP_MULTIPLY_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [BINARY_OP_SUBTRACT_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [BINARY_OP_SUBTRACT_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [BINARY_OP_ADD_UNICODE] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [BINARY_OP_INPLACE_ADD_UNICODE] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [BINARY_OP_ADD_FLOAT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [BINARY_OP_ADD_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [BINARY_SUBSCR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, - [BINARY_SLICE] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [STORE_SLICE] = { 4, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [BINARY_SUBSCR_LIST_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, - [BINARY_SUBSCR_TUPLE_INT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, - [BINARY_SUBSCR_DICT] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, - [BINARY_SUBSCR_GETITEM] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, - [LIST_APPEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [SET_ADD] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [STORE_SUBSCR] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [STORE_SUBSCR_LIST_INT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [STORE_SUBSCR_DICT] = { 3, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, - [DELETE_SUBSCR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [CALL_INTRINSIC_1] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [RAISE_VARARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [INTERPRETER_EXIT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [RETURN_VALUE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [GET_AITER] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [GET_ANEXT] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [GET_AWAITABLE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [SEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [YIELD_VALUE] = { 1, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [POP_EXCEPT] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [RERAISE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [PREP_RERAISE_STAR] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [END_ASYNC_FOR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [CLEANUP_THROW] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [LOAD_ASSERTION_ERROR] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [LOAD_BUILD_CLASS] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [STORE_NAME] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [DELETE_NAME] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNPACK_SEQUENCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNPACK_SEQUENCE_TWO_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [UNPACK_SEQUENCE_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNPACK_SEQUENCE_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [UNPACK_EX] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [STORE_ATTR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, - [DELETE_ATTR] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [STORE_GLOBAL] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [DELETE_GLOBAL] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_NAME] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_GLOBAL] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_GLOBAL_MODULE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_GLOBAL_BUILTIN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [DELETE_FAST] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [MAKE_CELL] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [DELETE_DEREF] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_CLASSDEREF] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_DEREF] = { 0, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [STORE_DEREF] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [COPY_FREE_VARS] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BUILD_STRING] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BUILD_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BUILD_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LIST_EXTEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [SET_UPDATE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BUILD_SET] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BUILD_MAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [SETUP_ANNOTATIONS] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [BUILD_CONST_KEY_MAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [DICT_UPDATE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [DICT_MERGE] = { 1, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [MAP_ADD] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_INSTANCE_VALUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_MODULE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_WITH_HINT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_SLOT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_PROPERTY] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [STORE_ATTR_INSTANCE_VALUE] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, - [STORE_ATTR_WITH_HINT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, - [STORE_ATTR_SLOT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, - [COMPARE_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [COMPARE_AND_BRANCH] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, - [COMPARE_AND_BRANCH_FLOAT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, - [COMPARE_AND_BRANCH_INT] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, - [COMPARE_AND_BRANCH_STR] = { 2, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, - [IS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CONTAINS_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CHECK_EG_MATCH] = { 2, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [CHECK_EXC_MATCH] = { 2, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [IMPORT_NAME] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [IMPORT_FROM] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [JUMP_FORWARD] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [JUMP_BACKWARD] = { 0, 0, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [POP_JUMP_IF_FALSE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [POP_JUMP_IF_TRUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [POP_JUMP_IF_NOT_NONE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [POP_JUMP_IF_NONE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [JUMP_IF_FALSE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [JUMP_IF_TRUE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [JUMP_BACKWARD_NO_INTERRUPT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [GET_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [MATCH_CLASS] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [MATCH_MAPPING] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [MATCH_SEQUENCE] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [MATCH_KEYS] = { 2, 3, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [GET_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [GET_YIELD_FROM_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [FOR_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_LIST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_TUPLE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_RANGE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FOR_ITER_GEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BEFORE_ASYNC_WITH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [BEFORE_WITH] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [WITH_EXCEPT_START] = { 4, 5, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [PUSH_EXC_INFO] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [LOAD_ATTR_METHOD_WITH_VALUES] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [LOAD_ATTR_METHOD_NO_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [LOAD_ATTR_METHOD_LAZY_DICT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [CALL_BOUND_METHOD_EXACT_ARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [KW_NAMES] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_PY_EXACT_ARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_PY_WITH_DEFAULTS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_TYPE_1] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_STR_1] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_TUPLE_1] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_BUILTIN_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_BUILTIN_O] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_BUILTIN_FAST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_ISINSTANCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_LIST_APPEND] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CALL_FUNCTION_EX] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [MAKE_FUNCTION] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [RETURN_GENERATOR] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [BUILD_SLICE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [FORMAT_VALUE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [COPY] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [BINARY_OP] = { 2, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [SWAP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [EXTENDED_ARG] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [CACHE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [NOP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [RESUME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_CLOSURE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_FAST_CHECK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_FAST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, + [LOAD_FAST__LOAD_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, + [STORE_FAST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, + [STORE_FAST__STORE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, + [LOAD_CONST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB }, + [POP_TOP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [PUSH_NULL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [END_FOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNARY_NEGATIVE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [UNARY_NOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [UNARY_INVERT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BINARY_OP_MULTIPLY_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_MULTIPLY_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_SUBTRACT_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_SUBTRACT_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_INPLACE_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BINARY_OP_ADD_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_OP_ADD_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [BINARY_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [BINARY_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [STORE_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BINARY_SUBSCR_LIST_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [BINARY_SUBSCR_TUPLE_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [BINARY_SUBSCR_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [BINARY_SUBSCR_GETITEM] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [LIST_APPEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SET_ADD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [STORE_SUBSCR_LIST_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [STORE_SUBSCR_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC }, + [DELETE_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [CALL_INTRINSIC_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [RAISE_VARARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [INTERPRETER_EXIT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [RETURN_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [GET_AITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [GET_ANEXT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [GET_AWAITABLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [YIELD_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [POP_EXCEPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [RERAISE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [PREP_RERAISE_STAR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [END_ASYNC_FOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [CLEANUP_THROW] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_ASSERTION_ERROR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_BUILD_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [STORE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DELETE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_SEQUENCE_TWO_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [UNPACK_SEQUENCE_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_SEQUENCE_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [UNPACK_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [DELETE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DELETE_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_GLOBAL_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_GLOBAL_BUILTIN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DELETE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MAKE_CELL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DELETE_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_CLASSDEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [COPY_FREE_VARS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_STRING] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LIST_EXTEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SET_UPDATE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_SET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BUILD_MAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [SETUP_ANNOTATIONS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BUILD_CONST_KEY_MAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DICT_UPDATE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [DICT_MERGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MAP_ADD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_SLOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_PROPERTY] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [STORE_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [STORE_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 }, + [STORE_ATTR_SLOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 }, + [COMPARE_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [COMPARE_AND_BRANCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, + [COMPARE_AND_BRANCH_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, + [COMPARE_AND_BRANCH_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, + [COMPARE_AND_BRANCH_STR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 }, + [IS_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CONTAINS_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CHECK_EG_MATCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [CHECK_EXC_MATCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [IMPORT_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [IMPORT_FROM] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [JUMP_FORWARD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [JUMP_BACKWARD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [POP_JUMP_IF_FALSE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [POP_JUMP_IF_TRUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [POP_JUMP_IF_NOT_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [POP_JUMP_IF_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [JUMP_IF_FALSE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [JUMP_IF_TRUE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [JUMP_BACKWARD_NO_INTERRUPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [GET_LEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [MATCH_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MATCH_MAPPING] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [MATCH_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [MATCH_KEYS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [GET_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [GET_YIELD_FROM_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BEFORE_ASYNC_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BEFORE_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [WITH_EXCEPT_START] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [PUSH_EXC_INFO] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_ATTR_METHOD_WITH_VALUES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_ATTR_METHOD_NO_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [LOAD_ATTR_METHOD_LAZY_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [CALL_BOUND_METHOD_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [KW_NAMES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_PY_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_PY_WITH_DEFAULTS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_TYPE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_STR_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_TUPLE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_BUILTIN_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_BUILTIN_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_BUILTIN_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_LEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_ISINSTANCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_LIST_APPEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CALL_FUNCTION_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MAKE_FUNCTION] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [RETURN_GENERATOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, + [BUILD_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FORMAT_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [COPY] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [BINARY_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, + [SWAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [EXTENDED_ARG] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [CACHE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, }; diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 429c9d34d606..3e2ddaaf2006 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -734,6 +734,60 @@ def stack_analysis( ] return stack, -lowest + def get_stack_effect_info( + self, thing: parser.InstDef | parser.Super | parser.Macro + ) -> tuple[Instruction, str, str]: + + def effect_str(effect: list[StackEffect]) -> str: + if getattr(thing, 'kind', None) == 'legacy': + return str(-1) + n_effect, sym_effect = list_effect_size(effect) + if sym_effect: + return f"{sym_effect} + {n_effect}" if n_effect else sym_effect + return str(n_effect) + + match thing: + case parser.InstDef(): + if thing.kind != "op": + instr = self.instrs[thing.name] + popped = effect_str(instr.input_effects) + pushed = effect_str(instr.output_effects) + case parser.Super(): + instr = self.super_instrs[thing.name] + popped = '+'.join(effect_str(comp.instr.input_effects) for comp in instr.parts) + pushed = '+'.join(effect_str(comp.instr.output_effects) for comp in instr.parts) + case parser.Macro(): + instr = self.macro_instrs[thing.name] + parts = [comp for comp in instr.parts if isinstance(comp, Component)] + popped = '+'.join(effect_str(comp.instr.input_effects) for comp in parts) + pushed = '+'.join(effect_str(comp.instr.output_effects) for comp in parts) + case _: + typing.assert_never(thing) + return instr, popped, pushed + + def write_stack_effect_functions(self) -> None: + popped_data = [] + pushed_data = [] + for thing in self.everything: + instr, popped, pushed = self.get_stack_effect_info(thing) + popped_data.append( (instr, popped) ) + pushed_data.append( (instr, pushed) ) + + def write_function(direction: str, data: list[tuple[Instruction, str]]) -> None: + self.out.emit("\nstatic int"); + self.out.emit(f"_PyOpcode_num_{direction}(int opcode, int oparg) {{") + self.out.emit(" switch(opcode) {"); + for instr, effect in data: + self.out.emit(f" case {instr.name}:") + self.out.emit(f" return {effect};") + self.out.emit(" default:") + self.out.emit(" Py_UNREACHABLE();") + self.out.emit(" }") + self.out.emit("}") + + write_function('popped', popped_data) + write_function('pushed', pushed_data) + def write_metadata(self) -> None: """Write instruction metadata to output file.""" @@ -762,13 +816,13 @@ def write_metadata(self) -> None: # Create formatter; the rest of the code uses this self.out = Formatter(f, 0) + self.write_stack_effect_functions() + # Write variable definition self.out.emit("enum Direction { DIR_NONE, DIR_READ, DIR_WRITE };") self.out.emit(f"enum InstructionFormat {{ {', '.join(format_enums)} }};") - self.out.emit("static const struct {") + self.out.emit("struct opcode_metadata {") with self.out.indent(): - self.out.emit("short n_popped;") - self.out.emit("short n_pushed;") self.out.emit("enum Direction dir_op1;") self.out.emit("enum Direction dir_op2;") self.out.emit("enum Direction dir_op3;") @@ -796,42 +850,30 @@ def write_metadata_for_inst(self, instr: Instruction) -> None: """Write metadata for a single instruction.""" dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" if instr.kind == "legacy": - n_popped = n_pushed = -1 assert not instr.register else: - n_popped, sym_popped = list_effect_size(instr.input_effects) - n_pushed, sym_pushed = list_effect_size(instr.output_effects) - if sym_popped or sym_pushed: - # TODO: Record symbolic effects (how?) - n_popped = n_pushed = -1 if instr.register: directions: list[str] = [] directions.extend("DIR_READ" for _ in instr.input_effects) directions.extend("DIR_WRITE" for _ in instr.output_effects) directions.extend("DIR_NONE" for _ in range(3)) dir_op1, dir_op2, dir_op3 = directions[:3] - n_popped = n_pushed = 0 self.out.emit( - f' [{instr.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{instr.instr_fmt} }},' + f' [{instr.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{instr.instr_fmt} }},' ) def write_metadata_for_super(self, sup: SuperInstruction) -> None: """Write metadata for a super-instruction.""" - n_popped = sum(len(comp.instr.input_effects) for comp in sup.parts) - n_pushed = sum(len(comp.instr.output_effects) for comp in sup.parts) dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" self.out.emit( - f' [{sup.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{sup.instr_fmt} }},' + f' [{sup.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{sup.instr_fmt} }},' ) def write_metadata_for_macro(self, mac: MacroInstruction) -> None: """Write metadata for a macro-instruction.""" - parts = [comp for comp in mac.parts if isinstance(comp, Component)] - n_popped = sum(len(comp.instr.input_effects) for comp in parts) - n_pushed = sum(len(comp.instr.output_effects) for comp in parts) dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" self.out.emit( - f' [{mac.name}] = {{ {n_popped}, {n_pushed}, {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{mac.instr_fmt} }},' + f' [{mac.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{mac.instr_fmt} }},' ) def write_instructions(self) -> None: From webhook-mailer at python.org Wed Jan 25 16:01:20 2023 From: webhook-mailer at python.org (hauntsaninja) Date: Wed, 25 Jan 2023 21:01:20 -0000 Subject: [Python-checkins] [3.11] gh-101326: Fix regression when passing None to FutureIter.throw (GH-101327) (#101328) Message-ID: https://github.com/python/cpython/commit/cd0fe5ba09df65bdee90738129b7315a936b83fb commit: cd0fe5ba09df65bdee90738129b7315a936b83fb branch: 3.11 author: Shantanu <12621235+hauntsaninja at users.noreply.github.com> committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com> date: 2023-01-25T13:01:13-08:00 summary: [3.11] gh-101326: Fix regression when passing None to FutureIter.throw (GH-101327) (#101328) (cherry picked from commit a178ba82bfe2f2fb6f6ff0e67cb734fd7c4321e3) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> files: A Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst M Lib/test/test_asyncio/test_futures.py M Modules/_asynciomodule.c diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 987772e0b196..c6aad824c78d 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -607,6 +607,8 @@ def test_future_iter_throw(self): Exception, Exception("elephant"), 32) self.assertRaises(TypeError, fi.throw, Exception("elephant"), Exception("elephant")) + # https://github.com/python/cpython/issues/101326 + self.assertRaises(ValueError, fi.throw, ValueError, None, None) self.assertRaises(TypeError, fi.throw, list) def test_future_del_collect(self): diff --git a/Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst b/Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst new file mode 100644 index 000000000000..54b69b943091 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-25-18-07-20.gh-issue-101326.KL4SFv.rst @@ -0,0 +1 @@ +Fix regression when passing ``None`` as second or third argument to ``FutureIter.throw``. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 40f1f80be447..b2fef017050b 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1666,7 +1666,12 @@ FutureIter_throw(futureiterobject *self, PyObject *const *args, Py_ssize_t nargs val = args[1]; } - if (tb != NULL && !PyTraceBack_Check(tb)) { + if (val == Py_None) { + val = NULL; + } + if (tb == Py_None ) { + tb = NULL; + } else if (tb != NULL && !PyTraceBack_Check(tb)) { PyErr_SetString(PyExc_TypeError, "throw() third argument must be a traceback"); return NULL; } From webhook-mailer at python.org Wed Jan 25 16:30:39 2023 From: webhook-mailer at python.org (brandtbucher) Date: Wed, 25 Jan 2023 21:30:39 -0000 Subject: [Python-checkins] Fix incorrect versions in magic number comments (GH-101301) Message-ID: https://github.com/python/cpython/commit/6162a0e305faf82534c011ddb2fb99a94ae84d29 commit: 6162a0e305faf82534c011ddb2fb99a94ae84d29 branch: main author: Brandt Bucher committer: brandtbucher date: 2023-01-25T13:30:33-08:00 summary: Fix incorrect versions in magic number comments (GH-101301) files: M Lib/importlib/_bootstrap_external.py diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index cd44d0050ede..e760fbb15759 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -423,14 +423,14 @@ def _write_atomic(path, data, mode=0o666): # Python 3.12a1 3507 (Set lineno of module's RESUME to 0) # Python 3.12a1 3508 (Add CLEANUP_THROW) # Python 3.12a1 3509 (Conditional jumps only jump forward) -# Python 3.12a1 3510 (FOR_ITER leaves iterator on the stack) -# Python 3.12a1 3511 (Add STOPITERATION_ERROR instruction) -# Python 3.12a1 3512 (Remove all unused consts from code objects) -# Python 3.12a1 3513 (Add CALL_INTRINSIC_1 instruction, removed STOPITERATION_ERROR, PRINT_EXPR, IMPORT_STAR) -# Python 3.12a1 3514 (Remove ASYNC_GEN_WRAP, LIST_TO_TUPLE, and UNARY_POSITIVE) -# Python 3.12a1 3515 (Embed jump mask in COMPARE_OP oparg) -# Python 3.12a1 3516 (Add COMPARE_AND_BRANCH instruction) -# Python 3.12a1 3517 (Change YIELD_VALUE oparg to exception block depth) +# Python 3.12a2 3510 (FOR_ITER leaves iterator on the stack) +# Python 3.12a2 3511 (Add STOPITERATION_ERROR instruction) +# Python 3.12a2 3512 (Remove all unused consts from code objects) +# Python 3.12a4 3513 (Add CALL_INTRINSIC_1 instruction, removed STOPITERATION_ERROR, PRINT_EXPR, IMPORT_STAR) +# Python 3.12a4 3514 (Remove ASYNC_GEN_WRAP, LIST_TO_TUPLE, and UNARY_POSITIVE) +# Python 3.12a5 3515 (Embed jump mask in COMPARE_OP oparg) +# Python 3.12a5 3516 (Add COMPARE_AND_BRANCH instruction) +# Python 3.12a5 3517 (Change YIELD_VALUE oparg to exception block depth) # Python 3.13 will start with 3550 From webhook-mailer at python.org Wed Jan 25 17:30:02 2023 From: webhook-mailer at python.org (iritkatriel) Date: Wed, 25 Jan 2023 22:30:02 -0000 Subject: [Python-checkins] gh-98831: rewrite RAISE_VARARGS in the instruction definition DSL (#101306) Message-ID: https://github.com/python/cpython/commit/b400219df5f245ef2eb3c7ce0589b1f8020a1192 commit: b400219df5f245ef2eb3c7ce0589b1f8020a1192 branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-25T22:29:56Z summary: gh-98831: rewrite RAISE_VARARGS in the instruction definition DSL (#101306) files: M Python/bytecodes.c M Python/generated_cases.c.h M Python/opcode_metadata.h M Tools/cases_generator/generate_cases.py diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d3e242b81e60..e5769f61fc28 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -505,27 +505,24 @@ dummy_func( ERROR_IF(res == NULL, error); } - // This should remain a legacy instruction. - inst(RAISE_VARARGS) { + inst(RAISE_VARARGS, (args[oparg] -- )) { PyObject *cause = NULL, *exc = NULL; switch (oparg) { case 2: - cause = POP(); /* cause */ + cause = args[1]; /* fall through */ case 1: - exc = POP(); /* exc */ + exc = args[0]; /* fall through */ case 0: - if (do_raise(tstate, exc, cause)) { - goto exception_unwind; - } + ERROR_IF(do_raise(tstate, exc, cause), exception_unwind); break; default: _PyErr_SetString(tstate, PyExc_SystemError, "bad RAISE_VARARGS oparg"); break; } - goto error; + ERROR_IF(true, error); } inst(INTERPRETER_EXIT, (retval --)) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 7d3396ad6bde..287a1f1f0420 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -689,25 +689,24 @@ } TARGET(RAISE_VARARGS) { + PyObject **args = &PEEK(oparg); PyObject *cause = NULL, *exc = NULL; switch (oparg) { case 2: - cause = POP(); /* cause */ + cause = args[1]; /* fall through */ case 1: - exc = POP(); /* exc */ + exc = args[0]; /* fall through */ case 0: - if (do_raise(tstate, exc, cause)) { - goto exception_unwind; - } + if (do_raise(tstate, exc, cause)) { STACK_SHRINK(oparg); goto exception_unwind; } break; default: _PyErr_SetString(tstate, PyExc_SystemError, "bad RAISE_VARARGS oparg"); break; } - goto error; + if (true) { STACK_SHRINK(oparg); goto error; } } TARGET(INTERPRETER_EXIT) { diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 46fd9673e8fb..cca86629e48d 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -2,6 +2,7 @@ // from Python/bytecodes.c // Do not edit! +#ifndef NDEBUG static int _PyOpcode_num_popped(int opcode, int oparg) { switch(opcode) { @@ -86,7 +87,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { case CALL_INTRINSIC_1: return 1; case RAISE_VARARGS: - return -1; + return oparg; case INTERPRETER_EXIT: return 1; case RETURN_VALUE: @@ -345,7 +346,9 @@ _PyOpcode_num_popped(int opcode, int oparg) { Py_UNREACHABLE(); } } +#endif +#ifndef NDEBUG static int _PyOpcode_num_pushed(int opcode, int oparg) { switch(opcode) { @@ -430,7 +433,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case CALL_INTRINSIC_1: return 1; case RAISE_VARARGS: - return -1; + return 0; case INTERPRETER_EXIT: return 0; case RETURN_VALUE: @@ -689,6 +692,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { Py_UNREACHABLE(); } } +#endif enum Direction { DIR_NONE, DIR_READ, DIR_WRITE }; enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; struct opcode_metadata { diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 3e2ddaaf2006..b7942410c82f 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -774,7 +774,8 @@ def write_stack_effect_functions(self) -> None: pushed_data.append( (instr, pushed) ) def write_function(direction: str, data: list[tuple[Instruction, str]]) -> None: - self.out.emit("\nstatic int"); + self.out.emit("\n#ifndef NDEBUG"); + self.out.emit("static int"); self.out.emit(f"_PyOpcode_num_{direction}(int opcode, int oparg) {{") self.out.emit(" switch(opcode) {"); for instr, effect in data: @@ -784,6 +785,7 @@ def write_function(direction: str, data: list[tuple[Instruction, str]]) -> None: self.out.emit(" Py_UNREACHABLE();") self.out.emit(" }") self.out.emit("}") + self.out.emit("#endif"); write_function('popped', popped_data) write_function('pushed', pushed_data) @@ -1023,7 +1025,7 @@ def always_exits(lines: list[str]) -> bool: return False line = line[12:] return line.startswith( - ("goto ", "return ", "DISPATCH", "GO_TO_", "Py_UNREACHABLE()") + ("goto ", "return ", "DISPATCH", "GO_TO_", "Py_UNREACHABLE()", "ERROR_IF(true, ") ) From webhook-mailer at python.org Thu Jan 26 01:50:40 2023 From: webhook-mailer at python.org (gpshead) Date: Thu, 26 Jan 2023 06:50:40 -0000 Subject: [Python-checkins] gh-94518: Rename `group*` to `extra_group*` to avoid confusion (#101054) Message-ID: https://github.com/python/cpython/commit/73245d084e383b5bc3affedc9444e6b6c881c546 commit: 73245d084e383b5bc3affedc9444e6b6c881c546 branch: main author: Oleg Iarygin committer: gpshead date: 2023-01-25T22:50:33-08:00 summary: gh-94518: Rename `group*` to `extra_group*` to avoid confusion (#101054) * Rename `group*` to `extra_group*` to avoid confusion * Rename `num_groups` into `extra_group_size` * Rename `groups_list` to `extra_groups_packed` files: A Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst M Modules/_posixsubprocess.c diff --git a/Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst b/Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst new file mode 100644 index 000000000000..77563090464d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-15-09-11-30.gh-issue-94518.jvxtxm.rst @@ -0,0 +1,3 @@ +Group-related variables of ``_posixsubprocess`` module are renamed to +stress that supplimentary group affinity is added to a fork, not +replace the inherited ones. Patch by Oleg Iarygin. diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 516f11d3543d..f3ff39215eab 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -519,7 +519,7 @@ child_exec(char *const exec_array[], int close_fds, int restore_signals, int call_setsid, pid_t pgid_to_set, gid_t gid, - Py_ssize_t groups_size, const gid_t *groups, + Py_ssize_t extra_group_size, const gid_t *extra_groups, uid_t uid, int child_umask, const void *child_sigmask, PyObject *py_fds_to_keep, @@ -619,8 +619,8 @@ child_exec(char *const exec_array[], #endif #ifdef HAVE_SETGROUPS - if (groups_size > 0) - POSIX_CALL(setgroups(groups_size, groups)); + if (extra_group_size > 0) + POSIX_CALL(setgroups(extra_group_size, extra_groups)); #endif /* HAVE_SETGROUPS */ #ifdef HAVE_SETREGID @@ -725,7 +725,7 @@ do_fork_exec(char *const exec_array[], int close_fds, int restore_signals, int call_setsid, pid_t pgid_to_set, gid_t gid, - Py_ssize_t groups_size, const gid_t *groups, + Py_ssize_t extra_group_size, const gid_t *extra_groups, uid_t uid, int child_umask, const void *child_sigmask, PyObject *py_fds_to_keep, @@ -740,7 +740,7 @@ do_fork_exec(char *const exec_array[], /* These are checked by our caller; verify them in debug builds. */ assert(uid == (uid_t)-1); assert(gid == (gid_t)-1); - assert(groups_size < 0); + assert(extra_group_size < 0); assert(preexec_fn == Py_None); pid = vfork(); @@ -777,7 +777,7 @@ do_fork_exec(char *const exec_array[], p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, errpipe_read, errpipe_write, close_fds, restore_signals, call_setsid, pgid_to_set, - gid, groups_size, groups, + gid, extra_group_size, extra_groups, uid, child_umask, child_sigmask, py_fds_to_keep, preexec_fn, preexec_fn_args_tuple); _exit(255); @@ -793,20 +793,20 @@ subprocess_fork_exec(PyObject *module, PyObject *args) PyObject *env_list, *preexec_fn; PyObject *process_args, *converted_args = NULL, *fast_args = NULL; PyObject *preexec_fn_args_tuple = NULL; - PyObject *groups_list; + PyObject *extra_groups_packed; PyObject *uid_object, *gid_object; int p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite; int errpipe_read, errpipe_write, close_fds, restore_signals; int call_setsid; pid_t pgid_to_set = -1; - gid_t *groups = NULL; + gid_t *extra_groups = NULL; int child_umask; PyObject *cwd_obj, *cwd_obj2 = NULL; const char *cwd; pid_t pid = -1; int need_to_reenable_gc = 0; char *const *exec_array, *const *argv = NULL, *const *envp = NULL; - Py_ssize_t arg_num, num_groups = 0; + Py_ssize_t arg_num, extra_group_size = 0; int need_after_fork = 0; int saved_errno = 0; int allow_vfork; @@ -819,7 +819,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args) &p2cread, &p2cwrite, &c2pread, &c2pwrite, &errread, &errwrite, &errpipe_read, &errpipe_write, &restore_signals, &call_setsid, &pgid_to_set, - &gid_object, &groups_list, &uid_object, &child_umask, + &gid_object, &extra_groups_packed, &uid_object, &child_umask, &preexec_fn, &allow_vfork)) return NULL; @@ -895,41 +895,41 @@ subprocess_fork_exec(PyObject *module, PyObject *args) cwd = NULL; } - if (groups_list != Py_None) { + if (extra_groups_packed != Py_None) { #ifdef HAVE_SETGROUPS - if (!PyList_Check(groups_list)) { + if (!PyList_Check(extra_groups_packed)) { PyErr_SetString(PyExc_TypeError, "setgroups argument must be a list"); goto cleanup; } - num_groups = PySequence_Size(groups_list); + extra_group_size = PySequence_Size(extra_groups_packed); - if (num_groups < 0) + if (extra_group_size < 0) goto cleanup; - if (num_groups > MAX_GROUPS) { - PyErr_SetString(PyExc_ValueError, "too many groups"); + if (extra_group_size > MAX_GROUPS) { + PyErr_SetString(PyExc_ValueError, "too many extra_groups"); goto cleanup; } - /* Deliberately keep groups == NULL for num_groups == 0 */ - if (num_groups > 0) { - groups = PyMem_RawMalloc(num_groups * sizeof(gid_t)); - if (groups == NULL) { + /* Deliberately keep extra_groups == NULL for extra_group_size == 0 */ + if (extra_group_size > 0) { + extra_groups = PyMem_RawMalloc(extra_group_size * sizeof(gid_t)); + if (extra_groups == NULL) { PyErr_SetString(PyExc_MemoryError, "failed to allocate memory for group list"); goto cleanup; } } - for (Py_ssize_t i = 0; i < num_groups; i++) { + for (Py_ssize_t i = 0; i < extra_group_size; i++) { PyObject *elem; - elem = PySequence_GetItem(groups_list, i); + elem = PySequence_GetItem(extra_groups_packed, i); if (!elem) goto cleanup; if (!PyLong_Check(elem)) { PyErr_SetString(PyExc_TypeError, - "groups must be integers"); + "extra_groups must be integers"); Py_DECREF(elem); goto cleanup; } else { @@ -939,7 +939,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args) PyErr_SetString(PyExc_ValueError, "invalid group id"); goto cleanup; } - groups[i] = gid; + extra_groups[i] = gid; } Py_DECREF(elem); } @@ -991,7 +991,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args) /* Use vfork() only if it's safe. See the comment above child_exec(). */ sigset_t old_sigs; if (preexec_fn == Py_None && allow_vfork && - uid == (uid_t)-1 && gid == (gid_t)-1 && num_groups < 0) { + uid == (uid_t)-1 && gid == (gid_t)-1 && extra_group_size < 0) { /* Block all signals to ensure that no signal handlers are run in the * child process while it shares memory with us. Note that signals * used internally by C libraries won't be blocked by @@ -1014,7 +1014,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args) p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, errpipe_read, errpipe_write, close_fds, restore_signals, call_setsid, pgid_to_set, - gid, num_groups, groups, + gid, extra_group_size, extra_groups, uid, child_umask, old_sigmask, py_fds_to_keep, preexec_fn, preexec_fn_args_tuple); @@ -1054,7 +1054,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args) } Py_XDECREF(preexec_fn_args_tuple); - PyMem_RawFree(groups); + PyMem_RawFree(extra_groups); Py_XDECREF(cwd_obj2); if (envp) _Py_FreeCharPArray(envp); @@ -1079,7 +1079,7 @@ PyDoc_STRVAR(subprocess_fork_exec_doc, p2cread, p2cwrite, c2pread, c2pwrite,\n\ errread, errwrite, errpipe_read, errpipe_write,\n\ restore_signals, call_setsid, pgid_to_set,\n\ - gid, groups_list, uid,\n\ + gid, extra_groups, uid,\n\ preexec_fn)\n\ \n\ Forks a child process, closes parent file descriptors as appropriate in the\n\ From webhook-mailer at python.org Thu Jan 26 02:01:17 2023 From: webhook-mailer at python.org (gpshead) Date: Thu, 26 Jan 2023 07:01:17 -0000 Subject: [Python-checkins] gh-100522 Add a test for 'futures.as_completed' timing out with a non-zero timeout value (#100523) Message-ID: https://github.com/python/cpython/commit/a2262789abccb68a61bb4047743fbcbd9a64b13c commit: a2262789abccb68a61bb4047743fbcbd9a64b13c branch: main author: JosephSBoyle <48555120+JosephSBoyle at users.noreply.github.com> committer: gpshead date: 2023-01-25T23:01:11-08:00 summary: gh-100522 Add a test for 'futures.as_completed' timing out with a non-zero timeout value (#100523) files: M Lib/test/test_concurrent_futures.py diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py index fe9fdc4f44d3..b3520ae3994e 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py @@ -711,7 +711,6 @@ def future_func(): class AsCompletedTests: - # TODO(brian at sweetapp.com): Should have a test with a non-zero timeout. def test_no_timeout(self): future1 = self.executor.submit(mul, 2, 21) future2 = self.executor.submit(mul, 7, 6) @@ -728,24 +727,29 @@ def test_no_timeout(self): future1, future2]), completed) - def test_zero_timeout(self): - future1 = self.executor.submit(time.sleep, 2) - completed_futures = set() - try: - for future in futures.as_completed( - [CANCELLED_AND_NOTIFIED_FUTURE, - EXCEPTION_FUTURE, - SUCCESSFUL_FUTURE, - future1], - timeout=0): - completed_futures.add(future) - except futures.TimeoutError: - pass + def test_future_times_out(self): + """Test ``futures.as_completed`` timing out before + completing it's final future.""" + already_completed = {CANCELLED_AND_NOTIFIED_FUTURE, + EXCEPTION_FUTURE, + SUCCESSFUL_FUTURE} - self.assertEqual(set([CANCELLED_AND_NOTIFIED_FUTURE, - EXCEPTION_FUTURE, - SUCCESSFUL_FUTURE]), - completed_futures) + for timeout in (0, 0.01): + with self.subTest(timeout): + + future = self.executor.submit(time.sleep, 0.1) + completed_futures = set() + try: + for f in futures.as_completed( + already_completed | {future}, + timeout + ): + completed_futures.add(f) + except futures.TimeoutError: + pass + + # Check that ``future`` wasn't completed. + self.assertEqual(completed_futures, already_completed) def test_duplicate_futures(self): # Issue 20367. Duplicate futures should not raise exceptions or give From webhook-mailer at python.org Thu Jan 26 02:25:50 2023 From: webhook-mailer at python.org (abalkin) Date: Thu, 26 Jan 2023 07:25:50 -0000 Subject: [Python-checkins] datetime.rst: improve combine() docs (#101338) Message-ID: https://github.com/python/cpython/commit/f5ad63f79af3a5876f90b409d0c8402fa54e878a commit: f5ad63f79af3a5876f90b409d0c8402fa54e878a branch: main author: John Belmonte committer: abalkin date: 2023-01-26T11:25:43+04:00 summary: datetime.rst: improve combine() docs (#101338) The explanation on handling of datetime as the date arg was confusingly mixed with an unrelated item, and lacked proper arg name formatting. files: M Doc/library/datetime.rst diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index eba8824f8351..ebb5f319efda 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -982,12 +982,11 @@ Other constructors, all class methods: are equal to the given :class:`.time` object's. If the *tzinfo* argument is provided, its value is used to set the :attr:`.tzinfo` attribute of the result, otherwise the :attr:`~.time.tzinfo` attribute of the *time* argument - is used. + 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*, - ``d == datetime.combine(d.date(), d.time(), d.tzinfo)``. If date is a - :class:`.datetime` object, its time components and :attr:`.tzinfo` attributes - are ignored. + ``d == datetime.combine(d.date(), d.time(), d.tzinfo)``. .. versionchanged:: 3.6 Added the *tzinfo* argument. From webhook-mailer at python.org Thu Jan 26 03:28:40 2023 From: webhook-mailer at python.org (gpshead) Date: Thu, 26 Jan 2023 08:28:40 -0000 Subject: [Python-checkins] gh-99952: [ctypes] fix refcount issues in from_param() result. (#100169) Message-ID: https://github.com/python/cpython/commit/dfad678d7024ab86d265d84ed45999e031a03691 commit: dfad678d7024ab86d265d84ed45999e031a03691 branch: main author: Yukihiro Nakadaira committer: gpshead date: 2023-01-26T00:28:34-08:00 summary: gh-99952: [ctypes] fix refcount issues in from_param() result. (#100169) Fixes a reference counting issue with `ctypes.Structure` when a `from_param()` method call is used and the structure size is larger than a C pointer `sizeof(void*)`. This problem existed for a very long time, but became more apparent in 3.8+ by change likely due to garbage collection cleanup timing changes. files: A Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst M Lib/test/test_ctypes/test_parameters.py M Modules/_ctypes/_ctypes.c M Modules/_ctypes/_ctypes_test.c diff --git a/Lib/test/test_ctypes/test_parameters.py b/Lib/test/test_ctypes/test_parameters.py index 22d290db1bcb..06cc95107b79 100644 --- a/Lib/test/test_ctypes/test_parameters.py +++ b/Lib/test/test_ctypes/test_parameters.py @@ -266,6 +266,58 @@ def test_parameter_repr(self): self.assertRegex(repr(c_wchar_p.from_param('hihi')), r"^$") self.assertRegex(repr(c_void_p.from_param(0x12)), r"^$") + @test.support.cpython_only + def test_from_param_result_refcount(self): + # Issue #99952 + import _ctypes_test + from ctypes import PyDLL, c_int, c_void_p, py_object, Structure + + class X(Structure): + """This struct size is <= sizeof(void*).""" + _fields_ = [("a", c_void_p)] + + def __del__(self): + trace.append(4) + + @classmethod + def from_param(cls, value): + trace.append(2) + return cls() + + PyList_Append = PyDLL(_ctypes_test.__file__)._testfunc_pylist_append + PyList_Append.restype = c_int + PyList_Append.argtypes = [py_object, py_object, X] + + trace = [] + trace.append(1) + PyList_Append(trace, 3, "dummy") + trace.append(5) + + self.assertEqual(trace, [1, 2, 3, 4, 5]) + + class Y(Structure): + """This struct size is > sizeof(void*).""" + _fields_ = [("a", c_void_p), ("b", c_void_p)] + + def __del__(self): + trace.append(4) + + @classmethod + def from_param(cls, value): + trace.append(2) + return cls() + + PyList_Append = PyDLL(_ctypes_test.__file__)._testfunc_pylist_append + PyList_Append.restype = c_int + PyList_Append.argtypes = [py_object, py_object, Y] + + trace = [] + trace.append(1) + PyList_Append(trace, 3, "dummy") + trace.append(5) + + self.assertEqual(trace, [1, 2, 3, 4, 5]) + ################################################################ if __name__ == '__main__': diff --git a/Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst b/Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst new file mode 100644 index 000000000000..09ec96124953 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst @@ -0,0 +1,2 @@ +Fix a reference undercounting issue in :class:`ctypes.Structure` with ``from_param()`` +results larger than a C pointer. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 272cafb5a9a3..8690f2c1b078 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -412,6 +412,7 @@ _ctypes_alloc_format_string_with_shape(int ndim, const Py_ssize_t *shape, typedef struct { PyObject_HEAD void *ptr; + PyObject *keep; // If set, a reference to the original CDataObject. } StructParamObject; @@ -419,6 +420,7 @@ static void StructParam_dealloc(PyObject *myself) { StructParamObject *self = (StructParamObject *)myself; + Py_XDECREF(self->keep); PyMem_Free(self->ptr); Py_TYPE(self)->tp_free(myself); } @@ -466,6 +468,7 @@ StructUnionType_paramfunc(CDataObject *self) StructParamObject *struct_param = (StructParamObject *)obj; struct_param->ptr = ptr; + struct_param->keep = Py_NewRef(self); } else { ptr = self->b_ptr; obj = Py_NewRef(self); diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c index e1f91b476a49..a8811d03cc91 100644 --- a/Modules/_ctypes/_ctypes_test.c +++ b/Modules/_ctypes/_ctypes_test.c @@ -1047,6 +1047,12 @@ EXPORT(long) _test_i38748_runCallback(_test_i38748_funcType callback, int a, int #endif +EXPORT(int) +_testfunc_pylist_append(PyObject *list, PyObject *item) +{ + return PyList_Append(list, item); +} + static struct PyModuleDef_Slot _ctypes_test_slots[] = { {0, NULL} }; From webhook-mailer at python.org Thu Jan 26 04:03:18 2023 From: webhook-mailer at python.org (gpshead) Date: Thu, 26 Jan 2023 09:03:18 -0000 Subject: [Python-checkins] [3.11] gh-99952: fix refcount issues in ctypes.Structure from_param() result (#101339) Message-ID: https://github.com/python/cpython/commit/fa7c37af4936abfe34aa261d6ed9703bc5842ad4 commit: fa7c37af4936abfe34aa261d6ed9703bc5842ad4 branch: 3.11 author: Gregory P. Smith committer: gpshead date: 2023-01-26T01:02:37-08:00 summary: [3.11] gh-99952: fix refcount issues in ctypes.Structure from_param() result (#101339) [3.11] gh-99952: [ctypes] fix refcount issues in from_param() result. (GH-100169) Fixes a reference counting issue with `ctypes.Structure` when a `from_param()` method call is used and the structure size is larger than a C pointer `sizeof(void*)`. This problem existed for a very long time, but became more apparent in 3.8+ by change likely due to garbage collection cleanup timing changes.. (cherry picked from commit dfad678d7024ab86d265d84ed45999e031a03691) Co-authored-by: Yukihiro Nakadaira files: A Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst M Lib/ctypes/test/test_parameters.py M Modules/_ctypes/_ctypes.c M Modules/_ctypes/_ctypes_test.c diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py index 38af7ac13d75..3fdc994e9078 100644 --- a/Lib/ctypes/test/test_parameters.py +++ b/Lib/ctypes/test/test_parameters.py @@ -244,6 +244,58 @@ def test_parameter_repr(self): self.assertRegex(repr(c_wchar_p.from_param('hihi')), r"^$") self.assertRegex(repr(c_void_p.from_param(0x12)), r"^$") + @test.support.cpython_only + def test_from_param_result_refcount(self): + # Issue #99952 + import _ctypes_test + from ctypes import PyDLL, c_int, c_void_p, py_object, Structure + + class X(Structure): + """This struct size is <= sizeof(void*).""" + _fields_ = [("a", c_void_p)] + + def __del__(self): + trace.append(4) + + @classmethod + def from_param(cls, value): + trace.append(2) + return cls() + + PyList_Append = PyDLL(_ctypes_test.__file__)._testfunc_pylist_append + PyList_Append.restype = c_int + PyList_Append.argtypes = [py_object, py_object, X] + + trace = [] + trace.append(1) + PyList_Append(trace, 3, "dummy") + trace.append(5) + + self.assertEqual(trace, [1, 2, 3, 4, 5]) + + class Y(Structure): + """This struct size is > sizeof(void*).""" + _fields_ = [("a", c_void_p), ("b", c_void_p)] + + def __del__(self): + trace.append(4) + + @classmethod + def from_param(cls, value): + trace.append(2) + return cls() + + PyList_Append = PyDLL(_ctypes_test.__file__)._testfunc_pylist_append + PyList_Append.restype = c_int + PyList_Append.argtypes = [py_object, py_object, Y] + + trace = [] + trace.append(1) + PyList_Append(trace, 3, "dummy") + trace.append(5) + + self.assertEqual(trace, [1, 2, 3, 4, 5]) + ################################################################ if __name__ == '__main__': diff --git a/Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst b/Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst new file mode 100644 index 000000000000..09ec96124953 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-11-14-38-59.gh-issue-99952.IYGLzr.rst @@ -0,0 +1,2 @@ +Fix a reference undercounting issue in :class:`ctypes.Structure` with ``from_param()`` +results larger than a C pointer. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index b5eafe49e4ee..fc73264ff5a3 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -416,6 +416,7 @@ _ctypes_alloc_format_string_with_shape(int ndim, const Py_ssize_t *shape, typedef struct { PyObject_HEAD void *ptr; + PyObject *keep; // If set, a reference to the original CDataObject. } StructParamObject; @@ -423,6 +424,7 @@ static void StructParam_dealloc(PyObject *myself) { StructParamObject *self = (StructParamObject *)myself; + Py_XDECREF(self->keep); PyMem_Free(self->ptr); Py_TYPE(self)->tp_free(myself); } @@ -470,6 +472,7 @@ StructUnionType_paramfunc(CDataObject *self) StructParamObject *struct_param = (StructParamObject *)obj; struct_param->ptr = ptr; + struct_param->keep = Py_NewRef(self); } else { ptr = self->b_ptr; obj = (PyObject *)self; diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c index 770c96c60d1f..8a6a11670ba1 100644 --- a/Modules/_ctypes/_ctypes_test.c +++ b/Modules/_ctypes/_ctypes_test.c @@ -1034,6 +1034,12 @@ EXPORT (HRESULT) KeepObject(IUnknown *punk) #endif +EXPORT(int) +_testfunc_pylist_append(PyObject *list, PyObject *item) +{ + return PyList_Append(list, item); +} + static struct PyModuleDef_Slot _ctypes_test_slots[] = { {0, NULL} }; From webhook-mailer at python.org Thu Jan 26 07:58:48 2023 From: webhook-mailer at python.org (corona10) Date: Thu, 26 Jan 2023 12:58:48 -0000 Subject: [Python-checkins] gh-85100: Migrate BPO link to the GitHub link for malloc warnings (gh-101343) Message-ID: https://github.com/python/cpython/commit/f2ac9510a5aa4f71d468f486140eae60f46833ad commit: f2ac9510a5aa4f71d468f486140eae60f46833ad branch: main author: Dong-hee Na committer: corona10 date: 2023-01-26T21:58:35+09:00 summary: gh-85100: Migrate BPO link to the GitHub link for malloc warnings (gh-101343) files: M Lib/test/support/__init__.py diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index e4e4de896dff..4a22ccdd4db4 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -582,7 +582,8 @@ def darwin_malloc_err_warning(test_name): msg = ' NOTICE ' detail = (f'{test_name} may generate "malloc can\'t allocate region"\n' 'warnings on macOS systems. This behavior is known. Do not\n' - 'report a bug unless tests are also failing. See bpo-40928.') + 'report a bug unless tests are also failing.\n' + 'See https://github.com/python/cpython/issues/85100') padding, _ = shutil.get_terminal_size() print(msg.center(padding, '-')) From webhook-mailer at python.org Thu Jan 26 09:17:09 2023 From: webhook-mailer at python.org (abalkin) Date: Thu, 26 Jan 2023 14:17:09 -0000 Subject: [Python-checkins] gh-60580: Fix a wrong type of `ctypes.wintypes.BYTE` (#97579) Message-ID: https://github.com/python/cpython/commit/409f5337a3e466a5ef673797575cbd1745d27ca9 commit: 409f5337a3e466a5ef673797575cbd1745d27ca9 branch: main author: Oleg Iarygin committer: abalkin date: 2023-01-26T18:16:27+04:00 summary: gh-60580: Fix a wrong type of `ctypes.wintypes.BYTE` (#97579) Created from a patch file attached to an issue, by Anatoly Techtonik. files: A Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst M Lib/ctypes/wintypes.py M Lib/test/test_ctypes/test_wintypes.py diff --git a/Lib/ctypes/wintypes.py b/Lib/ctypes/wintypes.py index c619d27596d4..9c4e721438aa 100644 --- a/Lib/ctypes/wintypes.py +++ b/Lib/ctypes/wintypes.py @@ -1,7 +1,7 @@ # The most useful windows datatypes import ctypes -BYTE = ctypes.c_byte +BYTE = ctypes.c_ubyte WORD = ctypes.c_ushort DWORD = ctypes.c_ulong diff --git a/Lib/test/test_ctypes/test_wintypes.py b/Lib/test/test_ctypes/test_wintypes.py index 243d5962ffa7..a01b9b1d0f31 100644 --- a/Lib/test/test_ctypes/test_wintypes.py +++ b/Lib/test/test_ctypes/test_wintypes.py @@ -1,3 +1,6 @@ +# See +# for reference. + import unittest # also work on POSIX @@ -38,6 +41,22 @@ def test_variant_bool(self): vb.value = [] self.assertIs(vb.value, False) + def assertIsSigned(self, ctype): + self.assertLess(ctype(-1).value, 0) + + def assertIsUnsigned(self, ctype): + self.assertGreater(ctype(-1).value, 0) + + def test_signedness(self): + for ctype in (wintypes.BYTE, wintypes.WORD, wintypes.DWORD, + wintypes.BOOLEAN, wintypes.UINT, wintypes.ULONG): + with self.subTest(ctype=ctype): + self.assertIsUnsigned(ctype) + + for ctype in (wintypes.BOOL, wintypes.INT, wintypes.LONG): + with self.subTest(ctype=ctype): + self.assertIsSigned(ctype) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst b/Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst new file mode 100644 index 000000000000..630e56cd2f7b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-09-26-21-18-47.gh-issue-60580.0hBgde.rst @@ -0,0 +1,3 @@ +:data:`ctypes.wintypes.BYTE` definition changed from +:data:`~ctypes.c_byte` to :data:`~ctypes.c_ubyte` to match Windows +SDK. Patch by Anatoly Techtonik and Oleg Iarygin. From webhook-mailer at python.org Thu Jan 26 12:15:39 2023 From: webhook-mailer at python.org (gvanrossum) Date: Thu, 26 Jan 2023 17:15:39 -0000 Subject: [Python-checkins] gh-98831: Fix two bugs in case generator (#101349) Message-ID: https://github.com/python/cpython/commit/9f2c479eaf7d922746ef2f3c85b5c781757686b1 commit: 9f2c479eaf7d922746ef2f3c85b5c781757686b1 branch: main author: Guido van Rossum committer: gvanrossum date: 2023-01-26T09:15:05-08:00 summary: gh-98831: Fix two bugs in case generator (#101349) Fix two bugs in case generator - UndefinedLocalError when generating metadata for an 'op' - Accidental newline inserted in test_generator.py files: M Tools/cases_generator/generate_cases.py M Tools/cases_generator/test_generator.py diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index b7942410c82f..9d894d2ff574 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -736,7 +736,7 @@ def stack_analysis( def get_stack_effect_info( self, thing: parser.InstDef | parser.Super | parser.Macro - ) -> tuple[Instruction, str, str]: + ) -> tuple[Instruction|None, str, str]: def effect_str(effect: list[StackEffect]) -> str: if getattr(thing, 'kind', None) == 'legacy': @@ -752,6 +752,9 @@ def effect_str(effect: list[StackEffect]) -> str: instr = self.instrs[thing.name] popped = effect_str(instr.input_effects) pushed = effect_str(instr.output_effects) + else: + instr = None + popped = pushed = "", "" case parser.Super(): instr = self.super_instrs[thing.name] popped = '+'.join(effect_str(comp.instr.input_effects) for comp in instr.parts) @@ -770,8 +773,9 @@ def write_stack_effect_functions(self) -> None: pushed_data = [] for thing in self.everything: instr, popped, pushed = self.get_stack_effect_info(thing) - popped_data.append( (instr, popped) ) - pushed_data.append( (instr, pushed) ) + if instr is not None: + popped_data.append( (instr, popped) ) + pushed_data.append( (instr, pushed) ) def write_function(direction: str, data: list[tuple[Instruction, str]]) -> None: self.out.emit("\n#ifndef NDEBUG"); diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index cf58e6aaf2b3..bd1b974399ab 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -358,8 +358,7 @@ def test_macro_instruction(): { PyObject *arg1 = _tmp_1; PyObject *interim; - uint16_t counter = re - ad_u16(&next_instr[0].cache); + uint16_t counter = read_u16(&next_instr[0].cache); interim = op1(arg1); _tmp_1 = interim; } From webhook-mailer at python.org Thu Jan 26 15:47:43 2023 From: webhook-mailer at python.org (zooba) Date: Thu, 26 Jan 2023 20:47:43 -0000 Subject: [Python-checkins] gh-99834: Update bundled copy of Tcl/Tk to 8.6.13.0 on Windows (GH-101307) Message-ID: https://github.com/python/cpython/commit/8d18d1ffd52eb3917c4566b09596d596116a5532 commit: 8d18d1ffd52eb3917c4566b09596d596116a5532 branch: main author: Steve Dower committer: zooba date: 2023-01-26T20:47:24Z summary: gh-99834: Update bundled copy of Tcl/Tk to 8.6.13.0 on Windows (GH-101307) files: A Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst M PC/layout/main.py M PCbuild/_tkinter.vcxproj M PCbuild/get_externals.bat M PCbuild/tcltk.props M Tools/msi/tcltk/tcltk_files.wxs diff --git a/Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst b/Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst new file mode 100644 index 000000000000..d3894fa4ea30 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-01-25-00-23-31.gh-issue-99834.WN41lc.rst @@ -0,0 +1 @@ +Updates bundled copy of Tcl/Tk to 8.6.13.0 diff --git a/PC/layout/main.py b/PC/layout/main.py index 17d27bba6640..c9246007d47d 100644 --- a/PC/layout/main.py +++ b/PC/layout/main.py @@ -35,7 +35,7 @@ IDLE_DIRS_ONLY = FileNameSet("idlelib") -TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter") +TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter", "zlib1") TCLTK_DIRS_ONLY = FileNameSet("tkinter", "turtledemo") TCLTK_FILES_ONLY = FileNameSet("turtle.py") diff --git a/PCbuild/_tkinter.vcxproj b/PCbuild/_tkinter.vcxproj index af813b77c1d1..30cedcbb43de 100644 --- a/PCbuild/_tkinter.vcxproj +++ b/PCbuild/_tkinter.vcxproj @@ -111,6 +111,7 @@ <_TclTkDLL Include="$(tcltkdir)\bin\$(tclDllName)" /> <_TclTkDLL Include="$(tcltkdir)\bin\$(tkDllName)" /> + <_TclTkDLL Include="$(tcltkdir)\bin\$(tclZlibDllName)" /> diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 7efdeb2d30a7..0a41d131a3e8 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -55,8 +55,8 @@ set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.3 if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1s set libraries=%libraries% sqlite-3.39.4.0 -if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.12.1 -if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.12.1 +if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.13.0 +if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.13.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tix-8.4.3.6 set libraries=%libraries% xz-5.2.5 set libraries=%libraries% zlib-1.2.13 @@ -78,7 +78,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.3 if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1s -if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.12.1 +if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.13.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 for %%b in (%binaries%) do ( diff --git a/PCbuild/tcltk.props b/PCbuild/tcltk.props index 7fd43e8279e8..15c03e20fe21 100644 --- a/PCbuild/tcltk.props +++ b/PCbuild/tcltk.props @@ -4,8 +4,8 @@ 8 6 - 12 - 1 + 13 + 0 $(TclMajorVersion) $(TclMinorVersion) $(TclPatchLevel) @@ -27,6 +27,7 @@ tclsh$(TclMajorVersion)$(TclMinorVersion)t$(TclDebugExt).exe tk$(TkMajorVersion)$(TkMinorVersion)t$(TclDebugExt).dll tk$(TkMajorVersion)$(TkMinorVersion)t$(TclDebugExt).lib + zlib1.dll tix$(TixMajorVersion)$(TixMinorVersion)$(TclDebugExt).dll $(tcltkDir)lib\tix$(TixMajorVersion).$(TixMinorVersion).$(TixPatchLevel)\$(tixDLLName) $(tcltkDir)lib\tcl$(TclMajorVersion)$(TclMinorVersion)t$(TclDebugExt).lib;$(tcltkDir)lib\tk$(TkMajorVersion)$(TkMinorVersion)t$(TclDebugExt).lib diff --git a/Tools/msi/tcltk/tcltk_files.wxs b/Tools/msi/tcltk/tcltk_files.wxs index 119451078096..5dad7c98d4f0 100644 --- a/Tools/msi/tcltk/tcltk_files.wxs +++ b/Tools/msi/tcltk/tcltk_files.wxs @@ -16,6 +16,9 @@ + + + From webhook-mailer at python.org Thu Jan 26 18:04:27 2023 From: webhook-mailer at python.org (hauntsaninja) Date: Thu, 26 Jan 2023 23:04:27 -0000 Subject: [Python-checkins] Fix typos in pystate.c file (#101348) Message-ID: https://github.com/python/cpython/commit/37f15a5efab847b8aca47981ab596e9c36445bf7 commit: 37f15a5efab847b8aca47981ab596e9c36445bf7 branch: main author: ??????? ???????? committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com> date: 2023-01-26T15:04:11-08:00 summary: Fix typos in pystate.c file (#101348) files: M Python/pystate.c diff --git a/Python/pystate.c b/Python/pystate.c index d31c1f166f22..bf7688fd3213 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -57,9 +57,9 @@ static void _PyThreadState_Delete(PyThreadState *tstate, int check_current); //------------------------------------------------- /* - The stored thread state is set by PyThraedState_Swap(). + The stored thread state is set by PyThreadState_Swap(). - For each of these functions, the GIL mus be held by the current thread. + For each of these functions, the GIL must be held by the current thread. */ static inline PyThreadState * @@ -232,7 +232,7 @@ unbind_tstate(PyThreadState *tstate) current_tss_clear(runtime); } - // We leave thread_id and native_thraed_id alone + // We leave thread_id and native_thread_id alone // since they can be useful for debugging. // Check the `_status` field to know if these values // are still valid. @@ -1140,7 +1140,7 @@ init_threadstate(PyThreadState *tstate, tstate->exc_info = &tstate->exc_state; // PyGILState_Release must not try to delete this thread state. - // This is cleared when PyGILState_Ensure() creates the thread sate. + // This is cleared when PyGILState_Ensure() creates the thread state. tstate->gilstate_counter = 1; tstate->cframe = &tstate->root_cframe; @@ -1220,7 +1220,7 @@ _PyThreadState_Prealloc(PyInterpreterState *interp) } // We keep this around for (accidental) stable ABI compatibility. -// Realisically, no extensions are using it. +// Realistically, no extensions are using it. void _PyThreadState_Init(PyThreadState *tstate) { From webhook-mailer at python.org Thu Jan 26 19:28:35 2023 From: webhook-mailer at python.org (AlexWaygood) Date: Fri, 27 Jan 2023 00:28:35 -0000 Subject: [Python-checkins] gh-101000: Add os.path.splitroot() (#101002) Message-ID: https://github.com/python/cpython/commit/e5b08ddddf1099f04bf65e63017de840bd4b5980 commit: e5b08ddddf1099f04bf65e63017de840bd4b5980 branch: main author: Barney Gale committer: AlexWaygood date: 2023-01-27T00:28:27Z summary: gh-101000: Add os.path.splitroot() (#101002) Co-authored-by: Eryk Sun Co-authored-by: Alex Waygood files: A Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst M Doc/library/os.path.rst M Doc/whatsnew/3.12.rst M Lib/ntpath.py M Lib/pathlib.py M Lib/posixpath.py M Lib/test/test_ntpath.py M Lib/test/test_pathlib.py M Lib/test/test_posixpath.py diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index 42bbe24830e6..786c2fd7f64f 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -488,6 +488,39 @@ the :mod:`glob` module.) Accepts a :term:`path-like object`. +.. function:: splitroot(path) + + Split the pathname *path* into a 3-item tuple ``(drive, root, tail)`` where + *drive* is a device name or mount point, *root* is a string of separators + after the drive, and *tail* is everything after the root. Any of these + items may be the empty string. In all cases, ``drive + root + tail`` will + be the same as *path*. + + On POSIX systems, *drive* is always empty. The *root* may be empty (if *path* is + relative), a single forward slash (if *path* is absolute), or two forward slashes + (implementation-defined per `IEEE Std 1003.1-2017; 4.13 Pathname Resolution + `_.) + For example:: + + >>> splitroot('/home/sam') + ('', '/', 'home/sam') + >>> splitroot('//home/sam') + ('', '//', 'home/sam') + >>> splitroot('///home/sam') + ('', '/', '//home/sam') + + On Windows, *drive* may be empty, a drive-letter name, a UNC share, or a device + name. The *root* may be empty, a forward slash, or a backward slash. For + example:: + + >>> splitroot('C:/Users/Sam') + ('C:', '/', 'Users/Sam') + >>> splitroot('//Server/Share/Users/Sam') + ('//Server/Share', '/', 'Users/Sam') + + .. versionadded:: 3.12 + + .. function:: splitext(path) Split the pathname *path* into a pair ``(root, ext)`` such that ``root + ext == diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 2f9ca1102d3d..a071159b800a 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -288,13 +288,18 @@ os for a process with :func:`os.pidfd_open` in non-blocking mode. (Contributed by Kumar Aditya in :gh:`93312`.) -* Add :func:`os.path.isjunction` to check if a given path is a junction. - (Contributed by Charles Machalow in :gh:`99547`.) - * :class:`os.DirEntry` now includes an :meth:`os.DirEntry.is_junction` method to check if the entry is a junction. (Contributed by Charles Machalow in :gh:`99547`.) +os.path +------- + +* Add :func:`os.path.isjunction` to check if a given path is a junction. + (Contributed by Charles Machalow in :gh:`99547`.) + +* Add :func:`os.path.splitroot` to split a path into a triad + ``(drive, root, tail)``. (Contributed by Barney Gale in :gh:`101000`.) shutil ------ diff --git a/Lib/ntpath.py b/Lib/ntpath.py index cd7fb58a88de..f9ee8e02a576 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -24,7 +24,7 @@ from genericpath import * -__all__ = ["normcase","isabs","join","splitdrive","split","splitext", +__all__ = ["normcase","isabs","join","splitdrive","splitroot","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", "getatime","getctime", "islink","exists","lexists","isdir","isfile", "ismount", "expanduser","expandvars","normpath","abspath", @@ -117,19 +117,21 @@ def join(path, *paths): try: if not paths: path[:0] + sep #23780: Ensure compatible data type even if p is null. - result_drive, result_path = splitdrive(path) + result_drive, result_root, result_path = splitroot(path) for p in map(os.fspath, paths): - p_drive, p_path = splitdrive(p) - if p_path and p_path[0] in seps: + p_drive, p_root, p_path = splitroot(p) + if p_root: # Second path is absolute if p_drive or not result_drive: result_drive = p_drive + result_root = p_root result_path = p_path continue elif p_drive and p_drive != result_drive: if p_drive.lower() != result_drive.lower(): # Different drives => ignore the first path entirely result_drive = p_drive + result_root = p_root result_path = p_path continue # Same drive in different case @@ -139,10 +141,10 @@ def join(path, *paths): result_path = result_path + sep result_path = result_path + p_path ## add separator between UNC and non-absolute path - if (result_path and result_path[0] not in seps and + if (result_path and not result_root and result_drive and result_drive[-1:] != colon): return result_drive + sep + result_path - return result_drive + result_path + return result_drive + result_root + result_path except (TypeError, AttributeError, BytesWarning): genericpath._check_arg_types('join', path, *paths) raise @@ -169,35 +171,61 @@ def splitdrive(p): Paths cannot contain both a drive letter and a UNC path. + """ + drive, root, tail = splitroot(p) + return drive, root + tail + + +def splitroot(p): + """Split a pathname into drive, root and tail. The drive is defined + exactly as in splitdrive(). On Windows, the root may be a single path + separator or an empty string. The tail contains anything after the root. + For example: + + splitroot('//server/share/') == ('//server/share', '/', '') + splitroot('C:/Users/Barney') == ('C:', '/', 'Users/Barney') + splitroot('C:///spam///ham') == ('C:', '/', '//spam///ham') + splitroot('Windows/notepad') == ('', '', 'Windows/notepad') """ p = os.fspath(p) - if len(p) >= 2: - if isinstance(p, bytes): - sep = b'\\' - altsep = b'/' - colon = b':' - unc_prefix = b'\\\\?\\UNC\\' - else: - sep = '\\' - altsep = '/' - colon = ':' - unc_prefix = '\\\\?\\UNC\\' - normp = p.replace(altsep, sep) - if normp[0:2] == sep * 2: + if isinstance(p, bytes): + sep = b'\\' + altsep = b'/' + colon = b':' + unc_prefix = b'\\\\?\\UNC\\' + empty = b'' + else: + sep = '\\' + altsep = '/' + colon = ':' + unc_prefix = '\\\\?\\UNC\\' + empty = '' + normp = p.replace(altsep, sep) + if normp[:1] == sep: + if normp[1:2] == sep: # UNC drives, e.g. \\server\share or \\?\UNC\server\share # Device drives, e.g. \\.\device or \\?\device start = 8 if normp[:8].upper() == unc_prefix else 2 index = normp.find(sep, start) if index == -1: - return p, p[:0] + return p, empty, empty index2 = normp.find(sep, index + 1) if index2 == -1: - return p, p[:0] - return p[:index2], p[index2:] - if normp[1:2] == colon: - # Drive-letter drives, e.g. X: - return p[:2], p[2:] - return p[:0], p + return p, empty, empty + return p[:index2], p[index2:index2 + 1], p[index2 + 1:] + else: + # Relative path with root, e.g. \Windows + return empty, p[:1], p[1:] + elif normp[1:2] == colon: + if normp[2:3] == sep: + # Absolute drive-letter path, e.g. X:\Windows + return p[:2], p[2:3], p[3:] + else: + # Relative path with drive, e.g. X:Windows + return p[:2], empty, p[2:] + else: + # Relative path, e.g. Windows + return empty, empty, p # Split a path in head (everything up to the last '/') and tail (the @@ -212,15 +240,13 @@ def split(p): Either part may be empty.""" p = os.fspath(p) seps = _get_bothseps(p) - d, p = splitdrive(p) + d, r, p = splitroot(p) # set i to index beyond p's last slash i = len(p) while i and p[i-1] not in seps: i -= 1 head, tail = p[:i], p[i:] # now tail has no slashes - # remove trailing slashes from head, unless it's all slashes - head = head.rstrip(seps) or head - return d + head, tail + return d + r + head.rstrip(seps), tail # Split a path in root and extension. @@ -311,10 +337,10 @@ def ismount(path): path = os.fspath(path) seps = _get_bothseps(path) path = abspath(path) - root, rest = splitdrive(path) - if root and root[0] in seps: - return (not rest) or (rest in seps) - if rest and rest in seps: + drive, root, rest = splitroot(path) + if drive and drive[0] in seps: + return not rest + if root and not rest: return True if _getvolumepathname: @@ -525,13 +551,8 @@ def normpath(path): curdir = '.' pardir = '..' path = path.replace(altsep, sep) - prefix, path = splitdrive(path) - - # collapse initial backslashes - if path.startswith(sep): - prefix += sep - path = path.lstrip(sep) - + drive, root, path = splitroot(path) + prefix = drive + root comps = path.split(sep) i = 0 while i < len(comps): @@ -541,7 +562,7 @@ def normpath(path): if i > 0 and comps[i-1] != pardir: del comps[i-1:i+1] i -= 1 - elif i == 0 and prefix.endswith(sep): + elif i == 0 and root: del comps[i] else: i += 1 @@ -765,8 +786,8 @@ def relpath(path, start=None): try: start_abs = abspath(normpath(start)) path_abs = abspath(normpath(path)) - start_drive, start_rest = splitdrive(start_abs) - path_drive, path_rest = splitdrive(path_abs) + start_drive, _, start_rest = splitroot(start_abs) + path_drive, _, path_rest = splitroot(path_abs) if normcase(start_drive) != normcase(path_drive): raise ValueError("path is on mount %r, start on mount %r" % ( path_drive, start_drive)) @@ -816,21 +837,19 @@ def commonpath(paths): curdir = '.' try: - drivesplits = [splitdrive(p.replace(altsep, sep).lower()) for p in paths] - split_paths = [p.split(sep) for d, p in drivesplits] + drivesplits = [splitroot(p.replace(altsep, sep).lower()) for p in paths] + split_paths = [p.split(sep) for d, r, p in drivesplits] - try: - isabs, = set(p[:1] == sep for d, p in drivesplits) - except ValueError: - raise ValueError("Can't mix absolute and relative paths") from None + if len({r for d, r, p in drivesplits}) != 1: + raise ValueError("Can't mix absolute and relative paths") # Check that all drive letters or UNC paths match. The check is made only # now otherwise type errors for mixing strings and bytes would not be # caught. - if len(set(d for d, p in drivesplits)) != 1: + if len({d for d, r, p in drivesplits}) != 1: raise ValueError("Paths don't have the same drive") - drive, path = splitdrive(paths[0].replace(altsep, sep)) + drive, root, path = splitroot(paths[0].replace(altsep, sep)) common = path.split(sep) common = [c for c in common if c and c != curdir] @@ -844,8 +863,7 @@ def commonpath(paths): else: common = common[:len(s1)] - prefix = drive + sep if isabs else drive - return prefix + sep.join(common) + return drive + root + sep.join(common) except (TypeError, AttributeError): genericpath._check_arg_types('commonpath', *paths) raise diff --git a/Lib/pathlib.py b/Lib/pathlib.py index ae7a62f8a4cd..17659bcd3e2d 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -271,19 +271,6 @@ def __reduce__(self): # when pickling related paths. return (self.__class__, tuple(self._parts)) - @classmethod - def _split_root(cls, part): - sep = cls._flavour.sep - rel = cls._flavour.splitdrive(part)[1].lstrip(sep) - anchor = part.removesuffix(rel) - if anchor: - anchor = cls._flavour.normpath(anchor) - drv, root = cls._flavour.splitdrive(anchor) - if drv.startswith(sep): - # UNC paths always have a root. - root = sep - return drv, root, rel - @classmethod def _parse_parts(cls, parts): if not parts: @@ -293,7 +280,10 @@ def _parse_parts(cls, parts): path = cls._flavour.join(*parts) if altsep: path = path.replace(altsep, sep) - drv, root, rel = cls._split_root(path) + drv, root, rel = cls._flavour.splitroot(path) + if drv.startswith(sep): + # pathlib assumes that UNC paths always have a root. + root = sep unfiltered_parsed = [drv + root] + rel.split(sep) parsed = [sys.intern(x) for x in unfiltered_parsed if x and x != '.'] return drv, root, parsed @@ -493,9 +483,9 @@ def with_name(self, name): """Return a new path with the file name changed.""" if not self.name: raise ValueError("%r has an empty name" % (self,)) - drv, root, parts = self._parse_parts((name,)) - if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep] - or drv or root or len(parts) != 1): + f = self._flavour + drv, root, tail = f.splitroot(name) + if drv or root or not tail or f.sep in tail or (f.altsep and f.altsep in tail): raise ValueError("Invalid name %r" % (name)) return self._from_parsed_parts(self._drv, self._root, self._parts[:-1] + [name]) diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 737f8a5c156d..32b5d6e105dd 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -28,7 +28,7 @@ import genericpath from genericpath import * -__all__ = ["normcase","isabs","join","splitdrive","split","splitext", +__all__ = ["normcase","isabs","join","splitdrive","splitroot","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", "getatime","getctime","islink","exists","lexists","isdir","isfile", "ismount", "expanduser","expandvars","normpath","abspath", @@ -135,6 +135,35 @@ def splitdrive(p): return p[:0], p +def splitroot(p): + """Split a pathname into drive, root and tail. On Posix, drive is always + empty; the root may be empty, a single slash, or two slashes. The tail + contains anything after the root. For example: + + splitroot('foo/bar') == ('', '', 'foo/bar') + splitroot('/foo/bar') == ('', '/', 'foo/bar') + splitroot('//foo/bar') == ('', '//', 'foo/bar') + splitroot('///foo/bar') == ('', '/', '//foo/bar') + """ + p = os.fspath(p) + if isinstance(p, bytes): + sep = b'/' + empty = b'' + else: + sep = '/' + empty = '' + if p[:1] != sep: + # Relative path, e.g.: 'foo' + return empty, empty, p + elif p[1:2] != sep or p[2:3] == sep: + # Absolute path, e.g.: '/foo', '///foo', '////foo', etc. + return empty, sep, p[1:] + else: + # Precisely two leading slashes, e.g.: '//foo'. Implementation defined per POSIX, see + # https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 + return empty, p[:2], p[2:] + + # Return the tail (basename) part of a path, same as split(path)[1]. def basename(p): @@ -372,13 +401,7 @@ def normpath(path): dotdot = '..' if path == empty: return dot - initial_slashes = path.startswith(sep) - # POSIX allows one or two initial slashes, but treats three or more - # as single slash. - # (see https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13) - if (initial_slashes and - path.startswith(sep*2) and not path.startswith(sep*3)): - initial_slashes = 2 + _, initial_slashes, path = splitroot(path) comps = path.split(sep) new_comps = [] for comp in comps: @@ -390,9 +413,7 @@ def normpath(path): elif new_comps: new_comps.pop() comps = new_comps - path = sep.join(comps) - if initial_slashes: - path = sep*initial_slashes + path + path = initial_slashes + sep.join(comps) return path or dot else: diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index f56de0be7721..bce38a534a6a 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -98,57 +98,106 @@ def test_splitext(self): tester('ntpath.splitext("c:a/b\\c.d")', ('c:a/b\\c', '.d')) def test_splitdrive(self): - tester('ntpath.splitdrive("c:\\foo\\bar")', - ('c:', '\\foo\\bar')) - tester('ntpath.splitdrive("c:/foo/bar")', - ('c:', '/foo/bar')) + tester("ntpath.splitdrive('')", ('', '')) + tester("ntpath.splitdrive('foo')", ('', 'foo')) + tester("ntpath.splitdrive('foo\\bar')", ('', 'foo\\bar')) + tester("ntpath.splitdrive('foo/bar')", ('', 'foo/bar')) + tester("ntpath.splitdrive('\\')", ('', '\\')) + tester("ntpath.splitdrive('/')", ('', '/')) + tester("ntpath.splitdrive('\\foo\\bar')", ('', '\\foo\\bar')) + tester("ntpath.splitdrive('/foo/bar')", ('', '/foo/bar')) + tester('ntpath.splitdrive("c:foo\\bar")', ('c:', 'foo\\bar')) + tester('ntpath.splitdrive("c:foo/bar")', ('c:', 'foo/bar')) + tester('ntpath.splitdrive("c:\\foo\\bar")', ('c:', '\\foo\\bar')) + tester('ntpath.splitdrive("c:/foo/bar")', ('c:', '/foo/bar')) + tester("ntpath.splitdrive('\\\\')", ('\\\\', '')) + tester("ntpath.splitdrive('//')", ('//', '')) tester('ntpath.splitdrive("\\\\conky\\mountpoint\\foo\\bar")', ('\\\\conky\\mountpoint', '\\foo\\bar')) tester('ntpath.splitdrive("//conky/mountpoint/foo/bar")', ('//conky/mountpoint', '/foo/bar')) - tester('ntpath.splitdrive("\\\\\\conky\\mountpoint\\foo\\bar")', - ('\\\\\\conky', '\\mountpoint\\foo\\bar')) - tester('ntpath.splitdrive("///conky/mountpoint/foo/bar")', - ('///conky', '/mountpoint/foo/bar')) - tester('ntpath.splitdrive("\\\\conky\\\\mountpoint\\foo\\bar")', - ('\\\\conky\\', '\\mountpoint\\foo\\bar')) - tester('ntpath.splitdrive("//conky//mountpoint/foo/bar")', - ('//conky/', '/mountpoint/foo/bar')) - # Issue #19911: UNC part containing U+0130 - self.assertEqual(ntpath.splitdrive('//conky/MOUNTPO?NT/foo/bar'), - ('//conky/MOUNTPO?NT', '/foo/bar')) - # gh-81790: support device namespace, including UNC drives. - tester('ntpath.splitdrive("//?/c:")', ("//?/c:", "")) - tester('ntpath.splitdrive("//?/c:/")', ("//?/c:", "/")) - tester('ntpath.splitdrive("//?/c:/dir")', ("//?/c:", "/dir")) - tester('ntpath.splitdrive("//?/UNC")', ("//?/UNC", "")) - tester('ntpath.splitdrive("//?/UNC/")', ("//?/UNC/", "")) - tester('ntpath.splitdrive("//?/UNC/server/")', ("//?/UNC/server/", "")) - tester('ntpath.splitdrive("//?/UNC/server/share")', ("//?/UNC/server/share", "")) - tester('ntpath.splitdrive("//?/UNC/server/share/dir")', ("//?/UNC/server/share", "/dir")) - tester('ntpath.splitdrive("//?/VOLUME{00000000-0000-0000-0000-000000000000}/spam")', - ('//?/VOLUME{00000000-0000-0000-0000-000000000000}', '/spam')) - tester('ntpath.splitdrive("//?/BootPartition/")', ("//?/BootPartition", "/")) - - tester('ntpath.splitdrive("\\\\?\\c:")', ("\\\\?\\c:", "")) - tester('ntpath.splitdrive("\\\\?\\c:\\")', ("\\\\?\\c:", "\\")) - tester('ntpath.splitdrive("\\\\?\\c:\\dir")', ("\\\\?\\c:", "\\dir")) - tester('ntpath.splitdrive("\\\\?\\UNC")', ("\\\\?\\UNC", "")) - tester('ntpath.splitdrive("\\\\?\\UNC\\")', ("\\\\?\\UNC\\", "")) - tester('ntpath.splitdrive("\\\\?\\UNC\\server\\")', ("\\\\?\\UNC\\server\\", "")) - tester('ntpath.splitdrive("\\\\?\\UNC\\server\\share")', ("\\\\?\\UNC\\server\\share", "")) tester('ntpath.splitdrive("\\\\?\\UNC\\server\\share\\dir")', ("\\\\?\\UNC\\server\\share", "\\dir")) - tester('ntpath.splitdrive("\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}\\spam")', - ('\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}', '\\spam')) - tester('ntpath.splitdrive("\\\\?\\BootPartition\\")', ("\\\\?\\BootPartition", "\\")) + tester('ntpath.splitdrive("//?/UNC/server/share/dir")', + ("//?/UNC/server/share", "/dir")) + + def test_splitroot(self): + tester("ntpath.splitroot('')", ('', '', '')) + tester("ntpath.splitroot('foo')", ('', '', 'foo')) + tester("ntpath.splitroot('foo\\bar')", ('', '', 'foo\\bar')) + tester("ntpath.splitroot('foo/bar')", ('', '', 'foo/bar')) + tester("ntpath.splitroot('\\')", ('', '\\', '')) + tester("ntpath.splitroot('/')", ('', '/', '')) + tester("ntpath.splitroot('\\foo\\bar')", ('', '\\', 'foo\\bar')) + tester("ntpath.splitroot('/foo/bar')", ('', '/', 'foo/bar')) + tester('ntpath.splitroot("c:foo\\bar")', ('c:', '', 'foo\\bar')) + tester('ntpath.splitroot("c:foo/bar")', ('c:', '', 'foo/bar')) + tester('ntpath.splitroot("c:\\foo\\bar")', ('c:', '\\', 'foo\\bar')) + tester('ntpath.splitroot("c:/foo/bar")', ('c:', '/', 'foo/bar')) + + # Redundant slashes are not included in the root. + tester("ntpath.splitroot('c:\\\\a')", ('c:', '\\', '\\a')) + tester("ntpath.splitroot('c:\\\\\\a/b')", ('c:', '\\', '\\\\a/b')) + + # Mixed path separators. + tester("ntpath.splitroot('c:/\\')", ('c:', '/', '\\')) + tester("ntpath.splitroot('c:\\/')", ('c:', '\\', '/')) + tester("ntpath.splitroot('/\\a/b\\/\\')", ('/\\a/b', '\\', '/\\')) + tester("ntpath.splitroot('\\/a\\b/\\/')", ('\\/a\\b', '/', '\\/')) + + # UNC paths. + tester("ntpath.splitroot('\\\\')", ('\\\\', '', '')) + tester("ntpath.splitroot('//')", ('//', '', '')) + tester('ntpath.splitroot("\\\\conky\\mountpoint\\foo\\bar")', + ('\\\\conky\\mountpoint', '\\', 'foo\\bar')) + tester('ntpath.splitroot("//conky/mountpoint/foo/bar")', + ('//conky/mountpoint', '/', 'foo/bar')) + tester('ntpath.splitroot("\\\\\\conky\\mountpoint\\foo\\bar")', + ('\\\\\\conky', '\\', 'mountpoint\\foo\\bar')) + tester('ntpath.splitroot("///conky/mountpoint/foo/bar")', + ('///conky', '/', 'mountpoint/foo/bar')) + tester('ntpath.splitroot("\\\\conky\\\\mountpoint\\foo\\bar")', + ('\\\\conky\\', '\\', 'mountpoint\\foo\\bar')) + tester('ntpath.splitroot("//conky//mountpoint/foo/bar")', + ('//conky/', '/', 'mountpoint/foo/bar')) + + # Issue #19911: UNC part containing U+0130 + self.assertEqual(ntpath.splitroot('//conky/MOUNTPO?NT/foo/bar'), + ('//conky/MOUNTPO?NT', '/', 'foo/bar')) + + # gh-81790: support device namespace, including UNC drives. + tester('ntpath.splitroot("//?/c:")', ("//?/c:", "", "")) + tester('ntpath.splitroot("//?/c:/")', ("//?/c:", "/", "")) + tester('ntpath.splitroot("//?/c:/dir")', ("//?/c:", "/", "dir")) + tester('ntpath.splitroot("//?/UNC")', ("//?/UNC", "", "")) + tester('ntpath.splitroot("//?/UNC/")', ("//?/UNC/", "", "")) + tester('ntpath.splitroot("//?/UNC/server/")', ("//?/UNC/server/", "", "")) + tester('ntpath.splitroot("//?/UNC/server/share")', ("//?/UNC/server/share", "", "")) + tester('ntpath.splitroot("//?/UNC/server/share/dir")', ("//?/UNC/server/share", "/", "dir")) + tester('ntpath.splitroot("//?/VOLUME{00000000-0000-0000-0000-000000000000}/spam")', + ('//?/VOLUME{00000000-0000-0000-0000-000000000000}', '/', 'spam')) + tester('ntpath.splitroot("//?/BootPartition/")', ("//?/BootPartition", "/", "")) + + tester('ntpath.splitroot("\\\\?\\c:")', ("\\\\?\\c:", "", "")) + tester('ntpath.splitroot("\\\\?\\c:\\")', ("\\\\?\\c:", "\\", "")) + tester('ntpath.splitroot("\\\\?\\c:\\dir")', ("\\\\?\\c:", "\\", "dir")) + tester('ntpath.splitroot("\\\\?\\UNC")', ("\\\\?\\UNC", "", "")) + tester('ntpath.splitroot("\\\\?\\UNC\\")', ("\\\\?\\UNC\\", "", "")) + tester('ntpath.splitroot("\\\\?\\UNC\\server\\")', ("\\\\?\\UNC\\server\\", "", "")) + tester('ntpath.splitroot("\\\\?\\UNC\\server\\share")', + ("\\\\?\\UNC\\server\\share", "", "")) + tester('ntpath.splitroot("\\\\?\\UNC\\server\\share\\dir")', + ("\\\\?\\UNC\\server\\share", "\\", "dir")) + tester('ntpath.splitroot("\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}\\spam")', + ('\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}', '\\', 'spam')) + tester('ntpath.splitroot("\\\\?\\BootPartition\\")', ("\\\\?\\BootPartition", "\\", "")) # gh-96290: support partial/invalid UNC drives - tester('ntpath.splitdrive("//")', ("//", "")) # empty server & missing share - tester('ntpath.splitdrive("///")', ("///", "")) # empty server & empty share - tester('ntpath.splitdrive("///y")', ("///y", "")) # empty server & non-empty share - tester('ntpath.splitdrive("//x")', ("//x", "")) # non-empty server & missing share - tester('ntpath.splitdrive("//x/")', ("//x/", "")) # non-empty server & empty share + tester('ntpath.splitroot("//")', ("//", "", "")) # empty server & missing share + tester('ntpath.splitroot("///")', ("///", "", "")) # empty server & empty share + tester('ntpath.splitroot("///y")', ("///y", "", "")) # empty server & non-empty share + tester('ntpath.splitroot("//x")', ("//x", "", "")) # non-empty server & missing share + tester('ntpath.splitroot("//x/")', ("//x/", "", "")) # non-empty server & empty share def test_split(self): tester('ntpath.split("c:\\foo\\bar")', ('c:\\foo', 'bar')) @@ -930,6 +979,9 @@ def test_path_splitext(self): def test_path_splitdrive(self): self._check_function(self.path.splitdrive) + def test_path_splitroot(self): + self._check_function(self.path.splitroot) + def test_path_basename(self): self._check_function(self.path.basename) diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 1fe242b7f6ab..a596795b44f0 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -80,26 +80,6 @@ def test_parse_parts(self): check(['c:\\a'], ('', '', ['c:\\a'])) check(['\\a'], ('', '', ['\\a'])) - def test_splitroot(self): - f = self.cls._split_root - self.assertEqual(f(''), ('', '', '')) - self.assertEqual(f('a'), ('', '', 'a')) - self.assertEqual(f('a/b'), ('', '', 'a/b')) - self.assertEqual(f('a/b/'), ('', '', 'a/b/')) - self.assertEqual(f('/a'), ('', '/', 'a')) - self.assertEqual(f('/a/b'), ('', '/', 'a/b')) - self.assertEqual(f('/a/b/'), ('', '/', 'a/b/')) - # The root is collapsed when there are redundant slashes - # except when there are exactly two leading slashes, which - # is a special case in POSIX. - self.assertEqual(f('//a'), ('', '//', 'a')) - self.assertEqual(f('///a'), ('', '/', 'a')) - self.assertEqual(f('///a/b'), ('', '/', 'a/b')) - # Paths which look like NT paths aren't treated specially. - self.assertEqual(f('c:/a/b'), ('', '', 'c:/a/b')) - self.assertEqual(f('\\/a/b'), ('', '', '\\/a/b')) - self.assertEqual(f('\\a\\b'), ('', '', '\\a\\b')) - class NTFlavourTest(_BaseFlavourTest, unittest.TestCase): cls = pathlib.PureWindowsPath @@ -143,23 +123,6 @@ def test_parse_parts(self): check(['c:/a/b', 'c:x/y'], ('c:', '\\', ['c:\\', 'a', 'b', 'x', 'y'])) check(['c:/a/b', 'c:/x/y'], ('c:', '\\', ['c:\\', 'x', 'y'])) - def test_splitroot(self): - f = self.cls._split_root - self.assertEqual(f(''), ('', '', '')) - self.assertEqual(f('a'), ('', '', 'a')) - self.assertEqual(f('a\\b'), ('', '', 'a\\b')) - self.assertEqual(f('\\a'), ('', '\\', 'a')) - self.assertEqual(f('\\a\\b'), ('', '\\', 'a\\b')) - self.assertEqual(f('c:a\\b'), ('c:', '', 'a\\b')) - self.assertEqual(f('c:\\a\\b'), ('c:', '\\', 'a\\b')) - # Redundant slashes in the root are collapsed. - self.assertEqual(f('c:\\\\a'), ('c:', '\\', 'a')) - self.assertEqual(f('c:\\\\\\a/b'), ('c:', '\\', 'a/b')) - # Valid UNC paths. - self.assertEqual(f('\\\\a\\b'), ('\\\\a\\b', '\\', '')) - self.assertEqual(f('\\\\a\\b\\'), ('\\\\a\\b', '\\', '')) - self.assertEqual(f('\\\\a\\b\\c\\d'), ('\\\\a\\b', '\\', 'c\\d')) - # # Tests for the pure classes. diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 6c1c0f5577b7..9be4640f970a 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -115,6 +115,32 @@ def test_splitext(self): self.splitextTest("........", "........", "") self.splitextTest("", "", "") + def test_splitroot(self): + f = posixpath.splitroot + self.assertEqual(f(''), ('', '', '')) + self.assertEqual(f('a'), ('', '', 'a')) + self.assertEqual(f('a/b'), ('', '', 'a/b')) + self.assertEqual(f('a/b/'), ('', '', 'a/b/')) + self.assertEqual(f('/a'), ('', '/', 'a')) + self.assertEqual(f('/a/b'), ('', '/', 'a/b')) + self.assertEqual(f('/a/b/'), ('', '/', 'a/b/')) + # The root is collapsed when there are redundant slashes + # except when there are exactly two leading slashes, which + # is a special case in POSIX. + self.assertEqual(f('//a'), ('', '//', 'a')) + self.assertEqual(f('///a'), ('', '/', '//a')) + self.assertEqual(f('///a/b'), ('', '/', '//a/b')) + # Paths which look like NT paths aren't treated specially. + self.assertEqual(f('c:/a/b'), ('', '', 'c:/a/b')) + self.assertEqual(f('\\/a/b'), ('', '', '\\/a/b')) + self.assertEqual(f('\\a\\b'), ('', '', '\\a\\b')) + # Byte paths are supported + self.assertEqual(f(b''), (b'', b'', b'')) + self.assertEqual(f(b'a'), (b'', b'', b'a')) + self.assertEqual(f(b'/a'), (b'', b'/', b'a')) + self.assertEqual(f(b'//a'), (b'', b'//', b'a')) + self.assertEqual(f(b'///a'), (b'', b'/', b'//a')) + def test_isabs(self): self.assertIs(posixpath.isabs(""), False) self.assertIs(posixpath.isabs("/"), True) @@ -752,6 +778,9 @@ def test_path_splitext(self): def test_path_splitdrive(self): self.assertPathEqual(self.path.splitdrive) + def test_path_splitroot(self): + self.assertPathEqual(self.path.splitroot) + def test_path_basename(self): self.assertPathEqual(self.path.basename) diff --git a/Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst b/Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst new file mode 100644 index 000000000000..2082361c41d6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-12-21-22-20.gh-issue-101000.wz4Xgc.rst @@ -0,0 +1,3 @@ +Add :func:`os.path.splitroot()`, which splits a path into a 3-item tuple +``(drive, root, tail)``. This new function is used by :mod:`pathlib` to +improve the performance of path construction by up to a third. From webhook-mailer at python.org Fri Jan 27 02:56:54 2023 From: webhook-mailer at python.org (rhettinger) Date: Fri, 27 Jan 2023 07:56:54 -0000 Subject: [Python-checkins] Speed-up and improve accuracy with Rump Algorithms (3.1) and (5.10) (GH-101366) Message-ID: https://github.com/python/cpython/commit/7956e0c30001cc0940caa66fab4d72455c865b3a commit: 7956e0c30001cc0940caa66fab4d72455c865b3a branch: main author: Raymond Hettinger committer: rhettinger date: 2023-01-27T01:56:19-06:00 summary: Speed-up and improve accuracy with Rump Algorithms (3.1) and (5.10) (GH-101366) files: M Modules/mathmodule.c diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 69907ea04ec8..2da50bbd54b5 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2851,17 +2851,17 @@ typedef struct{ double hi; double lo; } DoubleLength; static inline DoubleLength twosum(double a, double b) { - double s = a + b; - double ap = s - b; - double bp = s - a; - double da = a - ap; - double db = b - bp; - double t = da + db; - return (DoubleLength) {s, t}; + // Rump Algorithm 3.1 Error-free transformation of the sum + double x = a + b; + double z = x - a; + double y = (a - (x - z)) + (b - z); + return (DoubleLength) {x, y}; } static inline DoubleLength dl_split(double x) { + // Rump Algorithm 3.2 Error-free splitting of a floating point number + // Dekker (5.5) and (5.6). double t = x * 134217729.0; // Veltkamp constant = 2.0 ** 27 + 1 double hi = t - (t - x); double lo = x - hi; @@ -2871,7 +2871,7 @@ dl_split(double x) { static inline DoubleLength dl_mul(double x, double y) { - /* Dekker mul12(). Section (5.12) */ + // Dekker (5.12) and mul12() DoubleLength xx = dl_split(x); DoubleLength yy = dl_split(y); double p = xx.hi * yy.hi; @@ -2881,24 +2881,19 @@ dl_mul(double x, double y) return (DoubleLength) {z, zz}; } -typedef struct{ double hi; double lo; double tiny; } TripleLength; +typedef struct { double hi; double lo; double tiny; } TripleLength; static const TripleLength tl_zero = {0.0, 0.0, 0.0}; static inline TripleLength -tl_add(TripleLength total, double x) +tl_fma(TripleLength total, double x, double y) { - DoubleLength s = twosum(x, total.hi); - DoubleLength t = twosum(s.lo, total.lo); - return (TripleLength) {s.hi, t.hi, t.lo + total.tiny}; -} - -static inline TripleLength -tl_fma(TripleLength total, double p, double q) -{ - DoubleLength product = dl_mul(p, q); - total = tl_add(total, product.hi); - return tl_add(total, product.lo); + // Rump Algorithm 5.10 with K=3 and using SumKVert + DoubleLength pr = dl_mul(x, y); + DoubleLength sm = twosum(total.hi, pr.hi); + DoubleLength r1 = twosum(total.lo, pr.lo); + DoubleLength r2 = twosum(r1.hi, sm.lo); + return (TripleLength) {sm.hi, r2.hi, total.tiny + r1.lo + r2.lo}; } static inline double From webhook-mailer at python.org Fri Jan 27 09:45:15 2023 From: webhook-mailer at python.org (zooba) Date: Fri, 27 Jan 2023 14:45:15 -0000 Subject: [Python-checkins] gh-77532: Minor tweaks to allow compiling with PlatformToolset=ClangCL on Windows (GH-101352) Message-ID: https://github.com/python/cpython/commit/737d367b1f4bad76914173a64d6bbe19a984cd5f commit: 737d367b1f4bad76914173a64d6bbe19a984cd5f branch: main author: Steve Dower committer: zooba date: 2023-01-27T14:45:08Z summary: gh-77532: Minor tweaks to allow compiling with PlatformToolset=ClangCL on Windows (GH-101352) To use this, ensure that clang support was selected in Visual Studio Installer, then set the PlatformToolset environment variable to "ClangCL" and build as normal from the command line. It remains unsupported, but at least is possible now for experimentation. files: A Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst M Include/cpython/pytime.h M Include/internal/pycore_tracemalloc.h M Modules/_decimal/libmpdec/mpdecimal.c M PC/launcher2.c M PC/pyconfig.h M PCbuild/pyproject.props diff --git a/Include/cpython/pytime.h b/Include/cpython/pytime.h index e64f3b13e75c..16d88d191e9e 100644 --- a/Include/cpython/pytime.h +++ b/Include/cpython/pytime.h @@ -53,6 +53,10 @@ functions and constants extern "C" { #endif +#ifdef __clang__ +struct timeval; +#endif + /* _PyTime_t: Python timestamp with subsecond precision. It can be used to store a duration, and so indirectly a date (related to another date, like UNIX epoch). */ diff --git a/Include/internal/pycore_tracemalloc.h b/Include/internal/pycore_tracemalloc.h index 08d7d1096c78..d086adc61c31 100644 --- a/Include/internal/pycore_tracemalloc.h +++ b/Include/internal/pycore_tracemalloc.h @@ -36,11 +36,13 @@ struct _PyTraceMalloc_Config { /* Pack the frame_t structure to reduce the memory footprint on 64-bit architectures: 12 bytes instead of 16. */ +#if defined(_MSC_VER) +#pragma pack(push, 4) +#endif + struct #ifdef __GNUC__ __attribute__((packed)) -#elif defined(_MSC_VER) -#pragma pack(push, 4) #endif tracemalloc_frame { /* filename cannot be NULL: "" is used if the Python frame diff --git a/Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst b/Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst new file mode 100644 index 000000000000..5a746dca2e7d --- /dev/null +++ b/Misc/NEWS.d/next/Build/2023-01-26-19-02-11.gh-issue-77532.cXD8bg.rst @@ -0,0 +1 @@ +Minor fixes to allow building with ``PlatformToolset=ClangCL`` on Windows. diff --git a/Modules/_decimal/libmpdec/mpdecimal.c b/Modules/_decimal/libmpdec/mpdecimal.c index f1626df46ed4..959934bda7a4 100644 --- a/Modules/_decimal/libmpdec/mpdecimal.c +++ b/Modules/_decimal/libmpdec/mpdecimal.c @@ -76,6 +76,12 @@ #endif #endif +/* ClangCL claims to support 128-bit int, but doesn't */ +#if defined(__SIZEOF_INT128__) && defined(__clang__) && defined(_MSC_VER) +#undef __SIZEOF_INT128__ +#endif + + #define MPD_NEWTONDIV_CUTOFF 1024L diff --git a/PC/launcher2.c b/PC/launcher2.c index 4c77ec0be439..2052a2ffeb57 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -465,10 +465,14 @@ dumpSearchInfo(SearchInfo *search) return; } -#define DEBUGNAME(s) L"SearchInfo." ## s -#define DEBUG(s) debug(DEBUGNAME(#s) L": %s\n", (search->s) ? (search->s) : L"(null)") -#define DEBUG_2(s, sl) _debugStringAndLength((search->s), (search->sl), DEBUGNAME(#s)) -#define DEBUG_BOOL(s) debug(DEBUGNAME(#s) L": %s\n", (search->s) ? L"True" : L"False") +#ifdef __clang__ +#define DEBUGNAME(s) L # s +#else +#define DEBUGNAME(s) # s +#endif +#define DEBUG(s) debug(L"SearchInfo." DEBUGNAME(s) L": %s\n", (search->s) ? (search->s) : L"(null)") +#define DEBUG_2(s, sl) _debugStringAndLength((search->s), (search->sl), L"SearchInfo." DEBUGNAME(s)) +#define DEBUG_BOOL(s) debug(L"SearchInfo." DEBUGNAME(s) L": %s\n", (search->s) ? L"True" : L"False") DEBUG(originalCmdLine); DEBUG(restOfCmdLine); DEBUG(executablePath); diff --git a/PC/pyconfig.h b/PC/pyconfig.h index 1d8408b363a6..f5166a1506c9 100644 --- a/PC/pyconfig.h +++ b/PC/pyconfig.h @@ -76,30 +76,26 @@ WIN32 is still required for the locale module. /* Compiler specific defines */ /* ------------------------------------------------------------------------*/ -/* Microsoft C defines _MSC_VER */ +/* Microsoft C defines _MSC_VER, as does clang-cl.exe */ #ifdef _MSC_VER /* We want COMPILER to expand to a string containing _MSC_VER's *value*. * This is horridly tricky, because the stringization operator only works * on macro arguments, and doesn't evaluate macros passed *as* arguments. - * Attempts simpler than the following appear doomed to produce "_MSC_VER" - * literally in the string. */ #define _Py_PASTE_VERSION(SUFFIX) \ ("[MSC v." _Py_STRINGIZE(_MSC_VER) " " SUFFIX "]") /* e.g., this produces, after compile-time string catenation, - * ("[MSC v.1200 32 bit (Intel)]") + * ("[MSC v.1900 64 bit (Intel)]") * * _Py_STRINGIZE(_MSC_VER) expands to - * _Py_STRINGIZE1((_MSC_VER)) expands to - * _Py_STRINGIZE2(_MSC_VER) but as this call is the result of token-pasting - * it's scanned again for macros and so further expands to (under MSVC 6) - * _Py_STRINGIZE2(1200) which then expands to - * "1200" + * _Py_STRINGIZE1(_MSC_VER) and this second macro call is scanned + * again for macros and so further expands to + * _Py_STRINGIZE1(1900) which then expands to + * "1900" */ -#define _Py_STRINGIZE(X) _Py_STRINGIZE1((X)) -#define _Py_STRINGIZE1(X) _Py_STRINGIZE2 ## X -#define _Py_STRINGIZE2(X) #X +#define _Py_STRINGIZE(X) _Py_STRINGIZE1(X) +#define _Py_STRINGIZE1(X) #X /* MSVC defines _WINxx to differentiate the windows platform types @@ -122,13 +118,16 @@ WIN32 is still required for the locale module. */ #ifdef MS_WIN64 #if defined(_M_X64) || defined(_M_AMD64) -#if defined(__INTEL_COMPILER) +#if defined(__clang__) +#define COMPILER ("[Clang " __clang_version__ "] 64 bit (AMD64) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") +#define PY_SUPPORT_TIER 0 +#elif defined(__INTEL_COMPILER) #define COMPILER ("[ICC v." _Py_STRINGIZE(__INTEL_COMPILER) " 64 bit (amd64) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") #define PY_SUPPORT_TIER 0 #else #define COMPILER _Py_PASTE_VERSION("64 bit (AMD64)") #define PY_SUPPORT_TIER 1 -#endif /* __INTEL_COMPILER */ +#endif /* __clang__ */ #define PYD_PLATFORM_TAG "win_amd64" #elif defined(_M_ARM64) #define COMPILER _Py_PASTE_VERSION("64 bit (ARM64)") @@ -181,13 +180,16 @@ typedef _W64 int Py_ssize_t; #if defined(MS_WIN32) && !defined(MS_WIN64) #if defined(_M_IX86) -#if defined(__INTEL_COMPILER) +#if defined(__clang__) +#define COMPILER ("[Clang " __clang_version__ "] 32 bit (Intel) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") +#define PY_SUPPORT_TIER 0 +#elif defined(__INTEL_COMPILER) #define COMPILER ("[ICC v." _Py_STRINGIZE(__INTEL_COMPILER) " 32 bit (Intel) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") #define PY_SUPPORT_TIER 0 #else #define COMPILER _Py_PASTE_VERSION("32 bit (Intel)") #define PY_SUPPORT_TIER 1 -#endif /* __INTEL_COMPILER */ +#endif /* __clang__ */ #define PYD_PLATFORM_TAG "win32" #elif defined(_M_ARM) #define COMPILER _Py_PASTE_VERSION("32 bit (ARM)") diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props index e398b333572e..92c7849d3bcf 100644 --- a/PCbuild/pyproject.props +++ b/PCbuild/pyproject.props @@ -25,7 +25,8 @@ <_DebugPreprocessorDefinition>NDEBUG; <_DebugPreprocessorDefinition Condition="$(Configuration) == 'Debug'">_DEBUG; <_PlatformPreprocessorDefinition>_WIN32; - <_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64'">_WIN64;_M_X64; + <_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64'">_WIN64; + <_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64' and $(PlatformToolset) != 'ClangCL'">_M_X64;$(_PlatformPreprocessorDefinition) <_Py3NamePreprocessorDefinition>PY3_DLLNAME=L"$(Py3DllName)"; @@ -45,8 +46,10 @@ true true $(EnableControlFlowGuard) - /utf-8 %(AdditionalOptions) true + /utf-8 %(AdditionalOptions) + -Wno-deprecated-non-prototype -Wno-unused-label -Wno-pointer-sign -Wno-incompatible-pointer-types-discards-qualifiers -Wno-unused-function %(AdditionalOptions) + -flto %(AdditionalOptions) OnlyExplicitInline From webhook-mailer at python.org Fri Jan 27 09:50:16 2023 From: webhook-mailer at python.org (vsajip) Date: Fri, 27 Jan 2023 14:50:16 -0000 Subject: [Python-checkins] [doc] Add some notices to logging configuration documentation. (GH-101373) Message-ID: https://github.com/python/cpython/commit/d083df39fa61dc27b3c22a7bc06a71e95b718fa3 commit: d083df39fa61dc27b3c22a7bc06a71e95b718fa3 branch: main author: Vinay Sajip committer: vsajip date: 2023-01-27T14:50:09Z summary: [doc] Add some notices to logging configuration documentation. (GH-101373) files: M Doc/library/logging.config.rst diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index 9b82c1f75e3f..b4d0da1421dc 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -525,6 +525,11 @@ returned by the call:: my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42) +.. warning:: The values for keys such as ``bar``, ``spam`` and ``answer`` in + the above example should not be configuration dictionaries or references such + as ``cfg://foo`` or ``ext://bar``, because they will not be processed by the + configuration machinery, but passed to the callable as-is. + The key ``'()'`` has been used as the special key because it is not a valid keyword parameter name, and so will not clash with the names of the keyword arguments used in the call. The ``'()'`` also serves as a @@ -553,6 +558,11 @@ following configuration:: the returned formatter will have attribute ``foo`` set to ``'bar'`` and attribute ``baz`` set to ``'bozz'``. +.. warning:: The values for attributes such as ``foo`` and ``baz`` in + the above example should not be configuration dictionaries or references such + as ``cfg://foo`` or ``ext://bar``, because they will not be processed by the + configuration machinery, but set as attribute values as-is. + .. _logging-config-dict-externalobj: From webhook-mailer at python.org Fri Jan 27 12:45:28 2023 From: webhook-mailer at python.org (vsajip) Date: Fri, 27 Jan 2023 17:45:28 -0000 Subject: [Python-checkins] [3.10] [doc] Add some notices to logging configuration documentation. (GH-101373) (GH-101375) Message-ID: https://github.com/python/cpython/commit/0814a809d78d9a426bfa90838c6c13629ecd4fc1 commit: 0814a809d78d9a426bfa90838c6c13629ecd4fc1 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: vsajip date: 2023-01-27T17:45:22Z summary: [3.10] [doc] Add some notices to logging configuration documentation. (GH-101373) (GH-101375) Co-authored-by: Vinay Sajip files: M Doc/library/logging.config.rst diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index 88a835382300..0385e3013985 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -519,6 +519,11 @@ returned by the call:: my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42) +.. warning:: The values for keys such as ``bar``, ``spam`` and ``answer`` in + the above example should not be configuration dictionaries or references such + as ``cfg://foo`` or ``ext://bar``, because they will not be processed by the + configuration machinery, but passed to the callable as-is. + The key ``'()'`` has been used as the special key because it is not a valid keyword parameter name, and so will not clash with the names of the keyword arguments used in the call. The ``'()'`` also serves as a @@ -543,6 +548,11 @@ following configuration:: the returned formatter will have attribute ``foo`` set to ``'bar'`` and attribute ``baz`` set to ``'bozz'``. +.. warning:: The values for attributes such as ``foo`` and ``baz`` in + the above example should not be configuration dictionaries or references such + as ``cfg://foo`` or ``ext://bar``, because they will not be processed by the + configuration machinery, but set as attribute values as-is. + .. _logging-config-dict-externalobj: From webhook-mailer at python.org Fri Jan 27 12:46:05 2023 From: webhook-mailer at python.org (vsajip) Date: Fri, 27 Jan 2023 17:46:05 -0000 Subject: [Python-checkins] [3.11] [doc] Add some notices to logging configuration documentation. (GH-101373) (GH-101376) Message-ID: https://github.com/python/cpython/commit/1016909a6d6d3b3abb689a4c2dac808217695e79 commit: 1016909a6d6d3b3abb689a4c2dac808217695e79 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: vsajip date: 2023-01-27T17:45:59Z summary: [3.11] [doc] Add some notices to logging configuration documentation. (GH-101373) (GH-101376) Co-authored-by: Vinay Sajip files: M Doc/library/logging.config.rst diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index c36ad497f708..671f3c89ee83 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -525,6 +525,11 @@ returned by the call:: my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42) +.. warning:: The values for keys such as ``bar``, ``spam`` and ``answer`` in + the above example should not be configuration dictionaries or references such + as ``cfg://foo`` or ``ext://bar``, because they will not be processed by the + configuration machinery, but passed to the callable as-is. + The key ``'()'`` has been used as the special key because it is not a valid keyword parameter name, and so will not clash with the names of the keyword arguments used in the call. The ``'()'`` also serves as a @@ -553,6 +558,11 @@ following configuration:: the returned formatter will have attribute ``foo`` set to ``'bar'`` and attribute ``baz`` set to ``'bozz'``. +.. warning:: The values for attributes such as ``foo`` and ``baz`` in + the above example should not be configuration dictionaries or references such + as ``cfg://foo`` or ``ext://bar``, because they will not be processed by the + configuration machinery, but set as attribute values as-is. + .. _logging-config-dict-externalobj: From webhook-mailer at python.org Fri Jan 27 14:01:37 2023 From: webhook-mailer at python.org (vsajip) Date: Fri, 27 Jan 2023 19:01:37 -0000 Subject: [Python-checkins] [doc] Add a section on logging handler configuration order. (GH-101380) Message-ID: https://github.com/python/cpython/commit/b5c4d6064cc2ae7042d2dc7b533d74f6c4176a38 commit: b5c4d6064cc2ae7042d2dc7b533d74f6c4176a38 branch: main author: Vinay Sajip committer: vsajip date: 2023-01-27T19:01:30Z summary: [doc] Add a section on logging handler configuration order. (GH-101380) files: M Doc/library/logging.config.rst diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index b4d0da1421dc..2daf2422ebd5 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -564,6 +564,29 @@ attribute ``baz`` set to ``'bozz'``. configuration machinery, but set as attribute values as-is. +.. _handler-config-dict-order: + +Handler configuration order +""""""""""""""""""""""""""" + +Handlers are configured in alphabetical order of their keys, and a configured +handler replaces the configuration dictionary in (a working copy of) the +``handlers`` dictionary in the schema. If you use a construct such as +``cfg://handlers.foo``, then initially ``handlers['foo']`` points to the +configuration dictionary for the handler named ``foo``, and later (once that +handler has been configured) it points to the configured handler instance. +Thus, ``cfg://handlers.foo`` could resolve to either a dictionary or a handler +instance. In general, it is wise to name handlers in a way such that dependent +handlers are configured _after_ any handlers they depend on; that allows +something like ``cfg://handlers.foo`` to be used in configuring a handler that +depends on handler ``foo``. If that dependent handler were named ``bar``, +problems would result, because the configuration of ``bar`` would be attempted +before that of ``foo``, and ``foo`` would not yet have been configured. +However, if the dependent handler were named ``foobar``, it would be configured +after ``foo``, with the result that ``cfg://handlers.foo`` would resolve to +configured handler ``foo``, and not its configuration dictionary. + + .. _logging-config-dict-externalobj: Access to external objects From webhook-mailer at python.org Fri Jan 27 14:12:07 2023 From: webhook-mailer at python.org (vsajip) Date: Fri, 27 Jan 2023 19:12:07 -0000 Subject: [Python-checkins] [3.10] [doc] Add a section on logging handler configuration order. (GH-101380) (GH-101381) Message-ID: https://github.com/python/cpython/commit/95f75ca6d8189bd57b2a56d5560dcf9c668c8bda commit: 95f75ca6d8189bd57b2a56d5560dcf9c668c8bda branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: vsajip date: 2023-01-27T19:12:00Z summary: [3.10] [doc] Add a section on logging handler configuration order. (GH-101380) (GH-101381) Co-authored-by: Vinay Sajip files: M Doc/library/logging.config.rst diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index 0385e3013985..be5a76681904 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -554,6 +554,29 @@ attribute ``baz`` set to ``'bozz'``. configuration machinery, but set as attribute values as-is. +.. _handler-config-dict-order: + +Handler configuration order +""""""""""""""""""""""""""" + +Handlers are configured in alphabetical order of their keys, and a configured +handler replaces the configuration dictionary in (a working copy of) the +``handlers`` dictionary in the schema. If you use a construct such as +``cfg://handlers.foo``, then initially ``handlers['foo']`` points to the +configuration dictionary for the handler named ``foo``, and later (once that +handler has been configured) it points to the configured handler instance. +Thus, ``cfg://handlers.foo`` could resolve to either a dictionary or a handler +instance. In general, it is wise to name handlers in a way such that dependent +handlers are configured _after_ any handlers they depend on; that allows +something like ``cfg://handlers.foo`` to be used in configuring a handler that +depends on handler ``foo``. If that dependent handler were named ``bar``, +problems would result, because the configuration of ``bar`` would be attempted +before that of ``foo``, and ``foo`` would not yet have been configured. +However, if the dependent handler were named ``foobar``, it would be configured +after ``foo``, with the result that ``cfg://handlers.foo`` would resolve to +configured handler ``foo``, and not its configuration dictionary. + + .. _logging-config-dict-externalobj: Access to external objects From webhook-mailer at python.org Fri Jan 27 14:12:23 2023 From: webhook-mailer at python.org (vsajip) Date: Fri, 27 Jan 2023 19:12:23 -0000 Subject: [Python-checkins] [3.11] [doc] Add a section on logging handler configuration order. (GH-101380) (GH-101382) Message-ID: https://github.com/python/cpython/commit/88a1e6db0ff7856191a5d63d3d26a896f3ae5885 commit: 88a1e6db0ff7856191a5d63d3d26a896f3ae5885 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: vsajip date: 2023-01-27T19:12:18Z summary: [3.11] [doc] Add a section on logging handler configuration order. (GH-101380) (GH-101382) Co-authored-by: Vinay Sajip files: M Doc/library/logging.config.rst diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index 671f3c89ee83..93e452962218 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -564,6 +564,29 @@ attribute ``baz`` set to ``'bozz'``. configuration machinery, but set as attribute values as-is. +.. _handler-config-dict-order: + +Handler configuration order +""""""""""""""""""""""""""" + +Handlers are configured in alphabetical order of their keys, and a configured +handler replaces the configuration dictionary in (a working copy of) the +``handlers`` dictionary in the schema. If you use a construct such as +``cfg://handlers.foo``, then initially ``handlers['foo']`` points to the +configuration dictionary for the handler named ``foo``, and later (once that +handler has been configured) it points to the configured handler instance. +Thus, ``cfg://handlers.foo`` could resolve to either a dictionary or a handler +instance. In general, it is wise to name handlers in a way such that dependent +handlers are configured _after_ any handlers they depend on; that allows +something like ``cfg://handlers.foo`` to be used in configuring a handler that +depends on handler ``foo``. If that dependent handler were named ``bar``, +problems would result, because the configuration of ``bar`` would be attempted +before that of ``foo``, and ``foo`` would not yet have been configured. +However, if the dependent handler were named ``foobar``, it would be configured +after ``foo``, with the result that ``cfg://handlers.foo`` would resolve to +configured handler ``foo``, and not its configuration dictionary. + + .. _logging-config-dict-externalobj: Access to external objects From webhook-mailer at python.org Fri Jan 27 21:08:14 2023 From: webhook-mailer at python.org (corona10) Date: Sat, 28 Jan 2023 02:08:14 -0000 Subject: [Python-checkins] gh-101341: Remove unncessary enum._power_of_two function (gh-101342) Message-ID: https://github.com/python/cpython/commit/8cef9c0f92720f6810be1c74e00f611acb4b8b1e commit: 8cef9c0f92720f6810be1c74e00f611acb4b8b1e branch: main author: Dong-hee Na committer: corona10 date: 2023-01-28T11:08:08+09:00 summary: gh-101341: Remove unncessary enum._power_of_two function (gh-101342) files: M Lib/enum.py diff --git a/Lib/enum.py b/Lib/enum.py index 4658393d756e..adb61519abe9 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -1565,11 +1565,6 @@ def unique(enumeration): (enumeration, alias_details)) return enumeration -def _power_of_two(value): - if value < 1: - return False - return value == 2 ** _high_bit(value) - def _dataclass_repr(self): dcf = self.__dataclass_fields__ return ', '.join( From webhook-mailer at python.org Fri Jan 27 21:35:33 2023 From: webhook-mailer at python.org (gpshead) Date: Sat, 28 Jan 2023 02:35:33 -0000 Subject: [Python-checkins] gh-39615: Add warnings.warn() skip_file_prefixes support (#100840) Message-ID: https://github.com/python/cpython/commit/052f53d65d9f65c7c3223a383857ad07a182c2d7 commit: 052f53d65d9f65c7c3223a383857ad07a182c2d7 branch: main author: Gregory P. Smith committer: gpshead date: 2023-01-27T18:35:14-08:00 summary: gh-39615: Add warnings.warn() skip_file_prefixes support (#100840) `warnings.warn()` gains the ability to skip stack frames based on code filename prefix rather than only a numeric `stacklevel=` via a new `skip_file_prefixes=` keyword argument. files: A Lib/test/test_warnings/data/package_helper.py A Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst M Doc/library/warnings.rst M Include/internal/pycore_global_objects_fini_generated.h M Include/internal/pycore_global_strings.h M Include/internal/pycore_runtime_init_generated.h M Include/internal/pycore_unicodeobject_generated.h M Lib/test/test_warnings/__init__.py M Lib/test/test_warnings/data/stacklevel.py M Lib/warnings.py M Python/_warnings.c M Python/clinic/_warnings.c.h diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst index 28579ce8df4a..884de08eab1b 100644 --- a/Doc/library/warnings.rst +++ b/Doc/library/warnings.rst @@ -396,7 +396,7 @@ Available Functions ------------------- -.. function:: warn(message, category=None, stacklevel=1, source=None) +.. function:: warn(message, category=None, stacklevel=1, source=None, \*, skip_file_prefixes=None) Issue a warning, or maybe ignore it or raise an exception. The *category* argument, if given, must be a :ref:`warning category class `; it @@ -407,12 +407,39 @@ Available Functions :ref:`warnings filter `. The *stacklevel* argument can be used by wrapper functions written in Python, like this:: - def deprecation(message): + def deprecated_api(message): warnings.warn(message, DeprecationWarning, stacklevel=2) - This makes the warning refer to :func:`deprecation`'s caller, rather than to the - source of :func:`deprecation` itself (since the latter would defeat the purpose - of the warning message). + This makes the warning refer to ``deprecated_api``'s caller, rather than to + the source of ``deprecated_api`` itself (since the latter would defeat the + purpose of the warning message). + + The *skip_file_prefixes* keyword argument can be used to indicate which + stack frames are ignored when counting stack levels. This can be useful when + you want the warning to always appear at call sites outside of a package + when a constant *stacklevel* does not fit all call paths or is otherwise + challenging to maintain. If supplied, it must be a tuple of strings. When + prefixes are supplied, stacklevel is implicitly overridden to be ``max(2, + stacklevel)``. To cause a warning to be attributed to the caller from + outside of the current package you might write:: + + # example/lower.py + _warn_skips = (os.path.dirname(__file__),) + + def one_way(r_luxury_yacht=None, t_wobbler_mangrove=None): + if r_luxury_yacht: + warnings.warn("Please migrate to t_wobbler_mangrove=.", + skip_file_prefixes=_warn_skips) + + # example/higher.py + from . import lower + + def another_way(**kw): + lower.one_way(**kw) + + This makes the warning refer to both the ``example.lower.one_way()`` and + ``package.higher.another_way()`` call sites only from calling code living + outside of ``example`` package. *source*, if supplied, is the destroyed object which emitted a :exc:`ResourceWarning`. @@ -420,6 +447,9 @@ Available Functions .. versionchanged:: 3.6 Added *source* parameter. + .. versionchanged:: 3.12 + Added *skip_file_prefixes*. + .. function:: warn_explicit(message, category, filename, lineno, module=None, registry=None, module_globals=None, source=None) diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index a5365d53150b..8c210111b589 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -1151,6 +1151,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(signed)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(size)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sizehint)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(skip_file_prefixes)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sleep)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sock)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sort)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 3d9e61e50ecc..6b1c8424424d 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -637,6 +637,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(signed) STRUCT_FOR_ID(size) STRUCT_FOR_ID(sizehint) + STRUCT_FOR_ID(skip_file_prefixes) STRUCT_FOR_ID(sleep) STRUCT_FOR_ID(sock) STRUCT_FOR_ID(sort) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 3534b94753e0..fcb613083ffe 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -1143,6 +1143,7 @@ extern "C" { INIT_ID(signed), \ INIT_ID(size), \ INIT_ID(sizehint), \ + INIT_ID(skip_file_prefixes), \ INIT_ID(sleep), \ INIT_ID(sock), \ INIT_ID(sort), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 02435071bb2c..301aee5210e7 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -1180,6 +1180,8 @@ _PyUnicode_InitStaticStrings(void) { PyUnicode_InternInPlace(&string); string = &_Py_ID(sizehint); PyUnicode_InternInPlace(&string); + string = &_Py_ID(skip_file_prefixes); + PyUnicode_InternInPlace(&string); string = &_Py_ID(sleep); PyUnicode_InternInPlace(&string); string = &_Py_ID(sock); diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index 9e473e923cad..9e680c847dab 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -12,6 +12,7 @@ from test.support import warnings_helper from test.support.script_helper import assert_python_ok, assert_python_failure +from test.test_warnings.data import package_helper from test.test_warnings.data import stacklevel as warning_tests import warnings as original_warnings @@ -472,6 +473,42 @@ def test_stacklevel_import(self): self.assertEqual(len(w), 1) self.assertEqual(w[0].filename, __file__) + def test_skip_file_prefixes(self): + with warnings_state(self.module): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + self.module.simplefilter('always') + + # Warning never attributed to the data/ package. + package_helper.inner_api( + "inner_api", stacklevel=2, + warnings_module=warning_tests.warnings) + self.assertEqual(w[-1].filename, __file__) + warning_tests.package("package api", stacklevel=2) + self.assertEqual(w[-1].filename, __file__) + self.assertEqual(w[-2].filename, w[-1].filename) + # Low stacklevels are overridden to 2 behavior. + warning_tests.package("package api 1", stacklevel=1) + self.assertEqual(w[-1].filename, __file__) + warning_tests.package("package api 0", stacklevel=0) + self.assertEqual(w[-1].filename, __file__) + warning_tests.package("package api -99", stacklevel=-99) + self.assertEqual(w[-1].filename, __file__) + + # The stacklevel still goes up out of the package. + warning_tests.package("prefix02", stacklevel=3) + self.assertIn("unittest", w[-1].filename) + + def test_skip_file_prefixes_type_errors(self): + with warnings_state(self.module): + warn = warning_tests.warnings.warn + with self.assertRaises(TypeError): + warn("msg", skip_file_prefixes=[]) + with self.assertRaises(TypeError): + warn("msg", skip_file_prefixes=(b"bytes",)) + with self.assertRaises(TypeError): + warn("msg", skip_file_prefixes="a sequence of strs") + def test_exec_filename(self): filename = "" codeobj = compile(("import warnings\n" @@ -895,7 +932,7 @@ def test_formatwarning(self): message = "msg" category = Warning file_name = os.path.splitext(warning_tests.__file__)[0] + '.py' - line_num = 3 + line_num = 5 file_line = linecache.getline(file_name, line_num).strip() format = "%s:%s: %s: %s\n %s\n" expect = format % (file_name, line_num, category.__name__, message, diff --git a/Lib/test/test_warnings/data/package_helper.py b/Lib/test/test_warnings/data/package_helper.py new file mode 100644 index 000000000000..c22a4f6405c5 --- /dev/null +++ b/Lib/test/test_warnings/data/package_helper.py @@ -0,0 +1,10 @@ +# helper to the helper for testing skip_file_prefixes. + +import os + +package_path = os.path.dirname(__file__) + +def inner_api(message, *, stacklevel, warnings_module): + warnings_module.warn( + message, stacklevel=stacklevel, + skip_file_prefixes=(package_path,)) diff --git a/Lib/test/test_warnings/data/stacklevel.py b/Lib/test/test_warnings/data/stacklevel.py index d0519effdc87..c6dd24733b3b 100644 --- a/Lib/test/test_warnings/data/stacklevel.py +++ b/Lib/test/test_warnings/data/stacklevel.py @@ -1,9 +1,15 @@ -# Helper module for testing the skipmodules argument of warnings.warn() +# Helper module for testing stacklevel and skip_file_prefixes arguments +# of warnings.warn() import warnings +from test.test_warnings.data import package_helper def outer(message, stacklevel=1): inner(message, stacklevel) def inner(message, stacklevel=1): warnings.warn(message, stacklevel=stacklevel) + +def package(message, *, stacklevel): + package_helper.inner_api(message, stacklevel=stacklevel, + warnings_module=warnings) diff --git a/Lib/warnings.py b/Lib/warnings.py index 7d8c4400127f..98ae708ca340 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -269,22 +269,32 @@ def _getcategory(category): return cat +def _is_internal_filename(filename): + return 'importlib' in filename and '_bootstrap' in filename + + +def _is_filename_to_skip(filename, skip_file_prefixes): + return any(filename.startswith(prefix) for prefix in skip_file_prefixes) + + def _is_internal_frame(frame): """Signal whether the frame is an internal CPython implementation detail.""" - filename = frame.f_code.co_filename - return 'importlib' in filename and '_bootstrap' in filename + return _is_internal_filename(frame.f_code.co_filename) -def _next_external_frame(frame): - """Find the next frame that doesn't involve CPython internals.""" +def _next_external_frame(frame, skip_file_prefixes): + """Find the next frame that doesn't involve Python or user internals.""" frame = frame.f_back - while frame is not None and _is_internal_frame(frame): + while frame is not None and ( + _is_internal_filename(filename := frame.f_code.co_filename) or + _is_filename_to_skip(filename, skip_file_prefixes)): frame = frame.f_back return frame # Code typically replaced by _warnings -def warn(message, category=None, stacklevel=1, source=None): +def warn(message, category=None, stacklevel=1, source=None, + *, skip_file_prefixes=()): """Issue a warning, or maybe ignore it or raise an exception.""" # Check if message is already a Warning object if isinstance(message, Warning): @@ -295,6 +305,11 @@ def warn(message, category=None, stacklevel=1, source=None): if not (isinstance(category, type) and issubclass(category, Warning)): raise TypeError("category must be a Warning subclass, " "not '{:s}'".format(type(category).__name__)) + if not isinstance(skip_file_prefixes, tuple): + # The C version demands a tuple for implementation performance. + raise TypeError('skip_file_prefixes must be a tuple of strs.') + if skip_file_prefixes: + stacklevel = max(2, stacklevel) # Get context information try: if stacklevel <= 1 or _is_internal_frame(sys._getframe(1)): @@ -305,7 +320,7 @@ def warn(message, category=None, stacklevel=1, source=None): frame = sys._getframe(1) # Look for one frame less since the above line starts us off. for x in range(stacklevel-1): - frame = _next_external_frame(frame) + frame = _next_external_frame(frame, skip_file_prefixes) if frame is None: raise ValueError except ValueError: diff --git a/Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst b/Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst new file mode 100644 index 000000000000..1d04cc2cd54b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-08-00-12-44.gh-issue-39615.gn4PhB.rst @@ -0,0 +1,3 @@ +:func:`warnings.warn` now has the ability to skip stack frames based on code +filename prefix rather than only a numeric ``stacklevel`` via the new +``skip_file_prefixes`` keyword argument. diff --git a/Python/_warnings.c b/Python/_warnings.c index 046c37eb49ba..a8c1129356eb 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -761,56 +761,99 @@ warn_explicit(PyThreadState *tstate, PyObject *category, PyObject *message, return result; /* Py_None or NULL. */ } -static int -is_internal_frame(PyFrameObject *frame) +static PyObject * +get_frame_filename(PyFrameObject *frame) { - if (frame == NULL) { - return 0; - } - PyCodeObject *code = PyFrame_GetCode(frame); PyObject *filename = code->co_filename; Py_DECREF(code); + return filename; +} - if (filename == NULL) { - return 0; - } +static bool +is_internal_filename(PyObject *filename) +{ if (!PyUnicode_Check(filename)) { - return 0; + return false; } int contains = PyUnicode_Contains(filename, &_Py_ID(importlib)); if (contains < 0) { - return 0; + return false; } else if (contains > 0) { contains = PyUnicode_Contains(filename, &_Py_ID(_bootstrap)); if (contains < 0) { - return 0; + return false; } else if (contains > 0) { - return 1; + return true; } } - return 0; + return false; +} + +static bool +is_filename_to_skip(PyObject *filename, PyTupleObject *skip_file_prefixes) +{ + if (skip_file_prefixes) { + if (!PyUnicode_Check(filename)) { + return false; + } + + Py_ssize_t prefixes = PyTuple_GET_SIZE(skip_file_prefixes); + for (Py_ssize_t idx = 0; idx < prefixes; ++idx) + { + PyObject *prefix = PyTuple_GET_ITEM(skip_file_prefixes, idx); + int found = PyUnicode_Tailmatch(filename, prefix, 0, -1, -1); + if (found == 1) { + return true; + } + if (found < 0) { + return false; + } + } + } + return false; +} + +static bool +is_internal_frame(PyFrameObject *frame) +{ + if (frame == NULL) { + return false; + } + + PyObject *filename = get_frame_filename(frame); + if (filename == NULL) { + return false; + } + + return is_internal_filename(filename); } static PyFrameObject * -next_external_frame(PyFrameObject *frame) +next_external_frame(PyFrameObject *frame, PyTupleObject *skip_file_prefixes) { + PyObject *frame_filename; do { PyFrameObject *back = PyFrame_GetBack(frame); Py_SETREF(frame, back); - } while (frame != NULL && is_internal_frame(frame)); + } while (frame != NULL && (frame_filename = get_frame_filename(frame)) && + (is_internal_filename(frame_filename) || + is_filename_to_skip(frame_filename, skip_file_prefixes))); return frame; } /* filename, module, and registry are new refs, globals is borrowed */ +/* skip_file_prefixes is either NULL or a tuple of strs. */ /* Returns 0 on error (no new refs), 1 on success */ static int -setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno, +setup_context(Py_ssize_t stack_level, + PyTupleObject *skip_file_prefixes, + PyObject **filename, int *lineno, PyObject **module, PyObject **registry) { PyObject *globals; @@ -820,6 +863,21 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno, if (tstate == NULL) { return 0; } + if (skip_file_prefixes) { + /* Type check our data structure up front. Later code that uses it + * isn't structured to report errors. */ + Py_ssize_t prefixes = PyTuple_GET_SIZE(skip_file_prefixes); + for (Py_ssize_t idx = 0; idx < prefixes; ++idx) + { + PyObject *prefix = PyTuple_GET_ITEM(skip_file_prefixes, idx); + if (!PyUnicode_Check(prefix)) { + PyErr_Format(PyExc_TypeError, + "Found non-str '%s' in skip_file_prefixes.", + Py_TYPE(prefix)->tp_name); + return 0; + } + } + } PyInterpreterState *interp = tstate->interp; PyFrameObject *f = PyThreadState_GetFrame(tstate); // Stack level comparisons to Python code is off by one as there is no @@ -832,7 +890,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno, } else { while (--stack_level > 0 && f != NULL) { - f = next_external_frame(f); + f = next_external_frame(f, skip_file_prefixes); } } @@ -925,7 +983,7 @@ get_category(PyObject *message, PyObject *category) static PyObject * do_warn(PyObject *message, PyObject *category, Py_ssize_t stack_level, - PyObject *source) + PyObject *source, PyTupleObject *skip_file_prefixes) { PyObject *filename, *module, *registry, *res; int lineno; @@ -935,7 +993,8 @@ do_warn(PyObject *message, PyObject *category, Py_ssize_t stack_level, return NULL; } - if (!setup_context(stack_level, &filename, &lineno, &module, ®istry)) + if (!setup_context(stack_level, skip_file_prefixes, + &filename, &lineno, &module, ®istry)) return NULL; res = warn_explicit(tstate, category, message, filename, lineno, module, registry, @@ -950,22 +1009,42 @@ do_warn(PyObject *message, PyObject *category, Py_ssize_t stack_level, warn as warnings_warn message: object + Text of the warning message. category: object = None + The Warning category subclass. Defaults to UserWarning. stacklevel: Py_ssize_t = 1 + How far up the call stack to make this warning appear. A value of 2 for + example attributes the warning to the caller of the code calling warn(). source: object = None + If supplied, the destroyed object which emitted a ResourceWarning + * + skip_file_prefixes: object(type='PyTupleObject *', subclass_of='&PyTuple_Type') = NULL + An optional tuple of module filename prefixes indicating frames to skip + during stacklevel computations for stack frame attribution. Issue a warning, or maybe ignore it or raise an exception. [clinic start generated code]*/ static PyObject * warnings_warn_impl(PyObject *module, PyObject *message, PyObject *category, - Py_ssize_t stacklevel, PyObject *source) -/*[clinic end generated code: output=31ed5ab7d8d760b2 input=bfdf5cf99f6c4edd]*/ + Py_ssize_t stacklevel, PyObject *source, + PyTupleObject *skip_file_prefixes) +/*[clinic end generated code: output=a68e0f6906c65f80 input=eb37c6a18bec4ea1]*/ { category = get_category(message, category); if (category == NULL) return NULL; - return do_warn(message, category, stacklevel, source); + if (skip_file_prefixes) { + if (PyTuple_GET_SIZE(skip_file_prefixes) > 0) { + if (stacklevel < 2) { + stacklevel = 2; + } + } else { + Py_DECREF((PyObject *)skip_file_prefixes); + skip_file_prefixes = NULL; + } + } + return do_warn(message, category, stacklevel, source, skip_file_prefixes); } static PyObject * @@ -1113,7 +1192,7 @@ warn_unicode(PyObject *category, PyObject *message, if (category == NULL) category = PyExc_RuntimeWarning; - res = do_warn(message, category, stack_level, source); + res = do_warn(message, category, stack_level, source, NULL); if (res == NULL) return -1; Py_DECREF(res); diff --git a/Python/clinic/_warnings.c.h b/Python/clinic/_warnings.c.h index 8838a42afc1c..432e554af85c 100644 --- a/Python/clinic/_warnings.c.h +++ b/Python/clinic/_warnings.c.h @@ -9,17 +9,32 @@ preserve PyDoc_STRVAR(warnings_warn__doc__, -"warn($module, /, message, category=None, stacklevel=1, source=None)\n" +"warn($module, /, message, category=None, stacklevel=1, source=None, *,\n" +" skip_file_prefixes=)\n" "--\n" "\n" -"Issue a warning, or maybe ignore it or raise an exception."); +"Issue a warning, or maybe ignore it or raise an exception.\n" +"\n" +" message\n" +" Text of the warning message.\n" +" category\n" +" The Warning category subclass. Defaults to UserWarning.\n" +" stacklevel\n" +" How far up the call stack to make this warning appear. A value of 2 for\n" +" example attributes the warning to the caller of the code calling warn().\n" +" source\n" +" If supplied, the destroyed object which emitted a ResourceWarning\n" +" skip_file_prefixes\n" +" An optional tuple of module filename prefixes indicating frames to skip\n" +" during stacklevel computations for stack frame attribution."); #define WARNINGS_WARN_METHODDEF \ {"warn", _PyCFunction_CAST(warnings_warn), METH_FASTCALL|METH_KEYWORDS, warnings_warn__doc__}, static PyObject * warnings_warn_impl(PyObject *module, PyObject *message, PyObject *category, - Py_ssize_t stacklevel, PyObject *source); + Py_ssize_t stacklevel, PyObject *source, + PyTupleObject *skip_file_prefixes); static PyObject * warnings_warn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -27,14 +42,14 @@ warnings_warn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 4 + #define NUM_KEYWORDS 5 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD PyObject *ob_item[NUM_KEYWORDS]; } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(message), &_Py_ID(category), &_Py_ID(stacklevel), &_Py_ID(source), }, + .ob_item = { &_Py_ID(message), &_Py_ID(category), &_Py_ID(stacklevel), &_Py_ID(source), &_Py_ID(skip_file_prefixes), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -43,19 +58,20 @@ warnings_warn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"message", "category", "stacklevel", "source", NULL}; + static const char * const _keywords[] = {"message", "category", "stacklevel", "source", "skip_file_prefixes", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "warn", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[4]; + PyObject *argsbuf[5]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *message; PyObject *category = Py_None; Py_ssize_t stacklevel = 1; PyObject *source = Py_None; + PyTupleObject *skip_file_prefixes = NULL; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 4, 0, argsbuf); if (!args) { @@ -88,9 +104,23 @@ warnings_warn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec goto skip_optional_pos; } } - source = args[3]; + if (args[3]) { + source = args[3]; + if (!--noptargs) { + goto skip_optional_pos; + } + } skip_optional_pos: - return_value = warnings_warn_impl(module, message, category, stacklevel, source); + if (!noptargs) { + goto skip_optional_kwonly; + } + if (!PyTuple_Check(args[4])) { + _PyArg_BadArgument("warn", "argument 'skip_file_prefixes'", "tuple", args[4]); + goto exit; + } + skip_file_prefixes = (PyTupleObject *)args[4]; +skip_optional_kwonly: + return_value = warnings_warn_impl(module, message, category, stacklevel, source, skip_file_prefixes); exit: return return_value; @@ -216,4 +246,4 @@ warnings_filters_mutated(PyObject *module, PyObject *Py_UNUSED(ignored)) { return warnings_filters_mutated_impl(module); } -/*[clinic end generated code: output=0d264d1ddfc37100 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=20429719d7223bdc input=a9049054013a1b77]*/ From webhook-mailer at python.org Sat Jan 28 04:57:57 2023 From: webhook-mailer at python.org (hugovk) Date: Sat, 28 Jan 2023 09:57:57 -0000 Subject: [Python-checkins] gh-101386: fix typos found by codespell (#101387) Message-ID: https://github.com/python/cpython/commit/db757f0e440d2b3fd3a04dd771907838e0c2241c commit: db757f0e440d2b3fd3a04dd771907838e0c2241c branch: main author: Peter Jiping Xie committer: hugovk date: 2023-01-28T11:57:40+02:00 summary: gh-101386: fix typos found by codespell (#101387) files: M Doc/c-api/init_config.rst M Doc/howto/logging-cookbook.rst M Doc/library/sqlite3.rst M Doc/library/struct.rst diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index c3346b0421dd..161def0b4baf 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -839,7 +839,7 @@ PyConfig will produce an error. Configured by the :option:`-X int_max_str_digits <-X>` command line - flag or the :envvar:`PYTHONINTMAXSTRDIGITS` environment varable. + flag or the :envvar:`PYTHONINTMAXSTRDIGITS` environment variable. Default: ``-1`` in Python mode. 4300 (:data:`sys.int_info.default_max_str_digits`) in isolated mode. @@ -1582,7 +1582,7 @@ applied during the "Main" phase. It may allow to customize Python in Python to override or tune the :ref:`Path Configuration `, maybe install a custom :data:`sys.meta_path` importer or an import hook, etc. -It may become possible to calculatin the :ref:`Path Configuration +It may become possible to calculate the :ref:`Path Configuration ` in Python, after the Core phase and before the Main phase, which is one of the :pep:`432` motivation. diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 2ba5b5c13681..26cf40274fd3 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -3628,7 +3628,7 @@ refer to the comments in the code snippet for more detailed information. Logging to syslog with RFC5424 support -------------------------------------- -Although :rfc:`5424` dates from 2009, most syslog servers are configured by detault to +Although :rfc:`5424` dates from 2009, most syslog servers are configured by default to use the older :rfc:`3164`, which hails from 2001. When ``logging`` was added to Python in 2003, it supported the earlier (and only existing) protocol at the time. Since RFC5424 came out, as there has not been widespread deployment of it in syslog @@ -3819,7 +3819,7 @@ then running the script results in WARNING:demo:division by zero As you can see, this output isn't ideal. That's because the underlying code -which writes to ``sys.stderr`` makes mutiple writes, each of which results in a +which writes to ``sys.stderr`` makes multiple writes, each of which results in a separate logged line (for example, the last three lines above). To get around this problem, you need to buffer things and only output log lines when newlines are seen. Let's use a slghtly better implementation of ``LoggerWriter``: diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 8796bdfc0b6f..c0f0f133f4a2 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -2207,7 +2207,7 @@ This section shows recipes for common adapters and converters. assert convert_datetime(b"2019-05-18T15:17:08.123456") == dt # Using current time as fromtimestamp() returns local date/time. - # Droping microseconds as adapt_datetime_epoch truncates fractional second part. + # Dropping microseconds as adapt_datetime_epoch truncates fractional second part. now = datetime.datetime.now().replace(microsecond=0) current_timestamp = int(now.timestamp()) diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst index 830ba69e294d..69d95f27cb61 100644 --- a/Doc/library/struct.rst +++ b/Doc/library/struct.rst @@ -462,7 +462,7 @@ In such cases, the ``@`` format character should be used to specify native byte ordering and data sizes. Internal pad bytes are normally inserted automatically. It is possible that a zero-repeat format code will be needed at the end of a format string to round up to the correct -byte boundary for proper alignment of consective chunks of data. +byte boundary for proper alignment of consecutive chunks of data. Consider these two simple examples (on a 64-bit, little-endian machine):: From webhook-mailer at python.org Sat Jan 28 07:29:36 2023 From: webhook-mailer at python.org (rhettinger) Date: Sat, 28 Jan 2023 12:29:36 -0000 Subject: [Python-checkins] GH-100485: Add extended accuracy test. Switch to faster fma() based variant. GH-101383) Message-ID: https://github.com/python/cpython/commit/84483aacc005e6a535355cb68342fa0801d956cc commit: 84483aacc005e6a535355cb68342fa0801d956cc branch: main author: Raymond Hettinger committer: rhettinger date: 2023-01-28T06:29:21-06:00 summary: GH-100485: Add extended accuracy test. Switch to faster fma() based variant. GH-101383) files: M Lib/test/test_math.py M Modules/mathmodule.c diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index b8ac8f32055d..fcdec93484e0 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1369,6 +1369,89 @@ def run(func, *args): args, ) + @requires_IEEE_754 + @unittest.skipIf(HAVE_DOUBLE_ROUNDING, + "sumprod() accuracy not guaranteed on machines with double rounding") + @support.cpython_only # Other implementations may choose a different algorithm + @support.requires_resource('cpu') + def test_sumprod_extended_precision_accuracy(self): + import operator + from fractions import Fraction + from itertools import starmap + from collections import namedtuple + from math import log2, exp2, fabs + from random import choices, uniform, shuffle + from statistics import median + + DotExample = namedtuple('DotExample', ('x', 'y', 'target_sumprod', 'condition')) + + def DotExact(x, y): + vec1 = map(Fraction, x) + vec2 = map(Fraction, y) + return sum(starmap(operator.mul, zip(vec1, vec2, strict=True))) + + def Condition(x, y): + return 2.0 * DotExact(map(abs, x), map(abs, y)) / abs(DotExact(x, y)) + + def linspace(lo, hi, n): + width = (hi - lo) / (n - 1) + return [lo + width * i for i in range(n)] + + def GenDot(n, c): + """ Algorithm 6.1 (GenDot) works as follows. The condition number (5.7) of + the dot product xT y is proportional to the degree of cancellation. In + order to achieve a prescribed cancellation, we generate the first half of + the vectors x and y randomly within a large exponent range. This range is + chosen according to the anticipated condition number. The second half of x + and y is then constructed choosing xi randomly with decreasing exponent, + and calculating yi such that some cancellation occurs. Finally, we permute + the vectors x, y randomly and calculate the achieved condition number. + """ + + assert n >= 6 + n2 = n // 2 + x = [0.0] * n + y = [0.0] * n + b = log2(c) + + # First half with exponents from 0 to |_b/2_| and random ints in between + e = choices(range(int(b/2)), k=n2) + e[0] = int(b / 2) + 1 + e[-1] = 0.0 + + x[:n2] = [uniform(-1.0, 1.0) * exp2(p) for p in e] + y[:n2] = [uniform(-1.0, 1.0) * exp2(p) for p in e] + + # Second half + e = list(map(round, linspace(b/2, 0.0 , n-n2))) + for i in range(n2, n): + x[i] = uniform(-1.0, 1.0) * exp2(e[i - n2]) + y[i] = (uniform(-1.0, 1.0) * exp2(e[i - n2]) - DotExact(x, y)) / x[i] + + # Shuffle + pairs = list(zip(x, y)) + shuffle(pairs) + x, y = zip(*pairs) + + return DotExample(x, y, DotExact(x, y), Condition(x, y)) + + def RelativeError(res, ex): + x, y, target_sumprod, condition = ex + n = DotExact(list(x) + [-res], list(y) + [1]) + return fabs(n / target_sumprod) + + def Trial(dotfunc, c, n): + ex = GenDot(10, c) + res = dotfunc(ex.x, ex.y) + return RelativeError(res, ex) + + times = 1000 # Number of trials + n = 20 # Length of vectors + c = 1e30 # Target condition number + + relative_err = median(Trial(math.sumprod, c, n) for i in range(times)) + self.assertLess(relative_err, 1e-16) + def testModf(self): self.assertRaises(TypeError, math.modf) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 2da50bbd54b5..481e29915464 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2832,12 +2832,7 @@ long_add_would_overflow(long a, long b) } /* -Double and triple length extended precision floating point arithmetic -based on: - - A Floating-Point Technique for Extending the Available Precision - by T. J. Dekker - https://csclub.uwaterloo.ca/~pbarfuss/dekker1971.pdf +Double and triple length extended precision algorithms from: Accurate Sum and Dot Product by Takeshi Ogita, Siegfried M. Rump, and Shin?Ichi Oishi @@ -2848,36 +2843,22 @@ based on: typedef struct{ double hi; double lo; } DoubleLength; -static inline DoubleLength -twosum(double a, double b) +static DoubleLength +dl_sum(double a, double b) { - // Rump Algorithm 3.1 Error-free transformation of the sum + /* Algorithm 3.1 Error-free transformation of the sum */ double x = a + b; double z = x - a; double y = (a - (x - z)) + (b - z); return (DoubleLength) {x, y}; } -static inline DoubleLength -dl_split(double x) { - // Rump Algorithm 3.2 Error-free splitting of a floating point number - // Dekker (5.5) and (5.6). - double t = x * 134217729.0; // Veltkamp constant = 2.0 ** 27 + 1 - double hi = t - (t - x); - double lo = x - hi; - return (DoubleLength) {hi, lo}; -} - -static inline DoubleLength +static DoubleLength dl_mul(double x, double y) { - // Dekker (5.12) and mul12() - DoubleLength xx = dl_split(x); - DoubleLength yy = dl_split(y); - double p = xx.hi * yy.hi; - double q = xx.hi * yy.lo + xx.lo * yy.hi; - double z = p + q; - double zz = p - z + q + xx.lo * yy.lo; + /* Algorithm 3.5. Error-free transformation of a product */ + double z = x * y; + double zz = fma(x, y, -z); return (DoubleLength) {z, zz}; } @@ -2885,21 +2866,21 @@ typedef struct { double hi; double lo; double tiny; } TripleLength; static const TripleLength tl_zero = {0.0, 0.0, 0.0}; -static inline TripleLength -tl_fma(TripleLength total, double x, double y) +static TripleLength +tl_fma(double x, double y, TripleLength total) { - // Rump Algorithm 5.10 with K=3 and using SumKVert + /* Algorithm 5.10 with SumKVert for K=3 */ DoubleLength pr = dl_mul(x, y); - DoubleLength sm = twosum(total.hi, pr.hi); - DoubleLength r1 = twosum(total.lo, pr.lo); - DoubleLength r2 = twosum(r1.hi, sm.lo); + DoubleLength sm = dl_sum(total.hi, pr.hi); + DoubleLength r1 = dl_sum(total.lo, pr.lo); + DoubleLength r2 = dl_sum(r1.hi, sm.lo); return (TripleLength) {sm.hi, r2.hi, total.tiny + r1.lo + r2.lo}; } -static inline double +static double tl_to_d(TripleLength total) { - DoubleLength last = twosum(total.lo, total.hi); + DoubleLength last = dl_sum(total.lo, total.hi); return total.tiny + last.lo + last.hi; } @@ -3066,7 +3047,7 @@ math_sumprod_impl(PyObject *module, PyObject *p, PyObject *q) } else { goto finalize_flt_path; } - TripleLength new_flt_total = tl_fma(flt_total, flt_p, flt_q); + TripleLength new_flt_total = tl_fma(flt_p, flt_q, flt_total); if (isfinite(new_flt_total.hi)) { flt_total = new_flt_total; flt_total_in_use = true; From webhook-mailer at python.org Sat Jan 28 08:18:14 2023 From: webhook-mailer at python.org (serhiy-storchaka) Date: Sat, 28 Jan 2023 13:18:14 -0000 Subject: [Python-checkins] Fix trivial typo in shebang example (GH-101385) Message-ID: https://github.com/python/cpython/commit/666c0840dcac9941fa41ec619fef8d45cd849a0b commit: 666c0840dcac9941fa41ec619fef8d45cd849a0b branch: main author: socal-nerdtastic <37753609+socal-nerdtastic at users.noreply.github.com> committer: serhiy-storchaka date: 2023-01-28T15:18:04+02:00 summary: Fix trivial typo in shebang example (GH-101385) The example was showing the current version, but should be pinned to 3.7 to match the example command. files: M Doc/using/windows.rst diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index b2a0214026e4..bd09666d5e01 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -797,7 +797,7 @@ is printed. Now try changing the first line to be: Re-executing the command should now print the latest Python 3.x information. As with the above command-line examples, you can specify a more explicit version qualifier. Assuming you have Python 3.7 installed, try changing -the first line to ``#! python3.7`` and you should find the |version| +the first line to ``#! python3.7`` and you should find the 3.7 version information printed. Note that unlike interactive use, a bare "python" will use the latest From webhook-mailer at python.org Sun Jan 29 14:50:32 2023 From: webhook-mailer at python.org (hauntsaninja) Date: Sun, 29 Jan 2023 19:50:32 -0000 Subject: [Python-checkins] gh-89381: Fix invalid signatures of math/cmath.log (#101404) Message-ID: https://github.com/python/cpython/commit/0ef92d979311ba82d4c41b22ef38e12e1b08b13d commit: 0ef92d979311ba82d4c41b22ef38e12e1b08b13d branch: main author: Sergey B Kirpichev committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com> date: 2023-01-29T11:50:10-08:00 summary: gh-89381: Fix invalid signatures of math/cmath.log (#101404) files: A Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst M Doc/library/cmath.rst M Doc/library/math.rst M Lib/test/test_math.py M Modules/clinic/cmathmodule.c.h M Modules/clinic/mathmodule.c.h M Modules/cmathmodule.c M Modules/mathmodule.c diff --git a/Doc/library/cmath.rst b/Doc/library/cmath.rst index 28cd96b0e12d..c575b90e6461 100644 --- a/Doc/library/cmath.rst +++ b/Doc/library/cmath.rst @@ -89,7 +89,7 @@ Power and logarithmic functions logarithms. -.. function:: log(x[, base]) +.. function:: log(x, base=None) Returns the logarithm of *x* to the given *base*. If the *base* is not specified, returns the natural logarithm of *x*. There is one branch cut, from 0 diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 797f32408eac..9da22b6ad056 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -393,13 +393,12 @@ Power and logarithmic functions .. versionadded:: 3.2 -.. function:: log(x[, base]) +.. function:: log(x, base=None) - With one argument, return the natural logarithm of *x* (to base *e*). - - With two arguments, return the logarithm of *x* to the given *base*, - calculated as ``log(x)/log(base)``. + Return the logarithm of *x* to the given *base*. + If the *base* is not specified, returns the natural + logarithm (base *e*) of *x*. .. function:: log1p(x) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index fcdec93484e0..2c84e55ab45c 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1146,6 +1146,7 @@ def testLog(self): self.ftest('log(1/e)', math.log(1/math.e), -1) self.ftest('log(1)', math.log(1), 0) self.ftest('log(e)', math.log(math.e), 1) + self.ftest('log(e, None)', math.log(math.e, None), 1) self.ftest('log(32,2)', math.log(32,2), 5) self.ftest('log(10**40, 10)', math.log(10**40, 10), 40) self.ftest('log(10**40, 10**20)', math.log(10**40, 10**20), 2) diff --git a/Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst b/Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst new file mode 100644 index 000000000000..7bffe7d226e3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-16-10-42-58.gh-issue-89381.lM2WL0.rst @@ -0,0 +1 @@ +:func:`~math.log` and :func:`~cmath.log` support default base=None values. diff --git a/Modules/clinic/cmathmodule.c.h b/Modules/clinic/cmathmodule.c.h index b1da9452c61d..bc91c20f373b 100644 --- a/Modules/clinic/cmathmodule.c.h +++ b/Modules/clinic/cmathmodule.c.h @@ -639,12 +639,13 @@ cmath_tanh(PyObject *module, PyObject *arg) } PyDoc_STRVAR(cmath_log__doc__, -"log($module, z, base=, /)\n" +"log($module, z, base=None, /)\n" "--\n" "\n" "log(z[, base]) -> the logarithm of z to the given base.\n" "\n" -"If the base not specified, returns the natural logarithm (base e) of z."); +"If the base is not specified or is None, returns the\n" +"natural logarithm (base e) of z."); #define CMATH_LOG_METHODDEF \ {"log", _PyCFunction_CAST(cmath_log), METH_FASTCALL, cmath_log__doc__}, @@ -657,7 +658,7 @@ cmath_log(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; Py_complex x; - PyObject *y_obj = NULL; + PyObject *y_obj = Py_None; if (!_PyArg_CheckPositional("log", nargs, 1, 2)) { goto exit; @@ -982,4 +983,4 @@ cmath_isclose(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec exit: return return_value; } -/*[clinic end generated code: output=0146c656e67f5d5f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2630f8740909a8f7 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 1f9725883b98..0d61fd1be38d 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -187,43 +187,37 @@ math_modf(PyObject *module, PyObject *arg) } PyDoc_STRVAR(math_log__doc__, -"log(x, [base=math.e])\n" +"log($module, x, base=None, /)\n" +"--\n" +"\n" "Return the logarithm of x to the given base.\n" "\n" -"If the base not specified, returns the natural logarithm (base e) of x."); +"If the base is not specified or is None, returns the natural\n" +"logarithm (base e) of x."); #define MATH_LOG_METHODDEF \ - {"log", (PyCFunction)math_log, METH_VARARGS, math_log__doc__}, + {"log", _PyCFunction_CAST(math_log), METH_FASTCALL, math_log__doc__}, static PyObject * -math_log_impl(PyObject *module, PyObject *x, int group_right_1, - PyObject *base); +math_log_impl(PyObject *module, PyObject *x, PyObject *base); static PyObject * -math_log(PyObject *module, PyObject *args) +math_log(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; PyObject *x; - int group_right_1 = 0; - PyObject *base = NULL; + PyObject *base = Py_None; - switch (PyTuple_GET_SIZE(args)) { - case 1: - if (!PyArg_ParseTuple(args, "O:log", &x)) { - goto exit; - } - break; - case 2: - if (!PyArg_ParseTuple(args, "OO:log", &x, &base)) { - goto exit; - } - group_right_1 = 1; - break; - default: - PyErr_SetString(PyExc_TypeError, "math.log requires 1 to 2 arguments"); - goto exit; + if (!_PyArg_CheckPositional("log", nargs, 1, 2)) { + goto exit; + } + x = args[0]; + if (nargs < 2) { + goto skip_optional; } - return_value = math_log_impl(module, x, group_right_1, base); + base = args[1]; +skip_optional: + return_value = math_log_impl(module, x, base); exit: return return_value; @@ -954,4 +948,4 @@ math_ulp(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=899211ec70e4506c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=afec63ebb0da709a input=a9049054013a1b77]*/ diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 2038ac26e658..62caba031eda 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -952,23 +952,24 @@ cmath_tanh_impl(PyObject *module, Py_complex z) cmath.log z as x: Py_complex - base as y_obj: object = NULL + base as y_obj: object = None / log(z[, base]) -> the logarithm of z to the given base. -If the base not specified, returns the natural logarithm (base e) of z. +If the base is not specified or is None, returns the +natural logarithm (base e) of z. [clinic start generated code]*/ static PyObject * cmath_log_impl(PyObject *module, Py_complex x, PyObject *y_obj) -/*[clinic end generated code: output=4effdb7d258e0d94 input=230ed3a71ecd000a]*/ +/*[clinic end generated code: output=4effdb7d258e0d94 input=e7db51859ebf70bf]*/ { Py_complex y; errno = 0; x = c_log(x); - if (y_obj != NULL) { + if (y_obj != Py_None) { y = PyComplex_AsCComplex(y_obj); if (PyErr_Occurred()) { return NULL; diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 481e29915464..e6cdb3bae1ec 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2366,26 +2366,24 @@ loghelper(PyObject* arg, double (*func)(double)) math.log x: object - [ - base: object(c_default="NULL") = math.e - ] + base: object = None / Return the logarithm of x to the given base. -If the base not specified, returns the natural logarithm (base e) of x. +If the base is not specified or is None, returns the natural +logarithm (base e) of x. [clinic start generated code]*/ static PyObject * -math_log_impl(PyObject *module, PyObject *x, int group_right_1, - PyObject *base) -/*[clinic end generated code: output=7b5a39e526b73fc9 input=0f62d5726cbfebbd]*/ +math_log_impl(PyObject *module, PyObject *x, PyObject *base) +/*[clinic end generated code: output=1dead263cbb1e854 input=ef032cc9837943e1]*/ { PyObject *num, *den; PyObject *ans; num = loghelper(x, m_log); - if (num == NULL || base == NULL) + if (num == NULL || base == Py_None) return num; den = loghelper(base, m_log); From webhook-mailer at python.org Sun Jan 29 16:14:23 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 29 Jan 2023 21:14:23 -0000 Subject: [Python-checkins] Fix trivial typo in shebang example (GH-101385) Message-ID: https://github.com/python/cpython/commit/6aabbe2b15d7c1ad671c76f166bf553e7ebc7253 commit: 6aabbe2b15d7c1ad671c76f166bf553e7ebc7253 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-29T13:14:18-08:00 summary: Fix trivial typo in shebang example (GH-101385) The example was showing the current version, but should be pinned to 3.7 to match the example command. (cherry picked from commit 666c0840dcac9941fa41ec619fef8d45cd849a0b) Co-authored-by: socal-nerdtastic <37753609+socal-nerdtastic at users.noreply.github.com> files: M Doc/using/windows.rst diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index 9489609b4b48..0c5684e570a1 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -780,7 +780,7 @@ is printed. Now try changing the first line to be: Re-executing the command should now print the latest Python 3.x information. As with the above command-line examples, you can specify a more explicit version qualifier. Assuming you have Python 3.7 installed, try changing -the first line to ``#! python3.7`` and you should find the |version| +the first line to ``#! python3.7`` and you should find the 3.7 version information printed. Note that unlike interactive use, a bare "python" will use the latest From webhook-mailer at python.org Sun Jan 29 16:14:28 2023 From: webhook-mailer at python.org (miss-islington) Date: Sun, 29 Jan 2023 21:14:28 -0000 Subject: [Python-checkins] Fix trivial typo in shebang example (GH-101385) Message-ID: https://github.com/python/cpython/commit/34679913fe9ad8a13d2f431abe8af9e340831b1d commit: 34679913fe9ad8a13d2f431abe8af9e340831b1d branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-29T13:14:23-08:00 summary: Fix trivial typo in shebang example (GH-101385) The example was showing the current version, but should be pinned to 3.7 to match the example command. (cherry picked from commit 666c0840dcac9941fa41ec619fef8d45cd849a0b) Co-authored-by: socal-nerdtastic <37753609+socal-nerdtastic at users.noreply.github.com> files: M Doc/using/windows.rst diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index ce1d55e6e2e2..092ce47ad39a 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -784,7 +784,7 @@ is printed. Now try changing the first line to be: Re-executing the command should now print the latest Python 3.x information. As with the above command-line examples, you can specify a more explicit version qualifier. Assuming you have Python 3.7 installed, try changing -the first line to ``#! python3.7`` and you should find the |version| +the first line to ``#! python3.7`` and you should find the 3.7 version information printed. Note that unlike interactive use, a bare "python" will use the latest From webhook-mailer at python.org Sun Jan 29 19:41:37 2023 From: webhook-mailer at python.org (gpshead) Date: Mon, 30 Jan 2023 00:41:37 -0000 Subject: [Python-checkins] gh-39615: fix warning on return type mismatch (#101407) Message-ID: https://github.com/python/cpython/commit/c4170c36b0b54c10456f4b49245f570023a97f72 commit: c4170c36b0b54c10456f4b49245f570023a97f72 branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: gpshead date: 2023-01-29T16:41:27-08:00 summary: gh-39615: fix warning on return type mismatch (#101407) files: M Python/_warnings.c diff --git a/Python/_warnings.c b/Python/_warnings.c index a8c1129356eb..e78f21644f37 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -806,7 +806,7 @@ is_filename_to_skip(PyObject *filename, PyTupleObject *skip_file_prefixes) for (Py_ssize_t idx = 0; idx < prefixes; ++idx) { PyObject *prefix = PyTuple_GET_ITEM(skip_file_prefixes, idx); - int found = PyUnicode_Tailmatch(filename, prefix, 0, -1, -1); + Py_ssize_t found = PyUnicode_Tailmatch(filename, prefix, 0, -1, -1); if (found == 1) { return true; } From webhook-mailer at python.org Sun Jan 29 20:28:46 2023 From: webhook-mailer at python.org (gvanrossum) Date: Mon, 30 Jan 2023 01:28:46 -0000 Subject: [Python-checkins] gh-98831: Support conditional effects; use for LOAD_ATTR (#101333) Message-ID: https://github.com/python/cpython/commit/f5a3d91b6c56ddff4644b5a5ac34d8c6d23d7c79 commit: f5a3d91b6c56ddff4644b5a5ac34d8c6d23d7c79 branch: main author: Guido van Rossum committer: gvanrossum date: 2023-01-29T17:28:39-08:00 summary: gh-98831: Support conditional effects; use for LOAD_ATTR (#101333) files: M Python/bytecodes.c M Python/generated_cases.c.h M Python/opcode_metadata.h M Tools/cases_generator/generate_cases.py M Tools/cases_generator/parser.py M Tools/cases_generator/test_generator.py diff --git a/Python/bytecodes.c b/Python/bytecodes.c index e5769f61fc28..fb00b887732e 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -51,7 +51,7 @@ // Dummy variables for stack effects. static PyObject *value, *value1, *value2, *left, *right, *res, *sum, *prod, *sub; -static PyObject *container, *start, *stop, *v, *lhs, *rhs; +static PyObject *container, *start, *stop, *v, *lhs, *rhs, *res2; static PyObject *list, *tuple, *dict, *owner, *set, *str, *tup, *map, *keys; static PyObject *exit_func, *lasti, *val, *retval, *obj, *iter; static PyObject *aiter, *awaitable, *iterable, *w, *exc_value, *bc; @@ -1438,13 +1438,11 @@ dummy_func( PREDICT(JUMP_BACKWARD); } - // error: LOAD_ATTR has irregular stack effect - inst(LOAD_ATTR) { + inst(LOAD_ATTR, (unused/9, owner -- res2 if (oparg & 1), res)) { #if ENABLE_SPECIALIZATION _PyAttrCache *cache = (_PyAttrCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); - PyObject *owner = TOP(); PyObject *name = GETITEM(names, oparg>>1); next_instr--; _Py_Specialize_LoadAttr(owner, next_instr, name); @@ -1454,26 +1452,18 @@ dummy_func( DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ PyObject *name = GETITEM(names, oparg >> 1); - PyObject *owner = TOP(); if (oparg & 1) { - /* Designed to work in tandem with CALL. */ + /* Designed to work in tandem with CALL, pushes two values. */ PyObject* meth = NULL; - - int meth_found = _PyObject_GetMethod(owner, name, &meth); - - if (meth == NULL) { - /* Most likely attribute wasn't found. */ - goto error; - } - - if (meth_found) { + if (_PyObject_GetMethod(owner, name, &meth)) { /* We can bypass temporary bound method object. meth is unbound method and obj is self. meth | self | arg1 | ... | argN */ - SET_TOP(meth); - PUSH(owner); // self + assert(meth != NULL); // No errors on this branch + res2 = meth; + res = owner; // Transfer ownership } else { /* meth is not an unbound method (but a regular attr, or @@ -1483,20 +1473,18 @@ dummy_func( NULL | meth | arg1 | ... | argN */ - SET_TOP(NULL); Py_DECREF(owner); - PUSH(meth); + ERROR_IF(meth == NULL, error); + res2 = NULL; + res = meth; } } else { - PyObject *res = PyObject_GetAttr(owner, name); - if (res == NULL) { - goto error; - } + /* Classic, pushes one value. */ + res = PyObject_GetAttr(owner, name); Py_DECREF(owner); - SET_TOP(res); + ERROR_IF(res == NULL, error); } - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); } // error: LOAD_ATTR has irregular stack effect diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 287a1f1f0420..b5decf804ca6 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1745,11 +1745,13 @@ TARGET(LOAD_ATTR) { PREDICTED(LOAD_ATTR); + PyObject *owner = PEEK(1); + PyObject *res2 = NULL; + PyObject *res; #if ENABLE_SPECIALIZATION _PyAttrCache *cache = (_PyAttrCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); - PyObject *owner = TOP(); PyObject *name = GETITEM(names, oparg>>1); next_instr--; _Py_Specialize_LoadAttr(owner, next_instr, name); @@ -1759,26 +1761,18 @@ DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ PyObject *name = GETITEM(names, oparg >> 1); - PyObject *owner = TOP(); if (oparg & 1) { - /* Designed to work in tandem with CALL. */ + /* Designed to work in tandem with CALL, pushes two values. */ PyObject* meth = NULL; - - int meth_found = _PyObject_GetMethod(owner, name, &meth); - - if (meth == NULL) { - /* Most likely attribute wasn't found. */ - goto error; - } - - if (meth_found) { + if (_PyObject_GetMethod(owner, name, &meth)) { /* We can bypass temporary bound method object. meth is unbound method and obj is self. meth | self | arg1 | ... | argN */ - SET_TOP(meth); - PUSH(owner); // self + assert(meth != NULL); // No errors on this branch + res2 = meth; + res = owner; // Transfer ownership } else { /* meth is not an unbound method (but a regular attr, or @@ -1788,20 +1782,22 @@ NULL | meth | arg1 | ... | argN */ - SET_TOP(NULL); Py_DECREF(owner); - PUSH(meth); + if (meth == NULL) goto pop_1_error; + res2 = NULL; + res = meth; } } else { - PyObject *res = PyObject_GetAttr(owner, name); - if (res == NULL) { - goto error; - } + /* Classic, pushes one value. */ + res = PyObject_GetAttr(owner, name); Py_DECREF(owner); - SET_TOP(res); + if (res == NULL) goto pop_1_error; } - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); + STACK_GROW(((oparg & 1) ? 1 : 0)); + POKE(1, res); + if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); } + JUMPBY(9); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index cca86629e48d..e76ddda2f029 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -185,7 +185,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { case MAP_ADD: return 2; case LOAD_ATTR: - return -1; + return 1; case LOAD_ATTR_INSTANCE_VALUE: return -1; case LOAD_ATTR_MODULE: @@ -531,7 +531,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case MAP_ADD: return 0; case LOAD_ATTR: - return -1; + return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_INSTANCE_VALUE: return -1; case LOAD_ATTR_MODULE: @@ -694,7 +694,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { } #endif enum Direction { DIR_NONE, DIR_READ, DIR_WRITE }; -enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; +enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBC00000000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; struct opcode_metadata { enum Direction dir_op1; enum Direction dir_op2; @@ -791,7 +791,7 @@ struct opcode_metadata { [DICT_UPDATE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [DICT_MERGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [MAP_ADD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, - [LOAD_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [LOAD_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 }, [LOAD_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_ATTR_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [LOAD_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 9d894d2ff574..1d703a0a790e 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -26,7 +26,9 @@ ) BEGIN_MARKER = "// BEGIN BYTECODES //" END_MARKER = "// END BYTECODES //" -RE_PREDICTED = r"^\s*(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);\s*(?://.*)?$" +RE_PREDICTED = ( + r"^\s*(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);\s*(?://.*)?$" +) UNUSED = "unused" BITS_PER_CODE_UNIT = 16 @@ -59,7 +61,10 @@ def effect_size(effect: StackEffect) -> tuple[int, str]: At most one of these will be non-zero / non-empty. """ if effect.size: + assert not effect.cond, "Array effects cannot have a condition" return 0, effect.size + elif effect.cond: + return 0, f"{maybe_parenthesize(effect.cond)} ? 1 : 0" else: return 1, "" @@ -132,7 +137,12 @@ def block(self, head: str): yield self.emit("}") - def stack_adjust(self, diff: int, input_effects: list[StackEffect], output_effects: list[StackEffect]): + def stack_adjust( + self, + diff: int, + input_effects: list[StackEffect], + output_effects: list[StackEffect], + ): # TODO: Get rid of 'diff' parameter shrink, isym = list_effect_size(input_effects) grow, osym = list_effect_size(output_effects) @@ -150,10 +160,13 @@ def declare(self, dst: StackEffect, src: StackEffect | None): if dst.name == UNUSED: return typ = f"{dst.type}" if dst.type else "PyObject *" - init = "" if src: cast = self.cast(dst, src) init = f" = {cast}{src.name}" + elif dst.cond: + init = " = NULL" + else: + init = "" sepa = "" if typ.endswith("*") else " " self.emit(f"{typ}{sepa}{dst.name}{init};") @@ -162,7 +175,10 @@ def assign(self, dst: StackEffect, src: StackEffect): return cast = self.cast(dst, src) if m := re.match(r"^PEEK\((.*)\)$", dst.name): - self.emit(f"POKE({m.group(1)}, {cast}{src.name});") + stmt = f"POKE({m.group(1)}, {cast}{src.name});" + if src.cond: + stmt = f"if ({src.cond}) {{ {stmt} }}" + self.emit(stmt) elif m := re.match(r"^&PEEK\(.*\)$", dst.name): # NOTE: MOVE_ITEMS() does not actually exist. # The only supported output array forms are: @@ -234,7 +250,7 @@ def __init__(self, inst: parser.InstDef): if self.register: num_regs = len(self.input_effects) + len(self.output_effects) num_dummies = (num_regs // 2) * 2 + 1 - num_regs - fmt = "I" + "B"*num_regs + "X"*num_dummies + fmt = "I" + "B" * num_regs + "X" * num_dummies else: if variable_used(inst, "oparg"): fmt = "IB" @@ -276,9 +292,13 @@ def write(self, out: Formatter) -> None: # Write input stack effect variable declarations and initializations ieffects = list(reversed(self.input_effects)) for i, ieffect in enumerate(ieffects): - isize = string_effect_size(list_effect_size(ieffects[:i+1])) + isize = string_effect_size( + list_effect_size([ieff for ieff in ieffects[: i + 1]]) + ) if ieffect.size: src = StackEffect(f"&PEEK({isize})", "PyObject **") + elif ieffect.cond: + src = StackEffect(f"({ieffect.cond}) ? PEEK({isize}) : NULL", "") else: src = StackEffect(f"PEEK({isize})", "") out.declare(ieffect, src) @@ -304,14 +324,20 @@ def write(self, out: Formatter) -> None: if not self.register: # Write net stack growth/shrinkage - out.stack_adjust(0, self.input_effects, self.output_effects) + out.stack_adjust( + 0, + [ieff for ieff in self.input_effects], + [oeff for oeff in self.output_effects], + ) # Write output stack effect assignments oeffects = list(reversed(self.output_effects)) for i, oeffect in enumerate(oeffects): if oeffect.name in self.unmoved_names: continue - osize = string_effect_size(list_effect_size(oeffects[:i+1])) + osize = string_effect_size( + list_effect_size([oeff for oeff in oeffects[: i + 1]]) + ) if oeffect.size: dst = StackEffect(f"&PEEK({osize})", "PyObject **") else: @@ -438,6 +464,7 @@ class MacroInstruction(SuperOrMacroInstruction): parts: list[Component | parser.CacheEffect] +AnyInstruction = Instruction | SuperInstruction | MacroInstruction INSTR_FMT_PREFIX = "INSTR_FMT_" @@ -506,6 +533,7 @@ def parse(self) -> None: self.supers = {} self.macros = {} self.families = {} + thing: parser.InstDef | parser.Super | parser.Macro | parser.Family | None while thing := psr.definition(): match thing: case parser.InstDef(name=name): @@ -631,7 +659,9 @@ def analyze_super(self, super: parser.Super) -> SuperInstruction: parts.append(part) format += instr.instr_fmt final_sp = sp - return SuperInstruction(super.name, stack, initial_sp, final_sp, format, super, parts) + return SuperInstruction( + super.name, stack, initial_sp, final_sp, format, super, parts + ) def analyze_macro(self, macro: parser.Macro) -> MacroInstruction: components = self.check_macro_components(macro) @@ -657,7 +687,9 @@ def analyze_macro(self, macro: parser.Macro) -> MacroInstruction: case _: typing.assert_never(component) final_sp = sp - return MacroInstruction(macro.name, stack, initial_sp, final_sp, format, macro, parts) + return MacroInstruction( + macro.name, stack, initial_sp, final_sp, format, macro, parts + ) def analyze_instruction( self, instr: Instruction, stack: list[StackEffect], sp: int @@ -710,7 +742,9 @@ def stack_analysis( for thing in components: match thing: case Instruction() as instr: - if any(eff.size for eff in instr.input_effects + instr.output_effects): + if any( + eff.size for eff in instr.input_effects + instr.output_effects + ): # TODO: Eventually this will be needed, at least for macros. self.error( f"Instruction {instr.name!r} has variable-sized stack effect, " @@ -736,16 +770,16 @@ def stack_analysis( def get_stack_effect_info( self, thing: parser.InstDef | parser.Super | parser.Macro - ) -> tuple[Instruction|None, str, str]: - - def effect_str(effect: list[StackEffect]) -> str: - if getattr(thing, 'kind', None) == 'legacy': + ) -> tuple[AnyInstruction | None, str, str]: + def effect_str(effects: list[StackEffect]) -> str: + if getattr(thing, "kind", None) == "legacy": return str(-1) - n_effect, sym_effect = list_effect_size(effect) + n_effect, sym_effect = list_effect_size(effects) if sym_effect: return f"{sym_effect} + {n_effect}" if n_effect else sym_effect return str(n_effect) + instr: AnyInstruction | None match thing: case parser.InstDef(): if thing.kind != "op": @@ -754,34 +788,43 @@ def effect_str(effect: list[StackEffect]) -> str: pushed = effect_str(instr.output_effects) else: instr = None - popped = pushed = "", "" + popped = "" + pushed = "" case parser.Super(): instr = self.super_instrs[thing.name] - popped = '+'.join(effect_str(comp.instr.input_effects) for comp in instr.parts) - pushed = '+'.join(effect_str(comp.instr.output_effects) for comp in instr.parts) + popped = "+".join( + effect_str(comp.instr.input_effects) for comp in instr.parts + ) + pushed = "+".join( + effect_str(comp.instr.output_effects) for comp in instr.parts + ) case parser.Macro(): instr = self.macro_instrs[thing.name] parts = [comp for comp in instr.parts if isinstance(comp, Component)] - popped = '+'.join(effect_str(comp.instr.input_effects) for comp in parts) - pushed = '+'.join(effect_str(comp.instr.output_effects) for comp in parts) + popped = "+".join( + effect_str(comp.instr.input_effects) for comp in parts + ) + pushed = "+".join( + effect_str(comp.instr.output_effects) for comp in parts + ) case _: typing.assert_never(thing) return instr, popped, pushed def write_stack_effect_functions(self) -> None: - popped_data = [] - pushed_data = [] + popped_data: list[tuple[AnyInstruction, str]] = [] + pushed_data: list[tuple[AnyInstruction, str]] = [] for thing in self.everything: instr, popped, pushed = self.get_stack_effect_info(thing) if instr is not None: - popped_data.append( (instr, popped) ) - pushed_data.append( (instr, pushed) ) + popped_data.append((instr, popped)) + pushed_data.append((instr, pushed)) - def write_function(direction: str, data: list[tuple[Instruction, str]]) -> None: - self.out.emit("\n#ifndef NDEBUG"); - self.out.emit("static int"); + def write_function(direction: str, data: list[tuple[AnyInstruction, str]]) -> None: + self.out.emit("\n#ifndef NDEBUG") + self.out.emit("static int") self.out.emit(f"_PyOpcode_num_{direction}(int opcode, int oparg) {{") - self.out.emit(" switch(opcode) {"); + self.out.emit(" switch(opcode) {") for instr, effect in data: self.out.emit(f" case {instr.name}:") self.out.emit(f" return {effect};") @@ -789,10 +832,10 @@ def write_function(direction: str, data: list[tuple[Instruction, str]]) -> None: self.out.emit(" Py_UNREACHABLE();") self.out.emit(" }") self.out.emit("}") - self.out.emit("#endif"); + self.out.emit("#endif") - write_function('popped', popped_data) - write_function('pushed', pushed_data) + write_function("popped", popped_data) + write_function("pushed", pushed_data) def write_metadata(self) -> None: """Write instruction metadata to output file.""" @@ -865,21 +908,21 @@ def write_metadata_for_inst(self, instr: Instruction) -> None: directions.extend("DIR_NONE" for _ in range(3)) dir_op1, dir_op2, dir_op3 = directions[:3] self.out.emit( - f' [{instr.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{instr.instr_fmt} }},' + f" [{instr.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{instr.instr_fmt} }}," ) def write_metadata_for_super(self, sup: SuperInstruction) -> None: """Write metadata for a super-instruction.""" dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" self.out.emit( - f' [{sup.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{sup.instr_fmt} }},' + f" [{sup.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{sup.instr_fmt} }}," ) def write_metadata_for_macro(self, mac: MacroInstruction) -> None: """Write metadata for a macro-instruction.""" dir_op1 = dir_op2 = dir_op3 = "DIR_NONE" self.out.emit( - f' [{mac.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{mac.instr_fmt} }},' + f" [{mac.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{mac.instr_fmt} }}," ) def write_instructions(self) -> None: @@ -1012,7 +1055,9 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]: # Separate PREDICT(...) macros from end predictions: list[str] = [] - while blocklines and (m := re.match(r"^\s*PREDICT\((\w+)\);\s*(?://.*)?$", blocklines[-1])): + while blocklines and ( + m := re.match(r"^\s*PREDICT\((\w+)\);\s*(?://.*)?$", blocklines[-1]) + ): predictions.insert(0, m.group(1)) blocklines.pop() @@ -1029,13 +1074,22 @@ def always_exits(lines: list[str]) -> bool: return False line = line[12:] return line.startswith( - ("goto ", "return ", "DISPATCH", "GO_TO_", "Py_UNREACHABLE()", "ERROR_IF(true, ") + ( + "goto ", + "return ", + "DISPATCH", + "GO_TO_", + "Py_UNREACHABLE()", + "ERROR_IF(true, ", + ) ) def variable_used(node: parser.Node, name: str) -> bool: """Determine whether a variable with a given name is used in a node.""" - return any(token.kind == "IDENTIFIER" and token.text == name for token in node.tokens) + return any( + token.kind == "IDENTIFIER" and token.text == name for token in node.tokens + ) def main(): diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py index c2cebe96ccd6..ced66faee493 100644 --- a/Tools/cases_generator/parser.py +++ b/Tools/cases_generator/parser.py @@ -48,9 +48,6 @@ def to_text(self, dedent: int = 0) -> str: context = self.context if not context: return "" - tokens = context.owner.tokens - begin = context.begin - end = context.end return lx.to_text(self.tokens, dedent) @property @@ -74,13 +71,13 @@ class Block(Node): class StackEffect(Node): name: str type: str = "" # Optional `:type` + cond: str = "" # Optional `if (cond)` size: str = "" # Optional `[size]` - # Note: we can have type or size but not both - # TODO: condition (can be combined with type but not size) + # Note: size cannot be combined with type or cond @dataclass -class Dimension(Node): +class Expression(Node): size: str @@ -239,31 +236,39 @@ def cache_effect(self) -> CacheEffect | None: @contextual def stack_effect(self) -> StackEffect | None: - # IDENTIFIER - # | IDENTIFIER ':' IDENTIFIER - # | IDENTIFIER '[' dimension ']' - # TODO: Conditions + # IDENTIFIER [':' IDENTIFIER] ['if' '(' expression ')'] + # | IDENTIFIER '[' expression ']' if tkn := self.expect(lx.IDENTIFIER): + type_text = "" if self.expect(lx.COLON): - typ = self.require(lx.IDENTIFIER) - return StackEffect(tkn.text, typ.text) - elif self.expect(lx.LBRACKET): - if not (dim := self.dimension()): - raise self.make_syntax_error("Expected dimension") + type_text = self.require(lx.IDENTIFIER).text.strip() + cond_text = "" + if self.expect(lx.IF): + self.require(lx.LPAREN) + if not (cond := self.expression()): + raise self.make_syntax_error("Expected condition") + self.require(lx.RPAREN) + cond_text = cond.text.strip() + size_text = "" + if self.expect(lx.LBRACKET): + if type_text or cond_text: + raise self.make_syntax_error("Unexpected [") + if not (size := self.expression()): + raise self.make_syntax_error("Expected expression") self.require(lx.RBRACKET) - return StackEffect(tkn.text, "PyObject **", dim.text.strip()) - else: - return StackEffect(tkn.text) + type_text = "PyObject **" + size_text = size.text.strip() + return StackEffect(tkn.text, type_text, cond_text, size_text) @contextual - def dimension(self) -> Dimension | None: + def expression(self) -> Expression | None: tokens: list[lx.Token] = [] - while (tkn := self.peek()) and tkn.kind != lx.RBRACKET: + while (tkn := self.peek()) and tkn.kind not in (lx.RBRACKET, lx.RPAREN): tokens.append(tkn) self.next() if not tokens: return None - return Dimension(lx.to_text(tokens).strip()) + return Expression(lx.to_text(tokens).strip()) @contextual def super_def(self) -> Super | None: @@ -366,7 +371,7 @@ def members(self) -> list[str] | None: return None @contextual - def block(self) -> Block: + def block(self) -> Block | None: if self.c_blob(): return Block() diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index bd1b974399ab..6e6c60782d73 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -9,19 +9,19 @@ def test_effect_sizes(): input_effects = [ - x := StackEffect("x", "", ""), - y := StackEffect("y", "", "oparg"), - z := StackEffect("z", "", "oparg*2"), + x := StackEffect("x", "", "", ""), + y := StackEffect("y", "", "", "oparg"), + z := StackEffect("z", "", "", "oparg*2"), ] output_effects = [ - a := StackEffect("a", "", ""), - b := StackEffect("b", "", "oparg*4"), - c := StackEffect("c", "", ""), + StackEffect("a", "", "", ""), + StackEffect("b", "", "", "oparg*4"), + StackEffect("c", "", "", ""), ] other_effects = [ - p := StackEffect("p", "", "oparg<<1"), - q := StackEffect("q", "", ""), - r := StackEffect("r", "", ""), + StackEffect("p", "", "", "oparg<<1"), + StackEffect("q", "", "", ""), + StackEffect("r", "", "", ""), ] assert generate_cases.effect_size(x) == (1, "") assert generate_cases.effect_size(y) == (0, "oparg") @@ -54,6 +54,12 @@ def run_cases_test(input: str, expected: str): while lines and lines[0].startswith("// "): lines.pop(0) actual = "".join(lines) + # if actual.rstrip() != expected.rstrip(): + # print("Actual:") + # print(actual) + # print("Expected:") + # print(expected) + # print("End") assert actual.rstrip() == expected.rstrip() def test_legacy(): @@ -475,3 +481,28 @@ def test_register(): } """ run_cases_test(input, output) + +def test_cond_effect(): + input = """ + inst(OP, (aa, input if (oparg & 1), cc -- xx, output if (oparg & 2), zz)) { + output = spam(oparg, input); + } + """ + output = """ + TARGET(OP) { + PyObject *cc = PEEK(1); + PyObject *input = (oparg & 1) ? PEEK(1 + ((oparg & 1) ? 1 : 0)) : NULL; + PyObject *aa = PEEK(2 + ((oparg & 1) ? 1 : 0)); + PyObject *xx; + PyObject *output = NULL; + PyObject *zz; + output = spam(oparg, input); + STACK_SHRINK(((oparg & 1) ? 1 : 0)); + STACK_GROW(((oparg & 2) ? 1 : 0)); + POKE(1, zz); + if (oparg & 2) { POKE(1 + ((oparg & 2) ? 1 : 0), output); } + POKE(2 + ((oparg & 2) ? 1 : 0), xx); + DISPATCH(); + } + """ + run_cases_test(input, output) From webhook-mailer at python.org Sun Jan 29 20:52:34 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 30 Jan 2023 01:52:34 -0000 Subject: [Python-checkins] gh-98240: Updated Path.rename docs, when it is atomic (GH-98245) Message-ID: https://github.com/python/cpython/commit/3bcb630e078464185f605d0613d9633624273f74 commit: 3bcb630e078464185f605d0613d9633624273f74 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-29T17:52:29-08:00 summary: gh-98240: Updated Path.rename docs, when it is atomic (GH-98245) (cherry picked from commit 0023f51debeeeef483a6362ee12d67c4da086af3) Co-authored-by: Mateusz files: M Doc/library/pathlib.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index e6db8caa16bb..e8bb00fc5743 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1071,6 +1071,8 @@ call fails (for example because the path doesn't exist). relative to the current working directory, *not* the directory of the Path object. + It is implemented in terms of :func:`os.rename` and gives the same guarantees. + .. versionchanged:: 3.8 Added return value, return the new Path instance. From webhook-mailer at python.org Sun Jan 29 20:52:52 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 30 Jan 2023 01:52:52 -0000 Subject: [Python-checkins] gh-98240: Updated Path.rename docs, when it is atomic (GH-98245) Message-ID: https://github.com/python/cpython/commit/efcab4dd6353bdbfd39aeb3b6b23626579e91957 commit: efcab4dd6353bdbfd39aeb3b6b23626579e91957 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-29T17:52:46-08:00 summary: gh-98240: Updated Path.rename docs, when it is atomic (GH-98245) (cherry picked from commit 0023f51debeeeef483a6362ee12d67c4da086af3) Co-authored-by: Mateusz files: M Doc/library/pathlib.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index ee140e871cb4..878b97926bd2 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1068,6 +1068,8 @@ call fails (for example because the path doesn't exist). relative to the current working directory, *not* the directory of the Path object. + It is implemented in terms of :func:`os.rename` and gives the same guarantees. + .. versionchanged:: 3.8 Added return value, return the new Path instance. From webhook-mailer at python.org Mon Jan 30 05:03:11 2023 From: webhook-mailer at python.org (markshannon) Date: Mon, 30 Jan 2023 10:03:11 -0000 Subject: [Python-checkins] GH-101291: Refactor the `PyLongObject` struct into object header and PyLongValue struct. (GH-101292) Message-ID: https://github.com/python/cpython/commit/c1b1f51cd1632f0b77dacd43092fb44ed5e053a9 commit: c1b1f51cd1632f0b77dacd43092fb44ed5e053a9 branch: main author: Mark Shannon committer: markshannon date: 2023-01-30T10:03:04Z summary: GH-101291: Refactor the `PyLongObject` struct into object header and PyLongValue struct. (GH-101292) files: A Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst M Include/cpython/longintrepr.h M Include/internal/pycore_runtime_init.h M Modules/_decimal/_decimal.c M Objects/boolobject.c M Objects/listobject.c M Objects/longobject.c M Python/bltinmodule.c M Python/bytecodes.c M Python/generated_cases.c.h M Python/marshal.c M Python/specialize.c M Tools/gdb/libpython.py diff --git a/Include/cpython/longintrepr.h b/Include/cpython/longintrepr.h index 6d52427508b5..810daa83165e 100644 --- a/Include/cpython/longintrepr.h +++ b/Include/cpython/longintrepr.h @@ -79,9 +79,14 @@ typedef long stwodigits; /* signed variant of twodigits */ aware that ints abuse ob_size's sign bit. */ -struct _longobject { - PyObject_VAR_HEAD +typedef struct _PyLongValue { + Py_ssize_t ob_size; /* Number of items in variable part */ digit ob_digit[1]; +} _PyLongValue; + +struct _longobject { + PyObject_HEAD + _PyLongValue long_value; }; PyAPI_FUNC(PyLongObject *) _PyLong_New(Py_ssize_t); diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index f10dccc01580..c6a27d076eae 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -149,9 +149,11 @@ extern "C" { #define _PyLong_DIGIT_INIT(val) \ { \ - _PyVarObject_IMMORTAL_INIT(&PyLong_Type, \ - ((val) == 0 ? 0 : ((val) > 0 ? 1 : -1))), \ - .ob_digit = { ((val) >= 0 ? (val) : -(val)) }, \ + .ob_base = _PyObject_IMMORTAL_INIT(&PyLong_Type), \ + .long_value = { \ + ((val) == 0 ? 0 : ((val) > 0 ? 1 : -1)), \ + { ((val) >= 0 ? (val) : -(val)) }, \ + } \ } #define _PyBytes_SIMPLE_INIT(CH, LEN) \ diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst new file mode 100644 index 000000000000..b585ff5a817e --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-24-17-13-32.gh-issue-101291.Yr6u_c.rst @@ -0,0 +1,2 @@ +Refactor the ``PyLongObject`` struct into a normal Python object header and +a ``PyLongValue`` struct. diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index bc97615ffb4b..5936fbaaf35e 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -2171,16 +2171,16 @@ dec_from_long(PyTypeObject *type, PyObject *v, } if (len == 1) { - _dec_settriple(dec, sign, *l->ob_digit, 0); + _dec_settriple(dec, sign, *l->long_value.ob_digit, 0); mpd_qfinalize(MPD(dec), ctx, status); return dec; } #if PYLONG_BITS_IN_DIGIT == 30 - mpd_qimport_u32(MPD(dec), l->ob_digit, len, sign, PyLong_BASE, + mpd_qimport_u32(MPD(dec), l->long_value.ob_digit, len, sign, PyLong_BASE, ctx, status); #elif PYLONG_BITS_IN_DIGIT == 15 - mpd_qimport_u16(MPD(dec), l->ob_digit, len, sign, PyLong_BASE, + mpd_qimport_u16(MPD(dec), l->long_value.ob_digit, len, sign, PyLong_BASE, ctx, status); #else #error "PYLONG_BITS_IN_DIGIT should be 15 or 30" @@ -3543,11 +3543,11 @@ dec_as_long(PyObject *dec, PyObject *context, int round) return NULL; } - memcpy(pylong->ob_digit, ob_digit, n * sizeof(digit)); + memcpy(pylong->long_value.ob_digit, ob_digit, n * sizeof(digit)); mpd_free(ob_digit); i = n; - while ((i > 0) && (pylong->ob_digit[i-1] == 0)) { + while ((i > 0) && (pylong->long_value.ob_digit[i-1] == 0)) { i--; } diff --git a/Objects/boolobject.c b/Objects/boolobject.c index 18666f88cbde..55b4a4077ab7 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -195,11 +195,11 @@ PyTypeObject PyBool_Type = { /* The objects representing bool values False and True */ struct _longobject _Py_FalseStruct = { - PyVarObject_HEAD_INIT(&PyBool_Type, 0) - { 0 } + PyObject_HEAD_INIT(&PyBool_Type) + { 0, { 0 } } }; struct _longobject _Py_TrueStruct = { - PyVarObject_HEAD_INIT(&PyBool_Type, 1) - { 1 } + PyObject_HEAD_INIT(&PyBool_Type) + { 1, { 1 } } }; diff --git a/Objects/listobject.c b/Objects/listobject.c index 6629775604b6..ca6b71231134 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -2155,8 +2155,8 @@ unsafe_long_compare(PyObject *v, PyObject *w, MergeState *ms) vl = (PyLongObject*)v; wl = (PyLongObject*)w; - v0 = Py_SIZE(vl) == 0 ? 0 : (sdigit)vl->ob_digit[0]; - w0 = Py_SIZE(wl) == 0 ? 0 : (sdigit)wl->ob_digit[0]; + v0 = Py_SIZE(vl) == 0 ? 0 : (sdigit)vl->long_value.ob_digit[0]; + w0 = Py_SIZE(wl) == 0 ? 0 : (sdigit)wl->long_value.ob_digit[0]; if (Py_SIZE(vl) < 0) v0 = -v0; diff --git a/Objects/longobject.c b/Objects/longobject.c index 51ac86961c99..65bf15648b07 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -30,7 +30,7 @@ static inline stwodigits medium_value(PyLongObject *x) { assert(IS_MEDIUM_VALUE(x)); - return ((stwodigits)Py_SIZE(x)) * x->ob_digit[0]; + return ((stwodigits)Py_SIZE(x)) * x->long_value.ob_digit[0]; } #define IS_SMALL_INT(ival) (-_PY_NSMALLNEGINTS <= (ival) && (ival) < _PY_NSMALLPOSINTS) @@ -129,7 +129,7 @@ long_normalize(PyLongObject *v) Py_ssize_t j = Py_ABS(Py_SIZE(v)); Py_ssize_t i = j; - while (i > 0 && v->ob_digit[i-1] == 0) + while (i > 0 && v->long_value.ob_digit[i-1] == 0) --i; if (i != j) { Py_SET_SIZE(v, (Py_SIZE(v) < 0) ? -(i) : i); @@ -141,7 +141,7 @@ long_normalize(PyLongObject *v) Return NULL and set exception if we run out of memory. */ #define MAX_LONG_DIGITS \ - ((PY_SSIZE_T_MAX - offsetof(PyLongObject, ob_digit))/sizeof(digit)) + ((PY_SSIZE_T_MAX - offsetof(PyLongObject, long_value.ob_digit))/sizeof(digit)) PyLongObject * _PyLong_New(Py_ssize_t size) @@ -160,7 +160,7 @@ _PyLong_New(Py_ssize_t size) sizeof(PyVarObject) instead of the offsetof, but this risks being incorrect in the presence of padding between the PyVarObject header and the digits. */ - result = PyObject_Malloc(offsetof(PyLongObject, ob_digit) + + result = PyObject_Malloc(offsetof(PyLongObject, long_value.ob_digit) + ndigits*sizeof(digit)); if (!result) { PyErr_NoMemory(); @@ -190,7 +190,7 @@ _PyLong_Copy(PyLongObject *src) if (result != NULL) { Py_SET_SIZE(result, Py_SIZE(src)); while (--i >= 0) { - result->ob_digit[i] = src->ob_digit[i]; + result->long_value.ob_digit[i] = src->long_value.ob_digit[i]; } } return (PyObject *)result; @@ -210,7 +210,7 @@ _PyLong_FromMedium(sdigit x) Py_ssize_t sign = x < 0 ? -1: 1; digit abs_x = x < 0 ? -x : x; _PyObject_InitVar((PyVarObject*)v, &PyLong_Type, sign); - v->ob_digit[0] = abs_x; + v->long_value.ob_digit[0] = abs_x; return (PyObject*)v; } @@ -241,7 +241,7 @@ _PyLong_FromLarge(stwodigits ival) } PyLongObject *v = _PyLong_New(ndigits); if (v != NULL) { - digit *p = v->ob_digit; + digit *p = v->long_value.ob_digit; Py_SET_SIZE(v, ndigits * sign); t = abs_ival; while (t) { @@ -285,7 +285,7 @@ _PyLong_AssignValue(PyObject **target, Py_ssize_t value) // Since the primary use-case is iterating over ranges, which // are typically positive, only do this optimization // for positive integers (for now). - ((PyLongObject *)old)->ob_digit[0] = + ((PyLongObject *)old)->long_value.ob_digit[0] = Py_SAFE_DOWNCAST(value, Py_ssize_t, digit); return 0; } @@ -346,7 +346,7 @@ PyLong_FromLong(long ival) /* Construct output value. */ v = _PyLong_New(ndigits); if (v != NULL) { - digit *p = v->ob_digit; + digit *p = v->long_value.ob_digit; Py_SET_SIZE(v, ival < 0 ? -ndigits : ndigits); t = abs_ival; while (t) { @@ -373,7 +373,7 @@ PyLong_FromLong(long ival) if (v == NULL) { \ return NULL; \ } \ - digit *p = v->ob_digit; \ + digit *p = v->long_value.ob_digit; \ while ((ival)) { \ *p++ = (digit)((ival) & PyLong_MASK); \ (ival) >>= PyLong_SHIFT; \ @@ -452,7 +452,7 @@ PyLong_FromDouble(double dval) frac = ldexp(frac, (expo-1) % PyLong_SHIFT + 1); for (i = ndig; --i >= 0; ) { digit bits = (digit)frac; - v->ob_digit[i] = bits; + v->long_value.ob_digit[i] = bits; frac = frac - (double)bits; frac = ldexp(frac, PyLong_SHIFT); } @@ -516,13 +516,13 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) switch (i) { case -1: - res = -(sdigit)v->ob_digit[0]; + res = -(sdigit)v->long_value.ob_digit[0]; break; case 0: res = 0; break; case 1: - res = v->ob_digit[0]; + res = v->long_value.ob_digit[0]; break; default: sign = 1; @@ -533,7 +533,7 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) } while (--i >= 0) { prev = x; - x = (x << PyLong_SHIFT) | v->ob_digit[i]; + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; if ((x >> PyLong_SHIFT) != prev) { *overflow = sign; goto exit; @@ -617,9 +617,9 @@ PyLong_AsSsize_t(PyObject *vv) { v = (PyLongObject *)vv; i = Py_SIZE(v); switch (i) { - case -1: return -(sdigit)v->ob_digit[0]; + case -1: return -(sdigit)v->long_value.ob_digit[0]; case 0: return 0; - case 1: return v->ob_digit[0]; + case 1: return v->long_value.ob_digit[0]; } sign = 1; x = 0; @@ -629,7 +629,7 @@ PyLong_AsSsize_t(PyObject *vv) { } while (--i >= 0) { prev = x; - x = (x << PyLong_SHIFT) | v->ob_digit[i]; + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; if ((x >> PyLong_SHIFT) != prev) goto overflow; } @@ -679,11 +679,11 @@ PyLong_AsUnsignedLong(PyObject *vv) } switch (i) { case 0: return 0; - case 1: return v->ob_digit[0]; + case 1: return v->long_value.ob_digit[0]; } while (--i >= 0) { prev = x; - x = (x << PyLong_SHIFT) | v->ob_digit[i]; + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; if ((x >> PyLong_SHIFT) != prev) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert " @@ -723,11 +723,11 @@ PyLong_AsSize_t(PyObject *vv) } switch (i) { case 0: return 0; - case 1: return v->ob_digit[0]; + case 1: return v->long_value.ob_digit[0]; } while (--i >= 0) { prev = x; - x = (x << PyLong_SHIFT) | v->ob_digit[i]; + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; if ((x >> PyLong_SHIFT) != prev) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C size_t"); @@ -756,7 +756,7 @@ _PyLong_AsUnsignedLongMask(PyObject *vv) i = Py_SIZE(v); switch (i) { case 0: return 0; - case 1: return v->ob_digit[0]; + case 1: return v->long_value.ob_digit[0]; } sign = 1; x = 0; @@ -765,7 +765,7 @@ _PyLong_AsUnsignedLongMask(PyObject *vv) i = -i; } while (--i >= 0) { - x = (x << PyLong_SHIFT) | v->ob_digit[i]; + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; } return x * sign; } @@ -826,9 +826,9 @@ _PyLong_NumBits(PyObject *vv) assert(v != NULL); assert(PyLong_Check(v)); ndigits = Py_ABS(Py_SIZE(v)); - assert(ndigits == 0 || v->ob_digit[ndigits - 1] != 0); + assert(ndigits == 0 || v->long_value.ob_digit[ndigits - 1] != 0); if (ndigits > 0) { - digit msd = v->ob_digit[ndigits - 1]; + digit msd = v->long_value.ob_digit[ndigits - 1]; if ((size_t)(ndigits - 1) > SIZE_MAX / (size_t)PyLong_SHIFT) goto Overflow; result = (size_t)(ndigits - 1) * (size_t)PyLong_SHIFT; @@ -855,7 +855,7 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n, size_t numsignificantbytes; /* number of bytes that matter */ Py_ssize_t ndigits; /* number of Python int digits */ PyLongObject* v; /* result */ - Py_ssize_t idigit = 0; /* next free index in v->ob_digit */ + Py_ssize_t idigit = 0; /* next free index in v->long_value.ob_digit */ if (n == 0) return PyLong_FromLong(0L); @@ -937,7 +937,7 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n, if (accumbits >= PyLong_SHIFT) { /* There's enough to fill a Python digit. */ assert(idigit < ndigits); - v->ob_digit[idigit] = (digit)(accum & PyLong_MASK); + v->long_value.ob_digit[idigit] = (digit)(accum & PyLong_MASK); ++idigit; accum >>= PyLong_SHIFT; accumbits -= PyLong_SHIFT; @@ -947,7 +947,7 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n, assert(accumbits < PyLong_SHIFT); if (accumbits) { assert(idigit < ndigits); - v->ob_digit[idigit] = (digit)accum; + v->long_value.ob_digit[idigit] = (digit)accum; ++idigit; } } @@ -961,7 +961,7 @@ _PyLong_AsByteArray(PyLongObject* v, unsigned char* bytes, size_t n, int little_endian, int is_signed) { - Py_ssize_t i; /* index into v->ob_digit */ + Py_ssize_t i; /* index into v->long_value.ob_digit */ Py_ssize_t ndigits; /* |v->ob_size| */ twodigits accum; /* sliding register */ unsigned int accumbits; /* # bits in accum */ @@ -1000,13 +1000,13 @@ _PyLong_AsByteArray(PyLongObject* v, It's crucial that every Python digit except for the MSD contribute exactly PyLong_SHIFT bits to the total, so first assert that the int is normalized. */ - assert(ndigits == 0 || v->ob_digit[ndigits - 1] != 0); + assert(ndigits == 0 || v->long_value.ob_digit[ndigits - 1] != 0); j = 0; accum = 0; accumbits = 0; carry = do_twos_comp ? 1 : 0; for (i = 0; i < ndigits; ++i) { - digit thisdigit = v->ob_digit[i]; + digit thisdigit = v->long_value.ob_digit[i]; if (do_twos_comp) { thisdigit = (thisdigit ^ PyLong_MASK) + carry; carry = thisdigit >> PyLong_SHIFT; @@ -1173,7 +1173,7 @@ PyLong_FromLongLong(long long ival) /* Construct output value. */ v = _PyLong_New(ndigits); if (v != NULL) { - digit *p = v->ob_digit; + digit *p = v->long_value.ob_digit; Py_SET_SIZE(v, ival < 0 ? -ndigits : ndigits); t = abs_ival; while (t) { @@ -1216,7 +1216,7 @@ PyLong_FromSsize_t(Py_ssize_t ival) } v = _PyLong_New(ndigits); if (v != NULL) { - digit *p = v->ob_digit; + digit *p = v->long_value.ob_digit; Py_SET_SIZE(v, negative ? -ndigits : ndigits); t = abs_ival; while (t) { @@ -1256,13 +1256,13 @@ PyLong_AsLongLong(PyObject *vv) res = 0; switch(Py_SIZE(v)) { case -1: - bytes = -(sdigit)v->ob_digit[0]; + bytes = -(sdigit)v->long_value.ob_digit[0]; break; case 0: bytes = 0; break; case 1: - bytes = v->ob_digit[0]; + bytes = v->long_value.ob_digit[0]; break; default: res = _PyLong_AsByteArray((PyLongObject *)v, (unsigned char *)&bytes, @@ -1301,7 +1301,7 @@ PyLong_AsUnsignedLongLong(PyObject *vv) v = (PyLongObject*)vv; switch(Py_SIZE(v)) { case 0: return 0; - case 1: return v->ob_digit[0]; + case 1: return v->long_value.ob_digit[0]; } res = _PyLong_AsByteArray((PyLongObject *)vv, (unsigned char *)&bytes, @@ -1332,7 +1332,7 @@ _PyLong_AsUnsignedLongLongMask(PyObject *vv) v = (PyLongObject *)vv; switch(Py_SIZE(v)) { case 0: return 0; - case 1: return v->ob_digit[0]; + case 1: return v->long_value.ob_digit[0]; } i = Py_SIZE(v); sign = 1; @@ -1342,7 +1342,7 @@ _PyLong_AsUnsignedLongLongMask(PyObject *vv) i = -i; } while (--i >= 0) { - x = (x << PyLong_SHIFT) | v->ob_digit[i]; + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; } return x * sign; } @@ -1413,13 +1413,13 @@ PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow) switch (i) { case -1: - res = -(sdigit)v->ob_digit[0]; + res = -(sdigit)v->long_value.ob_digit[0]; break; case 0: res = 0; break; case 1: - res = v->ob_digit[0]; + res = v->long_value.ob_digit[0]; break; default: sign = 1; @@ -1430,7 +1430,7 @@ PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow) } while (--i >= 0) { prev = x; - x = (x << PyLong_SHIFT) + v->ob_digit[i]; + x = (x << PyLong_SHIFT) + v->long_value.ob_digit[i]; if ((x >> PyLong_SHIFT) != prev) { *overflow = sign; goto exit; @@ -1701,7 +1701,7 @@ divrem1(PyLongObject *a, digit n, digit *prem) z = _PyLong_New(size); if (z == NULL) return NULL; - *prem = inplace_divrem1(z->ob_digit, a->ob_digit, size, n); + *prem = inplace_divrem1(z->long_value.ob_digit, a->long_value.ob_digit, size, n); return long_normalize(z); } @@ -1730,7 +1730,7 @@ rem1(PyLongObject *a, digit n) assert(n > 0 && n <= PyLong_MASK); return (PyLongObject *)PyLong_FromLong( - (long)inplace_rem1(a->ob_digit, size, n) + (long)inplace_rem1(a->long_value.ob_digit, size, n) ); } @@ -1880,8 +1880,8 @@ long_to_decimal_string_internal(PyObject *aa, /* convert array of base _PyLong_BASE digits in pin to an array of base _PyLong_DECIMAL_BASE digits in pout, following Knuth (TAOCP, Volume 2 (3rd edn), section 4.4, Method 1b). */ - pin = a->ob_digit; - pout = scratch->ob_digit; + pin = a->long_value.ob_digit; + pout = scratch->long_value.ob_digit; size = 0; for (i = size_a; --i >= 0; ) { digit hi = pin[i]; @@ -2086,7 +2086,7 @@ long_format_binary(PyObject *aa, int base, int alternate, return -1; } size_a_in_bits = (size_a - 1) * PyLong_SHIFT + - bit_length_digit(a->ob_digit[size_a - 1]); + bit_length_digit(a->long_value.ob_digit[size_a - 1]); /* Allow 1 character for a '-' sign. */ sz = negative + (size_a_in_bits + (bits - 1)) / bits; } @@ -2123,7 +2123,7 @@ long_format_binary(PyObject *aa, int base, int alternate, int accumbits = 0; /* # of bits in accum */ \ Py_ssize_t i; \ for (i = 0; i < size_a; ++i) { \ - accum |= (twodigits)a->ob_digit[i] << accumbits; \ + accum |= (twodigits)a->long_value.ob_digit[i] << accumbits; \ accumbits += PyLong_SHIFT; \ assert(accumbits >= bits); \ do { \ @@ -2321,7 +2321,7 @@ long_from_binary_base(const char *start, const char *end, Py_ssize_t digits, int */ accum = 0; bits_in_accum = 0; - pdigit = z->ob_digit; + pdigit = z->long_value.ob_digit; p = end; while (--p >= start) { int k; @@ -2334,7 +2334,7 @@ long_from_binary_base(const char *start, const char *end, Py_ssize_t digits, int bits_in_accum += bits_per_char; if (bits_in_accum >= PyLong_SHIFT) { *pdigit++ = (digit)(accum & PyLong_MASK); - assert(pdigit - z->ob_digit <= n); + assert(pdigit - z->long_value.ob_digit <= n); accum >>= PyLong_SHIFT; bits_in_accum -= PyLong_SHIFT; assert(bits_in_accum < PyLong_SHIFT); @@ -2343,9 +2343,9 @@ long_from_binary_base(const char *start, const char *end, Py_ssize_t digits, int if (bits_in_accum) { assert(bits_in_accum <= PyLong_SHIFT); *pdigit++ = (digit)accum; - assert(pdigit - z->ob_digit <= n); + assert(pdigit - z->long_value.ob_digit <= n); } - while (pdigit - z->ob_digit < n) + while (pdigit - z->long_value.ob_digit < n) *pdigit++ = 0; *res = z; return 0; @@ -2512,7 +2512,7 @@ long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits, /* Create an int object that can contain the largest possible * integer with this base and length. Note that there's no - * need to initialize z->ob_digit -- no slot is read up before + * need to initialize z->long_value.ob_digit -- no slot is read up before * being stored into. */ double fsize_z = (double)digits * log_base_BASE[base] + 1.0; @@ -2571,7 +2571,7 @@ long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits, } /* Multiply z by convmult, and add c. */ - pz = z->ob_digit; + pz = z->long_value.ob_digit; pzstop = pz + Py_SIZE(z); for (; pz < pzstop; ++pz) { c += (twodigits)*pz * convmult; @@ -2595,11 +2595,11 @@ long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits, *res = NULL; return 0; } - memcpy(tmp->ob_digit, - z->ob_digit, + memcpy(tmp->long_value.ob_digit, + z->long_value.ob_digit, sizeof(digit) * size_z); Py_SETREF(z, tmp); - z->ob_digit[size_z] = (digit)c; + z->long_value.ob_digit[size_z] = (digit)c; ++size_z; } } @@ -2901,7 +2901,7 @@ long_divrem(PyLongObject *a, PyLongObject *b, } if (size_a < size_b || (size_a == size_b && - a->ob_digit[size_a-1] < b->ob_digit[size_b-1])) { + a->long_value.ob_digit[size_a-1] < b->long_value.ob_digit[size_b-1])) { /* |a| < |b|. */ *prem = (PyLongObject *)long_long((PyObject *)a); if (*prem == NULL) { @@ -2913,7 +2913,7 @@ long_divrem(PyLongObject *a, PyLongObject *b, } if (size_b == 1) { digit rem = 0; - z = divrem1(a, b->ob_digit[0], &rem); + z = divrem1(a, b->long_value.ob_digit[0], &rem); if (z == NULL) return -1; *prem = (PyLongObject *) PyLong_FromLong((long)rem); @@ -2965,13 +2965,13 @@ long_rem(PyLongObject *a, PyLongObject *b, PyLongObject **prem) } if (size_a < size_b || (size_a == size_b && - a->ob_digit[size_a-1] < b->ob_digit[size_b-1])) { + a->long_value.ob_digit[size_a-1] < b->long_value.ob_digit[size_b-1])) { /* |a| < |b|. */ *prem = (PyLongObject *)long_long((PyObject *)a); return -(*prem == NULL); } if (size_b == 1) { - *prem = rem1(a, b->ob_digit[0]); + *prem = rem1(a, b->long_value.ob_digit[0]); if (*prem == NULL) return -1; } @@ -3031,16 +3031,16 @@ x_divrem(PyLongObject *v1, PyLongObject *w1, PyLongObject **prem) /* normalize: shift w1 left so that its top digit is >= PyLong_BASE/2. shift v1 left by the same amount. Results go into w and v. */ - d = PyLong_SHIFT - bit_length_digit(w1->ob_digit[size_w-1]); - carry = v_lshift(w->ob_digit, w1->ob_digit, size_w, d); + d = PyLong_SHIFT - bit_length_digit(w1->long_value.ob_digit[size_w-1]); + carry = v_lshift(w->long_value.ob_digit, w1->long_value.ob_digit, size_w, d); assert(carry == 0); - carry = v_lshift(v->ob_digit, v1->ob_digit, size_v, d); - if (carry != 0 || v->ob_digit[size_v-1] >= w->ob_digit[size_w-1]) { - v->ob_digit[size_v] = carry; + carry = v_lshift(v->long_value.ob_digit, v1->long_value.ob_digit, size_v, d); + if (carry != 0 || v->long_value.ob_digit[size_v-1] >= w->long_value.ob_digit[size_w-1]) { + v->long_value.ob_digit[size_v] = carry; size_v++; } - /* Now v->ob_digit[size_v-1] < w->ob_digit[size_w-1], so quotient has + /* Now v->long_value.ob_digit[size_v-1] < w->long_value.ob_digit[size_w-1], so quotient has at most (and usually exactly) k = size_v - size_w digits. */ k = size_v - size_w; assert(k >= 0); @@ -3051,11 +3051,11 @@ x_divrem(PyLongObject *v1, PyLongObject *w1, PyLongObject **prem) *prem = NULL; return NULL; } - v0 = v->ob_digit; - w0 = w->ob_digit; + v0 = v->long_value.ob_digit; + w0 = w->long_value.ob_digit; wm1 = w0[size_w-1]; wm2 = w0[size_w-2]; - for (vk = v0+k, ak = a->ob_digit + k; vk-- > v0;) { + for (vk = v0+k, ak = a->long_value.ob_digit + k; vk-- > v0;) { /* inner loop: divide vk[0:size_w+1] by w0[0:size_w], giving single-digit quotient q, remainder in vk[0:size_w]. */ @@ -3160,7 +3160,7 @@ _PyLong_Frexp(PyLongObject *a, Py_ssize_t *e) *e = 0; return 0.0; } - a_bits = bit_length_digit(a->ob_digit[a_size-1]); + a_bits = bit_length_digit(a->long_value.ob_digit[a_size-1]); /* The following is an overflow-free version of the check "if ((a_size - 1) * PyLong_SHIFT + a_bits > PY_SSIZE_T_MAX) ..." */ if (a_size >= (PY_SSIZE_T_MAX - 1) / PyLong_SHIFT + 1 && @@ -3198,7 +3198,7 @@ _PyLong_Frexp(PyLongObject *a, Py_ssize_t *e) shift_digits = (DBL_MANT_DIG + 2 - a_bits) / PyLong_SHIFT; shift_bits = (DBL_MANT_DIG + 2 - a_bits) % PyLong_SHIFT; x_size = shift_digits; - rem = v_lshift(x_digits + x_size, a->ob_digit, a_size, + rem = v_lshift(x_digits + x_size, a->long_value.ob_digit, a_size, (int)shift_bits); x_size += a_size; x_digits[x_size++] = rem; @@ -3206,7 +3206,7 @@ _PyLong_Frexp(PyLongObject *a, Py_ssize_t *e) else { shift_digits = (a_bits - DBL_MANT_DIG - 2) / PyLong_SHIFT; shift_bits = (a_bits - DBL_MANT_DIG - 2) % PyLong_SHIFT; - rem = v_rshift(x_digits, a->ob_digit + shift_digits, + rem = v_rshift(x_digits, a->long_value.ob_digit + shift_digits, a_size - shift_digits, (int)shift_bits); x_size = a_size - shift_digits; /* For correct rounding below, we need the least significant @@ -3217,7 +3217,7 @@ _PyLong_Frexp(PyLongObject *a, Py_ssize_t *e) x_digits[0] |= 1; else while (shift_digits > 0) - if (a->ob_digit[--shift_digits]) { + if (a->long_value.ob_digit[--shift_digits]) { x_digits[0] |= 1; break; } @@ -3297,7 +3297,7 @@ long_compare(PyLongObject *a, PyLongObject *b) Py_ssize_t i = Py_ABS(Py_SIZE(a)); sdigit diff = 0; while (--i >= 0) { - diff = (sdigit) a->ob_digit[i] - (sdigit) b->ob_digit[i]; + diff = (sdigit) a->long_value.ob_digit[i] - (sdigit) b->long_value.ob_digit[i]; if (diff) { break; } @@ -3328,9 +3328,9 @@ long_hash(PyLongObject *v) i = Py_SIZE(v); switch(i) { - case -1: return v->ob_digit[0]==1 ? -2 : -(sdigit)v->ob_digit[0]; + case -1: return v->long_value.ob_digit[0]==1 ? -2 : -(sdigit)v->long_value.ob_digit[0]; case 0: return 0; - case 1: return v->ob_digit[0]; + case 1: return v->long_value.ob_digit[0]; } sign = 1; x = 0; @@ -3340,7 +3340,7 @@ long_hash(PyLongObject *v) } while (--i >= 0) { /* Here x is a quantity in the range [0, _PyHASH_MODULUS); we - want to compute x * 2**PyLong_SHIFT + v->ob_digit[i] modulo + want to compute x * 2**PyLong_SHIFT + v->long_value.ob_digit[i] modulo _PyHASH_MODULUS. The computation of x * 2**PyLong_SHIFT % _PyHASH_MODULUS @@ -3366,7 +3366,7 @@ long_hash(PyLongObject *v) _PyHASH_MODULUS. */ x = ((x << PyLong_SHIFT) & _PyHASH_MODULUS) | (x >> (_PyHASH_BITS - PyLong_SHIFT)); - x += v->ob_digit[i]; + x += v->long_value.ob_digit[i]; if (x >= _PyHASH_MODULUS) x -= _PyHASH_MODULUS; } @@ -3398,16 +3398,16 @@ x_add(PyLongObject *a, PyLongObject *b) if (z == NULL) return NULL; for (i = 0; i < size_b; ++i) { - carry += a->ob_digit[i] + b->ob_digit[i]; - z->ob_digit[i] = carry & PyLong_MASK; + carry += a->long_value.ob_digit[i] + b->long_value.ob_digit[i]; + z->long_value.ob_digit[i] = carry & PyLong_MASK; carry >>= PyLong_SHIFT; } for (; i < size_a; ++i) { - carry += a->ob_digit[i]; - z->ob_digit[i] = carry & PyLong_MASK; + carry += a->long_value.ob_digit[i]; + z->long_value.ob_digit[i] = carry & PyLong_MASK; carry >>= PyLong_SHIFT; } - z->ob_digit[i] = carry; + z->long_value.ob_digit[i] = carry; return long_normalize(z); } @@ -3433,11 +3433,11 @@ x_sub(PyLongObject *a, PyLongObject *b) else if (size_a == size_b) { /* Find highest digit where a and b differ: */ i = size_a; - while (--i >= 0 && a->ob_digit[i] == b->ob_digit[i]) + while (--i >= 0 && a->long_value.ob_digit[i] == b->long_value.ob_digit[i]) ; if (i < 0) return (PyLongObject *)PyLong_FromLong(0); - if (a->ob_digit[i] < b->ob_digit[i]) { + if (a->long_value.ob_digit[i] < b->long_value.ob_digit[i]) { sign = -1; { PyLongObject *temp = a; a = b; b = temp; } } @@ -3449,14 +3449,14 @@ x_sub(PyLongObject *a, PyLongObject *b) for (i = 0; i < size_b; ++i) { /* The following assumes unsigned arithmetic works module 2**N for some N>PyLong_SHIFT. */ - borrow = a->ob_digit[i] - b->ob_digit[i] - borrow; - z->ob_digit[i] = borrow & PyLong_MASK; + borrow = a->long_value.ob_digit[i] - b->long_value.ob_digit[i] - borrow; + z->long_value.ob_digit[i] = borrow & PyLong_MASK; borrow >>= PyLong_SHIFT; borrow &= 1; /* Keep only one sign bit */ } for (; i < size_a; ++i) { - borrow = a->ob_digit[i] - borrow; - z->ob_digit[i] = borrow & PyLong_MASK; + borrow = a->long_value.ob_digit[i] - borrow; + z->long_value.ob_digit[i] = borrow & PyLong_MASK; borrow >>= PyLong_SHIFT; borrow &= 1; /* Keep only one sign bit */ } @@ -3557,7 +3557,7 @@ x_mul(PyLongObject *a, PyLongObject *b) if (z == NULL) return NULL; - memset(z->ob_digit, 0, Py_SIZE(z) * sizeof(digit)); + memset(z->long_value.ob_digit, 0, Py_SIZE(z) * sizeof(digit)); if (a == b) { /* Efficient squaring per HAC, Algorithm 14.16: * http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf @@ -3565,12 +3565,12 @@ x_mul(PyLongObject *a, PyLongObject *b) * via exploiting that each entry in the multiplication * pyramid appears twice (except for the size_a squares). */ - digit *paend = a->ob_digit + size_a; + digit *paend = a->long_value.ob_digit + size_a; for (i = 0; i < size_a; ++i) { twodigits carry; - twodigits f = a->ob_digit[i]; - digit *pz = z->ob_digit + (i << 1); - digit *pa = a->ob_digit + i + 1; + twodigits f = a->long_value.ob_digit[i]; + digit *pz = z->long_value.ob_digit + (i << 1); + digit *pa = a->long_value.ob_digit + i + 1; SIGCHECK({ Py_DECREF(z); @@ -3619,10 +3619,10 @@ x_mul(PyLongObject *a, PyLongObject *b) else { /* a is not the same as b -- gradeschool int mult */ for (i = 0; i < size_a; ++i) { twodigits carry = 0; - twodigits f = a->ob_digit[i]; - digit *pz = z->ob_digit + i; - digit *pb = b->ob_digit; - digit *pbend = b->ob_digit + size_b; + twodigits f = a->long_value.ob_digit[i]; + digit *pz = z->long_value.ob_digit + i; + digit *pb = b->long_value.ob_digit; + digit *pbend = b->long_value.ob_digit + size_b; SIGCHECK({ Py_DECREF(z); @@ -3670,8 +3670,8 @@ kmul_split(PyLongObject *n, return -1; } - memcpy(lo->ob_digit, n->ob_digit, size_lo * sizeof(digit)); - memcpy(hi->ob_digit, n->ob_digit + size_lo, size_hi * sizeof(digit)); + memcpy(lo->long_value.ob_digit, n->long_value.ob_digit, size_lo * sizeof(digit)); + memcpy(hi->long_value.ob_digit, n->long_value.ob_digit + size_lo, size_hi * sizeof(digit)); *high = long_normalize(hi); *low = long_normalize(lo); @@ -3769,20 +3769,20 @@ k_mul(PyLongObject *a, PyLongObject *b) if (ret == NULL) goto fail; #ifdef Py_DEBUG /* Fill with trash, to catch reference to uninitialized digits. */ - memset(ret->ob_digit, 0xDF, Py_SIZE(ret) * sizeof(digit)); + memset(ret->long_value.ob_digit, 0xDF, Py_SIZE(ret) * sizeof(digit)); #endif /* 2. t1 <- ah*bh, and copy into high digits of result. */ if ((t1 = k_mul(ah, bh)) == NULL) goto fail; assert(Py_SIZE(t1) >= 0); assert(2*shift + Py_SIZE(t1) <= Py_SIZE(ret)); - memcpy(ret->ob_digit + 2*shift, t1->ob_digit, + memcpy(ret->long_value.ob_digit + 2*shift, t1->long_value.ob_digit, Py_SIZE(t1) * sizeof(digit)); /* Zero-out the digits higher than the ah*bh copy. */ i = Py_SIZE(ret) - 2*shift - Py_SIZE(t1); if (i) - memset(ret->ob_digit + 2*shift + Py_SIZE(t1), 0, + memset(ret->long_value.ob_digit + 2*shift + Py_SIZE(t1), 0, i * sizeof(digit)); /* 3. t2 <- al*bl, and copy into the low digits. */ @@ -3792,21 +3792,21 @@ k_mul(PyLongObject *a, PyLongObject *b) } assert(Py_SIZE(t2) >= 0); assert(Py_SIZE(t2) <= 2*shift); /* no overlap with high digits */ - memcpy(ret->ob_digit, t2->ob_digit, Py_SIZE(t2) * sizeof(digit)); + memcpy(ret->long_value.ob_digit, t2->long_value.ob_digit, Py_SIZE(t2) * sizeof(digit)); /* Zero out remaining digits. */ i = 2*shift - Py_SIZE(t2); /* number of uninitialized digits */ if (i) - memset(ret->ob_digit + Py_SIZE(t2), 0, i * sizeof(digit)); + memset(ret->long_value.ob_digit + Py_SIZE(t2), 0, i * sizeof(digit)); /* 4 & 5. Subtract ah*bh (t1) and al*bl (t2). We do al*bl first * because it's fresher in cache. */ i = Py_SIZE(ret) - shift; /* # digits after shift */ - (void)v_isub(ret->ob_digit + shift, i, t2->ob_digit, Py_SIZE(t2)); + (void)v_isub(ret->long_value.ob_digit + shift, i, t2->long_value.ob_digit, Py_SIZE(t2)); _Py_DECREF_INT(t2); - (void)v_isub(ret->ob_digit + shift, i, t1->ob_digit, Py_SIZE(t1)); + (void)v_isub(ret->long_value.ob_digit + shift, i, t1->long_value.ob_digit, Py_SIZE(t1)); _Py_DECREF_INT(t1); /* 6. t3 <- (ah+al)(bh+bl), and add into result. */ @@ -3835,7 +3835,7 @@ k_mul(PyLongObject *a, PyLongObject *b) /* Add t3. It's not obvious why we can't run out of room here. * See the (*) comment after this function. */ - (void)v_iadd(ret->ob_digit + shift, i, t3->ob_digit, Py_SIZE(t3)); + (void)v_iadd(ret->long_value.ob_digit + shift, i, t3->long_value.ob_digit, Py_SIZE(t3)); _Py_DECREF_INT(t3); return long_normalize(ret); @@ -3918,7 +3918,7 @@ k_lopsided_mul(PyLongObject *a, PyLongObject *b) ret = _PyLong_New(asize + bsize); if (ret == NULL) return NULL; - memset(ret->ob_digit, 0, Py_SIZE(ret) * sizeof(digit)); + memset(ret->long_value.ob_digit, 0, Py_SIZE(ret) * sizeof(digit)); /* Successive slices of b are copied into bslice. */ bslice = _PyLong_New(asize); @@ -3931,7 +3931,7 @@ k_lopsided_mul(PyLongObject *a, PyLongObject *b) const Py_ssize_t nbtouse = Py_MIN(bsize, asize); /* Multiply the next slice of b by a. */ - memcpy(bslice->ob_digit, b->ob_digit + nbdone, + memcpy(bslice->long_value.ob_digit, b->long_value.ob_digit + nbdone, nbtouse * sizeof(digit)); Py_SET_SIZE(bslice, nbtouse); product = k_mul(a, bslice); @@ -3939,8 +3939,8 @@ k_lopsided_mul(PyLongObject *a, PyLongObject *b) goto fail; /* Add into result. */ - (void)v_iadd(ret->ob_digit + nbdone, Py_SIZE(ret) - nbdone, - product->ob_digit, Py_SIZE(product)); + (void)v_iadd(ret->long_value.ob_digit + nbdone, Py_SIZE(ret) - nbdone, + product->long_value.ob_digit, Py_SIZE(product)); _Py_DECREF_INT(product); bsize -= nbtouse; @@ -3988,8 +3988,8 @@ long_mul(PyLongObject *a, PyLongObject *b) static PyObject * fast_mod(PyLongObject *a, PyLongObject *b) { - sdigit left = a->ob_digit[0]; - sdigit right = b->ob_digit[0]; + sdigit left = a->long_value.ob_digit[0]; + sdigit right = b->long_value.ob_digit[0]; sdigit mod; assert(Py_ABS(Py_SIZE(a)) == 1); @@ -4011,8 +4011,8 @@ fast_mod(PyLongObject *a, PyLongObject *b) static PyObject * fast_floor_div(PyLongObject *a, PyLongObject *b) { - sdigit left = a->ob_digit[0]; - sdigit right = b->ob_digit[0]; + sdigit left = a->long_value.ob_digit[0]; + sdigit right = b->long_value.ob_digit[0]; sdigit div; assert(Py_ABS(Py_SIZE(a)) == 1); @@ -4334,18 +4334,18 @@ long_true_divide(PyObject *v, PyObject *w) the x87 FPU set to 64-bit precision. */ a_is_small = a_size <= MANT_DIG_DIGITS || (a_size == MANT_DIG_DIGITS+1 && - a->ob_digit[MANT_DIG_DIGITS] >> MANT_DIG_BITS == 0); + a->long_value.ob_digit[MANT_DIG_DIGITS] >> MANT_DIG_BITS == 0); b_is_small = b_size <= MANT_DIG_DIGITS || (b_size == MANT_DIG_DIGITS+1 && - b->ob_digit[MANT_DIG_DIGITS] >> MANT_DIG_BITS == 0); + b->long_value.ob_digit[MANT_DIG_DIGITS] >> MANT_DIG_BITS == 0); if (a_is_small && b_is_small) { double da, db; - da = a->ob_digit[--a_size]; + da = a->long_value.ob_digit[--a_size]; while (a_size > 0) - da = da * PyLong_BASE + a->ob_digit[--a_size]; - db = b->ob_digit[--b_size]; + da = da * PyLong_BASE + a->long_value.ob_digit[--a_size]; + db = b->long_value.ob_digit[--b_size]; while (b_size > 0) - db = db * PyLong_BASE + b->ob_digit[--b_size]; + db = db * PyLong_BASE + b->long_value.ob_digit[--b_size]; result = da / db; goto success; } @@ -4359,8 +4359,8 @@ long_true_divide(PyObject *v, PyObject *w) /* Extreme underflow */ goto underflow_or_zero; /* Next line is now safe from overflowing a Py_ssize_t */ - diff = diff * PyLong_SHIFT + bit_length_digit(a->ob_digit[a_size - 1]) - - bit_length_digit(b->ob_digit[b_size - 1]); + diff = diff * PyLong_SHIFT + bit_length_digit(a->long_value.ob_digit[a_size - 1]) - + bit_length_digit(b->long_value.ob_digit[b_size - 1]); /* Now diff = a_bits - b_bits. */ if (diff > DBL_MAX_EXP) goto overflow; @@ -4389,10 +4389,10 @@ long_true_divide(PyObject *v, PyObject *w) if (x == NULL) goto error; for (i = 0; i < shift_digits; i++) - x->ob_digit[i] = 0; - rem = v_lshift(x->ob_digit + shift_digits, a->ob_digit, + x->long_value.ob_digit[i] = 0; + rem = v_lshift(x->long_value.ob_digit + shift_digits, a->long_value.ob_digit, a_size, -shift % PyLong_SHIFT); - x->ob_digit[a_size + shift_digits] = rem; + x->long_value.ob_digit[a_size + shift_digits] = rem; } else { Py_ssize_t shift_digits = shift / PyLong_SHIFT; @@ -4402,13 +4402,13 @@ long_true_divide(PyObject *v, PyObject *w) x = _PyLong_New(a_size - shift_digits); if (x == NULL) goto error; - rem = v_rshift(x->ob_digit, a->ob_digit + shift_digits, + rem = v_rshift(x->long_value.ob_digit, a->long_value.ob_digit + shift_digits, a_size - shift_digits, shift % PyLong_SHIFT); /* set inexact if any of the bits shifted out is nonzero */ if (rem) inexact = 1; while (!inexact && shift_digits > 0) - if (a->ob_digit[--shift_digits]) + if (a->long_value.ob_digit[--shift_digits]) inexact = 1; } long_normalize(x); @@ -4417,8 +4417,8 @@ long_true_divide(PyObject *v, PyObject *w) /* x //= b. If the remainder is nonzero, set inexact. We own the only reference to x, so it's safe to modify it in-place. */ if (b_size == 1) { - digit rem = inplace_divrem1(x->ob_digit, x->ob_digit, x_size, - b->ob_digit[0]); + digit rem = inplace_divrem1(x->long_value.ob_digit, x->long_value.ob_digit, x_size, + b->long_value.ob_digit[0]); long_normalize(x); if (rem) inexact = 1; @@ -4435,7 +4435,7 @@ long_true_divide(PyObject *v, PyObject *w) } x_size = Py_ABS(Py_SIZE(x)); assert(x_size > 0); /* result of division is never zero */ - x_bits = (x_size-1)*PyLong_SHIFT+bit_length_digit(x->ob_digit[x_size-1]); + x_bits = (x_size-1)*PyLong_SHIFT+bit_length_digit(x->long_value.ob_digit[x_size-1]); /* The number of extra bits that have to be rounded away. */ extra_bits = Py_MAX(x_bits, DBL_MIN_EXP - shift) - DBL_MANT_DIG; @@ -4443,15 +4443,15 @@ long_true_divide(PyObject *v, PyObject *w) /* Round by directly modifying the low digit of x. */ mask = (digit)1 << (extra_bits - 1); - low = x->ob_digit[0] | inexact; + low = x->long_value.ob_digit[0] | inexact; if ((low & mask) && (low & (3U*mask-1U))) low += mask; - x->ob_digit[0] = low & ~(2U*mask-1U); + x->long_value.ob_digit[0] = low & ~(2U*mask-1U); /* Convert x to a double dx; the conversion is exact. */ - dx = x->ob_digit[--x_size]; + dx = x->long_value.ob_digit[--x_size]; while (x_size > 0) - dx = dx * PyLong_BASE + x->ob_digit[--x_size]; + dx = dx * PyLong_BASE + x->long_value.ob_digit[--x_size]; Py_DECREF(x); /* Check whether ldexp result will overflow a double. */ @@ -4672,7 +4672,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x) /* if modulus == 1: return 0 */ - if ((Py_SIZE(c) == 1) && (c->ob_digit[0] == 1)) { + if ((Py_SIZE(c) == 1) && (c->long_value.ob_digit[0] == 1)) { z = (PyLongObject *)PyLong_FromLong(0L); goto Done; } @@ -4748,7 +4748,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x) } while(0) i = Py_SIZE(b); - digit bi = i ? b->ob_digit[i-1] : 0; + digit bi = i ? b->long_value.ob_digit[i-1] : 0; digit bit; if (i <= 1 && bi <= 3) { /* aim for minimal overhead */ @@ -4794,7 +4794,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x) if (--i < 0) { break; } - bi = b->ob_digit[i]; + bi = b->long_value.ob_digit[i]; bit = (digit)1 << (PyLong_SHIFT-1); } } @@ -4840,7 +4840,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x) } while(0) for (i = Py_SIZE(b) - 1; i >= 0; --i) { - const digit bi = b->ob_digit[i]; + const digit bi = b->long_value.ob_digit[i]; for (j = PyLong_SHIFT - 1; j >= 0; --j) { const int bit = (bi >> j) & 1; pending = (pending << 1) | bit; @@ -5011,7 +5011,7 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) } hishift = PyLong_SHIFT - remshift; - accum = a->ob_digit[wordshift]; + accum = a->long_value.ob_digit[wordshift]; if (a_negative) { /* For a positive integer a and nonnegative shift, we have: @@ -5028,19 +5028,19 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) digit sticky = 0; for (Py_ssize_t j = 0; j < wordshift; j++) { - sticky |= a->ob_digit[j]; + sticky |= a->long_value.ob_digit[j]; } accum += (PyLong_MASK >> hishift) + (digit)(sticky != 0); } accum >>= remshift; for (Py_ssize_t i = 0, j = wordshift + 1; j < size_a; i++, j++) { - accum += (twodigits)a->ob_digit[j] << hishift; - z->ob_digit[i] = (digit)(accum & PyLong_MASK); + accum += (twodigits)a->long_value.ob_digit[j] << hishift; + z->long_value.ob_digit[i] = (digit)(accum & PyLong_MASK); accum >>= PyLong_SHIFT; } assert(accum <= PyLong_MASK); - z->ob_digit[newsize - 1] = (digit)accum; + z->long_value.ob_digit[newsize - 1] = (digit)accum; z = maybe_small_long(long_normalize(z)); return (PyObject *)z; @@ -5108,15 +5108,15 @@ long_lshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) Py_SET_SIZE(z, -Py_SIZE(z)); } for (i = 0; i < wordshift; i++) - z->ob_digit[i] = 0; + z->long_value.ob_digit[i] = 0; accum = 0; for (j = 0; j < oldsize; i++, j++) { - accum |= (twodigits)a->ob_digit[j] << remshift; - z->ob_digit[i] = (digit)(accum & PyLong_MASK); + accum |= (twodigits)a->long_value.ob_digit[j] << remshift; + z->long_value.ob_digit[i] = (digit)(accum & PyLong_MASK); accum >>= PyLong_SHIFT; } if (remshift) - z->ob_digit[newsize-1] = (digit)accum; + z->long_value.ob_digit[newsize-1] = (digit)accum; else assert(!accum); z = long_normalize(z); @@ -5199,7 +5199,7 @@ long_bitwise(PyLongObject *a, z = _PyLong_New(size_a); if (z == NULL) return NULL; - v_complement(z->ob_digit, a->ob_digit, size_a); + v_complement(z->long_value.ob_digit, a->long_value.ob_digit, size_a); a = z; } else @@ -5215,7 +5215,7 @@ long_bitwise(PyLongObject *a, Py_DECREF(a); return NULL; } - v_complement(z->ob_digit, b->ob_digit, size_b); + v_complement(z->long_value.ob_digit, b->long_value.ob_digit, size_b); b = z; } else @@ -5265,15 +5265,15 @@ long_bitwise(PyLongObject *a, switch(op) { case '&': for (i = 0; i < size_b; ++i) - z->ob_digit[i] = a->ob_digit[i] & b->ob_digit[i]; + z->long_value.ob_digit[i] = a->long_value.ob_digit[i] & b->long_value.ob_digit[i]; break; case '|': for (i = 0; i < size_b; ++i) - z->ob_digit[i] = a->ob_digit[i] | b->ob_digit[i]; + z->long_value.ob_digit[i] = a->long_value.ob_digit[i] | b->long_value.ob_digit[i]; break; case '^': for (i = 0; i < size_b; ++i) - z->ob_digit[i] = a->ob_digit[i] ^ b->ob_digit[i]; + z->long_value.ob_digit[i] = a->long_value.ob_digit[i] ^ b->long_value.ob_digit[i]; break; default: Py_UNREACHABLE(); @@ -5282,16 +5282,16 @@ long_bitwise(PyLongObject *a, /* Copy any remaining digits of a, inverting if necessary. */ if (op == '^' && negb) for (; i < size_z; ++i) - z->ob_digit[i] = a->ob_digit[i] ^ PyLong_MASK; + z->long_value.ob_digit[i] = a->long_value.ob_digit[i] ^ PyLong_MASK; else if (i < size_z) - memcpy(&z->ob_digit[i], &a->ob_digit[i], + memcpy(&z->long_value.ob_digit[i], &a->long_value.ob_digit[i], (size_z-i)*sizeof(digit)); /* Complement result if negative. */ if (negz) { Py_SET_SIZE(z, -(Py_SIZE(z))); - z->ob_digit[size_z] = PyLong_MASK; - v_complement(z->ob_digit, z->ob_digit, size_z+1); + z->long_value.ob_digit[size_z] = PyLong_MASK; + v_complement(z->long_value.ob_digit, z->long_value.ob_digit, size_z+1); } Py_DECREF(a); @@ -5386,7 +5386,7 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg) alloc_b = Py_SIZE(b); /* reduce until a fits into 2 digits */ while ((size_a = Py_SIZE(a)) > 2) { - nbits = bit_length_digit(a->ob_digit[size_a-1]); + nbits = bit_length_digit(a->long_value.ob_digit[size_a-1]); /* extract top 2*PyLong_SHIFT bits of a into x, along with corresponding bits of b into y */ size_b = Py_SIZE(b); @@ -5403,13 +5403,13 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg) Py_XDECREF(d); return (PyObject *)r; } - x = (((twodigits)a->ob_digit[size_a-1] << (2*PyLong_SHIFT-nbits)) | - ((twodigits)a->ob_digit[size_a-2] << (PyLong_SHIFT-nbits)) | - (a->ob_digit[size_a-3] >> nbits)); + x = (((twodigits)a->long_value.ob_digit[size_a-1] << (2*PyLong_SHIFT-nbits)) | + ((twodigits)a->long_value.ob_digit[size_a-2] << (PyLong_SHIFT-nbits)) | + (a->long_value.ob_digit[size_a-3] >> nbits)); - y = ((size_b >= size_a - 2 ? b->ob_digit[size_a-3] >> nbits : 0) | - (size_b >= size_a - 1 ? (twodigits)b->ob_digit[size_a-2] << (PyLong_SHIFT-nbits) : 0) | - (size_b >= size_a ? (twodigits)b->ob_digit[size_a-1] << (2*PyLong_SHIFT-nbits) : 0)); + y = ((size_b >= size_a - 2 ? b->long_value.ob_digit[size_a-3] >> nbits : 0) | + (size_b >= size_a - 1 ? (twodigits)b->long_value.ob_digit[size_a-2] << (PyLong_SHIFT-nbits) : 0) | + (size_b >= size_a ? (twodigits)b->long_value.ob_digit[size_a-1] << (2*PyLong_SHIFT-nbits) : 0)); /* inner loop of Lehmer's algorithm; A, B, C, D never grow larger than PyLong_MASK during the algorithm. */ @@ -5471,14 +5471,14 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg) if (d == NULL) goto error; } - a_end = a->ob_digit + size_a; - b_end = b->ob_digit + size_b; + a_end = a->long_value.ob_digit + size_a; + b_end = b->long_value.ob_digit + size_b; /* compute new a and new b in parallel */ - a_digit = a->ob_digit; - b_digit = b->ob_digit; - c_digit = c->ob_digit; - d_digit = d->ob_digit; + a_digit = a->long_value.ob_digit; + b_digit = b->long_value.ob_digit; + c_digit = c->long_value.ob_digit; + d_digit = d->long_value.ob_digit; c_carry = 0; d_carry = 0; while (b_digit < b_end) { @@ -5651,7 +5651,7 @@ long_subtype_new(PyTypeObject *type, PyObject *x, PyObject *obase) assert(PyLong_Check(newobj)); Py_SET_SIZE(newobj, Py_SIZE(tmp)); for (i = 0; i < n; i++) { - newobj->ob_digit[i] = tmp->ob_digit[i]; + newobj->long_value.ob_digit[i] = tmp->long_value.ob_digit[i]; } Py_DECREF(tmp); return (PyObject *)newobj; @@ -5763,7 +5763,7 @@ _PyLong_DivmodNear(PyObject *a, PyObject *b) cmp = long_compare((PyLongObject *)twice_rem, (PyLongObject *)b); Py_DECREF(twice_rem); - quo_is_odd = Py_SIZE(quo) != 0 && ((quo->ob_digit[0] & 1) != 0); + quo_is_odd = Py_SIZE(quo) != 0 && ((quo->long_value.ob_digit[0] & 1) != 0); if ((Py_SIZE(b) < 0 ? cmp < 0 : cmp > 0) || (cmp == 0 && quo_is_odd)) { /* fix up quotient */ if (quo_is_neg) @@ -5884,7 +5884,7 @@ int___sizeof___impl(PyObject *self) { Py_ssize_t res; - res = offsetof(PyLongObject, ob_digit) + res = offsetof(PyLongObject, long_value.ob_digit) /* using Py_MAX(..., 1) because we always allocate space for at least one digit, even though the integer zero has a Py_SIZE of 0 */ + Py_MAX(Py_ABS(Py_SIZE(self)), 1)*sizeof(digit); @@ -5918,7 +5918,7 @@ int_bit_length_impl(PyObject *self) if (ndigits == 0) return PyLong_FromLong(0); - msd = ((PyLongObject *)self)->ob_digit[ndigits-1]; + msd = ((PyLongObject *)self)->long_value.ob_digit[ndigits-1]; msd_bits = bit_length_digit(msd); if (ndigits <= PY_SSIZE_T_MAX/PyLong_SHIFT) @@ -5991,7 +5991,7 @@ int_bit_count_impl(PyObject *self) Py_ssize_t. */ Py_ssize_t ndigits_fast = Py_MIN(ndigits, PY_SSIZE_T_MAX/PyLong_SHIFT); for (Py_ssize_t i = 0; i < ndigits_fast; i++) { - bit_count += popcount_digit(z->ob_digit[i]); + bit_count += popcount_digit(z->long_value.ob_digit[i]); } PyObject *result = PyLong_FromSsize_t(bit_count); @@ -6001,7 +6001,7 @@ int_bit_count_impl(PyObject *self) /* Use Python integers if bit_count would overflow. */ for (Py_ssize_t i = ndigits_fast; i < ndigits; i++) { - PyObject *x = PyLong_FromLong(popcount_digit(z->ob_digit[i])); + PyObject *x = PyLong_FromLong(popcount_digit(z->long_value.ob_digit[i])); if (x == NULL) { goto error; } @@ -6287,7 +6287,7 @@ static PyNumberMethods long_as_number = { PyTypeObject PyLong_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "int", /* tp_name */ - offsetof(PyLongObject, ob_digit), /* tp_basicsize */ + offsetof(PyLongObject, long_value.ob_digit), /* tp_basicsize */ sizeof(digit), /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index f97dd67269a2..53439ab16040 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2507,10 +2507,10 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) overflow = 0; /* Single digits are common, fast, and cannot overflow on unpacking. */ switch (Py_SIZE(item)) { - case -1: b = -(sdigit) ((PyLongObject*)item)->ob_digit[0]; break; + case -1: b = -(sdigit) ((PyLongObject*)item)->long_value.ob_digit[0]; break; // Note: the continue goes to the top of the "while" loop that iterates over the elements case 0: Py_DECREF(item); continue; - case 1: b = ((PyLongObject*)item)->ob_digit[0]; break; + case 1: b = ((PyLongObject*)item)->long_value.ob_digit[0]; break; default: b = PyLong_AsLongAndOverflow(item, &overflow); break; } if (overflow == 0 && diff --git a/Python/bytecodes.c b/Python/bytecodes.c index fb00b887732e..d1e59f7908b5 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -357,8 +357,8 @@ dummy_func( // Deopt unless 0 <= sub < PyList_Size(list) DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR); - assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0); - Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0]; + assert(((PyLongObject *)_PyLong_GetZero())->long_value.ob_digit[0] == 0); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR); STAT_INC(BINARY_SUBSCR, hit); res = PyList_GET_ITEM(list, index); @@ -375,8 +375,8 @@ dummy_func( // Deopt unless 0 <= sub < PyTuple_Size(list) DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR); - assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0); - Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0]; + assert(((PyLongObject *)_PyLong_GetZero())->long_value.ob_digit[0] == 0); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR); STAT_INC(BINARY_SUBSCR, hit); res = PyTuple_GET_ITEM(tuple, index); @@ -469,7 +469,7 @@ dummy_func( // Ensure nonnegative, zero-or-one-digit ints. DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), STORE_SUBSCR); - Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0]; + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; // Ensure index < len(list) DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR); STAT_INC(STORE_SUBSCR, hit); @@ -1834,8 +1834,8 @@ dummy_func( DEOPT_IF((size_t)(Py_SIZE(right) + 1) > 2, COMPARE_AND_BRANCH); STAT_INC(COMPARE_AND_BRANCH, hit); assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1); - Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0]; - Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0]; + Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->long_value.ob_digit[0]; + Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->long_value.ob_digit[0]; // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg int sign_ish = COMPARISON_BIT(ileft, iright); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index b5decf804ca6..3ee30ae8df9e 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -484,8 +484,8 @@ // Deopt unless 0 <= sub < PyList_Size(list) DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR); - assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0); - Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0]; + assert(((PyLongObject *)_PyLong_GetZero())->long_value.ob_digit[0] == 0); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR); STAT_INC(BINARY_SUBSCR, hit); res = PyList_GET_ITEM(list, index); @@ -509,8 +509,8 @@ // Deopt unless 0 <= sub < PyTuple_Size(list) DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR); - assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0); - Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0]; + assert(((PyLongObject *)_PyLong_GetZero())->long_value.ob_digit[0] == 0); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR); STAT_INC(BINARY_SUBSCR, hit); res = PyTuple_GET_ITEM(tuple, index); @@ -634,7 +634,7 @@ // Ensure nonnegative, zero-or-one-digit ints. DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), STORE_SUBSCR); - Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0]; + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; // Ensure index < len(list) DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR); STAT_INC(STORE_SUBSCR, hit); @@ -2179,8 +2179,8 @@ DEOPT_IF((size_t)(Py_SIZE(right) + 1) > 2, COMPARE_AND_BRANCH); STAT_INC(COMPARE_AND_BRANCH, hit); assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1); - Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0]; - Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0]; + Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->long_value.ob_digit[0]; + Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->long_value.ob_digit[0]; // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg int sign_ish = COMPARISON_BIT(ileft, iright); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); diff --git a/Python/marshal.c b/Python/marshal.c index 5f392d9e1ecf..94e79d4392ae 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -240,7 +240,7 @@ w_PyLong(const PyLongObject *ob, char flag, WFILE *p) /* set l to number of base PyLong_MARSHAL_BASE digits */ n = Py_ABS(Py_SIZE(ob)); l = (n-1) * PyLong_MARSHAL_RATIO; - d = ob->ob_digit[n-1]; + d = ob->long_value.ob_digit[n-1]; assert(d != 0); /* a PyLong is always normalized */ do { d >>= PyLong_MARSHAL_SHIFT; @@ -254,14 +254,14 @@ w_PyLong(const PyLongObject *ob, char flag, WFILE *p) w_long((long)(Py_SIZE(ob) > 0 ? l : -l), p); for (i=0; i < n-1; i++) { - d = ob->ob_digit[i]; + d = ob->long_value.ob_digit[i]; for (j=0; j < PyLong_MARSHAL_RATIO; j++) { w_short(d & PyLong_MARSHAL_MASK, p); d >>= PyLong_MARSHAL_SHIFT; } assert (d == 0); } - d = ob->ob_digit[n-1]; + d = ob->long_value.ob_digit[n-1]; do { w_short(d & PyLong_MARSHAL_MASK, p); d >>= PyLong_MARSHAL_SHIFT; @@ -853,7 +853,7 @@ r_PyLong(RFILE *p) goto bad_digit; d += (digit)md << j*PyLong_MARSHAL_SHIFT; } - ob->ob_digit[i] = d; + ob->long_value.ob_digit[i] = d; } d = 0; @@ -880,7 +880,7 @@ r_PyLong(RFILE *p) } /* top digit should be nonzero, else the resulting PyLong won't be normalized */ - ob->ob_digit[size-1] = d; + ob->long_value.ob_digit[size-1] = d; return (PyObject *)ob; bad_digit: Py_DECREF(ob); diff --git a/Python/specialize.c b/Python/specialize.c index 84784b2d149e..096687f5fdf0 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1411,7 +1411,7 @@ _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *ins if (container_type == &PyList_Type) { if (PyLong_CheckExact(sub)) { if ((Py_SIZE(sub) == 0 || Py_SIZE(sub) == 1) - && ((PyLongObject *)sub)->ob_digit[0] < (size_t)PyList_GET_SIZE(container)) + && ((PyLongObject *)sub)->long_value.ob_digit[0] < (size_t)PyList_GET_SIZE(container)) { _py_set_opcode(instr, STORE_SUBSCR_LIST_INT); goto success; diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 6453dff95df4..56d6970b2924 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -901,7 +901,7 @@ def proxyval(self, visited): if ob_size == 0: return 0 - ob_digit = self.field('ob_digit') + ob_digit = self.field('long_value')['ob_digit'] if gdb.lookup_type('digit').sizeof == 2: SHIFT = 15 From webhook-mailer at python.org Mon Jan 30 12:56:40 2023 From: webhook-mailer at python.org (ethanfurman) Date: Mon, 30 Jan 2023 17:56:40 -0000 Subject: [Python-checkins] gh-101422: (docs) TarFile default errorlevel argument is 1, not 0 (GH-101424) Message-ID: https://github.com/python/cpython/commit/ea232716d3de1675478db3a302629ba43194c967 commit: ea232716d3de1675478db3a302629ba43194c967 branch: main author: Owain Davies <116417456+OTheDev at users.noreply.github.com> committer: ethanfurman date: 2023-01-30T09:56:33-08:00 summary: gh-101422: (docs) TarFile default errorlevel argument is 1, not 0 (GH-101424) files: M Doc/library/tarfile.rst diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index 6e8baba04fb9..741d40da1521 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -279,7 +279,7 @@ be finalized; only the internally used file object will be closed. See the .. versionadded:: 3.2 Added support for the context management protocol. -.. class:: TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=0) +.. class:: TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=1) All following arguments are optional and can be accessed as instance attributes as well. From webhook-mailer at python.org Mon Jan 30 13:08:22 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 30 Jan 2023 18:08:22 -0000 Subject: [Python-checkins] gh-101422: (docs) TarFile default errorlevel argument is 1, not 0 (GH-101424) Message-ID: https://github.com/python/cpython/commit/d729c5c9d49df41d5986be7507b98cbec1b7349e commit: d729c5c9d49df41d5986be7507b98cbec1b7349e branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-30T10:08:15-08:00 summary: gh-101422: (docs) TarFile default errorlevel argument is 1, not 0 (GH-101424) (cherry picked from commit ea232716d3de1675478db3a302629ba43194c967) Co-authored-by: Owain Davies <116417456+OTheDev at users.noreply.github.com> files: M Doc/library/tarfile.rst diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index aa2844165897..7f57e00bbd5f 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -265,7 +265,7 @@ be finalized; only the internally used file object will be closed. See the .. versionadded:: 3.2 Added support for the context management protocol. -.. class:: TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=0) +.. class:: TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=1) All following arguments are optional and can be accessed as instance attributes as well. From webhook-mailer at python.org Mon Jan 30 13:10:09 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 30 Jan 2023 18:10:09 -0000 Subject: [Python-checkins] gh-101422: (docs) TarFile default errorlevel argument is 1, not 0 (GH-101424) Message-ID: https://github.com/python/cpython/commit/626e2cc123f3540369b97e63e5efc90c3fd28b2c commit: 626e2cc123f3540369b97e63e5efc90c3fd28b2c branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-30T10:10:01-08:00 summary: gh-101422: (docs) TarFile default errorlevel argument is 1, not 0 (GH-101424) (cherry picked from commit ea232716d3de1675478db3a302629ba43194c967) Co-authored-by: Owain Davies <116417456+OTheDev at users.noreply.github.com> files: M Doc/library/tarfile.rst diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index 44f34abfcf28..226513f5fc16 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -276,7 +276,7 @@ be finalized; only the internally used file object will be closed. See the .. versionadded:: 3.2 Added support for the context management protocol. -.. class:: TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=0) +.. class:: TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=1) All following arguments are optional and can be accessed as instance attributes as well. From webhook-mailer at python.org Mon Jan 30 13:10:33 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 30 Jan 2023 18:10:33 -0000 Subject: [Python-checkins] gh-101422: (docs) TarFile default errorlevel argument is 1, not 0 (GH-101424) Message-ID: https://github.com/python/cpython/commit/6ec9f2d5b84143dc41edceb996d67c3f923eebaa commit: 6ec9f2d5b84143dc41edceb996d67c3f923eebaa branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-30T10:10:26-08:00 summary: gh-101422: (docs) TarFile default errorlevel argument is 1, not 0 (GH-101424) (cherry picked from commit ea232716d3de1675478db3a302629ba43194c967) Co-authored-by: Owain Davies <116417456+OTheDev at users.noreply.github.com> files: M Doc/library/tarfile.rst diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index 6bfc9b5a9027..96444d4e28b6 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -276,7 +276,7 @@ be finalized; only the internally used file object will be closed. See the .. versionadded:: 3.2 Added support for the context management protocol. -.. class:: TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=0) +.. class:: TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=1) All following arguments are optional and can be accessed as instance attributes as well. From webhook-mailer at python.org Mon Jan 30 13:12:01 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 30 Jan 2023 18:12:01 -0000 Subject: [Python-checkins] gh-101422: (docs) TarFile default errorlevel argument is 1, not 0 (GH-101424) Message-ID: https://github.com/python/cpython/commit/db924a46d754bac6b8debffc3d9f3e9b67cbb884 commit: db924a46d754bac6b8debffc3d9f3e9b67cbb884 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-30T10:11:54-08:00 summary: gh-101422: (docs) TarFile default errorlevel argument is 1, not 0 (GH-101424) (cherry picked from commit ea232716d3de1675478db3a302629ba43194c967) Co-authored-by: Owain Davies <116417456+OTheDev at users.noreply.github.com> files: M Doc/library/tarfile.rst diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index ef81d9fa37f5..811f74f0e4d4 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -270,7 +270,7 @@ be finalized; only the internally used file object will be closed. See the .. versionadded:: 3.2 Added support for the context management protocol. -.. class:: TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=0) +.. class:: TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=1) All following arguments are optional and can be accessed as instance attributes as well. From webhook-mailer at python.org Mon Jan 30 13:21:15 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 30 Jan 2023 18:21:15 -0000 Subject: [Python-checkins] gh-101422: (docs) TarFile default errorlevel argument is 1, not 0 (GH-101424) Message-ID: https://github.com/python/cpython/commit/c33aaa9d559398bbf2b80e891bf3ae6a716e4b8c commit: c33aaa9d559398bbf2b80e891bf3ae6a716e4b8c branch: 3.9 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-30T10:21:08-08:00 summary: gh-101422: (docs) TarFile default errorlevel argument is 1, not 0 (GH-101424) (cherry picked from commit ea232716d3de1675478db3a302629ba43194c967) Co-authored-by: Owain Davies <116417456+OTheDev at users.noreply.github.com> files: M Doc/library/tarfile.rst diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index 6afb8397b786..0dc7c6191487 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -276,7 +276,7 @@ be finalized; only the internally used file object will be closed. See the .. versionadded:: 3.2 Added support for the context management protocol. -.. class:: TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=0) +.. class:: TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=1) All following arguments are optional and can be accessed as instance attributes as well. From webhook-mailer at python.org Mon Jan 30 14:08:14 2023 From: webhook-mailer at python.org (ericsnowcurrently) Date: Mon, 30 Jan 2023 19:08:14 -0000 Subject: [Python-checkins] gh-59956: Clarify Runtime State Status Expectations (gh-101308) Message-ID: https://github.com/python/cpython/commit/e11fc032a75d067d2167a21037722a770b9dfb51 commit: e11fc032a75d067d2167a21037722a770b9dfb51 branch: main author: Eric Snow committer: ericsnowcurrently date: 2023-01-30T12:07:48-07:00 summary: gh-59956: Clarify Runtime State Status Expectations (gh-101308) A PyThreadState can be in one of many states in its lifecycle, represented by some status value. Those statuses haven't been particularly clear, so we're addressing that here. Specifically: * made the distinct lifecycle statuses clear on PyThreadState * identified expectations of how various lifecycle-related functions relate to status * noted the various places where those expectations don't match the actual behavior At some point we'll need to address the mismatches. (This change also includes some cleanup.) https://github.com/python/cpython/issues/59956 files: M Include/cpython/pystate.h M Include/internal/pycore_pystate.h M Modules/_threadmodule.c M Python/ceval_gil.c M Python/pylifecycle.c M Python/pystate.c diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 3c1e70de3e48..f5db52f76e8f 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -119,7 +119,30 @@ struct _ts { PyThreadState *next; PyInterpreterState *interp; - int _status; + struct { + /* Has been initialized to a safe state. + + In order to be effective, this must be set to 0 during or right + after allocation. */ + unsigned int initialized:1; + + /* Has been bound to an OS thread. */ + unsigned int bound:1; + /* Has been unbound from its OS thread. */ + unsigned int unbound:1; + /* Has been bound aa current for the GILState API. */ + unsigned int bound_gilstate:1; + /* Currently in use (maybe holds the GIL). */ + unsigned int active:1; + + /* various stages of finalization */ + unsigned int finalizing:1; + unsigned int cleared:1; + unsigned int finalized:1; + + /* padding to align to 4 bytes */ + unsigned int :24; + } _status; int py_recursion_remaining; int py_recursion_limit; @@ -245,6 +268,8 @@ struct _ts { // Alias for backward compatibility with Python 3.8 #define _PyInterpreterState_Get PyInterpreterState_Get +/* An alias for the internal _PyThreadState_New(), + kept for stable ABI compatibility. */ PyAPI_FUNC(PyThreadState *) _PyThreadState_Prealloc(PyInterpreterState *); /* Similar to PyThreadState_Get(), but don't issue a fatal error diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 0e46693c1f12..7046ec8d9ada 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -120,13 +120,12 @@ static inline PyInterpreterState* _PyInterpreterState_GET(void) { // PyThreadState functions +PyAPI_FUNC(PyThreadState *) _PyThreadState_New(PyInterpreterState *interp); PyAPI_FUNC(void) _PyThreadState_Bind(PyThreadState *tstate); // We keep this around exclusively for stable ABI compatibility. PyAPI_FUNC(void) _PyThreadState_Init( PyThreadState *tstate); -PyAPI_FUNC(void) _PyThreadState_DeleteExcept( - _PyRuntimeState *runtime, - PyThreadState *tstate); +PyAPI_FUNC(void) _PyThreadState_DeleteExcept(PyThreadState *tstate); static inline void @@ -139,18 +138,6 @@ _PyThreadState_UpdateTracingState(PyThreadState *tstate) } -/* PyThreadState status */ - -#define PyThreadState_UNINITIALIZED 0 -/* Has been initialized to a safe state. - - In order to be effective, this must be set to 0 during or right - after allocation. */ -#define PyThreadState_INITIALIZED 1 -#define PyThreadState_BOUND 2 -#define PyThreadState_UNBOUND 3 - - /* Other */ PyAPI_FUNC(PyThreadState *) _PyThreadState_Swap( diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index bf4b6ec00e3e..9c12c6967574 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1161,7 +1161,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) return PyErr_NoMemory(); } boot->interp = _PyInterpreterState_GET(); - boot->tstate = _PyThreadState_Prealloc(boot->interp); + boot->tstate = _PyThreadState_New(boot->interp); if (boot->tstate == NULL) { PyMem_Free(boot); if (!PyErr_Occurred()) { diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index 73d412ba4d71..1bf223348d28 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -624,7 +624,7 @@ _PyEval_ReInitThreads(PyThreadState *tstate) } /* Destroy all threads except the current one */ - _PyThreadState_DeleteExcept(runtime, tstate); + _PyThreadState_DeleteExcept(tstate); return _PyStatus_OK(); } #endif diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 5ef2d3f6aa72..a8a8e7f3d84f 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -696,10 +696,11 @@ pycore_create_interpreter(_PyRuntimeState *runtime, const _PyInterpreterConfig config = _PyInterpreterConfig_LEGACY_INIT; init_interp_settings(interp, &config); - PyThreadState *tstate = PyThreadState_New(interp); + PyThreadState *tstate = _PyThreadState_New(interp); if (tstate == NULL) { return _PyStatus_ERR("can't make first thread"); } + _PyThreadState_Bind(tstate); (void) PyThreadState_Swap(tstate); status = init_interp_create_gil(tstate); @@ -1821,6 +1822,11 @@ Py_FinalizeEx(void) /* Get current thread state and interpreter pointer */ PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime); + // XXX assert(_Py_IsMainInterpreter(tstate->interp)); + // XXX assert(_Py_IsMainThread()); + + // Block some operations. + tstate->interp->finalizing = 1; // Wrap up existing "threading"-module-created, non-daemon threads. wait_for_thread_shutdown(tstate); @@ -1867,7 +1873,23 @@ Py_FinalizeEx(void) _PyRuntimeState_SetFinalizing() has been called, no other Python thread can take the GIL at this point: if they try, they will exit immediately. */ - _PyThreadState_DeleteExcept(runtime, tstate); + _PyThreadState_DeleteExcept(tstate); + + /* At this point no Python code should be running at all. + The only thread state left should be the main thread of the main + interpreter (AKA tstate), in which this code is running right now. + There may be other OS threads running but none of them will have + thread states associated with them, nor will be able to create + new thread states. + + Thus tstate is the only possible thread state from here on out. + It may still be used during finalization to run Python code as + needed or provide runtime state (e.g. sys.modules) but that will + happen sparingly. Furthermore, the order of finalization aims + to not need a thread (or interpreter) state as soon as possible. + */ + // XXX Make sure we are preventing the creating of any new thread states + // (or interpreters). /* Flush sys.stdout and sys.stderr */ if (flush_std_files() < 0) { @@ -1958,6 +1980,20 @@ Py_FinalizeEx(void) } #endif /* Py_TRACE_REFS */ + /* At this point there's almost no other Python code that will run, + nor interpreter state needed. The only possibility is the + finalizers of the objects stored on tstate (and tstate->interp), + which are triggered via finalize_interp_clear(). + + For now we operate as though none of those finalizers actually + need an operational thread state or interpreter. In reality, + those finalizers may rely on some part of tstate or + tstate->interp, and/or may raise exceptions + or otherwise fail. + */ + // XXX Do this sooner during finalization. + // XXX Ensure finalizer errors are handled properly. + finalize_interp_clear(tstate); finalize_interp_delete(tstate->interp); @@ -2039,12 +2075,13 @@ new_interpreter(PyThreadState **tstate_p, const _PyInterpreterConfig *config) return _PyStatus_OK(); } - PyThreadState *tstate = PyThreadState_New(interp); + PyThreadState *tstate = _PyThreadState_New(interp); if (tstate == NULL) { PyInterpreterState_Delete(interp); *tstate_p = NULL; return _PyStatus_OK(); } + _PyThreadState_Bind(tstate); PyThreadState *save_tstate = PyThreadState_Swap(tstate); diff --git a/Python/pystate.c b/Python/pystate.c index bf7688fd3213..8bb49d954a81 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -37,9 +37,6 @@ to avoid the expense of doing their own locking). extern "C" { #endif -/* Forward declarations */ -static void _PyThreadState_Delete(PyThreadState *tstate, int check_current); - /****************************************/ /* helpers for the current thread state */ @@ -81,69 +78,56 @@ current_fast_clear(_PyRuntimeState *runtime) _Py_atomic_store_relaxed(&runtime->tstate_current, (uintptr_t)NULL); } +#define tstate_verify_not_active(tstate) \ + if (tstate == current_fast_get((tstate)->interp->runtime)) { \ + _Py_FatalErrorFormat(__func__, "tstate %p is still current", tstate); \ + } + //------------------------------------------------ // the thread state bound to the current OS thread //------------------------------------------------ -/* - The stored thread state is set by bind_tstate() (AKA PyThreadState_Bind(). - - The GIL does no need to be held for these. - */ - -static int -current_tss_initialized(_PyRuntimeState *runtime) +static inline int +tstate_tss_initialized(Py_tss_t *key) { - return PyThread_tss_is_created(&runtime->autoTSSkey); + return PyThread_tss_is_created(key); } -static PyStatus -current_tss_init(_PyRuntimeState *runtime) +static inline int +tstate_tss_init(Py_tss_t *key) { - assert(!current_tss_initialized(runtime)); - if (PyThread_tss_create(&runtime->autoTSSkey) != 0) { - return _PyStatus_NO_MEMORY(); - } - return _PyStatus_OK(); + assert(!tstate_tss_initialized(key)); + return PyThread_tss_create(key); } -static void -current_tss_fini(_PyRuntimeState *runtime) +static inline void +tstate_tss_fini(Py_tss_t *key) { - assert(current_tss_initialized(runtime)); - PyThread_tss_delete(&runtime->autoTSSkey); + assert(tstate_tss_initialized(key)); + PyThread_tss_delete(key); } static inline PyThreadState * -current_tss_get(_PyRuntimeState *runtime) +tstate_tss_get(Py_tss_t *key) { - assert(current_tss_initialized(runtime)); - return (PyThreadState *)PyThread_tss_get(&runtime->autoTSSkey); + assert(tstate_tss_initialized(key)); + return (PyThreadState *)PyThread_tss_get(key); } static inline int -_current_tss_set(_PyRuntimeState *runtime, PyThreadState *tstate) +tstate_tss_set(Py_tss_t *key, PyThreadState *tstate) { assert(tstate != NULL); - assert(current_tss_initialized(runtime)); - return PyThread_tss_set(&runtime->autoTSSkey, (void *)tstate); -} -static inline void -current_tss_set(_PyRuntimeState *runtime, PyThreadState *tstate) -{ - if (_current_tss_set(runtime, tstate) != 0) { - Py_FatalError("failed to set current tstate (TSS)"); - } + assert(tstate_tss_initialized(key)); + return PyThread_tss_set(key, (void *)tstate); } -static inline void -current_tss_clear(_PyRuntimeState *runtime) +static inline int +tstate_tss_clear(Py_tss_t *key) { - assert(current_tss_initialized(runtime)); - if (PyThread_tss_set(&runtime->autoTSSkey, NULL) != 0) { - Py_FatalError("failed to clear current tstate (TSS)"); - } + assert(tstate_tss_initialized(key)); + return PyThread_tss_set(key, (void *)NULL); } #ifdef HAVE_FORK @@ -152,92 +136,178 @@ current_tss_clear(_PyRuntimeState *runtime) * don't reset TSS upon fork(), see issue #10517. */ static PyStatus -current_tss_reinit(_PyRuntimeState *runtime) +tstate_tss_reinit(Py_tss_t *key) { - if (!current_tss_initialized(runtime)) { + if (!tstate_tss_initialized(key)) { return _PyStatus_OK(); } - PyThreadState *tstate = current_tss_get(runtime); + PyThreadState *tstate = tstate_tss_get(key); - current_tss_fini(runtime); - PyStatus status = current_tss_init(runtime); - if (_PyStatus_EXCEPTION(status)) { - return status; + tstate_tss_fini(key); + if (tstate_tss_init(key) != 0) { + return _PyStatus_NO_MEMORY(); } /* If the thread had an associated auto thread state, reassociate it with * the new key. */ - if (tstate && _current_tss_set(runtime, tstate) != 0) { - return _PyStatus_ERR("failed to set autoTSSkey"); + if (tstate && tstate_tss_set(key, tstate) != 0) { + return _PyStatus_ERR("failed to re-set autoTSSkey"); } return _PyStatus_OK(); } #endif +/* + The stored thread state is set by bind_tstate() (AKA PyThreadState_Bind(). + + The GIL does no need to be held for these. + */ + +#define gilstate_tss_initialized(runtime) \ + tstate_tss_initialized(&(runtime)->autoTSSkey) +#define gilstate_tss_init(runtime) \ + tstate_tss_init(&(runtime)->autoTSSkey) +#define gilstate_tss_fini(runtime) \ + tstate_tss_fini(&(runtime)->autoTSSkey) +#define gilstate_tss_get(runtime) \ + tstate_tss_get(&(runtime)->autoTSSkey) +#define _gilstate_tss_set(runtime, tstate) \ + tstate_tss_set(&(runtime)->autoTSSkey, tstate) +#define _gilstate_tss_clear(runtime) \ + tstate_tss_clear(&(runtime)->autoTSSkey) +#define gilstate_tss_reinit(runtime) \ + tstate_tss_reinit(&(runtime)->autoTSSkey) + +static inline void +gilstate_tss_set(_PyRuntimeState *runtime, PyThreadState *tstate) +{ + assert(tstate != NULL && tstate->interp->runtime == runtime); + if (_gilstate_tss_set(runtime, tstate) != 0) { + Py_FatalError("failed to set current tstate (TSS)"); + } +} + +static inline void +gilstate_tss_clear(_PyRuntimeState *runtime) +{ + if (_gilstate_tss_clear(runtime) != 0) { + Py_FatalError("failed to clear current tstate (TSS)"); + } +} + + +static inline int tstate_is_alive(PyThreadState *tstate); + +static inline int +tstate_is_bound(PyThreadState *tstate) +{ + return tstate->_status.bound && !tstate->_status.unbound; +} + +static void bind_gilstate_tstate(PyThreadState *); +static void unbind_gilstate_tstate(PyThreadState *); + static void bind_tstate(PyThreadState *tstate) { assert(tstate != NULL); - assert(tstate->_status == PyThreadState_INITIALIZED); + assert(tstate_is_alive(tstate) && !tstate->_status.bound); + assert(!tstate->_status.unbound); // just in case + assert(!tstate->_status.bound_gilstate); + assert(tstate != gilstate_tss_get(tstate->interp->runtime)); + assert(!tstate->_status.active); assert(tstate->thread_id == 0); assert(tstate->native_thread_id == 0); - _PyRuntimeState *runtime = tstate->interp->runtime; - - /* Stick the thread state for this thread in thread specific storage. - The only situation where you can legitimately have more than one - thread state for an OS level thread is when there are multiple - interpreters. - - You shouldn't really be using the PyGILState_ APIs anyway (see issues - #10915 and #15751). - - The first thread state created for that given OS level thread will - "win", which seems reasonable behaviour. - */ - /* When a thread state is created for a thread by some mechanism - other than PyGILState_Ensure(), it's important that the GILState - machinery knows about it so it doesn't try to create another - thread state for the thread. - (This is a better fix for SF bug #1010677 than the first one attempted.) - */ - // XXX Skipping like this does not play nice with multiple interpreters. - if (current_tss_get(runtime) == NULL) { - current_tss_set(runtime, tstate); - } + // Currently we don't necessarily store the thread state + // in thread-local storage (e.g. per-interpreter). tstate->thread_id = PyThread_get_thread_ident(); #ifdef PY_HAVE_THREAD_NATIVE_ID tstate->native_thread_id = PyThread_get_thread_native_id(); #endif - tstate->_status = PyThreadState_BOUND; + tstate->_status.bound = 1; } static void unbind_tstate(PyThreadState *tstate) { assert(tstate != NULL); - assert(tstate->_status == PyThreadState_BOUND); + // XXX assert(tstate_is_alive(tstate)); + assert(tstate_is_bound(tstate)); + // XXX assert(!tstate->_status.active); assert(tstate->thread_id > 0); #ifdef PY_HAVE_THREAD_NATIVE_ID assert(tstate->native_thread_id > 0); #endif - _PyRuntimeState *runtime = tstate->interp->runtime; - - if (current_tss_initialized(runtime) && - tstate == current_tss_get(runtime)) - { - current_tss_clear(runtime); - } // We leave thread_id and native_thread_id alone // since they can be useful for debugging. // Check the `_status` field to know if these values // are still valid. - tstate->_status = PyThreadState_UNBOUND; + // We leave tstate->_status.bound set to 1 + // to indicate it was previously bound. + tstate->_status.unbound = 1; +} + + +/* Stick the thread state for this thread in thread specific storage. + + When a thread state is created for a thread by some mechanism + other than PyGILState_Ensure(), it's important that the GILState + machinery knows about it so it doesn't try to create another + thread state for the thread. + (This is a better fix for SF bug #1010677 than the first one attempted.) + + The only situation where you can legitimately have more than one + thread state for an OS level thread is when there are multiple + interpreters. + + The PyGILState_*() APIs don't work with multiple + interpreters (see bpo-10915 and bpo-15751), so this function + sets TSS only once. Thus, the first thread state created for that + given OS level thread will "win", which seems reasonable behaviour. +*/ + +static void +bind_gilstate_tstate(PyThreadState *tstate) +{ + assert(tstate != NULL); + assert(tstate_is_alive(tstate)); + assert(tstate_is_bound(tstate)); + // XXX assert(!tstate->_status.active); + assert(!tstate->_status.bound_gilstate); + + _PyRuntimeState *runtime = tstate->interp->runtime; + PyThreadState *tcur = gilstate_tss_get(runtime); + assert(tstate != tcur); + + if (tcur != NULL) { + // The original gilstate implementation only respects the + // first thread state set. + // XXX Skipping like this does not play nice with multiple interpreters. + return; + tcur->_status.bound_gilstate = 0; + } + gilstate_tss_set(runtime, tstate); + tstate->_status.bound_gilstate = 1; +} + +static void +unbind_gilstate_tstate(PyThreadState *tstate) +{ + assert(tstate != NULL); + // XXX assert(tstate_is_alive(tstate)); + assert(tstate_is_bound(tstate)); + // XXX assert(!tstate->_status.active); + assert(tstate->_status.bound_gilstate); + assert(tstate == gilstate_tss_get(tstate->interp->runtime)); + + gilstate_tss_clear(tstate->interp->runtime); + tstate->_status.bound_gilstate = 0; } @@ -261,7 +331,7 @@ holds_gil(PyThreadState *tstate) assert(tstate != NULL); _PyRuntimeState *runtime = tstate->interp->runtime; /* Must be the tstate for this thread */ - assert(tstate == current_tss_get(runtime)); + assert(tstate == gilstate_tss_get(runtime)); return tstate == current_fast_get(runtime); } @@ -394,10 +464,9 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) memcpy(runtime, &initial, sizeof(*runtime)); } - PyStatus status = current_tss_init(runtime); - if (_PyStatus_EXCEPTION(status)) { + if (gilstate_tss_init(runtime) != 0) { _PyRuntimeState_Fini(runtime); - return status; + return _PyStatus_NO_MEMORY(); } if (PyThread_tss_create(&runtime->trashTSSkey) != 0) { @@ -414,8 +483,8 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) void _PyRuntimeState_Fini(_PyRuntimeState *runtime) { - if (current_tss_initialized(runtime)) { - current_tss_fini(runtime); + if (gilstate_tss_initialized(runtime)) { + gilstate_tss_fini(runtime); } if (PyThread_tss_is_created(&runtime->trashTSSkey)) { @@ -475,7 +544,7 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime) } - PyStatus status = current_tss_reinit(runtime); + PyStatus status = gilstate_tss_reinit(runtime); if (_PyStatus_EXCEPTION(status)) { return status; } @@ -669,18 +738,38 @@ PyInterpreterState_New(void) static void interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) { + assert(interp != NULL); + assert(tstate != NULL); _PyRuntimeState *runtime = interp->runtime; + /* XXX Conditions we need to enforce: + + * the GIL must be held by the current thread + * tstate must be the "current" thread state (current_fast_get()) + * tstate->interp must be interp + * for the main interpreter, tstate must be the main thread + */ + // XXX Ideally, we would not rely on any thread state in this function + // (and we would drop the "tstate" argument). + if (_PySys_Audit(tstate, "cpython.PyInterpreterState_Clear", NULL) < 0) { _PyErr_Clear(tstate); } HEAD_LOCK(runtime); + // XXX Clear the current/main thread state last. for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { PyThreadState_Clear(p); } HEAD_UNLOCK(runtime); + /* It is possible that any of the objects below have a finalizer + that runs Python code or otherwise relies on a thread state + or even the interpreter state. For now we trust that isn't + a problem. + */ + // XXX Make sure we properly deal with problematic finalizers. + Py_CLEAR(interp->audit_hooks); PyConfig_Clear(&interp->config); @@ -751,7 +840,6 @@ PyInterpreterState_Clear(PyInterpreterState *interp) // garbage. It can be different than the current Python thread state // of 'interp'. PyThreadState *current_tstate = current_fast_get(interp->runtime); - interpreter_clear(interp, current_tstate); } @@ -763,29 +851,25 @@ _PyInterpreterState_Clear(PyThreadState *tstate) } -static void -zapthreads(PyInterpreterState *interp, int check_current) -{ - PyThreadState *tstate; - /* No need to lock the mutex here because this should only happen - when the threads are all really dead (XXX famous last words). */ - while ((tstate = interp->threads.head) != NULL) { - _PyThreadState_Delete(tstate, check_current); - } -} - +static void zapthreads(PyInterpreterState *interp); void PyInterpreterState_Delete(PyInterpreterState *interp) { _PyRuntimeState *runtime = interp->runtime; struct pyinterpreters *interpreters = &runtime->interpreters; - zapthreads(interp, 0); - _PyEval_FiniState(&interp->ceval); + // XXX Clearing the "current" thread state should happen before + // we start finalizing the interpreter (or the current thread state). + PyThreadState *tcur = current_fast_get(runtime); + if (tcur != NULL && interp == tcur->interp) { + /* Unset current thread. After this, many C API calls become crashy. */ + _PyThreadState_Swap(runtime, NULL); + } + + zapthreads(interp); - /* Delete current thread. After this, many C API calls become crashy. */ - _PyThreadState_Swap(runtime, NULL); + _PyEval_FiniState(&interp->ceval); HEAD_LOCK(runtime); PyInterpreterState **p; @@ -843,8 +927,10 @@ _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime) continue; } + // XXX Won't this fail since PyInterpreterState_Clear() requires + // the "current" tstate to be set? PyInterpreterState_Clear(interp); // XXX must activate? - zapthreads(interp, 1); + zapthreads(interp); if (interp->id_mutex != NULL) { PyThread_free_lock(interp->id_mutex); } @@ -1062,6 +1148,16 @@ _PyInterpreterState_LookUpID(int64_t requested_id) /* the per-thread runtime state */ /********************************/ +static inline int +tstate_is_alive(PyThreadState *tstate) +{ + return (tstate->_status.initialized && + !tstate->_status.finalized && + !tstate->_status.cleared && + !tstate->_status.finalizing); +} + + //---------- // lifecycle //---------- @@ -1112,7 +1208,7 @@ init_threadstate(PyThreadState *tstate, PyInterpreterState *interp, uint64_t id, PyThreadState *next) { - if (tstate->_status != PyThreadState_UNINITIALIZED) { + if (tstate->_status.initialized) { Py_FatalError("thread state already initialized"); } @@ -1148,7 +1244,7 @@ init_threadstate(PyThreadState *tstate, tstate->datastack_top = NULL; tstate->datastack_limit = NULL; - tstate->_status = PyThreadState_INITIALIZED; + tstate->_status.initialized = 1; } static PyThreadState * @@ -1208,17 +1304,29 @@ PyThreadState_New(PyInterpreterState *interp) PyThreadState *tstate = new_threadstate(interp); if (tstate) { bind_tstate(tstate); + // This makes sure there's a gilstate tstate bound + // as soon as possible. + if (gilstate_tss_get(tstate->interp->runtime) == NULL) { + bind_gilstate_tstate(tstate); + } } return tstate; } // This must be followed by a call to _PyThreadState_Bind(); PyThreadState * -_PyThreadState_Prealloc(PyInterpreterState *interp) +_PyThreadState_New(PyInterpreterState *interp) { return new_threadstate(interp); } +// We keep this for stable ABI compabibility. +PyThreadState * +_PyThreadState_Prealloc(PyInterpreterState *interp) +{ + return _PyThreadState_New(interp); +} + // We keep this around for (accidental) stable ABI compatibility. // Realistically, no extensions are using it. void @@ -1230,6 +1338,17 @@ _PyThreadState_Init(PyThreadState *tstate) void PyThreadState_Clear(PyThreadState *tstate) { + assert(tstate->_status.initialized && !tstate->_status.cleared); + // XXX assert(!tstate->_status.bound || tstate->_status.unbound); + tstate->_status.finalizing = 1; // just in case + + /* XXX Conditions we need to enforce: + + * the GIL must be held by the current thread + * current_fast_get()->interp must match tstate->interp + * for the main interpreter, current_fast_get() must be the main thread + */ + int verbose = _PyInterpreterState_GetConfig(tstate->interp)->verbose; if (verbose && tstate->cframe->current_frame != NULL) { @@ -1244,6 +1363,17 @@ PyThreadState_Clear(PyThreadState *tstate) "PyThreadState_Clear: warning: thread still has a frame\n"); } + /* At this point tstate shouldn't be used any more, + neither to run Python code nor for other uses. + + This is tricky when current_fast_get() == tstate, in the same way + as noted in interpreter_clear() above. The below finalizers + can possibly run Python code or otherwise use the partially + cleared thread state. For now we trust that isn't a problem + in practice. + */ + // XXX Deal with the possibility of problematic finalizers. + /* Don't clear tstate->pyframe: it is a borrowed reference */ Py_CLEAR(tstate->dict); @@ -1274,6 +1404,11 @@ PyThreadState_Clear(PyThreadState *tstate) if (tstate->on_delete != NULL) { tstate->on_delete(tstate->on_delete_data); } + + tstate->_status.cleared = 1; + + // XXX Call _PyThreadStateSwap(runtime, NULL) here if "current". + // XXX Do it as early in the function as possible. } @@ -1281,7 +1416,8 @@ PyThreadState_Clear(PyThreadState *tstate) static void tstate_delete_common(PyThreadState *tstate) { - _Py_EnsureTstateNotNULL(tstate); + assert(tstate->_status.cleared && !tstate->_status.finalized); + PyInterpreterState *interp = tstate->interp; if (interp == NULL) { Py_FatalError("NULL interpreter"); @@ -1300,7 +1436,11 @@ tstate_delete_common(PyThreadState *tstate) } HEAD_UNLOCK(runtime); - // XXX Do this in PyThreadState_Swap() (and assert not-equal here)? + // XXX Unbind in PyThreadState_Clear(), or earlier + // (and assert not-equal here)? + if (tstate->_status.bound_gilstate) { + unbind_gilstate_tstate(tstate); + } unbind_tstate(tstate); // XXX Move to PyThreadState_Clear()? @@ -1311,25 +1451,32 @@ tstate_delete_common(PyThreadState *tstate) _PyObject_VirtualFree(chunk, chunk->size); chunk = prev; } + + tstate->_status.finalized = 1; } + static void -_PyThreadState_Delete(PyThreadState *tstate, int check_current) +zapthreads(PyInterpreterState *interp) { - if (check_current) { - if (tstate == current_fast_get(tstate->interp->runtime)) { - _Py_FatalErrorFormat(__func__, "tstate %p is still current", tstate); - } + PyThreadState *tstate; + /* No need to lock the mutex here because this should only happen + when the threads are all really dead (XXX famous last words). */ + while ((tstate = interp->threads.head) != NULL) { + tstate_verify_not_active(tstate); + tstate_delete_common(tstate); + free_threadstate(tstate); } - tstate_delete_common(tstate); - free_threadstate(tstate); } void PyThreadState_Delete(PyThreadState *tstate) { - _PyThreadState_Delete(tstate, 1); + _Py_EnsureTstateNotNULL(tstate); + tstate_verify_not_active(tstate); + tstate_delete_common(tstate); + free_threadstate(tstate); } @@ -1359,9 +1506,11 @@ PyThreadState_DeleteCurrent(void) * be kept in those other interpreters. */ void -_PyThreadState_DeleteExcept(_PyRuntimeState *runtime, PyThreadState *tstate) +_PyThreadState_DeleteExcept(PyThreadState *tstate) { + assert(tstate != NULL); PyInterpreterState *interp = tstate->interp; + _PyRuntimeState *runtime = interp->runtime; HEAD_LOCK(runtime); /* Remove all thread states, except tstate, from the linked list of @@ -1460,6 +1609,38 @@ PyThreadState_GetID(PyThreadState *tstate) } +static inline void +tstate_activate(PyThreadState *tstate) +{ + assert(tstate != NULL); + // XXX assert(tstate_is_alive(tstate)); + assert(tstate_is_bound(tstate)); + assert(!tstate->_status.active); + + assert(!tstate->_status.bound_gilstate || + tstate == gilstate_tss_get((tstate->interp->runtime))); + if (!tstate->_status.bound_gilstate) { + bind_gilstate_tstate(tstate); + } + + tstate->_status.active = 1; +} + +static inline void +tstate_deactivate(PyThreadState *tstate) +{ + assert(tstate != NULL); + // XXX assert(tstate_is_alive(tstate)); + assert(tstate_is_bound(tstate)); + assert(tstate->_status.active); + + tstate->_status.active = 0; + + // We do not unbind the gilstate tstate here. + // It will still be used in PyGILState_Ensure(). +} + + //---------- // other API //---------- @@ -1535,31 +1716,43 @@ PyThreadState_Get(void) PyThreadState * _PyThreadState_Swap(_PyRuntimeState *runtime, PyThreadState *newts) { +#if defined(Py_DEBUG) + /* This can be called from PyEval_RestoreThread(). Similar + to it, we need to ensure errno doesn't change. + */ + int err = errno; +#endif PyThreadState *oldts = current_fast_get(runtime); - if (newts == NULL) { - current_fast_clear(runtime); + current_fast_clear(runtime); + + if (oldts != NULL) { + // XXX assert(tstate_is_alive(oldts) && tstate_is_bound(oldts)); + tstate_deactivate(oldts); } - else { + + if (newts != NULL) { + // XXX assert(tstate_is_alive(newts)); + assert(tstate_is_bound(newts)); current_fast_set(runtime, newts); + tstate_activate(newts); } + /* It should not be possible for more than one thread state to be used for a thread. Check this the best we can in debug builds. */ // XXX The above isn't true when multiple interpreters are involved. #if defined(Py_DEBUG) - if (newts && current_tss_initialized(runtime)) { - /* This can be called from PyEval_RestoreThread(). Similar - to it, we need to ensure errno doesn't change. - */ - int err = errno; - PyThreadState *check = current_tss_get(runtime); - if (check && check->interp == newts->interp && check != newts) { - Py_FatalError("Invalid thread state for this thread"); + if (newts && gilstate_tss_initialized(runtime)) { + PyThreadState *check = gilstate_tss_get(runtime); + if (check != newts) { + if (check && check->interp == newts->interp) { + Py_FatalError("Invalid thread state for this thread"); + } } - errno = err; } + errno = err; #endif return oldts; } @@ -1575,6 +1768,11 @@ void _PyThreadState_Bind(PyThreadState *tstate) { bind_tstate(tstate); + // This makes sure there's a gilstate tstate bound + // as soon as possible. + if (gilstate_tss_get(tstate->interp->runtime) == NULL) { + bind_gilstate_tstate(tstate); + } } @@ -1863,7 +2061,7 @@ _PyGILState_Init(PyInterpreterState *interp) return _PyStatus_OK(); } _PyRuntimeState *runtime = interp->runtime; - assert(current_tss_get(runtime) == NULL); + assert(gilstate_tss_get(runtime) == NULL); assert(runtime->gilstate.autoInterpreterState == NULL); runtime->gilstate.autoInterpreterState = interp; return _PyStatus_OK(); @@ -1899,7 +2097,7 @@ _PyGILState_SetTstate(PyThreadState *tstate) _PyRuntimeState *runtime = tstate->interp->runtime; assert(runtime->gilstate.autoInterpreterState == tstate->interp); - assert(current_tss_get(runtime) == tstate); + assert(gilstate_tss_get(runtime) == tstate); assert(tstate->gilstate_counter == 1); #endif @@ -1918,10 +2116,10 @@ PyThreadState * PyGILState_GetThisThreadState(void) { _PyRuntimeState *runtime = &_PyRuntime; - if (!current_tss_initialized(runtime)) { + if (!gilstate_tss_initialized(runtime)) { return NULL; } - return current_tss_get(runtime); + return gilstate_tss_get(runtime); } int @@ -1932,7 +2130,7 @@ PyGILState_Check(void) return 1; } - if (!current_tss_initialized(runtime)) { + if (!gilstate_tss_initialized(runtime)) { return 1; } @@ -1941,7 +2139,7 @@ PyGILState_Check(void) return 0; } - return (tstate == current_tss_get(runtime)); + return (tstate == gilstate_tss_get(runtime)); } PyGILState_STATE @@ -1957,17 +2155,19 @@ PyGILState_Ensure(void) /* Ensure that _PyEval_InitThreads() and _PyGILState_Init() have been called by Py_Initialize() */ assert(_PyEval_ThreadsInitialized(runtime)); - assert(current_tss_initialized(runtime)); + assert(gilstate_tss_initialized(runtime)); assert(runtime->gilstate.autoInterpreterState != NULL); - PyThreadState *tcur = current_tss_get(runtime); + PyThreadState *tcur = gilstate_tss_get(runtime); int has_gil; if (tcur == NULL) { /* Create a new Python thread state for this thread */ - tcur = PyThreadState_New(runtime->gilstate.autoInterpreterState); + tcur = new_threadstate(runtime->gilstate.autoInterpreterState); if (tcur == NULL) { Py_FatalError("Couldn't create thread-state for new thread"); } + bind_tstate(tcur); + bind_gilstate_tstate(tcur); /* This is our thread state! We'll need to delete it in the matching call to PyGILState_Release(). */ @@ -1997,7 +2197,7 @@ void PyGILState_Release(PyGILState_STATE oldstate) { _PyRuntimeState *runtime = &_PyRuntime; - PyThreadState *tstate = current_tss_get(runtime); + PyThreadState *tstate = gilstate_tss_get(runtime); if (tstate == NULL) { Py_FatalError("auto-releasing thread-state, " "but no thread-state for this thread"); @@ -2023,6 +2223,7 @@ PyGILState_Release(PyGILState_STATE oldstate) if (tstate->gilstate_counter == 0) { /* can't have been locked when we created it */ assert(oldstate == PyGILState_UNLOCKED); + // XXX Unbind tstate here. PyThreadState_Clear(tstate); /* Delete the thread-state. Note this releases the GIL too! * It's vital that the GIL be held here, to avoid shutdown From webhook-mailer at python.org Mon Jan 30 14:24:04 2023 From: webhook-mailer at python.org (gvanrossum) Date: Mon, 30 Jan 2023 19:24:04 -0000 Subject: [Python-checkins] GH-101369: Allow macros as family members (#101399) Message-ID: https://github.com/python/cpython/commit/7a3752338a2bfea023fcb119c842750fe799262f commit: 7a3752338a2bfea023fcb119c842750fe799262f branch: main author: Guido van Rossum committer: gvanrossum date: 2023-01-30T11:23:57-08:00 summary: GH-101369: Allow macros as family members (#101399) Also check for instructions straddling families (this includes macro parts). files: M Tools/cases_generator/generate_cases.py M Tools/cases_generator/test_generator.py diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 1d703a0a790e..f0c5f96733fe 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -426,10 +426,12 @@ class Component: def write_body(self, out: Formatter, cache_adjust: int) -> None: with out.block(""): + input_names = {ieffect.name for _, ieffect in self.input_mapping} for var, ieffect in self.input_mapping: out.declare(ieffect, var) for _, oeffect in self.output_mapping: - out.declare(oeffect, None) + if oeffect.name not in input_names: + out.declare(oeffect, None) self.instr.write_body(out, dedent=-4, cache_adjust=cache_adjust) @@ -565,10 +567,10 @@ def analyze(self) -> None: Raises SystemExit if there is an error. """ self.find_predictions() - self.map_families() - self.check_families() self.analyze_register_instrs() self.analyze_supers_and_macros() + self.map_families() + self.check_families() def find_predictions(self) -> None: """Find the instructions that need PREDICTED() labels.""" @@ -587,11 +589,30 @@ def find_predictions(self) -> None: ) def map_families(self) -> None: - """Make instruction names back to their family, if they have one.""" + """Link instruction names back to their family, if they have one.""" for family in self.families.values(): for member in family.members: if member_instr := self.instrs.get(member): - member_instr.family = family + if member_instr.family not in (family, None): + self.error( + f"Instruction {member} is a member of multiple families " + f"({member_instr.family.name}, {family.name}).", + family + ) + else: + member_instr.family = family + elif member_macro := self.macro_instrs.get(member): + for part in member_macro.parts: + if isinstance(part, Component): + if part.instr.family not in (family, None): + self.error( + f"Component {part.instr.name} of macro {member} " + f"is a member of multiple families " + f"({part.instr.family.name}, {family.name}).", + family + ) + else: + part.instr.family = family else: self.error( f"Unknown instruction {member!r} referenced in family {family.name!r}", @@ -608,7 +629,7 @@ def check_families(self) -> None: for family in self.families.values(): if len(family.members) < 2: self.error(f"Family {family.name!r} has insufficient members", family) - members = [member for member in family.members if member in self.instrs] + members = [member for member in family.members if member in self.instrs or member in self.macro_instrs] if members != family.members: unknown = set(family.members) - set(members) self.error( @@ -616,24 +637,42 @@ def check_families(self) -> None: ) if len(members) < 2: continue - head = self.instrs[members[0]] - cache = head.cache_offset - input = len(head.input_effects) - output = len(head.output_effects) + expected_effects = self.effect_counts(members[0]) for member in members[1:]: - instr = self.instrs[member] - c = instr.cache_offset - i = len(instr.input_effects) - o = len(instr.output_effects) - if (c, i, o) != (cache, input, output): + member_effects = self.effect_counts(member) + if member_effects != expected_effects: self.error( f"Family {family.name!r} has inconsistent " - f"(cache, inputs, outputs) effects:\n" - f" {family.members[0]} = {(cache, input, output)}; " - f"{member} = {(c, i, o)}", + f"(cache, input, output) effects:\n" + f" {family.members[0]} = {expected_effects}; " + f"{member} = {member_effects}", family, ) + def effect_counts(self, name: str) -> tuple[int, int, int]: + if instr := self.instrs.get(name): + cache = instr.cache_offset + input = len(instr.input_effects) + output = len(instr.output_effects) + elif macro := self.macro_instrs.get(name): + cache, input, output = 0, 0, 0 + for part in macro.parts: + if isinstance(part, Component): + cache += part.instr.cache_offset + # A component may pop what the previous component pushed, + # so we offset the input/output counts by that. + delta_i = len(part.instr.input_effects) + delta_o = len(part.instr.output_effects) + offset = min(delta_i, output) + input += delta_i - offset + output += delta_o - offset + else: + assert isinstance(part, parser.CacheEffect), part + cache += part.size + else: + assert False, f"Unknown instruction {name!r}" + return cache, input, output + def analyze_register_instrs(self) -> None: for instr in self.instrs.values(): if instr.register: diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index 6e6c60782d73..49a99377fc04 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -339,21 +339,24 @@ def test_super_instruction(): def test_macro_instruction(): input = """ - inst(OP1, (counter/1, arg1 -- interim)) { - interim = op1(arg1); + inst(OP1, (counter/1, left, right -- left, right)) { + op1(left, right); } - op(OP2, (extra/2, arg2, interim -- res)) { - res = op2(arg2, interim); + op(OP2, (extra/2, arg2, left, right -- res)) { + res = op2(arg2, left, right); } macro(OP) = OP1 + cache/2 + OP2; + inst(OP3, (unused/5, arg2, left, right -- res)) { + res = op3(arg2, left, right); + } + family(op, INLINE_CACHE_ENTRIES_OP) = { OP, OP3 }; """ output = """ TARGET(OP1) { - PyObject *arg1 = PEEK(1); - PyObject *interim; + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); uint16_t counter = read_u16(&next_instr[0].cache); - interim = op1(arg1); - POKE(1, interim); + op1(left, right); JUMPBY(1); DISPATCH(); } @@ -361,24 +364,39 @@ def test_macro_instruction(): TARGET(OP) { PyObject *_tmp_1 = PEEK(1); PyObject *_tmp_2 = PEEK(2); + PyObject *_tmp_3 = PEEK(3); { - PyObject *arg1 = _tmp_1; - PyObject *interim; + PyObject *right = _tmp_1; + PyObject *left = _tmp_2; uint16_t counter = read_u16(&next_instr[0].cache); - interim = op1(arg1); - _tmp_1 = interim; + op1(left, right); + _tmp_2 = left; + _tmp_1 = right; } { - PyObject *interim = _tmp_1; - PyObject *arg2 = _tmp_2; + PyObject *right = _tmp_1; + PyObject *left = _tmp_2; + PyObject *arg2 = _tmp_3; PyObject *res; uint32_t extra = read_u32(&next_instr[3].cache); - res = op2(arg2, interim); - _tmp_2 = res; + res = op2(arg2, left, right); + _tmp_3 = res; } JUMPBY(5); - STACK_SHRINK(1); - POKE(1, _tmp_2); + STACK_SHRINK(2); + POKE(1, _tmp_3); + DISPATCH(); + } + + TARGET(OP3) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + PyObject *arg2 = PEEK(3); + PyObject *res; + res = op3(arg2, left, right); + STACK_SHRINK(2); + POKE(1, res); + JUMPBY(5); DISPATCH(); } """ From webhook-mailer at python.org Mon Jan 30 14:49:18 2023 From: webhook-mailer at python.org (hauntsaninja) Date: Mon, 30 Jan 2023 19:49:18 -0000 Subject: [Python-checkins] gh-101229: Add tests for aliases of imported names (#101230) Message-ID: https://github.com/python/cpython/commit/28db978d7f134edf6c86f21c42e15003511e7e9b commit: 28db978d7f134edf6c86f21c42e15003511e7e9b branch: main author: Eclips4 <80244920+Eclips4 at users.noreply.github.com> committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com> date: 2023-01-30T11:49:06-08:00 summary: gh-101229: Add tests for aliases of imported names (#101230) files: M Lib/test/test_ast.py diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index c728d2b55e42..98d9b603bbc1 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -408,6 +408,24 @@ def test_alias(self): self.assertEqual(alias.col_offset, 16) self.assertEqual(alias.end_col_offset, 17) + im = ast.parse("from bar import y as z").body[0] + alias = im.names[0] + self.assertEqual(alias.name, "y") + self.assertEqual(alias.asname, "z") + self.assertEqual(alias.lineno, 1) + self.assertEqual(alias.end_lineno, 1) + self.assertEqual(alias.col_offset, 16) + self.assertEqual(alias.end_col_offset, 22) + + im = ast.parse("import bar as foo").body[0] + alias = im.names[0] + self.assertEqual(alias.name, "bar") + self.assertEqual(alias.asname, "foo") + self.assertEqual(alias.lineno, 1) + self.assertEqual(alias.end_lineno, 1) + self.assertEqual(alias.col_offset, 7) + self.assertEqual(alias.end_col_offset, 17) + def test_base_classes(self): self.assertTrue(issubclass(ast.For, ast.stmt)) self.assertTrue(issubclass(ast.Name, ast.expr)) From webhook-mailer at python.org Mon Jan 30 15:14:46 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 30 Jan 2023 20:14:46 -0000 Subject: [Python-checkins] gh-101229: Add tests for aliases of imported names (GH-101230) Message-ID: https://github.com/python/cpython/commit/3adac4126d659e1d42acf4625dedc8d92eb1c6c8 commit: 3adac4126d659e1d42acf4625dedc8d92eb1c6c8 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-30T12:14:38-08:00 summary: gh-101229: Add tests for aliases of imported names (GH-101230) (cherry picked from commit 28db978d7f134edf6c86f21c42e15003511e7e9b) Co-authored-by: Eclips4 <80244920+Eclips4 at users.noreply.github.com> files: M Lib/test/test_ast.py diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 55a3e6b82c9c..36fdb49bd1d7 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -355,6 +355,24 @@ def test_alias(self): self.assertEqual(alias.col_offset, 16) self.assertEqual(alias.end_col_offset, 17) + im = ast.parse("from bar import y as z").body[0] + alias = im.names[0] + self.assertEqual(alias.name, "y") + self.assertEqual(alias.asname, "z") + self.assertEqual(alias.lineno, 1) + self.assertEqual(alias.end_lineno, 1) + self.assertEqual(alias.col_offset, 16) + self.assertEqual(alias.end_col_offset, 22) + + im = ast.parse("import bar as foo").body[0] + alias = im.names[0] + self.assertEqual(alias.name, "bar") + self.assertEqual(alias.asname, "foo") + self.assertEqual(alias.lineno, 1) + self.assertEqual(alias.end_lineno, 1) + self.assertEqual(alias.col_offset, 7) + self.assertEqual(alias.end_col_offset, 17) + def test_base_classes(self): self.assertTrue(issubclass(ast.For, ast.stmt)) self.assertTrue(issubclass(ast.Name, ast.expr)) From webhook-mailer at python.org Mon Jan 30 15:21:26 2023 From: webhook-mailer at python.org (miss-islington) Date: Mon, 30 Jan 2023 20:21:26 -0000 Subject: [Python-checkins] gh-101229: Add tests for aliases of imported names (GH-101230) Message-ID: https://github.com/python/cpython/commit/faf8068dd01c8eee7f6ea3f9e608126bf2034dc1 commit: faf8068dd01c8eee7f6ea3f9e608126bf2034dc1 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-30T12:21:18-08:00 summary: gh-101229: Add tests for aliases of imported names (GH-101230) (cherry picked from commit 28db978d7f134edf6c86f21c42e15003511e7e9b) Co-authored-by: Eclips4 <80244920+Eclips4 at users.noreply.github.com> files: M Lib/test/test_ast.py diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index c33c0ae22379..b4ec1fef5ade 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -407,6 +407,24 @@ def test_alias(self): self.assertEqual(alias.col_offset, 16) self.assertEqual(alias.end_col_offset, 17) + im = ast.parse("from bar import y as z").body[0] + alias = im.names[0] + self.assertEqual(alias.name, "y") + self.assertEqual(alias.asname, "z") + self.assertEqual(alias.lineno, 1) + self.assertEqual(alias.end_lineno, 1) + self.assertEqual(alias.col_offset, 16) + self.assertEqual(alias.end_col_offset, 22) + + im = ast.parse("import bar as foo").body[0] + alias = im.names[0] + self.assertEqual(alias.name, "bar") + self.assertEqual(alias.asname, "foo") + self.assertEqual(alias.lineno, 1) + self.assertEqual(alias.end_lineno, 1) + self.assertEqual(alias.col_offset, 7) + self.assertEqual(alias.end_col_offset, 17) + def test_base_classes(self): self.assertTrue(issubclass(ast.For, ast.stmt)) self.assertTrue(issubclass(ast.Name, ast.expr)) From webhook-mailer at python.org Mon Jan 30 18:34:00 2023 From: webhook-mailer at python.org (iritkatriel) Date: Mon, 30 Jan 2023 23:34:00 -0000 Subject: [Python-checkins] gh-101400: Fix incorrect lineno in exception message on continue/break which are not in a loop (#101413) Message-ID: https://github.com/python/cpython/commit/e867c1b753424d8d3f9c9ba0b431d007fd958c80 commit: e867c1b753424d8d3f9c9ba0b431d007fd958c80 branch: main author: Dong-hee Na committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-30T23:33:54Z summary: gh-101400: Fix incorrect lineno in exception message on continue/break which are not in a loop (#101413) files: A Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst M Lib/test/test_syntax.py M Python/compile.c diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index cb284195d976..f23653558a91 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -1957,9 +1957,6 @@ def error2(): """ self._check_error(source, "parameter and nonlocal", lineno=3) - def test_break_outside_loop(self): - self._check_error("break", "outside loop") - def test_yield_outside_function(self): self._check_error("if 0: yield", "outside function") self._check_error("if 0: yield\nelse: x=1", "outside function") @@ -1988,20 +1985,27 @@ def test_return_outside_function(self): "outside function") def test_break_outside_loop(self): - self._check_error("if 0: break", "outside loop") - self._check_error("if 0: break\nelse: x=1", "outside loop") - self._check_error("if 1: pass\nelse: break", "outside loop") - self._check_error("class C:\n if 0: break", "outside loop") + msg = "outside loop" + self._check_error("break", msg, lineno=1) + self._check_error("if 0: break", msg, lineno=1) + self._check_error("if 0: break\nelse: x=1", msg, lineno=1) + self._check_error("if 1: pass\nelse: break", msg, lineno=2) + self._check_error("class C:\n if 0: break", msg, lineno=2) self._check_error("class C:\n if 1: pass\n else: break", - "outside loop") + msg, lineno=3) + self._check_error("with object() as obj:\n break", + msg, lineno=2) def test_continue_outside_loop(self): - self._check_error("if 0: continue", "not properly in loop") - self._check_error("if 0: continue\nelse: x=1", "not properly in loop") - self._check_error("if 1: pass\nelse: continue", "not properly in loop") - self._check_error("class C:\n if 0: continue", "not properly in loop") + msg = "not properly in loop" + self._check_error("if 0: continue", msg, lineno=1) + self._check_error("if 0: continue\nelse: x=1", msg, lineno=1) + self._check_error("if 1: pass\nelse: continue", msg, lineno=2) + self._check_error("class C:\n if 0: continue", msg, lineno=2) self._check_error("class C:\n if 1: pass\n else: continue", - "not properly in loop") + msg, lineno=3) + self._check_error("with object() as obj:\n continue", + msg, lineno=2) def test_unexpected_indent(self): self._check_error("foo()\n bar()\n", "unexpected indent", diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst new file mode 100644 index 000000000000..f3dd783c01e7 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst @@ -0,0 +1,2 @@ +Fix wrong lineno in exception message on :keyword:`continue` or +:keyword:`break` which are not in a loop. Patch by Dong-hee Na. diff --git a/Python/compile.c b/Python/compile.c index c31f08c0a179..70d05af58161 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -3246,11 +3246,12 @@ static int compiler_break(struct compiler *c, location loc) { struct fblockinfo *loop = NULL; + location origin_loc = loc; /* Emit instruction with line number */ ADDOP(c, loc, NOP); RETURN_IF_ERROR(compiler_unwind_fblock_stack(c, &loc, 0, &loop)); if (loop == NULL) { - return compiler_error(c, loc, "'break' outside loop"); + return compiler_error(c, origin_loc, "'break' outside loop"); } RETURN_IF_ERROR(compiler_unwind_fblock(c, &loc, loop, 0)); ADDOP_JUMP(c, loc, JUMP, loop->fb_exit); @@ -3261,11 +3262,12 @@ static int compiler_continue(struct compiler *c, location loc) { struct fblockinfo *loop = NULL; + location origin_loc = loc; /* Emit instruction with line number */ ADDOP(c, loc, NOP); RETURN_IF_ERROR(compiler_unwind_fblock_stack(c, &loc, 0, &loop)); if (loop == NULL) { - return compiler_error(c, loc, "'continue' not properly in loop"); + return compiler_error(c, origin_loc, "'continue' not properly in loop"); } ADDOP_JUMP(c, loc, JUMP, loop->fb_block); return SUCCESS; From webhook-mailer at python.org Mon Jan 30 19:06:26 2023 From: webhook-mailer at python.org (iritkatriel) Date: Tue, 31 Jan 2023 00:06:26 -0000 Subject: [Python-checkins] gh-99955: use SUCCESS/ERROR return values in optimizer and assembler. Use RETURN_IF_ERROR where appropriate. Fix a couple of bugs. (#101412) Message-ID: https://github.com/python/cpython/commit/af7b2db38497479238bba3f7edba1d8f8d685c4f commit: af7b2db38497479238bba3f7edba1d8f8d685c4f branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-31T00:06:19Z summary: gh-99955: use SUCCESS/ERROR return values in optimizer and assembler. Use RETURN_IF_ERROR where appropriate. Fix a couple of bugs. (#101412) files: M Python/compile.c diff --git a/Python/compile.c b/Python/compile.c index 70d05af58161..a11bcc79a6dd 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -176,13 +176,11 @@ static struct jump_target_label_ NO_LABEL = {-1}; #define NEW_JUMP_TARGET_LABEL(C, NAME) \ jump_target_label NAME = cfg_new_label(CFG_BUILDER(C)); \ if (!IS_LABEL(NAME)) { \ - return 0; \ + return ERROR; \ } #define USE_LABEL(C, LBL) \ - if (cfg_builder_use_label(CFG_BUILDER(C), LBL) < 0) { \ - return 0; \ - } + RETURN_IF_ERROR(cfg_builder_use_label(CFG_BUILDER(C), LBL)) struct instr { int i_opcode; @@ -619,8 +617,9 @@ _Py_Mangle(PyObject *privateobj, PyObject *ident) maxchar = PyUnicode_MAX_CHAR_VALUE(privateobj); result = PyUnicode_New(1 + nlen + plen, maxchar); - if (!result) - return 0; + if (!result) { + return NULL; + } /* ident = "_" + priv[ipriv:] + ident # i.e. 1+plen+nlen bytes */ PyUnicode_WRITE(PyUnicode_KIND(result), PyUnicode_DATA(result), 0, '_'); if (PyUnicode_CopyCharacters(result, 1, privateobj, ipriv, plen) < 0) { @@ -991,11 +990,11 @@ basicblock_append_instructions(basicblock *target, basicblock *source) for (int i = 0; i < source->b_iused; i++) { int n = basicblock_next_instr(target); if (n < 0) { - return -1; + return ERROR; } target->b_instr[n] = source->b_instr[i]; } - return 0; + return SUCCESS; } static basicblock * @@ -1029,7 +1028,7 @@ basicblock_next_instr(basicblock *b) DEFAULT_BLOCK_SIZE, sizeof(struct instr)); if (b->b_instr == NULL) { PyErr_NoMemory(); - return -1; + return ERROR; } b->b_ialloc = DEFAULT_BLOCK_SIZE; } @@ -1041,19 +1040,19 @@ basicblock_next_instr(basicblock *b) if (oldsize > (SIZE_MAX >> 1)) { PyErr_NoMemory(); - return -1; + return ERROR; } if (newsize == 0) { PyErr_NoMemory(); - return -1; + return ERROR; } b->b_ialloc <<= 1; tmp = (struct instr *)PyObject_Realloc( (void *)b->b_instr, newsize); if (tmp == NULL) { PyErr_NoMemory(); - return -1; + return ERROR; } b->b_instr = tmp; memset((char *)b->b_instr + oldsize, 0, newsize - oldsize); @@ -1371,21 +1370,19 @@ cfg_builder_maybe_start_new_block(cfg_builder *g) if (cfg_builder_current_block_is_terminated(g)) { basicblock *b = cfg_builder_new_block(g); if (b == NULL) { - return -1; + return ERROR; } b->b_label = g->g_current_label.id; g->g_current_label = NO_LABEL; cfg_builder_use_next_block(g, b); } - return 0; + return SUCCESS; } static int cfg_builder_addop(cfg_builder *g, int opcode, int oparg, location loc) { - if (cfg_builder_maybe_start_new_block(g) != 0) { - return -1; - } + RETURN_IF_ERROR(cfg_builder_maybe_start_new_block(g)); return basicblock_addop(g->g_curblock, opcode, oparg, loc); } @@ -1405,16 +1402,16 @@ dict_add_o(PyObject *dict, PyObject *o) v = PyDict_GetItemWithError(dict, o); if (!v) { if (PyErr_Occurred()) { - return -1; + return ERROR; } arg = PyDict_GET_SIZE(dict); v = PyLong_FromSsize_t(arg); if (!v) { - return -1; + return ERROR; } if (PyDict_SetItem(dict, o, v) < 0) { Py_DECREF(v); - return -1; + return ERROR; } Py_DECREF(v); } @@ -1536,7 +1533,7 @@ compiler_add_const(struct compiler *c, PyObject *o) { PyObject *key = merge_consts_recursive(c->c_const_cache, o); if (key == NULL) { - return -1; + return ERROR; } Py_ssize_t arg = dict_add_o(c->u->u_consts, key); @@ -1622,7 +1619,7 @@ cfg_builder_addop_j(cfg_builder *g, location loc, #define ADDOP_IN_SCOPE(C, LOC, OP) { \ if (cfg_builder_addop_noarg(CFG_BUILDER(C), (OP), (LOC)) < 0) { \ compiler_exit_scope(C); \ - return -1; \ + return ERROR; \ } \ } @@ -1697,8 +1694,7 @@ cfg_builder_addop_j(cfg_builder *g, location loc, asdl_ ## TYPE ## _seq *seq = (SEQ); /* avoid variable capture */ \ for (_i = 0; _i < asdl_seq_LEN(seq); _i++) { \ TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, _i); \ - if (compiler_visit_ ## TYPE((C), elt) < 0) \ - return ERROR; \ + RETURN_IF_ERROR(compiler_visit_ ## TYPE((C), elt)); \ } \ } @@ -2232,7 +2228,7 @@ get_ref_type(struct compiler *c, PyObject *name) name, c->u->u_name, c->u->u_ste->ste_id, c->u->u_ste->ste_symbols, c->u->u_varnames, c->u->u_names); - return -1; + return ERROR; } return scope; } @@ -2240,10 +2236,10 @@ get_ref_type(struct compiler *c, PyObject *name) static int compiler_lookup_arg(PyObject *dict, PyObject *name) { - PyObject *v; - v = PyDict_GetItemWithError(dict, name); - if (v == NULL) - return -1; + PyObject *v = PyDict_GetItemWithError(dict, name); + if (v == NULL) { + return ERROR; + } return PyLong_AS_LONG(v); } @@ -3022,7 +3018,7 @@ compiler_lambda(struct compiler *c, expr_ty e) location loc = LOC(e); funcflags = compiler_default_arguments(c, loc, args); if (funcflags == -1) { - return 0; + return ERROR; } _Py_DECLARE_STR(anon_lambda, ""); @@ -5083,9 +5079,9 @@ compiler_call_helper(struct compiler *c, location loc, if (n ==0 && nelts == 1 && ((expr_ty)asdl_seq_GET(args, 0))->kind == Starred_kind) { VISIT(c, expr, ((expr_ty)asdl_seq_GET(args, 0))->v.Starred.value); } - else if (starunpack_helper(c, loc, args, n, BUILD_LIST, - LIST_APPEND, LIST_EXTEND, 1) < 0) { - return ERROR; + else { + RETURN_IF_ERROR(starunpack_helper(c, loc, args, n, BUILD_LIST, + LIST_APPEND, LIST_EXTEND, 1)); } /* Then keyword arguments */ if (nkwelts) { @@ -7115,7 +7111,7 @@ stackdepth(basicblock *entryblock, int code_flags) } basicblock **stack = make_cfg_traversal_stack(entryblock); if (!stack) { - return -1; + return ERROR; } int maxdepth = 0; @@ -7138,7 +7134,7 @@ stackdepth(basicblock *entryblock, int code_flags) PyErr_Format(PyExc_SystemError, "compiler stack_effect(opcode=%d, arg=%i) failed", instr->i_opcode, instr->i_oparg); - return -1; + return ERROR; } int new_depth = depth + effect; assert(new_depth >= 0); /* invalid code or bug in stackdepth() */ @@ -7194,12 +7190,12 @@ assemble_init(struct assembler *a, int firstlineno) if (a->a_except_table == NULL) { goto error; } - return 0; + return SUCCESS; error: Py_XDECREF(a->a_bytecode); Py_XDECREF(a->a_linetable); Py_XDECREF(a->a_except_table); - return -1; + return ERROR; } static void @@ -7270,13 +7266,13 @@ static int label_exception_targets(basicblock *entryblock) { basicblock **todo_stack = make_cfg_traversal_stack(entryblock); if (todo_stack == NULL) { - return -1; + return ERROR; } ExceptStack *except_stack = make_except_stack(); if (except_stack == NULL) { PyMem_Free(todo_stack); PyErr_NoMemory(); - return -1; + return ERROR; } except_stack->depth = 0; todo_stack[0] = entryblock; @@ -7354,11 +7350,11 @@ label_exception_targets(basicblock *entryblock) { } #endif PyMem_Free(todo_stack); - return 0; + return SUCCESS; error: PyMem_Free(todo_stack); PyMem_Free(except_stack); - return -1; + return ERROR; } @@ -7377,14 +7373,14 @@ mark_except_handlers(basicblock *entryblock) { } } } - return 0; + return SUCCESS; } static int mark_warm(basicblock *entryblock) { basicblock **stack = make_cfg_traversal_stack(entryblock); if (stack == NULL) { - return -1; + return ERROR; } basicblock **sp = stack; @@ -7408,7 +7404,7 @@ mark_warm(basicblock *entryblock) { } } PyMem_Free(stack); - return 0; + return SUCCESS; } static int @@ -7417,12 +7413,12 @@ mark_cold(basicblock *entryblock) { assert(!b->b_cold && !b->b_warm); } if (mark_warm(entryblock) < 0) { - return -1; + return ERROR; } basicblock **stack = make_cfg_traversal_stack(entryblock); if (stack == NULL) { - return -1; + return ERROR; } basicblock **sp = stack; @@ -7457,7 +7453,7 @@ mark_cold(basicblock *entryblock) { } } PyMem_Free(stack); - return 0; + return SUCCESS; } static int @@ -7468,11 +7464,9 @@ push_cold_blocks_to_end(cfg_builder *g, int code_flags) { basicblock *entryblock = g->g_entryblock; if (entryblock->b_next == NULL) { /* single basicblock, no need to reorder */ - return 0; - } - if (mark_cold(entryblock) < 0) { - return -1; + return SUCCESS; } + RETURN_IF_ERROR(mark_cold(entryblock)); /* If we have a cold block with fallthrough to a warm block, add */ /* an explicit jump instead of fallthrough */ @@ -7480,7 +7474,7 @@ push_cold_blocks_to_end(cfg_builder *g, int code_flags) { if (b->b_cold && BB_HAS_FALLTHROUGH(b) && b->b_next && b->b_next->b_warm) { basicblock *explicit_jump = cfg_builder_new_block(g); if (explicit_jump == NULL) { - return -1; + return ERROR; } basicblock_addop(explicit_jump, JUMP, b->b_next->b_label, NO_LOCATION); explicit_jump->b_cold = 1; @@ -7534,11 +7528,9 @@ push_cold_blocks_to_end(cfg_builder *g, int code_flags) { b->b_next = cold_blocks; if (cold_blocks != NULL) { - if (remove_redundant_jumps(g) < 0) { - return -1; - } + RETURN_IF_ERROR(remove_redundant_jumps(g)); } - return 0; + return SUCCESS; } static void @@ -7596,9 +7588,7 @@ assemble_emit_exception_table_entry(struct assembler *a, int start, int end, bas { Py_ssize_t len = PyBytes_GET_SIZE(a->a_except_table); if (a->a_except_table_off + MAX_SIZE_OF_ENTRY >= len) { - if (_PyBytes_Resize(&a->a_except_table, len * 2) < 0) { - return -1; - } + RETURN_IF_ERROR(_PyBytes_Resize(&a->a_except_table, len * 2)); } int size = end-start; assert(end > start); @@ -7613,7 +7603,7 @@ assemble_emit_exception_table_entry(struct assembler *a, int start, int end, bas assemble_emit_exception_table_item(a, size, 0); assemble_emit_exception_table_item(a, target, 0); assemble_emit_exception_table_item(a, depth_lasti, 0); - return 0; + return SUCCESS; } static int @@ -7629,9 +7619,8 @@ assemble_exception_table(struct assembler *a, basicblock *entryblock) struct instr *instr = &b->b_instr[i]; if (instr->i_except != handler) { if (handler != NULL) { - if (assemble_emit_exception_table_entry(a, start, ioffset, handler) < 0) { - return -1; - } + RETURN_IF_ERROR( + assemble_emit_exception_table_entry(a, start, ioffset, handler)); } start = ioffset; handler = instr->i_except; @@ -7640,11 +7629,9 @@ assemble_exception_table(struct assembler *a, basicblock *entryblock) } } if (handler != NULL) { - if (assemble_emit_exception_table_entry(a, start, ioffset, handler) < 0) { - return -1; - } + RETURN_IF_ERROR(assemble_emit_exception_table_entry(a, start, ioffset, handler)); } - return 0; + return SUCCESS; } /* Code location emitting code. See locations.md for a description of the format. */ @@ -7746,13 +7733,11 @@ write_location_info_entry(struct assembler* a, location loc, int isize) Py_ssize_t len = PyBytes_GET_SIZE(a->a_linetable); if (a->a_location_off + THEORETICAL_MAX_ENTRY_SIZE >= len) { assert(len > THEORETICAL_MAX_ENTRY_SIZE); - if (_PyBytes_Resize(&a->a_linetable, len*2) < 0) { - return -1; - } + RETURN_IF_ERROR(_PyBytes_Resize(&a->a_linetable, len*2)); } if (loc.lineno < 0) { write_location_info_none(a, isize); - return 0; + return SUCCESS; } int line_delta = loc.lineno - a->a_lineno; int column = loc.col_offset; @@ -7763,35 +7748,33 @@ write_location_info_entry(struct assembler* a, location loc, int isize) if (loc.end_lineno == loc.lineno || loc.end_lineno == -1) { write_location_info_no_column(a, isize, line_delta); a->a_lineno = loc.lineno; - return 0; + return SUCCESS; } } else if (loc.end_lineno == loc.lineno) { if (line_delta == 0 && column < 80 && end_column - column < 16 && end_column >= column) { write_location_info_short_form(a, isize, column, end_column); - return 0; + return SUCCESS; } if (line_delta >= 0 && line_delta < 3 && column < 128 && end_column < 128) { write_location_info_oneline_form(a, isize, line_delta, column, end_column); a->a_lineno = loc.lineno; - return 0; + return SUCCESS; } } write_location_info_long_form(a, loc, isize); a->a_lineno = loc.lineno; - return 0; + return SUCCESS; } static int assemble_emit_location(struct assembler* a, location loc, int isize) { if (isize == 0) { - return 0; + return SUCCESS; } while (isize > 8) { - if (write_location_info_entry(a, loc, 8)) { - return -1; - } + RETURN_IF_ERROR(write_location_info_entry(a, loc, 8)); isize -= 8; } return write_location_info_entry(a, loc, isize); @@ -7811,34 +7794,32 @@ assemble_emit(struct assembler *a, struct instr *i) int size = instr_size(i); if (a->a_offset + size >= len / (int)sizeof(_Py_CODEUNIT)) { if (len > PY_SSIZE_T_MAX / 2) { - return -1; - } - if (_PyBytes_Resize(&a->a_bytecode, len * 2) < 0) { - return -1; + return ERROR; } + RETURN_IF_ERROR(_PyBytes_Resize(&a->a_bytecode, len * 2)); } code = (_Py_CODEUNIT *)PyBytes_AS_STRING(a->a_bytecode) + a->a_offset; a->a_offset += size; write_instr(code, i, size); - return 0; + return SUCCESS; } static int normalize_jumps_in_block(cfg_builder *g, basicblock *b) { struct instr *last = basicblock_last_instr(b); if (last == NULL || !is_jump(last)) { - return 0; + return SUCCESS; } assert(!IS_ASSEMBLER_OPCODE(last->i_opcode)); bool is_forward = last->i_target->b_visited == 0; switch(last->i_opcode) { case JUMP: last->i_opcode = is_forward ? JUMP_FORWARD : JUMP_BACKWARD; - return 0; + return SUCCESS; case JUMP_NO_INTERRUPT: last->i_opcode = is_forward ? JUMP_FORWARD : JUMP_BACKWARD_NO_INTERRUPT; - return 0; + return SUCCESS; } int reversed_opcode = 0; switch(last->i_opcode) { @@ -7868,10 +7849,10 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) { last->i_opcode == JUMP_IF_TRUE_OR_POP ? "JUMP_IF_TRUE_OR_POP" : "JUMP_IF_FALSE_OR_POP"); } - return 0; + return SUCCESS; } if (is_forward) { - return 0; + return SUCCESS; } /* transform 'conditional jump T' to @@ -7881,7 +7862,7 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) { basicblock *target = last->i_target; basicblock *backwards_jump = cfg_builder_new_block(g); if (backwards_jump == NULL) { - return -1; + return ERROR; } basicblock_addop(backwards_jump, JUMP, target->b_label, NO_LOCATION); backwards_jump->b_instr[0].i_target = target; @@ -7891,7 +7872,7 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) { backwards_jump->b_cold = b->b_cold; backwards_jump->b_next = b->b_next; b->b_next = backwards_jump; - return 0; + return SUCCESS; } static int @@ -7903,11 +7884,9 @@ normalize_jumps(cfg_builder *g) } for (basicblock *b = entryblock; b != NULL; b = b->b_next) { b->b_visited = 1; - if (normalize_jumps_in_block(g, b) < 0) { - return -1; - } + RETURN_IF_ERROR(normalize_jumps_in_block(g, b)); } - return 0; + return SUCCESS; } static void @@ -8047,7 +8026,7 @@ fast_scan_many_locals(basicblock *entryblock, int nlocals) Py_ssize_t *states = PyMem_Calloc(nlocals - 64, sizeof(Py_ssize_t)); if (states == NULL) { PyErr_NoMemory(); - return -1; + return ERROR; } Py_ssize_t blocknum = 0; // state[i - 64] == blocknum if local i is guaranteed to @@ -8083,7 +8062,7 @@ fast_scan_many_locals(basicblock *entryblock, int nlocals) } } PyMem_Free(states); - return 0; + return SUCCESS; } static int @@ -8092,20 +8071,20 @@ add_checks_for_loads_of_uninitialized_variables(basicblock *entryblock, { int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames); if (nlocals == 0) { - return 0; + return SUCCESS; } if (nlocals > 64) { // To avoid O(nlocals**2) compilation, locals beyond the first // 64 are only analyzed one basicblock at a time: initialization // info is not passed between basicblocks. if (fast_scan_many_locals(entryblock, nlocals) < 0) { - return -1; + return ERROR; } nlocals = 64; } basicblock **stack = make_cfg_traversal_stack(entryblock); if (stack == NULL) { - return -1; + return ERROR; } basicblock **sp = stack; @@ -8134,7 +8113,7 @@ add_checks_for_loads_of_uninitialized_variables(basicblock *entryblock, scan_block_for_locals(b, &sp); } PyMem_Free(stack); - return 0; + return SUCCESS; } static PyObject * @@ -8220,17 +8199,17 @@ merge_const_one(PyObject *const_cache, PyObject **obj) PyDict_CheckExact(const_cache); PyObject *key = _PyCode_ConstantKey(*obj); if (key == NULL) { - return -1; + return ERROR; } // t is borrowed reference PyObject *t = PyDict_SetDefault(const_cache, key, key); Py_DECREF(key); if (t == NULL) { - return -1; + return ERROR; } if (t == key) { // obj is new constant. - return 0; + return SUCCESS; } if (PyTuple_CheckExact(t)) { @@ -8239,7 +8218,7 @@ merge_const_one(PyObject *const_cache, PyObject **obj) } Py_SETREF(*obj, Py_NewRef(t)); - return 0; + return SUCCESS; } // This is in codeobject.c. @@ -8482,18 +8461,14 @@ insert_prefix_instructions(struct compiler *c, basicblock *entryblock, .i_loc = LOCATION(c->u->u_firstlineno, c->u->u_firstlineno, -1, -1), .i_target = NULL, }; - if (insert_instruction(entryblock, 0, &make_gen) < 0) { - return -1; - } + RETURN_IF_ERROR(insert_instruction(entryblock, 0, &make_gen)); struct instr pop_top = { .i_opcode = POP_TOP, .i_oparg = 0, .i_loc = NO_LOCATION, .i_target = NULL, }; - if (insert_instruction(entryblock, 1, &pop_top) < 0) { - return -1; - } + RETURN_IF_ERROR(insert_instruction(entryblock, 1, &pop_top)); } /* Set up cells for any variable that escapes, to be put in a closure. */ @@ -8506,7 +8481,7 @@ insert_prefix_instructions(struct compiler *c, basicblock *entryblock, int *sorted = PyMem_RawCalloc(nvars, sizeof(int)); if (sorted == NULL) { PyErr_NoMemory(); - return -1; + return ERROR; } for (int i = 0; i < ncellvars; i++) { sorted[fixed[i]] = i + 1; @@ -8523,9 +8498,7 @@ insert_prefix_instructions(struct compiler *c, basicblock *entryblock, .i_loc = NO_LOCATION, .i_target = NULL, }; - if (insert_instruction(entryblock, ncellsused, &make_cell) < 0) { - return -1; - } + RETURN_IF_ERROR(insert_instruction(entryblock, ncellsused, &make_cell)); ncellsused += 1; } PyMem_RawFree(sorted); @@ -8538,13 +8511,10 @@ insert_prefix_instructions(struct compiler *c, basicblock *entryblock, .i_loc = NO_LOCATION, .i_target = NULL, }; - if (insert_instruction(entryblock, 0, ©_frees) < 0) { - return -1; - } - + RETURN_IF_ERROR(insert_instruction(entryblock, 0, ©_frees)); } - return 0; + return SUCCESS; } /* Make sure that all returns have a line number, even if early passes @@ -8704,7 +8674,7 @@ remove_redundant_jumps(cfg_builder *g) { if (IS_UNCONDITIONAL_JUMP_OPCODE(last->i_opcode)) { if (last->i_target == NULL) { PyErr_SetString(PyExc_SystemError, "jump with NULL target"); - return -1; + return ERROR; } if (last->i_target == b->b_next) { assert(b->b_next->b_iused); @@ -8712,7 +8682,7 @@ remove_redundant_jumps(cfg_builder *g) { } } } - return 0; + return SUCCESS; } static int @@ -8729,7 +8699,7 @@ prepare_localsplus(struct compiler* c, int code_flags) int nlocalsplus = nlocals + ncellvars + nfreevars; int* cellfixedoffsets = build_cellfixedoffsets(c); if (cellfixedoffsets == NULL) { - return -1; + return ERROR; } cfg_builder* g = CFG_BUILDER(c); @@ -8737,14 +8707,14 @@ prepare_localsplus(struct compiler* c, int code_flags) // This must be called before fix_cell_offsets(). if (insert_prefix_instructions(c, g->g_entryblock, cellfixedoffsets, nfreevars, code_flags)) { PyMem_Free(cellfixedoffsets); - return -1; + return ERROR; } int numdropped = fix_cell_offsets(c, g->g_entryblock, cellfixedoffsets); PyMem_Free(cellfixedoffsets); // At this point we're done with it. cellfixedoffsets = NULL; if (numdropped < 0) { - return -1; + return ERROR; } nlocalsplus -= numdropped; return nlocalsplus; @@ -8825,7 +8795,7 @@ assemble(struct compiler *c, int addNone) if (add_checks_for_loads_of_uninitialized_variables(g->g_entryblock, c) < 0) { goto error; } - if (remove_unused_consts(g->g_entryblock, consts)) { + if (remove_unused_consts(g->g_entryblock, consts) < 0) { goto error; } @@ -8967,27 +8937,27 @@ fold_tuple_on_constants(PyObject *const_cache, for (int i = 0; i < n; i++) { if (!HAS_CONST(inst[i].i_opcode)) { - return 0; + return SUCCESS; } } /* Buildup new tuple of constants */ PyObject *newconst = PyTuple_New(n); if (newconst == NULL) { - return -1; + return ERROR; } for (int i = 0; i < n; i++) { int op = inst[i].i_opcode; int arg = inst[i].i_oparg; PyObject *constant = get_const_value(op, arg, consts); if (constant == NULL) { - return -1; + return ERROR; } PyTuple_SET_ITEM(newconst, i, constant); } if (merge_const_one(const_cache, &newconst) < 0) { Py_DECREF(newconst); - return -1; + return ERROR; } Py_ssize_t index; @@ -9000,11 +8970,11 @@ fold_tuple_on_constants(PyObject *const_cache, if ((size_t)index >= (size_t)INT_MAX - 1) { Py_DECREF(newconst); PyErr_SetString(PyExc_OverflowError, "too many constants"); - return -1; + return ERROR; } if (PyList_Append(consts, newconst)) { Py_DECREF(newconst); - return -1; + return ERROR; } } Py_DECREF(newconst); @@ -9012,7 +8982,7 @@ fold_tuple_on_constants(PyObject *const_cache, INSTR_SET_OP0(&inst[i], NOP); } INSTR_SET_OP1(&inst[n], LOAD_CONST, (int)index); - return 0; + return SUCCESS; } #define VISITED (-1) @@ -9045,13 +9015,13 @@ swaptimize(basicblock *block, int *ix) } // It's already optimal if there's only one SWAP: if (!more) { - return 0; + return SUCCESS; } // Create an array with elements {0, 1, 2, ..., depth - 1}: int *stack = PyMem_Malloc(depth * sizeof(int)); if (stack == NULL) { PyErr_NoMemory(); - return -1; + return ERROR; } for (int i = 0; i < depth; i++) { stack[i] = i; @@ -9112,7 +9082,7 @@ swaptimize(basicblock *block, int *ix) } PyMem_Free(stack); *ix += len - 1; - return 0; + return SUCCESS; } // This list is pretty small, since it's only okay to reorder opcodes that: @@ -9418,7 +9388,7 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) INSTR_SET_OP0(inst, NOP); break; } - if (swaptimize(bb, &i)) { + if (swaptimize(bb, &i) < 0) { goto error; } apply_static_swaps(bb, i); @@ -9436,9 +9406,9 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) assert (!HAS_CONST(inst->i_opcode)); } } - return 0; + return SUCCESS; error: - return -1; + return ERROR; } /* If this block ends with an unconditional jump to a small exit block, then @@ -9457,9 +9427,7 @@ inline_small_exit_blocks(basicblock *bb) { basicblock *target = last->i_target; if (basicblock_exits_scope(target) && target->b_iused <= MAX_COPY_SIZE) { INSTR_SET_OP0(last, NOP); - if (basicblock_append_instructions(bb, target) < 0) { - return -1; - } + RETURN_IF_ERROR(basicblock_append_instructions(bb, target)); return 1; } return 0; @@ -9528,19 +9496,19 @@ check_cfg(cfg_builder *g) { if (IS_TERMINATOR_OPCODE(opcode)) { if (i != b->b_iused - 1) { PyErr_SetString(PyExc_SystemError, "malformed control flow graph."); - return -1; + return ERROR; } } } } - return 0; + return SUCCESS; } static int mark_reachable(basicblock *entryblock) { basicblock **stack = make_cfg_traversal_stack(entryblock); if (stack == NULL) { - return -1; + return ERROR; } basicblock **sp = stack; entryblock->b_predecessors = 1; @@ -9569,7 +9537,7 @@ mark_reachable(basicblock *entryblock) { } } PyMem_Free(stack); - return 0; + return SUCCESS; } static void @@ -9658,7 +9626,7 @@ translate_jump_labels_to_targets(basicblock *entryblock) basicblock **label2block = (basicblock **)PyMem_Malloc(mapsize); if (!label2block) { PyErr_NoMemory(); - return -1; + return ERROR; } memset(label2block, 0, mapsize); for (basicblock *b = entryblock; b != NULL; b = b->b_next) { @@ -9680,7 +9648,7 @@ translate_jump_labels_to_targets(basicblock *entryblock) } } PyMem_Free(label2block); - return 0; + return SUCCESS; } /* Perform optimizations on a control flow graph. @@ -9695,31 +9663,22 @@ static int optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache) { assert(PyDict_CheckExact(const_cache)); - if (check_cfg(g) < 0) { - return -1; - } + RETURN_IF_ERROR(check_cfg(g)); eliminate_empty_basic_blocks(g); for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { - if (inline_small_exit_blocks(b) < 0) { - return -1; - } + RETURN_IF_ERROR(inline_small_exit_blocks(b)); } assert(no_empty_basic_blocks(g)); for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { - if (optimize_basic_block(const_cache, b, consts)) { - return -1; - } + RETURN_IF_ERROR(optimize_basic_block(const_cache, b, consts)); remove_redundant_nops(b); assert(b->b_predecessors == 0); } for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { - if (inline_small_exit_blocks(b) < 0) { - return -1; - } - } - if (mark_reachable(g->g_entryblock)) { - return -1; + RETURN_IF_ERROR(inline_small_exit_blocks(b)); } + RETURN_IF_ERROR(mark_reachable(g->g_entryblock)); + /* Delete unreachable instructions */ for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { if (b->b_predecessors == 0) { @@ -9731,10 +9690,8 @@ optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache) } eliminate_empty_basic_blocks(g); assert(no_redundant_nops(g)); - if (remove_redundant_jumps(g) < 0) { - return -1; - } - return 0; + RETURN_IF_ERROR(remove_redundant_jumps(g)); + return SUCCESS; } @@ -9744,12 +9701,12 @@ remove_unused_consts(basicblock *entryblock, PyObject *consts) assert(PyList_CheckExact(consts)); Py_ssize_t nconsts = PyList_GET_SIZE(consts); if (nconsts == 0) { - return 0; /* nothing to do */ + return SUCCESS; /* nothing to do */ } Py_ssize_t *index_map = NULL; Py_ssize_t *reverse_index_map = NULL; - int err = 1; + int err = ERROR; index_map = PyMem_Malloc(nconsts * sizeof(Py_ssize_t)); if (index_map == NULL) { @@ -9784,7 +9741,7 @@ remove_unused_consts(basicblock *entryblock, PyObject *consts) } if (n_used_consts == nconsts) { /* nothing to do */ - err = 0; + err = SUCCESS; goto end; } @@ -9832,7 +9789,7 @@ remove_unused_consts(basicblock *entryblock, PyObject *consts) } } - err = 0; + err = SUCCESS; end: PyMem_Free(index_map); PyMem_Free(reverse_index_map); @@ -9876,7 +9833,7 @@ duplicate_exits_without_lineno(cfg_builder *g) if (is_exit_without_lineno(target) && target->b_predecessors > 1) { basicblock *new_target = copy_basicblock(g, target); if (new_target == NULL) { - return -1; + return ERROR; } new_target->b_instr[0].i_loc = last->i_loc; last->i_target = new_target; @@ -9899,7 +9856,7 @@ duplicate_exits_without_lineno(cfg_builder *g) } } } - return 0; + return SUCCESS; } @@ -9928,54 +9885,50 @@ instructions_to_cfg(PyObject *instructions, cfg_builder *g) if (PyLong_Check(item)) { int lbl_id = PyLong_AsLong(item); if (PyErr_Occurred()) { - return -1; + return ERROR; } if (lbl_id <= 0 || lbl_id > instr_size) { /* expect label in a reasonable range */ PyErr_SetString(PyExc_ValueError, "label out of range"); - return -1; + return ERROR; } jump_target_label lbl = {lbl_id}; - if (cfg_builder_use_label(g, lbl) < 0) { - return -1; - } + RETURN_IF_ERROR(cfg_builder_use_label(g, lbl)); } else { if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 6) { PyErr_SetString(PyExc_ValueError, "expected a 6-tuple"); - return -1; + return ERROR; } int opcode = PyLong_AsLong(PyTuple_GET_ITEM(item, 0)); if (PyErr_Occurred()) { - return -1; + return ERROR; } int oparg = PyLong_AsLong(PyTuple_GET_ITEM(item, 1)); if (PyErr_Occurred()) { - return -1; + return ERROR; } location loc; loc.lineno = PyLong_AsLong(PyTuple_GET_ITEM(item, 2)); if (PyErr_Occurred()) { - return -1; + return ERROR; } loc.end_lineno = PyLong_AsLong(PyTuple_GET_ITEM(item, 3)); if (PyErr_Occurred()) { - return -1; + return ERROR; } loc.col_offset = PyLong_AsLong(PyTuple_GET_ITEM(item, 4)); if (PyErr_Occurred()) { - return -1; + return ERROR; } loc.end_col_offset = PyLong_AsLong(PyTuple_GET_ITEM(item, 5)); if (PyErr_Occurred()) { - return -1; - } - if (cfg_builder_addop(g, opcode, oparg, loc) < 0) { - return -1; + return ERROR; } + RETURN_IF_ERROR(cfg_builder_addop(g, opcode, oparg, loc)); } } - return 0; + return SUCCESS; } static PyObject * From webhook-mailer at python.org Mon Jan 30 20:27:57 2023 From: webhook-mailer at python.org (gvanrossum) Date: Tue, 31 Jan 2023 01:27:57 -0000 Subject: [Python-checkins] gh-98831: Clean up and add cache size static_assert to macro (#101442) Message-ID: https://github.com/python/cpython/commit/04ab767d28024b7a42b630a817227b02097aef30 commit: 04ab767d28024b7a42b630a817227b02097aef30 branch: main author: Guido van Rossum committer: gvanrossum date: 2023-01-30T17:27:51-08:00 summary: gh-98831: Clean up and add cache size static_assert to macro (#101442) files: M Tools/cases_generator/generate_cases.py M Tools/cases_generator/test_generator.py diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index f0c5f96733fe..43685450cc0d 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -597,7 +597,7 @@ def map_families(self) -> None: self.error( f"Instruction {member} is a member of multiple families " f"({member_instr.family.name}, {family.name}).", - family + family, ) else: member_instr.family = family @@ -609,7 +609,7 @@ def map_families(self) -> None: f"Component {part.instr.name} of macro {member} " f"is a member of multiple families " f"({part.instr.family.name}, {family.name}).", - family + family, ) else: part.instr.family = family @@ -629,7 +629,11 @@ def check_families(self) -> None: for family in self.families.values(): if len(family.members) < 2: self.error(f"Family {family.name!r} has insufficient members", family) - members = [member for member in family.members if member in self.instrs or member in self.macro_instrs] + members = [ + member + for member in family.members + if member in self.instrs or member in self.macro_instrs + ] if members != family.members: unknown = set(family.members) - set(members) self.error( @@ -859,7 +863,9 @@ def write_stack_effect_functions(self) -> None: popped_data.append((instr, popped)) pushed_data.append((instr, pushed)) - def write_function(direction: str, data: list[tuple[AnyInstruction, str]]) -> None: + def write_function( + direction: str, data: list[tuple[AnyInstruction, str]] + ) -> None: self.out.emit("\n#ifndef NDEBUG") self.out.emit("static int") self.out.emit(f"_PyOpcode_num_{direction}(int opcode, int oparg) {{") @@ -1031,6 +1037,7 @@ def write_super(self, sup: SuperInstruction) -> None: def write_macro(self, mac: MacroInstruction) -> None: """Write code for a macro instruction.""" + last_instr: Instruction | None = None with self.wrap_super_or_macro(mac): cache_adjust = 0 for part in mac.parts: @@ -1038,12 +1045,24 @@ def write_macro(self, mac: MacroInstruction) -> None: case parser.CacheEffect(size=size): cache_adjust += size case Component() as comp: + last_instr = comp.instr comp.write_body(self.out, cache_adjust) cache_adjust += comp.instr.cache_offset if cache_adjust: self.out.emit(f"JUMPBY({cache_adjust});") + if ( + last_instr + and (family := last_instr.family) + and mac.name == family.members[0] + and (cache_size := family.size) + ): + self.out.emit( + f"static_assert({cache_size} == " + f'{cache_adjust}, "incorrect cache size");' + ) + @contextlib.contextmanager def wrap_super_or_macro(self, up: SuperOrMacroInstruction): """Shared boilerplate for super- and macro instructions.""" diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index 49a99377fc04..9df97d24ab6f 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -383,6 +383,7 @@ def test_macro_instruction(): _tmp_3 = res; } JUMPBY(5); + static_assert(INLINE_CACHE_ENTRIES_OP == 5, "incorrect cache size"); STACK_SHRINK(2); POKE(1, _tmp_3); DISPATCH(); From webhook-mailer at python.org Tue Jan 31 00:36:46 2023 From: webhook-mailer at python.org (gvanrossum) Date: Tue, 31 Jan 2023 05:36:46 -0000 Subject: [Python-checkins] Fixes typo in asyncio.TaskGroup context manager code example (#101449) Message-ID: https://github.com/python/cpython/commit/ef09bf63d22b2efe5c0e9a2b9f25a9bec2ba1db0 commit: ef09bf63d22b2efe5c0e9a2b9f25a9bec2ba1db0 branch: main author: Ben <22038077+bcla22 at users.noreply.github.com> committer: gvanrossum date: 2023-01-30T21:36:40-08:00 summary: Fixes typo in asyncio.TaskGroup context manager code example (#101449) files: M Doc/library/asyncio-task.rst diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index 631a5ddc1f65..9a42b9631676 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -121,7 +121,7 @@ To actually run a coroutine, asyncio provides the following mechanisms: print(f"started at {time.strftime('%X')}") - # The wait is implicit when the context manager exits. + # The await is implicit when the context manager exits. print(f"finished at {time.strftime('%X')}") From webhook-mailer at python.org Tue Jan 31 00:39:37 2023 From: webhook-mailer at python.org (hauntsaninja) Date: Tue, 31 Jan 2023 05:39:37 -0000 Subject: [Python-checkins] gh-77607: Improve accuracy of os.path.join docs (#101406) Message-ID: https://github.com/python/cpython/commit/909a6746939ea1d09fab21f26b558cfd7e3e29a0 commit: 909a6746939ea1d09fab21f26b558cfd7e3e29a0 branch: main author: Shantanu <12621235+hauntsaninja at users.noreply.github.com> committer: hauntsaninja <12621235+hauntsaninja at users.noreply.github.com> date: 2023-01-30T21:39:30-08:00 summary: gh-77607: Improve accuracy of os.path.join docs (#101406) This is a follow-up to #100811. One of the changes in that PR isn't accurate in that `os.path.join('', '')` will not end in a separator. This reverts that change to the previous wording that used "only", but explicitly calls out the case where the last part ends in a separator, which is what caused confusin in #77607 and motivated the change in #100811. files: M Doc/library/os.path.rst diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index 786c2fd7f64f..96bcb48ad7d1 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -308,11 +308,11 @@ the :mod:`glob` module.) Join one or more path segments intelligently. The return value is the concatenation of *path* and all members of *\*paths*, with exactly one - directory separator following each non-empty part except the last. That is, - if the last part is empty, the result will end in a separator. If - a segment is an absolute path (which on Windows requires both a drive and a - root), then all previous segments are ignored and joining continues from the - absolute path segment. + directory separator following each non-empty part, except the last. That is, + the result will only end in a separator if the last part is either empty or + ends in a separator. If a segment is an absolute path (which on Windows + requires both a drive and a root), then all previous segments are ignored and + joining continues from the absolute path segment. On Windows, the drive is not reset when a rooted path segment (e.g., ``r'\foo'``) is encountered. If a segment is on a different drive or is an From webhook-mailer at python.org Tue Jan 31 00:46:41 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 31 Jan 2023 05:46:41 -0000 Subject: [Python-checkins] Fixes typo in asyncio.TaskGroup context manager code example (GH-101449) Message-ID: https://github.com/python/cpython/commit/6a94f4c9730a0a8d94cc497b19f08601668b1124 commit: 6a94f4c9730a0a8d94cc497b19f08601668b1124 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-30T21:46:35-08:00 summary: Fixes typo in asyncio.TaskGroup context manager code example (GH-101449) (cherry picked from commit ef09bf63d22b2efe5c0e9a2b9f25a9bec2ba1db0) Co-authored-by: Ben <22038077+bcla22 at users.noreply.github.com> files: M Doc/library/asyncio-task.rst diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index bf922fae40ab..24e43c3cbfcb 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -121,7 +121,7 @@ To actually run a coroutine, asyncio provides the following mechanisms: print(f"started at {time.strftime('%X')}") - # The wait is implicit when the context manager exits. + # The await is implicit when the context manager exits. print(f"finished at {time.strftime('%X')}") From webhook-mailer at python.org Tue Jan 31 00:46:49 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 31 Jan 2023 05:46:49 -0000 Subject: [Python-checkins] gh-77607: Improve accuracy of os.path.join docs (GH-101406) Message-ID: https://github.com/python/cpython/commit/f36c2729d62c90f7f667129b10f8161b1e4f5507 commit: f36c2729d62c90f7f667129b10f8161b1e4f5507 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-30T21:46:43-08:00 summary: gh-77607: Improve accuracy of os.path.join docs (GH-101406) This is a follow-up to GH-100811. One of the changes in that PR isn't accurate in that `os.path.join('', '')` will not end in a separator. This reverts that change to the previous wording that used "only", but explicitly calls out the case where the last part ends in a separator, which is what caused confusin in GH-77607 and motivated the change in GH-100811. (cherry picked from commit 909a6746939ea1d09fab21f26b558cfd7e3e29a0) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> files: M Doc/library/os.path.rst diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index 319b664dd46d..22d07c473838 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -299,11 +299,11 @@ the :mod:`glob` module.) Join one or more path segments intelligently. The return value is the concatenation of *path* and all members of *\*paths*, with exactly one - directory separator following each non-empty part except the last. That is, - if the last part is empty, the result will end in a separator. If - a segment is an absolute path (which on Windows requires both a drive and a - root), then all previous segments are ignored and joining continues from the - absolute path segment. + directory separator following each non-empty part, except the last. That is, + the result will only end in a separator if the last part is either empty or + ends in a separator. If a segment is an absolute path (which on Windows + requires both a drive and a root), then all previous segments are ignored and + joining continues from the absolute path segment. On Windows, the drive is not reset when a rooted path segment (e.g., ``r'\foo'``) is encountered. If a segment is on a different drive or is an From webhook-mailer at python.org Tue Jan 31 00:46:54 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 31 Jan 2023 05:46:54 -0000 Subject: [Python-checkins] gh-77607: Improve accuracy of os.path.join docs (GH-101406) Message-ID: https://github.com/python/cpython/commit/a79dd889738294fb0307d2216171ae5163aa7b41 commit: a79dd889738294fb0307d2216171ae5163aa7b41 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-30T21:46:48-08:00 summary: gh-77607: Improve accuracy of os.path.join docs (GH-101406) This is a follow-up to GH-100811. One of the changes in that PR isn't accurate in that `os.path.join('', '')` will not end in a separator. This reverts that change to the previous wording that used "only", but explicitly calls out the case where the last part ends in a separator, which is what caused confusin in GH-77607 and motivated the change in GH-100811. (cherry picked from commit 909a6746939ea1d09fab21f26b558cfd7e3e29a0) Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com> files: M Doc/library/os.path.rst diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index 319b664dd46d..22d07c473838 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -299,11 +299,11 @@ the :mod:`glob` module.) Join one or more path segments intelligently. The return value is the concatenation of *path* and all members of *\*paths*, with exactly one - directory separator following each non-empty part except the last. That is, - if the last part is empty, the result will end in a separator. If - a segment is an absolute path (which on Windows requires both a drive and a - root), then all previous segments are ignored and joining continues from the - absolute path segment. + directory separator following each non-empty part, except the last. That is, + the result will only end in a separator if the last part is either empty or + ends in a separator. If a segment is an absolute path (which on Windows + requires both a drive and a root), then all previous segments are ignored and + joining continues from the absolute path segment. On Windows, the drive is not reset when a rooted path segment (e.g., ``r'\foo'``) is encountered. If a segment is on a different drive or is an From webhook-mailer at python.org Tue Jan 31 05:23:37 2023 From: webhook-mailer at python.org (iritkatriel) Date: Tue, 31 Jan 2023 10:23:37 -0000 Subject: [Python-checkins] gh-98831: rewrite GET_LEN, GET_ITER, BEFORE_WITH and a few simple opcodes in the instruction definition DSL (#101443) Message-ID: https://github.com/python/cpython/commit/29a858b85f4c6479474b8d82c8995bde1166352b commit: 29a858b85f4c6479474b8d82c8995bde1166352b branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-31T10:23:15Z summary: gh-98831: rewrite GET_LEN, GET_ITER, BEFORE_WITH and a few simple opcodes in the instruction definition DSL (#101443) files: M Python/bytecodes.c M Python/generated_cases.c.h M Python/opcode_metadata.h diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d1e59f7908b5..5b3bf4ec7ba7 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2053,8 +2053,7 @@ dummy_func( } } - // stack effect: ( -- ) - inst(JUMP_BACKWARD_NO_INTERRUPT) { + inst(JUMP_BACKWARD_NO_INTERRUPT, (--)) { /* This bytecode is used in the `yield from` or `await` loop. * If there is an interrupt, we want it handled in the innermost * generator or coroutine, so we deliberately do not check it here. @@ -2063,18 +2062,12 @@ dummy_func( JUMPBY(-oparg); } - // stack effect: ( -- __0) - inst(GET_LEN) { + inst(GET_LEN, (obj -- obj, len_o)) { // PUSH(len(TOS)) - Py_ssize_t len_i = PyObject_Length(TOP()); - if (len_i < 0) { - goto error; - } - PyObject *len_o = PyLong_FromSsize_t(len_i); - if (len_o == NULL) { - goto error; - } - PUSH(len_o); + Py_ssize_t len_i = PyObject_Length(obj); + ERROR_IF(len_i < 0, error); + len_o = PyLong_FromSsize_t(len_i); + ERROR_IF(len_o == NULL, error); } inst(MATCH_CLASS, (subject, type, names -- attrs)) { @@ -2110,15 +2103,11 @@ dummy_func( ERROR_IF(values_or_none == NULL, error); } - // stack effect: ( -- ) - inst(GET_ITER) { + inst(GET_ITER, (iterable -- iter)) { /* before: [obj]; after [getiter(obj)] */ - PyObject *iterable = TOP(); - PyObject *iter = PyObject_GetIter(iterable); - Py_DECREF(iterable); - SET_TOP(iter); - if (iter == NULL) - goto error; + iter = PyObject_GetIter(iterable); + DECREF_INPUTS(); + ERROR_IF(iter == NULL, error); } // stack effect: ( -- ) @@ -2313,10 +2302,10 @@ dummy_func( PREDICT(GET_AWAITABLE); } - // stack effect: ( -- __0) - inst(BEFORE_WITH) { - PyObject *mgr = TOP(); - PyObject *res; + inst(BEFORE_WITH, (mgr -- exit, res)) { + /* pop the context manager, push its __exit__ and the + * value returned from calling its __enter__ + */ PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__enter__)); if (enter == NULL) { if (!_PyErr_Occurred(tstate)) { @@ -2327,7 +2316,7 @@ dummy_func( } goto error; } - PyObject *exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__)); + exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__)); if (exit == NULL) { if (!_PyErr_Occurred(tstate)) { _PyErr_Format(tstate, PyExc_TypeError, @@ -2339,14 +2328,13 @@ dummy_func( Py_DECREF(enter); goto error; } - SET_TOP(exit); - Py_DECREF(mgr); + DECREF_INPUTS(); res = _PyObject_CallNoArgs(enter); Py_DECREF(enter); if (res == NULL) { - goto error; + Py_DECREF(exit); + ERROR_IF(true, error); } - PUSH(res); } inst(WITH_EXCEPT_START, (exit_func, lasti, unused, val -- exit_func, lasti, unused, val, res)) { @@ -2469,8 +2457,7 @@ dummy_func( GO_TO_INSTRUCTION(CALL_PY_EXACT_ARGS); } - // stack effect: ( -- ) - inst(KW_NAMES) { + inst(KW_NAMES, (--)) { assert(kwnames == NULL); assert(oparg < PyTuple_GET_SIZE(consts)); kwnames = GETITEM(consts, oparg); @@ -3252,8 +3239,7 @@ dummy_func( PEEK(oparg) = top; } - // stack effect: ( -- ) - inst(EXTENDED_ARG) { + inst(EXTENDED_ARG, (--)) { assert(oparg); assert(cframe.use_tracing == 0); opcode = _Py_OPCODE(*next_instr); @@ -3262,8 +3248,7 @@ dummy_func( DISPATCH_GOTO(); } - // stack effect: ( -- ) - inst(CACHE) { + inst(CACHE, (--)) { Py_UNREACHABLE(); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 3ee30ae8df9e..661fe27f327b 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2460,16 +2460,15 @@ } TARGET(GET_LEN) { + PyObject *obj = PEEK(1); + PyObject *len_o; // PUSH(len(TOS)) - Py_ssize_t len_i = PyObject_Length(TOP()); - if (len_i < 0) { - goto error; - } - PyObject *len_o = PyLong_FromSsize_t(len_i); - if (len_o == NULL) { - goto error; - } - PUSH(len_o); + Py_ssize_t len_i = PyObject_Length(obj); + if (len_i < 0) goto error; + len_o = PyLong_FromSsize_t(len_i); + if (len_o == NULL) goto error; + STACK_GROW(1); + POKE(1, len_o); DISPATCH(); } @@ -2532,13 +2531,13 @@ } TARGET(GET_ITER) { + PyObject *iterable = PEEK(1); + PyObject *iter; /* before: [obj]; after [getiter(obj)] */ - PyObject *iterable = TOP(); - PyObject *iter = PyObject_GetIter(iterable); + iter = PyObject_GetIter(iterable); Py_DECREF(iterable); - SET_TOP(iter); - if (iter == NULL) - goto error; + if (iter == NULL) goto pop_1_error; + POKE(1, iter); DISPATCH(); } @@ -2736,8 +2735,12 @@ } TARGET(BEFORE_WITH) { - PyObject *mgr = TOP(); + PyObject *mgr = PEEK(1); + PyObject *exit; PyObject *res; + /* pop the context manager, push its __exit__ and the + * value returned from calling its __enter__ + */ PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__enter__)); if (enter == NULL) { if (!_PyErr_Occurred(tstate)) { @@ -2748,7 +2751,7 @@ } goto error; } - PyObject *exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__)); + exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__)); if (exit == NULL) { if (!_PyErr_Occurred(tstate)) { _PyErr_Format(tstate, PyExc_TypeError, @@ -2760,14 +2763,16 @@ Py_DECREF(enter); goto error; } - SET_TOP(exit); Py_DECREF(mgr); res = _PyObject_CallNoArgs(enter); Py_DECREF(enter); if (res == NULL) { - goto error; + Py_DECREF(exit); + if (true) goto pop_1_error; } - PUSH(res); + STACK_GROW(1); + POKE(1, res); + POKE(2, exit); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index e76ddda2f029..c40e40ff324d 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -245,9 +245,9 @@ _PyOpcode_num_popped(int opcode, int oparg) { case JUMP_IF_TRUE_OR_POP: return -1; case JUMP_BACKWARD_NO_INTERRUPT: - return -1; + return 0; case GET_LEN: - return -1; + return 1; case MATCH_CLASS: return 3; case MATCH_MAPPING: @@ -257,7 +257,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { case MATCH_KEYS: return 2; case GET_ITER: - return -1; + return 1; case GET_YIELD_FROM_ITER: return -1; case FOR_ITER: @@ -273,7 +273,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { case BEFORE_ASYNC_WITH: return -1; case BEFORE_WITH: - return -1; + return 1; case WITH_EXCEPT_START: return 4; case PUSH_EXC_INFO: @@ -287,7 +287,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { case CALL_BOUND_METHOD_EXACT_ARGS: return -1; case KW_NAMES: - return -1; + return 0; case CALL: return -1; case CALL_PY_EXACT_ARGS: @@ -339,9 +339,9 @@ _PyOpcode_num_popped(int opcode, int oparg) { case SWAP: return -1; case EXTENDED_ARG: - return -1; + return 0; case CACHE: - return -1; + return 0; default: Py_UNREACHABLE(); } @@ -591,9 +591,9 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case JUMP_IF_TRUE_OR_POP: return -1; case JUMP_BACKWARD_NO_INTERRUPT: - return -1; + return 0; case GET_LEN: - return -1; + return 2; case MATCH_CLASS: return 1; case MATCH_MAPPING: @@ -603,7 +603,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case MATCH_KEYS: return 3; case GET_ITER: - return -1; + return 1; case GET_YIELD_FROM_ITER: return -1; case FOR_ITER: @@ -619,7 +619,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case BEFORE_ASYNC_WITH: return -1; case BEFORE_WITH: - return -1; + return 2; case WITH_EXCEPT_START: return 5; case PUSH_EXC_INFO: @@ -633,7 +633,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case CALL_BOUND_METHOD_EXACT_ARGS: return -1; case KW_NAMES: - return -1; + return 0; case CALL: return -1; case CALL_PY_EXACT_ARGS: @@ -685,9 +685,9 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case SWAP: return -1; case EXTENDED_ARG: - return -1; + return 0; case CACHE: - return -1; + return 0; default: Py_UNREACHABLE(); } From webhook-mailer at python.org Tue Jan 31 08:29:47 2023 From: webhook-mailer at python.org (hugovk) Date: Tue, 31 Jan 2023 13:29:47 -0000 Subject: [Python-checkins] Add JOBS parameter to docs Makefile (#101395) Message-ID: https://github.com/python/cpython/commit/1a62ae84c687791bc1dfb54d1eb75e1c7277bb04 commit: 1a62ae84c687791bc1dfb54d1eb75e1c7277bb04 branch: main author: Christophe Nanteuil <35002064+christopheNan at users.noreply.github.com> committer: hugovk date: 2023-01-31T15:29:29+02:00 summary: Add JOBS parameter to docs Makefile (#101395) files: M Doc/Makefile diff --git a/Doc/Makefile b/Doc/Makefile index 3d484ac3ae79..ebe7f3698000 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -9,6 +9,7 @@ VENVDIR = ./venv SPHINXBUILD = PATH=$(VENVDIR)/bin:$$PATH sphinx-build SPHINXLINT = PATH=$(VENVDIR)/bin:$$PATH sphinx-lint BLURB = PATH=$(VENVDIR)/bin:$$PATH blurb +JOBS = auto PAPER = SOURCES = DISTVERSION = $(shell $(PYTHON) tools/extensions/patchlevel.py) @@ -18,7 +19,7 @@ SPHINXERRORHANDLING = -W PAPEROPT_a4 = -D latex_elements.papersize=a4paper PAPEROPT_letter = -D latex_elements.papersize=letterpaper -ALLSPHINXOPTS = -b $(BUILDER) -d build/doctrees $(PAPEROPT_$(PAPER)) -j auto \ +ALLSPHINXOPTS = -b $(BUILDER) -d build/doctrees $(PAPEROPT_$(PAPER)) -j $(JOBS) \ $(SPHINXOPTS) $(SPHINXERRORHANDLING) . build/$(BUILDER) $(SOURCES) .PHONY: help From webhook-mailer at python.org Tue Jan 31 08:37:01 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 31 Jan 2023 13:37:01 -0000 Subject: [Python-checkins] Add JOBS parameter to docs Makefile (GH-101395) Message-ID: https://github.com/python/cpython/commit/71db9c9ea50ccba47a3c1e31334747049a68487b commit: 71db9c9ea50ccba47a3c1e31334747049a68487b branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-31T05:36:54-08:00 summary: Add JOBS parameter to docs Makefile (GH-101395) (cherry picked from commit 1a62ae84c687791bc1dfb54d1eb75e1c7277bb04) Co-authored-by: Christophe Nanteuil <35002064+christopheNan at users.noreply.github.com> files: M Doc/Makefile diff --git a/Doc/Makefile b/Doc/Makefile index 939498e548a0..4188f88d046d 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -8,6 +8,7 @@ PYTHON = python3 VENVDIR = ./venv SPHINXBUILD = PATH=$(VENVDIR)/bin:$$PATH sphinx-build BLURB = PATH=$(VENVDIR)/bin:$$PATH blurb +JOBS = auto PAPER = SOURCES = DISTVERSION = $(shell $(PYTHON) tools/extensions/patchlevel.py) @@ -17,7 +18,7 @@ SPHINXERRORHANDLING = -W PAPEROPT_a4 = -D latex_elements.papersize=a4paper PAPEROPT_letter = -D latex_elements.papersize=letterpaper -ALLSPHINXOPTS = -b $(BUILDER) -d build/doctrees $(PAPEROPT_$(PAPER)) -j auto \ +ALLSPHINXOPTS = -b $(BUILDER) -d build/doctrees $(PAPEROPT_$(PAPER)) -j $(JOBS) \ $(SPHINXOPTS) $(SPHINXERRORHANDLING) . build/$(BUILDER) $(SOURCES) .PHONY: help build html htmlhelp latex text texinfo changes linkcheck \ From webhook-mailer at python.org Tue Jan 31 08:39:12 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 31 Jan 2023 13:39:12 -0000 Subject: [Python-checkins] Add JOBS parameter to docs Makefile (GH-101395) Message-ID: https://github.com/python/cpython/commit/43af2dbb54785e34f67813eee08156647f340b64 commit: 43af2dbb54785e34f67813eee08156647f340b64 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-31T05:39:06-08:00 summary: Add JOBS parameter to docs Makefile (GH-101395) (cherry picked from commit 1a62ae84c687791bc1dfb54d1eb75e1c7277bb04) Co-authored-by: Christophe Nanteuil <35002064+christopheNan at users.noreply.github.com> files: M Doc/Makefile diff --git a/Doc/Makefile b/Doc/Makefile index 5b6a95813abe..1814d4e86eb0 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -9,6 +9,7 @@ VENVDIR = ./venv SPHINXBUILD = PATH=$(VENVDIR)/bin:$$PATH sphinx-build SPHINXLINT = PATH=$(VENVDIR)/bin:$$PATH sphinx-lint BLURB = PATH=$(VENVDIR)/bin:$$PATH blurb +JOBS = auto PAPER = SOURCES = DISTVERSION = $(shell $(PYTHON) tools/extensions/patchlevel.py) @@ -18,7 +19,7 @@ SPHINXERRORHANDLING = -W PAPEROPT_a4 = -D latex_elements.papersize=a4paper PAPEROPT_letter = -D latex_elements.papersize=letterpaper -ALLSPHINXOPTS = -b $(BUILDER) -d build/doctrees $(PAPEROPT_$(PAPER)) -j auto \ +ALLSPHINXOPTS = -b $(BUILDER) -d build/doctrees $(PAPEROPT_$(PAPER)) -j $(JOBS) \ $(SPHINXOPTS) $(SPHINXERRORHANDLING) . build/$(BUILDER) $(SOURCES) .PHONY: help build html htmlhelp latex text texinfo changes linkcheck \ From webhook-mailer at python.org Tue Jan 31 09:42:55 2023 From: webhook-mailer at python.org (corona10) Date: Tue, 31 Jan 2023 14:42:55 -0000 Subject: [Python-checkins] =?utf-8?q?=5B3=2E10=5D_gh-101400=3A_Fix_incorr?= =?utf-8?q?ect_lineno_in_exception_message_on_contin=E2=80=A6_=28gh-101448?= =?utf-8?q?=29?= Message-ID: https://github.com/python/cpython/commit/740050af0493030b1f6ebf0b9ac39a356e2e74b6 commit: 740050af0493030b1f6ebf0b9ac39a356e2e74b6 branch: 3.10 author: Dong-hee Na committer: corona10 date: 2023-01-31T23:42:22+09:00 summary: [3.10] gh-101400: Fix incorrect lineno in exception message on contin? (gh-101448) files: A Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst M Lib/test/test_syntax.py M Python/compile.c diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index cd09d1eabdf5..5ab3ec6c7762 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -1410,9 +1410,6 @@ def error2(): """ self._check_error(source, "parameter and nonlocal", lineno=3) - def test_break_outside_loop(self): - self._check_error("break", "outside loop") - def test_yield_outside_function(self): self._check_error("if 0: yield", "outside function") self._check_error("if 0: yield\nelse: x=1", "outside function") @@ -1441,20 +1438,27 @@ def test_return_outside_function(self): "outside function") def test_break_outside_loop(self): - self._check_error("if 0: break", "outside loop") - self._check_error("if 0: break\nelse: x=1", "outside loop") - self._check_error("if 1: pass\nelse: break", "outside loop") - self._check_error("class C:\n if 0: break", "outside loop") + msg = "outside loop" + self._check_error("break", msg, lineno=1) + self._check_error("if 0: break", msg, lineno=1) + self._check_error("if 0: break\nelse: x=1", msg, lineno=1) + self._check_error("if 1: pass\nelse: break", msg, lineno=2) + self._check_error("class C:\n if 0: break", msg, lineno=2) self._check_error("class C:\n if 1: pass\n else: break", - "outside loop") + msg, lineno=3) + self._check_error("with object() as obj:\n break", + msg, lineno=2) def test_continue_outside_loop(self): - self._check_error("if 0: continue", "not properly in loop") - self._check_error("if 0: continue\nelse: x=1", "not properly in loop") - self._check_error("if 1: pass\nelse: continue", "not properly in loop") - self._check_error("class C:\n if 0: continue", "not properly in loop") + msg = "not properly in loop" + self._check_error("if 0: continue", msg, lineno=1) + self._check_error("if 0: continue\nelse: x=1", msg, lineno=1) + self._check_error("if 1: pass\nelse: continue", msg, lineno=2) + self._check_error("class C:\n if 0: continue", msg, lineno=2) self._check_error("class C:\n if 1: pass\n else: continue", - "not properly in loop") + msg, lineno=3) + self._check_error("with object() as obj:\n continue", + msg, lineno=2) def test_unexpected_indent(self): self._check_error("foo()\n bar()\n", "unexpected indent", diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst new file mode 100644 index 000000000000..f3dd783c01e7 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst @@ -0,0 +1,2 @@ +Fix wrong lineno in exception message on :keyword:`continue` or +:keyword:`break` which are not in a loop. Patch by Dong-hee Na. diff --git a/Python/compile.c b/Python/compile.c index e5438f7d007e..80caa8fab1f7 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -3030,12 +3030,14 @@ static int compiler_break(struct compiler *c) { struct fblockinfo *loop = NULL; + int origin_loc = c->u->u_lineno; /* Emit instruction with line number */ ADDOP(c, NOP); if (!compiler_unwind_fblock_stack(c, 0, &loop)) { return 0; } if (loop == NULL) { + c->u->u_lineno = origin_loc; return compiler_error(c, "'break' outside loop"); } if (!compiler_unwind_fblock(c, loop, 0)) { @@ -3050,12 +3052,14 @@ static int compiler_continue(struct compiler *c) { struct fblockinfo *loop = NULL; + int origin_loc = c->u->u_lineno; /* Emit instruction with line number */ ADDOP(c, NOP); if (!compiler_unwind_fblock_stack(c, 0, &loop)) { return 0; } if (loop == NULL) { + c->u->u_lineno = origin_loc; return compiler_error(c, "'continue' not properly in loop"); } ADDOP_JUMP(c, JUMP_ABSOLUTE, loop->fb_block); From webhook-mailer at python.org Tue Jan 31 09:53:29 2023 From: webhook-mailer at python.org (corona10) Date: Tue, 31 Jan 2023 14:53:29 -0000 Subject: [Python-checkins] =?utf-8?q?=5B3=2E11=5D_gh-101400=3A_Fix_incorr?= =?utf-8?q?ect_lineno_in_exception_message_on_contin=E2=80=A6_=28gh-101447?= =?utf-8?q?=29?= Message-ID: https://github.com/python/cpython/commit/0c37ea9abad2eae146ce117eca0503aaedc96c0f commit: 0c37ea9abad2eae146ce117eca0503aaedc96c0f branch: 3.11 author: Dong-hee Na committer: corona10 date: 2023-01-31T23:53:14+09:00 summary: [3.11] gh-101400: Fix incorrect lineno in exception message on contin? (gh-101447) files: A Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst M Lib/test/test_syntax.py M Python/compile.c diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 42d36e0b9d81..50168d9200a2 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -1907,9 +1907,6 @@ def error2(): """ self._check_error(source, "parameter and nonlocal", lineno=3) - def test_break_outside_loop(self): - self._check_error("break", "outside loop") - def test_yield_outside_function(self): self._check_error("if 0: yield", "outside function") self._check_error("if 0: yield\nelse: x=1", "outside function") @@ -1938,20 +1935,27 @@ def test_return_outside_function(self): "outside function") def test_break_outside_loop(self): - self._check_error("if 0: break", "outside loop") - self._check_error("if 0: break\nelse: x=1", "outside loop") - self._check_error("if 1: pass\nelse: break", "outside loop") - self._check_error("class C:\n if 0: break", "outside loop") + msg = "outside loop" + self._check_error("break", msg, lineno=1) + self._check_error("if 0: break", msg, lineno=1) + self._check_error("if 0: break\nelse: x=1", msg, lineno=1) + self._check_error("if 1: pass\nelse: break", msg, lineno=2) + self._check_error("class C:\n if 0: break", msg, lineno=2) self._check_error("class C:\n if 1: pass\n else: break", - "outside loop") + msg, lineno=3) + self._check_error("with object() as obj:\n break", + msg, lineno=2) def test_continue_outside_loop(self): - self._check_error("if 0: continue", "not properly in loop") - self._check_error("if 0: continue\nelse: x=1", "not properly in loop") - self._check_error("if 1: pass\nelse: continue", "not properly in loop") - self._check_error("class C:\n if 0: continue", "not properly in loop") + msg = "not properly in loop" + self._check_error("if 0: continue", msg, lineno=1) + self._check_error("if 0: continue\nelse: x=1", msg, lineno=1) + self._check_error("if 1: pass\nelse: continue", msg, lineno=2) + self._check_error("class C:\n if 0: continue", msg, lineno=2) self._check_error("class C:\n if 1: pass\n else: continue", - "not properly in loop") + msg, lineno=3) + self._check_error("with object() as obj:\n continue", + msg, lineno=2) def test_unexpected_indent(self): self._check_error("foo()\n bar()\n", "unexpected indent", diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst new file mode 100644 index 000000000000..f3dd783c01e7 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-30-08-59-47.gh-issue-101400.Di_ZFm.rst @@ -0,0 +1,2 @@ +Fix wrong lineno in exception message on :keyword:`continue` or +:keyword:`break` which are not in a loop. Patch by Dong-hee Na. diff --git a/Python/compile.c b/Python/compile.c index f4555b35ab95..17d1df2c51e8 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -3259,12 +3259,20 @@ static int compiler_break(struct compiler *c) { struct fblockinfo *loop = NULL; + int u_lineno = c->u->u_lineno; + int u_col_offset = c->u->u_col_offset; + int u_end_lineno = c->u->u_end_lineno; + int u_end_col_offset = c->u->u_end_col_offset; /* Emit instruction with line number */ ADDOP(c, NOP); if (!compiler_unwind_fblock_stack(c, 0, &loop)) { return 0; } if (loop == NULL) { + c->u->u_lineno = u_lineno; + c->u->u_col_offset = u_col_offset; + c->u->u_end_lineno = u_end_lineno; + c->u->u_end_col_offset = u_end_col_offset; return compiler_error(c, "'break' outside loop"); } if (!compiler_unwind_fblock(c, loop, 0)) { @@ -3278,12 +3286,20 @@ static int compiler_continue(struct compiler *c) { struct fblockinfo *loop = NULL; + int u_lineno = c->u->u_lineno; + int u_col_offset = c->u->u_col_offset; + int u_end_lineno = c->u->u_end_lineno; + int u_end_col_offset = c->u->u_end_col_offset; /* Emit instruction with line number */ ADDOP(c, NOP); if (!compiler_unwind_fblock_stack(c, 0, &loop)) { return 0; } if (loop == NULL) { + c->u->u_lineno = u_lineno; + c->u->u_col_offset = u_col_offset; + c->u->u_end_lineno = u_end_lineno; + c->u->u_end_col_offset = u_end_col_offset; return compiler_error(c, "'continue' not properly in loop"); } ADDOP_JUMP(c, JUMP, loop->fb_block); From webhook-mailer at python.org Tue Jan 31 10:16:53 2023 From: webhook-mailer at python.org (hugovk) Date: Tue, 31 Jan 2023 15:16:53 -0000 Subject: [Python-checkins] gh-99276 - Updated Doc/faq/general.rst (#101396) Message-ID: https://github.com/python/cpython/commit/df0068ce4827471cc2962631ee64f6f38e818ec4 commit: df0068ce4827471cc2962631ee64f6f38e818ec4 branch: main author: Raj <51259329+workingpayload at users.noreply.github.com> committer: hugovk date: 2023-01-31T17:16:17+02:00 summary: gh-99276 - Updated Doc/faq/general.rst (#101396) Co-authored-by: Hugo van Kemenade files: M Doc/faq/general.rst diff --git a/Doc/faq/general.rst b/Doc/faq/general.rst index 489bca76432d..6256deb5797c 100644 --- a/Doc/faq/general.rst +++ b/Doc/faq/general.rst @@ -248,8 +248,8 @@ Are there any published articles about Python that I can reference? It's probably best to cite your favorite book about Python. -The very first article about Python was written in 1991 and is now quite -outdated. +The `very first article `_ about Python was +written in 1991 and is now quite outdated. Guido van Rossum and Jelke de Boer, "Interactively Testing Remote Servers Using the Python Programming Language", CWI Quarterly, Volume 4, Issue 4 From webhook-mailer at python.org Tue Jan 31 10:28:09 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 31 Jan 2023 15:28:09 -0000 Subject: [Python-checkins] gh-99276 - Updated Doc/faq/general.rst (GH-101396) Message-ID: https://github.com/python/cpython/commit/9e4ba5c4bf84b2a77b5d2334105a7879c219456f commit: 9e4ba5c4bf84b2a77b5d2334105a7879c219456f branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-31T07:27:55-08:00 summary: gh-99276 - Updated Doc/faq/general.rst (GH-101396) (cherry picked from commit df0068ce4827471cc2962631ee64f6f38e818ec4) Co-authored-by: Raj <51259329+workingpayload at users.noreply.github.com> Co-authored-by: Hugo van Kemenade files: M Doc/faq/general.rst diff --git a/Doc/faq/general.rst b/Doc/faq/general.rst index 489bca76432d..6256deb5797c 100644 --- a/Doc/faq/general.rst +++ b/Doc/faq/general.rst @@ -248,8 +248,8 @@ Are there any published articles about Python that I can reference? It's probably best to cite your favorite book about Python. -The very first article about Python was written in 1991 and is now quite -outdated. +The `very first article `_ about Python was +written in 1991 and is now quite outdated. Guido van Rossum and Jelke de Boer, "Interactively Testing Remote Servers Using the Python Programming Language", CWI Quarterly, Volume 4, Issue 4 From webhook-mailer at python.org Tue Jan 31 10:28:13 2023 From: webhook-mailer at python.org (hugovk) Date: Tue, 31 Jan 2023 15:28:13 -0000 Subject: [Python-checkins] [3.10] gh-99276 - Updated Doc/faq/general.rst (GH-101396) (#101462) Message-ID: https://github.com/python/cpython/commit/ca1184898a158b3d2cb3363c425cdc16629b38f8 commit: ca1184898a158b3d2cb3363c425cdc16629b38f8 branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: hugovk date: 2023-01-31T17:28:07+02:00 summary: [3.10] gh-99276 - Updated Doc/faq/general.rst (GH-101396) (#101462) Co-authored-by: Raj <51259329+workingpayload at users.noreply.github.com> Co-authored-by: Hugo van Kemenade files: M Doc/faq/general.rst diff --git a/Doc/faq/general.rst b/Doc/faq/general.rst index 16f726c593cd..9b3ff3240381 100644 --- a/Doc/faq/general.rst +++ b/Doc/faq/general.rst @@ -254,8 +254,8 @@ Are there any published articles about Python that I can reference? It's probably best to cite your favorite book about Python. -The very first article about Python was written in 1991 and is now quite -outdated. +The `very first article `_ about Python was +written in 1991 and is now quite outdated. Guido van Rossum and Jelke de Boer, "Interactively Testing Remote Servers Using the Python Programming Language", CWI Quarterly, Volume 4, Issue 4 From webhook-mailer at python.org Tue Jan 31 10:30:44 2023 From: webhook-mailer at python.org (hugovk) Date: Tue, 31 Jan 2023 15:30:44 -0000 Subject: [Python-checkins] gh-101440: fix json snippet error in logging-cookbook.rst (#101439) Message-ID: https://github.com/python/cpython/commit/20c11f2e600e1c0bf42de4d6f2fb3ce5ccc2587c commit: 20c11f2e600e1c0bf42de4d6f2fb3ce5ccc2587c branch: main author: Peter Jiping Xie committer: hugovk date: 2023-01-31T17:30:38+02:00 summary: gh-101440: fix json snippet error in logging-cookbook.rst (#101439) files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 26cf40274fd3..7661249ad522 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -307,7 +307,7 @@ Suppose you configure logging with the following JSON: "class": "logging.StreamHandler", "level": "INFO", "formatter": "simple", - "stream": "ext://sys.stdout", + "stream": "ext://sys.stdout" }, "stderr": { "class": "logging.StreamHandler", From webhook-mailer at python.org Tue Jan 31 10:38:35 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 31 Jan 2023 15:38:35 -0000 Subject: [Python-checkins] gh-101440: fix json snippet error in logging-cookbook.rst (GH-101439) Message-ID: https://github.com/python/cpython/commit/6b2ed1c29954292e6d2cd3d8fd24bf11bd4cc6cc commit: 6b2ed1c29954292e6d2cd3d8fd24bf11bd4cc6cc branch: 3.10 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-31T07:38:28-08:00 summary: gh-101440: fix json snippet error in logging-cookbook.rst (GH-101439) (cherry picked from commit 20c11f2e600e1c0bf42de4d6f2fb3ce5ccc2587c) Co-authored-by: Peter Jiping Xie files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 66945bdf63eb..8b1e7e48bff7 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -307,7 +307,7 @@ Suppose you configure logging with the following JSON: "class": "logging.StreamHandler", "level": "INFO", "formatter": "simple", - "stream": "ext://sys.stdout", + "stream": "ext://sys.stdout" }, "stderr": { "class": "logging.StreamHandler", From webhook-mailer at python.org Tue Jan 31 10:52:04 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 31 Jan 2023 15:52:04 -0000 Subject: [Python-checkins] [3.11] gh-101440: fix json snippet error in logging-cookbook.rst (GH-101439) (GH-101463) Message-ID: https://github.com/python/cpython/commit/5e498072dcb7fe4980ffee45631fd2f65665ec49 commit: 5e498072dcb7fe4980ffee45631fd2f65665ec49 branch: 3.11 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-31T07:51:48-08:00 summary: [3.11] gh-101440: fix json snippet error in logging-cookbook.rst (GH-101439) (GH-101463) (cherry picked from commit 20c11f2e600e1c0bf42de4d6f2fb3ce5ccc2587c) Co-authored-by: Peter Jiping Xie Automerge-Triggered-By: GH:hugovk files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 2ba5b5c13681..97e990d0dd07 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -307,7 +307,7 @@ Suppose you configure logging with the following JSON: "class": "logging.StreamHandler", "level": "INFO", "formatter": "simple", - "stream": "ext://sys.stdout", + "stream": "ext://sys.stdout" }, "stderr": { "class": "logging.StreamHandler", From webhook-mailer at python.org Tue Jan 31 13:19:40 2023 From: webhook-mailer at python.org (miss-islington) Date: Tue, 31 Jan 2023 18:19:40 -0000 Subject: [Python-checkins] gh-101469: Optimise get_io_state() by using _PyModule_GetState() (GH-101470) Message-ID: https://github.com/python/cpython/commit/f80db6cef075186f888a85258ccf2164bf148921 commit: f80db6cef075186f888a85258ccf2164bf148921 branch: main author: Erlend E. Aasland committer: miss-islington <31488909+miss-islington at users.noreply.github.com> date: 2023-01-31T10:19:11-08:00 summary: gh-101469: Optimise get_io_state() by using _PyModule_GetState() (GH-101470) Automerge-Triggered-By: GH:erlend-aasland files: M Modules/_io/_iomodule.c diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index af5950cf66c1..175fa97479d2 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -10,6 +10,7 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" #include "_iomodule.h" +#include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_pystate.h" // _PyInterpreterState_GET() #ifdef HAVE_SYS_TYPES_H @@ -560,7 +561,7 @@ PyNumber_AsOff_t(PyObject *item, PyObject *err) static inline _PyIO_State* get_io_state(PyObject *module) { - void *state = PyModule_GetState(module); + void *state = _PyModule_GetState(module); assert(state != NULL); return (_PyIO_State *)state; } From webhook-mailer at python.org Tue Jan 31 13:47:57 2023 From: webhook-mailer at python.org (iritkatriel) Date: Tue, 31 Jan 2023 18:47:57 -0000 Subject: [Python-checkins] gh-98831: rewrite BEFORE_ASYNC_WITH and END_ASYNC_FOR in the instruction definition DSL (#101458) Message-ID: https://github.com/python/cpython/commit/0062f538d937de55cf3b66b4a8d527b1fe9d5182 commit: 0062f538d937de55cf3b66b4a8d527b1fe9d5182 branch: main author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com> committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com> date: 2023-01-31T18:47:50Z summary: gh-98831: rewrite BEFORE_ASYNC_WITH and END_ASYNC_FOR in the instruction definition DSL (#101458) files: M Python/bytecodes.c M Python/generated_cases.c.h M Python/opcode_metadata.h diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 5b3bf4ec7ba7..336088e08197 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -770,18 +770,16 @@ dummy_func( ERROR_IF(val == NULL, error); } - // stack effect: (__0, __1 -- ) - inst(END_ASYNC_FOR) { - PyObject *val = POP(); - assert(val && PyExceptionInstance_Check(val)); - if (PyErr_GivenExceptionMatches(val, PyExc_StopAsyncIteration)) { - Py_DECREF(val); - Py_DECREF(POP()); + inst(END_ASYNC_FOR, (awaitable, exc -- )) { + assert(exc && PyExceptionInstance_Check(exc)); + if (PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration)) { + DECREF_INPUTS(); } else { - PyObject *exc = Py_NewRef(PyExceptionInstance_Class(val)); - PyObject *tb = PyException_GetTraceback(val); - _PyErr_Restore(tstate, exc, val, tb); + Py_INCREF(exc); + PyObject *typ = Py_NewRef(PyExceptionInstance_Class(exc)); + PyObject *tb = PyException_GetTraceback(exc); + _PyErr_Restore(tstate, typ, exc, tb); goto exception_unwind; } } @@ -2266,10 +2264,7 @@ dummy_func( DISPATCH_INLINED(gen_frame); } - // stack effect: ( -- __0) - inst(BEFORE_ASYNC_WITH) { - PyObject *mgr = TOP(); - PyObject *res; + inst(BEFORE_ASYNC_WITH, (mgr -- exit, res)) { PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__aenter__)); if (enter == NULL) { if (!_PyErr_Occurred(tstate)) { @@ -2280,7 +2275,7 @@ dummy_func( } goto error; } - PyObject *exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__)); + exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__)); if (exit == NULL) { if (!_PyErr_Occurred(tstate)) { _PyErr_Format(tstate, PyExc_TypeError, @@ -2292,13 +2287,13 @@ dummy_func( Py_DECREF(enter); goto error; } - SET_TOP(exit); - Py_DECREF(mgr); + DECREF_INPUTS(); res = _PyObject_CallNoArgs(enter); Py_DECREF(enter); - if (res == NULL) - goto error; - PUSH(res); + if (res == NULL) { + Py_DECREF(exit); + ERROR_IF(true, error); + } PREDICT(GET_AWAITABLE); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 661fe27f327b..d70d64ecbdd5 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -981,18 +981,21 @@ } TARGET(END_ASYNC_FOR) { - PyObject *val = POP(); - assert(val && PyExceptionInstance_Check(val)); - if (PyErr_GivenExceptionMatches(val, PyExc_StopAsyncIteration)) { - Py_DECREF(val); - Py_DECREF(POP()); + PyObject *exc = PEEK(1); + PyObject *awaitable = PEEK(2); + assert(exc && PyExceptionInstance_Check(exc)); + if (PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration)) { + Py_DECREF(awaitable); + Py_DECREF(exc); } else { - PyObject *exc = Py_NewRef(PyExceptionInstance_Class(val)); - PyObject *tb = PyException_GetTraceback(val); - _PyErr_Restore(tstate, exc, val, tb); + Py_INCREF(exc); + PyObject *typ = Py_NewRef(PyExceptionInstance_Class(exc)); + PyObject *tb = PyException_GetTraceback(exc); + _PyErr_Restore(tstate, typ, exc, tb); goto exception_unwind; } + STACK_SHRINK(2); DISPATCH(); } @@ -2699,7 +2702,8 @@ } TARGET(BEFORE_ASYNC_WITH) { - PyObject *mgr = TOP(); + PyObject *mgr = PEEK(1); + PyObject *exit; PyObject *res; PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__aenter__)); if (enter == NULL) { @@ -2711,7 +2715,7 @@ } goto error; } - PyObject *exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__)); + exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__)); if (exit == NULL) { if (!_PyErr_Occurred(tstate)) { _PyErr_Format(tstate, PyExc_TypeError, @@ -2723,13 +2727,16 @@ Py_DECREF(enter); goto error; } - SET_TOP(exit); Py_DECREF(mgr); res = _PyObject_CallNoArgs(enter); Py_DECREF(enter); - if (res == NULL) - goto error; - PUSH(res); + if (res == NULL) { + Py_DECREF(exit); + if (true) goto pop_1_error; + } + STACK_GROW(1); + POKE(1, res); + POKE(2, exit); PREDICT(GET_AWAITABLE); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index c40e40ff324d..171fed363d3b 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -109,7 +109,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { case PREP_RERAISE_STAR: return 2; case END_ASYNC_FOR: - return -1; + return 2; case CLEANUP_THROW: return -1; case LOAD_ASSERTION_ERROR: @@ -271,7 +271,7 @@ _PyOpcode_num_popped(int opcode, int oparg) { case FOR_ITER_GEN: return -1; case BEFORE_ASYNC_WITH: - return -1; + return 1; case BEFORE_WITH: return 1; case WITH_EXCEPT_START: @@ -455,7 +455,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case PREP_RERAISE_STAR: return 1; case END_ASYNC_FOR: - return -1; + return 0; case CLEANUP_THROW: return -1; case LOAD_ASSERTION_ERROR: @@ -617,7 +617,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) { case FOR_ITER_GEN: return -1; case BEFORE_ASYNC_WITH: - return -1; + return 2; case BEFORE_WITH: return 2; case WITH_EXCEPT_START: From webhook-mailer at python.org Tue Jan 31 15:42:16 2023 From: webhook-mailer at python.org (erlend-aasland) Date: Tue, 31 Jan 2023 20:42:16 -0000 Subject: [Python-checkins] gh-101409: Improve generated clinic code for self type checks (#101411) Message-ID: https://github.com/python/cpython/commit/2753cf2ed6eb329bdc34b8f67228801182b82160 commit: 2753cf2ed6eb329bdc34b8f67228801182b82160 branch: main author: Erlend E. Aasland committer: erlend-aasland date: 2023-01-31T21:42:03+01:00 summary: gh-101409: Improve generated clinic code for self type checks (#101411) files: M Modules/_io/clinic/bufferedio.c.h M Modules/_sqlite/clinic/cursor.c.h M Modules/_sqlite/clinic/row.c.h M Modules/clinic/_collectionsmodule.c.h M Modules/clinic/_queuemodule.c.h M Modules/clinic/_ssl.c.h M Modules/clinic/itertoolsmodule.c.h M Modules/clinic/selectmodule.c.h M Objects/clinic/classobject.c.h M Objects/clinic/codeobject.c.h M Objects/clinic/enumobject.c.h M Objects/clinic/floatobject.c.h M Objects/clinic/listobject.c.h M Objects/clinic/tupleobject.c.h M Tools/clinic/clinic.py diff --git a/Modules/_io/clinic/bufferedio.c.h b/Modules/_io/clinic/bufferedio.c.h index 8a8f86b2eea3..38ea756879c1 100644 --- a/Modules/_io/clinic/bufferedio.c.h +++ b/Modules/_io/clinic/bufferedio.c.h @@ -601,12 +601,13 @@ static int _io_BufferedRWPair___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; + PyTypeObject *base_tp = &PyBufferedRWPair_Type; PyObject *reader; PyObject *writer; Py_ssize_t buffer_size = DEFAULT_BUFFER_SIZE; - if ((Py_IS_TYPE(self, &PyBufferedRWPair_Type) || - Py_TYPE(self)->tp_new == PyBufferedRWPair_Type.tp_new) && + if ((Py_IS_TYPE(self, base_tp) || + Py_TYPE(self)->tp_new == base_tp->tp_new) && !_PyArg_NoKeywords("BufferedRWPair", kwargs)) { goto exit; } @@ -713,4 +714,4 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=ca87adcfff6a810b input=a9049054013a1b77]*/ +/*[clinic end generated code: output=953f1577e96e8d86 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/clinic/cursor.c.h b/Modules/_sqlite/clinic/cursor.c.h index 36b8d0051a29..43e912d13479 100644 --- a/Modules/_sqlite/clinic/cursor.c.h +++ b/Modules/_sqlite/clinic/cursor.c.h @@ -16,10 +16,11 @@ static int pysqlite_cursor_init(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; + PyTypeObject *base_tp = clinic_state()->CursorType; pysqlite_Connection *connection; - if ((Py_IS_TYPE(self, clinic_state()->CursorType) || - Py_TYPE(self)->tp_new == clinic_state()->CursorType->tp_new) && + if ((Py_IS_TYPE(self, base_tp) || + Py_TYPE(self)->tp_new == base_tp->tp_new) && !_PyArg_NoKeywords("Cursor", kwargs)) { goto exit; } @@ -318,4 +319,4 @@ pysqlite_cursor_close(pysqlite_Cursor *self, PyObject *Py_UNUSED(ignored)) { return pysqlite_cursor_close_impl(self); } -/*[clinic end generated code: output=e53e75a32a9d92bd input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1f82e3c9791bb9a5 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/clinic/row.c.h b/Modules/_sqlite/clinic/row.c.h index c543b398db3f..89a48fd52da2 100644 --- a/Modules/_sqlite/clinic/row.c.h +++ b/Modules/_sqlite/clinic/row.c.h @@ -16,11 +16,11 @@ static PyObject * pysqlite_row_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = clinic_state()->RowType; pysqlite_Cursor *cursor; PyObject *data; - if ((type == clinic_state()->RowType || - type->tp_init == clinic_state()->RowType->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("Row", kwargs)) { goto exit; } @@ -60,4 +60,4 @@ pysqlite_row_keys(pysqlite_Row *self, PyObject *Py_UNUSED(ignored)) { return pysqlite_row_keys_impl(self); } -/*[clinic end generated code: output=87b91f234633702e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=157b31ac3f6af1ba input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_collectionsmodule.c.h b/Modules/clinic/_collectionsmodule.c.h index ad4da8856ac3..8ea0255b0610 100644 --- a/Modules/clinic/_collectionsmodule.c.h +++ b/Modules/clinic/_collectionsmodule.c.h @@ -46,11 +46,11 @@ static PyObject * tuplegetter_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &tuplegetter_type; Py_ssize_t index; PyObject *doc; - if ((type == &tuplegetter_type || - type->tp_init == tuplegetter_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("_tuplegetter", kwargs)) { goto exit; } @@ -75,4 +75,4 @@ tuplegetter_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=12168d58a11a4fb9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=91a0f221c7b1f96c input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_queuemodule.c.h b/Modules/clinic/_queuemodule.c.h index f86dac3c497d..94fb59a5b17a 100644 --- a/Modules/clinic/_queuemodule.c.h +++ b/Modules/clinic/_queuemodule.c.h @@ -21,14 +21,13 @@ static PyObject * simplequeue_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = simplequeue_get_state_by_type(type)->SimpleQueueType; - if ((type == simplequeue_get_state_by_type(type)->SimpleQueueType || - type->tp_init == simplequeue_get_state_by_type(type)->SimpleQueueType->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoPositional("SimpleQueue", args)) { goto exit; } - if ((type == simplequeue_get_state_by_type(type)->SimpleQueueType || - type->tp_init == simplequeue_get_state_by_type(type)->SimpleQueueType->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("SimpleQueue", kwargs)) { goto exit; } @@ -332,4 +331,4 @@ _queue_SimpleQueue_qsize(simplequeueobject *self, PyObject *Py_UNUSED(ignored)) exit: return return_value; } -/*[clinic end generated code: output=628e992d38f50aac input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9a72a8d1b5767f6a input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_ssl.c.h b/Modules/clinic/_ssl.c.h index 2d7c98c4f014..9f967ddc8e30 100644 --- a/Modules/clinic/_ssl.c.h +++ b/Modules/clinic/_ssl.c.h @@ -435,10 +435,10 @@ static PyObject * _ssl__SSLContext(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = get_state_type(type)->PySSLContext_Type; int proto_version; - if ((type == get_state_type(type)->PySSLContext_Type || - type->tp_init == get_state_type(type)->PySSLContext_Type->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("_SSLContext", kwargs)) { goto exit; } @@ -1028,14 +1028,13 @@ static PyObject * _ssl_MemoryBIO(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = get_state_type(type)->PySSLMemoryBIO_Type; - if ((type == get_state_type(type)->PySSLMemoryBIO_Type || - type->tp_init == get_state_type(type)->PySSLMemoryBIO_Type->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoPositional("MemoryBIO", args)) { goto exit; } - if ((type == get_state_type(type)->PySSLMemoryBIO_Type || - type->tp_init == get_state_type(type)->PySSLMemoryBIO_Type->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("MemoryBIO", kwargs)) { goto exit; } @@ -1543,4 +1542,4 @@ _ssl_enum_crls(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje #ifndef _SSL_ENUM_CRLS_METHODDEF #define _SSL_ENUM_CRLS_METHODDEF #endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */ -/*[clinic end generated code: output=a3d97a19163bb044 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=4d9b81fa81f520f0 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/itertoolsmodule.c.h b/Modules/clinic/itertoolsmodule.c.h index 287de524e913..70299aceb7a0 100644 --- a/Modules/clinic/itertoolsmodule.c.h +++ b/Modules/clinic/itertoolsmodule.c.h @@ -102,10 +102,10 @@ static PyObject * pairwise_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &pairwise_type; PyObject *iterable; - if ((type == &pairwise_type || - type->tp_init == pairwise_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("pairwise", kwargs)) { goto exit; } @@ -195,11 +195,11 @@ static PyObject * itertools__grouper(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &_grouper_type; PyObject *parent; PyObject *tgtkey; - if ((type == &_grouper_type || - type->tp_init == _grouper_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("_grouper", kwargs)) { goto exit; } @@ -232,12 +232,12 @@ static PyObject * itertools_teedataobject(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &teedataobject_type; PyObject *it; PyObject *values; PyObject *next; - if ((type == &teedataobject_type || - type->tp_init == teedataobject_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("teedataobject", kwargs)) { goto exit; } @@ -270,10 +270,10 @@ static PyObject * itertools__tee(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &tee_type; PyObject *iterable; - if ((type == &tee_type || - type->tp_init == tee_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("_tee", kwargs)) { goto exit; } @@ -345,10 +345,10 @@ static PyObject * itertools_cycle(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &cycle_type; PyObject *iterable; - if ((type == &cycle_type || - type->tp_init == cycle_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("cycle", kwargs)) { goto exit; } @@ -377,11 +377,11 @@ static PyObject * itertools_dropwhile(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &dropwhile_type; PyObject *func; PyObject *seq; - if ((type == &dropwhile_type || - type->tp_init == dropwhile_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("dropwhile", kwargs)) { goto exit; } @@ -409,11 +409,11 @@ static PyObject * itertools_takewhile(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &takewhile_type; PyObject *func; PyObject *seq; - if ((type == &takewhile_type || - type->tp_init == takewhile_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("takewhile", kwargs)) { goto exit; } @@ -441,11 +441,11 @@ static PyObject * itertools_starmap(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &starmap_type; PyObject *func; PyObject *seq; - if ((type == &starmap_type || - type->tp_init == starmap_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("starmap", kwargs)) { goto exit; } @@ -821,11 +821,11 @@ static PyObject * itertools_filterfalse(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &filterfalse_type; PyObject *func; PyObject *seq; - if ((type == &filterfalse_type || - type->tp_init == filterfalse_type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("filterfalse", kwargs)) { goto exit; } @@ -913,4 +913,4 @@ itertools_count(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=0229ebd72962f130 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=47c8c8ccec8740d7 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/selectmodule.c.h b/Modules/clinic/selectmodule.c.h index fda9aaab4755..f44ca1d70a1e 100644 --- a/Modules/clinic/selectmodule.c.h +++ b/Modules/clinic/selectmodule.c.h @@ -1061,14 +1061,13 @@ static PyObject * select_kqueue(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = _selectstate_by_type(type)->kqueue_queue_Type; - if ((type == _selectstate_by_type(type)->kqueue_queue_Type || - type->tp_init == _selectstate_by_type(type)->kqueue_queue_Type->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoPositional("kqueue", args)) { goto exit; } - if ((type == _selectstate_by_type(type)->kqueue_queue_Type || - type->tp_init == _selectstate_by_type(type)->kqueue_queue_Type->tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("kqueue", kwargs)) { goto exit; } @@ -1310,4 +1309,4 @@ select_kqueue_control(kqueue_queue_Object *self, PyObject *const *args, Py_ssize #ifndef SELECT_KQUEUE_CONTROL_METHODDEF #define SELECT_KQUEUE_CONTROL_METHODDEF #endif /* !defined(SELECT_KQUEUE_CONTROL_METHODDEF) */ -/*[clinic end generated code: output=9556c7d6cd5192d1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=64516114287e894d input=a9049054013a1b77]*/ diff --git a/Objects/clinic/classobject.c.h b/Objects/clinic/classobject.c.h index 6c449829662a..a7bac63052bc 100644 --- a/Objects/clinic/classobject.c.h +++ b/Objects/clinic/classobject.c.h @@ -38,11 +38,11 @@ static PyObject * method_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyMethod_Type; PyObject *function; PyObject *instance; - if ((type == &PyMethod_Type || - type->tp_init == PyMethod_Type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("method", kwargs)) { goto exit; } @@ -70,10 +70,10 @@ static PyObject * instancemethod_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyInstanceMethod_Type; PyObject *function; - if ((type == &PyInstanceMethod_Type || - type->tp_init == PyInstanceMethod_Type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("instancemethod", kwargs)) { goto exit; } @@ -86,4 +86,4 @@ instancemethod_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=e3294c26a71d456d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2a5e7fa5947a86cb input=a9049054013a1b77]*/ diff --git a/Objects/clinic/codeobject.c.h b/Objects/clinic/codeobject.c.h index da33f4a6a20c..5ad4b1fed734 100644 --- a/Objects/clinic/codeobject.c.h +++ b/Objects/clinic/codeobject.c.h @@ -30,6 +30,7 @@ static PyObject * code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyCode_Type; int argcount; int posonlyargcount; int kwonlyargcount; @@ -49,8 +50,7 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *freevars = NULL; PyObject *cellvars = NULL; - if ((type == &PyCode_Type || - type->tp_init == PyCode_Type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("code", kwargs)) { goto exit; } @@ -488,4 +488,4 @@ code__varname_from_oparg(PyCodeObject *self, PyObject *const *args, Py_ssize_t n exit: return return_value; } -/*[clinic end generated code: output=b6c98f17c60ace53 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f1fab6e71c785182 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/enumobject.c.h b/Objects/clinic/enumobject.c.h index 208a9e8be1a1..adf78efd0d66 100644 --- a/Objects/clinic/enumobject.c.h +++ b/Objects/clinic/enumobject.c.h @@ -91,10 +91,10 @@ static PyObject * reversed_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyReversed_Type; PyObject *seq; - if ((type == &PyReversed_Type || - type->tp_init == PyReversed_Type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("reversed", kwargs)) { goto exit; } @@ -107,4 +107,4 @@ reversed_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=683261097bfd794a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=aba0ddbeab1601e3 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/floatobject.c.h b/Objects/clinic/floatobject.c.h index 1a81e173231b..6bc25a0a409f 100644 --- a/Objects/clinic/floatobject.c.h +++ b/Objects/clinic/floatobject.c.h @@ -212,10 +212,10 @@ static PyObject * float_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyFloat_Type; PyObject *x = NULL; - if ((type == &PyFloat_Type || - type->tp_init == PyFloat_Type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("float", kwargs)) { goto exit; } @@ -327,4 +327,4 @@ float___format__(PyObject *self, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=ac6374ac606a505e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=74bc91bb44014df9 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/listobject.c.h b/Objects/clinic/listobject.c.h index 94852e996170..e3d6ffa9f76f 100644 --- a/Objects/clinic/listobject.c.h +++ b/Objects/clinic/listobject.c.h @@ -326,10 +326,11 @@ static int list___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; + PyTypeObject *base_tp = &PyList_Type; PyObject *iterable = NULL; - if ((Py_IS_TYPE(self, &PyList_Type) || - Py_TYPE(self)->tp_new == PyList_Type.tp_new) && + if ((Py_IS_TYPE(self, base_tp) || + Py_TYPE(self)->tp_new == base_tp->tp_new) && !_PyArg_NoKeywords("list", kwargs)) { goto exit; } @@ -382,4 +383,4 @@ list___reversed__(PyListObject *self, PyObject *Py_UNUSED(ignored)) { return list___reversed___impl(self); } -/*[clinic end generated code: output=4e6f38b655394564 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2ca109d8acc775bc input=a9049054013a1b77]*/ diff --git a/Objects/clinic/tupleobject.c.h b/Objects/clinic/tupleobject.c.h index a4776e14fa0a..3de95759a13f 100644 --- a/Objects/clinic/tupleobject.c.h +++ b/Objects/clinic/tupleobject.c.h @@ -81,10 +81,10 @@ static PyObject * tuple_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + PyTypeObject *base_tp = &PyTuple_Type; PyObject *iterable = NULL; - if ((type == &PyTuple_Type || - type->tp_init == PyTuple_Type.tp_init) && + if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("tuple", kwargs)) { goto exit; } @@ -118,4 +118,4 @@ tuple___getnewargs__(PyTupleObject *self, PyObject *Py_UNUSED(ignored)) { return tuple___getnewargs___impl(self); } -/*[clinic end generated code: output=441d2b880e865f87 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=48a9e0834b300ac3 input=a9049054013a1b77]*/ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index abff4d2583e1..b8b2b75c7491 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -1174,6 +1174,7 @@ def parser_body(prototype, *fields, declarations=''): raise ValueError("Slot methods cannot access their defining class.") if not parses_keywords: + declarations = '{base_type_ptr}' fields.insert(0, normalize_snippet(""" if ({self_type_check}!_PyArg_NoKeywords("{name}", kwargs)) {{ goto exit; @@ -1187,7 +1188,7 @@ def parser_body(prototype, *fields, declarations=''): """, indent=4)) parser_definition = parser_body(parser_prototype, *fields, - declarations=parser_body_declarations) + declarations=declarations) if flags in ('METH_NOARGS', 'METH_O', 'METH_VARARGS'): @@ -3839,21 +3840,22 @@ def set_template_dict(self, template_dict): cls = self.function.cls if ((kind in (METHOD_NEW, METHOD_INIT)) and cls and cls.typedef): - type_object = self.function.cls.type_object - prefix = (type_object[1:] + '.' if type_object[0] == '&' else - type_object + '->') if kind == METHOD_NEW: - type_check = ('({0} == {1} ||\n ' - ' {0}->tp_init == {2}tp_init)' - ).format(self.name, type_object, prefix) + type_check = ( + '({0} == base_tp || {0}->tp_init == base_tp->tp_init)' + ).format(self.name) else: - type_check = ('(Py_IS_TYPE({0}, {1}) ||\n ' - ' Py_TYPE({0})->tp_new == {2}tp_new)' - ).format(self.name, type_object, prefix) + type_check = ('(Py_IS_TYPE({0}, base_tp) ||\n ' + ' Py_TYPE({0})->tp_new == base_tp->tp_new)' + ).format(self.name) line = '{} &&\n '.format(type_check) template_dict['self_type_check'] = line + type_object = self.function.cls.type_object + type_ptr = f'PyTypeObject *base_tp = {type_object};' + template_dict['base_type_ptr'] = type_ptr + def add_c_return_converter(f, name=None): From webhook-mailer at python.org Tue Jan 31 16:28:42 2023 From: webhook-mailer at python.org (brandtbucher) Date: Tue, 31 Jan 2023 21:28:42 -0000 Subject: [Python-checkins] GH-100288: Skip extra work when failing to specialize LOAD_ATTR (GH-101354) Message-ID: https://github.com/python/cpython/commit/76efcb40930d1584e8706f015d0e5475fb16acb5 commit: 76efcb40930d1584e8706f015d0e5475fb16acb5 branch: main author: Brandt Bucher committer: brandtbucher date: 2023-01-31T13:28:32-08:00 summary: GH-100288: Skip extra work when failing to specialize LOAD_ATTR (GH-101354) files: M Python/specialize.c diff --git a/Python/specialize.c b/Python/specialize.c index 096687f5fdf0..908ad6dceb57 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1039,14 +1039,6 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, } } -typedef enum { - MANAGED_VALUES = 1, - MANAGED_DICT = 2, - OFFSET_DICT = 3, - NO_DICT = 4, - LAZY_DICT = 5, -} ObjectDictKind; - // Please collect stats carefully before and after modifying. A subtle change // can cause a significant drop in cache hits. A possible test is // python.exe -m test_typing test_re test_dis test_zlib. @@ -1058,71 +1050,45 @@ PyObject *descr, DescriptorClassification kind) PyTypeObject *owner_cls = Py_TYPE(owner); assert(kind == METHOD && descr != NULL); - ObjectDictKind dictkind; - PyDictKeysObject *keys; if (owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT) { PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - keys = ((PyHeapTypeObject *)owner_cls)->ht_cached_keys; - if (_PyDictOrValues_IsValues(dorv)) { - dictkind = MANAGED_VALUES; - } - else { - dictkind = MANAGED_DICT; - } - } - else { - Py_ssize_t dictoffset = owner_cls->tp_dictoffset; - if (dictoffset < 0 || dictoffset > INT16_MAX) { - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_RANGE); - goto fail; - } - if (dictoffset == 0) { - dictkind = NO_DICT; - keys = NULL; - } - else { - PyObject *dict = *(PyObject **) ((char *)owner + dictoffset); - if (dict == NULL) { - // This object will have a dict if user access __dict__ - dictkind = LAZY_DICT; - keys = NULL; - } - else { - keys = ((PyDictObject *)dict)->ma_keys; - dictkind = OFFSET_DICT; - } + PyDictKeysObject *keys = ((PyHeapTypeObject *)owner_cls)->ht_cached_keys; + if (!_PyDictOrValues_IsValues(dorv)) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_HAS_MANAGED_DICT); + return 0; } - } - if (dictkind == MANAGED_VALUES || dictkind == OFFSET_DICT) { Py_ssize_t index = _PyDictKeys_StringLookup(keys, name); if (index != DKIX_EMPTY) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_SHADOWED); - goto fail; + return 0; } uint32_t keys_version = _PyDictKeys_GetVersionForCurrentState(keys); if (keys_version == 0) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS); - goto fail; + return 0; } write_u32(cache->keys_version, keys_version); + _py_set_opcode(instr, LOAD_ATTR_METHOD_WITH_VALUES); } - switch(dictkind) { - case NO_DICT: + else { + Py_ssize_t dictoffset = owner_cls->tp_dictoffset; + if (dictoffset < 0 || dictoffset > INT16_MAX) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_RANGE); + return 0; + } + if (dictoffset == 0) { _py_set_opcode(instr, LOAD_ATTR_METHOD_NO_DICT); - break; - case MANAGED_VALUES: - _py_set_opcode(instr, LOAD_ATTR_METHOD_WITH_VALUES); - break; - case MANAGED_DICT: - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_HAS_MANAGED_DICT); - goto fail; - case OFFSET_DICT: - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NOT_MANAGED_DICT); - goto fail; - case LAZY_DICT: - assert(owner_cls->tp_dictoffset > 0 && owner_cls->tp_dictoffset <= INT16_MAX); + } + else { + PyObject *dict = *(PyObject **) ((char *)owner + dictoffset); + if (dict) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NOT_MANAGED_DICT); + return 0; + } + assert(owner_cls->tp_dictoffset > 0); + assert(owner_cls->tp_dictoffset <= INT16_MAX); _py_set_opcode(instr, LOAD_ATTR_METHOD_LAZY_DICT); - break; + } } /* `descr` is borrowed. This is safe for methods (even inherited ones from * super classes!) as long as tp_version_tag is validated for two main reasons: @@ -1141,8 +1107,6 @@ PyObject *descr, DescriptorClassification kind) write_u32(cache->type_version, owner_cls->tp_version_tag); write_obj(cache->descr, descr); return 1; -fail: - return 0; } void