From 487fdbed40734fd7721457c6f6ffeca03da0b0e7 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 12 Dec 2024 11:22:20 +0000 Subject: [PATCH 001/376] GH-125174: Fix compiler warning (GH-127860) Fix compiler warning --- Include/internal/pycore_object.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 22de3c9d4e32ea..668ea47ca727e2 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -184,7 +184,7 @@ PyAPI_FUNC(void) _Py_SetImmortalUntracked(PyObject *op); // Makes an immortal object mortal again with the specified refcnt. Should only // be used during runtime finalization. -static inline void _Py_SetMortal(PyObject *op, Py_ssize_t refcnt) +static inline void _Py_SetMortal(PyObject *op, short refcnt) { if (op) { assert(_Py_IsImmortal(op)); From 7146f1894638130940944d4808dae7d144d46227 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Thu, 12 Dec 2024 17:39:24 +0000 Subject: [PATCH 002/376] GH-127807: pathlib ABCs: remove `PathBase._unsupported_msg()` (#127855) This method helped us customise the `UnsupportedOperation` message depending on the type. But we're aiming to make `PathBase` a proper ABC soon, so `NotImplementedError` is the right exception to raise there. --- Lib/pathlib/__init__.py | 4 +-- Lib/pathlib/_abc.py | 35 +++++++-------------- Lib/pathlib/_local.py | 37 ++++++++++++++++++----- Lib/test/test_pathlib/test_pathlib.py | 8 +++++ Lib/test/test_pathlib/test_pathlib_abc.py | 12 ++------ 5 files changed, 52 insertions(+), 44 deletions(-) diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py index 5da3acd31997e5..ec1bac9ef49350 100644 --- a/Lib/pathlib/__init__.py +++ b/Lib/pathlib/__init__.py @@ -5,8 +5,6 @@ operating systems. """ -from pathlib._abc import * from pathlib._local import * -__all__ = (_abc.__all__ + - _local.__all__) +__all__ = _local.__all__ diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index b10aba85132332..b4560295300c28 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -20,15 +20,6 @@ from pathlib._os import copyfileobj -__all__ = ["UnsupportedOperation"] - - -class UnsupportedOperation(NotImplementedError): - """An exception that is raised when an unsupported operation is attempted. - """ - pass - - @functools.cache def _is_case_sensitive(parser): return parser.normcase('Aa') == 'Aa' @@ -353,8 +344,8 @@ class PathBase(PurePathBase): This class provides dummy implementations for many methods that derived classes can override selectively; the default implementations raise - UnsupportedOperation. The most basic methods, such as stat() and open(), - directly raise UnsupportedOperation; these basic methods are called by + NotImplementedError. The most basic methods, such as stat() and open(), + directly raise NotImplementedError; these basic methods are called by other methods such as is_dir() and read_text(). The Path class derives this class to implement local filesystem paths. @@ -363,16 +354,12 @@ class PathBase(PurePathBase): """ __slots__ = () - @classmethod - def _unsupported_msg(cls, attribute): - return f"{cls.__name__}.{attribute} is unsupported" - def stat(self, *, follow_symlinks=True): """ Return the result of the stat() system call on this path, like os.stat() does. """ - raise UnsupportedOperation(self._unsupported_msg('stat()')) + raise NotImplementedError # Convenience functions for querying the stat results @@ -448,7 +435,7 @@ def open(self, mode='r', buffering=-1, encoding=None, Open the file pointed to by this path and return a file object, as the built-in open() function does. """ - raise UnsupportedOperation(self._unsupported_msg('open()')) + raise NotImplementedError def read_bytes(self): """ @@ -498,7 +485,7 @@ def iterdir(self): The children are yielded in arbitrary order, and the special entries '.' and '..' are not included. """ - raise UnsupportedOperation(self._unsupported_msg('iterdir()')) + raise NotImplementedError def _glob_selector(self, parts, case_sensitive, recurse_symlinks): if case_sensitive is None: @@ -575,14 +562,14 @@ def readlink(self): """ Return the path to which the symbolic link points. """ - raise UnsupportedOperation(self._unsupported_msg('readlink()')) + raise NotImplementedError def symlink_to(self, target, target_is_directory=False): """ Make this path a symlink pointing to the target path. Note the order of arguments (link, target) is the reverse of os.symlink. """ - raise UnsupportedOperation(self._unsupported_msg('symlink_to()')) + raise NotImplementedError def _symlink_to_target_of(self, link): """ @@ -595,7 +582,7 @@ def mkdir(self, mode=0o777, parents=False, exist_ok=False): """ Create a new directory at this given path. """ - raise UnsupportedOperation(self._unsupported_msg('mkdir()')) + raise NotImplementedError # Metadata keys supported by this path type. _readable_metadata = _writable_metadata = frozenset() @@ -604,13 +591,13 @@ def _read_metadata(self, keys=None, *, follow_symlinks=True): """ Returns path metadata as a dict with string keys. """ - raise UnsupportedOperation(self._unsupported_msg('_read_metadata()')) + raise NotImplementedError def _write_metadata(self, metadata, *, follow_symlinks=True): """ Sets path metadata from the given dict with string keys. """ - raise UnsupportedOperation(self._unsupported_msg('_write_metadata()')) + raise NotImplementedError def _copy_metadata(self, target, *, follow_symlinks=True): """ @@ -687,7 +674,7 @@ def _delete(self): """ Delete this file or directory (including all sub-directories). """ - raise UnsupportedOperation(self._unsupported_msg('_delete()')) + raise NotImplementedError def move(self, target): """ diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index 0dfe9d2390ecff..b933dd512eeb28 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -21,15 +21,22 @@ from pathlib._os import (copyfile, file_metadata_keys, read_file_metadata, write_file_metadata) -from pathlib._abc import UnsupportedOperation, PurePathBase, PathBase +from pathlib._abc import PurePathBase, PathBase __all__ = [ + "UnsupportedOperation", "PurePath", "PurePosixPath", "PureWindowsPath", "Path", "PosixPath", "WindowsPath", ] +class UnsupportedOperation(NotImplementedError): + """An exception that is raised when an unsupported operation is attempted. + """ + pass + + class _PathParents(Sequence): """This object provides sequence-like access to the logical ancestors of a path. Don't try to construct it yourself.""" @@ -527,10 +534,6 @@ class Path(PathBase, PurePath): """ __slots__ = () - @classmethod - def _unsupported_msg(cls, attribute): - return f"{cls.__name__}.{attribute} is unsupported on this system" - def __new__(cls, *args, **kwargs): if cls is Path: cls = WindowsPath if os.name == 'nt' else PosixPath @@ -817,7 +820,8 @@ def owner(self, *, follow_symlinks=True): """ Return the login name of the file owner. """ - raise UnsupportedOperation(self._unsupported_msg('owner()')) + f = f"{type(self).__name__}.owner()" + raise UnsupportedOperation(f"{f} is unsupported on this system") if grp: def group(self, *, follow_symlinks=True): @@ -831,7 +835,8 @@ def group(self, *, follow_symlinks=True): """ Return the group name of the file gid. """ - raise UnsupportedOperation(self._unsupported_msg('group()')) + f = f"{type(self).__name__}.group()" + raise UnsupportedOperation(f"{f} is unsupported on this system") if hasattr(os, "readlink"): def readlink(self): @@ -839,6 +844,13 @@ def readlink(self): Return the path to which the symbolic link points. """ return self.with_segments(os.readlink(self)) + else: + def readlink(self): + """ + Return the path to which the symbolic link points. + """ + f = f"{type(self).__name__}.readlink()" + raise UnsupportedOperation(f"{f} is unsupported on this system") def touch(self, mode=0o666, exist_ok=True): """ @@ -989,6 +1001,14 @@ def symlink_to(self, target, target_is_directory=False): Note the order of arguments (link, target) is the reverse of os.symlink. """ os.symlink(target, self, target_is_directory) + else: + def symlink_to(self, target, target_is_directory=False): + """ + Make this path a symlink pointing to the target path. + Note the order of arguments (link, target) is the reverse of os.symlink. + """ + f = f"{type(self).__name__}.symlink_to()" + raise UnsupportedOperation(f"{f} is unsupported on this system") if os.name == 'nt': def _symlink_to_target_of(self, link): @@ -1013,7 +1033,8 @@ def hardlink_to(self, target): Note the order of arguments (self, target) is the reverse of os.link's. """ - raise UnsupportedOperation(self._unsupported_msg('hardlink_to()')) + f = f"{type(self).__name__}.hardlink_to()" + raise UnsupportedOperation(f"{f} is unsupported on this system") def expanduser(self): """ Return a new path with expanded ~ and ~user constructs diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index b57ef420bfcbcd..68bff2cf0d511e 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -63,6 +63,14 @@ def needs_symlinks(fn): _tests_needing_symlinks.add(fn.__name__) return fn + + +class UnsupportedOperationTest(unittest.TestCase): + def test_is_notimplemented(self): + self.assertTrue(issubclass(pathlib.UnsupportedOperation, NotImplementedError)) + self.assertTrue(isinstance(pathlib.UnsupportedOperation(), NotImplementedError)) + + # # Tests for the pure classes. # diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index d770b87dc6a104..e230dd188799a5 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -5,7 +5,7 @@ import stat import unittest -from pathlib._abc import UnsupportedOperation, PurePathBase, PathBase +from pathlib._abc import PurePathBase, PathBase from pathlib._types import Parser import posixpath @@ -27,11 +27,6 @@ def needs_windows(fn): return fn -class UnsupportedOperationTest(unittest.TestCase): - def test_is_notimplemented(self): - self.assertTrue(issubclass(UnsupportedOperation, NotImplementedError)) - self.assertTrue(isinstance(UnsupportedOperation(), NotImplementedError)) - # # Tests for the pure classes. # @@ -1294,10 +1289,9 @@ def test_is_absolute_windows(self): class PathBaseTest(PurePathBaseTest): cls = PathBase - def test_unsupported_operation(self): - P = self.cls + def test_not_implemented_error(self): p = self.cls('') - e = UnsupportedOperation + e = NotImplementedError self.assertRaises(e, p.stat) self.assertRaises(e, p.exists) self.assertRaises(e, p.is_dir) From f8dcb8200626a1a06c4a26d8129257f42658a9ff Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 12 Dec 2024 17:59:13 +0000 Subject: [PATCH 003/376] gh-127879: Fix data race in `_PyFreeList_Push` (#127880) Writes to the `ob_tid` field need to use atomics because it may be concurrently read by a non-locking dictionary, list, or structmember read. --- Include/internal/pycore_freelist.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_freelist.h b/Include/internal/pycore_freelist.h index da2d7bf6ae1393..84a5ab30f3eeea 100644 --- a/Include/internal/pycore_freelist.h +++ b/Include/internal/pycore_freelist.h @@ -51,7 +51,7 @@ static inline int _PyFreeList_Push(struct _Py_freelist *fl, void *obj, Py_ssize_t maxsize) { if (fl->size < maxsize && fl->size >= 0) { - *(void **)obj = fl->freelist; + FT_ATOMIC_STORE_PTR_RELAXED(*(void **)obj, fl->freelist); fl->freelist = obj; fl->size++; OBJECT_STAT_INC(to_freelist); From f823910bbd4bf01ec3e1ab7b3cb1d77815138296 Mon Sep 17 00:00:00 2001 From: velemas <10437413+velemas@users.noreply.github.com> Date: Thu, 12 Dec 2024 20:07:55 +0200 Subject: [PATCH 004/376] gh-127865: Fix build failure for systems without thread local support (GH-127866) This PR fixes the build issue introduced by the commit 628f6eb from GH-112207 on systems without thread local support. --- .../Build/2024-12-12-17-21-45.gh-issue-127865.30GDzs.rst | 1 + Python/import.c | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2024-12-12-17-21-45.gh-issue-127865.30GDzs.rst diff --git a/Misc/NEWS.d/next/Build/2024-12-12-17-21-45.gh-issue-127865.30GDzs.rst b/Misc/NEWS.d/next/Build/2024-12-12-17-21-45.gh-issue-127865.30GDzs.rst new file mode 100644 index 00000000000000..3fc1d8a1b51d30 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-12-12-17-21-45.gh-issue-127865.30GDzs.rst @@ -0,0 +1 @@ +Fix build failure on systems without thread-locals support. diff --git a/Python/import.c b/Python/import.c index b3c384c27718ce..f3511aaf7b8010 100644 --- a/Python/import.c +++ b/Python/import.c @@ -749,7 +749,7 @@ const char * _PyImport_ResolveNameWithPackageContext(const char *name) { #ifndef HAVE_THREAD_LOCAL - PyThread_acquire_lock(EXTENSIONS.mutex, WAIT_LOCK); + PyMutex_Lock(&EXTENSIONS.mutex); #endif if (PKGCONTEXT != NULL) { const char *p = strrchr(PKGCONTEXT, '.'); @@ -759,7 +759,7 @@ _PyImport_ResolveNameWithPackageContext(const char *name) } } #ifndef HAVE_THREAD_LOCAL - PyThread_release_lock(EXTENSIONS.mutex); + PyMutex_Unlock(&EXTENSIONS.mutex); #endif return name; } @@ -768,12 +768,12 @@ const char * _PyImport_SwapPackageContext(const char *newcontext) { #ifndef HAVE_THREAD_LOCAL - PyThread_acquire_lock(EXTENSIONS.mutex, WAIT_LOCK); + PyMutex_Lock(&EXTENSIONS.mutex); #endif const char *oldcontext = PKGCONTEXT; PKGCONTEXT = newcontext; #ifndef HAVE_THREAD_LOCAL - PyThread_release_lock(EXTENSIONS.mutex); + PyMutex_Unlock(&EXTENSIONS.mutex); #endif return oldcontext; } From 365451e28368db46ae89a3a990d85c10c2284aa2 Mon Sep 17 00:00:00 2001 From: Andrey Efremov Date: Fri, 13 Dec 2024 03:17:39 +0700 Subject: [PATCH 005/376] gh-127353: Allow to force color output on Windows (#127354) --- Lib/_colorize.py | 17 +++++---- Lib/test/test__colorize.py | 37 +++++++++++++++++++ ...-11-28-15-55-48.gh-issue-127353.i-XOXg.rst | 2 + 3 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 845fb57a90abb8..709081e25ec59b 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -32,14 +32,6 @@ def get_colors(colorize: bool = False) -> ANSIColors: def can_colorize() -> bool: - if sys.platform == "win32": - try: - import nt - - if not nt._supports_virtual_terminal(): - return False - except (ImportError, AttributeError): - return False if not sys.flags.ignore_environment: if os.environ.get("PYTHON_COLORS") == "0": return False @@ -58,6 +50,15 @@ def can_colorize() -> bool: if not hasattr(sys.stderr, "fileno"): return False + if sys.platform == "win32": + try: + import nt + + if not nt._supports_virtual_terminal(): + return False + except (ImportError, AttributeError): + return False + try: return os.isatty(sys.stderr.fileno()) except io.UnsupportedOperation: diff --git a/Lib/test/test__colorize.py b/Lib/test/test__colorize.py index d55b97ade68cef..7a65d63f49eed7 100644 --- a/Lib/test/test__colorize.py +++ b/Lib/test/test__colorize.py @@ -50,10 +50,47 @@ def test_colorized_detection_checks_for_environment_variables(self): with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1', "PYTHON_COLORS": '0'}): self.assertEqual(_colorize.can_colorize(), False) + with unittest.mock.patch("os.environ", {}): + self.assertEqual(_colorize.can_colorize(), True) + isatty_mock.return_value = False with unittest.mock.patch("os.environ", {}): self.assertEqual(_colorize.can_colorize(), False) + @force_not_colorized + @unittest.skipUnless(sys.platform == "win32", "Windows only") + def test_colorized_detection_checks_for_environment_variables_no_vt(self): + with (unittest.mock.patch("nt._supports_virtual_terminal", return_value=False), + unittest.mock.patch("os.isatty") as isatty_mock, + unittest.mock.patch("sys.flags", unittest.mock.MagicMock(ignore_environment=False)), + unittest.mock.patch("_colorize.can_colorize", ORIGINAL_CAN_COLORIZE)): + isatty_mock.return_value = True + with unittest.mock.patch("os.environ", {'TERM': 'dumb'}): + self.assertEqual(_colorize.can_colorize(), False) + with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '1'}): + self.assertEqual(_colorize.can_colorize(), True) + with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '0'}): + self.assertEqual(_colorize.can_colorize(), False) + with unittest.mock.patch("os.environ", {'NO_COLOR': '1'}): + self.assertEqual(_colorize.can_colorize(), False) + with unittest.mock.patch("os.environ", + {'NO_COLOR': '1', "PYTHON_COLORS": '1'}): + self.assertEqual(_colorize.can_colorize(), True) + with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1'}): + self.assertEqual(_colorize.can_colorize(), True) + with unittest.mock.patch("os.environ", + {'FORCE_COLOR': '1', 'NO_COLOR': '1'}): + self.assertEqual(_colorize.can_colorize(), False) + with unittest.mock.patch("os.environ", + {'FORCE_COLOR': '1', "PYTHON_COLORS": '0'}): + self.assertEqual(_colorize.can_colorize(), False) + with unittest.mock.patch("os.environ", {}): + self.assertEqual(_colorize.can_colorize(), False) + + isatty_mock.return_value = False + with unittest.mock.patch("os.environ", {}): + self.assertEqual(_colorize.can_colorize(), False) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst b/Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst new file mode 100644 index 00000000000000..88661b9a611071 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst @@ -0,0 +1,2 @@ +Allow to force color output on Windows using environment variables. Patch by +Andrey Efremov. From ed037d229f64db90aea00f397e9ce1b2f4a22d3f Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Thu, 12 Dec 2024 20:27:29 +0000 Subject: [PATCH 006/376] Fix typos in `Lib/_pydecimal.py` (#127700) --- Lib/_pydecimal.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index 5b60570c6c592a..ec036199331396 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -97,7 +97,7 @@ class DecimalException(ArithmeticError): Used exceptions derive from this. If an exception derives from another exception besides this (such as - Underflow (Inexact, Rounded, Subnormal) that indicates that it is only + Underflow (Inexact, Rounded, Subnormal)) that indicates that it is only called if the others are present. This isn't actually used for anything, though. @@ -145,7 +145,7 @@ class InvalidOperation(DecimalException): x ** (+-)INF An operand is invalid - The result of the operation after these is a quiet positive NaN, + The result of the operation after this is a quiet positive NaN, except when the cause is a signaling NaN, in which case the result is also a quiet NaN, but with the original sign, and an optional diagnostic information. From a8ffe661548e16ad02dbe6cb8a89513d7ed2a42c Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 12 Dec 2024 23:11:20 +0200 Subject: [PATCH 007/376] Revert "gh-127353: Allow to force color output on Windows (#127354)" (#127889) This reverts commit 365451e28368db46ae89a3a990d85c10c2284aa2. --- Lib/_colorize.py | 17 ++++----- Lib/test/test__colorize.py | 37 ------------------- ...-11-28-15-55-48.gh-issue-127353.i-XOXg.rst | 2 - 3 files changed, 8 insertions(+), 48 deletions(-) delete mode 100644 Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 709081e25ec59b..845fb57a90abb8 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -32,6 +32,14 @@ def get_colors(colorize: bool = False) -> ANSIColors: def can_colorize() -> bool: + if sys.platform == "win32": + try: + import nt + + if not nt._supports_virtual_terminal(): + return False + except (ImportError, AttributeError): + return False if not sys.flags.ignore_environment: if os.environ.get("PYTHON_COLORS") == "0": return False @@ -50,15 +58,6 @@ def can_colorize() -> bool: if not hasattr(sys.stderr, "fileno"): return False - if sys.platform == "win32": - try: - import nt - - if not nt._supports_virtual_terminal(): - return False - except (ImportError, AttributeError): - return False - try: return os.isatty(sys.stderr.fileno()) except io.UnsupportedOperation: diff --git a/Lib/test/test__colorize.py b/Lib/test/test__colorize.py index 7a65d63f49eed7..d55b97ade68cef 100644 --- a/Lib/test/test__colorize.py +++ b/Lib/test/test__colorize.py @@ -50,47 +50,10 @@ def test_colorized_detection_checks_for_environment_variables(self): with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1', "PYTHON_COLORS": '0'}): self.assertEqual(_colorize.can_colorize(), False) - with unittest.mock.patch("os.environ", {}): - self.assertEqual(_colorize.can_colorize(), True) - isatty_mock.return_value = False with unittest.mock.patch("os.environ", {}): self.assertEqual(_colorize.can_colorize(), False) - @force_not_colorized - @unittest.skipUnless(sys.platform == "win32", "Windows only") - def test_colorized_detection_checks_for_environment_variables_no_vt(self): - with (unittest.mock.patch("nt._supports_virtual_terminal", return_value=False), - unittest.mock.patch("os.isatty") as isatty_mock, - unittest.mock.patch("sys.flags", unittest.mock.MagicMock(ignore_environment=False)), - unittest.mock.patch("_colorize.can_colorize", ORIGINAL_CAN_COLORIZE)): - isatty_mock.return_value = True - with unittest.mock.patch("os.environ", {'TERM': 'dumb'}): - self.assertEqual(_colorize.can_colorize(), False) - with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '1'}): - self.assertEqual(_colorize.can_colorize(), True) - with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '0'}): - self.assertEqual(_colorize.can_colorize(), False) - with unittest.mock.patch("os.environ", {'NO_COLOR': '1'}): - self.assertEqual(_colorize.can_colorize(), False) - with unittest.mock.patch("os.environ", - {'NO_COLOR': '1', "PYTHON_COLORS": '1'}): - self.assertEqual(_colorize.can_colorize(), True) - with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1'}): - self.assertEqual(_colorize.can_colorize(), True) - with unittest.mock.patch("os.environ", - {'FORCE_COLOR': '1', 'NO_COLOR': '1'}): - self.assertEqual(_colorize.can_colorize(), False) - with unittest.mock.patch("os.environ", - {'FORCE_COLOR': '1', "PYTHON_COLORS": '0'}): - self.assertEqual(_colorize.can_colorize(), False) - with unittest.mock.patch("os.environ", {}): - self.assertEqual(_colorize.can_colorize(), False) - - isatty_mock.return_value = False - with unittest.mock.patch("os.environ", {}): - self.assertEqual(_colorize.can_colorize(), False) - if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst b/Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst deleted file mode 100644 index 88661b9a611071..00000000000000 --- a/Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst +++ /dev/null @@ -1,2 +0,0 @@ -Allow to force color output on Windows using environment variables. Patch by -Andrey Efremov. From 8ac307f0d6834148471d2e12a45bf022e659164c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns=20=F0=9F=87=B5=F0=9F=87=B8?= Date: Thu, 12 Dec 2024 21:41:46 +0000 Subject: [PATCH 008/376] GH-127724: don't use sysconfig to calculate the venv local include path (#127731) --- Lib/venv/__init__.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index ca1af84e6705fe..dc4c9ef3531991 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -103,8 +103,6 @@ def _venv_path(self, env_dir, name): vars = { 'base': env_dir, 'platbase': env_dir, - 'installed_base': env_dir, - 'installed_platbase': env_dir, } return sysconfig.get_path(name, scheme='venv', vars=vars) @@ -175,9 +173,20 @@ def create_if_needed(d): context.python_dir = dirname context.python_exe = exename binpath = self._venv_path(env_dir, 'scripts') - incpath = self._venv_path(env_dir, 'include') libpath = self._venv_path(env_dir, 'purelib') + # PEP 405 says venvs should create a local include directory. + # See https://peps.python.org/pep-0405/#include-files + # XXX: This directory is not exposed in sysconfig or anywhere else, and + # doesn't seem to be utilized by modern packaging tools. We keep it + # for backwards-compatibility, and to follow the PEP, but I would + # recommend against using it, as most tooling does not pass it to + # compilers. Instead, until we standardize a site-specific include + # directory, I would recommend installing headers as package data, + # and providing some sort of API to get the include directories. + # Example: https://numpy.org/doc/2.1/reference/generated/numpy.get_include.html + incpath = os.path.join(env_dir, 'Include' if os.name == 'nt' else 'include') + context.inc_path = incpath create_if_needed(incpath) context.lib_path = libpath From 0cbc19d59e409854f2b9bdda75e1af2b6cd89ac2 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Thu, 12 Dec 2024 22:43:44 +0100 Subject: [PATCH 009/376] Fix typo in traceback docs (#127884) --- Doc/library/traceback.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst index 4899ed64ebad8d..b0ee3fc56ad735 100644 --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -274,7 +274,7 @@ Module-Level Functions :class:`!TracebackException` objects are created from actual exceptions to capture data for later printing. They offer a more lightweight method of storing this information by avoiding holding references to -:ref:`traceback` and :ref:`frame` objects +:ref:`traceback` and :ref:`frame` objects. In addition, they expose more options to configure the output compared to the module-level functions described above. From ba2d2fda93a03a91ac6cdff319fd23ef51848d51 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 13 Dec 2024 05:49:02 +0800 Subject: [PATCH 010/376] gh-127845: Minor improvements to iOS test runner script (#127846) Uses symlinks to install iOS framework into testbed clone, adds a verbose mode to the iOS runner to hide most Xcode output, adds another mechanism to disable terminal colors, and ensures that stdout is flushed after every write. --- Makefile.pre.in | 2 +- iOS/testbed/__main__.py | 66 ++++++++++++++----- iOS/testbed/iOSTestbedTests/iOSTestbedTests.m | 5 +- 3 files changed, 53 insertions(+), 20 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 7b66802147dc3a..3e880f7800fccf 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2169,7 +2169,7 @@ testios: $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER)" # Run the testbed project - $(PYTHON_FOR_BUILD) "$(XCFOLDER)" run -- test -uall --single-process --rerun -W + $(PYTHON_FOR_BUILD) "$(XCFOLDER)" run --verbose -- test -uall --single-process --rerun -W # Like test, but using --slow-ci which enables all test resources and use # longer timeout. Run an optional pybuildbot.identify script to include diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py index 22570ee0f3ed04..068272835a5b95 100644 --- a/iOS/testbed/__main__.py +++ b/iOS/testbed/__main__.py @@ -141,10 +141,12 @@ async def log_stream_task(initial_devices): else: suppress_dupes = False sys.stdout.write(line) + sys.stdout.flush() -async def xcode_test(location, simulator): +async def xcode_test(location, simulator, verbose): # Run the test suite on the named simulator + print("Starting xcodebuild...") args = [ "xcodebuild", "test", @@ -159,6 +161,9 @@ async def xcode_test(location, simulator): "-derivedDataPath", str(location / "DerivedData"), ] + if not verbose: + args += ["-quiet"] + async with async_process( *args, stdout=subprocess.PIPE, @@ -166,6 +171,7 @@ async def xcode_test(location, simulator): ) as process: while line := (await process.stdout.readline()).decode(*DECODE_ARGS): sys.stdout.write(line) + sys.stdout.flush() status = await asyncio.wait_for(process.wait(), timeout=1) exit(status) @@ -182,7 +188,9 @@ def clone_testbed( sys.exit(10) if framework is None: - if not (source / "Python.xcframework/ios-arm64_x86_64-simulator/bin").is_dir(): + if not ( + source / "Python.xcframework/ios-arm64_x86_64-simulator/bin" + ).is_dir(): print( f"The testbed being cloned ({source}) does not contain " f"a simulator framework. Re-run with --framework" @@ -202,33 +210,48 @@ def clone_testbed( ) sys.exit(13) - print("Cloning testbed project...") - shutil.copytree(source, target) + print("Cloning testbed project:") + print(f" Cloning {source}...", end="", flush=True) + shutil.copytree(source, target, symlinks=True) + print(" done") if framework is not None: if framework.suffix == ".xcframework": - print("Installing XCFramework...") - xc_framework_path = target / "Python.xcframework" - shutil.rmtree(xc_framework_path) - shutil.copytree(framework, xc_framework_path) + print(" Installing XCFramework...", end="", flush=True) + xc_framework_path = (target / "Python.xcframework").resolve() + if xc_framework_path.is_dir(): + shutil.rmtree(xc_framework_path) + else: + xc_framework_path.unlink() + xc_framework_path.symlink_to( + framework.relative_to(xc_framework_path.parent, walk_up=True) + ) + print(" done") else: - print("Installing simulator Framework...") + print(" Installing simulator framework...", end="", flush=True) sim_framework_path = ( target / "Python.xcframework" / "ios-arm64_x86_64-simulator" + ).resolve() + if sim_framework_path.is_dir(): + shutil.rmtree(sim_framework_path) + else: + sim_framework_path.unlink() + sim_framework_path.symlink_to( + framework.relative_to(sim_framework_path.parent, walk_up=True) ) - shutil.rmtree(sim_framework_path) - shutil.copytree(framework, sim_framework_path) + print(" done") else: - print("Using pre-existing iOS framework.") + print(" Using pre-existing iOS framework.") for app_src in apps: - print(f"Installing app {app_src.name!r}...") + print(f" Installing app {app_src.name!r}...", end="", flush=True) app_target = target / f"iOSTestbed/app/{app_src.name}" if app_target.is_dir(): shutil.rmtree(app_target) shutil.copytree(app_src, app_target) + print(" done") - print(f"Testbed project created in {target}") + print(f"Successfully cloned testbed: {target.resolve()}") def update_plist(testbed_path, args): @@ -243,10 +266,11 @@ def update_plist(testbed_path, args): plistlib.dump(info, f) -async def run_testbed(simulator: str, args: list[str]): +async def run_testbed(simulator: str, args: list[str], verbose: bool=False): location = Path(__file__).parent - print("Updating plist...") + print("Updating plist...", end="", flush=True) update_plist(location, args) + print(" done.") # Get the list of devices that are booted at the start of the test run. # The simulator started by the test suite will be detected as the new @@ -256,7 +280,7 @@ async def run_testbed(simulator: str, args: list[str]): try: async with asyncio.TaskGroup() as tg: tg.create_task(log_stream_task(initial_devices)) - tg.create_task(xcode_test(location, simulator)) + tg.create_task(xcode_test(location, simulator=simulator, verbose=verbose)) except* MySystemExit as e: raise SystemExit(*e.exceptions[0].args) from None except* subprocess.CalledProcessError as e: @@ -315,6 +339,11 @@ def main(): default="iPhone SE (3rd Generation)", help="The name of the simulator to use (default: 'iPhone SE (3rd Generation)')", ) + run.add_argument( + "-v", "--verbose", + action="store_true", + help="Enable verbose output", + ) try: pos = sys.argv.index("--") @@ -330,7 +359,7 @@ def main(): clone_testbed( source=Path(__file__).parent, target=Path(context.location), - framework=Path(context.framework) if context.framework else None, + framework=Path(context.framework).resolve() if context.framework else None, apps=[Path(app) for app in context.apps], ) elif context.subcommand == "run": @@ -348,6 +377,7 @@ def main(): asyncio.run( run_testbed( simulator=context.simulator, + verbose=context.verbose, args=test_args, ) ) diff --git a/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m index ac78456a61e65e..6db38253396c8d 100644 --- a/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m +++ b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m @@ -24,8 +24,11 @@ - (void)testPython { NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; - // Disable all color, as the Xcode log can't display color + // Set some other common environment indicators to disable color, as the + // Xcode log can't display color. Stdout will report that it is *not* a + // TTY. setenv("NO_COLOR", "1", true); + setenv("PY_COLORS", "0", true); // Arguments to pass into the test suite runner. // argv[0] must identify the process; any subsequent arg From 58942a07df8811afba9c58dc16c1aab244ccf27a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Fri, 13 Dec 2024 10:26:22 +0100 Subject: [PATCH 011/376] Document PyObject_SelfIter (#127861) --- Doc/c-api/object.rst | 6 ++++++ Doc/data/refcounts.dat | 3 +++ 2 files changed, 9 insertions(+) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 1ae3c46bea46ea..f97ade01e67850 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -509,6 +509,12 @@ Object Protocol iterated. +.. c:function:: PyObject* PyObject_SelfIter(PyObject *obj) + + This is equivalent to the Python ``__iter__(self): return self`` method. + It is intended for :term:`iterator` types, to be used in the :c:member:`PyTypeObject.tp_iter` slot. + + .. c:function:: PyObject* PyObject_GetAIter(PyObject *o) This is the equivalent to the Python expression ``aiter(o)``. Takes an diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index 3f49c88c3cc028..a043af48ba7a05 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -1849,6 +1849,9 @@ PyObject_RichCompareBool:PyObject*:o1:0: PyObject_RichCompareBool:PyObject*:o2:0: PyObject_RichCompareBool:int:opid:: +PyObject_SelfIter:PyObject*::+1: +PyObject_SelfIter:PyObject*:obj:0: + PyObject_SetAttr:int::: PyObject_SetAttr:PyObject*:o:0: PyObject_SetAttr:PyObject*:attr_name:0: From 11ff3286b7e821bf439bc7caa0fa712e3bc3846a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20K=C3=A1lm=C3=A1n?= Date: Fri, 13 Dec 2024 10:27:02 +0100 Subject: [PATCH 012/376] link to the correct output method in documentation (#127857) --- Doc/library/http.cookies.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/http.cookies.rst b/Doc/library/http.cookies.rst index 4ce2e3c4f4cb42..ad37a0fca4742d 100644 --- a/Doc/library/http.cookies.rst +++ b/Doc/library/http.cookies.rst @@ -98,7 +98,7 @@ Cookie Objects .. method:: BaseCookie.output(attrs=None, header='Set-Cookie:', sep='\r\n') Return a string representation suitable to be sent as HTTP headers. *attrs* and - *header* are sent to each :class:`Morsel`'s :meth:`output` method. *sep* is used + *header* are sent to each :class:`Morsel`'s :meth:`~Morsel.output` method. *sep* is used to join the headers together, and is by default the combination ``'\r\n'`` (CRLF). From 9b4bbf4401291636e5db90511a0548fffb23a505 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 13 Dec 2024 09:54:59 +0000 Subject: [PATCH 013/376] GH-125174: Don't use `UINT32_MAX` in header file (GH-127863) --- Include/refcount.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/refcount.h b/Include/refcount.h index 6908c426141378..d98b2dfcf37202 100644 --- a/Include/refcount.h +++ b/Include/refcount.h @@ -377,7 +377,7 @@ static inline void Py_DECREF(const char *filename, int lineno, PyObject *op) #if SIZEOF_VOID_P > 4 /* If an object has been freed, it will have a negative full refcnt * If it has not it been freed, will have a very large refcnt */ - if (op->ob_refcnt_full <= 0 || op->ob_refcnt > (UINT32_MAX - (1<<20))) { + if (op->ob_refcnt_full <= 0 || op->ob_refcnt > (((PY_UINT32_T)-1) - (1<<20))) { #else if (op->ob_refcnt <= 0) { #endif From 5fc6bb2754a25157575efc0b37da78c629fea46e Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Fri, 13 Dec 2024 11:06:26 +0100 Subject: [PATCH 014/376] gh-126868: Add freelist for compact int objects (GH-126865) --- Include/internal/pycore_freelist_state.h | 2 + Include/internal/pycore_long.h | 2 + ...-11-16-22-37-46.gh-issue-126868.yOoHSY.rst | 1 + Objects/longobject.c | 78 ++++++++++++++----- Objects/object.c | 1 + Python/bytecodes.c | 25 +++--- Python/executor_cases.c.h | 24 +++--- Python/generated_cases.c.h | 24 +++--- 8 files changed, 102 insertions(+), 55 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-16-22-37-46.gh-issue-126868.yOoHSY.rst diff --git a/Include/internal/pycore_freelist_state.h b/Include/internal/pycore_freelist_state.h index 4e04cf431e0b31..a1a94c1f2dc880 100644 --- a/Include/internal/pycore_freelist_state.h +++ b/Include/internal/pycore_freelist_state.h @@ -14,6 +14,7 @@ extern "C" { # define Py_dicts_MAXFREELIST 80 # define Py_dictkeys_MAXFREELIST 80 # define Py_floats_MAXFREELIST 100 +# define Py_ints_MAXFREELIST 100 # define Py_slices_MAXFREELIST 1 # define Py_contexts_MAXFREELIST 255 # define Py_async_gens_MAXFREELIST 80 @@ -35,6 +36,7 @@ struct _Py_freelist { struct _Py_freelists { struct _Py_freelist floats; + struct _Py_freelist ints; struct _Py_freelist tuples[PyTuple_MAXSAVESIZE]; struct _Py_freelist lists; struct _Py_freelist dicts; diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index 196b4152280a35..8bead00e70640c 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -55,6 +55,8 @@ extern void _PyLong_FiniTypes(PyInterpreterState *interp); /* other API */ +PyAPI_FUNC(void) _PyLong_ExactDealloc(PyObject *self); + #define _PyLong_SMALL_INTS _Py_SINGLETON(small_ints) // _PyLong_GetZero() and _PyLong_GetOne() must always be available diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-16-22-37-46.gh-issue-126868.yOoHSY.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-16-22-37-46.gh-issue-126868.yOoHSY.rst new file mode 100644 index 00000000000000..fd1570908c1fd6 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-16-22-37-46.gh-issue-126868.yOoHSY.rst @@ -0,0 +1 @@ +Increase performance of :class:`int` by adding a freelist for compact ints. diff --git a/Objects/longobject.c b/Objects/longobject.c index 4aa35685b509f2..96d59f542a7c3c 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -6,6 +6,7 @@ #include "pycore_bitutils.h" // _Py_popcount32() #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_call.h" // _PyObject_MakeTpCall +#include "pycore_freelist.h" // _Py_FREELIST_FREE, _Py_FREELIST_POP #include "pycore_long.h" // _Py_SmallInts #include "pycore_object.h" // _PyObject_Init() #include "pycore_runtime.h" // _PY_NSMALLPOSINTS @@ -42,7 +43,7 @@ static inline void _Py_DECREF_INT(PyLongObject *op) { assert(PyLong_CheckExact(op)); - _Py_DECREF_SPECIALIZED((PyObject *)op, (destructor)PyObject_Free); + _Py_DECREF_SPECIALIZED((PyObject *)op, _PyLong_ExactDealloc); } static inline int @@ -220,15 +221,18 @@ _PyLong_FromMedium(sdigit x) { assert(!IS_SMALL_INT(x)); assert(is_medium_int(x)); - /* We could use a freelist here */ - PyLongObject *v = PyObject_Malloc(sizeof(PyLongObject)); + + PyLongObject *v = (PyLongObject *)_Py_FREELIST_POP(PyLongObject, ints); if (v == NULL) { - PyErr_NoMemory(); - return NULL; + v = PyObject_Malloc(sizeof(PyLongObject)); + if (v == NULL) { + PyErr_NoMemory(); + return NULL; + } + _PyObject_Init((PyObject*)v, &PyLong_Type); } digit abs_x = x < 0 ? -x : x; _PyLong_SetSignAndDigitCount(v, x<0?-1:1, 1); - _PyObject_Init((PyObject*)v, &PyLong_Type); v->long_value.ob_digit[0] = abs_x; return (PyObject*)v; } @@ -3611,24 +3615,60 @@ long_richcompare(PyObject *self, PyObject *other, int op) Py_RETURN_RICHCOMPARE(result, 0, op); } +static inline int +compact_int_is_small(PyObject *self) +{ + PyLongObject *pylong = (PyLongObject *)self; + assert(_PyLong_IsCompact(pylong)); + stwodigits ival = medium_value(pylong); + if (IS_SMALL_INT(ival)) { + PyLongObject *small_pylong = (PyLongObject *)get_small_int((sdigit)ival); + if (pylong == small_pylong) { + return 1; + } + } + return 0; +} + +void +_PyLong_ExactDealloc(PyObject *self) +{ + assert(PyLong_CheckExact(self)); + if (_PyLong_IsCompact((PyLongObject *)self)) { + #ifndef Py_GIL_DISABLED + if (compact_int_is_small(self)) { + // See PEP 683, section Accidental De-Immortalizing for details + _Py_SetImmortal(self); + return; + } + #endif + _Py_FREELIST_FREE(ints, self, PyObject_Free); + return; + } + PyObject_Free(self); +} + static void long_dealloc(PyObject *self) { - /* This should never get called, but we also don't want to SEGV if - * we accidentally decref small Ints out of existence. Instead, - * since small Ints are immortal, re-set the reference count. - */ - PyLongObject *pylong = (PyLongObject*)self; - if (pylong && _PyLong_IsCompact(pylong)) { - stwodigits ival = medium_value(pylong); - if (IS_SMALL_INT(ival)) { - PyLongObject *small_pylong = (PyLongObject *)get_small_int((sdigit)ival); - if (pylong == small_pylong) { - _Py_SetImmortal(self); - return; - } + assert(self); + if (_PyLong_IsCompact((PyLongObject *)self)) { + if (compact_int_is_small(self)) { + /* This should never get called, but we also don't want to SEGV if + * we accidentally decref small Ints out of existence. Instead, + * since small Ints are immortal, re-set the reference count. + * + * See PEP 683, section Accidental De-Immortalizing for details + */ + _Py_SetImmortal(self); + return; + } + if (PyLong_CheckExact(self)) { + _Py_FREELIST_FREE(ints, self, PyObject_Free); + return; } } + Py_TYPE(self)->tp_free(self); } diff --git a/Objects/object.c b/Objects/object.c index c64675b5e1d6c2..d584414c559b9d 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -936,6 +936,7 @@ _PyObject_ClearFreeLists(struct _Py_freelists *freelists, int is_finalization) clear_freelist(&freelists->object_stack_chunks, 1, PyMem_RawFree); } clear_freelist(&freelists->unicode_writers, is_finalization, PyMem_Free); + clear_freelist(&freelists->ints, is_finalization, free_object); } /* diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d0e4c2bc45489b..f0eb5405faeff5 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -26,6 +26,7 @@ #include "pycore_pyerrors.h" // _PyErr_GetRaisedException() #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_range.h" // _PyRangeIterObject +#include "pycore_long.h" // _PyLong_ExactDealloc() #include "pycore_setobject.h" // _PySet_NextEntry() #include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs #include "pycore_tuple.h" // _PyTuple_ITEMS() @@ -514,8 +515,8 @@ dummy_func( STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Multiply((PyLongObject *)left_o, (PyLongObject *)right_o); - PyStackRef_CLOSE_SPECIALIZED(right, (destructor)PyObject_Free); - PyStackRef_CLOSE_SPECIALIZED(left, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); INPUTS_DEAD(); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); @@ -527,8 +528,8 @@ dummy_func( STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Add((PyLongObject *)left_o, (PyLongObject *)right_o); - PyStackRef_CLOSE_SPECIALIZED(right, (destructor)PyObject_Free); - PyStackRef_CLOSE_SPECIALIZED(left, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); INPUTS_DEAD(); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); @@ -540,8 +541,8 @@ dummy_func( STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); - PyStackRef_CLOSE_SPECIALIZED(right, (destructor)PyObject_Free); - PyStackRef_CLOSE_SPECIALIZED(left, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); INPUTS_DEAD(); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); @@ -801,7 +802,7 @@ dummy_func( assert(res_o != NULL); Py_INCREF(res_o); #endif - PyStackRef_CLOSE_SPECIALIZED(sub_st, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); DEAD(sub_st); PyStackRef_CLOSE(list_st); res = PyStackRef_FromPyObjectSteal(res_o); @@ -821,7 +822,7 @@ dummy_func( DEOPT_IF(Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c); STAT_INC(BINARY_SUBSCR, hit); PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; - PyStackRef_CLOSE_SPECIALIZED(sub_st, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); DEAD(sub_st); PyStackRef_CLOSE(str_st); res = PyStackRef_FromPyObjectSteal(res_o); @@ -842,7 +843,7 @@ dummy_func( PyObject *res_o = PyTuple_GET_ITEM(tuple, index); assert(res_o != NULL); Py_INCREF(res_o); - PyStackRef_CLOSE_SPECIALIZED(sub_st, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); DEAD(sub_st); PyStackRef_CLOSE(tuple_st); res = PyStackRef_FromPyObjectSteal(res_o); @@ -959,7 +960,7 @@ dummy_func( assert(old_value != NULL); UNLOCK_OBJECT(list); // unlock before decrefs! Py_DECREF(old_value); - PyStackRef_CLOSE_SPECIALIZED(sub_st, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); DEAD(sub_st); PyStackRef_CLOSE(list_st); } @@ -2476,9 +2477,9 @@ dummy_func( Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right_o); // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg int sign_ish = COMPARISON_BIT(ileft, iright); - PyStackRef_CLOSE_SPECIALIZED(left, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); DEAD(left); - PyStackRef_CLOSE_SPECIALIZED(right, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); DEAD(right); res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; // It's always a bool, so we don't care about oparg & 16. diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 18f19773d25c90..19ba67a8af6769 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -626,8 +626,8 @@ PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Multiply((PyLongObject *)left_o, (PyLongObject *)right_o); - PyStackRef_CLOSE_SPECIALIZED(right, (destructor)PyObject_Free); - PyStackRef_CLOSE_SPECIALIZED(left, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); if (res_o == NULL) JUMP_TO_ERROR(); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; @@ -646,8 +646,8 @@ PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Add((PyLongObject *)left_o, (PyLongObject *)right_o); - PyStackRef_CLOSE_SPECIALIZED(right, (destructor)PyObject_Free); - PyStackRef_CLOSE_SPECIALIZED(left, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); if (res_o == NULL) JUMP_TO_ERROR(); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; @@ -666,8 +666,8 @@ PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); - PyStackRef_CLOSE_SPECIALIZED(right, (destructor)PyObject_Free); - PyStackRef_CLOSE_SPECIALIZED(left, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); if (res_o == NULL) JUMP_TO_ERROR(); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; @@ -1000,7 +1000,7 @@ assert(res_o != NULL); Py_INCREF(res_o); #endif - PyStackRef_CLOSE_SPECIALIZED(sub_st, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); PyStackRef_CLOSE(list_st); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; @@ -1042,7 +1042,7 @@ } STAT_INC(BINARY_SUBSCR, hit); PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; - PyStackRef_CLOSE_SPECIALIZED(sub_st, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); PyStackRef_CLOSE(str_st); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; @@ -1081,7 +1081,7 @@ PyObject *res_o = PyTuple_GET_ITEM(tuple, index); assert(res_o != NULL); Py_INCREF(res_o); - PyStackRef_CLOSE_SPECIALIZED(sub_st, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); PyStackRef_CLOSE(tuple_st); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; @@ -1264,7 +1264,7 @@ assert(old_value != NULL); UNLOCK_OBJECT(list); // unlock before decrefs! Py_DECREF(old_value); - PyStackRef_CLOSE_SPECIALIZED(sub_st, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); PyStackRef_CLOSE(list_st); stack_pointer += -3; assert(WITHIN_STACK_BOUNDS()); @@ -3075,8 +3075,8 @@ Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right_o); // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg int sign_ish = COMPARISON_BIT(ileft, iright); - PyStackRef_CLOSE_SPECIALIZED(left, (destructor)PyObject_Free); - PyStackRef_CLOSE_SPECIALIZED(right, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; // It's always a bool, so we don't care about oparg & 16. stack_pointer[-2] = res; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index fc0f55555f5c36..51227c9868b8cc 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -118,8 +118,8 @@ PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Add((PyLongObject *)left_o, (PyLongObject *)right_o); - PyStackRef_CLOSE_SPECIALIZED(right, (destructor)PyObject_Free); - PyStackRef_CLOSE_SPECIALIZED(left, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); if (res_o == NULL) goto pop_2_error; res = PyStackRef_FromPyObjectSteal(res_o); } @@ -285,8 +285,8 @@ PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Multiply((PyLongObject *)left_o, (PyLongObject *)right_o); - PyStackRef_CLOSE_SPECIALIZED(right, (destructor)PyObject_Free); - PyStackRef_CLOSE_SPECIALIZED(left, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); if (res_o == NULL) goto pop_2_error; res = PyStackRef_FromPyObjectSteal(res_o); } @@ -356,8 +356,8 @@ PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); - PyStackRef_CLOSE_SPECIALIZED(right, (destructor)PyObject_Free); - PyStackRef_CLOSE_SPECIALIZED(left, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); if (res_o == NULL) goto pop_2_error; res = PyStackRef_FromPyObjectSteal(res_o); } @@ -590,7 +590,7 @@ assert(res_o != NULL); Py_INCREF(res_o); #endif - PyStackRef_CLOSE_SPECIALIZED(sub_st, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); PyStackRef_CLOSE(list_st); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; @@ -622,7 +622,7 @@ DEOPT_IF(Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c, BINARY_SUBSCR); STAT_INC(BINARY_SUBSCR, hit); PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; - PyStackRef_CLOSE_SPECIALIZED(sub_st, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); PyStackRef_CLOSE(str_st); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; @@ -654,7 +654,7 @@ PyObject *res_o = PyTuple_GET_ITEM(tuple, index); assert(res_o != NULL); Py_INCREF(res_o); - PyStackRef_CLOSE_SPECIALIZED(sub_st, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); PyStackRef_CLOSE(tuple_st); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; @@ -3333,8 +3333,8 @@ Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right_o); // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg int sign_ish = COMPARISON_BIT(ileft, iright); - PyStackRef_CLOSE_SPECIALIZED(left, (destructor)PyObject_Free); - PyStackRef_CLOSE_SPECIALIZED(right, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; // It's always a bool, so we don't care about oparg & 16. } @@ -7721,7 +7721,7 @@ assert(old_value != NULL); UNLOCK_OBJECT(list); // unlock before decrefs! Py_DECREF(old_value); - PyStackRef_CLOSE_SPECIALIZED(sub_st, (destructor)PyObject_Free); + PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); PyStackRef_CLOSE(list_st); stack_pointer += -3; assert(WITHIN_STACK_BOUNDS()); From e62e1ca4553dbcf9d7f89be24bebcbd9213f9ae5 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 13 Dec 2024 11:00:00 +0000 Subject: [PATCH 015/376] GH-126833: Dumps graphviz representation of executor graph. (GH-126880) --- .../pycore_global_objects_fini_generated.h | 1 + Include/internal/pycore_global_strings.h | 1 + Include/internal/pycore_optimizer.h | 5 + .../internal/pycore_runtime_init_generated.h | 1 + .../internal/pycore_unicodeobject_generated.h | 4 + Python/ceval.c | 1 + Python/clinic/sysmodule.c.h | 58 +++++++- Python/optimizer.c | 136 +++++++++++++++++- Python/sysmodule.c | 25 ++++ 9 files changed, 230 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index c12e242d560bde..90214a314031d1 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -1129,6 +1129,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(origin)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(out_fd)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(outgoing)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(outpath)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(overlapped)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(owner)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(pages)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index dfd9f2b799ec8e..97a75d0c46c867 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -618,6 +618,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(origin) STRUCT_FOR_ID(out_fd) STRUCT_FOR_ID(outgoing) + STRUCT_FOR_ID(outpath) STRUCT_FOR_ID(overlapped) STRUCT_FOR_ID(owner) STRUCT_FOR_ID(pages) diff --git a/Include/internal/pycore_optimizer.h b/Include/internal/pycore_optimizer.h index 6d70b42f708854..bc7cfcde613d65 100644 --- a/Include/internal/pycore_optimizer.h +++ b/Include/internal/pycore_optimizer.h @@ -60,6 +60,9 @@ typedef struct { }; uint64_t operand0; // A cache entry uint64_t operand1; +#ifdef Py_STATS + uint64_t execution_count; +#endif } _PyUOpInstruction; typedef struct { @@ -285,6 +288,8 @@ static inline int is_terminator(const _PyUOpInstruction *uop) ); } +PyAPI_FUNC(int) _PyDumpExecutors(FILE *out); + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index b631382cae058a..4f928cc050bf8e 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -1127,6 +1127,7 @@ extern "C" { INIT_ID(origin), \ INIT_ID(out_fd), \ INIT_ID(outgoing), \ + INIT_ID(outpath), \ INIT_ID(overlapped), \ INIT_ID(owner), \ INIT_ID(pages), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 24cec3a4fded7a..5b78d038fc1192 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -2268,6 +2268,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(outpath); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(overlapped); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Python/ceval.c b/Python/ceval.c index 5eda033eced628..fd891d7839151e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1095,6 +1095,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int UOP_PAIR_INC(uopcode, lastuop); #ifdef Py_STATS trace_uop_execution_counter++; + ((_PyUOpInstruction *)next_uop)[-1].execution_count++; #endif switch (uopcode) { diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 86c42ceffc5e31..cfcbd55388efa0 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -1481,6 +1481,62 @@ sys_is_stack_trampoline_active(PyObject *module, PyObject *Py_UNUSED(ignored)) return sys_is_stack_trampoline_active_impl(module); } +PyDoc_STRVAR(sys__dump_tracelets__doc__, +"_dump_tracelets($module, /, outpath)\n" +"--\n" +"\n" +"Dump the graph of tracelets in graphviz format"); + +#define SYS__DUMP_TRACELETS_METHODDEF \ + {"_dump_tracelets", _PyCFunction_CAST(sys__dump_tracelets), METH_FASTCALL|METH_KEYWORDS, sys__dump_tracelets__doc__}, + +static PyObject * +sys__dump_tracelets_impl(PyObject *module, PyObject *outpath); + +static PyObject * +sys__dump_tracelets(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(outpath), }, + }; + #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[] = {"outpath", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_dump_tracelets", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *outpath; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + outpath = args[0]; + return_value = sys__dump_tracelets_impl(module, outpath); + +exit: + return return_value; +} + PyDoc_STRVAR(sys__getframemodulename__doc__, "_getframemodulename($module, /, depth=0)\n" "--\n" @@ -1668,4 +1724,4 @@ sys__is_gil_enabled(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=6d4f6cd20419b675 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=568b0a0069dc43e8 input=a9049054013a1b77]*/ diff --git a/Python/optimizer.c b/Python/optimizer.c index 6a232218981dcd..6a4d20fad76c15 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1,6 +1,7 @@ +#include "Python.h" + #ifdef _Py_TIER2 -#include "Python.h" #include "opcode.h" #include "pycore_interp.h" #include "pycore_backoff.h" @@ -474,6 +475,9 @@ add_to_trace( trace[trace_length].target = target; trace[trace_length].oparg = oparg; trace[trace_length].operand0 = operand; +#ifdef Py_STATS + trace[trace_length].execution_count = 0; +#endif return trace_length + 1; } @@ -983,6 +987,9 @@ static void make_exit(_PyUOpInstruction *inst, int opcode, int target) inst->operand0 = 0; inst->format = UOP_FORMAT_TARGET; inst->target = target; +#ifdef Py_STATS + inst->execution_count = 0; +#endif } /* Convert implicit exits, errors and deopts @@ -1709,4 +1716,131 @@ _Py_Executors_InvalidateCold(PyInterpreterState *interp) _Py_Executors_InvalidateAll(interp, 0); } +static void +write_str(PyObject *str, FILE *out) +{ + // Encode the Unicode object to the specified encoding + PyObject *encoded_obj = PyUnicode_AsEncodedString(str, "utf8", "strict"); + if (encoded_obj == NULL) { + PyErr_Clear(); + return; + } + const char *encoded_str = PyBytes_AsString(encoded_obj); + Py_ssize_t encoded_size = PyBytes_Size(encoded_obj); + fwrite(encoded_str, 1, encoded_size, out); + Py_DECREF(encoded_obj); +} + +static int +find_line_number(PyCodeObject *code, _PyExecutorObject *executor) +{ + int code_len = (int)Py_SIZE(code); + for (int i = 0; i < code_len; i++) { + _Py_CODEUNIT *instr = &_PyCode_CODE(code)[i]; + int opcode = instr->op.code; + if (opcode == ENTER_EXECUTOR) { + _PyExecutorObject *exec = code->co_executors->executors[instr->op.arg]; + if (exec == executor) { + return PyCode_Addr2Line(code, i*2); + } + } + i += _PyOpcode_Caches[_Py_GetBaseCodeUnit(code, i).op.code]; + } + return -1; +} + +/* Writes the node and outgoing edges for a single tracelet in graphviz format. + * Each tracelet is presented as a table of the uops it contains. + * If Py_STATS is enabled, execution counts are included. + * + * https://graphviz.readthedocs.io/en/stable/manual.html + * https://graphviz.org/gallery/ + */ +static void +executor_to_gv(_PyExecutorObject *executor, FILE *out) +{ + PyCodeObject *code = executor->vm_data.code; + fprintf(out, "executor_%p [\n", executor); + fprintf(out, " shape = none\n"); + + /* Write the HTML table for the uops */ + fprintf(out, " label = <\n"); + fprintf(out, " \n"); + if (code == NULL) { + fprintf(out, " \n"); + } + else { + fprintf(out, " \n", line); + } + for (uint32_t i = 0; i < executor->code_size; i++) { + /* Write row for uop. + * The `port` is a marker so that outgoing edges can + * be placed correctly. If a row is marked `port=17`, + * then the outgoing edge is `{EXEC_NAME}:17 -> {TARGET}` + * https://graphviz.readthedocs.io/en/stable/manual.html#node-ports-compass + */ + _PyUOpInstruction const *inst = &executor->trace[i]; + const char *opname = _PyOpcode_uop_name[inst->opcode]; +#ifdef Py_STATS + fprintf(out, " \n", i, opname, inst->execution_count); +#else + fprintf(out, " \n", i, opname); +#endif + if (inst->opcode == _EXIT_TRACE || inst->opcode == _JUMP_TO_TOP) { + break; + } + } + fprintf(out, "
Executor
No code object
"); + write_str(code->co_qualname, out); + int line = find_line_number(code, executor); + fprintf(out, ": %d
%s -- %" PRIu64 "
%s
>\n"); + fprintf(out, "]\n\n"); + + /* Write all the outgoing edges */ + for (uint32_t i = 0; i < executor->code_size; i++) { + _PyUOpInstruction const *inst = &executor->trace[i]; + uint16_t flags = _PyUop_Flags[inst->opcode]; + _PyExitData *exit = NULL; + if (inst->opcode == _EXIT_TRACE) { + exit = (_PyExitData *)inst->operand0; + } + else if (flags & HAS_EXIT_FLAG) { + assert(inst->format == UOP_FORMAT_JUMP); + _PyUOpInstruction const *exit_inst = &executor->trace[inst->jump_target]; + assert(exit_inst->opcode == _EXIT_TRACE); + exit = (_PyExitData *)exit_inst->operand0; + } + if (exit != NULL && exit->executor != NULL) { + fprintf(out, "executor_%p:i%d -> executor_%p:start\n", executor, i, exit->executor); + } + if (inst->opcode == _EXIT_TRACE || inst->opcode == _JUMP_TO_TOP) { + break; + } + } +} + +/* Write the graph of all the live tracelets in graphviz format. */ +int +_PyDumpExecutors(FILE *out) +{ + fprintf(out, "digraph ideal {\n\n"); + fprintf(out, " rankdir = \"LR\"\n\n"); + PyInterpreterState *interp = PyInterpreterState_Get(); + for (_PyExecutorObject *exec = interp->executor_list_head; exec != NULL;) { + executor_to_gv(exec, out); + exec = exec->vm_data.links.next; + } + fprintf(out, "}\n\n"); + return 0; +} + +#else + +int +_PyDumpExecutors(FILE *out) +{ + PyErr_SetString(PyExc_NotImplementedError, "No JIT available"); + return -1; +} + #endif /* _Py_TIER2 */ diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 6df297f364c5d3..d6719f9bb0af91 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2344,6 +2344,30 @@ sys_is_stack_trampoline_active_impl(PyObject *module) Py_RETURN_FALSE; } +/*[clinic input] +sys._dump_tracelets + + outpath: object + +Dump the graph of tracelets in graphviz format +[clinic start generated code]*/ + +static PyObject * +sys__dump_tracelets_impl(PyObject *module, PyObject *outpath) +/*[clinic end generated code: output=a7fe265e2bc3b674 input=5bff6880cd28ffd1]*/ +{ + FILE *out = _Py_fopen_obj(outpath, "wb"); + if (out == NULL) { + return NULL; + } + int err = _PyDumpExecutors(out); + fclose(out); + if (err) { + return NULL; + } + Py_RETURN_NONE; +} + /*[clinic input] sys._getframemodulename @@ -2603,6 +2627,7 @@ static PyMethodDef sys_methods[] = { #endif SYS__GET_CPU_COUNT_CONFIG_METHODDEF SYS__IS_GIL_ENABLED_METHODDEF + SYS__DUMP_TRACELETS_METHODDEF {NULL, NULL} // sentinel }; From 6ff38fc4e2af8e795dc791be6ea596d2146d4119 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 13 Dec 2024 13:53:47 +0100 Subject: [PATCH 016/376] gh-127870: Detect recursive calls in ctypes _as_parameter_ handling (#127872) --- Lib/test/test_ctypes/test_as_parameter.py | 12 ++++++++-- ...-12-12-16-59-42.gh-issue-127870._NFG-3.rst | 2 ++ Modules/_ctypes/_ctypes.c | 22 ++++++++++++++++++- 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-12-16-59-42.gh-issue-127870._NFG-3.rst diff --git a/Lib/test/test_ctypes/test_as_parameter.py b/Lib/test/test_ctypes/test_as_parameter.py index cc62b1a22a3b06..c5e1840b0eb7af 100644 --- a/Lib/test/test_ctypes/test_as_parameter.py +++ b/Lib/test/test_ctypes/test_as_parameter.py @@ -198,8 +198,16 @@ class A: a = A() a._as_parameter_ = a - with self.assertRaises(RecursionError): - c_int.from_param(a) + for c_type in ( + ctypes.c_wchar_p, + ctypes.c_char_p, + ctypes.c_void_p, + ctypes.c_int, # PyCSimpleType + POINT, # CDataType + ): + with self.subTest(c_type=c_type): + with self.assertRaises(RecursionError): + c_type.from_param(a) class AsParamWrapper: diff --git a/Misc/NEWS.d/next/Library/2024-12-12-16-59-42.gh-issue-127870._NFG-3.rst b/Misc/NEWS.d/next/Library/2024-12-12-16-59-42.gh-issue-127870._NFG-3.rst new file mode 100644 index 00000000000000..99b2df00032082 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-12-16-59-42.gh-issue-127870._NFG-3.rst @@ -0,0 +1,2 @@ +Detect recursive calls in ctypes ``_as_parameter_`` handling. +Patch by Victor Stinner. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 34529bce496d88..bb4699884057ba 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1052,8 +1052,13 @@ CDataType_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) return NULL; } if (as_parameter) { + if (_Py_EnterRecursiveCall(" while processing _as_parameter_")) { + Py_DECREF(as_parameter); + return NULL; + } value = CDataType_from_param_impl(type, cls, as_parameter); Py_DECREF(as_parameter); + _Py_LeaveRecursiveCall(); return value; } PyErr_Format(PyExc_TypeError, @@ -1843,8 +1848,13 @@ c_wchar_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) return NULL; } if (as_parameter) { + if (_Py_EnterRecursiveCall(" while processing _as_parameter_")) { + Py_DECREF(as_parameter); + return NULL; + } value = c_wchar_p_from_param_impl(type, cls, as_parameter); Py_DECREF(as_parameter); + _Py_LeaveRecursiveCall(); return value; } PyErr_Format(PyExc_TypeError, @@ -1927,8 +1937,13 @@ c_char_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) return NULL; } if (as_parameter) { + if (_Py_EnterRecursiveCall(" while processing _as_parameter_")) { + Py_DECREF(as_parameter); + return NULL; + } value = c_char_p_from_param_impl(type, cls, as_parameter); Py_DECREF(as_parameter); + _Py_LeaveRecursiveCall(); return value; } PyErr_Format(PyExc_TypeError, @@ -2079,8 +2094,13 @@ c_void_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) return NULL; } if (as_parameter) { + if (_Py_EnterRecursiveCall(" while processing _as_parameter_")) { + Py_DECREF(as_parameter); + return NULL; + } value = c_void_p_from_param_impl(type, cls, as_parameter); Py_DECREF(as_parameter); + _Py_LeaveRecursiveCall(); return value; } PyErr_Format(PyExc_TypeError, @@ -2447,9 +2467,9 @@ PyCSimpleType_from_param_impl(PyObject *type, PyTypeObject *cls, return NULL; } value = PyCSimpleType_from_param_impl(type, cls, as_parameter); - _Py_LeaveRecursiveCall(); Py_DECREF(as_parameter); Py_XDECREF(exc); + _Py_LeaveRecursiveCall(); return value; } if (exc) { From d05a4e6a0d366b854a3103cae0c941811fd48c4c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 13 Dec 2024 14:23:20 +0100 Subject: [PATCH 017/376] gh-127906: Test the limited C API in test_cppext (#127916) --- Lib/test/test_cppext/__init__.py | 13 ++++++++++--- Lib/test/test_cppext/extension.cpp | 9 +++++++++ Lib/test/test_cppext/setup.py | 6 ++++++ .../2024-12-13-13-41-34.gh-issue-127906.NuRHlB.rst | 1 + 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2024-12-13-13-41-34.gh-issue-127906.NuRHlB.rst diff --git a/Lib/test/test_cppext/__init__.py b/Lib/test/test_cppext/__init__.py index efd79448c66104..d5195227308fec 100644 --- a/Lib/test/test_cppext/__init__.py +++ b/Lib/test/test_cppext/__init__.py @@ -41,12 +41,17 @@ def test_build_cpp11(self): def test_build_cpp14(self): self.check_build('_testcpp14ext', std='c++14') - def check_build(self, extension_name, std=None): + @support.requires_gil_enabled('incompatible with Free Threading') + def test_build_limited(self): + self.check_build('_testcppext_limited', limited=True) + + def check_build(self, extension_name, std=None, limited=False): venv_dir = 'env' with support.setup_venv_with_pip_setuptools_wheel(venv_dir) as python_exe: - self._check_build(extension_name, python_exe, std=std) + self._check_build(extension_name, python_exe, + std=std, limited=limited) - def _check_build(self, extension_name, python_exe, std): + def _check_build(self, extension_name, python_exe, std, limited): pkg_dir = 'pkg' os.mkdir(pkg_dir) shutil.copy(SETUP, os.path.join(pkg_dir, os.path.basename(SETUP))) @@ -56,6 +61,8 @@ def run_cmd(operation, cmd): env = os.environ.copy() if std: env['CPYTHON_TEST_CPP_STD'] = std + if limited: + env['CPYTHON_TEST_LIMITED'] = '1' env['CPYTHON_TEST_EXT_NAME'] = extension_name if support.verbose: print('Run:', ' '.join(map(shlex.quote, cmd))) diff --git a/Lib/test/test_cppext/extension.cpp b/Lib/test/test_cppext/extension.cpp index ab485b629b7788..500d5918145c00 100644 --- a/Lib/test/test_cppext/extension.cpp +++ b/Lib/test/test_cppext/extension.cpp @@ -62,6 +62,7 @@ test_api_casts(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) Py_ssize_t refcnt = Py_REFCNT(obj); assert(refcnt >= 1); +#ifndef Py_LIMITED_API // gh-92138: For backward compatibility, functions of Python C API accepts // "const PyObject*". Check that using it does not emit C++ compiler // warnings. @@ -74,6 +75,7 @@ test_api_casts(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) assert(PyTuple_GET_SIZE(const_obj) == 2); PyObject *one = PyTuple_GET_ITEM(const_obj, 0); assert(PyLong_AsLong(one) == 1); +#endif // gh-92898: StrongRef doesn't inherit from PyObject but has an operator to // cast to PyObject*. @@ -106,6 +108,12 @@ test_unicode(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) } assert(PyUnicode_Check(str)); + + assert(PyUnicode_GetLength(str) == 3); + assert(PyUnicode_ReadChar(str, 0) == 'a'); + assert(PyUnicode_ReadChar(str, 1) == 'b'); + +#ifndef Py_LIMITED_API assert(PyUnicode_GET_LENGTH(str) == 3); // gh-92800: test PyUnicode_READ() @@ -121,6 +129,7 @@ test_unicode(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) assert(PyUnicode_READ(ukind, const_data, 2) == 'c'); assert(PyUnicode_READ_CHAR(str, 1) == 'b'); +#endif Py_DECREF(str); Py_RETURN_NONE; diff --git a/Lib/test/test_cppext/setup.py b/Lib/test/test_cppext/setup.py index d97b238b8d1477..019ff18446a2eb 100644 --- a/Lib/test/test_cppext/setup.py +++ b/Lib/test/test_cppext/setup.py @@ -33,6 +33,7 @@ def main(): cppflags = list(CPPFLAGS) std = os.environ.get("CPYTHON_TEST_CPP_STD", "") module_name = os.environ["CPYTHON_TEST_EXT_NAME"] + limited = bool(os.environ.get("CPYTHON_TEST_LIMITED", "")) cppflags = list(CPPFLAGS) cppflags.append(f'-DMODULE_NAME={module_name}') @@ -59,6 +60,11 @@ def main(): # CC env var overrides sysconfig CC variable in setuptools os.environ['CC'] = cmd + # Define Py_LIMITED_API macro + if limited: + version = sys.hexversion + cppflags.append(f'-DPy_LIMITED_API={version:#x}') + # On Windows, add PCbuild\amd64\ to include and library directories include_dirs = [] library_dirs = [] diff --git a/Misc/NEWS.d/next/Tests/2024-12-13-13-41-34.gh-issue-127906.NuRHlB.rst b/Misc/NEWS.d/next/Tests/2024-12-13-13-41-34.gh-issue-127906.NuRHlB.rst new file mode 100644 index 00000000000000..6f577e741dff7f --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2024-12-13-13-41-34.gh-issue-127906.NuRHlB.rst @@ -0,0 +1 @@ +Test the limited C API in test_cppext. Patch by Victor Stinner. From 6446408d426814bf2bc9d3911a91741f04d4bc4e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 13 Dec 2024 14:24:48 +0100 Subject: [PATCH 018/376] gh-102471, PEP 757: Add PyLong import and export API (#121339) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sergey B Kirpichev Co-authored-by: Steve Dower Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/c-api/long.rst | 174 ++++++++++++++++++ Doc/data/refcounts.dat | 7 + Doc/whatsnew/3.14.rst | 11 ++ Include/cpython/longintrepr.h | 38 ++++ Lib/test/test_capi/test_long.py | 91 +++++++++ ...-07-03-17-26-53.gh-issue-102471.XpmKYk.rst | 10 + Modules/_testcapi/long.c | 124 +++++++++++++ Objects/longobject.c | 120 ++++++++++++ Tools/c-analyzer/cpython/ignored.tsv | 1 + 9 files changed, 576 insertions(+) create mode 100644 Misc/NEWS.d/next/C_API/2024-07-03-17-26-53.gh-issue-102471.XpmKYk.rst diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index cb12d43d92026f..f48cd07a979f56 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -653,3 +653,177 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. versionadded:: 3.12 + +Export API +^^^^^^^^^^ + +.. versionadded:: next + +.. c:struct:: PyLongLayout + + Layout of an array of "digits" ("limbs" in the GMP terminology), used to + represent absolute value for arbitrary precision integers. + + Use :c:func:`PyLong_GetNativeLayout` to get the native layout of Python + :class:`int` objects, used internally for integers with "big enough" + absolute value. + + See also :data:`sys.int_info` which exposes similar information in Python. + + .. c:member:: uint8_t bits_per_digit + + Bits per digit. For example, a 15 bit digit means that bits 0-14 contain + meaningful information. + + .. c:member:: uint8_t digit_size + + Digit size in bytes. For example, a 15 bit digit will require at least 2 + bytes. + + .. c:member:: int8_t digits_order + + Digits order: + + - ``1`` for most significant digit first + - ``-1`` for least significant digit first + + .. c:member:: int8_t digit_endianness + + Digit endianness: + + - ``1`` for most significant byte first (big endian) + - ``-1`` for least significant byte first (little endian) + + +.. c:function:: const PyLongLayout* PyLong_GetNativeLayout(void) + + Get the native layout of Python :class:`int` objects. + + See the :c:struct:`PyLongLayout` structure. + + The function must not be called before Python initialization nor after + Python finalization. The returned layout is valid until Python is + finalized. The layout is the same for all Python sub-interpreters + in a process, and so it can be cached. + + +.. c:struct:: PyLongExport + + Export of a Python :class:`int` object. + + There are two cases: + + * If :c:member:`digits` is ``NULL``, only use the :c:member:`value` member. + * If :c:member:`digits` is not ``NULL``, use :c:member:`negative`, + :c:member:`ndigits` and :c:member:`digits` members. + + .. c:member:: int64_t value + + The native integer value of the exported :class:`int` object. + Only valid if :c:member:`digits` is ``NULL``. + + .. c:member:: uint8_t negative + + ``1`` if the number is negative, ``0`` otherwise. + Only valid if :c:member:`digits` is not ``NULL``. + + .. c:member:: Py_ssize_t ndigits + + Number of digits in :c:member:`digits` array. + Only valid if :c:member:`digits` is not ``NULL``. + + .. c:member:: const void *digits + + Read-only array of unsigned digits. Can be ``NULL``. + + +.. c:function:: int PyLong_Export(PyObject *obj, PyLongExport *export_long) + + Export a Python :class:`int` object. + + *export_long* must point to a :c:struct:`PyLongExport` structure allocated + by the caller. It must not be ``NULL``. + + On success, fill in *\*export_long* and return ``0``. + On error, set an exception and return ``-1``. + + :c:func:`PyLong_FreeExport` must be called when the export is no longer + needed. + + .. impl-detail:: + This function always succeeds if *obj* is a Python :class:`int` object + or a subclass. + + +.. c:function:: void PyLong_FreeExport(PyLongExport *export_long) + + Release the export *export_long* created by :c:func:`PyLong_Export`. + + .. impl-detail:: + Calling :c:func:`PyLong_FreeExport` is optional if *export_long->digits* + is ``NULL``. + + +PyLongWriter API +^^^^^^^^^^^^^^^^ + +The :c:type:`PyLongWriter` API can be used to import an integer. + +.. versionadded:: next + +.. c:struct:: PyLongWriter + + A Python :class:`int` writer instance. + + The instance must be destroyed by :c:func:`PyLongWriter_Finish` or + :c:func:`PyLongWriter_Discard`. + + +.. c:function:: PyLongWriter* PyLongWriter_Create(int negative, Py_ssize_t ndigits, void **digits) + + Create a :c:type:`PyLongWriter`. + + On success, allocate *\*digits* and return a writer. + On error, set an exception and return ``NULL``. + + *negative* is ``1`` if the number is negative, or ``0`` otherwise. + + *ndigits* is the number of digits in the *digits* array. It must be + greater than 0. + + *digits* must not be NULL. + + After a successful call to this function, the caller should fill in the + array of digits *digits* and then call :c:func:`PyLongWriter_Finish` to get + a Python :class:`int`. + The layout of *digits* is described by :c:func:`PyLong_GetNativeLayout`. + + Digits must be in the range [``0``; ``(1 << bits_per_digit) - 1``] + (where the :c:struct:`~PyLongLayout.bits_per_digit` is the number of bits + per digit). + Any unused most significant digits must be set to ``0``. + + Alternately, call :c:func:`PyLongWriter_Discard` to destroy the writer + instance without creating an :class:`~int` object. + + +.. c:function:: PyObject* PyLongWriter_Finish(PyLongWriter *writer) + + Finish a :c:type:`PyLongWriter` created by :c:func:`PyLongWriter_Create`. + + On success, return a Python :class:`int` object. + On error, set an exception and return ``NULL``. + + The function takes care of normalizing the digits and converts the object + to a compact integer if needed. + + The writer instance and the *digits* array are invalid after the call. + + +.. c:function:: void PyLongWriter_Discard(PyLongWriter *writer) + + Discard a :c:type:`PyLongWriter` created by :c:func:`PyLongWriter_Create`. + + *writer* must not be ``NULL``. + + The writer instance and the *digits* array are invalid after the call. diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index a043af48ba7a05..e78754e24e23d8 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -1299,6 +1299,13 @@ PyLong_GetSign:int::: PyLong_GetSign:PyObject*:v:0: PyLong_GetSign:int*:sign:: +PyLong_Export:int::: +PyLong_Export:PyObject*:obj:0: +PyLong_Export:PyLongExport*:export_long:: + +PyLongWriter_Finish:PyObject*::+1: +PyLongWriter_Finish:PyLongWriter*:writer:: + PyMapping_Check:int::: PyMapping_Check:PyObject*:o:0: diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index b71d31f9742fe0..5ce398ab93d6b4 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1018,6 +1018,17 @@ New features (Contributed by Victor Stinner in :gh:`107954`.) +* Add a new import and export API for Python :class:`int` objects (:pep:`757`): + + * :c:func:`PyLong_GetNativeLayout`; + * :c:func:`PyLong_Export`; + * :c:func:`PyLong_FreeExport`; + * :c:func:`PyLongWriter_Create`; + * :c:func:`PyLongWriter_Finish`; + * :c:func:`PyLongWriter_Discard`. + + (Contributed by Victor Stinner in :gh:`102471`.) + * Add :c:func:`PyType_GetBaseByToken` and :c:data:`Py_tp_token` slot for easier superclass identification, which attempts to resolve the `type checking issue `__ mentioned in :pep:`630` diff --git a/Include/cpython/longintrepr.h b/Include/cpython/longintrepr.h index c60ccc463653f9..357477b60d9a5a 100644 --- a/Include/cpython/longintrepr.h +++ b/Include/cpython/longintrepr.h @@ -139,6 +139,44 @@ _PyLong_CompactValue(const PyLongObject *op) #define PyUnstable_Long_CompactValue _PyLong_CompactValue +/* --- Import/Export API -------------------------------------------------- */ + +typedef struct PyLongLayout { + uint8_t bits_per_digit; + uint8_t digit_size; + int8_t digits_order; + int8_t digit_endianness; +} PyLongLayout; + +PyAPI_FUNC(const PyLongLayout*) PyLong_GetNativeLayout(void); + +typedef struct PyLongExport { + int64_t value; + uint8_t negative; + Py_ssize_t ndigits; + const void *digits; + // Member used internally, must not be used for other purpose. + Py_uintptr_t _reserved; +} PyLongExport; + +PyAPI_FUNC(int) PyLong_Export( + PyObject *obj, + PyLongExport *export_long); +PyAPI_FUNC(void) PyLong_FreeExport( + PyLongExport *export_long); + + +/* --- PyLongWriter API --------------------------------------------------- */ + +typedef struct PyLongWriter PyLongWriter; + +PyAPI_FUNC(PyLongWriter*) PyLongWriter_Create( + int negative, + Py_ssize_t ndigits, + void **digits); +PyAPI_FUNC(PyObject*) PyLongWriter_Finish(PyLongWriter *writer); +PyAPI_FUNC(void) PyLongWriter_Discard(PyLongWriter *writer); + #ifdef __cplusplus } #endif diff --git a/Lib/test/test_capi/test_long.py b/Lib/test/test_capi/test_long.py index a77094588a0edf..d45ac75c822ea9 100644 --- a/Lib/test/test_capi/test_long.py +++ b/Lib/test/test_capi/test_long.py @@ -10,6 +10,7 @@ NULL = None + class IntSubclass(int): pass @@ -714,5 +715,95 @@ def test_long_asuint64(self): self.check_long_asint(as_uint64, 0, UINT64_MAX, negative_value_error=ValueError) + def test_long_layout(self): + # Test PyLong_GetNativeLayout() + int_info = sys.int_info + layout = _testcapi.get_pylong_layout() + expected = { + 'bits_per_digit': int_info.bits_per_digit, + 'digit_size': int_info.sizeof_digit, + 'digits_order': -1, + 'digit_endianness': -1 if sys.byteorder == 'little' else 1, + } + self.assertEqual(layout, expected) + + def test_long_export(self): + # Test PyLong_Export() + layout = _testcapi.get_pylong_layout() + base = 2 ** layout['bits_per_digit'] + + pylong_export = _testcapi.pylong_export + + # value fits into int64_t + self.assertEqual(pylong_export(0), 0) + self.assertEqual(pylong_export(123), 123) + self.assertEqual(pylong_export(-123), -123) + self.assertEqual(pylong_export(IntSubclass(123)), 123) + + # use an array, doesn't fit into int64_t + self.assertEqual(pylong_export(base**10 * 2 + 1), + (0, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2])) + self.assertEqual(pylong_export(-(base**10 * 2 + 1)), + (1, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2])) + self.assertEqual(pylong_export(IntSubclass(base**10 * 2 + 1)), + (0, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2])) + + self.assertRaises(TypeError, pylong_export, 1.0) + self.assertRaises(TypeError, pylong_export, 0+1j) + self.assertRaises(TypeError, pylong_export, "abc") + + def test_longwriter_create(self): + # Test PyLongWriter_Create() + layout = _testcapi.get_pylong_layout() + base = 2 ** layout['bits_per_digit'] + + pylongwriter_create = _testcapi.pylongwriter_create + self.assertRaises(ValueError, pylongwriter_create, 0, []) + self.assertRaises(ValueError, pylongwriter_create, -123, []) + self.assertEqual(pylongwriter_create(0, [0]), 0) + self.assertEqual(pylongwriter_create(0, [123]), 123) + self.assertEqual(pylongwriter_create(1, [123]), -123) + self.assertEqual(pylongwriter_create(1, [1, 2]), + -(base * 2 + 1)) + self.assertEqual(pylongwriter_create(0, [1, 2, 3]), + base**2 * 3 + base * 2 + 1) + max_digit = base - 1 + self.assertEqual(pylongwriter_create(0, [max_digit, max_digit, max_digit]), + base**2 * max_digit + base * max_digit + max_digit) + + # normalize + self.assertEqual(pylongwriter_create(0, [123, 0, 0]), 123) + + # test singletons + normalize + for num in (-2, 0, 1, 5, 42, 100): + self.assertIs(pylongwriter_create(bool(num < 0), [abs(num), 0]), + num) + + def to_digits(num): + digits = [] + while True: + num, digit = divmod(num, base) + digits.append(digit) + if not num: + break + return digits + + # round trip: Python int -> export -> Python int + pylong_export = _testcapi.pylong_export + numbers = [*range(0, 10), 12345, 0xdeadbeef, 2**100, 2**100-1] + numbers.extend(-num for num in list(numbers)) + for num in numbers: + with self.subTest(num=num): + data = pylong_export(num) + if isinstance(data, tuple): + negative, digits = data + else: + value = data + negative = int(value < 0) + digits = to_digits(abs(value)) + self.assertEqual(pylongwriter_create(negative, digits), num, + (negative, digits)) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/C_API/2024-07-03-17-26-53.gh-issue-102471.XpmKYk.rst b/Misc/NEWS.d/next/C_API/2024-07-03-17-26-53.gh-issue-102471.XpmKYk.rst new file mode 100644 index 00000000000000..c18c159ac87d08 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2024-07-03-17-26-53.gh-issue-102471.XpmKYk.rst @@ -0,0 +1,10 @@ +Add a new import and export API for Python :class:`int` objects (:pep:`757`): + +* :c:func:`PyLong_GetNativeLayout`; +* :c:func:`PyLong_Export`; +* :c:func:`PyLong_FreeExport`; +* :c:func:`PyLongWriter_Create`; +* :c:func:`PyLongWriter_Finish`; +* :c:func:`PyLongWriter_Discard`. + +Patch by Victor Stinner. diff --git a/Modules/_testcapi/long.c b/Modules/_testcapi/long.c index ebea09080ef11c..42243023a45768 100644 --- a/Modules/_testcapi/long.c +++ b/Modules/_testcapi/long.c @@ -141,6 +141,127 @@ pylong_aspid(PyObject *module, PyObject *arg) } +static PyObject * +layout_to_dict(const PyLongLayout *layout) +{ + return Py_BuildValue("{sisisisi}", + "bits_per_digit", (int)layout->bits_per_digit, + "digit_size", (int)layout->digit_size, + "digits_order", (int)layout->digits_order, + "digit_endianness", (int)layout->digit_endianness); +} + + +static PyObject * +pylong_export(PyObject *module, PyObject *obj) +{ + PyLongExport export_long; + if (PyLong_Export(obj, &export_long) < 0) { + return NULL; + } + + if (export_long.digits == NULL) { + assert(export_long.negative == 0); + assert(export_long.ndigits == 0); + assert(export_long.digits == NULL); + PyObject *res = PyLong_FromInt64(export_long.value); + PyLong_FreeExport(&export_long); + return res; + } + + assert(PyLong_GetNativeLayout()->digit_size == sizeof(digit)); + const digit *export_long_digits = export_long.digits; + + PyObject *digits = PyList_New(0); + if (digits == NULL) { + goto error; + } + for (Py_ssize_t i = 0; i < export_long.ndigits; i++) { + PyObject *item = PyLong_FromUnsignedLong(export_long_digits[i]); + if (item == NULL) { + goto error; + } + + if (PyList_Append(digits, item) < 0) { + Py_DECREF(item); + goto error; + } + Py_DECREF(item); + } + + assert(export_long.value == 0); + PyObject *res = Py_BuildValue("(iN)", export_long.negative, digits); + + PyLong_FreeExport(&export_long); + assert(export_long._reserved == 0); + + return res; + +error: + Py_XDECREF(digits); + PyLong_FreeExport(&export_long); + return NULL; +} + + +static PyObject * +pylongwriter_create(PyObject *module, PyObject *args) +{ + int negative; + PyObject *list; + // TODO(vstinner): write test for negative ndigits and digits==NULL + if (!PyArg_ParseTuple(args, "iO!", &negative, &PyList_Type, &list)) { + return NULL; + } + Py_ssize_t ndigits = PyList_GET_SIZE(list); + + digit *digits = PyMem_Malloc((size_t)ndigits * sizeof(digit)); + if (digits == NULL) { + return PyErr_NoMemory(); + } + + for (Py_ssize_t i = 0; i < ndigits; i++) { + PyObject *item = PyList_GET_ITEM(list, i); + + long num = PyLong_AsLong(item); + if (num == -1 && PyErr_Occurred()) { + goto error; + } + + if (num < 0 || num >= PyLong_BASE) { + PyErr_SetString(PyExc_ValueError, "digit doesn't fit into digit"); + goto error; + } + digits[i] = (digit)num; + } + + void *writer_digits; + PyLongWriter *writer = PyLongWriter_Create(negative, ndigits, + &writer_digits); + if (writer == NULL) { + goto error; + } + assert(PyLong_GetNativeLayout()->digit_size == sizeof(digit)); + memcpy(writer_digits, digits, (size_t)ndigits * sizeof(digit)); + PyObject *res = PyLongWriter_Finish(writer); + PyMem_Free(digits); + + return res; + +error: + PyMem_Free(digits); + return NULL; +} + + +static PyObject * +get_pylong_layout(PyObject *module, PyObject *Py_UNUSED(args)) +{ + const PyLongLayout *layout = PyLong_GetNativeLayout(); + return layout_to_dict(layout); +} + + static PyMethodDef test_methods[] = { _TESTCAPI_CALL_LONG_COMPACT_API_METHODDEF {"pylong_fromunicodeobject", pylong_fromunicodeobject, METH_VARARGS}, @@ -148,6 +269,9 @@ static PyMethodDef test_methods[] = { {"pylong_fromnativebytes", pylong_fromnativebytes, METH_VARARGS}, {"pylong_getsign", pylong_getsign, METH_O}, {"pylong_aspid", pylong_aspid, METH_O}, + {"pylong_export", pylong_export, METH_O}, + {"pylongwriter_create", pylongwriter_create, METH_VARARGS}, + {"get_pylong_layout", get_pylong_layout, METH_NOARGS}, {"pylong_ispositive", pylong_ispositive, METH_O}, {"pylong_isnegative", pylong_isnegative, METH_O}, {"pylong_iszero", pylong_iszero, METH_O}, diff --git a/Objects/longobject.c b/Objects/longobject.c index 96d59f542a7c3c..bd7ff68d0899c6 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -6750,6 +6750,7 @@ PyUnstable_Long_CompactValue(const PyLongObject* op) { return _PyLong_CompactValue((PyLongObject*)op); } + PyObject* PyLong_FromInt32(int32_t value) { return PyLong_FromNativeBytes(&value, sizeof(value), -1); } @@ -6815,3 +6816,122 @@ int PyLong_AsUInt64(PyObject *obj, uint64_t *value) { LONG_TO_UINT(obj, value, "C uint64_t"); } + + +static const PyLongLayout PyLong_LAYOUT = { + .bits_per_digit = PyLong_SHIFT, + .digits_order = -1, // least significant first + .digit_endianness = PY_LITTLE_ENDIAN ? -1 : 1, + .digit_size = sizeof(digit), +}; + + +const PyLongLayout* +PyLong_GetNativeLayout(void) +{ + return &PyLong_LAYOUT; +} + + +int +PyLong_Export(PyObject *obj, PyLongExport *export_long) +{ + if (!PyLong_Check(obj)) { + memset(export_long, 0, sizeof(*export_long)); + PyErr_Format(PyExc_TypeError, "expect int, got %T", obj); + return -1; + } + + // Fast-path: try to convert to a int64_t + int overflow; +#if SIZEOF_LONG == 8 + long value = PyLong_AsLongAndOverflow(obj, &overflow); +#else + // Windows has 32-bit long, so use 64-bit long long instead + long long value = PyLong_AsLongLongAndOverflow(obj, &overflow); +#endif + Py_BUILD_ASSERT(sizeof(value) == sizeof(int64_t)); + // the function cannot fail since obj is a PyLongObject + assert(!(value == -1 && PyErr_Occurred())); + + if (!overflow) { + export_long->value = value; + export_long->negative = 0; + export_long->ndigits = 0; + export_long->digits = NULL; + export_long->_reserved = 0; + } + else { + PyLongObject *self = (PyLongObject*)obj; + export_long->value = 0; + export_long->negative = _PyLong_IsNegative(self); + export_long->ndigits = _PyLong_DigitCount(self); + if (export_long->ndigits == 0) { + export_long->ndigits = 1; + } + export_long->digits = self->long_value.ob_digit; + export_long->_reserved = (Py_uintptr_t)Py_NewRef(obj); + } + return 0; +} + + +void +PyLong_FreeExport(PyLongExport *export_long) +{ + PyObject *obj = (PyObject*)export_long->_reserved; + if (obj) { + export_long->_reserved = 0; + Py_DECREF(obj); + } +} + + +/* --- PyLongWriter API --------------------------------------------------- */ + +PyLongWriter* +PyLongWriter_Create(int negative, Py_ssize_t ndigits, void **digits) +{ + if (ndigits <= 0) { + PyErr_SetString(PyExc_ValueError, "ndigits must be positive"); + goto error; + } + assert(digits != NULL); + + PyLongObject *obj = _PyLong_New(ndigits); + if (obj == NULL) { + goto error; + } + if (negative) { + _PyLong_FlipSign(obj); + } + + *digits = obj->long_value.ob_digit; + return (PyLongWriter*)obj; + +error: + *digits = NULL; + return NULL; +} + + +void +PyLongWriter_Discard(PyLongWriter *writer) +{ + PyLongObject *obj = (PyLongObject *)writer; + assert(Py_REFCNT(obj) == 1); + Py_DECREF(obj); +} + + +PyObject* +PyLongWriter_Finish(PyLongWriter *writer) +{ + PyLongObject *obj = (PyLongObject *)writer; + assert(Py_REFCNT(obj) == 1); + + // Normalize and get singleton if possible + obj = maybe_small_long(long_normalize(obj)); + + return (PyObject*)obj; +} diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index 686f3935d91bda..c8c30a7985aa2e 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -319,6 +319,7 @@ Objects/exceptions.c - static_exceptions - Objects/genobject.c - ASYNC_GEN_IGNORED_EXIT_MSG - Objects/genobject.c - NON_INIT_CORO_MSG - Objects/longobject.c - _PyLong_DigitValue - +Objects/longobject.c - PyLong_LAYOUT - Objects/object.c - _Py_SwappedOp - Objects/object.c - _Py_abstract_hack - Objects/object.c - last_final_reftotal - From 8bc18182a7c28f86265c9d82bd0338137480921c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:16:22 +0100 Subject: [PATCH 019/376] gh-127691: add type checks when using `PyUnicodeError` objects (GH-127694) --- Doc/whatsnew/3.14.rst | 6 + ...-12-06-16-53-34.gh-issue-127691.k_Jitp.rst | 3 + Objects/exceptions.c | 216 ++++++++++++------ 3 files changed, 157 insertions(+), 68 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2024-12-06-16-53-34.gh-issue-127691.k_Jitp.rst diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 5ce398ab93d6b4..095949242c09d9 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1045,6 +1045,12 @@ New features * Add :c:func:`PyUnstable_Object_EnableDeferredRefcount` for enabling deferred reference counting, as outlined in :pep:`703`. +* The :ref:`Unicode Exception Objects ` C API + now raises a :exc:`TypeError` if its exception argument is not + a :exc:`UnicodeError` object. + (Contributed by Bénédikt Tran in :gh:`127691`.) + + Porting to Python 3.14 ---------------------- diff --git a/Misc/NEWS.d/next/C_API/2024-12-06-16-53-34.gh-issue-127691.k_Jitp.rst b/Misc/NEWS.d/next/C_API/2024-12-06-16-53-34.gh-issue-127691.k_Jitp.rst new file mode 100644 index 00000000000000..c942ff3d9eda53 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2024-12-06-16-53-34.gh-issue-127691.k_Jitp.rst @@ -0,0 +1,3 @@ +The :ref:`Unicode Exception Objects ` C API +now raises a :exc:`TypeError` if its exception argument is not +a :exc:`UnicodeError` object. Patch by Bénédikt Tran. diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 287cbc25305964..6880c24196cbb8 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -2668,7 +2668,7 @@ SimpleExtendsException(PyExc_ValueError, UnicodeError, "Unicode related error."); static PyObject * -get_string(PyObject *attr, const char *name) +get_bytes(PyObject *attr, const char *name) { if (!attr) { PyErr_Format(PyExc_TypeError, "%.200s attribute not set", name); @@ -2748,40 +2748,74 @@ unicode_error_adjust_end(Py_ssize_t end, Py_ssize_t objlen) return end; } +#define _PyUnicodeError_CAST(PTR) ((PyUnicodeErrorObject *)(PTR)) +#define PyUnicodeError_Check(PTR) \ + PyObject_TypeCheck((PTR), (PyTypeObject *)PyExc_UnicodeError) +#define PyUnicodeError_CAST(PTR) \ + (assert(PyUnicodeError_Check(PTR)), _PyUnicodeError_CAST(PTR)) + + +static inline int +check_unicode_error_type(PyObject *self, const char *expect_type) +{ + if (!PyUnicodeError_Check(self)) { + PyErr_Format(PyExc_TypeError, + "expecting a %s object, got %T", expect_type, self); + return -1; + } + return 0; +} + + +static inline PyUnicodeErrorObject * +as_unicode_error(PyObject *self, const char *expect_type) +{ + int rc = check_unicode_error_type(self, expect_type); + return rc < 0 ? NULL : _PyUnicodeError_CAST(self); +} + PyObject * -PyUnicodeEncodeError_GetEncoding(PyObject *exc) +PyUnicodeEncodeError_GetEncoding(PyObject *self) { - return get_unicode(((PyUnicodeErrorObject *)exc)->encoding, "encoding"); + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeEncodeError"); + return exc == NULL ? NULL : get_unicode(exc->encoding, "encoding"); } PyObject * -PyUnicodeDecodeError_GetEncoding(PyObject *exc) +PyUnicodeDecodeError_GetEncoding(PyObject *self) { - return get_unicode(((PyUnicodeErrorObject *)exc)->encoding, "encoding"); + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeDecodeError"); + return exc == NULL ? NULL : get_unicode(exc->encoding, "encoding"); } PyObject * -PyUnicodeEncodeError_GetObject(PyObject *exc) +PyUnicodeEncodeError_GetObject(PyObject *self) { - return get_unicode(((PyUnicodeErrorObject *)exc)->object, "object"); + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeEncodeError"); + return exc == NULL ? NULL : get_unicode(exc->object, "object"); } PyObject * -PyUnicodeDecodeError_GetObject(PyObject *exc) +PyUnicodeDecodeError_GetObject(PyObject *self) { - return get_string(((PyUnicodeErrorObject *)exc)->object, "object"); + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeDecodeError"); + return exc == NULL ? NULL : get_bytes(exc->object, "object"); } PyObject * -PyUnicodeTranslateError_GetObject(PyObject *exc) +PyUnicodeTranslateError_GetObject(PyObject *self) { - return get_unicode(((PyUnicodeErrorObject *)exc)->object, "object"); + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeTranslateError"); + return exc == NULL ? NULL : get_unicode(exc->object, "object"); } int PyUnicodeEncodeError_GetStart(PyObject *self, Py_ssize_t *start) { - PyUnicodeErrorObject *exc = (PyUnicodeErrorObject *)self; + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeEncodeError"); + if (exc == NULL) { + return -1; + } PyObject *obj = get_unicode(exc->object, "object"); if (obj == NULL) { return -1; @@ -2796,8 +2830,11 @@ PyUnicodeEncodeError_GetStart(PyObject *self, Py_ssize_t *start) int PyUnicodeDecodeError_GetStart(PyObject *self, Py_ssize_t *start) { - PyUnicodeErrorObject *exc = (PyUnicodeErrorObject *)self; - PyObject *obj = get_string(exc->object, "object"); + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeDecodeError"); + if (exc == NULL) { + return -1; + } + PyObject *obj = get_bytes(exc->object, "object"); if (obj == NULL) { return -1; } @@ -2809,45 +2846,63 @@ PyUnicodeDecodeError_GetStart(PyObject *self, Py_ssize_t *start) int -PyUnicodeTranslateError_GetStart(PyObject *exc, Py_ssize_t *start) +PyUnicodeTranslateError_GetStart(PyObject *self, Py_ssize_t *start) { - return PyUnicodeEncodeError_GetStart(exc, start); + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeTranslateError"); + if (exc == NULL) { + return -1; + } + PyObject *obj = get_unicode(exc->object, "object"); + if (obj == NULL) { + return -1; + } + Py_ssize_t size = PyUnicode_GET_LENGTH(obj); + Py_DECREF(obj); + *start = unicode_error_adjust_start(exc->start, size); + return 0; } static inline int unicode_error_set_start_impl(PyObject *self, Py_ssize_t start) { - ((PyUnicodeErrorObject *)self)->start = start; + PyUnicodeErrorObject *exc = _PyUnicodeError_CAST(self); + exc->start = start; return 0; } int -PyUnicodeEncodeError_SetStart(PyObject *exc, Py_ssize_t start) +PyUnicodeEncodeError_SetStart(PyObject *self, Py_ssize_t start) { - return unicode_error_set_start_impl(exc, start); + int rc = check_unicode_error_type(self, "UnicodeEncodeError"); + return rc < 0 ? -1 : unicode_error_set_start_impl(self, start); } int -PyUnicodeDecodeError_SetStart(PyObject *exc, Py_ssize_t start) +PyUnicodeDecodeError_SetStart(PyObject *self, Py_ssize_t start) { - return unicode_error_set_start_impl(exc, start); + int rc = check_unicode_error_type(self, "UnicodeDecodeError"); + return rc < 0 ? -1 : unicode_error_set_start_impl(self, start); } int -PyUnicodeTranslateError_SetStart(PyObject *exc, Py_ssize_t start) +PyUnicodeTranslateError_SetStart(PyObject *self, Py_ssize_t start) { - return unicode_error_set_start_impl(exc, start); + int rc = check_unicode_error_type(self, "UnicodeTranslateError"); + return rc < 0 ? -1 : unicode_error_set_start_impl(self, start); } int PyUnicodeEncodeError_GetEnd(PyObject *self, Py_ssize_t *end) { - PyUnicodeErrorObject *exc = (PyUnicodeErrorObject *)self; + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeEncodeError"); + if (exc == NULL) { + return -1; + } PyObject *obj = get_unicode(exc->object, "object"); if (obj == NULL) { return -1; @@ -2862,8 +2917,11 @@ PyUnicodeEncodeError_GetEnd(PyObject *self, Py_ssize_t *end) int PyUnicodeDecodeError_GetEnd(PyObject *self, Py_ssize_t *end) { - PyUnicodeErrorObject *exc = (PyUnicodeErrorObject *)self; - PyObject *obj = get_string(exc->object, "object"); + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeDecodeError"); + if (exc == NULL) { + return -1; + } + PyObject *obj = get_bytes(exc->object, "object"); if (obj == NULL) { return -1; } @@ -2875,108 +2933,130 @@ PyUnicodeDecodeError_GetEnd(PyObject *self, Py_ssize_t *end) int -PyUnicodeTranslateError_GetEnd(PyObject *exc, Py_ssize_t *end) +PyUnicodeTranslateError_GetEnd(PyObject *self, Py_ssize_t *end) { - return PyUnicodeEncodeError_GetEnd(exc, end); + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeTranslateError"); + if (exc == NULL) { + return -1; + } + PyObject *obj = get_unicode(exc->object, "object"); + if (obj == NULL) { + return -1; + } + Py_ssize_t size = PyUnicode_GET_LENGTH(obj); + Py_DECREF(obj); + *end = unicode_error_adjust_end(exc->end, size); + return 0; } static inline int -unicode_error_set_end_impl(PyObject *exc, Py_ssize_t end) +unicode_error_set_end_impl(PyObject *self, Py_ssize_t end) { - ((PyUnicodeErrorObject *)exc)->end = end; + PyUnicodeErrorObject *exc = _PyUnicodeError_CAST(self); + exc->end = end; return 0; } int -PyUnicodeEncodeError_SetEnd(PyObject *exc, Py_ssize_t end) +PyUnicodeEncodeError_SetEnd(PyObject *self, Py_ssize_t end) { - return unicode_error_set_end_impl(exc, end); + int rc = check_unicode_error_type(self, "UnicodeEncodeError"); + return rc < 0 ? -1 : unicode_error_set_end_impl(self, end); } int -PyUnicodeDecodeError_SetEnd(PyObject *exc, Py_ssize_t end) +PyUnicodeDecodeError_SetEnd(PyObject *self, Py_ssize_t end) { - return unicode_error_set_end_impl(exc, end); + int rc = check_unicode_error_type(self, "UnicodeDecodeError"); + return rc < 0 ? -1 : unicode_error_set_end_impl(self, end); } int -PyUnicodeTranslateError_SetEnd(PyObject *exc, Py_ssize_t end) +PyUnicodeTranslateError_SetEnd(PyObject *self, Py_ssize_t end) { - return unicode_error_set_end_impl(exc, end); + int rc = check_unicode_error_type(self, "UnicodeTranslateError"); + return rc < 0 ? -1 : unicode_error_set_end_impl(self, end); } + PyObject * -PyUnicodeEncodeError_GetReason(PyObject *exc) +PyUnicodeEncodeError_GetReason(PyObject *self) { - return get_unicode(((PyUnicodeErrorObject *)exc)->reason, "reason"); + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeEncodeError"); + return exc == NULL ? NULL : get_unicode(exc->reason, "reason"); } PyObject * -PyUnicodeDecodeError_GetReason(PyObject *exc) +PyUnicodeDecodeError_GetReason(PyObject *self) { - return get_unicode(((PyUnicodeErrorObject *)exc)->reason, "reason"); + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeDecodeError"); + return exc == NULL ? NULL : get_unicode(exc->reason, "reason"); } PyObject * -PyUnicodeTranslateError_GetReason(PyObject *exc) +PyUnicodeTranslateError_GetReason(PyObject *self) { - return get_unicode(((PyUnicodeErrorObject *)exc)->reason, "reason"); + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeTranslateError"); + return exc == NULL ? NULL : get_unicode(exc->reason, "reason"); } int -PyUnicodeEncodeError_SetReason(PyObject *exc, const char *reason) +PyUnicodeEncodeError_SetReason(PyObject *self, const char *reason) { - return set_unicodefromstring(&((PyUnicodeErrorObject *)exc)->reason, - reason); + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeEncodeError"); + return exc == NULL ? -1 : set_unicodefromstring(&exc->reason, reason); } int -PyUnicodeDecodeError_SetReason(PyObject *exc, const char *reason) +PyUnicodeDecodeError_SetReason(PyObject *self, const char *reason) { - return set_unicodefromstring(&((PyUnicodeErrorObject *)exc)->reason, - reason); + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeDecodeError"); + return exc == NULL ? -1 : set_unicodefromstring(&exc->reason, reason); } int -PyUnicodeTranslateError_SetReason(PyObject *exc, const char *reason) +PyUnicodeTranslateError_SetReason(PyObject *self, const char *reason) { - return set_unicodefromstring(&((PyUnicodeErrorObject *)exc)->reason, - reason); + PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeTranslateError"); + return exc == NULL ? -1 : set_unicodefromstring(&exc->reason, reason); } static int -UnicodeError_clear(PyUnicodeErrorObject *self) +UnicodeError_clear(PyObject *self) { - Py_CLEAR(self->encoding); - Py_CLEAR(self->object); - Py_CLEAR(self->reason); + PyUnicodeErrorObject *exc = PyUnicodeError_CAST(self); + Py_CLEAR(exc->encoding); + Py_CLEAR(exc->object); + Py_CLEAR(exc->reason); return BaseException_clear((PyBaseExceptionObject *)self); } static void -UnicodeError_dealloc(PyUnicodeErrorObject *self) +UnicodeError_dealloc(PyObject *self) { + PyTypeObject *type = Py_TYPE(self); _PyObject_GC_UNTRACK(self); - UnicodeError_clear(self); - Py_TYPE(self)->tp_free((PyObject *)self); + (void)UnicodeError_clear(self); + type->tp_free(self); } static int -UnicodeError_traverse(PyUnicodeErrorObject *self, visitproc visit, void *arg) +UnicodeError_traverse(PyObject *self, visitproc visit, void *arg) { - Py_VISIT(self->encoding); - Py_VISIT(self->object); - Py_VISIT(self->reason); + PyUnicodeErrorObject *exc = PyUnicodeError_CAST(self); + Py_VISIT(exc->encoding); + Py_VISIT(exc->object); + Py_VISIT(exc->reason); return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg); } @@ -3015,7 +3095,7 @@ UnicodeEncodeError_init(PyObject *self, PyObject *args, PyObject *kwds) return -1; } - PyUnicodeErrorObject *exc = (PyUnicodeErrorObject *)self; + PyUnicodeErrorObject *exc = PyUnicodeError_CAST(self); Py_XSETREF(exc->encoding, Py_NewRef(encoding)); Py_XSETREF(exc->object, Py_NewRef(object)); exc->start = start; @@ -3027,7 +3107,7 @@ UnicodeEncodeError_init(PyObject *self, PyObject *args, PyObject *kwds) static PyObject * UnicodeEncodeError_str(PyObject *self) { - PyUnicodeErrorObject *exc = (PyUnicodeErrorObject *)self; + PyUnicodeErrorObject *exc = PyUnicodeError_CAST(self); PyObject *result = NULL; PyObject *reason_str = NULL; PyObject *encoding_str = NULL; @@ -3135,7 +3215,7 @@ UnicodeDecodeError_init(PyObject *self, PyObject *args, PyObject *kwds) } } - PyUnicodeErrorObject *exc = (PyUnicodeErrorObject *)self; + PyUnicodeErrorObject *exc = PyUnicodeError_CAST(self); Py_XSETREF(exc->encoding, Py_NewRef(encoding)); Py_XSETREF(exc->object, object /* already a strong reference */); exc->start = start; @@ -3147,7 +3227,7 @@ UnicodeDecodeError_init(PyObject *self, PyObject *args, PyObject *kwds) static PyObject * UnicodeDecodeError_str(PyObject *self) { - PyUnicodeErrorObject *exc = (PyUnicodeErrorObject *)self; + PyUnicodeErrorObject *exc = PyUnicodeError_CAST(self); PyObject *result = NULL; PyObject *reason_str = NULL; PyObject *encoding_str = NULL; @@ -3236,7 +3316,7 @@ UnicodeTranslateError_init(PyObject *self, PyObject *args, PyObject *kwds) return -1; } - PyUnicodeErrorObject *exc = (PyUnicodeErrorObject *)self; + PyUnicodeErrorObject *exc = PyUnicodeError_CAST(self); Py_XSETREF(exc->object, Py_NewRef(object)); exc->start = start; exc->end = end; @@ -3248,7 +3328,7 @@ UnicodeTranslateError_init(PyObject *self, PyObject *args, PyObject *kwds) static PyObject * UnicodeTranslateError_str(PyObject *self) { - PyUnicodeErrorObject *exc = (PyUnicodeErrorObject *)self; + PyUnicodeErrorObject *exc = PyUnicodeError_CAST(self); PyObject *result = NULL; PyObject *reason_str = NULL; From 5dd775bed086909722ec7014a7c4f77a35f74a80 Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Sat, 14 Dec 2024 01:21:46 +0900 Subject: [PATCH 020/376] gh-126024: unicodeobject: optimize find_first_nonascii (GH-127790) Remove 1 branch. --- Objects/unicodeobject.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 33c4747bbef488..b7aeb06d32bcec 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -5077,21 +5077,24 @@ load_unaligned(const unsigned char *p, size_t size) static Py_ssize_t find_first_nonascii(const unsigned char *start, const unsigned char *end) { + // The search is done in `size_t` chunks. + // The start and end might not be aligned at `size_t` boundaries, + // so they're handled specially. + const unsigned char *p = start; if (end - start >= SIZEOF_SIZE_T) { - const unsigned char *p2 = _Py_ALIGN_UP(p, SIZEOF_SIZE_T); + // Avoid unaligned read. #if PY_LITTLE_ENDIAN && HAVE_CTZ - if (p < p2) { - size_t u; - memcpy(&u, p, sizeof(size_t)); - u &= ASCII_CHAR_MASK; - if (u) { - return (ctz(u) - 7) / 8; - } - p = p2; + size_t u; + memcpy(&u, p, sizeof(size_t)); + u &= ASCII_CHAR_MASK; + if (u) { + return (ctz(u) - 7) / 8; } + p = _Py_ALIGN_DOWN(p + SIZEOF_SIZE_T, SIZEOF_SIZE_T); #else /* PY_LITTLE_ENDIAN && HAVE_CTZ */ + const unsigned char *p2 = _Py_ALIGN_UP(p, SIZEOF_SIZE_T); while (p < p2) { if (*p & 0x80) { return p - start; @@ -5099,6 +5102,7 @@ find_first_nonascii(const unsigned char *start, const unsigned char *end) p++; } #endif + const unsigned char *e = end - SIZEOF_SIZE_T; while (p <= e) { size_t u = (*(const size_t *)p) & ASCII_CHAR_MASK; @@ -5115,6 +5119,7 @@ find_first_nonascii(const unsigned char *start, const unsigned char *end) } } #if PY_LITTLE_ENDIAN && HAVE_CTZ + assert((end - p) < SIZEOF_SIZE_T); // we can not use *(const size_t*)p to avoid buffer overrun. size_t u = load_unaligned(p, end - p) & ASCII_CHAR_MASK; if (u) { From 292067fbc9db81896c16ff12d51c21d2b0f233e2 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 13 Dec 2024 12:12:49 -0600 Subject: [PATCH 021/376] Minor readability improvements for the itertools recipes (gh-127928) --- Doc/library/itertools.rst | 74 ++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 39 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 3b90d7830f3681..d1fb952e36f686 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -30,11 +30,6 @@ For instance, SML provides a tabulation tool: ``tabulate(f)`` which produces a sequence ``f(0), f(1), ...``. The same effect can be achieved in Python 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(starmap(operator.mul, zip(vec1, vec2, strict=True)))``. - **Infinite iterators:** @@ -843,12 +838,11 @@ and :term:`generators ` which incur interpreter overhead. .. testcode:: - import collections - import contextlib - import functools - import math - import operator - import random + from collections import deque + from contextlib import suppress + from functools import reduce + from math import sumprod, isqrt + from operator import itemgetter, getitem, mul, neg def take(n, iterable): "Return first n items of the iterable as a list." @@ -863,11 +857,11 @@ and :term:`generators ` which incur interpreter overhead. "Return function(0), function(1), ..." return map(function, count(start)) - def repeatfunc(func, times=None, *args): - "Repeat calls to func with specified arguments." + def repeatfunc(function, times=None, *args): + "Repeat calls to a function with specified arguments." if times is None: - return starmap(func, repeat(args)) - return starmap(func, repeat(args, times)) + return starmap(function, repeat(args)) + return starmap(function, repeat(args, times)) def flatten(list_of_lists): "Flatten one level of nesting." @@ -885,13 +879,13 @@ and :term:`generators ` which incur interpreter overhead. def tail(n, iterable): "Return an iterator over the last n items." # tail(3, 'ABCDEFG') → E F G - return iter(collections.deque(iterable, maxlen=n)) + return iter(deque(iterable, maxlen=n)) def consume(iterator, n=None): "Advance the iterator n-steps ahead. If n is None, consume entirely." # Use functions that consume iterators at C speed. if n is None: - collections.deque(iterator, maxlen=0) + deque(iterator, maxlen=0) else: next(islice(iterator, n, n), None) @@ -919,8 +913,8 @@ and :term:`generators ` which incur interpreter overhead. # unique_justseen('AAAABBBCCDAABBB') → A B C D A B # unique_justseen('ABBcCAD', str.casefold) → A B c A D if key is None: - return map(operator.itemgetter(0), groupby(iterable)) - return map(next, map(operator.itemgetter(1), groupby(iterable, key))) + return map(itemgetter(0), groupby(iterable)) + return map(next, map(itemgetter(1), groupby(iterable, key))) def unique_everseen(iterable, key=None): "Yield unique elements, preserving order. Remember all elements ever seen." @@ -941,13 +935,14 @@ and :term:`generators ` which incur interpreter overhead. def unique(iterable, key=None, reverse=False): "Yield unique elements in sorted order. Supports unhashable inputs." # unique([[1, 2], [3, 4], [1, 2]]) → [1, 2] [3, 4] - return unique_justseen(sorted(iterable, key=key, reverse=reverse), key=key) + sequenced = sorted(iterable, key=key, reverse=reverse) + return unique_justseen(sequenced, key=key) def sliding_window(iterable, n): "Collect data into overlapping fixed-length chunks or blocks." # sliding_window('ABCDEFG', 4) → ABCD BCDE CDEF DEFG iterator = iter(iterable) - window = collections.deque(islice(iterator, n - 1), maxlen=n) + window = deque(islice(iterator, n - 1), maxlen=n) for x in iterator: window.append(x) yield tuple(window) @@ -981,7 +976,7 @@ and :term:`generators ` which incur interpreter overhead. "Return all contiguous non-empty subslices of a sequence." # subslices('ABCD') → A AB ABC ABCD B BC BCD C CD D slices = starmap(slice, combinations(range(len(seq) + 1), 2)) - return map(operator.getitem, repeat(seq), slices) + return map(getitem, repeat(seq), slices) def iter_index(iterable, value, start=0, stop=None): "Return indices where a value occurs in a sequence or iterable." @@ -995,19 +990,19 @@ and :term:`generators ` which incur interpreter overhead. else: stop = len(iterable) if stop is None else stop i = start - with contextlib.suppress(ValueError): + with suppress(ValueError): while True: yield (i := seq_index(value, i, stop)) i += 1 - def iter_except(func, exception, first=None): + def iter_except(function, exception, first=None): "Convert a call-until-exception interface to an iterator interface." # iter_except(d.popitem, KeyError) → non-blocking dictionary iterator - with contextlib.suppress(exception): + with suppress(exception): if first is not None: yield first() while True: - yield func() + yield function() The following recipes have a more mathematical flavor: @@ -1015,6 +1010,7 @@ The following recipes have a more mathematical flavor: .. testcode:: def powerset(iterable): + "Subsequences of the iterable from shortest to longest." # powerset([1,2,3]) → () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3) s = list(iterable) return chain.from_iterable(combinations(s, r) for r in range(len(s)+1)) @@ -1022,12 +1018,12 @@ The following recipes have a more mathematical flavor: def sum_of_squares(iterable): "Add up the squares of the input values." # sum_of_squares([10, 20, 30]) → 1400 - return math.sumprod(*tee(iterable)) + return sumprod(*tee(iterable)) - def reshape(matrix, cols): + def reshape(matrix, columns): "Reshape a 2-D matrix to have a given number of columns." # reshape([(0, 1), (2, 3), (4, 5)], 3) → (0, 1, 2), (3, 4, 5) - return batched(chain.from_iterable(matrix), cols, strict=True) + return batched(chain.from_iterable(matrix), columns, strict=True) def transpose(matrix): "Swap the rows and columns of a 2-D matrix." @@ -1038,7 +1034,7 @@ The following recipes have a more mathematical flavor: "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) + return batched(starmap(sumprod, product(m1, transpose(m2))), n) def convolve(signal, kernel): """Discrete linear convolution of two iterables. @@ -1059,7 +1055,7 @@ The following recipes have a more mathematical flavor: n = len(kernel) padded_signal = chain(repeat(0, n-1), signal, repeat(0, n-1)) windowed_signal = sliding_window(padded_signal, n) - return map(math.sumprod, repeat(kernel), windowed_signal) + return map(sumprod, repeat(kernel), windowed_signal) def polynomial_from_roots(roots): """Compute a polynomial's coefficients from its roots. @@ -1067,8 +1063,8 @@ The following recipes have a more mathematical flavor: (x - 5) (x + 4) (x - 3) expands to: x³ -4x² -17x + 60 """ # polynomial_from_roots([5, -4, 3]) → [1, -4, -17, 60] - factors = zip(repeat(1), map(operator.neg, roots)) - return list(functools.reduce(convolve, factors, [1])) + factors = zip(repeat(1), map(neg, roots)) + return list(reduce(convolve, factors, [1])) def polynomial_eval(coefficients, x): """Evaluate a polynomial at a specific value. @@ -1081,7 +1077,7 @@ The following recipes have a more mathematical flavor: if not n: return type(x)(0) powers = map(pow, repeat(x), reversed(range(n))) - return math.sumprod(coefficients, powers) + return sumprod(coefficients, powers) def polynomial_derivative(coefficients): """Compute the first derivative of a polynomial. @@ -1092,7 +1088,7 @@ The following recipes have a more mathematical flavor: # polynomial_derivative([1, -4, -17, 60]) → [3, -8, -17] n = len(coefficients) powers = reversed(range(1, n)) - return list(map(operator.mul, coefficients, powers)) + return list(map(mul, coefficients, powers)) def sieve(n): "Primes less than n." @@ -1100,7 +1096,7 @@ The following recipes have a more mathematical flavor: if n > 2: yield 2 data = bytearray((0, 1)) * (n // 2) - for p in iter_index(data, 1, start=3, stop=math.isqrt(n) + 1): + for p in iter_index(data, 1, start=3, stop=isqrt(n) + 1): data[p*p : n : p+p] = bytes(len(range(p*p, n, p+p))) yield from iter_index(data, 1, start=3) @@ -1109,7 +1105,7 @@ The following recipes have a more mathematical flavor: # factor(99) → 3 3 11 # factor(1_000_000_000_000_007) → 47 59 360620266859 # factor(1_000_000_000_000_403) → 1000000000000403 - for prime in sieve(math.isqrt(n) + 1): + for prime in sieve(isqrt(n) + 1): while not n % prime: yield prime n //= prime @@ -1740,7 +1736,7 @@ The following recipes have a more mathematical flavor: # Old recipes and their tests which are guaranteed to continue to work. - def sumprod(vec1, vec2): + def old_sumprod_recipe(vec1, vec2): "Compute a sum of products." return sum(starmap(operator.mul, zip(vec1, vec2, strict=True))) @@ -1823,7 +1819,7 @@ The following recipes have a more mathematical flavor: 32 - >>> sumprod([1,2,3], [4,5,6]) + >>> old_sumprod_recipe([1,2,3], [4,5,6]) 32 From 2de048ce79e621f5ae0574095b9600fe8595f607 Mon Sep 17 00:00:00 2001 From: mpage Date: Fri, 13 Dec 2024 10:17:16 -0800 Subject: [PATCH 022/376] gh-115999: Specialize loading attributes from modules in free-threaded builds (#127711) We use the same approach that was used for specialization of LOAD_GLOBAL in free-threaded builds: _CHECK_ATTR_MODULE is renamed to _CHECK_ATTR_MODULE_PUSH_KEYS; it pushes the keys object for the following _LOAD_ATTR_MODULE_FROM_KEYS (nee _LOAD_ATTR_MODULE). This arrangement avoids having to recheck the keys version. _LOAD_ATTR_MODULE is renamed to _LOAD_ATTR_MODULE_FROM_KEYS; it loads the value from the keys object pushed by the preceding _CHECK_ATTR_MODULE_PUSH_KEYS at the cached index. --- Include/internal/pycore_opcode_metadata.h | 4 +- Include/internal/pycore_uop_ids.h | 141 +++++++++++---------- Include/internal/pycore_uop_metadata.h | 16 ++- Lib/test/test_capi/test_misc.py | 47 +++++-- Lib/test/test_generated_cases.py | 74 +++++++++++ Lib/test/test_opcache.py | 2 +- Python/bytecodes.c | 59 ++++++--- Python/executor_cases.c.h | 70 ++++++++-- Python/generated_cases.c.h | 33 +++-- Python/optimizer_analysis.c | 2 +- Python/optimizer_bytecodes.c | 10 +- Python/optimizer_cases.c.h | 47 +++++-- Python/specialize.c | 92 ++++++++------ Tools/cases_generator/generators_common.py | 15 +++ Tools/cases_generator/stack.py | 4 + 15 files changed, 437 insertions(+), 179 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 81dde66a6f26c2..28aa1120414337 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1492,7 +1492,7 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) { return 0; } case LOAD_ATTR_MODULE: { - *effect = Py_MAX(0, (oparg & 1)); + *effect = Py_MAX(1, (oparg & 1)); return 0; } case LOAD_ATTR_NONDESCRIPTOR_NO_DICT: { @@ -2271,7 +2271,7 @@ _PyOpcode_macro_expansion[256] = { [LOAD_ATTR_METHOD_LAZY_DICT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_METHOD_LAZY_DICT, 1, 3 }, { _LOAD_ATTR_METHOD_LAZY_DICT, 4, 5 } } }, [LOAD_ATTR_METHOD_NO_DICT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_METHOD_NO_DICT, 4, 5 } } }, [LOAD_ATTR_METHOD_WITH_VALUES] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, 0, 0 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_METHOD_WITH_VALUES, 4, 5 } } }, - [LOAD_ATTR_MODULE] = { .nuops = 2, .uops = { { _CHECK_ATTR_MODULE, 2, 1 }, { _LOAD_ATTR_MODULE, 1, 3 } } }, + [LOAD_ATTR_MODULE] = { .nuops = 2, .uops = { { _CHECK_ATTR_MODULE_PUSH_KEYS, 2, 1 }, { _LOAD_ATTR_MODULE_FROM_KEYS, 1, 3 } } }, [LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_NONDESCRIPTOR_NO_DICT, 4, 5 } } }, [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, 0, 0 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, 4, 5 } } }, [LOAD_ATTR_PROPERTY] = { .nuops = 5, .uops = { { _CHECK_PEP_523, 0, 0 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_PROPERTY_FRAME, 4, 5 }, { _SAVE_RETURN_OFFSET, 7, 9 }, { _PUSH_FRAME, 0, 0 } } }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index fab4ce6a25b347..45563585dd5681 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -55,7 +55,7 @@ extern "C" { #define _CHECK_AND_ALLOCATE_OBJECT 327 #define _CHECK_ATTR_CLASS 328 #define _CHECK_ATTR_METHOD_LAZY_DICT 329 -#define _CHECK_ATTR_MODULE 330 +#define _CHECK_ATTR_MODULE_PUSH_KEYS 330 #define _CHECK_ATTR_WITH_HINT 331 #define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 332 #define _CHECK_EG_MATCH CHECK_EG_MATCH @@ -186,115 +186,116 @@ extern "C" { #define _LOAD_ATTR_METHOD_NO_DICT 416 #define _LOAD_ATTR_METHOD_WITH_VALUES 417 #define _LOAD_ATTR_MODULE 418 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 419 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 420 -#define _LOAD_ATTR_PROPERTY_FRAME 421 -#define _LOAD_ATTR_SLOT 422 -#define _LOAD_ATTR_SLOT_0 423 -#define _LOAD_ATTR_SLOT_1 424 -#define _LOAD_ATTR_WITH_HINT 425 +#define _LOAD_ATTR_MODULE_FROM_KEYS 419 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 420 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 421 +#define _LOAD_ATTR_PROPERTY_FRAME 422 +#define _LOAD_ATTR_SLOT 423 +#define _LOAD_ATTR_SLOT_0 424 +#define _LOAD_ATTR_SLOT_1 425 +#define _LOAD_ATTR_WITH_HINT 426 #define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS -#define _LOAD_BYTECODE 426 +#define _LOAD_BYTECODE 427 #define _LOAD_COMMON_CONSTANT LOAD_COMMON_CONSTANT #define _LOAD_CONST LOAD_CONST #define _LOAD_CONST_IMMORTAL LOAD_CONST_IMMORTAL -#define _LOAD_CONST_INLINE 427 -#define _LOAD_CONST_INLINE_BORROW 428 -#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 429 -#define _LOAD_CONST_INLINE_WITH_NULL 430 +#define _LOAD_CONST_INLINE 428 +#define _LOAD_CONST_INLINE_BORROW 429 +#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 430 +#define _LOAD_CONST_INLINE_WITH_NULL 431 #define _LOAD_DEREF LOAD_DEREF -#define _LOAD_FAST 431 -#define _LOAD_FAST_0 432 -#define _LOAD_FAST_1 433 -#define _LOAD_FAST_2 434 -#define _LOAD_FAST_3 435 -#define _LOAD_FAST_4 436 -#define _LOAD_FAST_5 437 -#define _LOAD_FAST_6 438 -#define _LOAD_FAST_7 439 +#define _LOAD_FAST 432 +#define _LOAD_FAST_0 433 +#define _LOAD_FAST_1 434 +#define _LOAD_FAST_2 435 +#define _LOAD_FAST_3 436 +#define _LOAD_FAST_4 437 +#define _LOAD_FAST_5 438 +#define _LOAD_FAST_6 439 +#define _LOAD_FAST_7 440 #define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR #define _LOAD_FAST_CHECK LOAD_FAST_CHECK #define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST #define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF #define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS -#define _LOAD_GLOBAL 440 -#define _LOAD_GLOBAL_BUILTINS 441 -#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 442 -#define _LOAD_GLOBAL_MODULE 443 -#define _LOAD_GLOBAL_MODULE_FROM_KEYS 444 +#define _LOAD_GLOBAL 441 +#define _LOAD_GLOBAL_BUILTINS 442 +#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 443 +#define _LOAD_GLOBAL_MODULE 444 +#define _LOAD_GLOBAL_MODULE_FROM_KEYS 445 #define _LOAD_LOCALS LOAD_LOCALS #define _LOAD_NAME LOAD_NAME -#define _LOAD_SMALL_INT 445 -#define _LOAD_SMALL_INT_0 446 -#define _LOAD_SMALL_INT_1 447 -#define _LOAD_SMALL_INT_2 448 -#define _LOAD_SMALL_INT_3 449 +#define _LOAD_SMALL_INT 446 +#define _LOAD_SMALL_INT_0 447 +#define _LOAD_SMALL_INT_1 448 +#define _LOAD_SMALL_INT_2 449 +#define _LOAD_SMALL_INT_3 450 #define _LOAD_SPECIAL LOAD_SPECIAL #define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR #define _LOAD_SUPER_ATTR_METHOD LOAD_SUPER_ATTR_METHOD -#define _MAKE_CALLARGS_A_TUPLE 450 +#define _MAKE_CALLARGS_A_TUPLE 451 #define _MAKE_CELL MAKE_CELL #define _MAKE_FUNCTION MAKE_FUNCTION -#define _MAKE_WARM 451 +#define _MAKE_WARM 452 #define _MAP_ADD MAP_ADD #define _MATCH_CLASS MATCH_CLASS #define _MATCH_KEYS MATCH_KEYS #define _MATCH_MAPPING MATCH_MAPPING #define _MATCH_SEQUENCE MATCH_SEQUENCE -#define _MAYBE_EXPAND_METHOD 452 -#define _MAYBE_EXPAND_METHOD_KW 453 -#define _MONITOR_CALL 454 -#define _MONITOR_JUMP_BACKWARD 455 -#define _MONITOR_RESUME 456 +#define _MAYBE_EXPAND_METHOD 453 +#define _MAYBE_EXPAND_METHOD_KW 454 +#define _MONITOR_CALL 455 +#define _MONITOR_JUMP_BACKWARD 456 +#define _MONITOR_RESUME 457 #define _NOP NOP #define _POP_EXCEPT POP_EXCEPT -#define _POP_JUMP_IF_FALSE 457 -#define _POP_JUMP_IF_TRUE 458 +#define _POP_JUMP_IF_FALSE 458 +#define _POP_JUMP_IF_TRUE 459 #define _POP_TOP POP_TOP -#define _POP_TOP_LOAD_CONST_INLINE_BORROW 459 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW 460 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 460 +#define _PUSH_FRAME 461 #define _PUSH_NULL PUSH_NULL -#define _PY_FRAME_GENERAL 461 -#define _PY_FRAME_KW 462 -#define _QUICKEN_RESUME 463 -#define _REPLACE_WITH_TRUE 464 +#define _PY_FRAME_GENERAL 462 +#define _PY_FRAME_KW 463 +#define _QUICKEN_RESUME 464 +#define _REPLACE_WITH_TRUE 465 #define _RESUME_CHECK RESUME_CHECK #define _RETURN_GENERATOR RETURN_GENERATOR #define _RETURN_VALUE RETURN_VALUE -#define _SAVE_RETURN_OFFSET 465 -#define _SEND 466 -#define _SEND_GEN_FRAME 467 +#define _SAVE_RETURN_OFFSET 466 +#define _SEND 467 +#define _SEND_GEN_FRAME 468 #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _START_EXECUTOR 468 -#define _STORE_ATTR 469 -#define _STORE_ATTR_INSTANCE_VALUE 470 -#define _STORE_ATTR_SLOT 471 -#define _STORE_ATTR_WITH_HINT 472 +#define _START_EXECUTOR 469 +#define _STORE_ATTR 470 +#define _STORE_ATTR_INSTANCE_VALUE 471 +#define _STORE_ATTR_SLOT 472 +#define _STORE_ATTR_WITH_HINT 473 #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 473 -#define _STORE_FAST_0 474 -#define _STORE_FAST_1 475 -#define _STORE_FAST_2 476 -#define _STORE_FAST_3 477 -#define _STORE_FAST_4 478 -#define _STORE_FAST_5 479 -#define _STORE_FAST_6 480 -#define _STORE_FAST_7 481 +#define _STORE_FAST 474 +#define _STORE_FAST_0 475 +#define _STORE_FAST_1 476 +#define _STORE_FAST_2 477 +#define _STORE_FAST_3 478 +#define _STORE_FAST_4 479 +#define _STORE_FAST_5 480 +#define _STORE_FAST_6 481 +#define _STORE_FAST_7 482 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME -#define _STORE_SLICE 482 -#define _STORE_SUBSCR 483 +#define _STORE_SLICE 483 +#define _STORE_SUBSCR 484 #define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT #define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT #define _SWAP SWAP -#define _TIER2_RESUME_CHECK 484 -#define _TO_BOOL 485 +#define _TIER2_RESUME_CHECK 485 +#define _TO_BOOL 486 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT #define _TO_BOOL_LIST TO_BOOL_LIST @@ -304,13 +305,13 @@ extern "C" { #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 486 +#define _UNPACK_SEQUENCE 487 #define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START #define _YIELD_VALUE YIELD_VALUE -#define MAX_UOP_ID 486 +#define MAX_UOP_ID 487 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 89fce193f40bd8..dd775d3f7d3cdd 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -152,8 +152,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_ATTR_INSTANCE_VALUE_0] = HAS_DEOPT_FLAG, [_LOAD_ATTR_INSTANCE_VALUE_1] = HAS_DEOPT_FLAG, [_LOAD_ATTR_INSTANCE_VALUE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_OPARG_AND_1_FLAG, - [_CHECK_ATTR_MODULE] = HAS_DEOPT_FLAG, - [_LOAD_ATTR_MODULE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, + [_CHECK_ATTR_MODULE_PUSH_KEYS] = HAS_DEOPT_FLAG, + [_LOAD_ATTR_MODULE_FROM_KEYS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_CHECK_ATTR_WITH_HINT] = HAS_EXIT_FLAG, [_LOAD_ATTR_WITH_HINT] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG, [_LOAD_ATTR_SLOT_0] = HAS_DEOPT_FLAG, @@ -283,6 +283,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_CHECK_FUNCTION] = HAS_DEOPT_FLAG, [_LOAD_GLOBAL_MODULE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_LOAD_GLOBAL_BUILTINS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, + [_LOAD_ATTR_MODULE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_INTERNAL_INCREMENT_OPT_COUNTER] = 0, [_DYNAMIC_EXIT] = HAS_ESCAPES_FLAG, [_START_EXECUTOR] = 0, @@ -346,7 +347,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_CHECK_AND_ALLOCATE_OBJECT] = "_CHECK_AND_ALLOCATE_OBJECT", [_CHECK_ATTR_CLASS] = "_CHECK_ATTR_CLASS", [_CHECK_ATTR_METHOD_LAZY_DICT] = "_CHECK_ATTR_METHOD_LAZY_DICT", - [_CHECK_ATTR_MODULE] = "_CHECK_ATTR_MODULE", + [_CHECK_ATTR_MODULE_PUSH_KEYS] = "_CHECK_ATTR_MODULE_PUSH_KEYS", [_CHECK_ATTR_WITH_HINT] = "_CHECK_ATTR_WITH_HINT", [_CHECK_CALL_BOUND_METHOD_EXACT_ARGS] = "_CHECK_CALL_BOUND_METHOD_EXACT_ARGS", [_CHECK_EG_MATCH] = "_CHECK_EG_MATCH", @@ -459,6 +460,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_LOAD_ATTR_METHOD_NO_DICT] = "_LOAD_ATTR_METHOD_NO_DICT", [_LOAD_ATTR_METHOD_WITH_VALUES] = "_LOAD_ATTR_METHOD_WITH_VALUES", [_LOAD_ATTR_MODULE] = "_LOAD_ATTR_MODULE", + [_LOAD_ATTR_MODULE_FROM_KEYS] = "_LOAD_ATTR_MODULE_FROM_KEYS", [_LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = "_LOAD_ATTR_NONDESCRIPTOR_NO_DICT", [_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = "_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", [_LOAD_ATTR_PROPERTY_FRAME] = "_LOAD_ATTR_PROPERTY_FRAME", @@ -845,10 +847,10 @@ int _PyUop_num_popped(int opcode, int oparg) return 1; case _LOAD_ATTR_INSTANCE_VALUE: return 1; - case _CHECK_ATTR_MODULE: + case _CHECK_ATTR_MODULE_PUSH_KEYS: return 0; - case _LOAD_ATTR_MODULE: - return 1; + case _LOAD_ATTR_MODULE_FROM_KEYS: + return 2; case _CHECK_ATTR_WITH_HINT: return 0; case _LOAD_ATTR_WITH_HINT: @@ -1107,6 +1109,8 @@ int _PyUop_num_popped(int opcode, int oparg) return 0; case _LOAD_GLOBAL_BUILTINS: return 0; + case _LOAD_ATTR_MODULE: + return 1; case _INTERNAL_INCREMENT_OPT_COUNTER: return 1; case _DYNAMIC_EXIT: diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 61512e610f46f2..ada30181aeeca9 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -48,6 +48,8 @@ # Skip this test if the _testcapi module isn't available. _testcapi = import_helper.import_module('_testcapi') +from _testcapi import HeapCTypeSubclass, HeapCTypeSubclassWithFinalizer + import _testlimitedcapi import _testinternalcapi @@ -653,9 +655,9 @@ def test_c_subclass_of_heap_ctype_with_tpdealloc_decrefs_once(self): self.assertEqual(type_refcnt - 1, sys.getrefcount(_testcapi.HeapCTypeSubclass)) def test_c_subclass_of_heap_ctype_with_del_modifying_dunder_class_only_decrefs_once(self): - subclass_instance = _testcapi.HeapCTypeSubclassWithFinalizer() - type_refcnt = sys.getrefcount(_testcapi.HeapCTypeSubclassWithFinalizer) - new_type_refcnt = sys.getrefcount(_testcapi.HeapCTypeSubclass) + subclass_instance = HeapCTypeSubclassWithFinalizer() + type_refcnt = sys.getrefcount(HeapCTypeSubclassWithFinalizer) + new_type_refcnt = sys.getrefcount(HeapCTypeSubclass) # Test that subclass instance was fully created self.assertEqual(subclass_instance.value, 10) @@ -665,19 +667,46 @@ def test_c_subclass_of_heap_ctype_with_del_modifying_dunder_class_only_decrefs_o del subclass_instance # Test that setting __class__ modified the reference counts of the types + # + # This is highly sensitive to implementation details and may break in the future. + # + # We expect the refcount on the old type, HeapCTypeSubclassWithFinalizer, to + # remain the same: the finalizer gets a strong reference (+1) when it gets the + # type from the module and setting __class__ decrements the refcount (-1). + # + # We expect the refcount on the new type, HeapCTypeSubclass, to increase by 2: + # the finalizer get a strong reference (+1) when it gets the type from the + # module and setting __class__ increments the refcount (+1). + expected_type_refcnt = type_refcnt + expected_new_type_refcnt = new_type_refcnt + 2 + + if not Py_GIL_DISABLED: + # In default builds the result returned from sys.getrefcount + # includes a temporary reference that is created by the interpreter + # when it pushes its argument on the operand stack. This temporary + # reference is not included in the result returned by Py_REFCNT, which + # is used in the finalizer. + # + # In free-threaded builds the result returned from sys.getrefcount + # does not include the temporary reference. Types use deferred + # refcounting and the interpreter will not create a new reference + # for deferred values on the operand stack. + expected_type_refcnt -= 1 + expected_new_type_refcnt -= 1 + if support.Py_DEBUG: # gh-89373: In debug mode, _Py_Dealloc() keeps a strong reference # to the type while calling tp_dealloc() - self.assertEqual(type_refcnt, _testcapi.HeapCTypeSubclassWithFinalizer.refcnt_in_del) - else: - self.assertEqual(type_refcnt - 1, _testcapi.HeapCTypeSubclassWithFinalizer.refcnt_in_del) - self.assertEqual(new_type_refcnt + 1, _testcapi.HeapCTypeSubclass.refcnt_in_del) + expected_type_refcnt += 1 + + self.assertEqual(expected_type_refcnt, HeapCTypeSubclassWithFinalizer.refcnt_in_del) + self.assertEqual(expected_new_type_refcnt, HeapCTypeSubclass.refcnt_in_del) # Test that the original type already has decreased its refcnt - self.assertEqual(type_refcnt - 1, sys.getrefcount(_testcapi.HeapCTypeSubclassWithFinalizer)) + self.assertEqual(type_refcnt - 1, sys.getrefcount(HeapCTypeSubclassWithFinalizer)) # Test that subtype_dealloc decref the newly assigned __class__ only once - self.assertEqual(new_type_refcnt, sys.getrefcount(_testcapi.HeapCTypeSubclass)) + self.assertEqual(new_type_refcnt, sys.getrefcount(HeapCTypeSubclass)) def test_heaptype_with_setattro(self): obj = _testcapi.HeapCTypeSetattr() diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 66862ec17cca98..9c65e81dfe4be1 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -1639,6 +1639,80 @@ def test_escaping_call_next_to_cmacro(self): """ self.run_cases_test(input, output) + def test_pop_dead_inputs_all_live(self): + input = """ + inst(OP, (a, b --)) { + POP_DEAD_INPUTS(); + HAM(a, b); + INPUTS_DEAD(); + } + """ + output = """ + TARGET(OP) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(OP); + _PyStackRef a; + _PyStackRef b; + b = stack_pointer[-1]; + a = stack_pointer[-2]; + HAM(a, b); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + DISPATCH(); + } + """ + self.run_cases_test(input, output) + + def test_pop_dead_inputs_some_live(self): + input = """ + inst(OP, (a, b, c --)) { + POP_DEAD_INPUTS(); + HAM(a); + INPUTS_DEAD(); + } + """ + output = """ + TARGET(OP) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(OP); + _PyStackRef a; + a = stack_pointer[-3]; + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + HAM(a); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + DISPATCH(); + } + """ + self.run_cases_test(input, output) + + def test_pop_dead_inputs_with_output(self): + input = """ + inst(OP, (a, b -- c)) { + POP_DEAD_INPUTS(); + c = SPAM(); + } + """ + output = """ + TARGET(OP) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(OP); + _PyStackRef c; + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + c = SPAM(); + stack_pointer[0] = c; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); + DISPATCH(); + } + """ + self.run_cases_test(input, output) + class TestGeneratedAbstractCases(unittest.TestCase): def setUp(self) -> None: diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index 50b5f365165921..0a7557adc4763b 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -892,7 +892,7 @@ def write(items): opname = "LOAD_ATTR_METHOD_WITH_VALUES" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization + @requires_specialization_ft def test_load_attr_module(self): def get_items(): items = [] diff --git a/Python/bytecodes.c b/Python/bytecodes.c index f0eb5405faeff5..772b46d17ec198 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2070,7 +2070,7 @@ dummy_func( }; specializing op(_SPECIALIZE_LOAD_ATTR, (counter/1, owner -- owner)) { - #if ENABLE_SPECIALIZATION + #if ENABLE_SPECIALIZATION_FT if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); next_instr = this_instr; @@ -2079,7 +2079,7 @@ dummy_func( } OPCODE_DEFERRED_INC(LOAD_ATTR); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION */ + #endif /* ENABLE_SPECIALIZATION_FT */ } op(_LOAD_ATTR, (owner -- attr, self_or_null if (oparg & 1))) { @@ -2158,33 +2158,43 @@ dummy_func( _LOAD_ATTR_INSTANCE_VALUE + unused/5; // Skip over rest of cache - op(_CHECK_ATTR_MODULE, (dict_version/2, owner -- owner)) { + op(_CHECK_ATTR_MODULE_PUSH_KEYS, (dict_version/2, owner -- owner, mod_keys: PyDictKeysObject *)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); DEOPT_IF(Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro); PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; assert(dict != NULL); - DEOPT_IF(dict->ma_keys->dk_version != dict_version); - } - - op(_LOAD_ATTR_MODULE, (index/1, owner -- attr, null if (oparg & 1))) { - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; - assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); - assert(index < dict->ma_keys->dk_nentries); - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index; - PyObject *attr_o = ep->me_value; + PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); + DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != dict_version); + mod_keys = keys; + } + + op(_LOAD_ATTR_MODULE_FROM_KEYS, (index/1, owner, mod_keys: PyDictKeysObject * -- attr, null if (oparg & 1))) { + assert(mod_keys->dk_kind == DICT_KEYS_UNICODE); + assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(mod_keys->dk_nentries)); + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(mod_keys) + index; + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value); + DEAD(mod_keys); + // Clear mod_keys from stack in case we need to deopt + POP_DEAD_INPUTS(); DEOPT_IF(attr_o == NULL); - STAT_INC(LOAD_ATTR, hit); + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr); + if (!increfed) { + DEOPT_IF(true); + } + #else Py_INCREF(attr_o); attr = PyStackRef_FromPyObjectSteal(attr_o); + #endif + STAT_INC(LOAD_ATTR, hit); null = PyStackRef_NULL; - DECREF_INPUTS(); + PyStackRef_CLOSE(owner); } macro(LOAD_ATTR_MODULE) = unused/1 + - _CHECK_ATTR_MODULE + - _LOAD_ATTR_MODULE + + _CHECK_ATTR_MODULE_PUSH_KEYS + + _LOAD_ATTR_MODULE_FROM_KEYS + unused/5; op(_CHECK_ATTR_WITH_HINT, (owner -- owner)) { @@ -4963,6 +4973,21 @@ dummy_func( null = PyStackRef_NULL; } + tier2 op(_LOAD_ATTR_MODULE, (index/1, owner -- attr, null if (oparg & 1))) { + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; + assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); + assert(index < dict->ma_keys->dk_nentries); + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index; + PyObject *attr_o = ep->me_value; + DEOPT_IF(attr_o == NULL); + STAT_INC(LOAD_ATTR, hit); + Py_INCREF(attr_o); + attr = PyStackRef_FromPyObjectSteal(attr_o); + null = PyStackRef_NULL; + DECREF_INPUTS(); + } + /* Internal -- for testing executors */ op(_INTERNAL_INCREMENT_OPT_COUNTER, (opt --)) { _PyCounterOptimizerObject *exe = (_PyCounterOptimizerObject *)PyStackRef_AsPyObjectBorrow(opt); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 19ba67a8af6769..55e9c3aa2db64d 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2641,8 +2641,9 @@ /* _LOAD_ATTR_INSTANCE_VALUE is split on (oparg & 1) */ - case _CHECK_ATTR_MODULE: { + case _CHECK_ATTR_MODULE_PUSH_KEYS: { _PyStackRef owner; + PyDictKeysObject *mod_keys; owner = stack_pointer[-1]; uint32_t dict_version = (uint32_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); @@ -2652,33 +2653,51 @@ } PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; assert(dict != NULL); - if (dict->ma_keys->dk_version != dict_version) { + PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != dict_version) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } + mod_keys = keys; + stack_pointer[0].bits = (uintptr_t)mod_keys; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); break; } - case _LOAD_ATTR_MODULE: { + case _LOAD_ATTR_MODULE_FROM_KEYS: { + PyDictKeysObject *mod_keys; _PyStackRef owner; _PyStackRef attr; _PyStackRef null = PyStackRef_NULL; oparg = CURRENT_OPARG(); - owner = stack_pointer[-1]; + mod_keys = (PyDictKeysObject *)stack_pointer[-1].bits; + owner = stack_pointer[-2]; uint16_t index = (uint16_t)CURRENT_OPERAND0(); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; - assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); - assert(index < dict->ma_keys->dk_nentries); - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index; - PyObject *attr_o = ep->me_value; + assert(mod_keys->dk_kind == DICT_KEYS_UNICODE); + assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(mod_keys->dk_nentries)); + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(mod_keys) + index; + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value); + // Clear mod_keys from stack in case we need to deopt + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); if (attr_o == NULL) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - STAT_INC(LOAD_ATTR, hit); + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr); + if (!increfed) { + if (true) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + } + #else Py_INCREF(attr_o); attr = PyStackRef_FromPyObjectSteal(attr_o); + #endif + STAT_INC(LOAD_ATTR, hit); null = PyStackRef_NULL; PyStackRef_CLOSE(owner); stack_pointer[-1] = attr; @@ -5928,6 +5947,35 @@ break; } + case _LOAD_ATTR_MODULE: { + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef null = PyStackRef_NULL; + oparg = CURRENT_OPARG(); + owner = stack_pointer[-1]; + uint16_t index = (uint16_t)CURRENT_OPERAND0(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; + assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); + assert(index < dict->ma_keys->dk_nentries); + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index; + PyObject *attr_o = ep->me_value; + if (attr_o == NULL) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(LOAD_ATTR, hit); + Py_INCREF(attr_o); + attr = PyStackRef_FromPyObjectSteal(attr_o); + null = PyStackRef_NULL; + PyStackRef_CLOSE(owner); + stack_pointer[-1] = attr; + if (oparg & 1) stack_pointer[0] = null; + stack_pointer += (oparg & 1); + assert(WITHIN_STACK_BOUNDS()); + break; + } + case _INTERNAL_INCREMENT_OPT_COUNTER: { _PyStackRef opt; opt = stack_pointer[-1]; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 51227c9868b8cc..94343f953221eb 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -5200,7 +5200,7 @@ owner = stack_pointer[-1]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION + #if ENABLE_SPECIALIZATION_FT if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); next_instr = this_instr; @@ -5211,7 +5211,7 @@ } OPCODE_DEFERRED_INC(LOAD_ATTR); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION */ + #endif /* ENABLE_SPECIALIZATION_FT */ } /* Skip 8 cache entries */ // _LOAD_ATTR @@ -5553,10 +5553,11 @@ INSTRUCTION_STATS(LOAD_ATTR_MODULE); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); _PyStackRef owner; + PyDictKeysObject *mod_keys; _PyStackRef attr; _PyStackRef null = PyStackRef_NULL; /* Skip 1 cache entry */ - // _CHECK_ATTR_MODULE + // _CHECK_ATTR_MODULE_PUSH_KEYS { owner = stack_pointer[-1]; uint32_t dict_version = read_u32(&this_instr[2].cache); @@ -5564,21 +5565,29 @@ DEOPT_IF(Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro, LOAD_ATTR); PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; assert(dict != NULL); - DEOPT_IF(dict->ma_keys->dk_version != dict_version, LOAD_ATTR); + PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); + DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != dict_version, LOAD_ATTR); + mod_keys = keys; } - // _LOAD_ATTR_MODULE + // _LOAD_ATTR_MODULE_FROM_KEYS { uint16_t index = read_u16(&this_instr[4].cache); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; - assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); - assert(index < dict->ma_keys->dk_nentries); - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index; - PyObject *attr_o = ep->me_value; + assert(mod_keys->dk_kind == DICT_KEYS_UNICODE); + assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(mod_keys->dk_nentries)); + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(mod_keys) + index; + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value); + // Clear mod_keys from stack in case we need to deopt DEOPT_IF(attr_o == NULL, LOAD_ATTR); - STAT_INC(LOAD_ATTR, hit); + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr); + if (!increfed) { + DEOPT_IF(true, LOAD_ATTR); + } + #else Py_INCREF(attr_o); attr = PyStackRef_FromPyObjectSteal(attr_o); + #endif + STAT_INC(LOAD_ATTR, hit); null = PyStackRef_NULL; PyStackRef_CLOSE(owner); } diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index a4a0472b64e57c..0ef15c630e91db 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -95,7 +95,7 @@ type_watcher_callback(PyTypeObject* type) static PyObject * convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj) { - assert(inst->opcode == _LOAD_GLOBAL_MODULE || inst->opcode == _LOAD_GLOBAL_BUILTINS || inst->opcode == _LOAD_ATTR_MODULE); + assert(inst->opcode == _LOAD_GLOBAL_MODULE || inst->opcode == _LOAD_GLOBAL_BUILTINS || inst->opcode == _LOAD_ATTR_MODULE_FROM_KEYS); assert(PyDict_CheckExact(obj)); PyDictObject *dict = (PyDictObject *)obj; assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 42bdbd9ca8d0cd..0b8aff02367e31 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -492,8 +492,9 @@ dummy_func(void) { (void)owner; } - op(_CHECK_ATTR_MODULE, (dict_version/2, owner -- owner)) { + op(_CHECK_ATTR_MODULE_PUSH_KEYS, (dict_version/2, owner -- owner, mod_keys)) { (void)dict_version; + mod_keys = sym_new_not_null(ctx); if (sym_is_const(owner)) { PyObject *cnst = sym_get_const(owner); if (PyModule_CheckExact(cnst)) { @@ -515,12 +516,12 @@ dummy_func(void) { self_or_null = sym_new_unknown(ctx); } - op(_LOAD_ATTR_MODULE, (index/1, owner -- attr, null if (oparg & 1))) { + op(_LOAD_ATTR_MODULE_FROM_KEYS, (index/1, owner, mod_keys -- attr, null if (oparg & 1))) { (void)index; null = sym_new_null(ctx); attr = NULL; if (this_instr[-1].opcode == _NOP) { - // Preceding _CHECK_ATTR_MODULE was removed: mod is const and dict is watched. + // Preceding _CHECK_ATTR_MODULE_PUSH_KEYS was removed: mod is const and dict is watched. assert(sym_is_const(owner)); PyModuleObject *mod = (PyModuleObject *)sym_get_const(owner); assert(PyModule_CheckExact(mod)); @@ -530,6 +531,9 @@ dummy_func(void) { this_instr[-1].opcode = _POP_TOP; attr = sym_new_const(ctx, res); } + else { + this_instr->opcode = _LOAD_ATTR_MODULE; + } } if (attr == NULL) { /* No conversion made. We don't know what `attr` is. */ diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index f77a5aa35bdf82..f4fbe8c8aa0480 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1134,61 +1134,74 @@ break; } - case _CHECK_ATTR_MODULE: { + case _CHECK_ATTR_MODULE_PUSH_KEYS: { _Py_UopsSymbol *owner; + _Py_UopsSymbol *mod_keys; owner = stack_pointer[-1]; uint32_t dict_version = (uint32_t)this_instr->operand0; (void)dict_version; + mod_keys = sym_new_not_null(ctx); if (sym_is_const(owner)) { PyObject *cnst = sym_get_const(owner); if (PyModule_CheckExact(cnst)) { PyModuleObject *mod = (PyModuleObject *)cnst; PyObject *dict = mod->md_dict; + stack_pointer[0] = mod_keys; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); uint64_t watched_mutations = get_mutations(dict); if (watched_mutations < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) { PyDict_Watch(GLOBALS_WATCHER_ID, dict); _Py_BloomFilter_Add(dependencies, dict); this_instr->opcode = _NOP; } + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); } } + stack_pointer[0] = mod_keys; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); break; } - case _LOAD_ATTR_MODULE: { + case _LOAD_ATTR_MODULE_FROM_KEYS: { _Py_UopsSymbol *owner; _Py_UopsSymbol *attr; _Py_UopsSymbol *null = NULL; - owner = stack_pointer[-1]; + owner = stack_pointer[-2]; uint16_t index = (uint16_t)this_instr->operand0; (void)index; null = sym_new_null(ctx); attr = NULL; if (this_instr[-1].opcode == _NOP) { - // Preceding _CHECK_ATTR_MODULE was removed: mod is const and dict is watched. + // Preceding _CHECK_ATTR_MODULE_PUSH_KEYS was removed: mod is const and dict is watched. assert(sym_is_const(owner)); PyModuleObject *mod = (PyModuleObject *)sym_get_const(owner); assert(PyModule_CheckExact(mod)); PyObject *dict = mod->md_dict; - stack_pointer[-1] = attr; - if (oparg & 1) stack_pointer[0] = null; - stack_pointer += (oparg & 1); + stack_pointer[-2] = attr; + if (oparg & 1) stack_pointer[-1] = null; + stack_pointer += -1 + (oparg & 1); assert(WITHIN_STACK_BOUNDS()); PyObject *res = convert_global_to_const(this_instr, dict); if (res != NULL) { this_instr[-1].opcode = _POP_TOP; attr = sym_new_const(ctx, res); } - stack_pointer += -(oparg & 1); + else { + this_instr->opcode = _LOAD_ATTR_MODULE; + } + stack_pointer += 1 - (oparg & 1); assert(WITHIN_STACK_BOUNDS()); } if (attr == NULL) { /* No conversion made. We don't know what `attr` is. */ attr = sym_new_not_null(ctx); } - stack_pointer[-1] = attr; - if (oparg & 1) stack_pointer[0] = null; - stack_pointer += (oparg & 1); + stack_pointer[-2] = attr; + if (oparg & 1) stack_pointer[-1] = null; + stack_pointer += -1 + (oparg & 1); assert(WITHIN_STACK_BOUNDS()); break; } @@ -2528,6 +2541,18 @@ break; } + case _LOAD_ATTR_MODULE: { + _Py_UopsSymbol *attr; + _Py_UopsSymbol *null = NULL; + attr = sym_new_not_null(ctx); + null = sym_new_null(ctx); + stack_pointer[-1] = attr; + if (oparg & 1) stack_pointer[0] = null; + stack_pointer += (oparg & 1); + assert(WITHIN_STACK_BOUNDS()); + break; + } + case _INTERNAL_INCREMENT_OPT_COUNTER: { stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); diff --git a/Python/specialize.c b/Python/specialize.c index fd182e7d7a9215..6eb298217ec2d3 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -738,22 +738,16 @@ unspecialize(_Py_CODEUNIT *instr) } static int function_kind(PyCodeObject *code); +#ifndef Py_GIL_DISABLED static bool function_check_args(PyObject *o, int expected_argcount, int opcode); static uint32_t function_get_version(PyObject *o, int opcode); +#endif static uint32_t type_get_version(PyTypeObject *t, int opcode); static int -specialize_module_load_attr( - PyObject *owner, _Py_CODEUNIT *instr, PyObject *name -) { +specialize_module_load_attr_lock_held(PyDictObject *dict, _Py_CODEUNIT *instr, PyObject *name) +{ _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); - PyModuleObject *m = (PyModuleObject *)owner; - assert((Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0); - PyDictObject *dict = (PyDictObject *)m->md_dict; - if (dict == NULL) { - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_NO_DICT); - return -1; - } if (dict->ma_keys->dk_kind != DICT_KEYS_UNICODE) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NON_STRING); return -1; @@ -773,19 +767,35 @@ specialize_module_load_attr( SPEC_FAIL_OUT_OF_RANGE); return -1; } - uint32_t keys_version = _PyDictKeys_GetVersionForCurrentState( - _PyInterpreterState_GET(), dict->ma_keys); + uint32_t keys_version = _PyDict_GetKeysVersionForCurrentState( + _PyInterpreterState_GET(), dict); if (keys_version == 0) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS); return -1; } write_u32(cache->version, keys_version); cache->index = (uint16_t)index; - instr->op.code = LOAD_ATTR_MODULE; + specialize(instr, LOAD_ATTR_MODULE); return 0; } - +static int +specialize_module_load_attr( + PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) +{ + PyModuleObject *m = (PyModuleObject *)owner; + assert((Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0); + PyDictObject *dict = (PyDictObject *)m->md_dict; + if (dict == NULL) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_NO_DICT); + return -1; + } + int result; + Py_BEGIN_CRITICAL_SECTION(dict); + result = specialize_module_load_attr_lock_held(dict, instr, name); + Py_END_CRITICAL_SECTION(); + return result; +} /* Attribute specialization */ @@ -968,7 +978,7 @@ specialize_dict_access( } write_u32(cache->version, type->tp_version_tag); cache->index = (uint16_t)offset; - instr->op.code = values_op; + specialize(instr, values_op); } else { PyDictObject *dict = _PyObject_GetManagedDict(owner); @@ -992,11 +1002,12 @@ specialize_dict_access( } cache->index = (uint16_t)index; write_u32(cache->version, type->tp_version_tag); - instr->op.code = hint_op; + specialize(instr, hint_op); } return 1; } +#ifndef Py_GIL_DISABLED static int specialize_attr_loadclassattr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* name, PyObject* descr, DescriptorClassification kind, bool is_method); static int specialize_class_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* name); @@ -1093,7 +1104,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na write_u32(lm_cache->type_version, type->tp_version_tag); /* borrowed */ write_obj(lm_cache->descr, fget); - instr->op.code = LOAD_ATTR_PROPERTY; + specialize(instr, LOAD_ATTR_PROPERTY); return 0; } case OBJECT_SLOT: @@ -1117,7 +1128,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na assert(offset > 0); cache->index = (uint16_t)offset; write_u32(cache->version, type->tp_version_tag); - instr->op.code = LOAD_ATTR_SLOT; + specialize(instr, LOAD_ATTR_SLOT); return 0; } case DUNDER_CLASS: @@ -1126,7 +1137,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na assert(offset == (uint16_t)offset); cache->index = (uint16_t)offset; write_u32(cache->version, type->tp_version_tag); - instr->op.code = LOAD_ATTR_SLOT; + specialize(instr, LOAD_ATTR_SLOT); return 0; } case OTHER_SLOT: @@ -1162,7 +1173,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na /* borrowed */ write_obj(lm_cache->descr, descr); write_u32(lm_cache->type_version, type->tp_version_tag); - instr->op.code = LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN; + specialize(instr, LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN); return 0; } case BUILTIN_CLASSMETHOD: @@ -1186,6 +1197,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na if (shadow) { goto try_instance; } + set_counter((_Py_BackoffCounter*)instr + 1, adaptive_counter_cooldown()); return 0; } Py_UNREACHABLE(); @@ -1197,14 +1209,14 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na } return -1; } +#endif // Py_GIL_DISABLED void _Py_Specialize_LoadAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *name) { - _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); PyObject *owner = PyStackRef_AsPyObjectBorrow(owner_st); - assert(ENABLE_SPECIALIZATION); + assert(ENABLE_SPECIALIZATION_FT); assert(_PyOpcode_Caches[LOAD_ATTR] == INLINE_CACHE_ENTRIES_LOAD_ATTR); PyTypeObject *type = Py_TYPE(owner); bool fail; @@ -1219,22 +1231,24 @@ _Py_Specialize_LoadAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *nam fail = specialize_module_load_attr(owner, instr, name); } else if (PyType_Check(owner)) { + #ifdef Py_GIL_DISABLED + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_EXPECTED_ERROR); + fail = true; + #else fail = specialize_class_load_attr(owner, instr, name); + #endif } else { + #ifdef Py_GIL_DISABLED + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_EXPECTED_ERROR); + fail = true; + #else fail = specialize_instance_load_attr(owner, instr, name); + #endif } if (fail) { - STAT_INC(LOAD_ATTR, failure); - assert(!PyErr_Occurred()); - instr->op.code = LOAD_ATTR; - cache->counter = adaptive_counter_backoff(cache->counter); - } - else { - STAT_INC(LOAD_ATTR, success); - assert(!PyErr_Occurred()); - cache->counter = adaptive_counter_cooldown(); + unspecialize(instr); } } @@ -1339,6 +1353,7 @@ _Py_Specialize_StoreAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *na cache->counter = adaptive_counter_cooldown(); } +#ifndef Py_GIL_DISABLED #ifdef Py_STATS static int @@ -1422,10 +1437,10 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, write_obj(cache->descr, descr); if (metaclass_check) { write_u32(cache->keys_version, Py_TYPE(cls)->tp_version_tag); - instr->op.code = LOAD_ATTR_CLASS_WITH_METACLASS_CHECK; + specialize(instr, LOAD_ATTR_CLASS_WITH_METACLASS_CHECK); } else { - instr->op.code = LOAD_ATTR_CLASS; + specialize(instr, LOAD_ATTR_CLASS); } return 0; #ifdef Py_STATS @@ -1461,7 +1476,7 @@ PyObject *descr, DescriptorClassification kind, bool is_method) return 0; } write_u32(cache->keys_version, keys_version); - instr->op.code = is_method ? LOAD_ATTR_METHOD_WITH_VALUES : LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES; + specialize(instr, is_method ? LOAD_ATTR_METHOD_WITH_VALUES : LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES); } else { Py_ssize_t dictoffset; @@ -1476,7 +1491,7 @@ PyObject *descr, DescriptorClassification kind, bool is_method) } } if (dictoffset == 0) { - instr->op.code = is_method ? LOAD_ATTR_METHOD_NO_DICT : LOAD_ATTR_NONDESCRIPTOR_NO_DICT; + specialize(instr, is_method ? LOAD_ATTR_METHOD_NO_DICT : LOAD_ATTR_NONDESCRIPTOR_NO_DICT); } else if (is_method) { PyObject *dict = *(PyObject **) ((char *)owner + dictoffset); @@ -1490,7 +1505,7 @@ PyObject *descr, DescriptorClassification kind, bool is_method) dictoffset -= MANAGED_DICT_OFFSET; assert(((uint16_t)dictoffset) == dictoffset); cache->dict_offset = (uint16_t)dictoffset; - instr->op.code = LOAD_ATTR_METHOD_LAZY_DICT; + specialize(instr, LOAD_ATTR_METHOD_LAZY_DICT); } else { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_CLASS_ATTR_SIMPLE); @@ -1516,6 +1531,9 @@ PyObject *descr, DescriptorClassification kind, bool is_method) return 1; } +#endif // Py_GIL_DISABLED + + static void specialize_load_global_lock_held( PyObject *globals, PyObject *builtins, @@ -1661,6 +1679,7 @@ function_kind(PyCodeObject *code) { return SIMPLE_FUNCTION; } +#ifndef Py_GIL_DISABLED /* Returning false indicates a failure. */ static bool function_check_args(PyObject *o, int expected_argcount, int opcode) @@ -1693,6 +1712,7 @@ function_get_version(PyObject *o, int opcode) } return version; } +#endif // Py_GIL_DISABLED /* Returning 0 indicates a failure. */ static uint32_t diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index dad2557e97a948..d17617cab0266b 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -120,6 +120,7 @@ def __init__(self, out: CWriter): "PyStackRef_AsPyObjectSteal": self.stackref_steal, "DISPATCH": self.dispatch, "INSTRUCTION_SIZE": self.instruction_size, + "POP_DEAD_INPUTS": self.pop_dead_inputs, } self.out = out @@ -348,6 +349,20 @@ def save_stack( self.emit_save(storage) return True + def pop_dead_inputs( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: Uop, + storage: Storage, + inst: Instruction | None, + ) -> bool: + next(tkn_iter) + next(tkn_iter) + next(tkn_iter) + storage.pop_dead_inputs(self.out) + return True + def emit_reload(self, storage: Storage) -> None: storage.reload(self.out) self._print_storage(storage) diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 286f47d0cfb11b..9471fe0e56f7d8 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -512,6 +512,10 @@ def flush(self, out: CWriter, cast_type: str = "uintptr_t", extract_bits: bool = self._push_defined_outputs() self.stack.flush(out, cast_type, extract_bits) + def pop_dead_inputs(self, out: CWriter, cast_type: str = "uintptr_t", extract_bits: bool = True) -> None: + self.clear_dead_inputs() + self.stack.flush(out, cast_type, extract_bits) + def save(self, out: CWriter) -> None: assert self.spilled >= 0 if self.spilled == 0: From c0264fc57c51e68015bef95a2208711356b57c1f Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Fri, 13 Dec 2024 23:36:47 -0800 Subject: [PATCH 023/376] gh-127747: Resolve BytesWarning in test.support.strace_helper (#127849) The strace_helper code has a _make_error function to simplify making StraceResult objects in error cases. That takes a details parameter which is either a caught OSError or `bytes`. If it's bytes, _make_error would implicitly coerce that to a str inside of a f-string, resulting in a BytesWarning. It's useful to see if it's an OSError or bytes when debugging, resolve by changing to format with repr(). This is an error message on an internal helper. A non-zero exit code occurs if the strace binary isn't found, and no events will be parsed in that case (there is no output). Handle that case by checking exit code before checking for events. Still asserting around events rather than returning false, so that hopefully if there's some change to `strace` that breaks the parsing, will see that as a test failure rather than silently loosing strace tests because they are auto-disabled. --- Lib/test/support/strace_helper.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Lib/test/support/strace_helper.py b/Lib/test/support/strace_helper.py index eab16ea3e2889f..798d6c6886962f 100644 --- a/Lib/test/support/strace_helper.py +++ b/Lib/test/support/strace_helper.py @@ -104,7 +104,7 @@ def _make_error(reason, details): return StraceResult( strace_returncode=-1, python_returncode=-1, - event_bytes=f"error({reason},details={details}) = -1".encode('utf-8'), + event_bytes= f"error({reason},details={details!r}) = -1".encode('utf-8'), stdout=res.out if res else b"", stderr=res.err if res else b"") @@ -179,9 +179,10 @@ def get_syscalls(code, strace_flags, prelude="", cleanup="", @cache def _can_strace(): res = strace_python("import sys; sys.exit(0)", [], check=False) - assert res.events(), "Should have parsed multiple calls" - - return res.strace_returncode == 0 and res.python_returncode == 0 + if res.strace_returncode == 0 and res.python_returncode == 0: + assert res.events(), "Should have parsed multiple calls" + return True + return False def requires_strace(): From 78e766f2e217572eacefba9ec31396b016aa88c2 Mon Sep 17 00:00:00 2001 From: Totosuki <116938397+totosuki@users.noreply.github.com> Date: Sat, 14 Dec 2024 16:38:54 +0900 Subject: [PATCH 024/376] Fix typo in docstring: quadruple double quotes (#127913) --- Lib/test/test_ssl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 59f37b3f9a7575..3f6f890bbdc658 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -151,7 +151,7 @@ def is_ubuntu(): if is_ubuntu(): def seclevel_workaround(*ctxs): - """"Lower security level to '1' and allow all ciphers for TLS 1.0/1""" + """Lower security level to '1' and allow all ciphers for TLS 1.0/1""" for ctx in ctxs: if ( hasattr(ctx, "minimum_version") and From e2325c9db0650fc06d909eb2b5930c0573f24f71 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 14 Dec 2024 16:28:26 +0300 Subject: [PATCH 025/376] gh-127852: add remark about ',' separator (#127854) Specify that it is valid for floats and ints with 'd' presentation and an error otherwise. Co-authored-by: Terry Jan Reedy --------- Co-authored-by: Terry Jan Reedy --- Doc/library/string.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Doc/library/string.rst b/Doc/library/string.rst index a000bb49f14800..913672a3ff2270 100644 --- a/Doc/library/string.rst +++ b/Doc/library/string.rst @@ -409,7 +409,9 @@ conversions, trailing zeros are not removed from the result. .. index:: single: , (comma); in string formatting -The ``','`` option signals the use of a comma for a thousands separator. +The ``','`` option signals the use of a comma for a thousands separator for +floating-point presentation types and for integer presentation type ``'d'``. +For other presentation types, this option is an error. For a locale aware separator, use the ``'n'`` integer presentation type instead. From 0ac40acec045c4ce780cf7d887fcbe4c661e82b7 Mon Sep 17 00:00:00 2001 From: Andrey Efremov Date: Sat, 14 Dec 2024 22:25:49 +0700 Subject: [PATCH 026/376] gh-127353: Allow to force color output on Windows V2 (#127926) --- Lib/_colorize.py | 17 ++--- Lib/test/test__colorize.py | 70 +++++++++++-------- ...-11-28-15-55-48.gh-issue-127353.i-XOXg.rst | 2 + 3 files changed, 51 insertions(+), 38 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 845fb57a90abb8..709081e25ec59b 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -32,14 +32,6 @@ def get_colors(colorize: bool = False) -> ANSIColors: def can_colorize() -> bool: - if sys.platform == "win32": - try: - import nt - - if not nt._supports_virtual_terminal(): - return False - except (ImportError, AttributeError): - return False if not sys.flags.ignore_environment: if os.environ.get("PYTHON_COLORS") == "0": return False @@ -58,6 +50,15 @@ def can_colorize() -> bool: if not hasattr(sys.stderr, "fileno"): return False + if sys.platform == "win32": + try: + import nt + + if not nt._supports_virtual_terminal(): + return False + except (ImportError, AttributeError): + return False + try: return os.isatty(sys.stderr.fileno()) except io.UnsupportedOperation: diff --git a/Lib/test/test__colorize.py b/Lib/test/test__colorize.py index d55b97ade68cef..1871775fa205a2 100644 --- a/Lib/test/test__colorize.py +++ b/Lib/test/test__colorize.py @@ -19,40 +19,50 @@ def tearDownModule(): class TestColorizeFunction(unittest.TestCase): @force_not_colorized def test_colorized_detection_checks_for_environment_variables(self): - if sys.platform == "win32": - virtual_patching = unittest.mock.patch("nt._supports_virtual_terminal", - return_value=True) - else: - virtual_patching = contextlib.nullcontext() - with virtual_patching: - - flags = unittest.mock.MagicMock(ignore_environment=False) - with (unittest.mock.patch("os.isatty") as isatty_mock, - unittest.mock.patch("sys.flags", flags), - unittest.mock.patch("_colorize.can_colorize", ORIGINAL_CAN_COLORIZE)): - isatty_mock.return_value = True - with unittest.mock.patch("os.environ", {'TERM': 'dumb'}): - self.assertEqual(_colorize.can_colorize(), False) - with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '1'}): - self.assertEqual(_colorize.can_colorize(), True) - with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '0'}): - self.assertEqual(_colorize.can_colorize(), False) - with unittest.mock.patch("os.environ", {'NO_COLOR': '1'}): + flags = unittest.mock.MagicMock(ignore_environment=False) + with (unittest.mock.patch("os.isatty") as isatty_mock, + unittest.mock.patch("sys.stderr") as stderr_mock, + unittest.mock.patch("sys.flags", flags), + unittest.mock.patch("_colorize.can_colorize", ORIGINAL_CAN_COLORIZE), + (unittest.mock.patch("nt._supports_virtual_terminal", return_value=False) + if sys.platform == "win32" else + contextlib.nullcontext()) as vt_mock): + + isatty_mock.return_value = True + stderr_mock.fileno.return_value = 2 + stderr_mock.isatty.return_value = True + with unittest.mock.patch("os.environ", {'TERM': 'dumb'}): + self.assertEqual(_colorize.can_colorize(), False) + with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '1'}): + self.assertEqual(_colorize.can_colorize(), True) + with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '0'}): + self.assertEqual(_colorize.can_colorize(), False) + with unittest.mock.patch("os.environ", {'NO_COLOR': '1'}): + self.assertEqual(_colorize.can_colorize(), False) + with unittest.mock.patch("os.environ", + {'NO_COLOR': '1', "PYTHON_COLORS": '1'}): + self.assertEqual(_colorize.can_colorize(), True) + with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1'}): + self.assertEqual(_colorize.can_colorize(), True) + with unittest.mock.patch("os.environ", + {'FORCE_COLOR': '1', 'NO_COLOR': '1'}): + self.assertEqual(_colorize.can_colorize(), False) + with unittest.mock.patch("os.environ", + {'FORCE_COLOR': '1', "PYTHON_COLORS": '0'}): + self.assertEqual(_colorize.can_colorize(), False) + + with unittest.mock.patch("os.environ", {}): + if sys.platform == "win32": self.assertEqual(_colorize.can_colorize(), False) - with unittest.mock.patch("os.environ", - {'NO_COLOR': '1', "PYTHON_COLORS": '1'}): + + vt_mock.return_value = True self.assertEqual(_colorize.can_colorize(), True) - with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1'}): + else: self.assertEqual(_colorize.can_colorize(), True) - with unittest.mock.patch("os.environ", - {'FORCE_COLOR': '1', 'NO_COLOR': '1'}): - self.assertEqual(_colorize.can_colorize(), False) - with unittest.mock.patch("os.environ", - {'FORCE_COLOR': '1', "PYTHON_COLORS": '0'}): - self.assertEqual(_colorize.can_colorize(), False) + isatty_mock.return_value = False - with unittest.mock.patch("os.environ", {}): - self.assertEqual(_colorize.can_colorize(), False) + stderr_mock.isatty.return_value = False + self.assertEqual(_colorize.can_colorize(), False) if __name__ == "__main__": diff --git a/Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst b/Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst new file mode 100644 index 00000000000000..88661b9a611071 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst @@ -0,0 +1,2 @@ +Allow to force color output on Windows using environment variables. Patch by +Andrey Efremov. From 7900a85019457c14e8c6abac532846bc9f26760d Mon Sep 17 00:00:00 2001 From: Steve C Date: Sun, 15 Dec 2024 07:28:43 -0500 Subject: [PATCH 027/376] Clarify ast docs to use a less confusing example for `ast.ParamSpec` (#127955) Fix typo in ast docs: ParamSpec defaults --- Doc/library/ast.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 22d8c87cb58e78..fd901e232855b5 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -1807,7 +1807,7 @@ aliases. .. doctest:: - >>> print(ast.dump(ast.parse("type Alias[**P = (int, str)] = Callable[P, int]"), indent=4)) + >>> print(ast.dump(ast.parse("type Alias[**P = [int, str]] = Callable[P, int]"), indent=4)) Module( body=[ TypeAlias( @@ -1815,7 +1815,7 @@ aliases. type_params=[ ParamSpec( name='P', - default_value=Tuple( + default_value=List( elts=[ Name(id='int', ctx=Load()), Name(id='str', ctx=Load())], From ab05beb8cea62636bd86f6f7cf1a82d7efca7162 Mon Sep 17 00:00:00 2001 From: Ed Nutting Date: Sun, 15 Dec 2024 14:51:03 +0100 Subject: [PATCH 028/376] gh-127599: Fix _Py_RefcntAdd missing calls to _Py_INCREF_STAT_INC/_Py_INCREF_IMMORTAL_STAT_INC (#127717) Previously, `_Py_RefcntAdd` hasn't called `_Py_INCREF_STAT_INC/_Py_INCREF_IMMORTAL_STAT_INC` which is incorrect. Now it has been fixed. --- Include/cpython/pystats.h | 6 ++++++ Include/internal/pycore_object.h | 5 +++++ .../2024-12-07-13-06-09.gh-issue-127599.tXCZb_.rst | 2 ++ 3 files changed, 13 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-07-13-06-09.gh-issue-127599.tXCZb_.rst diff --git a/Include/cpython/pystats.h b/Include/cpython/pystats.h index 2ae48002d720e9..29ef0c0e4d4e72 100644 --- a/Include/cpython/pystats.h +++ b/Include/cpython/pystats.h @@ -18,6 +18,12 @@ // // Define _PY_INTERPRETER macro to increment interpreter_increfs and // interpreter_decrefs. Otherwise, increment increfs and decrefs. +// +// The number of incref operations counted by `incref` and +// `interpreter_incref` is the number of increment operations, which is +// not equal to the total of all reference counts. A single increment +// operation may increase the reference count of an object by more than +// one. For example, see `_Py_RefcntAdd`. #ifndef Py_CPYTHON_PYSTATS_H # error "this header file must not be included directly" diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 668ea47ca727e2..d7d68f938a9f0a 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -131,6 +131,7 @@ extern void _Py_DecRefTotal(PyThreadState *); static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n) { if (_Py_IsImmortal(op)) { + _Py_INCREF_IMMORTAL_STAT_INC(); return; } #ifdef Py_REF_DEBUG @@ -159,6 +160,10 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n) _Py_atomic_add_ssize(&op->ob_ref_shared, (n << _Py_REF_SHARED_SHIFT)); } #endif + // Although the ref count was increased by `n` (which may be greater than 1) + // it is only a single increment (i.e. addition) operation, so only 1 refcnt + // increment operation is counted. + _Py_INCREF_STAT_INC(); } #define _Py_RefcntAdd(op, n) _Py_RefcntAdd(_PyObject_CAST(op), n) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-07-13-06-09.gh-issue-127599.tXCZb_.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-07-13-06-09.gh-issue-127599.tXCZb_.rst new file mode 100644 index 00000000000000..565ecb82c71926 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-07-13-06-09.gh-issue-127599.tXCZb_.rst @@ -0,0 +1,2 @@ +Fix statistics for increments of object reference counts (in particular, when +a reference count was increased by more than 1 in a single operation). From 3683b2f9e5972a2feb67a051fcf898a1b58a54fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns=20=F0=9F=87=B5=F0=9F=87=B8?= Date: Sun, 15 Dec 2024 15:40:19 +0000 Subject: [PATCH 029/376] getpath: Add comments highlighing details of the pyvenv.cfg detection (#127966) --- Modules/getpath.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Modules/getpath.py b/Modules/getpath.py index 7949fd813d0d07..b14f985a0d5f97 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -363,10 +363,20 @@ def search_up(prefix, *landmarks, test=isfile): venv_prefix = None pyvenvcfg = [] + # Search for the 'home' key in pyvenv.cfg. Currently, we don't consider the + # presence of a pyvenv.cfg file without a 'home' key to signify the + # existence of a virtual environment — we quietly ignore them. + # XXX: If we don't find a 'home' key, we don't look for another pyvenv.cfg! for line in pyvenvcfg: key, had_equ, value = line.partition('=') if had_equ and key.strip().lower() == 'home': + # Override executable_dir/real_executable_dir with the value from 'home'. + # These values may be later used to calculate prefix/base_prefix, if a more + # reliable source — like the runtime library (libpython) path — isn't available. executable_dir = real_executable_dir = value.strip() + # If base_executable — which points to the Python interpreted from + # the base installation — isn't set (eg. when embedded), try to find + # it in 'home'. if not base_executable: # First try to resolve symlinked executables, since that may be # more accurate than assuming the executable in 'home'. @@ -400,6 +410,7 @@ def search_up(prefix, *landmarks, test=isfile): break break else: + # We didn't find a 'home' key in pyvenv.cfg (no break), reset venv_prefix. venv_prefix = None From 7b8bd3b2b81f4aca63c5b603b56998f6b3ee2611 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Sun, 15 Dec 2024 17:11:50 +0000 Subject: [PATCH 030/376] gh-119786: Fix miscellaneous typos in `InternalDocs/interpreter_definition.md` (#127957) --- Tools/cases_generator/interpreter_definition.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tools/cases_generator/interpreter_definition.md b/Tools/cases_generator/interpreter_definition.md index d50c420307852f..7901f3d92e00bb 100644 --- a/Tools/cases_generator/interpreter_definition.md +++ b/Tools/cases_generator/interpreter_definition.md @@ -174,7 +174,7 @@ list of annotations and their meanings are as follows: * `override`. For external use by other interpreter definitions to override the current instruction definition. * `pure`. This instruction has no side effects. -* 'tierN'. This instruction only used by tier N interpreter. +* 'tierN'. This instruction is only used by the tier N interpreter. ### Special functions/macros @@ -393,7 +393,7 @@ which can be easily inserted. What is more complex is ensuring the correct stack and not generating excess pops and pushes. For example, in `CHECK_HAS_INSTANCE_VALUES`, `owner` occurs in the input, so it cannot be -redefined. Thus it doesn't need to be written and can be read without adjusting the stack pointer. +redefined. Thus, it doesn't need to be written and can be read without adjusting the stack pointer. The C code generated for `CHECK_HAS_INSTANCE_VALUES` would look something like: ```C @@ -404,7 +404,7 @@ The C code generated for `CHECK_HAS_INSTANCE_VALUES` would look something like: } ``` -When combining ops together to form instructions, temporary values should be used, +When combining ops to form instructions, temporary values should be used, rather than popping and pushing, such that `LOAD_ATTR_SLOT` would look something like: ```C From 46006a1b355f75d06c10e7b8086912c483b34487 Mon Sep 17 00:00:00 2001 From: Stephen Hansen Date: Sun, 15 Dec 2024 14:53:22 -0500 Subject: [PATCH 031/376] gh-127586: properly restore blocked signals in resource_tracker.py (GH-127587) * Correct pthread_sigmask in resource_tracker to restore old signals Using SIG_UNBLOCK to remove blocked "ignored signals" may accidentally cause side effects if the calling parent already had said signals blocked to begin with and did not intend to unblock them when creating a pool. Use SIG_SETMASK instead with the previous mask of blocked signals to restore the original blocked set. * Adding resource_tracker blocked signals test Co-authored-by: Peter Bierma Co-authored-by: Gregory P. Smith --- Lib/multiprocessing/resource_tracker.py | 7 ++++--- Lib/test/_test_multiprocessing.py | 15 +++++++++++++++ ...2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst | 3 +++ 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst diff --git a/Lib/multiprocessing/resource_tracker.py b/Lib/multiprocessing/resource_tracker.py index 20ddd9c50e3d88..90e036ae905afa 100644 --- a/Lib/multiprocessing/resource_tracker.py +++ b/Lib/multiprocessing/resource_tracker.py @@ -155,13 +155,14 @@ def ensure_running(self): # that can make the child die before it registers signal handlers # for SIGINT and SIGTERM. The mask is unregistered after spawning # the child. + prev_sigmask = None try: if _HAVE_SIGMASK: - signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS) + prev_sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS) pid = util.spawnv_passfds(exe, args, fds_to_pass) finally: - if _HAVE_SIGMASK: - signal.pthread_sigmask(signal.SIG_UNBLOCK, _IGNORED_SIGNALS) + if prev_sigmask is not None: + signal.pthread_sigmask(signal.SIG_SETMASK, prev_sigmask) except: os.close(w) raise diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 80b08b8ac66899..01e92f0d8d6b30 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -6044,6 +6044,21 @@ def test_resource_tracker_exit_code(self): self._test_resource_tracker_leak_resources( cleanup=cleanup, ) + @unittest.skipUnless(hasattr(signal, "pthread_sigmask"), "pthread_sigmask is not available") + def test_resource_tracker_blocked_signals(self): + # + # gh-127586: Check that resource_tracker does not override blocked signals of caller. + # + from multiprocessing.resource_tracker import ResourceTracker + signals = {signal.SIGTERM, signal.SIGINT, signal.SIGUSR1} + + for sig in signals: + signal.pthread_sigmask(signal.SIG_SETMASK, {sig}) + self.assertEqual(signal.pthread_sigmask(signal.SIG_BLOCK, set()), {sig}) + tracker = ResourceTracker() + tracker.ensure_running() + self.assertEqual(signal.pthread_sigmask(signal.SIG_BLOCK, set()), {sig}) + tracker._stop() class TestSimpleQueue(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst b/Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst new file mode 100644 index 00000000000000..80217bd4a10503 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst @@ -0,0 +1,3 @@ +:class:`multiprocessing.pool.Pool` now properly restores blocked signal handlers +of the parent thread when creating processes via either *spawn* or +*forkserver*. From b74c8f58e875d909ce6b5b9dbcddd6d8331d2081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns=20=F0=9F=87=B5=F0=9F=87=B8?= Date: Sun, 15 Dec 2024 21:12:13 +0000 Subject: [PATCH 032/376] GH-126985: Don't override venv detection with PYTHONHOME (#127968) --- Lib/test/test_getpath.py | 31 +++++++++++++++++++++++++++++++ Modules/getpath.py | 10 +++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_getpath.py b/Lib/test/test_getpath.py index 7e5c4a3d14ddc5..f86df9d0d03485 100644 --- a/Lib/test/test_getpath.py +++ b/Lib/test/test_getpath.py @@ -832,6 +832,37 @@ def test_explicitly_set_stdlib_dir(self): actual = getpath(ns, expected) self.assertEqual(expected, actual) + def test_PYTHONHOME_in_venv(self): + "Make sure prefix/exec_prefix still point to the venv if PYTHONHOME was used." + ns = MockPosixNamespace( + argv0="/venv/bin/python", + PREFIX="/usr", + ENV_PYTHONHOME="/pythonhome", + ) + # Setup venv + ns.add_known_xfile("/venv/bin/python") + ns.add_known_file("/venv/pyvenv.cfg", [ + r"home = /usr/bin" + ]) + # Seutup PYTHONHOME + ns.add_known_file("/pythonhome/lib/python9.8/os.py") + ns.add_known_dir("/pythonhome/lib/python9.8/lib-dynload") + + expected = dict( + executable="/venv/bin/python", + prefix="/venv", + exec_prefix="/venv", + base_prefix="/pythonhome", + base_exec_prefix="/pythonhome", + module_search_paths_set=1, + module_search_paths=[ + "/pythonhome/lib/python98.zip", + "/pythonhome/lib/python9.8", + "/pythonhome/lib/python9.8/lib-dynload", + ], + ) + actual = getpath(ns, expected) + self.assertEqual(expected, actual) # ****************************************************************************** diff --git a/Modules/getpath.py b/Modules/getpath.py index b14f985a0d5f97..c34101e720851d 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -344,9 +344,10 @@ def search_up(prefix, *landmarks, test=isfile): venv_prefix = None -# Calling Py_SetPythonHome(), Py_SetPath() or -# setting $PYTHONHOME will override venv detection. -if not home and not py_setpath: +# Calling Py_SetPath() will override venv detection. +# Calling Py_SetPythonHome() or setting $PYTHONHOME will override the 'home' key +# specified in pyvenv.cfg. +if not py_setpath: try: # prefix2 is just to avoid calculating dirname again later, # as the path in venv_prefix is the more common case. @@ -370,6 +371,9 @@ def search_up(prefix, *landmarks, test=isfile): for line in pyvenvcfg: key, had_equ, value = line.partition('=') if had_equ and key.strip().lower() == 'home': + # If PYTHONHOME was set, ignore 'home' from pyvenv.cfg. + if home: + break # Override executable_dir/real_executable_dir with the value from 'home'. # These values may be later used to calculate prefix/base_prefix, if a more # reliable source — like the runtime library (libpython) path — isn't available. From 47c5a0f307cff3ed477528536e8de095c0752efa Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Sun, 15 Dec 2024 23:17:01 +0000 Subject: [PATCH 033/376] gh-125588: Allow to regenerate the parser with Python < 3.12 (#127969) Signed-off-by: Pablo Galindo --- Tools/peg_generator/pegen/parser.py | 6 +++--- Tools/peg_generator/pegen/parser_generator.py | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Tools/peg_generator/pegen/parser.py b/Tools/peg_generator/pegen/parser.py index 692eb9ed2417d7..a987d30a9d6438 100644 --- a/Tools/peg_generator/pegen/parser.py +++ b/Tools/peg_generator/pegen/parser.py @@ -207,7 +207,7 @@ def string(self) -> Optional[tokenize.TokenInfo]: @memoize def fstring_start(self) -> Optional[tokenize.TokenInfo]: - FSTRING_START = getattr(token, "FSTRING_START") + FSTRING_START = getattr(token, "FSTRING_START", None) if not FSTRING_START: return None tok = self._tokenizer.peek() @@ -217,7 +217,7 @@ def fstring_start(self) -> Optional[tokenize.TokenInfo]: @memoize def fstring_middle(self) -> Optional[tokenize.TokenInfo]: - FSTRING_MIDDLE = getattr(token, "FSTRING_MIDDLE") + FSTRING_MIDDLE = getattr(token, "FSTRING_MIDDLE", None) if not FSTRING_MIDDLE: return None tok = self._tokenizer.peek() @@ -227,7 +227,7 @@ def fstring_middle(self) -> Optional[tokenize.TokenInfo]: @memoize def fstring_end(self) -> Optional[tokenize.TokenInfo]: - FSTRING_END = getattr(token, "FSTRING_END") + FSTRING_END = getattr(token, "FSTRING_END", None) if not FSTRING_END: return None tok = self._tokenizer.peek() diff --git a/Tools/peg_generator/pegen/parser_generator.py b/Tools/peg_generator/pegen/parser_generator.py index b42b12c8aa0dee..6ce0649aefe7ff 100644 --- a/Tools/peg_generator/pegen/parser_generator.py +++ b/Tools/peg_generator/pegen/parser_generator.py @@ -1,3 +1,4 @@ +import sys import ast import contextlib import re @@ -75,6 +76,11 @@ class RuleCheckingVisitor(GrammarVisitor): def __init__(self, rules: Dict[str, Rule], tokens: Set[str]): self.rules = rules self.tokens = tokens + # If python < 3.12 add the virtual fstring tokens + if sys.version_info < (3, 12): + self.tokens.add("FSTRING_START") + self.tokens.add("FSTRING_END") + self.tokens.add("FSTRING_MIDDLE") def visit_NameLeaf(self, node: NameLeaf) -> None: if node.value not in self.rules and node.value not in self.tokens: From 0d8e7106c260e96c4604f501165bd106bff51f6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 16 Dec 2024 14:40:26 +0100 Subject: [PATCH 034/376] gh-111178: fix UBSan failures in `_elementtree.c` (#127982) --- Modules/_elementtree.c | 133 +++++++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 53 deletions(-) diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index e134e096e044b7..355f322d304c2f 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -184,7 +184,7 @@ elementtree_traverse(PyObject *m, visitproc visit, void *arg) static void elementtree_free(void *m) { - elementtree_clear((PyObject *)m); + (void)elementtree_clear((PyObject *)m); } /* helpers */ @@ -257,6 +257,7 @@ typedef struct { } ElementObject; +#define _Element_CAST(op) ((ElementObject *)(op)) #define Element_CheckExact(st, op) Py_IS_TYPE(op, (st)->Element_Type) #define Element_Check(st, op) PyObject_TypeCheck(op, (st)->Element_Type) @@ -648,8 +649,9 @@ subelement(PyObject *self, PyObject *args, PyObject *kwds) } static int -element_gc_traverse(ElementObject *self, visitproc visit, void *arg) +element_gc_traverse(PyObject *op, visitproc visit, void *arg) { + ElementObject *self = _Element_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->tag); Py_VISIT(JOIN_OBJ(self->text)); @@ -666,8 +668,9 @@ element_gc_traverse(ElementObject *self, visitproc visit, void *arg) } static int -element_gc_clear(ElementObject *self) +element_gc_clear(PyObject *op) { + ElementObject *self = _Element_CAST(op); Py_CLEAR(self->tag); _clear_joined_ptr(&self->text); _clear_joined_ptr(&self->tail); @@ -680,8 +683,9 @@ element_gc_clear(ElementObject *self) } static void -element_dealloc(ElementObject* self) +element_dealloc(PyObject *op) { + ElementObject *self = _Element_CAST(op); PyTypeObject *tp = Py_TYPE(self); /* bpo-31095: UnTrack is needed before calling any callbacks */ @@ -689,13 +693,13 @@ element_dealloc(ElementObject* self) Py_TRASHCAN_BEGIN(self, element_dealloc) if (self->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) self); + PyObject_ClearWeakRefs(op); /* element_gc_clear clears all references and deallocates extra */ - element_gc_clear(self); + (void)element_gc_clear(op); - tp->tp_free((PyObject *)self); + tp->tp_free(self); Py_DECREF(tp); Py_TRASHCAN_END } @@ -1478,9 +1482,9 @@ _elementtree_Element_itertext_impl(ElementObject *self, PyTypeObject *cls) static PyObject* -element_getitem(PyObject* self_, Py_ssize_t index) +element_getitem(PyObject *op, Py_ssize_t index) { - ElementObject* self = (ElementObject*) self_; + ElementObject *self = _Element_CAST(op); if (!self->extra || index < 0 || index >= self->extra->length) { PyErr_SetString( @@ -1494,9 +1498,9 @@ element_getitem(PyObject* self_, Py_ssize_t index) } static int -element_bool(PyObject* self_) +element_bool(PyObject *op) { - ElementObject* self = (ElementObject*) self_; + ElementObject *self = _Element_CAST(op); if (PyErr_WarnEx(PyExc_DeprecationWarning, "Testing an element's truth value will always return True " "in future versions. Use specific 'len(elem)' or " @@ -1583,8 +1587,9 @@ _elementtree_Element_keys_impl(ElementObject *self) } static Py_ssize_t -element_length(ElementObject* self) +element_length(PyObject *op) { + ElementObject *self = _Element_CAST(op); if (!self->extra) return 0; @@ -1675,10 +1680,10 @@ _elementtree_Element_remove_impl(ElementObject *self, PyObject *subelement) } static PyObject* -element_repr(ElementObject* self) +element_repr(PyObject *op) { int status; - + ElementObject *self = _Element_CAST(op); if (self->tag == NULL) return PyUnicode_FromFormat("", self); @@ -1728,9 +1733,9 @@ _elementtree_Element_set_impl(ElementObject *self, PyObject *key, } static int -element_setitem(PyObject* self_, Py_ssize_t index, PyObject* item) +element_setitem(PyObject *op, Py_ssize_t index, PyObject* item) { - ElementObject* self = (ElementObject*) self_; + ElementObject *self = _Element_CAST(op); Py_ssize_t i; PyObject* old; @@ -1762,10 +1767,10 @@ element_setitem(PyObject* self_, Py_ssize_t index, PyObject* item) return 0; } -static PyObject* -element_subscr(PyObject* self_, PyObject* item) +static PyObject * +element_subscr(PyObject *op, PyObject *item) { - ElementObject* self = (ElementObject*) self_; + ElementObject *self = _Element_CAST(op); if (PyIndex_Check(item)) { Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); @@ -1775,7 +1780,7 @@ element_subscr(PyObject* self_, PyObject* item) } if (i < 0 && self->extra) i += self->extra->length; - return element_getitem(self_, i); + return element_getitem(op, i); } else if (PySlice_Check(item)) { Py_ssize_t start, stop, step, slicelen, i; @@ -1815,9 +1820,9 @@ element_subscr(PyObject* self_, PyObject* item) } static int -element_ass_subscr(PyObject* self_, PyObject* item, PyObject* value) +element_ass_subscr(PyObject *op, PyObject *item, PyObject *value) { - ElementObject* self = (ElementObject*) self_; + ElementObject *self = _Element_CAST(op); if (PyIndex_Check(item)) { Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); @@ -1827,7 +1832,7 @@ element_ass_subscr(PyObject* self_, PyObject* item, PyObject* value) } if (i < 0 && self->extra) i += self->extra->length; - return element_setitem(self_, i, value); + return element_setitem(op, i, value); } else if (PySlice_Check(item)) { Py_ssize_t start, stop, step, slicelen, newlen, i; @@ -1998,30 +2003,34 @@ element_ass_subscr(PyObject* self_, PyObject* item, PyObject* value) } static PyObject* -element_tag_getter(ElementObject *self, void *closure) +element_tag_getter(PyObject *op, void *closure) { + ElementObject *self = _Element_CAST(op); PyObject *res = self->tag; return Py_NewRef(res); } static PyObject* -element_text_getter(ElementObject *self, void *closure) +element_text_getter(PyObject *op, void *closure) { + ElementObject *self = _Element_CAST(op); PyObject *res = element_get_text(self); return Py_XNewRef(res); } static PyObject* -element_tail_getter(ElementObject *self, void *closure) +element_tail_getter(PyObject *op, void *closure) { + ElementObject *self = _Element_CAST(op); PyObject *res = element_get_tail(self); return Py_XNewRef(res); } static PyObject* -element_attrib_getter(ElementObject *self, void *closure) +element_attrib_getter(PyObject *op, void *closure) { PyObject *res; + ElementObject *self = _Element_CAST(op); if (!self->extra) { if (create_extra(self, NULL) < 0) return NULL; @@ -2040,31 +2049,34 @@ element_attrib_getter(ElementObject *self, void *closure) } static int -element_tag_setter(ElementObject *self, PyObject *value, void *closure) +element_tag_setter(PyObject *op, PyObject *value, void *closure) { _VALIDATE_ATTR_VALUE(value); + ElementObject *self = _Element_CAST(op); Py_SETREF(self->tag, Py_NewRef(value)); return 0; } static int -element_text_setter(ElementObject *self, PyObject *value, void *closure) +element_text_setter(PyObject *op, PyObject *value, void *closure) { _VALIDATE_ATTR_VALUE(value); + ElementObject *self = _Element_CAST(op); _set_joined_ptr(&self->text, Py_NewRef(value)); return 0; } static int -element_tail_setter(ElementObject *self, PyObject *value, void *closure) +element_tail_setter(PyObject *op, PyObject *value, void *closure) { _VALIDATE_ATTR_VALUE(value); + ElementObject *self = _Element_CAST(op); _set_joined_ptr(&self->tail, Py_NewRef(value)); return 0; } static int -element_attrib_setter(ElementObject *self, PyObject *value, void *closure) +element_attrib_setter(PyObject *op, PyObject *value, void *closure) { _VALIDATE_ATTR_VALUE(value); if (!PyDict_Check(value)) { @@ -2073,6 +2085,7 @@ element_attrib_setter(ElementObject *self, PyObject *value, void *closure) Py_TYPE(value)->tp_name); return -1; } + ElementObject *self = _Element_CAST(op); if (!self->extra) { if (create_extra(self, NULL) < 0) return -1; @@ -2106,11 +2119,14 @@ typedef struct { int gettext; } ElementIterObject; +#define _ElementIter_CAST(op) ((ElementIterObject *)(op)) + static void -elementiter_dealloc(ElementIterObject *it) +elementiter_dealloc(PyObject *op) { - PyTypeObject *tp = Py_TYPE(it); + PyTypeObject *tp = Py_TYPE(op); + ElementIterObject *it = _ElementIter_CAST(op); Py_ssize_t i = it->parent_stack_used; it->parent_stack_used = 0; /* bpo-31095: UnTrack is needed before calling any callbacks */ @@ -2127,8 +2143,9 @@ elementiter_dealloc(ElementIterObject *it) } static int -elementiter_traverse(ElementIterObject *it, visitproc visit, void *arg) +elementiter_traverse(PyObject *op, visitproc visit, void *arg) { + ElementIterObject *it = _ElementIter_CAST(op); Py_ssize_t i = it->parent_stack_used; while (i--) Py_VISIT(it->parent_stack[i].parent); @@ -2162,8 +2179,9 @@ parent_stack_push_new(ElementIterObject *it, ElementObject *parent) } static PyObject * -elementiter_next(ElementIterObject *it) +elementiter_next(PyObject *op) { + ElementIterObject *it = _ElementIter_CAST(op); /* Sub-element iterator. * * A short note on gettext: this function serves both the iter() and @@ -2354,6 +2372,8 @@ typedef struct { elementtreestate *state; } TreeBuilderObject; + +#define _TreeBuilder_CAST(op) ((TreeBuilderObject *)(op)) #define TreeBuilder_CheckExact(st, op) Py_IS_TYPE((op), (st)->TreeBuilder_Type) /* -------------------------------------------------------------------- */ @@ -2444,8 +2464,9 @@ _elementtree_TreeBuilder___init___impl(TreeBuilderObject *self, } static int -treebuilder_gc_traverse(TreeBuilderObject *self, visitproc visit, void *arg) +treebuilder_gc_traverse(PyObject *op, visitproc visit, void *arg) { + TreeBuilderObject *self = _TreeBuilder_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->pi_event_obj); Py_VISIT(self->comment_event_obj); @@ -2467,8 +2488,9 @@ treebuilder_gc_traverse(TreeBuilderObject *self, visitproc visit, void *arg) } static int -treebuilder_gc_clear(TreeBuilderObject *self) +treebuilder_gc_clear(PyObject *op) { + TreeBuilderObject *self = _TreeBuilder_CAST(op); Py_CLEAR(self->pi_event_obj); Py_CLEAR(self->comment_event_obj); Py_CLEAR(self->end_ns_event_obj); @@ -2489,11 +2511,11 @@ treebuilder_gc_clear(TreeBuilderObject *self) } static void -treebuilder_dealloc(TreeBuilderObject *self) +treebuilder_dealloc(PyObject *self) { PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - treebuilder_gc_clear(self); + (void)treebuilder_gc_clear(self); tp->tp_free(self); Py_DECREF(tp); } @@ -3061,6 +3083,9 @@ typedef struct { PyObject *elementtree_module; } XMLParserObject; + +#define _XMLParser_CAST(op) ((XMLParserObject *)(op)) + /* helpers */ LOCAL(PyObject*) @@ -3751,8 +3776,9 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *target, } static int -xmlparser_gc_traverse(XMLParserObject *self, visitproc visit, void *arg) +xmlparser_gc_traverse(PyObject *op, visitproc visit, void *arg) { + XMLParserObject *self = _XMLParser_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->handle_close); Py_VISIT(self->handle_pi); @@ -3772,8 +3798,9 @@ xmlparser_gc_traverse(XMLParserObject *self, visitproc visit, void *arg) } static int -xmlparser_gc_clear(XMLParserObject *self) +xmlparser_gc_clear(PyObject *op) { + XMLParserObject *self = _XMLParser_CAST(op); elementtreestate *st = self->state; if (self->parser != NULL) { XML_Parser parser = self->parser; @@ -3800,11 +3827,11 @@ xmlparser_gc_clear(XMLParserObject *self) } static void -xmlparser_dealloc(XMLParserObject* self) +xmlparser_dealloc(PyObject *self) { PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - xmlparser_gc_clear(self); + (void)xmlparser_gc_clear(self); tp->tp_free(self); Py_DECREF(tp); } @@ -4172,7 +4199,7 @@ static PyMemberDef xmlparser_members[] = { }; static PyObject* -xmlparser_version_getter(XMLParserObject *self, void *closure) +xmlparser_version_getter(PyObject *op, void *closure) { return PyUnicode_FromFormat( "Expat %d.%d.%d", XML_MAJOR_VERSION, @@ -4180,7 +4207,7 @@ xmlparser_version_getter(XMLParserObject *self, void *closure) } static PyGetSetDef xmlparser_getsetlist[] = { - {"version", (getter)xmlparser_version_getter, NULL, NULL}, + {"version", xmlparser_version_getter, NULL, NULL}, {NULL}, }; @@ -4229,20 +4256,20 @@ static struct PyMemberDef element_members[] = { static PyGetSetDef element_getsetlist[] = { {"tag", - (getter)element_tag_getter, - (setter)element_tag_setter, + element_tag_getter, + element_tag_setter, "A string identifying what kind of data this element represents"}, {"text", - (getter)element_text_getter, - (setter)element_text_setter, + element_text_getter, + element_text_setter, "A string of text directly after the start tag, or None"}, {"tail", - (getter)element_tail_getter, - (setter)element_tail_setter, + element_tail_getter, + element_tail_setter, "A string of text directly after the end tag, or None"}, {"attrib", - (getter)element_attrib_getter, - (setter)element_attrib_setter, + element_attrib_getter, + element_attrib_setter, "A dictionary containing the element's attributes"}, {NULL}, }; From 52d552cda7614c7aa9f08b680089c630587e747f Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi Date: Mon, 16 Dec 2024 22:56:04 +0900 Subject: [PATCH 035/376] gh-127896: Add missing documentation of `PySequence_In` (GH-127979) Co-authored-by: Sergey B Kirpichev --- Doc/c-api/sequence.rst | 9 +++++++++ Doc/whatsnew/3.14.rst | 4 ++++ .../C_API/2024-12-16-07-12-15.gh-issue-127896.HmI9pk.rst | 2 ++ 3 files changed, 15 insertions(+) create mode 100644 Misc/NEWS.d/next/C_API/2024-12-16-07-12-15.gh-issue-127896.HmI9pk.rst diff --git a/Doc/c-api/sequence.rst b/Doc/c-api/sequence.rst index ce28839f5ba739..df5bf6b64a93a0 100644 --- a/Doc/c-api/sequence.rst +++ b/Doc/c-api/sequence.rst @@ -105,6 +105,15 @@ Sequence Protocol equivalent to the Python expression ``value in o``. +.. c:function:: int PySequence_In(PyObject *o, PyObject *value) + + Alias for :c:func:`PySequence_Contains`. + + .. deprecated:: 3.14 + The function is :term:`soft deprecated` and should no longer be used to + write new code. + + .. c:function:: Py_ssize_t PySequence_Index(PyObject *o, PyObject *value) Return the first index *i* for which ``o[i] == value``. On error, return diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 095949242c09d9..d13cd2d5173a04 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1073,6 +1073,10 @@ Deprecated :c:macro:`!isfinite` available from :file:`math.h` since C99. (Contributed by Sergey B Kirpichev in :gh:`119613`.) +* The previously undocumented function :c:func:`PySequence_In` is :term:`soft deprecated`. + Use :c:func:`PySequence_Contains` instead. + (Contributed by Yuki Kobayashi in :gh:`127896`.) + .. Add C API deprecations above alphabetically, not here at the end. .. include:: ../deprecations/c-api-pending-removal-in-3.15.rst diff --git a/Misc/NEWS.d/next/C_API/2024-12-16-07-12-15.gh-issue-127896.HmI9pk.rst b/Misc/NEWS.d/next/C_API/2024-12-16-07-12-15.gh-issue-127896.HmI9pk.rst new file mode 100644 index 00000000000000..82b4f563591fe1 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2024-12-16-07-12-15.gh-issue-127896.HmI9pk.rst @@ -0,0 +1,2 @@ +The previously undocumented function :c:func:`PySequence_In` is :term:`soft deprecated`. +Use :c:func:`PySequence_Contains` instead. From 081673801e3d47d931d2e2b6a4a1515e1207d938 Mon Sep 17 00:00:00 2001 From: "Tomas R." Date: Mon, 16 Dec 2024 17:57:18 +0100 Subject: [PATCH 036/376] gh-127864: Fix compiler warning (-Wstringop-truncation) (GH-127878) --- Python/import.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Python/import.c b/Python/import.c index f3511aaf7b8010..a9282dde633959 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1176,9 +1176,10 @@ hashtable_key_from_2_strings(PyObject *str1, PyObject *str2, const char sep) return NULL; } - strncpy(key, str1_data, str1_len); + memcpy(key, str1_data, str1_len); key[str1_len] = sep; - strncpy(key + str1_len + 1, str2_data, str2_len + 1); + memcpy(key + str1_len + 1, str2_data, str2_len); + key[size - 1] = '\0'; assert(strlen(key) == size - 1); return key; } From e4981e33b82ac14cca0f2d9b95257301fa201810 Mon Sep 17 00:00:00 2001 From: Gugubo <29143981+Gugubo@users.noreply.github.com> Date: Mon, 16 Dec 2024 18:08:25 +0100 Subject: [PATCH 037/376] Fix typo in itertools docs (gh-127995) --- Doc/library/itertools.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index d1fb952e36f686..eb61453718bd3c 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -681,7 +681,7 @@ loops that truncate the stream. consumed from the input iterator and there is no way to access it. This could be an issue if an application wants to further consume the input iterator after *takewhile* has been run to exhaustion. To work - around this problem, consider using `more-iterools before_and_after() + around this problem, consider using `more-itertools before_and_after() `_ instead. From 1d276ec6f8403590a6a1a18c560ce75b9221572b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:18:02 +0200 Subject: [PATCH 038/376] Revert "gh-127586: properly restore blocked signals in resource_tracker.py (GH-127587)" (#127983) This reverts commit 46006a1b355f75d06c10e7b8086912c483b34487. --- Lib/multiprocessing/resource_tracker.py | 7 +++---- Lib/test/_test_multiprocessing.py | 15 --------------- ...2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst | 3 --- 3 files changed, 3 insertions(+), 22 deletions(-) delete mode 100644 Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst diff --git a/Lib/multiprocessing/resource_tracker.py b/Lib/multiprocessing/resource_tracker.py index 90e036ae905afa..20ddd9c50e3d88 100644 --- a/Lib/multiprocessing/resource_tracker.py +++ b/Lib/multiprocessing/resource_tracker.py @@ -155,14 +155,13 @@ def ensure_running(self): # that can make the child die before it registers signal handlers # for SIGINT and SIGTERM. The mask is unregistered after spawning # the child. - prev_sigmask = None try: if _HAVE_SIGMASK: - prev_sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS) + signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS) pid = util.spawnv_passfds(exe, args, fds_to_pass) finally: - if prev_sigmask is not None: - signal.pthread_sigmask(signal.SIG_SETMASK, prev_sigmask) + if _HAVE_SIGMASK: + signal.pthread_sigmask(signal.SIG_UNBLOCK, _IGNORED_SIGNALS) except: os.close(w) raise diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 01e92f0d8d6b30..80b08b8ac66899 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -6044,21 +6044,6 @@ def test_resource_tracker_exit_code(self): self._test_resource_tracker_leak_resources( cleanup=cleanup, ) - @unittest.skipUnless(hasattr(signal, "pthread_sigmask"), "pthread_sigmask is not available") - def test_resource_tracker_blocked_signals(self): - # - # gh-127586: Check that resource_tracker does not override blocked signals of caller. - # - from multiprocessing.resource_tracker import ResourceTracker - signals = {signal.SIGTERM, signal.SIGINT, signal.SIGUSR1} - - for sig in signals: - signal.pthread_sigmask(signal.SIG_SETMASK, {sig}) - self.assertEqual(signal.pthread_sigmask(signal.SIG_BLOCK, set()), {sig}) - tracker = ResourceTracker() - tracker.ensure_running() - self.assertEqual(signal.pthread_sigmask(signal.SIG_BLOCK, set()), {sig}) - tracker._stop() class TestSimpleQueue(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst b/Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst deleted file mode 100644 index 80217bd4a10503..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst +++ /dev/null @@ -1,3 +0,0 @@ -:class:`multiprocessing.pool.Pool` now properly restores blocked signal handlers -of the parent thread when creating processes via either *spawn* or -*forkserver*. From 4937ba54c0ff7cc4a83d7345d398b804365af2d6 Mon Sep 17 00:00:00 2001 From: Edward Xu Date: Tue, 17 Dec 2024 03:12:19 +0800 Subject: [PATCH 039/376] gh-127085: fix some data races in memoryview in free-threading (#127412) --- Lib/test/test_memoryview.py | 26 ++++++++++++++- ...-11-30-23-35-45.gh-issue-127085.KLKylb.rst | 1 + Objects/memoryobject.c | 33 +++++++++++++++---- 3 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-23-35-45.gh-issue-127085.KLKylb.rst diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py index 96320ebef6467a..856e17918048e4 100644 --- a/Lib/test/test_memoryview.py +++ b/Lib/test/test_memoryview.py @@ -16,7 +16,8 @@ import struct from itertools import product -from test.support import import_helper +from test import support +from test.support import import_helper, threading_helper class MyObject: @@ -733,5 +734,28 @@ def test_picklebuffer_reference_loop(self): self.assertIsNone(wr()) +@threading_helper.requires_working_threading() +@support.requires_resource("cpu") +class RacingTest(unittest.TestCase): + def test_racing_getbuf_and_releasebuf(self): + """Repeatly access the memoryview for racing.""" + from multiprocessing.managers import SharedMemoryManager + from threading import Thread + + n = 100 + with SharedMemoryManager() as smm: + obj = smm.ShareableList(range(100)) + threads = [] + for _ in range(n): + # Issue gh-127085, the `ShareableList.count` is just a convenient way to mess the `exports` + # counter of `memoryview`, this issue has no direct relation with `ShareableList`. + threads.append(Thread(target=obj.count, args=(1,))) + + with threading_helper.start_threads(threads): + pass + + del obj + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-23-35-45.gh-issue-127085.KLKylb.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-23-35-45.gh-issue-127085.KLKylb.rst new file mode 100644 index 00000000000000..a59b9502eaa33e --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-23-35-45.gh-issue-127085.KLKylb.rst @@ -0,0 +1 @@ +Fix race when exporting a buffer from a :class:`memoryview` object on the :term:`free-threaded ` build. diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index afe2dcb127adb4..ea4d24dc690768 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -1086,6 +1086,16 @@ PyBuffer_ToContiguous(void *buf, const Py_buffer *src, Py_ssize_t len, char orde return ret; } +static inline Py_ssize_t +get_exports(PyMemoryViewObject *buf) +{ +#ifdef Py_GIL_DISABLED + return _Py_atomic_load_ssize_relaxed(&buf->exports); +#else + return buf->exports; +#endif +} + /****************************************************************************/ /* Release/GC management */ @@ -1098,7 +1108,7 @@ PyBuffer_ToContiguous(void *buf, const Py_buffer *src, Py_ssize_t len, char orde static void _memory_release(PyMemoryViewObject *self) { - assert(self->exports == 0); + assert(get_exports(self) == 0); if (self->flags & _Py_MEMORYVIEW_RELEASED) return; @@ -1119,15 +1129,16 @@ static PyObject * memoryview_release_impl(PyMemoryViewObject *self) /*[clinic end generated code: output=d0b7e3ba95b7fcb9 input=bc71d1d51f4a52f0]*/ { - if (self->exports == 0) { + Py_ssize_t exports = get_exports(self); + if (exports == 0) { _memory_release(self); Py_RETURN_NONE; } - if (self->exports > 0) { + if (exports > 0) { PyErr_Format(PyExc_BufferError, - "memoryview has %zd exported buffer%s", self->exports, - self->exports==1 ? "" : "s"); + "memoryview has %zd exported buffer%s", exports, + exports==1 ? "" : "s"); return NULL; } @@ -1140,7 +1151,7 @@ static void memory_dealloc(PyObject *_self) { PyMemoryViewObject *self = (PyMemoryViewObject *)_self; - assert(self->exports == 0); + assert(get_exports(self) == 0); _PyObject_GC_UNTRACK(self); _memory_release(self); Py_CLEAR(self->mbuf); @@ -1161,7 +1172,7 @@ static int memory_clear(PyObject *_self) { PyMemoryViewObject *self = (PyMemoryViewObject *)_self; - if (self->exports == 0) { + if (get_exports(self) == 0) { _memory_release(self); Py_CLEAR(self->mbuf); } @@ -1589,7 +1600,11 @@ memory_getbuf(PyObject *_self, Py_buffer *view, int flags) view->obj = Py_NewRef(self); +#ifdef Py_GIL_DISABLED + _Py_atomic_add_ssize(&self->exports, 1); +#else self->exports++; +#endif return 0; } @@ -1598,7 +1613,11 @@ static void memory_releasebuf(PyObject *_self, Py_buffer *view) { PyMemoryViewObject *self = (PyMemoryViewObject *)_self; +#ifdef Py_GIL_DISABLED + _Py_atomic_add_ssize(&self->exports, -1); +#else self->exports--; +#endif return; /* PyBuffer_Release() decrements view->obj after this function returns. */ } From 3b766824fe59030100964752be0556084d4461af Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Mon, 16 Dec 2024 14:31:44 -0500 Subject: [PATCH 040/376] gh-126907: make `atexit` thread safe in free-threading (#127935) Co-authored-by: Victor Stinner Co-authored-by: Kumar Aditya --- Include/internal/pycore_atexit.h | 24 +-- Lib/test/test_atexit.py | 35 +++- ...-12-13-22-20-54.gh-issue-126907.fWRL_R.rst | 2 + Modules/atexitmodule.c | 153 +++++++++--------- 4 files changed, 126 insertions(+), 88 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-13-22-20-54.gh-issue-126907.fWRL_R.rst diff --git a/Include/internal/pycore_atexit.h b/Include/internal/pycore_atexit.h index cde5b530baf00c..db1e5568e09413 100644 --- a/Include/internal/pycore_atexit.h +++ b/Include/internal/pycore_atexit.h @@ -36,23 +36,29 @@ typedef struct atexit_callback { struct atexit_callback *next; } atexit_callback; -typedef struct { - PyObject *func; - PyObject *args; - PyObject *kwargs; -} atexit_py_callback; - struct atexit_state { +#ifdef Py_GIL_DISABLED + PyMutex ll_callbacks_lock; +#endif atexit_callback *ll_callbacks; // XXX The rest of the state could be moved to the atexit module state // and a low-level callback added for it during module exec. // For the moment we leave it here. - atexit_py_callback **callbacks; - int ncallbacks; - int callback_len; + + // List containing tuples with callback information. + // e.g. [(func, args, kwargs), ...] + PyObject *callbacks; }; +#ifdef Py_GIL_DISABLED +# define _PyAtExit_LockCallbacks(state) PyMutex_Lock(&state->ll_callbacks_lock); +# define _PyAtExit_UnlockCallbacks(state) PyMutex_Unlock(&state->ll_callbacks_lock); +#else +# define _PyAtExit_LockCallbacks(state) +# define _PyAtExit_UnlockCallbacks(state) +#endif + // Export for '_interpchannels' shared extension PyAPI_FUNC(int) _Py_AtExit( PyInterpreterState *interp, diff --git a/Lib/test/test_atexit.py b/Lib/test/test_atexit.py index 913b7556be83af..eb01da6e88a8bc 100644 --- a/Lib/test/test_atexit.py +++ b/Lib/test/test_atexit.py @@ -4,7 +4,7 @@ import unittest from test import support from test.support import script_helper - +from test.support import threading_helper class GeneralTest(unittest.TestCase): def test_general(self): @@ -46,6 +46,39 @@ def test_atexit_instances(self): self.assertEqual(res.out.decode().splitlines(), ["atexit2", "atexit1"]) self.assertFalse(res.err) + @threading_helper.requires_working_threading() + @support.requires_resource("cpu") + @unittest.skipUnless(support.Py_GIL_DISABLED, "only meaningful without the GIL") + def test_atexit_thread_safety(self): + # GH-126907: atexit was not thread safe on the free-threaded build + source = """ + from threading import Thread + + def dummy(): + pass + + + def thready(): + for _ in range(100): + atexit.register(dummy) + atexit._clear() + atexit.register(dummy) + atexit.unregister(dummy) + atexit._run_exitfuncs() + + + threads = [Thread(target=thready) for _ in range(10)] + for thread in threads: + thread.start() + + for thread in threads: + thread.join() + """ + + # atexit._clear() has some evil side effects, and we don't + # want them to affect the rest of the tests. + script_helper.assert_python_ok("-c", textwrap.dedent(source)) + @support.cpython_only class SubinterpreterTest(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2024-12-13-22-20-54.gh-issue-126907.fWRL_R.rst b/Misc/NEWS.d/next/Library/2024-12-13-22-20-54.gh-issue-126907.fWRL_R.rst new file mode 100644 index 00000000000000..d33d2aa23b21b3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-13-22-20-54.gh-issue-126907.fWRL_R.rst @@ -0,0 +1,2 @@ +Fix crash when using :mod:`atexit` concurrently on the :term:`free-threaded +` build. diff --git a/Modules/atexitmodule.c b/Modules/atexitmodule.c index c009235b7a36c2..1b89b32ba907d7 100644 --- a/Modules/atexitmodule.c +++ b/Modules/atexitmodule.c @@ -41,6 +41,7 @@ PyUnstable_AtExit(PyInterpreterState *interp, callback->next = NULL; struct atexit_state *state = &interp->atexit; + _PyAtExit_LockCallbacks(state); atexit_callback *top = state->ll_callbacks; if (top == NULL) { state->ll_callbacks = callback; @@ -49,36 +50,16 @@ PyUnstable_AtExit(PyInterpreterState *interp, callback->next = top; state->ll_callbacks = callback; } + _PyAtExit_UnlockCallbacks(state); return 0; } -static void -atexit_delete_cb(struct atexit_state *state, int i) -{ - atexit_py_callback *cb = state->callbacks[i]; - state->callbacks[i] = NULL; - - Py_DECREF(cb->func); - Py_DECREF(cb->args); - Py_XDECREF(cb->kwargs); - PyMem_Free(cb); -} - - /* Clear all callbacks without calling them */ static void atexit_cleanup(struct atexit_state *state) { - atexit_py_callback *cb; - for (int i = 0; i < state->ncallbacks; i++) { - cb = state->callbacks[i]; - if (cb == NULL) - continue; - - atexit_delete_cb(state, i); - } - state->ncallbacks = 0; + PyList_Clear(state->callbacks); } @@ -89,23 +70,21 @@ _PyAtExit_Init(PyInterpreterState *interp) // _PyAtExit_Init() must only be called once assert(state->callbacks == NULL); - state->callback_len = 32; - state->ncallbacks = 0; - state->callbacks = PyMem_New(atexit_py_callback*, state->callback_len); + state->callbacks = PyList_New(0); if (state->callbacks == NULL) { return _PyStatus_NO_MEMORY(); } return _PyStatus_OK(); } - void _PyAtExit_Fini(PyInterpreterState *interp) { + // In theory, there shouldn't be any threads left by now, so we + // won't lock this. struct atexit_state *state = &interp->atexit; atexit_cleanup(state); - PyMem_Free(state->callbacks); - state->callbacks = NULL; + Py_CLEAR(state->callbacks); atexit_callback *next = state->ll_callbacks; state->ll_callbacks = NULL; @@ -120,35 +99,44 @@ _PyAtExit_Fini(PyInterpreterState *interp) } } - static void atexit_callfuncs(struct atexit_state *state) { assert(!PyErr_Occurred()); + assert(state->callbacks != NULL); + assert(PyList_CheckExact(state->callbacks)); - if (state->ncallbacks == 0) { + // Create a copy of the list for thread safety + PyObject *copy = PyList_GetSlice(state->callbacks, 0, PyList_GET_SIZE(state->callbacks)); + if (copy == NULL) + { + PyErr_WriteUnraisable(NULL); return; } - for (int i = state->ncallbacks - 1; i >= 0; i--) { - atexit_py_callback *cb = state->callbacks[i]; - if (cb == NULL) { - continue; - } + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(copy); ++i) { + // We don't have to worry about evil borrowed references, because + // no other threads can access this list. + PyObject *tuple = PyList_GET_ITEM(copy, i); + assert(PyTuple_CheckExact(tuple)); + + PyObject *func = PyTuple_GET_ITEM(tuple, 0); + PyObject *args = PyTuple_GET_ITEM(tuple, 1); + PyObject *kwargs = PyTuple_GET_ITEM(tuple, 2); - // bpo-46025: Increment the refcount of cb->func as the call itself may unregister it - PyObject* the_func = Py_NewRef(cb->func); - PyObject *res = PyObject_Call(cb->func, cb->args, cb->kwargs); + PyObject *res = PyObject_Call(func, + args, + kwargs == Py_None ? NULL : kwargs); if (res == NULL) { PyErr_FormatUnraisable( - "Exception ignored in atexit callback %R", the_func); + "Exception ignored in atexit callback %R", func); } else { Py_DECREF(res); } - Py_DECREF(the_func); } + Py_DECREF(copy); atexit_cleanup(state); assert(!PyErr_Occurred()); @@ -194,33 +182,27 @@ atexit_register(PyObject *module, PyObject *args, PyObject *kwargs) "the first argument must be callable"); return NULL; } + PyObject *func_args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args)); + PyObject *func_kwargs = kwargs; - struct atexit_state *state = get_atexit_state(); - if (state->ncallbacks >= state->callback_len) { - atexit_py_callback **r; - state->callback_len += 16; - size_t size = sizeof(atexit_py_callback*) * (size_t)state->callback_len; - r = (atexit_py_callback**)PyMem_Realloc(state->callbacks, size); - if (r == NULL) { - return PyErr_NoMemory(); - } - state->callbacks = r; + if (func_kwargs == NULL) + { + func_kwargs = Py_None; } - - atexit_py_callback *callback = PyMem_Malloc(sizeof(atexit_py_callback)); - if (callback == NULL) { - return PyErr_NoMemory(); + PyObject *callback = PyTuple_Pack(3, func, func_args, func_kwargs); + if (callback == NULL) + { + return NULL; } - callback->args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args)); - if (callback->args == NULL) { - PyMem_Free(callback); + struct atexit_state *state = get_atexit_state(); + // atexit callbacks go in a LIFO order + if (PyList_Insert(state->callbacks, 0, callback) < 0) + { + Py_DECREF(callback); return NULL; } - callback->func = Py_NewRef(func); - callback->kwargs = Py_XNewRef(kwargs); - - state->callbacks[state->ncallbacks++] = callback; + Py_DECREF(callback); return Py_NewRef(func); } @@ -264,7 +246,33 @@ static PyObject * atexit_ncallbacks(PyObject *module, PyObject *unused) { struct atexit_state *state = get_atexit_state(); - return PyLong_FromSsize_t(state->ncallbacks); + assert(state->callbacks != NULL); + assert(PyList_CheckExact(state->callbacks)); + return PyLong_FromSsize_t(PyList_GET_SIZE(state->callbacks)); +} + +static int +atexit_unregister_locked(PyObject *callbacks, PyObject *func) +{ + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(callbacks); ++i) { + PyObject *tuple = PyList_GET_ITEM(callbacks, i); + assert(PyTuple_CheckExact(tuple)); + PyObject *to_compare = PyTuple_GET_ITEM(tuple, 0); + int cmp = PyObject_RichCompareBool(func, to_compare, Py_EQ); + if (cmp < 0) + { + return -1; + } + if (cmp == 1) { + // We found a callback! + if (PyList_SetSlice(callbacks, i, i + 1, NULL) < 0) { + return -1; + } + --i; + } + } + + return 0; } PyDoc_STRVAR(atexit_unregister__doc__, @@ -280,22 +288,11 @@ static PyObject * atexit_unregister(PyObject *module, PyObject *func) { struct atexit_state *state = get_atexit_state(); - for (int i = 0; i < state->ncallbacks; i++) - { - atexit_py_callback *cb = state->callbacks[i]; - if (cb == NULL) { - continue; - } - - int eq = PyObject_RichCompareBool(cb->func, func, Py_EQ); - if (eq < 0) { - return NULL; - } - if (eq) { - atexit_delete_cb(state, i); - } - } - Py_RETURN_NONE; + int result; + Py_BEGIN_CRITICAL_SECTION(state->callbacks); + result = atexit_unregister_locked(state->callbacks, func); + Py_END_CRITICAL_SECTION(); + return result < 0 ? NULL : Py_None; } From cfeaa992ba9bad9be2687afcafd85156703d74e8 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Mon, 16 Dec 2024 22:59:36 +0200 Subject: [PATCH 041/376] Free arena on _PyCompile_AstOptimize failure in Py_CompileStringObject (GH-127910) After commit 10a91d7e9 introduced arena cleanup, commit 2dfbd4f36 removed the free call when _PyCompile_AstOptimize fails. --- Python/pythonrun.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 8b57018321c070..31e065ff00d59a 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1486,6 +1486,7 @@ Py_CompileStringObject(const char *str, PyObject *filename, int start, if (flags && (flags->cf_flags & PyCF_ONLY_AST)) { if ((flags->cf_flags & PyCF_OPTIMIZED_AST) == PyCF_OPTIMIZED_AST) { if (_PyCompile_AstOptimize(mod, filename, flags, optimize, arena) < 0) { + _PyArena_Free(arena); return NULL; } } From 1183e4ce2f7c07aeeff7c757ec749ef5af9d4415 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 17 Dec 2024 08:48:23 +0100 Subject: [PATCH 042/376] gh-127146: Emscripten clean up test suite (#127984) Removed test skips that are no longer required as a result of Emscripten updates. --- Lib/test/libregrtest/setup.py | 4 +-- Lib/test/libregrtest/utils.py | 11 ------- Lib/test/test_fileio.py | 4 +-- Lib/test/test_logging.py | 3 -- Lib/test/test_os.py | 44 ++++++++----------------- Lib/test/test_pathlib/test_pathlib.py | 7 +--- Lib/test/test_pydoc/test_pydoc.py | 1 - Lib/test/test_strptime.py | 6 ---- Lib/test/test_support.py | 1 - Lib/test/test_tempfile.py | 10 ------ Lib/test/test_time.py | 6 ---- Lib/test/test_unicode_file_functions.py | 4 +-- 12 files changed, 19 insertions(+), 82 deletions(-) diff --git a/Lib/test/libregrtest/setup.py b/Lib/test/libregrtest/setup.py index ba57f06b4841d4..c0346aa934d394 100644 --- a/Lib/test/libregrtest/setup.py +++ b/Lib/test/libregrtest/setup.py @@ -11,7 +11,7 @@ from .filter import set_match_tests from .runtests import RunTests from .utils import ( - setup_unraisable_hook, setup_threading_excepthook, fix_umask, + setup_unraisable_hook, setup_threading_excepthook, adjust_rlimit_nofile) @@ -26,8 +26,6 @@ def setup_test_dir(testdir: str | None) -> None: def setup_process() -> None: - fix_umask() - assert sys.__stderr__ is not None, "sys.__stderr__ is None" try: stderr_fd = sys.__stderr__.fileno() diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index 1ecfc61af9e39d..3eff9e753b6d84 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -478,17 +478,6 @@ def get_temp_dir(tmp_dir: StrPath | None = None) -> StrPath: return os.path.abspath(tmp_dir) -def fix_umask() -> None: - if support.is_emscripten: - # Emscripten has default umask 0o777, which breaks some tests. - # see https://github.com/emscripten-core/emscripten/issues/17269 - old_mask = os.umask(0) - if old_mask == 0o777: - os.umask(0o027) - else: - os.umask(old_mask) - - def get_work_dir(parent_dir: StrPath, worker: bool = False) -> StrPath: # Define a writable temp dir that will be used as cwd while running # the tests. The name of the dir includes the pid to allow parallel diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py index e681417e15d34b..5a0f033ebb82d2 100644 --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -10,7 +10,7 @@ from functools import wraps from test.support import ( - cpython_only, swap_attr, gc_collect, is_emscripten, is_wasi, + cpython_only, swap_attr, gc_collect, is_wasi, infinite_recursion, strace_helper ) from test.support.os_helper import ( @@ -531,7 +531,7 @@ def testAbles(self): self.assertEqual(f.isatty(), False) f.close() - if sys.platform != "win32" and not is_emscripten: + if sys.platform != "win32": try: f = self.FileIO("/dev/tty", "a") except OSError: diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index e72f222e1c7eeb..671b4c57a809aa 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -680,9 +680,6 @@ def test_pathlike_objects(self): os.unlink(fn) @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.') - @unittest.skipIf( - support.is_emscripten, "Emscripten cannot fstat unlinked files." - ) @threading_helper.requires_working_threading() @support.requires_resource('walltime') def test_race(self): diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 8aac92934f6ac0..b0e686cb754b93 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -188,9 +188,6 @@ def test_access(self): os.close(f) self.assertTrue(os.access(os_helper.TESTFN, os.W_OK)) - @unittest.skipIf( - support.is_emscripten, "Test is unstable under Emscripten." - ) @unittest.skipIf( support.is_wasi, "WASI does not support dup." ) @@ -1428,9 +1425,7 @@ def setUp(self): else: self.sub2_tree = (sub2_path, ["SUB21"], ["tmp3"]) - if not support.is_emscripten: - # Emscripten fails with inaccessible directory - os.chmod(sub21_path, 0) + os.chmod(sub21_path, 0) try: os.listdir(sub21_path) except PermissionError: @@ -1726,9 +1721,6 @@ def test_yields_correct_dir_fd(self): # check that listdir() returns consistent information self.assertEqual(set(os.listdir(rootfd)), set(dirs) | set(files)) - @unittest.skipIf( - support.is_emscripten, "Cannot dup stdout on Emscripten" - ) @unittest.skipIf( support.is_android, "dup return value is unpredictable on Android" ) @@ -1745,9 +1737,6 @@ def test_fd_leak(self): self.addCleanup(os.close, newfd) self.assertEqual(newfd, minfd) - @unittest.skipIf( - support.is_emscripten, "Cannot dup stdout on Emscripten" - ) @unittest.skipIf( support.is_android, "dup return value is unpredictable on Android" ) @@ -1816,8 +1805,8 @@ def test_makedir(self): os.makedirs(path) @unittest.skipIf( - support.is_emscripten or support.is_wasi, - "Emscripten's/WASI's umask is a stub." + support.is_wasi, + "WASI's umask is a stub." ) def test_mode(self): with os_helper.temp_umask(0o002): @@ -1832,8 +1821,8 @@ def test_mode(self): self.assertEqual(os.stat(parent).st_mode & 0o777, 0o775) @unittest.skipIf( - support.is_emscripten or support.is_wasi, - "Emscripten's/WASI's umask is a stub." + support.is_wasi, + "WASI's umask is a stub." ) def test_exist_ok_existing_directory(self): path = os.path.join(os_helper.TESTFN, 'dir1') @@ -1850,8 +1839,8 @@ def test_exist_ok_existing_directory(self): os.makedirs(os.path.abspath('/'), exist_ok=True) @unittest.skipIf( - support.is_emscripten or support.is_wasi, - "Emscripten's/WASI's umask is a stub." + support.is_wasi, + "WASI's umask is a stub." ) def test_exist_ok_s_isgid_directory(self): path = os.path.join(os_helper.TESTFN, 'dir1') @@ -2429,10 +2418,6 @@ def test_dup2(self): self.check(os.dup2, 20) @unittest.skipUnless(hasattr(os, 'dup2'), 'test needs os.dup2()') - @unittest.skipIf( - support.is_emscripten, - "dup2() with negative fds is broken on Emscripten (see gh-102179)" - ) def test_dup2_negative_fd(self): valid_fd = os.open(__file__, os.O_RDONLY) self.addCleanup(os.close, valid_fd) @@ -2457,14 +2442,14 @@ def test_fchown(self): self.check(os.fchown, -1, -1) @unittest.skipUnless(hasattr(os, 'fpathconf'), 'test needs os.fpathconf()') - @unittest.skipIf( - support.is_emscripten or support.is_wasi, - "musl libc issue on Emscripten/WASI, bpo-46390" - ) def test_fpathconf(self): self.assertIn("PC_NAME_MAX", os.pathconf_names) - self.check(os.pathconf, "PC_NAME_MAX") - self.check(os.fpathconf, "PC_NAME_MAX") + if not (support.is_emscripten or support.is_wasi): + # musl libc pathconf ignores the file descriptor and always returns + # a constant, so the assertion that it should notice a bad file + # descriptor and return EBADF fails. + self.check(os.pathconf, "PC_NAME_MAX") + self.check(os.fpathconf, "PC_NAME_MAX") self.check_bool(os.pathconf, "PC_NAME_MAX") self.check_bool(os.fpathconf, "PC_NAME_MAX") @@ -3395,9 +3380,6 @@ def test_bad_fd(self): @unittest.skipUnless(os.isatty(0) and not win32_is_iot() and (sys.platform.startswith('win') or (hasattr(locale, 'nl_langinfo') and hasattr(locale, 'CODESET'))), 'test requires a tty and either Windows or nl_langinfo(CODESET)') - @unittest.skipIf( - support.is_emscripten, "Cannot get encoding of stdin on Emscripten" - ) def test_device_encoding(self): encoding = os.device_encoding(0) self.assertIsNotNone(encoding) diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index 68bff2cf0d511e..ac3a3b4f15c10e 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -1673,7 +1673,6 @@ def test_mkdir_exist_ok_with_parent(self): self.assertTrue(p.exists()) self.assertEqual(p.stat().st_ctime, st_ctime_first) - @unittest.skipIf(is_emscripten, "FS root cannot be modified on Emscripten.") def test_mkdir_exist_ok_root(self): # Issue #25803: A drive root could raise PermissionError on Windows. self.cls('/').resolve().mkdir(exist_ok=True) @@ -2039,7 +2038,6 @@ def test_rglob_pathlike(self): self.assertEqual(expect, set(p.rglob(FakePath(pattern)))) @needs_symlinks - @unittest.skipIf(is_emscripten, "Hangs") def test_glob_recurse_symlinks_common(self): def _check(path, glob, expected): actual = {path for path in path.glob(glob, recurse_symlinks=True) @@ -2077,7 +2075,6 @@ def _check(path, glob, expected): _check(p, "*/dirD/**/", ["dirC/dirD/"]) @needs_symlinks - @unittest.skipIf(is_emscripten, "Hangs") def test_rglob_recurse_symlinks_common(self): def _check(path, glob, expected): actual = {path for path in path.rglob(glob, recurse_symlinks=True) @@ -2484,9 +2481,7 @@ def setUp(self): os.symlink(tmp5_path, broken_link3_path) self.sub2_tree[2].append('broken_link3') self.sub2_tree[2].sort() - if not is_emscripten: - # Emscripten fails with inaccessible directories. - os.chmod(sub21_path, 0) + os.chmod(sub21_path, 0) try: os.listdir(sub21_path) except PermissionError: diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index 2a4d3ab73db608..3283fde9e12a8a 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -1224,7 +1224,6 @@ def test_apropos_with_unreadable_dir(self): self.assertEqual(err.getvalue(), '') @os_helper.skip_unless_working_chmod - @unittest.skipIf(is_emscripten, "cannot remove x bit") def test_apropos_empty_doc(self): pkgdir = os.path.join(TESTFN, 'walkpkg') os.mkdir(pkgdir) diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index 9f5cfca9c7f124..0d30a63ab0c140 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -79,9 +79,6 @@ def test_am_pm(self): self.assertEqual(self.LT_ins.am_pm[position], strftime_output, "AM/PM representation in the wrong position within the tuple") - @unittest.skipIf( - support.is_emscripten, "musl libc issue on Emscripten, bpo-46390" - ) def test_timezone(self): # Make sure timezone is correct timezone = time.strftime("%Z", self.time_tuple).lower() @@ -431,9 +428,6 @@ def test_bad_offset(self): self.assertEqual("Inconsistent use of : in -01:3030", str(err.exception)) @skip_if_buggy_ucrt_strfptime - @unittest.skipIf( - support.is_emscripten, "musl libc issue on Emscripten, bpo-46390" - ) def test_timezone(self): # Test timezone directives. # When gmtime() is used with %Z, entire result of strftime() is empty. diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 635ae03a404988..d900db546ada8d 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -549,7 +549,6 @@ def test_optim_args_from_interpreter_flags(self): self.check_options(opts, 'optim_args_from_interpreter_flags') @unittest.skipIf(support.is_apple_mobile, "Unstable on Apple Mobile") - @unittest.skipIf(support.is_emscripten, "Unstable in Emscripten") @unittest.skipIf(support.is_wasi, "Unavailable on WASI") def test_fd_count(self): # We cannot test the absolute value of fd_count(): on old Linux kernel diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index a5e182cef23dc5..57e9bd20c77ee1 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -328,10 +328,6 @@ def _mock_candidate_names(*names): class TestBadTempdir: - - @unittest.skipIf( - support.is_emscripten, "Emscripten cannot remove write bits." - ) def test_read_only_directory(self): with _inside_empty_temp_dir(): oldmode = mode = os.stat(tempfile.tempdir).st_mode @@ -1240,9 +1236,6 @@ def test_del_unrolled_file(self): with self.assertWarns(ResourceWarning): f.__del__() - @unittest.skipIf( - support.is_emscripten, "Emscripten cannot fstat renamed files." - ) def test_del_rolled_file(self): # The rolled file should be deleted when the SpooledTemporaryFile # object is deleted. This should raise a ResourceWarning since the file @@ -1468,9 +1461,6 @@ def use_closed(): pass self.assertRaises(ValueError, use_closed) - @unittest.skipIf( - support.is_emscripten, "Emscripten cannot fstat renamed files." - ) def test_truncate_with_size_parameter(self): # A SpooledTemporaryFile can be truncated to zero size f = tempfile.SpooledTemporaryFile(max_size=10) diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index 92398300f26577..1c540bed33c71e 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -361,9 +361,6 @@ def test_asctime(self): def test_asctime_bounding_check(self): self._bounds_checking(time.asctime) - @unittest.skipIf( - support.is_emscripten, "musl libc issue on Emscripten, bpo-46390" - ) def test_ctime(self): t = time.mktime((1973, 9, 16, 1, 3, 52, 0, 0, -1)) self.assertEqual(time.ctime(t), 'Sun Sep 16 01:03:52 1973') @@ -746,9 +743,6 @@ class TestStrftime4dyear(_TestStrftimeYear, _Test4dYear, unittest.TestCase): class TestPytime(unittest.TestCase): @skip_if_buggy_ucrt_strfptime @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") - @unittest.skipIf( - support.is_emscripten, "musl libc issue on Emscripten, bpo-46390" - ) def test_localtime_timezone(self): # Get the localtime and examine it for the offset and zone. diff --git a/Lib/test/test_unicode_file_functions.py b/Lib/test/test_unicode_file_functions.py index 25c16e3a0b7e43..4a067d714e12e3 100644 --- a/Lib/test/test_unicode_file_functions.py +++ b/Lib/test/test_unicode_file_functions.py @@ -125,8 +125,8 @@ def test_open(self): # open(), os.stat(), etc. don't raise any exception. @unittest.skipIf(is_apple, 'irrelevant test on Apple platforms') @unittest.skipIf( - support.is_emscripten or support.is_wasi, - "test fails on Emscripten/WASI when host platform is macOS." + support.is_wasi, + "test fails on WASI when host platform is macOS." ) def test_normalize(self): files = set(self.files) From b9a492b809d8765ee365a5dd3c6ba4e5130a80af Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 17 Dec 2024 16:18:33 +0800 Subject: [PATCH 043/376] gh-127085: Add a test skip if multiprocessing isn't available (#128019) Add a test skip if multiprocessing isn't available. --- Lib/test/test_memoryview.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py index 856e17918048e4..61b068c630c7ce 100644 --- a/Lib/test/test_memoryview.py +++ b/Lib/test/test_memoryview.py @@ -739,7 +739,10 @@ def test_picklebuffer_reference_loop(self): class RacingTest(unittest.TestCase): def test_racing_getbuf_and_releasebuf(self): """Repeatly access the memoryview for racing.""" - from multiprocessing.managers import SharedMemoryManager + try: + from multiprocessing.managers import SharedMemoryManager + except ImportError: + self.skipTest("Test requires multiprocessing") from threading import Thread n = 100 From 401bfc69d1258fde07141cd1a12af00aa44a8e0f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:49:37 +0200 Subject: [PATCH 044/376] Python 3.14.0a3 --- Doc/c-api/long.rst | 4 +- Doc/library/ctypes.rst | 4 +- Doc/library/errno.rst | 2 +- Doc/library/select.rst | 2 +- Doc/library/stdtypes.rst | 4 +- Include/patchlevel.h | 4 +- Lib/pydoc_data/topics.py | 57 +- Misc/NEWS.d/3.14.0a3.rst | 1152 +++++++++++++++++ ...-11-20-17-12-40.gh-issue-126898.I2zILt.rst | 1 - ...-11-22-08-46-46.gh-issue-115869.UVLSKd.rst | 1 - ...-11-30-16-36-09.gh-issue-127111.QI9mMZ.rst | 2 - ...-12-06-12-47-52.gh-issue-127629.tD-ERQ.rst | 1 - ...-12-12-17-21-45.gh-issue-127865.30GDzs.rst | 1 - ...-07-03-13-39-13.gh-issue-121058.MKi1MV.rst | 2 - ...-07-03-17-26-53.gh-issue-102471.XpmKYk.rst | 10 - ...-08-12-10-15-19.gh-issue-109523.S2c3fi.rst | 1 - ...-08-27-09-07-56.gh-issue-123378.JJ6n_u.rst | 6 - ...-11-26-22-06-10.gh-issue-127314.SsRrIu.rst | 2 - ...-12-02-16-10-36.gh-issue-123378.Q6YRwe.rst | 6 - ...-12-06-16-53-34.gh-issue-127691.k_Jitp.rst | 3 - ...-12-10-14-25-22.gh-issue-127791.YRw4GU.rst | 2 - ...-12-16-07-12-15.gh-issue-127896.HmI9pk.rst | 2 - ...-09-22-21-01-56.gh-issue-109746.32MHt9.rst | 1 - ...-06-04-08-26-25.gh-issue-120010._z-AWz.rst | 2 - ...4-08-03-14-02-27.gh-issue-69639.mW3iKq.rst | 2 - ...-09-25-21-50-23.gh-issue-124470.pFr3_d.rst | 1 - ...-10-14-12-34-51.gh-issue-125420.jABXoZ.rst | 2 - ...-10-14-13-28-16.gh-issue-125420.hNKixM.rst | 2 - ...-10-27-04-47-28.gh-issue-126024.XCQSqT.rst | 2 - ...-11-07-21-48-23.gh-issue-126091.ETaRGE.rst | 2 - ...-11-15-16-39-37.gh-issue-126892.QR6Yo3.rst | 2 - ...-11-16-11-11-35.gh-issue-126881.ijofLZ.rst | 1 - ...-11-16-22-37-46.gh-issue-126868.yOoHSY.rst | 1 - ...-11-17-21-35-55.gh-issue-126937.qluVM0.rst | 3 - ...-11-18-23-18-17.gh-issue-126980.r8QHdi.rst | 3 - ...-11-19-17-17-32.gh-issue-127010.9Cl4bb.rst | 4 - ...-11-19-21-49-58.gh-issue-127020.5vvI17.rst | 4 - ...-11-21-16-13-52.gh-issue-126491.0YvL94.rst | 4 - ...-11-23-04-54-42.gh-issue-127133.WMoJjF.rst | 6 - ...-11-24-07-01-28.gh-issue-113841.WFg-Bu.rst | 2 - ...-11-25-05-15-21.gh-issue-127238.O8wkH-.rst | 1 - ...-11-30-23-35-45.gh-issue-127085.KLKylb.rst | 1 - ...-12-03-21-07-06.gh-issue-127536.3jMMrT.rst | 2 - ...-12-04-09-52-08.gh-issue-127434.RjkGT_.rst | 1 - ...-12-05-19-25-00.gh-issue-127582.ogUY2a.rst | 2 - ...-12-06-01-09-40.gh-issue-127651.80cm6j.rst | 1 - ...-12-07-13-06-09.gh-issue-127599.tXCZb_.rst | 2 - ...-12-09-11-29-10.gh-issue-127058.pqtBcZ.rst | 3 - ...-12-10-21-08-05.gh-issue-127740.0tWC9h.rst | 3 - ...-11-27-22-56-48.gh-issue-127347.xyddWS.rst | 1 - ...2-11-10-17-16-45.gh-issue-97514.kzA0zl.rst | 10 - ...3-02-15-23-54-42.gh-issue-88110.KU6erv.rst | 2 - ...-07-25-18-06-51.gh-issue-122288.-_xxOR.rst | 2 - ...-07-29-15-20-30.gh-issue-122356.wKCmFx.rst | 3 - ...-07-30-11-37-40.gh-issue-122431.lAzVtu.rst | 1 - ...-08-27-18-58-01.gh-issue-123401.t4-FpI.rst | 3 - ...-09-13-18-24-27.gh-issue-124008.XaiPQx.rst | 2 - ...4-10-23-20-05-54.gh-issue-86463.jvFTI_.rst | 2 - ...-10-28-19-49-18.gh-issue-118201.v41XXh.rst | 2 - ...4-11-04-22-02-30.gh-issue-85046.Y5d_ZN.rst | 1 - ...-11-12-13-14-47.gh-issue-126727.5Eqfqd.rst | 3 - ...-11-12-20-05-09.gh-issue-126601.Nj7bA9.rst | 3 - ...-11-13-10-44-25.gh-issue-126775.a3ubjh.rst | 1 - ...-11-13-19-15-18.gh-issue-126780.ZZqJvI.rst | 1 - ...4-11-15-01-50-36.gh-issue-85168.bP8VIN.rst | 4 - ...-11-16-10-52-48.gh-issue-126899.GFnfBt.rst | 2 - ...-11-18-16-43-11.gh-issue-126946.52Ou-B.rst | 3 - ...-11-18-19-03-46.gh-issue-126947.NiDYUe.rst | 2 - ...-11-18-22-02-47.gh-issue-118761.GQKD_J.rst | 2 - ...-11-18-23-18-27.gh-issue-112192.DRdRgP.rst | 1 - ...-11-18-23-42-06.gh-issue-126985.7XplY9.rst | 3 - ...-11-19-14-34-05.gh-issue-126615.LOskwi.rst | 2 - ...-11-20-08-54-11.gh-issue-126618.ef_53g.rst | 2 - ...-11-20-11-37-08.gh-issue-126316.ElkZmE.rst | 2 - ...-11-20-16-58-59.gh-issue-126997.0PI41Y.rst | 3 - ...-11-20-21-20-56.gh-issue-126992.RbU0FZ.rst | 1 - ...-11-21-06-03-46.gh-issue-127090.yUYwdh.rst | 3 - ...-11-21-16-23-16.gh-issue-127065.cfL1zd.rst | 2 - ...-11-22-02-31-55.gh-issue-126766.jfkhBH.rst | 2 - ...-11-22-03-40-02.gh-issue-127078.gI_PaP.rst | 2 - ...-11-22-04-49-31.gh-issue-125866.TUtvPK.rst | 2 - ...-11-22-09-23-41.gh-issue-122273.H8M6fd.rst | 1 - ...-11-22-10-42-34.gh-issue-127035.UnbDlr.rst | 4 - ...-11-23-00-17-29.gh-issue-127221.OSXdFE.rst | 1 - ...-11-23-12-25-06.gh-issue-125866.wEOP66.rst | 5 - ...-11-24-12-41-31.gh-issue-127217.UAXGFr.rst | 2 - ...-11-24-14-20-17.gh-issue-127182.WmfY2g.rst | 2 - ...-11-25-15-02-44.gh-issue-127255.UXeljc.rst | 2 - ...-11-25-19-04-10.gh-issue-127072.-c284K.rst | 1 - ...-11-26-17-42-00.gh-issue-127178.U8hxjc.rst | 4 - ...-11-27-14-06-35.gh-issue-123967.wxUmnW.rst | 2 - ...-11-27-14-23-02.gh-issue-127331.9sNEC9.rst | 1 - ...-11-27-16-06-10.gh-issue-127303.asqkgh.rst | 1 - ...4-11-27-17-04-38.gh-issue-59705.sAGyvs.rst | 2 - ...-11-28-14-14-46.gh-issue-127257.n6-jU9.rst | 2 - ...-11-29-00-15-59.gh-issue-125413.WCN0vv.rst | 3 - ...-11-29-14-45-26.gh-issue-127413.z11AUc.rst | 2 - ...-11-29-23-02-43.gh-issue-127429.dQf2w4.rst | 3 - ...-11-30-21-46-15.gh-issue-127321.M78fBv.rst | 1 - ...-12-01-22-28-41.gh-issue-127065.tFpRer.rst | 1 - ...-12-01-23-18-43.gh-issue-127481.K36AoP.rst | 1 - ...4-12-04-11-01-16.gh-issue-93312.9sB-Qw.rst | 2 - ...-12-04-15-04-12.gh-issue-126821.lKCLVV.rst | 2 - ...-12-05-10-14-52.gh-issue-127627.fgCHOZ.rst | 2 - ...-12-06-17-28-55.gh-issue-127610.ctv_NP.rst | 3 - ...-12-07-15-28-31.gh-issue-127718.9dpLfi.rst | 1 - ...-12-07-23-06-44.gh-issue-126789.4dqfV1.rst | 5 - ...-12-08-08-36-18.gh-issue-127732.UEKxoa.rst | 1 - ...-12-12-16-59-42.gh-issue-127870._NFG-3.rst | 2 - ...-12-13-22-20-54.gh-issue-126907.fWRL_R.rst | 2 - ...-12-05-21-35-19.gh-issue-127655.xpPoOf.rst | 1 - ...-11-20-18-49-01.gh-issue-127076.DHnXxo.rst | 2 - ...-11-21-02-03-48.gh-issue-127076.a3avV1.rst | 1 - ...-12-04-15-03-24.gh-issue-126925.uxAMK-.rst | 2 - ...-12-09-12-35-44.gh-issue-127637.KLx-9I.rst | 1 - ...-12-13-13-41-34.gh-issue-127906.NuRHlB.rst | 1 - ...-11-16-20-47-20.gh-issue-126700.ayrHv4.rst | 1 - ...-10-31-09-46-53.gh-issue-125729.KdKVLa.rst | 1 - ...-11-28-15-55-48.gh-issue-127353.i-XOXg.rst | 2 - README.rst | 2 +- 120 files changed, 1200 insertions(+), 279 deletions(-) create mode 100644 Misc/NEWS.d/3.14.0a3.rst delete mode 100644 Misc/NEWS.d/next/Build/2024-11-20-17-12-40.gh-issue-126898.I2zILt.rst delete mode 100644 Misc/NEWS.d/next/Build/2024-11-22-08-46-46.gh-issue-115869.UVLSKd.rst delete mode 100644 Misc/NEWS.d/next/Build/2024-11-30-16-36-09.gh-issue-127111.QI9mMZ.rst delete mode 100644 Misc/NEWS.d/next/Build/2024-12-06-12-47-52.gh-issue-127629.tD-ERQ.rst delete mode 100644 Misc/NEWS.d/next/Build/2024-12-12-17-21-45.gh-issue-127865.30GDzs.rst delete mode 100644 Misc/NEWS.d/next/C_API/2024-07-03-13-39-13.gh-issue-121058.MKi1MV.rst delete mode 100644 Misc/NEWS.d/next/C_API/2024-07-03-17-26-53.gh-issue-102471.XpmKYk.rst delete mode 100644 Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst delete mode 100644 Misc/NEWS.d/next/C_API/2024-08-27-09-07-56.gh-issue-123378.JJ6n_u.rst delete mode 100644 Misc/NEWS.d/next/C_API/2024-11-26-22-06-10.gh-issue-127314.SsRrIu.rst delete mode 100644 Misc/NEWS.d/next/C_API/2024-12-02-16-10-36.gh-issue-123378.Q6YRwe.rst delete mode 100644 Misc/NEWS.d/next/C_API/2024-12-06-16-53-34.gh-issue-127691.k_Jitp.rst delete mode 100644 Misc/NEWS.d/next/C_API/2024-12-10-14-25-22.gh-issue-127791.YRw4GU.rst delete mode 100644 Misc/NEWS.d/next/C_API/2024-12-16-07-12-15.gh-issue-127896.HmI9pk.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2023-09-22-21-01-56.gh-issue-109746.32MHt9.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-06-04-08-26-25.gh-issue-120010._z-AWz.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-08-03-14-02-27.gh-issue-69639.mW3iKq.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-09-25-21-50-23.gh-issue-124470.pFr3_d.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-10-14-12-34-51.gh-issue-125420.jABXoZ.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-10-14-13-28-16.gh-issue-125420.hNKixM.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-10-27-04-47-28.gh-issue-126024.XCQSqT.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-07-21-48-23.gh-issue-126091.ETaRGE.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-15-16-39-37.gh-issue-126892.QR6Yo3.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-16-11-11-35.gh-issue-126881.ijofLZ.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-16-22-37-46.gh-issue-126868.yOoHSY.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-17-21-35-55.gh-issue-126937.qluVM0.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-18-23-18-17.gh-issue-126980.r8QHdi.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-19-17-17-32.gh-issue-127010.9Cl4bb.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-19-21-49-58.gh-issue-127020.5vvI17.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-21-16-13-52.gh-issue-126491.0YvL94.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-23-04-54-42.gh-issue-127133.WMoJjF.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-24-07-01-28.gh-issue-113841.WFg-Bu.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-25-05-15-21.gh-issue-127238.O8wkH-.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-23-35-45.gh-issue-127085.KLKylb.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-03-21-07-06.gh-issue-127536.3jMMrT.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-04-09-52-08.gh-issue-127434.RjkGT_.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-05-19-25-00.gh-issue-127582.ogUY2a.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-06-01-09-40.gh-issue-127651.80cm6j.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-07-13-06-09.gh-issue-127599.tXCZb_.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-09-11-29-10.gh-issue-127058.pqtBcZ.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-10-21-08-05.gh-issue-127740.0tWC9h.rst delete mode 100644 Misc/NEWS.d/next/Documentation/2024-11-27-22-56-48.gh-issue-127347.xyddWS.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-11-10-17-16-45.gh-issue-97514.kzA0zl.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-02-15-23-54-42.gh-issue-88110.KU6erv.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-07-25-18-06-51.gh-issue-122288.-_xxOR.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-07-29-15-20-30.gh-issue-122356.wKCmFx.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-07-30-11-37-40.gh-issue-122431.lAzVtu.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-08-27-18-58-01.gh-issue-123401.t4-FpI.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-09-13-18-24-27.gh-issue-124008.XaiPQx.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-10-23-20-05-54.gh-issue-86463.jvFTI_.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-10-28-19-49-18.gh-issue-118201.v41XXh.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-04-22-02-30.gh-issue-85046.Y5d_ZN.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-12-13-14-47.gh-issue-126727.5Eqfqd.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-12-20-05-09.gh-issue-126601.Nj7bA9.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-13-10-44-25.gh-issue-126775.a3ubjh.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-13-19-15-18.gh-issue-126780.ZZqJvI.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-15-01-50-36.gh-issue-85168.bP8VIN.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-16-10-52-48.gh-issue-126899.GFnfBt.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-18-16-43-11.gh-issue-126946.52Ou-B.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-18-19-03-46.gh-issue-126947.NiDYUe.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-18-22-02-47.gh-issue-118761.GQKD_J.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-18-23-18-27.gh-issue-112192.DRdRgP.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-18-23-42-06.gh-issue-126985.7XplY9.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-19-14-34-05.gh-issue-126615.LOskwi.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-20-08-54-11.gh-issue-126618.ef_53g.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-20-11-37-08.gh-issue-126316.ElkZmE.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-20-16-58-59.gh-issue-126997.0PI41Y.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-20-21-20-56.gh-issue-126992.RbU0FZ.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-21-06-03-46.gh-issue-127090.yUYwdh.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-21-16-23-16.gh-issue-127065.cfL1zd.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-22-02-31-55.gh-issue-126766.jfkhBH.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-22-03-40-02.gh-issue-127078.gI_PaP.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-22-04-49-31.gh-issue-125866.TUtvPK.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-22-09-23-41.gh-issue-122273.H8M6fd.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-22-10-42-34.gh-issue-127035.UnbDlr.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-23-00-17-29.gh-issue-127221.OSXdFE.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-23-12-25-06.gh-issue-125866.wEOP66.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-24-12-41-31.gh-issue-127217.UAXGFr.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-24-14-20-17.gh-issue-127182.WmfY2g.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-25-15-02-44.gh-issue-127255.UXeljc.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-25-19-04-10.gh-issue-127072.-c284K.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-26-17-42-00.gh-issue-127178.U8hxjc.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-27-14-06-35.gh-issue-123967.wxUmnW.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-27-14-23-02.gh-issue-127331.9sNEC9.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-27-16-06-10.gh-issue-127303.asqkgh.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-27-17-04-38.gh-issue-59705.sAGyvs.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-28-14-14-46.gh-issue-127257.n6-jU9.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-29-00-15-59.gh-issue-125413.WCN0vv.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-29-14-45-26.gh-issue-127413.z11AUc.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-29-23-02-43.gh-issue-127429.dQf2w4.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-30-21-46-15.gh-issue-127321.M78fBv.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-01-22-28-41.gh-issue-127065.tFpRer.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-01-23-18-43.gh-issue-127481.K36AoP.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-04-11-01-16.gh-issue-93312.9sB-Qw.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-04-15-04-12.gh-issue-126821.lKCLVV.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-05-10-14-52.gh-issue-127627.fgCHOZ.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-06-17-28-55.gh-issue-127610.ctv_NP.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-07-15-28-31.gh-issue-127718.9dpLfi.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-07-23-06-44.gh-issue-126789.4dqfV1.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-08-08-36-18.gh-issue-127732.UEKxoa.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-12-16-59-42.gh-issue-127870._NFG-3.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-13-22-20-54.gh-issue-126907.fWRL_R.rst delete mode 100644 Misc/NEWS.d/next/Security/2024-12-05-21-35-19.gh-issue-127655.xpPoOf.rst delete mode 100644 Misc/NEWS.d/next/Tests/2024-11-20-18-49-01.gh-issue-127076.DHnXxo.rst delete mode 100644 Misc/NEWS.d/next/Tests/2024-11-21-02-03-48.gh-issue-127076.a3avV1.rst delete mode 100644 Misc/NEWS.d/next/Tests/2024-12-04-15-03-24.gh-issue-126925.uxAMK-.rst delete mode 100644 Misc/NEWS.d/next/Tests/2024-12-09-12-35-44.gh-issue-127637.KLx-9I.rst delete mode 100644 Misc/NEWS.d/next/Tests/2024-12-13-13-41-34.gh-issue-127906.NuRHlB.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2024-11-16-20-47-20.gh-issue-126700.ayrHv4.rst delete mode 100644 Misc/NEWS.d/next/Windows/2024-10-31-09-46-53.gh-issue-125729.KdKVLa.rst delete mode 100644 Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index f48cd07a979f56..084ba513493ffe 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -657,7 +657,7 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. Export API ^^^^^^^^^^ -.. versionadded:: next +.. versionadded:: 3.14 .. c:struct:: PyLongLayout @@ -769,7 +769,7 @@ PyLongWriter API The :c:type:`PyLongWriter` API can be used to import an integer. -.. versionadded:: next +.. versionadded:: 3.14 .. c:struct:: PyLongWriter diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index bd88fa377fb39d..09692e56d29a39 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1964,7 +1964,7 @@ Utility functions .. availability:: Windows - .. versionadded:: next + .. versionadded:: 3.14 .. function:: cast(obj, type) @@ -2825,4 +2825,4 @@ Exceptions .. availability:: Windows - .. versionadded:: next + .. versionadded:: 3.14 diff --git a/Doc/library/errno.rst b/Doc/library/errno.rst index 824d489818fac9..d8033663ea8eac 100644 --- a/Doc/library/errno.rst +++ b/Doc/library/errno.rst @@ -617,7 +617,7 @@ defined by the module. The specific list of defined symbols is available as Memory page has hardware error. - .. versionadded:: next + .. versionadded:: 3.14 .. data:: EALREADY diff --git a/Doc/library/select.rst b/Doc/library/select.rst index 4fcff9198944a8..457970aed2dc73 100644 --- a/Doc/library/select.rst +++ b/Doc/library/select.rst @@ -324,7 +324,7 @@ Edge and Level Trigger Polling (epoll) Objects :const:`EPOLLEXCLUSIVE` was added. It's only supported by Linux Kernel 4.5 or later. - .. versionadded:: next + .. versionadded:: 3.14 :const:`EPOLLWAKEUP` was added. It's only supported by Linux Kernel 3.5 or later. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index d5f7714ced6873..191827526e890f 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4153,7 +4153,7 @@ copying. Count the number of occurrences of *value*. - .. versionadded:: next + .. versionadded:: 3.14 .. method:: index(value, start=0, stop=sys.maxsize, /) @@ -4162,7 +4162,7 @@ copying. Raises a :exc:`ValueError` if *value* cannot be found. - .. versionadded:: next + .. versionadded:: 3.14 There are also several readonly attributes available: diff --git a/Include/patchlevel.h b/Include/patchlevel.h index e99c3a66f84e4f..1e9df2d9ebd07d 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -20,10 +20,10 @@ #define PY_MINOR_VERSION 14 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA -#define PY_RELEASE_SERIAL 2 +#define PY_RELEASE_SERIAL 3 /* Version as a string */ -#define PY_VERSION "3.14.0a2+" +#define PY_VERSION "3.14.0a3" /*--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 f73e55d77311ae..aebcef2b81d43d 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 Nov 19 16:52:22 2024 +# Autogenerated by Sphinx on Tue Dec 17 11:49:52 2024 # as part of the release process. topics = {'assert': 'The "assert" statement\n' '**********************\n' @@ -1312,15 +1312,19 @@ 'The arguments must either both be numbers, or one argument must be ' 'an\n' 'integer and the other must be a sequence. In the former case, the\n' - 'numbers are converted to a common type and then multiplied ' - 'together.\n' - 'In the latter case, sequence repetition is performed; a negative\n' - 'repetition factor yields an empty sequence.\n' + 'numbers are converted to a common real type and then multiplied\n' + 'together. In the latter case, sequence repetition is performed; ' + 'a\n' + 'negative repetition factor yields an empty sequence.\n' '\n' 'This operation can be customized using the special "__mul__()" ' 'and\n' '"__rmul__()" methods.\n' '\n' + 'Changed in version 3.14: If only one operand is a complex number, ' + 'the\n' + 'other operand is converted to a floating-point number.\n' + '\n' 'The "@" (at) operator is intended to be used for matrix\n' 'multiplication. No builtin Python types implement this operator.\n' '\n' @@ -1391,21 +1395,30 @@ 'arguments must either both be numbers or both be sequences of the ' 'same\n' 'type. In the former case, the numbers are converted to a common ' - 'type\n' - 'and then added together. In the latter case, the sequences are\n' + 'real\n' + 'type and then added together. In the latter case, the sequences ' + 'are\n' 'concatenated.\n' '\n' 'This operation can be customized using the special "__add__()" ' 'and\n' '"__radd__()" methods.\n' '\n' + 'Changed in version 3.14: If only one operand is a complex number, ' + 'the\n' + 'other operand is converted to a floating-point number.\n' + '\n' 'The "-" (subtraction) operator yields the difference of its ' 'arguments.\n' - 'The numeric arguments are first converted to a common type.\n' + 'The numeric arguments are first converted to a common real type.\n' '\n' 'This operation can be customized using the special "__sub__()" ' 'and\n' - '"__rsub__()" methods.\n', + '"__rsub__()" methods.\n' + '\n' + 'Changed in version 3.14: If only one operand is a complex number, ' + 'the\n' + 'other operand is converted to a floating-point number.\n', 'bitwise': 'Binary bitwise operations\n' '*************************\n' '\n' @@ -4561,18 +4574,18 @@ '\n' 'When a description of an arithmetic operator below uses the ' 'phrase\n' - '“the numeric arguments are converted to a common type”, this ' - 'means\n' - 'that the operator implementation for built-in types works as ' + '“the numeric arguments are converted to a common real type”, ' + 'this\n' + 'means that the operator implementation for built-in types ' + 'works as\n' 'follows:\n' '\n' - '* If either argument is a complex number, the other is ' - 'converted to\n' - ' complex;\n' + '* If both arguments are complex numbers, no conversion is ' + 'performed;\n' '\n' - '* otherwise, if either argument is a floating-point number, ' - 'the other\n' - ' is converted to floating point;\n' + '* if either argument is a complex or a floating-point number, ' + 'the\n' + ' other is converted to a floating-point number;\n' '\n' '* otherwise, both must be integers and no conversion is ' 'necessary.\n' @@ -7144,8 +7157,12 @@ 'trailing zeros are not removed from the result.\n' '\n' 'The "\',\'" option signals the use of a comma for a ' - 'thousands separator.\n' - 'For a locale aware separator, use the "\'n\'" integer ' + 'thousands separator\n' + 'for floating-point presentation types and for integer ' + 'presentation\n' + 'type "\'d\'". For other presentation types, this option is ' + 'an error. For\n' + 'a locale aware separator, use the "\'n\'" integer ' 'presentation type\n' 'instead.\n' '\n' diff --git a/Misc/NEWS.d/3.14.0a3.rst b/Misc/NEWS.d/3.14.0a3.rst new file mode 100644 index 00000000000000..8393be8909ff8f --- /dev/null +++ b/Misc/NEWS.d/3.14.0a3.rst @@ -0,0 +1,1152 @@ +.. date: 2024-11-28-15-55-48 +.. gh-issue: 127353 +.. nonce: i-XOXg +.. release date: 2024-12-17 +.. section: Windows + +Allow to force color output on Windows using environment variables. Patch by +Andrey Efremov. + +.. + +.. date: 2024-10-31-09-46-53 +.. gh-issue: 125729 +.. nonce: KdKVLa +.. section: Windows + +Makes the presence of the :mod:`turtle` module dependent on the Tcl/Tk +installer option. Previously, the module was always installed but would be +unusable without Tcl/Tk. + +.. + +.. date: 2024-11-16-20-47-20 +.. gh-issue: 126700 +.. nonce: ayrHv4 +.. section: Tools/Demos + +Add support for multi-argument :mod:`gettext` functions in +:program:`pygettext.py`. + +.. + +.. date: 2024-12-13-13-41-34 +.. gh-issue: 127906 +.. nonce: NuRHlB +.. section: Tests + +Test the limited C API in test_cppext. Patch by Victor Stinner. + +.. + +.. date: 2024-12-09-12-35-44 +.. gh-issue: 127637 +.. nonce: KLx-9I +.. section: Tests + +Add tests for the :mod:`dis` command-line interface. Patch by Bénédikt Tran. + +.. + +.. date: 2024-12-04-15-03-24 +.. gh-issue: 126925 +.. nonce: uxAMK- +.. section: Tests + +iOS test results are now streamed during test execution, and the deprecated +xcresulttool is no longer used. + +.. + +.. date: 2024-11-21-02-03-48 +.. gh-issue: 127076 +.. nonce: a3avV1 +.. section: Tests + +Disable strace based system call tests when LD_PRELOAD is set. + +.. + +.. date: 2024-11-20-18-49-01 +.. gh-issue: 127076 +.. nonce: DHnXxo +.. section: Tests + +Filter out memory-related ``mmap``, ``munmap``, and ``mprotect`` calls from +file-related ones when testing :mod:`io` behavior using strace. + +.. + +.. date: 2024-12-05-21-35-19 +.. gh-issue: 127655 +.. nonce: xpPoOf +.. section: Security + +Fixed the :class:`!asyncio.selector_events._SelectorSocketTransport` +transport not pausing writes for the protocol when the buffer reaches the +high water mark when using :meth:`asyncio.WriteTransport.writelines`. + +.. + +.. date: 2024-12-13-22-20-54 +.. gh-issue: 126907 +.. nonce: fWRL_R +.. section: Library + +Fix crash when using :mod:`atexit` concurrently on the :term:`free-threaded +` build. + +.. + +.. date: 2024-12-12-16-59-42 +.. gh-issue: 127870 +.. nonce: _NFG-3 +.. section: Library + +Detect recursive calls in ctypes ``_as_parameter_`` handling. Patch by +Victor Stinner. + +.. + +.. date: 2024-12-08-08-36-18 +.. gh-issue: 127732 +.. nonce: UEKxoa +.. section: Library + +The :mod:`platform` module now correctly detects Windows Server 2025. + +.. + +.. date: 2024-12-07-23-06-44 +.. gh-issue: 126789 +.. nonce: 4dqfV1 +.. section: Library + +Fixed :func:`sysconfig.get_config_vars`, :func:`sysconfig.get_paths`, and +siblings, returning outdated cached data if the value of :data:`sys.prefix` +or :data:`sys.exec_prefix` changes. Overwriting :data:`sys.prefix` or +:data:`sys.exec_prefix` still is discouraged, as that might break other +parts of the code. + +.. + +.. date: 2024-12-07-15-28-31 +.. gh-issue: 127718 +.. nonce: 9dpLfi +.. section: Library + +Add colour to :mod:`test.regrtest` output. Patch by Hugo van Kemenade. + +.. + +.. date: 2024-12-06-17-28-55 +.. gh-issue: 127610 +.. nonce: ctv_NP +.. section: Library + +Added validation for more than one var-positional or var-keyword parameters +in :class:`inspect.Signature`. Patch by Maxim Ageev. + +.. + +.. date: 2024-12-05-10-14-52 +.. gh-issue: 127627 +.. nonce: fgCHOZ +.. section: Library + +Added ``posix._emscripten_debugger()`` to help with debugging the test suite +on the Emscripten target. + +.. + +.. date: 2024-12-04-15-04-12 +.. gh-issue: 126821 +.. nonce: lKCLVV +.. section: Library + +macOS and iOS apps can now choose to redirect stdout and stderr to the +system log during interpreter configuration. + +.. + +.. date: 2024-12-04-11-01-16 +.. gh-issue: 93312 +.. nonce: 9sB-Qw +.. section: Library + +Include ```` to get ``os.PIDFD_NONBLOCK`` constant. Patch by +Victor Stinner. + +.. + +.. date: 2024-12-01-23-18-43 +.. gh-issue: 127481 +.. nonce: K36AoP +.. section: Library + +Add the ``EPOLLWAKEUP`` constant to the :mod:`select` module. + +.. + +.. date: 2024-12-01-22-28-41 +.. gh-issue: 127065 +.. nonce: tFpRer +.. section: Library + +Make :func:`operator.methodcaller` thread-safe and re-entrant safe. + +.. + +.. date: 2024-11-30-21-46-15 +.. gh-issue: 127321 +.. nonce: M78fBv +.. section: Library + +:func:`pdb.set_trace` will not stop at an opcode that does not have an +associated line number anymore. + +.. + +.. date: 2024-11-29-23-02-43 +.. gh-issue: 127429 +.. nonce: dQf2w4 +.. section: Library + +Fixed bug where, on cross-builds, the :mod:`sysconfig` POSIX data was being +generated with the host Python's ``Makefile``. The data is now generated +from current build's ``Makefile``. + +.. + +.. date: 2024-11-29-14-45-26 +.. gh-issue: 127413 +.. nonce: z11AUc +.. section: Library + +Add the :option:`dis --specialized` command-line option to show specialized +bytecode. Patch by Bénédikt Tran. + +.. + +.. date: 2024-11-29-00-15-59 +.. gh-issue: 125413 +.. nonce: WCN0vv +.. section: Library + +Revert addition of :meth:`!pathlib.Path.scandir`. This method was added in +3.14.0a2. The optimizations remain for file system paths, but other +subclasses should only have to implement :meth:`pathlib.Path.iterdir`. + +.. + +.. date: 2024-11-28-14-14-46 +.. gh-issue: 127257 +.. nonce: n6-jU9 +.. section: Library + +In :mod:`ssl`, system call failures that OpenSSL reports using +``ERR_LIB_SYS`` are now raised as :exc:`OSError`. + +.. + +.. date: 2024-11-27-17-04-38 +.. gh-issue: 59705 +.. nonce: sAGyvs +.. section: Library + +On Linux, :class:`threading.Thread` now sets the thread name to the +operating system. Patch by Victor Stinner. + +.. + +.. date: 2024-11-27-16-06-10 +.. gh-issue: 127303 +.. nonce: asqkgh +.. section: Library + +Publicly expose :data:`~token.EXACT_TOKEN_TYPES` in :attr:`!token.__all__`. + +.. + +.. date: 2024-11-27-14-23-02 +.. gh-issue: 127331 +.. nonce: 9sNEC9 +.. section: Library + +:mod:`ssl` can show descriptions for errors added in OpenSSL 3.4. + +.. + +.. date: 2024-11-27-14-06-35 +.. gh-issue: 123967 +.. nonce: wxUmnW +.. section: Library + +Fix faulthandler for trampoline frames. If the top-most frame is a +trampoline frame, skip it. Patch by Victor Stinner. + +.. + +.. date: 2024-11-26-17-42-00 +.. gh-issue: 127178 +.. nonce: U8hxjc +.. section: Library + +A ``_sysconfig_vars_(...).json`` file is now shipped in the standard library +directory. It contains the output of :func:`sysconfig.get_config_vars` on +the default environment encoded as JSON data. This is an implementation +detail, and may change at any time. + +.. + +.. date: 2024-11-25-19-04-10 +.. gh-issue: 127072 +.. nonce: -c284K +.. section: Library + +Remove outdated ``socket.NETLINK_*`` constants not present in Linux kernels +beyond 2.6.17. + +.. + +.. date: 2024-11-25-15-02-44 +.. gh-issue: 127255 +.. nonce: UXeljc +.. section: Library + +The :func:`~ctypes.CopyComPointer` function is now public. Previously, this +was private and only available in ``_ctypes``. + +.. + +.. date: 2024-11-24-14-20-17 +.. gh-issue: 127182 +.. nonce: WmfY2g +.. section: Library + +Fix :meth:`!io.StringIO.__setstate__` crash, when :const:`None` was passed +as the first value. + +.. + +.. date: 2024-11-24-12-41-31 +.. gh-issue: 127217 +.. nonce: UAXGFr +.. section: Library + +Fix :func:`urllib.request.pathname2url` for paths starting with multiple +slashes on Posix. + +.. + +.. date: 2024-11-23-12-25-06 +.. gh-issue: 125866 +.. nonce: wEOP66 +.. section: Library + +:func:`urllib.request.pathname2url` now adds an empty authority when +generating a URL for a path that begins with exactly one slash. For example, +the path ``/etc/hosts`` is converted to the scheme-less URL +``///etc/hosts``. As a result of this change, URLs without authorities are +only generated for relative paths. + +.. + +.. date: 2024-11-23-00-17-29 +.. gh-issue: 127221 +.. nonce: OSXdFE +.. section: Library + +Add colour to :mod:`unittest` output. Patch by Hugo van Kemenade. + +.. + +.. date: 2024-11-22-10-42-34 +.. gh-issue: 127035 +.. nonce: UnbDlr +.. section: Library + +Fix :mod:`shutil.which` on Windows. Now it looks at direct match if and only +if the command ends with a PATHEXT extension or X_OK is not in mode. Support +extensionless files if "." is in PATHEXT. Support PATHEXT extensions that +end with a dot. + +.. + +.. date: 2024-11-22-09-23-41 +.. gh-issue: 122273 +.. nonce: H8M6fd +.. section: Library + +Support PyREPL history on Windows. Patch by devdanzin and Victor Stinner. + +.. + +.. date: 2024-11-22-04-49-31 +.. gh-issue: 125866 +.. nonce: TUtvPK +.. section: Library + +:func:`urllib.request.pathname2url` and :func:`~urllib.request.url2pathname` +no longer convert Windows drive letters to uppercase. + +.. + +.. date: 2024-11-22-03-40-02 +.. gh-issue: 127078 +.. nonce: gI_PaP +.. section: Library + +Fix issue where :func:`urllib.request.url2pathname` failed to discard an +extra slash before a UNC drive in the URL path on Windows. + +.. + +.. date: 2024-11-22-02-31-55 +.. gh-issue: 126766 +.. nonce: jfkhBH +.. section: Library + +Fix issue where :func:`urllib.request.url2pathname` failed to discard any +'localhost' authority present in the URL. + +.. + +.. date: 2024-11-21-16-23-16 +.. gh-issue: 127065 +.. nonce: cfL1zd +.. section: Library + +Fix crash when calling a :func:`operator.methodcaller` instance from +multiple threads in the free threading build. + +.. + +.. date: 2024-11-21-06-03-46 +.. gh-issue: 127090 +.. nonce: yUYwdh +.. section: Library + +Fix value of :attr:`urllib.response.addinfourl.url` for ``file:`` URLs that +express relative paths and absolute Windows paths. The canonical URL +generated by :func:`urllib.request.pathname2url` is now used. + +.. + +.. date: 2024-11-20-21-20-56 +.. gh-issue: 126992 +.. nonce: RbU0FZ +.. section: Library + +Fix LONG and INT opcodes to only use base 10 for string to integer +conversion in :mod:`pickle`. + +.. + +.. date: 2024-11-20-16-58-59 +.. gh-issue: 126997 +.. nonce: 0PI41Y +.. section: Library + +Fix support of STRING and GLOBAL opcodes with non-ASCII arguments in +:mod:`pickletools`. :func:`pickletools.dis` now outputs non-ASCII bytes in +STRING, BINSTRING and SHORT_BINSTRING arguments as escaped (``\xXX``). + +.. + +.. date: 2024-11-20-11-37-08 +.. gh-issue: 126316 +.. nonce: ElkZmE +.. section: Library + +:mod:`grp`: Make :func:`grp.getgrall` thread-safe by adding a mutex. Patch +by Victor Stinner. + +.. + +.. date: 2024-11-20-08-54-11 +.. gh-issue: 126618 +.. nonce: ef_53g +.. section: Library + +Fix the representation of :class:`itertools.count` objects when the count +value is :data:`sys.maxsize`. + +.. + +.. date: 2024-11-19-14-34-05 +.. gh-issue: 126615 +.. nonce: LOskwi +.. section: Library + +The :exc:`~ctypes.COMError` exception is now public. Previously, this was +private and only available in ``_ctypes``. + +.. + +.. date: 2024-11-18-23-42-06 +.. gh-issue: 126985 +.. nonce: 7XplY9 +.. section: Library + +When running under a virtual environment with the :mod:`site` disabled (see +:option:`-S`), :data:`sys.prefix` and :data:`sys.base_prefix` will now point +to the virtual environment, instead of the base installation. + +.. + +.. date: 2024-11-18-23-18-27 +.. gh-issue: 112192 +.. nonce: DRdRgP +.. section: Library + +In the :mod:`trace` module, increase the coverage precision (``cov%``) to +one decimal. + +.. + +.. date: 2024-11-18-22-02-47 +.. gh-issue: 118761 +.. nonce: GQKD_J +.. section: Library + +Improve import time of :mod:`mimetypes` by around 11-16 times. Patch by Hugo +van Kemenade. + +.. + +.. date: 2024-11-18-19-03-46 +.. gh-issue: 126947 +.. nonce: NiDYUe +.. section: Library + +Raise :exc:`TypeError` in :meth:`!_pydatetime.timedelta.__new__` if the +passed arguments are not :class:`int` or :class:`float`, so that the Python +implementation is in line with the C implementation. + +.. + +.. date: 2024-11-18-16-43-11 +.. gh-issue: 126946 +.. nonce: 52Ou-B +.. section: Library + +Improve the :exc:`~getopt.GetoptError` error message when a long option +prefix matches multiple accepted options in :func:`getopt.getopt` and +:func:`getopt.gnu_getopt`. + +.. + +.. date: 2024-11-16-10-52-48 +.. gh-issue: 126899 +.. nonce: GFnfBt +.. section: Library + +Make tkinter widget methods :meth:`!after` and :meth:`!after_idle` accept +arguments passed by keyword. + +.. + +.. date: 2024-11-15-01-50-36 +.. gh-issue: 85168 +.. nonce: bP8VIN +.. section: Library + +Fix issue where :func:`urllib.request.url2pathname` and +:func:`~urllib.request.pathname2url` always used UTF-8 when quoting and +unquoting file URIs. They now use the :term:`filesystem encoding and error +handler`. + +.. + +.. date: 2024-11-13-19-15-18 +.. gh-issue: 126780 +.. nonce: ZZqJvI +.. section: Library + +Fix :func:`os.path.normpath` for drive-relative paths on Windows. + +.. + +.. date: 2024-11-13-10-44-25 +.. gh-issue: 126775 +.. nonce: a3ubjh +.. section: Library + +Make :func:`linecache.checkcache` thread safe and GC re-entrancy safe. + +.. + +.. date: 2024-11-12-20-05-09 +.. gh-issue: 126601 +.. nonce: Nj7bA9 +.. section: Library + +Fix issue where :func:`urllib.request.pathname2url` raised :exc:`OSError` +when given a Windows path containing a colon character not following a drive +letter, such as before an NTFS alternate data stream. + +.. + +.. date: 2024-11-12-13-14-47 +.. gh-issue: 126727 +.. nonce: 5Eqfqd +.. section: Library + +``locale.nl_langinfo(locale.ERA)`` now returns multiple era description +segments separated by semicolons. Previously it only returned the first +segment on platforms with Glibc. + +.. + +.. date: 2024-11-04-22-02-30 +.. gh-issue: 85046 +.. nonce: Y5d_ZN +.. section: Library + +Add :data:`~errno.EHWPOISON` error code to :mod:`errno`. + +.. + +.. date: 2024-10-28-19-49-18 +.. gh-issue: 118201 +.. nonce: v41XXh +.. section: Library + +Fixed intermittent failures of :any:`os.confstr`, :any:`os.pathconf` and +:any:`os.sysconf` on iOS and Android. + +.. + +.. date: 2024-10-23-20-05-54 +.. gh-issue: 86463 +.. nonce: jvFTI_ +.. section: Library + +The ``usage`` parameter of :class:`argparse.ArgumentParser` no longer +affects the default value of the ``prog`` parameter in subparsers. + +.. + +.. date: 2024-09-13-18-24-27 +.. gh-issue: 124008 +.. nonce: XaiPQx +.. section: Library + +Fix possible crash (in debug build), incorrect output or returning incorrect +value from raw binary ``write()`` when writing to console on Windows. + +.. + +.. date: 2024-08-27-18-58-01 +.. gh-issue: 123401 +.. nonce: t4-FpI +.. section: Library + +The :mod:`http.cookies` module now supports parsing obsolete :rfc:`850` date +formats, in accordance with :rfc:`9110` requirements. Patch by Nano Zheng. + +.. + +.. date: 2024-07-30-11-37-40 +.. gh-issue: 122431 +.. nonce: lAzVtu +.. section: Library + +:func:`readline.append_history_file` now raises a :exc:`ValueError` when +given a negative value. + +.. + +.. date: 2024-07-29-15-20-30 +.. gh-issue: 122356 +.. nonce: wKCmFx +.. section: Library + +Guarantee that the position of a file-like object passed to +:func:`zipfile.is_zipfile` is left untouched after the call. Patch by +Bénédikt Tran. + +.. + +.. date: 2024-07-25-18-06-51 +.. gh-issue: 122288 +.. nonce: -_xxOR +.. section: Library + +Improve the performances of :func:`fnmatch.translate` by a factor 1.7. Patch +by Bénédikt Tran. + +.. + +.. date: 2023-02-15-23-54-42 +.. gh-issue: 88110 +.. nonce: KU6erv +.. section: Library + +Fixed :class:`multiprocessing.Process` reporting a ``.exitcode`` of 1 even +on success when using the ``"fork"`` start method while using a +:class:`concurrent.futures.ThreadPoolExecutor`. + +.. + +.. date: 2022-11-10-17-16-45 +.. gh-issue: 97514 +.. nonce: kzA0zl +.. section: Library + +Authentication was added to the :mod:`multiprocessing` forkserver start +method control socket so that only processes with the authentication key +generated by the process that spawned the forkserver can control it. This +is an enhancement over the other :gh:`97514` fixes so that access is no +longer limited only by filesystem permissions. + +The file descriptor exchange of control pipes with the forked worker process +now requires an explicit acknowledgement byte to be sent over the socket +after the exchange on all forkserver supporting platforms. That makes +testing the above much easier. + +.. + +.. date: 2024-11-27-22-56-48 +.. gh-issue: 127347 +.. nonce: xyddWS +.. section: Documentation + +Publicly expose :func:`traceback.print_list` in :attr:`!traceback.__all__`. + +.. + +.. date: 2024-12-10-21-08-05 +.. gh-issue: 127740 +.. nonce: 0tWC9h +.. section: Core and Builtins + +Fix error message in :func:`bytes.fromhex` when given an odd number of +digits to properly indicate that an even number of hexadecimal digits is +required. + +.. + +.. date: 2024-12-09-11-29-10 +.. gh-issue: 127058 +.. nonce: pqtBcZ +.. section: Core and Builtins + +``PySequence_Tuple`` now creates the resulting tuple atomically, preventing +partially created tuples being visible to the garbage collector or through +``gc.get_referrers()`` + +.. + +.. date: 2024-12-07-13-06-09 +.. gh-issue: 127599 +.. nonce: tXCZb_ +.. section: Core and Builtins + +Fix statistics for increments of object reference counts (in particular, +when a reference count was increased by more than 1 in a single operation). + +.. + +.. date: 2024-12-06-01-09-40 +.. gh-issue: 127651 +.. nonce: 80cm6j +.. section: Core and Builtins + +When raising :exc:`ImportError` for missing symbols in ``from`` imports, use +``__file__`` in the error message if ``__spec__.origin`` is not a location + +.. + +.. date: 2024-12-05-19-25-00 +.. gh-issue: 127582 +.. nonce: ogUY2a +.. section: Core and Builtins + +Fix non-thread-safe object resurrection when calling finalizers and watcher +callbacks in the free threading build. + +.. + +.. date: 2024-12-04-09-52-08 +.. gh-issue: 127434 +.. nonce: RjkGT_ +.. section: Core and Builtins + +The iOS compiler shims can now accept arguments with spaces. + +.. + +.. date: 2024-12-03-21-07-06 +.. gh-issue: 127536 +.. nonce: 3jMMrT +.. section: Core and Builtins + +Add missing locks around some list assignment operations in the free +threading build. + +.. + +.. date: 2024-11-30-23-35-45 +.. gh-issue: 127085 +.. nonce: KLKylb +.. section: Core and Builtins + +Fix race when exporting a buffer from a :class:`memoryview` object on the +:term:`free-threaded ` build. + +.. + +.. date: 2024-11-25-05-15-21 +.. gh-issue: 127238 +.. nonce: O8wkH- +.. section: Core and Builtins + +Correct error message for :func:`sys.set_int_max_str_digits`. + +.. + +.. date: 2024-11-24-07-01-28 +.. gh-issue: 113841 +.. nonce: WFg-Bu +.. section: Core and Builtins + +Fix possible undefined behavior division by zero in :class:`complex`'s +:c:func:`_Py_c_pow`. + +.. + +.. date: 2024-11-23-04-54-42 +.. gh-issue: 127133 +.. nonce: WMoJjF +.. section: Core and Builtins + +Calling :meth:`argparse.ArgumentParser.add_argument_group` on an argument +group, and calling :meth:`argparse.ArgumentParser.add_argument_group` or +:meth:`argparse.ArgumentParser.add_mutually_exclusive_group` on a mutually +exclusive group now raise exceptions. This nesting was never supported, +often failed to work correctly, and was unintentionally exposed through +inheritance. This functionality has been deprecated since Python 3.11. + +.. + +.. date: 2024-11-21-16-13-52 +.. gh-issue: 126491 +.. nonce: 0YvL94 +.. section: Core and Builtins + +Add a marking phase to the GC. All objects that can be transitively reached +from builtin modules or the stacks are marked as reachable before cycle +detection. This reduces the amount of work done by the GC by approximately +half. + +.. + +.. date: 2024-11-19-21-49-58 +.. gh-issue: 127020 +.. nonce: 5vvI17 +.. section: Core and Builtins + +Fix a crash in the free threading build when :c:func:`PyCode_GetCode`, +:c:func:`PyCode_GetVarnames`, :c:func:`PyCode_GetCellvars`, or +:c:func:`PyCode_GetFreevars` were called from multiple threads at the same +time. + +.. + +.. date: 2024-11-19-17-17-32 +.. gh-issue: 127010 +.. nonce: 9Cl4bb +.. section: Core and Builtins + +Simplify GC tracking of dictionaries. All dictionaries are tracked when +created, rather than being lazily tracked when a trackable object was added +to them. This simplifies the code considerably and results in a slight +speedup. + +.. + +.. date: 2024-11-18-23-18-17 +.. gh-issue: 126980 +.. nonce: r8QHdi +.. section: Core and Builtins + +Fix :meth:`~object.__buffer__` of :class:`bytearray` crashing when +:attr:`~inspect.BufferFlags.READ` or :attr:`~inspect.BufferFlags.WRITE` are +passed as flags. + +.. + +.. date: 2024-11-17-21-35-55 +.. gh-issue: 126937 +.. nonce: qluVM0 +.. section: Core and Builtins + +Fix :exc:`TypeError` when a :class:`ctypes.Structure` has a field size that +doesn't fit into an unsigned 16-bit integer. Instead, the maximum number of +*bits* is :data:`sys.maxsize`. + +.. + +.. date: 2024-11-16-22-37-46 +.. gh-issue: 126868 +.. nonce: yOoHSY +.. section: Core and Builtins + +Increase performance of :class:`int` by adding a freelist for compact ints. + +.. + +.. date: 2024-11-16-11-11-35 +.. gh-issue: 126881 +.. nonce: ijofLZ +.. section: Core and Builtins + +Fix crash in finalization of dtoa state. Patch by Kumar Aditya. + +.. + +.. date: 2024-11-15-16-39-37 +.. gh-issue: 126892 +.. nonce: QR6Yo3 +.. section: Core and Builtins + +Require cold or invalidated code to "warm up" before being JIT compiled +again. + +.. + +.. date: 2024-11-07-21-48-23 +.. gh-issue: 126091 +.. nonce: ETaRGE +.. section: Core and Builtins + +Ensure stack traces are complete when throwing into a generator chain that +ends in a custom generator. + +.. + +.. date: 2024-10-27-04-47-28 +.. gh-issue: 126024 +.. nonce: XCQSqT +.. section: Core and Builtins + +Optimize decoding of short UTF-8 sequences containing non-ASCII characters +by approximately 15%. + +.. + +.. date: 2024-10-14-13-28-16 +.. gh-issue: 125420 +.. nonce: hNKixM +.. section: Core and Builtins + +Add :meth:`memoryview.index` to :class:`memoryview` objects. Patch by +Bénédikt Tran. + +.. + +.. date: 2024-10-14-12-34-51 +.. gh-issue: 125420 +.. nonce: jABXoZ +.. section: Core and Builtins + +Add :meth:`memoryview.count` to :class:`memoryview` objects. Patch by +Bénédikt Tran. + +.. + +.. date: 2024-09-25-21-50-23 +.. gh-issue: 124470 +.. nonce: pFr3_d +.. section: Core and Builtins + +Fix crash in free-threaded builds when replacing object dictionary while +reading attribute on another thread + +.. + +.. date: 2024-08-03-14-02-27 +.. gh-issue: 69639 +.. nonce: mW3iKq +.. section: Core and Builtins + +Implement mixed-mode arithmetic rules combining real and complex numbers as +specified by C standards since C99. Patch by Sergey B Kirpichev. + +.. + +.. date: 2024-06-04-08-26-25 +.. gh-issue: 120010 +.. nonce: _z-AWz +.. section: Core and Builtins + +Correct invalid corner cases which resulted in ``(nan+nanj)`` output in +complex multiplication, e.g., ``(1e300+1j)*(nan+infj)``. Patch by Sergey B +Kirpichev. + +.. + +.. date: 2023-09-22-21-01-56 +.. gh-issue: 109746 +.. nonce: 32MHt9 +.. section: Core and Builtins + +If :func:`!_thread.start_new_thread` fails to start a new thread, it deletes +its state from interpreter and thus avoids its repeated cleanup on +finalization. + +.. + +.. date: 2024-12-16-07-12-15 +.. gh-issue: 127896 +.. nonce: HmI9pk +.. section: C API + +The previously undocumented function :c:func:`PySequence_In` is :term:`soft +deprecated`. Use :c:func:`PySequence_Contains` instead. + +.. + +.. date: 2024-12-10-14-25-22 +.. gh-issue: 127791 +.. nonce: YRw4GU +.. section: C API + +Fix loss of callbacks after more than one call to +:c:func:`PyUnstable_AtExit`. + +.. + +.. date: 2024-12-06-16-53-34 +.. gh-issue: 127691 +.. nonce: k_Jitp +.. section: C API + +The :ref:`Unicode Exception Objects ` C API now raises a +:exc:`TypeError` if its exception argument is not a :exc:`UnicodeError` +object. Patch by Bénédikt Tran. + +.. + +.. date: 2024-12-02-16-10-36 +.. gh-issue: 123378 +.. nonce: Q6YRwe +.. section: C API + +Ensure that the value of :attr:`UnicodeEncodeError.end ` +retrieved by :c:func:`PyUnicodeEncodeError_GetEnd` lies in ``[min(1, +objlen), max(min(1, objlen), objlen)]`` where *objlen* is the length of +:attr:`UnicodeEncodeError.object `. Similar arguments +apply to :exc:`UnicodeDecodeError` and :exc:`UnicodeTranslateError` and +their corresponding C interface. Patch by Bénédikt Tran. + +.. + +.. date: 2024-11-26-22-06-10 +.. gh-issue: 127314 +.. nonce: SsRrIu +.. section: C API + +Improve error message when calling the C API without an active thread state +on the :term:`free-threaded ` build. + +.. + +.. date: 2024-08-27-09-07-56 +.. gh-issue: 123378 +.. nonce: JJ6n_u +.. section: C API + +Ensure that the value of :attr:`UnicodeEncodeError.start +` retrieved by :c:func:`PyUnicodeEncodeError_GetStart` +lies in ``[0, max(0, objlen - 1)]`` where *objlen* is the length of +:attr:`UnicodeEncodeError.object `. Similar arguments +apply to :exc:`UnicodeDecodeError` and :exc:`UnicodeTranslateError` and +their corresponding C interface. Patch by Bénédikt Tran. + +.. + +.. date: 2024-08-12-10-15-19 +.. gh-issue: 109523 +.. nonce: S2c3fi +.. section: C API + +Reading text from a non-blocking stream with ``read`` may now raise a +:exc:`BlockingIOError` if the operation cannot immediately return bytes. + +.. + +.. date: 2024-07-03-17-26-53 +.. gh-issue: 102471 +.. nonce: XpmKYk +.. section: C API + +Add a new import and export API for Python :class:`int` objects +(:pep:`757`): + +* :c:func:`PyLong_GetNativeLayout`; +* :c:func:`PyLong_Export`; +* :c:func:`PyLong_FreeExport`; +* :c:func:`PyLongWriter_Create`; +* :c:func:`PyLongWriter_Finish`; +* :c:func:`PyLongWriter_Discard`. + +Patch by Victor Stinner. + +.. + +.. date: 2024-07-03-13-39-13 +.. gh-issue: 121058 +.. nonce: MKi1MV +.. section: C API + +``PyThreadState_Clear()`` now warns (and calls ``sys.excepthook``) if the +thread state still has an active exception. + +.. + +.. date: 2024-12-12-17-21-45 +.. gh-issue: 127865 +.. nonce: 30GDzs +.. section: Build + +Fix build failure on systems without thread-locals support. + +.. + +.. date: 2024-12-06-12-47-52 +.. gh-issue: 127629 +.. nonce: tD-ERQ +.. section: Build + +Emscripten builds now include ctypes support. + +.. + +.. date: 2024-11-30-16-36-09 +.. gh-issue: 127111 +.. nonce: QI9mMZ +.. section: Build + +Updated the Emscripten web example to use ES6 modules and be built into a +distinct ``web_example`` subfolder. + +.. + +.. date: 2024-11-22-08-46-46 +.. gh-issue: 115869 +.. nonce: UVLSKd +.. section: Build + +Make ``jit_stencils.h`` (which is produced during JIT builds) reproducible. + +.. + +.. date: 2024-11-20-17-12-40 +.. gh-issue: 126898 +.. nonce: I2zILt +.. section: Build + +The Emscripten build of Python is now based on ES6 modules. diff --git a/Misc/NEWS.d/next/Build/2024-11-20-17-12-40.gh-issue-126898.I2zILt.rst b/Misc/NEWS.d/next/Build/2024-11-20-17-12-40.gh-issue-126898.I2zILt.rst deleted file mode 100644 index 37783c4e890015..00000000000000 --- a/Misc/NEWS.d/next/Build/2024-11-20-17-12-40.gh-issue-126898.I2zILt.rst +++ /dev/null @@ -1 +0,0 @@ -The Emscripten build of Python is now based on ES6 modules. diff --git a/Misc/NEWS.d/next/Build/2024-11-22-08-46-46.gh-issue-115869.UVLSKd.rst b/Misc/NEWS.d/next/Build/2024-11-22-08-46-46.gh-issue-115869.UVLSKd.rst deleted file mode 100644 index 9e8a078983f20b..00000000000000 --- a/Misc/NEWS.d/next/Build/2024-11-22-08-46-46.gh-issue-115869.UVLSKd.rst +++ /dev/null @@ -1 +0,0 @@ -Make ``jit_stencils.h`` (which is produced during JIT builds) reproducible. diff --git a/Misc/NEWS.d/next/Build/2024-11-30-16-36-09.gh-issue-127111.QI9mMZ.rst b/Misc/NEWS.d/next/Build/2024-11-30-16-36-09.gh-issue-127111.QI9mMZ.rst deleted file mode 100644 index d90067cd3bfaa3..00000000000000 --- a/Misc/NEWS.d/next/Build/2024-11-30-16-36-09.gh-issue-127111.QI9mMZ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Updated the Emscripten web example to use ES6 modules and be built into a -distinct ``web_example`` subfolder. diff --git a/Misc/NEWS.d/next/Build/2024-12-06-12-47-52.gh-issue-127629.tD-ERQ.rst b/Misc/NEWS.d/next/Build/2024-12-06-12-47-52.gh-issue-127629.tD-ERQ.rst deleted file mode 100644 index 52ee84f5d6f6c3..00000000000000 --- a/Misc/NEWS.d/next/Build/2024-12-06-12-47-52.gh-issue-127629.tD-ERQ.rst +++ /dev/null @@ -1 +0,0 @@ -Emscripten builds now include ctypes support. diff --git a/Misc/NEWS.d/next/Build/2024-12-12-17-21-45.gh-issue-127865.30GDzs.rst b/Misc/NEWS.d/next/Build/2024-12-12-17-21-45.gh-issue-127865.30GDzs.rst deleted file mode 100644 index 3fc1d8a1b51d30..00000000000000 --- a/Misc/NEWS.d/next/Build/2024-12-12-17-21-45.gh-issue-127865.30GDzs.rst +++ /dev/null @@ -1 +0,0 @@ -Fix build failure on systems without thread-locals support. diff --git a/Misc/NEWS.d/next/C_API/2024-07-03-13-39-13.gh-issue-121058.MKi1MV.rst b/Misc/NEWS.d/next/C_API/2024-07-03-13-39-13.gh-issue-121058.MKi1MV.rst deleted file mode 100644 index 133d8cb6fe4b9e..00000000000000 --- a/Misc/NEWS.d/next/C_API/2024-07-03-13-39-13.gh-issue-121058.MKi1MV.rst +++ /dev/null @@ -1,2 +0,0 @@ -``PyThreadState_Clear()`` now warns (and calls ``sys.excepthook``) if the -thread state still has an active exception. diff --git a/Misc/NEWS.d/next/C_API/2024-07-03-17-26-53.gh-issue-102471.XpmKYk.rst b/Misc/NEWS.d/next/C_API/2024-07-03-17-26-53.gh-issue-102471.XpmKYk.rst deleted file mode 100644 index c18c159ac87d08..00000000000000 --- a/Misc/NEWS.d/next/C_API/2024-07-03-17-26-53.gh-issue-102471.XpmKYk.rst +++ /dev/null @@ -1,10 +0,0 @@ -Add a new import and export API for Python :class:`int` objects (:pep:`757`): - -* :c:func:`PyLong_GetNativeLayout`; -* :c:func:`PyLong_Export`; -* :c:func:`PyLong_FreeExport`; -* :c:func:`PyLongWriter_Create`; -* :c:func:`PyLongWriter_Finish`; -* :c:func:`PyLongWriter_Discard`. - -Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst b/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst deleted file mode 100644 index 9d6b2e0c565623..00000000000000 --- a/Misc/NEWS.d/next/C_API/2024-08-12-10-15-19.gh-issue-109523.S2c3fi.rst +++ /dev/null @@ -1 +0,0 @@ -Reading text from a non-blocking stream with ``read`` may now raise a :exc:`BlockingIOError` if the operation cannot immediately return bytes. diff --git a/Misc/NEWS.d/next/C_API/2024-08-27-09-07-56.gh-issue-123378.JJ6n_u.rst b/Misc/NEWS.d/next/C_API/2024-08-27-09-07-56.gh-issue-123378.JJ6n_u.rst deleted file mode 100644 index 7254a04f61843d..00000000000000 --- a/Misc/NEWS.d/next/C_API/2024-08-27-09-07-56.gh-issue-123378.JJ6n_u.rst +++ /dev/null @@ -1,6 +0,0 @@ -Ensure that the value of :attr:`UnicodeEncodeError.start ` -retrieved by :c:func:`PyUnicodeEncodeError_GetStart` lies in -``[0, max(0, objlen - 1)]`` where *objlen* is the length of -:attr:`UnicodeEncodeError.object `. Similar -arguments apply to :exc:`UnicodeDecodeError` and :exc:`UnicodeTranslateError` -and their corresponding C interface. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/C_API/2024-11-26-22-06-10.gh-issue-127314.SsRrIu.rst b/Misc/NEWS.d/next/C_API/2024-11-26-22-06-10.gh-issue-127314.SsRrIu.rst deleted file mode 100644 index 8ea3c4ee2a2c53..00000000000000 --- a/Misc/NEWS.d/next/C_API/2024-11-26-22-06-10.gh-issue-127314.SsRrIu.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve error message when calling the C API without an active thread state -on the :term:`free-threaded ` build. diff --git a/Misc/NEWS.d/next/C_API/2024-12-02-16-10-36.gh-issue-123378.Q6YRwe.rst b/Misc/NEWS.d/next/C_API/2024-12-02-16-10-36.gh-issue-123378.Q6YRwe.rst deleted file mode 100644 index 107751579c4d91..00000000000000 --- a/Misc/NEWS.d/next/C_API/2024-12-02-16-10-36.gh-issue-123378.Q6YRwe.rst +++ /dev/null @@ -1,6 +0,0 @@ -Ensure that the value of :attr:`UnicodeEncodeError.end ` -retrieved by :c:func:`PyUnicodeEncodeError_GetEnd` lies in ``[min(1, objlen), -max(min(1, objlen), objlen)]`` where *objlen* is the length of -:attr:`UnicodeEncodeError.object `. Similar arguments -apply to :exc:`UnicodeDecodeError` and :exc:`UnicodeTranslateError` and their -corresponding C interface. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/C_API/2024-12-06-16-53-34.gh-issue-127691.k_Jitp.rst b/Misc/NEWS.d/next/C_API/2024-12-06-16-53-34.gh-issue-127691.k_Jitp.rst deleted file mode 100644 index c942ff3d9eda53..00000000000000 --- a/Misc/NEWS.d/next/C_API/2024-12-06-16-53-34.gh-issue-127691.k_Jitp.rst +++ /dev/null @@ -1,3 +0,0 @@ -The :ref:`Unicode Exception Objects ` C API -now raises a :exc:`TypeError` if its exception argument is not -a :exc:`UnicodeError` object. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/C_API/2024-12-10-14-25-22.gh-issue-127791.YRw4GU.rst b/Misc/NEWS.d/next/C_API/2024-12-10-14-25-22.gh-issue-127791.YRw4GU.rst deleted file mode 100644 index 70751f18f5cf17..00000000000000 --- a/Misc/NEWS.d/next/C_API/2024-12-10-14-25-22.gh-issue-127791.YRw4GU.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix loss of callbacks after more than one call to -:c:func:`PyUnstable_AtExit`. diff --git a/Misc/NEWS.d/next/C_API/2024-12-16-07-12-15.gh-issue-127896.HmI9pk.rst b/Misc/NEWS.d/next/C_API/2024-12-16-07-12-15.gh-issue-127896.HmI9pk.rst deleted file mode 100644 index 82b4f563591fe1..00000000000000 --- a/Misc/NEWS.d/next/C_API/2024-12-16-07-12-15.gh-issue-127896.HmI9pk.rst +++ /dev/null @@ -1,2 +0,0 @@ -The previously undocumented function :c:func:`PySequence_In` is :term:`soft deprecated`. -Use :c:func:`PySequence_Contains` instead. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2023-09-22-21-01-56.gh-issue-109746.32MHt9.rst b/Misc/NEWS.d/next/Core_and_Builtins/2023-09-22-21-01-56.gh-issue-109746.32MHt9.rst deleted file mode 100644 index 2d350c33aa6975..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2023-09-22-21-01-56.gh-issue-109746.32MHt9.rst +++ /dev/null @@ -1 +0,0 @@ -If :func:`!_thread.start_new_thread` fails to start a new thread, it deletes its state from interpreter and thus avoids its repeated cleanup on finalization. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-06-04-08-26-25.gh-issue-120010._z-AWz.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-06-04-08-26-25.gh-issue-120010._z-AWz.rst deleted file mode 100644 index 7954c7f5927397..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-06-04-08-26-25.gh-issue-120010._z-AWz.rst +++ /dev/null @@ -1,2 +0,0 @@ -Correct invalid corner cases which resulted in ``(nan+nanj)`` output in complex -multiplication, e.g., ``(1e300+1j)*(nan+infj)``. Patch by Sergey B Kirpichev. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-08-03-14-02-27.gh-issue-69639.mW3iKq.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-08-03-14-02-27.gh-issue-69639.mW3iKq.rst deleted file mode 100644 index 72596b0302aa45..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-08-03-14-02-27.gh-issue-69639.mW3iKq.rst +++ /dev/null @@ -1,2 +0,0 @@ -Implement mixed-mode arithmetic rules combining real and complex numbers -as specified by C standards since C99. Patch by Sergey B Kirpichev. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-09-25-21-50-23.gh-issue-124470.pFr3_d.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-09-25-21-50-23.gh-issue-124470.pFr3_d.rst deleted file mode 100644 index 8f2f37146d3c13..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-09-25-21-50-23.gh-issue-124470.pFr3_d.rst +++ /dev/null @@ -1 +0,0 @@ -Fix crash in free-threaded builds when replacing object dictionary while reading attribute on another thread diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-14-12-34-51.gh-issue-125420.jABXoZ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-14-12-34-51.gh-issue-125420.jABXoZ.rst deleted file mode 100644 index ef120802b5dc59..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-14-12-34-51.gh-issue-125420.jABXoZ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :meth:`memoryview.count` to :class:`memoryview` objects. Patch by -Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-14-13-28-16.gh-issue-125420.hNKixM.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-14-13-28-16.gh-issue-125420.hNKixM.rst deleted file mode 100644 index 6ed823175f6754..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-14-13-28-16.gh-issue-125420.hNKixM.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :meth:`memoryview.index` to :class:`memoryview` objects. Patch by -Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-27-04-47-28.gh-issue-126024.XCQSqT.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-27-04-47-28.gh-issue-126024.XCQSqT.rst deleted file mode 100644 index b41fff30433c34..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-27-04-47-28.gh-issue-126024.XCQSqT.rst +++ /dev/null @@ -1,2 +0,0 @@ -Optimize decoding of short UTF-8 sequences containing non-ASCII characters -by approximately 15%. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-07-21-48-23.gh-issue-126091.ETaRGE.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-07-21-48-23.gh-issue-126091.ETaRGE.rst deleted file mode 100644 index 08118ff1af657d..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-07-21-48-23.gh-issue-126091.ETaRGE.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensure stack traces are complete when throwing into a generator chain that -ends in a custom generator. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-15-16-39-37.gh-issue-126892.QR6Yo3.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-15-16-39-37.gh-issue-126892.QR6Yo3.rst deleted file mode 100644 index db3c398e5dbdbe..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-15-16-39-37.gh-issue-126892.QR6Yo3.rst +++ /dev/null @@ -1,2 +0,0 @@ -Require cold or invalidated code to "warm up" before being JIT compiled -again. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-16-11-11-35.gh-issue-126881.ijofLZ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-16-11-11-35.gh-issue-126881.ijofLZ.rst deleted file mode 100644 index 13381c7630d7ce..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-16-11-11-35.gh-issue-126881.ijofLZ.rst +++ /dev/null @@ -1 +0,0 @@ -Fix crash in finalization of dtoa state. Patch by Kumar Aditya. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-16-22-37-46.gh-issue-126868.yOoHSY.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-16-22-37-46.gh-issue-126868.yOoHSY.rst deleted file mode 100644 index fd1570908c1fd6..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-16-22-37-46.gh-issue-126868.yOoHSY.rst +++ /dev/null @@ -1 +0,0 @@ -Increase performance of :class:`int` by adding a freelist for compact ints. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-17-21-35-55.gh-issue-126937.qluVM0.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-17-21-35-55.gh-issue-126937.qluVM0.rst deleted file mode 100644 index 8d7da0d4107021..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-17-21-35-55.gh-issue-126937.qluVM0.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix :exc:`TypeError` when a :class:`ctypes.Structure` has a field size -that doesn't fit into an unsigned 16-bit integer. -Instead, the maximum number of *bits* is :data:`sys.maxsize`. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-18-23-18-17.gh-issue-126980.r8QHdi.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-18-23-18-17.gh-issue-126980.r8QHdi.rst deleted file mode 100644 index 84484e7c3001da..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-18-23-18-17.gh-issue-126980.r8QHdi.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix :meth:`~object.__buffer__` of :class:`bytearray` crashing when -:attr:`~inspect.BufferFlags.READ` or :attr:`~inspect.BufferFlags.WRITE` are -passed as flags. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-19-17-17-32.gh-issue-127010.9Cl4bb.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-19-17-17-32.gh-issue-127010.9Cl4bb.rst deleted file mode 100644 index 36e379c88ab27e..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-19-17-17-32.gh-issue-127010.9Cl4bb.rst +++ /dev/null @@ -1,4 +0,0 @@ -Simplify GC tracking of dictionaries. All dictionaries are tracked when -created, rather than being lazily tracked when a trackable object was added -to them. This simplifies the code considerably and results in a slight -speedup. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-19-21-49-58.gh-issue-127020.5vvI17.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-19-21-49-58.gh-issue-127020.5vvI17.rst deleted file mode 100644 index a8fd9272f5a923..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-19-21-49-58.gh-issue-127020.5vvI17.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix a crash in the free threading build when :c:func:`PyCode_GetCode`, -:c:func:`PyCode_GetVarnames`, :c:func:`PyCode_GetCellvars`, or -:c:func:`PyCode_GetFreevars` were called from multiple threads at the same -time. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-21-16-13-52.gh-issue-126491.0YvL94.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-21-16-13-52.gh-issue-126491.0YvL94.rst deleted file mode 100644 index 9ef2b8dc33ed0f..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-21-16-13-52.gh-issue-126491.0YvL94.rst +++ /dev/null @@ -1,4 +0,0 @@ -Add a marking phase to the GC. All objects that can be transitively reached -from builtin modules or the stacks are marked as reachable before cycle -detection. This reduces the amount of work done by the GC by approximately -half. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-23-04-54-42.gh-issue-127133.WMoJjF.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-23-04-54-42.gh-issue-127133.WMoJjF.rst deleted file mode 100644 index 56b496bdf1e310..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-23-04-54-42.gh-issue-127133.WMoJjF.rst +++ /dev/null @@ -1,6 +0,0 @@ -Calling :meth:`argparse.ArgumentParser.add_argument_group` on an argument group, -and calling :meth:`argparse.ArgumentParser.add_argument_group` or -:meth:`argparse.ArgumentParser.add_mutually_exclusive_group` on a mutually -exclusive group now raise exceptions. This nesting was never supported, often -failed to work correctly, and was unintentionally exposed through inheritance. -This functionality has been deprecated since Python 3.11. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-24-07-01-28.gh-issue-113841.WFg-Bu.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-24-07-01-28.gh-issue-113841.WFg-Bu.rst deleted file mode 100644 index 2b07fdfcc6b527..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-24-07-01-28.gh-issue-113841.WFg-Bu.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix possible undefined behavior division by zero in :class:`complex`'s -:c:func:`_Py_c_pow`. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-25-05-15-21.gh-issue-127238.O8wkH-.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-25-05-15-21.gh-issue-127238.O8wkH-.rst deleted file mode 100644 index e8a274fcd31f26..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-25-05-15-21.gh-issue-127238.O8wkH-.rst +++ /dev/null @@ -1 +0,0 @@ -Correct error message for :func:`sys.set_int_max_str_digits`. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-23-35-45.gh-issue-127085.KLKylb.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-23-35-45.gh-issue-127085.KLKylb.rst deleted file mode 100644 index a59b9502eaa33e..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-23-35-45.gh-issue-127085.KLKylb.rst +++ /dev/null @@ -1 +0,0 @@ -Fix race when exporting a buffer from a :class:`memoryview` object on the :term:`free-threaded ` build. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-03-21-07-06.gh-issue-127536.3jMMrT.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-03-21-07-06.gh-issue-127536.3jMMrT.rst deleted file mode 100644 index 6e2b87fe38146b..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-03-21-07-06.gh-issue-127536.3jMMrT.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add missing locks around some list assignment operations in the free -threading build. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-04-09-52-08.gh-issue-127434.RjkGT_.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-04-09-52-08.gh-issue-127434.RjkGT_.rst deleted file mode 100644 index 08b27a7890bb1c..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-04-09-52-08.gh-issue-127434.RjkGT_.rst +++ /dev/null @@ -1 +0,0 @@ -The iOS compiler shims can now accept arguments with spaces. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-05-19-25-00.gh-issue-127582.ogUY2a.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-05-19-25-00.gh-issue-127582.ogUY2a.rst deleted file mode 100644 index 59491feeb9bcfa..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-05-19-25-00.gh-issue-127582.ogUY2a.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix non-thread-safe object resurrection when calling finalizers and watcher -callbacks in the free threading build. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-06-01-09-40.gh-issue-127651.80cm6j.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-06-01-09-40.gh-issue-127651.80cm6j.rst deleted file mode 100644 index 92b18b082eb30e..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-06-01-09-40.gh-issue-127651.80cm6j.rst +++ /dev/null @@ -1 +0,0 @@ -When raising :exc:`ImportError` for missing symbols in ``from`` imports, use ``__file__`` in the error message if ``__spec__.origin`` is not a location diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-07-13-06-09.gh-issue-127599.tXCZb_.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-07-13-06-09.gh-issue-127599.tXCZb_.rst deleted file mode 100644 index 565ecb82c71926..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-07-13-06-09.gh-issue-127599.tXCZb_.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix statistics for increments of object reference counts (in particular, when -a reference count was increased by more than 1 in a single operation). diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-09-11-29-10.gh-issue-127058.pqtBcZ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-09-11-29-10.gh-issue-127058.pqtBcZ.rst deleted file mode 100644 index 248e1b4855afb8..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-09-11-29-10.gh-issue-127058.pqtBcZ.rst +++ /dev/null @@ -1,3 +0,0 @@ -``PySequence_Tuple`` now creates the resulting tuple atomically, preventing -partially created tuples being visible to the garbage collector or through -``gc.get_referrers()`` diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-10-21-08-05.gh-issue-127740.0tWC9h.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-10-21-08-05.gh-issue-127740.0tWC9h.rst deleted file mode 100644 index f614dbb59bdc87..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-10-21-08-05.gh-issue-127740.0tWC9h.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix error message in :func:`bytes.fromhex` when given an odd number of -digits to properly indicate that an even number of hexadecimal digits is -required. diff --git a/Misc/NEWS.d/next/Documentation/2024-11-27-22-56-48.gh-issue-127347.xyddWS.rst b/Misc/NEWS.d/next/Documentation/2024-11-27-22-56-48.gh-issue-127347.xyddWS.rst deleted file mode 100644 index 79b3faa3d0d385..00000000000000 --- a/Misc/NEWS.d/next/Documentation/2024-11-27-22-56-48.gh-issue-127347.xyddWS.rst +++ /dev/null @@ -1 +0,0 @@ -Publicly expose :func:`traceback.print_list` in :attr:`!traceback.__all__`. diff --git a/Misc/NEWS.d/next/Library/2022-11-10-17-16-45.gh-issue-97514.kzA0zl.rst b/Misc/NEWS.d/next/Library/2022-11-10-17-16-45.gh-issue-97514.kzA0zl.rst deleted file mode 100644 index 10c56edb8c7303..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-11-10-17-16-45.gh-issue-97514.kzA0zl.rst +++ /dev/null @@ -1,10 +0,0 @@ -Authentication was added to the :mod:`multiprocessing` forkserver start -method control socket so that only processes with the authentication key -generated by the process that spawned the forkserver can control it. This -is an enhancement over the other :gh:`97514` fixes so that access is no -longer limited only by filesystem permissions. - -The file descriptor exchange of control pipes with the forked worker process -now requires an explicit acknowledgement byte to be sent over the socket after -the exchange on all forkserver supporting platforms. That makes testing the -above much easier. diff --git a/Misc/NEWS.d/next/Library/2023-02-15-23-54-42.gh-issue-88110.KU6erv.rst b/Misc/NEWS.d/next/Library/2023-02-15-23-54-42.gh-issue-88110.KU6erv.rst deleted file mode 100644 index 42a83edc3ba68d..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-02-15-23-54-42.gh-issue-88110.KU6erv.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed :class:`multiprocessing.Process` reporting a ``.exitcode`` of 1 even on success when -using the ``"fork"`` start method while using a :class:`concurrent.futures.ThreadPoolExecutor`. diff --git a/Misc/NEWS.d/next/Library/2024-07-25-18-06-51.gh-issue-122288.-_xxOR.rst b/Misc/NEWS.d/next/Library/2024-07-25-18-06-51.gh-issue-122288.-_xxOR.rst deleted file mode 100644 index 26a18afca945d9..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-07-25-18-06-51.gh-issue-122288.-_xxOR.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve the performances of :func:`fnmatch.translate` by a factor 1.7. Patch -by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2024-07-29-15-20-30.gh-issue-122356.wKCmFx.rst b/Misc/NEWS.d/next/Library/2024-07-29-15-20-30.gh-issue-122356.wKCmFx.rst deleted file mode 100644 index 0a4632ca975f6b..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-07-29-15-20-30.gh-issue-122356.wKCmFx.rst +++ /dev/null @@ -1,3 +0,0 @@ -Guarantee that the position of a file-like object passed to -:func:`zipfile.is_zipfile` is left untouched after the call. -Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2024-07-30-11-37-40.gh-issue-122431.lAzVtu.rst b/Misc/NEWS.d/next/Library/2024-07-30-11-37-40.gh-issue-122431.lAzVtu.rst deleted file mode 100644 index 16ad75792aefa2..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-07-30-11-37-40.gh-issue-122431.lAzVtu.rst +++ /dev/null @@ -1 +0,0 @@ -:func:`readline.append_history_file` now raises a :exc:`ValueError` when given a negative value. diff --git a/Misc/NEWS.d/next/Library/2024-08-27-18-58-01.gh-issue-123401.t4-FpI.rst b/Misc/NEWS.d/next/Library/2024-08-27-18-58-01.gh-issue-123401.t4-FpI.rst deleted file mode 100644 index 638f3f76239ca6..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-08-27-18-58-01.gh-issue-123401.t4-FpI.rst +++ /dev/null @@ -1,3 +0,0 @@ -The :mod:`http.cookies` module now supports parsing obsolete :rfc:`850` -date formats, in accordance with :rfc:`9110` requirements. -Patch by Nano Zheng. diff --git a/Misc/NEWS.d/next/Library/2024-09-13-18-24-27.gh-issue-124008.XaiPQx.rst b/Misc/NEWS.d/next/Library/2024-09-13-18-24-27.gh-issue-124008.XaiPQx.rst deleted file mode 100644 index cd6dd9a7a97e90..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-09-13-18-24-27.gh-issue-124008.XaiPQx.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix possible crash (in debug build), incorrect output or returning incorrect -value from raw binary ``write()`` when writing to console on Windows. diff --git a/Misc/NEWS.d/next/Library/2024-10-23-20-05-54.gh-issue-86463.jvFTI_.rst b/Misc/NEWS.d/next/Library/2024-10-23-20-05-54.gh-issue-86463.jvFTI_.rst deleted file mode 100644 index 9ac155770e2254..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-10-23-20-05-54.gh-issue-86463.jvFTI_.rst +++ /dev/null @@ -1,2 +0,0 @@ -The ``usage`` parameter of :class:`argparse.ArgumentParser` no longer -affects the default value of the ``prog`` parameter in subparsers. diff --git a/Misc/NEWS.d/next/Library/2024-10-28-19-49-18.gh-issue-118201.v41XXh.rst b/Misc/NEWS.d/next/Library/2024-10-28-19-49-18.gh-issue-118201.v41XXh.rst deleted file mode 100644 index bed4b3b5956f31..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-10-28-19-49-18.gh-issue-118201.v41XXh.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed intermittent failures of :any:`os.confstr`, :any:`os.pathconf` and -:any:`os.sysconf` on iOS and Android. diff --git a/Misc/NEWS.d/next/Library/2024-11-04-22-02-30.gh-issue-85046.Y5d_ZN.rst b/Misc/NEWS.d/next/Library/2024-11-04-22-02-30.gh-issue-85046.Y5d_ZN.rst deleted file mode 100644 index ae1392e2caf387..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-04-22-02-30.gh-issue-85046.Y5d_ZN.rst +++ /dev/null @@ -1 +0,0 @@ -Add :data:`~errno.EHWPOISON` error code to :mod:`errno`. diff --git a/Misc/NEWS.d/next/Library/2024-11-12-13-14-47.gh-issue-126727.5Eqfqd.rst b/Misc/NEWS.d/next/Library/2024-11-12-13-14-47.gh-issue-126727.5Eqfqd.rst deleted file mode 100644 index 7bec8a6b7a830a..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-12-13-14-47.gh-issue-126727.5Eqfqd.rst +++ /dev/null @@ -1,3 +0,0 @@ -``locale.nl_langinfo(locale.ERA)`` now returns multiple era description -segments separated by semicolons. Previously it only returned the first -segment on platforms with Glibc. diff --git a/Misc/NEWS.d/next/Library/2024-11-12-20-05-09.gh-issue-126601.Nj7bA9.rst b/Misc/NEWS.d/next/Library/2024-11-12-20-05-09.gh-issue-126601.Nj7bA9.rst deleted file mode 100644 index 11e2b7350a0e48..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-12-20-05-09.gh-issue-126601.Nj7bA9.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix issue where :func:`urllib.request.pathname2url` raised :exc:`OSError` -when given a Windows path containing a colon character not following a -drive letter, such as before an NTFS alternate data stream. diff --git a/Misc/NEWS.d/next/Library/2024-11-13-10-44-25.gh-issue-126775.a3ubjh.rst b/Misc/NEWS.d/next/Library/2024-11-13-10-44-25.gh-issue-126775.a3ubjh.rst deleted file mode 100644 index 429fc2aa17bd42..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-13-10-44-25.gh-issue-126775.a3ubjh.rst +++ /dev/null @@ -1 +0,0 @@ -Make :func:`linecache.checkcache` thread safe and GC re-entrancy safe. diff --git a/Misc/NEWS.d/next/Library/2024-11-13-19-15-18.gh-issue-126780.ZZqJvI.rst b/Misc/NEWS.d/next/Library/2024-11-13-19-15-18.gh-issue-126780.ZZqJvI.rst deleted file mode 100644 index 93d45caf5cad72..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-13-19-15-18.gh-issue-126780.ZZqJvI.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :func:`os.path.normpath` for drive-relative paths on Windows. diff --git a/Misc/NEWS.d/next/Library/2024-11-15-01-50-36.gh-issue-85168.bP8VIN.rst b/Misc/NEWS.d/next/Library/2024-11-15-01-50-36.gh-issue-85168.bP8VIN.rst deleted file mode 100644 index abceda8f6fd707..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-15-01-50-36.gh-issue-85168.bP8VIN.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix issue where :func:`urllib.request.url2pathname` and -:func:`~urllib.request.pathname2url` always used UTF-8 when quoting and -unquoting file URIs. They now use the :term:`filesystem encoding and error -handler`. diff --git a/Misc/NEWS.d/next/Library/2024-11-16-10-52-48.gh-issue-126899.GFnfBt.rst b/Misc/NEWS.d/next/Library/2024-11-16-10-52-48.gh-issue-126899.GFnfBt.rst deleted file mode 100644 index c1a0ed6438135d..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-16-10-52-48.gh-issue-126899.GFnfBt.rst +++ /dev/null @@ -1,2 +0,0 @@ -Make tkinter widget methods :meth:`!after` and :meth:`!after_idle` accept -arguments passed by keyword. diff --git a/Misc/NEWS.d/next/Library/2024-11-18-16-43-11.gh-issue-126946.52Ou-B.rst b/Misc/NEWS.d/next/Library/2024-11-18-16-43-11.gh-issue-126946.52Ou-B.rst deleted file mode 100644 index 448055ccfdff40..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-18-16-43-11.gh-issue-126946.52Ou-B.rst +++ /dev/null @@ -1,3 +0,0 @@ -Improve the :exc:`~getopt.GetoptError` error message when a long option -prefix matches multiple accepted options in :func:`getopt.getopt` and -:func:`getopt.gnu_getopt`. diff --git a/Misc/NEWS.d/next/Library/2024-11-18-19-03-46.gh-issue-126947.NiDYUe.rst b/Misc/NEWS.d/next/Library/2024-11-18-19-03-46.gh-issue-126947.NiDYUe.rst deleted file mode 100644 index 29ba4f21454fe1..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-18-19-03-46.gh-issue-126947.NiDYUe.rst +++ /dev/null @@ -1,2 +0,0 @@ -Raise :exc:`TypeError` in :meth:`!_pydatetime.timedelta.__new__` if the passed arguments are not :class:`int` or :class:`float`, so that the Python -implementation is in line with the C implementation. diff --git a/Misc/NEWS.d/next/Library/2024-11-18-22-02-47.gh-issue-118761.GQKD_J.rst b/Misc/NEWS.d/next/Library/2024-11-18-22-02-47.gh-issue-118761.GQKD_J.rst deleted file mode 100644 index ebb9fe8016de21..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-18-22-02-47.gh-issue-118761.GQKD_J.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve import time of :mod:`mimetypes` by around 11-16 times. Patch by Hugo -van Kemenade. diff --git a/Misc/NEWS.d/next/Library/2024-11-18-23-18-27.gh-issue-112192.DRdRgP.rst b/Misc/NEWS.d/next/Library/2024-11-18-23-18-27.gh-issue-112192.DRdRgP.rst deleted file mode 100644 index b169c1508d0d30..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-18-23-18-27.gh-issue-112192.DRdRgP.rst +++ /dev/null @@ -1 +0,0 @@ -In the :mod:`trace` module, increase the coverage precision (``cov%``) to one decimal. diff --git a/Misc/NEWS.d/next/Library/2024-11-18-23-42-06.gh-issue-126985.7XplY9.rst b/Misc/NEWS.d/next/Library/2024-11-18-23-42-06.gh-issue-126985.7XplY9.rst deleted file mode 100644 index c875c7b547bba9..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-18-23-42-06.gh-issue-126985.7XplY9.rst +++ /dev/null @@ -1,3 +0,0 @@ -When running under a virtual environment with the :mod:`site` disabled (see -:option:`-S`), :data:`sys.prefix` and :data:`sys.base_prefix` will now point -to the virtual environment, instead of the base installation. diff --git a/Misc/NEWS.d/next/Library/2024-11-19-14-34-05.gh-issue-126615.LOskwi.rst b/Misc/NEWS.d/next/Library/2024-11-19-14-34-05.gh-issue-126615.LOskwi.rst deleted file mode 100644 index 8c7a2ade03c19e..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-19-14-34-05.gh-issue-126615.LOskwi.rst +++ /dev/null @@ -1,2 +0,0 @@ -The :exc:`~ctypes.COMError` exception is now public. -Previously, this was private and only available in ``_ctypes``. diff --git a/Misc/NEWS.d/next/Library/2024-11-20-08-54-11.gh-issue-126618.ef_53g.rst b/Misc/NEWS.d/next/Library/2024-11-20-08-54-11.gh-issue-126618.ef_53g.rst deleted file mode 100644 index 7a0a7b7517b70d..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-20-08-54-11.gh-issue-126618.ef_53g.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix the representation of :class:`itertools.count` objects when the count -value is :data:`sys.maxsize`. diff --git a/Misc/NEWS.d/next/Library/2024-11-20-11-37-08.gh-issue-126316.ElkZmE.rst b/Misc/NEWS.d/next/Library/2024-11-20-11-37-08.gh-issue-126316.ElkZmE.rst deleted file mode 100644 index d643254c5b3564..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-20-11-37-08.gh-issue-126316.ElkZmE.rst +++ /dev/null @@ -1,2 +0,0 @@ -:mod:`grp`: Make :func:`grp.getgrall` thread-safe by adding a mutex. Patch -by Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2024-11-20-16-58-59.gh-issue-126997.0PI41Y.rst b/Misc/NEWS.d/next/Library/2024-11-20-16-58-59.gh-issue-126997.0PI41Y.rst deleted file mode 100644 index b85c51ef07dcbe..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-20-16-58-59.gh-issue-126997.0PI41Y.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix support of STRING and GLOBAL opcodes with non-ASCII arguments in -:mod:`pickletools`. :func:`pickletools.dis` now outputs non-ASCII bytes in -STRING, BINSTRING and SHORT_BINSTRING arguments as escaped (``\xXX``). diff --git a/Misc/NEWS.d/next/Library/2024-11-20-21-20-56.gh-issue-126992.RbU0FZ.rst b/Misc/NEWS.d/next/Library/2024-11-20-21-20-56.gh-issue-126992.RbU0FZ.rst deleted file mode 100644 index 526785f68cc807..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-20-21-20-56.gh-issue-126992.RbU0FZ.rst +++ /dev/null @@ -1 +0,0 @@ -Fix LONG and INT opcodes to only use base 10 for string to integer conversion in :mod:`pickle`. diff --git a/Misc/NEWS.d/next/Library/2024-11-21-06-03-46.gh-issue-127090.yUYwdh.rst b/Misc/NEWS.d/next/Library/2024-11-21-06-03-46.gh-issue-127090.yUYwdh.rst deleted file mode 100644 index 8efe563f443774..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-21-06-03-46.gh-issue-127090.yUYwdh.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix value of :attr:`urllib.response.addinfourl.url` for ``file:`` URLs that -express relative paths and absolute Windows paths. The canonical URL generated -by :func:`urllib.request.pathname2url` is now used. diff --git a/Misc/NEWS.d/next/Library/2024-11-21-16-23-16.gh-issue-127065.cfL1zd.rst b/Misc/NEWS.d/next/Library/2024-11-21-16-23-16.gh-issue-127065.cfL1zd.rst deleted file mode 100644 index 83457da467ffa9..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-21-16-23-16.gh-issue-127065.cfL1zd.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix crash when calling a :func:`operator.methodcaller` instance from -multiple threads in the free threading build. diff --git a/Misc/NEWS.d/next/Library/2024-11-22-02-31-55.gh-issue-126766.jfkhBH.rst b/Misc/NEWS.d/next/Library/2024-11-22-02-31-55.gh-issue-126766.jfkhBH.rst deleted file mode 100644 index 998c99bf4358d5..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-22-02-31-55.gh-issue-126766.jfkhBH.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix issue where :func:`urllib.request.url2pathname` failed to discard any -'localhost' authority present in the URL. diff --git a/Misc/NEWS.d/next/Library/2024-11-22-03-40-02.gh-issue-127078.gI_PaP.rst b/Misc/NEWS.d/next/Library/2024-11-22-03-40-02.gh-issue-127078.gI_PaP.rst deleted file mode 100644 index a84c06f3c7a273..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-22-03-40-02.gh-issue-127078.gI_PaP.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix issue where :func:`urllib.request.url2pathname` failed to discard an -extra slash before a UNC drive in the URL path on Windows. diff --git a/Misc/NEWS.d/next/Library/2024-11-22-04-49-31.gh-issue-125866.TUtvPK.rst b/Misc/NEWS.d/next/Library/2024-11-22-04-49-31.gh-issue-125866.TUtvPK.rst deleted file mode 100644 index 682e061747689b..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-22-04-49-31.gh-issue-125866.TUtvPK.rst +++ /dev/null @@ -1,2 +0,0 @@ -:func:`urllib.request.pathname2url` and :func:`~urllib.request.url2pathname` -no longer convert Windows drive letters to uppercase. diff --git a/Misc/NEWS.d/next/Library/2024-11-22-09-23-41.gh-issue-122273.H8M6fd.rst b/Misc/NEWS.d/next/Library/2024-11-22-09-23-41.gh-issue-122273.H8M6fd.rst deleted file mode 100644 index 99071e05377e33..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-22-09-23-41.gh-issue-122273.H8M6fd.rst +++ /dev/null @@ -1 +0,0 @@ -Support PyREPL history on Windows. Patch by devdanzin and Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2024-11-22-10-42-34.gh-issue-127035.UnbDlr.rst b/Misc/NEWS.d/next/Library/2024-11-22-10-42-34.gh-issue-127035.UnbDlr.rst deleted file mode 100644 index 6bb7abfdd50040..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-22-10-42-34.gh-issue-127035.UnbDlr.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix :mod:`shutil.which` on Windows. Now it looks at direct match if and only -if the command ends with a PATHEXT extension or X_OK is not in mode. Support -extensionless files if "." is in PATHEXT. Support PATHEXT extensions that end -with a dot. diff --git a/Misc/NEWS.d/next/Library/2024-11-23-00-17-29.gh-issue-127221.OSXdFE.rst b/Misc/NEWS.d/next/Library/2024-11-23-00-17-29.gh-issue-127221.OSXdFE.rst deleted file mode 100644 index 0e4a03caf9f49d..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-23-00-17-29.gh-issue-127221.OSXdFE.rst +++ /dev/null @@ -1 +0,0 @@ -Add colour to :mod:`unittest` output. Patch by Hugo van Kemenade. diff --git a/Misc/NEWS.d/next/Library/2024-11-23-12-25-06.gh-issue-125866.wEOP66.rst b/Misc/NEWS.d/next/Library/2024-11-23-12-25-06.gh-issue-125866.wEOP66.rst deleted file mode 100644 index 0b8ffdb3901db3..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-23-12-25-06.gh-issue-125866.wEOP66.rst +++ /dev/null @@ -1,5 +0,0 @@ -:func:`urllib.request.pathname2url` now adds an empty authority when -generating a URL for a path that begins with exactly one slash. For example, -the path ``/etc/hosts`` is converted to the scheme-less URL ``///etc/hosts``. -As a result of this change, URLs without authorities are only generated for -relative paths. diff --git a/Misc/NEWS.d/next/Library/2024-11-24-12-41-31.gh-issue-127217.UAXGFr.rst b/Misc/NEWS.d/next/Library/2024-11-24-12-41-31.gh-issue-127217.UAXGFr.rst deleted file mode 100644 index 3139e33302f378..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-24-12-41-31.gh-issue-127217.UAXGFr.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :func:`urllib.request.pathname2url` for paths starting with multiple -slashes on Posix. diff --git a/Misc/NEWS.d/next/Library/2024-11-24-14-20-17.gh-issue-127182.WmfY2g.rst b/Misc/NEWS.d/next/Library/2024-11-24-14-20-17.gh-issue-127182.WmfY2g.rst deleted file mode 100644 index 2cc46ca3d33977..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-24-14-20-17.gh-issue-127182.WmfY2g.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :meth:`!io.StringIO.__setstate__` crash, when :const:`None` was passed as -the first value. diff --git a/Misc/NEWS.d/next/Library/2024-11-25-15-02-44.gh-issue-127255.UXeljc.rst b/Misc/NEWS.d/next/Library/2024-11-25-15-02-44.gh-issue-127255.UXeljc.rst deleted file mode 100644 index 9fe7815e93cf4f..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-25-15-02-44.gh-issue-127255.UXeljc.rst +++ /dev/null @@ -1,2 +0,0 @@ -The :func:`~ctypes.CopyComPointer` function is now public. -Previously, this was private and only available in ``_ctypes``. diff --git a/Misc/NEWS.d/next/Library/2024-11-25-19-04-10.gh-issue-127072.-c284K.rst b/Misc/NEWS.d/next/Library/2024-11-25-19-04-10.gh-issue-127072.-c284K.rst deleted file mode 100644 index 1bc7e1f0de9e0b..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-25-19-04-10.gh-issue-127072.-c284K.rst +++ /dev/null @@ -1 +0,0 @@ -Remove outdated ``socket.NETLINK_*`` constants not present in Linux kernels beyond 2.6.17. diff --git a/Misc/NEWS.d/next/Library/2024-11-26-17-42-00.gh-issue-127178.U8hxjc.rst b/Misc/NEWS.d/next/Library/2024-11-26-17-42-00.gh-issue-127178.U8hxjc.rst deleted file mode 100644 index b703b58ea8e1d9..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-26-17-42-00.gh-issue-127178.U8hxjc.rst +++ /dev/null @@ -1,4 +0,0 @@ -A ``_sysconfig_vars_(...).json`` file is now shipped in the standard library -directory. It contains the output of :func:`sysconfig.get_config_vars` on -the default environment encoded as JSON data. This is an implementation -detail, and may change at any time. diff --git a/Misc/NEWS.d/next/Library/2024-11-27-14-06-35.gh-issue-123967.wxUmnW.rst b/Misc/NEWS.d/next/Library/2024-11-27-14-06-35.gh-issue-123967.wxUmnW.rst deleted file mode 100644 index 788fe0c78ef257..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-27-14-06-35.gh-issue-123967.wxUmnW.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix faulthandler for trampoline frames. If the top-most frame is a -trampoline frame, skip it. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2024-11-27-14-23-02.gh-issue-127331.9sNEC9.rst b/Misc/NEWS.d/next/Library/2024-11-27-14-23-02.gh-issue-127331.9sNEC9.rst deleted file mode 100644 index c668816955ca59..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-27-14-23-02.gh-issue-127331.9sNEC9.rst +++ /dev/null @@ -1 +0,0 @@ -:mod:`ssl` can show descriptions for errors added in OpenSSL 3.4. diff --git a/Misc/NEWS.d/next/Library/2024-11-27-16-06-10.gh-issue-127303.asqkgh.rst b/Misc/NEWS.d/next/Library/2024-11-27-16-06-10.gh-issue-127303.asqkgh.rst deleted file mode 100644 index 58ebf5d0abe141..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-27-16-06-10.gh-issue-127303.asqkgh.rst +++ /dev/null @@ -1 +0,0 @@ -Publicly expose :data:`~token.EXACT_TOKEN_TYPES` in :attr:`!token.__all__`. diff --git a/Misc/NEWS.d/next/Library/2024-11-27-17-04-38.gh-issue-59705.sAGyvs.rst b/Misc/NEWS.d/next/Library/2024-11-27-17-04-38.gh-issue-59705.sAGyvs.rst deleted file mode 100644 index a8c7b3d00755e6..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-27-17-04-38.gh-issue-59705.sAGyvs.rst +++ /dev/null @@ -1,2 +0,0 @@ -On Linux, :class:`threading.Thread` now sets the thread name to the -operating system. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2024-11-28-14-14-46.gh-issue-127257.n6-jU9.rst b/Misc/NEWS.d/next/Library/2024-11-28-14-14-46.gh-issue-127257.n6-jU9.rst deleted file mode 100644 index fb0380cba0b607..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-28-14-14-46.gh-issue-127257.n6-jU9.rst +++ /dev/null @@ -1,2 +0,0 @@ -In :mod:`ssl`, system call failures that OpenSSL reports using -``ERR_LIB_SYS`` are now raised as :exc:`OSError`. diff --git a/Misc/NEWS.d/next/Library/2024-11-29-00-15-59.gh-issue-125413.WCN0vv.rst b/Misc/NEWS.d/next/Library/2024-11-29-00-15-59.gh-issue-125413.WCN0vv.rst deleted file mode 100644 index b56a77b4294ace..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-29-00-15-59.gh-issue-125413.WCN0vv.rst +++ /dev/null @@ -1,3 +0,0 @@ -Revert addition of :meth:`!pathlib.Path.scandir`. This method was added in -3.14.0a2. The optimizations remain for file system paths, but other -subclasses should only have to implement :meth:`pathlib.Path.iterdir`. diff --git a/Misc/NEWS.d/next/Library/2024-11-29-14-45-26.gh-issue-127413.z11AUc.rst b/Misc/NEWS.d/next/Library/2024-11-29-14-45-26.gh-issue-127413.z11AUc.rst deleted file mode 100644 index 2330fb66253265..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-29-14-45-26.gh-issue-127413.z11AUc.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add the :option:`dis --specialized` command-line option to show specialized -bytecode. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2024-11-29-23-02-43.gh-issue-127429.dQf2w4.rst b/Misc/NEWS.d/next/Library/2024-11-29-23-02-43.gh-issue-127429.dQf2w4.rst deleted file mode 100644 index 708c1a6437d812..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-29-23-02-43.gh-issue-127429.dQf2w4.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fixed bug where, on cross-builds, the :mod:`sysconfig` POSIX data was being -generated with the host Python's ``Makefile``. The data is now generated from -current build's ``Makefile``. diff --git a/Misc/NEWS.d/next/Library/2024-11-30-21-46-15.gh-issue-127321.M78fBv.rst b/Misc/NEWS.d/next/Library/2024-11-30-21-46-15.gh-issue-127321.M78fBv.rst deleted file mode 100644 index 69b6ce68a47509..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-30-21-46-15.gh-issue-127321.M78fBv.rst +++ /dev/null @@ -1 +0,0 @@ -:func:`pdb.set_trace` will not stop at an opcode that does not have an associated line number anymore. diff --git a/Misc/NEWS.d/next/Library/2024-12-01-22-28-41.gh-issue-127065.tFpRer.rst b/Misc/NEWS.d/next/Library/2024-12-01-22-28-41.gh-issue-127065.tFpRer.rst deleted file mode 100644 index 03d6953b9ddfa1..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-01-22-28-41.gh-issue-127065.tFpRer.rst +++ /dev/null @@ -1 +0,0 @@ -Make :func:`operator.methodcaller` thread-safe and re-entrant safe. diff --git a/Misc/NEWS.d/next/Library/2024-12-01-23-18-43.gh-issue-127481.K36AoP.rst b/Misc/NEWS.d/next/Library/2024-12-01-23-18-43.gh-issue-127481.K36AoP.rst deleted file mode 100644 index 8ada0b57ddc257..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-01-23-18-43.gh-issue-127481.K36AoP.rst +++ /dev/null @@ -1 +0,0 @@ -Add the ``EPOLLWAKEUP`` constant to the :mod:`select` module. diff --git a/Misc/NEWS.d/next/Library/2024-12-04-11-01-16.gh-issue-93312.9sB-Qw.rst b/Misc/NEWS.d/next/Library/2024-12-04-11-01-16.gh-issue-93312.9sB-Qw.rst deleted file mode 100644 index e245fa2bdd00b4..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-04-11-01-16.gh-issue-93312.9sB-Qw.rst +++ /dev/null @@ -1,2 +0,0 @@ -Include ```` to get ``os.PIDFD_NONBLOCK`` constant. Patch by -Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2024-12-04-15-04-12.gh-issue-126821.lKCLVV.rst b/Misc/NEWS.d/next/Library/2024-12-04-15-04-12.gh-issue-126821.lKCLVV.rst deleted file mode 100644 index 677acf5baab3fa..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-04-15-04-12.gh-issue-126821.lKCLVV.rst +++ /dev/null @@ -1,2 +0,0 @@ -macOS and iOS apps can now choose to redirect stdout and stderr to the -system log during interpreter configuration. diff --git a/Misc/NEWS.d/next/Library/2024-12-05-10-14-52.gh-issue-127627.fgCHOZ.rst b/Misc/NEWS.d/next/Library/2024-12-05-10-14-52.gh-issue-127627.fgCHOZ.rst deleted file mode 100644 index 48a6c7d30b4a26..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-05-10-14-52.gh-issue-127627.fgCHOZ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added ``posix._emscripten_debugger()`` to help with debugging the test suite on -the Emscripten target. diff --git a/Misc/NEWS.d/next/Library/2024-12-06-17-28-55.gh-issue-127610.ctv_NP.rst b/Misc/NEWS.d/next/Library/2024-12-06-17-28-55.gh-issue-127610.ctv_NP.rst deleted file mode 100644 index 58769029d79977..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-06-17-28-55.gh-issue-127610.ctv_NP.rst +++ /dev/null @@ -1,3 +0,0 @@ -Added validation for more than one var-positional or -var-keyword parameters in :class:`inspect.Signature`. -Patch by Maxim Ageev. diff --git a/Misc/NEWS.d/next/Library/2024-12-07-15-28-31.gh-issue-127718.9dpLfi.rst b/Misc/NEWS.d/next/Library/2024-12-07-15-28-31.gh-issue-127718.9dpLfi.rst deleted file mode 100644 index 6c1b7bef610e8c..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-07-15-28-31.gh-issue-127718.9dpLfi.rst +++ /dev/null @@ -1 +0,0 @@ -Add colour to :mod:`test.regrtest` output. Patch by Hugo van Kemenade. diff --git a/Misc/NEWS.d/next/Library/2024-12-07-23-06-44.gh-issue-126789.4dqfV1.rst b/Misc/NEWS.d/next/Library/2024-12-07-23-06-44.gh-issue-126789.4dqfV1.rst deleted file mode 100644 index 417e9ac986f27a..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-07-23-06-44.gh-issue-126789.4dqfV1.rst +++ /dev/null @@ -1,5 +0,0 @@ -Fixed :func:`sysconfig.get_config_vars`, :func:`sysconfig.get_paths`, and -siblings, returning outdated cached data if the value of :data:`sys.prefix` -or :data:`sys.exec_prefix` changes. Overwriting :data:`sys.prefix` or -:data:`sys.exec_prefix` still is discouraged, as that might break other -parts of the code. diff --git a/Misc/NEWS.d/next/Library/2024-12-08-08-36-18.gh-issue-127732.UEKxoa.rst b/Misc/NEWS.d/next/Library/2024-12-08-08-36-18.gh-issue-127732.UEKxoa.rst deleted file mode 100644 index 44821300f6e4e6..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-08-08-36-18.gh-issue-127732.UEKxoa.rst +++ /dev/null @@ -1 +0,0 @@ -The :mod:`platform` module now correctly detects Windows Server 2025. diff --git a/Misc/NEWS.d/next/Library/2024-12-12-16-59-42.gh-issue-127870._NFG-3.rst b/Misc/NEWS.d/next/Library/2024-12-12-16-59-42.gh-issue-127870._NFG-3.rst deleted file mode 100644 index 99b2df00032082..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-12-16-59-42.gh-issue-127870._NFG-3.rst +++ /dev/null @@ -1,2 +0,0 @@ -Detect recursive calls in ctypes ``_as_parameter_`` handling. -Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2024-12-13-22-20-54.gh-issue-126907.fWRL_R.rst b/Misc/NEWS.d/next/Library/2024-12-13-22-20-54.gh-issue-126907.fWRL_R.rst deleted file mode 100644 index d33d2aa23b21b3..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-13-22-20-54.gh-issue-126907.fWRL_R.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix crash when using :mod:`atexit` concurrently on the :term:`free-threaded -` build. diff --git a/Misc/NEWS.d/next/Security/2024-12-05-21-35-19.gh-issue-127655.xpPoOf.rst b/Misc/NEWS.d/next/Security/2024-12-05-21-35-19.gh-issue-127655.xpPoOf.rst deleted file mode 100644 index 76cfc58121d3bd..00000000000000 --- a/Misc/NEWS.d/next/Security/2024-12-05-21-35-19.gh-issue-127655.xpPoOf.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed the :class:`!asyncio.selector_events._SelectorSocketTransport` transport not pausing writes for the protocol when the buffer reaches the high water mark when using :meth:`asyncio.WriteTransport.writelines`. diff --git a/Misc/NEWS.d/next/Tests/2024-11-20-18-49-01.gh-issue-127076.DHnXxo.rst b/Misc/NEWS.d/next/Tests/2024-11-20-18-49-01.gh-issue-127076.DHnXxo.rst deleted file mode 100644 index 39323604bbef56..00000000000000 --- a/Misc/NEWS.d/next/Tests/2024-11-20-18-49-01.gh-issue-127076.DHnXxo.rst +++ /dev/null @@ -1,2 +0,0 @@ -Filter out memory-related ``mmap``, ``munmap``, and ``mprotect`` calls from -file-related ones when testing :mod:`io` behavior using strace. diff --git a/Misc/NEWS.d/next/Tests/2024-11-21-02-03-48.gh-issue-127076.a3avV1.rst b/Misc/NEWS.d/next/Tests/2024-11-21-02-03-48.gh-issue-127076.a3avV1.rst deleted file mode 100644 index 7dec8bd627c063..00000000000000 --- a/Misc/NEWS.d/next/Tests/2024-11-21-02-03-48.gh-issue-127076.a3avV1.rst +++ /dev/null @@ -1 +0,0 @@ -Disable strace based system call tests when LD_PRELOAD is set. diff --git a/Misc/NEWS.d/next/Tests/2024-12-04-15-03-24.gh-issue-126925.uxAMK-.rst b/Misc/NEWS.d/next/Tests/2024-12-04-15-03-24.gh-issue-126925.uxAMK-.rst deleted file mode 100644 index fb307c7cb9bf1d..00000000000000 --- a/Misc/NEWS.d/next/Tests/2024-12-04-15-03-24.gh-issue-126925.uxAMK-.rst +++ /dev/null @@ -1,2 +0,0 @@ -iOS test results are now streamed during test execution, and the deprecated -xcresulttool is no longer used. diff --git a/Misc/NEWS.d/next/Tests/2024-12-09-12-35-44.gh-issue-127637.KLx-9I.rst b/Misc/NEWS.d/next/Tests/2024-12-09-12-35-44.gh-issue-127637.KLx-9I.rst deleted file mode 100644 index ac5d9827b07199..00000000000000 --- a/Misc/NEWS.d/next/Tests/2024-12-09-12-35-44.gh-issue-127637.KLx-9I.rst +++ /dev/null @@ -1 +0,0 @@ -Add tests for the :mod:`dis` command-line interface. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Tests/2024-12-13-13-41-34.gh-issue-127906.NuRHlB.rst b/Misc/NEWS.d/next/Tests/2024-12-13-13-41-34.gh-issue-127906.NuRHlB.rst deleted file mode 100644 index 6f577e741dff7f..00000000000000 --- a/Misc/NEWS.d/next/Tests/2024-12-13-13-41-34.gh-issue-127906.NuRHlB.rst +++ /dev/null @@ -1 +0,0 @@ -Test the limited C API in test_cppext. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tools-Demos/2024-11-16-20-47-20.gh-issue-126700.ayrHv4.rst b/Misc/NEWS.d/next/Tools-Demos/2024-11-16-20-47-20.gh-issue-126700.ayrHv4.rst deleted file mode 100644 index c08ad9d7059904..00000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2024-11-16-20-47-20.gh-issue-126700.ayrHv4.rst +++ /dev/null @@ -1 +0,0 @@ -Add support for multi-argument :mod:`gettext` functions in :program:`pygettext.py`. diff --git a/Misc/NEWS.d/next/Windows/2024-10-31-09-46-53.gh-issue-125729.KdKVLa.rst b/Misc/NEWS.d/next/Windows/2024-10-31-09-46-53.gh-issue-125729.KdKVLa.rst deleted file mode 100644 index fbf4ab1cd1a11a..00000000000000 --- a/Misc/NEWS.d/next/Windows/2024-10-31-09-46-53.gh-issue-125729.KdKVLa.rst +++ /dev/null @@ -1 +0,0 @@ -Makes the presence of the :mod:`turtle` module dependent on the Tcl/Tk installer option. Previously, the module was always installed but would be unusable without Tcl/Tk. diff --git a/Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst b/Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst deleted file mode 100644 index 88661b9a611071..00000000000000 --- a/Misc/NEWS.d/next/Windows/2024-11-28-15-55-48.gh-issue-127353.i-XOXg.rst +++ /dev/null @@ -1,2 +0,0 @@ -Allow to force color output on Windows using environment variables. Patch by -Andrey Efremov. diff --git a/README.rst b/README.rst index 394cdc3638485d..02776205e6dcc9 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.14.0 alpha 2 +This is Python version 3.14.0 alpha 3 ===================================== .. image:: https://github.com/python/cpython/actions/workflows/build.yml/badge.svg?branch=main&event=push From 7303f06846b69016a075bca7ad7c6055f29ad024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:12:45 +0100 Subject: [PATCH 045/376] gh-126742: Add _PyErr_SetLocaleString, use it for gdbm & dlerror messages (GH-126746) - Add a helper to set an error from locale-encoded `char*` - Use the helper for gdbm & dlerror messages Co-authored-by: Victor Stinner --- Include/internal/pycore_pyerrors.h | 12 +++++ Lib/test/test_ctypes/test_dlerror.py | 80 ++++++++++++++++++++++++---- Lib/test/test_dbm_gnu.py | 21 ++++++-- Modules/_ctypes/_ctypes.c | 28 +++------- Modules/_ctypes/callproc.c | 38 +++++++------ Modules/_gdbmmodule.c | 44 +++++++++++---- Modules/_hashopenssl.c | 1 + Modules/_sqlite/util.c | 1 + Modules/_testcapi/exceptions.c | 1 + Modules/main.c | 1 + Modules/pyexpat.c | 7 ++- Python/errors.c | 9 ++++ 12 files changed, 175 insertions(+), 68 deletions(-) diff --git a/Include/internal/pycore_pyerrors.h b/Include/internal/pycore_pyerrors.h index 02945f0e71a145..6f2fdda9a9f12f 100644 --- a/Include/internal/pycore_pyerrors.h +++ b/Include/internal/pycore_pyerrors.h @@ -130,6 +130,18 @@ PyAPI_FUNC(void) _PyErr_SetString( PyObject *exception, const char *string); +/* + * Set an exception with the error message decoded from the current locale + * encoding (LC_CTYPE). + * + * Exceptions occurring in decoding take priority over the desired exception. + * + * Exported for '_ctypes' shared extensions. + */ +PyAPI_FUNC(void) _PyErr_SetLocaleString( + PyObject *exception, + const char *string); + PyAPI_FUNC(PyObject*) _PyErr_Format( PyThreadState *tstate, PyObject *exception, diff --git a/Lib/test/test_ctypes/test_dlerror.py b/Lib/test/test_ctypes/test_dlerror.py index 4441e30cd7a2a7..c3c34d43481d36 100644 --- a/Lib/test/test_ctypes/test_dlerror.py +++ b/Lib/test/test_ctypes/test_dlerror.py @@ -1,7 +1,12 @@ +import _ctypes import os +import platform import sys +import test.support import unittest -import platform +from ctypes import CDLL, c_int +from ctypes.util import find_library + FOO_C = r""" #include @@ -26,7 +31,7 @@ @unittest.skipUnless(sys.platform.startswith('linux'), - 'Test only valid for Linux') + 'test requires GNU IFUNC support') class TestNullDlsym(unittest.TestCase): """GH-126554: Ensure that we catch NULL dlsym return values @@ -53,14 +58,6 @@ def test_null_dlsym(self): import subprocess import tempfile - # To avoid ImportErrors on Windows, where _ctypes does not have - # dlopen and dlsym, - # import here, i.e., inside the test function. - # The skipUnless('linux') decorator ensures that we're on linux - # if we're executing these statements. - from ctypes import CDLL, c_int - from _ctypes import dlopen, dlsym - retcode = subprocess.call(["gcc", "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) @@ -111,6 +108,8 @@ def test_null_dlsym(self): self.assertEqual(os.read(pipe_r, 2), b'OK') # Case #3: Test 'py_dl_sym' from Modules/_ctypes/callproc.c + dlopen = test.support.get_attribute(_ctypes, 'dlopen') + dlsym = test.support.get_attribute(_ctypes, 'dlsym') L = dlopen(dstname) with self.assertRaisesRegex(OSError, "symbol 'foo' not found"): dlsym(L, "foo") @@ -119,5 +118,66 @@ def test_null_dlsym(self): self.assertEqual(os.read(pipe_r, 2), b'OK') +@unittest.skipUnless(os.name != 'nt', 'test requires dlerror() calls') +class TestLocalization(unittest.TestCase): + + @staticmethod + def configure_locales(func): + return test.support.run_with_locale( + 'LC_ALL', + 'fr_FR.iso88591', 'ja_JP.sjis', 'zh_CN.gbk', + 'fr_FR.utf8', 'en_US.utf8', + '', + )(func) + + @classmethod + def setUpClass(cls): + cls.libc_filename = find_library("c") + + @configure_locales + def test_localized_error_from_dll(self): + dll = CDLL(self.libc_filename) + with self.assertRaises(AttributeError) as cm: + dll.this_name_does_not_exist + if sys.platform.startswith('linux'): + # On macOS, the filename is not reported by dlerror(). + self.assertIn(self.libc_filename, str(cm.exception)) + + @configure_locales + def test_localized_error_in_dll(self): + dll = CDLL(self.libc_filename) + with self.assertRaises(ValueError) as cm: + c_int.in_dll(dll, 'this_name_does_not_exist') + if sys.platform.startswith('linux'): + # On macOS, the filename is not reported by dlerror(). + self.assertIn(self.libc_filename, str(cm.exception)) + + @unittest.skipUnless(hasattr(_ctypes, 'dlopen'), + 'test requires _ctypes.dlopen()') + @configure_locales + def test_localized_error_dlopen(self): + missing_filename = b'missing\xff.so' + # Depending whether the locale, we may encode '\xff' differently + # but we are only interested in avoiding a UnicodeDecodeError + # when reporting the dlerror() error message which contains + # the localized filename. + filename_pattern = r'missing.*?\.so' + with self.assertRaisesRegex(OSError, filename_pattern): + _ctypes.dlopen(missing_filename, 2) + + @unittest.skipUnless(hasattr(_ctypes, 'dlopen'), + 'test requires _ctypes.dlopen()') + @unittest.skipUnless(hasattr(_ctypes, 'dlsym'), + 'test requires _ctypes.dlsym()') + @configure_locales + def test_localized_error_dlsym(self): + dll = _ctypes.dlopen(self.libc_filename) + with self.assertRaises(OSError) as cm: + _ctypes.dlsym(dll, 'this_name_does_not_exist') + if sys.platform.startswith('linux'): + # On macOS, the filename is not reported by dlerror(). + self.assertIn(self.libc_filename, str(cm.exception)) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_dbm_gnu.py b/Lib/test/test_dbm_gnu.py index e20addf1f04f1b..66268c42a300b5 100644 --- a/Lib/test/test_dbm_gnu.py +++ b/Lib/test/test_dbm_gnu.py @@ -1,10 +1,11 @@ -from test import support -from test.support import import_helper, cpython_only -gdbm = import_helper.import_module("dbm.gnu") #skip if not supported -import unittest import os -from test.support.os_helper import TESTFN, TESTFN_NONASCII, unlink, FakePath +import unittest +from test import support +from test.support import cpython_only, import_helper +from test.support.os_helper import (TESTFN, TESTFN_NONASCII, FakePath, + create_empty_file, temp_dir, unlink) +gdbm = import_helper.import_module("dbm.gnu") # skip if not supported filename = TESTFN @@ -205,6 +206,16 @@ def test_clear(self): self.assertNotIn(k, db) self.assertEqual(len(db), 0) + @support.run_with_locale( + 'LC_ALL', + 'fr_FR.iso88591', 'ja_JP.sjis', 'zh_CN.gbk', + 'fr_FR.utf8', 'en_US.utf8', + '', + ) + def test_localized_error(self): + with temp_dir() as d: + create_empty_file(os.path.join(d, 'test')) + self.assertRaises(gdbm.error, gdbm.open, filename, 'r') if __name__ == '__main__': diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index bb4699884057ba..3a3b1da5084a67 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -984,15 +984,8 @@ CDataType_in_dll_impl(PyObject *type, PyTypeObject *cls, PyObject *dll, #ifdef USE_DLERROR const char *dlerr = dlerror(); if (dlerr) { - PyObject *message = PyUnicode_DecodeLocale(dlerr, "surrogateescape"); - if (message) { - PyErr_SetObject(PyExc_ValueError, message); - Py_DECREF(message); - return NULL; - } - // Ignore errors from PyUnicode_DecodeLocale, - // fall back to the generic error below. - PyErr_Clear(); + _PyErr_SetLocaleString(PyExc_ValueError, dlerr); + return NULL; } #endif #undef USE_DLERROR @@ -3825,21 +3818,14 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) address = (PPROC)dlsym(handle, name); if (!address) { - #ifdef USE_DLERROR + #ifdef USE_DLERROR const char *dlerr = dlerror(); if (dlerr) { - PyObject *message = PyUnicode_DecodeLocale(dlerr, "surrogateescape"); - if (message) { - PyErr_SetObject(PyExc_AttributeError, message); - Py_DECREF(ftuple); - Py_DECREF(message); - return NULL; - } - // Ignore errors from PyUnicode_DecodeLocale, - // fall back to the generic error below. - PyErr_Clear(); + _PyErr_SetLocaleString(PyExc_AttributeError, dlerr); + Py_DECREF(ftuple); + return NULL; } - #endif + #endif PyErr_Format(PyExc_AttributeError, "function '%s' not found", name); Py_DECREF(ftuple); return NULL; diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 218c3a9c81e05f..92eedff5ec94f1 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1588,10 +1588,11 @@ static PyObject *py_dl_open(PyObject *self, PyObject *args) Py_XDECREF(name2); if (!handle) { const char *errmsg = dlerror(); - if (!errmsg) - errmsg = "dlopen() error"; - PyErr_SetString(PyExc_OSError, - errmsg); + if (errmsg) { + _PyErr_SetLocaleString(PyExc_OSError, errmsg); + return NULL; + } + PyErr_SetString(PyExc_OSError, "dlopen() error"); return NULL; } return PyLong_FromVoidPtr(handle); @@ -1604,8 +1605,12 @@ static PyObject *py_dl_close(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "O&:dlclose", &_parse_voidp, &handle)) return NULL; if (dlclose(handle)) { - PyErr_SetString(PyExc_OSError, - dlerror()); + const char *errmsg = dlerror(); + if (errmsg) { + _PyErr_SetLocaleString(PyExc_OSError, errmsg); + return NULL; + } + PyErr_SetString(PyExc_OSError, "dlclose() error"); return NULL; } Py_RETURN_NONE; @@ -1639,21 +1644,14 @@ static PyObject *py_dl_sym(PyObject *self, PyObject *args) if (ptr) { return PyLong_FromVoidPtr(ptr); } - #ifdef USE_DLERROR - const char *dlerr = dlerror(); - if (dlerr) { - PyObject *message = PyUnicode_DecodeLocale(dlerr, "surrogateescape"); - if (message) { - PyErr_SetObject(PyExc_OSError, message); - Py_DECREF(message); - return NULL; - } - // Ignore errors from PyUnicode_DecodeLocale, - // fall back to the generic error below. - PyErr_Clear(); + #ifdef USE_DLERROR + const char *errmsg = dlerror(); + if (errmsg) { + _PyErr_SetLocaleString(PyExc_OSError, errmsg); + return NULL; } - #endif - #undef USE_DLERROR + #endif + #undef USE_DLERROR PyErr_Format(PyExc_OSError, "symbol '%s' not found", name); return NULL; } diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index df7fba67810ed0..ea4fe247987e9d 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -8,10 +8,11 @@ #endif #include "Python.h" +#include "pycore_pyerrors.h" // _PyErr_SetLocaleString() #include "gdbm.h" #include -#include // free() +#include // free() #include #include @@ -33,6 +34,24 @@ get_gdbm_state(PyObject *module) return (_gdbm_state *)state; } +/* + * Set the gdbm error obtained by gdbm_strerror(gdbm_errno). + * + * If no error message exists, a generic (UTF-8) error message + * is used instead. + */ +static void +set_gdbm_error(_gdbm_state *state, const char *generic_error) +{ + const char *gdbm_errmsg = gdbm_strerror(gdbm_errno); + if (gdbm_errmsg) { + _PyErr_SetLocaleString(state->gdbm_error, gdbm_errmsg); + } + else { + PyErr_SetString(state->gdbm_error, generic_error); + } +} + /*[clinic input] module _gdbm class _gdbm.gdbm "gdbmobject *" "&Gdbmtype" @@ -91,7 +110,7 @@ newgdbmobject(_gdbm_state *state, const char *file, int flags, int mode) PyErr_SetFromErrnoWithFilename(state->gdbm_error, file); } else { - PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno)); + set_gdbm_error(state, "gdbm_open() error"); } Py_DECREF(dp); return NULL; @@ -136,7 +155,7 @@ gdbm_length(gdbmobject *dp) PyErr_SetFromErrno(state->gdbm_error); } else { - PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno)); + set_gdbm_error(state, "gdbm_count() error"); } return -1; } @@ -286,7 +305,7 @@ gdbm_ass_sub(gdbmobject *dp, PyObject *v, PyObject *w) PyErr_SetObject(PyExc_KeyError, v); } else { - PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno)); + set_gdbm_error(state, "gdbm_delete() error"); } return -1; } @@ -297,11 +316,12 @@ gdbm_ass_sub(gdbmobject *dp, PyObject *v, PyObject *w) } errno = 0; if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0) { - if (errno != 0) + if (errno != 0) { PyErr_SetFromErrno(state->gdbm_error); - else - PyErr_SetString(state->gdbm_error, - gdbm_strerror(gdbm_errno)); + } + else { + set_gdbm_error(state, "gdbm_store() error"); + } return -1; } } @@ -534,10 +554,12 @@ _gdbm_gdbm_reorganize_impl(gdbmobject *self, PyTypeObject *cls) check_gdbmobject_open(self, state->gdbm_error); errno = 0; if (gdbm_reorganize(self->di_dbm) < 0) { - if (errno != 0) + if (errno != 0) { PyErr_SetFromErrno(state->gdbm_error); - else - PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno)); + } + else { + set_gdbm_error(state, "gdbm_reorganize() error"); + } return NULL; } Py_RETURN_NONE; diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index 2c9a9feecc79f0..082929be3c77b7 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -319,6 +319,7 @@ _setException(PyObject *exc, const char* altmsg, ...) va_end(vargs); ERR_clear_error(); + /* ERR_ERROR_STRING(3) ensures that the messages below are ASCII */ lib = ERR_lib_error_string(errcode); func = ERR_func_error_string(errcode); reason = ERR_reason_error_string(errcode); diff --git a/Modules/_sqlite/util.c b/Modules/_sqlite/util.c index 9e8613ef67916e..b0622e66928f47 100644 --- a/Modules/_sqlite/util.c +++ b/Modules/_sqlite/util.c @@ -134,6 +134,7 @@ _pysqlite_seterror(pysqlite_state *state, sqlite3 *db) /* Create and set the exception. */ int extended_errcode = sqlite3_extended_errcode(db); + // sqlite3_errmsg() always returns an UTF-8 encoded message const char *errmsg = sqlite3_errmsg(db); raise_exception(exc_class, extended_errcode, errmsg); return extended_errcode; diff --git a/Modules/_testcapi/exceptions.c b/Modules/_testcapi/exceptions.c index e92d9670e7c792..b647bfc71eae24 100644 --- a/Modules/_testcapi/exceptions.c +++ b/Modules/_testcapi/exceptions.c @@ -3,6 +3,7 @@ #include "parts.h" #include "util.h" + #include "clinic/exceptions.c.h" diff --git a/Modules/main.c b/Modules/main.c index 15ea49a1bad19e..3bf2241f2837a3 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -374,6 +374,7 @@ pymain_run_file_obj(PyObject *program_name, PyObject *filename, if (fp == NULL) { // Ignore the OSError PyErr_Clear(); + // TODO(picnixz): strerror() is locale dependent but not PySys_FormatStderr(). PySys_FormatStderr("%S: can't open file %R: [Errno %d] %s\n", program_name, filename, errno, strerror(errno)); return 2; diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index 9733bc34f7c80a..cf7714e7656205 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -1782,7 +1782,12 @@ add_error(PyObject *errors_module, PyObject *codes_dict, * with the other uses of the XML_ErrorString function * elsewhere within this file. pyexpat's copy of the messages * only acts as a fallback in case of outdated runtime libexpat, - * where it returns NULL. */ + * where it returns NULL. + * + * In addition, XML_ErrorString is assumed to return UTF-8 encoded + * strings (in conv_string_to_unicode, we decode them using 'strict' + * error handling). + */ const char *error_string = XML_ErrorString(error_code); if (error_string == NULL) { error_string = error_info_of[error_index].description; diff --git a/Python/errors.c b/Python/errors.c index 7f3b4aabc432d7..2d362c1864ffff 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -301,6 +301,15 @@ PyErr_SetString(PyObject *exception, const char *string) _PyErr_SetString(tstate, exception, string); } +void +_PyErr_SetLocaleString(PyObject *exception, const char *string) +{ + PyObject *value = PyUnicode_DecodeLocale(string, "surrogateescape"); + if (value != NULL) { + PyErr_SetObject(exception, value); + Py_DECREF(value); + } +} PyObject* _Py_HOT_FUNCTION PyErr_Occurred(void) From d70e5c1fefb69c541205a7e97795f10fd61c2905 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:52:43 +0200 Subject: [PATCH 046/376] Post 3.14.0a3 --- Include/patchlevel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 1e9df2d9ebd07d..6d4f719fcde5a8 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -23,7 +23,7 @@ #define PY_RELEASE_SERIAL 3 /* Version as a string */ -#define PY_VERSION "3.14.0a3" +#define PY_VERSION "3.14.0a3+" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. From b92f101d0f19a1df32050b8502cfcce777b079b2 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Wed, 18 Dec 2024 00:17:09 +0100 Subject: [PATCH 047/376] gh-127146: Emscripten Include compiler version in _PYTHON_HOST_PLATFORM (#127992) Modifies _PYTHON_HOST_PLATFORM to include the compiler version under Emscripten. The Emscripten compiler version is the platform version compatibility identifier. --- configure | 3 +++ configure.ac | 3 +++ 2 files changed, 6 insertions(+) diff --git a/configure b/configure index 57be576e3cae99..6df1116fc600f2 100755 --- a/configure +++ b/configure @@ -4545,6 +4545,9 @@ printf "%s\n" "$IPHONEOS_DEPLOYMENT_TARGET" >&6; } *-*-vxworks*) _host_ident=$host_cpu ;; + *-*-emscripten) + _host_ident=$(emcc -dumpversion)-$host_cpu + ;; wasm32-*-* | wasm64-*-*) _host_ident=$host_cpu ;; diff --git a/configure.ac b/configure.ac index bd0221481c5341..8295b59b8e45fb 100644 --- a/configure.ac +++ b/configure.ac @@ -793,6 +793,9 @@ if test "$cross_compiling" = yes; then *-*-vxworks*) _host_ident=$host_cpu ;; + *-*-emscripten) + _host_ident=$(emcc -dumpversion)-$host_cpu + ;; wasm32-*-* | wasm64-*-*) _host_ident=$host_cpu ;; From 559b0e7013f9cbf42a12fe9c86048d5cbb2f6f22 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 18 Dec 2024 06:35:05 +0100 Subject: [PATCH 048/376] gh-127060: Disable traceback colors in IDLE (#128028) Set TERM environment variable to "dumb" to disable traceback colors in IDLE, since IDLE doesn't understand ANSI escape sequences. --- Lib/idlelib/pyshell.py | 4 +++- .../Library/2024-12-17-13-21-52.gh-issue-127060.mv2bX6.rst | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-17-13-21-52.gh-issue-127060.mv2bX6.rst diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index e882c6cb3b8d19..66fbbd4a97b7af 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -424,7 +424,9 @@ def __init__(self, tkconsole): def spawn_subprocess(self): if self.subprocess_arglist is None: self.subprocess_arglist = self.build_subprocess_arglist() - self.rpcsubproc = subprocess.Popen(self.subprocess_arglist) + # gh-127060: Disable traceback colors + env = dict(os.environ, TERM='dumb') + self.rpcsubproc = subprocess.Popen(self.subprocess_arglist, env=env) def build_subprocess_arglist(self): assert (self.port!=0), ( diff --git a/Misc/NEWS.d/next/Library/2024-12-17-13-21-52.gh-issue-127060.mv2bX6.rst b/Misc/NEWS.d/next/Library/2024-12-17-13-21-52.gh-issue-127060.mv2bX6.rst new file mode 100644 index 00000000000000..1da89e7a282147 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-17-13-21-52.gh-issue-127060.mv2bX6.rst @@ -0,0 +1,2 @@ +Set TERM environment variable to "dumb" to disable traceback colors in IDLE, +since IDLE doesn't understand ANSI escape sequences. Patch by Victor Stinner. From 5892853fb71acd6530e1e241a9a4bcf71a61fb21 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 18 Dec 2024 11:35:29 +0530 Subject: [PATCH 049/376] gh-127949: deprecate `asyncio.set_event_loop_policy` (#128024) First step towards deprecating the asyncio policy system. This deprecates `asyncio.set_event_loop_policy` and will be removed in Python 3.16. --- Doc/library/asyncio-policy.rst | 4 ++++ Lib/asyncio/events.py | 10 +++++++-- Lib/test/libregrtest/save_env.py | 2 +- Lib/test/test_asyncgen.py | 2 +- Lib/test/test_asyncio/test_base_events.py | 2 +- Lib/test/test_asyncio/test_buffered_proto.py | 2 +- Lib/test/test_asyncio/test_context.py | 2 +- .../test_asyncio/test_eager_task_factory.py | 2 +- Lib/test/test_asyncio/test_events.py | 22 +++++++++++-------- Lib/test/test_asyncio/test_futures.py | 2 +- Lib/test/test_asyncio/test_futures2.py | 2 +- Lib/test/test_asyncio/test_locks.py | 2 +- Lib/test/test_asyncio/test_pep492.py | 2 +- Lib/test/test_asyncio/test_proactor_events.py | 2 +- Lib/test/test_asyncio/test_protocols.py | 2 +- Lib/test/test_asyncio/test_queues.py | 2 +- Lib/test/test_asyncio/test_runners.py | 8 +++---- Lib/test/test_asyncio/test_selector_events.py | 2 +- Lib/test/test_asyncio/test_sendfile.py | 2 +- Lib/test/test_asyncio/test_server.py | 2 +- Lib/test/test_asyncio/test_sock_lowlevel.py | 2 +- Lib/test/test_asyncio/test_ssl.py | 2 +- Lib/test/test_asyncio/test_sslproto.py | 2 +- Lib/test/test_asyncio/test_staggered.py | 2 +- Lib/test/test_asyncio/test_streams.py | 2 +- Lib/test/test_asyncio/test_subprocess.py | 2 +- Lib/test/test_asyncio/test_taskgroups.py | 2 +- Lib/test/test_asyncio/test_tasks.py | 2 +- Lib/test/test_asyncio/test_threads.py | 2 +- Lib/test/test_asyncio/test_timeouts.py | 2 +- Lib/test/test_asyncio/test_transports.py | 2 +- Lib/test/test_asyncio/test_unix_events.py | 2 +- Lib/test/test_asyncio/test_waitfor.py | 2 +- Lib/test/test_asyncio/test_windows_events.py | 10 ++++----- Lib/test/test_asyncio/test_windows_utils.py | 2 +- Lib/test/test_builtin.py | 4 ++-- .../test_interpreter_pool.py | 2 +- Lib/test/test_contextlib_async.py | 2 +- Lib/test/test_coroutines.py | 2 +- Lib/test/test_inspect/test_inspect.py | 2 +- Lib/test/test_logging.py | 6 ++--- Lib/test/test_os.py | 2 +- Lib/test/test_pdb.py | 6 ++--- Lib/test/test_sys_settrace.py | 2 +- Lib/test/test_unittest/test_async_case.py | 4 ++-- Lib/test/test_unittest/testmock/testasync.py | 2 +- 46 files changed, 81 insertions(+), 67 deletions(-) diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst index 09b75762ff0272..e8e470c1b343fd 100644 --- a/Doc/library/asyncio-policy.rst +++ b/Doc/library/asyncio-policy.rst @@ -46,6 +46,10 @@ for the current process: If *policy* is set to ``None``, the default policy is restored. + .. deprecated:: next + The :func:`set_event_loop_policy` function is deprecated and + will be removed in Python 3.16. + .. _asyncio-policy-objects: diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index ca0a4f2fee5840..0926cfe2323478 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -8,7 +8,9 @@ 'AbstractEventLoopPolicy', 'AbstractEventLoop', 'AbstractServer', 'Handle', 'TimerHandle', - 'get_event_loop_policy', 'set_event_loop_policy', + 'get_event_loop_policy', + '_set_event_loop_policy', + 'set_event_loop_policy', 'get_event_loop', 'set_event_loop', 'new_event_loop', '_set_running_loop', 'get_running_loop', '_get_running_loop', @@ -21,6 +23,7 @@ import subprocess import sys import threading +import warnings from . import format_helpers @@ -765,7 +768,7 @@ def get_event_loop_policy(): return _event_loop_policy -def set_event_loop_policy(policy): +def _set_event_loop_policy(policy): """Set the current event loop policy. If policy is None, the default policy is restored.""" @@ -774,6 +777,9 @@ def set_event_loop_policy(policy): raise TypeError(f"policy must be an instance of AbstractEventLoopPolicy or None, not '{type(policy).__name__}'") _event_loop_policy = policy +def set_event_loop_policy(policy): + warnings._deprecated('set_event_loop_policy', remove=(3,16)) + _set_event_loop_policy(policy) def get_event_loop(): """Return an asyncio event loop. diff --git a/Lib/test/libregrtest/save_env.py b/Lib/test/libregrtest/save_env.py index b2cc381344b2ef..ffc29fa8dc686a 100644 --- a/Lib/test/libregrtest/save_env.py +++ b/Lib/test/libregrtest/save_env.py @@ -97,7 +97,7 @@ def get_asyncio_events__event_loop_policy(self): return support.maybe_get_event_loop_policy() def restore_asyncio_events__event_loop_policy(self, policy): asyncio = self.get_module('asyncio') - asyncio.set_event_loop_policy(policy) + asyncio._set_event_loop_policy(policy) def get_sys_argv(self): return id(sys.argv), sys.argv, sys.argv[:] diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py index 4f2278bb263681..4bce6d5c1b1d2f 100644 --- a/Lib/test/test_asyncgen.py +++ b/Lib/test/test_asyncgen.py @@ -629,7 +629,7 @@ def setUp(self): def tearDown(self): self.loop.close() self.loop = None - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) def check_async_iterator_anext(self, ait_class): with self.subTest(anext="pure-Python"): diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index c14a0bb180d79b..08e38b047d519b 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -25,7 +25,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) def mock_socket_module(): diff --git a/Lib/test/test_asyncio/test_buffered_proto.py b/Lib/test/test_asyncio/test_buffered_proto.py index f24e363ebfcfa3..9c386dd2e63815 100644 --- a/Lib/test/test_asyncio/test_buffered_proto.py +++ b/Lib/test/test_asyncio/test_buffered_proto.py @@ -5,7 +5,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class ReceiveStuffProto(asyncio.BufferedProtocol): diff --git a/Lib/test/test_asyncio/test_context.py b/Lib/test/test_asyncio/test_context.py index 6b80721873d95c..ad394f44e7e5f6 100644 --- a/Lib/test/test_asyncio/test_context.py +++ b/Lib/test/test_asyncio/test_context.py @@ -4,7 +4,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) @unittest.skipUnless(decimal.HAVE_CONTEXTVAR, "decimal is built with a thread-local context") diff --git a/Lib/test/test_asyncio/test_eager_task_factory.py b/Lib/test/test_asyncio/test_eager_task_factory.py index 0e2b189f761521..dcf9ff716ad399 100644 --- a/Lib/test/test_asyncio/test_eager_task_factory.py +++ b/Lib/test/test_asyncio/test_eager_task_factory.py @@ -13,7 +13,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class EagerTaskFactoryLoopTests: diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 2ab638dc527aec..50df1b6ff9e09f 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -36,10 +36,10 @@ from test.support import socket_helper from test.support import threading_helper from test.support import ALWAYS_EQ, LARGEST, SMALLEST - +from test.support import warnings_helper def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) def broken_unix_getsockname(): @@ -2764,13 +2764,17 @@ def test_get_event_loop_policy(self): self.assertIs(policy, asyncio.get_event_loop_policy()) def test_set_event_loop_policy(self): - self.assertRaises( - TypeError, asyncio.set_event_loop_policy, object()) + with self.assertWarnsRegex( + DeprecationWarning, "'set_event_loop_policy' is deprecated"): + self.assertRaises( + TypeError, asyncio.set_event_loop_policy, object()) old_policy = asyncio.get_event_loop_policy() policy = asyncio.DefaultEventLoopPolicy() - asyncio.set_event_loop_policy(policy) + with self.assertWarnsRegex( + DeprecationWarning, "'set_event_loop_policy' is deprecated"): + asyncio.set_event_loop_policy(policy) self.assertIs(policy, asyncio.get_event_loop_policy()) self.assertIsNot(policy, old_policy) @@ -2857,7 +2861,7 @@ def get_event_loop(self): old_policy = asyncio.get_event_loop_policy() try: - asyncio.set_event_loop_policy(Policy()) + asyncio._set_event_loop_policy(Policy()) loop = asyncio.new_event_loop() with self.assertRaises(TestError): @@ -2885,7 +2889,7 @@ async def func(): asyncio.get_event_loop() finally: - asyncio.set_event_loop_policy(old_policy) + asyncio._set_event_loop_policy(old_policy) if loop is not None: loop.close() @@ -2897,7 +2901,7 @@ async def func(): def test_get_event_loop_returns_running_loop2(self): old_policy = asyncio.get_event_loop_policy() try: - asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy()) + asyncio._set_event_loop_policy(asyncio.DefaultEventLoopPolicy()) loop = asyncio.new_event_loop() self.addCleanup(loop.close) @@ -2923,7 +2927,7 @@ async def func(): asyncio.get_event_loop() finally: - asyncio.set_event_loop_policy(old_policy) + asyncio._set_event_loop_policy(old_policy) if loop is not None: loop.close() diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 3a4291e3a68ca6..7db70a4c81d483 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -17,7 +17,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) def _fakefunc(f): diff --git a/Lib/test/test_asyncio/test_futures2.py b/Lib/test/test_asyncio/test_futures2.py index b7cfffb76bd8f1..e2cddea01ecd93 100644 --- a/Lib/test/test_asyncio/test_futures2.py +++ b/Lib/test/test_asyncio/test_futures2.py @@ -7,7 +7,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class FutureTests: diff --git a/Lib/test/test_asyncio/test_locks.py b/Lib/test/test_asyncio/test_locks.py index c3bff760f7307e..aabfcd418829b2 100644 --- a/Lib/test/test_asyncio/test_locks.py +++ b/Lib/test/test_asyncio/test_locks.py @@ -20,7 +20,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class LockTests(unittest.IsolatedAsyncioTestCase): diff --git a/Lib/test/test_asyncio/test_pep492.py b/Lib/test/test_asyncio/test_pep492.py index 84c5f99129585b..48f4a75e0fd56c 100644 --- a/Lib/test/test_asyncio/test_pep492.py +++ b/Lib/test/test_asyncio/test_pep492.py @@ -11,7 +11,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) # Test that asyncio.iscoroutine() uses collections.abc.Coroutine diff --git a/Lib/test/test_asyncio/test_proactor_events.py b/Lib/test/test_asyncio/test_proactor_events.py index 4b3d551dd7b3a2..24c4e8546b17aa 100644 --- a/Lib/test/test_asyncio/test_proactor_events.py +++ b/Lib/test/test_asyncio/test_proactor_events.py @@ -18,7 +18,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) def close_transport(transport): diff --git a/Lib/test/test_asyncio/test_protocols.py b/Lib/test/test_asyncio/test_protocols.py index 0f232631867db5..a8627b5b5b87f2 100644 --- a/Lib/test/test_asyncio/test_protocols.py +++ b/Lib/test/test_asyncio/test_protocols.py @@ -7,7 +7,7 @@ def tearDownModule(): # not needed for the test file but added for uniformness with all other # asyncio test files for the sake of unified cleanup - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class ProtocolsAbsTests(unittest.TestCase): diff --git a/Lib/test/test_asyncio/test_queues.py b/Lib/test/test_asyncio/test_queues.py index 5019e9a293525d..1a8d604faea1fd 100644 --- a/Lib/test/test_asyncio/test_queues.py +++ b/Lib/test/test_asyncio/test_queues.py @@ -6,7 +6,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class QueueBasicTests(unittest.IsolatedAsyncioTestCase): diff --git a/Lib/test/test_asyncio/test_runners.py b/Lib/test/test_asyncio/test_runners.py index 45f70d09a2083a..f9afccc937f1de 100644 --- a/Lib/test/test_asyncio/test_runners.py +++ b/Lib/test/test_asyncio/test_runners.py @@ -12,7 +12,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) def interrupt_self(): @@ -61,7 +61,7 @@ def setUp(self): super().setUp() policy = TestPolicy(self.new_loop) - asyncio.set_event_loop_policy(policy) + asyncio._set_event_loop_policy(policy) def tearDown(self): policy = asyncio.get_event_loop_policy() @@ -69,7 +69,7 @@ def tearDown(self): self.assertTrue(policy.loop.is_closed()) self.assertTrue(policy.loop.shutdown_ag_run) - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) super().tearDown() @@ -259,7 +259,7 @@ def new_event_loop(): loop.set_task_factory(Task) return loop - asyncio.set_event_loop_policy(TestPolicy(new_event_loop)) + asyncio._set_event_loop_policy(TestPolicy(new_event_loop)) with self.assertRaises(asyncio.CancelledError): asyncio.run(main()) diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py index efca30f37414f9..f984dc96415ba3 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -24,7 +24,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class TestBaseSelectorEventLoop(BaseSelectorEventLoop): diff --git a/Lib/test/test_asyncio/test_sendfile.py b/Lib/test/test_asyncio/test_sendfile.py index 2509d4382cdebd..e1b766d06cbe1e 100644 --- a/Lib/test/test_asyncio/test_sendfile.py +++ b/Lib/test/test_asyncio/test_sendfile.py @@ -22,7 +22,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class MySendfileProto(asyncio.Protocol): diff --git a/Lib/test/test_asyncio/test_server.py b/Lib/test/test_asyncio/test_server.py index 60a40cc8349fed..32211f4cba32cb 100644 --- a/Lib/test/test_asyncio/test_server.py +++ b/Lib/test/test_asyncio/test_server.py @@ -11,7 +11,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class BaseStartServer(func_tests.FunctionalTestCaseMixin): diff --git a/Lib/test/test_asyncio/test_sock_lowlevel.py b/Lib/test/test_asyncio/test_sock_lowlevel.py index acef24a703ba38..5b1e5143820cad 100644 --- a/Lib/test/test_asyncio/test_sock_lowlevel.py +++ b/Lib/test/test_asyncio/test_sock_lowlevel.py @@ -15,7 +15,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class MyProto(asyncio.Protocol): diff --git a/Lib/test/test_asyncio/test_ssl.py b/Lib/test/test_asyncio/test_ssl.py index e072ede29ee3c7..125a6c35793c44 100644 --- a/Lib/test/test_asyncio/test_ssl.py +++ b/Lib/test/test_asyncio/test_ssl.py @@ -29,7 +29,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class MyBaseProto(asyncio.Protocol): diff --git a/Lib/test/test_asyncio/test_sslproto.py b/Lib/test/test_asyncio/test_sslproto.py index 761904c5146b6a..aa248c5786f634 100644 --- a/Lib/test/test_asyncio/test_sslproto.py +++ b/Lib/test/test_asyncio/test_sslproto.py @@ -21,7 +21,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) @unittest.skipIf(ssl is None, 'No ssl module') diff --git a/Lib/test/test_asyncio/test_staggered.py b/Lib/test/test_asyncio/test_staggered.py index 74941f704c4890..3c81b629693596 100644 --- a/Lib/test/test_asyncio/test_staggered.py +++ b/Lib/test/test_asyncio/test_staggered.py @@ -8,7 +8,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class StaggeredTests(unittest.IsolatedAsyncioTestCase): diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index dbe5646c2b7c08..c3ba90b309e49f 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -21,7 +21,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class StreamTests(test_utils.TestCase): diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index ec748b9bb3e357..467e964b26c824 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -37,7 +37,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class TestSubprocessTransport(base_subprocess.BaseSubprocessTransport): diff --git a/Lib/test/test_asyncio/test_taskgroups.py b/Lib/test/test_asyncio/test_taskgroups.py index 1b4de96a572fb9..c47bf4ec9ed64b 100644 --- a/Lib/test/test_asyncio/test_taskgroups.py +++ b/Lib/test/test_asyncio/test_taskgroups.py @@ -14,7 +14,7 @@ # To prevent a warning "test altered the execution environment" def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class MyExc(Exception): diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 9d2d356631b42c..5b8979a8bbd13a 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -24,7 +24,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) async def coroutine_function(): diff --git a/Lib/test/test_asyncio/test_threads.py b/Lib/test/test_asyncio/test_threads.py index 774380270a7d70..c98c9a9b395ff9 100644 --- a/Lib/test/test_asyncio/test_threads.py +++ b/Lib/test/test_asyncio/test_threads.py @@ -8,7 +8,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class ToThreadTests(unittest.IsolatedAsyncioTestCase): diff --git a/Lib/test/test_asyncio/test_timeouts.py b/Lib/test/test_asyncio/test_timeouts.py index f5543e191d07ff..3ba84d63b2ca5f 100644 --- a/Lib/test/test_asyncio/test_timeouts.py +++ b/Lib/test/test_asyncio/test_timeouts.py @@ -9,7 +9,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class TimeoutTests(unittest.IsolatedAsyncioTestCase): diff --git a/Lib/test/test_asyncio/test_transports.py b/Lib/test/test_asyncio/test_transports.py index bbdb218efaa3b6..af10d3dc2a80df 100644 --- a/Lib/test/test_asyncio/test_transports.py +++ b/Lib/test/test_asyncio/test_transports.py @@ -10,7 +10,7 @@ def tearDownModule(): # not needed for the test file but added for uniformness with all other # asyncio test files for the sake of unified cleanup - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class TransportTests(unittest.TestCase): diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index 021f45478d6f48..e9ee9702248015 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -33,7 +33,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) MOCK_ANY = mock.ANY diff --git a/Lib/test/test_asyncio/test_waitfor.py b/Lib/test/test_asyncio/test_waitfor.py index 11a8eeeab37634..d083f6b4d2a535 100644 --- a/Lib/test/test_asyncio/test_waitfor.py +++ b/Lib/test/test_asyncio/test_waitfor.py @@ -5,7 +5,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) # The following value can be used as a very small timeout: diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index 0c128c599ba011..26ca5f905f752f 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -19,7 +19,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class UpperProto(asyncio.Protocol): @@ -334,11 +334,11 @@ async def main(): old_policy = asyncio.get_event_loop_policy() try: - asyncio.set_event_loop_policy( + asyncio._set_event_loop_policy( asyncio.WindowsSelectorEventLoopPolicy()) asyncio.run(main()) finally: - asyncio.set_event_loop_policy(old_policy) + asyncio._set_event_loop_policy(old_policy) def test_proactor_win_policy(self): async def main(): @@ -348,11 +348,11 @@ async def main(): old_policy = asyncio.get_event_loop_policy() try: - asyncio.set_event_loop_policy( + asyncio._set_event_loop_policy( asyncio.WindowsProactorEventLoopPolicy()) asyncio.run(main()) finally: - asyncio.set_event_loop_policy(old_policy) + asyncio._set_event_loop_policy(old_policy) if __name__ == '__main__': diff --git a/Lib/test/test_asyncio/test_windows_utils.py b/Lib/test/test_asyncio/test_windows_utils.py index eafa5be3829682..be70720707cea7 100644 --- a/Lib/test/test_asyncio/test_windows_utils.py +++ b/Lib/test/test_asyncio/test_windows_utils.py @@ -16,7 +16,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class PipeTests(unittest.TestCase): diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 06df217881a52f..a92edad86839e6 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -493,7 +493,7 @@ async def arange(n): asyncio.run(eval(co, globals_)) self.assertEqual(globals_['a'], 1) finally: - asyncio.set_event_loop_policy(policy) + asyncio._set_event_loop_policy(policy) def test_compile_top_level_await_invalid_cases(self): # helper function just to check we can run top=level async-for @@ -530,7 +530,7 @@ async def arange(n): mode, flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT) finally: - asyncio.set_event_loop_policy(policy) + asyncio._set_event_loop_policy(policy) def test_compile_async_generator(self): diff --git a/Lib/test/test_concurrent_futures/test_interpreter_pool.py b/Lib/test/test_concurrent_futures/test_interpreter_pool.py index ea1512fc830d0c..93eec08bfe10d5 100644 --- a/Lib/test/test_concurrent_futures/test_interpreter_pool.py +++ b/Lib/test/test_concurrent_futures/test_interpreter_pool.py @@ -311,7 +311,7 @@ def setUpClass(cls): # tests left a policy in place, just in case. policy = support.maybe_get_event_loop_policy() assert policy is None, policy - cls.addClassCleanup(lambda: asyncio.set_event_loop_policy(None)) + cls.addClassCleanup(lambda: asyncio._set_event_loop_policy(None)) def setUp(self): super().setUp() diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py index ca7315783b9674..d8ee5757b12c15 100644 --- a/Lib/test/test_contextlib_async.py +++ b/Lib/test/test_contextlib_async.py @@ -11,7 +11,7 @@ support.requires_working_socket(module=True) def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class TestAbstractAsyncContextManager(unittest.IsolatedAsyncioTestCase): diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index e6d65e7d90abb1..a72c43f9b47947 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -2294,7 +2294,7 @@ async def f(): pass finally: loop.close() - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) self.assertEqual(buffer, [1, 2, 'MyException']) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 1ecf18bf49fa7e..2c950e46b3ed8a 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -75,7 +75,7 @@ def revise(filename, *args): def tearDownModule(): if support.has_socket_support: - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) def signatures_with_lexicographic_keyword_only_parameters(): diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 671b4c57a809aa..44c854f02a73c6 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -5352,7 +5352,7 @@ def test_taskName_with_asyncio_imported(self): logging.logAsyncioTasks = False runner.run(make_record(self.assertIsNone)) finally: - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) @support.requires_working_socket() def test_taskName_without_asyncio_imported(self): @@ -5364,7 +5364,7 @@ def test_taskName_without_asyncio_imported(self): logging.logAsyncioTasks = False runner.run(make_record(self.assertIsNone)) finally: - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class BasicConfigTest(unittest.TestCase): @@ -5668,7 +5668,7 @@ async def log_record(): data = f.read().strip() self.assertRegex(data, r'Task-\d+ - hello world') finally: - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) if handler: handler.close() diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index b0e686cb754b93..d688a225538c11 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -105,7 +105,7 @@ def create_file(filename, content=b'content'): def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class MiscTests(unittest.TestCase): diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 48a4c568651879..58295cff84310f 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -2132,7 +2132,7 @@ def test_pdb_next_command_for_asyncgen(): ... loop = asyncio.new_event_loop() ... loop.run_until_complete(test_main()) ... loop.close() - ... asyncio.set_event_loop_policy(None) + ... asyncio._set_event_loop_policy(None) ... print("finished") >>> with PdbTestInput(['step', @@ -2253,7 +2253,7 @@ def test_pdb_return_command_for_coroutine(): ... loop = asyncio.new_event_loop() ... loop.run_until_complete(test_main()) ... loop.close() - ... asyncio.set_event_loop_policy(None) + ... asyncio._set_event_loop_policy(None) ... print("finished") >>> with PdbTestInput(['step', @@ -2353,7 +2353,7 @@ def test_pdb_until_command_for_coroutine(): ... loop = asyncio.new_event_loop() ... loop.run_until_complete(test_main()) ... loop.close() - ... asyncio.set_event_loop_policy(None) + ... asyncio._set_event_loop_policy(None) ... print("finished") >>> with PdbTestInput(['step', diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index 95cf0d1ec2d9ab..e5cf88177f7131 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -2070,7 +2070,7 @@ def run_async_test(self, func, jumpFrom, jumpTo, expected, error=None, asyncio.run(func(output)) sys.settrace(None) - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) self.compare_jump_output(expected, output) def jump_test(jumpFrom, jumpTo, expected, error=None, event='line', warning=None): diff --git a/Lib/test/test_unittest/test_async_case.py b/Lib/test/test_unittest/test_async_case.py index 8ea244bff05c5f..664ca5efe57f84 100644 --- a/Lib/test/test_unittest/test_async_case.py +++ b/Lib/test/test_unittest/test_async_case.py @@ -12,7 +12,7 @@ class MyException(Exception): def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class TestCM: @@ -490,7 +490,7 @@ async def test_demo1(self): self.assertTrue(result.wasSuccessful()) def test_loop_factory(self): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class TestCase1(unittest.IsolatedAsyncioTestCase): loop_factory = asyncio.EventLoop diff --git a/Lib/test/test_unittest/testmock/testasync.py b/Lib/test/test_unittest/testmock/testasync.py index 73f04291373f91..afc9d1f11da1e2 100644 --- a/Lib/test/test_unittest/testmock/testasync.py +++ b/Lib/test/test_unittest/testmock/testasync.py @@ -15,7 +15,7 @@ def tearDownModule(): - asyncio.set_event_loop_policy(None) + asyncio._set_event_loop_policy(None) class AsyncClass: From 329165639f9ac00ba64f6493dbcafcef6955e2cb Mon Sep 17 00:00:00 2001 From: aeiouaeiouaeiouaeiouaeiouaeiou Date: Wed, 18 Dec 2024 09:14:16 +0300 Subject: [PATCH 050/376] gh-127897: fix HACL* build on macOS/Catalina (GH-127932) gh-127897: Update HACL* module from upstream sources to get: - Lib_Memzero0.c: don't use memset_s() on macOS <10.9 - Use _mm_malloc() for KRML_ALIGNED_MALLOC on macOS <10.15 - Add LEGACY_MACOS macros, use _mm_free() for KRML_ALIGNED_FREE on macOS <10.15 --- Misc/sbom.spdx.json | 8 ++++---- Modules/_hacl/Lib_Memzero0.c | 6 +++++- Modules/_hacl/include/krml/internal/target.h | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Misc/sbom.spdx.json b/Misc/sbom.spdx.json index 739e005646ba97..b4d785f65639a5 100644 --- a/Misc/sbom.spdx.json +++ b/Misc/sbom.spdx.json @@ -566,11 +566,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "2e08072c0c57dac02b67f3f71d77068c537ac02e" + "checksumValue": "118dc712780ea680affa8d9794470440eb87ff10" }, { "algorithm": "SHA256", - "checksumValue": "e69fd3e84f77873ecb414f5300761b686321d01f5710ccf2517765236b08fc25" + "checksumValue": "b017e7d5662a308c938cf4e4b919680c8f3e27f42975ca152b62fe65c5f7fb0c" } ], "fileName": "Modules/_hacl/Lib_Memzero0.c" @@ -622,11 +622,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "9881567f43deb32bae77a84b2d349858a24b6685" + "checksumValue": "9c5cac1582dcd6e0d0a4142e6e8b285b4cb7d9e6" }, { "algorithm": "SHA256", - "checksumValue": "3382156e32fcb376009177d3d2dc9712ff7c8c02afb97b3e16d98b41a2114f84" + "checksumValue": "b1e32138ac8c262e872f7da43ec80c1e54c08bcbdec4b7be17117aa25807f87e" } ], "fileName": "Modules/_hacl/include/krml/internal/target.h" diff --git a/Modules/_hacl/Lib_Memzero0.c b/Modules/_hacl/Lib_Memzero0.c index 5c269d231de82f..f01568a138648f 100644 --- a/Modules/_hacl/Lib_Memzero0.c +++ b/Modules/_hacl/Lib_Memzero0.c @@ -8,6 +8,10 @@ #include #endif +#if defined(__APPLE__) && defined(__MACH__) +#include +#endif + #if (defined(__APPLE__) && defined(__MACH__)) || defined(__linux__) #define __STDC_WANT_LIB_EXT1__ 1 #include @@ -37,7 +41,7 @@ void Lib_Memzero0_memzero0(void *dst, uint64_t len) { #ifdef _WIN32 SecureZeroMemory(dst, len_); - #elif defined(__APPLE__) && defined(__MACH__) + #elif defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_MIN_REQUIRED) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) memset_s(dst, len_, 0, len_); #elif (defined(__linux__) && !defined(LINUX_NO_EXPLICIT_BZERO)) || defined(__FreeBSD__) explicit_bzero(dst, len_); diff --git a/Modules/_hacl/include/krml/internal/target.h b/Modules/_hacl/include/krml/internal/target.h index fd74d3da684567..9b403c36ceca19 100644 --- a/Modules/_hacl/include/krml/internal/target.h +++ b/Modules/_hacl/include/krml/internal/target.h @@ -19,6 +19,20 @@ # define inline __inline__ #endif +/* There is no support for aligned_alloc() in macOS before Catalina, so + * let's make a macro to use _mm_malloc() and _mm_free() functions + * from mm_malloc.h. */ +#if defined(__APPLE__) && defined(__MACH__) +# include +# if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED < 101500) +# include +# define LEGACY_MACOS +# else +# undef LEGACY_MACOS +#endif +#endif + /******************************************************************************/ /* Macros that KaRaMeL will generate. */ /******************************************************************************/ @@ -133,6 +147,8 @@ defined(_MSC_VER) || \ (defined(__MINGW32__) && defined(__MINGW64_VERSION_MAJOR))) # define KRML_ALIGNED_MALLOC(X, Y) _aligned_malloc(Y, X) +# elif defined(LEGACY_MACOS) +# define KRML_ALIGNED_MALLOC(X, Y) _mm_malloc(Y, X) # else # define KRML_ALIGNED_MALLOC(X, Y) aligned_alloc(X, Y) # endif @@ -150,6 +166,8 @@ defined(_MSC_VER) || \ (defined(__MINGW32__) && defined(__MINGW64_VERSION_MAJOR))) # define KRML_ALIGNED_FREE(X) _aligned_free(X) +# elif defined(LEGACY_MACOS) +# define KRML_ALIGNED_FREE(X) _mm_free(X) # else # define KRML_ALIGNED_FREE(X) free(X) # endif From 0581e3f52b6116774698761088f0daa7ec23fc0b Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 18 Dec 2024 08:20:05 +0000 Subject: [PATCH 051/376] gh-127174: add docs for asyncio.get_event_loop replacements (#127640) Co-authored-by: Kumar Aditya --- Doc/whatsnew/3.14.rst | 90 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index d13cd2d5173a04..342456cbc397f3 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -780,6 +780,96 @@ asyncio It now raises a :exc:`RuntimeError` if there is no current event loop. (Contributed by Kumar Aditya in :gh:`126353`.) + There's a few patterns that use :func:`asyncio.get_event_loop`, most + of them can be replaced with :func:`asyncio.run`. + + If you're running an async function, simply use :func:`asyncio.run`. + + Before:: + + async def main(): + ... + + + loop = asyncio.get_event_loop() + try: + loop.run_until_complete(main()) + finally: + loop.close() + + After:: + + async def main(): + ... + + asyncio.run(main()) + + If you need to start something, e.g. a server listening on a socket + and then run forever, use :func:`asyncio.run` and an + :class:`asyncio.Event`. + + Before:: + + def start_server(loop): + ... + + loop = asyncio.get_event_loop() + try: + start_server(loop) + loop.run_forever() + finally: + loop.close() + + After:: + + def start_server(loop): + ... + + async def main(): + start_server(asyncio.get_running_loop()) + await asyncio.Event().wait() + + asyncio.run(main()) + + If you need to run something in an event loop, then run some blocking + code around it, use :class:`asyncio.Runner`. + + Before:: + + async def operation_one(): + ... + + def blocking_code(): + ... + + async def operation_two(): + ... + + loop = asyncio.get_event_loop() + try: + loop.run_until_complete(operation_one()) + blocking_code() + loop.run_until_complete(operation_two()) + finally: + loop.close() + + After:: + + async def operation_one(): + ... + + def blocking_code(): + ... + + async def operation_two(): + ... + + with asyncio.Runner() as runner: + runner.run(operation_one()) + blocking_code() + runner.run(operation_two()) + + collections.abc --------------- From 335e24fb0a5805ac1ecdbc01e0331b38fc33849d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 18 Dec 2024 09:25:42 +0100 Subject: [PATCH 052/376] gh-126742: Avoid checking for library filename in test_ctypes (#128034) Avoid checking for library filename in `dlerror()` error messages of test_ctypes. --- Lib/test/test_ctypes/test_dlerror.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_ctypes/test_dlerror.py b/Lib/test/test_ctypes/test_dlerror.py index c3c34d43481d36..6bf492399cbf95 100644 --- a/Lib/test/test_ctypes/test_dlerror.py +++ b/Lib/test/test_ctypes/test_dlerror.py @@ -133,24 +133,20 @@ def configure_locales(func): @classmethod def setUpClass(cls): cls.libc_filename = find_library("c") + if cls.libc_filename is None: + raise unittest.SkipTest('cannot find libc') @configure_locales def test_localized_error_from_dll(self): dll = CDLL(self.libc_filename) - with self.assertRaises(AttributeError) as cm: + with self.assertRaises(AttributeError): dll.this_name_does_not_exist - if sys.platform.startswith('linux'): - # On macOS, the filename is not reported by dlerror(). - self.assertIn(self.libc_filename, str(cm.exception)) @configure_locales def test_localized_error_in_dll(self): dll = CDLL(self.libc_filename) - with self.assertRaises(ValueError) as cm: + with self.assertRaises(ValueError): c_int.in_dll(dll, 'this_name_does_not_exist') - if sys.platform.startswith('linux'): - # On macOS, the filename is not reported by dlerror(). - self.assertIn(self.libc_filename, str(cm.exception)) @unittest.skipUnless(hasattr(_ctypes, 'dlopen'), 'test requires _ctypes.dlopen()') @@ -172,11 +168,8 @@ def test_localized_error_dlopen(self): @configure_locales def test_localized_error_dlsym(self): dll = _ctypes.dlopen(self.libc_filename) - with self.assertRaises(OSError) as cm: + with self.assertRaises(OSError): _ctypes.dlsym(dll, 'this_name_does_not_exist') - if sys.platform.startswith('linux'): - # On macOS, the filename is not reported by dlerror(). - self.assertIn(self.libc_filename, str(cm.exception)) if __name__ == "__main__": From 2610bccfdf55bc6519808f8e1b5db2cfb03ae809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 18 Dec 2024 09:59:37 +0100 Subject: [PATCH 053/376] gh-126742: add NEWS entry for fix of localized error messages (GH-128025) --- .../Library/2024-12-17-12-41-07.gh-issue-126742.l07qvT.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-12-17-12-41-07.gh-issue-126742.l07qvT.rst diff --git a/Misc/NEWS.d/next/Library/2024-12-17-12-41-07.gh-issue-126742.l07qvT.rst b/Misc/NEWS.d/next/Library/2024-12-17-12-41-07.gh-issue-126742.l07qvT.rst new file mode 100644 index 00000000000000..70f7cc129f66e3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-17-12-41-07.gh-issue-126742.l07qvT.rst @@ -0,0 +1,3 @@ +Fix support of localized error messages reported by :manpage:`dlerror(3)` and +:manpage:`gdbm_strerror ` in :mod:`ctypes` and :mod:`dbm.gnu` +functions respectively. Patch by Bénédikt Tran. From bad3cdefa840ff099e5e08cf88dcf6dfed7d37b8 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 18 Dec 2024 10:12:24 +0000 Subject: [PATCH 054/376] gh-126639: Add ResourceWarning to NamedTemporaryFile (#126677) Co-authored-by: Kumar Aditya --- Lib/tempfile.py | 26 ++++++++++++++++--- Lib/test/test_tempfile.py | 9 ++++--- ...-11-11-07-56-03.gh-issue-126639.AmVSt-.rst | 1 + 3 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-11-07-56-03.gh-issue-126639.AmVSt-.rst diff --git a/Lib/tempfile.py b/Lib/tempfile.py index b5a15f7b72c872..0eb9ddeb6ac377 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -437,11 +437,19 @@ class _TemporaryFileCloser: cleanup_called = False close_called = False - def __init__(self, file, name, delete=True, delete_on_close=True): + def __init__( + self, + file, + name, + delete=True, + delete_on_close=True, + warn_message="Implicitly cleaning up unknown file", + ): self.file = file self.name = name self.delete = delete self.delete_on_close = delete_on_close + self.warn_message = warn_message def cleanup(self, windows=(_os.name == 'nt'), unlink=_os.unlink): if not self.cleanup_called: @@ -469,7 +477,10 @@ def close(self): self.cleanup() def __del__(self): + close_called = self.close_called self.cleanup() + if not close_called: + _warnings.warn(self.warn_message, ResourceWarning) class _TemporaryFileWrapper: @@ -483,8 +494,17 @@ class _TemporaryFileWrapper: def __init__(self, file, name, delete=True, delete_on_close=True): self.file = file self.name = name - self._closer = _TemporaryFileCloser(file, name, delete, - delete_on_close) + self._closer = _TemporaryFileCloser( + file, + name, + delete, + delete_on_close, + warn_message=f"Implicitly cleaning up {self!r}", + ) + + def __repr__(self): + file = self.__dict__['file'] + return f"<{type(self).__name__} {file=}>" def __getattr__(self, name): # Attribute lookups are delegated to the underlying file diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index 57e9bd20c77ee1..7adc021d298254 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -1112,11 +1112,14 @@ def my_func(dir): # Testing extreme case, where the file is not explicitly closed # f.close() return tmp_name - # Make sure that the garbage collector has finalized the file object. - gc.collect() dir = tempfile.mkdtemp() try: - tmp_name = my_func(dir) + with self.assertWarnsRegex( + expected_warning=ResourceWarning, + expected_regex=r"Implicitly cleaning up <_TemporaryFileWrapper file=.*>", + ): + tmp_name = my_func(dir) + support.gc_collect() self.assertFalse(os.path.exists(tmp_name), f"NamedTemporaryFile {tmp_name!r} " f"exists after finalizer ") diff --git a/Misc/NEWS.d/next/Library/2024-11-11-07-56-03.gh-issue-126639.AmVSt-.rst b/Misc/NEWS.d/next/Library/2024-11-11-07-56-03.gh-issue-126639.AmVSt-.rst new file mode 100644 index 00000000000000..0b75e5858de731 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-11-07-56-03.gh-issue-126639.AmVSt-.rst @@ -0,0 +1 @@ +:class:`tempfile.NamedTemporaryFile` will now issue a :exc:`ResourceWarning` when it is finalized by the garbage collector without being explicitly closed. From dbd08fb60d1c434ebb6009d28d2b97f90e011032 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 18 Dec 2024 18:04:20 +0530 Subject: [PATCH 055/376] gh-127949: deprecate `asyncio.get_event_loop_policy` (#128053) This deprecates `asyncio.get_event_loop_policy` and will be removed in Python 3.16. --- Doc/library/asyncio-policy.rst | 4 +++ Lib/asyncio/events.py | 14 ++++++---- Lib/test/test_asyncio/test_events.py | 29 ++++++++++++-------- Lib/test/test_asyncio/test_runners.py | 6 ++-- Lib/test/test_asyncio/test_subprocess.py | 3 +- Lib/test/test_asyncio/test_windows_events.py | 4 +-- Lib/test/test_contextlib_async.py | 2 +- Lib/test/test_unittest/test_async_case.py | 2 +- Modules/_asynciomodule.c | 2 +- 9 files changed, 40 insertions(+), 26 deletions(-) diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst index e8e470c1b343fd..2d05c3a9f7f157 100644 --- a/Doc/library/asyncio-policy.rst +++ b/Doc/library/asyncio-policy.rst @@ -40,6 +40,10 @@ for the current process: Return the current process-wide policy. + .. deprecated:: next + The :func:`get_event_loop_policy` function is deprecated and + will be removed in Python 3.16. + .. function:: set_event_loop_policy(policy) Set the current process-wide policy to *policy*. diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index 0926cfe2323478..1449245edc7c7e 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -8,6 +8,7 @@ 'AbstractEventLoopPolicy', 'AbstractEventLoop', 'AbstractServer', 'Handle', 'TimerHandle', + '_get_event_loop_policy', 'get_event_loop_policy', '_set_event_loop_policy', 'set_event_loop_policy', @@ -761,12 +762,15 @@ def _init_event_loop_policy(): _event_loop_policy = DefaultEventLoopPolicy() -def get_event_loop_policy(): +def _get_event_loop_policy(): """Get the current event loop policy.""" if _event_loop_policy is None: _init_event_loop_policy() return _event_loop_policy +def get_event_loop_policy(): + warnings._deprecated('asyncio.get_event_loop_policy', remove=(3, 16)) + return _get_event_loop_policy() def _set_event_loop_policy(policy): """Set the current event loop policy. @@ -778,7 +782,7 @@ def _set_event_loop_policy(policy): _event_loop_policy = policy def set_event_loop_policy(policy): - warnings._deprecated('set_event_loop_policy', remove=(3,16)) + warnings._deprecated('asyncio.set_event_loop_policy', remove=(3,16)) _set_event_loop_policy(policy) def get_event_loop(): @@ -794,17 +798,17 @@ def get_event_loop(): current_loop = _get_running_loop() if current_loop is not None: return current_loop - return get_event_loop_policy().get_event_loop() + return _get_event_loop_policy().get_event_loop() def set_event_loop(loop): """Equivalent to calling get_event_loop_policy().set_event_loop(loop).""" - get_event_loop_policy().set_event_loop(loop) + _get_event_loop_policy().set_event_loop(loop) def new_event_loop(): """Equivalent to calling get_event_loop_policy().new_event_loop().""" - return get_event_loop_policy().new_event_loop() + return _get_event_loop_policy().new_event_loop() # Alias pure-Python implementations for testing purposes. diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 50df1b6ff9e09f..d43f66c13d2f96 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -2397,7 +2397,7 @@ def test_handle_repr_debug(self): self.assertRegex(repr(h), regex) def test_handle_source_traceback(self): - loop = asyncio.get_event_loop_policy().new_event_loop() + loop = asyncio.new_event_loop() loop.set_debug(True) self.set_event_loop(loop) @@ -2759,24 +2759,31 @@ def test_set_event_loop(self): old_loop.close() def test_get_event_loop_policy(self): - policy = asyncio.get_event_loop_policy() - self.assertIsInstance(policy, asyncio.AbstractEventLoopPolicy) - self.assertIs(policy, asyncio.get_event_loop_policy()) + with self.assertWarnsRegex( + DeprecationWarning, "'asyncio.get_event_loop_policy' is deprecated"): + policy = asyncio.get_event_loop_policy() + self.assertIsInstance(policy, asyncio.AbstractEventLoopPolicy) + self.assertIs(policy, asyncio.get_event_loop_policy()) def test_set_event_loop_policy(self): with self.assertWarnsRegex( - DeprecationWarning, "'set_event_loop_policy' is deprecated"): + DeprecationWarning, "'asyncio.set_event_loop_policy' is deprecated"): self.assertRaises( TypeError, asyncio.set_event_loop_policy, object()) - old_policy = asyncio.get_event_loop_policy() + with self.assertWarnsRegex( + DeprecationWarning, "'asyncio.get_event_loop_policy' is deprecated"): + old_policy = asyncio.get_event_loop_policy() policy = asyncio.DefaultEventLoopPolicy() with self.assertWarnsRegex( - DeprecationWarning, "'set_event_loop_policy' is deprecated"): + DeprecationWarning, "'asyncio.set_event_loop_policy' is deprecated"): asyncio.set_event_loop_policy(policy) - self.assertIs(policy, asyncio.get_event_loop_policy()) - self.assertIsNot(policy, old_policy) + + with self.assertWarnsRegex( + DeprecationWarning, "'asyncio.get_event_loop_policy' is deprecated"): + self.assertIs(policy, asyncio.get_event_loop_policy()) + self.assertIsNot(policy, old_policy) class GetEventLoopTestsMixin: @@ -2859,7 +2866,7 @@ class Policy(asyncio.DefaultEventLoopPolicy): def get_event_loop(self): raise TestError - old_policy = asyncio.get_event_loop_policy() + old_policy = asyncio._get_event_loop_policy() try: asyncio._set_event_loop_policy(Policy()) loop = asyncio.new_event_loop() @@ -2899,7 +2906,7 @@ async def func(): self.assertIs(asyncio._get_running_loop(), None) def test_get_event_loop_returns_running_loop2(self): - old_policy = asyncio.get_event_loop_policy() + old_policy = asyncio._get_event_loop_policy() try: asyncio._set_event_loop_policy(asyncio.DefaultEventLoopPolicy()) loop = asyncio.new_event_loop() diff --git a/Lib/test/test_asyncio/test_runners.py b/Lib/test/test_asyncio/test_runners.py index f9afccc937f1de..e1f82f7f7bec0c 100644 --- a/Lib/test/test_asyncio/test_runners.py +++ b/Lib/test/test_asyncio/test_runners.py @@ -64,7 +64,7 @@ def setUp(self): asyncio._set_event_loop_policy(policy) def tearDown(self): - policy = asyncio.get_event_loop_policy() + policy = asyncio._get_event_loop_policy() if policy.loop is not None: self.assertTrue(policy.loop.is_closed()) self.assertTrue(policy.loop.shutdown_ag_run) @@ -208,7 +208,7 @@ async def main(): await asyncio.sleep(0) return 42 - policy = asyncio.get_event_loop_policy() + policy = asyncio._get_event_loop_policy() policy.set_event_loop = mock.Mock() asyncio.run(main()) self.assertTrue(policy.set_event_loop.called) @@ -495,7 +495,7 @@ def test_set_event_loop_called_once(self): async def coro(): pass - policy = asyncio.get_event_loop_policy() + policy = asyncio._get_event_loop_policy() policy.set_event_loop = mock.Mock() runner = asyncio.Runner() runner.run(coro()) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 467e964b26c824..57decaf2d277fb 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -886,8 +886,7 @@ class SubprocessWatcherMixin(SubprocessMixin): def setUp(self): super().setUp() - policy = asyncio.get_event_loop_policy() - self.loop = policy.new_event_loop() + self.loop = asyncio.new_event_loop() self.set_event_loop(self.loop) def test_watcher_implementation(self): diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index 26ca5f905f752f..28b05d24dc25a1 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -332,7 +332,7 @@ async def main(): asyncio.get_running_loop(), asyncio.SelectorEventLoop) - old_policy = asyncio.get_event_loop_policy() + old_policy = asyncio._get_event_loop_policy() try: asyncio._set_event_loop_policy( asyncio.WindowsSelectorEventLoopPolicy()) @@ -346,7 +346,7 @@ async def main(): asyncio.get_running_loop(), asyncio.ProactorEventLoop) - old_policy = asyncio.get_event_loop_policy() + old_policy = asyncio._get_event_loop_policy() try: asyncio._set_event_loop_policy( asyncio.WindowsProactorEventLoopPolicy()) diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py index d8ee5757b12c15..88dcdadd5e027d 100644 --- a/Lib/test/test_contextlib_async.py +++ b/Lib/test/test_contextlib_async.py @@ -496,7 +496,7 @@ class TestAsyncExitStack(TestBaseExitStack, unittest.IsolatedAsyncioTestCase): class SyncAsyncExitStack(AsyncExitStack): @staticmethod def run_coroutine(coro): - loop = asyncio.get_event_loop_policy().get_event_loop() + loop = asyncio.new_event_loop() t = loop.create_task(coro) t.add_done_callback(lambda f: loop.stop()) loop.run_forever() diff --git a/Lib/test/test_unittest/test_async_case.py b/Lib/test/test_unittest/test_async_case.py index 664ca5efe57f84..993e6bf013cfbf 100644 --- a/Lib/test/test_unittest/test_async_case.py +++ b/Lib/test/test_unittest/test_async_case.py @@ -480,7 +480,7 @@ def test_setup_get_event_loop(self): class TestCase1(unittest.IsolatedAsyncioTestCase): def setUp(self): - asyncio.get_event_loop_policy().get_event_loop() + asyncio._get_event_loop_policy().get_event_loop() async def test_demo1(self): pass diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index f883125a2c70b2..13dd2fd55fd454 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -3773,7 +3773,7 @@ module_init(asyncio_state *state) } WITH_MOD("asyncio.events") - GET_MOD_ATTR(state->asyncio_get_event_loop_policy, "get_event_loop_policy") + GET_MOD_ATTR(state->asyncio_get_event_loop_policy, "_get_event_loop_policy") WITH_MOD("asyncio.base_futures") GET_MOD_ATTR(state->asyncio_future_repr_func, "_future_repr") From 8a433b683fecafe1cb04469a301df2b4618167d0 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 18 Dec 2024 19:55:03 +0530 Subject: [PATCH 056/376] gh-121621: clear running loop early in asyncio (#128004) --- Modules/_asynciomodule.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 13dd2fd55fd454..27c16364457336 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -3723,6 +3723,11 @@ module_clear(PyObject *mod) Py_CLEAR(state->iscoroutine_typecache); Py_CLEAR(state->context_kwname); + // Clear the ref to running loop so that finalizers can run early. + // If there are other running loops in different threads, + // those get cleared in PyThreadState_Clear. + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); + Py_CLEAR(ts->asyncio_running_loop); return 0; } From 91c55085a959016250f1877e147ef379bb97dd12 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 18 Dec 2024 20:49:00 +0530 Subject: [PATCH 057/376] gh-128033: change `PyMutex_LockFast` to take `PyMutex` as argument (#128054) Change `PyMutex_LockFast` to take `PyMutex` as argument. --- Include/internal/pycore_critical_section.h | 6 +++--- Include/internal/pycore_lock.h | 3 ++- Python/ceval_macros.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Include/internal/pycore_critical_section.h b/Include/internal/pycore_critical_section.h index 78cd0d54972660..9ba2fce56d3c9c 100644 --- a/Include/internal/pycore_critical_section.h +++ b/Include/internal/pycore_critical_section.h @@ -109,7 +109,7 @@ _PyCriticalSection_IsActive(uintptr_t tag) static inline void _PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *m) { - if (PyMutex_LockFast(&m->_bits)) { + if (PyMutex_LockFast(m)) { PyThreadState *tstate = _PyThreadState_GET(); c->_cs_mutex = m; c->_cs_prev = tstate->critical_section; @@ -170,8 +170,8 @@ _PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2) m2 = tmp; } - if (PyMutex_LockFast(&m1->_bits)) { - if (PyMutex_LockFast(&m2->_bits)) { + if (PyMutex_LockFast(m1)) { + if (PyMutex_LockFast(m2)) { PyThreadState *tstate = _PyThreadState_GET(); c->_cs_base._cs_mutex = m1; c->_cs_mutex2 = m2; diff --git a/Include/internal/pycore_lock.h b/Include/internal/pycore_lock.h index 57cbce8f126aca..8bcb23a6ce9f9d 100644 --- a/Include/internal/pycore_lock.h +++ b/Include/internal/pycore_lock.h @@ -18,9 +18,10 @@ extern "C" { #define _Py_ONCE_INITIALIZED 4 static inline int -PyMutex_LockFast(uint8_t *lock_bits) +PyMutex_LockFast(PyMutex *m) { uint8_t expected = _Py_UNLOCKED; + uint8_t *lock_bits = &m->_bits; return _Py_atomic_compare_exchange_uint8(lock_bits, &expected, _Py_LOCKED); } diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 9250b86e42ced1..398816d5f36a1d 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -300,7 +300,7 @@ GETITEM(PyObject *v, Py_ssize_t i) { // avoid any potentially escaping calls (like PyStackRef_CLOSE) while the // object is locked. #ifdef Py_GIL_DISABLED -# define LOCK_OBJECT(op) PyMutex_LockFast(&(_PyObject_CAST(op))->ob_mutex._bits) +# define LOCK_OBJECT(op) PyMutex_LockFast(&(_PyObject_CAST(op))->ob_mutex) # define UNLOCK_OBJECT(op) PyMutex_Unlock(&(_PyObject_CAST(op))->ob_mutex) #else # define LOCK_OBJECT(op) (1) From f802c8bf872ab882d3056675acc79c950fe5b93c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 18 Dec 2024 16:34:31 +0100 Subject: [PATCH 058/376] gh-128013: Convert unicodeobject.c macros to functions (#128061) Convert unicodeobject.c macros to static inline functions. * Add _PyUnicode_SET_UTF8() and _PyUnicode_SET_UTF8_LENGTH() macros. * Add PyUnicode_HASH() and PyUnicode_SET_HASH() macros. * Remove unused _PyUnicode_KIND() and _PyUnicode_GET_LENGTH() macros. --- Objects/unicodeobject.c | 123 +++++++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 45 deletions(-) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index b7aeb06d32bcec..53be6f5b9017a3 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -112,20 +112,42 @@ NOTE: In the interpreter's initialization phase, some globals are currently # define _PyUnicode_CHECK(op) PyUnicode_Check(op) #endif -#define _PyUnicode_UTF8(op) \ - (_PyCompactUnicodeObject_CAST(op)->utf8) -#define PyUnicode_UTF8(op) \ - (assert(_PyUnicode_CHECK(op)), \ - PyUnicode_IS_COMPACT_ASCII(op) ? \ - ((char*)(_PyASCIIObject_CAST(op) + 1)) : \ - _PyUnicode_UTF8(op)) -#define _PyUnicode_UTF8_LENGTH(op) \ - (_PyCompactUnicodeObject_CAST(op)->utf8_length) -#define PyUnicode_UTF8_LENGTH(op) \ - (assert(_PyUnicode_CHECK(op)), \ - PyUnicode_IS_COMPACT_ASCII(op) ? \ - _PyASCIIObject_CAST(op)->length : \ - _PyUnicode_UTF8_LENGTH(op)) +static inline char* _PyUnicode_UTF8(PyObject *op) +{ + return (_PyCompactUnicodeObject_CAST(op)->utf8); +} + +static inline char* PyUnicode_UTF8(PyObject *op) +{ + assert(_PyUnicode_CHECK(op)); + if (PyUnicode_IS_COMPACT_ASCII(op)) { + return ((char*)(_PyASCIIObject_CAST(op) + 1)); + } + else { + return _PyUnicode_UTF8(op); + } +} + +static inline void PyUnicode_SET_UTF8(PyObject *op, char *utf8) +{ + _PyCompactUnicodeObject_CAST(op)->utf8 = utf8; +} + +static inline Py_ssize_t PyUnicode_UTF8_LENGTH(PyObject *op) +{ + assert(_PyUnicode_CHECK(op)); + if (PyUnicode_IS_COMPACT_ASCII(op)) { + return _PyASCIIObject_CAST(op)->length; + } + else { + return _PyCompactUnicodeObject_CAST(op)->utf8_length; + } +} + +static inline void PyUnicode_SET_UTF8_LENGTH(PyObject *op, Py_ssize_t length) +{ + _PyCompactUnicodeObject_CAST(op)->utf8_length = length; +} #define _PyUnicode_LENGTH(op) \ (_PyASCIIObject_CAST(op)->length) @@ -133,26 +155,37 @@ NOTE: In the interpreter's initialization phase, some globals are currently (_PyASCIIObject_CAST(op)->state) #define _PyUnicode_HASH(op) \ (_PyASCIIObject_CAST(op)->hash) -#define _PyUnicode_KIND(op) \ - (assert(_PyUnicode_CHECK(op)), \ - _PyASCIIObject_CAST(op)->state.kind) -#define _PyUnicode_GET_LENGTH(op) \ - (assert(_PyUnicode_CHECK(op)), \ - _PyASCIIObject_CAST(op)->length) + +static inline Py_hash_t PyUnicode_HASH(PyObject *op) +{ + assert(_PyUnicode_CHECK(op)); + return FT_ATOMIC_LOAD_SSIZE_RELAXED(_PyASCIIObject_CAST(op)->hash); +} + +static inline void PyUnicode_SET_HASH(PyObject *op, Py_hash_t hash) +{ + FT_ATOMIC_STORE_SSIZE_RELAXED(_PyASCIIObject_CAST(op)->hash, hash); +} + #define _PyUnicode_DATA_ANY(op) \ (_PyUnicodeObject_CAST(op)->data.any) -#define _PyUnicode_SHARE_UTF8(op) \ - (assert(_PyUnicode_CHECK(op)), \ - assert(!PyUnicode_IS_COMPACT_ASCII(op)), \ - (_PyUnicode_UTF8(op) == PyUnicode_DATA(op))) +static inline int _PyUnicode_SHARE_UTF8(PyObject *op) +{ + assert(_PyUnicode_CHECK(op)); + assert(!PyUnicode_IS_COMPACT_ASCII(op)); + return (_PyUnicode_UTF8(op) == PyUnicode_DATA(op)); +} /* true if the Unicode object has an allocated UTF-8 memory block (not shared with other data) */ -#define _PyUnicode_HAS_UTF8_MEMORY(op) \ - ((!PyUnicode_IS_COMPACT_ASCII(op) \ - && _PyUnicode_UTF8(op) \ - && _PyUnicode_UTF8(op) != PyUnicode_DATA(op))) +static inline int _PyUnicode_HAS_UTF8_MEMORY(PyObject *op) +{ + return (!PyUnicode_IS_COMPACT_ASCII(op) + && _PyUnicode_UTF8(op) != NULL + && _PyUnicode_UTF8(op) != PyUnicode_DATA(op)); +} + /* Generic helper macro to convert characters of different types. from_type and to_type have to be valid type names, begin and end @@ -1123,8 +1156,8 @@ resize_compact(PyObject *unicode, Py_ssize_t length) if (_PyUnicode_HAS_UTF8_MEMORY(unicode)) { PyMem_Free(_PyUnicode_UTF8(unicode)); - _PyUnicode_UTF8(unicode) = NULL; - _PyUnicode_UTF8_LENGTH(unicode) = 0; + PyUnicode_SET_UTF8(unicode, NULL); + PyUnicode_SET_UTF8_LENGTH(unicode, 0); } #ifdef Py_TRACE_REFS _Py_ForgetReference(unicode); @@ -1177,8 +1210,8 @@ resize_inplace(PyObject *unicode, Py_ssize_t length) if (!share_utf8 && _PyUnicode_HAS_UTF8_MEMORY(unicode)) { PyMem_Free(_PyUnicode_UTF8(unicode)); - _PyUnicode_UTF8(unicode) = NULL; - _PyUnicode_UTF8_LENGTH(unicode) = 0; + PyUnicode_SET_UTF8(unicode, NULL); + PyUnicode_SET_UTF8_LENGTH(unicode, 0); } data = (PyObject *)PyObject_Realloc(data, new_size); @@ -1188,8 +1221,8 @@ resize_inplace(PyObject *unicode, Py_ssize_t length) } _PyUnicode_DATA_ANY(unicode) = data; if (share_utf8) { - _PyUnicode_UTF8(unicode) = data; - _PyUnicode_UTF8_LENGTH(unicode) = length; + PyUnicode_SET_UTF8(unicode, data); + PyUnicode_SET_UTF8_LENGTH(unicode, length); } _PyUnicode_LENGTH(unicode) = length; PyUnicode_WRITE(PyUnicode_KIND(unicode), data, length, 0); @@ -1769,7 +1802,7 @@ unicode_modifiable(PyObject *unicode) assert(_PyUnicode_CHECK(unicode)); if (Py_REFCNT(unicode) != 1) return 0; - if (FT_ATOMIC_LOAD_SSIZE_RELAXED(_PyUnicode_HASH(unicode)) != -1) + if (PyUnicode_HASH(unicode) != -1) return 0; if (PyUnicode_CHECK_INTERNED(unicode)) return 0; @@ -5862,8 +5895,8 @@ unicode_fill_utf8(PyObject *unicode) PyErr_NoMemory(); return -1; } - _PyUnicode_UTF8(unicode) = cache; - _PyUnicode_UTF8_LENGTH(unicode) = len; + PyUnicode_SET_UTF8(unicode, cache); + PyUnicode_SET_UTF8_LENGTH(unicode, len); memcpy(cache, start, len); cache[len] = '\0'; _PyBytesWriter_Dealloc(&writer); @@ -11434,9 +11467,9 @@ _PyUnicode_EqualToASCIIId(PyObject *left, _Py_Identifier *right) return 0; } - Py_hash_t right_hash = FT_ATOMIC_LOAD_SSIZE_RELAXED(_PyUnicode_HASH(right_uni)); + Py_hash_t right_hash = PyUnicode_HASH(right_uni); assert(right_hash != -1); - Py_hash_t hash = FT_ATOMIC_LOAD_SSIZE_RELAXED(_PyUnicode_HASH(left)); + Py_hash_t hash = PyUnicode_HASH(left); if (hash != -1 && hash != right_hash) { return 0; } @@ -11916,14 +11949,14 @@ unicode_hash(PyObject *self) #ifdef Py_DEBUG assert(_Py_HashSecret_Initialized); #endif - Py_hash_t hash = FT_ATOMIC_LOAD_SSIZE_RELAXED(_PyUnicode_HASH(self)); + Py_hash_t hash = PyUnicode_HASH(self); if (hash != -1) { return hash; } x = Py_HashBuffer(PyUnicode_DATA(self), PyUnicode_GET_LENGTH(self) * PyUnicode_KIND(self)); - FT_ATOMIC_STORE_SSIZE_RELAXED(_PyUnicode_HASH(self), x); + PyUnicode_SET_HASH(self, x); return x; } @@ -15427,8 +15460,8 @@ unicode_subtype_new(PyTypeObject *type, PyObject *unicode) _PyUnicode_STATE(self).compact = 0; _PyUnicode_STATE(self).ascii = _PyUnicode_STATE(unicode).ascii; _PyUnicode_STATE(self).statically_allocated = 0; - _PyUnicode_UTF8_LENGTH(self) = 0; - _PyUnicode_UTF8(self) = NULL; + PyUnicode_SET_UTF8_LENGTH(self, 0); + PyUnicode_SET_UTF8(self, NULL); _PyUnicode_DATA_ANY(self) = NULL; share_utf8 = 0; @@ -15458,8 +15491,8 @@ unicode_subtype_new(PyTypeObject *type, PyObject *unicode) _PyUnicode_DATA_ANY(self) = data; if (share_utf8) { - _PyUnicode_UTF8_LENGTH(self) = length; - _PyUnicode_UTF8(self) = data; + PyUnicode_SET_UTF8_LENGTH(self, length); + PyUnicode_SET_UTF8(self, data); } memcpy(data, PyUnicode_DATA(unicode), kind * (length + 1)); From 48c70b8f7dfd00a018abbac50ea987f54fa4db51 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Thu, 19 Dec 2024 11:08:17 +0900 Subject: [PATCH 059/376] gh-115999: Enable BINARY_SUBSCR_GETITEM for free-threaded build (gh-127737) --- Include/internal/pycore_opcode_metadata.h | 4 +-- Include/internal/pycore_typeobject.h | 1 + Include/internal/pycore_uop_metadata.h | 2 +- Lib/test/test_opcache.py | 19 +++++++++++- Objects/typeobject.c | 25 ++++++++++++++++ Programs/test_frozenmain.h | 2 +- Python/bytecodes.c | 22 +++++++------- Python/executor_cases.c.h | 32 ++++++++++++--------- Python/generated_cases.c.h | 19 ++++++------ Python/optimizer_bytecodes.c | 3 +- Python/optimizer_cases.c.h | 16 ++++++++--- Python/specialize.c | 35 ++++++++++++----------- 12 files changed, 118 insertions(+), 62 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 28aa1120414337..d2ae8928a8fe8f 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -994,7 +994,7 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) { return 0; } case BINARY_SUBSCR: { - *effect = 0; + *effect = 1; return 0; } case BINARY_SUBSCR_DICT: { @@ -1002,7 +1002,7 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) { return 0; } case BINARY_SUBSCR_GETITEM: { - *effect = 0; + *effect = 1; return 0; } case BINARY_SUBSCR_LIST_INT: { diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 7b39d07f976ee3..581153344a8e05 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -278,6 +278,7 @@ typedef int (*_py_validate_type)(PyTypeObject *); // and if the validation is passed, it will set the ``tp_version`` as valid // tp_version_tag from the ``ty``. extern int _PyType_Validate(PyTypeObject *ty, _py_validate_type validate, unsigned int *tp_version); +extern int _PyType_CacheGetItemForSpecialization(PyHeapTypeObject *ht, PyObject *descriptor, uint32_t tp_version); #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index dd775d3f7d3cdd..eadfda472a7270 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -722,7 +722,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _BINARY_SUBSCR_CHECK_FUNC: return 0; case _BINARY_SUBSCR_INIT_CALL: - return 2; + return 3; case _LIST_APPEND: return 1; case _SET_ADD: diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index 0a7557adc4763b..94709e2022550a 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -1069,7 +1069,7 @@ def write(items): opname = "STORE_SUBSCR_LIST_INT" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization + @requires_specialization_ft def test_unpack_sequence_list(self): def get_items(): items = [] @@ -1245,6 +1245,14 @@ def f(o, n): f(test_obj, 1) self.assertEqual(test_obj.b, 0) +# gh-127274: BINARY_SUBSCR_GETITEM will only cache __getitem__ methods that +# are deferred. We only defer functions defined at the top-level. +class CGetItem: + def __init__(self, val): + self.val = val + def __getitem__(self, item): + return self.val + class TestSpecializer(TestBase): @@ -1520,6 +1528,15 @@ def binary_subscr_str_int(): self.assert_specialized(binary_subscr_str_int, "BINARY_SUBSCR_STR_INT") self.assert_no_opcode(binary_subscr_str_int, "BINARY_SUBSCR") + def binary_subscr_getitems(): + items = [CGetItem(i) for i in range(100)] + for i in range(100): + self.assertEqual(items[i][i], i) + + binary_subscr_getitems() + self.assert_specialized(binary_subscr_getitems, "BINARY_SUBSCR_GETITEM") + self.assert_no_opcode(binary_subscr_getitems, "BINARY_SUBSCR") + if __name__ == "__main__": unittest.main() diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 2068d6aa9be52b..7f95b519561e68 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5679,6 +5679,31 @@ _PyType_CacheInitForSpecialization(PyHeapTypeObject *type, PyObject *init, return can_cache; } +int +_PyType_CacheGetItemForSpecialization(PyHeapTypeObject *ht, PyObject *descriptor, uint32_t tp_version) +{ + if (!descriptor || !tp_version) { + return 0; + } + int can_cache; + BEGIN_TYPE_LOCK(); + can_cache = ((PyTypeObject*)ht)->tp_version_tag == tp_version; + // This pointer is invalidated by PyType_Modified (see the comment on + // struct _specialization_cache): + PyFunctionObject *func = (PyFunctionObject *)descriptor; + uint32_t version = _PyFunction_GetVersionForCurrentState(func); + can_cache = can_cache && _PyFunction_IsVersionValid(version); +#ifdef Py_GIL_DISABLED + can_cache = can_cache && _PyObject_HasDeferredRefcount(descriptor); +#endif + if (can_cache) { + FT_ATOMIC_STORE_PTR_RELEASE(ht->_spec_cache.getitem, descriptor); + FT_ATOMIC_STORE_UINT32_RELAXED(ht->_spec_cache.getitem_version, version); + } + END_TYPE_LOCK(); + return can_cache; +} + static void set_flags(PyTypeObject *self, unsigned long mask, unsigned long flags) { diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index c936622c020e3c..99b0fa48e01c8b 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -1,6 +1,6 @@ // Auto-generated by Programs/freeze_test_frozenmain.py unsigned char M_test_frozenmain[] = { - 227,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0, + 227,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, 0,0,0,0,0,243,168,0,0,0,149,0,89,0,79,0, 70,0,111,0,89,0,79,0,70,1,111,1,88,2,31,0, 79,1,49,1,0,0,0,0,0,0,29,0,88,2,31,0, diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 772b46d17ec198..b67264f0440869 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -865,26 +865,24 @@ dummy_func( res = PyStackRef_FromPyObjectSteal(res_o); } - op(_BINARY_SUBSCR_CHECK_FUNC, (container, unused -- container, unused)) { + op(_BINARY_SUBSCR_CHECK_FUNC, (container, unused -- container, unused, getitem)) { PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container)); DEOPT_IF(!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)); PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; - PyObject *getitem = ht->_spec_cache.getitem; - DEOPT_IF(getitem == NULL); - assert(PyFunction_Check(getitem)); - uint32_t cached_version = ht->_spec_cache.getitem_version; - DEOPT_IF(((PyFunctionObject *)getitem)->func_version != cached_version); - PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(getitem); + PyObject *getitem_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(ht->_spec_cache.getitem); + DEOPT_IF(getitem_o == NULL); + assert(PyFunction_Check(getitem_o)); + uint32_t cached_version = FT_ATOMIC_LOAD_UINT32_RELAXED(ht->_spec_cache.getitem_version); + DEOPT_IF(((PyFunctionObject *)getitem_o)->func_version != cached_version); + PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(getitem_o); assert(code->co_argcount == 2); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize)); + getitem = PyStackRef_FromPyObjectNew(getitem_o); STAT_INC(BINARY_SUBSCR, hit); } - op(_BINARY_SUBSCR_INIT_CALL, (container, sub -- new_frame: _PyInterpreterFrame* )) { - PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container)); - PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; - PyObject *getitem = ht->_spec_cache.getitem; - new_frame = _PyFrame_PushUnchecked(tstate, PyStackRef_FromPyObjectNew(getitem), 2, frame); + op(_BINARY_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame: _PyInterpreterFrame* )) { + new_frame = _PyFrame_PushUnchecked(tstate, getitem, 2, frame); new_frame->localsplus[0] = container; new_frame->localsplus[1] = sub; INPUTS_DEAD(); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 55e9c3aa2db64d..de61a64a6e3374 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1125,6 +1125,7 @@ case _BINARY_SUBSCR_CHECK_FUNC: { _PyStackRef container; + _PyStackRef getitem; container = stack_pointer[-2]; PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container)); if (!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) { @@ -1132,42 +1133,45 @@ JUMP_TO_JUMP_TARGET(); } PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; - PyObject *getitem = ht->_spec_cache.getitem; - if (getitem == NULL) { + PyObject *getitem_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(ht->_spec_cache.getitem); + if (getitem_o == NULL) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - assert(PyFunction_Check(getitem)); - uint32_t cached_version = ht->_spec_cache.getitem_version; - if (((PyFunctionObject *)getitem)->func_version != cached_version) { + assert(PyFunction_Check(getitem_o)); + uint32_t cached_version = FT_ATOMIC_LOAD_UINT32_RELAXED(ht->_spec_cache.getitem_version); + if (((PyFunctionObject *)getitem_o)->func_version != cached_version) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(getitem); + PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(getitem_o); assert(code->co_argcount == 2); if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } + getitem = PyStackRef_FromPyObjectNew(getitem_o); STAT_INC(BINARY_SUBSCR, hit); + stack_pointer[0] = getitem; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); break; } case _BINARY_SUBSCR_INIT_CALL: { + _PyStackRef getitem; _PyStackRef sub; _PyStackRef container; _PyInterpreterFrame *new_frame; - sub = stack_pointer[-1]; - container = stack_pointer[-2]; - PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container)); - PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; - PyObject *getitem = ht->_spec_cache.getitem; - new_frame = _PyFrame_PushUnchecked(tstate, PyStackRef_FromPyObjectNew(getitem), 2, frame); + getitem = stack_pointer[-1]; + sub = stack_pointer[-2]; + container = stack_pointer[-3]; + new_frame = _PyFrame_PushUnchecked(tstate, getitem, 2, frame); new_frame->localsplus[0] = container; new_frame->localsplus[1] = sub; frame->return_offset = 2 ; - stack_pointer[-2].bits = (uintptr_t)new_frame; - stack_pointer += -1; + stack_pointer[-3].bits = (uintptr_t)new_frame; + stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); break; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 94343f953221eb..8a89ba890fd9c9 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -505,6 +505,7 @@ INSTRUCTION_STATS(BINARY_SUBSCR_GETITEM); static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); _PyStackRef container; + _PyStackRef getitem; _PyStackRef sub; _PyInterpreterFrame *new_frame; /* Skip 1 cache entry */ @@ -518,23 +519,21 @@ PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container)); DEOPT_IF(!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE), BINARY_SUBSCR); PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; - PyObject *getitem = ht->_spec_cache.getitem; - DEOPT_IF(getitem == NULL, BINARY_SUBSCR); - assert(PyFunction_Check(getitem)); - uint32_t cached_version = ht->_spec_cache.getitem_version; - DEOPT_IF(((PyFunctionObject *)getitem)->func_version != cached_version, BINARY_SUBSCR); - PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(getitem); + PyObject *getitem_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(ht->_spec_cache.getitem); + DEOPT_IF(getitem_o == NULL, BINARY_SUBSCR); + assert(PyFunction_Check(getitem_o)); + uint32_t cached_version = FT_ATOMIC_LOAD_UINT32_RELAXED(ht->_spec_cache.getitem_version); + DEOPT_IF(((PyFunctionObject *)getitem_o)->func_version != cached_version, BINARY_SUBSCR); + PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(getitem_o); assert(code->co_argcount == 2); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), BINARY_SUBSCR); + getitem = PyStackRef_FromPyObjectNew(getitem_o); STAT_INC(BINARY_SUBSCR, hit); } // _BINARY_SUBSCR_INIT_CALL { sub = stack_pointer[-1]; - PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container)); - PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; - PyObject *getitem = ht->_spec_cache.getitem; - new_frame = _PyFrame_PushUnchecked(tstate, PyStackRef_FromPyObjectNew(getitem), 2, frame); + new_frame = _PyFrame_PushUnchecked(tstate, getitem, 2, frame); new_frame->localsplus[0] = container; new_frame->localsplus[1] = sub; frame->return_offset = 2 ; diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 0b8aff02367e31..e60c0d38425bfe 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -349,9 +349,10 @@ dummy_func(void) { GETLOCAL(this_instr->operand0) = res; } - op(_BINARY_SUBSCR_INIT_CALL, (container, sub -- new_frame: _Py_UOpsAbstractFrame *)) { + op(_BINARY_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame: _Py_UOpsAbstractFrame *)) { (void)container; (void)sub; + (void)getitem; new_frame = NULL; ctx->done = true; } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index f4fbe8c8aa0480..33b34d6fa0d3f9 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -592,21 +592,29 @@ } case _BINARY_SUBSCR_CHECK_FUNC: { + _Py_UopsSymbol *getitem; + getitem = sym_new_not_null(ctx); + stack_pointer[0] = getitem; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); break; } case _BINARY_SUBSCR_INIT_CALL: { + _Py_UopsSymbol *getitem; _Py_UopsSymbol *sub; _Py_UopsSymbol *container; _Py_UOpsAbstractFrame *new_frame; - sub = stack_pointer[-1]; - container = stack_pointer[-2]; + getitem = stack_pointer[-1]; + sub = stack_pointer[-2]; + container = stack_pointer[-3]; (void)container; (void)sub; + (void)getitem; new_frame = NULL; ctx->done = true; - stack_pointer[-2] = (_Py_UopsSymbol *)new_frame; - stack_pointer += -1; + stack_pointer[-3] = (_Py_UopsSymbol *)new_frame; + stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); break; } diff --git a/Python/specialize.c b/Python/specialize.c index 6eb298217ec2d3..6c45320f95db8e 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1096,6 +1096,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_METHOD); return -1; } + /* Don't specialize if PEP 523 is active */ if (_PyInterpreterState_GET()->eval_frame) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER); return -1; @@ -1165,6 +1166,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na if (version == 0) { return -1; } + /* Don't specialize if PEP 523 is active */ if (_PyInterpreterState_GET()->eval_frame) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER); return -1; @@ -1781,12 +1783,12 @@ _Py_Specialize_BinarySubscr( specialized_op = BINARY_SUBSCR_DICT; goto success; } -#ifndef Py_GIL_DISABLED - PyTypeObject *cls = Py_TYPE(container); - PyObject *descriptor = _PyType_Lookup(cls, &_Py_ID(__getitem__)); + unsigned int tp_version; + PyObject *descriptor = _PyType_LookupRefAndVersion(container_type, &_Py_ID(__getitem__), &tp_version); if (descriptor && Py_TYPE(descriptor) == &PyFunction_Type) { if (!(container_type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_SUBSCR_NOT_HEAP_TYPE); + Py_DECREF(descriptor); goto fail; } PyFunctionObject *func = (PyFunctionObject *)descriptor; @@ -1794,30 +1796,29 @@ _Py_Specialize_BinarySubscr( int kind = function_kind(fcode); if (kind != SIMPLE_FUNCTION) { SPECIALIZATION_FAIL(BINARY_SUBSCR, kind); + Py_DECREF(descriptor); goto fail; } if (fcode->co_argcount != 2) { SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_WRONG_NUMBER_ARGUMENTS); + Py_DECREF(descriptor); goto fail; } - uint32_t version = _PyFunction_GetVersionForCurrentState(func); - if (!_PyFunction_IsVersionValid(version)) { - SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OUT_OF_VERSIONS); - goto fail; - } + + PyHeapTypeObject *ht = (PyHeapTypeObject *)container_type; + /* Don't specialize if PEP 523 is active */ if (_PyInterpreterState_GET()->eval_frame) { SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OTHER); + Py_DECREF(descriptor); goto fail; } - PyHeapTypeObject *ht = (PyHeapTypeObject *)container_type; - // This pointer is invalidated by PyType_Modified (see the comment on - // struct _specialization_cache): - ht->_spec_cache.getitem = descriptor; - ht->_spec_cache.getitem_version = version; - specialized_op = BINARY_SUBSCR_GETITEM; - goto success; + if (_PyType_CacheGetItemForSpecialization(ht, descriptor, (uint32_t)tp_version)) { + specialized_op = BINARY_SUBSCR_GETITEM; + Py_DECREF(descriptor); + goto success; + } } -#endif // Py_GIL_DISABLED + Py_XDECREF(descriptor); SPECIALIZATION_FAIL(BINARY_SUBSCR, binary_subscr_fail_kind(container_type, sub)); fail: @@ -2617,6 +2618,7 @@ _Py_Specialize_ForIter(_PyStackRef iter, _Py_CODEUNIT *instr, int oparg) assert(instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == END_FOR || instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == INSTRUMENTED_END_FOR ); + /* Don't specialize if PEP 523 is active */ if (_PyInterpreterState_GET()->eval_frame) { SPECIALIZATION_FAIL(FOR_ITER, SPEC_FAIL_OTHER); goto failure; @@ -2645,6 +2647,7 @@ _Py_Specialize_Send(_PyStackRef receiver_st, _Py_CODEUNIT *instr) assert(_PyOpcode_Caches[SEND] == INLINE_CACHE_ENTRIES_SEND); PyTypeObject *tp = Py_TYPE(receiver); if (tp == &PyGen_Type || tp == &PyCoro_Type) { + /* Don't specialize if PEP 523 is active */ if (_PyInterpreterState_GET()->eval_frame) { SPECIALIZATION_FAIL(SEND, SPEC_FAIL_OTHER); goto failure; From 46dc1ba9c6e8b95635fa27607d01d6108d8f677e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 19 Dec 2024 11:27:29 +0100 Subject: [PATCH 060/376] gh-128069: brew link --overwrite tcl-tk@8 to prevent conflict with GitHub image's version (#128090) brew link --overwrite tcl-tk@8 to prevent conflict with GitHub image's version Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- .github/workflows/reusable-macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-macos.yml b/.github/workflows/reusable-macos.yml index 36ae3e27207e37..6fa389b2d66e5e 100644 --- a/.github/workflows/reusable-macos.yml +++ b/.github/workflows/reusable-macos.yml @@ -42,7 +42,7 @@ jobs: run: | brew install pkg-config openssl@3.0 xz gdbm tcl-tk@8 make # Because alternate versions are not symlinked into place by default: - brew link tcl-tk@8 + brew link --overwrite tcl-tk@8 - name: Configure CPython run: | GDBM_CFLAGS="-I$(brew --prefix gdbm)/include" \ From 3c168f7f79d1da2323d35dcf88c2d3c8730e5df6 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 19 Dec 2024 17:08:32 +0530 Subject: [PATCH 061/376] gh-128013: fix data race in `PyUnicode_AsUTF8AndSize` on free-threading (#128021) --- Lib/test/test_capi/test_unicode.py | 20 +++++++++++- Objects/unicodeobject.c | 49 +++++++++++++++++++----------- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/Lib/test/test_capi/test_unicode.py b/Lib/test/test_capi/test_unicode.py index 65d8242ad3fc60..3408c10f426058 100644 --- a/Lib/test/test_capi/test_unicode.py +++ b/Lib/test/test_capi/test_unicode.py @@ -1,7 +1,7 @@ import unittest import sys from test import support -from test.support import import_helper +from test.support import threading_helper try: import _testcapi @@ -1005,6 +1005,24 @@ def test_asutf8(self): self.assertRaises(TypeError, unicode_asutf8, [], 0) # CRASHES unicode_asutf8(NULL, 0) + @unittest.skipIf(_testcapi is None, 'need _testcapi module') + @threading_helper.requires_working_threading() + def test_asutf8_race(self): + """Test that there's no race condition in PyUnicode_AsUTF8()""" + unicode_asutf8 = _testcapi.unicode_asutf8 + from threading import Thread + + data = "😊" + + def worker(): + for _ in range(1000): + self.assertEqual(unicode_asutf8(data, 5), b'\xf0\x9f\x98\x8a\0') + + threads = [Thread(target=worker) for _ in range(10)] + with threading_helper.start_threads(threads): + pass + + @support.cpython_only @unittest.skipIf(_testlimitedcapi is None, 'need _testlimitedcapi module') def test_asutf8andsize(self): diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 53be6f5b9017a3..1aab9cf37768a8 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -114,7 +114,7 @@ NOTE: In the interpreter's initialization phase, some globals are currently static inline char* _PyUnicode_UTF8(PyObject *op) { - return (_PyCompactUnicodeObject_CAST(op)->utf8); + return FT_ATOMIC_LOAD_PTR_ACQUIRE(_PyCompactUnicodeObject_CAST(op)->utf8); } static inline char* PyUnicode_UTF8(PyObject *op) @@ -130,7 +130,7 @@ static inline char* PyUnicode_UTF8(PyObject *op) static inline void PyUnicode_SET_UTF8(PyObject *op, char *utf8) { - _PyCompactUnicodeObject_CAST(op)->utf8 = utf8; + FT_ATOMIC_STORE_PTR_RELEASE(_PyCompactUnicodeObject_CAST(op)->utf8, utf8); } static inline Py_ssize_t PyUnicode_UTF8_LENGTH(PyObject *op) @@ -700,16 +700,17 @@ _PyUnicode_CheckConsistency(PyObject *op, int check_content) CHECK(ascii->state.compact == 0); CHECK(data != NULL); if (ascii->state.ascii) { - CHECK(compact->utf8 == data); + CHECK(_PyUnicode_UTF8(op) == data); CHECK(compact->utf8_length == ascii->length); } else { - CHECK(compact->utf8 != data); + CHECK(_PyUnicode_UTF8(op) != data); } } - - if (compact->utf8 == NULL) +#ifndef Py_GIL_DISABLED + if (_PyUnicode_UTF8(op) == NULL) CHECK(compact->utf8_length == 0); +#endif } /* check that the best kind is used: O(n) operation */ @@ -1156,8 +1157,8 @@ resize_compact(PyObject *unicode, Py_ssize_t length) if (_PyUnicode_HAS_UTF8_MEMORY(unicode)) { PyMem_Free(_PyUnicode_UTF8(unicode)); - PyUnicode_SET_UTF8(unicode, NULL); PyUnicode_SET_UTF8_LENGTH(unicode, 0); + PyUnicode_SET_UTF8(unicode, NULL); } #ifdef Py_TRACE_REFS _Py_ForgetReference(unicode); @@ -1210,8 +1211,8 @@ resize_inplace(PyObject *unicode, Py_ssize_t length) if (!share_utf8 && _PyUnicode_HAS_UTF8_MEMORY(unicode)) { PyMem_Free(_PyUnicode_UTF8(unicode)); - PyUnicode_SET_UTF8(unicode, NULL); PyUnicode_SET_UTF8_LENGTH(unicode, 0); + PyUnicode_SET_UTF8(unicode, NULL); } data = (PyObject *)PyObject_Realloc(data, new_size); @@ -1221,8 +1222,8 @@ resize_inplace(PyObject *unicode, Py_ssize_t length) } _PyUnicode_DATA_ANY(unicode) = data; if (share_utf8) { - PyUnicode_SET_UTF8(unicode, data); PyUnicode_SET_UTF8_LENGTH(unicode, length); + PyUnicode_SET_UTF8(unicode, data); } _PyUnicode_LENGTH(unicode) = length; PyUnicode_WRITE(PyUnicode_KIND(unicode), data, length, 0); @@ -4216,6 +4217,21 @@ PyUnicode_FSDecoder(PyObject* arg, void* addr) static int unicode_fill_utf8(PyObject *unicode); + +static int +unicode_ensure_utf8(PyObject *unicode) +{ + int err = 0; + if (PyUnicode_UTF8(unicode) == NULL) { + Py_BEGIN_CRITICAL_SECTION(unicode); + if (PyUnicode_UTF8(unicode) == NULL) { + err = unicode_fill_utf8(unicode); + } + Py_END_CRITICAL_SECTION(); + } + return err; +} + const char * PyUnicode_AsUTF8AndSize(PyObject *unicode, Py_ssize_t *psize) { @@ -4227,13 +4243,11 @@ PyUnicode_AsUTF8AndSize(PyObject *unicode, Py_ssize_t *psize) return NULL; } - if (PyUnicode_UTF8(unicode) == NULL) { - if (unicode_fill_utf8(unicode) == -1) { - if (psize) { - *psize = -1; - } - return NULL; + if (unicode_ensure_utf8(unicode) == -1) { + if (psize) { + *psize = -1; } + return NULL; } if (psize) { @@ -5854,6 +5868,7 @@ unicode_encode_utf8(PyObject *unicode, _Py_error_handler error_handler, static int unicode_fill_utf8(PyObject *unicode) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(unicode); /* the string cannot be ASCII, or PyUnicode_UTF8() would be set */ assert(!PyUnicode_IS_ASCII(unicode)); @@ -5895,10 +5910,10 @@ unicode_fill_utf8(PyObject *unicode) PyErr_NoMemory(); return -1; } - PyUnicode_SET_UTF8(unicode, cache); - PyUnicode_SET_UTF8_LENGTH(unicode, len); memcpy(cache, start, len); cache[len] = '\0'; + PyUnicode_SET_UTF8_LENGTH(unicode, len); + PyUnicode_SET_UTF8(unicode, cache); _PyBytesWriter_Dealloc(&writer); return 0; } From 19c5134d57764d3db7b1cacec4f090c74849a5c1 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 19 Dec 2024 18:15:36 +0530 Subject: [PATCH 062/376] gh-122706: fix docs for asyncio ssl sockets (#128092) --- Doc/library/ssl.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index b7fb1fc07d199f..f07d151a885692 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -2508,8 +2508,8 @@ thus several things you need to be aware of: .. seealso:: The :mod:`asyncio` module supports :ref:`non-blocking SSL sockets - ` and provides a - higher level API. It polls for events using the :mod:`selectors` module and + ` and provides a higher level :ref:`Streams API `. + It polls for events using the :mod:`selectors` module and handles :exc:`SSLWantWriteError`, :exc:`SSLWantReadError` and :exc:`BlockingIOError` exceptions. It runs the SSL handshake asynchronously as well. From ea578fc6d310c85538aefbb900a326c5c3424dd5 Mon Sep 17 00:00:00 2001 From: "RUANG (James Roy)" Date: Thu, 19 Dec 2024 21:51:21 +0800 Subject: [PATCH 063/376] gh-127688: Add `SCHED_DEADLINE` and `SCHED_NORMAL` constants to `os` module (GH-127689) --- Doc/library/os.rst | 12 ++++++++++++ Doc/whatsnew/3.14.rst | 4 ++++ .../2024-12-06-21-03-11.gh-issue-127688.NJqtc-.rst | 2 ++ Modules/posixmodule.c | 10 ++++++++++ configure | 6 ++++++ configure.ac | 2 +- pyconfig.h.in | 3 +++ 7 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-06-21-03-11.gh-issue-127688.NJqtc-.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index dfe5ef0726ff7d..69e6192038ab2b 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -5420,10 +5420,22 @@ operating system. Scheduling policy for CPU-intensive processes that tries to preserve interactivity on the rest of the computer. +.. data:: SCHED_DEADLINE + + Scheduling policy for tasks with deadline constraints. + + .. versionadded:: next + .. data:: SCHED_IDLE Scheduling policy for extremely low priority background tasks. +.. data:: SCHED_NORMAL + + Alias for :data:`SCHED_OTHER`. + + .. versionadded:: next + .. data:: SCHED_SPORADIC Scheduling policy for sporadic server programs. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 342456cbc397f3..2e43dce5e061b4 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -525,6 +525,10 @@ os same process. (Contributed by Victor Stinner in :gh:`120057`.) +* Add the :data:`~os.SCHED_DEADLINE` and :data:`~os.SCHED_NORMAL` constants + to the :mod:`os` module. + (Contributed by James Roy in :gh:`127688`.) + pathlib ------- diff --git a/Misc/NEWS.d/next/Library/2024-12-06-21-03-11.gh-issue-127688.NJqtc-.rst b/Misc/NEWS.d/next/Library/2024-12-06-21-03-11.gh-issue-127688.NJqtc-.rst new file mode 100644 index 00000000000000..a22b136da72faf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-06-21-03-11.gh-issue-127688.NJqtc-.rst @@ -0,0 +1,2 @@ +Add the :data:`~os.SCHED_DEADLINE` and :data:`~os.SCHED_NORMAL` constants +to the :mod:`os` module. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 2045c6065b8e7a..151d469983fafb 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -311,6 +311,10 @@ corresponding Unix manual entries for more information on calls."); # include #endif +#ifdef HAVE_LINUX_SCHED_H +# include +#endif + #if !defined(CPU_ALLOC) && defined(HAVE_SCHED_SETAFFINITY) # undef HAVE_SCHED_SETAFFINITY #endif @@ -17523,9 +17527,15 @@ all_ins(PyObject *m) #ifdef SCHED_OTHER if (PyModule_AddIntMacro(m, SCHED_OTHER)) return -1; #endif +#ifdef SCHED_DEADLINE + if (PyModule_AddIntMacro(m, SCHED_DEADLINE)) return -1; +#endif #ifdef SCHED_FIFO if (PyModule_AddIntMacro(m, SCHED_FIFO)) return -1; #endif +#ifdef SCHED_NORMAL + if (PyModule_AddIntMacro(m, SCHED_NORMAL)) return -1; +#endif #ifdef SCHED_RR if (PyModule_AddIntMacro(m, SCHED_RR)) return -1; #endif diff --git a/configure b/configure index 6df1116fc600f2..e59c7046305d46 100755 --- a/configure +++ b/configure @@ -10984,6 +10984,12 @@ if test "x$ac_cv_header_linux_soundcard_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_SOUNDCARD_H 1" >>confdefs.h +fi +ac_fn_c_check_header_compile "$LINENO" "linux/sched.h" "ac_cv_header_linux_sched_h" "$ac_includes_default" +if test "x$ac_cv_header_linux_sched_h" = xyes +then : + printf "%s\n" "#define HAVE_LINUX_SCHED_H 1" >>confdefs.h + fi ac_fn_c_check_header_compile "$LINENO" "linux/tipc.h" "ac_cv_header_linux_tipc_h" "$ac_includes_default" if test "x$ac_cv_header_linux_tipc_h" = xyes diff --git a/configure.ac b/configure.ac index 8295b59b8e45fb..074e2ce3dd3024 100644 --- a/configure.ac +++ b/configure.ac @@ -2931,7 +2931,7 @@ AC_DEFINE([STDC_HEADERS], [1], AC_CHECK_HEADERS([ \ alloca.h asm/types.h bluetooth.h conio.h direct.h dlfcn.h endian.h errno.h fcntl.h grp.h \ io.h langinfo.h libintl.h libutil.h linux/auxvec.h sys/auxv.h linux/fs.h linux/limits.h linux/memfd.h \ - linux/netfilter_ipv4.h linux/random.h linux/soundcard.h \ + linux/netfilter_ipv4.h linux/random.h linux/soundcard.h linux/sched.h \ linux/tipc.h linux/wait.h netdb.h net/ethernet.h netinet/in.h netpacket/packet.h poll.h process.h pthread.h pty.h \ sched.h setjmp.h shadow.h signal.h spawn.h stropts.h sys/audioio.h sys/bsdtty.h sys/devpoll.h \ sys/endian.h sys/epoll.h sys/event.h sys/eventfd.h sys/file.h sys/ioctl.h sys/kern_control.h \ diff --git a/pyconfig.h.in b/pyconfig.h.in index 166c195a8c66fc..1ca83fd2f2ca1b 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -744,6 +744,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_RANDOM_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_SCHED_H + /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_SOUNDCARD_H From b5d1e4552f0ba40d8380368e1b099261686a89cf Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 19 Dec 2024 09:00:30 -0500 Subject: [PATCH 064/376] gh-128083: Fix macro redefinition warning in clinic. (GH-127950) --- Lib/test/clinic.test.c | 69 ++++++++++++++++++++++++++++ Modules/_io/clinic/textio.c.h | 11 ++++- Modules/clinic/_ssl.c.h | 50 +++++++++++++++++++- Tools/clinic/libclinic/parse_args.py | 3 ++ 4 files changed, 131 insertions(+), 2 deletions(-) diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index b6ae04ecf2f8ed..e4f146c0841188 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -5358,6 +5358,75 @@ static int Test_property_set_impl(TestObj *self, PyObject *value) /*[clinic end generated code: output=e4342fe9bb1d7817 input=3bc3f46a23c83a88]*/ +/*[clinic input] +@setter +Test.setter_first_with_docstr +[clinic start generated code]*/ + +#if !defined(Test_setter_first_with_docstr_DOCSTR) +# define Test_setter_first_with_docstr_DOCSTR NULL +#endif +#if defined(TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF) +# undef TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF +# define TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF {"setter_first_with_docstr", (getter)Test_setter_first_with_docstr_get, (setter)Test_setter_first_with_docstr_set, Test_setter_first_with_docstr_DOCSTR}, +#else +# define TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF {"setter_first_with_docstr", NULL, (setter)Test_setter_first_with_docstr_set, NULL}, +#endif + +static int +Test_setter_first_with_docstr_set_impl(TestObj *self, PyObject *value); + +static int +Test_setter_first_with_docstr_set(TestObj *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + return_value = Test_setter_first_with_docstr_set_impl(self, value); + + return return_value; +} + +static int +Test_setter_first_with_docstr_set_impl(TestObj *self, PyObject *value) +/*[clinic end generated code: output=e4d76b558a4061db input=31a045ce11bbe961]*/ + +/*[clinic input] +@getter +Test.setter_first_with_docstr + +my silly docstring +[clinic start generated code]*/ + +PyDoc_STRVAR(Test_setter_first_with_docstr__doc__, +"my silly docstring"); +#if defined(Test_setter_first_with_docstr_DOCSTR) +# undef Test_setter_first_with_docstr_DOCSTR +#endif +#define Test_setter_first_with_docstr_DOCSTR Test_setter_first_with_docstr__doc__ + +#if !defined(Test_setter_first_with_docstr_DOCSTR) +# define Test_setter_first_with_docstr_DOCSTR NULL +#endif +#if defined(TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF) +# undef TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF +# define TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF {"setter_first_with_docstr", (getter)Test_setter_first_with_docstr_get, (setter)Test_setter_first_with_docstr_set, Test_setter_first_with_docstr_DOCSTR}, +#else +# define TEST_SETTER_FIRST_WITH_DOCSTR_GETSETDEF {"setter_first_with_docstr", (getter)Test_setter_first_with_docstr_get, NULL, Test_setter_first_with_docstr_DOCSTR}, +#endif + +static PyObject * +Test_setter_first_with_docstr_get_impl(TestObj *self); + +static PyObject * +Test_setter_first_with_docstr_get(TestObj *self, void *Py_UNUSED(context)) +{ + return Test_setter_first_with_docstr_get_impl(self); +} + +static PyObject * +Test_setter_first_with_docstr_get_impl(TestObj *self) +/*[clinic end generated code: output=749a30266f9fb443 input=10af4e43b3cb34dc]*/ + /*[clinic input] output push output preset buffer diff --git a/Modules/_io/clinic/textio.c.h b/Modules/_io/clinic/textio.c.h index 160f80ada43660..0acc1f060c811b 100644 --- a/Modules/_io/clinic/textio.c.h +++ b/Modules/_io/clinic/textio.c.h @@ -208,6 +208,9 @@ PyDoc_STRVAR(_io__TextIOBase_encoding__doc__, "Encoding of the text stream.\n" "\n" "Subclasses should override."); +#if defined(_io__TextIOBase_encoding_DOCSTR) +# undef _io__TextIOBase_encoding_DOCSTR +#endif #define _io__TextIOBase_encoding_DOCSTR _io__TextIOBase_encoding__doc__ #if !defined(_io__TextIOBase_encoding_DOCSTR) @@ -235,6 +238,9 @@ PyDoc_STRVAR(_io__TextIOBase_newlines__doc__, "Only line endings translated during reading are considered.\n" "\n" "Subclasses should override."); +#if defined(_io__TextIOBase_newlines_DOCSTR) +# undef _io__TextIOBase_newlines_DOCSTR +#endif #define _io__TextIOBase_newlines_DOCSTR _io__TextIOBase_newlines__doc__ #if !defined(_io__TextIOBase_newlines_DOCSTR) @@ -260,6 +266,9 @@ PyDoc_STRVAR(_io__TextIOBase_errors__doc__, "The error setting of the decoder or encoder.\n" "\n" "Subclasses should override."); +#if defined(_io__TextIOBase_errors_DOCSTR) +# undef _io__TextIOBase_errors_DOCSTR +#endif #define _io__TextIOBase_errors_DOCSTR _io__TextIOBase_errors__doc__ #if !defined(_io__TextIOBase_errors_DOCSTR) @@ -1281,4 +1290,4 @@ _io_TextIOWrapper__CHUNK_SIZE_set(textio *self, PyObject *value, void *Py_UNUSED return return_value; } -/*[clinic end generated code: output=1172c500a022c65d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=423a320f087792b9 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_ssl.c.h b/Modules/clinic/_ssl.c.h index 1ff85e32ffe5a0..becdb9cc1831fa 100644 --- a/Modules/clinic/_ssl.c.h +++ b/Modules/clinic/_ssl.c.h @@ -264,6 +264,9 @@ PyDoc_STRVAR(_ssl__SSLSocket_context__doc__, "This is typically used from within a callback function set by the sni_callback\n" "on the SSLContext to change the certificate information associated with the\n" "SSLSocket before the cryptographic exchange handshake messages."); +#if defined(_ssl__SSLSocket_context_DOCSTR) +# undef _ssl__SSLSocket_context_DOCSTR +#endif #define _ssl__SSLSocket_context_DOCSTR _ssl__SSLSocket_context__doc__ #if !defined(_ssl__SSLSocket_context_DOCSTR) @@ -318,6 +321,9 @@ _ssl__SSLSocket_context_set(PySSLSocket *self, PyObject *value, void *Py_UNUSED( PyDoc_STRVAR(_ssl__SSLSocket_server_side__doc__, "Whether this is a server-side socket."); +#if defined(_ssl__SSLSocket_server_side_DOCSTR) +# undef _ssl__SSLSocket_server_side_DOCSTR +#endif #define _ssl__SSLSocket_server_side_DOCSTR _ssl__SSLSocket_server_side__doc__ #if !defined(_ssl__SSLSocket_server_side_DOCSTR) @@ -347,6 +353,9 @@ _ssl__SSLSocket_server_side_get(PySSLSocket *self, void *Py_UNUSED(context)) PyDoc_STRVAR(_ssl__SSLSocket_server_hostname__doc__, "The currently set server hostname (for SNI)."); +#if defined(_ssl__SSLSocket_server_hostname_DOCSTR) +# undef _ssl__SSLSocket_server_hostname_DOCSTR +#endif #define _ssl__SSLSocket_server_hostname_DOCSTR _ssl__SSLSocket_server_hostname__doc__ #if !defined(_ssl__SSLSocket_server_hostname_DOCSTR) @@ -378,6 +387,9 @@ PyDoc_STRVAR(_ssl__SSLSocket_owner__doc__, "The Python-level owner of this object.\n" "\n" "Passed as \"self\" in servername callback."); +#if defined(_ssl__SSLSocket_owner_DOCSTR) +# undef _ssl__SSLSocket_owner_DOCSTR +#endif #define _ssl__SSLSocket_owner_DOCSTR _ssl__SSLSocket_owner__doc__ #if !defined(_ssl__SSLSocket_owner_DOCSTR) @@ -668,6 +680,9 @@ _ssl__SSLSocket_verify_client_post_handshake(PySSLSocket *self, PyObject *Py_UNU PyDoc_STRVAR(_ssl__SSLSocket_session__doc__, "The underlying SSLSession object."); +#if defined(_ssl__SSLSocket_session_DOCSTR) +# undef _ssl__SSLSocket_session_DOCSTR +#endif #define _ssl__SSLSocket_session_DOCSTR _ssl__SSLSocket_session__doc__ #if !defined(_ssl__SSLSocket_session_DOCSTR) @@ -722,6 +737,9 @@ _ssl__SSLSocket_session_set(PySSLSocket *self, PyObject *value, void *Py_UNUSED( PyDoc_STRVAR(_ssl__SSLSocket_session_reused__doc__, "Was the client session reused during handshake?"); +#if defined(_ssl__SSLSocket_session_reused_DOCSTR) +# undef _ssl__SSLSocket_session_reused_DOCSTR +#endif #define _ssl__SSLSocket_session_reused_DOCSTR _ssl__SSLSocket_session_reused__doc__ #if !defined(_ssl__SSLSocket_session_reused_DOCSTR) @@ -1077,6 +1095,9 @@ _ssl__SSLContext_maximum_version_set(PySSLContext *self, PyObject *value, void * PyDoc_STRVAR(_ssl__SSLContext_num_tickets__doc__, "Control the number of TLSv1.3 session tickets."); +#if defined(_ssl__SSLContext_num_tickets_DOCSTR) +# undef _ssl__SSLContext_num_tickets_DOCSTR +#endif #define _ssl__SSLContext_num_tickets_DOCSTR _ssl__SSLContext_num_tickets__doc__ #if !defined(_ssl__SSLContext_num_tickets_DOCSTR) @@ -1131,6 +1152,9 @@ _ssl__SSLContext_num_tickets_set(PySSLContext *self, PyObject *value, void *Py_U PyDoc_STRVAR(_ssl__SSLContext_security_level__doc__, "The current security level."); +#if defined(_ssl__SSLContext_security_level_DOCSTR) +# undef _ssl__SSLContext_security_level_DOCSTR +#endif #define _ssl__SSLContext_security_level_DOCSTR _ssl__SSLContext_security_level__doc__ #if !defined(_ssl__SSLContext_security_level_DOCSTR) @@ -1778,6 +1802,9 @@ PyDoc_STRVAR(_ssl__SSLContext_sni_callback__doc__, "with the SSLSocket, the server name as a string, and the SSLContext object.\n" "\n" "See RFC 6066 for details of the SNI extension."); +#if defined(_ssl__SSLContext_sni_callback_DOCSTR) +# undef _ssl__SSLContext_sni_callback_DOCSTR +#endif #define _ssl__SSLContext_sni_callback_DOCSTR _ssl__SSLContext_sni_callback__doc__ #if !defined(_ssl__SSLContext_sni_callback_DOCSTR) @@ -2100,6 +2127,9 @@ _ssl_MemoryBIO(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyDoc_STRVAR(_ssl_MemoryBIO_pending__doc__, "The number of bytes pending in the memory BIO."); +#if defined(_ssl_MemoryBIO_pending_DOCSTR) +# undef _ssl_MemoryBIO_pending_DOCSTR +#endif #define _ssl_MemoryBIO_pending_DOCSTR _ssl_MemoryBIO_pending__doc__ #if !defined(_ssl_MemoryBIO_pending_DOCSTR) @@ -2129,6 +2159,9 @@ _ssl_MemoryBIO_pending_get(PySSLMemoryBIO *self, void *Py_UNUSED(context)) PyDoc_STRVAR(_ssl_MemoryBIO_eof__doc__, "Whether the memory BIO is at EOF."); +#if defined(_ssl_MemoryBIO_eof_DOCSTR) +# undef _ssl_MemoryBIO_eof_DOCSTR +#endif #define _ssl_MemoryBIO_eof_DOCSTR _ssl_MemoryBIO_eof__doc__ #if !defined(_ssl_MemoryBIO_eof_DOCSTR) @@ -2262,6 +2295,9 @@ _ssl_MemoryBIO_write_eof(PySSLMemoryBIO *self, PyObject *Py_UNUSED(ignored)) PyDoc_STRVAR(_ssl_SSLSession_time__doc__, "Session creation time (seconds since epoch)."); +#if defined(_ssl_SSLSession_time_DOCSTR) +# undef _ssl_SSLSession_time_DOCSTR +#endif #define _ssl_SSLSession_time_DOCSTR _ssl_SSLSession_time__doc__ #if !defined(_ssl_SSLSession_time_DOCSTR) @@ -2291,6 +2327,9 @@ _ssl_SSLSession_time_get(PySSLSession *self, void *Py_UNUSED(context)) PyDoc_STRVAR(_ssl_SSLSession_timeout__doc__, "Session timeout (delta in seconds)."); +#if defined(_ssl_SSLSession_timeout_DOCSTR) +# undef _ssl_SSLSession_timeout_DOCSTR +#endif #define _ssl_SSLSession_timeout_DOCSTR _ssl_SSLSession_timeout__doc__ #if !defined(_ssl_SSLSession_timeout_DOCSTR) @@ -2320,6 +2359,9 @@ _ssl_SSLSession_timeout_get(PySSLSession *self, void *Py_UNUSED(context)) PyDoc_STRVAR(_ssl_SSLSession_ticket_lifetime_hint__doc__, "Ticket life time hint."); +#if defined(_ssl_SSLSession_ticket_lifetime_hint_DOCSTR) +# undef _ssl_SSLSession_ticket_lifetime_hint_DOCSTR +#endif #define _ssl_SSLSession_ticket_lifetime_hint_DOCSTR _ssl_SSLSession_ticket_lifetime_hint__doc__ #if !defined(_ssl_SSLSession_ticket_lifetime_hint_DOCSTR) @@ -2349,6 +2391,9 @@ _ssl_SSLSession_ticket_lifetime_hint_get(PySSLSession *self, void *Py_UNUSED(con PyDoc_STRVAR(_ssl_SSLSession_id__doc__, "Session ID."); +#if defined(_ssl_SSLSession_id_DOCSTR) +# undef _ssl_SSLSession_id_DOCSTR +#endif #define _ssl_SSLSession_id_DOCSTR _ssl_SSLSession_id__doc__ #if !defined(_ssl_SSLSession_id_DOCSTR) @@ -2378,6 +2423,9 @@ _ssl_SSLSession_id_get(PySSLSession *self, void *Py_UNUSED(context)) PyDoc_STRVAR(_ssl_SSLSession_has_ticket__doc__, "Does the session contain a ticket?"); +#if defined(_ssl_SSLSession_has_ticket_DOCSTR) +# undef _ssl_SSLSession_has_ticket_DOCSTR +#endif #define _ssl_SSLSession_has_ticket_DOCSTR _ssl_SSLSession_has_ticket__doc__ #if !defined(_ssl_SSLSession_has_ticket_DOCSTR) @@ -2830,4 +2878,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=654d6d7af659f6cd input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e71f1ef621aead08 input=a9049054013a1b77]*/ diff --git a/Tools/clinic/libclinic/parse_args.py b/Tools/clinic/libclinic/parse_args.py index a57d729bec5733..ff4731e99b98d4 100644 --- a/Tools/clinic/libclinic/parse_args.py +++ b/Tools/clinic/libclinic/parse_args.py @@ -146,6 +146,9 @@ def declare_parser( GETSET_DOCSTRING_PROTOTYPE_STRVAR: Final[str] = libclinic.normalize_snippet(""" PyDoc_STRVAR({getset_basename}__doc__, {docstring}); + #if defined({getset_basename}_DOCSTR) + # undef {getset_basename}_DOCSTR + #endif #define {getset_basename}_DOCSTR {getset_basename}__doc__ """) IMPL_DEFINITION_PROTOTYPE: Final[str] = libclinic.normalize_snippet(""" From b9b3e4a076caddf7876d1d4d762a117a26faffcf Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 19 Dec 2024 15:45:34 +0100 Subject: [PATCH 065/376] gh-127951: Add build option to enable pystats on Windows (GH-127952) --- .../next/Build/2024-12-16-16-16-35.gh-issue-127951.lpE13-.rst | 1 + PCbuild/build.bat | 3 +++ PCbuild/pyproject.props | 4 +++- 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Build/2024-12-16-16-16-35.gh-issue-127951.lpE13-.rst diff --git a/Misc/NEWS.d/next/Build/2024-12-16-16-16-35.gh-issue-127951.lpE13-.rst b/Misc/NEWS.d/next/Build/2024-12-16-16-16-35.gh-issue-127951.lpE13-.rst new file mode 100644 index 00000000000000..0c1df0e6bd7baa --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-12-16-16-16-35.gh-issue-127951.lpE13-.rst @@ -0,0 +1 @@ +Add option ``--pystats`` to the Windows build to enable performance statistics collection. diff --git a/PCbuild/build.bat b/PCbuild/build.bat index 6d3ce81651ade5..d3e3894c203a65 100644 --- a/PCbuild/build.bat +++ b/PCbuild/build.bat @@ -39,6 +39,7 @@ echo. --regen Regenerate all opcodes, grammar and tokens. echo. --experimental-jit Enable the experimental just-in-time compiler. echo. --experimental-jit-off Ditto but off by default (PYTHON_JIT=1 enables). echo. --experimental-jit-interpreter Enable the experimental Tier 2 interpreter. +echo. --pystats Enable PyStats collection. echo. echo.Available flags to avoid building certain modules. echo.These flags have no effect if '-e' is not given: @@ -93,6 +94,7 @@ if "%~1"=="--experimental-jit" (set UseJIT=true) & (set UseTIER2=1) & shift & go if "%~1"=="--experimental-jit-off" (set UseJIT=true) & (set UseTIER2=3) & shift & goto CheckOpts if "%~1"=="--experimental-jit-interpreter" (set UseTIER2=4) & shift & goto CheckOpts if "%~1"=="--experimental-jit-interpreter-off" (set UseTIER2=6) & shift & goto CheckOpts +if "%~1"=="--pystats" (set PyStats=1) & shift & goto CheckOpts rem These use the actual property names used by MSBuild. We could just let rem them in through the environment, but we specify them on the command line rem anyway for visibility so set defaults after this @@ -186,6 +188,7 @@ echo on /p:UseTestMarker=%UseTestMarker% %GITProperty%^ /p:UseJIT=%UseJIT%^ /p:UseTIER2=%UseTIER2%^ + /p:PyStats=%PyStats%^ %1 %2 %3 %4 %5 %6 %7 %8 %9 @echo off diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props index c65341179376ea..17abfa85201a90 100644 --- a/PCbuild/pyproject.props +++ b/PCbuild/pyproject.props @@ -42,6 +42,8 @@ <_DebugPreprocessorDefinition>NDEBUG; <_DebugPreprocessorDefinition Condition="$(Configuration) == 'Debug'">_DEBUG; + <_PyStatsPreprocessorDefinition>PyStats; + <_PyStatsPreprocessorDefinition Condition="$(PySTATS) != ''">Py_STATS; <_PlatformPreprocessorDefinition>_WIN32; <_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64'">_WIN64; <_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64' and $(PlatformToolset) != 'ClangCL'">_M_X64;$(_PlatformPreprocessorDefinition) @@ -50,7 +52,7 @@ $(PySourcePath)Include;$(PySourcePath)Include\internal;$(PySourcePath)Include\internal\mimalloc;$(GeneratedPyConfigDir);$(PySourcePath)PC;%(AdditionalIncludeDirectories) - WIN32;$(_Py3NamePreprocessorDefinition);$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PydPreprocessorDefinition)%(PreprocessorDefinitions) + WIN32;$(_Py3NamePreprocessorDefinition);$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PyStatsPreprocessorDefinition)$(_PydPreprocessorDefinition)%(PreprocessorDefinitions) _Py_USING_PGO=1;%(PreprocessorDefinitions) MaxSpeed From 7b811d0562a0bf7433165785f1549ac199610f8b Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 19 Dec 2024 10:17:15 -0500 Subject: [PATCH 066/376] gh-128008: Add `PyWeakref_IsDead()` (GH-128009) The `PyWeakref_IsDead()` function tests if a weak reference is dead without any side effects. Although you can also detect if a weak reference is dead using `PyWeakref_GetRef()`, that function returns a strong reference that must be `Py_DECREF()`'d, which can introduce side effects if the last reference is concurrently dropped (at least in the free threading build). --- Doc/c-api/weakref.rst | 9 +++++++++ Include/cpython/weakrefobject.h | 3 +++ .../2024-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst | 2 ++ Modules/_testcapimodule.c | 14 ++++++++++++++ Objects/weakrefobject.c | 13 +++++++++++++ 5 files changed, 41 insertions(+) create mode 100644 Misc/NEWS.d/next/C_API/2024-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst diff --git a/Doc/c-api/weakref.rst b/Doc/c-api/weakref.rst index 8f233e16fb17cf..c3c6cf413dcef5 100644 --- a/Doc/c-api/weakref.rst +++ b/Doc/c-api/weakref.rst @@ -88,6 +88,15 @@ as much as it can. Use :c:func:`PyWeakref_GetRef` instead. +.. c:function:: int PyWeakref_IsDead(PyObject *ref) + + Test if the weak reference *ref* is dead. Returns 1 if the reference is + dead, 0 if it is alive, and -1 with an error set if *ref* is not a weak + reference object. + + .. versionadded:: 3.14 + + .. c:function:: void PyObject_ClearWeakRefs(PyObject *object) This function is called by the :c:member:`~PyTypeObject.tp_dealloc` handler diff --git a/Include/cpython/weakrefobject.h b/Include/cpython/weakrefobject.h index 9aa1a92c413fe9..da8e77cddaca63 100644 --- a/Include/cpython/weakrefobject.h +++ b/Include/cpython/weakrefobject.h @@ -45,6 +45,9 @@ PyAPI_FUNC(void) _PyWeakref_ClearRef(PyWeakReference *self); #define _PyWeakref_CAST(op) \ (assert(PyWeakref_Check(op)), _Py_CAST(PyWeakReference*, (op))) +// Test if a weak reference is dead. +PyAPI_FUNC(int) PyWeakref_IsDead(PyObject *ref); + Py_DEPRECATED(3.13) static inline PyObject* PyWeakref_GET_OBJECT(PyObject *ref_obj) { PyWeakReference *ref = _PyWeakref_CAST(ref_obj); diff --git a/Misc/NEWS.d/next/C_API/2024-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst b/Misc/NEWS.d/next/C_API/2024-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst new file mode 100644 index 00000000000000..2349eccac8fedc --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2024-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst @@ -0,0 +1,2 @@ +Add :c:func:`PyWeakref_IsDead` function, which tests if a weak reference is +dead. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 8d86b535effb9a..f737250ac29d57 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3144,6 +3144,7 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) PyObject *ref = UNINITIALIZED_PTR; assert(PyWeakref_GetRef(weakref, &ref) == 1); assert(ref == obj); + assert(!PyWeakref_IsDead(weakref)); assert(Py_REFCNT(obj) == (refcnt + 1)); Py_DECREF(ref); @@ -3159,6 +3160,8 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) assert(Py_REFCNT(obj) == 1); Py_DECREF(obj); + assert(PyWeakref_IsDead(weakref)); + // test PyWeakref_GET_OBJECT(), reference is dead assert(PyWeakref_GET_OBJECT(weakref) == Py_None); @@ -3181,6 +3184,12 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) PyErr_Clear(); assert(ref == NULL); + // test PyWeakRef_IsDead(), invalid type + assert(!PyErr_Occurred()); + assert(PyWeakref_IsDead(invalid_weakref) == -1); + assert(PyErr_ExceptionMatches(PyExc_TypeError)); + PyErr_Clear(); + // test PyWeakref_GetObject(), invalid type assert(PyWeakref_GetObject(invalid_weakref) == NULL); assert(PyErr_ExceptionMatches(PyExc_SystemError)); @@ -3193,6 +3202,11 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) assert(ref == NULL); PyErr_Clear(); + // test PyWeakref_IsDead(NULL) + assert(PyWeakref_IsDead(NULL) == -1); + assert(PyErr_ExceptionMatches(PyExc_SystemError)); + PyErr_Clear(); + // test PyWeakref_GetObject(NULL) assert(PyWeakref_GetObject(NULL) == NULL); assert(PyErr_ExceptionMatches(PyExc_SystemError)); diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 9e3da1c3394d5b..0ee64ed70a63cd 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -932,6 +932,19 @@ PyWeakref_NewProxy(PyObject *ob, PyObject *callback) return (PyObject *)get_or_create_weakref(type, ob, callback); } +int +PyWeakref_IsDead(PyObject *ref) +{ + if (ref == NULL) { + PyErr_BadInternalCall(); + return -1; + } + if (!PyWeakref_Check(ref)) { + PyErr_Format(PyExc_TypeError, "expected a weakref, got %T", ref); + return -1; + } + return _PyWeakref_IS_DEAD(ref); +} int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) From d2f1d917e8b3d2dd8f35495c7632a32688883332 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 19 Dec 2024 16:59:51 +0000 Subject: [PATCH 067/376] GH-122548: Implement branch taken and not taken events for sys.monitoring (GH-122564) --- Doc/c-api/monitoring.rst | 12 +- Doc/library/sys.monitoring.rst | 29 +- Doc/whatsnew/3.13.rst | 2 +- Doc/whatsnew/3.14.rst | 14 + Include/cpython/code.h | 6 +- Include/cpython/monitoring.h | 43 +- Include/internal/pycore_code.h | 2 + Include/internal/pycore_magic_number.h | 3 +- Include/internal/pycore_opcode_metadata.h | 25 +- Include/internal/pycore_opcode_utils.h | 6 + Include/internal/pycore_uop_ids.h | 1 + Include/opcode_ids.h | 198 ++++----- Lib/_opcode_metadata.py | 198 ++++----- Lib/test/test_compiler_codegen.py | 2 + Lib/test/test_dis.py | 391 +++++++++--------- Lib/test/test_monitoring.py | 67 ++- ...-12-13-14-21-04.gh-issue-122548.hq3Vud.rst | 4 + Modules/_testcapi/monitoring.c | 25 +- Objects/codeobject.c | 7 + Programs/test_frozenmain.h | 66 +-- Python/bytecodes.c | 60 ++- Python/codegen.c | 9 +- Python/executor_cases.c.h | 2 + Python/flowgraph.c | 6 + Python/generated_cases.c.h | 74 ++-- Python/instrumentation.c | 321 +++++++++++--- Python/opcode_targets.h | 4 +- Python/optimizer_cases.c.h | 2 + Tools/c-analyzer/cpython/globals-to-fix.tsv | 2 + 29 files changed, 998 insertions(+), 583 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-13-14-21-04.gh-issue-122548.hq3Vud.rst diff --git a/Doc/c-api/monitoring.rst b/Doc/c-api/monitoring.rst index 51d866cfd47469..bda6cd271197d0 100644 --- a/Doc/c-api/monitoring.rst +++ b/Doc/c-api/monitoring.rst @@ -75,9 +75,14 @@ See :mod:`sys.monitoring` for descriptions of the events. Fire a ``JUMP`` event. -.. c:function:: int PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset) +.. c:function:: int PyMonitoring_FireBranchLeftEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset) - Fire a ``BRANCH`` event. + Fire a ``BRANCH_LEFT`` event. + + +.. c:function:: int PyMonitoring_FireBranchRightEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset) + + Fire a ``BRANCH_RIGHT`` event. .. c:function:: int PyMonitoring_FireCReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *retval) @@ -168,7 +173,8 @@ would typically correspond to a python function. ================================================== ===================================== Macro Event ================================================== ===================================== - .. c:macro:: PY_MONITORING_EVENT_BRANCH :monitoring-event:`BRANCH` + .. c:macro:: PY_MONITORING_EVENT_BRANCH_LEFT :monitoring-event:`BRANCH_LEFT` + .. c:macro:: PY_MONITORING_EVENT_BRANCH_RIGHT :monitoring-event:`BRANCH_RIGHT` .. c:macro:: PY_MONITORING_EVENT_CALL :monitoring-event:`CALL` .. c:macro:: PY_MONITORING_EVENT_C_RAISE :monitoring-event:`C_RAISE` .. c:macro:: PY_MONITORING_EVENT_C_RETURN :monitoring-event:`C_RETURN` diff --git a/Doc/library/sys.monitoring.rst b/Doc/library/sys.monitoring.rst index f7140af2494898..cfdcdf2e2df476 100644 --- a/Doc/library/sys.monitoring.rst +++ b/Doc/library/sys.monitoring.rst @@ -79,9 +79,17 @@ Events The following events are supported: -.. monitoring-event:: BRANCH +.. monitoring-event:: BRANCH_LEFT - A conditional branch is taken (or not). + A conditional branch goes left. + + It is up to the tool to determine how to present "left" and "right" branches. + There is no guarantee which branch is "left" and which is "right", except + that it will be consistent for the duration of the program. + +.. monitoring-event:: BRANCH_RIGHT + + A conditional branch goes right. .. monitoring-event:: CALL @@ -180,9 +188,20 @@ The local events are: * :monitoring-event:`LINE` * :monitoring-event:`INSTRUCTION` * :monitoring-event:`JUMP` -* :monitoring-event:`BRANCH` +* :monitoring-event:`BRANCH_LEFT` +* :monitoring-event:`BRANCH_RIGHT` * :monitoring-event:`STOP_ITERATION` +Deprecated event +'''''''''''''''' + +* ``BRANCH`` + +The ``BRANCH`` event is deprecated in 3.14. +Using :monitoring-event:`BRANCH_LEFT` and :monitoring-event:`BRANCH_RIGHT` +events will give much better performance as they can be disabled +independently. + Ancillary events '''''''''''''''' @@ -357,13 +376,11 @@ Different events will provide the callback function with different arguments, as func(code: CodeType, line_number: int) -> DISABLE | Any -* :monitoring-event:`BRANCH` and :monitoring-event:`JUMP`:: +* :monitoring-event:`BRANCH_LEFT`, :monitoring-event:`BRANCH_RIGHT` and :monitoring-event:`JUMP`:: func(code: CodeType, instruction_offset: int, destination_offset: int) -> DISABLE | Any Note that the *destination_offset* is where the code will next execute. - For an untaken branch this will be the offset of the instruction following - the branch. * :monitoring-event:`INSTRUCTION`:: diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 9f6d98b9950d19..45cc1b5bad9b18 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1971,7 +1971,7 @@ New Features * :c:func:`PyMonitoring_FireCallEvent` * :c:func:`PyMonitoring_FireLineEvent` * :c:func:`PyMonitoring_FireJumpEvent` - * :c:func:`PyMonitoring_FireBranchEvent` + * ``PyMonitoring_FireBranchEvent`` * :c:func:`PyMonitoring_FireCReturnEvent` * :c:func:`PyMonitoring_FirePyThrowEvent` * :c:func:`PyMonitoring_FireRaiseEvent` diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 2e43dce5e061b4..97a37a82f76b9b 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -603,6 +603,11 @@ sys which only exists in specialized builds of Python, may now return objects from other interpreters than the one it's called in. +sys.monitoring +-------------- + +Two new events are added: :monitoring-event:`BRANCH_LEFT` and +:monitoring-event:`BRANCH_RIGHT`. The ``BRANCH`` event is deprecated. tkinter ------- @@ -1144,6 +1149,11 @@ New features a :exc:`UnicodeError` object. (Contributed by Bénédikt Tran in :gh:`127691`.) +* Add :c:func:`PyMonitoring_FireBranchLeftEvent` and + :c:func:`PyMonitoring_FireBranchRightEvent` for generating + :monitoring-event:`BRANCH_LEFT` and :monitoring-event:`BRANCH_RIGHT` + events, respectively. + Porting to Python 3.14 ---------------------- @@ -1177,6 +1187,10 @@ Deprecated .. include:: ../deprecations/c-api-pending-removal-in-future.rst +* The ``PyMonitoring_FireBranchEvent`` function is deprecated and should + be replaced with calls to :c:func:`PyMonitoring_FireBranchLeftEvent` + and :c:func:`PyMonitoring_FireBranchRightEvent`. + Removed ------- diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 3899d4269233a1..c3c0165d556ead 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -11,11 +11,11 @@ extern "C" { /* Total tool ids available */ #define _PY_MONITORING_TOOL_IDS 8 /* Count of all local monitoring events */ -#define _PY_MONITORING_LOCAL_EVENTS 10 +#define _PY_MONITORING_LOCAL_EVENTS 11 /* Count of all "real" monitoring events (not derived from other events) */ -#define _PY_MONITORING_UNGROUPED_EVENTS 15 +#define _PY_MONITORING_UNGROUPED_EVENTS 16 /* Count of all monitoring events */ -#define _PY_MONITORING_EVENTS 17 +#define _PY_MONITORING_EVENTS 19 /* Tables of which tools are active for each monitored event. */ typedef struct _Py_LocalMonitors { diff --git a/Include/cpython/monitoring.h b/Include/cpython/monitoring.h index 797ba51246b1c6..ce92942404c9f7 100644 --- a/Include/cpython/monitoring.h +++ b/Include/cpython/monitoring.h @@ -13,25 +13,27 @@ #define PY_MONITORING_EVENT_LINE 5 #define PY_MONITORING_EVENT_INSTRUCTION 6 #define PY_MONITORING_EVENT_JUMP 7 -#define PY_MONITORING_EVENT_BRANCH 8 -#define PY_MONITORING_EVENT_STOP_ITERATION 9 +#define PY_MONITORING_EVENT_BRANCH_LEFT 8 +#define PY_MONITORING_EVENT_BRANCH_RIGHT 9 +#define PY_MONITORING_EVENT_STOP_ITERATION 10 #define PY_MONITORING_IS_INSTRUMENTED_EVENT(ev) \ ((ev) < _PY_MONITORING_LOCAL_EVENTS) /* Other events, mainly exceptions */ -#define PY_MONITORING_EVENT_RAISE 10 -#define PY_MONITORING_EVENT_EXCEPTION_HANDLED 11 -#define PY_MONITORING_EVENT_PY_UNWIND 12 -#define PY_MONITORING_EVENT_PY_THROW 13 -#define PY_MONITORING_EVENT_RERAISE 14 +#define PY_MONITORING_EVENT_RAISE 11 +#define PY_MONITORING_EVENT_EXCEPTION_HANDLED 12 +#define PY_MONITORING_EVENT_PY_UNWIND 13 +#define PY_MONITORING_EVENT_PY_THROW 14 +#define PY_MONITORING_EVENT_RERAISE 15 /* Ancillary events */ -#define PY_MONITORING_EVENT_C_RETURN 15 -#define PY_MONITORING_EVENT_C_RAISE 16 +#define PY_MONITORING_EVENT_C_RETURN 16 +#define PY_MONITORING_EVENT_C_RAISE 17 +#define PY_MONITORING_EVENT_BRANCH 18 typedef struct _PyMonitoringState { @@ -74,10 +76,18 @@ PyAPI_FUNC(int) _PyMonitoring_FireJumpEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset); -PyAPI_FUNC(int) +Py_DEPRECATED(3.14) PyAPI_FUNC(int) _PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset); +PyAPI_FUNC(int) +_PyMonitoring_FireBranchRightEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *target_offset); + +PyAPI_FUNC(int) +_PyMonitoring_FireBranchLeftEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *target_offset); + PyAPI_FUNC(int) _PyMonitoring_FireCReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *retval); @@ -174,12 +184,21 @@ PyMonitoring_FireJumpEvent(PyMonitoringState *state, PyObject *codelike, int32_t } static inline int -PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, +PyMonitoring_FireBranchRightEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *target_offset) +{ + _PYMONITORING_IF_ACTIVE( + state, + _PyMonitoring_FireBranchRightEvent(state, codelike, offset, target_offset)); +} + +static inline int +PyMonitoring_FireBranchLeftEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset) { _PYMONITORING_IF_ACTIVE( state, - _PyMonitoring_FireBranchEvent(state, codelike, offset, target_offset)); + _PyMonitoring_FireBranchLeftEvent(state, codelike, offset, target_offset)); } static inline int diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index d607a54aa4a2f5..d97fe81a2fc54a 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -603,6 +603,8 @@ extern _Py_CODEUNIT _Py_GetBaseCodeUnit(PyCodeObject *code, int offset); extern int _PyInstruction_GetLength(PyCodeObject *code, int offset); +extern PyObject *_PyInstrumentation_BranchesIterator(PyCodeObject *code); + struct _PyCode8 _PyCode_DEF(8); PyAPI_DATA(const struct _PyCode8) _Py_InitCleanup; diff --git a/Include/internal/pycore_magic_number.h b/Include/internal/pycore_magic_number.h index 14e29576875c6e..079a9befcd4c5e 100644 --- a/Include/internal/pycore_magic_number.h +++ b/Include/internal/pycore_magic_number.h @@ -262,6 +262,7 @@ Known values: Python 3.14a1 3607 (Add pseudo instructions JUMP_IF_TRUE/FALSE) Python 3.14a1 3608 (Add support for slices) Python 3.14a2 3609 (Add LOAD_SMALL_INT and LOAD_CONST_IMMORTAL instructions, remove RETURN_CONST) + Python 3.14a3 3610 (Add NOT_TAKEN instruction) Python 3.15 will start with 3650 @@ -274,7 +275,7 @@ PC/launcher.c must also be updated. */ -#define PYC_MAGIC_NUMBER 3609 +#define PYC_MAGIC_NUMBER 3611 /* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes (little-endian) and then appending b'\r\n'. */ #define PYC_MAGIC_NUMBER_TOKEN \ diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index d2ae8928a8fe8f..e09fff062b5202 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -243,6 +243,8 @@ int _PyOpcode_num_popped(int opcode, int oparg) { return 0; case INSTRUMENTED_LOAD_SUPER_ATTR: return 0; + case INSTRUMENTED_NOT_TAKEN: + return 0; case INSTRUMENTED_POP_JUMP_IF_FALSE: return 0; case INSTRUMENTED_POP_JUMP_IF_NONE: @@ -367,6 +369,8 @@ int _PyOpcode_num_popped(int opcode, int oparg) { return 1; case NOP: return 0; + case NOT_TAKEN: + return 0; case POP_BLOCK: return 0; case POP_EXCEPT: @@ -702,6 +706,8 @@ int _PyOpcode_num_pushed(int opcode, int oparg) { return 0; case INSTRUMENTED_LOAD_SUPER_ATTR: return 0; + case INSTRUMENTED_NOT_TAKEN: + return 0; case INSTRUMENTED_POP_JUMP_IF_FALSE: return 0; case INSTRUMENTED_POP_JUMP_IF_NONE: @@ -826,6 +832,8 @@ int _PyOpcode_num_pushed(int opcode, int oparg) { return 2; case NOP: return 0; + case NOT_TAKEN: + return 0; case POP_BLOCK: return 0; case POP_EXCEPT: @@ -1387,6 +1395,10 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) { *effect = 0; return 0; } + case INSTRUMENTED_NOT_TAKEN: { + *effect = 0; + return 0; + } case INSTRUMENTED_POP_JUMP_IF_FALSE: { *effect = 0; return 0; @@ -1635,6 +1647,10 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) { *effect = 0; return 0; } + case NOT_TAKEN: { + *effect = 0; + return 0; + } case POP_BLOCK: { *effect = 0; return 0; @@ -2043,6 +2059,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [INSTRUMENTED_JUMP_FORWARD] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, [INSTRUMENTED_LINE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, [INSTRUMENTED_LOAD_SUPER_ATTR] = { true, INSTR_FMT_IXC, 0 }, + [INSTRUMENTED_NOT_TAKEN] = { true, INSTR_FMT_IX, 0 }, [INSTRUMENTED_POP_JUMP_IF_FALSE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG }, [INSTRUMENTED_POP_JUMP_IF_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG }, [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG }, @@ -2100,6 +2117,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [MATCH_MAPPING] = { true, INSTR_FMT_IX, 0 }, [MATCH_SEQUENCE] = { true, INSTR_FMT_IX, 0 }, [NOP] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, + [NOT_TAKEN] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, [POP_EXCEPT] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, [POP_JUMP_IF_FALSE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [POP_JUMP_IF_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, @@ -2304,6 +2322,7 @@ _PyOpcode_macro_expansion[256] = { [MATCH_MAPPING] = { .nuops = 1, .uops = { { _MATCH_MAPPING, 0, 0 } } }, [MATCH_SEQUENCE] = { .nuops = 1, .uops = { { _MATCH_SEQUENCE, 0, 0 } } }, [NOP] = { .nuops = 1, .uops = { { _NOP, 0, 0 } } }, + [NOT_TAKEN] = { .nuops = 1, .uops = { { _NOP, 0, 0 } } }, [POP_EXCEPT] = { .nuops = 1, .uops = { { _POP_EXCEPT, 0, 0 } } }, [POP_JUMP_IF_FALSE] = { .nuops = 1, .uops = { { _POP_JUMP_IF_FALSE, 9, 1 } } }, [POP_JUMP_IF_NONE] = { .nuops = 2, .uops = { { _IS_NONE, 0, 0 }, { _POP_JUMP_IF_TRUE, 9, 1 } } }, @@ -2462,6 +2481,7 @@ const char *_PyOpcode_OpName[266] = { [INSTRUMENTED_JUMP_FORWARD] = "INSTRUMENTED_JUMP_FORWARD", [INSTRUMENTED_LINE] = "INSTRUMENTED_LINE", [INSTRUMENTED_LOAD_SUPER_ATTR] = "INSTRUMENTED_LOAD_SUPER_ATTR", + [INSTRUMENTED_NOT_TAKEN] = "INSTRUMENTED_NOT_TAKEN", [INSTRUMENTED_POP_JUMP_IF_FALSE] = "INSTRUMENTED_POP_JUMP_IF_FALSE", [INSTRUMENTED_POP_JUMP_IF_NONE] = "INSTRUMENTED_POP_JUMP_IF_NONE", [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = "INSTRUMENTED_POP_JUMP_IF_NOT_NONE", @@ -2524,6 +2544,7 @@ const char *_PyOpcode_OpName[266] = { [MATCH_MAPPING] = "MATCH_MAPPING", [MATCH_SEQUENCE] = "MATCH_SEQUENCE", [NOP] = "NOP", + [NOT_TAKEN] = "NOT_TAKEN", [POP_BLOCK] = "POP_BLOCK", [POP_EXCEPT] = "POP_EXCEPT", [POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE", @@ -2718,6 +2739,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [INSTRUMENTED_JUMP_FORWARD] = INSTRUMENTED_JUMP_FORWARD, [INSTRUMENTED_LINE] = INSTRUMENTED_LINE, [INSTRUMENTED_LOAD_SUPER_ATTR] = INSTRUMENTED_LOAD_SUPER_ATTR, + [INSTRUMENTED_NOT_TAKEN] = INSTRUMENTED_NOT_TAKEN, [INSTRUMENTED_POP_JUMP_IF_FALSE] = INSTRUMENTED_POP_JUMP_IF_FALSE, [INSTRUMENTED_POP_JUMP_IF_NONE] = INSTRUMENTED_POP_JUMP_IF_NONE, [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = INSTRUMENTED_POP_JUMP_IF_NOT_NONE, @@ -2775,6 +2797,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [MATCH_MAPPING] = MATCH_MAPPING, [MATCH_SEQUENCE] = MATCH_SEQUENCE, [NOP] = NOP, + [NOT_TAKEN] = NOT_TAKEN, [POP_EXCEPT] = POP_EXCEPT, [POP_JUMP_IF_FALSE] = POP_JUMP_IF_FALSE, [POP_JUMP_IF_NONE] = POP_JUMP_IF_NONE, @@ -2833,7 +2856,6 @@ const uint8_t _PyOpcode_Deopt[256] = { #endif // NEED_OPCODE_METADATA #define EXTRA_CASES \ - case 116: \ case 117: \ case 118: \ case 119: \ @@ -2874,7 +2896,6 @@ const uint8_t _PyOpcode_Deopt[256] = { case 233: \ case 234: \ case 235: \ - case 236: \ ; struct pseudo_targets { uint8_t as_sequence; diff --git a/Include/internal/pycore_opcode_utils.h b/Include/internal/pycore_opcode_utils.h index c6ce7e65a65d4b..0872231d1f2d11 100644 --- a/Include/internal/pycore_opcode_utils.h +++ b/Include/internal/pycore_opcode_utils.h @@ -45,6 +45,12 @@ extern "C" { (opcode) == JUMP_BACKWARD || \ (opcode) == JUMP_BACKWARD_NO_INTERRUPT) +#define IS_CONDITIONAL_JUMP_OPCODE(opcode) \ + ((opcode) == POP_JUMP_IF_FALSE || \ + (opcode) == POP_JUMP_IF_TRUE || \ + (opcode) == POP_JUMP_IF_NONE || \ + (opcode) == POP_JUMP_IF_NOT_NONE) + #define IS_SCOPE_EXIT_OPCODE(opcode) \ ((opcode) == RETURN_VALUE || \ (opcode) == RAISE_VARARGS || \ diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 45563585dd5681..5fc57e48f500d0 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -155,6 +155,7 @@ extern "C" { #define _INSTRUMENTED_JUMP_FORWARD INSTRUMENTED_JUMP_FORWARD #define _INSTRUMENTED_LINE INSTRUMENTED_LINE #define _INSTRUMENTED_LOAD_SUPER_ATTR INSTRUMENTED_LOAD_SUPER_ATTR +#define _INSTRUMENTED_NOT_TAKEN INSTRUMENTED_NOT_TAKEN #define _INSTRUMENTED_POP_JUMP_IF_FALSE INSTRUMENTED_POP_JUMP_IF_FALSE #define _INSTRUMENTED_POP_JUMP_IF_NONE INSTRUMENTED_POP_JUMP_IF_NONE #define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE INSTRUMENTED_POP_JUMP_IF_NOT_NONE diff --git a/Include/opcode_ids.h b/Include/opcode_ids.h index ce3d23eaa6d56d..3cd189b93dd9d6 100644 --- a/Include/opcode_ids.h +++ b/Include/opcode_ids.h @@ -38,94 +38,95 @@ extern "C" { #define MATCH_MAPPING 25 #define MATCH_SEQUENCE 26 #define NOP 27 -#define POP_EXCEPT 28 -#define POP_TOP 29 -#define PUSH_EXC_INFO 30 -#define PUSH_NULL 31 -#define RETURN_GENERATOR 32 -#define RETURN_VALUE 33 -#define SETUP_ANNOTATIONS 34 -#define STORE_SLICE 35 -#define STORE_SUBSCR 36 -#define TO_BOOL 37 -#define UNARY_INVERT 38 -#define UNARY_NEGATIVE 39 -#define UNARY_NOT 40 -#define WITH_EXCEPT_START 41 -#define BINARY_OP 42 -#define BUILD_LIST 43 -#define BUILD_MAP 44 -#define BUILD_SET 45 -#define BUILD_SLICE 46 -#define BUILD_STRING 47 -#define BUILD_TUPLE 48 -#define CALL 49 -#define CALL_FUNCTION_EX 50 -#define CALL_INTRINSIC_1 51 -#define CALL_INTRINSIC_2 52 -#define CALL_KW 53 -#define COMPARE_OP 54 -#define CONTAINS_OP 55 -#define CONVERT_VALUE 56 -#define COPY 57 -#define COPY_FREE_VARS 58 -#define DELETE_ATTR 59 -#define DELETE_DEREF 60 -#define DELETE_FAST 61 -#define DELETE_GLOBAL 62 -#define DELETE_NAME 63 -#define DICT_MERGE 64 -#define DICT_UPDATE 65 -#define EXTENDED_ARG 66 -#define FOR_ITER 67 -#define GET_AWAITABLE 68 -#define IMPORT_FROM 69 -#define IMPORT_NAME 70 -#define IS_OP 71 -#define JUMP_BACKWARD 72 -#define JUMP_BACKWARD_NO_INTERRUPT 73 -#define JUMP_FORWARD 74 -#define LIST_APPEND 75 -#define LIST_EXTEND 76 -#define LOAD_ATTR 77 -#define LOAD_COMMON_CONSTANT 78 -#define LOAD_CONST 79 -#define LOAD_DEREF 80 -#define LOAD_FAST 81 -#define LOAD_FAST_AND_CLEAR 82 -#define LOAD_FAST_CHECK 83 -#define LOAD_FAST_LOAD_FAST 84 -#define LOAD_FROM_DICT_OR_DEREF 85 -#define LOAD_FROM_DICT_OR_GLOBALS 86 -#define LOAD_GLOBAL 87 -#define LOAD_NAME 88 -#define LOAD_SMALL_INT 89 -#define LOAD_SPECIAL 90 -#define LOAD_SUPER_ATTR 91 -#define MAKE_CELL 92 -#define MAP_ADD 93 -#define MATCH_CLASS 94 -#define POP_JUMP_IF_FALSE 95 -#define POP_JUMP_IF_NONE 96 -#define POP_JUMP_IF_NOT_NONE 97 -#define POP_JUMP_IF_TRUE 98 -#define RAISE_VARARGS 99 -#define RERAISE 100 -#define SEND 101 -#define SET_ADD 102 -#define SET_FUNCTION_ATTRIBUTE 103 -#define SET_UPDATE 104 -#define STORE_ATTR 105 -#define STORE_DEREF 106 -#define STORE_FAST 107 -#define STORE_FAST_LOAD_FAST 108 -#define STORE_FAST_STORE_FAST 109 -#define STORE_GLOBAL 110 -#define STORE_NAME 111 -#define SWAP 112 -#define UNPACK_EX 113 -#define UNPACK_SEQUENCE 114 -#define YIELD_VALUE 115 +#define NOT_TAKEN 28 +#define POP_EXCEPT 29 +#define POP_TOP 30 +#define PUSH_EXC_INFO 31 +#define PUSH_NULL 32 +#define RETURN_GENERATOR 33 +#define RETURN_VALUE 34 +#define SETUP_ANNOTATIONS 35 +#define STORE_SLICE 36 +#define STORE_SUBSCR 37 +#define TO_BOOL 38 +#define UNARY_INVERT 39 +#define UNARY_NEGATIVE 40 +#define UNARY_NOT 41 +#define WITH_EXCEPT_START 42 +#define BINARY_OP 43 +#define BUILD_LIST 44 +#define BUILD_MAP 45 +#define BUILD_SET 46 +#define BUILD_SLICE 47 +#define BUILD_STRING 48 +#define BUILD_TUPLE 49 +#define CALL 50 +#define CALL_FUNCTION_EX 51 +#define CALL_INTRINSIC_1 52 +#define CALL_INTRINSIC_2 53 +#define CALL_KW 54 +#define COMPARE_OP 55 +#define CONTAINS_OP 56 +#define CONVERT_VALUE 57 +#define COPY 58 +#define COPY_FREE_VARS 59 +#define DELETE_ATTR 60 +#define DELETE_DEREF 61 +#define DELETE_FAST 62 +#define DELETE_GLOBAL 63 +#define DELETE_NAME 64 +#define DICT_MERGE 65 +#define DICT_UPDATE 66 +#define EXTENDED_ARG 67 +#define FOR_ITER 68 +#define GET_AWAITABLE 69 +#define IMPORT_FROM 70 +#define IMPORT_NAME 71 +#define IS_OP 72 +#define JUMP_BACKWARD 73 +#define JUMP_BACKWARD_NO_INTERRUPT 74 +#define JUMP_FORWARD 75 +#define LIST_APPEND 76 +#define LIST_EXTEND 77 +#define LOAD_ATTR 78 +#define LOAD_COMMON_CONSTANT 79 +#define LOAD_CONST 80 +#define LOAD_DEREF 81 +#define LOAD_FAST 82 +#define LOAD_FAST_AND_CLEAR 83 +#define LOAD_FAST_CHECK 84 +#define LOAD_FAST_LOAD_FAST 85 +#define LOAD_FROM_DICT_OR_DEREF 86 +#define LOAD_FROM_DICT_OR_GLOBALS 87 +#define LOAD_GLOBAL 88 +#define LOAD_NAME 89 +#define LOAD_SMALL_INT 90 +#define LOAD_SPECIAL 91 +#define LOAD_SUPER_ATTR 92 +#define MAKE_CELL 93 +#define MAP_ADD 94 +#define MATCH_CLASS 95 +#define POP_JUMP_IF_FALSE 96 +#define POP_JUMP_IF_NONE 97 +#define POP_JUMP_IF_NOT_NONE 98 +#define POP_JUMP_IF_TRUE 99 +#define RAISE_VARARGS 100 +#define RERAISE 101 +#define SEND 102 +#define SET_ADD 103 +#define SET_FUNCTION_ATTRIBUTE 104 +#define SET_UPDATE 105 +#define STORE_ATTR 106 +#define STORE_DEREF 107 +#define STORE_FAST 108 +#define STORE_FAST_LOAD_FAST 109 +#define STORE_FAST_STORE_FAST 110 +#define STORE_GLOBAL 111 +#define STORE_NAME 112 +#define SWAP 113 +#define UNPACK_EX 114 +#define UNPACK_SEQUENCE 115 +#define YIELD_VALUE 116 #define RESUME 149 #define BINARY_OP_ADD_FLOAT 150 #define BINARY_OP_ADD_INT 151 @@ -205,14 +206,15 @@ extern "C" { #define UNPACK_SEQUENCE_LIST 225 #define UNPACK_SEQUENCE_TUPLE 226 #define UNPACK_SEQUENCE_TWO_TUPLE 227 -#define INSTRUMENTED_END_FOR 237 -#define INSTRUMENTED_END_SEND 238 -#define INSTRUMENTED_LOAD_SUPER_ATTR 239 -#define INSTRUMENTED_FOR_ITER 240 -#define INSTRUMENTED_CALL_KW 241 -#define INSTRUMENTED_CALL_FUNCTION_EX 242 -#define INSTRUMENTED_INSTRUCTION 243 -#define INSTRUMENTED_JUMP_FORWARD 244 +#define INSTRUMENTED_END_FOR 236 +#define INSTRUMENTED_END_SEND 237 +#define INSTRUMENTED_LOAD_SUPER_ATTR 238 +#define INSTRUMENTED_FOR_ITER 239 +#define INSTRUMENTED_CALL_KW 240 +#define INSTRUMENTED_CALL_FUNCTION_EX 241 +#define INSTRUMENTED_INSTRUCTION 242 +#define INSTRUMENTED_JUMP_FORWARD 243 +#define INSTRUMENTED_NOT_TAKEN 244 #define INSTRUMENTED_POP_JUMP_IF_TRUE 245 #define INSTRUMENTED_POP_JUMP_IF_FALSE 246 #define INSTRUMENTED_POP_JUMP_IF_NONE 247 @@ -235,9 +237,9 @@ extern "C" { #define SETUP_WITH 264 #define STORE_FAST_MAYBE_NULL 265 -#define HAVE_ARGUMENT 41 +#define HAVE_ARGUMENT 42 #define MIN_SPECIALIZED_OPCODE 150 -#define MIN_INSTRUMENTED_OPCODE 237 +#define MIN_INSTRUMENTED_OPCODE 236 #ifdef __cplusplus } diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py index cda3c340c322f3..dada2cb5fa033f 100644 --- a/Lib/_opcode_metadata.py +++ b/Lib/_opcode_metadata.py @@ -231,102 +231,104 @@ 'MATCH_MAPPING': 25, 'MATCH_SEQUENCE': 26, 'NOP': 27, - 'POP_EXCEPT': 28, - 'POP_TOP': 29, - 'PUSH_EXC_INFO': 30, - 'PUSH_NULL': 31, - 'RETURN_GENERATOR': 32, - 'RETURN_VALUE': 33, - 'SETUP_ANNOTATIONS': 34, - 'STORE_SLICE': 35, - 'STORE_SUBSCR': 36, - 'TO_BOOL': 37, - 'UNARY_INVERT': 38, - 'UNARY_NEGATIVE': 39, - 'UNARY_NOT': 40, - 'WITH_EXCEPT_START': 41, - 'BINARY_OP': 42, - 'BUILD_LIST': 43, - 'BUILD_MAP': 44, - 'BUILD_SET': 45, - 'BUILD_SLICE': 46, - 'BUILD_STRING': 47, - 'BUILD_TUPLE': 48, - 'CALL': 49, - 'CALL_FUNCTION_EX': 50, - 'CALL_INTRINSIC_1': 51, - 'CALL_INTRINSIC_2': 52, - 'CALL_KW': 53, - 'COMPARE_OP': 54, - 'CONTAINS_OP': 55, - 'CONVERT_VALUE': 56, - 'COPY': 57, - 'COPY_FREE_VARS': 58, - 'DELETE_ATTR': 59, - 'DELETE_DEREF': 60, - 'DELETE_FAST': 61, - 'DELETE_GLOBAL': 62, - 'DELETE_NAME': 63, - 'DICT_MERGE': 64, - 'DICT_UPDATE': 65, - 'EXTENDED_ARG': 66, - 'FOR_ITER': 67, - 'GET_AWAITABLE': 68, - 'IMPORT_FROM': 69, - 'IMPORT_NAME': 70, - 'IS_OP': 71, - 'JUMP_BACKWARD': 72, - 'JUMP_BACKWARD_NO_INTERRUPT': 73, - 'JUMP_FORWARD': 74, - 'LIST_APPEND': 75, - 'LIST_EXTEND': 76, - 'LOAD_ATTR': 77, - 'LOAD_COMMON_CONSTANT': 78, - 'LOAD_CONST': 79, - 'LOAD_DEREF': 80, - 'LOAD_FAST': 81, - 'LOAD_FAST_AND_CLEAR': 82, - 'LOAD_FAST_CHECK': 83, - 'LOAD_FAST_LOAD_FAST': 84, - 'LOAD_FROM_DICT_OR_DEREF': 85, - 'LOAD_FROM_DICT_OR_GLOBALS': 86, - 'LOAD_GLOBAL': 87, - 'LOAD_NAME': 88, - 'LOAD_SMALL_INT': 89, - 'LOAD_SPECIAL': 90, - 'LOAD_SUPER_ATTR': 91, - 'MAKE_CELL': 92, - 'MAP_ADD': 93, - 'MATCH_CLASS': 94, - 'POP_JUMP_IF_FALSE': 95, - 'POP_JUMP_IF_NONE': 96, - 'POP_JUMP_IF_NOT_NONE': 97, - 'POP_JUMP_IF_TRUE': 98, - 'RAISE_VARARGS': 99, - 'RERAISE': 100, - 'SEND': 101, - 'SET_ADD': 102, - 'SET_FUNCTION_ATTRIBUTE': 103, - 'SET_UPDATE': 104, - 'STORE_ATTR': 105, - 'STORE_DEREF': 106, - 'STORE_FAST': 107, - 'STORE_FAST_LOAD_FAST': 108, - 'STORE_FAST_STORE_FAST': 109, - 'STORE_GLOBAL': 110, - 'STORE_NAME': 111, - 'SWAP': 112, - 'UNPACK_EX': 113, - 'UNPACK_SEQUENCE': 114, - 'YIELD_VALUE': 115, - 'INSTRUMENTED_END_FOR': 237, - 'INSTRUMENTED_END_SEND': 238, - 'INSTRUMENTED_LOAD_SUPER_ATTR': 239, - 'INSTRUMENTED_FOR_ITER': 240, - 'INSTRUMENTED_CALL_KW': 241, - 'INSTRUMENTED_CALL_FUNCTION_EX': 242, - 'INSTRUMENTED_INSTRUCTION': 243, - 'INSTRUMENTED_JUMP_FORWARD': 244, + 'NOT_TAKEN': 28, + 'POP_EXCEPT': 29, + 'POP_TOP': 30, + 'PUSH_EXC_INFO': 31, + 'PUSH_NULL': 32, + 'RETURN_GENERATOR': 33, + 'RETURN_VALUE': 34, + 'SETUP_ANNOTATIONS': 35, + 'STORE_SLICE': 36, + 'STORE_SUBSCR': 37, + 'TO_BOOL': 38, + 'UNARY_INVERT': 39, + 'UNARY_NEGATIVE': 40, + 'UNARY_NOT': 41, + 'WITH_EXCEPT_START': 42, + 'BINARY_OP': 43, + 'BUILD_LIST': 44, + 'BUILD_MAP': 45, + 'BUILD_SET': 46, + 'BUILD_SLICE': 47, + 'BUILD_STRING': 48, + 'BUILD_TUPLE': 49, + 'CALL': 50, + 'CALL_FUNCTION_EX': 51, + 'CALL_INTRINSIC_1': 52, + 'CALL_INTRINSIC_2': 53, + 'CALL_KW': 54, + 'COMPARE_OP': 55, + 'CONTAINS_OP': 56, + 'CONVERT_VALUE': 57, + 'COPY': 58, + 'COPY_FREE_VARS': 59, + 'DELETE_ATTR': 60, + 'DELETE_DEREF': 61, + 'DELETE_FAST': 62, + 'DELETE_GLOBAL': 63, + 'DELETE_NAME': 64, + 'DICT_MERGE': 65, + 'DICT_UPDATE': 66, + 'EXTENDED_ARG': 67, + 'FOR_ITER': 68, + 'GET_AWAITABLE': 69, + 'IMPORT_FROM': 70, + 'IMPORT_NAME': 71, + 'IS_OP': 72, + 'JUMP_BACKWARD': 73, + 'JUMP_BACKWARD_NO_INTERRUPT': 74, + 'JUMP_FORWARD': 75, + 'LIST_APPEND': 76, + 'LIST_EXTEND': 77, + 'LOAD_ATTR': 78, + 'LOAD_COMMON_CONSTANT': 79, + 'LOAD_CONST': 80, + 'LOAD_DEREF': 81, + 'LOAD_FAST': 82, + 'LOAD_FAST_AND_CLEAR': 83, + 'LOAD_FAST_CHECK': 84, + 'LOAD_FAST_LOAD_FAST': 85, + 'LOAD_FROM_DICT_OR_DEREF': 86, + 'LOAD_FROM_DICT_OR_GLOBALS': 87, + 'LOAD_GLOBAL': 88, + 'LOAD_NAME': 89, + 'LOAD_SMALL_INT': 90, + 'LOAD_SPECIAL': 91, + 'LOAD_SUPER_ATTR': 92, + 'MAKE_CELL': 93, + 'MAP_ADD': 94, + 'MATCH_CLASS': 95, + 'POP_JUMP_IF_FALSE': 96, + 'POP_JUMP_IF_NONE': 97, + 'POP_JUMP_IF_NOT_NONE': 98, + 'POP_JUMP_IF_TRUE': 99, + 'RAISE_VARARGS': 100, + 'RERAISE': 101, + 'SEND': 102, + 'SET_ADD': 103, + 'SET_FUNCTION_ATTRIBUTE': 104, + 'SET_UPDATE': 105, + 'STORE_ATTR': 106, + 'STORE_DEREF': 107, + 'STORE_FAST': 108, + 'STORE_FAST_LOAD_FAST': 109, + 'STORE_FAST_STORE_FAST': 110, + 'STORE_GLOBAL': 111, + 'STORE_NAME': 112, + 'SWAP': 113, + 'UNPACK_EX': 114, + 'UNPACK_SEQUENCE': 115, + 'YIELD_VALUE': 116, + 'INSTRUMENTED_END_FOR': 236, + 'INSTRUMENTED_END_SEND': 237, + 'INSTRUMENTED_LOAD_SUPER_ATTR': 238, + 'INSTRUMENTED_FOR_ITER': 239, + 'INSTRUMENTED_CALL_KW': 240, + 'INSTRUMENTED_CALL_FUNCTION_EX': 241, + 'INSTRUMENTED_INSTRUCTION': 242, + 'INSTRUMENTED_JUMP_FORWARD': 243, + 'INSTRUMENTED_NOT_TAKEN': 244, 'INSTRUMENTED_POP_JUMP_IF_TRUE': 245, 'INSTRUMENTED_POP_JUMP_IF_FALSE': 246, 'INSTRUMENTED_POP_JUMP_IF_NONE': 247, @@ -348,5 +350,5 @@ 'STORE_FAST_MAYBE_NULL': 265, } -HAVE_ARGUMENT = 41 -MIN_INSTRUMENTED_OPCODE = 237 +HAVE_ARGUMENT = 42 +MIN_INSTRUMENTED_OPCODE = 236 diff --git a/Lib/test/test_compiler_codegen.py b/Lib/test/test_compiler_codegen.py index 2dd7cf65ee3c2a..f8c4fc14c91ebe 100644 --- a/Lib/test/test_compiler_codegen.py +++ b/Lib/test/test_compiler_codegen.py @@ -29,6 +29,7 @@ def test_if_expression(self): ('LOAD_CONST', 0, 1), ('TO_BOOL', 0, 1), ('POP_JUMP_IF_FALSE', false_lbl := self.Label(), 1), + ('NOT_TAKEN', None, 1), ('LOAD_SMALL_INT', 42, 1), ('JUMP_NO_INTERRUPT', exit_lbl := self.Label()), false_lbl, @@ -49,6 +50,7 @@ def test_for_loop(self): ('GET_ITER', None, 1), loop_lbl := self.Label(), ('FOR_ITER', exit_lbl := self.Label(), 1), + ('NOT_TAKEN', None, 1), ('NOP', None, 1, 1), ('STORE_NAME', 1, 1), ('LOAD_NAME', 2, 2), diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index c719f571152d61..955a3e4cb9e4f7 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -175,10 +175,11 @@ def bug708901(): %3d CALL 2 GET_ITER - L1: FOR_ITER 3 (to L2) + L1: FOR_ITER 4 (to L2) + NOT_TAKEN STORE_FAST 0 (res) -%3d JUMP_BACKWARD 5 (to L1) +%3d JUMP_BACKWARD 6 (to L1) %3d L2: END_FOR POP_TOP @@ -200,7 +201,8 @@ def bug1333982(x=[]): dis_bug1333982 = """\ %3d RESUME 0 -%3d LOAD_COMMON_CONSTANT 0 (AssertionError) +%3d NOT_TAKEN + LOAD_COMMON_CONSTANT 0 (AssertionError) LOAD_CONST 0 ( at 0x..., file "%s", line %d>) MAKE_FUNCTION LOAD_FAST 0 (x) @@ -432,7 +434,7 @@ def foo(a: int, b: str) -> str: 1 LOAD_SMALL_INT 0 STORE_NAME 0 (x) - 2 L1: NOP + 2 L1: NOT_TAKEN 3 LOAD_NAME 0 (x) LOAD_SMALL_INT 1 @@ -458,7 +460,8 @@ def foo(a: int, b: str) -> str: %4d LOAD_GLOBAL 0 (Exception) CHECK_EXC_MATCH - POP_JUMP_IF_FALSE 23 (to L7) + POP_JUMP_IF_FALSE 24 (to L7) + NOT_TAKEN STORE_FAST 0 (e) %4d L4: LOAD_FAST 0 (e) @@ -555,7 +558,8 @@ def _with(c): %4d L3: PUSH_EXC_INFO WITH_EXCEPT_START TO_BOOL - POP_JUMP_IF_TRUE 1 (to L4) + POP_JUMP_IF_TRUE 2 (to L4) + NOT_TAKEN RERAISE 2 L4: POP_TOP L5: POP_EXCEPT @@ -645,7 +649,8 @@ async def _asyncwith(c): L20: CLEANUP_THROW L21: END_SEND TO_BOOL - POP_JUMP_IF_TRUE 1 (to L22) + POP_JUMP_IF_TRUE 2 (to L22) + NOT_TAKEN RERAISE 2 L22: POP_TOP L23: POP_EXCEPT @@ -839,7 +844,8 @@ def foo(x): L1: RESUME 0 LOAD_FAST 0 (.0) GET_ITER - L2: FOR_ITER 10 (to L3) + L2: FOR_ITER 11 (to L3) + NOT_TAKEN STORE_FAST 1 (z) LOAD_DEREF 2 (x) LOAD_FAST 1 (z) @@ -847,7 +853,7 @@ def foo(x): YIELD_VALUE 0 RESUME 5 POP_TOP - JUMP_BACKWARD 12 (to L2) + JUMP_BACKWARD 13 (to L2) L3: END_FOR POP_TOP LOAD_CONST 0 (None) @@ -893,14 +899,15 @@ def loop_test(): LOAD_SMALL_INT 3 BINARY_OP 5 (*) GET_ITER - L1: FOR_ITER_LIST 14 (to L2) + L1: FOR_ITER_LIST 15 (to L2) + NOT_TAKEN STORE_FAST 0 (i) %3d LOAD_GLOBAL_MODULE 1 (load_test + NULL) LOAD_FAST 0 (i) CALL_PY_GENERAL 1 POP_TOP - JUMP_BACKWARD 16 (to L1) + JUMP_BACKWARD 17 (to L1) %3d L2: END_FOR POP_TOP @@ -1699,204 +1706,214 @@ def _prepare_test_cases(): Instruction = dis.Instruction expected_opinfo_outer = [ - Instruction(opname='MAKE_CELL', opcode=92, arg=0, argval='a', argrepr='a', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='MAKE_CELL', opcode=92, arg=1, argval='b', argrepr='b', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='MAKE_CELL', opcode=93, arg=0, argval='a', argrepr='a', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='MAKE_CELL', opcode=93, arg=1, argval='b', argrepr='b', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=4, start_offset=4, starts_line=True, line_number=1, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=79, arg=3, argval=(3, 4), argrepr='(3, 4)', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=81, arg=0, argval='a', argrepr='a', offset=8, start_offset=8, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=81, arg=1, argval='b', argrepr='b', offset=10, start_offset=10, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='BUILD_TUPLE', opcode=48, arg=2, argval=2, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=79, arg=0, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=(3, 4), argrepr='(3, 4)', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='a', argrepr='a', offset=8, start_offset=8, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=1, argval='b', argrepr='b', offset=10, start_offset=10, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='BUILD_TUPLE', opcode=49, arg=2, argval=2, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), Instruction(opname='MAKE_FUNCTION', opcode=23, arg=None, argval=None, argrepr='', offset=16, start_offset=16, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=103, arg=8, argval=8, argrepr='closure', offset=18, start_offset=18, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=103, arg=1, argval=1, argrepr='defaults', offset=20, start_offset=20, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='STORE_FAST', opcode=107, arg=2, argval='f', argrepr='f', offset=22, start_offset=22, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=87, arg=1, argval='print', argrepr='print + NULL', offset=24, start_offset=24, starts_line=True, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_DEREF', opcode=80, arg=0, argval='a', argrepr='a', offset=34, start_offset=34, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=80, arg=1, argval='b', argrepr='b', offset=36, start_offset=36, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=79, arg=1, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=89, arg=1, argval=1, argrepr='', offset=40, start_offset=40, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='BUILD_LIST', opcode=43, arg=0, argval=0, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='BUILD_MAP', opcode=44, arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=79, arg=2, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=49, arg=7, argval=7, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=81, arg=2, argval='f', argrepr='f', offset=58, start_offset=58, starts_line=True, line_number=8, label=None, positions=None, cache_info=None), - Instruction(opname='RETURN_VALUE', opcode=33, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=8, label=None, positions=None, cache_info=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=8, argval=8, argrepr='closure', offset=18, start_offset=18, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=1, argval=1, argrepr='defaults', offset=20, start_offset=20, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='STORE_FAST', opcode=108, arg=2, argval='f', argrepr='f', offset=22, start_offset=22, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='print', argrepr='print + NULL', offset=24, start_offset=24, starts_line=True, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_DEREF', opcode=81, arg=0, argval='a', argrepr='a', offset=34, start_offset=34, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=81, arg=1, argval='b', argrepr='b', offset=36, start_offset=36, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=1, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=1, argval=1, argrepr='', offset=40, start_offset=40, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='BUILD_LIST', opcode=44, arg=0, argval=0, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='BUILD_MAP', opcode=45, arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=2, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=50, arg=7, argval=7, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=2, argval='f', argrepr='f', offset=58, start_offset=58, starts_line=True, line_number=8, label=None, positions=None, cache_info=None), + Instruction(opname='RETURN_VALUE', opcode=34, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=8, label=None, positions=None, cache_info=None), ] expected_opinfo_f = [ - Instruction(opname='COPY_FREE_VARS', opcode=58, arg=2, argval=2, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='MAKE_CELL', opcode=92, arg=0, argval='c', argrepr='c', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='MAKE_CELL', opcode=92, arg=1, argval='d', argrepr='d', offset=4, start_offset=4, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='COPY_FREE_VARS', opcode=59, arg=2, argval=2, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='MAKE_CELL', opcode=93, arg=0, argval='c', argrepr='c', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='MAKE_CELL', opcode=93, arg=1, argval='d', argrepr='d', offset=4, start_offset=4, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=79, arg=1, argval=(5, 6), argrepr='(5, 6)', offset=8, start_offset=8, starts_line=True, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=81, arg=3, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=81, arg=4, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=81, arg=0, argval='c', argrepr='c', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=81, arg=1, argval='d', argrepr='d', offset=16, start_offset=16, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='BUILD_TUPLE', opcode=48, arg=4, argval=4, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=79, arg=0, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, start_offset=20, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=1, argval=(5, 6), argrepr='(5, 6)', offset=8, start_offset=8, starts_line=True, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=3, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=4, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='c', argrepr='c', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=1, argval='d', argrepr='d', offset=16, start_offset=16, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='BUILD_TUPLE', opcode=49, arg=4, argval=4, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, start_offset=20, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), Instruction(opname='MAKE_FUNCTION', opcode=23, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=103, arg=8, argval=8, argrepr='closure', offset=24, start_offset=24, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=103, arg=1, argval=1, argrepr='defaults', offset=26, start_offset=26, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='STORE_FAST', opcode=107, arg=2, argval='inner', argrepr='inner', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=87, arg=1, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_DEREF', opcode=80, arg=3, argval='a', argrepr='a', offset=40, start_offset=40, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=80, arg=4, argval='b', argrepr='b', offset=42, start_offset=42, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=80, arg=0, argval='c', argrepr='c', offset=44, start_offset=44, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=80, arg=1, argval='d', argrepr='d', offset=46, start_offset=46, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=49, arg=4, argval=4, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=81, arg=2, argval='inner', argrepr='inner', offset=58, start_offset=58, starts_line=True, line_number=6, label=None, positions=None, cache_info=None), - Instruction(opname='RETURN_VALUE', opcode=33, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=6, label=None, positions=None, cache_info=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=8, argval=8, argrepr='closure', offset=24, start_offset=24, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=1, argval=1, argrepr='defaults', offset=26, start_offset=26, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='STORE_FAST', opcode=108, arg=2, argval='inner', argrepr='inner', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_DEREF', opcode=81, arg=3, argval='a', argrepr='a', offset=40, start_offset=40, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=81, arg=4, argval='b', argrepr='b', offset=42, start_offset=42, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=81, arg=0, argval='c', argrepr='c', offset=44, start_offset=44, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=81, arg=1, argval='d', argrepr='d', offset=46, start_offset=46, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=50, arg=4, argval=4, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=2, argval='inner', argrepr='inner', offset=58, start_offset=58, starts_line=True, line_number=6, label=None, positions=None, cache_info=None), + Instruction(opname='RETURN_VALUE', opcode=34, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=6, label=None, positions=None, cache_info=None), ] expected_opinfo_inner = [ - Instruction(opname='COPY_FREE_VARS', opcode=58, arg=4, argval=4, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='COPY_FREE_VARS', opcode=59, arg=4, argval=4, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=87, arg=1, argval='print', argrepr='print + NULL', offset=4, start_offset=4, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_DEREF', opcode=80, arg=2, argval='a', argrepr='a', offset=14, start_offset=14, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=80, arg=3, argval='b', argrepr='b', offset=16, start_offset=16, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=80, arg=4, argval='c', argrepr='c', offset=18, start_offset=18, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=80, arg=5, argval='d', argrepr='d', offset=20, start_offset=20, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST_LOAD_FAST', opcode=84, arg=1, argval=('e', 'f'), argrepr='e, f', offset=22, start_offset=22, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=49, arg=6, argval=6, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=32, start_offset=32, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=79, arg=0, argval=None, argrepr='None', offset=34, start_offset=34, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='RETURN_VALUE', opcode=33, arg=None, argval=None, argrepr='', offset=36, start_offset=36, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='print', argrepr='print + NULL', offset=4, start_offset=4, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_DEREF', opcode=81, arg=2, argval='a', argrepr='a', offset=14, start_offset=14, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=81, arg=3, argval='b', argrepr='b', offset=16, start_offset=16, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=81, arg=4, argval='c', argrepr='c', offset=18, start_offset=18, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=81, arg=5, argval='d', argrepr='d', offset=20, start_offset=20, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST_LOAD_FAST', opcode=85, arg=1, argval=('e', 'f'), argrepr='e, f', offset=22, start_offset=22, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=50, arg=6, argval=6, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=32, start_offset=32, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval=None, argrepr='None', offset=34, start_offset=34, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='RETURN_VALUE', opcode=34, arg=None, argval=None, argrepr='', offset=36, start_offset=36, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), ] expected_opinfo_jumpy = [ Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=1, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=87, arg=1, argval='range', argrepr='range + NULL', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_SMALL_INT', opcode=89, arg=10, argval=10, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='range', argrepr='range + NULL', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=10, argval=10, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), Instruction(opname='GET_ITER', opcode=16, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='FOR_ITER', opcode=67, arg=30, argval=88, argrepr='to L4', offset=24, start_offset=24, starts_line=False, line_number=3, label=1, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='STORE_FAST', opcode=107, arg=0, argval='i', argrepr='i', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=87, arg=3, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_FAST', opcode=81, arg=0, argval='i', argrepr='i', offset=40, start_offset=40, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=50, start_offset=50, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=81, arg=0, argval='i', argrepr='i', offset=52, start_offset=52, starts_line=True, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=89, arg=4, argval=4, argrepr='', offset=54, start_offset=54, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=54, arg=18, argval='<', argrepr='bool(<)', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=95, arg=2, argval=68, argrepr='to L2', offset=60, start_offset=60, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='JUMP_BACKWARD', opcode=72, arg=22, argval=24, argrepr='to L1', offset=64, start_offset=64, starts_line=True, line_number=6, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='LOAD_FAST', opcode=81, arg=0, argval='i', argrepr='i', offset=68, start_offset=68, starts_line=True, line_number=7, label=2, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=89, arg=6, argval=6, argrepr='', offset=70, start_offset=70, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=54, arg=148, argval='>', argrepr='bool(>)', offset=72, start_offset=72, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=98, arg=2, argval=84, argrepr='to L3', offset=76, start_offset=76, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='JUMP_BACKWARD', opcode=72, arg=30, argval=24, argrepr='to L1', offset=80, start_offset=80, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=84, start_offset=84, starts_line=True, line_number=8, label=3, positions=None, cache_info=None), - Instruction(opname='JUMP_FORWARD', opcode=74, arg=13, argval=114, argrepr='to L5', offset=86, start_offset=86, starts_line=False, line_number=8, label=None, positions=None, cache_info=None), - Instruction(opname='END_FOR', opcode=9, arg=None, argval=None, argrepr='', offset=88, start_offset=88, starts_line=True, line_number=3, label=4, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=90, start_offset=90, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=87, arg=3, argval='print', argrepr='print + NULL', offset=92, start_offset=92, starts_line=True, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=79, arg=0, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=102, start_offset=102, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=104, start_offset=104, starts_line=False, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=112, start_offset=112, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST_CHECK', opcode=83, arg=0, argval='i', argrepr='i', offset=114, start_offset=114, starts_line=True, line_number=11, label=5, positions=None, cache_info=None), - Instruction(opname='TO_BOOL', opcode=37, arg=None, argval=None, argrepr='', offset=116, start_offset=116, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=95, arg=33, argval=194, argrepr='to L8', offset=124, start_offset=124, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='LOAD_GLOBAL', opcode=87, arg=3, argval='print', argrepr='print + NULL', offset=128, start_offset=128, starts_line=True, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_FAST', opcode=81, arg=0, argval='i', argrepr='i', offset=138, start_offset=138, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=140, start_offset=140, starts_line=False, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=148, start_offset=148, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=81, arg=0, argval='i', argrepr='i', offset=150, start_offset=150, starts_line=True, line_number=13, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=89, arg=1, argval=1, argrepr='', offset=152, start_offset=152, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), - Instruction(opname='BINARY_OP', opcode=42, arg=23, argval=23, argrepr='-=', offset=154, start_offset=154, starts_line=False, line_number=13, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='STORE_FAST', opcode=107, arg=0, argval='i', argrepr='i', offset=158, start_offset=158, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=81, arg=0, argval='i', argrepr='i', offset=160, start_offset=160, starts_line=True, line_number=14, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=89, arg=6, argval=6, argrepr='', offset=162, start_offset=162, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=54, arg=148, argval='>', argrepr='bool(>)', offset=164, start_offset=164, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=95, arg=2, argval=176, argrepr='to L6', offset=168, start_offset=168, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='JUMP_BACKWARD', opcode=72, arg=31, argval=114, argrepr='to L5', offset=172, start_offset=172, starts_line=True, line_number=15, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='LOAD_FAST', opcode=81, arg=0, argval='i', argrepr='i', offset=176, start_offset=176, starts_line=True, line_number=16, label=6, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=89, arg=4, argval=4, argrepr='', offset=178, start_offset=178, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=54, arg=18, argval='<', argrepr='bool(<)', offset=180, start_offset=180, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=98, arg=2, argval=192, argrepr='to L7', offset=184, start_offset=184, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='JUMP_BACKWARD', opcode=72, arg=39, argval=114, argrepr='to L5', offset=188, start_offset=188, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='JUMP_FORWARD', opcode=74, arg=11, argval=216, argrepr='to L9', offset=192, start_offset=192, starts_line=True, line_number=17, label=7, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=87, arg=3, argval='print', argrepr='print + NULL', offset=194, start_offset=194, starts_line=True, line_number=19, label=8, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=79, arg=1, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=204, start_offset=204, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=206, start_offset=206, starts_line=False, line_number=19, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=214, start_offset=214, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), - Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=216, start_offset=216, starts_line=True, line_number=20, label=9, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=89, arg=1, argval=1, argrepr='', offset=218, start_offset=218, starts_line=True, line_number=21, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=89, arg=0, argval=0, argrepr='', offset=220, start_offset=220, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), - Instruction(opname='BINARY_OP', opcode=42, arg=11, argval=11, argrepr='/', offset=222, start_offset=222, starts_line=False, line_number=21, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=226, start_offset=226, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=81, arg=0, argval='i', argrepr='i', offset=228, start_offset=228, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=57, arg=1, argval=1, argrepr='', offset=230, start_offset=230, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SPECIAL', opcode=90, arg=1, argval=1, argrepr='__exit__', offset=232, start_offset=232, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='SWAP', opcode=112, arg=2, argval=2, argrepr='', offset=234, start_offset=234, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='SWAP', opcode=112, arg=3, argval=3, argrepr='', offset=236, start_offset=236, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SPECIAL', opcode=90, arg=0, argval=0, argrepr='__enter__', offset=238, start_offset=238, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=49, arg=0, argval=0, argrepr='', offset=240, start_offset=240, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='STORE_FAST', opcode=107, arg=1, argval='dodgy', argrepr='dodgy', offset=248, start_offset=248, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=87, arg=3, argval='print', argrepr='print + NULL', offset=250, start_offset=250, starts_line=True, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=79, arg=2, argval='Never reach this', argrepr="'Never reach this'", offset=260, start_offset=260, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=262, start_offset=262, starts_line=False, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=270, start_offset=270, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=79, arg=3, argval=None, argrepr='None', offset=272, start_offset=272, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=79, arg=3, argval=None, argrepr='None', offset=274, start_offset=274, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=79, arg=3, argval=None, argrepr='None', offset=276, start_offset=276, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=49, arg=3, argval=3, argrepr='', offset=278, start_offset=278, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=286, start_offset=286, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=87, arg=3, argval='print', argrepr='print + NULL', offset=288, start_offset=288, starts_line=True, line_number=28, label=10, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=79, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=298, start_offset=298, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=300, start_offset=300, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=308, start_offset=308, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=79, arg=3, argval=None, argrepr='None', offset=310, start_offset=310, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='RETURN_VALUE', opcode=33, arg=None, argval=None, argrepr='', offset=312, start_offset=312, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='PUSH_EXC_INFO', opcode=30, arg=None, argval=None, argrepr='', offset=314, start_offset=314, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='WITH_EXCEPT_START', opcode=41, arg=None, argval=None, argrepr='', offset=316, start_offset=316, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='TO_BOOL', opcode=37, arg=None, argval=None, argrepr='', offset=318, start_offset=318, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=98, arg=1, argval=332, argrepr='to L11', offset=326, start_offset=326, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='RERAISE', opcode=100, arg=2, argval=2, argrepr='', offset=330, start_offset=330, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=332, start_offset=332, starts_line=False, line_number=25, label=11, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=28, arg=None, argval=None, argrepr='', offset=334, start_offset=334, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=336, start_offset=336, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=338, start_offset=338, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=340, start_offset=340, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=73, arg=28, argval=288, argrepr='to L10', offset=342, start_offset=342, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=57, arg=3, argval=3, argrepr='', offset=344, start_offset=344, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=28, arg=None, argval=None, argrepr='', offset=346, start_offset=346, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=100, arg=1, argval=1, argrepr='', offset=348, start_offset=348, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='PUSH_EXC_INFO', opcode=30, arg=None, argval=None, argrepr='', offset=350, start_offset=350, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=87, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=352, start_offset=352, starts_line=True, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='CHECK_EXC_MATCH', opcode=5, arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=95, arg=14, argval=396, argrepr='to L12', offset=364, start_offset=364, starts_line=False, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=368, start_offset=368, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=87, arg=3, argval='print', argrepr='print + NULL', offset=370, start_offset=370, starts_line=True, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=79, arg=4, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=380, start_offset=380, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=382, start_offset=382, starts_line=False, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=390, start_offset=390, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=28, arg=None, argval=None, argrepr='', offset=392, start_offset=392, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=73, arg=54, argval=288, argrepr='to L10', offset=394, start_offset=394, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=100, arg=0, argval=0, argrepr='', offset=396, start_offset=396, starts_line=True, line_number=22, label=12, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=57, arg=3, argval=3, argrepr='', offset=398, start_offset=398, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=28, arg=None, argval=None, argrepr='', offset=400, start_offset=400, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=100, arg=1, argval=1, argrepr='', offset=402, start_offset=402, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='PUSH_EXC_INFO', opcode=30, arg=None, argval=None, argrepr='', offset=404, start_offset=404, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=87, arg=3, argval='print', argrepr='print + NULL', offset=406, start_offset=406, starts_line=True, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=79, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=416, start_offset=416, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=418, start_offset=418, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=426, start_offset=426, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=100, arg=0, argval=0, argrepr='', offset=428, start_offset=428, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=57, arg=3, argval=3, argrepr='', offset=430, start_offset=430, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=28, arg=None, argval=None, argrepr='', offset=432, start_offset=432, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=100, arg=1, argval=1, argrepr='', offset=434, start_offset=434, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='FOR_ITER', opcode=68, arg=34, argval=96, argrepr='to L4', offset=24, start_offset=24, starts_line=False, line_number=3, label=1, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='STORE_FAST', opcode=108, arg=0, argval='i', argrepr='i', offset=30, start_offset=30, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=32, start_offset=32, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=42, start_offset=42, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=52, start_offset=52, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=54, start_offset=54, starts_line=True, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=4, argval=4, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=55, arg=18, argval='<', argrepr='bool(<)', offset=58, start_offset=58, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=3, argval=72, argrepr='to L2', offset=62, start_offset=62, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=66, start_offset=66, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=73, arg=24, argval=24, argrepr='to L1', offset=68, start_offset=68, starts_line=True, line_number=6, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=72, start_offset=72, starts_line=True, line_number=7, label=2, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=6, argval=6, argrepr='', offset=74, start_offset=74, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=55, arg=148, argval='>', argrepr='bool(>)', offset=76, start_offset=76, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=99, arg=3, argval=90, argrepr='to L3', offset=80, start_offset=80, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=84, start_offset=84, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=73, arg=33, argval=24, argrepr='to L1', offset=86, start_offset=86, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=90, start_offset=90, starts_line=True, line_number=None, label=3, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=92, start_offset=92, starts_line=True, line_number=8, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_FORWARD', opcode=75, arg=13, argval=122, argrepr='to L5', offset=94, start_offset=94, starts_line=False, line_number=8, label=None, positions=None, cache_info=None), + Instruction(opname='END_FOR', opcode=9, arg=None, argval=None, argrepr='', offset=96, start_offset=96, starts_line=True, line_number=3, label=4, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=98, start_offset=98, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=100, start_offset=100, starts_line=True, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=110, start_offset=110, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=112, start_offset=112, starts_line=False, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=120, start_offset=120, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST_CHECK', opcode=84, arg=0, argval='i', argrepr='i', offset=122, start_offset=122, starts_line=True, line_number=11, label=5, positions=None, cache_info=None), + Instruction(opname='TO_BOOL', opcode=38, arg=None, argval=None, argrepr='', offset=124, start_offset=124, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=37, argval=210, argrepr='to L8', offset=132, start_offset=132, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=136, start_offset=136, starts_line=False, line_number=11, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=138, start_offset=138, starts_line=True, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=148, start_offset=148, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=150, start_offset=150, starts_line=False, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=158, start_offset=158, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=160, start_offset=160, starts_line=True, line_number=13, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=1, argval=1, argrepr='', offset=162, start_offset=162, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), + Instruction(opname='BINARY_OP', opcode=43, arg=23, argval=23, argrepr='-=', offset=164, start_offset=164, starts_line=False, line_number=13, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='STORE_FAST', opcode=108, arg=0, argval='i', argrepr='i', offset=168, start_offset=168, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=170, start_offset=170, starts_line=True, line_number=14, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=6, argval=6, argrepr='', offset=172, start_offset=172, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=55, arg=148, argval='>', argrepr='bool(>)', offset=174, start_offset=174, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=3, argval=188, argrepr='to L6', offset=178, start_offset=178, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=182, start_offset=182, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=73, arg=33, argval=122, argrepr='to L5', offset=184, start_offset=184, starts_line=True, line_number=15, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=188, start_offset=188, starts_line=True, line_number=16, label=6, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=4, argval=4, argrepr='', offset=190, start_offset=190, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=55, arg=18, argval='<', argrepr='bool(<)', offset=192, start_offset=192, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=99, arg=3, argval=206, argrepr='to L7', offset=196, start_offset=196, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=200, start_offset=200, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=73, arg=42, argval=122, argrepr='to L5', offset=202, start_offset=202, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=206, start_offset=206, starts_line=True, line_number=None, label=7, positions=None, cache_info=None), + Instruction(opname='JUMP_FORWARD', opcode=75, arg=11, argval=232, argrepr='to L9', offset=208, start_offset=208, starts_line=True, line_number=17, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=210, start_offset=210, starts_line=True, line_number=19, label=8, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=80, arg=1, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=220, start_offset=220, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=222, start_offset=222, starts_line=False, line_number=19, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=230, start_offset=230, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), + Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=232, start_offset=232, starts_line=True, line_number=20, label=9, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=1, argval=1, argrepr='', offset=234, start_offset=234, starts_line=True, line_number=21, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=0, argval=0, argrepr='', offset=236, start_offset=236, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), + Instruction(opname='BINARY_OP', opcode=43, arg=11, argval=11, argrepr='/', offset=238, start_offset=238, starts_line=False, line_number=21, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=242, start_offset=242, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=244, start_offset=244, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=58, arg=1, argval=1, argrepr='', offset=246, start_offset=246, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SPECIAL', opcode=91, arg=1, argval=1, argrepr='__exit__', offset=248, start_offset=248, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='SWAP', opcode=113, arg=2, argval=2, argrepr='', offset=250, start_offset=250, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='SWAP', opcode=113, arg=3, argval=3, argrepr='', offset=252, start_offset=252, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SPECIAL', opcode=91, arg=0, argval=0, argrepr='__enter__', offset=254, start_offset=254, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=50, arg=0, argval=0, argrepr='', offset=256, start_offset=256, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='STORE_FAST', opcode=108, arg=1, argval='dodgy', argrepr='dodgy', offset=264, start_offset=264, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=266, start_offset=266, starts_line=True, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=80, arg=2, argval='Never reach this', argrepr="'Never reach this'", offset=276, start_offset=276, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=278, start_offset=278, starts_line=False, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=286, start_offset=286, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=None, argrepr='None', offset=288, start_offset=288, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=None, argrepr='None', offset=290, start_offset=290, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=None, argrepr='None', offset=292, start_offset=292, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=50, arg=3, argval=3, argrepr='', offset=294, start_offset=294, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=302, start_offset=302, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=304, start_offset=304, starts_line=True, line_number=28, label=10, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=80, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=314, start_offset=314, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=316, start_offset=316, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=324, start_offset=324, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=None, argrepr='None', offset=326, start_offset=326, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='RETURN_VALUE', opcode=34, arg=None, argval=None, argrepr='', offset=328, start_offset=328, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='PUSH_EXC_INFO', opcode=31, arg=None, argval=None, argrepr='', offset=330, start_offset=330, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='WITH_EXCEPT_START', opcode=42, arg=None, argval=None, argrepr='', offset=332, start_offset=332, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='TO_BOOL', opcode=38, arg=None, argval=None, argrepr='', offset=334, start_offset=334, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=99, arg=2, argval=350, argrepr='to L11', offset=342, start_offset=342, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=346, start_offset=346, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=101, arg=2, argval=2, argrepr='', offset=348, start_offset=348, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=350, start_offset=350, starts_line=False, line_number=25, label=11, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=352, start_offset=352, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=354, start_offset=354, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=356, start_offset=356, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=358, start_offset=358, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=74, arg=29, argval=304, argrepr='to L10', offset=360, start_offset=360, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=58, arg=3, argval=3, argrepr='', offset=362, start_offset=362, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=364, start_offset=364, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=101, arg=1, argval=1, argrepr='', offset=366, start_offset=366, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='PUSH_EXC_INFO', opcode=31, arg=None, argval=None, argrepr='', offset=368, start_offset=368, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=370, start_offset=370, starts_line=True, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='CHECK_EXC_MATCH', opcode=5, arg=None, argval=None, argrepr='', offset=380, start_offset=380, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=15, argval=416, argrepr='to L12', offset=382, start_offset=382, starts_line=False, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=386, start_offset=386, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=388, start_offset=388, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=390, start_offset=390, starts_line=True, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=80, arg=4, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=400, start_offset=400, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=402, start_offset=402, starts_line=False, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=410, start_offset=410, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=412, start_offset=412, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=74, arg=56, argval=304, argrepr='to L10', offset=414, start_offset=414, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=101, arg=0, argval=0, argrepr='', offset=416, start_offset=416, starts_line=True, line_number=22, label=12, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=58, arg=3, argval=3, argrepr='', offset=418, start_offset=418, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=420, start_offset=420, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=101, arg=1, argval=1, argrepr='', offset=422, start_offset=422, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='PUSH_EXC_INFO', opcode=31, arg=None, argval=None, argrepr='', offset=424, start_offset=424, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=426, start_offset=426, starts_line=True, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=80, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=436, start_offset=436, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=438, start_offset=438, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=446, start_offset=446, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=101, arg=0, argval=0, argrepr='', offset=448, start_offset=448, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=58, arg=3, argval=3, argrepr='', offset=450, start_offset=450, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=452, start_offset=452, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=101, arg=1, argval=1, argrepr='', offset=454, start_offset=454, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), ] # One last piece of inspect fodder to check the default line number handling def simple(): pass expected_opinfo_simple = [ Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=simple.__code__.co_firstlineno, label=None, positions=None), - Instruction(opname='LOAD_CONST', opcode=79, arg=0, argval=None, argrepr='None', offset=2, start_offset=2, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None), - Instruction(opname='RETURN_VALUE', opcode=33, arg=None, argval=None, argrepr='', offset=4, start_offset=4, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval=None, argrepr='None', offset=2, start_offset=2, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None), + Instruction(opname='RETURN_VALUE', opcode=34, arg=None, argval=None, argrepr='', offset=4, start_offset=4, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None), ] diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py index 5a4bcebedf19de..087ac8d456b843 100644 --- a/Lib/test/test_monitoring.py +++ b/Lib/test/test_monitoring.py @@ -1491,7 +1491,15 @@ class BranchRecorder(JumpRecorder): event_type = E.BRANCH name = "branch" +class BranchRightRecorder(JumpRecorder): + event_type = E.BRANCH_RIGHT + name = "branch right" + +class BranchLeftRecorder(JumpRecorder): + + event_type = E.BRANCH_LEFT + name = "branch left" class JumpOffsetRecorder: @@ -1504,16 +1512,23 @@ def __init__(self, events, offsets=False): def __call__(self, code, from_, to): self.events.append((self.name, code.co_name, from_, to)) -class BranchOffsetRecorder(JumpOffsetRecorder): +class BranchLeftOffsetRecorder(JumpOffsetRecorder): - event_type = E.BRANCH - name = "branch" + event_type = E.BRANCH_LEFT + name = "branch left" + +class BranchRightOffsetRecorder(JumpOffsetRecorder): + + event_type = E.BRANCH_RIGHT + name = "branch right" JUMP_AND_BRANCH_RECORDERS = JumpRecorder, BranchRecorder JUMP_BRANCH_AND_LINE_RECORDERS = JumpRecorder, BranchRecorder, LineRecorder FLOW_AND_LINE_RECORDERS = JumpRecorder, BranchRecorder, LineRecorder, ExceptionRecorder, ReturnRecorder -BRANCH_OFFSET_RECORDERS = BranchOffsetRecorder, + +BRANCHES_RECORDERS = BranchLeftRecorder, BranchRightRecorder +BRANCH_OFFSET_RECORDERS = BranchLeftOffsetRecorder, BranchRightOffsetRecorder class TestBranchAndJumpEvents(CheckEvents): maxDiff = None @@ -1529,6 +1544,11 @@ def func(): x = 6 7 + def whilefunc(n=0): + while n < 3: + n += 1 # line 2 + 3 + self.check_events(func, recorders = JUMP_AND_BRANCH_RECORDERS, expected = [ ('branch', 'func', 2, 2), ('branch', 'func', 3, 6), @@ -1558,6 +1578,26 @@ def func(): ('line', 'func', 7), ('line', 'get_events', 11)]) + self.check_events(func, recorders = BRANCHES_RECORDERS, expected = [ + ('branch left', 'func', 2, 2), + ('branch right', 'func', 3, 6), + ('branch left', 'func', 2, 2), + ('branch left', 'func', 3, 4), + ('branch right', 'func', 2, 7)]) + + self.check_events(whilefunc, recorders = BRANCHES_RECORDERS, expected = [ + ('branch left', 'whilefunc', 1, 2), + ('branch left', 'whilefunc', 1, 2), + ('branch left', 'whilefunc', 1, 2), + ('branch right', 'whilefunc', 1, 3)]) + + self.check_events(func, recorders = BRANCH_OFFSET_RECORDERS, expected = [ + ('branch left', 'func', 28, 34), + ('branch right', 'func', 46, 60), + ('branch left', 'func', 28, 34), + ('branch left', 'func', 46, 52), + ('branch right', 'func', 28, 72)]) + def test_except_star(self): class Foo: @@ -1583,8 +1623,8 @@ def func(): ('branch', 'func', 4, 4), ('line', 'func', 5), ('line', 'meth', 1), - ('jump', 'func', 5, '[offset=118]'), - ('branch', 'func', '[offset=122]', '[offset=126]'), + ('jump', 'func', 5, '[offset=120]'), + ('branch', 'func', '[offset=124]', '[offset=130]'), ('line', 'get_events', 11)]) self.check_events(func, recorders = FLOW_AND_LINE_RECORDERS, expected = [ @@ -1598,8 +1638,8 @@ def func(): ('line', 'func', 5), ('line', 'meth', 1), ('return', 'meth', None), - ('jump', 'func', 5, '[offset=118]'), - ('branch', 'func', '[offset=122]', '[offset=126]'), + ('jump', 'func', 5, '[offset=120]'), + ('branch', 'func', '[offset=124]', '[offset=130]'), ('return', 'func', None), ('line', 'get_events', 11)]) @@ -1611,8 +1651,8 @@ def foo(n=0): n += 1 return None - in_loop = ('branch', 'foo', 10, 14) - exit_loop = ('branch', 'foo', 10, 30) + in_loop = ('branch left', 'foo', 10, 16) + exit_loop = ('branch right', 'foo', 10, 32) self.check_events(foo, recorders = BRANCH_OFFSET_RECORDERS, expected = [ in_loop, in_loop, @@ -1852,6 +1892,10 @@ def test_local(self): code = f1.__code__ sys.monitoring.set_local_events(TEST_TOOL, code, E.PY_START) self.assertEqual(sys.monitoring.get_local_events(TEST_TOOL, code), E.PY_START) + sys.monitoring.set_local_events(TEST_TOOL, code, 0) + sys.monitoring.set_local_events(TEST_TOOL, code, E.BRANCH) + self.assertEqual(sys.monitoring.get_local_events(TEST_TOOL, code), E.BRANCH_LEFT | E.BRANCH_RIGHT) + sys.monitoring.set_local_events(TEST_TOOL, code, 0) sys.monitoring.set_local_events(TEST_TOOL2, code, E.PY_START) self.assertEqual(sys.monitoring.get_local_events(TEST_TOOL2, code), E.PY_START) sys.monitoring.set_local_events(TEST_TOOL, code, 0) @@ -2053,7 +2097,8 @@ def setUp(self): ( 1, E.PY_RETURN, capi.fire_event_py_return, 20), ( 2, E.CALL, capi.fire_event_call, callable, 40), ( 1, E.JUMP, capi.fire_event_jump, 60), - ( 1, E.BRANCH, capi.fire_event_branch, 70), + ( 1, E.BRANCH_RIGHT, capi.fire_event_branch_right, 70), + ( 1, E.BRANCH_LEFT, capi.fire_event_branch_left, 80), ( 1, E.PY_THROW, capi.fire_event_py_throw, ValueError(1)), ( 1, E.RAISE, capi.fire_event_raise, ValueError(2)), ( 1, E.EXCEPTION_HANDLED, capi.fire_event_exception_handled, ValueError(5)), diff --git a/Misc/NEWS.d/next/Library/2024-12-13-14-21-04.gh-issue-122548.hq3Vud.rst b/Misc/NEWS.d/next/Library/2024-12-13-14-21-04.gh-issue-122548.hq3Vud.rst new file mode 100644 index 00000000000000..6cd13572ff1893 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-13-14-21-04.gh-issue-122548.hq3Vud.rst @@ -0,0 +1,4 @@ +Adds two new local events to sys.monitoring, ``BRANCH_LEFT`` and +``BRANCH_RIGHT``. This allows the two arms of the branch to be disabled +independently, which should hugely improve performance of branch-level +coverage tools. The old branch event, ``BRANCH`` is now deprecated. diff --git a/Modules/_testcapi/monitoring.c b/Modules/_testcapi/monitoring.c index 6fd4a405688f48..e475e3b5937199 100644 --- a/Modules/_testcapi/monitoring.c +++ b/Modules/_testcapi/monitoring.c @@ -286,7 +286,7 @@ fire_event_jump(PyObject *self, PyObject *args) } static PyObject * -fire_event_branch(PyObject *self, PyObject *args) +fire_event_branch_right(PyObject *self, PyObject *args) { PyObject *codelike; int offset; @@ -299,7 +299,25 @@ fire_event_branch(PyObject *self, PyObject *args) if (state == NULL) { return NULL; } - int res = PyMonitoring_FireBranchEvent(state, codelike, offset, target_offset); + int res = PyMonitoring_FireBranchRightEvent(state, codelike, offset, target_offset); + RETURN_INT(teardown_fire(res, state, exception)); +} + +static PyObject * +fire_event_branch_left(PyObject *self, PyObject *args) +{ + PyObject *codelike; + int offset; + PyObject *target_offset; + if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &target_offset)) { + return NULL; + } + PyObject *exception = NULL; + PyMonitoringState *state = setup_fire(codelike, offset, exception); + if (state == NULL) { + return NULL; + } + int res = PyMonitoring_FireBranchLeftEvent(state, codelike, offset, target_offset); RETURN_INT(teardown_fire(res, state, exception)); } @@ -478,7 +496,8 @@ static PyMethodDef TestMethods[] = { {"fire_event_call", fire_event_call, METH_VARARGS}, {"fire_event_line", fire_event_line, METH_VARARGS}, {"fire_event_jump", fire_event_jump, METH_VARARGS}, - {"fire_event_branch", fire_event_branch, METH_VARARGS}, + {"fire_event_branch_left", fire_event_branch_left, METH_VARARGS}, + {"fire_event_branch_right", fire_event_branch_right, METH_VARARGS}, {"fire_event_py_throw", fire_event_py_throw, METH_VARARGS}, {"fire_event_raise", fire_event_raise, METH_VARARGS}, {"fire_event_c_raise", fire_event_c_raise, METH_VARARGS}, diff --git a/Objects/codeobject.c b/Objects/codeobject.c index eb8de136ee6432..ae232cae86799b 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -2197,6 +2197,12 @@ code_linesiterator(PyObject *self, PyObject *Py_UNUSED(args)) return (PyObject *)new_linesiterator(code); } +static PyObject * +code_branchesiterator(PyCodeObject *code, PyObject *Py_UNUSED(args)) +{ + return _PyInstrumentation_BranchesIterator(code); +} + /*[clinic input] @text_signature "($self, /, **changes)" code.replace @@ -2337,6 +2343,7 @@ code__varname_from_oparg_impl(PyCodeObject *self, int oparg) static struct PyMethodDef code_methods[] = { {"__sizeof__", code_sizeof, METH_NOARGS}, {"co_lines", code_linesiterator, METH_NOARGS}, + {"co_branches", (PyCFunction)code_branchesiterator, METH_NOARGS}, {"co_positions", code_positionsiterator, METH_NOARGS}, CODE_REPLACE_METHODDEF CODE__VARNAME_FROM_OPARG_METHODDEF diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index 99b0fa48e01c8b..a0007830e8cbc0 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -1,37 +1,37 @@ // Auto-generated by Programs/freeze_test_frozenmain.py unsigned char M_test_frozenmain[] = { 227,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, - 0,0,0,0,0,243,168,0,0,0,149,0,89,0,79,0, - 70,0,111,0,89,0,79,0,70,1,111,1,88,2,31,0, - 79,1,49,1,0,0,0,0,0,0,29,0,88,2,31,0, - 79,2,88,0,77,6,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,49,2,0,0,0,0,0,0, - 29,0,88,1,77,8,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,31,0,49,0,0,0,0,0, - 0,0,79,3,2,0,0,0,111,5,79,4,16,0,67,20, - 0,0,111,6,88,2,31,0,79,5,88,6,12,0,79,6, - 88,5,88,6,2,0,0,0,12,0,47,4,49,1,0,0, - 0,0,0,0,29,0,72,22,0,0,9,0,29,0,79,0, - 33,0,41,7,78,122,18,70,114,111,122,101,110,32,72,101, - 108,108,111,32,87,111,114,108,100,122,8,115,121,115,46,97, - 114,103,118,218,6,99,111,110,102,105,103,41,5,218,12,112, - 114,111,103,114,97,109,95,110,97,109,101,218,10,101,120,101, - 99,117,116,97,98,108,101,218,15,117,115,101,95,101,110,118, - 105,114,111,110,109,101,110,116,218,17,99,111,110,102,105,103, - 117,114,101,95,99,95,115,116,100,105,111,218,14,98,117,102, - 102,101,114,101,100,95,115,116,100,105,111,122,7,99,111,110, - 102,105,103,32,122,2,58,32,41,7,218,3,115,121,115,218, - 17,95,116,101,115,116,105,110,116,101,114,110,97,108,99,97, - 112,105,218,5,112,114,105,110,116,218,4,97,114,103,118,218, - 11,103,101,116,95,99,111,110,102,105,103,115,114,2,0,0, - 0,218,3,107,101,121,169,0,243,0,0,0,0,218,18,116, - 101,115,116,95,102,114,111,122,101,110,109,97,105,110,46,112, - 121,218,8,60,109,111,100,117,108,101,62,114,17,0,0,0, - 1,0,0,0,115,94,0,0,0,240,3,1,1,1,243,8, - 0,1,11,219,0,24,225,0,5,208,6,26,212,0,27,217, - 0,5,128,106,144,35,151,40,145,40,212,0,27,216,9,26, - 215,9,38,210,9,38,211,9,40,168,24,209,9,50,128,6, - 243,2,6,12,2,128,67,241,14,0,5,10,136,71,144,67, - 144,53,152,2,152,54,160,35,153,59,152,45,208,10,40,214, - 4,41,243,15,6,12,2,114,15,0,0,0, + 0,0,0,0,0,243,170,0,0,0,149,0,90,0,80,0, + 71,0,112,0,90,0,80,0,71,1,112,1,89,2,32,0, + 80,1,50,1,0,0,0,0,0,0,30,0,89,2,32,0, + 80,2,89,0,78,6,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,50,2,0,0,0,0,0,0, + 30,0,89,1,78,8,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,32,0,50,0,0,0,0,0, + 0,0,80,3,2,0,0,0,112,5,80,4,16,0,68,21, + 0,0,28,0,112,6,89,2,32,0,80,5,89,6,12,0, + 80,6,89,5,89,6,2,0,0,0,12,0,48,4,50,1, + 0,0,0,0,0,0,30,0,73,23,0,0,9,0,30,0, + 80,0,34,0,41,7,78,122,18,70,114,111,122,101,110,32, + 72,101,108,108,111,32,87,111,114,108,100,122,8,115,121,115, + 46,97,114,103,118,218,6,99,111,110,102,105,103,41,5,218, + 12,112,114,111,103,114,97,109,95,110,97,109,101,218,10,101, + 120,101,99,117,116,97,98,108,101,218,15,117,115,101,95,101, + 110,118,105,114,111,110,109,101,110,116,218,17,99,111,110,102, + 105,103,117,114,101,95,99,95,115,116,100,105,111,218,14,98, + 117,102,102,101,114,101,100,95,115,116,100,105,111,122,7,99, + 111,110,102,105,103,32,122,2,58,32,41,7,218,3,115,121, + 115,218,17,95,116,101,115,116,105,110,116,101,114,110,97,108, + 99,97,112,105,218,5,112,114,105,110,116,218,4,97,114,103, + 118,218,11,103,101,116,95,99,111,110,102,105,103,115,114,2, + 0,0,0,218,3,107,101,121,169,0,243,0,0,0,0,218, + 18,116,101,115,116,95,102,114,111,122,101,110,109,97,105,110, + 46,112,121,218,8,60,109,111,100,117,108,101,62,114,17,0, + 0,0,1,0,0,0,115,94,0,0,0,240,3,1,1,1, + 243,8,0,1,11,219,0,24,225,0,5,208,6,26,212,0, + 27,217,0,5,128,106,144,35,151,40,145,40,212,0,27,216, + 9,26,215,9,38,210,9,38,211,9,40,168,24,209,9,50, + 128,6,244,2,6,12,2,128,67,241,14,0,5,10,136,71, + 144,67,144,53,152,2,152,54,160,35,153,59,152,45,208,10, + 40,214,4,41,243,15,6,12,2,114,15,0,0,0, }; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index b67264f0440869..cf089c368b651c 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -148,6 +148,8 @@ dummy_func( RESUME_CHECK, }; + macro(NOT_TAKEN) = NOP; + op(_CHECK_PERIODIC, (--)) { _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); QSBR_QUIESCENT_STATE(tstate); @@ -2723,7 +2725,7 @@ dummy_func( int flag = PyStackRef_IsFalse(cond); DEAD(cond); RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); - JUMPBY(oparg * flag); + JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN); } replaced op(_POP_JUMP_IF_TRUE, (cond -- )) { @@ -2731,7 +2733,7 @@ dummy_func( int flag = PyStackRef_IsTrue(cond); DEAD(cond); RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); - JUMPBY(oparg * flag); + JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN); } op(_IS_NONE, (value -- b)) { @@ -2923,13 +2925,11 @@ dummy_func( macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _FOR_ITER; inst(INSTRUMENTED_FOR_ITER, (unused/1 -- )) { - _Py_CODEUNIT *target; _PyStackRef iter_stackref = TOP(); PyObject *iter = PyStackRef_AsPyObjectBorrow(iter_stackref); PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); if (next != NULL) { PUSH(PyStackRef_FromPyObjectSteal(next)); - target = next_instr; } else { if (_PyErr_Occurred(tstate)) { @@ -2946,9 +2946,9 @@ dummy_func( STACK_SHRINK(1); PyStackRef_CLOSE(iter_stackref); /* Skip END_FOR and POP_TOP */ - target = next_instr + oparg + 2; + _Py_CODEUNIT *target = next_instr + oparg + 2; + INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH_RIGHT); } - INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH); } op(_ITER_CHECK_LIST, (iter -- iter)) { @@ -4736,6 +4736,10 @@ dummy_func( INSTRUMENTED_JUMP(this_instr, next_instr - oparg, PY_MONITORING_EVENT_JUMP); } + inst(INSTRUMENTED_NOT_TAKEN, ( -- )) { + INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT); + } + macro(INSTRUMENTED_JUMP_BACKWARD) = unused/1 + _CHECK_PERIODIC + @@ -4744,51 +4748,43 @@ dummy_func( inst(INSTRUMENTED_POP_JUMP_IF_TRUE, (unused/1 -- )) { _PyStackRef cond = POP(); assert(PyStackRef_BoolCheck(cond)); - int flag = PyStackRef_IsTrue(cond); - int offset = flag * oparg; - RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + int jump = PyStackRef_IsTrue(cond); + RECORD_BRANCH_TAKEN(this_instr[1].cache, jump); + if (jump) { + INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); + } } inst(INSTRUMENTED_POP_JUMP_IF_FALSE, (unused/1 -- )) { _PyStackRef cond = POP(); assert(PyStackRef_BoolCheck(cond)); - int flag = PyStackRef_IsFalse(cond); - int offset = flag * oparg; - RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + int jump = PyStackRef_IsFalse(cond); + RECORD_BRANCH_TAKEN(this_instr[1].cache, jump); + if (jump) { + INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); + } } inst(INSTRUMENTED_POP_JUMP_IF_NONE, (unused/1 -- )) { _PyStackRef value_stackref = POP(); - int flag = PyStackRef_IsNone(value_stackref); - int offset; - if (flag) { - offset = oparg; + int jump = PyStackRef_IsNone(value_stackref); + RECORD_BRANCH_TAKEN(this_instr[1].cache, jump); + if (jump) { + INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); } else { PyStackRef_CLOSE(value_stackref); - offset = 0; } - RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); } inst(INSTRUMENTED_POP_JUMP_IF_NOT_NONE, (unused/1 -- )) { _PyStackRef value_stackref = POP(); - int offset; - int nflag = PyStackRef_IsNone(value_stackref); - if (nflag) { - offset = 0; - } - else { + int jump = !PyStackRef_IsNone(value_stackref); + RECORD_BRANCH_TAKEN(this_instr[1].cache, jump); + if (jump) { PyStackRef_CLOSE(value_stackref); - offset = oparg; + INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); } - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | !nflag; - #endif - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); } tier1 inst(EXTENDED_ARG, ( -- )) { diff --git a/Python/codegen.c b/Python/codegen.c index a5e550cf8c947e..6d3272edfdbf94 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -406,7 +406,13 @@ codegen_addop_j(instr_sequence *seq, location loc, assert(IS_JUMP_TARGET_LABEL(target)); assert(OPCODE_HAS_JUMP(opcode) || IS_BLOCK_PUSH_OPCODE(opcode)); assert(!IS_ASSEMBLER_OPCODE(opcode)); - return _PyInstructionSequence_Addop(seq, opcode, target.id, loc); + if (_PyInstructionSequence_Addop(seq, opcode, target.id, loc) != SUCCESS) { + return ERROR; + } + if (IS_CONDITIONAL_JUMP_OPCODE(opcode) || opcode == FOR_ITER) { + return _PyInstructionSequence_Addop(seq, NOT_TAKEN, 0, NO_LOCATION); + } + return SUCCESS; } #define ADDOP_JUMP(C, LOC, OP, O) \ @@ -682,7 +688,6 @@ codegen_setup_annotations_scope(compiler *c, location loc, ADDOP_I(c, loc, COMPARE_OP, (Py_GT << 5) | compare_masks[Py_GT]); NEW_JUMP_TARGET_LABEL(c, body); ADDOP_JUMP(c, loc, POP_JUMP_IF_FALSE, body); - ADDOP_I(c, loc, LOAD_COMMON_CONSTANT, CONSTANT_NOTIMPLEMENTEDERROR); ADDOP_I(c, loc, RAISE_VARARGS, 1); USE_LABEL(c, body); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index de61a64a6e3374..9bfc3e7f0b112b 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -5668,6 +5668,8 @@ /* _MONITOR_JUMP_BACKWARD is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ + /* _INSTRUMENTED_NOT_TAKEN is not a viable micro-op for tier 2 because it is instrumented */ + /* _INSTRUMENTED_POP_JUMP_IF_TRUE is not a viable micro-op for tier 2 because it is instrumented */ /* _INSTRUMENTED_POP_JUMP_IF_FALSE is not a viable micro-op for tier 2 because it is instrumented */ diff --git a/Python/flowgraph.c b/Python/flowgraph.c index b1097b64469ecd..64df6290de06ba 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -557,6 +557,12 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) { if (backwards_jump == NULL) { return ERROR; } + assert(b->b_next->b_iused > 0); + assert(b->b_next->b_instr[0].i_opcode == NOT_TAKEN); + b->b_next->b_instr[0].i_opcode = NOP; + b->b_next->b_instr[0].i_loc = NO_LOCATION; + RETURN_IF_ERROR( + basicblock_addop(backwards_jump, NOT_TAKEN, 0, last->i_loc)); RETURN_IF_ERROR( basicblock_add_jump(backwards_jump, JUMP, target, last->i_loc)); last->i_opcode = reversed_opcode; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 8a89ba890fd9c9..ac89891df80590 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -4618,7 +4618,6 @@ next_instr += 2; INSTRUCTION_STATS(INSTRUMENTED_FOR_ITER); /* Skip 1 cache entry */ - _Py_CODEUNIT *target; _PyStackRef iter_stackref = TOP(); PyObject *iter = PyStackRef_AsPyObjectBorrow(iter_stackref); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -4626,7 +4625,6 @@ stack_pointer = _PyFrame_GetStackPointer(frame); if (next != NULL) { PUSH(PyStackRef_FromPyObjectSteal(next)); - target = next_instr; } else { if (_PyErr_Occurred(tstate)) { @@ -4647,9 +4645,9 @@ STACK_SHRINK(1); PyStackRef_CLOSE(iter_stackref); /* Skip END_FOR and POP_TOP */ - target = next_instr + oparg + 2; + _Py_CODEUNIT *target = next_instr + oparg + 2; + INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH_RIGHT); } - INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } @@ -4754,6 +4752,15 @@ GO_TO_INSTRUCTION(LOAD_SUPER_ATTR); } + TARGET(INSTRUMENTED_NOT_TAKEN) { + _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + (void)this_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_NOT_TAKEN); + INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT); + DISPATCH(); + } + TARGET(INSTRUMENTED_POP_JUMP_IF_FALSE) { _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; (void)this_instr; @@ -4762,10 +4769,11 @@ /* Skip 1 cache entry */ _PyStackRef cond = POP(); assert(PyStackRef_BoolCheck(cond)); - int flag = PyStackRef_IsFalse(cond); - int offset = flag * oparg; - RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + int jump = PyStackRef_IsFalse(cond); + RECORD_BRANCH_TAKEN(this_instr[1].cache, jump); + if (jump) { + INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); + } DISPATCH(); } @@ -4776,17 +4784,14 @@ INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NONE); /* Skip 1 cache entry */ _PyStackRef value_stackref = POP(); - int flag = PyStackRef_IsNone(value_stackref); - int offset; - if (flag) { - offset = oparg; + int jump = PyStackRef_IsNone(value_stackref); + RECORD_BRANCH_TAKEN(this_instr[1].cache, jump); + if (jump) { + INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); } else { PyStackRef_CLOSE(value_stackref); - offset = 0; } - RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } @@ -4797,19 +4802,12 @@ INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NOT_NONE); /* Skip 1 cache entry */ _PyStackRef value_stackref = POP(); - int offset; - int nflag = PyStackRef_IsNone(value_stackref); - if (nflag) { - offset = 0; - } - else { + int jump = !PyStackRef_IsNone(value_stackref); + RECORD_BRANCH_TAKEN(this_instr[1].cache, jump); + if (jump) { PyStackRef_CLOSE(value_stackref); - offset = oparg; + INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); } - #if ENABLE_SPECIALIZATION - this_instr[1].cache = (this_instr[1].cache << 1) | !nflag; - #endif - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); DISPATCH(); } @@ -4821,10 +4819,11 @@ /* Skip 1 cache entry */ _PyStackRef cond = POP(); assert(PyStackRef_BoolCheck(cond)); - int flag = PyStackRef_IsTrue(cond); - int offset = flag * oparg; - RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); - INSTRUMENTED_JUMP(this_instr, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + int jump = PyStackRef_IsTrue(cond); + RECORD_BRANCH_TAKEN(this_instr[1].cache, jump); + if (jump) { + INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); + } DISPATCH(); } @@ -6659,6 +6658,13 @@ DISPATCH(); } + TARGET(NOT_TAKEN) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(NOT_TAKEN); + DISPATCH(); + } + TARGET(POP_EXCEPT) { frame->instr_ptr = next_instr; next_instr += 1; @@ -6687,7 +6693,7 @@ assert(PyStackRef_BoolCheck(cond)); int flag = PyStackRef_IsFalse(cond); RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); - JUMPBY(oparg * flag); + JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN); stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -6719,7 +6725,7 @@ assert(PyStackRef_BoolCheck(cond)); int flag = PyStackRef_IsTrue(cond); RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); - JUMPBY(oparg * flag); + JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN); } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -6752,7 +6758,7 @@ assert(PyStackRef_BoolCheck(cond)); int flag = PyStackRef_IsFalse(cond); RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); - JUMPBY(oparg * flag); + JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN); } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -6770,7 +6776,7 @@ assert(PyStackRef_BoolCheck(cond)); int flag = PyStackRef_IsTrue(cond); RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); - JUMPBY(oparg * flag); + JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN); stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 3503809e3306cb..e4255bfad8c41a 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -85,22 +85,24 @@ static const int8_t EVENT_FOR_OPCODE[256] = { [INSTRUMENTED_YIELD_VALUE] = PY_MONITORING_EVENT_PY_YIELD, [JUMP_FORWARD] = PY_MONITORING_EVENT_JUMP, [JUMP_BACKWARD] = PY_MONITORING_EVENT_JUMP, - [POP_JUMP_IF_FALSE] = PY_MONITORING_EVENT_BRANCH, - [POP_JUMP_IF_TRUE] = PY_MONITORING_EVENT_BRANCH, - [POP_JUMP_IF_NONE] = PY_MONITORING_EVENT_BRANCH, - [POP_JUMP_IF_NOT_NONE] = PY_MONITORING_EVENT_BRANCH, + [POP_JUMP_IF_FALSE] = PY_MONITORING_EVENT_BRANCH_RIGHT, + [POP_JUMP_IF_TRUE] = PY_MONITORING_EVENT_BRANCH_RIGHT, + [POP_JUMP_IF_NONE] = PY_MONITORING_EVENT_BRANCH_RIGHT, + [POP_JUMP_IF_NOT_NONE] = PY_MONITORING_EVENT_BRANCH_RIGHT, [INSTRUMENTED_JUMP_FORWARD] = PY_MONITORING_EVENT_JUMP, [INSTRUMENTED_JUMP_BACKWARD] = PY_MONITORING_EVENT_JUMP, - [INSTRUMENTED_POP_JUMP_IF_FALSE] = PY_MONITORING_EVENT_BRANCH, - [INSTRUMENTED_POP_JUMP_IF_TRUE] = PY_MONITORING_EVENT_BRANCH, - [INSTRUMENTED_POP_JUMP_IF_NONE] = PY_MONITORING_EVENT_BRANCH, - [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = PY_MONITORING_EVENT_BRANCH, - [FOR_ITER] = PY_MONITORING_EVENT_BRANCH, - [INSTRUMENTED_FOR_ITER] = PY_MONITORING_EVENT_BRANCH, + [INSTRUMENTED_POP_JUMP_IF_FALSE] = PY_MONITORING_EVENT_BRANCH_RIGHT, + [INSTRUMENTED_POP_JUMP_IF_TRUE] = PY_MONITORING_EVENT_BRANCH_RIGHT, + [INSTRUMENTED_POP_JUMP_IF_NONE] = PY_MONITORING_EVENT_BRANCH_RIGHT, + [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = PY_MONITORING_EVENT_BRANCH_RIGHT, + [FOR_ITER] = PY_MONITORING_EVENT_BRANCH_RIGHT, + [INSTRUMENTED_FOR_ITER] = PY_MONITORING_EVENT_BRANCH_RIGHT, [END_FOR] = PY_MONITORING_EVENT_STOP_ITERATION, [INSTRUMENTED_END_FOR] = PY_MONITORING_EVENT_STOP_ITERATION, [END_SEND] = PY_MONITORING_EVENT_STOP_ITERATION, [INSTRUMENTED_END_SEND] = PY_MONITORING_EVENT_STOP_ITERATION, + [NOT_TAKEN] = PY_MONITORING_EVENT_BRANCH_LEFT, + [INSTRUMENTED_NOT_TAKEN] = PY_MONITORING_EVENT_BRANCH_LEFT, }; static const uint8_t DE_INSTRUMENT[256] = { @@ -120,6 +122,7 @@ static const uint8_t DE_INSTRUMENT[256] = { [INSTRUMENTED_END_FOR] = END_FOR, [INSTRUMENTED_END_SEND] = END_SEND, [INSTRUMENTED_LOAD_SUPER_ATTR] = LOAD_SUPER_ATTR, + [INSTRUMENTED_NOT_TAKEN] = NOT_TAKEN, }; static const uint8_t INSTRUMENTED_OPCODES[256] = { @@ -155,6 +158,8 @@ static const uint8_t INSTRUMENTED_OPCODES[256] = { [INSTRUMENTED_FOR_ITER] = INSTRUMENTED_FOR_ITER, [LOAD_SUPER_ATTR] = INSTRUMENTED_LOAD_SUPER_ATTR, [INSTRUMENTED_LOAD_SUPER_ATTR] = INSTRUMENTED_LOAD_SUPER_ATTR, + [NOT_TAKEN] = INSTRUMENTED_NOT_TAKEN, + [INSTRUMENTED_NOT_TAKEN] = INSTRUMENTED_NOT_TAKEN, [INSTRUMENTED_LINE] = INSTRUMENTED_LINE, [INSTRUMENTED_INSTRUCTION] = INSTRUMENTED_INSTRUCTION, @@ -323,33 +328,8 @@ _PyInstruction_GetLength(PyCodeObject *code, int offset) { ASSERT_WORLD_STOPPED_OR_LOCKED(code); - int opcode = - FT_ATOMIC_LOAD_UINT8_RELAXED(_PyCode_CODE(code)[offset].op.code); - assert(opcode != 0); - assert(opcode != RESERVED); - if (opcode == INSTRUMENTED_LINE) { - opcode = code->_co_monitoring->lines[offset].original_opcode; - } - if (opcode == INSTRUMENTED_INSTRUCTION) { - opcode = code->_co_monitoring->per_instruction_opcodes[offset]; - } - int deinstrumented = DE_INSTRUMENT[opcode]; - if (deinstrumented) { - opcode = deinstrumented; - } - else { - opcode = _PyOpcode_Deopt[opcode]; - } - assert(opcode != 0); - if (opcode == ENTER_EXECUTOR) { - int exec_index = _PyCode_CODE(code)[offset].op.arg; - _PyExecutorObject *exec = code->co_executors->executors[exec_index]; - opcode = _PyOpcode_Deopt[exec->vm_data.opcode]; - } - assert(!is_instrumented(opcode)); - assert(opcode != ENTER_EXECUTOR); - assert(opcode == _PyOpcode_Deopt[opcode]); - return 1 + _PyOpcode_Caches[opcode]; + _Py_CODEUNIT inst = _Py_GetBaseCodeUnit(code, offset); + return 1 + _PyOpcode_Caches[inst.op.code]; } #ifdef INSTRUMENT_DEBUG @@ -599,16 +579,15 @@ _Py_GetBaseCodeUnit(PyCodeObject *code, int i) int opcode = inst.op.code; if (opcode < MIN_INSTRUMENTED_OPCODE) { inst.op.code = _PyOpcode_Deopt[opcode]; - assert(inst.op.code <= RESUME); + assert(inst.op.code < MIN_SPECIALIZED_OPCODE); return inst; } if (opcode == ENTER_EXECUTOR) { _PyExecutorObject *exec = code->co_executors->executors[inst.op.arg]; opcode = _PyOpcode_Deopt[exec->vm_data.opcode]; inst.op.code = opcode; - assert(opcode <= RESUME); inst.op.arg = exec->vm_data.oparg; - assert(inst.op.code <= RESUME); + assert(inst.op.code < MIN_SPECIALIZED_OPCODE); return inst; } if (opcode == INSTRUMENTED_LINE) { @@ -1084,6 +1063,8 @@ static const char *const event_names [] = { [PY_MONITORING_EVENT_INSTRUCTION] = "INSTRUCTION", [PY_MONITORING_EVENT_JUMP] = "JUMP", [PY_MONITORING_EVENT_BRANCH] = "BRANCH", + [PY_MONITORING_EVENT_BRANCH_LEFT] = "BRANCH_LEFT", + [PY_MONITORING_EVENT_BRANCH_RIGHT] = "BRANCH_RIGHT", [PY_MONITORING_EVENT_C_RETURN] = "C_RETURN", [PY_MONITORING_EVENT_PY_THROW] = "PY_THROW", [PY_MONITORING_EVENT_RAISE] = "RAISE", @@ -1111,6 +1092,10 @@ call_instrumentation_vector( /* Offset visible to user should be the offset in bytes, as that is the * convention for APIs involving code offsets. */ int bytes_offset = offset * (int)sizeof(_Py_CODEUNIT); + if (event == PY_MONITORING_EVENT_BRANCH_LEFT) { + assert(EVENT_FOR_OPCODE[_Py_GetBaseCodeUnit(code, offset-2).op.code] == PY_MONITORING_EVENT_BRANCH_RIGHT); + bytes_offset -= 4; + } PyObject *offset_obj = PyLong_FromLong(bytes_offset); if (offset_obj == NULL) { return -1; @@ -1191,7 +1176,8 @@ _Py_call_instrumentation_jump( _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, _Py_CODEUNIT *target) { assert(event == PY_MONITORING_EVENT_JUMP || - event == PY_MONITORING_EVENT_BRANCH); + event == PY_MONITORING_EVENT_BRANCH_RIGHT || + event == PY_MONITORING_EVENT_BRANCH_LEFT); assert(frame->instr_ptr == instr); int to = (int)(target - _PyFrame_GetBytecode(frame)); PyObject *to_obj = PyLong_FromLong(to * (int)sizeof(_Py_CODEUNIT)); @@ -1427,19 +1413,6 @@ _Py_call_instrumentation_instruction(PyThreadState *tstate, _PyInterpreterFrame* return next_opcode; } - -PyObject * -_PyMonitoring_RegisterCallback(int tool_id, int event_id, PyObject *obj) -{ - PyInterpreterState *is = _PyInterpreterState_GET(); - assert(0 <= tool_id && tool_id < PY_MONITORING_TOOL_IDS); - assert(0 <= event_id && event_id < _PY_MONITORING_EVENTS); - PyObject *callback = _Py_atomic_exchange_ptr(&is->monitoring_callables[tool_id][event_id], - Py_XNewRef(obj)); - - return callback; -} - static void initialize_tools(PyCodeObject *code) { @@ -2312,6 +2285,10 @@ monitoring_set_events_impl(PyObject *module, int tool_id, int event_set) return NULL; } event_set &= ~C_RETURN_EVENTS; + if (event_set & (1 << PY_MONITORING_EVENT_BRANCH)) { + event_set &= ~(1 << PY_MONITORING_EVENT_BRANCH); + event_set |= (1 << PY_MONITORING_EVENT_BRANCH_RIGHT) | (1 << PY_MONITORING_EVENT_BRANCH_LEFT); + } if (_PyMonitoring_SetEvents(tool_id, event_set)) { return NULL; } @@ -2384,6 +2361,10 @@ monitoring_set_local_events_impl(PyObject *module, int tool_id, return NULL; } event_set &= ~C_RETURN_EVENTS; + if (event_set & (1 << PY_MONITORING_EVENT_BRANCH)) { + event_set &= ~(1 << PY_MONITORING_EVENT_BRANCH); + event_set |= (1 << PY_MONITORING_EVENT_BRANCH_RIGHT) | (1 << PY_MONITORING_EVENT_BRANCH_LEFT); + } if (event_set < 0 || event_set >= (1 << _PY_MONITORING_LOCAL_EVENTS)) { PyErr_Format(PyExc_ValueError, "invalid local event set 0x%x", event_set); return NULL; @@ -2711,7 +2692,27 @@ _PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int3 assert(state->active); PyObject *args[4] = { NULL, NULL, NULL, target_offset }; return capi_call_instrumentation(state, codelike, offset, args, 3, - PY_MONITORING_EVENT_BRANCH); + PY_MONITORING_EVENT_BRANCH_RIGHT); +} + +int +_PyMonitoring_FireBranchRightEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *target_offset) +{ + assert(state->active); + PyObject *args[4] = { NULL, NULL, NULL, target_offset }; + return capi_call_instrumentation(state, codelike, offset, args, 3, + PY_MONITORING_EVENT_BRANCH_RIGHT); +} + +int +_PyMonitoring_FireBranchLeftEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, + PyObject *target_offset) +{ + assert(state->active); + PyObject *args[4] = { NULL, NULL, NULL, target_offset }; + return capi_call_instrumentation(state, codelike, offset, args, 3, + PY_MONITORING_EVENT_BRANCH_LEFT); } int @@ -2849,3 +2850,213 @@ _PyMonitoring_FireStopIterationEvent(PyMonitoringState *state, PyObject *codelik Py_DECREF(exc); return exception_event_teardown(err, NULL); } + + + +/* Handle legacy BRANCH event */ + +typedef struct _PyLegacyBranchEventHandler { + PyObject_HEAD + vectorcallfunc vectorcall; + PyObject *handler; + bool right; + int tool_id; +} _PyLegacyBranchEventHandler; + +static void +dealloc_branch_handler(_PyLegacyBranchEventHandler *self) +{ + Py_CLEAR(self->handler); + PyObject_Free((PyObject *)self); +} + +static PyTypeObject _PyLegacyBranchEventHandler_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "sys.monitoring.branch_event_handler", + sizeof(_PyLegacyBranchEventHandler), + .tp_dealloc = (destructor)dealloc_branch_handler, + .tp_vectorcall_offset = offsetof(_PyLegacyBranchEventHandler, vectorcall), + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_DISALLOW_INSTANTIATION, + .tp_call = PyVectorcall_Call, +}; + + +static PyObject * +branch_handler( + _PyLegacyBranchEventHandler *self, PyObject *const *args, + size_t nargsf, PyObject *kwnames +) { + PyObject *res = PyObject_Vectorcall(self->handler, args, nargsf, kwnames); + if (res == &_PyInstrumentation_DISABLE) { + // Find the other instrumented instruction and remove tool + assert(PyVectorcall_NARGS(nargsf) >= 2); + PyObject *offset_obj = args[1]; + int bytes_offset = PyLong_AsLong(offset_obj); + if (PyErr_Occurred()) { + return NULL; + } + PyCodeObject *code = (PyCodeObject *)args[0]; + if (!PyCode_Check(code) || (bytes_offset & 1)) { + return res; + } + int offset = bytes_offset / 2; + /* We need FOR_ITER and POP_JUMP_ to be the same size */ + assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1); + if (self->right) { + offset += 2; + } + if (offset >= Py_SIZE(code)) { + return res; + } + int other_event = self->right ? + PY_MONITORING_EVENT_BRANCH_LEFT : PY_MONITORING_EVENT_BRANCH_RIGHT; + LOCK_CODE(code); + remove_tools(code, offset, other_event, 1 << self->tool_id); + UNLOCK_CODE(); + } + return res; +} + +static PyObject *make_branch_handler(int tool_id, PyObject *handler, bool right) +{ + _PyLegacyBranchEventHandler *callback = + PyObject_NEW(_PyLegacyBranchEventHandler, &_PyLegacyBranchEventHandler_Type); + if (callback == NULL) { + return NULL; + } + callback->vectorcall = (vectorcallfunc)branch_handler; + callback->handler = Py_NewRef(handler); + callback->right = right; + callback->tool_id = tool_id; + return (PyObject *)callback; +} + +/* Consumes a reference to obj */ +static PyObject *exchange_callables(int tool_id, int event_id, PyObject *obj) +{ + PyInterpreterState *is = _PyInterpreterState_GET(); + return _Py_atomic_exchange_ptr(&is->monitoring_callables[tool_id][event_id], obj); +} + +PyObject * +_PyMonitoring_RegisterCallback(int tool_id, int event_id, PyObject *obj) +{ + assert(0 <= tool_id && tool_id < PY_MONITORING_TOOL_IDS); + assert(0 <= event_id && event_id < _PY_MONITORING_EVENTS); + PyObject *res; + if (event_id == PY_MONITORING_EVENT_BRANCH) { + PyObject *left, *right; + if (obj == NULL) { + left = NULL; + right = NULL; + } + else { + right = make_branch_handler(tool_id, obj, true); + if (right == NULL) { + return NULL; + } + left = make_branch_handler(tool_id, obj, false); + if (left == NULL) { + Py_DECREF(right); + return NULL; + } + } + Py_XDECREF(exchange_callables(tool_id, PY_MONITORING_EVENT_BRANCH_RIGHT, right)); + res = exchange_callables(tool_id, PY_MONITORING_EVENT_BRANCH_LEFT, left); + } + else { + res = exchange_callables(tool_id, event_id, Py_XNewRef(obj)); + } + if (res != NULL && Py_TYPE(res) == &_PyLegacyBranchEventHandler_Type) { + _PyLegacyBranchEventHandler *wrapper = (_PyLegacyBranchEventHandler *)res; + res = Py_NewRef(wrapper->handler); + Py_DECREF(wrapper); + } + return res; +} + +/* Branch Iterator */ + +typedef struct { + PyObject_HEAD + PyCodeObject *bi_code; + int bi_offset; +} branchesiterator; + +static PyObject * +int_triple(int a, int b, int c) { + PyObject *obja = PyLong_FromLong(a); + PyObject *objb = NULL; + PyObject *objc = NULL; + if (obja == NULL) { + goto error; + } + objb = PyLong_FromLong(b); + if (objb == NULL) { + goto error; + } + objc = PyLong_FromLong(c); + if (objc == NULL) { + goto error; + } + PyObject *array[3] = { obja, objb, objc }; + return _PyTuple_FromArraySteal(array, 3); +error: + Py_XDECREF(obja); + Py_XDECREF(objb); + Py_XDECREF(objc); + return NULL; +} + +static PyObject * +branchesiter_next(branchesiterator *bi) +{ + int offset = bi->bi_offset; + while (offset < Py_SIZE(bi->bi_code)) { + _Py_CODEUNIT inst = _Py_GetBaseCodeUnit(bi->bi_code, offset); + int next_offset = offset + _PyInstruction_GetLength(bi->bi_code, offset); + int event = EVENT_FOR_OPCODE[inst.op.code]; + if (event == PY_MONITORING_EVENT_BRANCH_RIGHT) { + /* Skip NOT_TAKEN */ + int not_taken = next_offset + 1; + bi->bi_offset = not_taken; + return int_triple(offset*2, not_taken*2, (next_offset + inst.op.arg)*2); + } + offset = next_offset; + } + return NULL; +} + +static void +branchesiter_dealloc(branchesiterator *bi) +{ + Py_DECREF(bi->bi_code); + PyObject_Free(bi); +} + +static PyTypeObject _PyBranchesIterator = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "line_iterator", /* tp_name */ + sizeof(branchesiterator), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + .tp_dealloc = (destructor)branchesiter_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_iter = PyObject_SelfIter, + .tp_iternext = (iternextfunc)branchesiter_next, + .tp_free = PyObject_Del, +}; + +PyObject * +_PyInstrumentation_BranchesIterator(PyCodeObject *code) +{ + + branchesiterator *bi = (branchesiterator *)PyType_GenericAlloc(&_PyBranchesIterator, 0); + if (bi == NULL) { + return NULL; + } + bi->bi_code = (PyCodeObject*)Py_NewRef(code); + bi->bi_offset = 0; + return (PyObject *)bi; +} diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index c93941dcac4abf..7f3fb9c9a63dd1 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -27,6 +27,7 @@ static void *opcode_targets[256] = { &&TARGET_MATCH_MAPPING, &&TARGET_MATCH_SEQUENCE, &&TARGET_NOP, + &&TARGET_NOT_TAKEN, &&TARGET_POP_EXCEPT, &&TARGET_POP_TOP, &&TARGET_PUSH_EXC_INFO, @@ -147,7 +148,6 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, - &&_unknown_opcode, &&TARGET_RESUME, &&TARGET_BINARY_OP_ADD_FLOAT, &&TARGET_BINARY_OP_ADD_INT, @@ -235,7 +235,6 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, - &&_unknown_opcode, &&TARGET_INSTRUMENTED_END_FOR, &&TARGET_INSTRUMENTED_END_SEND, &&TARGET_INSTRUMENTED_LOAD_SUPER_ATTR, @@ -244,6 +243,7 @@ static void *opcode_targets[256] = { &&TARGET_INSTRUMENTED_CALL_FUNCTION_EX, &&TARGET_INSTRUMENTED_INSTRUCTION, &&TARGET_INSTRUMENTED_JUMP_FORWARD, + &&TARGET_INSTRUMENTED_NOT_TAKEN, &&TARGET_INSTRUMENTED_POP_JUMP_IF_TRUE, &&TARGET_INSTRUMENTED_POP_JUMP_IF_FALSE, &&TARGET_INSTRUMENTED_POP_JUMP_IF_NONE, diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 33b34d6fa0d3f9..2c3133d7107df2 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -2343,6 +2343,8 @@ /* _MONITOR_JUMP_BACKWARD is not a viable micro-op for tier 2 */ + /* _INSTRUMENTED_NOT_TAKEN is not a viable micro-op for tier 2 */ + /* _INSTRUMENTED_POP_JUMP_IF_TRUE is not a viable micro-op for tier 2 */ /* _INSTRUMENTED_POP_JUMP_IF_FALSE is not a viable micro-op for tier 2 */ diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index badd7b79102310..a1ec1927eb56df 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -106,6 +106,8 @@ Python/context.c - PyContextToken_Type - Python/context.c - PyContextVar_Type - Python/context.c - PyContext_Type - Python/instruction_sequence.c - _PyInstructionSequence_Type - +Python/instrumentation.c - _PyLegacyBranchEventHandler_Type - +Python/instrumentation.c - _PyBranchesIterator - Python/traceback.c - PyTraceBack_Type - ##----------------------- From 1b15c89a17ca3de6b05de5379b8717e9738c51ef Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Thu, 19 Dec 2024 10:21:17 -0800 Subject: [PATCH 068/376] gh-115999: Specialize `STORE_ATTR` in free-threaded builds. (gh-127838) * Add `_PyDictKeys_StringLookupSplit` which does locking on dict keys and use in place of `_PyDictKeys_StringLookup`. * Change `_PyObject_TryGetInstanceAttribute` to use that function in the case of split keys. * Add `unicodekeys_lookup_split` helper which allows code sharing between `_Py_dict_lookup` and `_PyDictKeys_StringLookupSplit`. * Fix locking for `STORE_ATTR_INSTANCE_VALUE`. Create `_GUARD_TYPE_VERSION_AND_LOCK` uop so that object stays locked and `tp_version_tag` cannot change. * Pass `tp_version_tag` to `specialize_dict_access()`, ensuring the version we store on the cache is the correct one (in case of it changing during the specalize analysis). * Split `analyze_descriptor` into `analyze_descriptor_load` and `analyze_descriptor_store` since those don't share much logic. Add `descriptor_is_class` helper function. * In `specialize_dict_access`, double check `_PyObject_GetManagedDict()` in case we race and dict was materialized before the lock. * Avoid borrowed references in `_Py_Specialize_StoreAttr()`. * Use `specialize()` and `unspecialize()` helpers. * Add unit tests to ensure specializing happens as expected in FT builds. * Add unit tests to attempt to trigger data races (useful for running under TSAN). * Add `has_split_table` function to `_testinternalcapi`. --- Include/internal/pycore_dict.h | 1 + Include/internal/pycore_opcode_metadata.h | 4 +- Include/internal/pycore_uop_ids.h | 201 +++++++-------- Include/internal/pycore_uop_metadata.h | 6 +- Lib/test/test_free_threading/test_races.py | 141 +++++++++++ Lib/test/test_opcache.py | 66 +++++ Modules/_testinternalcapi.c | 9 + Objects/dictobject.c | 66 +++-- Python/bytecodes.c | 65 +++-- Python/executor_cases.c.h | 93 +++++-- Python/generated_cases.c.h | 86 ++++--- Python/optimizer_cases.c.h | 4 + Python/specialize.c | 271 +++++++++++++-------- 13 files changed, 716 insertions(+), 297 deletions(-) diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index 6e4a308226f3fe..71927006d1cd48 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -114,6 +114,7 @@ extern Py_ssize_t _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject extern Py_ssize_t _PyDict_LookupIndex(PyDictObject *, PyObject *); extern Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject *key); +extern Py_ssize_t _PyDictKeys_StringLookupSplit(PyDictKeysObject* dictkeys, PyObject *key); PyAPI_FUNC(PyObject *)_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *); PyAPI_FUNC(void) _PyDict_LoadGlobalStackRef(PyDictObject *, PyDictObject *, PyObject *, _PyStackRef *); diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index e09fff062b5202..5fb236836dccd9 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -2141,7 +2141,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [SET_UPDATE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [STORE_ATTR] = { true, INSTR_FMT_IBC000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [STORE_ATTR_INSTANCE_VALUE] = { true, INSTR_FMT_IXC000, HAS_EXIT_FLAG }, - [STORE_ATTR_SLOT] = { true, INSTR_FMT_IXC000, HAS_EXIT_FLAG }, + [STORE_ATTR_SLOT] = { true, INSTR_FMT_IXC000, HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, [STORE_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG }, [STORE_DEREF] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ESCAPES_FLAG }, [STORE_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG }, @@ -2340,7 +2340,7 @@ _PyOpcode_macro_expansion[256] = { [SET_FUNCTION_ATTRIBUTE] = { .nuops = 1, .uops = { { _SET_FUNCTION_ATTRIBUTE, 0, 0 } } }, [SET_UPDATE] = { .nuops = 1, .uops = { { _SET_UPDATE, 0, 0 } } }, [STORE_ATTR] = { .nuops = 1, .uops = { { _STORE_ATTR, 0, 0 } } }, - [STORE_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_NO_DICT, 0, 0 }, { _STORE_ATTR_INSTANCE_VALUE, 1, 3 } } }, + [STORE_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION_AND_LOCK, 2, 1 }, { _GUARD_DORV_NO_DICT, 0, 0 }, { _STORE_ATTR_INSTANCE_VALUE, 1, 3 } } }, [STORE_ATTR_SLOT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _STORE_ATTR_SLOT, 1, 3 } } }, [STORE_ATTR_WITH_HINT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _STORE_ATTR_WITH_HINT, 1, 3 } } }, [STORE_DEREF] = { .nuops = 1, .uops = { { _STORE_DEREF, 0, 0 } } }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 5fc57e48f500d0..92515b4230ccb4 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -139,15 +139,16 @@ extern "C" { #define _GUARD_TOS_FLOAT 386 #define _GUARD_TOS_INT 387 #define _GUARD_TYPE_VERSION 388 +#define _GUARD_TYPE_VERSION_AND_LOCK 389 #define _IMPORT_FROM IMPORT_FROM #define _IMPORT_NAME IMPORT_NAME -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 389 -#define _INIT_CALL_PY_EXACT_ARGS 390 -#define _INIT_CALL_PY_EXACT_ARGS_0 391 -#define _INIT_CALL_PY_EXACT_ARGS_1 392 -#define _INIT_CALL_PY_EXACT_ARGS_2 393 -#define _INIT_CALL_PY_EXACT_ARGS_3 394 -#define _INIT_CALL_PY_EXACT_ARGS_4 395 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 390 +#define _INIT_CALL_PY_EXACT_ARGS 391 +#define _INIT_CALL_PY_EXACT_ARGS_0 392 +#define _INIT_CALL_PY_EXACT_ARGS_1 393 +#define _INIT_CALL_PY_EXACT_ARGS_2 394 +#define _INIT_CALL_PY_EXACT_ARGS_3 395 +#define _INIT_CALL_PY_EXACT_ARGS_4 396 #define _INSTRUMENTED_CALL_FUNCTION_EX INSTRUMENTED_CALL_FUNCTION_EX #define _INSTRUMENTED_CALL_KW INSTRUMENTED_CALL_KW #define _INSTRUMENTED_FOR_ITER INSTRUMENTED_FOR_ITER @@ -160,143 +161,143 @@ extern "C" { #define _INSTRUMENTED_POP_JUMP_IF_NONE INSTRUMENTED_POP_JUMP_IF_NONE #define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE INSTRUMENTED_POP_JUMP_IF_NOT_NONE #define _INSTRUMENTED_POP_JUMP_IF_TRUE INSTRUMENTED_POP_JUMP_IF_TRUE -#define _INTERNAL_INCREMENT_OPT_COUNTER 396 -#define _IS_NONE 397 +#define _INTERNAL_INCREMENT_OPT_COUNTER 397 +#define _IS_NONE 398 #define _IS_OP IS_OP -#define _ITER_CHECK_LIST 398 -#define _ITER_CHECK_RANGE 399 -#define _ITER_CHECK_TUPLE 400 -#define _ITER_JUMP_LIST 401 -#define _ITER_JUMP_RANGE 402 -#define _ITER_JUMP_TUPLE 403 -#define _ITER_NEXT_LIST 404 -#define _ITER_NEXT_RANGE 405 -#define _ITER_NEXT_TUPLE 406 -#define _JUMP_TO_TOP 407 +#define _ITER_CHECK_LIST 399 +#define _ITER_CHECK_RANGE 400 +#define _ITER_CHECK_TUPLE 401 +#define _ITER_JUMP_LIST 402 +#define _ITER_JUMP_RANGE 403 +#define _ITER_JUMP_TUPLE 404 +#define _ITER_NEXT_LIST 405 +#define _ITER_NEXT_RANGE 406 +#define _ITER_NEXT_TUPLE 407 +#define _JUMP_TO_TOP 408 #define _LIST_APPEND LIST_APPEND #define _LIST_EXTEND LIST_EXTEND -#define _LOAD_ATTR 408 -#define _LOAD_ATTR_CLASS 409 -#define _LOAD_ATTR_CLASS_0 410 -#define _LOAD_ATTR_CLASS_1 411 +#define _LOAD_ATTR 409 +#define _LOAD_ATTR_CLASS 410 +#define _LOAD_ATTR_CLASS_0 411 +#define _LOAD_ATTR_CLASS_1 412 #define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN -#define _LOAD_ATTR_INSTANCE_VALUE 412 -#define _LOAD_ATTR_INSTANCE_VALUE_0 413 -#define _LOAD_ATTR_INSTANCE_VALUE_1 414 -#define _LOAD_ATTR_METHOD_LAZY_DICT 415 -#define _LOAD_ATTR_METHOD_NO_DICT 416 -#define _LOAD_ATTR_METHOD_WITH_VALUES 417 -#define _LOAD_ATTR_MODULE 418 -#define _LOAD_ATTR_MODULE_FROM_KEYS 419 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 420 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 421 -#define _LOAD_ATTR_PROPERTY_FRAME 422 -#define _LOAD_ATTR_SLOT 423 -#define _LOAD_ATTR_SLOT_0 424 -#define _LOAD_ATTR_SLOT_1 425 -#define _LOAD_ATTR_WITH_HINT 426 +#define _LOAD_ATTR_INSTANCE_VALUE 413 +#define _LOAD_ATTR_INSTANCE_VALUE_0 414 +#define _LOAD_ATTR_INSTANCE_VALUE_1 415 +#define _LOAD_ATTR_METHOD_LAZY_DICT 416 +#define _LOAD_ATTR_METHOD_NO_DICT 417 +#define _LOAD_ATTR_METHOD_WITH_VALUES 418 +#define _LOAD_ATTR_MODULE 419 +#define _LOAD_ATTR_MODULE_FROM_KEYS 420 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 421 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 422 +#define _LOAD_ATTR_PROPERTY_FRAME 423 +#define _LOAD_ATTR_SLOT 424 +#define _LOAD_ATTR_SLOT_0 425 +#define _LOAD_ATTR_SLOT_1 426 +#define _LOAD_ATTR_WITH_HINT 427 #define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS -#define _LOAD_BYTECODE 427 +#define _LOAD_BYTECODE 428 #define _LOAD_COMMON_CONSTANT LOAD_COMMON_CONSTANT #define _LOAD_CONST LOAD_CONST #define _LOAD_CONST_IMMORTAL LOAD_CONST_IMMORTAL -#define _LOAD_CONST_INLINE 428 -#define _LOAD_CONST_INLINE_BORROW 429 -#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 430 -#define _LOAD_CONST_INLINE_WITH_NULL 431 +#define _LOAD_CONST_INLINE 429 +#define _LOAD_CONST_INLINE_BORROW 430 +#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 431 +#define _LOAD_CONST_INLINE_WITH_NULL 432 #define _LOAD_DEREF LOAD_DEREF -#define _LOAD_FAST 432 -#define _LOAD_FAST_0 433 -#define _LOAD_FAST_1 434 -#define _LOAD_FAST_2 435 -#define _LOAD_FAST_3 436 -#define _LOAD_FAST_4 437 -#define _LOAD_FAST_5 438 -#define _LOAD_FAST_6 439 -#define _LOAD_FAST_7 440 +#define _LOAD_FAST 433 +#define _LOAD_FAST_0 434 +#define _LOAD_FAST_1 435 +#define _LOAD_FAST_2 436 +#define _LOAD_FAST_3 437 +#define _LOAD_FAST_4 438 +#define _LOAD_FAST_5 439 +#define _LOAD_FAST_6 440 +#define _LOAD_FAST_7 441 #define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR #define _LOAD_FAST_CHECK LOAD_FAST_CHECK #define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST #define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF #define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS -#define _LOAD_GLOBAL 441 -#define _LOAD_GLOBAL_BUILTINS 442 -#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 443 -#define _LOAD_GLOBAL_MODULE 444 -#define _LOAD_GLOBAL_MODULE_FROM_KEYS 445 +#define _LOAD_GLOBAL 442 +#define _LOAD_GLOBAL_BUILTINS 443 +#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 444 +#define _LOAD_GLOBAL_MODULE 445 +#define _LOAD_GLOBAL_MODULE_FROM_KEYS 446 #define _LOAD_LOCALS LOAD_LOCALS #define _LOAD_NAME LOAD_NAME -#define _LOAD_SMALL_INT 446 -#define _LOAD_SMALL_INT_0 447 -#define _LOAD_SMALL_INT_1 448 -#define _LOAD_SMALL_INT_2 449 -#define _LOAD_SMALL_INT_3 450 +#define _LOAD_SMALL_INT 447 +#define _LOAD_SMALL_INT_0 448 +#define _LOAD_SMALL_INT_1 449 +#define _LOAD_SMALL_INT_2 450 +#define _LOAD_SMALL_INT_3 451 #define _LOAD_SPECIAL LOAD_SPECIAL #define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR #define _LOAD_SUPER_ATTR_METHOD LOAD_SUPER_ATTR_METHOD -#define _MAKE_CALLARGS_A_TUPLE 451 +#define _MAKE_CALLARGS_A_TUPLE 452 #define _MAKE_CELL MAKE_CELL #define _MAKE_FUNCTION MAKE_FUNCTION -#define _MAKE_WARM 452 +#define _MAKE_WARM 453 #define _MAP_ADD MAP_ADD #define _MATCH_CLASS MATCH_CLASS #define _MATCH_KEYS MATCH_KEYS #define _MATCH_MAPPING MATCH_MAPPING #define _MATCH_SEQUENCE MATCH_SEQUENCE -#define _MAYBE_EXPAND_METHOD 453 -#define _MAYBE_EXPAND_METHOD_KW 454 -#define _MONITOR_CALL 455 -#define _MONITOR_JUMP_BACKWARD 456 -#define _MONITOR_RESUME 457 +#define _MAYBE_EXPAND_METHOD 454 +#define _MAYBE_EXPAND_METHOD_KW 455 +#define _MONITOR_CALL 456 +#define _MONITOR_JUMP_BACKWARD 457 +#define _MONITOR_RESUME 458 #define _NOP NOP #define _POP_EXCEPT POP_EXCEPT -#define _POP_JUMP_IF_FALSE 458 -#define _POP_JUMP_IF_TRUE 459 +#define _POP_JUMP_IF_FALSE 459 +#define _POP_JUMP_IF_TRUE 460 #define _POP_TOP POP_TOP -#define _POP_TOP_LOAD_CONST_INLINE_BORROW 460 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW 461 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 461 +#define _PUSH_FRAME 462 #define _PUSH_NULL PUSH_NULL -#define _PY_FRAME_GENERAL 462 -#define _PY_FRAME_KW 463 -#define _QUICKEN_RESUME 464 -#define _REPLACE_WITH_TRUE 465 +#define _PY_FRAME_GENERAL 463 +#define _PY_FRAME_KW 464 +#define _QUICKEN_RESUME 465 +#define _REPLACE_WITH_TRUE 466 #define _RESUME_CHECK RESUME_CHECK #define _RETURN_GENERATOR RETURN_GENERATOR #define _RETURN_VALUE RETURN_VALUE -#define _SAVE_RETURN_OFFSET 466 -#define _SEND 467 -#define _SEND_GEN_FRAME 468 +#define _SAVE_RETURN_OFFSET 467 +#define _SEND 468 +#define _SEND_GEN_FRAME 469 #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _START_EXECUTOR 469 -#define _STORE_ATTR 470 -#define _STORE_ATTR_INSTANCE_VALUE 471 -#define _STORE_ATTR_SLOT 472 -#define _STORE_ATTR_WITH_HINT 473 +#define _START_EXECUTOR 470 +#define _STORE_ATTR 471 +#define _STORE_ATTR_INSTANCE_VALUE 472 +#define _STORE_ATTR_SLOT 473 +#define _STORE_ATTR_WITH_HINT 474 #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 474 -#define _STORE_FAST_0 475 -#define _STORE_FAST_1 476 -#define _STORE_FAST_2 477 -#define _STORE_FAST_3 478 -#define _STORE_FAST_4 479 -#define _STORE_FAST_5 480 -#define _STORE_FAST_6 481 -#define _STORE_FAST_7 482 +#define _STORE_FAST 475 +#define _STORE_FAST_0 476 +#define _STORE_FAST_1 477 +#define _STORE_FAST_2 478 +#define _STORE_FAST_3 479 +#define _STORE_FAST_4 480 +#define _STORE_FAST_5 481 +#define _STORE_FAST_6 482 +#define _STORE_FAST_7 483 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME -#define _STORE_SLICE 483 -#define _STORE_SUBSCR 484 +#define _STORE_SLICE 484 +#define _STORE_SUBSCR 485 #define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT #define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT #define _SWAP SWAP -#define _TIER2_RESUME_CHECK 485 -#define _TO_BOOL 486 +#define _TIER2_RESUME_CHECK 486 +#define _TO_BOOL 487 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT #define _TO_BOOL_LIST TO_BOOL_LIST @@ -306,13 +307,13 @@ extern "C" { #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 487 +#define _UNPACK_SEQUENCE 488 #define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START #define _YIELD_VALUE YIELD_VALUE -#define MAX_UOP_ID 487 +#define MAX_UOP_ID 488 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index eadfda472a7270..e71194b116e020 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -148,6 +148,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_SUPER_ATTR_METHOD] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_LOAD_ATTR] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GUARD_TYPE_VERSION] = HAS_EXIT_FLAG, + [_GUARD_TYPE_VERSION_AND_LOCK] = HAS_EXIT_FLAG, [_CHECK_MANAGED_OBJECT_HAS_VALUES] = HAS_DEOPT_FLAG, [_LOAD_ATTR_INSTANCE_VALUE_0] = HAS_DEOPT_FLAG, [_LOAD_ATTR_INSTANCE_VALUE_1] = HAS_DEOPT_FLAG, @@ -167,7 +168,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_GUARD_DORV_NO_DICT] = HAS_EXIT_FLAG, [_STORE_ATTR_INSTANCE_VALUE] = 0, [_STORE_ATTR_WITH_HINT] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, - [_STORE_ATTR_SLOT] = 0, + [_STORE_ATTR_SLOT] = HAS_DEOPT_FLAG, [_COMPARE_OP] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_COMPARE_OP_FLOAT] = HAS_ARG_FLAG, [_COMPARE_OP_INT] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, @@ -428,6 +429,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_GUARD_TOS_FLOAT] = "_GUARD_TOS_FLOAT", [_GUARD_TOS_INT] = "_GUARD_TOS_INT", [_GUARD_TYPE_VERSION] = "_GUARD_TYPE_VERSION", + [_GUARD_TYPE_VERSION_AND_LOCK] = "_GUARD_TYPE_VERSION_AND_LOCK", [_IMPORT_FROM] = "_IMPORT_FROM", [_IMPORT_NAME] = "_IMPORT_NAME", [_INIT_CALL_BOUND_METHOD_EXACT_ARGS] = "_INIT_CALL_BOUND_METHOD_EXACT_ARGS", @@ -839,6 +841,8 @@ int _PyUop_num_popped(int opcode, int oparg) return 1; case _GUARD_TYPE_VERSION: return 0; + case _GUARD_TYPE_VERSION_AND_LOCK: + return 0; case _CHECK_MANAGED_OBJECT_HAS_VALUES: return 0; case _LOAD_ATTR_INSTANCE_VALUE_0: diff --git a/Lib/test/test_free_threading/test_races.py b/Lib/test/test_free_threading/test_races.py index 09e1d52e3509f9..69982558a067a5 100644 --- a/Lib/test/test_free_threading/test_races.py +++ b/Lib/test/test_free_threading/test_races.py @@ -4,6 +4,7 @@ import threading import time import unittest +import _testinternalcapi from test.support import threading_helper @@ -129,6 +130,146 @@ def mutate(): # with the cell binding being changed). do_race(access, mutate) + def test_racing_to_bool(self): + + seq = [1] + + class C: + def __bool__(self): + return False + + def access(): + if seq: + return 1 + else: + return 2 + + def mutate(): + nonlocal seq + seq = [1] + time.sleep(0) + seq = C() + time.sleep(0) + + do_race(access, mutate) + + def test_racing_store_attr_slot(self): + class C: + __slots__ = ['x', '__dict__'] + + c = C() + + def set_slot(): + for i in range(10): + c.x = i + time.sleep(0) + + def change_type(): + def set_x(self, x): + pass + + def get_x(self): + pass + + C.x = property(get_x, set_x) + time.sleep(0) + del C.x + time.sleep(0) + + do_race(set_slot, change_type) + + def set_getattribute(): + C.__getattribute__ = lambda self, x: x + time.sleep(0) + del C.__getattribute__ + time.sleep(0) + + do_race(set_slot, set_getattribute) + + def test_racing_store_attr_instance_value(self): + class C: + pass + + c = C() + + def set_value(): + for i in range(100): + c.x = i + + set_value() + + def read(): + x = c.x + + def mutate(): + # Adding a property for 'x' should unspecialize it. + C.x = property(lambda self: None, lambda self, x: None) + time.sleep(0) + del C.x + time.sleep(0) + + do_race(read, mutate) + + def test_racing_store_attr_with_hint(self): + class C: + pass + + c = C() + for i in range(29): + setattr(c, f"_{i}", None) + + def set_value(): + for i in range(100): + c.x = i + + set_value() + + def read(): + x = c.x + + def mutate(): + # Adding a property for 'x' should unspecialize it. + C.x = property(lambda self: None, lambda self, x: None) + time.sleep(0) + del C.x + time.sleep(0) + + do_race(read, mutate) + + def make_shared_key_dict(self): + class C: + pass + + a = C() + a.x = 1 + return a.__dict__ + + def test_racing_store_attr_dict(self): + """Test STORE_ATTR with various dictionary types.""" + class C: + pass + + c = C() + + def set_value(): + for i in range(20): + c.x = i + + def mutate(): + nonlocal c + c.x = 1 + self.assertTrue(_testinternalcapi.has_inline_values(c)) + for i in range(30): + setattr(c, f"_{i}", None) + self.assertFalse(_testinternalcapi.has_inline_values(c.__dict__)) + c.__dict__ = self.make_shared_key_dict() + self.assertTrue(_testinternalcapi.has_split_table(c.__dict__)) + c.__dict__[1] = None + self.assertFalse(_testinternalcapi.has_split_table(c.__dict__)) + c = C() + + do_race(set_value, mutate) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index 94709e2022550a..ad0b0c487a4118 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -1383,6 +1383,72 @@ def send_yield_from(): self.assert_specialized(send_yield_from, "SEND_GEN") self.assert_no_opcode(send_yield_from, "SEND") + @cpython_only + @requires_specialization_ft + def test_store_attr_slot(self): + class C: + __slots__ = ['x'] + + def set_slot(): + c = C() + for i in range(100): + c.x = i + + set_slot() + + self.assert_specialized(set_slot, "STORE_ATTR_SLOT") + self.assert_no_opcode(set_slot, "STORE_ATTR") + + # Adding a property for 'x' should unspecialize it. + C.x = property(lambda self: None, lambda self, x: None) + set_slot() + self.assert_no_opcode(set_slot, "STORE_ATTR_SLOT") + + @cpython_only + @requires_specialization_ft + def test_store_attr_instance_value(self): + class C: + pass + + def set_value(): + c = C() + for i in range(100): + c.x = i + + set_value() + + self.assert_specialized(set_value, "STORE_ATTR_INSTANCE_VALUE") + self.assert_no_opcode(set_value, "STORE_ATTR") + + # Adding a property for 'x' should unspecialize it. + C.x = property(lambda self: None, lambda self, x: None) + set_value() + self.assert_no_opcode(set_value, "STORE_ATTR_INSTANCE_VALUE") + + @cpython_only + @requires_specialization_ft + def test_store_attr_with_hint(self): + class C: + pass + + c = C() + for i in range(29): + setattr(c, f"_{i}", None) + + def set_value(): + for i in range(100): + c.x = i + + set_value() + + self.assert_specialized(set_value, "STORE_ATTR_WITH_HINT") + self.assert_no_opcode(set_value, "STORE_ATTR") + + # Adding a property for 'x' should unspecialize it. + C.x = property(lambda self: None, lambda self, x: None) + set_value() + self.assert_no_opcode(set_value, "STORE_ATTR_WITH_HINT") + @cpython_only @requires_specialization_ft def test_to_bool(self): diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 014f89997f7f60..150d34d168f5e4 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -1989,6 +1989,14 @@ has_inline_values(PyObject *self, PyObject *obj) Py_RETURN_FALSE; } +static PyObject * +has_split_table(PyObject *self, PyObject *obj) +{ + if (PyDict_Check(obj) && _PyDict_HasSplitTable((PyDictObject *)obj)) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} // Circumvents standard version assignment machinery - use with caution and only on // short-lived heap types @@ -2139,6 +2147,7 @@ static PyMethodDef module_functions[] = { {"get_rare_event_counters", get_rare_event_counters, METH_NOARGS}, {"reset_rare_event_counters", reset_rare_event_counters, METH_NOARGS}, {"has_inline_values", has_inline_values, METH_O}, + {"has_split_table", has_split_table, METH_O}, {"type_assign_specific_version_unsafe", type_assign_specific_version_unsafe, METH_VARARGS, PyDoc_STR("forcefully assign type->tp_version_tag")}, diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 05c93a3e448181..2a054c3f2ae0ff 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1129,6 +1129,35 @@ dictkeys_generic_lookup(PyDictObject *mp, PyDictKeysObject* dk, PyObject *key, P return do_lookup(mp, dk, key, hash, compare_generic); } +#ifdef Py_GIL_DISABLED +static Py_ssize_t +unicodekeys_lookup_unicode_threadsafe(PyDictKeysObject* dk, PyObject *key, + Py_hash_t hash); +#endif + +static Py_ssize_t +unicodekeys_lookup_split(PyDictKeysObject* dk, PyObject *key, Py_hash_t hash) +{ + Py_ssize_t ix; + assert(dk->dk_kind == DICT_KEYS_SPLIT); + assert(PyUnicode_CheckExact(key)); + +#ifdef Py_GIL_DISABLED + // A split dictionaries keys can be mutated by other dictionaries + // but if we have a unicode key we can avoid locking the shared + // keys. + ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash); + if (ix == DKIX_KEY_CHANGED) { + LOCK_KEYS(dk); + ix = unicodekeys_lookup_unicode(dk, key, hash); + UNLOCK_KEYS(dk); + } +#else + ix = unicodekeys_lookup_unicode(dk, key, hash); +#endif + return ix; +} + /* Lookup a string in a (all unicode) dict keys. * Returns DKIX_ERROR if key is not a string, * or if the dict keys is not all strings. @@ -1153,13 +1182,24 @@ _PyDictKeys_StringLookup(PyDictKeysObject* dk, PyObject *key) return unicodekeys_lookup_unicode(dk, key, hash); } -#ifdef Py_GIL_DISABLED - -static Py_ssize_t -unicodekeys_lookup_unicode_threadsafe(PyDictKeysObject* dk, PyObject *key, - Py_hash_t hash); - -#endif +/* Like _PyDictKeys_StringLookup() but only works on split keys. Note + * that in free-threaded builds this locks the keys object as required. + */ +Py_ssize_t +_PyDictKeys_StringLookupSplit(PyDictKeysObject* dk, PyObject *key) +{ + assert(dk->dk_kind == DICT_KEYS_SPLIT); + assert(PyUnicode_CheckExact(key)); + Py_hash_t hash = unicode_get_hash(key); + if (hash == -1) { + hash = PyUnicode_Type.tp_hash(key); + if (hash == -1) { + PyErr_Clear(); + return DKIX_ERROR; + } + } + return unicodekeys_lookup_split(dk, key, hash); +} /* The basic lookup function used by all operations. @@ -1192,15 +1232,7 @@ _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **valu if (PyUnicode_CheckExact(key)) { #ifdef Py_GIL_DISABLED if (kind == DICT_KEYS_SPLIT) { - // A split dictionaries keys can be mutated by other - // dictionaries but if we have a unicode key we can avoid - // locking the shared keys. - ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash); - if (ix == DKIX_KEY_CHANGED) { - LOCK_KEYS(dk); - ix = unicodekeys_lookup_unicode(dk, key, hash); - UNLOCK_KEYS(dk); - } + ix = unicodekeys_lookup_split(dk, key, hash); } else { ix = unicodekeys_lookup_unicode(dk, key, hash); @@ -6967,7 +6999,7 @@ _PyObject_TryGetInstanceAttribute(PyObject *obj, PyObject *name, PyObject **attr PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj)); assert(keys != NULL); - Py_ssize_t ix = _PyDictKeys_StringLookup(keys, name); + Py_ssize_t ix = _PyDictKeys_StringLookupSplit(keys, name); if (ix == DKIX_EMPTY) { *attr = NULL; return true; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index cf089c368b651c..30c12dd4dc9205 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1467,7 +1467,7 @@ dummy_func( }; specializing op(_SPECIALIZE_STORE_ATTR, (counter/1, owner -- owner)) { - #if ENABLE_SPECIALIZATION + #if ENABLE_SPECIALIZATION_FT if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); next_instr = this_instr; @@ -1476,7 +1476,7 @@ dummy_func( } OPCODE_DEFERRED_INC(STORE_ATTR); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION */ + #endif /* ENABLE_SPECIALIZATION_FT */ } op(_STORE_ATTR, (v, owner --)) { @@ -2129,7 +2129,18 @@ dummy_func( op(_GUARD_TYPE_VERSION, (type_version/2, owner -- owner)) { PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - EXIT_IF(tp->tp_version_tag != type_version); + EXIT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version); + } + + op(_GUARD_TYPE_VERSION_AND_LOCK, (type_version/2, owner -- owner)) { + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(type_version != 0); + EXIT_IF(!LOCK_OBJECT(owner_o)); + PyTypeObject *tp = Py_TYPE(owner_o); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UNLOCK_OBJECT(owner_o); + EXIT_IF(true); + } } op(_CHECK_MANAGED_OBJECT_HAS_VALUES, (owner -- owner)) { @@ -2336,8 +2347,11 @@ dummy_func( assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - EXIT_IF(_PyObject_GetManagedDict(owner_o)); - EXIT_IF(_PyObject_InlineValues(owner_o)->valid == 0); + if (_PyObject_GetManagedDict(owner_o) || + !FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + UNLOCK_OBJECT(owner_o); + EXIT_IF(true); + } } op(_STORE_ATTR_INSTANCE_VALUE, (offset/1, value, owner --)) { @@ -2347,21 +2361,20 @@ dummy_func( assert(_PyObject_GetManagedDict(owner_o) == NULL); PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); PyObject *old_value = *value_ptr; - *value_ptr = PyStackRef_AsPyObjectSteal(value); + FT_ATOMIC_STORE_PTR_RELEASE(*value_ptr, PyStackRef_AsPyObjectSteal(value)); if (old_value == NULL) { PyDictValues *values = _PyObject_InlineValues(owner_o); Py_ssize_t index = value_ptr - values->values; _PyDictValues_AddToInsertionOrder(values, index); } - else { - Py_DECREF(old_value); - } + UNLOCK_OBJECT(owner_o); + Py_XDECREF(old_value); PyStackRef_CLOSE(owner); } macro(STORE_ATTR_INSTANCE_VALUE) = unused/1 + - _GUARD_TYPE_VERSION + + _GUARD_TYPE_VERSION_AND_LOCK + _GUARD_DORV_NO_DICT + _STORE_ATTR_INSTANCE_VALUE; @@ -2370,16 +2383,34 @@ dummy_func( assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = _PyObject_GetManagedDict(owner_o); DEOPT_IF(dict == NULL); + DEOPT_IF(!LOCK_OBJECT(dict)); + #ifdef Py_GIL_DISABLED + if (dict != _PyObject_GetManagedDict(owner_o)) { + UNLOCK_OBJECT(dict); + DEOPT_IF(true); + } + #endif assert(PyDict_CheckExact((PyObject *)dict)); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries); - DEOPT_IF(!DK_IS_UNICODE(dict->ma_keys)); + if (hint >= (size_t)dict->ma_keys->dk_nentries || + !DK_IS_UNICODE(dict->ma_keys)) { + UNLOCK_OBJECT(dict); + DEOPT_IF(true); + } PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; - DEOPT_IF(ep->me_key != name); + if (ep->me_key != name) { + UNLOCK_OBJECT(dict); + DEOPT_IF(true); + } PyObject *old_value = ep->me_value; - DEOPT_IF(old_value == NULL); + if (old_value == NULL) { + UNLOCK_OBJECT(dict); + DEOPT_IF(true); + } _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); - ep->me_value = PyStackRef_AsPyObjectSteal(value); + FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value)); + UNLOCK_OBJECT(dict); + // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault, // when dict only holds the strong reference to value in ep->me_value. Py_XDECREF(old_value); @@ -2395,10 +2426,12 @@ dummy_func( op(_STORE_ATTR_SLOT, (index/1, value, owner --)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + DEOPT_IF(!LOCK_OBJECT(owner_o)); char *addr = (char *)owner_o + index; STAT_INC(STORE_ATTR, hit); PyObject *old_value = *(PyObject **)addr; - *(PyObject **)addr = PyStackRef_AsPyObjectSteal(value); + FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, PyStackRef_AsPyObjectSteal(value)); + UNLOCK_OBJECT(owner_o); Py_XDECREF(old_value); PyStackRef_CLOSE(owner); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 9bfc3e7f0b112b..6e752c57cd70f3 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2574,13 +2574,34 @@ uint32_t type_version = (uint32_t)CURRENT_OPERAND0(); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - if (tp->tp_version_tag != type_version) { + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } break; } + case _GUARD_TYPE_VERSION_AND_LOCK: { + _PyStackRef owner; + owner = stack_pointer[-1]; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(type_version != 0); + if (!LOCK_OBJECT(owner_o)) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + PyTypeObject *tp = Py_TYPE(owner_o); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UNLOCK_OBJECT(owner_o); + if (true) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + } + break; + } + case _CHECK_MANAGED_OBJECT_HAS_VALUES: { _PyStackRef owner; owner = stack_pointer[-1]; @@ -2910,13 +2931,13 @@ PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (_PyObject_GetManagedDict(owner_o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - if (_PyObject_InlineValues(owner_o)->valid == 0) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + if (_PyObject_GetManagedDict(owner_o) || + !FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + UNLOCK_OBJECT(owner_o); + if (true) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } } break; } @@ -2932,15 +2953,14 @@ assert(_PyObject_GetManagedDict(owner_o) == NULL); PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); PyObject *old_value = *value_ptr; - *value_ptr = PyStackRef_AsPyObjectSteal(value); + FT_ATOMIC_STORE_PTR_RELEASE(*value_ptr, PyStackRef_AsPyObjectSteal(value)); if (old_value == NULL) { PyDictValues *values = _PyObject_InlineValues(owner_o); Py_ssize_t index = value_ptr - values->values; _PyDictValues_AddToInsertionOrder(values, index); } - else { - Py_DECREF(old_value); - } + UNLOCK_OBJECT(owner_o); + Py_XDECREF(old_value); PyStackRef_CLOSE(owner); stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); @@ -2961,30 +2981,50 @@ UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - assert(PyDict_CheckExact((PyObject *)dict)); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - if (hint >= (size_t)dict->ma_keys->dk_nentries) { + if (!LOCK_OBJECT(dict)) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - if (!DK_IS_UNICODE(dict->ma_keys)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + #ifdef Py_GIL_DISABLED + if (dict != _PyObject_GetManagedDict(owner_o)) { + UNLOCK_OBJECT(dict); + if (true) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + } + #endif + assert(PyDict_CheckExact((PyObject *)dict)); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + if (hint >= (size_t)dict->ma_keys->dk_nentries || + !DK_IS_UNICODE(dict->ma_keys)) { + UNLOCK_OBJECT(dict); + if (true) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } } PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; if (ep->me_key != name) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + UNLOCK_OBJECT(dict); + if (true) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } } PyObject *old_value = ep->me_value; if (old_value == NULL) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + UNLOCK_OBJECT(dict); + if (true) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } } _PyFrame_SetStackPointer(frame, stack_pointer); _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); - ep->me_value = PyStackRef_AsPyObjectSteal(value); + FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value)); + UNLOCK_OBJECT(dict); // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault, // when dict only holds the strong reference to value in ep->me_value. Py_XDECREF(old_value); @@ -3002,10 +3042,15 @@ value = stack_pointer[-2]; uint16_t index = (uint16_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + if (!LOCK_OBJECT(owner_o)) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } char *addr = (char *)owner_o + index; STAT_INC(STORE_ATTR, hit); PyObject *old_value = *(PyObject **)addr; - *(PyObject **)addr = PyStackRef_AsPyObjectSteal(value); + FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, PyStackRef_AsPyObjectSteal(value)); + UNLOCK_OBJECT(owner_o); Py_XDECREF(old_value); PyStackRef_CLOSE(owner); stack_pointer += -2; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ac89891df80590..ee5c55a832d460 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -5319,7 +5319,7 @@ uint32_t type_version = read_u32(&this_instr[4].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); } // _LOAD_ATTR_CLASS { @@ -5388,7 +5388,7 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); } // _CHECK_MANAGED_OBJECT_HAS_VALUES { @@ -5433,7 +5433,7 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); } // _CHECK_ATTR_METHOD_LAZY_DICT { @@ -5476,7 +5476,7 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); } /* Skip 2 cache entries */ // _LOAD_ATTR_METHOD_NO_DICT @@ -5512,7 +5512,7 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); } // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT { @@ -5611,7 +5611,7 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); } /* Skip 2 cache entries */ // _LOAD_ATTR_NONDESCRIPTOR_NO_DICT @@ -5642,7 +5642,7 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); } // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT { @@ -5688,7 +5688,7 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); } /* Skip 2 cache entries */ // _LOAD_ATTR_PROPERTY_FRAME @@ -5750,7 +5750,7 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); } // _LOAD_ATTR_SLOT { @@ -5787,7 +5787,7 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); } // _CHECK_ATTR_WITH_HINT { @@ -7314,7 +7314,7 @@ owner = stack_pointer[-1]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION + #if ENABLE_SPECIALIZATION_FT if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); next_instr = this_instr; @@ -7325,7 +7325,7 @@ } OPCODE_DEFERRED_INC(STORE_ATTR); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION */ + #endif /* ENABLE_SPECIALIZATION_FT */ } /* Skip 3 cache entries */ // _STORE_ATTR @@ -7353,21 +7353,29 @@ _PyStackRef owner; _PyStackRef value; /* Skip 1 cache entry */ - // _GUARD_TYPE_VERSION + // _GUARD_TYPE_VERSION_AND_LOCK { owner = stack_pointer[-1]; uint32_t type_version = read_u32(&this_instr[2].cache); - PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); + DEOPT_IF(!LOCK_OBJECT(owner_o), STORE_ATTR); + PyTypeObject *tp = Py_TYPE(owner_o); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UNLOCK_OBJECT(owner_o); + DEOPT_IF(true, STORE_ATTR); + } } // _GUARD_DORV_NO_DICT { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - DEOPT_IF(_PyObject_GetManagedDict(owner_o), STORE_ATTR); - DEOPT_IF(_PyObject_InlineValues(owner_o)->valid == 0, STORE_ATTR); + if (_PyObject_GetManagedDict(owner_o) || + !FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + UNLOCK_OBJECT(owner_o); + DEOPT_IF(true, STORE_ATTR); + } } // _STORE_ATTR_INSTANCE_VALUE { @@ -7378,15 +7386,14 @@ assert(_PyObject_GetManagedDict(owner_o) == NULL); PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); PyObject *old_value = *value_ptr; - *value_ptr = PyStackRef_AsPyObjectSteal(value); + FT_ATOMIC_STORE_PTR_RELEASE(*value_ptr, PyStackRef_AsPyObjectSteal(value)); if (old_value == NULL) { PyDictValues *values = _PyObject_InlineValues(owner_o); Py_ssize_t index = value_ptr - values->values; _PyDictValues_AddToInsertionOrder(values, index); } - else { - Py_DECREF(old_value); - } + UNLOCK_OBJECT(owner_o); + Py_XDECREF(old_value); PyStackRef_CLOSE(owner); } stack_pointer += -2; @@ -7408,17 +7415,19 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, STORE_ATTR); } // _STORE_ATTR_SLOT { value = stack_pointer[-2]; uint16_t index = read_u16(&this_instr[4].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + DEOPT_IF(!LOCK_OBJECT(owner_o), STORE_ATTR); char *addr = (char *)owner_o + index; STAT_INC(STORE_ATTR, hit); PyObject *old_value = *(PyObject **)addr; - *(PyObject **)addr = PyStackRef_AsPyObjectSteal(value); + FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, PyStackRef_AsPyObjectSteal(value)); + UNLOCK_OBJECT(owner_o); Py_XDECREF(old_value); PyStackRef_CLOSE(owner); } @@ -7441,7 +7450,7 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, STORE_ATTR); } // _STORE_ATTR_WITH_HINT { @@ -7451,18 +7460,35 @@ assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = _PyObject_GetManagedDict(owner_o); DEOPT_IF(dict == NULL, STORE_ATTR); + DEOPT_IF(!LOCK_OBJECT(dict), STORE_ATTR); + #ifdef Py_GIL_DISABLED + if (dict != _PyObject_GetManagedDict(owner_o)) { + UNLOCK_OBJECT(dict); + DEOPT_IF(true, STORE_ATTR); + } + #endif assert(PyDict_CheckExact((PyObject *)dict)); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR); - DEOPT_IF(!DK_IS_UNICODE(dict->ma_keys), STORE_ATTR); + if (hint >= (size_t)dict->ma_keys->dk_nentries || + !DK_IS_UNICODE(dict->ma_keys)) { + UNLOCK_OBJECT(dict); + DEOPT_IF(true, STORE_ATTR); + } PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; - DEOPT_IF(ep->me_key != name, STORE_ATTR); + if (ep->me_key != name) { + UNLOCK_OBJECT(dict); + DEOPT_IF(true, STORE_ATTR); + } PyObject *old_value = ep->me_value; - DEOPT_IF(old_value == NULL, STORE_ATTR); + if (old_value == NULL) { + UNLOCK_OBJECT(dict); + DEOPT_IF(true, STORE_ATTR); + } _PyFrame_SetStackPointer(frame, stack_pointer); _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); - ep->me_value = PyStackRef_AsPyObjectSteal(value); + FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value)); + UNLOCK_OBJECT(dict); // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault, // when dict only holds the strong reference to value in ep->me_value. Py_XDECREF(old_value); @@ -7815,7 +7841,7 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, TO_BOOL); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, TO_BOOL); } // _REPLACE_WITH_TRUE { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 2c3133d7107df2..b46079ec8a1992 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1121,6 +1121,10 @@ break; } + case _GUARD_TYPE_VERSION_AND_LOCK: { + break; + } + case _CHECK_MANAGED_OBJECT_HAS_VALUES: { break; } diff --git a/Python/specialize.c b/Python/specialize.c index 6c45320f95db8e..349ed472298945 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -741,8 +741,8 @@ static int function_kind(PyCodeObject *code); #ifndef Py_GIL_DISABLED static bool function_check_args(PyObject *o, int expected_argcount, int opcode); static uint32_t function_get_version(PyObject *o, int opcode); -#endif static uint32_t type_get_version(PyTypeObject *t, int opcode); +#endif static int specialize_module_load_attr_lock_held(PyDictObject *dict, _Py_CODEUNIT *instr, PyObject *name) @@ -881,71 +881,142 @@ classify_descriptor(PyObject *descriptor, bool has_getattr) return NON_DESCRIPTOR; } -static DescriptorClassification -analyze_descriptor(PyTypeObject *type, PyObject *name, PyObject **descr, int store) +static bool +descriptor_is_class(PyObject *descriptor, PyObject *name) { + return ((PyUnicode_CompareWithASCIIString(name, "__class__") == 0) && + (descriptor == _PyType_Lookup(&PyBaseObject_Type, name))); +} + +#ifndef Py_GIL_DISABLED +static DescriptorClassification +analyze_descriptor_load(PyTypeObject *type, PyObject *name, PyObject **descr) { bool has_getattr = false; - if (store) { - if (type->tp_setattro != PyObject_GenericSetAttr) { + getattrofunc getattro_slot = type->tp_getattro; + if (getattro_slot == PyObject_GenericGetAttr) { + /* Normal attribute lookup; */ + has_getattr = false; + } + else if (getattro_slot == _Py_slot_tp_getattr_hook || + getattro_slot == _Py_slot_tp_getattro) { + /* One or both of __getattribute__ or __getattr__ may have been + overridden See typeobject.c for why these functions are special. */ + PyObject *getattribute = _PyType_LookupRef(type, &_Py_ID(__getattribute__)); + PyInterpreterState *interp = _PyInterpreterState_GET(); + bool has_custom_getattribute = getattribute != NULL && + getattribute != interp->callable_cache.object__getattribute__; + PyObject *getattr = _PyType_LookupRef(type, &_Py_ID(__getattr__)); + has_getattr = getattr != NULL; + Py_XDECREF(getattr); + if (has_custom_getattribute) { + if (getattro_slot == _Py_slot_tp_getattro && + !has_getattr && + Py_IS_TYPE(getattribute, &PyFunction_Type)) { + *descr = getattribute; + return GETATTRIBUTE_IS_PYTHON_FUNCTION; + } + /* Potentially both __getattr__ and __getattribute__ are set. + Too complicated */ + Py_DECREF(getattribute); *descr = NULL; return GETSET_OVERRIDDEN; } + /* Potentially has __getattr__ but no custom __getattribute__. + Fall through to usual descriptor analysis. + Usual attribute lookup should only be allowed at runtime + if we can guarantee that there is no way an exception can be + raised. This means some specializations, e.g. specializing + for property() isn't safe. + */ + Py_XDECREF(getattribute); } else { - getattrofunc getattro_slot = type->tp_getattro; - if (getattro_slot == PyObject_GenericGetAttr) { - /* Normal attribute lookup; */ - has_getattr = false; - } - else if (getattro_slot == _Py_slot_tp_getattr_hook || - getattro_slot == _Py_slot_tp_getattro) { - /* One or both of __getattribute__ or __getattr__ may have been - overridden See typeobject.c for why these functions are special. */ - PyObject *getattribute = _PyType_Lookup(type, - &_Py_ID(__getattribute__)); - PyInterpreterState *interp = _PyInterpreterState_GET(); - bool has_custom_getattribute = getattribute != NULL && - getattribute != interp->callable_cache.object__getattribute__; - has_getattr = _PyType_Lookup(type, &_Py_ID(__getattr__)) != NULL; - if (has_custom_getattribute) { - if (getattro_slot == _Py_slot_tp_getattro && - !has_getattr && - Py_IS_TYPE(getattribute, &PyFunction_Type)) { - *descr = getattribute; - return GETATTRIBUTE_IS_PYTHON_FUNCTION; - } - /* Potentially both __getattr__ and __getattribute__ are set. - Too complicated */ - *descr = NULL; - return GETSET_OVERRIDDEN; - } - /* Potentially has __getattr__ but no custom __getattribute__. - Fall through to usual descriptor analysis. - Usual attribute lookup should only be allowed at runtime - if we can guarantee that there is no way an exception can be - raised. This means some specializations, e.g. specializing - for property() isn't safe. - */ - } - else { - *descr = NULL; - return GETSET_OVERRIDDEN; - } + *descr = NULL; + return GETSET_OVERRIDDEN; } - PyObject *descriptor = _PyType_Lookup(type, name); + PyObject *descriptor = _PyType_LookupRef(type, name); *descr = descriptor; - if (PyUnicode_CompareWithASCIIString(name, "__class__") == 0) { - if (descriptor == _PyType_Lookup(&PyBaseObject_Type, name)) { - return DUNDER_CLASS; - } + if (descriptor_is_class(descriptor, name)) { + return DUNDER_CLASS; } return classify_descriptor(descriptor, has_getattr); } +#endif //!Py_GIL_DISABLED + +static DescriptorClassification +analyze_descriptor_store(PyTypeObject *type, PyObject *name, PyObject **descr, unsigned int *tp_version) +{ + if (type->tp_setattro != PyObject_GenericSetAttr) { + *descr = NULL; + return GETSET_OVERRIDDEN; + } + PyObject *descriptor = _PyType_LookupRefAndVersion(type, name, tp_version); + *descr = descriptor; + if (descriptor_is_class(descriptor, name)) { + return DUNDER_CLASS; + } + return classify_descriptor(descriptor, false); +} + +static int +specialize_dict_access_inline( + PyObject *owner, _Py_CODEUNIT *instr, PyTypeObject *type, + DescriptorClassification kind, PyObject *name, unsigned int tp_version, + int base_op, int values_op) +{ + _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); + PyDictKeysObject *keys = ((PyHeapTypeObject *)type)->ht_cached_keys; + assert(PyUnicode_CheckExact(name)); + Py_ssize_t index = _PyDictKeys_StringLookupSplit(keys, name); + assert (index != DKIX_ERROR); + if (index == DKIX_EMPTY) { + SPECIALIZATION_FAIL(base_op, SPEC_FAIL_ATTR_NOT_IN_KEYS); + return 0; + } + assert(index >= 0); + char *value_addr = (char *)&_PyObject_InlineValues(owner)->values[index]; + Py_ssize_t offset = value_addr - (char *)owner; + if (offset != (uint16_t)offset) { + SPECIALIZATION_FAIL(base_op, SPEC_FAIL_OUT_OF_RANGE); + return 0; + } + cache->index = (uint16_t)offset; + write_u32(cache->version, tp_version); + specialize(instr, values_op); + return 1; +} + +static int +specialize_dict_access_hint( + PyDictObject *dict, _Py_CODEUNIT *instr, PyTypeObject *type, + DescriptorClassification kind, PyObject *name, unsigned int tp_version, + int base_op, int hint_op) +{ + _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); + // We found an instance with a __dict__. + if (_PyDict_HasSplitTable(dict)) { + SPECIALIZATION_FAIL(base_op, SPEC_FAIL_ATTR_SPLIT_DICT); + return 0; + } + Py_ssize_t index = _PyDict_LookupIndex(dict, name); + if (index != (uint16_t)index) { + SPECIALIZATION_FAIL(base_op, + index == DKIX_EMPTY ? + SPEC_FAIL_ATTR_NOT_IN_DICT : + SPEC_FAIL_OUT_OF_RANGE); + return 0; + } + cache->index = (uint16_t)index; + write_u32(cache->version, tp_version); + specialize(instr, hint_op); + return 1; +} + static int specialize_dict_access( PyObject *owner, _Py_CODEUNIT *instr, PyTypeObject *type, - DescriptorClassification kind, PyObject *name, + DescriptorClassification kind, PyObject *name, unsigned int tp_version, int base_op, int values_op, int hint_op) { assert(kind == NON_OVERRIDING || kind == NON_DESCRIPTOR || kind == ABSENT || @@ -956,29 +1027,25 @@ specialize_dict_access( SPECIALIZATION_FAIL(base_op, SPEC_FAIL_ATTR_NOT_MANAGED_DICT); return 0; } - _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); if (type->tp_flags & Py_TPFLAGS_INLINE_VALUES && - _PyObject_InlineValues(owner)->valid && + FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner)->valid) && !(base_op == STORE_ATTR && _PyObject_GetManagedDict(owner) != NULL)) { - PyDictKeysObject *keys = ((PyHeapTypeObject *)type)->ht_cached_keys; - assert(PyUnicode_CheckExact(name)); - Py_ssize_t index = _PyDictKeys_StringLookup(keys, name); - assert (index != DKIX_ERROR); - if (index == DKIX_EMPTY) { - SPECIALIZATION_FAIL(base_op, SPEC_FAIL_ATTR_NOT_IN_KEYS); - return 0; + int res; + Py_BEGIN_CRITICAL_SECTION(owner); + PyDictObject *dict = _PyObject_GetManagedDict(owner); + if (dict == NULL) { + // managed dict, not materialized, inline values valid + res = specialize_dict_access_inline(owner, instr, type, kind, name, + tp_version, base_op, values_op); } - assert(index >= 0); - char *value_addr = (char *)&_PyObject_InlineValues(owner)->values[index]; - Py_ssize_t offset = value_addr - (char *)owner; - if (offset != (uint16_t)offset) { - SPECIALIZATION_FAIL(base_op, SPEC_FAIL_OUT_OF_RANGE); - return 0; + else { + // lost race and dict was created, fail specialization + SPECIALIZATION_FAIL(STORE_ATTR, SPEC_FAIL_OTHER); + res = 0; } - write_u32(cache->version, type->tp_version_tag); - cache->index = (uint16_t)offset; - specialize(instr, values_op); + Py_END_CRITICAL_SECTION(); + return res; } else { PyDictObject *dict = _PyObject_GetManagedDict(owner); @@ -986,25 +1053,14 @@ specialize_dict_access( SPECIALIZATION_FAIL(base_op, SPEC_FAIL_NO_DICT); return 0; } - // We found an instance with a __dict__. - if (dict->ma_values) { - SPECIALIZATION_FAIL(base_op, SPEC_FAIL_ATTR_SPLIT_DICT); - return 0; - } - Py_ssize_t index = - _PyDict_LookupIndex(dict, name); - if (index != (uint16_t)index) { - SPECIALIZATION_FAIL(base_op, - index == DKIX_EMPTY ? - SPEC_FAIL_ATTR_NOT_IN_DICT : - SPEC_FAIL_OUT_OF_RANGE); - return 0; - } - cache->index = (uint16_t)index; - write_u32(cache->version, type->tp_version_tag); - specialize(instr, hint_op); + int res; + Py_BEGIN_CRITICAL_SECTION(dict); + // materialized managed dict + res = specialize_dict_access_hint(dict, instr, type, kind, name, + tp_version, base_op, hint_op); + Py_END_CRITICAL_SECTION(); + return res; } - return 1; } #ifndef Py_GIL_DISABLED @@ -1050,7 +1106,8 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na PyTypeObject *type = Py_TYPE(owner); bool shadow = instance_has_key(owner, name); PyObject *descr = NULL; - DescriptorClassification kind = analyze_descriptor(type, name, &descr, 0); + DescriptorClassification kind = analyze_descriptor_load(type, name, &descr); + Py_XDECREF(descr); // turn strong ref into a borrowed ref assert(descr != NULL || kind == ABSENT || kind == GETSET_OVERRIDDEN); if (type_get_version(type, LOAD_ATTR) == 0) { return -1; @@ -1204,8 +1261,8 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na } Py_UNREACHABLE(); try_instance: - if (specialize_dict_access(owner, instr, type, kind, name, LOAD_ATTR, - LOAD_ATTR_INSTANCE_VALUE, LOAD_ATTR_WITH_HINT)) + if (specialize_dict_access(owner, instr, type, kind, name, type->tp_version_tag, + LOAD_ATTR, LOAD_ATTR_INSTANCE_VALUE, LOAD_ATTR_WITH_HINT)) { return 0; } @@ -1259,8 +1316,9 @@ _Py_Specialize_StoreAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *na { PyObject *owner = PyStackRef_AsPyObjectBorrow(owner_st); - assert(ENABLE_SPECIALIZATION); + assert(ENABLE_SPECIALIZATION_FT); assert(_PyOpcode_Caches[STORE_ATTR] == INLINE_CACHE_ENTRIES_STORE_ATTR); + PyObject *descr = NULL; _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); PyTypeObject *type = Py_TYPE(owner); if (!_PyType_IsReady(type)) { @@ -1274,11 +1332,12 @@ _Py_Specialize_StoreAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *na SPECIALIZATION_FAIL(STORE_ATTR, SPEC_FAIL_OVERRIDDEN); goto fail; } - PyObject *descr; - DescriptorClassification kind = analyze_descriptor(type, name, &descr, 1); - if (type_get_version(type, STORE_ATTR) == 0) { + unsigned int tp_version = 0; + DescriptorClassification kind = analyze_descriptor_store(type, name, &descr, &tp_version); + if (tp_version == 0) { goto fail; } + assert(descr != NULL || kind == ABSENT || kind == GETSET_OVERRIDDEN); switch(kind) { case OVERRIDING: SPECIALIZATION_FAIL(STORE_ATTR, SPEC_FAIL_ATTR_OVERRIDING_DESCRIPTOR); @@ -1309,8 +1368,8 @@ _Py_Specialize_StoreAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *na assert(dmem->type == Py_T_OBJECT_EX || dmem->type == _Py_T_OBJECT); assert(offset > 0); cache->index = (uint16_t)offset; - write_u32(cache->version, type->tp_version_tag); - instr->op.code = STORE_ATTR_SLOT; + write_u32(cache->version, tp_version); + specialize(instr, STORE_ATTR_SLOT); goto success; } case DUNDER_CLASS: @@ -1337,22 +1396,19 @@ _Py_Specialize_StoreAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *na SPECIALIZATION_FAIL(STORE_ATTR, SPEC_FAIL_ATTR_CLASS_ATTR_SIMPLE); goto fail; case ABSENT: - if (specialize_dict_access(owner, instr, type, kind, name, STORE_ATTR, - STORE_ATTR_INSTANCE_VALUE, STORE_ATTR_WITH_HINT)) - { + if (specialize_dict_access(owner, instr, type, kind, name, tp_version, + STORE_ATTR, STORE_ATTR_INSTANCE_VALUE, + STORE_ATTR_WITH_HINT)) { goto success; } } fail: - STAT_INC(STORE_ATTR, failure); - assert(!PyErr_Occurred()); - instr->op.code = STORE_ATTR; - cache->counter = adaptive_counter_backoff(cache->counter); + Py_XDECREF(descr); + unspecialize(instr); return; success: - STAT_INC(STORE_ATTR, success); - assert(!PyErr_Occurred()); - cache->counter = adaptive_counter_cooldown(); + Py_XDECREF(descr); + return; } #ifndef Py_GIL_DISABLED @@ -1421,7 +1477,8 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, } PyObject *descr = NULL; DescriptorClassification kind = 0; - kind = analyze_descriptor(cls, name, &descr, 0); + kind = analyze_descriptor_load(cls, name, &descr); + Py_XDECREF(descr); // turn strong ref into a borrowed ref if (type_get_version(cls, LOAD_ATTR) == 0) { return -1; } @@ -1714,7 +1771,6 @@ function_get_version(PyObject *o, int opcode) } return version; } -#endif // Py_GIL_DISABLED /* Returning 0 indicates a failure. */ static uint32_t @@ -1727,6 +1783,7 @@ type_get_version(PyTypeObject *t, int opcode) } return version; } +#endif // Py_GIL_DISABLED void _Py_Specialize_BinarySubscr( From e163e8d4e1a9844b8615ef38b9917b887a377948 Mon Sep 17 00:00:00 2001 From: Zhikang Yan <2951256653@qq.com> Date: Fri, 20 Dec 2024 04:24:47 +0800 Subject: [PATCH 069/376] gh-128062: Fix the font size and shortcut display of the turtledemo menu (#128063) Leave the font of the menu bar the default to keep it consistent with the rest of the world. Display the shortcut keys in the right way, using the 'accelerator' option. --------- Co-authored-by: Peter Bierma Co-authored-by: Terry Jan Reedy --- Lib/turtledemo/__main__.py | 17 +++++++---------- ...24-12-18-10-18-55.gh-issue-128062.E9oU7-.rst | 2 ++ 2 files changed, 9 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-18-10-18-55.gh-issue-128062.E9oU7-.rst diff --git a/Lib/turtledemo/__main__.py b/Lib/turtledemo/__main__.py index 9c15916fb6672e..b49c0beab3ccf7 100644 --- a/Lib/turtledemo/__main__.py +++ b/Lib/turtledemo/__main__.py @@ -105,7 +105,6 @@ DONE = 4 EVENTDRIVEN = 5 -menufont = ("Arial", 12, NORMAL) btnfont = ("Arial", 12, 'bold') txtfont = ['Lucida Console', 10, 'normal'] @@ -297,23 +296,21 @@ def makeLoadDemoMenu(self, master): for entry in getExampleEntries(): def load(entry=entry): self.loadfile(entry) - menu.add_command(label=entry, underline=0, - font=menufont, command=load) + menu.add_command(label=entry, underline=0, command=load) return menu def makeFontMenu(self, master): menu = Menu(master, tearoff=0) - menu.add_command(label="Decrease (C-'-')", command=self.decrease_size, - font=menufont) - menu.add_command(label="Increase (C-'+')", command=self.increase_size, - font=menufont) + menu.add_command(label="Decrease", command=self.decrease_size, + accelerator=f"{'Command' if darwin else 'Ctrl'}+-") + menu.add_command(label="Increase", command=self.increase_size, + accelerator=f"{'Command' if darwin else 'Ctrl'}+=") menu.add_separator() for size in font_sizes: def resize(size=size): self.set_txtsize(size) - menu.add_command(label=str(size), underline=0, - font=menufont, command=resize) + menu.add_command(label=str(size), underline=0, command=resize) return menu def makeHelpMenu(self, master): @@ -322,7 +319,7 @@ def makeHelpMenu(self, master): for help_label, help_file in help_entries: def show(help_label=help_label, help_file=help_file): view_text(self.root, help_label, help_file) - menu.add_command(label=help_label, font=menufont, command=show) + menu.add_command(label=help_label, command=show) return menu def refreshCanvas(self): diff --git a/Misc/NEWS.d/next/Library/2024-12-18-10-18-55.gh-issue-128062.E9oU7-.rst b/Misc/NEWS.d/next/Library/2024-12-18-10-18-55.gh-issue-128062.E9oU7-.rst new file mode 100644 index 00000000000000..d8e262e0848077 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-18-10-18-55.gh-issue-128062.E9oU7-.rst @@ -0,0 +1,2 @@ +Revert the font of :mod:`turtledemo`'s menu bar to its default value and +display the shortcut keys in the correct position. From 255762c09fe518757bb3e8ce1bb6e5d8eec9f466 Mon Sep 17 00:00:00 2001 From: mpage Date: Thu, 19 Dec 2024 13:03:14 -0800 Subject: [PATCH 070/376] gh-127274: Defer nested methods (#128012) Methods (functions defined in class scope) are likely to be cleaned up by the GC anyway. Add a new code flag, `CO_METHOD`, that is set for functions defined in a class scope. Use that when deciding to defer functions. --- Doc/library/inspect.rst | 7 +++++++ Include/cpython/code.h | 3 +++ Include/internal/pycore_symtable.h | 1 + Lib/dis.py | 1 + Lib/inspect.py | 1 + Lib/test/test_monitoring.py | 9 +++------ Lib/test/test_opcache.py | 11 ++++------- .../2024-12-17-13-45-33.gh-issue-127274.deNxNC.rst | 3 +++ Objects/funcobject.c | 6 +++++- Python/compile.c | 2 ++ Python/symtable.c | 7 +++++++ 11 files changed, 37 insertions(+), 14 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-13-45-33.gh-issue-127274.deNxNC.rst diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index ca5dac87aff2b4..0085207d3055f2 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -1708,6 +1708,13 @@ which is a bitmap of the following flags: .. versionadded:: 3.14 +.. data:: CO_METHOD + + The flag is set when the code object is a function defined in class + scope. + + .. versionadded:: 3.14 + .. note:: The flags are specific to CPython, and may not be defined in other Python implementations. Furthermore, the flags are an implementation diff --git a/Include/cpython/code.h b/Include/cpython/code.h index c3c0165d556ead..cb6261ddde941b 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -199,6 +199,9 @@ struct PyCodeObject _PyCode_DEF(1); */ #define CO_HAS_DOCSTRING 0x4000000 +/* A function defined in class scope */ +#define CO_METHOD 0x8000000 + /* This should be defined if a future statement modifies the syntax. For example, when a keyword is added. */ diff --git a/Include/internal/pycore_symtable.h b/Include/internal/pycore_symtable.h index 91dac767d5885b..b7e274296112aa 100644 --- a/Include/internal/pycore_symtable.h +++ b/Include/internal/pycore_symtable.h @@ -124,6 +124,7 @@ typedef struct _symtable_entry { unsigned ste_can_see_class_scope : 1; /* true if this block can see names bound in an enclosing class scope */ unsigned ste_has_docstring : 1; /* true if docstring present */ + unsigned ste_method : 1; /* true if block is a function block defined in class scope */ int ste_comp_iter_expr; /* non-zero if visiting a comprehension range expression */ _Py_SourceLocation ste_loc; /* source location of block */ struct _symtable_entry *ste_annotation_block; /* symbol table entry for this entry's annotations */ diff --git a/Lib/dis.py b/Lib/dis.py index aa22404c6687e1..109c986bbe3d7d 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -162,6 +162,7 @@ def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets 256: "ITERABLE_COROUTINE", 512: "ASYNC_GENERATOR", 0x4000000: "HAS_DOCSTRING", + 0x8000000: "METHOD", } def pretty_flags(flags): diff --git a/Lib/inspect.py b/Lib/inspect.py index b7d8271f8a471f..5b7c4df8927c87 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -57,6 +57,7 @@ "CO_VARARGS", "CO_VARKEYWORDS", "CO_HAS_DOCSTRING", + "CO_METHOD", "ClassFoundException", "ClosureVars", "EndOfBlock", diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py index 087ac8d456b843..32b3a6ac049e28 100644 --- a/Lib/test/test_monitoring.py +++ b/Lib/test/test_monitoring.py @@ -850,12 +850,6 @@ def __init__(self, events): def __call__(self, code, offset, val): self.events.append(("return", code.co_name, val)) -# gh-127274: CALL_ALLOC_AND_ENTER_INIT will only cache __init__ methods that -# are deferred. We only defer functions defined at the top-level. -class ValueErrorRaiser: - def __init__(self): - raise ValueError() - class ExceptionMonitoringTest(CheckEvents): @@ -1054,6 +1048,9 @@ def func(): @requires_specialization_ft def test_no_unwind_for_shim_frame(self): + class ValueErrorRaiser: + def __init__(self): + raise ValueError() def f(): try: diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index ad0b0c487a4118..ba111b5117b41d 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -493,13 +493,6 @@ def f(): self.assertFalse(f()) -# gh-127274: CALL_ALLOC_AND_ENTER_INIT will only cache __init__ methods that -# are deferred. We only defer functions defined at the top-level. -class MyClass: - def __init__(self): - pass - - class InitTakesArg: def __init__(self, arg): self.arg = arg @@ -536,6 +529,10 @@ def f(x, y): @disabling_optimizer @requires_specialization_ft def test_assign_init_code(self): + class MyClass: + def __init__(self): + pass + def instantiate(): return MyClass() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-13-45-33.gh-issue-127274.deNxNC.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-13-45-33.gh-issue-127274.deNxNC.rst new file mode 100644 index 00000000000000..a4608fbbbf19ec --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-13-45-33.gh-issue-127274.deNxNC.rst @@ -0,0 +1,3 @@ +Add a new flag, ``CO_METHOD``, to :attr:`~codeobject.co_flags` that +indicates whether the code object belongs to a function defined in class +scope. diff --git a/Objects/funcobject.c b/Objects/funcobject.c index cca7f01498013e..7b17a9ba31fac4 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -210,10 +210,14 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname op->func_typeparams = NULL; op->vectorcall = _PyFunction_Vectorcall; op->func_version = FUNC_VERSION_UNSET; - if ((code_obj->co_flags & CO_NESTED) == 0) { + if (((code_obj->co_flags & CO_NESTED) == 0) || + (code_obj->co_flags & CO_METHOD)) { // Use deferred reference counting for top-level functions, but not // nested functions because they are more likely to capture variables, // which makes prompt deallocation more important. + // + // Nested methods (functions defined in class scope) are also deferred, + // since they will likely be cleaned up by GC anyway. _PyObject_SetDeferredRefcount((PyObject *)op); } _PyObject_GC_TRACK(op); diff --git a/Python/compile.c b/Python/compile.c index cbfba7f493e07d..ef470830336dde 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1289,6 +1289,8 @@ compute_code_flags(compiler *c) flags |= CO_VARKEYWORDS; if (ste->ste_has_docstring) flags |= CO_HAS_DOCSTRING; + if (ste->ste_method) + flags |= CO_METHOD; } if (ste->ste_coroutine && !ste->ste_generator) { diff --git a/Python/symtable.c b/Python/symtable.c index ebddb0b93fca0a..49bd01ba68ac9e 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -138,6 +138,13 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block, ste->ste_has_docstring = 0; + ste->ste_method = 0; + if (st->st_cur != NULL && + st->st_cur->ste_type == ClassBlock && + block == FunctionBlock) { + ste->ste_method = 1; + } + ste->ste_symbols = PyDict_New(); ste->ste_varnames = PyList_New(0); ste->ste_children = PyList_New(0); From c14db202750ff9eaf3919298f1172270b7dfd64e Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Thu, 19 Dec 2024 14:07:17 -0800 Subject: [PATCH 071/376] gh-128080: remove unnecessary `__init__` method from Enum (GH-128081) remove unnecessary __init__ method from Enum Co-authored-by: Peter Bierma --- Lib/enum.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Lib/enum.py b/Lib/enum.py index 27be3fb83b2afb..ccc1da42206474 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -1211,9 +1211,6 @@ def __new__(cls, value): exc = None ve_exc = None - def __init__(self, *args, **kwds): - pass - def _add_alias_(self, name): self.__class__._add_member_(name, self) From 39e69a7cd54d44c9061db89bb15c460d30fba7a6 Mon Sep 17 00:00:00 2001 From: Md Rokibul Islam Date: Fri, 20 Dec 2024 00:38:42 +0100 Subject: [PATCH 072/376] gh-112328: Document EnumDict in docs and release notes (GH-121720) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) Co-authored-by: Ethan Furman --- Doc/library/enum.rst | 17 ++++++++++++++++- Doc/whatsnew/3.13.rst | 5 +++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 16a9b0326e9f3d..51292a11f507c4 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -149,9 +149,14 @@ Module Contents Return a list of all power-of-two integers contained in a flag. + :class:`EnumDict` + + A subclass of :class:`dict` for use when subclassing :class:`EnumType`. + .. versionadded:: 3.6 ``Flag``, ``IntFlag``, ``auto`` .. versionadded:: 3.11 ``StrEnum``, ``EnumCheck``, ``ReprEnum``, ``FlagBoundary``, ``property``, ``member``, ``nonmember``, ``global_enum``, ``show_flag_values`` +.. versionadded:: 3.14 ``EnumDict`` --------------- @@ -821,7 +826,17 @@ Data Types >>> KeepFlag(2**2 + 2**4) -.. versionadded:: 3.11 + .. versionadded:: 3.11 + +.. class:: EnumDict + + *EnumDict* is a subclass of :class:`dict` for use when subclassing :class:`EnumType`. + + .. attribute:: EnumDict.member_names + + Return list of member names. + + .. versionadded:: 3.14 --------------- diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 45cc1b5bad9b18..a291122aefc2ce 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -879,6 +879,11 @@ email (Contributed by Thomas Dwyer and Victor Stinner for :gh:`102988` to improve the :cve:`2023-27043` fix.) +enum +---- + +* :class:`~enum.EnumDict` has been made public in :mod:`enum` to better support + subclassing :class:`~enum.EnumType`. fractions --------- From daa260ebb1c1b20321e7f26df7c9dbd35d4edcbf Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 20 Dec 2024 08:50:18 +0100 Subject: [PATCH 073/376] gh-128058: Fix test_builtin ImmortalTests (#128068) On 32-bit Free Threading systems, immortal reference count is 5 << 28, instead of 7 << 28. Co-authored-by: Peter Bierma --- Lib/test/test_builtin.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index a92edad86839e6..f98138391bc1a8 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -2691,7 +2691,10 @@ def __del__(self): class ImmortalTests(unittest.TestCase): if sys.maxsize < (1 << 32): - IMMORTAL_REFCOUNT = 7 << 28 + if support.Py_GIL_DISABLED: + IMMORTAL_REFCOUNT = 5 << 28 + else: + IMMORTAL_REFCOUNT = 7 << 28 else: IMMORTAL_REFCOUNT = 3 << 30 From 45e6dd63b88a782f2ec96ab1da54eb5a074d8f4c Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 20 Dec 2024 00:22:26 -0800 Subject: [PATCH 074/376] gh-128030: Avoid error from PyModule_GetFilenameObject for non-module (#128047) I missed the extra `PyModule_Check` in #127660 because I was looking at 3.12 as the base implementation for import from. This meant that I missed the `PyModuleCheck` introduced in #112661. --- Lib/test/test_import/__init__.py | 23 +++++++++++++++++++ ...-12-17-22-28-15.gh-issue-128030.H1ptOD.rst | 1 + Python/ceval.c | 2 +- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-22-28-15.gh-issue-128030.H1ptOD.rst diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 83efbc1e25e77a..c2cec6444cb43a 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -851,6 +851,29 @@ def test_frozen_module_from_import_error(self): stdout, stderr = popen.communicate() self.assertIn(expected_error, stdout) + def test_non_module_from_import_error(self): + prefix = """ +import sys +class NotAModule: ... +nm = NotAModule() +nm.symbol = 123 +sys.modules["not_a_module"] = nm +from not_a_module import symbol +""" + scripts = [ + prefix + "from not_a_module import missing_symbol", + prefix + "nm.__spec__ = []\nfrom not_a_module import missing_symbol", + ] + for script in scripts: + with self.subTest(script=script): + expected_error = ( + b"ImportError: cannot import name 'missing_symbol' from " + b"'' (unknown location)" + ) + popen = script_helper.spawn_python("-c", script) + stdout, stderr = popen.communicate() + self.assertIn(expected_error, stdout) + def test_script_shadowing_stdlib(self): script_errors = [ ( diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-22-28-15.gh-issue-128030.H1ptOD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-22-28-15.gh-issue-128030.H1ptOD.rst new file mode 100644 index 00000000000000..93d78632355b76 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-22-28-15.gh-issue-128030.H1ptOD.rst @@ -0,0 +1 @@ +Avoid error from calling ``PyModule_GetFilenameObject`` on a non-module object when importing a non-existent symbol from a non-module object. diff --git a/Python/ceval.c b/Python/ceval.c index fd891d7839151e..bfdf5687c287db 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2860,7 +2860,7 @@ _PyEval_ImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name) } } - if (origin == NULL) { + if (origin == NULL && PyModule_Check(v)) { // Fall back to __file__ for diagnostics if we don't have // an origin that is a location origin = PyModule_GetFilenameObject(v); From df46c780febab667ee01264ae32c4e866cecd911 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 20 Dec 2024 11:57:44 +0000 Subject: [PATCH 075/376] GH-122548: Correct magic number comment (GH-128115) Correct magic number comment --- Include/internal/pycore_magic_number.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Include/internal/pycore_magic_number.h b/Include/internal/pycore_magic_number.h index 079a9befcd4c5e..4c3b9c4c71da1b 100644 --- a/Include/internal/pycore_magic_number.h +++ b/Include/internal/pycore_magic_number.h @@ -262,7 +262,8 @@ Known values: Python 3.14a1 3607 (Add pseudo instructions JUMP_IF_TRUE/FALSE) Python 3.14a1 3608 (Add support for slices) Python 3.14a2 3609 (Add LOAD_SMALL_INT and LOAD_CONST_IMMORTAL instructions, remove RETURN_CONST) - Python 3.14a3 3610 (Add NOT_TAKEN instruction) + (3610 accidentally omitted) + Python 3.14a4 3611 (Add NOT_TAKEN instruction) Python 3.15 will start with 3650 From 0974d7bb866062ed4aaa40f705d6cc4c294d99f1 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 20 Dec 2024 13:37:20 +0100 Subject: [PATCH 076/376] gh-109959: Log the current directory in test_glob.test_selflink() (#128122) --- Lib/test/test_glob.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py index b72640bd871ba6..00187a3fb3537d 100644 --- a/Lib/test/test_glob.py +++ b/Lib/test/test_glob.py @@ -6,6 +6,7 @@ import unittest import warnings +from test import support from test.support import is_wasi, Py_DEBUG from test.support.os_helper import (TESTFN, skip_unless_symlink, can_symlink, create_empty_file, change_cwd) @@ -515,6 +516,12 @@ def test_selflink(self): os.makedirs(tempdir) self.addCleanup(shutil.rmtree, tempdir) with change_cwd(tempdir): + if support.verbose: + cwd = os.getcwd() + print(f"cwd: {cwd} ({len(cwd)} chars)") + cwdb = os.getcwdb() + print(f"cwdb: {cwdb!r} ({len(cwdb)} bytes)") + os.makedirs('dir') create_empty_file(os.path.join('dir', 'file')) os.symlink(os.curdir, os.path.join('dir', 'link')) From cbfe3023e46b544b80ea1a38a8c900c6fb881554 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 20 Dec 2024 13:38:00 +0100 Subject: [PATCH 077/376] gh-128116: Skip test_socket VSOCK testStream() on PermissionError (#128120) --- Lib/test/test_socket.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 307d6e886c617f..aac213e36aecf0 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -547,7 +547,10 @@ def clientSetUp(self): self.cli.connect((cid, VSOCKPORT)) def testStream(self): - msg = self.conn.recv(1024) + try: + msg = self.conn.recv(1024) + except PermissionError as exc: + self.skipTest(repr(exc)) self.assertEqual(msg, MSG) def _testStream(self): From ba45e5cdd41a39ce0b3de08bdcfa9d8e28e0e4f3 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Fri, 20 Dec 2024 08:02:46 -0500 Subject: [PATCH 078/376] gh-127946: Use a critical section for `CFuncPtr` attributes (GH-128109) --- Lib/test/test_ctypes/test_cfuncs.py | 20 +- ...-12-19-20-46-01.gh-issue-127946.4lM3Op.rst | 2 + Modules/_ctypes/_ctypes.c | 100 +++++++--- Modules/_ctypes/clinic/_ctypes.c.h | 174 +++++++++++++++++- 4 files changed, 266 insertions(+), 30 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-19-20-46-01.gh-issue-127946.4lM3Op.rst diff --git a/Lib/test/test_ctypes/test_cfuncs.py b/Lib/test/test_ctypes/test_cfuncs.py index 48330c4b0a763b..e0c124607cb2e9 100644 --- a/Lib/test/test_ctypes/test_cfuncs.py +++ b/Lib/test/test_ctypes/test_cfuncs.py @@ -5,7 +5,8 @@ c_short, c_ushort, c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong, c_float, c_double, c_longdouble) -from test.support import import_helper +from test import support +from test.support import import_helper, threading_helper _ctypes_test = import_helper.import_module("_ctypes_test") @@ -191,6 +192,23 @@ def test_void(self): self.assertEqual(self._dll.tv_i(-42), None) self.assertEqual(self.S(), -42) + @threading_helper.requires_working_threading() + @support.requires_resource("cpu") + @unittest.skipUnless(support.Py_GIL_DISABLED, "only meaningful on free-threading") + def test_thread_safety(self): + from threading import Thread + + def concurrent(): + for _ in range(100): + self._dll.tf_b.restype = c_byte + self._dll.tf_b.argtypes = (c_byte,) + + with threading_helper.catch_threading_exception() as exc: + with threading_helper.start_threads((Thread(target=concurrent) for _ in range(10))): + pass + + self.assertIsNone(exc.exc_value) + # The following repeats the above tests with stdcall functions (where # they are available) diff --git a/Misc/NEWS.d/next/Library/2024-12-19-20-46-01.gh-issue-127946.4lM3Op.rst b/Misc/NEWS.d/next/Library/2024-12-19-20-46-01.gh-issue-127946.4lM3Op.rst new file mode 100644 index 00000000000000..faf1ec042bc2b9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-19-20-46-01.gh-issue-127946.4lM3Op.rst @@ -0,0 +1,2 @@ +Fix crash when modifying :class:`ctypes._CFuncPtr` objects concurrently on +the :term:`free threaded ` build. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 3a3b1da5084a67..dcdb7b2052a11e 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -128,8 +128,9 @@ bytes(cdata) /*[clinic input] module _ctypes +class _ctypes.CFuncPtr "PyCFuncPtrObject *" "&PyCFuncPtr_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=476a19c49b31a75c]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=58e8c99474bc631e]*/ #define clinic_state() (get_module_state_by_class(cls)) #define clinic_state_sub() (get_module_state_by_class(cls->tp_base)) @@ -3422,21 +3423,37 @@ generic_pycdata_new(ctypes_state *st, PyCFuncPtr_Type */ +/*[clinic input] +@critical_section +@setter +_ctypes.CFuncPtr.errcheck +[clinic start generated code]*/ + static int -PyCFuncPtr_set_errcheck(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored)) +_ctypes_CFuncPtr_errcheck_set_impl(PyCFuncPtrObject *self, PyObject *value) +/*[clinic end generated code: output=6580cf1ffdf3b9fb input=84930bb16c490b33]*/ { - if (ob && !PyCallable_Check(ob)) { + if (value && !PyCallable_Check(value)) { PyErr_SetString(PyExc_TypeError, "the errcheck attribute must be callable"); return -1; } - Py_XINCREF(ob); - Py_XSETREF(self->errcheck, ob); + Py_XINCREF(value); + Py_XSETREF(self->errcheck, value); return 0; } +/*[clinic input] +@critical_section +@getter +_ctypes.CFuncPtr.errcheck + +a function to check for errors +[clinic start generated code]*/ + static PyObject * -PyCFuncPtr_get_errcheck(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) +_ctypes_CFuncPtr_errcheck_get_impl(PyCFuncPtrObject *self) +/*[clinic end generated code: output=dfa6fb5c6f90fd14 input=4672135fef37819f]*/ { if (self->errcheck) { return Py_NewRef(self->errcheck); @@ -3444,11 +3461,18 @@ PyCFuncPtr_get_errcheck(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) Py_RETURN_NONE; } +/*[clinic input] +@setter +@critical_section +_ctypes.CFuncPtr.restype +[clinic start generated code]*/ + static int -PyCFuncPtr_set_restype(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored)) +_ctypes_CFuncPtr_restype_set_impl(PyCFuncPtrObject *self, PyObject *value) +/*[clinic end generated code: output=0be0a086abbabf18 input=683c3bef4562ccc6]*/ { PyObject *checker, *oldchecker; - if (ob == NULL) { + if (value == NULL) { oldchecker = self->checker; self->checker = NULL; Py_CLEAR(self->restype); @@ -3457,27 +3481,36 @@ PyCFuncPtr_set_restype(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ign } ctypes_state *st = get_module_state_by_def(Py_TYPE(Py_TYPE(self))); StgInfo *info; - if (PyStgInfo_FromType(st, ob, &info) < 0) { + if (PyStgInfo_FromType(st, value, &info) < 0) { return -1; } - if (ob != Py_None && !info && !PyCallable_Check(ob)) { + if (value != Py_None && !info && !PyCallable_Check(value)) { PyErr_SetString(PyExc_TypeError, "restype must be a type, a callable, or None"); return -1; } - if (PyObject_GetOptionalAttr(ob, &_Py_ID(_check_retval_), &checker) < 0) { + if (PyObject_GetOptionalAttr(value, &_Py_ID(_check_retval_), &checker) < 0) { return -1; } oldchecker = self->checker; self->checker = checker; - Py_INCREF(ob); - Py_XSETREF(self->restype, ob); + Py_INCREF(value); + Py_XSETREF(self->restype, value); Py_XDECREF(oldchecker); return 0; } +/*[clinic input] +@getter +@critical_section +_ctypes.CFuncPtr.restype + +specify the result type +[clinic start generated code]*/ + static PyObject * -PyCFuncPtr_get_restype(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) +_ctypes_CFuncPtr_restype_get_impl(PyCFuncPtrObject *self) +/*[clinic end generated code: output=c8f44cd16f1dee5e input=5e3ed95116204fd2]*/ { if (self->restype) { return Py_NewRef(self->restype); @@ -3495,28 +3528,44 @@ PyCFuncPtr_get_restype(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) } } +/*[clinic input] +@setter +@critical_section +_ctypes.CFuncPtr.argtypes +[clinic start generated code]*/ + static int -PyCFuncPtr_set_argtypes(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored)) +_ctypes_CFuncPtr_argtypes_set_impl(PyCFuncPtrObject *self, PyObject *value) +/*[clinic end generated code: output=596a36e2ae89d7d1 input=c4627573e980aa8b]*/ { PyObject *converters; - if (ob == NULL || ob == Py_None) { + if (value == NULL || value == Py_None) { Py_CLEAR(self->converters); Py_CLEAR(self->argtypes); } else { ctypes_state *st = get_module_state_by_def(Py_TYPE(Py_TYPE(self))); - converters = converters_from_argtypes(st, ob); + converters = converters_from_argtypes(st, value); if (!converters) return -1; Py_XSETREF(self->converters, converters); - Py_INCREF(ob); - Py_XSETREF(self->argtypes, ob); + Py_INCREF(value); + Py_XSETREF(self->argtypes, value); } return 0; } +/*[clinic input] +@getter +@critical_section +_ctypes.CFuncPtr.argtypes + +specify the argument types +[clinic start generated code]*/ + static PyObject * -PyCFuncPtr_get_argtypes(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) +_ctypes_CFuncPtr_argtypes_get_impl(PyCFuncPtrObject *self) +/*[clinic end generated code: output=c46b05a1b0f99172 input=37a8a545a56f8ae2]*/ { if (self->argtypes) { return Py_NewRef(self->argtypes); @@ -3535,13 +3584,9 @@ PyCFuncPtr_get_argtypes(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) } static PyGetSetDef PyCFuncPtr_getsets[] = { - { "errcheck", (getter)PyCFuncPtr_get_errcheck, (setter)PyCFuncPtr_set_errcheck, - "a function to check for errors", NULL }, - { "restype", (getter)PyCFuncPtr_get_restype, (setter)PyCFuncPtr_set_restype, - "specify the result type", NULL }, - { "argtypes", (getter)PyCFuncPtr_get_argtypes, - (setter)PyCFuncPtr_set_argtypes, - "specify the argument types", NULL }, + _CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF + _CTYPES_CFUNCPTR_RESTYPE_GETSETDEF + _CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF { NULL, NULL } }; @@ -5054,7 +5099,6 @@ class _ctypes.Simple "PyObject *" "clinic_state()->Simple_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=016c476c7aa8b8a8]*/ - static int Simple_set_value(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored)) { diff --git a/Modules/_ctypes/clinic/_ctypes.c.h b/Modules/_ctypes/clinic/_ctypes.c.h index 1332ba04cdfecd..405a3c9238d77d 100644 --- a/Modules/_ctypes/clinic/_ctypes.c.h +++ b/Modules/_ctypes/clinic/_ctypes.c.h @@ -6,6 +6,7 @@ preserve # include "pycore_runtime.h" // _Py_SINGLETON() #endif #include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(_ctypes_CType_Type___sizeof____doc__, @@ -601,6 +602,177 @@ PyCData_reduce(PyObject *myself, PyTypeObject *cls, PyObject *const *args, Py_ss return PyCData_reduce_impl(myself, cls); } +#if !defined(_ctypes_CFuncPtr_errcheck_DOCSTR) +# define _ctypes_CFuncPtr_errcheck_DOCSTR NULL +#endif +#if defined(_CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF) +# undef _CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF +# define _CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF {"errcheck", (getter)_ctypes_CFuncPtr_errcheck_get, (setter)_ctypes_CFuncPtr_errcheck_set, _ctypes_CFuncPtr_errcheck_DOCSTR}, +#else +# define _CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF {"errcheck", NULL, (setter)_ctypes_CFuncPtr_errcheck_set, NULL}, +#endif + +static int +_ctypes_CFuncPtr_errcheck_set_impl(PyCFuncPtrObject *self, PyObject *value); + +static int +_ctypes_CFuncPtr_errcheck_set(PyCFuncPtrObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ctypes_CFuncPtr_errcheck_set_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_ctypes_CFuncPtr_errcheck__doc__, +"a function to check for errors"); +#if defined(_ctypes_CFuncPtr_errcheck_DOCSTR) +# undef _ctypes_CFuncPtr_errcheck_DOCSTR +#endif +#define _ctypes_CFuncPtr_errcheck_DOCSTR _ctypes_CFuncPtr_errcheck__doc__ + +#if !defined(_ctypes_CFuncPtr_errcheck_DOCSTR) +# define _ctypes_CFuncPtr_errcheck_DOCSTR NULL +#endif +#if defined(_CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF) +# undef _CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF +# define _CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF {"errcheck", (getter)_ctypes_CFuncPtr_errcheck_get, (setter)_ctypes_CFuncPtr_errcheck_set, _ctypes_CFuncPtr_errcheck_DOCSTR}, +#else +# define _CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF {"errcheck", (getter)_ctypes_CFuncPtr_errcheck_get, NULL, _ctypes_CFuncPtr_errcheck_DOCSTR}, +#endif + +static PyObject * +_ctypes_CFuncPtr_errcheck_get_impl(PyCFuncPtrObject *self); + +static PyObject * +_ctypes_CFuncPtr_errcheck_get(PyCFuncPtrObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ctypes_CFuncPtr_errcheck_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_ctypes_CFuncPtr_restype_DOCSTR) +# define _ctypes_CFuncPtr_restype_DOCSTR NULL +#endif +#if defined(_CTYPES_CFUNCPTR_RESTYPE_GETSETDEF) +# undef _CTYPES_CFUNCPTR_RESTYPE_GETSETDEF +# define _CTYPES_CFUNCPTR_RESTYPE_GETSETDEF {"restype", (getter)_ctypes_CFuncPtr_restype_get, (setter)_ctypes_CFuncPtr_restype_set, _ctypes_CFuncPtr_restype_DOCSTR}, +#else +# define _CTYPES_CFUNCPTR_RESTYPE_GETSETDEF {"restype", NULL, (setter)_ctypes_CFuncPtr_restype_set, NULL}, +#endif + +static int +_ctypes_CFuncPtr_restype_set_impl(PyCFuncPtrObject *self, PyObject *value); + +static int +_ctypes_CFuncPtr_restype_set(PyCFuncPtrObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ctypes_CFuncPtr_restype_set_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_ctypes_CFuncPtr_restype__doc__, +"specify the result type"); +#if defined(_ctypes_CFuncPtr_restype_DOCSTR) +# undef _ctypes_CFuncPtr_restype_DOCSTR +#endif +#define _ctypes_CFuncPtr_restype_DOCSTR _ctypes_CFuncPtr_restype__doc__ + +#if !defined(_ctypes_CFuncPtr_restype_DOCSTR) +# define _ctypes_CFuncPtr_restype_DOCSTR NULL +#endif +#if defined(_CTYPES_CFUNCPTR_RESTYPE_GETSETDEF) +# undef _CTYPES_CFUNCPTR_RESTYPE_GETSETDEF +# define _CTYPES_CFUNCPTR_RESTYPE_GETSETDEF {"restype", (getter)_ctypes_CFuncPtr_restype_get, (setter)_ctypes_CFuncPtr_restype_set, _ctypes_CFuncPtr_restype_DOCSTR}, +#else +# define _CTYPES_CFUNCPTR_RESTYPE_GETSETDEF {"restype", (getter)_ctypes_CFuncPtr_restype_get, NULL, _ctypes_CFuncPtr_restype_DOCSTR}, +#endif + +static PyObject * +_ctypes_CFuncPtr_restype_get_impl(PyCFuncPtrObject *self); + +static PyObject * +_ctypes_CFuncPtr_restype_get(PyCFuncPtrObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ctypes_CFuncPtr_restype_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_ctypes_CFuncPtr_argtypes_DOCSTR) +# define _ctypes_CFuncPtr_argtypes_DOCSTR NULL +#endif +#if defined(_CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF) +# undef _CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF +# define _CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF {"argtypes", (getter)_ctypes_CFuncPtr_argtypes_get, (setter)_ctypes_CFuncPtr_argtypes_set, _ctypes_CFuncPtr_argtypes_DOCSTR}, +#else +# define _CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF {"argtypes", NULL, (setter)_ctypes_CFuncPtr_argtypes_set, NULL}, +#endif + +static int +_ctypes_CFuncPtr_argtypes_set_impl(PyCFuncPtrObject *self, PyObject *value); + +static int +_ctypes_CFuncPtr_argtypes_set(PyCFuncPtrObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ctypes_CFuncPtr_argtypes_set_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_ctypes_CFuncPtr_argtypes__doc__, +"specify the argument types"); +#if defined(_ctypes_CFuncPtr_argtypes_DOCSTR) +# undef _ctypes_CFuncPtr_argtypes_DOCSTR +#endif +#define _ctypes_CFuncPtr_argtypes_DOCSTR _ctypes_CFuncPtr_argtypes__doc__ + +#if !defined(_ctypes_CFuncPtr_argtypes_DOCSTR) +# define _ctypes_CFuncPtr_argtypes_DOCSTR NULL +#endif +#if defined(_CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF) +# undef _CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF +# define _CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF {"argtypes", (getter)_ctypes_CFuncPtr_argtypes_get, (setter)_ctypes_CFuncPtr_argtypes_set, _ctypes_CFuncPtr_argtypes_DOCSTR}, +#else +# define _CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF {"argtypes", (getter)_ctypes_CFuncPtr_argtypes_get, NULL, _ctypes_CFuncPtr_argtypes_DOCSTR}, +#endif + +static PyObject * +_ctypes_CFuncPtr_argtypes_get_impl(PyCFuncPtrObject *self); + +static PyObject * +_ctypes_CFuncPtr_argtypes_get(PyCFuncPtrObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ctypes_CFuncPtr_argtypes_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(Simple_from_outparm__doc__, "__ctypes_from_outparam__($self, /)\n" "--\n" @@ -621,4 +793,4 @@ Simple_from_outparm(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py } return Simple_from_outparm_impl(self, cls); } -/*[clinic end generated code: output=52724c091e3a8b8d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=cb3583522a2c5ce5 input=a9049054013a1b77]*/ From 78ffba4221dcb2e39fd5db80c297d1777588bb59 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 20 Dec 2024 14:28:18 +0100 Subject: [PATCH 079/376] gh-127295: ctypes: Switch field accessors to fixed-width integers (GH-127297) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should be a pure refactoring, without user-visible behaviour changes. Before this change, ctypes uses traditional native C types, usually identified by [`struct` format characters][struct-chars] when a short (and identifier-friendly) name is needed: - `signed char` (`b`) / `unsigned char` (`B`) - `short` (`h`) / `unsigned short` (`h`) - `int` (`i`) / `unsigned int` (`i`) - `long` (`l`) / `unsigned long` (`l`) - `long long` (`q`) / `unsigned long long` (`q`) These map to C99 fixed-width types, which this PR switches to: - - `int8_t`/`uint8_t` - `int16_t`/`uint16_t` - `int32_t`/`uint32_t` - `int64_t`/`uint64_t` The C standard doesn't guarantee that the “traditional” types must map to the fixints. But, [`ctypes` currently requires it][swapdefs], so the assumption won't break anything. By “map” I mean that the *size* of the types matches. The *alignment* requirements might not. This needs to be kept in mind but is not an issue in `ctypes` accessors, which [explicitly handle unaligned memory][memcpy] for the integer types. Note that there are 5 “traditional” C type sizes, but 4 fixed-width ones. Two of the former are functionally identical to one another; which ones they are is platform-specific (e.g. `int`==`long`==`int32_t`.) This means that one of the [current][current-impls-1] [implementations][current-impls-2] is redundant on any given platform. The fixint types are parametrized by the number of bytes/bits, and one bit for signedness. This makes it easier to autogenerate code for them or to write generic macros (though generic API like [`PyLong_AsNativeBytes`][PyLong_AsNativeBytes] is problematic for performance reasons -- especially compared to a `memcpy` with compile-time-constant size). When one has a *different* integer type, determining the corresponding fixint means a `sizeof` and signedness check. This is easier and more robust than the current implementations (see [`wchar_t`][sizeof-wchar_t] or [`_Bool`][sizeof-bool]). [swapdefs]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L420-L444 [struct-chars]: https://docs.python.org/3/library/struct.html#format-characters [current-impls-1]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L470-L653 [current-impls-2]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L703-L944 [memcpy]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L613 [PyLong_AsNativeBytes]: https://docs.python.org/3/c-api/long.html#c.PyLong_AsNativeBytes [sizeof-wchar_t]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L1547-L1555 [sizeof-bool]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L1562-L1572 Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Modules/_ctypes/_ctypes.c | 4 +- Modules/_ctypes/callbacks.c | 2 +- Modules/_ctypes/cfield.c | 1132 +++++++++++++++-------------------- Modules/_ctypes/ctypes.h | 15 +- 4 files changed, 501 insertions(+), 652 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index dcdb7b2052a11e..ac520ffaad6c90 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1979,7 +1979,7 @@ c_void_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) return NULL; parg->pffi_type = &ffi_type_pointer; parg->tag = 'P'; - parg->obj = fd->setfunc(&parg->value, value, 0); + parg->obj = fd->setfunc(&parg->value, value, sizeof(void*)); if (parg->obj == NULL) { Py_DECREF(parg); return NULL; @@ -2444,7 +2444,7 @@ PyCSimpleType_from_param_impl(PyObject *type, PyTypeObject *cls, parg->tag = fmt[0]; parg->pffi_type = fd->pffi_type; - parg->obj = fd->setfunc(&parg->value, value, 0); + parg->obj = fd->setfunc(&parg->value, value, info->size); if (parg->obj) return (PyObject *)parg; PyObject *exc = PyErr_GetRaisedException(); diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 7b9f6437c7d55f..89c0749a093765 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -264,7 +264,7 @@ static void _CallPythonObject(ctypes_state *st, be the result. EXCEPT when restype is py_object - Python itself knows how to manage the refcount of these objects. */ - PyObject *keep = setfunc(mem, result, 0); + PyObject *keep = setfunc(mem, result, restype->size); if (keep == NULL) { /* Could not convert callback result. */ diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 2b9e8a1a10d6f5..dcac9da75360a4 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -10,6 +10,7 @@ #include "pycore_bitutils.h" // _Py_bswap32() #include "pycore_call.h" // _PyObject_CallNoArgs() +#include // bool #include #include "ctypes.h" @@ -320,61 +321,6 @@ PyType_Spec cfield_spec = { }; -/******************************************************************/ -/* - Accessor functions -*/ - -/* Derived from Modules/structmodule.c: - Helper routine to get a Python integer and raise the appropriate error - if it isn't one */ - -static int -get_long(PyObject *v, long *p) -{ - long x = PyLong_AsUnsignedLongMask(v); - if (x == -1 && PyErr_Occurred()) - return -1; - *p = x; - return 0; -} - -/* Same, but handling unsigned long */ - -static int -get_ulong(PyObject *v, unsigned long *p) -{ - unsigned long x = PyLong_AsUnsignedLongMask(v); - if (x == (unsigned long)-1 && PyErr_Occurred()) - return -1; - *p = x; - return 0; -} - -/* Same, but handling native long long. */ - -static int -get_longlong(PyObject *v, long long *p) -{ - long long x = PyLong_AsUnsignedLongLongMask(v); - if (x == -1 && PyErr_Occurred()) - return -1; - *p = x; - return 0; -} - -/* Same, but handling native unsigned long long. */ - -static int -get_ulonglong(PyObject *v, unsigned long long *p) -{ - unsigned long long x = PyLong_AsUnsignedLongLongMask(v); - if (x == (unsigned long long)-1 && PyErr_Occurred()) - return -1; - *p = x; - return 0; -} - /***************************************************************** * Integer fields, with bitfield support */ @@ -404,34 +350,8 @@ Py_ssize_t NUM_BITS(Py_ssize_t bitsize) { /* This macro RETURNS the first parameter with the bit field CHANGED. */ #define SET(type, x, v, size) \ (NUM_BITS(size) ? \ - ( ( (type)x & ~(BIT_MASK(type, size) << LOW_BIT(size)) ) | ( ((type)v & BIT_MASK(type, size)) << LOW_BIT(size) ) ) \ - : (type)v) - -#if SIZEOF_SHORT == 2 -# define SWAP_SHORT _Py_bswap16 -#else -# error "unsupported short size" -#endif - -#if SIZEOF_INT == 4 -# define SWAP_INT _Py_bswap32 -#else -# error "unsupported int size" -#endif - -#if SIZEOF_LONG == 4 -# define SWAP_LONG _Py_bswap32 -#elif SIZEOF_LONG == 8 -# define SWAP_LONG _Py_bswap64 -#else -# error "unsupported long size" -#endif - -#if SIZEOF_LONG_LONG == 8 -# define SWAP_LONG_LONG _Py_bswap64 -#else -# error "unsupported long long size" -#endif + ( ( (type)(x) & ~(BIT_MASK(type, size) << LOW_BIT(size)) ) | ( ((type)(v) & BIT_MASK(type, size)) << LOW_BIT(size) ) ) \ + : (type)(v)) /***************************************************************** * The setter methods return an object which must be kept alive, to keep the @@ -454,203 +374,145 @@ Py_ssize_t NUM_BITS(Py_ssize_t bitsize) { #endif /***************************************************************** - * integer accessor methods, supporting bit fields + * accessor methods for fixed-width integers (e.g. int8_t, uint64_t), + * supporting bit fields. + * These are named e.g. `i8_set`/`i8_get` or `u64_set`/`u64_get`, + * and are all alike, so they're defined using a macro. */ -static PyObject * -b_set(void *ptr, PyObject *value, Py_ssize_t size) -{ - long val; - if (get_long(value, &val) < 0) - return NULL; - *(signed char *)ptr = SET(signed char, *(signed char *)ptr, val, size); - _RET(value); -} - - -static PyObject * -b_get(void *ptr, Py_ssize_t size) -{ - signed char val = *(signed char *)ptr; - GET_BITFIELD(val, size); - return PyLong_FromLong(val); -} - -static PyObject * -B_set(void *ptr, PyObject *value, Py_ssize_t size) -{ - unsigned long val; - if (get_ulong(value, &val) < 0) - return NULL; - *(unsigned char *)ptr = SET(unsigned char, *(unsigned char*)ptr, val, size); - _RET(value); -} - - -static PyObject * -B_get(void *ptr, Py_ssize_t size) -{ - unsigned char val = *(unsigned char *)ptr; - GET_BITFIELD(val, size); - return PyLong_FromLong(val); -} - -static PyObject * -h_set(void *ptr, PyObject *value, Py_ssize_t size) -{ - long val; - short x; - if (get_long(value, &val) < 0) - return NULL; - memcpy(&x, ptr, sizeof(x)); - x = SET(short, x, val, size); - memcpy(ptr, &x, sizeof(x)); - _RET(value); -} - - -static PyObject * -h_set_sw(void *ptr, PyObject *value, Py_ssize_t size) -{ - long val; - short field; - if (get_long(value, &val) < 0) { - return NULL; - } - memcpy(&field, ptr, sizeof(field)); - field = SWAP_SHORT(field); - field = SET(short, field, val, size); - field = SWAP_SHORT(field); - memcpy(ptr, &field, sizeof(field)); - _RET(value); -} - -static PyObject * -h_get(void *ptr, Py_ssize_t size) -{ - short val; - memcpy(&val, ptr, sizeof(val)); - GET_BITFIELD(val, size); - return PyLong_FromLong((long)val); -} - -static PyObject * -h_get_sw(void *ptr, Py_ssize_t size) -{ - short val; - memcpy(&val, ptr, sizeof(val)); - val = SWAP_SHORT(val); - GET_BITFIELD(val, size); - return PyLong_FromLong(val); -} - -static PyObject * -H_set(void *ptr, PyObject *value, Py_ssize_t size) -{ - unsigned long val; - unsigned short x; - if (get_ulong(value, &val) < 0) - return NULL; - memcpy(&x, ptr, sizeof(x)); - x = SET(unsigned short, x, val, size); - memcpy(ptr, &x, sizeof(x)); - _RET(value); -} - -static PyObject * -H_set_sw(void *ptr, PyObject *value, Py_ssize_t size) -{ - unsigned long val; - unsigned short field; - if (get_ulong(value, &val) < 0) { - return NULL; - } - memcpy(&field, ptr, sizeof(field)); - field = SWAP_SHORT(field); - field = SET(unsigned short, field, val, size); - field = SWAP_SHORT(field); - memcpy(ptr, &field, sizeof(field)); - _RET(value); -} - - -static PyObject * -H_get(void *ptr, Py_ssize_t size) -{ - unsigned short val; - memcpy(&val, ptr, sizeof(val)); - GET_BITFIELD(val, size); - return PyLong_FromLong(val); -} - -static PyObject * -H_get_sw(void *ptr, Py_ssize_t size) -{ - unsigned short val; - memcpy(&val, ptr, sizeof(val)); - val = SWAP_SHORT(val); - GET_BITFIELD(val, size); - return PyLong_FromLong(val); -} - -static PyObject * -i_set(void *ptr, PyObject *value, Py_ssize_t size) -{ - long val; - int x; - if (get_long(value, &val) < 0) - return NULL; - memcpy(&x, ptr, sizeof(x)); - x = SET(int, x, val, size); - memcpy(ptr, &x, sizeof(x)); - _RET(value); -} - -static PyObject * -i_set_sw(void *ptr, PyObject *value, Py_ssize_t size) -{ - long val; - int field; - if (get_long(value, &val) < 0) { - return NULL; - } - memcpy(&field, ptr, sizeof(field)); - field = SWAP_INT(field); - field = SET(int, field, val, size); - field = SWAP_INT(field); - memcpy(ptr, &field, sizeof(field)); - _RET(value); -} +#define FIXINT_GETSET(TAG, CTYPE, NBITS, PYAPI_FROMFUNC) \ + static PyObject * \ + TAG ## _set(void *ptr, PyObject *value, Py_ssize_t size_arg) \ + { \ + assert(NUM_BITS(size_arg) || (size_arg == (NBITS) / 8)); \ + CTYPE val; \ + if (PyLong_Check(value) \ + && PyUnstable_Long_IsCompact((PyLongObject *)value)) \ + { \ + val = (CTYPE)PyUnstable_Long_CompactValue( \ + (PyLongObject *)value); \ + } \ + else { \ + Py_ssize_t res = PyLong_AsNativeBytes( \ + value, &val, (NBITS) / 8, \ + Py_ASNATIVEBYTES_NATIVE_ENDIAN \ + | Py_ASNATIVEBYTES_ALLOW_INDEX); \ + if (res < 0) { \ + return NULL; \ + } \ + } \ + CTYPE prev; \ + memcpy(&prev, ptr, (NBITS) / 8); \ + val = SET(CTYPE, prev, val, size_arg); \ + memcpy(ptr, &val, (NBITS) / 8); \ + _RET(value); \ + } \ + \ + static PyObject * \ + TAG ## _get(void *ptr, Py_ssize_t size_arg) \ + { \ + assert(NUM_BITS(size_arg) || (size_arg == (NBITS) / 8)); \ + CTYPE val; \ + memcpy(&val, ptr, sizeof(val)); \ + GET_BITFIELD(val, size_arg); \ + return PYAPI_FROMFUNC(val); \ + } \ + /////////////////////////////////////////////////////////////////////////// + +/* Another macro for byte-swapped variants (e.g. `i8_set_sw`/`i8_get_sw`) */ + +#define FIXINT_GETSET_SW(TAG, CTYPE, NBITS, PYAPI_FROMFUNC, PY_SWAPFUNC) \ + static PyObject * \ + TAG ## _set_sw(void *ptr, PyObject *value, Py_ssize_t size_arg) \ + { \ + CTYPE val; \ + PyObject *res = TAG ## _set(&val, value, (NBITS) / 8); \ + if (res == NULL) { \ + return NULL; \ + } \ + Py_DECREF(res); \ + CTYPE field; \ + memcpy(&field, ptr, sizeof(field)); \ + field = PY_SWAPFUNC(field); \ + field = SET(CTYPE, field, val, size_arg); \ + field = PY_SWAPFUNC(field); \ + memcpy(ptr, &field, sizeof(field)); \ + _RET(value); \ + } \ + \ + static PyObject * \ + TAG ## _get_sw(void *ptr, Py_ssize_t size_arg) \ + { \ + assert(NUM_BITS(size_arg) || (size_arg == (NBITS) / 8)); \ + CTYPE val; \ + memcpy(&val, ptr, sizeof(val)); \ + val = PY_SWAPFUNC(val); \ + GET_BITFIELD(val, size_arg); \ + return PYAPI_FROMFUNC(val); \ + } \ + /////////////////////////////////////////////////////////////////////////// + +/* These macros are expanded for all supported combinations of byte sizes + * (1, 2, 4, 8), signed and unsigned, native and swapped byteorder. + * That's a lot, so generate the list with Argument Clinic (`make clinic`). + */ +/*[python input] +for nbits in 8, 16, 32, 64: + for sgn in 'i', 'u': + u = 'u' if sgn == 'u' else '' + U = u.upper() + apibits = max(nbits, 32) + parts = [ + f'{sgn}{nbits}', + f'{u}int{nbits}_t', + f'{nbits}', + f'PyLong_From{U}Int{apibits}', + ] + print(f'FIXINT_GETSET({", ".join(parts)})') + if nbits > 8: + parts.append(f'_Py_bswap{nbits}') + print(f'FIXINT_GETSET_SW({", ".join(parts)})') +[python start generated code]*/ +FIXINT_GETSET(i8, int8_t, 8, PyLong_FromInt32) +FIXINT_GETSET(u8, uint8_t, 8, PyLong_FromUInt32) +FIXINT_GETSET(i16, int16_t, 16, PyLong_FromInt32) +FIXINT_GETSET_SW(i16, int16_t, 16, PyLong_FromInt32, _Py_bswap16) +FIXINT_GETSET(u16, uint16_t, 16, PyLong_FromUInt32) +FIXINT_GETSET_SW(u16, uint16_t, 16, PyLong_FromUInt32, _Py_bswap16) +FIXINT_GETSET(i32, int32_t, 32, PyLong_FromInt32) +FIXINT_GETSET_SW(i32, int32_t, 32, PyLong_FromInt32, _Py_bswap32) +FIXINT_GETSET(u32, uint32_t, 32, PyLong_FromUInt32) +FIXINT_GETSET_SW(u32, uint32_t, 32, PyLong_FromUInt32, _Py_bswap32) +FIXINT_GETSET(i64, int64_t, 64, PyLong_FromInt64) +FIXINT_GETSET_SW(i64, int64_t, 64, PyLong_FromInt64, _Py_bswap64) +FIXINT_GETSET(u64, uint64_t, 64, PyLong_FromUInt64) +FIXINT_GETSET_SW(u64, uint64_t, 64, PyLong_FromUInt64, _Py_bswap64) +/*[python end generated code: output=3d60c96fa58e07d5 input=0b7e166f2ea18e70]*/ + +// For one-byte types, swapped variants are the same as native +#define i8_set_sw i8_set +#define i8_get_sw i8_get +#define u8_set_sw u8_set +#define u8_get_sw u8_get + +#undef FIXINT_GETSET +#undef FIXINT_GETSET_SW -static PyObject * -i_get(void *ptr, Py_ssize_t size) -{ - int val; - memcpy(&val, ptr, sizeof(val)); - GET_BITFIELD(val, size); - return PyLong_FromLong(val); -} - -static PyObject * -i_get_sw(void *ptr, Py_ssize_t size) -{ - int val; - memcpy(&val, ptr, sizeof(val)); - val = SWAP_INT(val); - GET_BITFIELD(val, size); - return PyLong_FromLong(val); -} +/***************************************************************** + * non-integer accessor methods, not supporting bit fields + */ #ifndef MS_WIN32 /* http://msdn.microsoft.com/en-us/library/cc237864.aspx */ #define VARIANT_FALSE 0x0000 #define VARIANT_TRUE 0xFFFF #endif -/* short BOOL - VARIANT_BOOL */ +/* v: short BOOL - VARIANT_BOOL */ static PyObject * -vBOOL_set(void *ptr, PyObject *value, Py_ssize_t size) +v_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(short int))); switch (PyObject_IsTrue(value)) { case -1: return NULL; @@ -664,22 +526,25 @@ vBOOL_set(void *ptr, PyObject *value, Py_ssize_t size) } static PyObject * -vBOOL_get(void *ptr, Py_ssize_t size) +v_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(short int))); return PyBool_FromLong((long)*(short int *)ptr); } +/* bool ('?'): bool (i.e. _Bool) */ static PyObject * bool_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(bool))); switch (PyObject_IsTrue(value)) { case -1: return NULL; case 0: - *(_Bool *)ptr = 0; + *(bool *)ptr = 0; _RET(value); default: - *(_Bool *)ptr = 1; + *(bool *)ptr = 1; _RET(value); } } @@ -687,260 +552,15 @@ bool_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * bool_get(void *ptr, Py_ssize_t size) { - return PyBool_FromLong((long)*(_Bool *)ptr); -} - -static PyObject * -I_set(void *ptr, PyObject *value, Py_ssize_t size) -{ - unsigned long val; - unsigned int x; - if (get_ulong(value, &val) < 0) - return NULL; - memcpy(&x, ptr, sizeof(x)); - x = SET(unsigned int, x, val, size); - memcpy(ptr, &x, sizeof(x)); - _RET(value); -} - -static PyObject * -I_set_sw(void *ptr, PyObject *value, Py_ssize_t size) -{ - unsigned long val; - unsigned int field; - if (get_ulong(value, &val) < 0) { - return NULL; - } - memcpy(&field, ptr, sizeof(field)); - field = SWAP_INT(field); - field = SET(unsigned int, field, (unsigned int)val, size); - field = SWAP_INT(field); - memcpy(ptr, &field, sizeof(field)); - _RET(value); -} - - -static PyObject * -I_get(void *ptr, Py_ssize_t size) -{ - unsigned int val; - memcpy(&val, ptr, sizeof(val)); - GET_BITFIELD(val, size); - return PyLong_FromUnsignedLong(val); -} - -static PyObject * -I_get_sw(void *ptr, Py_ssize_t size) -{ - unsigned int val; - memcpy(&val, ptr, sizeof(val)); - val = SWAP_INT(val); - GET_BITFIELD(val, size); - return PyLong_FromUnsignedLong(val); -} - -static PyObject * -l_set(void *ptr, PyObject *value, Py_ssize_t size) -{ - long val; - long x; - if (get_long(value, &val) < 0) - return NULL; - memcpy(&x, ptr, sizeof(x)); - x = SET(long, x, val, size); - memcpy(ptr, &x, sizeof(x)); - _RET(value); -} - -static PyObject * -l_set_sw(void *ptr, PyObject *value, Py_ssize_t size) -{ - long val; - long field; - if (get_long(value, &val) < 0) { - return NULL; - } - memcpy(&field, ptr, sizeof(field)); - field = SWAP_LONG(field); - field = SET(long, field, val, size); - field = SWAP_LONG(field); - memcpy(ptr, &field, sizeof(field)); - _RET(value); -} - - -static PyObject * -l_get(void *ptr, Py_ssize_t size) -{ - long val; - memcpy(&val, ptr, sizeof(val)); - GET_BITFIELD(val, size); - return PyLong_FromLong(val); -} - -static PyObject * -l_get_sw(void *ptr, Py_ssize_t size) -{ - long val; - memcpy(&val, ptr, sizeof(val)); - val = SWAP_LONG(val); - GET_BITFIELD(val, size); - return PyLong_FromLong(val); -} - -static PyObject * -L_set(void *ptr, PyObject *value, Py_ssize_t size) -{ - unsigned long val; - unsigned long x; - if (get_ulong(value, &val) < 0) - return NULL; - memcpy(&x, ptr, sizeof(x)); - x = SET(unsigned long, x, val, size); - memcpy(ptr, &x, sizeof(x)); - _RET(value); -} - -static PyObject * -L_set_sw(void *ptr, PyObject *value, Py_ssize_t size) -{ - unsigned long val; - unsigned long field; - if (get_ulong(value, &val) < 0) { - return NULL; - } - memcpy(&field, ptr, sizeof(field)); - field = SWAP_LONG(field); - field = SET(unsigned long, field, val, size); - field = SWAP_LONG(field); - memcpy(ptr, &field, sizeof(field)); - _RET(value); -} - - -static PyObject * -L_get(void *ptr, Py_ssize_t size) -{ - unsigned long val; - memcpy(&val, ptr, sizeof(val)); - GET_BITFIELD(val, size); - return PyLong_FromUnsignedLong(val); -} - -static PyObject * -L_get_sw(void *ptr, Py_ssize_t size) -{ - unsigned long val; - memcpy(&val, ptr, sizeof(val)); - val = SWAP_LONG(val); - GET_BITFIELD(val, size); - return PyLong_FromUnsignedLong(val); -} - -static PyObject * -q_set(void *ptr, PyObject *value, Py_ssize_t size) -{ - long long val; - long long x; - if (get_longlong(value, &val) < 0) - return NULL; - memcpy(&x, ptr, sizeof(x)); - x = SET(long long, x, val, size); - memcpy(ptr, &x, sizeof(x)); - _RET(value); -} - -static PyObject * -q_set_sw(void *ptr, PyObject *value, Py_ssize_t size) -{ - long long val; - long long field; - if (get_longlong(value, &val) < 0) { - return NULL; - } - memcpy(&field, ptr, sizeof(field)); - field = SWAP_LONG_LONG(field); - field = SET(long long, field, val, size); - field = SWAP_LONG_LONG(field); - memcpy(ptr, &field, sizeof(field)); - _RET(value); -} - -static PyObject * -q_get(void *ptr, Py_ssize_t size) -{ - long long val; - memcpy(&val, ptr, sizeof(val)); - GET_BITFIELD(val, size); - return PyLong_FromLongLong(val); -} - -static PyObject * -q_get_sw(void *ptr, Py_ssize_t size) -{ - long long val; - memcpy(&val, ptr, sizeof(val)); - val = SWAP_LONG_LONG(val); - GET_BITFIELD(val, size); - return PyLong_FromLongLong(val); -} - -static PyObject * -Q_set(void *ptr, PyObject *value, Py_ssize_t size) -{ - unsigned long long val; - unsigned long long x; - if (get_ulonglong(value, &val) < 0) - return NULL; - memcpy(&x, ptr, sizeof(x)); - x = SET(long long, x, val, size); - memcpy(ptr, &x, sizeof(x)); - _RET(value); -} - -static PyObject * -Q_set_sw(void *ptr, PyObject *value, Py_ssize_t size) -{ - unsigned long long val; - unsigned long long field; - if (get_ulonglong(value, &val) < 0) { - return NULL; - } - memcpy(&field, ptr, sizeof(field)); - field = SWAP_LONG_LONG(field); - field = SET(unsigned long long, field, val, size); - field = SWAP_LONG_LONG(field); - memcpy(ptr, &field, sizeof(field)); - _RET(value); -} - -static PyObject * -Q_get(void *ptr, Py_ssize_t size) -{ - unsigned long long val; - memcpy(&val, ptr, sizeof(val)); - GET_BITFIELD(val, size); - return PyLong_FromUnsignedLongLong(val); -} - -static PyObject * -Q_get_sw(void *ptr, Py_ssize_t size) -{ - unsigned long long val; - memcpy(&val, ptr, sizeof(val)); - val = SWAP_LONG_LONG(val); - GET_BITFIELD(val, size); - return PyLong_FromUnsignedLongLong(val); + assert(NUM_BITS(size) || (size == sizeof(bool))); + return PyBool_FromLong((long)*(bool *)ptr); } -/***************************************************************** - * non-integer accessor methods, not supporting bit fields - */ - - +/* g: long double */ static PyObject * g_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(long double))); long double x; x = PyFloat_AsDouble(value); @@ -953,14 +573,17 @@ g_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * g_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(long double))); long double val; memcpy(&val, ptr, sizeof(long double)); return PyFloat_FromDouble(val); } +/* d: double */ static PyObject * d_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(double))); double x; x = PyFloat_AsDouble(value); @@ -973,15 +596,18 @@ d_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * d_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(double))); double val; memcpy(&val, ptr, sizeof(val)); return PyFloat_FromDouble(val); } #if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX) +/* C: double complex */ static PyObject * C_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(double complex))); Py_complex c = PyComplex_AsCComplex(value); if (c.real == -1 && PyErr_Occurred()) { @@ -995,15 +621,18 @@ C_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * C_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(double complex))); double complex x; memcpy(&x, ptr, sizeof(x)); return PyComplex_FromDoubles(creal(x), cimag(x)); } +/* E: float complex */ static PyObject * E_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(float complex))); Py_complex c = PyComplex_AsCComplex(value); if (c.real == -1 && PyErr_Occurred()) { @@ -1017,15 +646,18 @@ E_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * E_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(float complex))); float complex x; memcpy(&x, ptr, sizeof(x)); return PyComplex_FromDoubles(crealf(x), cimagf(x)); } +/* F: long double complex */ static PyObject * F_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(long double complex))); Py_complex c = PyComplex_AsCComplex(value); if (c.real == -1 && PyErr_Occurred()) { @@ -1039,6 +671,7 @@ F_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * F_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(long double complex))); long double complex x; memcpy(&x, ptr, sizeof(x)); @@ -1046,9 +679,11 @@ F_get(void *ptr, Py_ssize_t size) } #endif +/* d: double */ static PyObject * d_set_sw(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(double))); double x; x = PyFloat_AsDouble(value); @@ -1067,6 +702,7 @@ d_set_sw(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * d_get_sw(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(double))); #ifdef WORDS_BIGENDIAN return PyFloat_FromDouble(PyFloat_Unpack8(ptr, 1)); #else @@ -1074,9 +710,11 @@ d_get_sw(void *ptr, Py_ssize_t size) #endif } +/* f: float */ static PyObject * f_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(float))); float x; x = (float)PyFloat_AsDouble(value); @@ -1089,6 +727,7 @@ f_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * f_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(float))); float val; memcpy(&val, ptr, sizeof(val)); return PyFloat_FromDouble(val); @@ -1097,6 +736,7 @@ f_get(void *ptr, Py_ssize_t size) static PyObject * f_set_sw(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(float))); float x; x = (float)PyFloat_AsDouble(value); @@ -1115,6 +755,7 @@ f_set_sw(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * f_get_sw(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(float))); #ifdef WORDS_BIGENDIAN return PyFloat_FromDouble(PyFloat_Unpack4(ptr, 1)); #else @@ -1122,6 +763,7 @@ f_get_sw(void *ptr, Py_ssize_t size) #endif } +/* O: Python object */ /* py_object refcounts: @@ -1135,6 +777,7 @@ f_get_sw(void *ptr, Py_ssize_t size) static PyObject * O_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(PyObject *))); PyObject *ob = *(PyObject **)ptr; if (ob == NULL) { if (!PyErr_Occurred()) @@ -1149,15 +792,18 @@ O_get(void *ptr, Py_ssize_t size) static PyObject * O_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(PyObject *))); /* Hm, does the memory block need it's own refcount or not? */ *(PyObject **)ptr = value; return Py_NewRef(value); } +/* c: a single byte-character */ static PyObject * c_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(char))); if (PyBytes_Check(value)) { if (PyBytes_GET_SIZE(value) != 1) { PyErr_Format(PyExc_TypeError, @@ -1204,13 +850,15 @@ c_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * c_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(char))); return PyBytes_FromStringAndSize((char *)ptr, 1); } -/* u - a single wchar_t character */ +/* u: a single wchar_t character */ static PyObject * u_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(wchar_t))); Py_ssize_t len; wchar_t chars[2]; if (!PyUnicode_Check(value)) { @@ -1244,10 +892,11 @@ u_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * u_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(wchar_t))); return PyUnicode_FromWideChar((wchar_t *)ptr, 1); } -/* U - a unicode string */ +/* U: a wchar_t* unicode string */ static PyObject * U_get(void *ptr, Py_ssize_t size) { @@ -1306,6 +955,7 @@ U_set(void *ptr, PyObject *value, Py_ssize_t length) } +/* s: a byte string */ static PyObject * s_get(void *ptr, Py_ssize_t size) { @@ -1355,6 +1005,7 @@ s_set(void *ptr, PyObject *value, Py_ssize_t length) _RET(value); } +/* z: a byte string, can be set from integer pointer */ static PyObject * z_set(void *ptr, PyObject *value, Py_ssize_t size) { @@ -1391,6 +1042,7 @@ z_get(void *ptr, Py_ssize_t size) } } +/* Z: a wchar* string, can be set from integer pointer */ static PyObject * Z_set(void *ptr, PyObject *value, Py_ssize_t size) { @@ -1445,8 +1097,9 @@ Z_get(void *ptr, Py_ssize_t size) #ifdef MS_WIN32 +/* X: COM BSTR (wide-char string to be handled handled using Windows API) */ static PyObject * -BSTR_set(void *ptr, PyObject *value, Py_ssize_t size) +X_set(void *ptr, PyObject *value, Py_ssize_t size) { BSTR bstr; @@ -1490,7 +1143,7 @@ BSTR_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * -BSTR_get(void *ptr, Py_ssize_t size) +X_get(void *ptr, Py_ssize_t size) { BSTR p; p = *(BSTR *)ptr; @@ -1505,9 +1158,11 @@ BSTR_get(void *ptr, Py_ssize_t size) } #endif +/* P: generic pointer */ static PyObject * P_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(void *))); void *v; if (value == Py_None) { *(void **)ptr = NULL; @@ -1539,154 +1194,339 @@ P_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * P_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(void *))); if (*(void **)ptr == NULL) { Py_RETURN_NONE; } return PyLong_FromVoidPtr(*(void **)ptr); } -static struct fielddesc formattable[] = { - { 's', s_set, s_get, NULL}, - { 'b', b_set, b_get, NULL}, - { 'B', B_set, B_get, NULL}, - { 'c', c_set, c_get, NULL}, - { 'd', d_set, d_get, NULL, d_set_sw, d_get_sw}, -#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX) - { 'C', C_set, C_get, NULL}, - { 'E', E_set, E_get, NULL}, - { 'F', F_set, F_get, NULL}, -#endif - { 'g', g_set, g_get, NULL}, - { 'f', f_set, f_get, NULL, f_set_sw, f_get_sw}, - { 'h', h_set, h_get, NULL, h_set_sw, h_get_sw}, - { 'H', H_set, H_get, NULL, H_set_sw, H_get_sw}, - { 'i', i_set, i_get, NULL, i_set_sw, i_get_sw}, - { 'I', I_set, I_get, NULL, I_set_sw, I_get_sw}, - { 'l', l_set, l_get, NULL, l_set_sw, l_get_sw}, - { 'L', L_set, L_get, NULL, L_set_sw, L_get_sw}, - { 'q', q_set, q_get, NULL, q_set_sw, q_get_sw}, - { 'Q', Q_set, Q_get, NULL, Q_set_sw, Q_get_sw}, - { 'P', P_set, P_get, NULL}, - { 'z', z_set, z_get, NULL}, - { 'u', u_set, u_get, NULL}, - { 'U', U_set, U_get, NULL}, - { 'Z', Z_set, Z_get, NULL}, -#ifdef MS_WIN32 - { 'X', BSTR_set, BSTR_get, NULL}, -#endif - { 'v', vBOOL_set, vBOOL_get, NULL}, -#if SIZEOF__BOOL == SIZEOF_INT - { '?', bool_set, bool_get, NULL, I_set_sw, I_get_sw}, -#elif SIZEOF__BOOL == SIZEOF_LONG - { '?', bool_set, bool_get, NULL, L_set_sw, L_get_sw}, -#elif SIZEOF__BOOL == SIZEOF_LONG_LONG - { '?', bool_set, bool_get, NULL, Q_set_sw, Q_get_sw}, -#else - { '?', bool_set, bool_get, NULL}, -#endif /* SIZEOF__BOOL */ - { 'O', O_set, O_get, NULL}, - { 0, NULL, NULL, NULL}, +/* Table with info about all formats. + * Must be accessed via _ctypes_get_fielddesc, which initializes it on + * first use. After initialization it's treated as constant & read-only. + */ + +struct formattable { +/*[python input] +for nbytes in 8, 16, 32, 64: + for sgn in 'i', 'u': + print(f' struct fielddesc fmt_{sgn}{nbytes};') +for code in 'sbBcdCEFgfhHiIlLqQPzuUZXvO': + print(f' struct fielddesc fmt_{code};') +[python start generated code]*/ + struct fielddesc fmt_i8; + struct fielddesc fmt_u8; + struct fielddesc fmt_i16; + struct fielddesc fmt_u16; + struct fielddesc fmt_i32; + struct fielddesc fmt_u32; + struct fielddesc fmt_i64; + struct fielddesc fmt_u64; + struct fielddesc fmt_s; + struct fielddesc fmt_b; + struct fielddesc fmt_B; + struct fielddesc fmt_c; + struct fielddesc fmt_d; + struct fielddesc fmt_C; + struct fielddesc fmt_E; + struct fielddesc fmt_F; + struct fielddesc fmt_g; + struct fielddesc fmt_f; + struct fielddesc fmt_h; + struct fielddesc fmt_H; + struct fielddesc fmt_i; + struct fielddesc fmt_I; + struct fielddesc fmt_l; + struct fielddesc fmt_L; + struct fielddesc fmt_q; + struct fielddesc fmt_Q; + struct fielddesc fmt_P; + struct fielddesc fmt_z; + struct fielddesc fmt_u; + struct fielddesc fmt_U; + struct fielddesc fmt_Z; + struct fielddesc fmt_X; + struct fielddesc fmt_v; + struct fielddesc fmt_O; +/*[python end generated code: output=fa648744ec7f919d input=087d58357d4bf2c5]*/ + + // bool has code '?': + struct fielddesc fmt_bool; + + // always contains NULLs: + struct fielddesc fmt_nil; }; -/* - Ideas: Implement VARIANT in this table, using 'V' code. - Use '?' as code for BOOL. -*/ +static struct formattable formattable; + + +/* Get fielddesc info for a fixed-width integer. + * N.B: - must be called after (or from) _ctypes_init_fielddesc! + * - nbytes must be one of the supported values + */ + +static inline struct fielddesc * +_ctypes_fixint_fielddesc(Py_ssize_t nbytes, bool is_signed) +{ +#define _PACK(NBYTES, SGN) ((NBYTES<<2) + (SGN ? 1 : 0)) + switch (_PACK(nbytes, is_signed)) { +/*[python input] +for nbytes in 8, 16, 32, 64: + for sgn in 'i', 'u': + is_signed = sgn == 'i' + print(f' case (_PACK({nbytes // 8}, {int(is_signed)})): ' + + f'return &formattable.fmt_{sgn}{nbytes};') +[python start generated code]*/ + case (_PACK(1, 1)): return &formattable.fmt_i8; + case (_PACK(1, 0)): return &formattable.fmt_u8; + case (_PACK(2, 1)): return &formattable.fmt_i16; + case (_PACK(2, 0)): return &formattable.fmt_u16; + case (_PACK(4, 1)): return &formattable.fmt_i32; + case (_PACK(4, 0)): return &formattable.fmt_u32; + case (_PACK(8, 1)): return &formattable.fmt_i64; + case (_PACK(8, 0)): return &formattable.fmt_u64; +/*[python end generated code: output=0194ba35c4d64ff3 input=ee9f6f5bb872d645]*/ +#undef _PACK + } + /* ctypes currently only supports platforms where the basic integer types + * (`char`, `short`, `int`, `long`, `long long`) have 1, 2, 4, or 8 bytes + * (i.e. 8 to 64 bits). + */ + Py_UNREACHABLE(); +} + + +/* Macro to call _ctypes_fixint_fielddesc for a given C type. */ + +_Py_COMP_DIAG_PUSH +#if defined(__GNUC__) && (__GNUC__ < 14) +/* The signedness check expands to an expression that's always true or false. + * Older GCC gives a '-Wtype-limits' warning for this, which is a GCC bug + * (docs say it should "not warn for constant expressions"): + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86647 + * Silence that warning. + */ +#pragma GCC diagnostic ignored "-Wtype-limits" +#endif + +#define FIXINT_FIELDDESC_FOR(C_TYPE) \ + _ctypes_fixint_fielddesc(sizeof(C_TYPE), (C_TYPE)-1 < 0) + /* Delayed initialization. Windows cannot statically reference dynamically loaded addresses from DLLs. */ void _ctypes_init_fielddesc(void) { - struct fielddesc *fd = formattable; - for (; fd->code; ++fd) { - switch (fd->code) { - case 's': fd->pffi_type = &ffi_type_pointer; break; - case 'b': fd->pffi_type = &ffi_type_schar; break; - case 'B': fd->pffi_type = &ffi_type_uchar; break; - case 'c': fd->pffi_type = &ffi_type_schar; break; - case 'd': fd->pffi_type = &ffi_type_double; break; + /* Fixed-width integers */ + +/*[python input] +for nbytes in 8, 16, 32, 64: + for sgn in 'i', 'u': + is_signed = sgn == 'i' + u = 'u' if sgn == 'u' else 's' + parts = [ + f"0", + f'&ffi_type_{u}int{nbytes}', + f'{sgn}{nbytes}_set', + f'{sgn}{nbytes}_get', + f'{sgn}{nbytes}_set_sw', + f'{sgn}{nbytes}_get_sw', + ] + print(f' formattable.fmt_{sgn}{nbytes} = (struct fielddesc){{') + print(f' {', '.join(parts)} }};') +[python start generated code]*/ + formattable.fmt_i8 = (struct fielddesc){ + 0, &ffi_type_sint8, i8_set, i8_get, i8_set_sw, i8_get_sw }; + formattable.fmt_u8 = (struct fielddesc){ + 0, &ffi_type_uint8, u8_set, u8_get, u8_set_sw, u8_get_sw }; + formattable.fmt_i16 = (struct fielddesc){ + 0, &ffi_type_sint16, i16_set, i16_get, i16_set_sw, i16_get_sw }; + formattable.fmt_u16 = (struct fielddesc){ + 0, &ffi_type_uint16, u16_set, u16_get, u16_set_sw, u16_get_sw }; + formattable.fmt_i32 = (struct fielddesc){ + 0, &ffi_type_sint32, i32_set, i32_get, i32_set_sw, i32_get_sw }; + formattable.fmt_u32 = (struct fielddesc){ + 0, &ffi_type_uint32, u32_set, u32_get, u32_set_sw, u32_get_sw }; + formattable.fmt_i64 = (struct fielddesc){ + 0, &ffi_type_sint64, i64_set, i64_get, i64_set_sw, i64_get_sw }; + formattable.fmt_u64 = (struct fielddesc){ + 0, &ffi_type_uint64, u64_set, u64_get, u64_set_sw, u64_get_sw }; +/*[python end generated code: output=16806fe0ca3a9c4c input=850b8dd6388b1b10]*/ + + + /* Native C integers. + * These use getters/setters for fixed-width ints but have their own + * `code` and `pffi_type`. + */ + +/*[python input] +for base_code, base_c_type in [ + ('b', 'char'), + ('h', 'short'), + ('i', 'int'), + ('l', 'long'), + ('q', 'long long'), +]: + for code, c_type, ffi_type in [ + (base_code, 'signed ' + base_c_type, 's' + base_c_type), + (base_code.upper(), 'unsigned ' + base_c_type, 'u' + base_c_type), + ]: + print(f' formattable.fmt_{code} = *FIXINT_FIELDDESC_FOR({c_type});') + print(f" formattable.fmt_{code}.code = '{code}';") + if base_code == 'q': + # ffi doesn't have `long long`; keep use the fixint type + pass + else: + print(f' formattable.fmt_{code}.pffi_type = &ffi_type_{ffi_type};') +[python start generated code]*/ + formattable.fmt_b = *FIXINT_FIELDDESC_FOR(signed char); + formattable.fmt_b.code = 'b'; + formattable.fmt_b.pffi_type = &ffi_type_schar; + formattable.fmt_B = *FIXINT_FIELDDESC_FOR(unsigned char); + formattable.fmt_B.code = 'B'; + formattable.fmt_B.pffi_type = &ffi_type_uchar; + formattable.fmt_h = *FIXINT_FIELDDESC_FOR(signed short); + formattable.fmt_h.code = 'h'; + formattable.fmt_h.pffi_type = &ffi_type_sshort; + formattable.fmt_H = *FIXINT_FIELDDESC_FOR(unsigned short); + formattable.fmt_H.code = 'H'; + formattable.fmt_H.pffi_type = &ffi_type_ushort; + formattable.fmt_i = *FIXINT_FIELDDESC_FOR(signed int); + formattable.fmt_i.code = 'i'; + formattable.fmt_i.pffi_type = &ffi_type_sint; + formattable.fmt_I = *FIXINT_FIELDDESC_FOR(unsigned int); + formattable.fmt_I.code = 'I'; + formattable.fmt_I.pffi_type = &ffi_type_uint; + formattable.fmt_l = *FIXINT_FIELDDESC_FOR(signed long); + formattable.fmt_l.code = 'l'; + formattable.fmt_l.pffi_type = &ffi_type_slong; + formattable.fmt_L = *FIXINT_FIELDDESC_FOR(unsigned long); + formattable.fmt_L.code = 'L'; + formattable.fmt_L.pffi_type = &ffi_type_ulong; + formattable.fmt_q = *FIXINT_FIELDDESC_FOR(signed long long); + formattable.fmt_q.code = 'q'; + formattable.fmt_Q = *FIXINT_FIELDDESC_FOR(unsigned long long); + formattable.fmt_Q.code = 'Q'; +/*[python end generated code: output=873c87a2e6b5075a input=ee814ca263aac18e]*/ + + + /* Other types have bespoke setters and getters named `@_set` and `@_get`, + * where `@` is the type code. + * Some have swapped variants, `@_set_sw` and `@_get_sw` + */ + +#define _TABLE_ENTRY(SYMBOL, FFI_TYPE, ...) \ + formattable.fmt_ ## SYMBOL = \ + (struct fielddesc){(#SYMBOL)[0], (FFI_TYPE), __VA_ARGS__}; \ + /////////////////////////////////////////////////////////////////////////// + +#define TABLE_ENTRY(SYMBOL, FFI_TYPE) \ + _TABLE_ENTRY(SYMBOL, FFI_TYPE, SYMBOL ## _set, SYMBOL ## _get) \ + /////////////////////////////////////////////////////////////////////////// + +#define TABLE_ENTRY_SW(SYMBOL, FFI_TYPE) \ + _TABLE_ENTRY(SYMBOL, FFI_TYPE, SYMBOL ## _set, \ + SYMBOL ## _get, SYMBOL ## _set_sw, SYMBOL ## _get_sw) \ + /////////////////////////////////////////////////////////////////////////// + + TABLE_ENTRY_SW(d, &ffi_type_double); #if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX) - case 'C': fd->pffi_type = &ffi_type_complex_double; break; - case 'E': fd->pffi_type = &ffi_type_complex_float; break; - case 'F': fd->pffi_type = &ffi_type_complex_longdouble; break; + TABLE_ENTRY(C, &ffi_type_complex_double); + TABLE_ENTRY(E, &ffi_type_complex_float); + TABLE_ENTRY(F, &ffi_type_complex_longdouble); #endif - case 'g': fd->pffi_type = &ffi_type_longdouble; break; - case 'f': fd->pffi_type = &ffi_type_float; break; - case 'h': fd->pffi_type = &ffi_type_sshort; break; - case 'H': fd->pffi_type = &ffi_type_ushort; break; - case 'i': fd->pffi_type = &ffi_type_sint; break; - case 'I': fd->pffi_type = &ffi_type_uint; break; - /* XXX Hm, sizeof(int) == sizeof(long) doesn't hold on every platform */ - /* As soon as we can get rid of the type codes, this is no longer a problem */ - #if SIZEOF_LONG == 4 - case 'l': fd->pffi_type = &ffi_type_sint32; break; - case 'L': fd->pffi_type = &ffi_type_uint32; break; - #elif SIZEOF_LONG == 8 - case 'l': fd->pffi_type = &ffi_type_sint64; break; - case 'L': fd->pffi_type = &ffi_type_uint64; break; - #else - #error - #endif - #if SIZEOF_LONG_LONG == 8 - case 'q': fd->pffi_type = &ffi_type_sint64; break; - case 'Q': fd->pffi_type = &ffi_type_uint64; break; - #else - #error - #endif - case 'P': fd->pffi_type = &ffi_type_pointer; break; - case 'z': fd->pffi_type = &ffi_type_pointer; break; - case 'u': - if (sizeof(wchar_t) == sizeof(short)) - fd->pffi_type = &ffi_type_sshort; - else if (sizeof(wchar_t) == sizeof(int)) - fd->pffi_type = &ffi_type_sint; - else if (sizeof(wchar_t) == sizeof(long)) - fd->pffi_type = &ffi_type_slong; - else - Py_UNREACHABLE(); - break; - case 'U': fd->pffi_type = &ffi_type_pointer; break; - case 'Z': fd->pffi_type = &ffi_type_pointer; break; - #ifdef MS_WIN32 - case 'X': fd->pffi_type = &ffi_type_pointer; break; - #endif - case 'v': fd->pffi_type = &ffi_type_sshort; break; - #if SIZEOF__BOOL == 1 - case '?': fd->pffi_type = &ffi_type_uchar; break; /* Also fallback for no native _Bool support */ - #elif SIZEOF__BOOL == SIZEOF_SHORT - case '?': fd->pffi_type = &ffi_type_ushort; break; - #elif SIZEOF__BOOL == SIZEOF_INT - case '?': fd->pffi_type = &ffi_type_uint; break; - #elif SIZEOF__BOOL == SIZEOF_LONG - case '?': fd->pffi_type = &ffi_type_ulong; break; - #elif SIZEOF__BOOL == SIZEOF_LONG_LONG - case '?': fd->pffi_type = &ffi_type_ulong; break; - #endif /* SIZEOF__BOOL */ - case 'O': fd->pffi_type = &ffi_type_pointer; break; - default: - Py_UNREACHABLE(); - } - } + TABLE_ENTRY(g, &ffi_type_longdouble); + TABLE_ENTRY_SW(f, &ffi_type_float); + TABLE_ENTRY(v, &ffi_type_sshort); /* vBOOL */ + + // ctypes.c_char is signed for FFI, even where C wchar_t is unsigned. + TABLE_ENTRY(c, _ctypes_fixint_fielddesc(sizeof(char), true)->pffi_type); + // ctypes.c_wchar is signed for FFI, even where C wchar_t is unsigned. + TABLE_ENTRY(u, _ctypes_fixint_fielddesc(sizeof(wchar_t), true)->pffi_type); + + TABLE_ENTRY(s, &ffi_type_pointer); + TABLE_ENTRY(P, &ffi_type_pointer); + TABLE_ENTRY(z, &ffi_type_pointer); + TABLE_ENTRY(U, &ffi_type_pointer); + TABLE_ENTRY(Z, &ffi_type_pointer); +#ifdef MS_WIN32 + TABLE_ENTRY(X, &ffi_type_pointer); +#endif + TABLE_ENTRY(O, &ffi_type_pointer); + +#undef TABLE_ENTRY_SW +#undef TABLE_ENTRY +#undef _TABLE_ENTRY + /* bool has code '?', fill it in manually */ + + // ctypes.c_bool is unsigned for FFI, even where C bool is signed. + formattable.fmt_bool = *_ctypes_fixint_fielddesc(sizeof(bool), false); + formattable.fmt_bool.code = '?'; + formattable.fmt_bool.setfunc = bool_set; + formattable.fmt_bool.getfunc = bool_get; } +#undef FIXINT_FIELDDESC_FOR +_Py_COMP_DIAG_POP struct fielddesc * _ctypes_get_fielddesc(const char *fmt) { - static int initialized = 0; - struct fielddesc *table = formattable; - + static bool initialized = false; + static PyMutex mutex = {0}; + PyMutex_Lock(&mutex); if (!initialized) { - initialized = 1; _ctypes_init_fielddesc(); + initialized = true; } - - for (; table->code; ++table) { - if (table->code == fmt[0]) - return table; + PyMutex_Unlock(&mutex); + struct fielddesc *result = NULL; + switch(fmt[0]) { +/*[python input] +for code in 'sbBcdCEFgfhHiIlLqQPzuUZXvO': + print(f" case '{code}': result = &formattable.fmt_{code}; break;") +[python start generated code]*/ + case 's': result = &formattable.fmt_s; break; + case 'b': result = &formattable.fmt_b; break; + case 'B': result = &formattable.fmt_B; break; + case 'c': result = &formattable.fmt_c; break; + case 'd': result = &formattable.fmt_d; break; + case 'C': result = &formattable.fmt_C; break; + case 'E': result = &formattable.fmt_E; break; + case 'F': result = &formattable.fmt_F; break; + case 'g': result = &formattable.fmt_g; break; + case 'f': result = &formattable.fmt_f; break; + case 'h': result = &formattable.fmt_h; break; + case 'H': result = &formattable.fmt_H; break; + case 'i': result = &formattable.fmt_i; break; + case 'I': result = &formattable.fmt_I; break; + case 'l': result = &formattable.fmt_l; break; + case 'L': result = &formattable.fmt_L; break; + case 'q': result = &formattable.fmt_q; break; + case 'Q': result = &formattable.fmt_Q; break; + case 'P': result = &formattable.fmt_P; break; + case 'z': result = &formattable.fmt_z; break; + case 'u': result = &formattable.fmt_u; break; + case 'U': result = &formattable.fmt_U; break; + case 'Z': result = &formattable.fmt_Z; break; + case 'X': result = &formattable.fmt_X; break; + case 'v': result = &formattable.fmt_v; break; + case 'O': result = &formattable.fmt_O; break; +/*[python end generated code: output=81a8223dda9f81f7 input=2f59666d3c024edf]*/ + case '?': result = &formattable.fmt_bool; break; } - return NULL; + if (!result || !result->code) { + return NULL; + } + assert(result->pffi_type); + assert(result->setfunc); + assert(result->getfunc); + return result; } +/* + Ideas: Implement VARIANT in this table, using 'V' code. +*/ + /*---------------- EOF ----------------*/ diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 7e0804054cded4..45e00a538fb5a5 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -113,8 +113,17 @@ extern PyType_Spec cthunk_spec; typedef struct tagPyCArgObject PyCArgObject; typedef struct tagCDataObject CDataObject; -typedef PyObject *(* GETFUNC)(void *, Py_ssize_t size); -typedef PyObject *(* SETFUNC)(void *, PyObject *value, Py_ssize_t size); + +// GETFUNC: convert the C value at *ptr* to Python object, return the object +// SETFUNC: write content of the PyObject *value* to the location at *ptr*; +// return a new reference to either *value*, or None for simple types +// (see _CTYPES_DEBUG_KEEP). +// Note that the *size* arg can have different meanings depending on context: +// for string-like arrays it's the size in bytes +// for int-style fields it's either the type size, or bitfiled info +// that can be unpacked using the LOW_BIT & NUM_BITS macros. +typedef PyObject *(* GETFUNC)(void *ptr, Py_ssize_t size); +typedef PyObject *(* SETFUNC)(void *ptr, PyObject *value, Py_ssize_t size); typedef PyCArgObject *(* PARAMFUNC)(ctypes_state *st, CDataObject *obj); /* A default buffer in CDataObject, which can be used for small C types. If @@ -239,9 +248,9 @@ extern CThunkObject *_ctypes_alloc_callback(ctypes_state *st, /* a table entry describing a predefined ctypes type */ struct fielddesc { char code; + ffi_type *pffi_type; /* always statically allocated */ SETFUNC setfunc; GETFUNC getfunc; - ffi_type *pffi_type; /* always statically allocated */ SETFUNC setfunc_swapped; GETFUNC getfunc_swapped; }; From 128cc47fbd44e3e09c50d9674fe4a4bba3be450c Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 20 Dec 2024 16:52:20 +0000 Subject: [PATCH 080/376] GH-127705: Add debug mode for `_PyStackRef`s inspired by HPy debug mode (GH-128121) --- Include/internal/pycore_interp.h | 6 + Include/internal/pycore_stackref.h | 113 +++++++++++++ Makefile.pre.in | 1 + ...-12-20-12-25-16.gh-issue-127705.WmCz1z.rst | 4 + Objects/frameobject.c | 4 +- Python/bytecodes.c | 17 +- Python/ceval.c | 67 ++++++-- Python/ceval_macros.h | 6 +- Python/executor_cases.c.h | 2 +- Python/generated_cases.c.h | 23 ++- Python/pystate.c | 29 ++++ Python/stackrefs.c | 156 ++++++++++++++++++ 12 files changed, 395 insertions(+), 33 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-12-25-16.gh-issue-127705.WmCz1z.rst create mode 100644 Python/stackrefs.c diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 87cdcb5b119d15..a3c14dceffd7a0 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -34,6 +34,7 @@ extern "C" { #include "pycore_optimizer.h" // _PyOptimizerObject #include "pycore_obmalloc.h" // struct _obmalloc_state #include "pycore_qsbr.h" // struct _qsbr_state +#include "pycore_stackref.h" // Py_STACKREF_DEBUG #include "pycore_tstate.h" // _PyThreadStateImpl #include "pycore_tuple.h" // struct _Py_tuple_state #include "pycore_uniqueid.h" // struct _Py_unique_id_pool @@ -285,6 +286,11 @@ struct _is { _PyThreadStateImpl _initial_thread; // _initial_thread should be the last field of PyInterpreterState. // See https://github.com/python/cpython/issues/127117. + +#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG) + uint64_t next_stackref; + _Py_hashtable_t *stackref_debug_table; +#endif }; diff --git a/Include/internal/pycore_stackref.h b/Include/internal/pycore_stackref.h index 90a3118352f7ae..1ae62cc69bb364 100644 --- a/Include/internal/pycore_stackref.h +++ b/Include/internal/pycore_stackref.h @@ -4,6 +4,9 @@ extern "C" { #endif +// Define this to get precise tracking of stackrefs. +// #define Py_STACKREF_DEBUG 1 + #ifndef Py_BUILD_CORE # error "this header requires Py_BUILD_CORE define" #endif @@ -49,6 +52,113 @@ extern "C" { CPython refcounting operations on it! */ + +#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG) + + + +typedef union _PyStackRef { + uint64_t index; +} _PyStackRef; + +#define Py_TAG_BITS 0 + +PyAPI_FUNC(PyObject *) _Py_stackref_get_object(_PyStackRef ref); +PyAPI_FUNC(PyObject *) _Py_stackref_close(_PyStackRef ref); +PyAPI_FUNC(_PyStackRef) _Py_stackref_create(PyObject *obj, const char *filename, int linenumber); +PyAPI_FUNC(void) _Py_stackref_record_borrow(_PyStackRef ref, const char *filename, int linenumber); +extern void _Py_stackref_associate(PyInterpreterState *interp, PyObject *obj, _PyStackRef ref); + +static const _PyStackRef PyStackRef_NULL = { .index = 0 }; + +#define PyStackRef_None ((_PyStackRef){ .index = 1 } ) +#define PyStackRef_False ((_PyStackRef){ .index = 2 }) +#define PyStackRef_True ((_PyStackRef){ .index = 3 }) + +#define LAST_PREDEFINED_STACKREF_INDEX 3 + +static inline int +PyStackRef_IsNull(_PyStackRef ref) +{ + return ref.index == 0; +} + +static inline int +PyStackRef_IsTrue(_PyStackRef ref) +{ + return _Py_stackref_get_object(ref) == Py_True; +} + +static inline int +PyStackRef_IsFalse(_PyStackRef ref) +{ + return _Py_stackref_get_object(ref) == Py_False; +} + +static inline int +PyStackRef_IsNone(_PyStackRef ref) +{ + return _Py_stackref_get_object(ref) == Py_None; +} + +static inline PyObject * +_PyStackRef_AsPyObjectBorrow(_PyStackRef ref, const char *filename, int linenumber) +{ + _Py_stackref_record_borrow(ref, filename, linenumber); + return _Py_stackref_get_object(ref); +} + +#define PyStackRef_AsPyObjectBorrow(REF) _PyStackRef_AsPyObjectBorrow((REF), __FILE__, __LINE__) + +static inline PyObject * +PyStackRef_AsPyObjectSteal(_PyStackRef ref) +{ + return _Py_stackref_close(ref); +} + +static inline _PyStackRef +_PyStackRef_FromPyObjectNew(PyObject *obj, const char *filename, int linenumber) +{ + Py_INCREF(obj); + return _Py_stackref_create(obj, filename, linenumber); +} +#define PyStackRef_FromPyObjectNew(obj) _PyStackRef_FromPyObjectNew(_PyObject_CAST(obj), __FILE__, __LINE__) + +static inline _PyStackRef +_PyStackRef_FromPyObjectSteal(PyObject *obj, const char *filename, int linenumber) +{ + return _Py_stackref_create(obj, filename, linenumber); +} +#define PyStackRef_FromPyObjectSteal(obj) _PyStackRef_FromPyObjectSteal(_PyObject_CAST(obj), __FILE__, __LINE__) + +static inline _PyStackRef +_PyStackRef_FromPyObjectImmortal(PyObject *obj, const char *filename, int linenumber) +{ + assert(_Py_IsImmortal(obj)); + return _Py_stackref_create(obj, filename, linenumber); +} +#define PyStackRef_FromPyObjectImmortal(obj) _PyStackRef_FromPyObjectImmortal(_PyObject_CAST(obj), __FILE__, __LINE__) + +static inline void +PyStackRef_CLOSE(_PyStackRef ref) +{ + PyObject *obj = _Py_stackref_close(ref); + Py_DECREF(obj); +} + +static inline _PyStackRef +_PyStackRef_DUP(_PyStackRef ref, const char *filename, int linenumber) +{ + PyObject *obj = _Py_stackref_get_object(ref); + Py_INCREF(obj); + return _Py_stackref_create(obj, filename, linenumber); +} +#define PyStackRef_DUP(REF) _PyStackRef_DUP(REF, __FILE__, __LINE__) + +#define PyStackRef_CLOSE_SPECIALIZED(stackref, dealloc) PyStackRef_CLOSE(stackref) + +#else + typedef union _PyStackRef { uintptr_t bits; } _PyStackRef; @@ -200,12 +310,15 @@ static const _PyStackRef PyStackRef_NULL = { .bits = 0 }; #define PyStackRef_IsTrue(ref) (PyStackRef_AsPyObjectBorrow(ref) == Py_True) #define PyStackRef_IsFalse(ref) (PyStackRef_AsPyObjectBorrow(ref) == Py_False) +#endif + // Converts a PyStackRef back to a PyObject *, converting the // stackref to a new reference. #define PyStackRef_AsPyObjectNew(stackref) Py_NewRef(PyStackRef_AsPyObjectBorrow(stackref)) #define PyStackRef_TYPE(stackref) Py_TYPE(PyStackRef_AsPyObjectBorrow(stackref)) + #define PyStackRef_CLEAR(op) \ do { \ _PyStackRef *_tmp_op_ptr = &(op); \ diff --git a/Makefile.pre.in b/Makefile.pre.in index 3e880f7800fccf..67acf0fc520087 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -488,6 +488,7 @@ PYTHON_OBJS= \ Python/qsbr.o \ Python/bootstrap_hash.o \ Python/specialize.o \ + Python/stackrefs.o \ Python/structmember.o \ Python/symtable.o \ Python/sysmodule.o \ diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-12-25-16.gh-issue-127705.WmCz1z.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-12-25-16.gh-issue-127705.WmCz1z.rst new file mode 100644 index 00000000000000..fde12b78ce0444 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-12-25-16.gh-issue-127705.WmCz1z.rst @@ -0,0 +1,4 @@ +Adds stackref debugging when ``Py_STACKREF_DEBUG`` is set. Finds all +double-closes and leaks, logging the origin and last borrow. + +Inspired by HPy's debug mode. https://docs.hpyproject.org/en/latest/debug-mode.html diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 03ed2b9480f8c9..10fd3a982c36f4 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -179,9 +179,9 @@ framelocalsproxy_setitem(PyObject *self, PyObject *key, PyObject *value) if (kind == CO_FAST_FREE) { // The cell was set when the frame was created from // the function's closure. - assert(oldvalue.bits != 0 && PyCell_Check(PyStackRef_AsPyObjectBorrow(oldvalue))); + assert(!PyStackRef_IsNull(oldvalue) && PyCell_Check(PyStackRef_AsPyObjectBorrow(oldvalue))); cell = PyStackRef_AsPyObjectBorrow(oldvalue); - } else if (kind & CO_FAST_CELL && oldvalue.bits != 0) { + } else if (kind & CO_FAST_CELL && !PyStackRef_IsNull(oldvalue)) { PyObject *as_obj = PyStackRef_AsPyObjectBorrow(oldvalue); if (PyCell_Check(as_obj)) { cell = as_obj; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 30c12dd4dc9205..63cf1978e8abe5 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -681,7 +681,7 @@ dummy_func( assert(Py_REFCNT(left_o) >= 2); PyStackRef_CLOSE(left); DEAD(left); - PyObject *temp = PyStackRef_AsPyObjectBorrow(*target_local); + PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local); PyUnicode_Append(&temp, right_o); *target_local = PyStackRef_FromPyObjectSteal(temp); PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); @@ -4509,17 +4509,17 @@ dummy_func( op(_DO_CALL_FUNCTION_EX, (func_st, unused, callargs_st, kwargs_st if (oparg & 1) -- result)) { PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); - PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); - PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); // DICT_MERGE is called before this opcode if there are kwargs. // It converts all dict subtypes in kwargs into regular dicts. - assert(kwargs == NULL || PyDict_CheckExact(kwargs)); - assert(PyTuple_CheckExact(callargs)); EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_FUNCTION_EX, func); PyObject *result_o; assert(!_PyErr_Occurred(tstate)); if (opcode == INSTRUMENTED_CALL_FUNCTION_EX) { + PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); + PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + assert(PyTuple_CheckExact(callargs)); PyObject *arg = PyTuple_GET_SIZE(callargs) > 0 ? PyTuple_GET_ITEM(callargs, 0) : &_PyInstrumentation_MISSING; int err = _Py_call_instrumentation_2args( @@ -4550,7 +4550,10 @@ dummy_func( if (Py_TYPE(func) == &PyFunction_Type && tstate->interp->eval_frame == NULL && ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { + PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); assert(PyTuple_CheckExact(callargs)); + PyObject *kwargs = PyStackRef_IsNull(kwargs_st) ? NULL : PyStackRef_AsPyObjectSteal(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); @@ -4568,6 +4571,10 @@ dummy_func( frame->return_offset = 1; DISPATCH_INLINED(new_frame); } + PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); + assert(PyTuple_CheckExact(callargs)); + PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); result_o = PyObject_Call(func, callargs, kwargs); } PyStackRef_XCLOSE(kwargs_st); diff --git a/Python/ceval.c b/Python/ceval.c index bfdf5687c287db..3cf11b663c571b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -164,7 +164,7 @@ dump_stack(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer) PyErr_Clear(); } // Don't call __repr__(), it might recurse into the interpreter. - printf("<%s at %p>", Py_TYPE(obj)->tp_name, (void *)(ptr->bits)); + printf("<%s at %p>", Py_TYPE(obj)->tp_name, PyStackRef_AsPyObjectBorrow(*ptr)); } printf("]\n"); fflush(stdout); @@ -805,7 +805,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int -#ifdef Py_DEBUG +#if defined(Py_DEBUG) && !defined(Py_STACKREF_DEBUG) /* Set these to invalid but identifiable values for debugging. */ entry_frame.f_funcobj = (_PyStackRef){.bits = 0xaaa0}; entry_frame.f_locals = (PyObject*)0xaaa1; @@ -1810,27 +1810,48 @@ _PyEvalFramePushAndInit_Ex(PyThreadState *tstate, _PyStackRef func, { bool has_dict = (kwargs != NULL && PyDict_GET_SIZE(kwargs) > 0); PyObject *kwnames = NULL; - PyObject *const *newargs; + _PyStackRef *newargs; + PyObject *const *object_array = NULL; + _PyStackRef stack_array[8]; if (has_dict) { - newargs = _PyStack_UnpackDict(tstate, _PyTuple_ITEMS(callargs), nargs, kwargs, &kwnames); - if (newargs == NULL) { + object_array = _PyStack_UnpackDict(tstate, _PyTuple_ITEMS(callargs), nargs, kwargs, &kwnames); + if (object_array == NULL) { PyStackRef_CLOSE(func); goto error; } + size_t total_args = nargs + PyDict_GET_SIZE(kwargs); + assert(sizeof(PyObject *) == sizeof(_PyStackRef)); + newargs = (_PyStackRef *)object_array; + for (size_t i = 0; i < total_args; i++) { + newargs[i] = PyStackRef_FromPyObjectSteal(object_array[i]); + } } else { - newargs = &PyTuple_GET_ITEM(callargs, 0); - /* We need to incref all our args since the new frame steals the references. */ - for (Py_ssize_t i = 0; i < nargs; ++i) { - Py_INCREF(PyTuple_GET_ITEM(callargs, i)); + if (nargs <= 8) { + newargs = stack_array; + } + else { + newargs = PyMem_Malloc(sizeof(_PyStackRef) *nargs); + if (newargs == NULL) { + PyErr_NoMemory(); + PyStackRef_CLOSE(func); + goto error; + } + } + /* We need to create a new reference for all our args since the new frame steals them. */ + for (Py_ssize_t i = 0; i < nargs; i++) { + newargs[i] = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(callargs, i)); } } _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( tstate, func, locals, - (_PyStackRef const *)newargs, nargs, kwnames, previous + newargs, nargs, kwnames, previous ); if (has_dict) { - _PyStack_UnpackDict_FreeNoDecRef(newargs, kwnames); + _PyStack_UnpackDict_FreeNoDecRef(object_array, kwnames); + } + else if (nargs > 8) { + PyMem_Free((void *)newargs); } /* No need to decref func here because the reference has been stolen by _PyEvalFramePushAndInit. @@ -1850,21 +1871,39 @@ _PyEval_Vector(PyThreadState *tstate, PyFunctionObject *func, PyObject* const* args, size_t argcount, PyObject *kwnames) { + size_t total_args = argcount; + if (kwnames) { + total_args += PyTuple_GET_SIZE(kwnames); + } + _PyStackRef stack_array[8]; + _PyStackRef *arguments; + if (total_args <= 8) { + arguments = stack_array; + } + else { + arguments = PyMem_Malloc(sizeof(_PyStackRef) * total_args); + if (arguments == NULL) { + return PyErr_NoMemory(); + } + } /* _PyEvalFramePushAndInit consumes the references * to func, locals and all its arguments */ Py_XINCREF(locals); for (size_t i = 0; i < argcount; i++) { - Py_INCREF(args[i]); + arguments[i] = PyStackRef_FromPyObjectNew(args[i]); } if (kwnames) { Py_ssize_t kwcount = PyTuple_GET_SIZE(kwnames); for (Py_ssize_t i = 0; i < kwcount; i++) { - Py_INCREF(args[i+argcount]); + arguments[i+argcount] = PyStackRef_FromPyObjectNew(args[i+argcount]); } } _PyInterpreterFrame *frame = _PyEvalFramePushAndInit( tstate, PyStackRef_FromPyObjectNew(func), locals, - (_PyStackRef const *)args, argcount, kwnames, NULL); + arguments, argcount, kwnames, NULL); + if (total_args > 8) { + PyMem_Free(arguments); + } if (frame == NULL) { return NULL; } diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 398816d5f36a1d..f15633fa467376 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -450,7 +450,7 @@ do { \ /* How much scratch space to give stackref to PyObject* conversion. */ #define MAX_STACKREF_SCRATCH 10 -#ifdef Py_GIL_DISABLED +#if defined(Py_GIL_DISABLED) || defined(Py_STACKREF_DEBUG) #define STACKREFS_TO_PYOBJECTS(ARGS, ARG_COUNT, NAME) \ /* +1 because vectorcall might use -1 to write self */ \ PyObject *NAME##_temp[MAX_STACKREF_SCRATCH+1]; \ @@ -461,7 +461,7 @@ do { \ assert(NAME != NULL); #endif -#ifdef Py_GIL_DISABLED +#if defined(Py_GIL_DISABLED) || defined(Py_STACKREF_DEBUG) #define STACKREFS_TO_PYOBJECTS_CLEANUP(NAME) \ /* +1 because we +1 previously */ \ _PyObjectArray_Free(NAME - 1, NAME##_temp); @@ -470,7 +470,7 @@ do { \ (void)(NAME); #endif -#ifdef Py_GIL_DISABLED +#if defined(Py_GIL_DISABLED) || defined(Py_STACKREF_DEBUG) #define CONVERSION_FAILED(NAME) ((NAME) == NULL) #else #define CONVERSION_FAILED(NAME) (0) diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 6e752c57cd70f3..22335021faaa6d 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -850,7 +850,7 @@ */ assert(Py_REFCNT(left_o) >= 2); PyStackRef_CLOSE(left); - PyObject *temp = PyStackRef_AsPyObjectBorrow(*target_local); + PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local); PyUnicode_Append(&temp, right_o); *target_local = PyStackRef_FromPyObjectSteal(temp); PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ee5c55a832d460..bed16b60b76a2f 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -208,7 +208,7 @@ */ assert(Py_REFCNT(left_o) >= 2); PyStackRef_CLOSE(left); - PyObject *temp = PyStackRef_AsPyObjectBorrow(*target_local); + PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local); PyUnicode_Append(&temp, right_o); *target_local = PyStackRef_FromPyObjectSteal(temp); PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); @@ -1675,16 +1675,16 @@ callargs_st = tuple; func_st = func; PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); - PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); - PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); // DICT_MERGE is called before this opcode if there are kwargs. // It converts all dict subtypes in kwargs into regular dicts. - assert(kwargs == NULL || PyDict_CheckExact(kwargs)); - assert(PyTuple_CheckExact(callargs)); EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_FUNCTION_EX, func); PyObject *result_o; assert(!_PyErr_Occurred(tstate)); if (opcode == INSTRUMENTED_CALL_FUNCTION_EX) { + PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); + PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + assert(PyTuple_CheckExact(callargs)); PyObject *arg = PyTuple_GET_SIZE(callargs) > 0 ? PyTuple_GET_ITEM(callargs, 0) : &_PyInstrumentation_MISSING; stack_pointer[-1 - (oparg & 1)] = callargs_st; @@ -1724,19 +1724,22 @@ if (Py_TYPE(func) == &PyFunction_Type && tstate->interp->eval_frame == NULL && ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { + PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); assert(PyTuple_CheckExact(callargs)); + PyObject *kwargs = PyStackRef_IsNull(kwargs_st) ? NULL : PyStackRef_AsPyObjectSteal(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); - stack_pointer[-1 - (oparg & 1)] = callargs_st; - if (oparg & 1) stack_pointer[-(oparg & 1)] = kwargs_st; + stack_pointer += -2 - (oparg & 1); + assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex( tstate, func_st, locals, nargs, callargs, kwargs, frame); stack_pointer = _PyFrame_GetStackPointer(frame); // Need to sync the stack since we exit with DISPATCH_INLINED. - stack_pointer += -3 - (oparg & 1); + stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); if (new_frame == NULL) { goto error; @@ -1745,6 +1748,10 @@ frame->return_offset = 1; DISPATCH_INLINED(new_frame); } + PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); + assert(PyTuple_CheckExact(callargs)); + PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); stack_pointer[-1 - (oparg & 1)] = callargs_st; if (oparg & 1) stack_pointer[-(oparg & 1)] = kwargs_st; _PyFrame_SetStackPointer(frame, stack_pointer); diff --git a/Python/pystate.c b/Python/pystate.c index 839413a65a42fb..c546b7c3a9f10e 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -19,6 +19,7 @@ #include "pycore_pymem.h" // _PyMem_SetDefaultAllocator() #include "pycore_pystate.h" #include "pycore_runtime_init.h" // _PyRuntimeState_INIT +#include "pycore_stackref.h" // Py_STACKREF_DEBUG #include "pycore_obmalloc.h" // _PyMem_obmalloc_state_on_heap() #include "pycore_uniqueid.h" // _PyObject_FinalizePerThreadRefcounts() @@ -663,6 +664,23 @@ init_interpreter(PyInterpreterState *interp, /* Fix the self-referential, statically initialized fields. */ interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp); } +#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG) + interp->next_stackref = 1; + _Py_hashtable_allocator_t alloc = { + .malloc = malloc, + .free = free, + }; + interp->stackref_debug_table = _Py_hashtable_new_full( + _Py_hashtable_hash_ptr, + _Py_hashtable_compare_direct, + NULL, + NULL, + &alloc + ); + _Py_stackref_associate(interp, Py_None, PyStackRef_None); + _Py_stackref_associate(interp, Py_False, PyStackRef_False); + _Py_stackref_associate(interp, Py_True, PyStackRef_True); +#endif interp->_initialized = 1; return _PyStatus_OK(); @@ -768,6 +786,11 @@ PyInterpreterState_New(void) return interp; } +#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG) +extern void +_Py_stackref_report_leaks(PyInterpreterState *interp); +#endif + static void interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) { @@ -877,6 +900,12 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) Py_CLEAR(interp->sysdict); Py_CLEAR(interp->builtins); +#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG) + _Py_stackref_report_leaks(interp); + _Py_hashtable_destroy(interp->stackref_debug_table); + interp->stackref_debug_table = NULL; +#endif + if (tstate->interp == interp) { /* We are now safe to fix tstate->_status.cleared. */ // XXX Do this (much) earlier? diff --git a/Python/stackrefs.c b/Python/stackrefs.c new file mode 100644 index 00000000000000..9bb46897685570 --- /dev/null +++ b/Python/stackrefs.c @@ -0,0 +1,156 @@ + +#include "Python.h" + +#include "pycore_stackref.h" + +#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG) + +#if SIZEOF_VOID_P < 8 +#error "Py_STACKREF_DEBUG requires 64 bit machine" +#endif + +#include "pycore_interp.h" +#include "pycore_hashtable.h" + +typedef struct _table_entry { + PyObject *obj; + const char *classname; + const char *filename; + int linenumber; + const char *filename_borrow; + int linenumber_borrow; +} TableEntry; + +TableEntry * +make_table_entry(PyObject *obj, const char *filename, int linenumber) +{ + TableEntry *result = malloc(sizeof(TableEntry)); + if (result == NULL) { + return NULL; + } + result->obj = obj; + result->classname = Py_TYPE(obj)->tp_name; + result->filename = filename; + result->linenumber = linenumber; + result->filename_borrow = NULL; + return result; +} + +PyObject * +_Py_stackref_get_object(_PyStackRef ref) +{ + if (ref.index == 0) { + return NULL; + } + PyInterpreterState *interp = PyInterpreterState_Get(); + assert(interp != NULL); + if (ref.index >= interp->next_stackref) { + _Py_FatalErrorFormat(__func__, "Garbled stack ref with ID %" PRIu64 "\n", ref.index); + } + TableEntry *entry = _Py_hashtable_get(interp->stackref_debug_table, (void *)ref.index); + if (entry == NULL) { + _Py_FatalErrorFormat(__func__, "Accessing closed stack ref with ID %" PRIu64 "\n", ref.index); + } + return entry->obj; +} + +PyObject * +_Py_stackref_close(_PyStackRef ref) +{ + PyInterpreterState *interp = PyInterpreterState_Get(); + if (ref.index >= interp->next_stackref) { + _Py_FatalErrorFormat(__func__, "Garbled stack ref with ID %" PRIu64 "\n", ref.index); + } + PyObject *obj; + if (ref.index <= LAST_PREDEFINED_STACKREF_INDEX) { + // Pre-allocated reference to None, False or True -- Do not clear + TableEntry *entry = _Py_hashtable_get(interp->stackref_debug_table, (void *)ref.index); + obj = entry->obj; + } + else { + TableEntry *entry = _Py_hashtable_steal(interp->stackref_debug_table, (void *)ref.index); + if (entry == NULL) { + _Py_FatalErrorFormat(__func__, "Invalid StackRef with ID %" PRIu64 "\n", (void *)ref.index); + } + obj = entry->obj; + free(entry); + } + return obj; +} + +_PyStackRef +_Py_stackref_create(PyObject *obj, const char *filename, int linenumber) +{ + if (obj == NULL) { + Py_FatalError("Cannot create a stackref for NULL"); + } + PyInterpreterState *interp = PyInterpreterState_Get(); + uint64_t new_id = interp->next_stackref++; + TableEntry *entry = make_table_entry(obj, filename, linenumber); + if (entry == NULL) { + Py_FatalError("No memory left for stackref debug table"); + } + if (_Py_hashtable_set(interp->stackref_debug_table, (void *)new_id, entry) < 0) { + Py_FatalError("No memory left for stackref debug table"); + } + return (_PyStackRef){ .index = new_id }; +} + +void +_Py_stackref_record_borrow(_PyStackRef ref, const char *filename, int linenumber) +{ + if (ref.index <= LAST_PREDEFINED_STACKREF_INDEX) { + return; + } + PyInterpreterState *interp = PyInterpreterState_Get(); + TableEntry *entry = _Py_hashtable_get(interp->stackref_debug_table, (void *)ref.index); + if (entry == NULL) { + _Py_FatalErrorFormat(__func__, "Invalid StackRef with ID %" PRIu64 "\n", (void *)ref.index); + } + entry->filename_borrow = filename; + entry->linenumber_borrow = linenumber; +} + + +void +_Py_stackref_associate(PyInterpreterState *interp, PyObject *obj, _PyStackRef ref) +{ + assert(interp->next_stackref >= ref.index); + interp->next_stackref = ref.index+1; + TableEntry *entry = make_table_entry(obj, "builtin-object", 0); + if (entry == NULL) { + Py_FatalError("No memory left for stackref debug table"); + } + if (_Py_hashtable_set(interp->stackref_debug_table, (void *)ref.index, (void *)entry) < 0) { + Py_FatalError("No memory left for stackref debug table"); + } +} + + +static int +report_leak(_Py_hashtable_t *ht, const void *key, const void *value, void *leak) +{ + TableEntry *entry = (TableEntry *)value; + if (!_Py_IsStaticImmortal(entry->obj)) { + *(int *)leak = 1; + printf("Stackref leak. Refers to instance of %s at %p. Created at %s:%d", + entry->classname, entry->obj, entry->filename, entry->linenumber); + if (entry->filename_borrow != NULL) { + printf(". Last borrow at %s:%d",entry->filename_borrow, entry->linenumber_borrow); + } + printf("\n"); + } + return 0; +} + +void +_Py_stackref_report_leaks(PyInterpreterState *interp) +{ + int leak = 0; + _Py_hashtable_foreach(interp->stackref_debug_table, report_leak, &leak); + if (leak) { + Py_FatalError("Stackrefs leaked."); + } +} + +#endif From 5a584c8f54bbeceae7ffa501291e29b7ddc8a0b9 Mon Sep 17 00:00:00 2001 From: shallow-beach <96891913+shallow-beach@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:09:56 -0800 Subject: [PATCH 081/376] Python Tutorial typo fix (#128077) --- Doc/tutorial/classes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/tutorial/classes.rst b/Doc/tutorial/classes.rst index 492568961d8a51..9d0fab8861d2a9 100644 --- a/Doc/tutorial/classes.rst +++ b/Doc/tutorial/classes.rst @@ -325,7 +325,7 @@ Now what can we do with instance objects? The only operations understood by instance objects are attribute references. There are two kinds of valid attribute names: data attributes and methods. -*data attributes* correspond to "instance variables" in Smalltalk, and to "data +*Data attributes* correspond to "instance variables" in Smalltalk, and to "data members" in C++. Data attributes need not be declared; like local variables, they spring into existence when they are first assigned to. For example, if ``x`` is the instance of :class:`!MyClass` created above, the following piece of From 3879ca0100942ae15a09ac22889cbe3e46d424eb Mon Sep 17 00:00:00 2001 From: Nico-Posada <102486290+Nico-Posada@users.noreply.github.com> Date: Fri, 20 Dec 2024 13:20:31 -0600 Subject: [PATCH 082/376] gh-128049: Fix type confusion bug with the return value of a custom ExceptionGroup split function (#128079) --- Lib/test/test_except_star.py | 43 +++++++++++++++++++ ...-12-18-14-22-48.gh-issue-128079.SUD5le.rst | 5 +++ Python/ceval.c | 21 ++++++++- 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-18-14-22-48.gh-issue-128079.SUD5le.rst diff --git a/Lib/test/test_except_star.py b/Lib/test/test_except_star.py index c49c6008e08e8c..284907f61213f8 100644 --- a/Lib/test/test_except_star.py +++ b/Lib/test/test_except_star.py @@ -952,6 +952,49 @@ def derive(self, excs): self.assertExceptionIsLike(tes, FalsyEG("eg", [TypeError(1)])) self.assertExceptionIsLike(ves, FalsyEG("eg", [ValueError(2)])) + def test_exception_group_subclass_with_bad_split_func(self): + # see gh-128049. + class BadEG1(ExceptionGroup): + def split(self, *args): + return "NOT A 2-TUPLE!" + + class BadEG2(ExceptionGroup): + def split(self, *args): + return ("NOT A 2-TUPLE!",) + + eg_list = [ + (BadEG1("eg", [OSError(123), ValueError(456)]), + r"split must return a tuple, not str"), + (BadEG2("eg", [OSError(123), ValueError(456)]), + r"split must return a 2-tuple, got tuple of size 1") + ] + + for eg_class, msg in eg_list: + with self.assertRaisesRegex(TypeError, msg) as m: + try: + raise eg_class + except* ValueError: + pass + except* OSError: + pass + + self.assertExceptionIsLike(m.exception.__context__, eg_class) + + # we allow tuples of length > 2 for backwards compatibility + class WeirdEG(ExceptionGroup): + def split(self, *args): + return super().split(*args) + ("anything", 123456, None) + + try: + raise WeirdEG("eg", [OSError(123), ValueError(456)]) + except* OSError as e: + oeg = e + except* ValueError as e: + veg = e + + self.assertExceptionIsLike(oeg, WeirdEG("eg", [OSError(123)])) + self.assertExceptionIsLike(veg, WeirdEG("eg", [ValueError(456)])) + class TestExceptStarCleanup(ExceptStarTest): def test_sys_exception_restored(self): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-18-14-22-48.gh-issue-128079.SUD5le.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-18-14-22-48.gh-issue-128079.SUD5le.rst new file mode 100644 index 00000000000000..8da4e677f068a3 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-18-14-22-48.gh-issue-128079.SUD5le.rst @@ -0,0 +1,5 @@ +Fix a bug where :keyword:`except* ` does not properly check the +return value of an :exc:`ExceptionGroup`'s :meth:`~BaseExceptionGroup.split` +function, leading to a crash in some cases. Now when :meth:`~BaseExceptionGroup.split` +returns an invalid object, :keyword:`except* ` raises a :exc:`TypeError` +with the original raised :exc:`ExceptionGroup` object chained to it. diff --git a/Python/ceval.c b/Python/ceval.c index 3cf11b663c571b..e92a11b16cec81 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2134,8 +2134,25 @@ _PyEval_ExceptionGroupMatch(PyObject* exc_value, PyObject *match_type, if (pair == NULL) { return -1; } - assert(PyTuple_CheckExact(pair)); - assert(PyTuple_GET_SIZE(pair) == 2); + + if (!PyTuple_CheckExact(pair)) { + PyErr_Format(PyExc_TypeError, + "%.200s.split must return a tuple, not %.200s", + Py_TYPE(exc_value)->tp_name, Py_TYPE(pair)->tp_name); + Py_DECREF(pair); + return -1; + } + + // allow tuples of length > 2 for backwards compatibility + if (PyTuple_GET_SIZE(pair) < 2) { + PyErr_Format(PyExc_TypeError, + "%.200s.split must return a 2-tuple, " + "got tuple of size %zd", + Py_TYPE(exc_value)->tp_name, PyTuple_GET_SIZE(pair)); + Py_DECREF(pair); + return -1; + } + *match = Py_NewRef(PyTuple_GET_ITEM(pair, 0)); *rest = Py_NewRef(PyTuple_GET_ITEM(pair, 1)); Py_DECREF(pair); From 2a66dd33dfc0b845042da9bb54aaa4e890733f54 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 20 Dec 2024 20:40:58 +0100 Subject: [PATCH 083/376] gh-112328: Make EnumDict usable on its own and document it (GH-123669) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rafi Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) Co-authored-by: Ethan Furman --- Doc/library/enum.rst | 26 ++++++++++----- Doc/whatsnew/3.13.rst | 6 ++-- Lib/enum.py | 10 +++--- Lib/test/test_enum.py | 33 ++++++++++++++++++- ...-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst | 1 + 5 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 51292a11f507c4..8ca949368db4ff 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -110,6 +110,10 @@ Module Contents ``KEEP`` which allows for more fine-grained control over how invalid values are dealt with in an enumeration. + :class:`EnumDict` + + A subclass of :class:`dict` for use when subclassing :class:`EnumType`. + :class:`auto` Instances are replaced with an appropriate value for Enum members. @@ -149,14 +153,10 @@ Module Contents Return a list of all power-of-two integers contained in a flag. - :class:`EnumDict` - - A subclass of :class:`dict` for use when subclassing :class:`EnumType`. - .. versionadded:: 3.6 ``Flag``, ``IntFlag``, ``auto`` .. versionadded:: 3.11 ``StrEnum``, ``EnumCheck``, ``ReprEnum``, ``FlagBoundary``, ``property``, ``member``, ``nonmember``, ``global_enum``, ``show_flag_values`` -.. versionadded:: 3.14 ``EnumDict`` +.. versionadded:: 3.13 ``EnumDict`` --------------- @@ -830,13 +830,23 @@ Data Types .. class:: EnumDict - *EnumDict* is a subclass of :class:`dict` for use when subclassing :class:`EnumType`. + *EnumDict* is a subclass of :class:`dict` that is used as the namespace + for defining enum classes (see :ref:`prepare`). + It is exposed to allow subclasses of :class:`EnumType` with advanced + behavior like having multiple values per member. + It should be called with the name of the enum class being created, otherwise + private names and internal classes will not be handled correctly. + + Note that only the :class:`~collections.abc.MutableMapping` interface + (:meth:`~object.__setitem__` and :meth:`~dict.update`) is overridden. + It may be possible to bypass the checks using other :class:`!dict` + operations like :meth:`|= `. .. attribute:: EnumDict.member_names - Return list of member names. + A list of member names. - .. versionadded:: 3.14 + .. versionadded:: 3.13 --------------- diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index a291122aefc2ce..c8e0f94f4246fb 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -879,11 +879,13 @@ email (Contributed by Thomas Dwyer and Victor Stinner for :gh:`102988` to improve the :cve:`2023-27043` fix.) + enum ---- -* :class:`~enum.EnumDict` has been made public in :mod:`enum` to better support - subclassing :class:`~enum.EnumType`. +* :class:`~enum.EnumDict` has been made public to better support subclassing + :class:`~enum.EnumType`. + fractions --------- diff --git a/Lib/enum.py b/Lib/enum.py index ccc1da42206474..04443471b40bff 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -342,12 +342,13 @@ class EnumDict(dict): EnumType will use the names found in self._member_names as the enumeration member names. """ - def __init__(self): + def __init__(self, cls_name=None): super().__init__() self._member_names = {} # use a dict -- faster look-up than a list, and keeps insertion order since 3.7 self._last_values = [] self._ignore = [] self._auto_called = False + self._cls_name = cls_name def __setitem__(self, key, value): """ @@ -358,7 +359,7 @@ def __setitem__(self, key, value): Single underscore (sunder) names are reserved. """ - if _is_private(self._cls_name, key): + if self._cls_name is not None and _is_private(self._cls_name, key): # do nothing, name will be a normal attribute pass elif _is_sunder(key): @@ -406,7 +407,7 @@ def __setitem__(self, key, value): value = value.value elif _is_descriptor(value): pass - elif _is_internal_class(self._cls_name, value): + elif self._cls_name is not None and _is_internal_class(self._cls_name, value): # do nothing, name will be a normal attribute pass else: @@ -478,8 +479,7 @@ def __prepare__(metacls, cls, bases, **kwds): # check that previous enum members do not exist metacls._check_for_existing_members_(cls, bases) # create the namespace dict - enum_dict = EnumDict() - enum_dict._cls_name = cls + enum_dict = EnumDict(cls) # inherit previous flags and _generate_next_value_ function member_type, first_enum = metacls._get_mixins_(cls, bases) if first_enum is not None: diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index b9e13fb8c3585e..8884295b1ab89c 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -14,7 +14,7 @@ from enum import Enum, EnumMeta, IntEnum, StrEnum, EnumType, Flag, IntFlag, unique, auto from enum import STRICT, CONFORM, EJECT, KEEP, _simple_enum, _test_simple_enum from enum import verify, UNIQUE, CONTINUOUS, NAMED_FLAGS, ReprEnum -from enum import member, nonmember, _iter_bits_lsb +from enum import member, nonmember, _iter_bits_lsb, EnumDict from io import StringIO from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL from test import support @@ -5440,6 +5440,37 @@ def test_convert_repr_and_str(self): self.assertEqual(format(test_type.CONVERT_STRING_TEST_NAME_A), '5') +class TestEnumDict(unittest.TestCase): + def test_enum_dict_in_metaclass(self): + """Test that EnumDict is usable as a class namespace""" + class Meta(type): + @classmethod + def __prepare__(metacls, cls, bases, **kwds): + return EnumDict(cls) + + class MyClass(metaclass=Meta): + a = 1 + + with self.assertRaises(TypeError): + a = 2 # duplicate + + with self.assertRaises(ValueError): + _a_sunder_ = 3 + + def test_enum_dict_standalone(self): + """Test that EnumDict is usable on its own""" + enumdict = EnumDict() + enumdict['a'] = 1 + + with self.assertRaises(TypeError): + enumdict['a'] = 'other value' + + # Only MutableMapping interface is overridden for now. + # If this stops passing, update the documentation. + enumdict |= {'a': 'other value'} + self.assertEqual(enumdict['a'], 'other value') + + # helpers def enum_dir(cls): diff --git a/Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst b/Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst new file mode 100644 index 00000000000000..96da94a9f211af --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst @@ -0,0 +1 @@ +:class:`enum.EnumDict` can now be used without resorting to private API. From a959ea1b0a026ff118975b9b539513b06dde3190 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sun, 22 Dec 2024 01:17:59 +0000 Subject: [PATCH 084/376] GH-127807: pathlib ABCs: remove `PurePathBase._raw_paths` (#127883) Remove the `PurePathBase` initializer, and make `with_segments()` and `__str__()` abstract. This allows us to drop the `_raw_paths` attribute, and also the `Parser.join()` protocol method. --- Lib/pathlib/_abc.py | 40 +++-------- Lib/pathlib/_local.py | 37 ++++++++-- Lib/pathlib/_types.py | 1 - Lib/test/test_pathlib/test_pathlib.py | 25 +++++++ Lib/test/test_pathlib/test_pathlib_abc.py | 85 ++++++++--------------- 5 files changed, 92 insertions(+), 96 deletions(-) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index b4560295300c28..4402efe3a02310 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -44,49 +44,25 @@ class PurePathBase: """Base class for pure path objects. This class *does not* provide several magic methods that are defined in - its subclass PurePath. They are: __fspath__, __bytes__, __reduce__, - __hash__, __eq__, __lt__, __le__, __gt__, __ge__. Its initializer and path - joining methods accept only strings, not os.PathLike objects more broadly. + its subclass PurePath. They are: __init__, __fspath__, __bytes__, + __reduce__, __hash__, __eq__, __lt__, __le__, __gt__, __ge__. """ - __slots__ = ( - # The `_raw_paths` slot stores unjoined string paths. This is set in - # the `__init__()` method. - '_raw_paths', - ) + __slots__ = () parser = posixpath _globber = PathGlobber - def __init__(self, *args): - for arg in args: - if not isinstance(arg, str): - raise TypeError( - f"argument should be a str, not {type(arg).__name__!r}") - self._raw_paths = list(args) - def with_segments(self, *pathsegments): """Construct a new path object from any number of path-like objects. Subclasses may override this method to customize how new path objects are created from methods like `iterdir()`. """ - return type(self)(*pathsegments) + raise NotImplementedError def __str__(self): """Return the string representation of the path, suitable for passing to system calls.""" - paths = self._raw_paths - if len(paths) == 1: - return paths[0] - elif paths: - # Join path segments from the initializer. - path = self.parser.join(*paths) - # Cache the joined path. - paths.clear() - paths.append(path) - return path - else: - paths.append('') - return '' + raise NotImplementedError def as_posix(self): """Return the string representation of the path with forward (/) @@ -234,17 +210,17 @@ def joinpath(self, *pathsegments): paths) or a totally different path (if one of the arguments is anchored). """ - return self.with_segments(*self._raw_paths, *pathsegments) + return self.with_segments(str(self), *pathsegments) def __truediv__(self, key): try: - return self.with_segments(*self._raw_paths, key) + return self.with_segments(str(self), key) except TypeError: return NotImplemented def __rtruediv__(self, key): try: - return self.with_segments(key, *self._raw_paths) + return self.with_segments(key, str(self)) except TypeError: return NotImplemented diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index b933dd512eeb28..7689c10604d4e6 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -77,6 +77,10 @@ class PurePath(PurePathBase): """ __slots__ = ( + # The `_raw_paths` slot stores unjoined string paths. This is set in + # the `__init__()` method. + '_raw_paths', + # The `_drv`, `_root` and `_tail_cached` slots store parsed and # normalized parts of the path. They are set when any of the `drive`, # `root` or `_tail` properties are accessed for the first time. The @@ -140,9 +144,15 @@ def __init__(self, *args): "object where __fspath__ returns a str, " f"not {type(path).__name__!r}") paths.append(path) - # Avoid calling super().__init__, as an optimisation self._raw_paths = paths + def with_segments(self, *pathsegments): + """Construct a new path object from any number of path-like objects. + Subclasses may override this method to customize how new path objects + are created from methods like `iterdir()`. + """ + return type(self)(*pathsegments) + def joinpath(self, *pathsegments): """Combine this path with one or several arguments, and return a new path representing either a subpath (if all arguments are relative @@ -304,14 +314,29 @@ def _parse_pattern(cls, pattern): parts.append('') return parts + @property + def _raw_path(self): + paths = self._raw_paths + if len(paths) == 1: + return paths[0] + elif paths: + # Join path segments from the initializer. + path = self.parser.join(*paths) + # Cache the joined path. + paths.clear() + paths.append(path) + return path + else: + paths.append('') + return '' + @property def drive(self): """The drive prefix (letter or UNC path), if any.""" try: return self._drv except AttributeError: - raw_path = PurePathBase.__str__(self) - self._drv, self._root, self._tail_cached = self._parse_path(raw_path) + self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path) return self._drv @property @@ -320,8 +345,7 @@ def root(self): try: return self._root except AttributeError: - raw_path = PurePathBase.__str__(self) - self._drv, self._root, self._tail_cached = self._parse_path(raw_path) + self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path) return self._root @property @@ -329,8 +353,7 @@ def _tail(self): try: return self._tail_cached except AttributeError: - raw_path = PurePathBase.__str__(self) - self._drv, self._root, self._tail_cached = self._parse_path(raw_path) + self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path) return self._tail_cached @property diff --git a/Lib/pathlib/_types.py b/Lib/pathlib/_types.py index 60df94d0b46049..baa4a7e2af5b68 100644 --- a/Lib/pathlib/_types.py +++ b/Lib/pathlib/_types.py @@ -14,7 +14,6 @@ class Parser(Protocol): """ sep: str - def join(self, path: str, *paths: str) -> str: ... def split(self, path: str) -> tuple[str, str]: ... def splitdrive(self, path: str) -> tuple[str, str]: ... def splitext(self, path: str) -> tuple[str, str]: ... diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index ac3a3b4f15c10e..ef482c311542fa 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -229,6 +229,31 @@ def test_fspath_common(self): self._check_str(p.__fspath__(), ('a/b',)) self._check_str(os.fspath(p), ('a/b',)) + def test_bytes(self): + P = self.cls + with self.assertRaises(TypeError): + P(b'a') + with self.assertRaises(TypeError): + P(b'a', 'b') + with self.assertRaises(TypeError): + P('a', b'b') + with self.assertRaises(TypeError): + P('a').joinpath(b'b') + with self.assertRaises(TypeError): + P('a') / b'b' + with self.assertRaises(TypeError): + b'a' / P('b') + with self.assertRaises(TypeError): + P('a').match(b'b') + with self.assertRaises(TypeError): + P('a').relative_to(b'b') + with self.assertRaises(TypeError): + P('a').with_name(b'b') + with self.assertRaises(TypeError): + P('a').with_stem(b'b') + with self.assertRaises(TypeError): + P('a').with_suffix(b'b') + def test_bytes_exc_message(self): P = self.cls message = (r"argument should be a str or an os\.PathLike object " diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index e230dd188799a5..9198a0cbc45cee 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -53,7 +53,15 @@ def test_parser(self): class DummyPurePath(PurePathBase): - __slots__ = () + __slots__ = ('_segments',) + + def __init__(self, *segments): + self._segments = segments + + def __str__(self): + if self._segments: + return self.parser.join(*self._segments) + return '' def __eq__(self, other): if not isinstance(other, DummyPurePath): @@ -66,6 +74,9 @@ def __hash__(self): def __repr__(self): return "{}({!r})".format(self.__class__.__name__, self.as_posix()) + def with_segments(self, *pathsegments): + return type(self)(*pathsegments) + class DummyPurePathTest(unittest.TestCase): cls = DummyPurePath @@ -97,30 +108,11 @@ def test_constructor_common(self): P('a/b/c') P('/a/b/c') - def test_bytes(self): - P = self.cls - with self.assertRaises(TypeError): - P(b'a') - with self.assertRaises(TypeError): - P(b'a', 'b') - with self.assertRaises(TypeError): - P('a', b'b') - with self.assertRaises(TypeError): - P('a').joinpath(b'b') - with self.assertRaises(TypeError): - P('a') / b'b' - with self.assertRaises(TypeError): - b'a' / P('b') - with self.assertRaises(TypeError): - P('a').match(b'b') - with self.assertRaises(TypeError): - P('a').relative_to(b'b') - with self.assertRaises(TypeError): - P('a').with_name(b'b') - with self.assertRaises(TypeError): - P('a').with_stem(b'b') - with self.assertRaises(TypeError): - P('a').with_suffix(b'b') + def test_fspath_common(self): + self.assertRaises(TypeError, os.fspath, self.cls('')) + + def test_as_bytes_common(self): + self.assertRaises(TypeError, bytes, self.cls('')) def _check_str_subclass(self, *args): # Issue #21127: it should be possible to construct a PurePath object @@ -1286,36 +1278,6 @@ def test_is_absolute_windows(self): # Tests for the virtual classes. # -class PathBaseTest(PurePathBaseTest): - cls = PathBase - - def test_not_implemented_error(self): - p = self.cls('') - e = NotImplementedError - self.assertRaises(e, p.stat) - self.assertRaises(e, p.exists) - self.assertRaises(e, p.is_dir) - self.assertRaises(e, p.is_file) - self.assertRaises(e, p.is_symlink) - self.assertRaises(e, p.open) - self.assertRaises(e, p.read_bytes) - self.assertRaises(e, p.read_text) - self.assertRaises(e, p.write_bytes, b'foo') - self.assertRaises(e, p.write_text, 'foo') - self.assertRaises(e, p.iterdir) - self.assertRaises(e, lambda: list(p.glob('*'))) - self.assertRaises(e, lambda: list(p.rglob('*'))) - self.assertRaises(e, lambda: list(p.walk())) - self.assertRaises(e, p.readlink) - self.assertRaises(e, p.symlink_to, 'foo') - self.assertRaises(e, p.mkdir) - - def test_fspath_common(self): - self.assertRaises(TypeError, os.fspath, self.cls('')) - - def test_as_bytes_common(self): - self.assertRaises(TypeError, bytes, self.cls('')) - class DummyPathIO(io.BytesIO): """ @@ -1342,11 +1304,19 @@ class DummyPath(PathBase): Simple implementation of PathBase that keeps files and directories in memory. """ - __slots__ = () + __slots__ = ('_segments') _files = {} _directories = {} + def __init__(self, *segments): + self._segments = segments + + def __str__(self): + if self._segments: + return self.parser.join(*self._segments) + return '' + def __eq__(self, other): if not isinstance(other, DummyPath): return NotImplemented @@ -1358,6 +1328,9 @@ def __hash__(self): def __repr__(self): return "{}({!r})".format(self.__class__.__name__, self.as_posix()) + def with_segments(self, *pathsegments): + return type(self)(*pathsegments) + def stat(self, *, follow_symlinks=True): path = str(self).rstrip('/') if path in self._files: From f5ba74b81979b621e38be70ec3ddad1e7f1365ce Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sun, 22 Dec 2024 01:41:38 +0000 Subject: [PATCH 085/376] GH-127807: pathlib ABCs: remove a few private attributes (#127851) From `PurePathBase` delete `_globber`, `_stack` and `_pattern_str`, and from `PathBase` delete `_glob_selector`. This helps avoid an unpleasant surprise for a users who try to use these names. --- Lib/pathlib/_abc.py | 86 +++++++++++++++++++------------------------ Lib/pathlib/_local.py | 34 +++++++++++++---- 2 files changed, 64 insertions(+), 56 deletions(-) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 4402efe3a02310..b521c757561a99 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -25,6 +25,23 @@ def _is_case_sensitive(parser): return parser.normcase('Aa') == 'Aa' +def _explode_path(path): + """ + Split the path into a 2-tuple (anchor, parts), where *anchor* is the + uppermost parent of the path (equivalent to path.parents[-1]), and + *parts* is a reversed list of parts following the anchor. + """ + split = path.parser.split + path = str(path) + parent, name = split(path) + names = [] + while path != parent: + names.append(name) + path = parent + parent, name = split(path) + return path, names + + class PathGlobber(_GlobberBase): """ Class providing shell-style globbing for path objects. @@ -50,7 +67,6 @@ class PurePathBase: __slots__ = () parser = posixpath - _globber = PathGlobber def with_segments(self, *pathsegments): """Construct a new path object from any number of path-like objects. @@ -82,7 +98,7 @@ def root(self): @property def anchor(self): """The concatenation of the drive and root, or ''.""" - return self._stack[0] + return _explode_path(self)[0] @property def name(self): @@ -160,8 +176,8 @@ def relative_to(self, other, *, walk_up=False): """ if not isinstance(other, PurePathBase): other = self.with_segments(other) - anchor0, parts0 = self._stack - anchor1, parts1 = other._stack + anchor0, parts0 = _explode_path(self) + anchor1, parts1 = _explode_path(other) if anchor0 != anchor1: raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors") while parts0 and parts1 and parts0[-1] == parts1[-1]: @@ -183,8 +199,8 @@ def is_relative_to(self, other): """ if not isinstance(other, PurePathBase): other = self.with_segments(other) - anchor0, parts0 = self._stack - anchor1, parts1 = other._stack + anchor0, parts0 = _explode_path(self) + anchor1, parts1 = _explode_path(other) if anchor0 != anchor1: return False while parts0 and parts1 and parts0[-1] == parts1[-1]: @@ -199,7 +215,7 @@ def is_relative_to(self, other): def parts(self): """An object providing sequence-like access to the components in the filesystem path.""" - anchor, parts = self._stack + anchor, parts = _explode_path(self) if anchor: parts.append(anchor) return tuple(reversed(parts)) @@ -224,23 +240,6 @@ def __rtruediv__(self, key): except TypeError: return NotImplemented - @property - def _stack(self): - """ - Split the path into a 2-tuple (anchor, parts), where *anchor* is the - uppermost parent of the path (equivalent to path.parents[-1]), and - *parts* is a reversed list of parts following the anchor. - """ - split = self.parser.split - path = str(self) - parent, name = split(path) - names = [] - while path != parent: - names.append(name) - path = parent - parent, name = split(path) - return path, names - @property def parent(self): """The logical parent of the path.""" @@ -268,11 +267,6 @@ def is_absolute(self): a drive).""" return self.parser.isabs(str(self)) - @property - def _pattern_str(self): - """The path expressed as a string, for use in pattern-matching.""" - return str(self) - def match(self, path_pattern, *, case_sensitive=None): """ Return True if this path matches the given pattern. If the pattern is @@ -293,7 +287,7 @@ def match(self, path_pattern, *, case_sensitive=None): return False if len(path_parts) > len(pattern_parts) and path_pattern.anchor: return False - globber = self._globber(sep, case_sensitive) + globber = PathGlobber(sep, case_sensitive) for path_part, pattern_part in zip(path_parts, pattern_parts): match = globber.compile(pattern_part) if match(path_part) is None: @@ -309,9 +303,9 @@ def full_match(self, pattern, *, case_sensitive=None): pattern = self.with_segments(pattern) if case_sensitive is None: case_sensitive = _is_case_sensitive(self.parser) - globber = self._globber(pattern.parser.sep, case_sensitive, recursive=True) - match = globber.compile(pattern._pattern_str) - return match(self._pattern_str) is not None + globber = PathGlobber(pattern.parser.sep, case_sensitive, recursive=True) + match = globber.compile(str(pattern)) + return match(str(self)) is not None @@ -463,29 +457,25 @@ def iterdir(self): """ raise NotImplementedError - def _glob_selector(self, parts, case_sensitive, recurse_symlinks): - if case_sensitive is None: - case_sensitive = _is_case_sensitive(self.parser) - case_pedantic = False - else: - # The user has expressed a case sensitivity choice, but we don't - # know the case sensitivity of the underlying filesystem, so we - # must use scandir() for everything, including non-wildcard parts. - case_pedantic = True - recursive = True if recurse_symlinks else _no_recurse_symlinks - globber = self._globber(self.parser.sep, case_sensitive, case_pedantic, recursive) - return globber.selector(parts) - def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=True): """Iterate over this subtree and yield all existing files (of any kind, including directories) matching the given relative pattern. """ if not isinstance(pattern, PurePathBase): pattern = self.with_segments(pattern) - anchor, parts = pattern._stack + anchor, parts = _explode_path(pattern) if anchor: raise NotImplementedError("Non-relative patterns are unsupported") - select = self._glob_selector(parts, case_sensitive, recurse_symlinks) + if case_sensitive is None: + case_sensitive = _is_case_sensitive(self.parser) + case_pedantic = False + elif case_sensitive == _is_case_sensitive(self.parser): + case_pedantic = False + else: + case_pedantic = True + recursive = True if recurse_symlinks else _no_recurse_symlinks + globber = PathGlobber(self.parser.sep, case_sensitive, case_pedantic, recursive) + select = globber.selector(parts) return select(self) def rglob(self, pattern, *, case_sensitive=None, recurse_symlinks=True): diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index 7689c10604d4e6..4897149d7e8a8e 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -5,7 +5,7 @@ import posixpath import sys from errno import EINVAL, EXDEV -from glob import _StringGlobber +from glob import _StringGlobber, _no_recurse_symlinks from itertools import chain from stat import S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO from _collections_abc import Sequence @@ -112,7 +112,6 @@ class PurePath(PurePathBase): '_hash', ) parser = os.path - _globber = _StringGlobber def __new__(cls, *args, **kwargs): """Construct a PurePath from one or several strings and or existing @@ -513,13 +512,22 @@ def as_uri(self): from urllib.parse import quote_from_bytes return prefix + quote_from_bytes(os.fsencode(path)) - @property - def _pattern_str(self): - """The path expressed as a string, for use in pattern-matching.""" + def full_match(self, pattern, *, case_sensitive=None): + """ + Return True if this path matches the given glob-style pattern. The + pattern is matched against the entire path. + """ + if not isinstance(pattern, PurePathBase): + pattern = self.with_segments(pattern) + if case_sensitive is None: + case_sensitive = self.parser is posixpath + # The string representation of an empty path is a single dot ('.'). Empty # paths shouldn't match wildcards, so we change it to the empty string. - path_str = str(self) - return '' if path_str == '.' else path_str + path = str(self) if self.parts else '' + pattern = str(pattern) if pattern.parts else '' + globber = _StringGlobber(self.parser.sep, case_sensitive, recursive=True) + return globber.compile(pattern)(path) is not None # Subclassing os.PathLike makes isinstance() checks slower, # which in turn makes Path construction slower. Register instead! @@ -749,8 +757,18 @@ def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=False): kind, including directories) matching the given relative pattern. """ sys.audit("pathlib.Path.glob", self, pattern) + if case_sensitive is None: + case_sensitive = self.parser is posixpath + case_pedantic = False + else: + # The user has expressed a case sensitivity choice, but we don't + # know the case sensitivity of the underlying filesystem, so we + # must use scandir() for everything, including non-wildcard parts. + case_pedantic = True parts = self._parse_pattern(pattern) - select = self._glob_selector(parts[::-1], case_sensitive, recurse_symlinks) + recursive = True if recurse_symlinks else _no_recurse_symlinks + globber = _StringGlobber(self.parser.sep, case_sensitive, case_pedantic, recursive) + select = globber.selector(parts[::-1]) root = str(self) paths = select(root) From 8d9f52a7be5c09c0fd4423943edadaacf6d7f917 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sun, 22 Dec 2024 02:22:08 +0000 Subject: [PATCH 086/376] GH-127807: pathlib ABCs: move private copying methods to dedicated class (#127810) Move 9 private `PathBase` attributes and methods into a new `CopyWorker` class. Change `PathBase.copy` from a method to a `CopyWorker` instance. The methods remain private in the `CopyWorker` class. In future we might make some/all of them public so that user subclasses of `PathBase` can customize the copying process (in particular reading/writing of metadata,) but we'd need to make `PathBase` public first. --- Lib/pathlib/_abc.py | 228 +++++++++++++++++++++++------------------- Lib/pathlib/_local.py | 183 ++++++++++++++++++++++++--------- Lib/pathlib/_os.py | 98 ------------------ 3 files changed, 261 insertions(+), 248 deletions(-) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index b521c757561a99..6acc29ebab2bc5 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -57,6 +57,132 @@ def concat_path(path, text): return path.with_segments(str(path) + text) +class CopyWorker: + """ + Class that implements copying between path objects. An instance of this + class is available from the PathBase.copy property; it's made callable so + that PathBase.copy() can be treated as a method. + + The target path's CopyWorker drives the process from its _create() method. + Files and directories are exchanged by calling methods on the source and + target paths, and metadata is exchanged by calling + source.copy._read_metadata() and target.copy._write_metadata(). + """ + __slots__ = ('_path',) + + def __init__(self, path): + self._path = path + + def __call__(self, target, follow_symlinks=True, dirs_exist_ok=False, + preserve_metadata=False): + """ + Recursively copy this file or directory tree to the given destination. + """ + if not isinstance(target, PathBase): + target = self._path.with_segments(target) + + # Delegate to the target path's CopyWorker object. + return target.copy._create(self._path, follow_symlinks, dirs_exist_ok, preserve_metadata) + + _readable_metakeys = frozenset() + + def _read_metadata(self, metakeys, *, follow_symlinks=True): + """ + Returns path metadata as a dict with string keys. + """ + raise NotImplementedError + + _writable_metakeys = frozenset() + + def _write_metadata(self, metadata, *, follow_symlinks=True): + """ + Sets path metadata from the given dict with string keys. + """ + raise NotImplementedError + + def _create(self, source, follow_symlinks, dirs_exist_ok, preserve_metadata): + self._ensure_distinct_path(source) + if preserve_metadata: + metakeys = self._writable_metakeys & source.copy._readable_metakeys + else: + metakeys = None + if not follow_symlinks and source.is_symlink(): + self._create_symlink(source, metakeys) + elif source.is_dir(): + self._create_dir(source, metakeys, follow_symlinks, dirs_exist_ok) + else: + self._create_file(source, metakeys) + return self._path + + def _create_dir(self, source, metakeys, follow_symlinks, dirs_exist_ok): + """Copy the given directory to our path.""" + children = list(source.iterdir()) + self._path.mkdir(exist_ok=dirs_exist_ok) + for src in children: + dst = self._path.joinpath(src.name) + if not follow_symlinks and src.is_symlink(): + dst.copy._create_symlink(src, metakeys) + elif src.is_dir(): + dst.copy._create_dir(src, metakeys, follow_symlinks, dirs_exist_ok) + else: + dst.copy._create_file(src, metakeys) + if metakeys: + metadata = source.copy._read_metadata(metakeys) + if metadata: + self._write_metadata(metadata) + + def _create_file(self, source, metakeys): + """Copy the given file to our path.""" + self._ensure_different_file(source) + with source.open('rb') as source_f: + try: + with self._path.open('wb') as target_f: + copyfileobj(source_f, target_f) + except IsADirectoryError as e: + if not self._path.exists(): + # Raise a less confusing exception. + raise FileNotFoundError( + f'Directory does not exist: {self._path}') from e + raise + if metakeys: + metadata = source.copy._read_metadata(metakeys) + if metadata: + self._write_metadata(metadata) + + def _create_symlink(self, source, metakeys): + """Copy the given symbolic link to our path.""" + self._path.symlink_to(source.readlink()) + if metakeys: + metadata = source.copy._read_metadata(metakeys, follow_symlinks=False) + if metadata: + self._write_metadata(metadata, follow_symlinks=False) + + def _ensure_different_file(self, source): + """ + Raise OSError(EINVAL) if both paths refer to the same file. + """ + pass + + def _ensure_distinct_path(self, source): + """ + Raise OSError(EINVAL) if the other path is within this path. + """ + # Note: there is no straightforward, foolproof algorithm to determine + # if one directory is within another (a particularly perverse example + # would be a single network share mounted in one location via NFS, and + # in another location via CIFS), so we simply checks whether the + # other path is lexically equal to, or within, this path. + if source == self._path: + err = OSError(EINVAL, "Source and target are the same path") + elif source in self._path.parents: + err = OSError(EINVAL, "Source path is a parent of target path") + else: + return + err.filename = str(source) + err.filename2 = str(self._path) + raise err + + class PurePathBase: """Base class for pure path objects. @@ -374,31 +500,6 @@ def is_symlink(self): except (OSError, ValueError): return False - def _ensure_different_file(self, other_path): - """ - Raise OSError(EINVAL) if both paths refer to the same file. - """ - pass - - def _ensure_distinct_path(self, other_path): - """ - Raise OSError(EINVAL) if the other path is within this path. - """ - # Note: there is no straightforward, foolproof algorithm to determine - # if one directory is within another (a particularly perverse example - # would be a single network share mounted in one location via NFS, and - # in another location via CIFS), so we simply checks whether the - # other path is lexically equal to, or within, this path. - if self == other_path: - err = OSError(EINVAL, "Source and target are the same path") - elif self in other_path.parents: - err = OSError(EINVAL, "Source path is a parent of target path") - else: - return - err.filename = str(self) - err.filename2 = str(other_path) - raise err - def open(self, mode='r', buffering=-1, encoding=None, errors=None, newline=None): """ @@ -537,88 +638,13 @@ def symlink_to(self, target, target_is_directory=False): """ raise NotImplementedError - def _symlink_to_target_of(self, link): - """ - Make this path a symlink with the same target as the given link. This - is used by copy(). - """ - self.symlink_to(link.readlink()) - def mkdir(self, mode=0o777, parents=False, exist_ok=False): """ Create a new directory at this given path. """ raise NotImplementedError - # Metadata keys supported by this path type. - _readable_metadata = _writable_metadata = frozenset() - - def _read_metadata(self, keys=None, *, follow_symlinks=True): - """ - Returns path metadata as a dict with string keys. - """ - raise NotImplementedError - - def _write_metadata(self, metadata, *, follow_symlinks=True): - """ - Sets path metadata from the given dict with string keys. - """ - raise NotImplementedError - - def _copy_metadata(self, target, *, follow_symlinks=True): - """ - Copies metadata (permissions, timestamps, etc) from this path to target. - """ - # Metadata types supported by both source and target. - keys = self._readable_metadata & target._writable_metadata - if keys: - metadata = self._read_metadata(keys, follow_symlinks=follow_symlinks) - target._write_metadata(metadata, follow_symlinks=follow_symlinks) - - def _copy_file(self, target): - """ - Copy the contents of this file to the given target. - """ - self._ensure_different_file(target) - with self.open('rb') as source_f: - try: - with target.open('wb') as target_f: - copyfileobj(source_f, target_f) - except IsADirectoryError as e: - if not target.exists(): - # Raise a less confusing exception. - raise FileNotFoundError( - f'Directory does not exist: {target}') from e - else: - raise - - def copy(self, target, *, follow_symlinks=True, dirs_exist_ok=False, - preserve_metadata=False): - """ - Recursively copy this file or directory tree to the given destination. - """ - if not isinstance(target, PathBase): - target = self.with_segments(target) - self._ensure_distinct_path(target) - stack = [(self, target)] - while stack: - src, dst = stack.pop() - if not follow_symlinks and src.is_symlink(): - dst._symlink_to_target_of(src) - if preserve_metadata: - src._copy_metadata(dst, follow_symlinks=False) - elif src.is_dir(): - children = src.iterdir() - dst.mkdir(exist_ok=dirs_exist_ok) - stack.extend((child, dst.joinpath(child.name)) - for child in children) - if preserve_metadata: - src._copy_metadata(dst) - else: - src._copy_file(dst) - if preserve_metadata: - src._copy_metadata(dst) - return target + copy = property(CopyWorker, doc=CopyWorker.__call__.__doc__) def copy_into(self, target_dir, *, follow_symlinks=True, dirs_exist_ok=False, preserve_metadata=False): diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index 4897149d7e8a8e..915402e6c65b29 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -4,10 +4,10 @@ import os import posixpath import sys -from errno import EINVAL, EXDEV +from errno import * from glob import _StringGlobber, _no_recurse_symlinks from itertools import chain -from stat import S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO +from stat import S_IMODE, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO from _collections_abc import Sequence try: @@ -19,9 +19,8 @@ except ImportError: grp = None -from pathlib._os import (copyfile, file_metadata_keys, read_file_metadata, - write_file_metadata) -from pathlib._abc import PurePathBase, PathBase +from pathlib._os import copyfile +from pathlib._abc import CopyWorker, PurePathBase, PathBase __all__ = [ @@ -66,6 +65,131 @@ def __repr__(self): return "<{}.parents>".format(type(self._path).__name__) +class _LocalCopyWorker(CopyWorker): + """This object implements the Path.copy callable. Don't try to construct + it yourself.""" + __slots__ = () + + _readable_metakeys = {'mode', 'times_ns'} + if hasattr(os.stat_result, 'st_flags'): + _readable_metakeys.add('flags') + if hasattr(os, 'listxattr'): + _readable_metakeys.add('xattrs') + _readable_metakeys = _writable_metakeys = frozenset(_readable_metakeys) + + def _read_metadata(self, metakeys, *, follow_symlinks=True): + metadata = {} + if 'mode' in metakeys or 'times_ns' in metakeys or 'flags' in metakeys: + st = self._path.stat(follow_symlinks=follow_symlinks) + if 'mode' in metakeys: + metadata['mode'] = S_IMODE(st.st_mode) + if 'times_ns' in metakeys: + metadata['times_ns'] = st.st_atime_ns, st.st_mtime_ns + if 'flags' in metakeys: + metadata['flags'] = st.st_flags + if 'xattrs' in metakeys: + try: + metadata['xattrs'] = [ + (attr, os.getxattr(self._path, attr, follow_symlinks=follow_symlinks)) + for attr in os.listxattr(self._path, follow_symlinks=follow_symlinks)] + except OSError as err: + if err.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES): + raise + return metadata + + def _write_metadata(self, metadata, *, follow_symlinks=True): + def _nop(*args, ns=None, follow_symlinks=None): + pass + + if follow_symlinks: + # use the real function if it exists + def lookup(name): + return getattr(os, name, _nop) + else: + # use the real function only if it exists + # *and* it supports follow_symlinks + def lookup(name): + fn = getattr(os, name, _nop) + if fn in os.supports_follow_symlinks: + return fn + return _nop + + times_ns = metadata.get('times_ns') + if times_ns is not None: + lookup("utime")(self._path, ns=times_ns, follow_symlinks=follow_symlinks) + # We must copy extended attributes before the file is (potentially) + # chmod()'ed read-only, otherwise setxattr() will error with -EACCES. + xattrs = metadata.get('xattrs') + if xattrs is not None: + for attr, value in xattrs: + try: + os.setxattr(self._path, attr, value, follow_symlinks=follow_symlinks) + except OSError as e: + if e.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES): + raise + mode = metadata.get('mode') + if mode is not None: + try: + lookup("chmod")(self._path, mode, follow_symlinks=follow_symlinks) + except NotImplementedError: + # if we got a NotImplementedError, it's because + # * follow_symlinks=False, + # * lchown() is unavailable, and + # * either + # * fchownat() is unavailable or + # * fchownat() doesn't implement AT_SYMLINK_NOFOLLOW. + # (it returned ENOSUP.) + # therefore we're out of options--we simply cannot chown the + # symlink. give up, suppress the error. + # (which is what shutil always did in this circumstance.) + pass + flags = metadata.get('flags') + if flags is not None: + try: + lookup("chflags")(self._path, flags, follow_symlinks=follow_symlinks) + except OSError as why: + if why.errno not in (EOPNOTSUPP, ENOTSUP): + raise + + if copyfile: + # Use fast OS routine for local file copying where available. + def _create_file(self, source, metakeys): + """Copy the given file to the given target.""" + try: + source = os.fspath(source) + except TypeError: + if not isinstance(source, PathBase): + raise + super()._create_file(source, metakeys) + else: + copyfile(source, os.fspath(self._path)) + + if os.name == 'nt': + # Windows: symlink target might not exist yet if we're copying several + # files, so ensure we pass is_dir to os.symlink(). + def _create_symlink(self, source, metakeys): + """Copy the given symlink to the given target.""" + self._path.symlink_to(source.readlink(), source.is_dir()) + if metakeys: + metadata = source.copy._read_metadata(metakeys, follow_symlinks=False) + if metadata: + self._write_metadata(metadata, follow_symlinks=False) + + def _ensure_different_file(self, source): + """ + Raise OSError(EINVAL) if both paths refer to the same file. + """ + try: + if not self._path.samefile(source): + return + except (OSError, ValueError): + return + err = OSError(EINVAL, "Source and target are the same file") + err.filename = str(source) + err.filename2 = str(self._path) + raise err + + class PurePath(PurePathBase): """Base class for manipulating paths without I/O. @@ -678,20 +802,6 @@ def samefile(self, other_path): return (st.st_ino == other_st.st_ino and st.st_dev == other_st.st_dev) - def _ensure_different_file(self, other_path): - """ - Raise OSError(EINVAL) if both paths refer to the same file. - """ - try: - if not self.samefile(other_path): - return - except (OSError, ValueError): - return - err = OSError(EINVAL, "Source and target are the same file") - err.filename = str(self) - err.filename2 = str(other_path) - raise err - def open(self, mode='r', buffering=-1, encoding=None, errors=None, newline=None): """ @@ -932,24 +1042,6 @@ def mkdir(self, mode=0o777, parents=False, exist_ok=False): if not exist_ok or not self.is_dir(): raise - _readable_metadata = _writable_metadata = file_metadata_keys - _read_metadata = read_file_metadata - _write_metadata = write_file_metadata - - if copyfile: - def _copy_file(self, target): - """ - Copy the contents of this file to the given target. - """ - try: - target = os.fspath(target) - except TypeError: - if not isinstance(target, PathBase): - raise - PathBase._copy_file(self, target) - else: - copyfile(os.fspath(self), target) - def chmod(self, mode, *, follow_symlinks=True): """ Change the permissions of the path, like os.chmod(). @@ -1019,16 +1111,17 @@ def replace(self, target): os.replace(self, target) return self.with_segments(target) + copy = property(_LocalCopyWorker, doc=_LocalCopyWorker.__call__.__doc__) + def move(self, target): """ Recursively move this file or directory tree to the given destination. """ - self._ensure_different_file(target) + if not isinstance(target, PathBase): + target = self.with_segments(target) + target.copy._ensure_different_file(self) try: return self.replace(target) - except TypeError: - if not isinstance(target, PathBase): - raise except OSError as err: if err.errno != EXDEV: raise @@ -1051,14 +1144,6 @@ def symlink_to(self, target, target_is_directory=False): f = f"{type(self).__name__}.symlink_to()" raise UnsupportedOperation(f"{f} is unsupported on this system") - if os.name == 'nt': - def _symlink_to_target_of(self, link): - """ - Make this path a symlink with the same target as the given link. - This is used by copy(). - """ - self.symlink_to(link.readlink(), link.is_dir()) - if hasattr(os, "link"): def hardlink_to(self, target): """ diff --git a/Lib/pathlib/_os.py b/Lib/pathlib/_os.py index 642b3a57c59a1d..57bcaf3d680138 100644 --- a/Lib/pathlib/_os.py +++ b/Lib/pathlib/_os.py @@ -4,7 +4,6 @@ from errno import * import os -import stat import sys try: import fcntl @@ -163,100 +162,3 @@ def copyfileobj(source_f, target_f): write_target = target_f.write while buf := read_source(1024 * 1024): write_target(buf) - - -# Kinds of metadata supported by the operating system. -file_metadata_keys = {'mode', 'times_ns'} -if hasattr(os.stat_result, 'st_flags'): - file_metadata_keys.add('flags') -if hasattr(os, 'listxattr'): - file_metadata_keys.add('xattrs') -file_metadata_keys = frozenset(file_metadata_keys) - - -def read_file_metadata(path, keys=None, *, follow_symlinks=True): - """ - Returns local path metadata as a dict with string keys. - """ - if keys is None: - keys = file_metadata_keys - assert keys.issubset(file_metadata_keys) - result = {} - for key in keys: - if key == 'xattrs': - try: - result['xattrs'] = [ - (attr, os.getxattr(path, attr, follow_symlinks=follow_symlinks)) - for attr in os.listxattr(path, follow_symlinks=follow_symlinks)] - except OSError as err: - if err.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES): - raise - continue - st = os.stat(path, follow_symlinks=follow_symlinks) - if key == 'mode': - result['mode'] = stat.S_IMODE(st.st_mode) - elif key == 'times_ns': - result['times_ns'] = st.st_atime_ns, st.st_mtime_ns - elif key == 'flags': - result['flags'] = st.st_flags - return result - - -def write_file_metadata(path, metadata, *, follow_symlinks=True): - """ - Sets local path metadata from the given dict with string keys. - """ - assert frozenset(metadata.keys()).issubset(file_metadata_keys) - - def _nop(*args, ns=None, follow_symlinks=None): - pass - - if follow_symlinks: - # use the real function if it exists - def lookup(name): - return getattr(os, name, _nop) - else: - # use the real function only if it exists - # *and* it supports follow_symlinks - def lookup(name): - fn = getattr(os, name, _nop) - if fn in os.supports_follow_symlinks: - return fn - return _nop - - times_ns = metadata.get('times_ns') - if times_ns is not None: - lookup("utime")(path, ns=times_ns, follow_symlinks=follow_symlinks) - # We must copy extended attributes before the file is (potentially) - # chmod()'ed read-only, otherwise setxattr() will error with -EACCES. - xattrs = metadata.get('xattrs') - if xattrs is not None: - for attr, value in xattrs: - try: - os.setxattr(path, attr, value, follow_symlinks=follow_symlinks) - except OSError as e: - if e.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES): - raise - mode = metadata.get('mode') - if mode is not None: - try: - lookup("chmod")(path, mode, follow_symlinks=follow_symlinks) - except NotImplementedError: - # if we got a NotImplementedError, it's because - # * follow_symlinks=False, - # * lchown() is unavailable, and - # * either - # * fchownat() is unavailable or - # * fchownat() doesn't implement AT_SYMLINK_NOFOLLOW. - # (it returned ENOSUP.) - # therefore we're out of options--we simply cannot chown the - # symlink. give up, suppress the error. - # (which is what shutil always did in this circumstance.) - pass - flags = metadata.get('flags') - if flags is not None: - try: - lookup("chflags")(path, flags, follow_symlinks=follow_symlinks) - except OSError as why: - if why.errno not in (EOPNOTSUPP, ENOTSUP): - raise From 228f275737615cc9be713a8c3f9325b359bf8aec Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 22 Dec 2024 01:47:41 -0800 Subject: [PATCH 087/376] gh-126664: revert: Use `else` instead of `finally` in docs explaining "with" (#128169) Revert "gh-126664: Use `else` instead of `finally` in "The with statement" documentation. (GH-126665)" This reverts commit 25257d61cfccc3b4189f96390a5c4db73fd5302c. --- Doc/reference/compound_stmts.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index e73ce44270b082..1b1e9f479cbe08 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -534,15 +534,18 @@ is semantically equivalent to:: enter = type(manager).__enter__ exit = type(manager).__exit__ value = enter(manager) + hit_except = False try: TARGET = value SUITE except: + hit_except = True if not exit(manager, *sys.exc_info()): raise - else: - exit(manager, None, None, None) + finally: + if not hit_except: + exit(manager, None, None, None) With more than one item, the context managers are processed as if multiple :keyword:`with` statements were nested:: From b66a4ad9fc32b63da2ba10db24cbc8f4e29f781a Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sun, 22 Dec 2024 12:46:02 +0000 Subject: [PATCH 088/376] gh-127949: fix resource warnings in `test_tasks.py` (#128172) --- Lib/test/test_asyncio/test_tasks.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 5b8979a8bbd13a..7d6d0564a9a9db 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -2698,17 +2698,17 @@ def __str__(self): initial_refcount = sys.getrefcount(obj) coro = coroutine_function() - loop = asyncio.new_event_loop() - task = asyncio.Task.__new__(asyncio.Task) + with contextlib.closing(asyncio.EventLoop()) as loop: + task = asyncio.Task.__new__(asyncio.Task) - for _ in range(5): - with self.assertRaisesRegex(RuntimeError, 'break'): - task.__init__(coro, loop=loop, context=obj, name=Break()) + for _ in range(5): + with self.assertRaisesRegex(RuntimeError, 'break'): + task.__init__(coro, loop=loop, context=obj, name=Break()) - coro.close() - del task + coro.close() + del task - self.assertEqual(sys.getrefcount(obj), initial_refcount) + self.assertEqual(sys.getrefcount(obj), initial_refcount) def add_subclass_tests(cls): From f420bdd29fbc1a97ad20d88075c38c937c1f8479 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Sun, 22 Dec 2024 17:34:16 +0100 Subject: [PATCH 089/376] gh-119786: Fix typos in `InternalDocs/interpreter.md` (#128174) --- InternalDocs/interpreter.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/InternalDocs/interpreter.md b/InternalDocs/interpreter.md index fa4a54fdc54fac..52702792c6cb7b 100644 --- a/InternalDocs/interpreter.md +++ b/InternalDocs/interpreter.md @@ -20,7 +20,7 @@ When the interpreter's [`PyEval_EvalCode()`](https://docs.python.org/3.14/c-api/veryhigh.html#c.PyEval_EvalCode) function is called to execute a `CodeObject`, it constructs a [`Frame`](frames.md) and calls [`_PyEval_EvalFrame()`](https://docs.python.org/3.14/c-api/veryhigh.html#c.PyEval_EvalCode) -to execute the code object in this frame. The frame hold the dynamic state of the +to execute the code object in this frame. The frame holds the dynamic state of the `CodeObject`'s execution, including the instruction pointer, the globals and builtins. It also has a reference to the `CodeObject` itself. @@ -153,9 +153,9 @@ More information about the use of inline caches can be found in Most instructions read or write some data in the form of object references (`PyObject *`). The CPython bytecode interpreter is a stack machine, meaning that its instructions operate by pushing data onto and popping it off the stack. -The stack is forms part of the frame for the code object. Its maximum depth is calculated +The stack forms part of the frame for the code object. Its maximum depth is calculated by the compiler and stored in the `co_stacksize` field of the code object, so that the -stack can be pre-allocated is a contiguous array of `PyObject*` pointers, when the frame +stack can be pre-allocated as a contiguous array of `PyObject*` pointers, when the frame is created. The stack effects of each instruction are also exposed through the @@ -462,7 +462,7 @@ set of values that allows them to: 2. Perform the operation quickly. This requires that the set of values is chosen such that membership can be -tested quickly and that membership is sufficient to allow the operation to +tested quickly and that membership is sufficient to allow the operation to be performed quickly. For example, `LOAD_GLOBAL_MODULE` is specialized for `globals()` From 9d3a8f494985e8bbef698c467099370e233fcbd4 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Sun, 22 Dec 2024 13:01:45 -0600 Subject: [PATCH 090/376] gh-100384: Error on `unguarded-availability` in macOS builds (#128155) Generate a build error on ``unguarded-availability`` in portable macOS builds (i.e. using MACOSX_DEPLOYMENT_TARGET), preventing invalid use of symbols that are not available in older versions of the OS. --- .github/workflows/reusable-macos.yml | 1 + ...-12-21-09-56-37.gh-issue-100384.Ib-XrN.rst | 2 + configure | 41 +++++++++++++++++++ configure.ac | 7 ++++ 4 files changed, 51 insertions(+) create mode 100644 Misc/NEWS.d/next/Build/2024-12-21-09-56-37.gh-issue-100384.Ib-XrN.rst diff --git a/.github/workflows/reusable-macos.yml b/.github/workflows/reusable-macos.yml index 6fa389b2d66e5e..cdbe05e09fb8e7 100644 --- a/.github/workflows/reusable-macos.yml +++ b/.github/workflows/reusable-macos.yml @@ -45,6 +45,7 @@ jobs: brew link --overwrite tcl-tk@8 - name: Configure CPython run: | + MACOSX_DEPLOYMENT_TARGET=10.15 \ GDBM_CFLAGS="-I$(brew --prefix gdbm)/include" \ GDBM_LIBS="-L$(brew --prefix gdbm)/lib -lgdbm" \ ./configure \ diff --git a/Misc/NEWS.d/next/Build/2024-12-21-09-56-37.gh-issue-100384.Ib-XrN.rst b/Misc/NEWS.d/next/Build/2024-12-21-09-56-37.gh-issue-100384.Ib-XrN.rst new file mode 100644 index 00000000000000..75c19fe3d8cef9 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-12-21-09-56-37.gh-issue-100384.Ib-XrN.rst @@ -0,0 +1,2 @@ +Error on ``unguarded-availability`` in macOS builds, preventing invalid +use of symbols that are not available in older versions of the OS. diff --git a/configure b/configure index e59c7046305d46..a697bc1f87b012 100755 --- a/configure +++ b/configure @@ -10406,6 +10406,47 @@ printf %s "checking which compiler should be used... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } + # Error on unguarded use of new symbols, which will fail at runtime for + # users on older versions of macOS + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wunguarded-availability" >&5 +printf %s "checking whether C compiler accepts -Wunguarded-availability... " >&6; } +if test ${ax_cv_check_cflags__Werror__Wunguarded_availability+y} +then : + printf %s "(cached) " >&6 +else $as_nop + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Werror -Wunguarded-availability" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ax_cv_check_cflags__Werror__Wunguarded_availability=yes +else $as_nop + ax_cv_check_cflags__Werror__Wunguarded_availability=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__Wunguarded_availability" >&5 +printf "%s\n" "$ax_cv_check_cflags__Werror__Wunguarded_availability" >&6; } +if test "x$ax_cv_check_cflags__Werror__Wunguarded_availability" = xyes +then : + as_fn_append CFLAGS_NODIST " -Werror=unguarded-availability" +else $as_nop + : +fi + + LIPO_INTEL64_FLAGS="" if test "${enable_universalsdk}" then diff --git a/configure.ac b/configure.ac index 074e2ce3dd3024..ebc15503f069cc 100644 --- a/configure.ac +++ b/configure.ac @@ -2603,6 +2603,13 @@ AS_VAR_IF([ac_cv_gcc_compat], [yes], [ esac AC_MSG_RESULT([$CC]) + # Error on unguarded use of new symbols, which will fail at runtime for + # users on older versions of macOS + AX_CHECK_COMPILE_FLAG([-Wunguarded-availability], + [AS_VAR_APPEND([CFLAGS_NODIST], [" -Werror=unguarded-availability"])], + [], + [-Werror]) + LIPO_INTEL64_FLAGS="" if test "${enable_universalsdk}" then From 831b6de6d725c697f2f61fd35c4448cd8a9354ff Mon Sep 17 00:00:00 2001 From: Alyssa Coghlan Date: Mon, 23 Dec 2024 14:17:19 +1000 Subject: [PATCH 091/376] gh-126180: Remove getopt and optparse deprecation notices (GH-126227) * Remove getopt and optparse deprecation notices * Add new docs sections for command line app helper libraries * Add guidance on choosing a CLI parsing library to the optparse docs * Link to the new guidance from the argparse and getopt docs * Reword intro in docs section for superseded stdlib modules * Reframe the optparse->argparse guide as a migration guide rather than as an upgrade guide --------- Co-authored-by: Serhiy Storchaka --- Doc/howto/argparse-optparse.rst | 36 +++-- Doc/howto/argparse.rst | 15 +- Doc/library/allos.rst | 5 - Doc/library/argparse.rst | 12 ++ Doc/library/cmdlinelibs.rst | 21 +++ Doc/library/filesys.rst | 1 - Doc/library/getopt.rst | 54 +++++-- Doc/library/index.rst | 1 + Doc/library/optparse.rst | 139 ++++++++++++++++-- Doc/library/superseded.rst | 17 ++- Doc/whatsnew/3.13.rst | 24 ++- ...-10-31-14-31-36.gh-issue-126225.vTxGXm.rst | 6 + 12 files changed, 266 insertions(+), 65 deletions(-) create mode 100644 Doc/library/cmdlinelibs.rst create mode 100644 Misc/NEWS.d/next/Library/2024-10-31-14-31-36.gh-issue-126225.vTxGXm.rst diff --git a/Doc/howto/argparse-optparse.rst b/Doc/howto/argparse-optparse.rst index cef2d893b28a62..b684619885b4c7 100644 --- a/Doc/howto/argparse-optparse.rst +++ b/Doc/howto/argparse-optparse.rst @@ -1,20 +1,14 @@ .. currentmodule:: argparse .. _upgrading-optparse-code: +.. _migrating-optparse-code: -========================== -Upgrading optparse code -========================== +============================================ +Migrating ``optparse`` code to ``argparse`` +============================================ -Originally, the :mod:`argparse` module had attempted to maintain compatibility -with :mod:`optparse`. However, :mod:`optparse` was difficult to extend -transparently, particularly with the changes required to support -``nargs=`` specifiers and better usage messages. When most everything in -:mod:`optparse` had either been copy-pasted over or monkey-patched, it no -longer seemed practical to try to maintain the backwards compatibility. - -The :mod:`argparse` module improves on the :mod:`optparse` -module in a number of ways including: +The :mod:`argparse` module offers several higher level features not natively +provided by the :mod:`optparse` module, including: * Handling positional arguments. * Supporting subcommands. @@ -23,7 +17,23 @@ module in a number of ways including: * Producing more informative usage messages. * Providing a much simpler interface for custom ``type`` and ``action``. -A partial upgrade path from :mod:`optparse` to :mod:`argparse`: +Originally, the :mod:`argparse` module attempted to maintain compatibility +with :mod:`optparse`. However, the fundamental design differences between +supporting declarative command line option processing (while leaving positional +argument processing to application code), and supporting both named options +and positional arguments in the declarative interface mean that the +API has diverged from that of ``optparse`` over time. + +As described in :ref:`choosing-an-argument-parser`, applications that are +currently using :mod:`optparse` and are happy with the way it works can +just continue to use ``optparse``. + +Application developers that are considering migrating should also review +the list of intrinsic behavioural differences described in that section +before deciding whether or not migration is desirable. + +For applications that do choose to migrate from :mod:`optparse` to :mod:`argparse`, +the following suggestions should be helpful: * Replace all :meth:`optparse.OptionParser.add_option` calls with :meth:`ArgumentParser.add_argument` calls. diff --git a/Doc/howto/argparse.rst b/Doc/howto/argparse.rst index 1efbee64d60bb3..902c50de00803c 100644 --- a/Doc/howto/argparse.rst +++ b/Doc/howto/argparse.rst @@ -13,11 +13,16 @@ recommended command-line parsing module in the Python standard library. .. note:: - There are two other modules that fulfill the same task, namely - :mod:`getopt` (an equivalent for ``getopt()`` from the C - language) and the deprecated :mod:`optparse`. - Note also that :mod:`argparse` is based on :mod:`optparse`, - and therefore very similar in terms of usage. + The standard library includes two other libraries directly related + to command-line parameter processing: the lower level :mod:`optparse` + module (which may require more code to configure for a given application, + but also allows an application to request behaviors that ``argparse`` + doesn't support), and the very low level :mod:`getopt` (which specifically + serves as an equivalent to the :c:func:`!getopt` family of functions + available to C programmers). + While neither of those modules is covered directly in this guide, many of + the core concepts in ``argparse`` first originated in ``optparse``, so + some aspects of this tutorial will also be relevant to ``optparse`` users. Concepts diff --git a/Doc/library/allos.rst b/Doc/library/allos.rst index 0223c1054ea5d8..1aed340b2527ac 100644 --- a/Doc/library/allos.rst +++ b/Doc/library/allos.rst @@ -15,14 +15,9 @@ but they are available on most other systems as well. Here's an overview: os.rst io.rst time.rst - argparse.rst logging.rst logging.config.rst logging.handlers.rst - getpass.rst - curses.rst - curses.ascii.rst - curses.panel.rst platform.rst errno.rst ctypes.rst diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index da4071dee34b8c..8d0116d8c060b8 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -11,6 +11,18 @@ **Source code:** :source:`Lib/argparse.py` +.. note:: + + While :mod:`argparse` is the default recommended standard library module + for implementing basic command line applications, authors with more + exacting requirements for exactly how their command line applications + behave may find it doesn't provide the necessary level of control. + Refer to :ref:`choosing-an-argument-parser` for alternatives to + consider when ``argparse`` doesn't support behaviors that the application + requires (such as entirely disabling support for interspersed options and + positional arguments, or accepting option parameter values that start + with ``-`` even when they correspond to another defined option). + -------------- .. sidebar:: Tutorial diff --git a/Doc/library/cmdlinelibs.rst b/Doc/library/cmdlinelibs.rst new file mode 100644 index 00000000000000..085d31af7bca1f --- /dev/null +++ b/Doc/library/cmdlinelibs.rst @@ -0,0 +1,21 @@ +.. _cmdlinelibs: + +******************************** +Command Line Interface Libraries +******************************** + +The modules described in this chapter assist with implementing +command line and terminal interfaces for applications. + +Here's an overview: + +.. toctree:: + :maxdepth: 1 + + argparse.rst + optparse.rst + getpass.rst + fileinput.rst + curses.rst + curses.ascii.rst + curses.panel.rst diff --git a/Doc/library/filesys.rst b/Doc/library/filesys.rst index 0ccf2b7bf59a0f..f1ea4761af7cb1 100644 --- a/Doc/library/filesys.rst +++ b/Doc/library/filesys.rst @@ -14,7 +14,6 @@ in this chapter is: pathlib.rst os.path.rst - fileinput.rst stat.rst filecmp.rst tempfile.rst diff --git a/Doc/library/getopt.rst b/Doc/library/getopt.rst index 891885d3afbf7a..5c63009e22d58c 100644 --- a/Doc/library/getopt.rst +++ b/Doc/library/getopt.rst @@ -7,18 +7,13 @@ **Source code:** :source:`Lib/getopt.py` -.. deprecated:: 3.13 - The :mod:`getopt` module is :term:`soft deprecated` and will not be - developed further; development will continue with the :mod:`argparse` - module. - .. note:: - The :mod:`getopt` module is a parser for command line options whose API is - designed to be familiar to users of the C :c:func:`!getopt` function. Users who - are unfamiliar with the C :c:func:`!getopt` function or who would like to write - less code and get better help and error messages should consider using the - :mod:`argparse` module instead. + This module is considered feature complete. A more declarative and + extensible alternative to this API is provided in the :mod:`optparse` + module. Further functional enhancements for command line parameter + processing are provided either as third party modules on PyPI, + or else as features in the :mod:`argparse` module. -------------- @@ -28,6 +23,13 @@ the special meanings of arguments of the form '``-``' and '``--``'). Long options similar to those supported by GNU software may be used as well via an optional third argument. +Users who are unfamiliar with the Unix :c:func:`!getopt` function should consider +using the :mod:`argparse` module instead. Users who are familiar with the Unix +:c:func:`!getopt` function, but would like to get equivalent behavior while +writing less code and getting better help and error messages should consider +using the :mod:`optparse` module. See :ref:`choosing-an-argument-parser` for +additional details. + This module provides two functions and an exception: @@ -194,13 +196,27 @@ In a script, typical usage is something like this: output = a else: assert False, "unhandled option" - # ... + process(args, output=output, verbose=verbose) if __name__ == "__main__": main() Note that an equivalent command line interface could be produced with less code -and more informative help and error messages by using the :mod:`argparse` module: +and more informative help and error messages by using the :mod:`optparse` module: + +.. testcode:: + + import optparse + + if __name__ == '__main__': + parser = optparse.OptionParser() + parser.add_option('-o', '--output') + parser.add_option('-v', dest='verbose', action='store_true') + opts, args = parser.parse_args() + process(args, output=opts.output, verbose=opts.verbose) + +A roughly equivalent command line interface for this case can also be +produced by using the :mod:`argparse` module: .. testcode:: @@ -210,12 +226,18 @@ and more informative help and error messages by using the :mod:`argparse` module parser = argparse.ArgumentParser() parser.add_argument('-o', '--output') parser.add_argument('-v', dest='verbose', action='store_true') + parser.add_argument('rest', nargs='*') args = parser.parse_args() - # ... do something with args.output ... - # ... do something with args.verbose .. + process(args.rest, output=args.output, verbose=args.verbose) + +See :ref:`choosing-an-argument-parser` for details on how the ``argparse`` +version of this code differs in behaviour from the ``optparse`` (and +``getopt``) version. .. seealso:: - Module :mod:`argparse` - Alternative command line option and argument parsing library. + Module :mod:`optparse` + Declarative command line option parsing. + Module :mod:`argparse` + More opinionated command line option and argument parsing library. diff --git a/Doc/library/index.rst b/Doc/library/index.rst index 951fbcf13fbb13..44b218948d07e1 100644 --- a/Doc/library/index.rst +++ b/Doc/library/index.rst @@ -55,6 +55,7 @@ the `Python Package Index `_. fileformats.rst crypto.rst allos.rst + cmdlinelibs.rst concurrency.rst ipc.rst netdata.rst diff --git a/Doc/library/optparse.rst b/Doc/library/optparse.rst index 74a49a8fb33666..ff327cf9162a8c 100644 --- a/Doc/library/optparse.rst +++ b/Doc/library/optparse.rst @@ -3,25 +3,135 @@ .. module:: optparse :synopsis: Command-line option parsing library. - :deprecated: .. moduleauthor:: Greg Ward .. sectionauthor:: Greg Ward **Source code:** :source:`Lib/optparse.py` -.. deprecated:: 3.2 - The :mod:`optparse` module is :term:`soft deprecated` and will not be - developed further; development will continue with the :mod:`argparse` - module. - -------------- +.. _choosing-an-argument-parser: + +Choosing an argument parsing library +------------------------------------ + +The standard library includes three argument parsing libraries: + +* :mod:`getopt`: a module that closely mirrors the procedural C ``getopt`` API. + Included in the standard library since before the initial Python 1.0 release. +* :mod:`optparse`: a declarative replacement for ``getopt`` that + provides equivalent functionality without requiring each application + to implement its own procedural option parsing logic. Included + in the standard library since the Python 2.3 release. +* :mod:`argparse`: a more opinionated alternative to ``optparse`` that + provides more functionality by default, at the expense of reduced application + flexibility in controlling exactly how arguments are processed. Included in + the standard library since the Python 2.7 and Python 3.2 releases. + +In the absence of more specific argument parsing design constraints, :mod:`argparse` +is the recommended choice for implementing command line applications, as it offers +the highest level of baseline functionality with the least application level code. + +:mod:`getopt` is retained almost entirely for backwards compatibility reasons. +However, it also serves a niche use case as a tool for prototyping and testing +command line argument handling in ``getopt``-based C applications. + +:mod:`optparse` should be considered as an alternative to :mod:`argparse` in the +following cases: + +* an application is already using :mod:`optparse` and doesn't want to risk the + subtle behavioural changes that may arise when migrating to :mod:`argparse` +* the application requires additional control over the way options and + positional parameters are interleaved on the command line (including + the ability to disable the interleaving feature completely) +* the application requires additional control over the incremental parsing + of command line elements (while ``argparse`` does support this, the + exact way it works in practice is undesirable for some use cases) +* the application requires additional control over the handling of options + which accept parameter values that may start with ``-`` (such as delegated + options to be passed to invoked subprocesses) +* the application requires some other command line parameter processing + behavior which ``argparse`` does not support, but which can be implemented + in terms of the lower level interface offered by ``optparse`` + +These considerations also mean that :mod:`optparse` is likely to provide a +better foundation for library authors writing third party command line +argument processing libraries. + +As a concrete example, consider the following two command line argument +parsing configurations, the first using ``optparse``, and the second +using ``argparse``: + +.. testcode:: + + import optparse + + if __name__ == '__main__': + parser = optparse.OptionParser() + parser.add_option('-o', '--output') + parser.add_option('-v', dest='verbose', action='store_true') + opts, args = parser.parse_args() + process(args, output=opts.output, verbose=opts.verbose) + +.. testcode:: + + import argparse + + if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('-o', '--output') + parser.add_argument('-v', dest='verbose', action='store_true') + parser.add_argument('rest', nargs='*') + args = parser.parse_args() + process(args.rest, output=args.output, verbose=args.verbose) + +The most obvious difference is that in the ``optparse`` version, the non-option +arguments are processed separately by the application after the option processing +is complete. In the ``argparse`` version, positional arguments are declared and +processed in the same way as the named options. + +However, the ``argparse`` version will also handle some parameter combination +differently from the way the ``optparse`` version would handle them. +For example (amongst other differences): + +* supplying ``-o -v`` gives ``output="-v"`` and ``verbose=False`` + when using ``optparse``, but a usage error with ``argparse`` + (complaining that no value has been supplied for ``-o/--output``, + since ``-v`` is interpreted as meaning the verbosity flag) +* similarly, supplying ``-o --`` gives ``output="--"`` and ``args=()`` + when using ``optparse``, but a usage error with ``argparse`` + (also complaining that no value has been supplied for ``-o/--output``, + since ``--`` is interpreted as terminating the option processing + and treating all remaining values as positional arguments) +* supplying ``-o=foo`` gives ``output="=foo"`` when using ``optparse``, + but gives ``output="foo"`` with ``argparse`` (since ``=`` is special + cased as an alternative separator for option parameter values) + +Whether these differing behaviors in the ``argparse`` version are +considered desirable or a problem will depend on the specific command line +application use case. + +.. seealso:: + + :pypi:`click` is a third party argument processing library (originally + based on ``optparse``), which allows command line applications to be + developed as a set of decorated command implementation functions. + + Other third party libraries, such as :pypi:`typer` or :pypi:`msgspec-click`, + allow command line interfaces to be specified in ways that more effectively + integrate with static checking of Python type annotations. + + +Introduction +------------ + :mod:`optparse` is a more convenient, flexible, and powerful library for parsing -command-line options than the old :mod:`getopt` module. :mod:`optparse` uses a -more declarative style of command-line parsing: you create an instance of -:class:`OptionParser`, populate it with options, and parse the command -line. :mod:`optparse` allows users to specify options in the conventional +command-line options than the minimalist :mod:`getopt` module. +:mod:`optparse` uses a more declarative style of command-line parsing: +you create an instance of :class:`OptionParser`, +populate it with options, and parse the command line. +:mod:`optparse` allows users to specify options in the conventional GNU/POSIX syntax, and additionally generates usage and help messages for you. Here's an example of using :mod:`optparse` in a simple script:: @@ -82,10 +192,11 @@ Background ---------- :mod:`optparse` was explicitly designed to encourage the creation of programs -with straightforward, conventional command-line interfaces. To that end, it -supports only the most common command-line syntax and semantics conventionally -used under Unix. If you are unfamiliar with these conventions, read this -section to acquaint yourself with them. +with straightforward command-line interfaces that follow the conventions +established by the :c:func:`!getopt` family of functions available to C developers. +To that end, it supports only the most common command-line syntax and semantics +conventionally used under Unix. If you are unfamiliar with these conventions, +reading this section will allow you to acquaint yourself with them. .. _optparse-terminology: diff --git a/Doc/library/superseded.rst b/Doc/library/superseded.rst index 17bfa66f043302..d120c6acf621e3 100644 --- a/Doc/library/superseded.rst +++ b/Doc/library/superseded.rst @@ -4,12 +4,23 @@ Superseded Modules ****************** -The modules described in this chapter are deprecated or :term:`soft deprecated` and only kept for -backwards compatibility. They have been superseded by other modules. +The modules described in this chapter have been superseded by other modules +for most use cases, and are retained primarily to preserve backwards compatibility. +Modules may appear in this chapter because they only cover a limited subset of +a problem space, and a more generally applicable solution is available elsewhere +in the standard library (for example, :mod:`getopt` covers the very specific +task of "mimic the C :c:func:`!getopt` API in Python", rather than the broader +command line option parsing and argument parsing capabilities offered by +:mod:`optparse` and :mod:`argparse`). + +Alternatively, modules may appear in this chapter because they are deprecated +outright, and awaiting removal in a future release, or they are +:term:`soft deprecated` and their use is actively discouraged in new projects. +With the removal of various obsolete modules through :pep:`594`, there are +currently no modules in this latter category. .. toctree:: :maxdepth: 1 getopt.rst - optparse.rst diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index c8e0f94f4246fb..6a0e483bd895d6 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1648,6 +1648,22 @@ opcode (Contributed by Irit Katriel in :gh:`105481`.) +optparse +-------- + +* This module is no longer considered :term:`soft deprecated`. + While :mod:`argparse` remains preferred for new projects that + aren't using a third party command line argument processing + library, there are aspects of the way ``argparse`` works that + mean the lower level ``optparse`` module may provide a better + foundation for *writing* argument processing libraries, and + for implementing command line applications which adhere more + strictly than ``argparse`` does to various Unix command line + processing conventions that originate in the behaviour of the + C :c:func:`!getopt` function . + (Contributed by Alyssa Coghlan and Serhiy Storchaka in :gh:`126180`.) + + pathlib ------- @@ -1787,14 +1803,6 @@ New Deprecations Check membership in :data:`~dis.hasarg` instead. (Contributed by Irit Katriel in :gh:`109319`.) -* :mod:`getopt` and :mod:`optparse`: - - * Both modules are now :term:`soft deprecated`, - with :mod:`argparse` preferred for new projects. - This is a new soft-deprecation for the :mod:`!getopt` module, - whereas the :mod:`!optparse` module was already *de facto* soft deprecated. - (Contributed by Victor Stinner in :gh:`106535`.) - * :mod:`gettext`: * Deprecate non-integer numbers as arguments to functions and methods diff --git a/Misc/NEWS.d/next/Library/2024-10-31-14-31-36.gh-issue-126225.vTxGXm.rst b/Misc/NEWS.d/next/Library/2024-10-31-14-31-36.gh-issue-126225.vTxGXm.rst new file mode 100644 index 00000000000000..13a1f213c7a58e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-31-14-31-36.gh-issue-126225.vTxGXm.rst @@ -0,0 +1,6 @@ +:mod:`getopt` and :mod:`optparse` are no longer marked as deprecated. +There are legitimate reasons to use one of these modules in preference to +:mod:`argparse`, and none of these modules are at risk of being removed +from the standard library. Of the three, ``argparse`` remains the +recommended default choice, *unless* one of the concerns noted at the top of +the ``optparse`` module documentation applies. From 180d417e9f9456defd4c5b53cae678c318287921 Mon Sep 17 00:00:00 2001 From: "T. Wouters" Date: Mon, 23 Dec 2024 04:31:33 -0800 Subject: [PATCH 092/376] gh-114203: Optimise simple recursive critical sections (#128126) Add a fast path to (single-mutex) critical section locking _iff_ the mutex is already held by the currently active, top-most critical section of this thread. This can matter a lot for indirectly recursive critical sections without intervening critical sections. --- Include/internal/pycore_critical_section.h | 14 +++++++++++ ...-12-20-23-07-33.gh-issue-114203.84NgoW.rst | 1 + Python/critical_section.c | 24 +++++++++++++------ 3 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-23-07-33.gh-issue-114203.84NgoW.rst diff --git a/Include/internal/pycore_critical_section.h b/Include/internal/pycore_critical_section.h index 9ba2fce56d3c9c..e66d6d805c1b3b 100644 --- a/Include/internal/pycore_critical_section.h +++ b/Include/internal/pycore_critical_section.h @@ -145,6 +145,12 @@ _PyCriticalSection_Pop(PyCriticalSection *c) static inline void _PyCriticalSection_End(PyCriticalSection *c) { + // If the mutex is NULL, we used the fast path in + // _PyCriticalSection_BeginSlow for locks already held in the top-most + // critical section, and we shouldn't unlock or pop this critical section. + if (c->_cs_mutex == NULL) { + return; + } PyMutex_Unlock(c->_cs_mutex); _PyCriticalSection_Pop(c); } @@ -199,6 +205,14 @@ _PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b) static inline void _PyCriticalSection2_End(PyCriticalSection2 *c) { + // if mutex1 is NULL, we used the fast path in + // _PyCriticalSection_BeginSlow for mutexes that are already held, + // which should only happen when mutex1 and mutex2 were the same mutex, + // and mutex2 should also be NULL. + if (c->_cs_base._cs_mutex == NULL) { + assert(c->_cs_mutex2 == NULL); + return; + } if (c->_cs_mutex2) { PyMutex_Unlock(c->_cs_mutex2); } diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-23-07-33.gh-issue-114203.84NgoW.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-23-07-33.gh-issue-114203.84NgoW.rst new file mode 100644 index 00000000000000..6a9856e90c32bc --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-23-07-33.gh-issue-114203.84NgoW.rst @@ -0,0 +1 @@ +Optimize ``Py_BEGIN_CRITICAL_SECTION`` for simple recursive calls. diff --git a/Python/critical_section.c b/Python/critical_section.c index 62ed25523fd6dc..73857b85496316 100644 --- a/Python/critical_section.c +++ b/Python/critical_section.c @@ -8,11 +8,28 @@ static_assert(_Alignof(PyCriticalSection) >= 4, "critical section must be aligned to at least 4 bytes"); #endif +#ifdef Py_GIL_DISABLED +static PyCriticalSection * +untag_critical_section(uintptr_t tag) +{ + return (PyCriticalSection *)(tag & ~_Py_CRITICAL_SECTION_MASK); +} +#endif + void _PyCriticalSection_BeginSlow(PyCriticalSection *c, PyMutex *m) { #ifdef Py_GIL_DISABLED PyThreadState *tstate = _PyThreadState_GET(); + // As an optimisation for locking the same object recursively, skip + // locking if the mutex is currently locked by the top-most critical + // section. + if (tstate->critical_section && + untag_critical_section(tstate->critical_section)->_cs_mutex == m) { + c->_cs_mutex = NULL; + c->_cs_prev = 0; + return; + } c->_cs_mutex = NULL; c->_cs_prev = (uintptr_t)tstate->critical_section; tstate->critical_section = (uintptr_t)c; @@ -42,13 +59,6 @@ _PyCriticalSection2_BeginSlow(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2, #endif } -#ifdef Py_GIL_DISABLED -static PyCriticalSection * -untag_critical_section(uintptr_t tag) -{ - return (PyCriticalSection *)(tag & ~_Py_CRITICAL_SECTION_MASK); -} -#endif // Release all locks held by critical sections. This is called by // _PyThreadState_Detach. From c5b0c90b62f1a10b0742db4bcd17da080d4e9111 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Tue, 24 Dec 2024 02:08:34 +0900 Subject: [PATCH 093/376] gh-115999: Update test_opcache to test with nested method (gh-128166) gh-115999: Update test_opcace to test with nested method --- Lib/test/test_opcache.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index ba111b5117b41d..79f452f8068c7f 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -606,7 +606,7 @@ def assert_races_do_not_crash( for writer in writers: writer.join() - @requires_specialization + @requires_specialization_ft def test_binary_subscr_getitem(self): def get_items(): class C: @@ -1242,14 +1242,6 @@ def f(o, n): f(test_obj, 1) self.assertEqual(test_obj.b, 0) -# gh-127274: BINARY_SUBSCR_GETITEM will only cache __getitem__ methods that -# are deferred. We only defer functions defined at the top-level. -class CGetItem: - def __init__(self, val): - self.val = val - def __getitem__(self, item): - return self.val - class TestSpecializer(TestBase): @@ -1592,7 +1584,13 @@ def binary_subscr_str_int(): self.assert_no_opcode(binary_subscr_str_int, "BINARY_SUBSCR") def binary_subscr_getitems(): - items = [CGetItem(i) for i in range(100)] + class C: + def __init__(self, val): + self.val = val + def __getitem__(self, item): + return self.val + + items = [C(i) for i in range(100)] for i in range(100): self.assertEqual(items[i][i], i) From d61542b5ff1fe64705e5ce1bcc53048f14098dba Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Mon, 23 Dec 2024 17:22:15 +0000 Subject: [PATCH 094/376] pathlib tests: create test hierarchy without using class under test (#128200) In the pathlib tests, avoid using the path class under test (`self.cls`) in test setup. Instead we use `os` functions in `test_pathlib`, and direct manipulation of `DummyPath` internal data in `test_pathlib_abc`. --- Lib/test/test_pathlib/test_pathlib.py | 38 ++++++++++++++++++- Lib/test/test_pathlib/test_pathlib_abc.py | 46 ++++++++++------------- 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index ef482c311542fa..fac8cbdf65a122 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -578,10 +578,44 @@ def setUp(self): if name in _tests_needing_symlinks and not self.can_symlink: self.skipTest('requires symlinks') super().setUp() - os.chmod(self.parser.join(self.base, 'dirE'), 0) + + def createTestHierarchy(self): + os.mkdir(self.base) + os.mkdir(os.path.join(self.base, 'dirA')) + os.mkdir(os.path.join(self.base, 'dirB')) + os.mkdir(os.path.join(self.base, 'dirC')) + os.mkdir(os.path.join(self.base, 'dirC', 'dirD')) + os.mkdir(os.path.join(self.base, 'dirE')) + with open(os.path.join(self.base, 'fileA'), 'wb') as f: + f.write(b"this is file A\n") + with open(os.path.join(self.base, 'dirB', 'fileB'), 'wb') as f: + f.write(b"this is file B\n") + with open(os.path.join(self.base, 'dirC', 'fileC'), 'wb') as f: + f.write(b"this is file C\n") + with open(os.path.join(self.base, 'dirC', 'novel.txt'), 'wb') as f: + f.write(b"this is a novel\n") + with open(os.path.join(self.base, 'dirC', 'dirD', 'fileD'), 'wb') as f: + f.write(b"this is file D\n") + os.chmod(os.path.join(self.base, 'dirE'), 0) + if self.can_symlink: + # Relative symlinks. + os.symlink('fileA', os.path.join(self.base, 'linkA')) + os.symlink('non-existing', os.path.join(self.base, 'brokenLink')) + os.symlink('dirB', + os.path.join(self.base, 'linkB'), + target_is_directory=True) + os.symlink(os.path.join('..', 'dirB'), + os.path.join(self.base, 'dirA', 'linkC'), + target_is_directory=True) + # This one goes upwards, creating a loop. + os.symlink(os.path.join('..', 'dirB'), + os.path.join(self.base, 'dirB', 'linkD'), + target_is_directory=True) + # Broken symlink (pointing to itself). + os.symlink('brokenLinkLoop', os.path.join(self.base, 'brokenLinkLoop')) def tearDown(self): - os.chmod(self.parser.join(self.base, 'dirE'), 0o777) + os.chmod(os.path.join(self.base, 'dirE'), 0o777) os_helper.rmtree(self.base) def tempdir(self): diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index 9198a0cbc45cee..787fb0f82257e6 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -1437,33 +1437,25 @@ class DummyPathTest(DummyPurePathTest): def setUp(self): super().setUp() - parser = self.cls.parser - p = self.cls(self.base) - p.mkdir(parents=True) - p.joinpath('dirA').mkdir() - p.joinpath('dirB').mkdir() - p.joinpath('dirC').mkdir() - p.joinpath('dirC', 'dirD').mkdir() - p.joinpath('dirE').mkdir() - with p.joinpath('fileA').open('wb') as f: - f.write(b"this is file A\n") - with p.joinpath('dirB', 'fileB').open('wb') as f: - f.write(b"this is file B\n") - with p.joinpath('dirC', 'fileC').open('wb') as f: - f.write(b"this is file C\n") - with p.joinpath('dirC', 'novel.txt').open('wb') as f: - f.write(b"this is a novel\n") - with p.joinpath('dirC', 'dirD', 'fileD').open('wb') as f: - f.write(b"this is file D\n") - if self.can_symlink: - p.joinpath('linkA').symlink_to('fileA') - p.joinpath('brokenLink').symlink_to('non-existing') - p.joinpath('linkB').symlink_to('dirB', target_is_directory=True) - p.joinpath('dirA', 'linkC').symlink_to( - parser.join('..', 'dirB'), target_is_directory=True) - p.joinpath('dirB', 'linkD').symlink_to( - parser.join('..', 'dirB'), target_is_directory=True) - p.joinpath('brokenLinkLoop').symlink_to('brokenLinkLoop') + self.createTestHierarchy() + + def createTestHierarchy(self): + cls = self.cls + cls._files = { + f'{self.base}/fileA': b'this is file A\n', + f'{self.base}/dirB/fileB': b'this is file B\n', + f'{self.base}/dirC/fileC': b'this is file C\n', + f'{self.base}/dirC/dirD/fileD': b'this is file D\n', + f'{self.base}/dirC/novel.txt': b'this is a novel\n', + } + cls._directories = { + f'{self.base}': {'fileA', 'dirA', 'dirB', 'dirC', 'dirE'}, + f'{self.base}/dirA': set(), + f'{self.base}/dirB': {'fileB'}, + f'{self.base}/dirC': {'fileC', 'dirD', 'novel.txt'}, + f'{self.base}/dirC/dirD': {'fileD'}, + f'{self.base}/dirE': set(), + } def tearDown(self): cls = self.cls From 30efede33ca1fe32debbae93cc40b0e7e0b133b3 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Mon, 23 Dec 2024 22:17:47 +0100 Subject: [PATCH 095/376] gh-128195: Add `_REPLACE_WITH_TRUE` to the tier2 optimizer (GH-128203) Add `_REPLACE_WITH_TRUE` to the tier2 optimizer --- Python/optimizer_bytecodes.c | 4 ++++ Python/optimizer_cases.c.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index e60c0d38425bfe..a14d119b7a1dec 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -899,6 +899,10 @@ dummy_func(void) { (void)version; } + op(_REPLACE_WITH_TRUE, (value -- res)) { + res = sym_new_const(ctx, Py_True); + } + // END BYTECODES // } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index b46079ec8a1992..0fcf5e18ed5808 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -211,7 +211,7 @@ case _REPLACE_WITH_TRUE: { _Py_UopsSymbol *res; - res = sym_new_not_null(ctx); + res = sym_new_const(ctx, Py_True); stack_pointer[-1] = res; break; } From 3f6a618e49b1c8c12a7bc0c26e846735e108dc97 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Tue, 24 Dec 2024 09:43:31 +0000 Subject: [PATCH 096/376] gh-127949: fix `DeprecationWarning` in test_inspect.py (#128215) --- Lib/test/test_inspect/test_inspect.py | 28 +++++++++++++-------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 2c950e46b3ed8a..d536d04d2e7d88 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -1,5 +1,4 @@ from annotationlib import Format, ForwardRef -import asyncio import builtins import collections import copy @@ -73,11 +72,6 @@ def revise(filename, *args): git = mod.StupidGit() -def tearDownModule(): - if support.has_socket_support: - asyncio._set_event_loop_policy(None) - - def signatures_with_lexicographic_keyword_only_parameters(): """ Yields a whole bunch of functions with only keyword-only parameters, @@ -205,7 +199,7 @@ def test_excluding_predicates(self): self.assertFalse(inspect.ismethodwrapper(type("AnyClass", (), {}))) def test_ispackage(self): - self.istest(inspect.ispackage, 'asyncio') + self.istest(inspect.ispackage, 'unittest') self.istest(inspect.ispackage, 'importlib') self.assertFalse(inspect.ispackage(inspect)) self.assertFalse(inspect.ispackage(mod)) @@ -1166,16 +1160,20 @@ def f(self): # This is necessary when the test is run multiple times. sys.modules.pop("inspect_actual") - @unittest.skipIf( - support.is_emscripten or support.is_wasi, - "socket.accept is broken" - ) def test_nested_class_definition_inside_async_function(self): - import asyncio - self.addCleanup(asyncio.set_event_loop_policy, None) - self.assertSourceEqual(asyncio.run(mod2.func225()), 226, 227) + def run(coro): + try: + coro.send(None) + except StopIteration as e: + return e.value + else: + raise RuntimeError("coroutine did not complete synchronously!") + finally: + coro.close() + + self.assertSourceEqual(run(mod2.func225()), 226, 227) self.assertSourceEqual(mod2.cls226, 231, 235) - self.assertSourceEqual(asyncio.run(mod2.cls226().func232()), 233, 234) + self.assertSourceEqual(run(mod2.cls226().func232()), 233, 234) def test_class_definition_same_name_diff_methods(self): self.assertSourceEqual(mod2.cls296, 296, 298) From a391d80f4bf5a3cf5aa95340ca848b9a0294778d Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Tue, 24 Dec 2024 17:30:26 +0530 Subject: [PATCH 097/376] gh-127949: deprecate asyncio policy classes (#128216) --- Doc/library/asyncio-policy.rst | 16 +++++++++ Lib/asyncio/__init__.py | 16 +++++++++ Lib/asyncio/events.py | 14 ++++---- Lib/asyncio/unix_events.py | 6 ++-- Lib/asyncio/windows_events.py | 10 +++--- Lib/test/test_asyncio/test_events.py | 34 +++++++++++++------- Lib/test/test_asyncio/test_runners.py | 2 +- Lib/test/test_asyncio/test_windows_events.py | 18 +++++++---- 8 files changed, 82 insertions(+), 34 deletions(-) diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst index 2d05c3a9f7f157..ea7fe957da7c40 100644 --- a/Doc/library/asyncio-policy.rst +++ b/Doc/library/asyncio-policy.rst @@ -87,6 +87,10 @@ The abstract event loop policy base class is defined as follows: This method should never return ``None``. + .. deprecated:: next + The :class:`AbstractEventLoopPolicy` class is deprecated and + will be removed in Python 3.16. + .. _asyncio-policy-builtin: @@ -109,6 +113,10 @@ asyncio ships with the following built-in policies: The :meth:`get_event_loop` method of the default asyncio policy now raises a :exc:`RuntimeError` if there is no set event loop. + .. deprecated:: next + The :class:`DefaultEventLoopPolicy` class is deprecated and + will be removed in Python 3.16. + .. class:: WindowsSelectorEventLoopPolicy @@ -117,6 +125,10 @@ asyncio ships with the following built-in policies: .. availability:: Windows. + .. deprecated:: next + The :class:`WindowsSelectorEventLoopPolicy` class is deprecated and + will be removed in Python 3.16. + .. class:: WindowsProactorEventLoopPolicy @@ -125,6 +137,10 @@ asyncio ships with the following built-in policies: .. availability:: Windows. + .. deprecated:: next + The :class:`WindowsProactorEventLoopPolicy` class is deprecated and + will be removed in Python 3.16. + .. _asyncio-custom-policies: diff --git a/Lib/asyncio/__init__.py b/Lib/asyncio/__init__.py index 03165a425eb7d2..edb615b1b6b1c6 100644 --- a/Lib/asyncio/__init__.py +++ b/Lib/asyncio/__init__.py @@ -45,3 +45,19 @@ else: from .unix_events import * # pragma: no cover __all__ += unix_events.__all__ + +def __getattr__(name: str): + import warnings + + deprecated = { + "AbstractEventLoopPolicy", + "DefaultEventLoopPolicy", + "WindowsSelectorEventLoopPolicy", + "WindowsProactorEventLoopPolicy", + } + if name in deprecated: + warnings._deprecated(f"asyncio.{name}", remove=(3, 16)) + # deprecated things have underscores in front of them + return globals()["_" + name] + + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index 1449245edc7c7e..3ade7747149b32 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -5,7 +5,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2015-2021 MagicStack Inc. http://magic.io __all__ = ( - 'AbstractEventLoopPolicy', + '_AbstractEventLoopPolicy', 'AbstractEventLoop', 'AbstractServer', 'Handle', 'TimerHandle', '_get_event_loop_policy', @@ -632,7 +632,7 @@ def set_debug(self, enabled): raise NotImplementedError -class AbstractEventLoopPolicy: +class _AbstractEventLoopPolicy: """Abstract policy for accessing the event loop.""" def get_event_loop(self): @@ -655,7 +655,7 @@ def new_event_loop(self): the current context, set_event_loop must be called explicitly.""" raise NotImplementedError -class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy): +class _BaseDefaultEventLoopPolicy(_AbstractEventLoopPolicy): """Default policy implementation for accessing the event loop. In this policy, each thread has its own event loop. However, we @@ -758,8 +758,8 @@ def _init_event_loop_policy(): global _event_loop_policy with _lock: if _event_loop_policy is None: # pragma: no branch - from . import DefaultEventLoopPolicy - _event_loop_policy = DefaultEventLoopPolicy() + from . import _DefaultEventLoopPolicy + _event_loop_policy = _DefaultEventLoopPolicy() def _get_event_loop_policy(): @@ -777,7 +777,7 @@ def _set_event_loop_policy(policy): If policy is None, the default policy is restored.""" global _event_loop_policy - if policy is not None and not isinstance(policy, AbstractEventLoopPolicy): + if policy is not None and not isinstance(policy, _AbstractEventLoopPolicy): raise TypeError(f"policy must be an instance of AbstractEventLoopPolicy or None, not '{type(policy).__name__}'") _event_loop_policy = policy @@ -838,7 +838,7 @@ def new_event_loop(): def on_fork(): # Reset the loop and wakeupfd in the forked child process. if _event_loop_policy is not None: - _event_loop_policy._local = BaseDefaultEventLoopPolicy._Local() + _event_loop_policy._local = _BaseDefaultEventLoopPolicy._Local() _set_running_loop(None) signal.set_wakeup_fd(-1) diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index 0227eb506c6016..f69c6a64c39ae6 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -28,7 +28,7 @@ __all__ = ( 'SelectorEventLoop', - 'DefaultEventLoopPolicy', + '_DefaultEventLoopPolicy', 'EventLoop', ) @@ -963,11 +963,11 @@ def can_use_pidfd(): return True -class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy): +class _UnixDefaultEventLoopPolicy(events._BaseDefaultEventLoopPolicy): """UNIX event loop policy""" _loop_factory = _UnixSelectorEventLoop SelectorEventLoop = _UnixSelectorEventLoop -DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy +_DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy EventLoop = SelectorEventLoop diff --git a/Lib/asyncio/windows_events.py b/Lib/asyncio/windows_events.py index bf99bc271c7acd..5f75b17d8ca649 100644 --- a/Lib/asyncio/windows_events.py +++ b/Lib/asyncio/windows_events.py @@ -29,8 +29,8 @@ __all__ = ( 'SelectorEventLoop', 'ProactorEventLoop', 'IocpProactor', - 'DefaultEventLoopPolicy', 'WindowsSelectorEventLoopPolicy', - 'WindowsProactorEventLoopPolicy', 'EventLoop', + '_DefaultEventLoopPolicy', '_WindowsSelectorEventLoopPolicy', + '_WindowsProactorEventLoopPolicy', 'EventLoop', ) @@ -891,13 +891,13 @@ def callback(f): SelectorEventLoop = _WindowsSelectorEventLoop -class WindowsSelectorEventLoopPolicy(events.BaseDefaultEventLoopPolicy): +class _WindowsSelectorEventLoopPolicy(events._BaseDefaultEventLoopPolicy): _loop_factory = SelectorEventLoop -class WindowsProactorEventLoopPolicy(events.BaseDefaultEventLoopPolicy): +class _WindowsProactorEventLoopPolicy(events._BaseDefaultEventLoopPolicy): _loop_factory = ProactorEventLoop -DefaultEventLoopPolicy = WindowsProactorEventLoopPolicy +_DefaultEventLoopPolicy = _WindowsProactorEventLoopPolicy EventLoop = ProactorEventLoop diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index d43f66c13d2f96..c626670f72a084 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -2695,14 +2695,26 @@ async def inner(): class PolicyTests(unittest.TestCase): + def test_abstract_event_loop_policy_deprecation(self): + with self.assertWarnsRegex( + DeprecationWarning, "'asyncio.AbstractEventLoopPolicy' is deprecated"): + policy = asyncio.AbstractEventLoopPolicy() + self.assertIsInstance(policy, asyncio.AbstractEventLoopPolicy) + + def test_default_event_loop_policy_deprecation(self): + with self.assertWarnsRegex( + DeprecationWarning, "'asyncio.DefaultEventLoopPolicy' is deprecated"): + policy = asyncio.DefaultEventLoopPolicy() + self.assertIsInstance(policy, asyncio.DefaultEventLoopPolicy) + def test_event_loop_policy(self): - policy = asyncio.AbstractEventLoopPolicy() + policy = asyncio._AbstractEventLoopPolicy() self.assertRaises(NotImplementedError, policy.get_event_loop) self.assertRaises(NotImplementedError, policy.set_event_loop, object()) self.assertRaises(NotImplementedError, policy.new_event_loop) def test_get_event_loop(self): - policy = asyncio.DefaultEventLoopPolicy() + policy = asyncio._DefaultEventLoopPolicy() self.assertIsNone(policy._local._loop) with self.assertRaises(RuntimeError): @@ -2710,7 +2722,7 @@ def test_get_event_loop(self): self.assertIsNone(policy._local._loop) def test_get_event_loop_does_not_call_set_event_loop(self): - policy = asyncio.DefaultEventLoopPolicy() + policy = asyncio._DefaultEventLoopPolicy() with mock.patch.object( policy, "set_event_loop", @@ -2722,7 +2734,7 @@ def test_get_event_loop_does_not_call_set_event_loop(self): m_set_event_loop.assert_not_called() def test_get_event_loop_after_set_none(self): - policy = asyncio.DefaultEventLoopPolicy() + policy = asyncio._DefaultEventLoopPolicy() policy.set_event_loop(None) self.assertRaises(RuntimeError, policy.get_event_loop) @@ -2730,7 +2742,7 @@ def test_get_event_loop_after_set_none(self): def test_get_event_loop_thread(self, m_current_thread): def f(): - policy = asyncio.DefaultEventLoopPolicy() + policy = asyncio._DefaultEventLoopPolicy() self.assertRaises(RuntimeError, policy.get_event_loop) th = threading.Thread(target=f) @@ -2738,14 +2750,14 @@ def f(): th.join() def test_new_event_loop(self): - policy = asyncio.DefaultEventLoopPolicy() + policy = asyncio._DefaultEventLoopPolicy() loop = policy.new_event_loop() self.assertIsInstance(loop, asyncio.AbstractEventLoop) loop.close() def test_set_event_loop(self): - policy = asyncio.DefaultEventLoopPolicy() + policy = asyncio._DefaultEventLoopPolicy() old_loop = policy.new_event_loop() policy.set_event_loop(old_loop) @@ -2762,7 +2774,7 @@ def test_get_event_loop_policy(self): with self.assertWarnsRegex( DeprecationWarning, "'asyncio.get_event_loop_policy' is deprecated"): policy = asyncio.get_event_loop_policy() - self.assertIsInstance(policy, asyncio.AbstractEventLoopPolicy) + self.assertIsInstance(policy, asyncio._AbstractEventLoopPolicy) self.assertIs(policy, asyncio.get_event_loop_policy()) def test_set_event_loop_policy(self): @@ -2775,7 +2787,7 @@ def test_set_event_loop_policy(self): DeprecationWarning, "'asyncio.get_event_loop_policy' is deprecated"): old_policy = asyncio.get_event_loop_policy() - policy = asyncio.DefaultEventLoopPolicy() + policy = asyncio._DefaultEventLoopPolicy() with self.assertWarnsRegex( DeprecationWarning, "'asyncio.set_event_loop_policy' is deprecated"): asyncio.set_event_loop_policy(policy) @@ -2862,7 +2874,7 @@ def test_get_event_loop_returns_running_loop(self): class TestError(Exception): pass - class Policy(asyncio.DefaultEventLoopPolicy): + class Policy(asyncio._DefaultEventLoopPolicy): def get_event_loop(self): raise TestError @@ -2908,7 +2920,7 @@ async def func(): def test_get_event_loop_returns_running_loop2(self): old_policy = asyncio._get_event_loop_policy() try: - asyncio._set_event_loop_policy(asyncio.DefaultEventLoopPolicy()) + asyncio._set_event_loop_policy(asyncio._DefaultEventLoopPolicy()) loop = asyncio.new_event_loop() self.addCleanup(loop.close) diff --git a/Lib/test/test_asyncio/test_runners.py b/Lib/test/test_asyncio/test_runners.py index e1f82f7f7bec0c..21f277bc2d8d5f 100644 --- a/Lib/test/test_asyncio/test_runners.py +++ b/Lib/test/test_asyncio/test_runners.py @@ -19,7 +19,7 @@ def interrupt_self(): _thread.interrupt_main() -class TestPolicy(asyncio.AbstractEventLoopPolicy): +class TestPolicy(asyncio._AbstractEventLoopPolicy): def __init__(self, loop_factory): self.loop_factory = loop_factory diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index 28b05d24dc25a1..69e9905205eee0 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -328,14 +328,15 @@ class WinPolicyTests(WindowsEventsTestCase): def test_selector_win_policy(self): async def main(): - self.assertIsInstance( - asyncio.get_running_loop(), - asyncio.SelectorEventLoop) + self.assertIsInstance(asyncio.get_running_loop(), asyncio.SelectorEventLoop) old_policy = asyncio._get_event_loop_policy() try: - asyncio._set_event_loop_policy( - asyncio.WindowsSelectorEventLoopPolicy()) + with self.assertWarnsRegex( + DeprecationWarning, + "'asyncio.WindowsSelectorEventLoopPolicy' is deprecated", + ): + asyncio._set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) asyncio.run(main()) finally: asyncio._set_event_loop_policy(old_policy) @@ -348,8 +349,11 @@ async def main(): old_policy = asyncio._get_event_loop_policy() try: - asyncio._set_event_loop_policy( - asyncio.WindowsProactorEventLoopPolicy()) + with self.assertWarnsRegex( + DeprecationWarning, + "'asyncio.WindowsProactorEventLoopPolicy' is deprecated", + ): + asyncio._set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) asyncio.run(main()) finally: asyncio._set_event_loop_policy(old_policy) From bd3e200cce6601684108e23e7621bbe05b53a323 Mon Sep 17 00:00:00 2001 From: AraHaan Date: Tue, 24 Dec 2024 08:01:21 -0500 Subject: [PATCH 098/376] Add Windows version comments to the python manifest. (GH-127439) --- PC/python.manifest | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/PC/python.manifest b/PC/python.manifest index 8e1bc022adfb4f..19c9fc1b80a3ec 100644 --- a/PC/python.manifest +++ b/PC/python.manifest @@ -9,10 +9,15 @@ + + + + + From 3ddd70ceaaf67b111ee4251817e150396d6d10a9 Mon Sep 17 00:00:00 2001 From: Sergey Muraviov Date: Tue, 24 Dec 2024 16:06:41 +0300 Subject: [PATCH 099/376] gh-128217: Validate the normalized_environment variable instead of the similarly named function (GH-128220) --- Modules/_winapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_winapi.c b/Modules/_winapi.c index 4ce689fe30e6df..260cab48091c16 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -1048,7 +1048,7 @@ getenvironment(PyObject* environment) } normalized_environment = normalize_environment(environment); - if (normalize_environment == NULL) { + if (normalized_environment == NULL) { return NULL; } From 9fce90682553e2cfe93e98e2ae5948bf9c7c4456 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Tue, 24 Dec 2024 19:24:28 +0530 Subject: [PATCH 100/376] gh-127949: deprecate `asyncio.set_event_loop` (#128218) Deprecate `asyncio.set_event_loop` to be removed in Python 3.16. --- Doc/library/asyncio-eventloop.rst | 4 +++ Lib/asyncio/__main__.py | 2 +- Lib/asyncio/events.py | 32 +++++++++++++++-------- Lib/asyncio/runners.py | 4 +-- Lib/test/test_asyncgen.py | 2 +- Lib/test/test_asyncio/functional.py | 4 +-- Lib/test/test_asyncio/test_base_events.py | 8 +++--- Lib/test/test_asyncio/test_events.py | 26 +++++++++++------- Lib/test/test_asyncio/test_futures.py | 8 +++--- Lib/test/test_asyncio/test_streams.py | 10 +++---- Lib/test/test_asyncio/test_tasks.py | 18 ++++++------- Lib/test/test_asyncio/test_unix_events.py | 4 +-- Lib/test/test_asyncio/utils.py | 4 +-- Lib/test/test_coroutines.py | 2 +- Lib/test/test_type_params.py | 2 +- Lib/test/test_unittest/test_async_case.py | 2 +- 16 files changed, 77 insertions(+), 55 deletions(-) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 9f1aec148f8750..29f843123f8560 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -66,6 +66,10 @@ an event loop: Set *loop* as the current event loop for the current OS thread. + .. deprecated:: next + The :func:`set_event_loop` function is deprecated and will be removed + in Python 3.16. + .. function:: new_event_loop() Create and return a new event loop object. diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py index 95c636f9e02866..662ba649aa08be 100644 --- a/Lib/asyncio/__main__.py +++ b/Lib/asyncio/__main__.py @@ -149,7 +149,7 @@ def interrupt(self) -> None: return_code = 0 loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) + asyncio._set_event_loop(loop) repl_locals = {'asyncio': asyncio} for key in {'__name__', '__package__', diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index 3ade7747149b32..6e291d28ec81ae 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -5,16 +5,22 @@ # SPDX-FileCopyrightText: Copyright (c) 2015-2021 MagicStack Inc. http://magic.io __all__ = ( - '_AbstractEventLoopPolicy', - 'AbstractEventLoop', 'AbstractServer', - 'Handle', 'TimerHandle', - '_get_event_loop_policy', - 'get_event_loop_policy', - '_set_event_loop_policy', - 'set_event_loop_policy', - 'get_event_loop', 'set_event_loop', 'new_event_loop', - '_set_running_loop', 'get_running_loop', - '_get_running_loop', + "_AbstractEventLoopPolicy", + "AbstractEventLoop", + "AbstractServer", + "Handle", + "TimerHandle", + "_get_event_loop_policy", + "get_event_loop_policy", + "_set_event_loop_policy", + "set_event_loop_policy", + "get_event_loop", + "_set_event_loop", + "set_event_loop", + "new_event_loop", + "_set_running_loop", + "get_running_loop", + "_get_running_loop", ) import contextvars @@ -801,9 +807,13 @@ def get_event_loop(): return _get_event_loop_policy().get_event_loop() +def _set_event_loop(loop): + _get_event_loop_policy().set_event_loop(loop) + def set_event_loop(loop): """Equivalent to calling get_event_loop_policy().set_event_loop(loop).""" - _get_event_loop_policy().set_event_loop(loop) + warnings._deprecated('asyncio.set_event_loop', remove=(3,16)) + _set_event_loop(loop) def new_event_loop(): diff --git a/Lib/asyncio/runners.py b/Lib/asyncio/runners.py index 0e63c34f60f4d9..b9adf291d4817f 100644 --- a/Lib/asyncio/runners.py +++ b/Lib/asyncio/runners.py @@ -74,7 +74,7 @@ def close(self): loop.shutdown_default_executor(constants.THREAD_JOIN_TIMEOUT)) finally: if self._set_event_loop: - events.set_event_loop(None) + events._set_event_loop(None) loop.close() self._loop = None self._state = _State.CLOSED @@ -147,7 +147,7 @@ def _lazy_init(self): if not self._set_event_loop: # Call set_event_loop only once to avoid calling # attach_loop multiple times on child watchers - events.set_event_loop(self._loop) + events._set_event_loop(self._loop) self._set_event_loop = True else: self._loop = self._loop_factory() diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py index 4bce6d5c1b1d2f..5bfd789185c675 100644 --- a/Lib/test/test_asyncgen.py +++ b/Lib/test/test_asyncgen.py @@ -624,7 +624,7 @@ class AsyncGenAsyncioTest(unittest.TestCase): def setUp(self): self.loop = asyncio.new_event_loop() - asyncio.set_event_loop(None) + asyncio._set_event_loop(None) def tearDown(self): self.loop.close() diff --git a/Lib/test/test_asyncio/functional.py b/Lib/test/test_asyncio/functional.py index d19c7a612ccf86..2934325b6dfbc7 100644 --- a/Lib/test/test_asyncio/functional.py +++ b/Lib/test/test_asyncio/functional.py @@ -24,7 +24,7 @@ def loop_exception_handler(self, loop, context): def setUp(self): self.loop = self.new_loop() - asyncio.set_event_loop(None) + asyncio._set_event_loop(None) self.loop.set_exception_handler(self.loop_exception_handler) self.__unhandled_exceptions = [] @@ -39,7 +39,7 @@ def tearDown(self): self.fail('unexpected calls to loop.call_exception_handler()') finally: - asyncio.set_event_loop(None) + asyncio._set_event_loop(None) self.loop = None def tcp_server(self, server_prog, *, diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index 08e38b047d519b..1e063c1352ecb9 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -331,10 +331,10 @@ def check_in_thread(loop, event, debug, create_loop, fut): if create_loop: loop2 = base_events.BaseEventLoop() try: - asyncio.set_event_loop(loop2) + asyncio._set_event_loop(loop2) self.check_thread(loop, debug) finally: - asyncio.set_event_loop(None) + asyncio._set_event_loop(None) loop2.close() else: self.check_thread(loop, debug) @@ -690,7 +690,7 @@ def default_exception_handler(self, context): loop = Loop() self.addCleanup(loop.close) - asyncio.set_event_loop(loop) + asyncio._set_event_loop(loop) def run_loop(): def zero_error(): @@ -1983,7 +1983,7 @@ def stop_loop_cb(loop): async def stop_loop_coro(loop): loop.stop() - asyncio.set_event_loop(self.loop) + asyncio._set_event_loop(self.loop) self.loop.set_debug(True) self.loop.slow_callback_duration = 0.0 diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index c626670f72a084..c8439c9af5e6ba 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -58,7 +58,7 @@ async def doit(): return 'hello' loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) + asyncio._set_event_loop(loop) return loop.run_until_complete(doit()) @@ -2695,6 +2695,14 @@ async def inner(): class PolicyTests(unittest.TestCase): + def test_asyncio_set_event_loop_deprecation(self): + with self.assertWarnsRegex( + DeprecationWarning, "'asyncio.set_event_loop' is deprecated"): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + self.assertIs(loop, asyncio.get_event_loop()) + loop.close() + def test_abstract_event_loop_policy_deprecation(self): with self.assertWarnsRegex( DeprecationWarning, "'asyncio.AbstractEventLoopPolicy' is deprecated"): @@ -2824,14 +2832,14 @@ def setUp(self): super().setUp() self.loop = asyncio.new_event_loop() - asyncio.set_event_loop(self.loop) + asyncio._set_event_loop(self.loop) def tearDown(self): try: super().tearDown() finally: self.loop.close() - asyncio.set_event_loop(None) + asyncio._set_event_loop(None) events._get_running_loop = self._get_running_loop_saved events._set_running_loop = self._set_running_loop_saved @@ -2885,7 +2893,7 @@ def get_event_loop(self): with self.assertRaises(TestError): asyncio.get_event_loop() - asyncio.set_event_loop(None) + asyncio._set_event_loop(None) with self.assertRaises(TestError): asyncio.get_event_loop() @@ -2900,10 +2908,10 @@ async def func(): loop.run_until_complete(func()) - asyncio.set_event_loop(loop) + asyncio._set_event_loop(loop) with self.assertRaises(TestError): asyncio.get_event_loop() - asyncio.set_event_loop(None) + asyncio._set_event_loop(None) with self.assertRaises(TestError): asyncio.get_event_loop() @@ -2927,7 +2935,7 @@ def test_get_event_loop_returns_running_loop2(self): with self.assertRaisesRegex(RuntimeError, 'no current'): asyncio.get_event_loop() - asyncio.set_event_loop(None) + asyncio._set_event_loop(None) with self.assertRaisesRegex(RuntimeError, 'no current'): asyncio.get_event_loop() @@ -2938,10 +2946,10 @@ async def func(): loop.run_until_complete(func()) - asyncio.set_event_loop(loop) + asyncio._set_event_loop(loop) self.assertIs(asyncio.get_event_loop(), loop) - asyncio.set_event_loop(None) + asyncio._set_event_loop(None) with self.assertRaisesRegex(RuntimeError, 'no current'): asyncio.get_event_loop() diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 7db70a4c81d483..84b44011b9a844 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -178,8 +178,8 @@ async def test(): def test_constructor_use_global_loop(self): # Deprecated in 3.10, undeprecated in 3.12 - asyncio.set_event_loop(self.loop) - self.addCleanup(asyncio.set_event_loop, None) + asyncio._set_event_loop(self.loop) + self.addCleanup(asyncio._set_event_loop, None) f = self._new_future() self.assertIs(f._loop, self.loop) self.assertIs(f.get_loop(), self.loop) @@ -566,8 +566,8 @@ async def test(): def test_wrap_future_use_global_loop(self): # Deprecated in 3.10, undeprecated in 3.12 - asyncio.set_event_loop(self.loop) - self.addCleanup(asyncio.set_event_loop, None) + asyncio._set_event_loop(self.loop) + self.addCleanup(asyncio._set_event_loop, None) def run(arg): return (arg, threading.get_ident()) ex = concurrent.futures.ThreadPoolExecutor(1) diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index c3ba90b309e49f..047ada8c5d23df 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -71,7 +71,7 @@ def _basetest_open_connection_no_loop_ssl(self, open_connection_fut): try: reader, writer = self.loop.run_until_complete(open_connection_fut) finally: - asyncio.set_event_loop(None) + asyncio._set_event_loop(None) writer.write(b'GET / HTTP/1.0\r\n\r\n') f = reader.read() data = self.loop.run_until_complete(f) @@ -839,8 +839,8 @@ def test_streamreader_constructor_use_global_loop(self): # asyncio issue #184: Ensure that StreamReaderProtocol constructor # retrieves the current loop if the loop parameter is not set # Deprecated in 3.10, undeprecated in 3.12 - self.addCleanup(asyncio.set_event_loop, None) - asyncio.set_event_loop(self.loop) + self.addCleanup(asyncio._set_event_loop, None) + asyncio._set_event_loop(self.loop) reader = asyncio.StreamReader() self.assertIs(reader._loop, self.loop) @@ -863,8 +863,8 @@ def test_streamreaderprotocol_constructor_use_global_loop(self): # asyncio issue #184: Ensure that StreamReaderProtocol constructor # retrieves the current loop if the loop parameter is not set # Deprecated in 3.10, undeprecated in 3.12 - self.addCleanup(asyncio.set_event_loop, None) - asyncio.set_event_loop(self.loop) + self.addCleanup(asyncio._set_event_loop, None) + asyncio._set_event_loop(self.loop) reader = mock.Mock() protocol = asyncio.StreamReaderProtocol(reader) self.assertIs(protocol._loop, self.loop) diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 7d6d0564a9a9db..b5363226ad79f4 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -212,8 +212,8 @@ async def test(): self.assertEqual(t.result(), 'ok') # Deprecated in 3.10, undeprecated in 3.12 - asyncio.set_event_loop(self.loop) - self.addCleanup(asyncio.set_event_loop, None) + asyncio._set_event_loop(self.loop) + self.addCleanup(asyncio._set_event_loop, None) t = asyncio.ensure_future(notmuch()) self.assertIs(t._loop, self.loop) self.loop.run_until_complete(t) @@ -2202,8 +2202,8 @@ def test_shield_coroutine_use_global_loop(self): async def coro(): return 42 - asyncio.set_event_loop(self.loop) - self.addCleanup(asyncio.set_event_loop, None) + asyncio._set_event_loop(self.loop) + self.addCleanup(asyncio._set_event_loop, None) outer = asyncio.shield(coro()) self.assertEqual(outer._loop, self.loop) res = self.loop.run_until_complete(outer) @@ -2273,7 +2273,7 @@ async def kill_me(loop): self.assertEqual(self.all_tasks(loop=self.loop), {task}) - asyncio.set_event_loop(None) + asyncio._set_event_loop(None) # execute the task so it waits for future self.loop._run_once() @@ -3278,8 +3278,8 @@ async def gather(): def test_constructor_empty_sequence_use_global_loop(self): # Deprecated in 3.10, undeprecated in 3.12 - asyncio.set_event_loop(self.one_loop) - self.addCleanup(asyncio.set_event_loop, None) + asyncio._set_event_loop(self.one_loop) + self.addCleanup(asyncio._set_event_loop, None) fut = asyncio.gather() self.assertIsInstance(fut, asyncio.Future) self.assertIs(fut._loop, self.one_loop) @@ -3386,8 +3386,8 @@ def test_constructor_use_global_loop(self): # Deprecated in 3.10, undeprecated in 3.12 async def coro(): return 'abc' - asyncio.set_event_loop(self.other_loop) - self.addCleanup(asyncio.set_event_loop, None) + asyncio._set_event_loop(self.other_loop) + self.addCleanup(asyncio._set_event_loop, None) gen1 = coro() gen2 = coro() fut = asyncio.gather(gen1, gen2) diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index e9ee9702248015..ebb4cc0f7b64fd 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -1116,11 +1116,11 @@ class TestFunctional(unittest.TestCase): def setUp(self): self.loop = asyncio.new_event_loop() - asyncio.set_event_loop(self.loop) + asyncio._set_event_loop(self.loop) def tearDown(self): self.loop.close() - asyncio.set_event_loop(None) + asyncio._set_event_loop(None) def test_add_reader_invalid_argument(self): def assert_raises(): diff --git a/Lib/test/test_asyncio/utils.py b/Lib/test/test_asyncio/utils.py index b8dbe7feaac3f4..35ce13896da08f 100644 --- a/Lib/test/test_asyncio/utils.py +++ b/Lib/test/test_asyncio/utils.py @@ -541,7 +541,7 @@ def set_event_loop(self, loop, *, cleanup=True): if loop is None: raise AssertionError('loop is None') # ensure that the event loop is passed explicitly in asyncio - events.set_event_loop(None) + events._set_event_loop(None) if cleanup: self.addCleanup(self.close_loop, loop) @@ -554,7 +554,7 @@ def setUp(self): self._thread_cleanup = threading_helper.threading_setup() def tearDown(self): - events.set_event_loop(None) + events._set_event_loop(None) # Detect CPython bug #23353: ensure that yield/yield-from is not used # in an except block of a generator diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index a72c43f9b47947..840043d5271224 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -2287,7 +2287,7 @@ async def f(): buffer.append('unreachable') loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) + asyncio._set_event_loop(loop) try: loop.run_until_complete(f()) except MyException: diff --git a/Lib/test/test_type_params.py b/Lib/test/test_type_params.py index 433b19593bdd04..89f836cf722966 100644 --- a/Lib/test/test_type_params.py +++ b/Lib/test/test_type_params.py @@ -1060,7 +1060,7 @@ async def coroutine[B](): co = get_coroutine() - self.addCleanup(asyncio.set_event_loop_policy, None) + self.addCleanup(asyncio._set_event_loop_policy, None) a, b = asyncio.run(co()) self.assertIsInstance(a, TypeVar) diff --git a/Lib/test/test_unittest/test_async_case.py b/Lib/test/test_unittest/test_async_case.py index 993e6bf013cfbf..fc996b42149dcb 100644 --- a/Lib/test/test_unittest/test_async_case.py +++ b/Lib/test/test_unittest/test_async_case.py @@ -476,7 +476,7 @@ async def cleanup(self, fut): def test_setup_get_event_loop(self): # See https://github.com/python/cpython/issues/95736 # Make sure the default event loop is not used - asyncio.set_event_loop(None) + asyncio._set_event_loop(None) class TestCase1(unittest.IsolatedAsyncioTestCase): def setUp(self): From 7ed6c5c6961d0849f163d4d449fb36bae312b6bc Mon Sep 17 00:00:00 2001 From: Dima Ryazanov Date: Tue, 24 Dec 2024 07:56:42 -0800 Subject: [PATCH 101/376] gh-127847: Fix position in the special-cased zipfile seek (#127856) --------- Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Peter Bierma Co-authored-by: Jason R. Coombs --- Lib/test/test_zipfile/test_core.py | 12 ++++++++++++ Lib/zipfile/__init__.py | 5 ++++- .../2024-12-12-07-27-51.gh-issue-127847.ksfNKM.rst | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-12-07-27-51.gh-issue-127847.ksfNKM.rst diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py index c36228c033a414..124e088fd15b80 100644 --- a/Lib/test/test_zipfile/test_core.py +++ b/Lib/test/test_zipfile/test_core.py @@ -2333,6 +2333,18 @@ def test_read_after_seek(self): fp.seek(1, os.SEEK_CUR) self.assertEqual(fp.read(-1), b'men!') + def test_uncompressed_interleaved_seek_read(self): + # gh-127847: Make sure the position in the archive is correct + # in the special case of seeking in a ZIP_STORED entry. + with zipfile.ZipFile(TESTFN, "w") as zipf: + zipf.writestr("a.txt", "123") + zipf.writestr("b.txt", "456") + with zipfile.ZipFile(TESTFN, "r") as zipf: + with zipf.open("a.txt", "r") as a, zipf.open("b.txt", "r") as b: + self.assertEqual(a.read(1), b"1") + self.assertEqual(b.seek(1), 1) + self.assertEqual(b.read(1), b"5") + @requires_bz2() def test_decompress_without_3rd_party_library(self): data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py index 6907ae6d5b7464..f4d396abb6e639 100644 --- a/Lib/zipfile/__init__.py +++ b/Lib/zipfile/__init__.py @@ -819,7 +819,10 @@ def seek(self, offset, whence=0): raise ValueError("Can't reposition in the ZIP file while " "there is an open writing handle on it. " "Close the writing handle before trying to read.") - self._file.seek(offset, whence) + if whence == os.SEEK_CUR: + self._file.seek(self._pos + offset) + else: + self._file.seek(offset, whence) self._pos = self._file.tell() return self._pos diff --git a/Misc/NEWS.d/next/Library/2024-12-12-07-27-51.gh-issue-127847.ksfNKM.rst b/Misc/NEWS.d/next/Library/2024-12-12-07-27-51.gh-issue-127847.ksfNKM.rst new file mode 100644 index 00000000000000..3d6e36fb538bca --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-12-07-27-51.gh-issue-127847.ksfNKM.rst @@ -0,0 +1 @@ +Fix the position when doing interleaved seeks and reads in uncompressed, unencrypted zip files returned by :meth:`zipfile.ZipFile.open`. From 7985d460c731b2c48419a33fc1820f9512bb6f21 Mon Sep 17 00:00:00 2001 From: Bogdan Romanyuk <65823030+wrongnull@users.noreply.github.com> Date: Tue, 24 Dec 2024 21:00:24 +0300 Subject: [PATCH 102/376] gh-128227: Regenerate `Doc/requirements-oldest-sphinx.txt` (#128228) --- Doc/requirements-oldest-sphinx.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Doc/requirements-oldest-sphinx.txt b/Doc/requirements-oldest-sphinx.txt index 3483faea6b56cb..c8027a05706c21 100644 --- a/Doc/requirements-oldest-sphinx.txt +++ b/Doc/requirements-oldest-sphinx.txt @@ -13,15 +13,15 @@ python-docs-theme>=2022.1 # Sphinx 7.2.6 comes from ``needs_sphinx = '7.2.6'`` in ``Doc/conf.py``. alabaster==0.7.16 -Babel==2.16.0 -certifi==2024.8.30 +babel==2.16.0 +certifi==2024.12.14 charset-normalizer==3.4.0 docutils==0.20.1 idna==3.10 imagesize==1.4.1 -Jinja2==3.1.4 -MarkupSafe==3.0.1 -packaging==24.1 +Jinja2==3.1.5 +MarkupSafe==3.0.2 +packaging==24.2 Pygments==2.18.0 requests==2.32.3 snowballstemmer==2.2.0 @@ -32,4 +32,4 @@ sphinxcontrib-htmlhelp==2.1.0 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==2.0.0 sphinxcontrib-serializinghtml==2.0.0 -urllib3==2.2.3 +urllib3==2.3.0 From 418114c139666f33abff937e40ccbbbdce15bc39 Mon Sep 17 00:00:00 2001 From: Will Childs-Klein Date: Tue, 24 Dec 2024 12:29:27 -0600 Subject: [PATCH 103/376] gh-128035: Add ssl.HAS_PHA to detect libssl PHA support (GH-128036) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add ssl.HAS_PHA to detect libssl Post-Handshake-Auth support Co-authored-by: Tomas R. Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/ssl.rst | 6 ++++++ Doc/whatsnew/3.14.rst | 8 ++++++++ Lib/ssl.py | 2 +- Lib/test/test_httplib.py | 4 ++-- Lib/test/test_ssl.py | 3 ++- .../2024-12-17-18-20-37.gh-issue-128035.JwqHdB.rst | 1 + Modules/_ssl.c | 6 ++++++ 7 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-18-20-37.gh-issue-128035.JwqHdB.rst diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index f07d151a885692..9d7b6aa66cd443 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -934,6 +934,12 @@ Constants .. versionadded:: 3.13 +.. data:: HAS_PHA + + Whether the OpenSSL library has built-in support for TLS-PHA. + + .. versionadded:: next + .. data:: CHANNEL_BINDING_TYPES List of supported TLS channel binding types. Strings in this list diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 97a37a82f76b9b..0dcee56b7d233f 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -584,6 +584,14 @@ pydoc (Contributed by Jelle Zijlstra in :gh:`101552`.) +ssl +--- + +* Indicate through :data:`ssl.HAS_PHA` whether the :mod:`ssl` module supports + TLSv1.3 post-handshake client authentication (PHA). + (Contributed by Will Childs-Klein in :gh:`128036`.) + + symtable -------- diff --git a/Lib/ssl.py b/Lib/ssl.py index c8703b046cfd4b..05df4ad7f0f05c 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -116,7 +116,7 @@ from _ssl import ( HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_SSLv2, HAS_SSLv3, HAS_TLSv1, - HAS_TLSv1_1, HAS_TLSv1_2, HAS_TLSv1_3, HAS_PSK + HAS_TLSv1_1, HAS_TLSv1_2, HAS_TLSv1_3, HAS_PSK, HAS_PHA ) from _ssl import _DEFAULT_CIPHERS, _OPENSSL_API_VERSION diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 9d853d254db7c6..89963dadeb152b 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -2073,8 +2073,8 @@ def test_host_port(self): def test_tls13_pha(self): import ssl - if not ssl.HAS_TLSv1_3: - self.skipTest('TLS 1.3 support required') + if not ssl.HAS_TLSv1_3 or not ssl.HAS_PHA: + self.skipTest('TLS 1.3 PHA support required') # just check status of PHA flag h = client.HTTPSConnection('localhost', 443) self.assertTrue(h._context.post_handshake_auth) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 3f6f890bbdc658..c16ef3f96f9a21 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -4494,7 +4494,8 @@ def server_callback(identity): s.connect((HOST, server.port)) -@unittest.skipUnless(has_tls_version('TLSv1_3'), "Test needs TLS 1.3") +@unittest.skipUnless(has_tls_version('TLSv1_3') and ssl.HAS_PHA, + "Test needs TLS 1.3 PHA") class TestPostHandshakeAuth(unittest.TestCase): def test_pha_setter(self): protocols = [ diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-18-20-37.gh-issue-128035.JwqHdB.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-18-20-37.gh-issue-128035.JwqHdB.rst new file mode 100644 index 00000000000000..27815d48425334 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-18-20-37.gh-issue-128035.JwqHdB.rst @@ -0,0 +1 @@ +Indicate through :data:`ssl.HAS_PHA` whether the :mod:`ssl` module supports TLSv1.3 post-handshake client authentication (PHA). Patch by Will Childs-Klein. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index e7df132869fee6..74cf99957389e2 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -6553,6 +6553,12 @@ sslmodule_init_constants(PyObject *m) addbool(m, "HAS_PSK", 1); #endif +#ifdef SSL_VERIFY_POST_HANDSHAKE + addbool(m, "HAS_PHA", 1); +#else + addbool(m, "HAS_PHA", 0); +#endif + #undef addbool #undef ADD_INT_CONST From d9ed42bc00c74b3150be2a0eb28da03e01dffcc7 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Wed, 25 Dec 2024 09:26:51 +0200 Subject: [PATCH 104/376] gh-128201: Fix ``DeprecationWarning`` in ``test_pdb`` (#128202) --- Lib/test/test_pdb.py | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 58295cff84310f..9b0806d8b2a9bd 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -2065,10 +2065,7 @@ def test_pdb_next_command_for_coroutine(): ... await test_coro() >>> def test_function(): - ... loop = asyncio.new_event_loop() - ... loop.run_until_complete(test_main()) - ... loop.close() - ... asyncio.set_event_loop_policy(None) + ... asyncio.run(test_main()) ... print("finished") >>> with PdbTestInput(['step', @@ -2129,10 +2126,7 @@ def test_pdb_next_command_for_asyncgen(): ... await test_coro() >>> def test_function(): - ... loop = asyncio.new_event_loop() - ... loop.run_until_complete(test_main()) - ... loop.close() - ... asyncio._set_event_loop_policy(None) + ... asyncio.run(test_main()) ... print("finished") >>> with PdbTestInput(['step', @@ -2250,10 +2244,7 @@ def test_pdb_return_command_for_coroutine(): ... await test_coro() >>> def test_function(): - ... loop = asyncio.new_event_loop() - ... loop.run_until_complete(test_main()) - ... loop.close() - ... asyncio._set_event_loop_policy(None) + ... asyncio.run(test_main()) ... print("finished") >>> with PdbTestInput(['step', @@ -2350,10 +2341,7 @@ def test_pdb_until_command_for_coroutine(): ... await test_coro() >>> def test_function(): - ... loop = asyncio.new_event_loop() - ... loop.run_until_complete(test_main()) - ... loop.close() - ... asyncio._set_event_loop_policy(None) + ... asyncio.run(test_main()) ... print("finished") >>> with PdbTestInput(['step', From 76f1785657fde0ebee082b0df54da8f7c911c369 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 25 Dec 2024 17:51:27 +0530 Subject: [PATCH 105/376] gh-128002: use internal llist implementation for asyncio tasks (#128256) --- Modules/_asynciomodule.c | 70 ++++++++++++---------------------------- 1 file changed, 20 insertions(+), 50 deletions(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 27c16364457336..603b77a70b15d4 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -6,9 +6,9 @@ #include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION_MUT() #include "pycore_dict.h" // _PyDict_GetItem_KnownHash() #include "pycore_freelist.h" // _Py_FREELIST_POP() +#include "pycore_llist.h" // struct llist_node #include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_moduleobject.h" // _PyModule_GetState() -#include "pycore_object.h" // _Py_SetImmortalUntracked() #include "pycore_pyerrors.h" // _PyErr_ClearExcState() #include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() #include "pycore_pystate.h" // _PyThreadState_GET() @@ -60,8 +60,7 @@ typedef struct TaskObj { PyObject *task_coro; PyObject *task_name; PyObject *task_context; - struct TaskObj *next; - struct TaskObj *prev; + struct llist_node task_node; } TaskObj; typedef struct { @@ -136,21 +135,11 @@ typedef struct { /* Counter for autogenerated Task names */ uint64_t task_name_counter; - /* Circular linked-list of all tasks which are instances of asyncio.Task or subclasses - of it. Third party tasks implementations which don't inherit from - asyncio.Task are tracked separately using the 'non_asyncio_tasks' WeakSet. - `first` is used as a sentinel to mark the end of the linked-list. It avoids one - branch in checking for empty list when adding a new task, the list is - initialized with `head`, `head->next` and `head->prev` pointing to `first` - to mark an empty list. - + /* Head of circular linked-list of all tasks which are instances of `asyncio.Task` + or subclasses of it. Third party tasks implementations which don't inherit from + `asyncio.Task` are tracked separately using the `non_asyncio_tasks` WeakSet. */ - - struct { - TaskObj first; - TaskObj *head; - } asyncio_tasks; - + struct llist_node asyncio_tasks_head; } asyncio_state; static inline asyncio_state * @@ -1896,19 +1885,12 @@ register_task(asyncio_state *state, TaskObj *task) { ASYNCIO_STATE_LOCK(state); assert(Task_Check(state, task)); - assert(task != &state->asyncio_tasks.first); - if (task->next != NULL) { + if (task->task_node.next != NULL) { // already registered + assert(task->task_node.prev != NULL); goto exit; } - assert(task->prev == NULL); - assert(state->asyncio_tasks.head != NULL); - - task->next = state->asyncio_tasks.head; - task->prev = state->asyncio_tasks.head->prev; - state->asyncio_tasks.head->prev->next = task; - state->asyncio_tasks.head->prev = task; - + llist_insert_tail(&state->asyncio_tasks_head, &task->task_node); exit: ASYNCIO_STATE_UNLOCK(state); } @@ -1924,18 +1906,12 @@ unregister_task(asyncio_state *state, TaskObj *task) { ASYNCIO_STATE_LOCK(state); assert(Task_Check(state, task)); - assert(task != &state->asyncio_tasks.first); - if (task->next == NULL) { + if (task->task_node.next == NULL) { // not registered - assert(task->prev == NULL); - assert(state->asyncio_tasks.head != task); + assert(task->task_node.prev == NULL); goto exit; } - task->next->prev = task->prev; - task->prev->next = task->next; - task->next = NULL; - task->prev = NULL; - assert(state->asyncio_tasks.head != task); + llist_remove(&task->task_node); exit: ASYNCIO_STATE_UNLOCK(state); } @@ -3625,20 +3601,18 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) Py_DECREF(eager_iter); int err = 0; ASYNCIO_STATE_LOCK(state); - TaskObj *first = &state->asyncio_tasks.first; - TaskObj *head = state->asyncio_tasks.head->next; - Py_INCREF(head); - while (head != first) - { - if (add_one_task(state, tasks, (PyObject *)head, loop) < 0) { + struct llist_node *node; + llist_for_each_safe(node, &state->asyncio_tasks_head) { + TaskObj *task = llist_data(node, TaskObj, task_node); + Py_INCREF(task); + if (add_one_task(state, tasks, (PyObject *)task, loop) < 0) { + Py_DECREF(task); Py_DECREF(tasks); Py_DECREF(loop); - Py_DECREF(head); err = 1; break; } - Py_INCREF(head->next); - Py_SETREF(head, head->next); + Py_DECREF(task); } ASYNCIO_STATE_UNLOCK(state); if (err) { @@ -3847,11 +3821,7 @@ module_exec(PyObject *mod) { asyncio_state *state = get_asyncio_state(mod); - Py_SET_TYPE(&state->asyncio_tasks.first, state->TaskType); - _Py_SetImmortalUntracked((PyObject *)&state->asyncio_tasks.first); - state->asyncio_tasks.head = &state->asyncio_tasks.first; - state->asyncio_tasks.head->next = &state->asyncio_tasks.first; - state->asyncio_tasks.head->prev = &state->asyncio_tasks.first; + llist_init(&state->asyncio_tasks_head); #define CREATE_TYPE(m, tp, spec, base) \ do { \ From 81636d3bbd7f126692326bf957707e8a88c91739 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 25 Dec 2024 13:23:44 +0000 Subject: [PATCH 106/376] gh-128234: support emscripten and wasi in async contextlib tests by removing asyncio from contextlib async tests (#95888) Co-authored-by: Kumar Aditya --- Lib/test/test_contextlib_async.py | 94 ++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 33 deletions(-) diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py index 88dcdadd5e027d..d496aa611d1068 100644 --- a/Lib/test/test_contextlib_async.py +++ b/Lib/test/test_contextlib_async.py @@ -1,4 +1,4 @@ -import asyncio +import functools from contextlib import ( asynccontextmanager, AbstractAsyncContextManager, AsyncExitStack, nullcontext, aclosing, contextmanager) @@ -8,14 +8,31 @@ from test.test_contextlib import TestBaseExitStack -support.requires_working_socket(module=True) -def tearDownModule(): - asyncio._set_event_loop_policy(None) +def _run_async_fn(async_fn, /, *args, **kwargs): + coro = async_fn(*args, **kwargs) + try: + coro.send(None) + except StopIteration as e: + return e.value + else: + raise AssertionError("coroutine did not complete") + finally: + coro.close() -class TestAbstractAsyncContextManager(unittest.IsolatedAsyncioTestCase): +def _async_test(async_fn): + """Decorator to turn an async function into a synchronous function""" + @functools.wraps(async_fn) + def wrapper(*args, **kwargs): + return _run_async_fn(async_fn, *args, **kwargs) + return wrapper + + +class TestAbstractAsyncContextManager(unittest.TestCase): + + @_async_test async def test_enter(self): class DefaultEnter(AbstractAsyncContextManager): async def __aexit__(self, *args): @@ -27,6 +44,7 @@ async def __aexit__(self, *args): async with manager as context: self.assertIs(manager, context) + @_async_test async def test_slots(self): class DefaultAsyncContextManager(AbstractAsyncContextManager): __slots__ = () @@ -38,6 +56,7 @@ async def __aexit__(self, *args): manager = DefaultAsyncContextManager() manager.var = 42 + @_async_test async def test_async_gen_propagates_generator_exit(self): # A regression test for https://bugs.python.org/issue33786. @@ -88,8 +107,9 @@ class NoneAexit(ManagerFromScratch): self.assertFalse(issubclass(NoneAexit, AbstractAsyncContextManager)) -class AsyncContextManagerTestCase(unittest.IsolatedAsyncioTestCase): +class AsyncContextManagerTestCase(unittest.TestCase): + @_async_test async def test_contextmanager_plain(self): state = [] @asynccontextmanager @@ -103,6 +123,7 @@ async def woohoo(): state.append(x) self.assertEqual(state, [1, 42, 999]) + @_async_test async def test_contextmanager_finally(self): state = [] @asynccontextmanager @@ -120,6 +141,7 @@ async def woohoo(): raise ZeroDivisionError() self.assertEqual(state, [1, 42, 999]) + @_async_test async def test_contextmanager_traceback(self): @asynccontextmanager async def f(): @@ -175,6 +197,7 @@ class StopAsyncIterationSubclass(StopAsyncIteration): 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 async def whee(): @@ -184,6 +207,7 @@ async def whee(): # Calling __aexit__ should not result in an exception self.assertFalse(await ctx.__aexit__(TypeError, TypeError("foo"), None)) + @_async_test async def test_contextmanager_trap_yield_after_throw(self): @asynccontextmanager async def whoo(): @@ -199,6 +223,7 @@ async def whoo(): # The "gen" attribute is an implementation detail. self.assertFalse(ctx.gen.ag_suspended) + @_async_test async def test_contextmanager_trap_no_yield(self): @asynccontextmanager async def whoo(): @@ -208,6 +233,7 @@ async def whoo(): with self.assertRaises(RuntimeError): await ctx.__aenter__() + @_async_test async def test_contextmanager_trap_second_yield(self): @asynccontextmanager async def whoo(): @@ -221,6 +247,7 @@ async def whoo(): # The "gen" attribute is an implementation detail. self.assertFalse(ctx.gen.ag_suspended) + @_async_test async def test_contextmanager_non_normalised(self): @asynccontextmanager async def whoo(): @@ -234,6 +261,7 @@ async def whoo(): with self.assertRaises(SyntaxError): await ctx.__aexit__(RuntimeError, None, None) + @_async_test async def test_contextmanager_except(self): state = [] @asynccontextmanager @@ -251,6 +279,7 @@ async def woohoo(): raise ZeroDivisionError(999) self.assertEqual(state, [1, 42, 999]) + @_async_test async def test_contextmanager_except_stopiter(self): @asynccontextmanager async def woohoo(): @@ -277,6 +306,7 @@ class StopAsyncIterationSubclass(StopAsyncIteration): else: self.fail(f'{stop_exc} was suppressed') + @_async_test async def test_contextmanager_wrap_runtimeerror(self): @asynccontextmanager async def woohoo(): @@ -321,12 +351,14 @@ def test_contextmanager_doc_attrib(self): self.assertEqual(baz.__doc__, "Whee!") @support.requires_docstrings + @_async_test async def test_instance_docstring_given_cm_docstring(self): baz = self._create_contextmanager_attribs()(None) self.assertEqual(baz.__doc__, "Whee!") async with baz: pass # suppress warning + @_async_test async def test_keywords(self): # Ensure no keyword arguments are inhibited @asynccontextmanager @@ -335,6 +367,7 @@ async def woohoo(self, func, args, kwds): async with woohoo(self=11, func=22, args=33, kwds=44) as target: self.assertEqual(target, (11, 22, 33, 44)) + @_async_test async def test_recursive(self): depth = 0 ncols = 0 @@ -361,6 +394,7 @@ async def recursive(): self.assertEqual(ncols, 10) self.assertEqual(depth, 0) + @_async_test async def test_decorator(self): entered = False @@ -379,6 +413,7 @@ async def test(): await test() self.assertFalse(entered) + @_async_test async def test_decorator_with_exception(self): entered = False @@ -401,6 +436,7 @@ async def test(): await test() self.assertFalse(entered) + @_async_test async def test_decorating_method(self): @asynccontextmanager @@ -435,7 +471,7 @@ async def method(self, a, b, c=None): self.assertEqual(test.b, 2) -class AclosingTestCase(unittest.IsolatedAsyncioTestCase): +class AclosingTestCase(unittest.TestCase): @support.requires_docstrings def test_instance_docs(self): @@ -443,6 +479,7 @@ def test_instance_docs(self): obj = aclosing(None) self.assertEqual(obj.__doc__, cm_docstring) + @_async_test async def test_aclosing(self): state = [] class C: @@ -454,6 +491,7 @@ async def aclose(self): self.assertEqual(x, y) self.assertEqual(state, [1]) + @_async_test async def test_aclosing_error(self): state = [] class C: @@ -467,6 +505,7 @@ async def aclose(self): 1 / 0 self.assertEqual(state, [1]) + @_async_test async def test_aclosing_bpo41229(self): state = [] @@ -492,45 +531,27 @@ async def agenfunc(): self.assertEqual(state, [1]) -class TestAsyncExitStack(TestBaseExitStack, unittest.IsolatedAsyncioTestCase): +class TestAsyncExitStack(TestBaseExitStack, unittest.TestCase): class SyncAsyncExitStack(AsyncExitStack): - @staticmethod - def run_coroutine(coro): - loop = asyncio.new_event_loop() - t = loop.create_task(coro) - t.add_done_callback(lambda f: loop.stop()) - loop.run_forever() - - exc = t.exception() - if not exc: - return t.result() - else: - context = exc.__context__ - - try: - raise exc - except: - exc.__context__ = context - raise exc def close(self): - return self.run_coroutine(self.aclose()) + return _run_async_fn(self.aclose) def __enter__(self): - return self.run_coroutine(self.__aenter__()) + return _run_async_fn(self.__aenter__) def __exit__(self, *exc_details): - return self.run_coroutine(self.__aexit__(*exc_details)) + return _run_async_fn(self.__aexit__, *exc_details) exit_stack = SyncAsyncExitStack callback_error_internal_frames = [ - ('__exit__', 'return self.run_coroutine(self.__aexit__(*exc_details))'), - ('run_coroutine', 'raise exc'), - ('run_coroutine', 'raise exc'), + ('__exit__', 'return _run_async_fn(self.__aexit__, *exc_details)'), + ('_run_async_fn', 'coro.send(None)'), ('__aexit__', 'raise exc'), ('__aexit__', 'cb_suppress = cb(*exc_details)'), ] + @_async_test async def test_async_callback(self): expected = [ ((), {}), @@ -573,6 +594,7 @@ async def _exit(*args, **kwds): stack.push_async_callback(callback=_exit, arg=3) self.assertEqual(result, []) + @_async_test async def test_async_push(self): exc_raised = ZeroDivisionError async def _expect_exc(exc_type, exc, exc_tb): @@ -608,6 +630,7 @@ async def __aexit__(self, *exc_details): self.assertIs(stack._exit_callbacks[-1][1], _expect_exc) 1/0 + @_async_test async def test_enter_async_context(self): class TestCM(object): async def __aenter__(self): @@ -629,6 +652,7 @@ async def _exit(): self.assertEqual(result, [1, 2, 3, 4]) + @_async_test async def test_enter_async_context_errors(self): class LacksEnterAndExit: pass @@ -648,6 +672,7 @@ async def __aenter__(self): await stack.enter_async_context(LacksExit()) self.assertFalse(stack._exit_callbacks) + @_async_test async def test_async_exit_exception_chaining(self): # Ensure exception chaining matches the reference behaviour async def raise_exc(exc): @@ -679,6 +704,7 @@ async def suppress_exc(*exc_details): self.assertIsInstance(inner_exc, ValueError) self.assertIsInstance(inner_exc.__context__, ZeroDivisionError) + @_async_test async def test_async_exit_exception_explicit_none_context(self): # Ensure AsyncExitStack chaining matches actual nested `with` statements # regarding explicit __context__ = None. @@ -713,6 +739,7 @@ async def my_cm_with_exit_stack(): else: self.fail("Expected IndexError, but no exception was raised") + @_async_test async def test_instance_bypass_async(self): class Example(object): pass cm = Example() @@ -725,7 +752,8 @@ class Example(object): pass self.assertIs(stack._exit_callbacks[-1][1], cm) -class TestAsyncNullcontext(unittest.IsolatedAsyncioTestCase): +class TestAsyncNullcontext(unittest.TestCase): + @_async_test async def test_async_nullcontext(self): class C: pass From 5c814c83cdd3dc42bd9682106ffb7ade7ce6b5b3 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Wed, 25 Dec 2024 18:42:04 +0100 Subject: [PATCH 107/376] gh-128198: Add missing error checks for usages of PyIter_Next() (GH-128199) --- Modules/_asynciomodule.c | 13 +++++++++++++ Objects/frameobject.c | 4 ++++ Objects/namespaceobject.c | 4 ++++ 3 files changed, 21 insertions(+) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 603b77a70b15d4..74db4c74af905a 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -3599,6 +3599,13 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) Py_DECREF(item); } Py_DECREF(eager_iter); + + if (PyErr_Occurred()) { + Py_DECREF(tasks); + Py_DECREF(loop); + return NULL; + } + int err = 0; ASYNCIO_STATE_LOCK(state); struct llist_node *node; @@ -3636,6 +3643,12 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) } Py_DECREF(scheduled_iter); Py_DECREF(loop); + + if (PyErr_Occurred()) { + Py_DECREF(tasks); + return NULL; + } + return tasks; } diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 10fd3a982c36f4..4f0040df4f3017 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -264,6 +264,10 @@ framelocalsproxy_merge(PyObject* self, PyObject* other) Py_DECREF(iter); + if (PyErr_Occurred()) { + return -1; + } + return 0; } diff --git a/Objects/namespaceobject.c b/Objects/namespaceobject.c index 5b7547103a2b3f..4ef3bd92f5a569 100644 --- a/Objects/namespaceobject.c +++ b/Objects/namespaceobject.c @@ -141,6 +141,10 @@ namespace_repr(PyObject *ns) goto error; } + if (PyErr_Occurred()) { + goto error; + } + separator = PyUnicode_FromString(", "); if (separator == NULL) goto error; From 3eb746a7b9122c7c3cfc3fbd41d9d079d8b02845 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 26 Dec 2024 20:02:23 +0530 Subject: [PATCH 108/376] gh-127949: add docs for asyncio policy deprecation (#128269) --- Doc/library/asyncio-policy.rst | 8 ++++++++ Doc/whatsnew/3.14.rst | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst index ea7fe957da7c40..9f86234ce941d1 100644 --- a/Doc/library/asyncio-policy.rst +++ b/Doc/library/asyncio-policy.rst @@ -7,6 +7,14 @@ Policies ======== +.. warning:: + + Policies are deprecated and will be removed in Python 3.16. + Users are encouraged to use the :func:`asyncio.run` function + or the :class:`asyncio.Runner` with *loop_factory* to use + the desired loop implementation. + + An event loop policy is a global object used to get and set the current :ref:`event loop `, as well as create new event loops. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 0dcee56b7d233f..935c61c474e889 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -691,10 +691,36 @@ Deprecated (Contributed by Serhiy Storchaka in :gh:`58032`.) * :mod:`asyncio`: - :func:`!asyncio.iscoroutinefunction` is deprecated - and will be removed in Python 3.16, - use :func:`inspect.iscoroutinefunction` instead. - (Contributed by Jiahao Li and Kumar Aditya in :gh:`122875`.) + + * :func:`!asyncio.iscoroutinefunction` is deprecated + and will be removed in Python 3.16; + use :func:`inspect.iscoroutinefunction` instead. + (Contributed by Jiahao Li and Kumar Aditya in :gh:`122875`.) + + * :mod:`asyncio` policy system is deprecated and will be removed in Python 3.16. + In particular, the following classes and functions are deprecated: + + * :class:`asyncio.AbstractEventLoopPolicy` + * :class:`asyncio.DefaultEventLoopPolicy` + * :class:`asyncio.WindowsSelectorEventLoopPolicy` + * :class:`asyncio.WindowsProactorEventLoopPolicy` + * :func:`asyncio.get_event_loop_policy` + * :func:`asyncio.set_event_loop_policy` + * :func:`asyncio.set_event_loop` + + Users should use :func:`asyncio.run` or :class:`asyncio.Runner` with + *loop_factory* to use the desired event loop implementation. + + For example, to use :class:`asyncio.SelectorEventLoop` on Windows:: + + import asyncio + + async def main(): + ... + + asyncio.run(main(), loop_factory=asyncio.SelectorEventLoop) + + (Contributed by Kumar Aditya in :gh:`127949`.) * :mod:`builtins`: Passing a complex number as the *real* or *imag* argument in the From c6563f3f22e715780e8182481879b0c9110b71ac Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Thu, 26 Dec 2024 15:39:15 +0100 Subject: [PATCH 109/376] gh-119786: Fix typos in `InternalDocs/frames.md` (#128275) Fix typos in `InternalDocs/frames.md` --- InternalDocs/frames.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/InternalDocs/frames.md b/InternalDocs/frames.md index 2598873ca98479..1a909009eea610 100644 --- a/InternalDocs/frames.md +++ b/InternalDocs/frames.md @@ -33,7 +33,7 @@ Each activation record is laid out as: * Stack This seems to provide the best performance without excessive complexity. -The specials have a fixed size, so the offset of the locals is know. The +The specials have a fixed size, so the offset of the locals is known. The interpreter needs to hold two pointers, a frame pointer and a stack pointer. #### Alternative layout @@ -52,7 +52,7 @@ an extra pointer for the locals, which can hurt performance. ### Generators and Coroutines Generators and coroutines contain a `_PyInterpreterFrame` -The specials sections contains the following pointers: +The specials section contains the following pointers: * Globals dict * Builtins dict @@ -69,7 +69,7 @@ and builtins, than strong references to both globals and builtins. When creating a backtrace or when calling `sys._getframe()` the frame becomes visible to Python code. When this happens a new `PyFrameObject` is created -and a strong reference to it placed in the `frame_obj` field of the specials +and a strong reference to it is placed in the `frame_obj` field of the specials section. The `frame_obj` field is initially `NULL`. The `PyFrameObject` may outlive a stack-allocated `_PyInterpreterFrame`. @@ -128,7 +128,7 @@ The `return_offset` field determines where a `RETURN` should go in the caller, relative to `instr_ptr`. It is only meaningful to the callee, so it needs to be set in any instruction that implements a call (to a Python function), including CALL, SEND and BINARY_SUBSCR_GETITEM, among others. If there is no -callee, then return_offset is meaningless. It is necessary to have a separate +callee, then return_offset is meaningless. It is necessary to have a separate field for the return offset because (1) if we apply this offset to `instr_ptr` while executing the `RETURN`, this is too early and would lose us information about the previous instruction which we could need for introspecting and From 8a26c7b2af59b999ae41b8189295cc550f297f6e Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi Date: Thu, 26 Dec 2024 23:39:44 +0900 Subject: [PATCH 110/376] Docs: Fix comment out in `c-api/typeobj.rst` (#128266) --- Doc/c-api/typeobj.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index ba58cc1c26c70b..69c518d8e64cbc 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -1023,6 +1023,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is clear in the subtype and the :c:member:`~PyTypeObject.tp_traverse` and :c:member:`~PyTypeObject.tp_clear` fields in the subtype exist and have ``NULL`` values. + .. XXX are most flag bits *really* inherited individually? **Default:** From 42f7a00ae8b6b3fa09115e24b9512216c6c8978e Mon Sep 17 00:00:00 2001 From: da-woods Date: Thu, 26 Dec 2024 14:40:48 +0000 Subject: [PATCH 111/376] Clean up redundant ifdef in list getitem (#128257) It's already inside a `Py_GIL_DISABLED` block so the `#else` clause is always unused. --- Objects/listobject.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Objects/listobject.c b/Objects/listobject.c index a877bad66be45f..bbd53e7de94a31 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -335,11 +335,7 @@ list_item_impl(PyListObject *self, Py_ssize_t idx) if (!valid_index(idx, size)) { goto exit; } -#ifdef Py_GIL_DISABLED item = _Py_NewRefWithLock(self->ob_item[idx]); -#else - item = Py_NewRef(self->ob_item[idx]); -#endif exit: Py_END_CRITICAL_SECTION(); return item; From 9ddc388527477afdae17252628d63138631072ba Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 26 Dec 2024 14:50:20 +0000 Subject: [PATCH 112/376] gh-124761: add `socket.SO_REUSEPORT_LB` (#124961) --- Doc/library/socket.rst | 8 ++++++++ .../2024-10-04-09-56-45.gh-issue-124761.N4pSD6.rst | 1 + Modules/socketmodule.c | 3 +++ 3 files changed, 12 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-10-04-09-56-45.gh-issue-124761.N4pSD6.rst diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 58323ba6514eac..8ba2bd1dcce8cc 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -674,6 +674,14 @@ Constants .. availability:: Linux >= 3.9 +.. data:: SO_REUSEPORT_LB + + Constant to enable duplicate address and port bindings with load balancing. + + .. versionadded:: next + + .. availability:: FreeBSD >= 12.0 + .. data:: AF_HYPERV HV_PROTOCOL_RAW HVSOCKET_CONNECT_TIMEOUT diff --git a/Misc/NEWS.d/next/Library/2024-10-04-09-56-45.gh-issue-124761.N4pSD6.rst b/Misc/NEWS.d/next/Library/2024-10-04-09-56-45.gh-issue-124761.N4pSD6.rst new file mode 100644 index 00000000000000..797dd31b368548 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-04-09-56-45.gh-issue-124761.N4pSD6.rst @@ -0,0 +1 @@ +Add :data:`~socket.SO_REUSEPORT_LB` constant to :mod:`socket` for FreeBSD. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 9394f1c940bedf..1e95be9b1bc9f4 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -7916,6 +7916,9 @@ socket_exec(PyObject *m) ADD_INT_MACRO(m, SO_REUSEPORT); #endif #endif +#ifdef SO_REUSEPORT_LB + ADD_INT_MACRO(m, SO_REUSEPORT_LB); +#endif #ifdef SO_SNDBUF ADD_INT_MACRO(m, SO_SNDBUF); #endif From fb0b94223d481ca58b7c77332348d1ab2c9ab272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 26 Dec 2024 16:03:57 +0100 Subject: [PATCH 113/376] gh-87138: convert blake2b/2s types to heap types (#127669) --- Modules/blake2module.c | 89 ++++++++++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 20 deletions(-) diff --git a/Modules/blake2module.c b/Modules/blake2module.c index 94cdfe7fd2e962..6723e7de4675a5 100644 --- a/Modules/blake2module.c +++ b/Modules/blake2module.c @@ -379,13 +379,13 @@ class _blake2.blake2s "Blake2Object *" "&PyBlake2_BLAKE2sType" static Blake2Object * new_Blake2Object(PyTypeObject *type) { - Blake2Object *self; - self = (Blake2Object *)type->tp_alloc(type, 0); + Blake2Object *self = PyObject_GC_New(Blake2Object, type); if (self == NULL) { return NULL; } HASHLIB_INIT_MUTEX(self); + PyObject_GC_Track(self); return self; } @@ -454,7 +454,28 @@ py_blake2b_or_s_new(PyTypeObject *type, PyObject *data, int digest_size, } self->impl = type_to_impl(type); - + // Ensure that the states are NULL-initialized in case of an error. + // See: py_blake2_clear() for more details. + switch (self->impl) { +#if HACL_CAN_COMPILE_SIMD256 + case Blake2b_256: + self->blake2b_256_state = NULL; + break; +#endif +#if HACL_CAN_COMPILE_SIMD128 + case Blake2s_128: + self->blake2s_128_state = NULL; + break; +#endif + case Blake2b: + self->blake2b_state = NULL; + break; + case Blake2s: + self->blake2s_state = NULL; + break; + default: + Py_UNREACHABLE(); + } // Using Blake2b because we statically know that these are greater than the // Blake2s sizes -- this avoids a VLA. uint8_t salt_[HACL_HASH_BLAKE2B_SALT_BYTES] = { 0 }; @@ -595,7 +616,7 @@ py_blake2b_or_s_new(PyTypeObject *type, PyObject *data, int digest_size, return (PyObject *)self; error: - Py_XDECREF(self); + Py_XDECREF(self); return NULL; } @@ -875,46 +896,70 @@ static PyGetSetDef py_blake2b_getsetters[] = { {NULL} }; - -static void -py_blake2b_dealloc(Blake2Object *self) +static int +py_blake2_clear(PyObject *op) { + Blake2Object *self = (Blake2Object *)op; + // The initialization function uses PyObject_GC_New() but explicitly + // initializes the HACL* internal state to NULL before allocating + // it. If an error occurs in the constructor, we should only free + // states that were allocated (i.e. that are not NULL). switch (self->impl) { #if HACL_CAN_COMPILE_SIMD256 case Blake2b_256: - if (self->blake2b_256_state != NULL) + if (self->blake2b_256_state != NULL) { Hacl_Hash_Blake2b_Simd256_free(self->blake2b_256_state); + self->blake2b_256_state = NULL; + } break; #endif #if HACL_CAN_COMPILE_SIMD128 case Blake2s_128: - if (self->blake2s_128_state != NULL) + if (self->blake2s_128_state != NULL) { Hacl_Hash_Blake2s_Simd128_free(self->blake2s_128_state); + self->blake2s_128_state = NULL; + } break; #endif case Blake2b: - // This happens if we hit "goto error" in the middle of the - // initialization function. We leverage the fact that tp_alloc - // guarantees that the contents of the object are NULL-initialized - // (see documentation for PyType_GenericAlloc) to detect this case. - if (self->blake2b_state != NULL) + if (self->blake2b_state != NULL) { Hacl_Hash_Blake2b_free(self->blake2b_state); + self->blake2b_state = NULL; + } break; case Blake2s: - if (self->blake2s_state != NULL) + if (self->blake2s_state != NULL) { Hacl_Hash_Blake2s_free(self->blake2s_state); + self->blake2s_state = NULL; + } break; default: Py_UNREACHABLE(); } + return 0; +} +static void +py_blake2_dealloc(PyObject *self) +{ PyTypeObject *type = Py_TYPE(self); - PyObject_Free(self); + PyObject_GC_UnTrack(self); + (void)py_blake2_clear(self); + type->tp_free(self); Py_DECREF(type); } +static int +py_blake2_traverse(PyObject *self, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(self)); + return 0; +} + static PyType_Slot blake2b_type_slots[] = { - {Py_tp_dealloc, py_blake2b_dealloc}, + {Py_tp_clear, py_blake2_clear}, + {Py_tp_dealloc, py_blake2_dealloc}, + {Py_tp_traverse, py_blake2_traverse}, {Py_tp_doc, (char *)py_blake2b_new__doc__}, {Py_tp_methods, py_blake2b_methods}, {Py_tp_getset, py_blake2b_getsetters}, @@ -923,7 +968,9 @@ static PyType_Slot blake2b_type_slots[] = { }; static PyType_Slot blake2s_type_slots[] = { - {Py_tp_dealloc, py_blake2b_dealloc}, + {Py_tp_clear, py_blake2_clear}, + {Py_tp_dealloc, py_blake2_dealloc}, + {Py_tp_traverse, py_blake2_traverse}, {Py_tp_doc, (char *)py_blake2s_new__doc__}, {Py_tp_methods, py_blake2b_methods}, {Py_tp_getset, py_blake2b_getsetters}, @@ -936,13 +983,15 @@ static PyType_Slot blake2s_type_slots[] = { static PyType_Spec blake2b_type_spec = { .name = "_blake2.blake2b", .basicsize = sizeof(Blake2Object), - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE + | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HEAPTYPE, .slots = blake2b_type_slots }; static PyType_Spec blake2s_type_spec = { .name = "_blake2.blake2s", .basicsize = sizeof(Blake2Object), - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE + | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HEAPTYPE, .slots = blake2s_type_slots }; From 3bd7730bbda3e38db5920a7b8c95958ca90342bf Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 26 Dec 2024 16:17:22 +0100 Subject: [PATCH 114/376] gh-126868: Add freelist for compact ints to `_PyLong_New` (#128181) Co-authored-by: Kumar Aditya --- ...-12-22-15-47-44.gh-issue-126868.RpjKez.rst | 1 + Objects/longobject.c | 30 +++++++++++-------- 2 files changed, 19 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-22-15-47-44.gh-issue-126868.RpjKez.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-22-15-47-44.gh-issue-126868.RpjKez.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-22-15-47-44.gh-issue-126868.RpjKez.rst new file mode 100644 index 00000000000000..ede383deb4ad31 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-22-15-47-44.gh-issue-126868.RpjKez.rst @@ -0,0 +1 @@ +Increase usage of freelist for :class:`int` allocation. diff --git a/Objects/longobject.c b/Objects/longobject.c index bd7ff68d0899c6..d449a01cedf886 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -156,7 +156,7 @@ PyLongObject * _PyLong_New(Py_ssize_t size) { assert(size >= 0); - PyLongObject *result; + PyLongObject *result = NULL; if (size > (Py_ssize_t)MAX_LONG_DIGITS) { PyErr_SetString(PyExc_OverflowError, "too many digits in integer"); @@ -165,19 +165,25 @@ _PyLong_New(Py_ssize_t size) /* Fast operations for single digit integers (including zero) * assume that there is always at least one digit present. */ Py_ssize_t ndigits = size ? size : 1; - /* Number of bytes needed is: offsetof(PyLongObject, ob_digit) + - sizeof(digit)*size. Previous incarnations of this code used - sizeof() instead of the offsetof, but this risks being - incorrect in the presence of padding between the header - and the digits. */ - result = PyObject_Malloc(offsetof(PyLongObject, long_value.ob_digit) + - ndigits*sizeof(digit)); - if (!result) { - PyErr_NoMemory(); - return NULL; + + if (ndigits == 1) { + result = (PyLongObject *)_Py_FREELIST_POP(PyLongObject, ints); + } + if (result == NULL) { + /* Number of bytes needed is: offsetof(PyLongObject, ob_digit) + + sizeof(digit)*size. Previous incarnations of this code used + sizeof() instead of the offsetof, but this risks being + incorrect in the presence of padding between the header + and the digits. */ + result = PyObject_Malloc(offsetof(PyLongObject, long_value.ob_digit) + + ndigits*sizeof(digit)); + if (!result) { + PyErr_NoMemory(); + return NULL; + } + _PyObject_Init((PyObject*)result, &PyLong_Type); } _PyLong_SetSignAndDigitCount(result, size != 0, size); - _PyObject_Init((PyObject*)result, &PyLong_Type); /* The digit has to be initialized explicitly to avoid * use-of-uninitialized-value. */ result->long_value.ob_digit[0] = 0; From ea2b53739f1128184b4140decbeffeac6cfe966f Mon Sep 17 00:00:00 2001 From: Moshe Kaplan Date: Thu, 26 Dec 2024 16:53:37 -0500 Subject: [PATCH 115/376] Remove incorrect imports rationale comment in `http.server` (#128278) Remove reference to gethostbyaddr(), because it's not actually used within this code. --- Lib/http/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/http/server.py b/Lib/http/server.py index a6f7aecc78763f..a90c8d34c394db 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -99,7 +99,7 @@ import posixpath import select import shutil -import socket # For gethostbyaddr() +import socket import socketserver import sys import time From 401bba6b58497ce59e7b45ad33e43ae8c67abcb9 Mon Sep 17 00:00:00 2001 From: CF Bolz-Tereick Date: Fri, 27 Dec 2024 02:03:47 +0100 Subject: [PATCH 116/376] gh-127537: Add __class_getitem__ to the python implementation of functools.partial (#127537) --- Lib/functools.py | 3 +++ Lib/test/test_functools.py | 6 ++++++ .../Library/2024-12-04-10-39-29.gh-issue-83662.CG1s3m.rst | 5 +++++ 3 files changed, 14 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-12-04-10-39-29.gh-issue-83662.CG1s3m.rst diff --git a/Lib/functools.py b/Lib/functools.py index eff6540c7f606e..786b8aedfd77f5 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -433,6 +433,9 @@ def __setstate__(self, state): self._phcount = phcount self._merger = merger + __class_getitem__ = classmethod(GenericAlias) + + try: from _functools import partial, Placeholder, _PlaceholderType except ImportError: diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index ffd2adb8665b45..4a0252cb637a52 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -473,6 +473,12 @@ class A: self.assertEqual(a.cmeth(3, b=4), ((1, A, 3), {'a': 2, 'b': 4})) self.assertEqual(a.smeth(3, b=4), ((1, 3), {'a': 2, 'b': 4})) + def test_partial_genericalias(self): + alias = self.partial[int] + self.assertIs(alias.__origin__, self.partial) + self.assertEqual(alias.__args__, (int,)) + self.assertEqual(alias.__parameters__, ()) + @unittest.skipUnless(c_functools, 'requires the C _functools module') class TestPartialC(TestPartial, unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2024-12-04-10-39-29.gh-issue-83662.CG1s3m.rst b/Misc/NEWS.d/next/Library/2024-12-04-10-39-29.gh-issue-83662.CG1s3m.rst new file mode 100644 index 00000000000000..5e39933047993c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-04-10-39-29.gh-issue-83662.CG1s3m.rst @@ -0,0 +1,5 @@ +Add missing ``__class_getitem__`` method to the Python implementation of +:func:`functools.partial`, to make it compatible with the C version. This is +mainly relevant for alternative Python implementations like PyPy and +GraalPy, because CPython will usually use the C-implementation of that +function. From 08a0728d6c32986d35edb26872b4512a71ae60f3 Mon Sep 17 00:00:00 2001 From: Damien <81557462+Damien-Chen@users.noreply.github.com> Date: Fri, 27 Dec 2024 09:57:55 +0800 Subject: [PATCH 117/376] gh-125887: Update PyObject_HasAttr exception behavior (#125907) Update PyObject_HasAttr exception behavior Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- Doc/c-api/object.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index f97ade01e67850..a137688fe07545 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -111,7 +111,8 @@ Object Protocol .. note:: Exceptions that occur when this calls :meth:`~object.__getattr__` and - :meth:`~object.__getattribute__` methods are silently ignored. + :meth:`~object.__getattribute__` methods aren't propagated, + but instead given to :func:`sys.unraisablehook`. For proper error handling, use :c:func:`PyObject_HasAttrWithError`, :c:func:`PyObject_GetOptionalAttr` or :c:func:`PyObject_GetAttr` instead. From 0b5f1fae573a2c658eb000433ad7b87e9c40c697 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Fri, 27 Dec 2024 14:58:35 +0100 Subject: [PATCH 118/376] Mention loop_factory argument in docstring for asyncio.run() (#128288) --- Lib/asyncio/runners.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/asyncio/runners.py b/Lib/asyncio/runners.py index b9adf291d4817f..14397b4ad0c732 100644 --- a/Lib/asyncio/runners.py +++ b/Lib/asyncio/runners.py @@ -177,6 +177,7 @@ def run(main, *, debug=None, loop_factory=None): running in the same thread. If debug is True, the event loop will be run in debug mode. + If loop_factory is passed, it is used for new event loop creation. This function always creates a new event loop and closes it at the end. It should be used as a main entry point for asyncio programs, and should From 71de839ec987bb67b98fcfecfc687281841a713c Mon Sep 17 00:00:00 2001 From: donBarbos Date: Fri, 27 Dec 2024 18:12:25 +0400 Subject: [PATCH 119/376] gh-127089: Add missing description for codes in `http.HTTPStatus` (#127100) Co-authored-by: Ethan Furman Co-authored-by: Andrew Svetlov --- Lib/http/__init__.py | 41 +++++++++++++++++++++++++--------------- Lib/test/test_httplib.py | 41 +++++++++++++++++++++++++--------------- 2 files changed, 52 insertions(+), 30 deletions(-) diff --git a/Lib/http/__init__.py b/Lib/http/__init__.py index d64741ec0dd29a..9f278289420713 100644 --- a/Lib/http/__init__.py +++ b/Lib/http/__init__.py @@ -54,8 +54,9 @@ def is_server_error(self): CONTINUE = 100, 'Continue', 'Request received, please continue' SWITCHING_PROTOCOLS = (101, 'Switching Protocols', 'Switching to new protocol; obey Upgrade header') - PROCESSING = 102, 'Processing' - EARLY_HINTS = 103, 'Early Hints' + PROCESSING = 102, 'Processing', 'Server is processing the request' + EARLY_HINTS = (103, 'Early Hints', + 'Headers sent to prepare for the response') # success OK = 200, 'OK', 'Request fulfilled, document follows' @@ -67,9 +68,11 @@ def is_server_error(self): NO_CONTENT = 204, 'No Content', 'Request fulfilled, nothing follows' RESET_CONTENT = 205, 'Reset Content', 'Clear input form for further input' PARTIAL_CONTENT = 206, 'Partial Content', 'Partial content follows' - MULTI_STATUS = 207, 'Multi-Status' - ALREADY_REPORTED = 208, 'Already Reported' - IM_USED = 226, 'IM Used' + MULTI_STATUS = (207, 'Multi-Status', + 'Response contains multiple statuses in the body') + ALREADY_REPORTED = (208, 'Already Reported', + 'Operation has already been reported') + IM_USED = 226, 'IM Used', 'Request completed using instance manipulations' # redirection MULTIPLE_CHOICES = (300, 'Multiple Choices', @@ -128,15 +131,19 @@ def is_server_error(self): EXPECTATION_FAILED = (417, 'Expectation Failed', 'Expect condition could not be satisfied') IM_A_TEAPOT = (418, 'I\'m a Teapot', - 'Server refuses to brew coffee because it is a teapot.') + 'Server refuses to brew coffee because it is a teapot') MISDIRECTED_REQUEST = (421, 'Misdirected Request', 'Server is not able to produce a response') - UNPROCESSABLE_CONTENT = 422, 'Unprocessable Content' + UNPROCESSABLE_CONTENT = (422, 'Unprocessable Content', + 'Server is not able to process the contained instructions') UNPROCESSABLE_ENTITY = UNPROCESSABLE_CONTENT - LOCKED = 423, 'Locked' - FAILED_DEPENDENCY = 424, 'Failed Dependency' - TOO_EARLY = 425, 'Too Early' - UPGRADE_REQUIRED = 426, 'Upgrade Required' + LOCKED = 423, 'Locked', 'Resource of a method is locked' + FAILED_DEPENDENCY = (424, 'Failed Dependency', + 'Dependent action of the request failed') + TOO_EARLY = (425, 'Too Early', + 'Server refuses to process a request that might be replayed') + UPGRADE_REQUIRED = (426, 'Upgrade Required', + 'Server refuses to perform the request using the current protocol') PRECONDITION_REQUIRED = (428, 'Precondition Required', 'The origin server requires the request to be conditional') TOO_MANY_REQUESTS = (429, 'Too Many Requests', @@ -164,10 +171,14 @@ def is_server_error(self): 'The gateway server did not receive a timely response') HTTP_VERSION_NOT_SUPPORTED = (505, 'HTTP Version Not Supported', 'Cannot fulfill request') - VARIANT_ALSO_NEGOTIATES = 506, 'Variant Also Negotiates' - INSUFFICIENT_STORAGE = 507, 'Insufficient Storage' - LOOP_DETECTED = 508, 'Loop Detected' - NOT_EXTENDED = 510, 'Not Extended' + VARIANT_ALSO_NEGOTIATES = (506, 'Variant Also Negotiates', + 'Server has an internal configuration error') + INSUFFICIENT_STORAGE = (507, 'Insufficient Storage', + 'Server is not able to store the representation') + LOOP_DETECTED = (508, 'Loop Detected', + 'Server encountered an infinite loop while processing a request') + NOT_EXTENDED = (510, 'Not Extended', + 'Request does not meet the resource access policy') NETWORK_AUTHENTICATION_REQUIRED = (511, 'Network Authentication Required', 'The client needs to authenticate to gain network access') diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 89963dadeb152b..7a7ec555a2dbbb 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -594,8 +594,9 @@ def is_server_error(self): CONTINUE = 100, 'Continue', 'Request received, please continue' SWITCHING_PROTOCOLS = (101, 'Switching Protocols', 'Switching to new protocol; obey Upgrade header') - PROCESSING = 102, 'Processing' - EARLY_HINTS = 103, 'Early Hints' + PROCESSING = 102, 'Processing', 'Server is processing the request' + EARLY_HINTS = (103, 'Early Hints', + 'Headers sent to prepare for the response') # success OK = 200, 'OK', 'Request fulfilled, document follows' CREATED = 201, 'Created', 'Document created, URL follows' @@ -606,9 +607,11 @@ def is_server_error(self): NO_CONTENT = 204, 'No Content', 'Request fulfilled, nothing follows' RESET_CONTENT = 205, 'Reset Content', 'Clear input form for further input' PARTIAL_CONTENT = 206, 'Partial Content', 'Partial content follows' - MULTI_STATUS = 207, 'Multi-Status' - ALREADY_REPORTED = 208, 'Already Reported' - IM_USED = 226, 'IM Used' + MULTI_STATUS = (207, 'Multi-Status', + 'Response contains multiple statuses in the body') + ALREADY_REPORTED = (208, 'Already Reported', + 'Operation has already been reported') + IM_USED = 226, 'IM Used', 'Request completed using instance manipulations' # redirection MULTIPLE_CHOICES = (300, 'Multiple Choices', 'Object has several resources -- see URI list') @@ -665,15 +668,19 @@ def is_server_error(self): EXPECTATION_FAILED = (417, 'Expectation Failed', 'Expect condition could not be satisfied') IM_A_TEAPOT = (418, 'I\'m a Teapot', - 'Server refuses to brew coffee because it is a teapot.') + 'Server refuses to brew coffee because it is a teapot') MISDIRECTED_REQUEST = (421, 'Misdirected Request', 'Server is not able to produce a response') - UNPROCESSABLE_CONTENT = 422, 'Unprocessable Content' + UNPROCESSABLE_CONTENT = (422, 'Unprocessable Content', + 'Server is not able to process the contained instructions') UNPROCESSABLE_ENTITY = UNPROCESSABLE_CONTENT - LOCKED = 423, 'Locked' - FAILED_DEPENDENCY = 424, 'Failed Dependency' - TOO_EARLY = 425, 'Too Early' - UPGRADE_REQUIRED = 426, 'Upgrade Required' + LOCKED = 423, 'Locked', 'Resource of a method is locked' + FAILED_DEPENDENCY = (424, 'Failed Dependency', + 'Dependent action of the request failed') + TOO_EARLY = (425, 'Too Early', + 'Server refuses to process a request that might be replayed') + UPGRADE_REQUIRED = (426, 'Upgrade Required', + 'Server refuses to perform the request using the current protocol') PRECONDITION_REQUIRED = (428, 'Precondition Required', 'The origin server requires the request to be conditional') TOO_MANY_REQUESTS = (429, 'Too Many Requests', @@ -700,10 +707,14 @@ def is_server_error(self): 'The gateway server did not receive a timely response') HTTP_VERSION_NOT_SUPPORTED = (505, 'HTTP Version Not Supported', 'Cannot fulfill request') - VARIANT_ALSO_NEGOTIATES = 506, 'Variant Also Negotiates' - INSUFFICIENT_STORAGE = 507, 'Insufficient Storage' - LOOP_DETECTED = 508, 'Loop Detected' - NOT_EXTENDED = 510, 'Not Extended' + VARIANT_ALSO_NEGOTIATES = (506, 'Variant Also Negotiates', + 'Server has an internal configuration error') + INSUFFICIENT_STORAGE = (507, 'Insufficient Storage', + 'Server is not able to store the representation') + LOOP_DETECTED = (508, 'Loop Detected', + 'Server encountered an infinite loop while processing a request') + NOT_EXTENDED = (510, 'Not Extended', + 'Request does not meet the resource access policy') NETWORK_AUTHENTICATION_REQUIRED = (511, 'Network Authentication Required', 'The client needs to authenticate to gain network access') From 64173cd6f2d8dc95c6f8b67912d0edd1c1b707d5 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 27 Dec 2024 20:43:41 +0530 Subject: [PATCH 120/376] gh-127949: make deprecation of policy system more prominent (#128290) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/deprecations/pending-removal-in-3.16.rst | 27 +++++++++++++++++++- Doc/library/asyncio-eventloop.rst | 16 ++++++++---- Doc/library/asyncio-runner.rst | 6 +++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/Doc/deprecations/pending-removal-in-3.16.rst b/Doc/deprecations/pending-removal-in-3.16.rst index 6f6954b783a1ae..f2b818f14c63cd 100644 --- a/Doc/deprecations/pending-removal-in-3.16.rst +++ b/Doc/deprecations/pending-removal-in-3.16.rst @@ -19,10 +19,35 @@ Pending removal in Python 3.16 * :mod:`asyncio`: * :func:`!asyncio.iscoroutinefunction` is deprecated - and will be removed in Python 3.16, + and will be removed in Python 3.16; use :func:`inspect.iscoroutinefunction` instead. (Contributed by Jiahao Li and Kumar Aditya in :gh:`122875`.) + * :mod:`asyncio` policy system is deprecated and will be removed in Python 3.16. + In particular, the following classes and functions are deprecated: + + * :class:`asyncio.AbstractEventLoopPolicy` + * :class:`asyncio.DefaultEventLoopPolicy` + * :class:`asyncio.WindowsSelectorEventLoopPolicy` + * :class:`asyncio.WindowsProactorEventLoopPolicy` + * :func:`asyncio.get_event_loop_policy` + * :func:`asyncio.set_event_loop_policy` + * :func:`asyncio.set_event_loop` + + Users should use :func:`asyncio.run` or :class:`asyncio.Runner` with + *loop_factory* to use the desired event loop implementation. + + For example, to use :class:`asyncio.SelectorEventLoop` on Windows:: + + import asyncio + + async def main(): + ... + + asyncio.run(main(), loop_factory=asyncio.SelectorEventLoop) + + (Contributed by Kumar Aditya in :gh:`127949`.) + * :mod:`builtins`: * Bitwise inversion on boolean types, ``~True`` or ``~False`` diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 29f843123f8560..ccb362d8c31ddf 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -62,6 +62,13 @@ an event loop: .. versionchanged:: 3.14 Raises a :exc:`RuntimeError` if there is no current event loop. + .. note:: + + The :mod:`!asyncio` policy system is deprecated and will be removed + in Python 3.16; from there on, this function will always return the + running event loop. + + .. function:: set_event_loop(loop) Set *loop* as the current event loop for the current OS thread. @@ -1781,12 +1788,11 @@ By default asyncio is configured to use :class:`EventLoop`. import asyncio import selectors - class MyPolicy(asyncio.DefaultEventLoopPolicy): - def new_event_loop(self): - selector = selectors.SelectSelector() - return asyncio.SelectorEventLoop(selector) + async def main(): + ... - asyncio.set_event_loop_policy(MyPolicy()) + loop_factory = lambda: asyncio.SelectorEventLoop(selectors.SelectSelector()) + asyncio.run(main(), loop_factory=loop_factory) .. availability:: Unix, Windows. diff --git a/Doc/library/asyncio-runner.rst b/Doc/library/asyncio-runner.rst index 28d5aaf3692baa..48d78099fd3ce7 100644 --- a/Doc/library/asyncio-runner.rst +++ b/Doc/library/asyncio-runner.rst @@ -76,6 +76,12 @@ Running an asyncio Program *coro* can be any awaitable object. + .. note:: + + The :mod:`!asyncio` policy system is deprecated and will be removed + in Python 3.16; from there on, an explicit *loop_factory* is needed + to configure the event loop. + Runner context manager ====================== From aeb9b65aa26444529e4adc7d6e5b0d3dd9889ec2 Mon Sep 17 00:00:00 2001 From: Stephen Hansen Date: Fri, 27 Dec 2024 17:09:01 -0500 Subject: [PATCH 121/376] gh-127586: multiprocessing.Pool does not properly restore blocked signals (try 2) (GH-128011) Correct pthread_sigmask in resource_tracker to restore old signals Using SIG_UNBLOCK to remove blocked "ignored signals" may accidentally cause side effects if the calling parent already had said signals blocked to begin with and did not intend to unblock them when creating a pool. Use SIG_SETMASK instead with the previous mask of blocked signals to restore the original blocked set. Co-authored-by: Peter Bierma Co-authored-by: Gregory P. Smith --- Lib/multiprocessing/resource_tracker.py | 7 ++++--- Lib/test/_test_multiprocessing.py | 21 +++++++++++++++++++ ...-12-03-20-28-08.gh-issue-127586.zgotYF.rst | 3 +++ 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst diff --git a/Lib/multiprocessing/resource_tracker.py b/Lib/multiprocessing/resource_tracker.py index 20ddd9c50e3d88..90e036ae905afa 100644 --- a/Lib/multiprocessing/resource_tracker.py +++ b/Lib/multiprocessing/resource_tracker.py @@ -155,13 +155,14 @@ def ensure_running(self): # that can make the child die before it registers signal handlers # for SIGINT and SIGTERM. The mask is unregistered after spawning # the child. + prev_sigmask = None try: if _HAVE_SIGMASK: - signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS) + prev_sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS) pid = util.spawnv_passfds(exe, args, fds_to_pass) finally: - if _HAVE_SIGMASK: - signal.pthread_sigmask(signal.SIG_UNBLOCK, _IGNORED_SIGNALS) + if prev_sigmask is not None: + signal.pthread_sigmask(signal.SIG_SETMASK, prev_sigmask) except: os.close(w) raise diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 80b08b8ac66899..38a03f3391d31d 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -6045,6 +6045,27 @@ def test_resource_tracker_exit_code(self): cleanup=cleanup, ) + @unittest.skipUnless(hasattr(signal, "pthread_sigmask"), "pthread_sigmask is not available") + def test_resource_tracker_blocked_signals(self): + # + # gh-127586: Check that resource_tracker does not override blocked signals of caller. + # + from multiprocessing.resource_tracker import ResourceTracker + orig_sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, set()) + signals = {signal.SIGTERM, signal.SIGINT, signal.SIGUSR1} + + try: + for sig in signals: + signal.pthread_sigmask(signal.SIG_SETMASK, {sig}) + self.assertEqual(signal.pthread_sigmask(signal.SIG_BLOCK, set()), {sig}) + tracker = ResourceTracker() + tracker.ensure_running() + self.assertEqual(signal.pthread_sigmask(signal.SIG_BLOCK, set()), {sig}) + tracker._stop() + finally: + # restore sigmask to what it was before executing test + signal.pthread_sigmask(signal.SIG_SETMASK, orig_sigmask) + class TestSimpleQueue(unittest.TestCase): @classmethod diff --git a/Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst b/Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst new file mode 100644 index 00000000000000..80217bd4a10503 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst @@ -0,0 +1,3 @@ +:class:`multiprocessing.pool.Pool` now properly restores blocked signal handlers +of the parent thread when creating processes via either *spawn* or +*forkserver*. From aab51c3414bc815c4c31e8ef2a9003f4a546faa9 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sat, 28 Dec 2024 14:59:49 +0000 Subject: [PATCH 122/376] gh-128265: Support WASI/Emscripten on PDB tests, by removing asyncio from pdb tests (#128264) A part of `Lib/test/test_pdb.py` was previously unable to run on WASI/Emscripten platforms because it lacked support for `asyncio`. In fact, these tests could be rewritten without the `asyncio` framework because `test_pdb` tests the behavior of coroutines, which are not part of `asyncio`. Now reliance on the availability of `asyncio` has been removed and part of `test_pdb` that deals with coroutines working on WASI/Emscripten platforms. --- Lib/test/support/__init__.py | 29 +++++++++++ Lib/test/test_contextlib_async.py | 15 +----- Lib/test/test_inspect/test_inspect.py | 15 ++---- Lib/test/test_pdb.py | 71 +++++++++++++-------------- 4 files changed, 70 insertions(+), 60 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 5c738ffaa27713..cf3077f2a4a409 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -62,6 +62,7 @@ "force_not_colorized", "BrokenIter", "in_systemd_nspawn_sync_suppressed", + "run_no_yield_async_fn", "run_yielding_async_fn", "async_yield", ] @@ -2940,3 +2941,31 @@ def in_systemd_nspawn_sync_suppressed() -> bool: os.close(fd) return False + +def run_no_yield_async_fn(async_fn, /, *args, **kwargs): + coro = async_fn(*args, **kwargs) + try: + coro.send(None) + except StopIteration as e: + return e.value + else: + raise AssertionError("coroutine did not complete") + finally: + coro.close() + + +@types.coroutine +def async_yield(v): + return (yield v) + + +def run_yielding_async_fn(async_fn, /, *args, **kwargs): + coro = async_fn(*args, **kwargs) + try: + while True: + try: + coro.send(None) + except StopIteration as e: + return e.value + finally: + coro.close() diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py index d496aa611d1068..7750186e56a5cc 100644 --- a/Lib/test/test_contextlib_async.py +++ b/Lib/test/test_contextlib_async.py @@ -3,24 +3,13 @@ asynccontextmanager, AbstractAsyncContextManager, AsyncExitStack, nullcontext, aclosing, contextmanager) from test import support +from test.support import run_no_yield_async_fn as _run_async_fn import unittest import traceback from test.test_contextlib import TestBaseExitStack -def _run_async_fn(async_fn, /, *args, **kwargs): - coro = async_fn(*args, **kwargs) - try: - coro.send(None) - except StopIteration as e: - return e.value - else: - raise AssertionError("coroutine did not complete") - finally: - coro.close() - - def _async_test(async_fn): """Decorator to turn an async function into a synchronous function""" @functools.wraps(async_fn) @@ -546,7 +535,7 @@ def __exit__(self, *exc_details): exit_stack = SyncAsyncExitStack callback_error_internal_frames = [ ('__exit__', 'return _run_async_fn(self.__aexit__, *exc_details)'), - ('_run_async_fn', 'coro.send(None)'), + ('run_no_yield_async_fn', 'coro.send(None)'), ('__aexit__', 'raise exc'), ('__aexit__', 'cb_suppress = cb(*exc_details)'), ] diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index d536d04d2e7d88..345a57a5cfee2d 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -36,6 +36,7 @@ from test.support import cpython_only, import_helper from test.support import MISSING_C_DOCSTRINGS, ALWAYS_EQ +from test.support import run_no_yield_async_fn from test.support.import_helper import DirsOnSysPath, ready_to_import from test.support.os_helper import TESTFN, temp_cwd from test.support.script_helper import assert_python_ok, assert_python_failure, kill_python @@ -1161,19 +1162,11 @@ def f(self): sys.modules.pop("inspect_actual") def test_nested_class_definition_inside_async_function(self): - def run(coro): - try: - coro.send(None) - except StopIteration as e: - return e.value - else: - raise RuntimeError("coroutine did not complete synchronously!") - finally: - coro.close() + run = run_no_yield_async_fn - self.assertSourceEqual(run(mod2.func225()), 226, 227) + self.assertSourceEqual(run(mod2.func225), 226, 227) self.assertSourceEqual(mod2.cls226, 231, 235) - self.assertSourceEqual(run(mod2.cls226().func232()), 233, 234) + self.assertSourceEqual(run(mod2.cls226().func232), 233, 234) def test_class_definition_same_name_diff_methods(self): self.assertSourceEqual(mod2.cls296, 296, 298) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 9b0806d8b2a9bd..c5ee8c5fb25350 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -20,8 +20,7 @@ from test.support.pty_helper import run_pty, FakeInput from unittest.mock import patch -# gh-114275: WASI fails to run asyncio tests, similar skip than test_asyncio. -SKIP_ASYNCIO_TESTS = (not support.has_socket_support) +SKIP_CORO_TESTS = False class PdbTestInput(object): @@ -1987,7 +1986,7 @@ def test_next_until_return_at_return_event(): """ def test_pdb_next_command_for_generator(): - """Testing skip unwindng stack on yield for generators for "next" command + """Testing skip unwinding stack on yield for generators for "next" command >>> def test_gen(): ... yield 0 @@ -2049,23 +2048,23 @@ def test_pdb_next_command_for_generator(): finished """ -if not SKIP_ASYNCIO_TESTS: +if not SKIP_CORO_TESTS: def test_pdb_next_command_for_coroutine(): - """Testing skip unwindng stack on yield for coroutines for "next" command + """Testing skip unwinding stack on yield for coroutines for "next" command - >>> import asyncio + >>> from test.support import run_yielding_async_fn, async_yield >>> async def test_coro(): - ... await asyncio.sleep(0) - ... await asyncio.sleep(0) - ... await asyncio.sleep(0) + ... await async_yield(0) + ... await async_yield(0) + ... await async_yield(0) >>> async def test_main(): ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() ... await test_coro() >>> def test_function(): - ... asyncio.run(test_main()) + ... run_yielding_async_fn(test_main) ... print("finished") >>> with PdbTestInput(['step', @@ -2088,13 +2087,13 @@ def test_pdb_next_command_for_coroutine(): -> async def test_coro(): (Pdb) step > (2)test_coro() - -> await asyncio.sleep(0) + -> await async_yield(0) (Pdb) next > (3)test_coro() - -> await asyncio.sleep(0) + -> await async_yield(0) (Pdb) next > (4)test_coro() - -> await asyncio.sleep(0) + -> await async_yield(0) (Pdb) next Internal StopIteration > (3)test_main() @@ -2108,13 +2107,13 @@ def test_pdb_next_command_for_coroutine(): """ def test_pdb_next_command_for_asyncgen(): - """Testing skip unwindng stack on yield for coroutines for "next" command + """Testing skip unwinding stack on yield for coroutines for "next" command - >>> import asyncio + >>> from test.support import run_yielding_async_fn, async_yield >>> async def agen(): ... yield 1 - ... await asyncio.sleep(0) + ... await async_yield(0) ... yield 2 >>> async def test_coro(): @@ -2126,7 +2125,7 @@ def test_pdb_next_command_for_asyncgen(): ... await test_coro() >>> def test_function(): - ... asyncio.run(test_main()) + ... run_yielding_async_fn(test_main) ... print("finished") >>> with PdbTestInput(['step', @@ -2163,14 +2162,14 @@ def test_pdb_next_command_for_asyncgen(): -> yield 1 (Pdb) next > (3)agen() - -> await asyncio.sleep(0) + -> await async_yield(0) (Pdb) continue 2 finished """ def test_pdb_return_command_for_generator(): - """Testing no unwindng stack on yield for generators + """Testing no unwinding stack on yield for generators for "return" command >>> def test_gen(): @@ -2228,23 +2227,23 @@ def test_pdb_return_command_for_generator(): finished """ -if not SKIP_ASYNCIO_TESTS: +if not SKIP_CORO_TESTS: def test_pdb_return_command_for_coroutine(): - """Testing no unwindng stack on yield for coroutines for "return" command + """Testing no unwinding stack on yield for coroutines for "return" command - >>> import asyncio + >>> from test.support import run_yielding_async_fn, async_yield >>> async def test_coro(): - ... await asyncio.sleep(0) - ... await asyncio.sleep(0) - ... await asyncio.sleep(0) + ... await async_yield(0) + ... await async_yield(0) + ... await async_yield(0) >>> async def test_main(): ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() ... await test_coro() >>> def test_function(): - ... asyncio.run(test_main()) + ... run_yielding_async_fn(test_main) ... print("finished") >>> with PdbTestInput(['step', @@ -2264,16 +2263,16 @@ def test_pdb_return_command_for_coroutine(): -> async def test_coro(): (Pdb) step > (2)test_coro() - -> await asyncio.sleep(0) + -> await async_yield(0) (Pdb) next > (3)test_coro() - -> await asyncio.sleep(0) + -> await async_yield(0) (Pdb) continue finished """ def test_pdb_until_command_for_generator(): - """Testing no unwindng stack on yield for generators + """Testing no unwinding stack on yield for generators for "until" command if target breakpoint is not reached >>> def test_gen(): @@ -2320,20 +2319,20 @@ def test_pdb_until_command_for_generator(): finished """ -if not SKIP_ASYNCIO_TESTS: +if not SKIP_CORO_TESTS: def test_pdb_until_command_for_coroutine(): - """Testing no unwindng stack for coroutines + """Testing no unwinding stack for coroutines for "until" command if target breakpoint is not reached - >>> import asyncio + >>> from test.support import run_yielding_async_fn, async_yield >>> async def test_coro(): ... print(0) - ... await asyncio.sleep(0) + ... await async_yield(0) ... print(1) - ... await asyncio.sleep(0) + ... await async_yield(0) ... print(2) - ... await asyncio.sleep(0) + ... await async_yield(0) ... print(3) >>> async def test_main(): @@ -2341,7 +2340,7 @@ def test_pdb_until_command_for_coroutine(): ... await test_coro() >>> def test_function(): - ... asyncio.run(test_main()) + ... run_yielding_async_fn(test_main) ... print("finished") >>> with PdbTestInput(['step', From 2cf396c368a188e9142843e566ce6d8e6eb08999 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Sat, 28 Dec 2024 16:05:00 +0100 Subject: [PATCH 123/376] gh-119786: Fix typos in `InternalDocs/parser.md` (#128314) --- InternalDocs/parser.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/InternalDocs/parser.md b/InternalDocs/parser.md index 445b866fc0cb96..be47efe24356d4 100644 --- a/InternalDocs/parser.md +++ b/InternalDocs/parser.md @@ -56,7 +56,7 @@ an input string as its argument, and yields one of the following results: Note that "failure" results do not imply that the program is incorrect, nor do they necessarily mean that the parsing has failed. Since the choice operator is -ordered, a failure very often merely indicates "try the following option". A +ordered, a failure very often merely indicates "try the following option". A direct implementation of a PEG parser as a recursive descent parser will present exponential time performance in the worst case, because PEG parsers have infinite lookahead (this means that they can consider an arbitrary number of @@ -253,7 +253,7 @@ inside curly-braces, which specifies the return value of the alternative: If the action is omitted, a default action is generated: - If there is a single name in the rule, it gets returned. -- If there multiple names in the rule, a collection with all parsed +- If there are multiple names in the rule, a collection with all parsed expressions gets returned (the type of the collection will be different in C and Python). @@ -447,7 +447,7 @@ parser (the one used by the interpreter) just execute: $ make regen-pegen ``` -using the `Makefile` in the main directory. If you are on Windows you can +using the `Makefile` in the main directory. If you are on Windows you can use the Visual Studio project files to regenerate the parser or to execute: ```dos @@ -539,7 +539,7 @@ memoization is used. The C parser used by Python is highly optimized and memoization can be expensive both in memory and time. Although the memory cost is obvious (the parser needs memory for storing previous results in the cache) the execution time cost comes -for continuously checking if the given rule has a cache hit or not. In many +from continuously checking if the given rule has a cache hit or not. In many situations, just parsing it again can be faster. Pegen **disables memoization by default** except for rules with the special marker `memo` after the rule name (and type, if present): @@ -605,7 +605,7 @@ File "", line 1 SyntaxError: invalid syntax ``` -While soft keywords don't have this limitation if used in a context other the +While soft keywords don't have this limitation if used in a context other than one where they are defined as keywords: ```pycon From 492b224b991cd9027f1bc6d9988d01e94f764992 Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Sat, 28 Dec 2024 21:49:45 +0300 Subject: [PATCH 124/376] gh-128279: Enhance the NetBSD compatibility for thread naming (#128280) Enhance NetBSD compatibility for thread naming in _threadmodule.c. --- Modules/_threadmodule.c | 3 +++ configure | 1 + configure.ac | 1 + 3 files changed, 5 insertions(+) diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 75b34a8df7622c..2cbdfeb09b95ae 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -2438,6 +2438,9 @@ _thread_set_name_impl(PyObject *module, PyObject *name_obj) const char *name = PyBytes_AS_STRING(name_encoded); #ifdef __APPLE__ int rc = pthread_setname_np(name); +#elif defined(__NetBSD__) + pthread_t thread = pthread_self(); + int rc = pthread_setname_np(thread, "%s", (void *)name); #else pthread_t thread = pthread_self(); int rc = pthread_setname_np(thread, name); diff --git a/configure b/configure index a697bc1f87b012..299eff6bc3bf70 100755 --- a/configure +++ b/configure @@ -29148,6 +29148,7 @@ CPPFLAGS=$save_CPPFLAGS case "$ac_sys_system" in Linux*) PYTHREAD_NAME_MAXLEN=15;; # Linux and Android SunOS*) PYTHREAD_NAME_MAXLEN=31;; + NetBSD*) PYTHREAD_NAME_MAXLEN=31;; Darwin) PYTHREAD_NAME_MAXLEN=63;; iOS) PYTHREAD_NAME_MAXLEN=63;; FreeBSD*) PYTHREAD_NAME_MAXLEN=98;; diff --git a/configure.ac b/configure.ac index ebc15503f069cc..badb19d55895de 100644 --- a/configure.ac +++ b/configure.ac @@ -7514,6 +7514,7 @@ _RESTORE_VAR([CPPFLAGS]) case "$ac_sys_system" in Linux*) PYTHREAD_NAME_MAXLEN=15;; # Linux and Android SunOS*) PYTHREAD_NAME_MAXLEN=31;; + NetBSD*) PYTHREAD_NAME_MAXLEN=31;; Darwin) PYTHREAD_NAME_MAXLEN=63;; iOS) PYTHREAD_NAME_MAXLEN=63;; FreeBSD*) PYTHREAD_NAME_MAXLEN=98;; From f9a5a3a3ef34e63dc197156e9a5f57842859ca04 Mon Sep 17 00:00:00 2001 From: Calvin Bui <3604363+calvinbui@users.noreply.github.com> Date: Sun, 29 Dec 2024 08:05:34 +1100 Subject: [PATCH 125/376] gh-128192: support HTTP sha-256 digest authentication as per RFC-7617 (GH-128193) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit support sha-256 digest authentication Co-authored-by: Peter Bierma Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Co-authored-by: Gregory P. Smith --- Doc/library/urllib.request.rst | 3 +++ Doc/whatsnew/3.14.rst | 8 ++++++ Lib/test/test_urllib2.py | 25 ++++++++++++++++--- Lib/urllib/request.py | 7 ++++-- Misc/ACKS | 1 + ...-12-23-11-14-07.gh-issue-128192.02mEhD.rst | 2 ++ 6 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-23-11-14-07.gh-issue-128192.02mEhD.rst diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst index 3c07dc4adf434a..b3efde3f189566 100644 --- a/Doc/library/urllib.request.rst +++ b/Doc/library/urllib.request.rst @@ -411,6 +411,9 @@ The following classes are provided: :ref:`http-password-mgr` for information on the interface that must be supported. + .. versionchanged:: 3.14 + Added support for HTTP digest authentication algorithm ``SHA-256``. + .. class:: HTTPDigestAuthHandler(password_mgr=None) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 935c61c474e889..2767fd3ca48b29 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -646,6 +646,14 @@ unittest (Contributed by Jacob Walls in :gh:`80958`.) +urllib +------ + +* Upgrade HTTP digest authentication algorithm for :mod:`urllib.request` by + supporting SHA-256 digest authentication as specified in :rfc:`7616`. + (Contributed by Calvin Bui in :gh:`128193`.) + + uuid ---- diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 4a9e653515be5b..96d91c1f1c2f8a 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -1962,10 +1962,29 @@ def test_parse_proxy(self): self.assertRaises(ValueError, _parse_proxy, 'file:/ftp.example.com'), - def test_unsupported_algorithm(self): - handler = AbstractDigestAuthHandler() + +class TestDigestAlgorithms(unittest.TestCase): + def setUp(self): + self.handler = AbstractDigestAuthHandler() + + def test_md5_algorithm(self): + H, KD = self.handler.get_algorithm_impls('MD5') + self.assertEqual(H("foo"), "acbd18db4cc2f85cedef654fccc4a4d8") + self.assertEqual(KD("foo", "bar"), "4e99e8c12de7e01535248d2bac85e732") + + def test_sha_algorithm(self): + H, KD = self.handler.get_algorithm_impls('SHA') + self.assertEqual(H("foo"), "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33") + self.assertEqual(KD("foo", "bar"), "54dcbe67d21d5eb39493d46d89ae1f412d3bd6de") + + def test_sha256_algorithm(self): + H, KD = self.handler.get_algorithm_impls('SHA-256') + self.assertEqual(H("foo"), "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae") + self.assertEqual(KD("foo", "bar"), "a765a8beaa9d561d4c5cbed29d8f4e30870297fdfa9cb7d6e9848a95fec9f937") + + def test_invalid_algorithm(self): with self.assertRaises(ValueError) as exc: - handler.get_algorithm_impls('invalid') + self.handler.get_algorithm_impls('invalid') self.assertEqual( str(exc.exception), "Unsupported digest authentication algorithm 'invalid'" diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index c5a6a18a32bba1..0d1b594b8cf20b 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -1048,7 +1048,7 @@ def http_error_407(self, req, fp, code, msg, headers): class AbstractDigestAuthHandler: - # Digest authentication is specified in RFC 2617. + # Digest authentication is specified in RFC 2617/7616. # XXX The client does not inspect the Authentication-Info header # in a successful response. @@ -1176,11 +1176,14 @@ def get_authorization(self, req, chal): return base def get_algorithm_impls(self, algorithm): + # algorithm names taken from RFC 7616 Section 6.1 # lambdas assume digest modules are imported at the top level if algorithm == 'MD5': H = lambda x: hashlib.md5(x.encode("ascii")).hexdigest() - elif algorithm == 'SHA': + elif algorithm == 'SHA': # non-standard, retained for compatibility. H = lambda x: hashlib.sha1(x.encode("ascii")).hexdigest() + elif algorithm == 'SHA-256': + H = lambda x: hashlib.sha256(x.encode("ascii")).hexdigest() # XXX MD5-sess else: raise ValueError("Unsupported digest authentication " diff --git a/Misc/ACKS b/Misc/ACKS index 086930666822ad..c6e53317b37d78 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -258,6 +258,7 @@ Colm Buckley Erik de Bueger Jan-Hein Bührman Marc Bürg +Calvin Bui Lars Buitinck Artem Bulgakov Dick Bulterman diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-23-11-14-07.gh-issue-128192.02mEhD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-23-11-14-07.gh-issue-128192.02mEhD.rst new file mode 100644 index 00000000000000..b80ab715ffc7db --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-23-11-14-07.gh-issue-128192.02mEhD.rst @@ -0,0 +1,2 @@ +Upgrade HTTP digest authentication algorithm for :mod:`urllib.request` by +supporting SHA-256 digest authentication as specified in :rfc:`7616`. From c9159b7436363f0cfbddc3deae1bcf925b5c2d4b Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sun, 29 Dec 2024 06:22:29 +0000 Subject: [PATCH 126/376] expand the `asyncio.run_coroutine_threadsafe` recipes (#127576) Co-authored-by: Kumar Aditya --- Doc/library/asyncio-task.rst | 61 +++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index f27e858cf420f4..4541cf28de0605 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -1067,14 +1067,59 @@ Scheduling From Other Threads This function is meant to be called from a different OS thread than the one where the event loop is running. Example:: - # Create a coroutine - coro = asyncio.sleep(1, result=3) - - # Submit the coroutine to a given loop - future = asyncio.run_coroutine_threadsafe(coro, loop) - - # Wait for the result with an optional timeout argument - assert future.result(timeout) == 3 + def in_thread(loop: asyncio.AbstractEventLoop) -> None: + # Run some blocking IO + pathlib.Path("example.txt").write_text("hello world", encoding="utf8") + + # Create a coroutine + coro = asyncio.sleep(1, result=3) + + # Submit the coroutine to a given loop + future = asyncio.run_coroutine_threadsafe(coro, loop) + + # Wait for the result with an optional timeout argument + assert future.result(timeout=2) == 3 + + async def amain() -> None: + # Get the running loop + loop = asyncio.get_running_loop() + + # Run something in a thread + await asyncio.to_thread(in_thread, loop) + + It's also possible to run the other way around. Example:: + + @contextlib.contextmanager + def loop_in_thread() -> Generator[asyncio.AbstractEventLoop]: + loop_fut = concurrent.futures.Future[asyncio.AbstractEventLoop]() + stop_event = asyncio.Event() + + async def main() -> None: + loop_fut.set_result(asyncio.get_running_loop()) + await stop_event.wait() + + with concurrent.futures.ThreadPoolExecutor(1) as tpe: + complete_fut = tpe.submit(asyncio.run, main()) + for fut in concurrent.futures.as_completed((loop_fut, complete_fut)): + if fut is loop_fut: + loop = loop_fut.result() + try: + yield loop + finally: + loop.call_soon_threadsafe(stop_event.set) + else: + fut.result() + + # Create a loop in another thread + with loop_in_thread() as loop: + # Create a coroutine + coro = asyncio.sleep(1, result=3) + + # Submit the coroutine to a given loop + future = asyncio.run_coroutine_threadsafe(coro, loop) + + # Wait for the result with an optional timeout argument + assert future.result(timeout=2) == 3 If an exception is raised in the coroutine, the returned Future will be notified. It can also be used to cancel the task in From ffece5590e95e89d3b5b6847e3beb443ff65c3db Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 28 Dec 2024 22:32:32 -0800 Subject: [PATCH 127/376] gh-128192: mark new tests with skips based on hashlib algorithm availability (gh-128324) Puts the _hashlib get_fips_mode logic check into test.support rather than spreading it out among other tests. --- Lib/test/support/__init__.py | 8 ++++++++ Lib/test/test_urllib2.py | 12 +++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index cf3077f2a4a409..42e7b876594fa7 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2969,3 +2969,11 @@ def run_yielding_async_fn(async_fn, /, *args, **kwargs): return e.value finally: coro.close() + + +def is_libssl_fips_mode(): + try: + from _hashlib import get_fips_mode # ask _hashopenssl.c + except ImportError: + return False # more of a maybe, unless we add this to the _ssl module. + return get_fips_mode() != 0 diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 96d91c1f1c2f8a..085b24c25b2daa 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -27,6 +27,7 @@ import urllib.error import http.client + support.requires_working_socket(module=True) # XXX @@ -1963,20 +1964,29 @@ def test_parse_proxy(self): self.assertRaises(ValueError, _parse_proxy, 'file:/ftp.example.com'), -class TestDigestAlgorithms(unittest.TestCase): +skip_libssl_fips_mode = unittest.skipIf( + support.is_libssl_fips_mode(), + "conservative skip due to OpenSSL FIPS mode possible algorithm nerfing", +) + + +class TestDigestAuthAlgorithms(unittest.TestCase): def setUp(self): self.handler = AbstractDigestAuthHandler() + @skip_libssl_fips_mode def test_md5_algorithm(self): H, KD = self.handler.get_algorithm_impls('MD5') self.assertEqual(H("foo"), "acbd18db4cc2f85cedef654fccc4a4d8") self.assertEqual(KD("foo", "bar"), "4e99e8c12de7e01535248d2bac85e732") + @skip_libssl_fips_mode def test_sha_algorithm(self): H, KD = self.handler.get_algorithm_impls('SHA') self.assertEqual(H("foo"), "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33") self.assertEqual(KD("foo", "bar"), "54dcbe67d21d5eb39493d46d89ae1f412d3bd6de") + @skip_libssl_fips_mode def test_sha256_algorithm(self): H, KD = self.handler.get_algorithm_impls('SHA-256') self.assertEqual(H("foo"), "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae") From 7e819ce0f32068de7914cd1ba3b4b95e91ea9873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 29 Dec 2024 19:30:53 +0100 Subject: [PATCH 128/376] gh-123424: add `ZipInfo._for_archive` to set suitable default properties (#123429) --------- Co-authored-by: Jason R. Coombs --- Doc/library/zipfile.rst | 11 +++++++ Doc/whatsnew/3.14.rst | 8 +++++ Lib/test/test_zipfile/_path/test_path.py | 19 +---------- Lib/test/test_zipfile/test_core.py | 29 ++++++++++++++++ Lib/zipfile/__init__.py | 33 ++++++++++++------- ...-08-28-16-10-37.gh-issue-123424.u96_i6.rst | 1 + 6 files changed, 72 insertions(+), 29 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-08-28-16-10-37.gh-issue-123424.u96_i6.rst diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst index 5583c6b24be5c6..afe1cd5c75fcbb 100644 --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -84,6 +84,17 @@ The module defines the following items: formerly protected :attr:`!_compresslevel`. The older protected name continues to work as a property for backwards compatibility. + + .. method:: _for_archive(archive) + + Resolve the date_time, compression attributes, and external attributes + to suitable defaults as used by :meth:`ZipFile.writestr`. + + Returns self for chaining. + + .. versionadded:: 3.14 + + .. function:: is_zipfile(filename) Returns ``True`` if *filename* is a valid ZIP file based on its magic number, diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 2767fd3ca48b29..53415bb09bf080 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -661,6 +661,14 @@ uuid in :rfc:`9562`. (Contributed by Bénédikt Tran in :gh:`89083`.) +zipinfo +------- + +* Added :func:`ZipInfo._for_archive ` + to resolve suitable defaults for a :class:`~zipfile.ZipInfo` object + as used by :func:`ZipFile.writestr `. + + (Contributed by Bénédikt Tran in :gh:`123424`.) .. Add improved modules above alphabetically, not here at the end. diff --git a/Lib/test/test_zipfile/_path/test_path.py b/Lib/test/test_zipfile/_path/test_path.py index aba515536f0c1a..1ee45f5fc57104 100644 --- a/Lib/test/test_zipfile/_path/test_path.py +++ b/Lib/test/test_zipfile/_path/test_path.py @@ -634,7 +634,7 @@ def test_backslash_not_separator(self): """ data = io.BytesIO() zf = zipfile.ZipFile(data, "w") - zf.writestr(DirtyZipInfo.for_name("foo\\bar", zf), b"content") + zf.writestr(DirtyZipInfo("foo\\bar")._for_archive(zf), b"content") zf.filename = '' root = zipfile.Path(zf) (first,) = root.iterdir() @@ -657,20 +657,3 @@ class DirtyZipInfo(zipfile.ZipInfo): def __init__(self, filename, *args, **kwargs): super().__init__(filename, *args, **kwargs) self.filename = filename - - @classmethod - def for_name(cls, name, archive): - """ - Construct the same way that ZipFile.writestr does. - - TODO: extract this functionality and re-use - """ - self = cls(filename=name, date_time=time.localtime(time.time())[:6]) - self.compress_type = archive.compression - self.compress_level = archive.compresslevel - if self.filename.endswith('/'): # pragma: no cover - self.external_attr = 0o40775 << 16 # drwxrwxr-x - self.external_attr |= 0x10 # MS-DOS directory flag - else: - self.external_attr = 0o600 << 16 # ?rw------- - return self diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py index 124e088fd15b80..49f39b9337df85 100644 --- a/Lib/test/test_zipfile/test_core.py +++ b/Lib/test/test_zipfile/test_core.py @@ -5,6 +5,7 @@ import itertools import os import posixpath +import stat import struct import subprocess import sys @@ -2211,6 +2212,34 @@ def test_create_empty_zipinfo_repr(self): zi = zipfile.ZipInfo(filename="empty") self.assertEqual(repr(zi), "") + def test_for_archive(self): + base_filename = TESTFN2.rstrip('/') + + with zipfile.ZipFile(TESTFN, mode="w", compresslevel=1, + compression=zipfile.ZIP_STORED) as zf: + # no trailing forward slash + zi = zipfile.ZipInfo(base_filename)._for_archive(zf) + self.assertEqual(zi.compress_level, 1) + self.assertEqual(zi.compress_type, zipfile.ZIP_STORED) + # ?rw- --- --- + filemode = stat.S_IRUSR | stat.S_IWUSR + # filemode is stored as the highest 16 bits of external_attr + self.assertEqual(zi.external_attr >> 16, filemode) + self.assertEqual(zi.external_attr & 0xFF, 0) # no MS-DOS flag + + with zipfile.ZipFile(TESTFN, mode="w", compresslevel=1, + compression=zipfile.ZIP_STORED) as zf: + # with a trailing slash + zi = zipfile.ZipInfo(f'{base_filename}/')._for_archive(zf) + self.assertEqual(zi.compress_level, 1) + self.assertEqual(zi.compress_type, zipfile.ZIP_STORED) + # d rwx rwx r-x + filemode = stat.S_IFDIR + filemode |= stat.S_IRWXU | stat.S_IRWXG + filemode |= stat.S_IROTH | stat.S_IXOTH + self.assertEqual(zi.external_attr >> 16, filemode) + self.assertEqual(zi.external_attr & 0xFF, 0x10) # MS-DOS flag + def test_create_empty_zipinfo_default_attributes(self): """Ensure all required attributes are set.""" zi = zipfile.ZipInfo() diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py index f4d396abb6e639..052ef47b8f6598 100644 --- a/Lib/zipfile/__init__.py +++ b/Lib/zipfile/__init__.py @@ -13,6 +13,7 @@ import sys import threading import time +from typing import Self try: import zlib # We may need its compression method @@ -605,6 +606,24 @@ def from_file(cls, filename, arcname=None, *, strict_timestamps=True): return zinfo + def _for_archive(self, archive: ZipFile) -> Self: + """Resolve suitable defaults from the archive. + + Resolve the date_time, compression attributes, and external attributes + to suitable defaults as used by :method:`ZipFile.writestr`. + + Return self. + """ + self.date_time = time.localtime(time.time())[:6] + self.compress_type = archive.compression + self.compress_level = archive.compresslevel + if self.filename.endswith('/'): # pragma: no cover + self.external_attr = 0o40775 << 16 # drwxrwxr-x + self.external_attr |= 0x10 # MS-DOS directory flag + else: + self.external_attr = 0o600 << 16 # ?rw------- + return self + def is_dir(self): """Return True if this archive member is a directory.""" if self.filename.endswith('/'): @@ -1908,18 +1927,10 @@ def writestr(self, zinfo_or_arcname, data, the name of the file in the archive.""" if isinstance(data, str): data = data.encode("utf-8") - if not isinstance(zinfo_or_arcname, ZipInfo): - zinfo = ZipInfo(filename=zinfo_or_arcname, - date_time=time.localtime(time.time())[:6]) - zinfo.compress_type = self.compression - zinfo.compress_level = self.compresslevel - if zinfo.filename.endswith('/'): - zinfo.external_attr = 0o40775 << 16 # drwxrwxr-x - zinfo.external_attr |= 0x10 # MS-DOS directory flag - else: - zinfo.external_attr = 0o600 << 16 # ?rw------- - else: + if isinstance(zinfo_or_arcname, ZipInfo): zinfo = zinfo_or_arcname + else: + zinfo = ZipInfo(zinfo_or_arcname)._for_archive(self) if not self.fp: raise ValueError( diff --git a/Misc/NEWS.d/next/Library/2024-08-28-16-10-37.gh-issue-123424.u96_i6.rst b/Misc/NEWS.d/next/Library/2024-08-28-16-10-37.gh-issue-123424.u96_i6.rst new file mode 100644 index 00000000000000..4df4bbf2ba2b73 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-08-28-16-10-37.gh-issue-123424.u96_i6.rst @@ -0,0 +1 @@ +Add :meth:`zipfile.ZipInfo._for_archive` setting default properties on :class:`~zipfile.ZipInfo` objects. Patch by Bénédikt Tran and Jason R. Coombs. From c78729f2df7c0e220f1080edb2a0d0cdd49e22df Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sun, 29 Dec 2024 21:42:07 +0000 Subject: [PATCH 129/376] GH-127381: pathlib ABCs: remove `PathBase.stat()` (#128334) Remove the `PathBase.stat()` method. Its use of the `os.stat_result` API, with its 10 mandatory fields and low-level types, makes it an awkward fit for virtual filesystems. We'll look to add a `PathBase.info` attribute later - see GH-125413. --- Lib/pathlib/_abc.py | 31 ++------- Lib/pathlib/_local.py | 12 +++- Lib/test/test_pathlib/test_pathlib.py | 25 ++++++++ Lib/test/test_pathlib/test_pathlib_abc.py | 77 +++++++---------------- 4 files changed, 62 insertions(+), 83 deletions(-) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 6acc29ebab2bc5..4d5f7980fd30d2 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -16,7 +16,6 @@ import posixpath from errno import EINVAL from glob import _GlobberBase, _no_recurse_symlinks -from stat import S_ISDIR, S_ISLNK, S_ISREG from pathlib._os import copyfileobj @@ -450,15 +449,6 @@ class PathBase(PurePathBase): """ __slots__ = () - def stat(self, *, follow_symlinks=True): - """ - Return the result of the stat() system call on this path, like - os.stat() does. - """ - raise NotImplementedError - - # Convenience functions for querying the stat results - def exists(self, *, follow_symlinks=True): """ Whether this path exists. @@ -466,39 +456,26 @@ def exists(self, *, follow_symlinks=True): This method normally follows symlinks; to check whether a symlink exists, add the argument follow_symlinks=False. """ - try: - self.stat(follow_symlinks=follow_symlinks) - except (OSError, ValueError): - return False - return True + raise NotImplementedError def is_dir(self, *, follow_symlinks=True): """ Whether this path is a directory. """ - try: - return S_ISDIR(self.stat(follow_symlinks=follow_symlinks).st_mode) - except (OSError, ValueError): - return False + raise NotImplementedError def is_file(self, *, follow_symlinks=True): """ Whether this path is a regular file (also True for symlinks pointing to regular files). """ - try: - return S_ISREG(self.stat(follow_symlinks=follow_symlinks).st_mode) - except (OSError, ValueError): - return False + raise NotImplementedError def is_symlink(self): """ Whether this path is a symbolic link. """ - try: - return S_ISLNK(self.stat(follow_symlinks=False).st_mode) - except (OSError, ValueError): - return False + raise NotImplementedError def open(self, mode='r', buffering=-1, encoding=None, errors=None, newline=None): diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index 915402e6c65b29..4484d9553c164f 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -7,7 +7,7 @@ from errno import * from glob import _StringGlobber, _no_recurse_symlinks from itertools import chain -from stat import S_IMODE, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO +from stat import S_IMODE, S_ISDIR, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO from _collections_abc import Sequence try: @@ -725,7 +725,10 @@ def is_dir(self, *, follow_symlinks=True): """ if follow_symlinks: return os.path.isdir(self) - return PathBase.is_dir(self, follow_symlinks=follow_symlinks) + try: + return S_ISDIR(self.stat(follow_symlinks=follow_symlinks).st_mode) + except (OSError, ValueError): + return False def is_file(self, *, follow_symlinks=True): """ @@ -734,7 +737,10 @@ def is_file(self, *, follow_symlinks=True): """ if follow_symlinks: return os.path.isfile(self) - return PathBase.is_file(self, follow_symlinks=follow_symlinks) + try: + return S_ISREG(self.stat(follow_symlinks=follow_symlinks).st_mode) + except (OSError, ValueError): + return False def is_mount(self): """ diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index fac8cbdf65a122..0efa8270210aef 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -1835,6 +1835,31 @@ def test_symlink_to_unsupported(self): with self.assertRaises(pathlib.UnsupportedOperation): q.symlink_to(p) + def test_stat(self): + statA = self.cls(self.base).joinpath('fileA').stat() + statB = self.cls(self.base).joinpath('dirB', 'fileB').stat() + statC = self.cls(self.base).joinpath('dirC').stat() + # st_mode: files are the same, directory differs. + self.assertIsInstance(statA.st_mode, int) + self.assertEqual(statA.st_mode, statB.st_mode) + self.assertNotEqual(statA.st_mode, statC.st_mode) + self.assertNotEqual(statB.st_mode, statC.st_mode) + # st_ino: all different, + self.assertIsInstance(statA.st_ino, int) + self.assertNotEqual(statA.st_ino, statB.st_ino) + self.assertNotEqual(statA.st_ino, statC.st_ino) + self.assertNotEqual(statB.st_ino, statC.st_ino) + # st_dev: all the same. + self.assertIsInstance(statA.st_dev, int) + self.assertEqual(statA.st_dev, statB.st_dev) + self.assertEqual(statA.st_dev, statC.st_dev) + # other attributes not used by pathlib. + + def test_stat_no_follow_symlinks_nosymlink(self): + p = self.cls(self.base) / 'fileA' + st = p.stat() + self.assertEqual(st, p.stat(follow_symlinks=False)) + @needs_symlinks def test_stat_no_follow_symlinks(self): p = self.cls(self.base) / 'linkA' diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index 787fb0f82257e6..81d9d34506f04b 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -2,7 +2,6 @@ import io import os import errno -import stat import unittest from pathlib._abc import PurePathBase, PathBase @@ -1294,11 +1293,6 @@ def close(self): super().close() -DummyPathStatResult = collections.namedtuple( - 'DummyPathStatResult', - 'st_mode st_ino st_dev st_nlink st_uid st_gid st_size st_atime st_mtime st_ctime') - - class DummyPath(PathBase): """ Simple implementation of PathBase that keeps files and directories in @@ -1331,15 +1325,17 @@ def __repr__(self): def with_segments(self, *pathsegments): return type(self)(*pathsegments) - def stat(self, *, follow_symlinks=True): - path = str(self).rstrip('/') - if path in self._files: - st_mode = stat.S_IFREG - elif path in self._directories: - st_mode = stat.S_IFDIR - else: - raise FileNotFoundError(errno.ENOENT, "Not found", str(self)) - return DummyPathStatResult(st_mode, hash(str(self)), 0, 0, 0, 0, 0, 0, 0, 0) + def exists(self, *, follow_symlinks=True): + return self.is_dir() or self.is_file() + + def is_dir(self, *, follow_symlinks=True): + return str(self).rstrip('/') in self._directories + + def is_file(self, *, follow_symlinks=True): + return str(self) in self._files + + def is_symlink(self): + return False def open(self, mode='r', buffering=-1, encoding=None, errors=None, newline=None): @@ -1958,31 +1954,6 @@ def test_rglob_windows(self): self.assertEqual(set(p.rglob("FILEd")), { P(self.base, "dirC/dirD/fileD") }) self.assertEqual(set(p.rglob("*\\")), { P(self.base, "dirC/dirD/") }) - def test_stat(self): - statA = self.cls(self.base).joinpath('fileA').stat() - statB = self.cls(self.base).joinpath('dirB', 'fileB').stat() - statC = self.cls(self.base).joinpath('dirC').stat() - # st_mode: files are the same, directory differs. - self.assertIsInstance(statA.st_mode, int) - self.assertEqual(statA.st_mode, statB.st_mode) - self.assertNotEqual(statA.st_mode, statC.st_mode) - self.assertNotEqual(statB.st_mode, statC.st_mode) - # st_ino: all different, - self.assertIsInstance(statA.st_ino, int) - self.assertNotEqual(statA.st_ino, statB.st_ino) - self.assertNotEqual(statA.st_ino, statC.st_ino) - self.assertNotEqual(statB.st_ino, statC.st_ino) - # st_dev: all the same. - self.assertIsInstance(statA.st_dev, int) - self.assertEqual(statA.st_dev, statB.st_dev) - self.assertEqual(statA.st_dev, statC.st_dev) - # other attributes not used by pathlib. - - def test_stat_no_follow_symlinks_nosymlink(self): - p = self.cls(self.base) / 'fileA' - st = p.stat() - self.assertEqual(st, p.stat(follow_symlinks=False)) - def test_is_dir(self): P = self.cls(self.base) self.assertTrue((P / 'dirA').is_dir()) @@ -2054,26 +2025,26 @@ def test_is_symlink(self): def test_delete_file(self): p = self.cls(self.base) / 'fileA' p._delete() - self.assertFileNotFound(p.stat) + self.assertFalse(p.exists()) self.assertFileNotFound(p._delete) def test_delete_dir(self): base = self.cls(self.base) base.joinpath('dirA')._delete() - self.assertRaises(FileNotFoundError, base.joinpath('dirA').stat) - self.assertRaises(FileNotFoundError, base.joinpath('dirA', 'linkC').stat, - follow_symlinks=False) + self.assertFalse(base.joinpath('dirA').exists()) + self.assertFalse(base.joinpath('dirA', 'linkC').exists( + follow_symlinks=False)) base.joinpath('dirB')._delete() - self.assertRaises(FileNotFoundError, base.joinpath('dirB').stat) - self.assertRaises(FileNotFoundError, base.joinpath('dirB', 'fileB').stat) - self.assertRaises(FileNotFoundError, base.joinpath('dirB', 'linkD').stat, - follow_symlinks=False) + self.assertFalse(base.joinpath('dirB').exists()) + self.assertFalse(base.joinpath('dirB', 'fileB').exists()) + self.assertFalse(base.joinpath('dirB', 'linkD').exists( + follow_symlinks=False)) base.joinpath('dirC')._delete() - self.assertRaises(FileNotFoundError, base.joinpath('dirC').stat) - self.assertRaises(FileNotFoundError, base.joinpath('dirC', 'dirD').stat) - self.assertRaises(FileNotFoundError, base.joinpath('dirC', 'dirD', 'fileD').stat) - self.assertRaises(FileNotFoundError, base.joinpath('dirC', 'fileC').stat) - self.assertRaises(FileNotFoundError, base.joinpath('dirC', 'novel.txt').stat) + self.assertFalse(base.joinpath('dirC').exists()) + self.assertFalse(base.joinpath('dirC', 'dirD').exists()) + self.assertFalse(base.joinpath('dirC', 'dirD', 'fileD').exists()) + self.assertFalse(base.joinpath('dirC', 'fileC').exists()) + self.assertFalse(base.joinpath('dirC', 'novel.txt').exists()) def test_delete_missing(self): tmp = self.cls(self.base, 'delete') From ef63cca494571f50906baae1d176469a3dcf8838 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sun, 29 Dec 2024 22:07:12 +0000 Subject: [PATCH 130/376] GH-127381: pathlib ABCs: remove uncommon `PurePathBase` methods (#127853) Remove `PurePathBase.relative_to()` and `is_relative_to()` because they don't account for *other* being an entirely different kind of path, and they can't use `__eq__()` because it's not on the `PurePathBase` interface. Remove `PurePathBase.drive`, `root`, `is_absolute()` and `as_posix()`. These are all too specific to local filesystems. --- Lib/pathlib/_abc.py | 65 ---- Lib/pathlib/_local.py | 5 + Lib/pathlib/_types.py | 2 - Lib/test/test_pathlib/test_pathlib.py | 356 +++++++++++++++++++++ Lib/test/test_pathlib/test_pathlib_abc.py | 370 +--------------------- 5 files changed, 365 insertions(+), 433 deletions(-) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 4d5f7980fd30d2..e6ff3fe1187512 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -205,21 +205,6 @@ def __str__(self): passing to system calls.""" raise NotImplementedError - def as_posix(self): - """Return the string representation of the path with forward (/) - slashes.""" - return str(self).replace(self.parser.sep, '/') - - @property - def drive(self): - """The drive prefix (letter or UNC path), if any.""" - return self.parser.splitdrive(self.anchor)[0] - - @property - def root(self): - """The root of the path, if any.""" - return self.parser.splitdrive(self.anchor)[1] - @property def anchor(self): """The concatenation of the drive and root, or ''.""" @@ -291,51 +276,6 @@ def with_suffix(self, suffix): else: return self.with_name(stem + suffix) - def relative_to(self, other, *, walk_up=False): - """Return the relative path to another path identified by the passed - arguments. If the operation is not possible (because this is not - related to the other path), raise ValueError. - - The *walk_up* parameter controls whether `..` may be used to resolve - the path. - """ - if not isinstance(other, PurePathBase): - other = self.with_segments(other) - anchor0, parts0 = _explode_path(self) - anchor1, parts1 = _explode_path(other) - if anchor0 != anchor1: - raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors") - while parts0 and parts1 and parts0[-1] == parts1[-1]: - parts0.pop() - parts1.pop() - for part in parts1: - if not part or part == '.': - pass - elif not walk_up: - raise ValueError(f"{str(self)!r} is not in the subpath of {str(other)!r}") - elif part == '..': - raise ValueError(f"'..' segment in {str(other)!r} cannot be walked") - else: - parts0.append('..') - return self.with_segments(*reversed(parts0)) - - def is_relative_to(self, other): - """Return True if the path is relative to another path or False. - """ - if not isinstance(other, PurePathBase): - other = self.with_segments(other) - anchor0, parts0 = _explode_path(self) - anchor1, parts1 = _explode_path(other) - if anchor0 != anchor1: - return False - while parts0 and parts1 and parts0[-1] == parts1[-1]: - parts0.pop() - parts1.pop() - for part in parts1: - if part and part != '.': - return False - return True - @property def parts(self): """An object providing sequence-like access to the @@ -387,11 +327,6 @@ def parents(self): parent = split(path)[0] return tuple(parents) - def is_absolute(self): - """True if the path is absolute (has both a root and, if applicable, - a drive).""" - return self.parser.isabs(str(self)) - def match(self, path_pattern, *, case_sensitive=None): """ Return True if this path matches the given pattern. If the pattern is diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index 4484d9553c164f..c5721a69d00470 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -437,6 +437,11 @@ def _parse_pattern(cls, pattern): parts.append('') return parts + def as_posix(self): + """Return the string representation of the path with forward (/) + slashes.""" + return str(self).replace(self.parser.sep, '/') + @property def _raw_path(self): paths = self._raw_paths diff --git a/Lib/pathlib/_types.py b/Lib/pathlib/_types.py index baa4a7e2af5b68..72dac2e276fce0 100644 --- a/Lib/pathlib/_types.py +++ b/Lib/pathlib/_types.py @@ -15,7 +15,5 @@ class Parser(Protocol): sep: str def split(self, path: str) -> tuple[str, str]: ... - def splitdrive(self, path: str) -> tuple[str, str]: ... def splitext(self, path: str) -> tuple[str, str]: ... def normcase(self, path: str) -> str: ... - def isabs(self, path: str) -> bool: ... diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index 0efa8270210aef..d13daf8ac8cb07 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -270,6 +270,12 @@ def test_as_bytes_common(self): P = self.cls self.assertEqual(bytes(P('a/b')), b'a' + sep + b'b') + def test_as_posix_common(self): + P = self.cls + for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): + self.assertEqual(P(pathstr).as_posix(), pathstr) + # Other tests for as_posix() are in test_equivalences(). + def test_eq_common(self): P = self.cls self.assertEqual(P('a/b'), P('a/b')) @@ -349,6 +355,51 @@ def test_repr_roundtrips(self): self.assertEqual(q, p) self.assertEqual(repr(q), r) + def test_drive_common(self): + P = self.cls + self.assertEqual(P('a/b').drive, '') + self.assertEqual(P('/a/b').drive, '') + self.assertEqual(P('').drive, '') + + @needs_windows + def test_drive_windows(self): + P = self.cls + self.assertEqual(P('c:').drive, 'c:') + self.assertEqual(P('c:a/b').drive, 'c:') + self.assertEqual(P('c:/').drive, 'c:') + self.assertEqual(P('c:/a/b/').drive, 'c:') + self.assertEqual(P('//a/b').drive, '\\\\a\\b') + self.assertEqual(P('//a/b/').drive, '\\\\a\\b') + self.assertEqual(P('//a/b/c/d').drive, '\\\\a\\b') + self.assertEqual(P('./c:a').drive, '') + + + def test_root_common(self): + P = self.cls + sep = self.sep + self.assertEqual(P('').root, '') + self.assertEqual(P('a/b').root, '') + self.assertEqual(P('/').root, sep) + self.assertEqual(P('/a/b').root, sep) + + @needs_posix + def test_root_posix(self): + P = self.cls + self.assertEqual(P('/a/b').root, '/') + # POSIX special case for two leading slashes. + self.assertEqual(P('//a/b').root, '//') + + @needs_windows + def test_root_windows(self): + P = self.cls + self.assertEqual(P('c:').root, '') + self.assertEqual(P('c:a/b').root, '') + self.assertEqual(P('c:/').root, '\\') + self.assertEqual(P('c:/a/b/').root, '\\') + self.assertEqual(P('//a/b').root, '\\') + self.assertEqual(P('//a/b/').root, '\\') + self.assertEqual(P('//a/b/c/d').root, '\\') + def test_name_empty(self): P = self.cls self.assertEqual(P('').name, '') @@ -547,6 +598,311 @@ def assertOrderedEqual(a, b): self.assertFalse(p < q) self.assertFalse(p > q) + @needs_posix + def test_is_absolute_posix(self): + P = self.cls + self.assertFalse(P('').is_absolute()) + self.assertFalse(P('a').is_absolute()) + self.assertFalse(P('a/b/').is_absolute()) + self.assertTrue(P('/').is_absolute()) + self.assertTrue(P('/a').is_absolute()) + self.assertTrue(P('/a/b/').is_absolute()) + self.assertTrue(P('//a').is_absolute()) + self.assertTrue(P('//a/b').is_absolute()) + + @needs_windows + def test_is_absolute_windows(self): + P = self.cls + # Under NT, only paths with both a drive and a root are absolute. + self.assertFalse(P().is_absolute()) + self.assertFalse(P('a').is_absolute()) + self.assertFalse(P('a/b/').is_absolute()) + self.assertFalse(P('/').is_absolute()) + self.assertFalse(P('/a').is_absolute()) + self.assertFalse(P('/a/b/').is_absolute()) + self.assertFalse(P('c:').is_absolute()) + self.assertFalse(P('c:a').is_absolute()) + self.assertFalse(P('c:a/b/').is_absolute()) + self.assertTrue(P('c:/').is_absolute()) + self.assertTrue(P('c:/a').is_absolute()) + self.assertTrue(P('c:/a/b/').is_absolute()) + # UNC paths are absolute by definition. + self.assertTrue(P('//').is_absolute()) + self.assertTrue(P('//a').is_absolute()) + self.assertTrue(P('//a/b').is_absolute()) + self.assertTrue(P('//a/b/').is_absolute()) + self.assertTrue(P('//a/b/c').is_absolute()) + self.assertTrue(P('//a/b/c/d').is_absolute()) + self.assertTrue(P('//?/UNC/').is_absolute()) + self.assertTrue(P('//?/UNC/spam').is_absolute()) + + def test_relative_to_common(self): + P = self.cls + p = P('a/b') + self.assertRaises(TypeError, p.relative_to) + self.assertRaises(TypeError, p.relative_to, b'a') + self.assertEqual(p.relative_to(P('')), P('a/b')) + self.assertEqual(p.relative_to(''), P('a/b')) + self.assertEqual(p.relative_to(P('a')), P('b')) + self.assertEqual(p.relative_to('a'), P('b')) + self.assertEqual(p.relative_to('a/'), P('b')) + self.assertEqual(p.relative_to(P('a/b')), P('')) + self.assertEqual(p.relative_to('a/b'), P('')) + self.assertEqual(p.relative_to(P(''), walk_up=True), P('a/b')) + self.assertEqual(p.relative_to('', walk_up=True), P('a/b')) + self.assertEqual(p.relative_to(P('a'), walk_up=True), P('b')) + self.assertEqual(p.relative_to('a', walk_up=True), P('b')) + self.assertEqual(p.relative_to('a/', walk_up=True), P('b')) + self.assertEqual(p.relative_to(P('a/b'), walk_up=True), P('')) + self.assertEqual(p.relative_to('a/b', walk_up=True), P('')) + self.assertEqual(p.relative_to(P('a/c'), walk_up=True), P('../b')) + self.assertEqual(p.relative_to('a/c', walk_up=True), P('../b')) + self.assertEqual(p.relative_to(P('a/b/c'), walk_up=True), P('..')) + self.assertEqual(p.relative_to('a/b/c', walk_up=True), P('..')) + self.assertEqual(p.relative_to(P('c'), walk_up=True), P('../a/b')) + self.assertEqual(p.relative_to('c', walk_up=True), P('../a/b')) + # Unrelated paths. + self.assertRaises(ValueError, p.relative_to, P('c')) + self.assertRaises(ValueError, p.relative_to, P('a/b/c')) + self.assertRaises(ValueError, p.relative_to, P('a/c')) + self.assertRaises(ValueError, p.relative_to, P('/a')) + self.assertRaises(ValueError, p.relative_to, P("../a")) + self.assertRaises(ValueError, p.relative_to, P("a/..")) + self.assertRaises(ValueError, p.relative_to, P("/a/..")) + self.assertRaises(ValueError, p.relative_to, P('/'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('/a'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P("../a"), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P("a/.."), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P("/a/.."), walk_up=True) + p = P('/a/b') + self.assertEqual(p.relative_to(P('/')), P('a/b')) + self.assertEqual(p.relative_to('/'), P('a/b')) + self.assertEqual(p.relative_to(P('/a')), P('b')) + self.assertEqual(p.relative_to('/a'), P('b')) + self.assertEqual(p.relative_to('/a/'), P('b')) + self.assertEqual(p.relative_to(P('/a/b')), P('')) + self.assertEqual(p.relative_to('/a/b'), P('')) + self.assertEqual(p.relative_to(P('/'), walk_up=True), P('a/b')) + self.assertEqual(p.relative_to('/', walk_up=True), P('a/b')) + self.assertEqual(p.relative_to(P('/a'), walk_up=True), P('b')) + self.assertEqual(p.relative_to('/a', walk_up=True), P('b')) + self.assertEqual(p.relative_to('/a/', walk_up=True), P('b')) + self.assertEqual(p.relative_to(P('/a/b'), walk_up=True), P('')) + self.assertEqual(p.relative_to('/a/b', walk_up=True), P('')) + self.assertEqual(p.relative_to(P('/a/c'), walk_up=True), P('../b')) + self.assertEqual(p.relative_to('/a/c', walk_up=True), P('../b')) + self.assertEqual(p.relative_to(P('/a/b/c'), walk_up=True), P('..')) + self.assertEqual(p.relative_to('/a/b/c', walk_up=True), P('..')) + self.assertEqual(p.relative_to(P('/c'), walk_up=True), P('../a/b')) + self.assertEqual(p.relative_to('/c', walk_up=True), P('../a/b')) + # Unrelated paths. + self.assertRaises(ValueError, p.relative_to, P('/c')) + self.assertRaises(ValueError, p.relative_to, P('/a/b/c')) + self.assertRaises(ValueError, p.relative_to, P('/a/c')) + self.assertRaises(ValueError, p.relative_to, P('')) + self.assertRaises(ValueError, p.relative_to, '') + self.assertRaises(ValueError, p.relative_to, P('a')) + self.assertRaises(ValueError, p.relative_to, P("../a")) + self.assertRaises(ValueError, p.relative_to, P("a/..")) + self.assertRaises(ValueError, p.relative_to, P("/a/..")) + self.assertRaises(ValueError, p.relative_to, P(''), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('a'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P("../a"), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P("a/.."), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P("/a/.."), walk_up=True) + + @needs_windows + def test_relative_to_windows(self): + P = self.cls + p = P('C:Foo/Bar') + self.assertEqual(p.relative_to(P('c:')), P('Foo/Bar')) + self.assertEqual(p.relative_to('c:'), P('Foo/Bar')) + self.assertEqual(p.relative_to(P('c:foO')), P('Bar')) + self.assertEqual(p.relative_to('c:foO'), P('Bar')) + self.assertEqual(p.relative_to('c:foO/'), P('Bar')) + self.assertEqual(p.relative_to(P('c:foO/baR')), P()) + self.assertEqual(p.relative_to('c:foO/baR'), P()) + self.assertEqual(p.relative_to(P('c:'), walk_up=True), P('Foo/Bar')) + self.assertEqual(p.relative_to('c:', walk_up=True), P('Foo/Bar')) + self.assertEqual(p.relative_to(P('c:foO'), walk_up=True), P('Bar')) + self.assertEqual(p.relative_to('c:foO', walk_up=True), P('Bar')) + self.assertEqual(p.relative_to('c:foO/', walk_up=True), P('Bar')) + self.assertEqual(p.relative_to(P('c:foO/baR'), walk_up=True), P()) + self.assertEqual(p.relative_to('c:foO/baR', walk_up=True), P()) + self.assertEqual(p.relative_to(P('C:Foo/Bar/Baz'), walk_up=True), P('..')) + self.assertEqual(p.relative_to(P('C:Foo/Baz'), walk_up=True), P('../Bar')) + self.assertEqual(p.relative_to(P('C:Baz/Bar'), walk_up=True), P('../../Foo/Bar')) + # Unrelated paths. + self.assertRaises(ValueError, p.relative_to, P()) + self.assertRaises(ValueError, p.relative_to, '') + self.assertRaises(ValueError, p.relative_to, P('d:')) + self.assertRaises(ValueError, p.relative_to, P('/')) + self.assertRaises(ValueError, p.relative_to, P('Foo')) + self.assertRaises(ValueError, p.relative_to, P('/Foo')) + self.assertRaises(ValueError, p.relative_to, P('C:/Foo')) + self.assertRaises(ValueError, p.relative_to, P('C:Foo/Bar/Baz')) + self.assertRaises(ValueError, p.relative_to, P('C:Foo/Baz')) + self.assertRaises(ValueError, p.relative_to, P(), walk_up=True) + self.assertRaises(ValueError, p.relative_to, '', walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('d:'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('/'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('Foo'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('/Foo'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('C:/Foo'), walk_up=True) + p = P('C:/Foo/Bar') + self.assertEqual(p.relative_to(P('c:/')), P('Foo/Bar')) + self.assertEqual(p.relative_to('c:/'), P('Foo/Bar')) + self.assertEqual(p.relative_to(P('c:/foO')), P('Bar')) + self.assertEqual(p.relative_to('c:/foO'), P('Bar')) + self.assertEqual(p.relative_to('c:/foO/'), P('Bar')) + self.assertEqual(p.relative_to(P('c:/foO/baR')), P()) + self.assertEqual(p.relative_to('c:/foO/baR'), P()) + self.assertEqual(p.relative_to(P('c:/'), walk_up=True), P('Foo/Bar')) + self.assertEqual(p.relative_to('c:/', walk_up=True), P('Foo/Bar')) + self.assertEqual(p.relative_to(P('c:/foO'), walk_up=True), P('Bar')) + self.assertEqual(p.relative_to('c:/foO', walk_up=True), P('Bar')) + self.assertEqual(p.relative_to('c:/foO/', walk_up=True), P('Bar')) + self.assertEqual(p.relative_to(P('c:/foO/baR'), walk_up=True), P()) + self.assertEqual(p.relative_to('c:/foO/baR', walk_up=True), P()) + self.assertEqual(p.relative_to('C:/Baz', walk_up=True), P('../Foo/Bar')) + self.assertEqual(p.relative_to('C:/Foo/Bar/Baz', walk_up=True), P('..')) + self.assertEqual(p.relative_to('C:/Foo/Baz', walk_up=True), P('../Bar')) + # Unrelated paths. + self.assertRaises(ValueError, p.relative_to, 'c:') + self.assertRaises(ValueError, p.relative_to, P('c:')) + self.assertRaises(ValueError, p.relative_to, P('C:/Baz')) + self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Bar/Baz')) + self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Baz')) + self.assertRaises(ValueError, p.relative_to, P('C:Foo')) + self.assertRaises(ValueError, p.relative_to, P('d:')) + self.assertRaises(ValueError, p.relative_to, P('d:/')) + self.assertRaises(ValueError, p.relative_to, P('/')) + self.assertRaises(ValueError, p.relative_to, P('/Foo')) + self.assertRaises(ValueError, p.relative_to, P('//C/Foo')) + self.assertRaises(ValueError, p.relative_to, 'c:', walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('c:'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('C:Foo'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('d:'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('d:/'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('/'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('/Foo'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('//C/Foo'), walk_up=True) + # UNC paths. + p = P('//Server/Share/Foo/Bar') + self.assertEqual(p.relative_to(P('//sErver/sHare')), P('Foo/Bar')) + self.assertEqual(p.relative_to('//sErver/sHare'), P('Foo/Bar')) + self.assertEqual(p.relative_to('//sErver/sHare/'), P('Foo/Bar')) + self.assertEqual(p.relative_to(P('//sErver/sHare/Foo')), P('Bar')) + self.assertEqual(p.relative_to('//sErver/sHare/Foo'), P('Bar')) + self.assertEqual(p.relative_to('//sErver/sHare/Foo/'), P('Bar')) + self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar')), P()) + self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar'), P()) + self.assertEqual(p.relative_to(P('//sErver/sHare'), walk_up=True), P('Foo/Bar')) + self.assertEqual(p.relative_to('//sErver/sHare', walk_up=True), P('Foo/Bar')) + self.assertEqual(p.relative_to('//sErver/sHare/', walk_up=True), P('Foo/Bar')) + self.assertEqual(p.relative_to(P('//sErver/sHare/Foo'), walk_up=True), P('Bar')) + self.assertEqual(p.relative_to('//sErver/sHare/Foo', walk_up=True), P('Bar')) + self.assertEqual(p.relative_to('//sErver/sHare/Foo/', walk_up=True), P('Bar')) + self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar'), walk_up=True), P()) + self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar', walk_up=True), P()) + self.assertEqual(p.relative_to(P('//sErver/sHare/bar'), walk_up=True), P('../Foo/Bar')) + self.assertEqual(p.relative_to('//sErver/sHare/bar', walk_up=True), P('../Foo/Bar')) + # Unrelated paths. + self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo')) + self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo')) + self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo')) + self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo')) + self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo'), walk_up=True) + + def test_is_relative_to_common(self): + P = self.cls + p = P('a/b') + self.assertRaises(TypeError, p.is_relative_to) + self.assertRaises(TypeError, p.is_relative_to, b'a') + self.assertTrue(p.is_relative_to(P(''))) + self.assertTrue(p.is_relative_to('')) + self.assertTrue(p.is_relative_to(P('a'))) + self.assertTrue(p.is_relative_to('a/')) + self.assertTrue(p.is_relative_to(P('a/b'))) + self.assertTrue(p.is_relative_to('a/b')) + # Unrelated paths. + self.assertFalse(p.is_relative_to(P('c'))) + self.assertFalse(p.is_relative_to(P('a/b/c'))) + self.assertFalse(p.is_relative_to(P('a/c'))) + self.assertFalse(p.is_relative_to(P('/a'))) + p = P('/a/b') + self.assertTrue(p.is_relative_to(P('/'))) + self.assertTrue(p.is_relative_to('/')) + self.assertTrue(p.is_relative_to(P('/a'))) + self.assertTrue(p.is_relative_to('/a')) + self.assertTrue(p.is_relative_to('/a/')) + self.assertTrue(p.is_relative_to(P('/a/b'))) + self.assertTrue(p.is_relative_to('/a/b')) + # Unrelated paths. + self.assertFalse(p.is_relative_to(P('/c'))) + self.assertFalse(p.is_relative_to(P('/a/b/c'))) + self.assertFalse(p.is_relative_to(P('/a/c'))) + self.assertFalse(p.is_relative_to(P(''))) + self.assertFalse(p.is_relative_to('')) + self.assertFalse(p.is_relative_to(P('a'))) + + @needs_windows + def test_is_relative_to_windows(self): + P = self.cls + p = P('C:Foo/Bar') + self.assertTrue(p.is_relative_to(P('c:'))) + self.assertTrue(p.is_relative_to('c:')) + self.assertTrue(p.is_relative_to(P('c:foO'))) + self.assertTrue(p.is_relative_to('c:foO')) + self.assertTrue(p.is_relative_to('c:foO/')) + self.assertTrue(p.is_relative_to(P('c:foO/baR'))) + self.assertTrue(p.is_relative_to('c:foO/baR')) + # Unrelated paths. + self.assertFalse(p.is_relative_to(P())) + self.assertFalse(p.is_relative_to('')) + self.assertFalse(p.is_relative_to(P('d:'))) + self.assertFalse(p.is_relative_to(P('/'))) + self.assertFalse(p.is_relative_to(P('Foo'))) + self.assertFalse(p.is_relative_to(P('/Foo'))) + self.assertFalse(p.is_relative_to(P('C:/Foo'))) + self.assertFalse(p.is_relative_to(P('C:Foo/Bar/Baz'))) + self.assertFalse(p.is_relative_to(P('C:Foo/Baz'))) + p = P('C:/Foo/Bar') + self.assertTrue(p.is_relative_to(P('c:/'))) + self.assertTrue(p.is_relative_to(P('c:/foO'))) + self.assertTrue(p.is_relative_to('c:/foO/')) + self.assertTrue(p.is_relative_to(P('c:/foO/baR'))) + self.assertTrue(p.is_relative_to('c:/foO/baR')) + # Unrelated paths. + self.assertFalse(p.is_relative_to('c:')) + self.assertFalse(p.is_relative_to(P('C:/Baz'))) + self.assertFalse(p.is_relative_to(P('C:/Foo/Bar/Baz'))) + self.assertFalse(p.is_relative_to(P('C:/Foo/Baz'))) + self.assertFalse(p.is_relative_to(P('C:Foo'))) + self.assertFalse(p.is_relative_to(P('d:'))) + self.assertFalse(p.is_relative_to(P('d:/'))) + self.assertFalse(p.is_relative_to(P('/'))) + self.assertFalse(p.is_relative_to(P('/Foo'))) + self.assertFalse(p.is_relative_to(P('//C/Foo'))) + # UNC paths. + p = P('//Server/Share/Foo/Bar') + self.assertTrue(p.is_relative_to(P('//sErver/sHare'))) + self.assertTrue(p.is_relative_to('//sErver/sHare')) + self.assertTrue(p.is_relative_to('//sErver/sHare/')) + self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo'))) + self.assertTrue(p.is_relative_to('//sErver/sHare/Foo')) + self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/')) + self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo/Bar'))) + self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/Bar')) + # Unrelated paths. + self.assertFalse(p.is_relative_to(P('/Server/Share/Foo'))) + self.assertFalse(p.is_relative_to(P('c:/Server/Share/Foo'))) + self.assertFalse(p.is_relative_to(P('//z/Share/Foo'))) + self.assertFalse(p.is_relative_to(P('//Server/z/Foo'))) + class PurePosixPathTest(PurePathTest): cls = pathlib.PurePosixPath diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index 81d9d34506f04b..d588442bd11785 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -71,7 +71,7 @@ def __hash__(self): return hash(str(self)) def __repr__(self): - return "{}({!r})".format(self.__class__.__name__, self.as_posix()) + return "{}({!r})".format(self.__class__.__name__, str(self)) def with_segments(self, *pathsegments): return type(self)(*pathsegments) @@ -107,12 +107,6 @@ def test_constructor_common(self): P('a/b/c') P('/a/b/c') - def test_fspath_common(self): - self.assertRaises(TypeError, os.fspath, self.cls('')) - - def test_as_bytes_common(self): - self.assertRaises(TypeError, bytes, self.cls('')) - def _check_str_subclass(self, *args): # Issue #21127: it should be possible to construct a PurePath object # from a str subclass instance, and it then gets converted to @@ -161,7 +155,6 @@ def with_segments(self, *pathsegments): self.assertEqual(42, p.with_stem('foo').session_id) self.assertEqual(42, p.with_suffix('.foo').session_id) self.assertEqual(42, p.with_segments('foo').session_id) - self.assertEqual(42, p.relative_to('foo').session_id) self.assertEqual(42, p.parent.session_id) for parent in p.parents: self.assertEqual(42, parent.session_id) @@ -303,12 +296,6 @@ def test_str_windows(self): p = self.cls('//a/b/c/d') self.assertEqual(str(p), '\\\\a\\b\\c\\d') - def test_as_posix_common(self): - P = self.cls - for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): - self.assertEqual(P(pathstr).as_posix(), pathstr) - # Other tests for as_posix() are in test_equivalences(). - def test_match_empty(self): P = self.cls self.assertRaises(ValueError, P('a').match, '') @@ -615,50 +602,6 @@ def test_parents_windows(self): with self.assertRaises(IndexError): par[2] - def test_drive_common(self): - P = self.cls - self.assertEqual(P('a/b').drive, '') - self.assertEqual(P('/a/b').drive, '') - self.assertEqual(P('').drive, '') - - @needs_windows - def test_drive_windows(self): - P = self.cls - self.assertEqual(P('c:').drive, 'c:') - self.assertEqual(P('c:a/b').drive, 'c:') - self.assertEqual(P('c:/').drive, 'c:') - self.assertEqual(P('c:/a/b/').drive, 'c:') - self.assertEqual(P('//a/b').drive, '\\\\a\\b') - self.assertEqual(P('//a/b/').drive, '\\\\a\\b') - self.assertEqual(P('//a/b/c/d').drive, '\\\\a\\b') - self.assertEqual(P('./c:a').drive, '') - - def test_root_common(self): - P = self.cls - sep = self.sep - self.assertEqual(P('').root, '') - self.assertEqual(P('a/b').root, '') - self.assertEqual(P('/').root, sep) - self.assertEqual(P('/a/b').root, sep) - - @needs_posix - def test_root_posix(self): - P = self.cls - self.assertEqual(P('/a/b').root, '/') - # POSIX special case for two leading slashes. - self.assertEqual(P('//a/b').root, '//') - - @needs_windows - def test_root_windows(self): - P = self.cls - self.assertEqual(P('c:').root, '') - self.assertEqual(P('c:a/b').root, '') - self.assertEqual(P('c:/').root, '\\') - self.assertEqual(P('c:/a/b/').root, '\\') - self.assertEqual(P('//a/b').root, '\\') - self.assertEqual(P('//a/b/').root, '\\') - self.assertEqual(P('//a/b/c/d').root, '\\') - def test_anchor_common(self): P = self.cls sep = self.sep @@ -967,311 +910,6 @@ def test_with_suffix_invalid(self): self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.') self.assertRaises(TypeError, P('a/b').with_suffix, None) - def test_relative_to_common(self): - P = self.cls - p = P('a/b') - self.assertRaises(TypeError, p.relative_to) - self.assertRaises(TypeError, p.relative_to, b'a') - self.assertEqual(p.relative_to(P('')), P('a/b')) - self.assertEqual(p.relative_to(''), P('a/b')) - self.assertEqual(p.relative_to(P('a')), P('b')) - self.assertEqual(p.relative_to('a'), P('b')) - self.assertEqual(p.relative_to('a/'), P('b')) - self.assertEqual(p.relative_to(P('a/b')), P('')) - self.assertEqual(p.relative_to('a/b'), P('')) - self.assertEqual(p.relative_to(P(''), walk_up=True), P('a/b')) - self.assertEqual(p.relative_to('', walk_up=True), P('a/b')) - self.assertEqual(p.relative_to(P('a'), walk_up=True), P('b')) - self.assertEqual(p.relative_to('a', walk_up=True), P('b')) - self.assertEqual(p.relative_to('a/', walk_up=True), P('b')) - self.assertEqual(p.relative_to(P('a/b'), walk_up=True), P('')) - self.assertEqual(p.relative_to('a/b', walk_up=True), P('')) - self.assertEqual(p.relative_to(P('a/c'), walk_up=True), P('../b')) - self.assertEqual(p.relative_to('a/c', walk_up=True), P('../b')) - self.assertEqual(p.relative_to(P('a/b/c'), walk_up=True), P('..')) - self.assertEqual(p.relative_to('a/b/c', walk_up=True), P('..')) - self.assertEqual(p.relative_to(P('c'), walk_up=True), P('../a/b')) - self.assertEqual(p.relative_to('c', walk_up=True), P('../a/b')) - # Unrelated paths. - self.assertRaises(ValueError, p.relative_to, P('c')) - self.assertRaises(ValueError, p.relative_to, P('a/b/c')) - self.assertRaises(ValueError, p.relative_to, P('a/c')) - self.assertRaises(ValueError, p.relative_to, P('/a')) - self.assertRaises(ValueError, p.relative_to, P("../a")) - self.assertRaises(ValueError, p.relative_to, P("a/..")) - self.assertRaises(ValueError, p.relative_to, P("/a/..")) - self.assertRaises(ValueError, p.relative_to, P('/'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('/a'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P("../a"), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P("a/.."), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P("/a/.."), walk_up=True) - p = P('/a/b') - self.assertEqual(p.relative_to(P('/')), P('a/b')) - self.assertEqual(p.relative_to('/'), P('a/b')) - self.assertEqual(p.relative_to(P('/a')), P('b')) - self.assertEqual(p.relative_to('/a'), P('b')) - self.assertEqual(p.relative_to('/a/'), P('b')) - self.assertEqual(p.relative_to(P('/a/b')), P('')) - self.assertEqual(p.relative_to('/a/b'), P('')) - self.assertEqual(p.relative_to(P('/'), walk_up=True), P('a/b')) - self.assertEqual(p.relative_to('/', walk_up=True), P('a/b')) - self.assertEqual(p.relative_to(P('/a'), walk_up=True), P('b')) - self.assertEqual(p.relative_to('/a', walk_up=True), P('b')) - self.assertEqual(p.relative_to('/a/', walk_up=True), P('b')) - self.assertEqual(p.relative_to(P('/a/b'), walk_up=True), P('')) - self.assertEqual(p.relative_to('/a/b', walk_up=True), P('')) - self.assertEqual(p.relative_to(P('/a/c'), walk_up=True), P('../b')) - self.assertEqual(p.relative_to('/a/c', walk_up=True), P('../b')) - self.assertEqual(p.relative_to(P('/a/b/c'), walk_up=True), P('..')) - self.assertEqual(p.relative_to('/a/b/c', walk_up=True), P('..')) - self.assertEqual(p.relative_to(P('/c'), walk_up=True), P('../a/b')) - self.assertEqual(p.relative_to('/c', walk_up=True), P('../a/b')) - # Unrelated paths. - self.assertRaises(ValueError, p.relative_to, P('/c')) - self.assertRaises(ValueError, p.relative_to, P('/a/b/c')) - self.assertRaises(ValueError, p.relative_to, P('/a/c')) - self.assertRaises(ValueError, p.relative_to, P('')) - self.assertRaises(ValueError, p.relative_to, '') - self.assertRaises(ValueError, p.relative_to, P('a')) - self.assertRaises(ValueError, p.relative_to, P("../a")) - self.assertRaises(ValueError, p.relative_to, P("a/..")) - self.assertRaises(ValueError, p.relative_to, P("/a/..")) - self.assertRaises(ValueError, p.relative_to, P(''), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('a'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P("../a"), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P("a/.."), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P("/a/.."), walk_up=True) - - @needs_windows - def test_relative_to_windows(self): - P = self.cls - p = P('C:Foo/Bar') - self.assertEqual(p.relative_to(P('c:')), P('Foo/Bar')) - self.assertEqual(p.relative_to('c:'), P('Foo/Bar')) - self.assertEqual(p.relative_to(P('c:foO')), P('Bar')) - self.assertEqual(p.relative_to('c:foO'), P('Bar')) - self.assertEqual(p.relative_to('c:foO/'), P('Bar')) - self.assertEqual(p.relative_to(P('c:foO/baR')), P()) - self.assertEqual(p.relative_to('c:foO/baR'), P()) - self.assertEqual(p.relative_to(P('c:'), walk_up=True), P('Foo/Bar')) - self.assertEqual(p.relative_to('c:', walk_up=True), P('Foo/Bar')) - self.assertEqual(p.relative_to(P('c:foO'), walk_up=True), P('Bar')) - self.assertEqual(p.relative_to('c:foO', walk_up=True), P('Bar')) - self.assertEqual(p.relative_to('c:foO/', walk_up=True), P('Bar')) - self.assertEqual(p.relative_to(P('c:foO/baR'), walk_up=True), P()) - self.assertEqual(p.relative_to('c:foO/baR', walk_up=True), P()) - self.assertEqual(p.relative_to(P('C:Foo/Bar/Baz'), walk_up=True), P('..')) - self.assertEqual(p.relative_to(P('C:Foo/Baz'), walk_up=True), P('../Bar')) - self.assertEqual(p.relative_to(P('C:Baz/Bar'), walk_up=True), P('../../Foo/Bar')) - # Unrelated paths. - self.assertRaises(ValueError, p.relative_to, P()) - self.assertRaises(ValueError, p.relative_to, '') - self.assertRaises(ValueError, p.relative_to, P('d:')) - self.assertRaises(ValueError, p.relative_to, P('/')) - self.assertRaises(ValueError, p.relative_to, P('Foo')) - self.assertRaises(ValueError, p.relative_to, P('/Foo')) - self.assertRaises(ValueError, p.relative_to, P('C:/Foo')) - self.assertRaises(ValueError, p.relative_to, P('C:Foo/Bar/Baz')) - self.assertRaises(ValueError, p.relative_to, P('C:Foo/Baz')) - self.assertRaises(ValueError, p.relative_to, P(), walk_up=True) - self.assertRaises(ValueError, p.relative_to, '', walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('d:'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('/'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('Foo'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('/Foo'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('C:/Foo'), walk_up=True) - p = P('C:/Foo/Bar') - self.assertEqual(p.relative_to(P('c:/')), P('Foo/Bar')) - self.assertEqual(p.relative_to('c:/'), P('Foo/Bar')) - self.assertEqual(p.relative_to(P('c:/foO')), P('Bar')) - self.assertEqual(p.relative_to('c:/foO'), P('Bar')) - self.assertEqual(p.relative_to('c:/foO/'), P('Bar')) - self.assertEqual(p.relative_to(P('c:/foO/baR')), P()) - self.assertEqual(p.relative_to('c:/foO/baR'), P()) - self.assertEqual(p.relative_to(P('c:/'), walk_up=True), P('Foo/Bar')) - self.assertEqual(p.relative_to('c:/', walk_up=True), P('Foo/Bar')) - self.assertEqual(p.relative_to(P('c:/foO'), walk_up=True), P('Bar')) - self.assertEqual(p.relative_to('c:/foO', walk_up=True), P('Bar')) - self.assertEqual(p.relative_to('c:/foO/', walk_up=True), P('Bar')) - self.assertEqual(p.relative_to(P('c:/foO/baR'), walk_up=True), P()) - self.assertEqual(p.relative_to('c:/foO/baR', walk_up=True), P()) - self.assertEqual(p.relative_to('C:/Baz', walk_up=True), P('../Foo/Bar')) - self.assertEqual(p.relative_to('C:/Foo/Bar/Baz', walk_up=True), P('..')) - self.assertEqual(p.relative_to('C:/Foo/Baz', walk_up=True), P('../Bar')) - # Unrelated paths. - self.assertRaises(ValueError, p.relative_to, 'c:') - self.assertRaises(ValueError, p.relative_to, P('c:')) - self.assertRaises(ValueError, p.relative_to, P('C:/Baz')) - self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Bar/Baz')) - self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Baz')) - self.assertRaises(ValueError, p.relative_to, P('C:Foo')) - self.assertRaises(ValueError, p.relative_to, P('d:')) - self.assertRaises(ValueError, p.relative_to, P('d:/')) - self.assertRaises(ValueError, p.relative_to, P('/')) - self.assertRaises(ValueError, p.relative_to, P('/Foo')) - self.assertRaises(ValueError, p.relative_to, P('//C/Foo')) - self.assertRaises(ValueError, p.relative_to, 'c:', walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('c:'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('C:Foo'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('d:'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('d:/'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('/'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('/Foo'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('//C/Foo'), walk_up=True) - # UNC paths. - p = P('//Server/Share/Foo/Bar') - self.assertEqual(p.relative_to(P('//sErver/sHare')), P('Foo/Bar')) - self.assertEqual(p.relative_to('//sErver/sHare'), P('Foo/Bar')) - self.assertEqual(p.relative_to('//sErver/sHare/'), P('Foo/Bar')) - self.assertEqual(p.relative_to(P('//sErver/sHare/Foo')), P('Bar')) - self.assertEqual(p.relative_to('//sErver/sHare/Foo'), P('Bar')) - self.assertEqual(p.relative_to('//sErver/sHare/Foo/'), P('Bar')) - self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar')), P()) - self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar'), P()) - self.assertEqual(p.relative_to(P('//sErver/sHare'), walk_up=True), P('Foo/Bar')) - self.assertEqual(p.relative_to('//sErver/sHare', walk_up=True), P('Foo/Bar')) - self.assertEqual(p.relative_to('//sErver/sHare/', walk_up=True), P('Foo/Bar')) - self.assertEqual(p.relative_to(P('//sErver/sHare/Foo'), walk_up=True), P('Bar')) - self.assertEqual(p.relative_to('//sErver/sHare/Foo', walk_up=True), P('Bar')) - self.assertEqual(p.relative_to('//sErver/sHare/Foo/', walk_up=True), P('Bar')) - self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar'), walk_up=True), P()) - self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar', walk_up=True), P()) - self.assertEqual(p.relative_to(P('//sErver/sHare/bar'), walk_up=True), P('../Foo/Bar')) - self.assertEqual(p.relative_to('//sErver/sHare/bar', walk_up=True), P('../Foo/Bar')) - # Unrelated paths. - self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo')) - self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo')) - self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo')) - self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo')) - self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo'), walk_up=True) - self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo'), walk_up=True) - - def test_is_relative_to_common(self): - P = self.cls - p = P('a/b') - self.assertRaises(TypeError, p.is_relative_to) - self.assertRaises(TypeError, p.is_relative_to, b'a') - self.assertTrue(p.is_relative_to(P(''))) - self.assertTrue(p.is_relative_to('')) - self.assertTrue(p.is_relative_to(P('a'))) - self.assertTrue(p.is_relative_to('a/')) - self.assertTrue(p.is_relative_to(P('a/b'))) - self.assertTrue(p.is_relative_to('a/b')) - # Unrelated paths. - self.assertFalse(p.is_relative_to(P('c'))) - self.assertFalse(p.is_relative_to(P('a/b/c'))) - self.assertFalse(p.is_relative_to(P('a/c'))) - self.assertFalse(p.is_relative_to(P('/a'))) - p = P('/a/b') - self.assertTrue(p.is_relative_to(P('/'))) - self.assertTrue(p.is_relative_to('/')) - self.assertTrue(p.is_relative_to(P('/a'))) - self.assertTrue(p.is_relative_to('/a')) - self.assertTrue(p.is_relative_to('/a/')) - self.assertTrue(p.is_relative_to(P('/a/b'))) - self.assertTrue(p.is_relative_to('/a/b')) - # Unrelated paths. - self.assertFalse(p.is_relative_to(P('/c'))) - self.assertFalse(p.is_relative_to(P('/a/b/c'))) - self.assertFalse(p.is_relative_to(P('/a/c'))) - self.assertFalse(p.is_relative_to(P(''))) - self.assertFalse(p.is_relative_to('')) - self.assertFalse(p.is_relative_to(P('a'))) - - @needs_windows - def test_is_relative_to_windows(self): - P = self.cls - p = P('C:Foo/Bar') - self.assertTrue(p.is_relative_to(P('c:'))) - self.assertTrue(p.is_relative_to('c:')) - self.assertTrue(p.is_relative_to(P('c:foO'))) - self.assertTrue(p.is_relative_to('c:foO')) - self.assertTrue(p.is_relative_to('c:foO/')) - self.assertTrue(p.is_relative_to(P('c:foO/baR'))) - self.assertTrue(p.is_relative_to('c:foO/baR')) - # Unrelated paths. - self.assertFalse(p.is_relative_to(P())) - self.assertFalse(p.is_relative_to('')) - self.assertFalse(p.is_relative_to(P('d:'))) - self.assertFalse(p.is_relative_to(P('/'))) - self.assertFalse(p.is_relative_to(P('Foo'))) - self.assertFalse(p.is_relative_to(P('/Foo'))) - self.assertFalse(p.is_relative_to(P('C:/Foo'))) - self.assertFalse(p.is_relative_to(P('C:Foo/Bar/Baz'))) - self.assertFalse(p.is_relative_to(P('C:Foo/Baz'))) - p = P('C:/Foo/Bar') - self.assertTrue(p.is_relative_to(P('c:/'))) - self.assertTrue(p.is_relative_to(P('c:/foO'))) - self.assertTrue(p.is_relative_to('c:/foO/')) - self.assertTrue(p.is_relative_to(P('c:/foO/baR'))) - self.assertTrue(p.is_relative_to('c:/foO/baR')) - # Unrelated paths. - self.assertFalse(p.is_relative_to('c:')) - self.assertFalse(p.is_relative_to(P('C:/Baz'))) - self.assertFalse(p.is_relative_to(P('C:/Foo/Bar/Baz'))) - self.assertFalse(p.is_relative_to(P('C:/Foo/Baz'))) - self.assertFalse(p.is_relative_to(P('C:Foo'))) - self.assertFalse(p.is_relative_to(P('d:'))) - self.assertFalse(p.is_relative_to(P('d:/'))) - self.assertFalse(p.is_relative_to(P('/'))) - self.assertFalse(p.is_relative_to(P('/Foo'))) - self.assertFalse(p.is_relative_to(P('//C/Foo'))) - # UNC paths. - p = P('//Server/Share/Foo/Bar') - self.assertTrue(p.is_relative_to(P('//sErver/sHare'))) - self.assertTrue(p.is_relative_to('//sErver/sHare')) - self.assertTrue(p.is_relative_to('//sErver/sHare/')) - self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo'))) - self.assertTrue(p.is_relative_to('//sErver/sHare/Foo')) - self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/')) - self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo/Bar'))) - self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/Bar')) - # Unrelated paths. - self.assertFalse(p.is_relative_to(P('/Server/Share/Foo'))) - self.assertFalse(p.is_relative_to(P('c:/Server/Share/Foo'))) - self.assertFalse(p.is_relative_to(P('//z/Share/Foo'))) - self.assertFalse(p.is_relative_to(P('//Server/z/Foo'))) - - @needs_posix - def test_is_absolute_posix(self): - P = self.cls - self.assertFalse(P('').is_absolute()) - self.assertFalse(P('a').is_absolute()) - self.assertFalse(P('a/b/').is_absolute()) - self.assertTrue(P('/').is_absolute()) - self.assertTrue(P('/a').is_absolute()) - self.assertTrue(P('/a/b/').is_absolute()) - self.assertTrue(P('//a').is_absolute()) - self.assertTrue(P('//a/b').is_absolute()) - - @needs_windows - def test_is_absolute_windows(self): - P = self.cls - # Under NT, only paths with both a drive and a root are absolute. - self.assertFalse(P().is_absolute()) - self.assertFalse(P('a').is_absolute()) - self.assertFalse(P('a/b/').is_absolute()) - self.assertFalse(P('/').is_absolute()) - self.assertFalse(P('/a').is_absolute()) - self.assertFalse(P('/a/b/').is_absolute()) - self.assertFalse(P('c:').is_absolute()) - self.assertFalse(P('c:a').is_absolute()) - self.assertFalse(P('c:a/b/').is_absolute()) - self.assertTrue(P('c:/').is_absolute()) - self.assertTrue(P('c:/a').is_absolute()) - self.assertTrue(P('c:/a/b/').is_absolute()) - # UNC paths are absolute by definition. - self.assertTrue(P('//').is_absolute()) - self.assertTrue(P('//a').is_absolute()) - self.assertTrue(P('//a/b').is_absolute()) - self.assertTrue(P('//a/b/').is_absolute()) - self.assertTrue(P('//a/b/c').is_absolute()) - self.assertTrue(P('//a/b/c/d').is_absolute()) - self.assertTrue(P('//?/UNC/').is_absolute()) - self.assertTrue(P('//?/UNC/spam').is_absolute()) - # # Tests for the virtual classes. @@ -1320,7 +958,7 @@ def __hash__(self): return hash(str(self)) def __repr__(self): - return "{}({!r})".format(self.__class__.__name__, self.as_posix()) + return "{}({!r})".format(self.__class__.__name__, str(self)) def with_segments(self, *pathsegments): return type(self)(*pathsegments) @@ -1631,8 +1269,8 @@ def ordered_walk(path): source_walk = ordered_walk(source) target_walk = ordered_walk(target) for source_item, target_item in zip(source_walk, target_walk, strict=True): - self.assertEqual(source_item[0].relative_to(source), - target_item[0].relative_to(target)) # dirpath + self.assertEqual(source_item[0].parts[len(source.parts):], + target_item[0].parts[len(target.parts):]) # dirpath self.assertEqual(source_item[1], target_item[1]) # dirnames self.assertEqual(source_item[2], target_item[2]) # filenames # Compare files and symlinks From 81376fef76a53fb79893bfa9c9db18d97c228fbe Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Mon, 30 Dec 2024 02:55:14 -0600 Subject: [PATCH 131/376] gh-128321: Set LIBS instead of LDFLAGS when checking sqlite3 requirements (#128322) Co-authored-by: Erlend E. Aasland --- .../next/Build/2024-12-28-21-05-19.gh-issue-128321.0UvbXw.rst | 3 +++ configure | 2 +- configure.ac | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2024-12-28-21-05-19.gh-issue-128321.0UvbXw.rst diff --git a/Misc/NEWS.d/next/Build/2024-12-28-21-05-19.gh-issue-128321.0UvbXw.rst b/Misc/NEWS.d/next/Build/2024-12-28-21-05-19.gh-issue-128321.0UvbXw.rst new file mode 100644 index 00000000000000..ed72cc8ab1449a --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-12-28-21-05-19.gh-issue-128321.0UvbXw.rst @@ -0,0 +1,3 @@ +Set ``LIBS`` instead of ``LDFLAGS`` when checking if :mod:`sqlite3` library +functions are available. This fixes the ordering of linked libraries during +checks, which was incorrect when using a statically linked ``libsqlite3``. diff --git a/configure b/configure index 299eff6bc3bf70..3d2c60213db591 100755 --- a/configure +++ b/configure @@ -15434,7 +15434,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $LIBSQLITE3_CFLAGS" - LDFLAGS="$LIBSQLITE3_LIBS $LDFLAGS" + LIBS="$LIBSQLITE3_LIBS $LIBS" ac_fn_c_check_header_compile "$LINENO" "sqlite3.h" "ac_cv_header_sqlite3_h" "$ac_includes_default" if test "x$ac_cv_header_sqlite3_h" = xyes diff --git a/configure.ac b/configure.ac index badb19d55895de..ee034e5a9621df 100644 --- a/configure.ac +++ b/configure.ac @@ -4220,7 +4220,7 @@ WITH_SAVE_ENV([ dnl bpo-45774/GH-29507: The CPP check in AC_CHECK_HEADER can fail on FreeBSD, dnl hence CPPFLAGS instead of CFLAGS. CPPFLAGS="$CPPFLAGS $LIBSQLITE3_CFLAGS" - LDFLAGS="$LIBSQLITE3_LIBS $LDFLAGS" + LIBS="$LIBSQLITE3_LIBS $LIBS" AC_CHECK_HEADER([sqlite3.h], [ have_sqlite3=yes From 2bd5a7ab0f4a1f65ab8043001bd6e8416c5079bd Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 30 Dec 2024 13:53:40 +0100 Subject: [PATCH 132/376] Docs: correctly markup sys.monitoring "What's New" entry (#128346) The sys.monitoring entry was added with commit d2f1d917e. --- Doc/whatsnew/3.14.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 53415bb09bf080..63fa21e17bc834 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -614,8 +614,8 @@ sys sys.monitoring -------------- -Two new events are added: :monitoring-event:`BRANCH_LEFT` and -:monitoring-event:`BRANCH_RIGHT`. The ``BRANCH`` event is deprecated. +* Two new events are added: :monitoring-event:`BRANCH_LEFT` and + :monitoring-event:`BRANCH_RIGHT`. The ``BRANCH`` event is deprecated. tkinter ------- From 348012432155271cfbee71a78d0c27254fc230e2 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 30 Dec 2024 08:19:38 -0800 Subject: [PATCH 133/376] gh-119180: Set the name of the param to __annotate__ to "format" (#124730) --- Lib/test/test_pydoc/test_pydoc.py | 4 +-- Lib/test/test_type_annotations.py | 49 +++++++++++++++++++++++++++++++ Python/codegen.c | 27 +++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index 3283fde9e12a8a..c798b11f5aa56e 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -79,7 +79,7 @@ class A(builtins.object) class B(builtins.object) | Methods defined here: | - | __annotate__(...) + | __annotate__(format, /) | | ---------------------------------------------------------------------- | Data descriptors defined here: @@ -180,7 +180,7 @@ class A(builtins.object) class B(builtins.object) Methods defined here: - __annotate__(...) + __annotate__(format, /) ---------------------------------------------------------------------- Data descriptors defined here: __dict__ diff --git a/Lib/test/test_type_annotations.py b/Lib/test/test_type_annotations.py index 7d88f4cdfa3141..0afcd76af153e7 100644 --- a/Lib/test/test_type_annotations.py +++ b/Lib/test/test_type_annotations.py @@ -1,4 +1,5 @@ import annotationlib +import inspect import textwrap import types import unittest @@ -380,6 +381,11 @@ class X: annotate(None) self.assertEqual(annotate(annotationlib.Format.VALUE), {"x": int}) + sig = inspect.signature(annotate) + self.assertEqual(sig, inspect.Signature([ + inspect.Parameter("format", inspect.Parameter.POSITIONAL_ONLY) + ])) + def test_comprehension_in_annotation(self): # This crashed in an earlier version of the code ns = run_code("x: [y for y in range(10)]") @@ -400,6 +406,7 @@ def f(x: int) -> int: pass def test_name_clash_with_format(self): # this test would fail if __annotate__'s parameter was called "format" + # during symbol table construction code = """ class format: pass @@ -408,3 +415,45 @@ def f(x: format): pass ns = run_code(code) f = ns["f"] self.assertEqual(f.__annotations__, {"x": ns["format"]}) + + code = """ + class Outer: + class format: pass + + def meth(self, x: format): ... + """ + ns = run_code(code) + self.assertEqual(ns["Outer"].meth.__annotations__, {"x": ns["Outer"].format}) + + code = """ + def f(format): + def inner(x: format): pass + return inner + res = f("closure var") + """ + ns = run_code(code) + self.assertEqual(ns["res"].__annotations__, {"x": "closure var"}) + + code = """ + def f(x: format): + pass + """ + ns = run_code(code) + # picks up the format() builtin + self.assertEqual(ns["f"].__annotations__, {"x": format}) + + code = """ + def outer(): + def f(x: format): + pass + if False: + class format: pass + return f + f = outer() + """ + ns = run_code(code) + with self.assertRaisesRegex( + NameError, + "cannot access free variable 'format' where it is not associated with a value in enclosing scope", + ): + ns["f"].__annotations__ diff --git a/Python/codegen.c b/Python/codegen.c index 6d3272edfdbf94..7432415b17414e 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -701,6 +701,33 @@ codegen_leave_annotations_scope(compiler *c, location loc, ADDOP_I(c, loc, BUILD_MAP, annotations_len); ADDOP_IN_SCOPE(c, loc, RETURN_VALUE); PyCodeObject *co = _PyCompile_OptimizeAndAssemble(c, 1); + + // We want the parameter to __annotate__ to be named "format" in the + // signature shown by inspect.signature(), but we need to use a + // different name (.format) in the symtable; if the name + // "format" appears in the annotations, it doesn't get clobbered + // by this name. This code is essentially: + // co->co_localsplusnames = ("format", *co->co_localsplusnames[1:]) + const Py_ssize_t size = PyObject_Size(co->co_localsplusnames); + if (size == -1) { + return ERROR; + } + PyObject *new_names = PyTuple_New(size); + if (new_names == NULL) { + return ERROR; + } + PyTuple_SET_ITEM(new_names, 0, Py_NewRef(&_Py_ID(format))); + for (int i = 1; i < size; i++) { + PyObject *item = PyTuple_GetItem(co->co_localsplusnames, i); + if (item == NULL) { + Py_DECREF(new_names); + return ERROR; + } + Py_INCREF(item); + PyTuple_SET_ITEM(new_names, i, item); + } + Py_SETREF(co->co_localsplusnames, new_names); + _PyCompile_ExitScope(c); if (co == NULL) { return ERROR; From 34b85ef26c44bbbd8a05d3e7dee6997b3623aab0 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 30 Dec 2024 18:18:42 +0100 Subject: [PATCH 134/376] gh-128118: Speed up copy.copy with fast lookup for atomic and container types (#128119) --- Lib/copy.py | 27 +++++++------------ ...-12-20-10-57-10.gh-issue-128118.mYak8i.rst | 2 ++ 2 files changed, 11 insertions(+), 18 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-20-10-57-10.gh-issue-128118.mYak8i.rst diff --git a/Lib/copy.py b/Lib/copy.py index f27e109973cfb7..c64fc0761793f5 100644 --- a/Lib/copy.py +++ b/Lib/copy.py @@ -67,13 +67,15 @@ def copy(x): cls = type(x) - copier = _copy_dispatch.get(cls) - if copier: - return copier(x) + if cls in _copy_atomic_types: + return x + if cls in _copy_builtin_containers: + return cls.copy(x) + if issubclass(cls, type): # treat it as a regular class: - return _copy_immutable(x) + return x copier = getattr(cls, "__copy__", None) if copier is not None: @@ -98,23 +100,12 @@ def copy(x): return _reconstruct(x, None, *rv) -_copy_dispatch = d = {} - -def _copy_immutable(x): - return x -for t in (types.NoneType, int, float, bool, complex, str, tuple, +_copy_atomic_types = {types.NoneType, int, float, bool, complex, str, tuple, bytes, frozenset, type, range, slice, property, types.BuiltinFunctionType, types.EllipsisType, types.NotImplementedType, types.FunctionType, types.CodeType, - weakref.ref, super): - d[t] = _copy_immutable - -d[list] = list.copy -d[dict] = dict.copy -d[set] = set.copy -d[bytearray] = bytearray.copy - -del d, t + weakref.ref, super} +_copy_builtin_containers = {list, dict, set, bytearray} def deepcopy(x, memo=None, _nil=[]): """Deep copy operation on arbitrary Python objects. diff --git a/Misc/NEWS.d/next/Library/2024-12-20-10-57-10.gh-issue-128118.mYak8i.rst b/Misc/NEWS.d/next/Library/2024-12-20-10-57-10.gh-issue-128118.mYak8i.rst new file mode 100644 index 00000000000000..bc2898edfda721 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-20-10-57-10.gh-issue-128118.mYak8i.rst @@ -0,0 +1,2 @@ +Improve performance of :func:`copy.copy` by 30% via +a fast path for atomic types and container types. From 6dbace397fab5154f66226abb11af32d2d13219f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 30 Dec 2024 19:25:39 +0200 Subject: [PATCH 135/376] gh-128317: Document `calendar.TextCalendar.formatweek` (#128353) --- Doc/library/calendar.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index eafc038d6cb722..9a4395ecb5084d 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -138,6 +138,14 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is :class:`TextCalendar` instances have the following methods: + .. method:: formatweek(theweek, w=0) + + Return a single week in a string with no newline. If *w* is provided, it + specifies the width of the date columns, which are centered. Depends + on the first weekday as specified in the constructor or set by the + :meth:`setfirstweekday` method. + + .. method:: formatmonth(theyear, themonth, w=0, l=0) Return a month's calendar in a multi-line string. If *w* is provided, it From fe4dd07a84ba423179a93ed84bdcc2b4c99b35a9 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Mon, 30 Dec 2024 19:38:09 +0100 Subject: [PATCH 136/376] gh-119786: Mention `InternalDocs/interpreter.md` instead of non-existing `adaptive.md` (#128329) `Python/specialize.c`: Mention `InternalDocs/interpreter.md` instead of non-existing `adaptive.md` Co-authored-by: Peter Bierma --- Python/specialize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/specialize.c b/Python/specialize.c index 349ed472298945..2148b62c3a1953 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -21,7 +21,7 @@ extern const char *_PyUOpName(int index); /* For guidance on adding or extending families of instructions see - * ./adaptive.md + * InternalDocs/interpreter.md `Specialization` section. */ #ifdef Py_STATS From 47d2cb8eb7595df5940225867dbb66b6dd59413a Mon Sep 17 00:00:00 2001 From: Bogdan Romanyuk <65823030+wrongnull@users.noreply.github.com> Date: Mon, 30 Dec 2024 23:38:49 +0300 Subject: [PATCH 137/376] gh-128100: Use atomic dictionary load in `_PyObject_GenericGetAttrWithDict` (GH-128297) --- Objects/object.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Objects/object.c b/Objects/object.c index d584414c559b9d..4c30257ca26938 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1717,7 +1717,11 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, else { PyObject **dictptr = _PyObject_ComputedDictPointer(obj); if (dictptr) { +#ifdef Py_GIL_DISABLED + dict = _Py_atomic_load_ptr_acquire(dictptr); +#else dict = *dictptr; +#endif } } } From dafe7a44630aa32bb411cceb45c7b7df725e3fe3 Mon Sep 17 00:00:00 2001 From: n-l-i <57808975+n-l-i@users.noreply.github.com> Date: Mon, 30 Dec 2024 21:52:04 +0100 Subject: [PATCH 138/376] gh-128342: Specify timeout unit in subprocess docstrings (GH-128343) Specify timeout unit (seconds) in subprocess docstrings Co-authored-by: Terry Jan Reedy --- Lib/subprocess.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 88f0230b05fbc7..de88eedcf80ff9 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -386,7 +386,7 @@ def _text_encoding(): def call(*popenargs, timeout=None, **kwargs): """Run command with arguments. Wait for command to complete or - timeout, then return the returncode attribute. + for timeout seconds, then return the returncode attribute. The arguments are the same as for the Popen constructor. Example: @@ -523,8 +523,8 @@ def run(*popenargs, in the returncode attribute, and output & stderr attributes if those streams were captured. - If timeout is given, and the process takes too long, a TimeoutExpired - exception will be raised. + If timeout (seconds) is given and the process takes too long, + a TimeoutExpired exception will be raised. There is an optional argument "input", allowing you to pass bytes or a string to the subprocess's stdin. If you use this argument From 7ef49074123511003c8b7f7f3ba2a4e05285e8dc Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Tue, 31 Dec 2024 12:24:17 +0800 Subject: [PATCH 139/376] gh-128262: Allow specialization of calls to classes with __slots__ (GH-128263) --- Include/internal/pycore_uop_metadata.h | 2 +- Python/bytecodes.c | 6 ++++-- Python/executor_cases.c.h | 8 ++++++-- Python/generated_cases.c.h | 8 ++++++-- Python/specialize.c | 4 ---- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index e71194b116e020..73fc29eb78a7a4 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -234,7 +234,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_CALL_TYPE_1] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_CALL_STR_1] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_TUPLE_1] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_CHECK_AND_ALLOCATE_OBJECT] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG, + [_CHECK_AND_ALLOCATE_OBJECT] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_CREATE_INIT_FRAME] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_EXIT_INIT_CHECK] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_CALL_BUILTIN_CLASS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 63cf1978e8abe5..602cf7f47b812b 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -3765,13 +3765,15 @@ dummy_func( DEOPT_IF(!PyType_Check(callable_o)); PyTypeObject *tp = (PyTypeObject *)callable_o; DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(tp->tp_version_tag) != type_version); - assert(tp->tp_flags & Py_TPFLAGS_INLINE_VALUES); + assert(tp->tp_new == PyBaseObject_Type.tp_new); + assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE); + assert(tp->tp_alloc == PyType_GenericAlloc); PyHeapTypeObject *cls = (PyHeapTypeObject *)callable_o; PyFunctionObject *init_func = (PyFunctionObject *)FT_ATOMIC_LOAD_PTR_ACQUIRE(cls->_spec_cache.init); PyCodeObject *code = (PyCodeObject *)init_func->func_code; DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize)); STAT_INC(CALL, hit); - PyObject *self_o = _PyType_NewManagedObject(tp); + PyObject *self_o = PyType_GenericAlloc(tp, 0); if (self_o == NULL) { ERROR_NO_POP(); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 22335021faaa6d..f7374d52705960 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -4572,7 +4572,9 @@ UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - assert(tp->tp_flags & Py_TPFLAGS_INLINE_VALUES); + assert(tp->tp_new == PyBaseObject_Type.tp_new); + assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE); + assert(tp->tp_alloc == PyType_GenericAlloc); PyHeapTypeObject *cls = (PyHeapTypeObject *)callable_o; PyFunctionObject *init_func = (PyFunctionObject *)FT_ATOMIC_LOAD_PTR_ACQUIRE(cls->_spec_cache.init); PyCodeObject *code = (PyCodeObject *)init_func->func_code; @@ -4581,7 +4583,9 @@ JUMP_TO_JUMP_TARGET(); } STAT_INC(CALL, hit); - PyObject *self_o = _PyType_NewManagedObject(tp); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *self_o = PyType_GenericAlloc(tp, 0); + stack_pointer = _PyFrame_GetStackPointer(frame); if (self_o == NULL) { JUMP_TO_ERROR(); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index bed16b60b76a2f..98743c27c38524 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1048,13 +1048,17 @@ DEOPT_IF(!PyType_Check(callable_o), CALL); PyTypeObject *tp = (PyTypeObject *)callable_o; DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(tp->tp_version_tag) != type_version, CALL); - assert(tp->tp_flags & Py_TPFLAGS_INLINE_VALUES); + assert(tp->tp_new == PyBaseObject_Type.tp_new); + assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE); + assert(tp->tp_alloc == PyType_GenericAlloc); PyHeapTypeObject *cls = (PyHeapTypeObject *)callable_o; PyFunctionObject *init_func = (PyFunctionObject *)FT_ATOMIC_LOAD_PTR_ACQUIRE(cls->_spec_cache.init); PyCodeObject *code = (PyCodeObject *)init_func->func_code; DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize), CALL); STAT_INC(CALL, hit); - PyObject *self_o = _PyType_NewManagedObject(tp); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *self_o = PyType_GenericAlloc(tp, 0); + stack_pointer = _PyFrame_GetStackPointer(frame); if (self_o == NULL) { goto error; } diff --git a/Python/specialize.c b/Python/specialize.c index 2148b62c3a1953..c918c77779d20d 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -2002,10 +2002,6 @@ get_init_for_simple_managed_python_class(PyTypeObject *tp, unsigned int *tp_vers return NULL; } unsigned long tp_flags = PyType_GetFlags(tp); - if ((tp_flags & Py_TPFLAGS_INLINE_VALUES) == 0) { - SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_INIT_NOT_INLINE_VALUES); - return NULL; - } if (!(tp_flags & Py_TPFLAGS_HEAPTYPE)) { /* Is this possible? */ SPECIALIZATION_FAIL(CALL, SPEC_FAIL_EXPECTED_ERROR); From 7c72c1f0dfff8950e3c6f656307099f1b0a4f8a3 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Tue, 31 Dec 2024 11:50:35 +0530 Subject: [PATCH 140/376] gh-128277: use relaxed atomics for `sock_fd` (#128304) --- Modules/socketmodule.c | 167 +++++++++++++++++++++++++---------------- 1 file changed, 102 insertions(+), 65 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 1e95be9b1bc9f4..1f0cbf6abb184c 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -110,6 +110,7 @@ Local naming conventions: #include "pycore_fileutils.h" // _Py_set_inheritable() #include "pycore_moduleobject.h" // _PyModule_GetState #include "pycore_time.h" // _PyTime_AsMilliseconds() +#include "pycore_pyatomic_ft_wrappers.h" #ifdef _Py_MEMORY_SANITIZER # include @@ -564,6 +565,42 @@ typedef struct _socket_state { #endif } socket_state; +static inline void +set_sock_fd(PySocketSockObject *s, SOCKET_T fd) +{ +#ifdef Py_GIL_DISABLED +#if SIZEOF_SOCKET_T == SIZEOF_INT + _Py_atomic_store_int_relaxed((int *)&s->sock_fd, (int)fd); +#elif SIZEOF_SOCKET_T == SIZEOF_LONG + _Py_atomic_store_long_relaxed((long *)&s->sock_fd, (long)fd); +#elif SIZEOF_SOCKET_T == SIZEOF_LONG_LONG + _Py_atomic_store_llong_relaxed((long long *)&s->sock_fd, (long long)fd); +#else + #error "Unsupported SIZEOF_SOCKET_T" +#endif +#else + s->sock_fd = fd; +#endif +} + +static inline SOCKET_T +get_sock_fd(PySocketSockObject *s) +{ +#ifdef Py_GIL_DISABLED +#if SIZEOF_SOCKET_T == SIZEOF_INT + return (SOCKET_T)_Py_atomic_load_int_relaxed((int *)&s->sock_fd); +#elif SIZEOF_SOCKET_T == SIZEOF_LONG + return (SOCKET_T)_Py_atomic_load_long_relaxed((long *)&s->sock_fd); +#elif SIZEOF_SOCKET_T == SIZEOF_LONG_LONG + return (SOCKET_T)_Py_atomic_load_llong_relaxed((long long *)&s->sock_fd); +#else + #error "Unsupported SIZEOF_SOCKET_T" +#endif +#else + return s->sock_fd; +#endif +} + static inline socket_state * get_module_state(PyObject *mod) { @@ -736,10 +773,10 @@ internal_setblocking(PySocketSockObject *s, int block) #ifndef MS_WINDOWS #if (defined(HAVE_SYS_IOCTL_H) && defined(FIONBIO)) block = !block; - if (ioctl(s->sock_fd, FIONBIO, (unsigned int *)&block) == -1) + if (ioctl(get_sock_fd(s), FIONBIO, (unsigned int *)&block) == -1) goto done; #else - delay_flag = fcntl(s->sock_fd, F_GETFL, 0); + delay_flag = fcntl(get_sock_fd(s), F_GETFL, 0); if (delay_flag == -1) goto done; if (block) @@ -747,12 +784,12 @@ internal_setblocking(PySocketSockObject *s, int block) else new_delay_flag = delay_flag | O_NONBLOCK; if (new_delay_flag != delay_flag) - if (fcntl(s->sock_fd, F_SETFL, new_delay_flag) == -1) + if (fcntl(get_sock_fd(s), F_SETFL, new_delay_flag) == -1) goto done; #endif #else /* MS_WINDOWS */ arg = !block; - if (ioctlsocket(s->sock_fd, FIONBIO, &arg) != 0) + if (ioctlsocket(get_sock_fd(s), FIONBIO, &arg) != 0) goto done; #endif /* MS_WINDOWS */ @@ -792,13 +829,13 @@ internal_select(PySocketSockObject *s, int writing, PyTime_t interval, assert(!(connect && !writing)); /* Guard against closed socket */ - if (s->sock_fd == INVALID_SOCKET) + if (get_sock_fd(s) == INVALID_SOCKET) return 0; /* Prefer poll, if available, since you can poll() any fd * which can't be done with select(). */ #ifdef HAVE_POLL - pollfd.fd = s->sock_fd; + pollfd.fd = get_sock_fd(s); pollfd.events = writing ? POLLOUT : POLLIN; if (connect) { /* On Windows, the socket becomes writable on connection success, @@ -838,23 +875,23 @@ internal_select(PySocketSockObject *s, int writing, PyTime_t interval, tvp = NULL; FD_ZERO(&fds); - FD_SET(s->sock_fd, &fds); + FD_SET(get_sock_fd(s), &fds); FD_ZERO(&efds); if (connect) { /* On Windows, the socket becomes writable on connection success, but a connection failure is notified as an error. On POSIX, the socket becomes writable on connection success or on connection failure. */ - FD_SET(s->sock_fd, &efds); + FD_SET(get_sock_fd(s), &efds); } /* See if the socket is ready */ Py_BEGIN_ALLOW_THREADS; if (writing) - n = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int), + n = select(Py_SAFE_DOWNCAST(get_sock_fd(s)+1, SOCKET_T, int), NULL, &fds, &efds, tvp); else - n = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int), + n = select(Py_SAFE_DOWNCAST(get_sock_fd(s)+1, SOCKET_T, int), &fds, NULL, &efds, tvp); Py_END_ALLOW_THREADS; #endif @@ -1030,7 +1067,7 @@ static int init_sockobject(socket_state *state, PySocketSockObject *s, SOCKET_T fd, int family, int type, int proto) { - s->sock_fd = fd; + set_sock_fd(s, fd); s->sock_family = family; s->sock_type = type; @@ -2127,7 +2164,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, } strncpy(ifr.ifr_name, interfaceName, sizeof(ifr.ifr_name)); ifr.ifr_name[(sizeof(ifr.ifr_name))-1] = '\0'; - if (ioctl(s->sock_fd, SIOCGIFINDEX, &ifr) < 0) { + if (ioctl(get_sock_fd(s), SIOCGIFINDEX, &ifr) < 0) { s->errorhandler(); PyBuffer_Release(&haddr); return 0; @@ -2252,7 +2289,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, } else if ((size_t)len < sizeof(ifr.ifr_name)) { strncpy(ifr.ifr_name, PyBytes_AS_STRING(interfaceName), sizeof(ifr.ifr_name)); ifr.ifr_name[(sizeof(ifr.ifr_name))-1] = '\0'; - if (ioctl(s->sock_fd, SIOCGIFINDEX, &ifr) < 0) { + if (ioctl(get_sock_fd(s), SIOCGIFINDEX, &ifr) < 0) { s->errorhandler(); Py_DECREF(interfaceName); return 0; @@ -2296,7 +2333,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, } else if ((size_t)len < sizeof(ifr.ifr_name)) { strncpy(ifr.ifr_name, PyBytes_AS_STRING(interfaceName), sizeof(ifr.ifr_name)); ifr.ifr_name[(sizeof(ifr.ifr_name))-1] = '\0'; - if (ioctl(s->sock_fd, SIOCGIFINDEX, &ifr) < 0) { + if (ioctl(get_sock_fd(s), SIOCGIFINDEX, &ifr) < 0) { s->errorhandler(); Py_DECREF(interfaceName); return 0; @@ -2344,7 +2381,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, } else if ((size_t)len < sizeof(ifr.ifr_name)) { strncpy(ifr.ifr_name, PyBytes_AS_STRING(interfaceName), sizeof(ifr.ifr_name)); ifr.ifr_name[(sizeof(ifr.ifr_name))-1] = '\0'; - if (ioctl(s->sock_fd, SIOCGIFINDEX, &ifr) < 0) { + if (ioctl(get_sock_fd(s), SIOCGIFINDEX, &ifr) < 0) { s->errorhandler(); Py_DECREF(interfaceName); return 0; @@ -2403,7 +2440,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, sizeof(info.ctl_name)); Py_DECREF(ctl_name); - if (ioctl(s->sock_fd, CTLIOCGINFO, &info)) { + if (ioctl(get_sock_fd(s), CTLIOCGINFO, &info)) { PyErr_SetString(PyExc_OSError, "cannot find kernel control with provided name"); return 0; @@ -2869,7 +2906,7 @@ sock_accept_impl(PySocketSockObject *s, void *data) #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) socket_state *state = s->state; if (state->accept4_works != 0) { - ctx->result = accept4(s->sock_fd, addr, paddrlen, + ctx->result = accept4(get_sock_fd(s), addr, paddrlen, SOCK_CLOEXEC); if (ctx->result == INVALID_SOCKET && state->accept4_works == -1) { /* On Linux older than 2.6.28, accept4() fails with ENOSYS */ @@ -2877,9 +2914,9 @@ sock_accept_impl(PySocketSockObject *s, void *data) } } if (state->accept4_works == 0) - ctx->result = accept(s->sock_fd, addr, paddrlen); + ctx->result = accept(get_sock_fd(s), addr, paddrlen); #else - ctx->result = accept(s->sock_fd, addr, paddrlen); + ctx->result = accept(get_sock_fd(s), addr, paddrlen); #endif #ifdef MS_WINDOWS @@ -2946,7 +2983,7 @@ sock_accept(PySocketSockObject *s, PyObject *Py_UNUSED(ignored)) goto finally; } - addr = makesockaddr(s->sock_fd, SAS2SA(&addrbuf), + addr = makesockaddr(get_sock_fd(s), SAS2SA(&addrbuf), addrlen, s->sock_proto); if (addr == NULL) goto finally; @@ -3158,7 +3195,7 @@ sock_setsockopt(PySocketSockObject *s, PyObject *args) if (PyArg_ParseTuple(args, "iiK:setsockopt", &level, &optname, &vflag)) { // level should always be set to AF_VSOCK - res = setsockopt(s->sock_fd, level, optname, + res = setsockopt(get_sock_fd(s), level, optname, (void*)&vflag, sizeof vflag); goto done; } @@ -3172,7 +3209,7 @@ sock_setsockopt(PySocketSockObject *s, PyObject *args) #ifdef MS_WINDOWS if (optname == SIO_TCP_SET_ACK_FREQUENCY) { int dummy; - res = WSAIoctl(s->sock_fd, SIO_TCP_SET_ACK_FREQUENCY, &flag, + res = WSAIoctl(get_sock_fd(s), SIO_TCP_SET_ACK_FREQUENCY, &flag, sizeof(flag), NULL, 0, &dummy, NULL, NULL); if (res >= 0) { s->quickack = flag; @@ -3180,7 +3217,7 @@ sock_setsockopt(PySocketSockObject *s, PyObject *args) goto done; } #endif - res = setsockopt(s->sock_fd, level, optname, + res = setsockopt(get_sock_fd(s), level, optname, (char*)&flag, sizeof flag); goto done; } @@ -3190,7 +3227,7 @@ sock_setsockopt(PySocketSockObject *s, PyObject *args) if (PyArg_ParseTuple(args, "iiO!I:setsockopt", &level, &optname, Py_TYPE(Py_None), &none, &optlen)) { assert(sizeof(socklen_t) >= sizeof(unsigned int)); - res = setsockopt(s->sock_fd, level, optname, + res = setsockopt(get_sock_fd(s), level, optname, NULL, (socklen_t)optlen); goto done; } @@ -3209,10 +3246,10 @@ sock_setsockopt(PySocketSockObject *s, PyObject *args) INT_MAX); return NULL; } - res = setsockopt(s->sock_fd, level, optname, + res = setsockopt(get_sock_fd(s), level, optname, optval.buf, (int)optval.len); #else - res = setsockopt(s->sock_fd, level, optname, optval.buf, optval.len); + res = setsockopt(get_sock_fd(s), level, optname, optval.buf, optval.len); #endif PyBuffer_Release(&optval); @@ -3259,7 +3296,7 @@ sock_getsockopt(PySocketSockObject *s, PyObject *args) if (s->sock_family == AF_VSOCK) { uint64_t vflag = 0; // Must be set width of 64 bits flagsize = sizeof vflag; - res = getsockopt(s->sock_fd, level, optname, + res = getsockopt(get_sock_fd(s), level, optname, (void *)&vflag, &flagsize); if (res < 0) return s->errorhandler(); @@ -3272,7 +3309,7 @@ sock_getsockopt(PySocketSockObject *s, PyObject *args) } #endif flagsize = sizeof flag; - res = getsockopt(s->sock_fd, level, optname, + res = getsockopt(get_sock_fd(s), level, optname, (void *)&flag, &flagsize); if (res < 0) return s->errorhandler(); @@ -3293,7 +3330,7 @@ sock_getsockopt(PySocketSockObject *s, PyObject *args) buf = PyBytes_FromStringAndSize((char *)NULL, buflen); if (buf == NULL) return NULL; - res = getsockopt(s->sock_fd, level, optname, + res = getsockopt(get_sock_fd(s), level, optname, (void *)PyBytes_AS_STRING(buf), &buflen); if (res < 0) { Py_DECREF(buf); @@ -3330,7 +3367,7 @@ sock_bind(PySocketSockObject *s, PyObject *addro) } Py_BEGIN_ALLOW_THREADS - res = bind(s->sock_fd, SAS2SA(&addrbuf), addrlen); + res = bind(get_sock_fd(s), SAS2SA(&addrbuf), addrlen); Py_END_ALLOW_THREADS if (res < 0) return s->errorhandler(); @@ -3367,9 +3404,9 @@ _socket_socket_close_impl(PySocketSockObject *s) SOCKET_T fd; int res; - fd = s->sock_fd; + fd = get_sock_fd(s); if (fd != INVALID_SOCKET) { - s->sock_fd = INVALID_SOCKET; + set_sock_fd(s, INVALID_SOCKET); /* We do not want to retry upon EINTR: see http://lwn.net/Articles/576478/ and @@ -3390,8 +3427,8 @@ _socket_socket_close_impl(PySocketSockObject *s) static PyObject * sock_detach(PySocketSockObject *s, PyObject *Py_UNUSED(ignored)) { - SOCKET_T fd = s->sock_fd; - s->sock_fd = INVALID_SOCKET; + SOCKET_T fd = get_sock_fd(s); + set_sock_fd(s, INVALID_SOCKET); return PyLong_FromSocket_t(fd); } @@ -3409,7 +3446,7 @@ sock_connect_impl(PySocketSockObject *s, void* Py_UNUSED(data)) int err; socklen_t size = sizeof err; - if (getsockopt(s->sock_fd, SOL_SOCKET, SO_ERROR, (void *)&err, &size)) { + if (getsockopt(get_sock_fd(s), SOL_SOCKET, SO_ERROR, (void *)&err, &size)) { /* getsockopt() failed */ return 0; } @@ -3443,7 +3480,7 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen, int res, err, wait_connect; Py_BEGIN_ALLOW_THREADS - res = connect(s->sock_fd, addr, addrlen); + res = connect(get_sock_fd(s), addr, addrlen); Py_END_ALLOW_THREADS if (!res) { @@ -3573,7 +3610,7 @@ instead of raising an exception when an error occurs."); static PyObject * sock_fileno(PySocketSockObject *s, PyObject *Py_UNUSED(ignored)) { - return PyLong_FromSocket_t(s->sock_fd); + return PyLong_FromSocket_t(get_sock_fd(s)); } PyDoc_STRVAR(fileno_doc, @@ -3596,11 +3633,11 @@ sock_getsockname(PySocketSockObject *s, PyObject *Py_UNUSED(ignored)) return NULL; memset(&addrbuf, 0, addrlen); Py_BEGIN_ALLOW_THREADS - res = getsockname(s->sock_fd, SAS2SA(&addrbuf), &addrlen); + res = getsockname(get_sock_fd(s), SAS2SA(&addrbuf), &addrlen); Py_END_ALLOW_THREADS if (res < 0) return s->errorhandler(); - return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen, + return makesockaddr(get_sock_fd(s), SAS2SA(&addrbuf), addrlen, s->sock_proto); } @@ -3628,11 +3665,11 @@ sock_getpeername(PySocketSockObject *s, PyObject *Py_UNUSED(ignored)) return NULL; memset(&addrbuf, 0, addrlen); Py_BEGIN_ALLOW_THREADS - res = getpeername(s->sock_fd, SAS2SA(&addrbuf), &addrlen); + res = getpeername(get_sock_fd(s), SAS2SA(&addrbuf), &addrlen); Py_END_ALLOW_THREADS if (res < 0) return s->errorhandler(); - return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen, + return makesockaddr(get_sock_fd(s), SAS2SA(&addrbuf), addrlen, s->sock_proto); } @@ -3664,7 +3701,7 @@ sock_listen(PySocketSockObject *s, PyObject *args) * (which doesn't make sense anyway) we force a minimum value of 0. */ if (backlog < 0) backlog = 0; - res = listen(s->sock_fd, backlog); + res = listen(get_sock_fd(s), backlog); Py_END_ALLOW_THREADS if (res < 0) return s->errorhandler(); @@ -3695,9 +3732,9 @@ sock_recv_impl(PySocketSockObject *s, void *data) #ifdef MS_WINDOWS if (ctx->len > INT_MAX) ctx->len = INT_MAX; - ctx->result = recv(s->sock_fd, ctx->cbuf, (int)ctx->len, ctx->flags); + ctx->result = recv(get_sock_fd(s), ctx->cbuf, (int)ctx->len, ctx->flags); #else - ctx->result = recv(s->sock_fd, ctx->cbuf, ctx->len, ctx->flags); + ctx->result = recv(get_sock_fd(s), ctx->cbuf, ctx->len, ctx->flags); #endif return (ctx->result >= 0); } @@ -3866,10 +3903,10 @@ sock_recvfrom_impl(PySocketSockObject *s, void *data) #ifdef MS_WINDOWS if (ctx->len > INT_MAX) ctx->len = INT_MAX; - ctx->result = recvfrom(s->sock_fd, ctx->cbuf, (int)ctx->len, ctx->flags, + ctx->result = recvfrom(get_sock_fd(s), ctx->cbuf, (int)ctx->len, ctx->flags, SAS2SA(ctx->addrbuf), ctx->addrlen); #else - ctx->result = recvfrom(s->sock_fd, ctx->cbuf, ctx->len, ctx->flags, + ctx->result = recvfrom(get_sock_fd(s), ctx->cbuf, ctx->len, ctx->flags, SAS2SA(ctx->addrbuf), ctx->addrlen); #endif return (ctx->result >= 0); @@ -3913,7 +3950,7 @@ sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags, if (sock_call(s, 0, sock_recvfrom_impl, &ctx) < 0) return -1; - *addr = makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen, + *addr = makesockaddr(get_sock_fd(s), SAS2SA(&addrbuf), addrlen, s->sock_proto); if (*addr == NULL) return -1; @@ -4044,7 +4081,7 @@ sock_recvmsg_impl(PySocketSockObject *s, void *data) { struct sock_recvmsg *ctx = data; - ctx->result = recvmsg(s->sock_fd, ctx->msg, ctx->flags); + ctx->result = recvmsg(get_sock_fd(s), ctx->msg, ctx->flags); return (ctx->result >= 0); } @@ -4153,7 +4190,7 @@ sock_recvmsg_guts(PySocketSockObject *s, struct iovec *iov, int iovlen, (*makeval)(ctx.result, makeval_data), cmsg_list, (int)msg.msg_flags, - makesockaddr(s->sock_fd, SAS2SA(&addrbuf), + makesockaddr(get_sock_fd(s), SAS2SA(&addrbuf), ((msg.msg_namelen > addrbuflen) ? addrbuflen : msg.msg_namelen), s->sock_proto)); @@ -4372,9 +4409,9 @@ sock_send_impl(PySocketSockObject *s, void *data) #ifdef MS_WINDOWS if (ctx->len > INT_MAX) ctx->len = INT_MAX; - ctx->result = send(s->sock_fd, ctx->buf, (int)ctx->len, ctx->flags); + ctx->result = send(get_sock_fd(s), ctx->buf, (int)ctx->len, ctx->flags); #else - ctx->result = send(s->sock_fd, ctx->buf, ctx->len, ctx->flags); + ctx->result = send(get_sock_fd(s), ctx->buf, ctx->len, ctx->flags); #endif return (ctx->result >= 0); } @@ -4511,10 +4548,10 @@ sock_sendto_impl(PySocketSockObject *s, void *data) #ifdef MS_WINDOWS if (ctx->len > INT_MAX) ctx->len = INT_MAX; - ctx->result = sendto(s->sock_fd, ctx->buf, (int)ctx->len, ctx->flags, + ctx->result = sendto(get_sock_fd(s), ctx->buf, (int)ctx->len, ctx->flags, SAS2SA(ctx->addrbuf), ctx->addrlen); #else - ctx->result = sendto(s->sock_fd, ctx->buf, ctx->len, ctx->flags, + ctx->result = sendto(get_sock_fd(s), ctx->buf, ctx->len, ctx->flags, SAS2SA(ctx->addrbuf), ctx->addrlen); #endif return (ctx->result >= 0); @@ -4660,7 +4697,7 @@ sock_sendmsg_impl(PySocketSockObject *s, void *data) { struct sock_sendmsg *ctx = data; - ctx->result = sendmsg(s->sock_fd, ctx->msg, ctx->flags); + ctx->result = sendmsg(get_sock_fd(s), ctx->msg, ctx->flags); return (ctx->result >= 0); } @@ -5048,7 +5085,7 @@ sock_shutdown(PySocketSockObject *s, PyObject *arg) if (how == -1 && PyErr_Occurred()) return NULL; Py_BEGIN_ALLOW_THREADS - res = shutdown(s->sock_fd, how); + res = shutdown(get_sock_fd(s), how); Py_END_ALLOW_THREADS if (res < 0) return s->errorhandler(); @@ -5078,7 +5115,7 @@ sock_ioctl(PySocketSockObject *s, PyObject *arg) unsigned int option = RCVALL_ON; if (!PyArg_ParseTuple(arg, "kI:ioctl", &cmd, &option)) return NULL; - if (WSAIoctl(s->sock_fd, cmd, &option, sizeof(option), + if (WSAIoctl(get_sock_fd(s), cmd, &option, sizeof(option), NULL, 0, &recv, NULL, NULL) == SOCKET_ERROR) { return set_error(); } @@ -5088,7 +5125,7 @@ sock_ioctl(PySocketSockObject *s, PyObject *arg) if (!PyArg_ParseTuple(arg, "k(kkk):ioctl", &cmd, &ka.onoff, &ka.keepalivetime, &ka.keepaliveinterval)) return NULL; - if (WSAIoctl(s->sock_fd, cmd, &ka, sizeof(ka), + if (WSAIoctl(get_sock_fd(s), cmd, &ka, sizeof(ka), NULL, 0, &recv, NULL, NULL) == SOCKET_ERROR) { return set_error(); } @@ -5098,7 +5135,7 @@ sock_ioctl(PySocketSockObject *s, PyObject *arg) unsigned int option; if (!PyArg_ParseTuple(arg, "kI:ioctl", &cmd, &option)) return NULL; - if (WSAIoctl(s->sock_fd, cmd, &option, sizeof(option), + if (WSAIoctl(get_sock_fd(s), cmd, &option, sizeof(option), NULL, 0, &recv, NULL, NULL) == SOCKET_ERROR) { return set_error(); } @@ -5130,7 +5167,7 @@ sock_share(PySocketSockObject *s, PyObject *arg) return NULL; Py_BEGIN_ALLOW_THREADS - result = WSADuplicateSocketW(s->sock_fd, processId, &info); + result = WSADuplicateSocketW(get_sock_fd(s), processId, &info); Py_END_ALLOW_THREADS if (result == SOCKET_ERROR) return set_error(); @@ -5264,7 +5301,7 @@ sock_finalize(PySocketSockObject *s) /* Save the current exception, if any. */ PyObject *exc = PyErr_GetRaisedException(); - if (s->sock_fd != INVALID_SOCKET) { + if (get_sock_fd(s) != INVALID_SOCKET) { if (PyErr_ResourceWarning((PyObject *)s, 1, "unclosed %R", s)) { /* Spurious errors can appear at shutdown */ if (PyErr_ExceptionMatches(PyExc_Warning)) { @@ -5276,8 +5313,8 @@ sock_finalize(PySocketSockObject *s) to allow the logger to call socket methods like socket.getsockname(). If the socket is closed before, socket methods fails with the EBADF error. */ - fd = s->sock_fd; - s->sock_fd = INVALID_SOCKET; + fd = get_sock_fd(s); + set_sock_fd(s, INVALID_SOCKET); /* We do not want to retry upon EINTR: see sock_close() */ Py_BEGIN_ALLOW_THREADS @@ -5314,11 +5351,11 @@ sock_repr(PySocketSockObject *s) { long sock_fd; /* On Windows, this test is needed because SOCKET_T is unsigned */ - if (s->sock_fd == INVALID_SOCKET) { + if (get_sock_fd(s) == INVALID_SOCKET) { sock_fd = -1; } #if SIZEOF_SOCKET_T > SIZEOF_LONG - else if (s->sock_fd > LONG_MAX) { + else if (get_sock_fd(s) > LONG_MAX) { /* this can occur on Win64, and actually there is a special ugly printf formatter for decimal pointer length integer printing, only bother if necessary*/ @@ -5329,7 +5366,7 @@ sock_repr(PySocketSockObject *s) } #endif else - sock_fd = (long)s->sock_fd; + sock_fd = (long)get_sock_fd(s); return PyUnicode_FromFormat( "", sock_fd, s->sock_family, From b2ac70a62ad1be8e037ce45ccf5f1b753ea5e64b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 31 Dec 2024 10:02:58 +0200 Subject: [PATCH 141/376] gh-88834: Unify the instance check for typing.Union and types.UnionType (GH-128363) Union now uses the instance checks against its parameters instead of the subclass checks. --- Lib/test/test_typing.py | 75 ++++++++++++++++++- Lib/typing.py | 6 +- ...4-12-30-20-48-28.gh-issue-88834.RIvgwc.rst | 3 + 3 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-30-20-48-28.gh-issue-88834.RIvgwc.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index aa42beca5f9256..ef3cfc9517085e 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -122,7 +122,7 @@ class Sub(Any): pass def test_errors(self): with self.assertRaises(TypeError): - issubclass(42, Any) + isinstance(42, Any) with self.assertRaises(TypeError): Any[int] # Any is not a generic type. @@ -137,6 +137,9 @@ class Something: pass class MockSomething(Something, Mock): pass self.assertTrue(issubclass(MockSomething, Any)) + self.assertTrue(issubclass(MockSomething, MockSomething)) + self.assertTrue(issubclass(MockSomething, Something)) + self.assertTrue(issubclass(MockSomething, Mock)) ms = MockSomething() self.assertIsInstance(ms, MockSomething) self.assertIsInstance(ms, Something) @@ -2010,13 +2013,81 @@ def test_basics(self): u = Union[int, float] self.assertNotEqual(u, Union) - def test_subclass_error(self): + def test_union_isinstance(self): + self.assertTrue(isinstance(42, Union[int, str])) + self.assertTrue(isinstance('abc', Union[int, str])) + self.assertFalse(isinstance(3.14, Union[int, str])) + self.assertTrue(isinstance(42, Union[int, list[int]])) + self.assertTrue(isinstance(42, Union[int, Any])) + + def test_union_isinstance_type_error(self): + with self.assertRaises(TypeError): + isinstance(42, Union[str, list[int]]) + with self.assertRaises(TypeError): + isinstance(42, Union[list[int], int]) + with self.assertRaises(TypeError): + isinstance(42, Union[list[int], str]) + with self.assertRaises(TypeError): + isinstance(42, Union[str, Any]) + with self.assertRaises(TypeError): + isinstance(42, Union[Any, int]) + with self.assertRaises(TypeError): + isinstance(42, Union[Any, str]) + + def test_optional_isinstance(self): + self.assertTrue(isinstance(42, Optional[int])) + self.assertTrue(isinstance(None, Optional[int])) + self.assertFalse(isinstance('abc', Optional[int])) + + def test_optional_isinstance_type_error(self): + with self.assertRaises(TypeError): + isinstance(42, Optional[list[int]]) + with self.assertRaises(TypeError): + isinstance(None, Optional[list[int]]) + with self.assertRaises(TypeError): + isinstance(42, Optional[Any]) + with self.assertRaises(TypeError): + isinstance(None, Optional[Any]) + + def test_union_issubclass(self): + self.assertTrue(issubclass(int, Union[int, str])) + self.assertTrue(issubclass(str, Union[int, str])) + self.assertFalse(issubclass(float, Union[int, str])) + self.assertTrue(issubclass(int, Union[int, list[int]])) + self.assertTrue(issubclass(int, Union[int, Any])) + self.assertFalse(issubclass(int, Union[str, Any])) + self.assertTrue(issubclass(int, Union[Any, int])) + self.assertFalse(issubclass(int, Union[Any, str])) + + def test_union_issubclass_type_error(self): with self.assertRaises(TypeError): issubclass(int, Union) with self.assertRaises(TypeError): issubclass(Union, int) with self.assertRaises(TypeError): issubclass(Union[int, str], int) + with self.assertRaises(TypeError): + issubclass(int, Union[str, list[int]]) + with self.assertRaises(TypeError): + issubclass(int, Union[list[int], int]) + with self.assertRaises(TypeError): + issubclass(int, Union[list[int], str]) + + def test_optional_issubclass(self): + self.assertTrue(issubclass(int, Optional[int])) + self.assertTrue(issubclass(type(None), Optional[int])) + self.assertFalse(issubclass(str, Optional[int])) + self.assertTrue(issubclass(Any, Optional[Any])) + self.assertTrue(issubclass(type(None), Optional[Any])) + self.assertFalse(issubclass(int, Optional[Any])) + + def test_optional_issubclass_type_error(self): + with self.assertRaises(TypeError): + issubclass(list[int], Optional[list[int]]) + with self.assertRaises(TypeError): + issubclass(type(None), Optional[list[int]]) + with self.assertRaises(TypeError): + issubclass(int, Optional[list[int]]) def test_union_any(self): u = Union[Any] diff --git a/Lib/typing.py b/Lib/typing.py index 5f3aacd877221c..e69b485422cbd2 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1733,12 +1733,16 @@ def __repr__(self): return super().__repr__() def __instancecheck__(self, obj): - return self.__subclasscheck__(type(obj)) + for arg in self.__args__: + if isinstance(obj, arg): + return True + return False def __subclasscheck__(self, cls): for arg in self.__args__: if issubclass(cls, arg): return True + return False def __reduce__(self): func, (origin, args) = super().__reduce__() diff --git a/Misc/NEWS.d/next/Library/2024-12-30-20-48-28.gh-issue-88834.RIvgwc.rst b/Misc/NEWS.d/next/Library/2024-12-30-20-48-28.gh-issue-88834.RIvgwc.rst new file mode 100644 index 00000000000000..ca43f914880ba3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-30-20-48-28.gh-issue-88834.RIvgwc.rst @@ -0,0 +1,3 @@ +Unify the instance check for :class:`typing.Union` and +:class:`types.UnionType`: :class:`!Union` now uses the instance checks +against its parameters instead of the subclass checks. From e389d6c650ddacb55b08b657f1e4e9b1330f3455 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Tue, 31 Dec 2024 19:10:06 +0530 Subject: [PATCH 142/376] gh-128277: make globals variables thread safe in socket module (#128286) --- Modules/socketmodule.c | 56 +++++++++------------ Python/fileutils.c | 6 +-- Tools/c-analyzer/cpython/globals-to-fix.tsv | 2 + 3 files changed, 28 insertions(+), 36 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 1f0cbf6abb184c..efc8be3ab592ba 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -550,20 +550,20 @@ typedef struct _socket_state { /* Default timeout for new sockets */ PyTime_t defaulttimeout; +} socket_state; #if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4) #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) - /* accept4() is available on Linux 2.6.28+ and glibc 2.10 */ - int accept4_works; +/* accept4() is available on Linux 2.6.28+ and glibc 2.10 */ +static int accept4_works = -1; #endif #endif #ifdef SOCK_CLOEXEC - /* socket() and socketpair() fail with EINVAL on Linux kernel older - * than 2.6.27 if SOCK_CLOEXEC flag is set in the socket type. */ - int sock_cloexec_works; +/* socket() and socketpair() fail with EINVAL on Linux kernel older + * than 2.6.27 if SOCK_CLOEXEC flag is set in the socket type. */ +static int sock_cloexec_works = -1; #endif -} socket_state; static inline void set_sock_fd(PySocketSockObject *s, SOCKET_T fd) @@ -2904,16 +2904,15 @@ sock_accept_impl(PySocketSockObject *s, void *data) #endif #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) - socket_state *state = s->state; - if (state->accept4_works != 0) { + if (_Py_atomic_load_int_relaxed(&accept4_works) != 0) { ctx->result = accept4(get_sock_fd(s), addr, paddrlen, SOCK_CLOEXEC); - if (ctx->result == INVALID_SOCKET && state->accept4_works == -1) { + if (ctx->result == INVALID_SOCKET && _Py_atomic_load_int_relaxed(&accept4_works) == -1) { /* On Linux older than 2.6.28, accept4() fails with ENOSYS */ - state->accept4_works = (errno != ENOSYS); + _Py_atomic_store_int_relaxed(&accept4_works, errno != ENOSYS); } } - if (state->accept4_works == 0) + if (_Py_atomic_load_int_relaxed(&accept4_works) == 0) ctx->result = accept(get_sock_fd(s), addr, paddrlen); #else ctx->result = accept(get_sock_fd(s), addr, paddrlen); @@ -2966,8 +2965,7 @@ sock_accept(PySocketSockObject *s, PyObject *Py_UNUSED(ignored)) #else #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) - socket_state *state = s->state; - if (!state->accept4_works) + if (!_Py_atomic_load_int_relaxed(&accept4_works)) #endif { if (_Py_set_inheritable(newfd, 0, NULL) < 0) { @@ -5428,7 +5426,7 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto, #ifndef MS_WINDOWS #ifdef SOCK_CLOEXEC - int *atomic_flag_works = &state->sock_cloexec_works; + int *atomic_flag_works = &sock_cloexec_works; #else int *atomic_flag_works = NULL; #endif @@ -5583,15 +5581,16 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto, /* UNIX */ Py_BEGIN_ALLOW_THREADS #ifdef SOCK_CLOEXEC - if (state->sock_cloexec_works != 0) { + if (_Py_atomic_load_int_relaxed(&sock_cloexec_works) != 0) { fd = socket(family, type | SOCK_CLOEXEC, proto); - if (state->sock_cloexec_works == -1) { + if (_Py_atomic_load_int_relaxed(&sock_cloexec_works) == -1) { if (fd >= 0) { - state->sock_cloexec_works = 1; + _Py_atomic_store_int_relaxed(&sock_cloexec_works, 1); } + else if (errno == EINVAL) { /* Linux older than 2.6.27 does not support SOCK_CLOEXEC */ - state->sock_cloexec_works = 0; + _Py_atomic_store_int_relaxed(&sock_cloexec_works, 0); fd = socket(family, type, proto); } } @@ -6332,7 +6331,7 @@ socket_socketpair(PyObject *self, PyObject *args) PyObject *res = NULL; socket_state *state = get_module_state(self); #ifdef SOCK_CLOEXEC - int *atomic_flag_works = &state->sock_cloexec_works; + int *atomic_flag_works = &sock_cloexec_works; #else int *atomic_flag_works = NULL; #endif @@ -6350,15 +6349,15 @@ socket_socketpair(PyObject *self, PyObject *args) /* Create a pair of socket fds */ Py_BEGIN_ALLOW_THREADS #ifdef SOCK_CLOEXEC - if (state->sock_cloexec_works != 0) { + if (_Py_atomic_load_int_relaxed(&sock_cloexec_works) != 0) { ret = socketpair(family, type | SOCK_CLOEXEC, proto, sv); - if (state->sock_cloexec_works == -1) { + if (_Py_atomic_load_int_relaxed(&sock_cloexec_works) == -1) { if (ret >= 0) { - state->sock_cloexec_works = 1; + _Py_atomic_store_int_relaxed(&sock_cloexec_works, 1); } else if (errno == EINVAL) { /* Linux older than 2.6.27 does not support SOCK_CLOEXEC */ - state->sock_cloexec_works = 0; + _Py_atomic_store_int_relaxed(&sock_cloexec_works, 0); ret = socketpair(family, type, proto, sv); } } @@ -7466,17 +7465,8 @@ socket_exec(PyObject *m) } socket_state *state = get_module_state(m); - state->defaulttimeout = _PYTIME_FROMSECONDS(-1); - -#if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4) -#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) - state->accept4_works = -1; -#endif -#endif -#ifdef SOCK_CLOEXEC - state->sock_cloexec_works = -1; -#endif + _Py_atomic_store_int64_relaxed(&state->defaulttimeout, _PYTIME_FROMSECONDS(-1)); #define ADD_EXC(MOD, NAME, VAR, BASE) do { \ VAR = PyErr_NewException("socket." NAME, BASE, NULL); \ diff --git a/Python/fileutils.c b/Python/fileutils.c index 9529b14d377c60..81276651f6df44 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -1468,14 +1468,14 @@ set_inheritable(int fd, int inheritable, int raise, int *atomic_flag_works) assert(!(atomic_flag_works != NULL && inheritable)); if (atomic_flag_works != NULL && !inheritable) { - if (*atomic_flag_works == -1) { + if (_Py_atomic_load_int_relaxed(atomic_flag_works) == -1) { int isInheritable = get_inheritable(fd, raise); if (isInheritable == -1) return -1; - *atomic_flag_works = !isInheritable; + _Py_atomic_store_int_relaxed(atomic_flag_works, !isInheritable); } - if (*atomic_flag_works) + if (_Py_atomic_load_int_relaxed(atomic_flag_works)) return 0; } diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index a1ec1927eb56df..a74779803228c2 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -444,3 +444,5 @@ Modules/readline.c - completed_input_string - Modules/rotatingtree.c - random_stream - Modules/rotatingtree.c - random_value - Modules/rotatingtree.c - random_mutex - +Modules/socketmodule.c - accept4_works - +Modules/socketmodule.c - sock_cloexec_works - From c5438fdf4706a70bdd19338edc000dacffff6837 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Tue, 31 Dec 2024 21:22:33 +0000 Subject: [PATCH 143/376] Reword `about.rst` to not limit Sphinx (#128325) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/about.rst | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Doc/about.rst b/Doc/about.rst index 5e6160ff2700ed..8f635d7f743a98 100644 --- a/Doc/about.rst +++ b/Doc/about.rst @@ -1,10 +1,11 @@ -===================== -About these documents -===================== +======================== +About this documentation +======================== -These documents are generated from `reStructuredText`_ sources by `Sphinx`_, a -document processor specifically written for the Python documentation. +Python's documentation is generated from `reStructuredText`_ sources +using `Sphinx`_, a documentation generator originally created for Python +and now maintained as an independent project. .. _reStructuredText: https://docutils.sourceforge.io/rst.html .. _Sphinx: https://www.sphinx-doc.org/ @@ -20,14 +21,14 @@ volunteers are always welcome! Many thanks go to: * Fred L. Drake, Jr., the creator of the original Python documentation toolset - and writer of much of the content; + and author of much of the content; * the `Docutils `_ project for creating reStructuredText and the Docutils suite; * Fredrik Lundh for his Alternative Python Reference project from which Sphinx got many good ideas. -Contributors to the Python Documentation +Contributors to the Python documentation ---------------------------------------- Many people have contributed to the Python language, the Python standard From d903b17499b1a3bfb3ea848f6a1b6da02eac3328 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Wed, 1 Jan 2025 13:36:47 +0200 Subject: [PATCH 144/376] gh-121676: Raise a ``DeprecationWarning`` if the Python implementation of ``functools.reduce`` is called with `function` or `sequence` as a keyword args (#121677) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python implementation of `functools` allows calling `reduce` with `function` or `sequence` as keyword args. This doesn't match behavior of our C accelerator and our documentation for `functools.reduce` states that `function`and `sequence` are positional-only arguments. Now calling a Python implementation of `functools.reduce` with `function` or `sequence` as keyword args would raise a `DeprecationWarning` and is planned to be prohibited in Python 3.16. Co-authored-by: Victor Stinner Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/deprecations/pending-removal-in-3.16.rst | 5 +++ Doc/whatsnew/3.14.rst | 5 +++ Lib/functools.py | 33 ++++++++++++++++--- Lib/test/test_functools.py | 6 ++++ ...-07-13-13-25-31.gh-issue-121676.KDLS11.rst | 3 ++ 5 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-07-13-13-25-31.gh-issue-121676.KDLS11.rst diff --git a/Doc/deprecations/pending-removal-in-3.16.rst b/Doc/deprecations/pending-removal-in-3.16.rst index f2b818f14c63cd..d093deb648baf7 100644 --- a/Doc/deprecations/pending-removal-in-3.16.rst +++ b/Doc/deprecations/pending-removal-in-3.16.rst @@ -79,3 +79,8 @@ Pending removal in Python 3.16 * The undocumented and unused :attr:`!TarFile.tarfile` attribute has been deprecated since Python 3.13. + +* :mod:`functools`: + + * Calling the Python implementation of :func:`functools.reduce` with *function* + or *sequence* as keyword arguments has been deprecated since Python 3.14. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 63fa21e17bc834..4b3f1b2e8eed42 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -744,6 +744,11 @@ Deprecated as a single positional argument. (Contributed by Serhiy Storchaka in :gh:`109218`.) +* :mod:`functools`: + Calling the Python implementation of :func:`functools.reduce` with *function* + or *sequence* as keyword arguments is now deprecated. + (Contributed by Kirill Podoprigora in :gh:`121676`.) + * :mod:`os`: :term:`Soft deprecate ` :func:`os.popen` and :func:`os.spawn* ` functions. They should no longer be used to diff --git a/Lib/functools.py b/Lib/functools.py index 786b8aedfd77f5..fd33f0ae479ddc 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -264,11 +264,6 @@ def reduce(function, sequence, initial=_initial_missing): return value -try: - from _functools import reduce -except ImportError: - pass - ################################################################################ ### partial() argument application @@ -1124,3 +1119,31 @@ def __get__(self, instance, owner=None): return val __class_getitem__ = classmethod(GenericAlias) + +def _warn_python_reduce_kwargs(py_reduce): + @wraps(py_reduce) + def wrapper(*args, **kwargs): + if 'function' in kwargs or 'sequence' in kwargs: + import os + import warnings + warnings.warn( + 'Calling functools.reduce with keyword arguments ' + '"function" or "sequence" ' + 'is deprecated in Python 3.14 and will be ' + 'forbidden in Python 3.16.', + DeprecationWarning, + skip_file_prefixes=(os.path.dirname(__file__),)) + return py_reduce(*args, **kwargs) + return wrapper + +reduce = _warn_python_reduce_kwargs(reduce) +del _warn_python_reduce_kwargs + +# The import of the C accelerated version of reduce() has been moved +# here due to gh-121676. In Python 3.16, _warn_python_reduce_kwargs() +# should be removed and the import block should be moved back right +# after the definition of reduce(). +try: + from _functools import reduce +except ImportError: + pass diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 4a0252cb637a52..32224866082824 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -1045,6 +1045,12 @@ class TestReduceC(TestReduce, unittest.TestCase): class TestReducePy(TestReduce, unittest.TestCase): reduce = staticmethod(py_functools.reduce) + def test_reduce_with_kwargs(self): + with self.assertWarns(DeprecationWarning): + self.reduce(function=lambda x, y: x + y, sequence=[1, 2, 3, 4, 5], initial=1) + with self.assertWarns(DeprecationWarning): + self.reduce(lambda x, y: x + y, sequence=[1, 2, 3, 4, 5], initial=1) + class TestCmpToKey: diff --git a/Misc/NEWS.d/next/Library/2024-07-13-13-25-31.gh-issue-121676.KDLS11.rst b/Misc/NEWS.d/next/Library/2024-07-13-13-25-31.gh-issue-121676.KDLS11.rst new file mode 100644 index 00000000000000..be589b727a1968 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-07-13-13-25-31.gh-issue-121676.KDLS11.rst @@ -0,0 +1,3 @@ +Deprecate calling the Python implementation of :meth:`functools.reduce` +with a ``function`` or ``sequence`` as a :term:`keyword argument`. +This will be forbidden in Python 3.16 in order to match the C implementation. From bb9d955e16c5578bdbc72750fbbffc8313559109 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 1 Jan 2025 18:00:47 +0530 Subject: [PATCH 145/376] gh-128277: remove unnecessary critical section from `socket.close` (#128305) Remove unnecessary critical section from `socket.close` as it now uses relaxed atomics for `sock_fd`. --- Lib/test/test_socket.py | 20 ++++++++++++++++++++ Modules/clinic/socketmodule.c.h | 11 ++--------- Modules/socketmodule.c | 3 +-- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index aac213e36aecf0..faf326d9164e1b 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -7075,6 +7075,26 @@ def close_fds(fds): self.assertEqual(data, str(index).encode()) +class FreeThreadingTests(unittest.TestCase): + + def test_close_detach_race(self): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + def close(): + for _ in range(1000): + s.close() + + def detach(): + for _ in range(1000): + s.detach() + + t1 = threading.Thread(target=close) + t2 = threading.Thread(target=detach) + + with threading_helper.start_threads([t1, t2]): + pass + + def setUpModule(): thread_info = threading_helper.threading_setup() unittest.addModuleCleanup(threading_helper.threading_cleanup, *thread_info) diff --git a/Modules/clinic/socketmodule.c.h b/Modules/clinic/socketmodule.c.h index db1a28b86c8773..2152f288a9722f 100644 --- a/Modules/clinic/socketmodule.c.h +++ b/Modules/clinic/socketmodule.c.h @@ -6,7 +6,6 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif -#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(_socket_socket_close__doc__, @@ -26,13 +25,7 @@ _socket_socket_close_impl(PySocketSockObject *s); static PyObject * _socket_socket_close(PySocketSockObject *s, PyObject *Py_UNUSED(ignored)) { - PyObject *return_value = NULL; - - Py_BEGIN_CRITICAL_SECTION(s); - return_value = _socket_socket_close_impl(s); - Py_END_CRITICAL_SECTION(); - - return return_value; + return _socket_socket_close_impl(s); } static int @@ -287,4 +280,4 @@ _socket_socket_if_nametoindex(PySocketSockObject *self, PyObject *arg) #ifndef _SOCKET_SOCKET_IF_NAMETOINDEX_METHODDEF #define _SOCKET_SOCKET_IF_NAMETOINDEX_METHODDEF #endif /* !defined(_SOCKET_SOCKET_IF_NAMETOINDEX_METHODDEF) */ -/*[clinic end generated code: output=59c36bb31b05de68 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3e612e8df1c322dd input=a9049054013a1b77]*/ diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index efc8be3ab592ba..e70aa304f2f3a3 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -3386,7 +3386,6 @@ sockets the address is a tuple (ifname, proto [,pkttype [,hatype [,addr]]])"); will surely fail. */ /*[clinic input] -@critical_section _socket.socket.close self as s: self(type="PySocketSockObject *") @@ -3397,7 +3396,7 @@ Close the socket. It cannot be used after this call. static PyObject * _socket_socket_close_impl(PySocketSockObject *s) -/*[clinic end generated code: output=038b2418e07f6f6c input=9839a261e05bcb97]*/ +/*[clinic end generated code: output=038b2418e07f6f6c input=dc487e470e55a83c]*/ { SOCKET_T fd; int res; From a327810169982e3782bdefc2247789a71aa79b43 Mon Sep 17 00:00:00 2001 From: "RUANG (James Roy)" Date: Thu, 2 Jan 2025 04:39:28 +0800 Subject: [PATCH 146/376] Fix while statements with non-bool conditions in `_pyrepl` (#127509) Fix non-bool value conditions --- Lib/_pyrepl/simple_interact.py | 2 +- Lib/_pyrepl/unix_console.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/_pyrepl/simple_interact.py b/Lib/_pyrepl/simple_interact.py index 342a4b58bfd0f3..a5033496712a73 100644 --- a/Lib/_pyrepl/simple_interact.py +++ b/Lib/_pyrepl/simple_interact.py @@ -138,7 +138,7 @@ def maybe_run_command(statement: str) -> bool: return False - while 1: + while True: try: try: sys.stdout.flush() diff --git a/Lib/_pyrepl/unix_console.py b/Lib/_pyrepl/unix_console.py index 2576b938a34c64..63e8fc24dd7625 100644 --- a/Lib/_pyrepl/unix_console.py +++ b/Lib/_pyrepl/unix_console.py @@ -786,7 +786,7 @@ def __tputs(self, fmt, prog=delayprog): # only if the bps is actually needed (which I'm # betting is pretty unlkely) bps = ratedict.get(self.__svtermstate.ospeed) - while 1: + while True: m = prog.search(fmt) if not m: os.write(self.output_fd, fmt) From c810ed7c8e0a7464d19700ba1c8668a406f1c042 Mon Sep 17 00:00:00 2001 From: qqwqqw689 <114795525+qqwqqw689@users.noreply.github.com> Date: Thu, 2 Jan 2025 06:11:29 +0800 Subject: [PATCH 147/376] gh-126469: remove unnecessary error-checking branch in `lexer.c` (#126473) --- Parser/lexer/lexer.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Parser/lexer/lexer.c b/Parser/lexer/lexer.c index 8c868593f944c8..dbbb94a407c81d 100644 --- a/Parser/lexer/lexer.c +++ b/Parser/lexer/lexer.c @@ -329,11 +329,7 @@ verify_identifier(struct tok_state *tok) return 0; } Py_ssize_t invalid = _PyUnicode_ScanIdentifier(s); - if (invalid < 0) { - Py_DECREF(s); - tok->done = E_ERROR; - return 0; - } + assert(invalid >= 0); assert(PyUnicode_GET_LENGTH(s) > 0); if (invalid < PyUnicode_GET_LENGTH(s)) { Py_UCS4 ch = PyUnicode_READ_CHAR(s, invalid); From 60c65184695a3eab766b3bc26fc99f695deb998f Mon Sep 17 00:00:00 2001 From: abkmystery <36216019+abkmystery@users.noreply.github.com> Date: Thu, 2 Jan 2025 01:20:31 -0600 Subject: [PATCH 148/376] gh-128349: Use `.. data::` instead of `.. class::` for pre-defined decimal `Context` objects (#128379) --- Doc/library/decimal.rst | 6 +++--- Doc/whatsnew/3.3.rst | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index c9a3e448cad063..185eaf3f721c72 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -1033,7 +1033,7 @@ New contexts can also be created using the :class:`Context` constructor described below. In addition, the module provides three pre-made contexts: -.. class:: BasicContext +.. data:: BasicContext This is a standard context defined by the General Decimal Arithmetic Specification. Precision is set to nine. Rounding is set to @@ -1044,7 +1044,7 @@ described below. In addition, the module provides three pre-made contexts: Because many of the traps are enabled, this context is useful for debugging. -.. class:: ExtendedContext +.. data:: ExtendedContext This is a standard context defined by the General Decimal Arithmetic Specification. Precision is set to nine. Rounding is set to @@ -1057,7 +1057,7 @@ described below. In addition, the module provides three pre-made contexts: presence of conditions that would otherwise halt the program. -.. class:: DefaultContext +.. data:: DefaultContext This context is used by the :class:`Context` constructor as a prototype for new contexts. Changing a field (such a precision) has the effect of changing the diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index f814c4e90d5719..7a8eb47cbdb354 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -1147,8 +1147,8 @@ API changes | :const:`MIN_EMIN` | ``-425000000`` | ``-999999999999999999`` | +-------------------+----------------+-------------------------+ -* In the context templates (:class:`~decimal.DefaultContext`, - :class:`~decimal.BasicContext` and :class:`~decimal.ExtendedContext`) +* In the context templates (:const:`~decimal.DefaultContext`, + :const:`~decimal.BasicContext` and :const:`~decimal.ExtendedContext`) the magnitude of :attr:`~decimal.Context.Emax` and :attr:`~decimal.Context.Emin` has changed to ``999999``. From e1baa778f602ede66831eb34b9ef17f21e4d4347 Mon Sep 17 00:00:00 2001 From: Shin-myoung-serp Date: Thu, 2 Jan 2025 18:45:07 +0900 Subject: [PATCH 149/376] =?UTF-8?q?gh-95371:=20Add=20support=20for=20other?= =?UTF-8?q?=20image=20formats(e.g.=20PNG)=20to=20the=20turtle=E2=80=A6=20(?= =?UTF-8?q?#95378)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Oleg Iarygin --- Doc/library/turtle.rst | 24 +++++++++++---- Lib/turtle.py | 30 +++++++++---------- ...2-07-28-12-32-59.gh-issue-95371.F24IFC.rst | 1 + 3 files changed, 34 insertions(+), 21 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-07-28-12-32-59.gh-issue-95371.F24IFC.rst diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst index 8eb4f8271fcfae..512647f5f6e01f 100644 --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -1823,7 +1823,8 @@ Window control .. function:: bgpic(picname=None) - :param picname: a string, name of a gif-file or ``"nopic"``, or ``None`` + :param picname: a string, name of an image file (PNG, GIF, PGM, and PPM) + or ``"nopic"``, or ``None`` Set background image or return name of current backgroundimage. If *picname* is a filename, set the corresponding image as background. If *picname* is @@ -2200,9 +2201,9 @@ Settings and special methods .. function:: register_shape(name, shape=None) addshape(name, shape=None) - There are three different ways to call this function: + There are four different ways to call this function: - (1) *name* is the name of a gif-file and *shape* is ``None``: Install the + (1) *name* is the name of an image file (PNG, GIF, PGM, and PPM) and *shape* is ``None``: Install the corresponding image shape. :: >>> screen.register_shape("turtle.gif") @@ -2211,7 +2212,16 @@ Settings and special methods Image shapes *do not* rotate when turning the turtle, so they do not display the heading of the turtle! - (2) *name* is an arbitrary string and *shape* is a tuple of pairs of + (2) *name* is an arbitrary string and *shape* is the name of an image file (PNG, GIF, PGM, and PPM): Install the + corresponding image shape. :: + + >>> screen.register_shape("turtle", "turtle.gif") + + .. note:: + Image shapes *do not* rotate when turning the turtle, so they do not + display the heading of the turtle! + + (3) *name* is an arbitrary string and *shape* is a tuple of pairs of coordinates: Install the corresponding polygon shape. .. doctest:: @@ -2219,12 +2229,16 @@ Settings and special methods >>> screen.register_shape("triangle", ((5,-3), (0,5), (-5,-3))) - (3) *name* is an arbitrary string and *shape* is a (compound) :class:`Shape` + (4) *name* is an arbitrary string and *shape* is a (compound) :class:`Shape` object: Install the corresponding compound shape. Add a turtle shape to TurtleScreen's shapelist. Only thusly registered shapes can be used by issuing the command ``shape(shapename)``. + .. versionchanged:: next + Added support for PNG, PGM, and PPM image formats. + Both a shape name and an image file name can be specified. + .. function:: turtles() diff --git a/Lib/turtle.py b/Lib/turtle.py index 8a5801f2efe625..1320cfd93fd6db 100644 --- a/Lib/turtle.py +++ b/Lib/turtle.py @@ -51,7 +51,7 @@ turtle. So the turtles can more easily be used as a visual feedback instrument by the (beginning) programmer. -- Different turtle shapes, gif-images as turtle shapes, user defined +- Different turtle shapes, image files as turtle shapes, user defined and user controllable turtle shapes, among them compound (multicolored) shapes. Turtle shapes can be stretched and tilted, which makes turtles very versatile geometrical objects. @@ -468,7 +468,7 @@ def _blankimage(self): def _image(self, filename): """return an image object containing the - imagedata from a gif-file named filename. + imagedata from an image file named filename. """ return TK.PhotoImage(file=filename, master=self.cv) @@ -872,10 +872,7 @@ def __init__(self, type_, data=None): if isinstance(data, list): data = tuple(data) elif type_ == "image": - if isinstance(data, str): - if data.lower().endswith(".gif") and isfile(data): - data = TurtleScreen._image(data) - # else data assumed to be PhotoImage + assert(isinstance(data, TK.PhotoImage)) elif type_ == "compound": data = [] else: @@ -1100,14 +1097,18 @@ def register_shape(self, name, shape=None): """Adds a turtle shape to TurtleScreen's shapelist. Arguments: - (1) name is the name of a gif-file and shape is None. + (1) name is the name of an image file (PNG, GIF, PGM, and PPM) and shape is None. Installs the corresponding image shape. !! Image-shapes DO NOT rotate when turning the turtle, !! so they do not display the heading of the turtle! - (2) name is an arbitrary string and shape is a tuple + (2) name is an arbitrary string and shape is the name of an image file (PNG, GIF, PGM, and PPM). + Installs the corresponding image shape. + !! Image-shapes DO NOT rotate when turning the turtle, + !! so they do not display the heading of the turtle! + (3) name is an arbitrary string and shape is a tuple of pairs of coordinates. Installs the corresponding polygon shape - (3) name is an arbitrary string and shape is a + (4) name is an arbitrary string and shape is a (compound) Shape object. Installs the corresponding compound shape. To use a shape, you have to issue the command shape(shapename). @@ -1120,12 +1121,9 @@ def register_shape(self, name, shape=None): """ if shape is None: - # image - if name.lower().endswith(".gif"): - shape = Shape("image", self._image(name)) - else: - raise TurtleGraphicsError("Bad arguments for register_shape.\n" - + "Use help(register_shape)" ) + shape = Shape("image", self._image(name)) + elif isinstance(shape, str): + shape = Shape("image", self._image(shape)) elif isinstance(shape, tuple): shape = Shape("polygon", shape) ## else shape assumed to be Shape-instance @@ -1454,7 +1452,7 @@ def bgpic(self, picname=None): """Set background image or return name of current backgroundimage. Optional argument: - picname -- a string, name of a gif-file or "nopic". + picname -- a string, name of an image file (PNG, GIF, PGM, and PPM) or "nopic". If picname is a filename, set the corresponding image as background. If picname is "nopic", delete backgroundimage, if present. diff --git a/Misc/NEWS.d/next/Library/2022-07-28-12-32-59.gh-issue-95371.F24IFC.rst b/Misc/NEWS.d/next/Library/2022-07-28-12-32-59.gh-issue-95371.F24IFC.rst new file mode 100644 index 00000000000000..4a62aaed78b425 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-07-28-12-32-59.gh-issue-95371.F24IFC.rst @@ -0,0 +1 @@ +Added support for other image formats (PNG, PGM, and PPM) to the turtle module. Patch by Shin-myoung-serp. From 8d16919a06a55a50756bf083221a6f6cab43de50 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 2 Jan 2025 13:38:21 +0200 Subject: [PATCH 150/376] gh-123925: Fix building curses on platforms without libncursesw (GH-128405) --- .../next/Build/2025-01-02-11-02-45.gh-issue-123925.TLlyUi.rst | 2 ++ Modules/_cursesmodule.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Build/2025-01-02-11-02-45.gh-issue-123925.TLlyUi.rst diff --git a/Misc/NEWS.d/next/Build/2025-01-02-11-02-45.gh-issue-123925.TLlyUi.rst b/Misc/NEWS.d/next/Build/2025-01-02-11-02-45.gh-issue-123925.TLlyUi.rst new file mode 100644 index 00000000000000..a2a9c6fc7680b5 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-01-02-11-02-45.gh-issue-123925.TLlyUi.rst @@ -0,0 +1,2 @@ +Fix building the :mod:`curses` module on platforms with libncurses but +without libncursesw. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 040ffa81153ebe..92961af381b9cb 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -138,7 +138,7 @@ typedef chtype attr_t; /* No attr_t type is available */ #define STRICT_SYSV_CURSES #endif -#if NCURSES_EXT_FUNCS+0 >= 20170401 && NCURSES_EXT_COLORS+0 >= 20170401 +#if defined(HAVE_NCURSESW) && NCURSES_EXT_FUNCS+0 >= 20170401 && NCURSES_EXT_COLORS+0 >= 20170401 #define _NCURSES_EXTENDED_COLOR_FUNCS 1 #else #define _NCURSES_EXTENDED_COLOR_FUNCS 0 From a3711d1541c1b7987941b41d2247f87dae347117 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 2 Jan 2025 14:11:21 +0200 Subject: [PATCH 151/376] gh-124130: Fix a bug in matching regular expression \B in empty string (GH-127007) --- Doc/library/re.rst | 7 ++----- Doc/whatsnew/3.14.rst | 4 ++++ Lib/test/test_re.py | 13 +++++-------- .../2024-11-19-10-46-57.gh-issue-124130.OZ_vR5.rst | 4 ++++ Modules/_sre/sre_lib.h | 12 ------------ 5 files changed, 15 insertions(+), 25 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-19-10-46-57.gh-issue-124130.OZ_vR5.rst diff --git a/Doc/library/re.rst b/Doc/library/re.rst index 9db6f1da3be4db..29387a429b844c 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -572,11 +572,8 @@ character ``'$'``. Word boundaries are determined by the current locale if the :py:const:`~re.LOCALE` flag is used. - .. note:: - - Note that ``\B`` does not match an empty string, which differs from - RE implementations in other programming languages such as Perl. - This behavior is kept for compatibility reasons. + .. versionchanged:: next + ``\B`` now matches empty input string. .. index:: single: \d; in regular expressions diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 4b3f1b2e8eed42..61f5ffdb6c89d1 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -245,6 +245,10 @@ Other language changes making it a :term:`generic type`. (Contributed by Brian Schubert in :gh:`126012`.) +* ``\B`` in :mod:`regular expression ` now matches empty input string. + Now it is always the opposite of ``\b``. + (Contributed by Serhiy Storchaka in :gh:`124130`.) + * iOS and macOS apps can now be configured to redirect ``stdout`` and ``stderr`` content to the system log. (Contributed by Russell Keith-Magee in :gh:`127592`.) diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 0d3599be87f228..5538de60b2a03a 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -978,18 +978,15 @@ def test_word_boundaries(self): self.assertIsNone(re.fullmatch(br".+\B", b"abc", re.LOCALE)) self.assertIsNone(re.fullmatch(r".+\B", "ьюя")) self.assertTrue(re.fullmatch(r".+\B", "ьюя", re.ASCII)) - # However, an empty string contains no word boundaries, and also no - # non-boundaries. + # However, an empty string contains no word boundaries. self.assertIsNone(re.search(r"\b", "")) self.assertIsNone(re.search(r"\b", "", re.ASCII)) self.assertIsNone(re.search(br"\b", b"")) self.assertIsNone(re.search(br"\b", b"", re.LOCALE)) - # This one is questionable and different from the perlre behaviour, - # but describes current behavior. - self.assertIsNone(re.search(r"\B", "")) - self.assertIsNone(re.search(r"\B", "", re.ASCII)) - self.assertIsNone(re.search(br"\B", b"")) - self.assertIsNone(re.search(br"\B", b"", re.LOCALE)) + self.assertTrue(re.search(r"\B", "")) + self.assertTrue(re.search(r"\B", "", re.ASCII)) + self.assertTrue(re.search(br"\B", b"")) + self.assertTrue(re.search(br"\B", b"", re.LOCALE)) # A single word-character string has two boundaries, but no # non-boundary gaps. self.assertEqual(len(re.findall(r"\b", "a")), 2) diff --git a/Misc/NEWS.d/next/Library/2024-11-19-10-46-57.gh-issue-124130.OZ_vR5.rst b/Misc/NEWS.d/next/Library/2024-11-19-10-46-57.gh-issue-124130.OZ_vR5.rst new file mode 100644 index 00000000000000..a1d4fc8ff4c22f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-19-10-46-57.gh-issue-124130.OZ_vR5.rst @@ -0,0 +1,4 @@ +Fix a bug in matching regular expression ``\B`` in empty input string. +Now it is always the opposite of ``\b``. +To get an old behavior, use ``(?!\A\Z)\B``. +To get a new behavior in old Python versions, use ``(?!\b)``. diff --git a/Modules/_sre/sre_lib.h b/Modules/_sre/sre_lib.h index af4bfc56083bcb..df377905bfae0d 100644 --- a/Modules/_sre/sre_lib.h +++ b/Modules/_sre/sre_lib.h @@ -42,8 +42,6 @@ SRE(at)(SRE_STATE* state, const SRE_CHAR* ptr, SRE_CODE at) return ((void*) ptr == state->end); case SRE_AT_BOUNDARY: - if (state->beginning == state->end) - return 0; thatp = ((void*) ptr > state->beginning) ? SRE_IS_WORD((int) ptr[-1]) : 0; thisp = ((void*) ptr < state->end) ? @@ -51,8 +49,6 @@ SRE(at)(SRE_STATE* state, const SRE_CHAR* ptr, SRE_CODE at) return thisp != thatp; case SRE_AT_NON_BOUNDARY: - if (state->beginning == state->end) - return 0; thatp = ((void*) ptr > state->beginning) ? SRE_IS_WORD((int) ptr[-1]) : 0; thisp = ((void*) ptr < state->end) ? @@ -60,8 +56,6 @@ SRE(at)(SRE_STATE* state, const SRE_CHAR* ptr, SRE_CODE at) return thisp == thatp; case SRE_AT_LOC_BOUNDARY: - if (state->beginning == state->end) - return 0; thatp = ((void*) ptr > state->beginning) ? SRE_LOC_IS_WORD((int) ptr[-1]) : 0; thisp = ((void*) ptr < state->end) ? @@ -69,8 +63,6 @@ SRE(at)(SRE_STATE* state, const SRE_CHAR* ptr, SRE_CODE at) return thisp != thatp; case SRE_AT_LOC_NON_BOUNDARY: - if (state->beginning == state->end) - return 0; thatp = ((void*) ptr > state->beginning) ? SRE_LOC_IS_WORD((int) ptr[-1]) : 0; thisp = ((void*) ptr < state->end) ? @@ -78,8 +70,6 @@ SRE(at)(SRE_STATE* state, const SRE_CHAR* ptr, SRE_CODE at) return thisp == thatp; case SRE_AT_UNI_BOUNDARY: - if (state->beginning == state->end) - return 0; thatp = ((void*) ptr > state->beginning) ? SRE_UNI_IS_WORD((int) ptr[-1]) : 0; thisp = ((void*) ptr < state->end) ? @@ -87,8 +77,6 @@ SRE(at)(SRE_STATE* state, const SRE_CHAR* ptr, SRE_CODE at) return thisp != thatp; case SRE_AT_UNI_NON_BOUNDARY: - if (state->beginning == state->end) - return 0; thatp = ((void*) ptr > state->beginning) ? SRE_UNI_IS_WORD((int) ptr[-1]) : 0; thisp = ((void*) ptr < state->end) ? From 8e48a6edc75ca67a34924bbe54463ca913ae6e58 Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Thu, 2 Jan 2025 13:54:38 +0100 Subject: [PATCH 152/376] gh-126624: Expose error code ``XML_ERROR_NOT_STARTED`` of Expat >=2.6.4 (#126625) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose error code ``XML_ERROR_NOT_STARTED`` in `xml.parsers.expat.errors` which was introduced in Expat 2.6.4. Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/pyexpat.rst | 7 +++++++ .../Library/2024-11-09-15-59-51.gh-issue-126624.bN53Va.rst | 2 ++ Modules/pyexpat.c | 5 ++++- 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-09-15-59-51.gh-issue-126624.bN53Va.rst diff --git a/Doc/library/pyexpat.rst b/Doc/library/pyexpat.rst index c0e9999f4b1270..0f3b58ef6ea5af 100644 --- a/Doc/library/pyexpat.rst +++ b/Doc/library/pyexpat.rst @@ -941,6 +941,13 @@ The ``errors`` module has the following attributes: has been breached. +.. data:: XML_ERROR_NOT_STARTED + + The parser was tried to be stopped or suspended before it started. + + .. versionadded:: next + + .. rubric:: Footnotes .. [1] The encoding string included in XML output should conform to the diff --git a/Misc/NEWS.d/next/Library/2024-11-09-15-59-51.gh-issue-126624.bN53Va.rst b/Misc/NEWS.d/next/Library/2024-11-09-15-59-51.gh-issue-126624.bN53Va.rst new file mode 100644 index 00000000000000..468840a651c253 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-09-15-59-51.gh-issue-126624.bN53Va.rst @@ -0,0 +1,2 @@ +Expose error code :data:`~xml.parsers.expat.errors.XML_ERROR_NOT_STARTED` +of Expat >=2.6.4 in :mod:`xml.parsers.expat.errors`. diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index cf7714e7656205..9931ca2a8d4749 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -1767,7 +1767,10 @@ struct ErrorInfo error_info_of[] = { {"XML_ERROR_NO_BUFFER", "a successful prior call to function XML_GetBuffer is required"}, /* Added in 2.4.0. */ - {"XML_ERROR_AMPLIFICATION_LIMIT_BREACH", "limit on input amplification factor (from DTD and entities) breached"} + {"XML_ERROR_AMPLIFICATION_LIMIT_BREACH", "limit on input amplification factor (from DTD and entities) breached"}, + + /* Added in 2.6.4. */ + {"XML_ERROR_NOT_STARTED", "parser not started"}, }; static int From 9ba0528537b2befcd02a7fdc5904e0cfbc33a887 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 2 Jan 2025 13:00:26 +0000 Subject: [PATCH 153/376] gh-128404: Remove ``asyncio`` from ``test_builtin`` (#128403) Co-authored-by: Kumar Aditya --- Lib/test/test_builtin.py | 123 ++++++++++++++++++++------------------- 1 file changed, 64 insertions(+), 59 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index f98138391bc1a8..5f4eac5267622f 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1,7 +1,6 @@ # Python test set -- built-in functions import ast -import asyncio import builtins import collections import contextlib @@ -31,7 +30,8 @@ from types import AsyncGeneratorType, FunctionType, CellType from operator import neg from test import support -from test.support import (cpython_only, swap_attr, maybe_get_event_loop_policy) +from test.support import cpython_only, swap_attr +from test.support import async_yield, run_yielding_async_fn from test.support.import_helper import import_module from test.support.os_helper import (EnvironmentVarGuard, TESTFN, unlink) from test.support.script_helper import assert_python_ok @@ -414,10 +414,6 @@ def test_compile_top_level_await_no_coro(self): msg=f"source={source} mode={mode}") - @unittest.skipIf( - support.is_emscripten or support.is_wasi, - "socket.accept is broken" - ) def test_compile_top_level_await(self): """Test whether code with top level await can be compiled. @@ -432,13 +428,25 @@ async def arange(n): for i in range(n): yield i + class Lock: + async def __aenter__(self): + return self + + async def __aexit__(self, *exc_info): + pass + + async def sleep(delay, result=None): + assert delay == 0 + await async_yield(None) + return result + modes = ('single', 'exec') optimizations = (-1, 0, 1, 2) code_samples = [ - '''a = await asyncio.sleep(0, result=1)''', + '''a = await sleep(0, result=1)''', '''async for i in arange(1): a = 1''', - '''async with asyncio.Lock() as l: + '''async with Lock() as l: a = 1''', '''a = [x async for x in arange(2)][1]''', '''a = 1 in {x async for x in arange(2)}''', @@ -446,16 +454,16 @@ async def arange(n): '''a = [x async for x in arange(2) async for x in arange(2)][1]''', '''a = [x async for x in (x async for x in arange(5))][1]''', '''a, = [1 for x in {x async for x in arange(1)}]''', - '''a = [await asyncio.sleep(0, x) async for x in arange(2)][1]''', + '''a = [await sleep(0, x) async for x in arange(2)][1]''', # gh-121637: Make sure we correctly handle the case where the # async code is optimized away - '''assert not await asyncio.sleep(0); a = 1''', + '''assert not await sleep(0); a = 1''', '''assert [x async for x in arange(1)]; a = 1''', '''assert {x async for x in arange(1)}; a = 1''', '''assert {x: x async for x in arange(1)}; a = 1''', ''' if (a := 1) and __debug__: - async with asyncio.Lock() as l: + async with Lock() as l: pass ''', ''' @@ -464,36 +472,31 @@ async def arange(n): pass ''', ] - policy = maybe_get_event_loop_policy() - try: - for mode, code_sample, optimize in product(modes, code_samples, optimizations): - with self.subTest(mode=mode, code_sample=code_sample, optimize=optimize): - source = dedent(code_sample) - with self.assertRaises( - SyntaxError, msg=f"source={source} mode={mode}"): - compile(source, '?', mode, optimize=optimize) - - co = compile(source, - '?', - mode, - flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT, - optimize=optimize) - - self.assertEqual(co.co_flags & CO_COROUTINE, CO_COROUTINE, - msg=f"source={source} mode={mode}") - - # test we can create and advance a function type - globals_ = {'asyncio': asyncio, 'a': 0, 'arange': arange} - async_f = FunctionType(co, globals_) - asyncio.run(async_f()) - self.assertEqual(globals_['a'], 1) - - # test we can await-eval, - globals_ = {'asyncio': asyncio, 'a': 0, 'arange': arange} - asyncio.run(eval(co, globals_)) - self.assertEqual(globals_['a'], 1) - finally: - asyncio._set_event_loop_policy(policy) + for mode, code_sample, optimize in product(modes, code_samples, optimizations): + with self.subTest(mode=mode, code_sample=code_sample, optimize=optimize): + source = dedent(code_sample) + with self.assertRaises( + SyntaxError, msg=f"source={source} mode={mode}"): + compile(source, '?', mode, optimize=optimize) + + co = compile(source, + '?', + mode, + flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT, + optimize=optimize) + + self.assertEqual(co.co_flags & CO_COROUTINE, CO_COROUTINE, + msg=f"source={source} mode={mode}") + + # test we can create and advance a function type + globals_ = {'Lock': Lock, 'a': 0, 'arange': arange, 'sleep': sleep} + run_yielding_async_fn(FunctionType(co, globals_)) + self.assertEqual(globals_['a'], 1) + + # test we can await-eval, + globals_ = {'Lock': Lock, 'a': 0, 'arange': arange, 'sleep': sleep} + run_yielding_async_fn(lambda: eval(co, globals_)) + self.assertEqual(globals_['a'], 1) def test_compile_top_level_await_invalid_cases(self): # helper function just to check we can run top=level async-for @@ -501,6 +504,13 @@ async def arange(n): for i in range(n): yield i + class Lock: + async def __aenter__(self): + return self + + async def __aexit__(self, *exc_info): + pass + modes = ('single', 'exec') code_samples = [ '''def f(): await arange(10)\n''', @@ -511,27 +521,22 @@ async def arange(n): a = 1 ''', '''def f(): - async with asyncio.Lock() as l: + async with Lock() as l: a = 1 ''' ] - policy = maybe_get_event_loop_policy() - try: - for mode, code_sample in product(modes, code_samples): - source = dedent(code_sample) - with self.assertRaises( - SyntaxError, msg=f"source={source} mode={mode}"): - compile(source, '?', mode) - - with self.assertRaises( - SyntaxError, msg=f"source={source} mode={mode}"): - co = compile(source, - '?', - mode, - flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT) - finally: - asyncio._set_event_loop_policy(policy) + for mode, code_sample in product(modes, code_samples): + source = dedent(code_sample) + with self.assertRaises( + SyntaxError, msg=f"source={source} mode={mode}"): + compile(source, '?', mode) + with self.assertRaises( + SyntaxError, msg=f"source={source} mode={mode}"): + co = compile(source, + '?', + mode, + flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT) def test_compile_async_generator(self): """ @@ -542,7 +547,7 @@ def test_compile_async_generator(self): code = dedent("""async def ticker(): for i in range(10): yield i - await asyncio.sleep(0)""") + await sleep(0)""") co = compile(code, '?', 'exec', flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT) glob = {} From c9d2bc6d7f6d74e0539afb0f7066997ae736dfc8 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:35:00 +0200 Subject: [PATCH 154/376] Revert "Doc: Show object descriptions in the table of contents (#125757)" (#128406) --- Doc/conf.py | 4 +--- Doc/tools/extensions/pyspecific.py | 1 - Doc/tools/static/sidebar-wrap.css | 6 ------ 3 files changed, 1 insertion(+), 10 deletions(-) delete mode 100644 Doc/tools/static/sidebar-wrap.css diff --git a/Doc/conf.py b/Doc/conf.py index 9cde394cbaed69..ae08c7fa288080 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -101,9 +101,7 @@ # Create table of contents entries for domain objects (e.g. functions, classes, # attributes, etc.). Default is True. -toc_object_entries = True -# Hide parents to tidy up long entries in sidebar -toc_object_entries_show_parents = 'hide' +toc_object_entries = False # Ignore any .rst files in the includes/ directory; # they're embedded in pages but not rendered as individual pages. diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index f4df7ec0839339..bcb8a421e32d09 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -434,6 +434,5 @@ def setup(app): app.add_directive_to_domain('py', 'awaitablemethod', PyAwaitableMethod) app.add_directive_to_domain('py', 'abstractmethod', PyAbstractMethod) app.add_directive('miscnews', MiscNews) - app.add_css_file('sidebar-wrap.css') app.connect('env-check-consistency', patch_pairindextypes) return {'version': '1.0', 'parallel_read_safe': True} diff --git a/Doc/tools/static/sidebar-wrap.css b/Doc/tools/static/sidebar-wrap.css deleted file mode 100644 index 0a80f516f28349..00000000000000 --- a/Doc/tools/static/sidebar-wrap.css +++ /dev/null @@ -1,6 +0,0 @@ -div.sphinxsidebarwrapper { - overflow-x: scroll; -} -div.sphinxsidebarwrapper li code { - overflow-wrap: normal; -} From 58e9f95c4aa970db32a94b9152b51ede22f823bd Mon Sep 17 00:00:00 2001 From: Zhikang Yan <2951256653@qq.com> Date: Thu, 2 Jan 2025 23:51:57 +0800 Subject: [PATCH 155/376] gh-128014: Fix passing default='' to the tkinter method wm_iconbitmap() (GH-128015) Co-authored-by: Serhiy Storchaka --- Lib/test/test_tkinter/test_misc.py | 28 ++++++++++++++++++- Lib/tkinter/__init__.py | 2 +- ...-12-18-00-07-50.gh-issue-128014.F3aUbz.rst | 2 ++ 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-18-00-07-50.gh-issue-128014.F3aUbz.rst diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py index 475edcbd5338a7..3362169391818b 100644 --- a/Lib/test/test_tkinter/test_misc.py +++ b/Lib/test/test_tkinter/test_misc.py @@ -4,7 +4,8 @@ from tkinter import TclError import enum from test import support -from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest, requires_tk +from test.test_tkinter.support import (AbstractTkTest, AbstractDefaultRootTest, + requires_tk, get_tk_patchlevel) support.requires('gui') @@ -554,6 +555,31 @@ def test_wm_attribute(self): self.assertEqual(w.wm_attributes('alpha'), 1.0 if self.wantobjects else '1.0') + def test_wm_iconbitmap(self): + t = tkinter.Toplevel(self.root) + self.assertEqual(t.wm_iconbitmap(), '') + t.wm_iconbitmap('hourglass') + bug = False + if t._windowingsystem == 'aqua': + # Tk bug 13ac26b35dc55f7c37f70b39d59d7ef3e63017c8. + patchlevel = get_tk_patchlevel(t) + if patchlevel < (8, 6, 17) or (9, 0) <= patchlevel < (9, 0, 2): + bug = True + if not bug: + self.assertEqual(t.wm_iconbitmap(), 'hourglass') + self.assertEqual(self.root.wm_iconbitmap(), '') + t.wm_iconbitmap('') + self.assertEqual(t.wm_iconbitmap(), '') + + if t._windowingsystem == 'win32': + t.wm_iconbitmap(default='hourglass') + self.assertEqual(t.wm_iconbitmap(), 'hourglass') + self.assertEqual(self.root.wm_iconbitmap(), '') + t.wm_iconbitmap(default='') + self.assertEqual(t.wm_iconbitmap(), '') + + t.destroy() + class EventTest(AbstractTkTest, unittest.TestCase): diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index bfec04bb6c1e6e..d494c0c9687cd1 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -2265,7 +2265,7 @@ def wm_iconbitmap(self, bitmap=None, default=None): explicitly. DEFAULT can be the relative path to a .ico file (example: root.iconbitmap(default='myicon.ico') ). See Tk documentation for more information.""" - if default: + if default is not None: return self.tk.call('wm', 'iconbitmap', self._w, '-default', default) else: return self.tk.call('wm', 'iconbitmap', self._w, bitmap) diff --git a/Misc/NEWS.d/next/Library/2024-12-18-00-07-50.gh-issue-128014.F3aUbz.rst b/Misc/NEWS.d/next/Library/2024-12-18-00-07-50.gh-issue-128014.F3aUbz.rst new file mode 100644 index 00000000000000..ef339a291f0ddd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-18-00-07-50.gh-issue-128014.F3aUbz.rst @@ -0,0 +1,2 @@ +Fix resetting the default window icon by passing ``default=''`` to the +:mod:`tkinter` method :meth:`!wm_iconbitmap`. From a626f9a67b76e5fe69677afd5f8317d8c61de8de Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Thu, 2 Jan 2025 08:55:33 -0800 Subject: [PATCH 156/376] Remove asserts that confuse `enum _framestate` with `enum _frameowner` (GH-124148) The `owner` field of `_PyInterpreterFrame` is supposed to be a member of `enum _frameowner`, but `FRAME_CLEARED` is a member of `enum _framestate`. At present, it happens that `FRAME_CLEARED` is not numerically equal to any member of `enum _frameowner`, but that could change in the future. The code that incorrectly assigned `owner = FRAME_CLEARED` was deleted in commit a53cc3f49463e50cb3e2b839b3a82e6bf7f73fee (GH-116687). Remove the incorrect checks for `owner != FRAME_CLEARED` as well. --- Python/frame.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Python/frame.c b/Python/frame.c index 9a865e57d97cc6..6eb32bcce0b799 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -40,7 +40,6 @@ _PyFrame_MakeAndSetFrameObject(_PyInterpreterFrame *frame) // here. assert(frame->frame_obj == NULL); assert(frame->owner != FRAME_OWNED_BY_FRAME_OBJECT); - assert(frame->owner != FRAME_CLEARED); f->f_frame = frame; frame->frame_obj = f; return f; @@ -51,7 +50,6 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) { assert(frame->owner != FRAME_OWNED_BY_CSTACK); assert(frame->owner != FRAME_OWNED_BY_FRAME_OBJECT); - assert(frame->owner != FRAME_CLEARED); Py_ssize_t size = ((char*)frame->stackpointer) - (char *)frame; memcpy((_PyInterpreterFrame *)f->_f_frame_data, frame, size); frame = (_PyInterpreterFrame *)f->_f_frame_data; From c9356feef28e6dfc4dc32830d3427a5ae0e426e2 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 2 Jan 2025 13:56:01 -0500 Subject: [PATCH 157/376] gh-128400: Stop-the-world when manually calling `faulthandler` (GH-128422) --- Lib/test/test_faulthandler.py | 30 ++++++++++++++++++- ...-01-02-13-05-16.gh-issue-128400.5N43fF.rst | 2 ++ Modules/faulthandler.c | 5 ++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-02-13-05-16.gh-issue-128400.5N43fF.rst diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 60815be96e14eb..fd56dee5d842ac 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -7,7 +7,7 @@ import subprocess import sys from test import support -from test.support import os_helper, script_helper, is_android, MS_WINDOWS +from test.support import os_helper, script_helper, is_android, MS_WINDOWS, threading_helper import tempfile import unittest from textwrap import dedent @@ -896,6 +896,34 @@ def test_cancel_later_without_dump_traceback_later(self): self.assertEqual(output, []) self.assertEqual(exitcode, 0) + @threading_helper.requires_working_threading() + @unittest.skipUnless(support.Py_GIL_DISABLED, "only meaningful if the GIL is disabled") + def test_free_threaded_dump_traceback(self): + # gh-128400: Other threads need to be paused to invoke faulthandler + code = dedent(""" + import faulthandler + from threading import Thread, Event + + class Waiter(Thread): + def __init__(self): + Thread.__init__(self) + self.running = Event() + self.stop = Event() + + def run(self): + self.running.set() + self.stop.wait() + + for _ in range(100): + waiter = Waiter() + waiter.start() + waiter.running.wait() + faulthandler.dump_traceback(all_threads=True) + waiter.stop.set() + waiter.join() + """) + _, exitcode = self.get_output(code) + self.assertEqual(exitcode, 0) if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2025-01-02-13-05-16.gh-issue-128400.5N43fF.rst b/Misc/NEWS.d/next/Library/2025-01-02-13-05-16.gh-issue-128400.5N43fF.rst new file mode 100644 index 00000000000000..4033dea4eaf7bf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-02-13-05-16.gh-issue-128400.5N43fF.rst @@ -0,0 +1,2 @@ +Fix crash when using :func:`faulthandler.dump_traceback` while other threads +are active on the :term:`free threaded ` build. diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index b62362f277797e..2d16028a5232d0 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -237,7 +237,12 @@ faulthandler_dump_traceback_py(PyObject *self, return NULL; if (all_threads) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + /* gh-128400: Accessing other thread states while they're running + * isn't safe if those threads are running. */ + _PyEval_StopTheWorld(interp); errmsg = _Py_DumpTracebackThreads(fd, NULL, tstate); + _PyEval_StartTheWorld(interp); if (errmsg != NULL) { PyErr_SetString(PyExc_RuntimeError, errmsg); return NULL; From 8eebe4e6d02bb4ad3f1ca6c52624186903dce893 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 2 Jan 2025 14:02:54 -0500 Subject: [PATCH 158/376] gh-128212: Fix race in `_PyUnicode_CheckConsistency` (GH-128367) There was a data race on the utf8 field between `PyUnicode_SET_UTF8` and `_PyUnicode_CheckConsistency`. Use the `_PyUnicode_UTF8()` accessor, which uses an atomic load internally, to avoid the data race. --- Objects/unicodeobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 1aab9cf37768a8..9f0a4d4785eda6 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -688,7 +688,7 @@ _PyUnicode_CheckConsistency(PyObject *op, int check_content) || kind == PyUnicode_2BYTE_KIND || kind == PyUnicode_4BYTE_KIND); CHECK(ascii->state.ascii == 0); - CHECK(compact->utf8 != data); + CHECK(_PyUnicode_UTF8(op) != data); } else { PyUnicodeObject *unicode = _PyUnicodeObject_CAST(op); From e7adeecc2b318505eb53bc779320f028be40cccc Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Fri, 3 Jan 2025 00:40:24 -0700 Subject: [PATCH 159/376] gh-128426: Mention PySequence_Fast in free-threading C API HOWTO (#128428) --- Doc/howto/free-threading-extensions.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Doc/howto/free-threading-extensions.rst b/Doc/howto/free-threading-extensions.rst index c1ad42e7e55ee5..95f214179bfb0e 100644 --- a/Doc/howto/free-threading-extensions.rst +++ b/Doc/howto/free-threading-extensions.rst @@ -96,8 +96,10 @@ Most of the C API is thread-safe, but there are some exceptions. * **Struct Fields**: Accessing fields in Python C API objects or structs directly is not thread-safe if the field may be concurrently modified. -* **Macros**: Accessor macros like :c:macro:`PyList_GET_ITEM` and - :c:macro:`PyList_SET_ITEM` do not perform any error checking or locking. +* **Macros**: Accessor macros like :c:macro:`PyList_GET_ITEM`, + :c:macro:`PyList_SET_ITEM`, and macros like + :c:macro:`PySequence_Fast_GET_SIZE` that use the object returned by + :c:func:`PySequence_Fast` do not perform any error checking or locking. These macros are not thread-safe if the container object may be modified concurrently. * **Borrowed References**: C API functions that return From b49c68a1b3dfd2c2567c38b2d044c4a1c14a26a7 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Fri, 3 Jan 2025 07:50:24 +0000 Subject: [PATCH 160/376] gh-128404: remove asyncio from test_type_params (#128436) --- Lib/test/test_type_params.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_type_params.py b/Lib/test/test_type_params.py index 89f836cf722966..0f393def827271 100644 --- a/Lib/test/test_type_params.py +++ b/Lib/test/test_type_params.py @@ -1,11 +1,10 @@ import annotationlib -import asyncio import textwrap import types import unittest import pickle import weakref -from test.support import requires_working_socket, check_syntax_error, run_code +from test.support import check_syntax_error, run_code, run_no_yield_async_fn from typing import Generic, NoDefault, Sequence, TypeAliasType, TypeVar, TypeVarTuple, ParamSpec, get_args @@ -1051,7 +1050,6 @@ def generator2[B](): self.assertIsInstance(c, TypeVar) self.assertEqual(c.__name__, "C") - @requires_working_socket() def test_typevar_coroutine(self): def get_coroutine[A](): async def coroutine[B](): @@ -1060,8 +1058,7 @@ async def coroutine[B](): co = get_coroutine() - self.addCleanup(asyncio._set_event_loop_policy, None) - a, b = asyncio.run(co()) + a, b = run_no_yield_async_fn(co) self.assertIsInstance(a, TypeVar) self.assertEqual(a.__name__, "A") From 0706bab1c0985761cdbc07ab448f98c717276b36 Mon Sep 17 00:00:00 2001 From: Abhijeet Date: Fri, 3 Jan 2025 09:20:56 +0100 Subject: [PATCH 161/376] gh-128133: use relaxed atomics for hash of bytes (#128412) --- Objects/bytesobject.c | 70 ++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 533089d25cd73a..90e8a9af88b4a3 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -51,6 +51,33 @@ static inline PyObject* bytes_get_empty(void) } +static inline void +set_ob_shash(PyBytesObject *a, Py_hash_t hash) +{ +_Py_COMP_DIAG_PUSH +_Py_COMP_DIAG_IGNORE_DEPR_DECLS +#ifdef Py_GIL_DISABLED + _Py_atomic_store_ssize_relaxed(&a->ob_shash, hash); +#else + a->ob_shash = hash; +#endif +_Py_COMP_DIAG_POP +} + +static inline Py_hash_t +get_ob_shash(PyBytesObject *a) +{ +_Py_COMP_DIAG_PUSH +_Py_COMP_DIAG_IGNORE_DEPR_DECLS +#ifdef Py_GIL_DISABLED + return _Py_atomic_load_ssize_relaxed(&a->ob_shash); +#else + return a->ob_shash; +#endif +_Py_COMP_DIAG_POP +} + + /* For PyBytes_FromString(), the parameter 'str' points to a null-terminated string containing exactly 'size' bytes. @@ -98,10 +125,7 @@ _PyBytes_FromSize(Py_ssize_t size, int use_calloc) return PyErr_NoMemory(); } _PyObject_InitVar((PyVarObject*)op, &PyBytes_Type, size); -_Py_COMP_DIAG_PUSH -_Py_COMP_DIAG_IGNORE_DEPR_DECLS - op->ob_shash = -1; -_Py_COMP_DIAG_POP + set_ob_shash(op, -1); if (!use_calloc) { op->ob_sval[size] = '\0'; } @@ -165,10 +189,7 @@ PyBytes_FromString(const char *str) return PyErr_NoMemory(); } _PyObject_InitVar((PyVarObject*)op, &PyBytes_Type, size); -_Py_COMP_DIAG_PUSH -_Py_COMP_DIAG_IGNORE_DEPR_DECLS - op->ob_shash = -1; -_Py_COMP_DIAG_POP + set_ob_shash(op, -1); memcpy(op->ob_sval, str, size+1); return (PyObject *) op; } @@ -1485,10 +1506,7 @@ bytes_repeat(PyObject *self, Py_ssize_t n) return PyErr_NoMemory(); } _PyObject_InitVar((PyVarObject*)op, &PyBytes_Type, size); -_Py_COMP_DIAG_PUSH -_Py_COMP_DIAG_IGNORE_DEPR_DECLS - op->ob_shash = -1; -_Py_COMP_DIAG_POP + set_ob_shash(op, -1); op->ob_sval[size] = '\0'; _PyBytes_Repeat(op->ob_sval, size, a->ob_sval, Py_SIZE(a)); @@ -1597,14 +1615,13 @@ static Py_hash_t bytes_hash(PyObject *self) { PyBytesObject *a = _PyBytes_CAST(self); -_Py_COMP_DIAG_PUSH -_Py_COMP_DIAG_IGNORE_DEPR_DECLS - if (a->ob_shash == -1) { + Py_hash_t hash = get_ob_shash(a); + if (hash == -1) { /* Can't fail */ - a->ob_shash = Py_HashBuffer(a->ob_sval, Py_SIZE(a)); + hash = Py_HashBuffer(a->ob_sval, Py_SIZE(a)); + set_ob_shash(a, hash); } - return a->ob_shash; -_Py_COMP_DIAG_POP + return hash; } static PyObject* @@ -3004,10 +3021,7 @@ bytes_alloc(PyTypeObject *self, Py_ssize_t nitems) if (obj == NULL) { return NULL; } -_Py_COMP_DIAG_PUSH -_Py_COMP_DIAG_IGNORE_DEPR_DECLS - obj->ob_shash = -1; -_Py_COMP_DIAG_POP + set_ob_shash(obj, -1); return (PyObject*)obj; } @@ -3024,11 +3038,8 @@ bytes_subtype_new(PyTypeObject *type, PyObject *tmp) if (pnew != NULL) { memcpy(PyBytes_AS_STRING(pnew), PyBytes_AS_STRING(tmp), n+1); -_Py_COMP_DIAG_PUSH -_Py_COMP_DIAG_IGNORE_DEPR_DECLS - ((PyBytesObject *)pnew)->ob_shash = - ((PyBytesObject *)tmp)->ob_shash; -_Py_COMP_DIAG_POP + set_ob_shash((PyBytesObject *)pnew, + get_ob_shash((PyBytesObject *)tmp)); } return pnew; } @@ -3221,10 +3232,7 @@ _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize) sv = (PyBytesObject *) *pv; Py_SET_SIZE(sv, newsize); sv->ob_sval[newsize] = '\0'; -_Py_COMP_DIAG_PUSH -_Py_COMP_DIAG_IGNORE_DEPR_DECLS - sv->ob_shash = -1; /* invalidate cached hash value */ -_Py_COMP_DIAG_POP + set_ob_shash(sv, -1); /* invalidate cached hash value */ return 0; } From bb73426cafb78691b249ffa50f1872ab6f899d4a Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Fri, 3 Jan 2025 09:57:15 +0000 Subject: [PATCH 162/376] gh-108202: Document calendar.TextCalendar formatting helpers (#127608) Document the following TextCalendar methods: - formatday() - formatmonthname() - formatweekday() - formatweekheader() --- Doc/library/calendar.rst | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index 9a4395ecb5084d..97ca34b6c6184c 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -138,6 +138,14 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is :class:`TextCalendar` instances have the following methods: + + .. method:: formatday(theday, weekday, width) + + Return a string representing a single day formatted with the given *width*. + If *theday* is ``0``, return a string of spaces of + the specified width, representing an empty day. The *weekday* parameter + is unused. + .. method:: formatweek(theweek, w=0) Return a single week in a string with no newline. If *w* is provided, it @@ -145,6 +153,17 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is on the first weekday as specified in the constructor or set by the :meth:`setfirstweekday` method. + .. method:: formatweekday(weekday, width) + + Return a string representing the name of a single weekday formatted to + the specified *width*. The *weekday* parameter is an integer representing + the day of the week, where ``0`` is Monday and ``6`` is Sunday. + + .. method:: formatweekheader(width) + + Return a string containing the header row of weekday names, formatted + with the given *width* for each column. The names depend on the locale + settings and are padded to the specified width. .. method:: formatmonth(theyear, themonth, w=0, l=0) @@ -154,6 +173,12 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is on the first weekday as specified in the constructor or set by the :meth:`setfirstweekday` method. + .. method:: formatmonthname(theyear, themonth, width=0, withyear=True) + + Return a string representing the month's name centered within the + specified *width*. If *withyear* is ``True``, include the year in the + output. The *theyear* and *themonth* parameters specify the year + and month for the name to be formatted respectively. .. method:: prmonth(theyear, themonth, w=0, l=0) @@ -445,7 +470,7 @@ The :mod:`calendar` module exports the following data attributes: A sequence that represents the months of the year in the current locale. This follows normal convention of January being month number 1, so it has a length of - 13 and ``month_name[0]`` is the empty string. + 13 and ``month_name[0]`` is the empty string. >>> import calendar >>> list(calendar.month_name) From bb2dfadb9221fa3035fda42a2c153c831013e3d3 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Fri, 3 Jan 2025 04:04:03 -0600 Subject: [PATCH 163/376] gh-128104: Remove `Py_STRFTIME_C99_SUPPORT`; require C99-compliant strftime (#128106) --- .../2024-12-20-09-03-22.gh-issue-128104.m_SoVx.rst | 3 +++ Modules/_datetimemodule.c | 6 ------ configure | 14 ++++---------- configure.ac | 11 +++-------- pyconfig.h.in | 3 --- 5 files changed, 10 insertions(+), 27 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2024-12-20-09-03-22.gh-issue-128104.m_SoVx.rst diff --git a/Misc/NEWS.d/next/Build/2024-12-20-09-03-22.gh-issue-128104.m_SoVx.rst b/Misc/NEWS.d/next/Build/2024-12-20-09-03-22.gh-issue-128104.m_SoVx.rst new file mode 100644 index 00000000000000..c3a47fbecd1dad --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-12-20-09-03-22.gh-issue-128104.m_SoVx.rst @@ -0,0 +1,3 @@ +Remove ``Py_STRFTIME_C99_SUPPORT`` conditions in favor of requiring C99 +:manpage:`strftime(3)` specifier support at build time. When cross-compiling, +there is no build time check and support is assumed. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index b1102984cb5e9e..368d10411366c4 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1912,9 +1912,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, } #ifdef Py_NORMALIZE_CENTURY else if (ch == 'Y' || ch == 'G' -#ifdef Py_STRFTIME_C99_SUPPORT || ch == 'F' || ch == 'C' -#endif ) { /* 0-pad year with century as necessary */ PyObject *item = PySequence_GetItem(timetuple, 0); @@ -1952,15 +1950,11 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, * +6 to accommodate dashes, 2-digit month and day for %F. */ char buf[SIZEOF_LONG * 5 / 2 + 2 + 6]; Py_ssize_t n = PyOS_snprintf(buf, sizeof(buf), -#ifdef Py_STRFTIME_C99_SUPPORT ch == 'F' ? "%04ld-%%m-%%d" : -#endif "%04ld", year_long); -#ifdef Py_STRFTIME_C99_SUPPORT if (ch == 'C') { n -= 2; } -#endif if (_PyUnicodeWriter_WriteSubstring(&writer, format, start, end) < 0) { goto Error; } diff --git a/configure b/configure index 3d2c60213db591..2c3046e89bcb65 100755 --- a/configure +++ b/configure @@ -26436,8 +26436,8 @@ printf "%s\n" "#define Py_NORMALIZE_CENTURY 1" >>confdefs.h fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C99-specific strftime specifiers are supported" >&5 -printf %s "checking whether C99-specific strftime specifiers are supported... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C99-compatible strftime specifiers are supported" >&5 +printf %s "checking whether C99-compatible strftime specifiers are supported... " >&6; } if test ${ac_cv_strftime_c99_support+y} then : printf %s "(cached) " >&6 @@ -26445,7 +26445,7 @@ else $as_nop if test "$cross_compiling" = yes then : - ac_cv_strftime_c99_support=no + ac_cv_strftime_c99_support= else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -26472,7 +26472,7 @@ if ac_fn_c_try_run "$LINENO" then : ac_cv_strftime_c99_support=yes else $as_nop - ac_cv_strftime_c99_support=no + as_fn_error $? "Python requires C99-compatible strftime specifiers" "$LINENO" 5 fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -26481,12 +26481,6 @@ fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_strftime_c99_support" >&5 printf "%s\n" "$ac_cv_strftime_c99_support" >&6; } -if test "$ac_cv_strftime_c99_support" = yes -then - -printf "%s\n" "#define Py_STRFTIME_C99_SUPPORT 1" >>confdefs.h - -fi have_curses=no have_panel=no diff --git a/configure.ac b/configure.ac index ee034e5a9621df..50b130f2c802b5 100644 --- a/configure.ac +++ b/configure.ac @@ -6672,7 +6672,7 @@ then [Define if year with century should be normalized for strftime.]) fi -AC_CACHE_CHECK([whether C99-specific strftime specifiers are supported], [ac_cv_strftime_c99_support], [ +AC_CACHE_CHECK([whether C99-compatible strftime specifiers are supported], [ac_cv_strftime_c99_support], [ AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include @@ -6692,13 +6692,8 @@ int main(void) } ]])], [ac_cv_strftime_c99_support=yes], -[ac_cv_strftime_c99_support=no], -[ac_cv_strftime_c99_support=no])]) -if test "$ac_cv_strftime_c99_support" = yes -then - AC_DEFINE([Py_STRFTIME_C99_SUPPORT], [1], - [Define if C99-specific strftime specifiers are supported.]) -fi +[AC_MSG_ERROR([Python requires C99-compatible strftime specifiers])], +[ac_cv_strftime_c99_support=])]) dnl check for ncursesw/ncurses and panelw/panel dnl NOTE: old curses is not detected. diff --git a/pyconfig.h.in b/pyconfig.h.in index 1ca83fd2f2ca1b..ca08f087a85e9f 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1715,9 +1715,6 @@ /* Define if you want to enable internal statistics gathering. */ #undef Py_STATS -/* Define if C99-specific strftime specifiers are supported. */ -#undef Py_STRFTIME_C99_SUPPORT - /* The version of SunOS/Solaris as reported by `uname -r' without the dot. */ #undef Py_SUNOS_VERSION From 830e10651b1f45cd0af36ff611397b9f53171220 Mon Sep 17 00:00:00 2001 From: jb2170 Date: Fri, 3 Jan 2025 10:32:36 +0000 Subject: [PATCH 164/376] gh-127529: Correct asyncio's `accept_connection` behaviour for handling `ConnectionAbortedError` (#127532) Co-authored-by: Kumar Aditya --- Lib/asyncio/selector_events.py | 10 +++++--- Lib/test/test_asyncio/test_selector_events.py | 25 +++++++++++++++++++ ...-12-02-19-13-19.gh-issue-127529.Pj1Xtf.rst | 4 +++ 3 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-02-19-13-19.gh-issue-127529.Pj1Xtf.rst diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index f1ab9b12d69a5d..50992a607b3a1c 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -180,9 +180,13 @@ def _accept_connection( logger.debug("%r got a new connection from %r: %r", server, addr, conn) conn.setblocking(False) - except (BlockingIOError, InterruptedError, ConnectionAbortedError): - # Early exit because the socket accept buffer is empty. - return None + except ConnectionAbortedError: + # Discard connections that were aborted before accept(). + continue + except (BlockingIOError, InterruptedError): + # Early exit because of a signal or + # the socket accept buffer is empty. + return except OSError as exc: # There's nowhere to send the error, so just log it. if exc.errno in (errno.EMFILE, errno.ENFILE, diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py index f984dc96415ba3..c9217d04bcd322 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -364,6 +364,31 @@ def test_accept_connection_multiple(self): self.loop.run_until_complete(asyncio.sleep(0)) self.assertEqual(sock.accept.call_count, backlog) + def test_accept_connection_skip_connectionabortederror(self): + sock = mock.Mock() + + def mock_sock_accept(): + # mock accept(2) returning -ECONNABORTED every-other + # time that it's called. This applies most to OpenBSD + # whose sockets generate this errno more reproducibly than + # Linux and other OS. + if sock.accept.call_count % 2 == 0: + raise ConnectionAbortedError + return (mock.Mock(), mock.Mock()) + + sock.accept.side_effect = mock_sock_accept + backlog = 100 + # test that _accept_connection's loop calls sock.accept + # all 100 times, continuing past ConnectionAbortedError + # instead of unnecessarily returning early + mock_obj = mock.patch.object + with mock_obj(self.loop, '_accept_connection2') as accept2_mock: + self.loop._accept_connection( + mock.Mock(), sock, backlog=backlog) + # as in test_accept_connection_multiple avoid task pending + # warnings by using asyncio.sleep(0) + self.loop.run_until_complete(asyncio.sleep(0)) + self.assertEqual(sock.accept.call_count, backlog) class SelectorTransportTests(test_utils.TestCase): diff --git a/Misc/NEWS.d/next/Library/2024-12-02-19-13-19.gh-issue-127529.Pj1Xtf.rst b/Misc/NEWS.d/next/Library/2024-12-02-19-13-19.gh-issue-127529.Pj1Xtf.rst new file mode 100644 index 00000000000000..26f2fd5923ab7b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-02-19-13-19.gh-issue-127529.Pj1Xtf.rst @@ -0,0 +1,4 @@ +Correct behavior of +:func:`!asyncio.selector_events.BaseSelectorEventLoop._accept_connection` +in handling :exc:`ConnectionAbortedError` in a loop. This improves +performance on OpenBSD. From 4ed36d6efb3e3bc613045016dee594d671997709 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Fri, 3 Jan 2025 10:33:35 +0000 Subject: [PATCH 165/376] gh-128404: Remove `asyncio` from `Lib/test/test_sys_settrace` (#128435) --- Lib/test/test_sys_settrace.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index e5cf88177f7131..28c2c681babe18 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -6,8 +6,7 @@ import difflib import gc from functools import wraps -import asyncio -from test.support import import_helper, requires_subprocess +from test.support import import_helper, requires_subprocess, run_no_yield_async_fn import contextlib import os import tempfile @@ -19,8 +18,6 @@ except ImportError: _testinternalcapi = None -support.requires_working_socket(module=True) - class tracecontext: """Context manager that traces its enter and exit.""" def __init__(self, output, value): @@ -2067,10 +2064,9 @@ def run_async_test(self, func, jumpFrom, jumpTo, expected, error=None, stack.enter_context(self.assertRaisesRegex(*error)) if warning is not None: stack.enter_context(self.assertWarnsRegex(*warning)) - asyncio.run(func(output)) + run_no_yield_async_fn(func, output) sys.settrace(None) - asyncio._set_event_loop_policy(None) self.compare_jump_output(expected, output) def jump_test(jumpFrom, jumpTo, expected, error=None, event='line', warning=None): From 8abd6cef68a0582a4d912be76caddd9da5d55ccd Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 3 Jan 2025 12:37:54 +0100 Subject: [PATCH 166/376] gh-115765: Upgrade to GNU Autoconf 2.72 (#128411) --- .github/workflows/build.yml | 4 +- Doc/whatsnew/3.14.rst | 3 + ...-01-02-12-50-46.gh-issue-115765.jko7Fg.rst | 2 + Tools/build/regen-configure.sh | 2 +- config.guess | 107 +- configure | 6885 ++++++++++------- install-sh | 8 +- pyconfig.h.in | 606 +- 8 files changed, 4511 insertions(+), 3106 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2025-01-02-12-50-46.gh-issue-115765.jko7Fg.rst diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8787402ccc4423..9adf860632e8a2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,7 +46,7 @@ jobs: # reproducible: to get the same tools versions (autoconf, aclocal, ...) runs-on: ubuntu-24.04 container: - image: ghcr.io/python/autoconf:2024.11.11.11786316759 + image: ghcr.io/python/autoconf:2025.01.02.12581854023 timeout-minutes: 60 needs: check_source if: needs.check_source.outputs.run_tests == 'true' @@ -63,7 +63,7 @@ jobs: run: echo "IMAGE_VERSION=${ImageVersion}" >> "$GITHUB_ENV" - name: Check Autoconf and aclocal versions run: | - grep "Generated by GNU Autoconf 2.71" configure + grep "Generated by GNU Autoconf 2.72" configure grep "aclocal 1.16.5" aclocal.m4 grep -q "runstatedir" configure grep -q "PKG_PROG_PKG_CONFIG" aclocal.m4 diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 61f5ffdb6c89d1..69c356ba60e6d4 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1069,6 +1069,9 @@ Changes in the Python API Build changes ============= +* GNU Autoconf 2.72 is now required to generate :file:`!configure`. + (Contributed by Erlend Aasland in :gh:`115765`.) + PEP 761: Discontinuation of PGP signatures ------------------------------------------ diff --git a/Misc/NEWS.d/next/Build/2025-01-02-12-50-46.gh-issue-115765.jko7Fg.rst b/Misc/NEWS.d/next/Build/2025-01-02-12-50-46.gh-issue-115765.jko7Fg.rst new file mode 100644 index 00000000000000..34618c2c1288bc --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-01-02-12-50-46.gh-issue-115765.jko7Fg.rst @@ -0,0 +1,2 @@ +GNU Autoconf 2.72 is now required to generate :file:`!configure`. +Patch by Erlend Aasland. diff --git a/Tools/build/regen-configure.sh b/Tools/build/regen-configure.sh index d2a613b1e40dc1..c7683eb36763af 100755 --- a/Tools/build/regen-configure.sh +++ b/Tools/build/regen-configure.sh @@ -5,7 +5,7 @@ set -e -x # The check_autoconf_regen job of .github/workflows/build.yml must kept in # sync with this script. Use the same container image than the job so the job # doesn't need to run autoreconf in a container. -IMAGE="ghcr.io/python/autoconf:2024.11.11.11786316759" +IMAGE="ghcr.io/python/autoconf:2025.01.02.12581854023" AUTORECONF="autoreconf -ivf -Werror" WORK_DIR="/src" diff --git a/config.guess b/config.guess index e81d3ae7c210ba..cdfc4392047ce3 100755 --- a/config.guess +++ b/config.guess @@ -1,14 +1,14 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2021 Free Software Foundation, Inc. +# Copyright 1992-2023 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale -timestamp='2021-06-03' +timestamp='2023-08-22' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or +# the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but @@ -47,7 +47,7 @@ me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] -Output the configuration name of the system \`$me' is run on. +Output the configuration name of the system '$me' is run on. Options: -h, --help print this help, then exit @@ -60,13 +60,13 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2021 Free Software Foundation, Inc. +Copyright 1992-2023 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" -Try \`$me --help' for more information." +Try '$me --help' for more information." # Parse command line while test $# -gt 0 ; do @@ -102,8 +102,8 @@ GUESS= # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. -# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still -# use `HOST_CC' if defined, but it is deprecated. +# Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still +# use 'HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. @@ -155,6 +155,9 @@ Linux|GNU|GNU/*) set_cc_for_build cat <<-EOF > "$dummy.c" + #if defined(__ANDROID__) + LIBC=android + #else #include #if defined(__UCLIBC__) LIBC=uclibc @@ -169,6 +172,7 @@ Linux|GNU|GNU/*) LIBC=musl #endif #endif + #endif EOF cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` eval "$cc_set_libc" @@ -437,7 +441,7 @@ case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in # This test works for both compilers. if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 @@ -459,7 +463,7 @@ case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in UNAME_RELEASE=`uname -v` ;; esac - # Japanese Language versions have a version number like `4.1.3-JL'. + # Japanese Language versions have a version number like '4.1.3-JL'. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` GUESS=sparc-sun-sunos$SUN_REL ;; @@ -904,7 +908,7 @@ EOF fi ;; *:FreeBSD:*:*) - UNAME_PROCESSOR=`/usr/bin/uname -p` + UNAME_PROCESSOR=`uname -p` case $UNAME_PROCESSOR in amd64) UNAME_PROCESSOR=x86_64 ;; @@ -929,6 +933,9 @@ EOF i*:PW*:*) GUESS=$UNAME_MACHINE-pc-pw32 ;; + *:SerenityOS:*:*) + GUESS=$UNAME_MACHINE-pc-serenity + ;; *:Interix*:*) case $UNAME_MACHINE in x86) @@ -963,11 +970,37 @@ EOF GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC ;; + x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*) + GUESS="$UNAME_MACHINE-pc-managarm-mlibc" + ;; + *:[Mm]anagarm:*:*) + GUESS="$UNAME_MACHINE-unknown-managarm-mlibc" + ;; *:Minix:*:*) GUESS=$UNAME_MACHINE-unknown-minix ;; aarch64:Linux:*:*) - GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + set_cc_for_build + CPU=$UNAME_MACHINE + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __ARM_EABI__ + #ifdef __ARM_PCS_VFP + ABI=eabihf + #else + ABI=eabi + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;; + esac + fi + GUESS=$CPU-unknown-linux-$LIBCABI ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be @@ -1033,7 +1066,16 @@ EOF k1om:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; - loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) + kvx:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + kvx:cos:*:*) + GUESS=$UNAME_MACHINE-unknown-cos + ;; + kvx:mbr:*:*) + GUESS=$UNAME_MACHINE-unknown-mbr + ;; + loongarch32:Linux:*:* | loongarch64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m32r*:Linux:*:*) @@ -1148,16 +1190,27 @@ EOF ;; x86_64:Linux:*:*) set_cc_for_build + CPU=$UNAME_MACHINE LIBCABI=$LIBC if test "$CC_FOR_BUILD" != no_compiler_found; then - if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_X32 >/dev/null - then - LIBCABI=${LIBC}x32 - fi + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __i386__ + ABI=x86 + #else + #ifdef __ILP32__ + ABI=x32 + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + x86) CPU=i686 ;; + x32) LIBCABI=${LIBC}x32 ;; + esac fi - GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI + GUESS=$CPU-pc-linux-$LIBCABI ;; xtensa*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC @@ -1177,7 +1230,7 @@ EOF GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION ;; i*86:OS/2:*:*) - # If we were able to find `uname', then EMX Unix compatibility + # If we were able to find 'uname', then EMX Unix compatibility # is probably installed. GUESS=$UNAME_MACHINE-pc-os2-emx ;; @@ -1318,7 +1371,7 @@ EOF GUESS=ns32k-sni-sysv fi ;; - PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort # says GUESS=i586-unisys-sysv4 ;; @@ -1364,8 +1417,11 @@ EOF BePC:Haiku:*:*) # Haiku running on Intel PC compatible. GUESS=i586-pc-haiku ;; - x86_64:Haiku:*:*) - GUESS=x86_64-unknown-haiku + ppc:Haiku:*:*) # Haiku running on Apple PowerPC + GUESS=powerpc-apple-haiku + ;; + *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) + GUESS=$UNAME_MACHINE-unknown-haiku ;; SX-4:SUPER-UX:*:*) GUESS=sx4-nec-superux$UNAME_RELEASE @@ -1522,6 +1578,9 @@ EOF i*86:rdos:*:*) GUESS=$UNAME_MACHINE-pc-rdos ;; + i*86:Fiwix:*:*) + GUESS=$UNAME_MACHINE-pc-fiwix + ;; *:AROS:*:*) GUESS=$UNAME_MACHINE-unknown-aros ;; diff --git a/configure b/configure index 2c3046e89bcb65..86b96d6208fe40 100755 --- a/configure +++ b/configure @@ -1,11 +1,11 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for python 3.14. +# Generated by GNU Autoconf 2.72 for python 3.14. # # Report bugs to . # # -# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, +# Copyright (C) 1992-1996, 1998-2017, 2020-2023 Free Software Foundation, # Inc. # # @@ -17,7 +17,6 @@ # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh -as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh @@ -26,12 +25,13 @@ then : # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST -else $as_nop - case `(set -o) 2>/dev/null` in #( +else case e in #( + e) case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; +esac ;; esac fi @@ -103,7 +103,7 @@ IFS=$as_save_IFS ;; esac -# We did not find ourselves, most probably we were run as `sh COMMAND' +# We did not find ourselves, most probably we were run as 'sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 @@ -133,15 +133,14 @@ case $- in # (((( esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. +# out after a failed 'exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then - as_bourne_compatible="as_nop=: -if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 + as_bourne_compatible="if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: @@ -149,12 +148,13 @@ then : # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST -else \$as_nop - case \`(set -o) 2>/dev/null\` in #( +else case e in #( + e) case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; +esac ;; esac fi " @@ -172,8 +172,9 @@ as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ) then : -else \$as_nop - exitcode=1; echo positional parameters were not saved. +else case e in #( + e) exitcode=1; echo positional parameters were not saved. ;; +esac fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) @@ -187,14 +188,15 @@ test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null then : as_have_required=yes -else $as_nop - as_have_required=no +else case e in #( + e) as_have_required=no ;; +esac fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : -else $as_nop - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +else case e in #( + e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do @@ -227,12 +229,13 @@ IFS=$as_save_IFS if $as_found then : -else $as_nop - if { test -f "$SHELL" || test -f "$SHELL.exe"; } && +else case e in #( + e) if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes -fi +fi ;; +esac fi @@ -254,7 +257,7 @@ case $- in # (((( esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. +# out after a failed 'exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi @@ -274,7 +277,8 @@ $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 -fi +fi ;; +esac fi fi SHELL=${CONFIG_SHELL-/bin/sh} @@ -313,14 +317,6 @@ as_fn_exit () as_fn_set_status $1 exit $1 } # as_fn_exit -# as_fn_nop -# --------- -# Do nothing but, unlike ":", preserve the value of $?. -as_fn_nop () -{ - return $? -} -as_nop=as_fn_nop # as_fn_mkdir_p # ------------- @@ -389,11 +385,12 @@ then : { eval $1+=\$2 }' -else $as_nop - as_fn_append () +else case e in #( + e) as_fn_append () { eval $1=\$$1\$2 - } + } ;; +esac fi # as_fn_append # as_fn_arith ARG... @@ -407,21 +404,14 @@ then : { as_val=$(( $* )) }' -else $as_nop - as_fn_arith () +else case e in #( + e) as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` - } + } ;; +esac fi # as_fn_arith -# as_fn_nop -# --------- -# Do nothing but, unlike ":", preserve the value of $?. -as_fn_nop () -{ - return $? -} -as_nop=as_fn_nop # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- @@ -495,6 +485,8 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits /[$]LINENO/= ' <$as_myself | sed ' + t clear + :clear s/[$]LINENO.*/&-/ t lineno b @@ -543,7 +535,6 @@ esac as_echo='printf %s\n' as_echo_n='printf %s' - rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file @@ -555,9 +546,9 @@ if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. + # 1) On MSYS, both 'ln -s file dir' and 'ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; 'ln -s' creates a wrapper executable. + # In both cases, we have to default to 'cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then @@ -582,10 +573,12 @@ as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" +as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" +as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated # Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" +as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g" +as_tr_sh="eval sed '$as_sed_sh'" # deprecated test -n "$DJDIR" || exec 7<&0 /dev/null && - as_fn_error $? "invalid feature name: \`$ac_useropt'" + as_fn_error $? "invalid feature name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1313,7 +1306,7 @@ do ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: \`$ac_useropt'" + as_fn_error $? "invalid feature name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1526,7 +1519,7 @@ do ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: \`$ac_useropt'" + as_fn_error $? "invalid package name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1542,7 +1535,7 @@ do ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: \`$ac_useropt'" + as_fn_error $? "invalid package name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1572,8 +1565,8 @@ do | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; - -*) as_fn_error $? "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information" + -*) as_fn_error $? "unrecognized option: '$ac_option' +Try '$0 --help' for more information" ;; *=*) @@ -1581,7 +1574,7 @@ Try \`$0 --help' for more information" # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + as_fn_error $? "invalid variable name: '$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; @@ -1631,7 +1624,7 @@ do as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done -# There might be people who depend on the old broken behavior: `$host' +# There might be people who depend on the old broken behavior: '$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias @@ -1699,7 +1692,7 @@ if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi -ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_msg="sources are in $srcdir, but 'cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` @@ -1727,7 +1720,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures python 3.14 to adapt to many kinds of systems. +'configure' configures python 3.14 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1741,11 +1734,11 @@ Configuration: --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking ...' messages + -q, --quiet, --silent do not print 'checking ...' messages --cache-file=FILE cache test results in FILE [disabled] - -C, --config-cache alias for \`--cache-file=config.cache' + -C, --config-cache alias for '--cache-file=config.cache' -n, --no-create do not create output files - --srcdir=DIR find the sources in DIR [configure dir or \`..'] + --srcdir=DIR find the sources in DIR [configure dir or '..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX @@ -1753,10 +1746,10 @@ Installation directories: --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] -By default, \`make install' will install all the files in -\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify -an installation prefix other than \`$ac_default_prefix' using \`--prefix', -for instance \`--prefix=\$HOME'. +By default, 'make install' will install all the files in +'$ac_default_prefix/bin', '$ac_default_prefix/lib' etc. You can specify +an installation prefix other than '$ac_default_prefix' using '--prefix', +for instance '--prefix=\$HOME'. For better control, use the options below. @@ -2020,7 +2013,7 @@ Some influential environment variables: C compiler flags for PANEL, overriding pkg-config PANEL_LIBS linker flags for PANEL, overriding pkg-config -Use these variables to override the choices made by `configure' or to help +Use these variables to override the choices made by 'configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . @@ -2088,9 +2081,9 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF python configure 3.14 -generated by GNU Autoconf 2.71 +generated by GNU Autoconf 2.72 -Copyright (C) 2021 Free Software Foundation, Inc. +Copyright (C) 2023 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF @@ -2129,11 +2122,12 @@ printf "%s\n" "$ac_try_echo"; } >&5 } && test -s conftest.$ac_objext then : ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 +else case e in #( + e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_retval=1 + ac_retval=1 ;; +esac fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval @@ -2167,11 +2161,12 @@ printf "%s\n" "$ac_try_echo"; } >&5 } then : ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 +else case e in #( + e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_retval=1 + ac_retval=1 ;; +esac fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval @@ -2190,8 +2185,8 @@ printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> @@ -2199,10 +2194,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" -else $as_nop - eval "$3=no" +else case e in #( + e) eval "$3=no" ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 @@ -2242,11 +2239,12 @@ printf "%s\n" "$ac_try_echo"; } >&5 } then : ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 +else case e in #( + e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_retval=1 + ac_retval=1 ;; +esac fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would @@ -2288,12 +2286,13 @@ printf "%s\n" "$ac_try_echo"; } >&5 test $ac_status = 0; }; } then : ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: program exited with status $ac_status" >&5 +else case e in #( + e) printf "%s\n" "$as_me: program exited with status $ac_status" >&5 printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_retval=$ac_status + ac_retval=$ac_status ;; +esac fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno @@ -2313,8 +2312,8 @@ printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 -else $as_nop - eval "$3=no" +else case e in #( + e) eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 @@ -2344,12 +2343,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : -else $as_nop - eval "$3=yes" +else case e in #( + e) eval "$3=yes" ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 @@ -2403,18 +2404,19 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_hi=$ac_mid; break -else $as_nop - as_fn_arith $ac_mid + 1 && ac_lo=$as_val +else case e in #( + e) as_fn_arith $ac_mid + 1 && ac_lo=$as_val if test $ac_lo -le $ac_mid; then ac_lo= ac_hi= break fi - as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val + as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int @@ -2449,20 +2451,23 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_lo=$ac_mid; break -else $as_nop - as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val +else case e in #( + e) as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val if test $ac_mid -le $ac_hi; then ac_lo= ac_hi= break fi - as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val + as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done -else $as_nop - ac_lo= ac_hi= +else case e in #( + e) ac_lo= ac_hi= ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext # Binary search between lo and hi bounds. @@ -2485,8 +2490,9 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_hi=$ac_mid -else $as_nop - as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val +else case e in #( + e) as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done @@ -2534,8 +2540,9 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : echo >>conftest.val; read $3 &6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $2 (); below. */ + which can conflict with char $2 (void); below. */ #include #undef $2 @@ -2577,7 +2584,7 @@ else $as_nop #ifdef __cplusplus extern "C" #endif -char $2 (); +char $2 (void); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ @@ -2596,11 +2603,13 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" -else $as_nop - eval "$3=no" +else case e in #( + e) eval "$3=no" ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext + conftest$ac_exeext conftest.$ac_ext ;; +esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 @@ -2622,8 +2631,8 @@ printf %s "checking whether $as_decl_name is declared... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 -else $as_nop - as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` +else case e in #( + e) as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` eval ac_save_FLAGS=\$$6 as_fn_append $6 " $5" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -2647,12 +2656,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" -else $as_nop - eval "$3=no" +else case e in #( + e) eval "$3=no" ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext eval $6=\$ac_save_FLAGS - + ;; +esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 @@ -2673,8 +2684,8 @@ printf %s "checking for $2.$3... " >&6; } if eval test \${$4+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int @@ -2690,8 +2701,8 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$4=yes" -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int @@ -2707,12 +2718,15 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$4=yes" -else $as_nop - eval "$4=no" +else case e in #( + e) eval "$4=no" ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi eval ac_res=\$$4 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 @@ -2745,7 +2759,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by python $as_me 3.14, which was -generated by GNU Autoconf 2.71. Invocation command line was +generated by GNU Autoconf 2.72. Invocation command line was $ $0$ac_configure_args_raw @@ -2991,10 +3005,10 @@ esac printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ - || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } fi done @@ -3030,9 +3044,7 @@ struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; +static char *e (char **p, int i) { return p[i]; } @@ -3046,6 +3058,21 @@ static char *f (char * (*g) (char **, int), char **p, ...) return s; } +/* C89 style stringification. */ +#define noexpand_stringify(a) #a +const char *stringified = noexpand_stringify(arbitrary+token=sequence); + +/* C89 style token pasting. Exercises some of the corner cases that + e.g. old MSVC gets wrong, but not very hard. */ +#define noexpand_concat(a,b) a##b +#define expand_concat(a,b) noexpand_concat(a,b) +extern int vA; +extern int vbee; +#define aye A +#define bee B +int *pvA = &expand_concat(v,aye); +int *pvbee = &noexpand_concat(v,bee); + /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not \xHH hex character constants. These do not provoke an error unfortunately, instead are silently treated @@ -3073,16 +3100,19 @@ ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' -// Does the compiler advertise C99 conformance? +/* Does the compiler advertise C99 conformance? */ #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif +// See if C++-style comments work. + #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); +extern void free (void *); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare @@ -3132,7 +3162,6 @@ typedef const char *ccp; static inline int test_restrict (ccp restrict text) { - // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) @@ -3198,6 +3227,8 @@ ac_c_conftest_c99_main=' ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; + // Work around memory leak warnings. + free (ia); // Check named initializers. struct named_init ni = { @@ -3219,7 +3250,7 @@ ac_c_conftest_c99_main=' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' -// Does the compiler advertise C11 conformance? +/* Does the compiler advertise C11 conformance? */ #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif @@ -3413,8 +3444,9 @@ IFS=$as_save_IFS if $as_found then : -else $as_nop - as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 +else case e in #( + e) as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 ;; +esac fi @@ -3442,12 +3474,12 @@ for ac_var in $ac_precious_vars; do eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&5 +printf "%s\n" "$as_me: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 -printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was not set in the previous run" >&5 +printf "%s\n" "$as_me: error: '$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) @@ -3456,18 +3488,18 @@ printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 -printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' has changed since the previous run:" >&5 +printf "%s\n" "$as_me: error: '$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 -printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&5 +printf "%s\n" "$as_me: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 -printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 -printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: '$ac_old_val'" >&5 +printf "%s\n" "$as_me: former value: '$ac_old_val'" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: '$ac_new_val'" >&5 +printf "%s\n" "$as_me: current value: '$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. @@ -3483,11 +3515,11 @@ printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} fi done if $ac_cache_corrupted; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' + as_fn_error $? "run '${MAKE-make} distclean' and/or 'rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## @@ -3538,8 +3570,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_HAS_GIT+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$HAS_GIT"; then +else case e in #( + e) if test -n "$HAS_GIT"; then ac_cv_prog_HAS_GIT="$HAS_GIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -3562,7 +3594,8 @@ done IFS=$as_save_IFS test -z "$ac_cv_prog_HAS_GIT" && ac_cv_prog_HAS_GIT="not-found" -fi +fi ;; +esac fi HAS_GIT=$ac_cv_prog_HAS_GIT if test -n "$HAS_GIT"; then @@ -3604,15 +3637,16 @@ printf %s "checking build system type... " >&6; } if test ${ac_cv_build+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_build_alias=$build_alias +else case e in #( + e) ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 printf "%s\n" "$ac_cv_build" >&6; } @@ -3639,14 +3673,15 @@ printf %s "checking host system type... " >&6; } if test ${ac_cv_host+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "x$host_alias" = x; then +else case e in #( + e) if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 printf "%s\n" "$ac_cv_host" >&6; } @@ -3710,8 +3745,8 @@ fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_build_python" >&5 printf "%s\n" "$with_build_python" >&6; } -else $as_nop - +else case e in #( + e) if test "x$cross_compiling" = xyes then : as_fn_error $? "Cross compiling requires --with-build-python" "$LINENO" 5 @@ -3720,7 +3755,8 @@ fi PYTHON_FOR_BUILD='./$(BUILDPYTHON) -E' PYTHON_FOR_FREEZE="./_bootstrap_python" - + ;; +esac fi @@ -3740,15 +3776,16 @@ then : FREEZE_MODULE_DEPS='$(FREEZE_MODULE_BOOTSTRAP_DEPS)' PYTHON_FOR_BUILD_DEPS='' -else $as_nop - +else case e in #( + e) FREEZE_MODULE_BOOTSTRAP='./Programs/_freeze_module' FREEZE_MODULE_BOOTSTRAP_DEPS="Programs/_freeze_module" FREEZE_MODULE='$(PYTHON_FOR_FREEZE) $(srcdir)/Programs/_freeze_module.py' FREEZE_MODULE_DEPS="_bootstrap_python \$(srcdir)/Programs/_freeze_module.py" PYTHON_FOR_BUILD_DEPS='$(BUILDPYTHON)' - + ;; +esac fi @@ -3765,8 +3802,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_PYTHON_FOR_REGEN+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$PYTHON_FOR_REGEN"; then +else case e in #( + e) if test -n "$PYTHON_FOR_REGEN"; then ac_cv_prog_PYTHON_FOR_REGEN="$PYTHON_FOR_REGEN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -3788,7 +3825,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi PYTHON_FOR_REGEN=$ac_cv_prog_PYTHON_FOR_REGEN if test -n "$PYTHON_FOR_REGEN"; then @@ -3870,9 +3908,10 @@ CONFIG_ARGS="$ac_configure_args" if test ${with_pkg_config+y} then : withval=$with_pkg_config; -else $as_nop - with_pkg_config=check - +else case e in #( + e) with_pkg_config=check + ;; +esac fi case $with_pkg_config in #( @@ -3899,8 +3938,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_PKG_CONFIG+y} then : printf %s "(cached) " >&6 -else $as_nop - case $PKG_CONFIG in +else case e in #( + e) case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; @@ -3925,6 +3964,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG @@ -3947,8 +3987,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_PKG_CONFIG+y} then : printf %s "(cached) " >&6 -else $as_nop - case $ac_pt_PKG_CONFIG in +else case e in #( + e) case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; @@ -3973,6 +4013,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG @@ -4208,11 +4249,12 @@ then : esac -else $as_nop - +else case e in #( + e) UNIVERSALSDK= enable_universalsdk= - + ;; +esac fi if test -n "${UNIVERSALSDK}" @@ -4273,12 +4315,13 @@ then : PYTHONFRAMEWORKDIR=${withval}.framework PYTHONFRAMEWORKIDENTIFIER=org.python.`echo $withval | tr 'A-Z' 'a-z'` -else $as_nop - +else case e in #( + e) PYTHONFRAMEWORK=Python PYTHONFRAMEWORKDIR=Python.framework PYTHONFRAMEWORKIDENTIFIER=org.python.python - + ;; +esac fi # Check whether --enable-framework was given. @@ -4411,8 +4454,8 @@ then : esac esac -else $as_nop - +else case e in #( + e) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; *) @@ -4435,7 +4478,8 @@ else $as_nop fi enable_framework= esac - + ;; +esac fi @@ -4484,8 +4528,8 @@ printf "%s\n" "applying custom app store compliance patch" >&6; } ;; esac -else $as_nop - +else case e in #( + e) case $ac_sys_system in iOS) # Always apply the compliance patch on iOS; we can use the macOS patch @@ -4500,7 +4544,8 @@ printf "%s\n" "applying default app store compliance patch" >&6; } printf "%s\n" "not patching for app store compliance" >&6; } ;; esac - + ;; +esac fi @@ -4742,8 +4787,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_HAS_XCRUN+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$HAS_XCRUN"; then +else case e in #( + e) if test -n "$HAS_XCRUN"; then ac_cv_prog_HAS_XCRUN="$HAS_XCRUN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -4766,7 +4811,8 @@ done IFS=$as_save_IFS test -z "$ac_cv_prog_HAS_XCRUN" && ac_cv_prog_HAS_XCRUN="missing" -fi +fi ;; +esac fi HAS_XCRUN=$ac_cv_prog_HAS_XCRUN if test -n "$HAS_XCRUN"; then @@ -4869,8 +4915,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then +else case e in #( + e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -4892,7 +4938,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then @@ -4914,8 +4961,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_CC"; then +else case e in #( + e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -4937,7 +4984,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then @@ -4972,8 +5020,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then +else case e in #( + e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -4995,7 +5043,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then @@ -5017,8 +5066,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then +else case e in #( + e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no @@ -5057,7 +5106,8 @@ if test $ac_prog_rejected = yes; then ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi -fi +fi ;; +esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then @@ -5081,8 +5131,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then +else case e in #( + e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -5104,7 +5154,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then @@ -5130,8 +5181,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_CC"; then +else case e in #( + e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -5153,7 +5204,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then @@ -5191,8 +5243,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then +else case e in #( + e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -5214,7 +5266,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then @@ -5236,8 +5289,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_CC"; then +else case e in #( + e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -5259,7 +5312,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then @@ -5288,10 +5342,10 @@ fi fi -test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 @@ -5363,8 +5417,8 @@ printf "%s\n" "$ac_try_echo"; } >&5 printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : - # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. -# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' + # Autoconf-2.13 could set the ac_cv_exeext variable to 'no'. +# So ignore a value of 'no', otherwise this would lead to 'EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. @@ -5384,7 +5438,7 @@ do ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not - # safe: cross compilers may not add the suffix if given an `-o' + # safe: cross compilers may not add the suffix if given an '-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. @@ -5395,8 +5449,9 @@ do done test "$ac_cv_exeext" = no && ac_cv_exeext= -else $as_nop - ac_file='' +else case e in #( + e) ac_file='' ;; +esac fi if test -z "$ac_file" then : @@ -5405,13 +5460,14 @@ printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf "%s\n" "yes" >&6; } +See 'config.log' for more details" "$LINENO" 5; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } @@ -5435,10 +5491,10 @@ printf "%s\n" "$ac_try_echo"; } >&5 printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. + # If both 'conftest.exe' and 'conftest' are 'present' (well, observable) +# catch 'conftest.exe'. For instance with Cygwin, 'ls conftest' will +# work properly (i.e., refer to 'conftest.exe'), while it won't with +# 'rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in @@ -5448,11 +5504,12 @@ for ac_file in conftest.exe conftest conftest.*; do * ) break;; esac done -else $as_nop - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } ;; +esac fi rm -f conftest conftest$ac_cv_exeext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 @@ -5468,6 +5525,8 @@ int main (void) { FILE *f = fopen ("conftest.out", "w"); + if (!f) + return 1; return ferror (f) || fclose (f) != 0; ; @@ -5507,26 +5566,27 @@ printf "%s\n" "$ac_try_echo"; } >&5 if test "$cross_compiling" = maybe; then cross_compiling=yes else - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5; } +If you meant to cross compile, use '--host'. +See 'config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } -rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +rm -f conftest.$ac_ext conftest$ac_cv_exeext \ + conftest.o conftest.obj conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -5558,16 +5618,18 @@ then : break;; esac done -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 +else case e in #( + e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } ;; +esac fi -rm -f conftest.$ac_cv_objext conftest.$ac_ext +rm -f conftest.$ac_cv_objext conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } @@ -5578,8 +5640,8 @@ printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -5596,12 +5658,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes -else $as_nop - ac_compiler_gnu=no +else case e in #( + e) ac_compiler_gnu=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } @@ -5619,8 +5683,8 @@ printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_save_c_werror_flag=$ac_c_werror_flag +else case e in #( + e) ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" @@ -5638,8 +5702,8 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes -else $as_nop - CFLAGS="" +else case e in #( + e) CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -5654,8 +5718,8 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : -else $as_nop - ac_c_werror_flag=$ac_save_c_werror_flag +else case e in #( + e) ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -5672,12 +5736,15 @@ if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag + ac_c_werror_flag=$ac_save_c_werror_flag ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } @@ -5704,8 +5771,8 @@ printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cc_c11=no +else case e in #( + e) ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -5722,25 +5789,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext -CC=$ac_save_CC +CC=$ac_save_CC ;; +esac fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cc_c11" = x +else case e in #( + e) if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } - CC="$CC $ac_cv_prog_cc_c11" + CC="$CC $ac_cv_prog_cc_c11" ;; +esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 - ac_prog_cc_stdc=c11 + ac_prog_cc_stdc=c11 ;; +esac fi fi if test x$ac_prog_cc_stdc = xno @@ -5750,8 +5820,8 @@ printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cc_c99=no +else case e in #( + e) ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -5768,25 +5838,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext -CC=$ac_save_CC +CC=$ac_save_CC ;; +esac fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cc_c99" = x +else case e in #( + e) if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } - CC="$CC $ac_cv_prog_cc_c99" + CC="$CC $ac_cv_prog_cc_c99" ;; +esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 - ac_prog_cc_stdc=c99 + ac_prog_cc_stdc=c99 ;; +esac fi fi if test x$ac_prog_cc_stdc = xno @@ -5796,8 +5869,8 @@ printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cc_c89=no +else case e in #( + e) ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -5814,25 +5887,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext -CC=$ac_save_CC +CC=$ac_save_CC ;; +esac fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cc_c89" = x +else case e in #( + e) if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } - CC="$CC $ac_cv_prog_cc_c89" + CC="$CC $ac_cv_prog_cc_c89" ;; +esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 - ac_prog_cc_stdc=c89 + ac_prog_cc_stdc=c89 ;; +esac fi fi @@ -5857,8 +5933,8 @@ if test -z "$CPP"; then if test ${ac_cv_prog_CPP+y} then : printf %s "(cached) " >&6 -else $as_nop - # Double quotes because $CC needs to be expanded +else case e in #( + e) # Double quotes because $CC needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" cpp /lib/cpp do ac_preproc_ok=false @@ -5876,9 +5952,10 @@ _ACEOF if ac_fn_c_try_cpp "$LINENO" then : -else $as_nop - # Broken: fails on valid input. -continue +else case e in #( + e) # Broken: fails on valid input. +continue ;; +esac fi rm -f conftest.err conftest.i conftest.$ac_ext @@ -5892,15 +5969,16 @@ if ac_fn_c_try_cpp "$LINENO" then : # Broken: success on invalid input. continue -else $as_nop - # Passes both tests. +else case e in #( + e) # Passes both tests. ac_preproc_ok=: -break +break ;; +esac fi rm -f conftest.err conftest.i conftest.$ac_ext done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +# Because of 'break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok then : @@ -5909,7 +5987,8 @@ fi done ac_cv_prog_CPP=$CPP - + ;; +esac fi CPP=$ac_cv_prog_CPP else @@ -5932,9 +6011,10 @@ _ACEOF if ac_fn_c_try_cpp "$LINENO" then : -else $as_nop - # Broken: fails on valid input. -continue +else case e in #( + e) # Broken: fails on valid input. +continue ;; +esac fi rm -f conftest.err conftest.i conftest.$ac_ext @@ -5948,24 +6028,26 @@ if ac_fn_c_try_cpp "$LINENO" then : # Broken: success on invalid input. continue -else $as_nop - # Passes both tests. +else case e in #( + e) # Passes both tests. ac_preproc_ok=: -break +break ;; +esac fi rm -f conftest.err conftest.i conftest.$ac_ext done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +# Because of 'break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok then : -else $as_nop - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } ;; +esac fi ac_ext=c @@ -5979,8 +6061,8 @@ printf %s "checking for grep that handles long lines and -e... " >&6; } if test ${ac_cv_path_GREP+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -z "$GREP"; then +else case e in #( + e) if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -5999,9 +6081,10 @@ do as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP -case `"$ac_path_GREP" --version 2>&1` in +case `"$ac_path_GREP" --version 2>&1` in #( *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +#( *) ac_count=0 printf %s 0123456789 >"conftest.in" @@ -6036,7 +6119,8 @@ IFS=$as_save_IFS else ac_cv_path_GREP=$GREP fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 printf "%s\n" "$ac_cv_path_GREP" >&6; } @@ -6048,8 +6132,8 @@ printf %s "checking for a sed that does not truncate output... " >&6; } if test ${ac_cv_path_SED+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ +else case e in #( + e) ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done @@ -6074,9 +6158,10 @@ do as_fn_executable_p "$ac_path_SED" || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED -case `"$ac_path_SED" --version 2>&1` in +case `"$ac_path_SED" --version 2>&1` in #( *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; +#( *) ac_count=0 printf %s 0123456789 >"conftest.in" @@ -6111,7 +6196,8 @@ IFS=$as_save_IFS else ac_cv_path_SED=$SED fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 printf "%s\n" "$ac_cv_path_SED" >&6; } @@ -6123,8 +6209,8 @@ printf %s "checking for egrep... " >&6; } if test ${ac_cv_path_EGREP+y} then : printf %s "(cached) " >&6 -else $as_nop - if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 +else case e in #( + e) if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then @@ -6146,9 +6232,10 @@ do as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP -case `"$ac_path_EGREP" --version 2>&1` in +case `"$ac_path_EGREP" --version 2>&1` in #( *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +#( *) ac_count=0 printf %s 0123456789 >"conftest.in" @@ -6184,12 +6271,15 @@ else ac_cv_path_EGREP=$EGREP fi - fi + fi ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 printf "%s\n" "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" + EGREP_TRADITIONAL=$EGREP + ac_cv_path_EGREP_TRADITIONAL=$EGREP CC_BASENAME=$(expr "//$CC" : '.*/\(.*\)') @@ -6199,8 +6289,8 @@ printf %s "checking for CC compiler name... " >&6; } if test ${ac_cv_cc_name+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat > conftest.c <&5 printf "%s\n" "$ac_cv_cc_name" >&6; } @@ -6278,8 +6369,8 @@ printf %s "checking whether it is safe to define __EXTENSIONS__... " >&6; } if test ${ac_cv_safe_to_define___extensions__+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ # define __EXTENSIONS__ 1 @@ -6295,10 +6386,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_safe_to_define___extensions__=yes -else $as_nop - ac_cv_safe_to_define___extensions__=no +else case e in #( + e) ac_cv_safe_to_define___extensions__=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 printf "%s\n" "$ac_cv_safe_to_define___extensions__" >&6; } @@ -6308,8 +6401,8 @@ printf %s "checking whether _XOPEN_SOURCE should be defined... " >&6; } if test ${ac_cv_should_define__xopen_source+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_cv_should_define__xopen_source=no +else case e in #( + e) ac_cv_should_define__xopen_source=no if test $ac_cv_header_wchar_h = yes then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -6328,8 +6421,8 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _XOPEN_SOURCE 500 @@ -6347,10 +6440,12 @@ if ac_fn_c_try_compile "$LINENO" then : ac_cv_should_define__xopen_source=yes fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi +fi ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_should_define__xopen_source" >&5 printf "%s\n" "$ac_cv_should_define__xopen_source" >&6; } @@ -6375,6 +6470,8 @@ printf "%s\n" "$ac_cv_should_define__xopen_source" >&6; } printf "%s\n" "#define __STDC_WANT_IEC_60559_DFP_EXT__ 1" >>confdefs.h + printf "%s\n" "#define __STDC_WANT_IEC_60559_EXT__ 1" >>confdefs.h + printf "%s\n" "#define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1" >>confdefs.h printf "%s\n" "#define __STDC_WANT_IEC_60559_TYPES_EXT__ 1" >>confdefs.h @@ -6394,8 +6491,9 @@ then : printf "%s\n" "#define _POSIX_1_SOURCE 2" >>confdefs.h -else $as_nop - MINIX= +else case e in #( + e) MINIX= ;; +esac fi if test $ac_cv_safe_to_define___extensions__ = yes then : @@ -6415,8 +6513,8 @@ printf %s "checking for GCC compatible compiler... " >&6; } if test ${ac_cv_gcc_compat+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if !defined(__GNUC__) @@ -6429,10 +6527,12 @@ _ACEOF if ac_fn_c_try_cpp "$LINENO" then : ac_cv_gcc_compat=yes -else $as_nop - ac_cv_gcc_compat=no +else case e in #( + e) ac_cv_gcc_compat=no ;; +esac fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.i conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_gcc_compat" >&5 printf "%s\n" "$ac_cv_gcc_compat" >&6; } @@ -6451,8 +6551,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_CXX+y} then : printf %s "(cached) " >&6 -else $as_nop - case $CXX in +else case e in #( + e) case $CXX in [\\/]* | ?:[\\/]*) ac_cv_path_CXX="$CXX" # Let the user override the test with a path. ;; @@ -6477,6 +6577,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi CXX=$ac_cv_path_CXX @@ -6499,8 +6600,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_CXX+y} then : printf %s "(cached) " >&6 -else $as_nop - case $ac_pt_CXX in +else case e in #( + e) case $ac_pt_CXX in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_CXX="$ac_pt_CXX" # Let the user override the test with a path. ;; @@ -6525,6 +6626,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi ac_pt_CXX=$ac_cv_path_ac_pt_CXX @@ -6559,8 +6661,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_CXX+y} then : printf %s "(cached) " >&6 -else $as_nop - case $CXX in +else case e in #( + e) case $CXX in [\\/]* | ?:[\\/]*) ac_cv_path_CXX="$CXX" # Let the user override the test with a path. ;; @@ -6585,6 +6687,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi CXX=$ac_cv_path_CXX @@ -6607,8 +6710,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_CXX+y} then : printf %s "(cached) " >&6 -else $as_nop - case $ac_pt_CXX in +else case e in #( + e) case $ac_pt_CXX in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_CXX="$ac_pt_CXX" # Let the user override the test with a path. ;; @@ -6633,6 +6736,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi ac_pt_CXX=$ac_cv_path_ac_pt_CXX @@ -6667,8 +6771,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_CXX+y} then : printf %s "(cached) " >&6 -else $as_nop - case $CXX in +else case e in #( + e) case $CXX in [\\/]* | ?:[\\/]*) ac_cv_path_CXX="$CXX" # Let the user override the test with a path. ;; @@ -6693,6 +6797,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi CXX=$ac_cv_path_CXX @@ -6715,8 +6820,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_CXX+y} then : printf %s "(cached) " >&6 -else $as_nop - case $ac_pt_CXX in +else case e in #( + e) case $ac_pt_CXX in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_CXX="$ac_pt_CXX" # Let the user override the test with a path. ;; @@ -6741,6 +6846,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi ac_pt_CXX=$ac_cv_path_ac_pt_CXX @@ -6775,8 +6881,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_CXX+y} then : printf %s "(cached) " >&6 -else $as_nop - case $CXX in +else case e in #( + e) case $CXX in [\\/]* | ?:[\\/]*) ac_cv_path_CXX="$CXX" # Let the user override the test with a path. ;; @@ -6801,6 +6907,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi CXX=$ac_cv_path_CXX @@ -6823,8 +6930,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_CXX+y} then : printf %s "(cached) " >&6 -else $as_nop - case $ac_pt_CXX in +else case e in #( + e) case $ac_pt_CXX in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_CXX="$ac_pt_CXX" # Let the user override the test with a path. ;; @@ -6849,6 +6956,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi ac_pt_CXX=$ac_cv_path_ac_pt_CXX @@ -6893,8 +7001,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CXX+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$CXX"; then +else case e in #( + e) if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -6916,7 +7024,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then @@ -6942,8 +7051,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CXX+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_CXX"; then +else case e in #( + e) if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -6965,7 +7074,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then @@ -7139,8 +7249,8 @@ printf %s "checking for -Wl,--no-as-needed... " >&6; } if test ${ac_cv_wl_no_as_needed+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) save_LDFLAGS="$LDFLAGS" as_fn_append LDFLAGS " -Wl,--no-as-needed" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -7158,14 +7268,16 @@ if ac_fn_c_try_link "$LINENO" then : NO_AS_NEEDED="-Wl,--no-as-needed" ac_cv_wl_no_as_needed=yes -else $as_nop - NO_AS_NEEDED="" - ac_cv_wl_no_as_needed=no +else case e in #( + e) NO_AS_NEEDED="" + ac_cv_wl_no_as_needed=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LDFLAGS="$save_LDFLAGS" - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_wl_no_as_needed" >&5 printf "%s\n" "$ac_cv_wl_no_as_needed" >&6; } @@ -7238,10 +7350,11 @@ then : ;; esac -else $as_nop - +else case e in #( + e) enable_wasm_dynamic_linking=missing - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_wasm_dynamic_linking" >&5 @@ -7263,10 +7376,11 @@ then : ;; esac -else $as_nop - +else case e in #( + e) enable_wasm_pthreads=missing - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_wasm_pthreads" >&5 @@ -7289,8 +7403,8 @@ then : ;; esac -else $as_nop - +else case e in #( + e) case $ac_sys_system in #( Emscripten) : EXEEXT=.mjs ;; #( @@ -7300,7 +7414,8 @@ else $as_nop EXEEXT= ;; esac - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $EXEEXT" >&5 @@ -7476,9 +7591,10 @@ else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; }; fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf "%s\n" "yes" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } ;; +esac fi @@ -7501,8 +7617,9 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : -else $as_nop - enable_profiling=no +else case e in #( + e) enable_profiling=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -7640,8 +7757,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_NODE+y} then : printf %s "(cached) " >&6 -else $as_nop - case $NODE in +else case e in #( + e) case $NODE in [\\/]* | ?:[\\/]*) ac_cv_path_NODE="$NODE" # Let the user override the test with a path. ;; @@ -7666,6 +7783,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi NODE=$ac_cv_path_NODE @@ -7688,8 +7806,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_NODE+y} then : printf %s "(cached) " >&6 -else $as_nop - case $ac_pt_NODE in +else case e in #( + e) case $ac_pt_NODE in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_NODE="$ac_pt_NODE" # Let the user override the test with a path. ;; @@ -7714,6 +7832,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi ac_pt_NODE=$ac_cv_path_ac_pt_NODE @@ -7798,8 +7917,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_AR+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$AR"; then +else case e in #( + e) if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -7821,7 +7940,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi AR=$ac_cv_prog_AR if test -n "$AR"; then @@ -7847,8 +7967,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_AR+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_AR"; then +else case e in #( + e) if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -7870,7 +7990,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then @@ -7935,8 +8056,8 @@ if test -z "$INSTALL"; then if test ${ac_cv_path_install+y} then : printf %s "(cached) " >&6 -else $as_nop - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +else case e in #( + e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS @@ -7990,7 +8111,8 @@ esac IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir - + ;; +esac fi if test ${ac_cv_path_install+y}; then INSTALL=$ac_cv_path_install @@ -8020,8 +8142,8 @@ if test -z "$MKDIR_P"; then if test ${ac_cv_path_mkdir+y} then : printf %s "(cached) " >&6 -else $as_nop - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +else case e in #( + e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS @@ -8035,7 +8157,7 @@ do as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext" || continue case `"$as_dir$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir ('*'coreutils) '* | \ - 'BusyBox '* | \ + *'BusyBox '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir$ac_prog$ac_exec_ext break 3;; @@ -8044,18 +8166,17 @@ do done done IFS=$as_save_IFS - + ;; +esac fi test -d ./--version && rmdir ./--version if test ${ac_cv_path_mkdir+y}; then MKDIR_P="$ac_cv_path_mkdir -p" else - # As a last resort, use the slow shell script. Don't cache a - # value for MKDIR_P within a source directory, because that will - # break other packages using the cache if that directory is - # removed, or if the value is a relative name. - MKDIR_P="$ac_install_sh -d" + # As a last resort, use plain mkdir -p, + # in the hope it doesn't have the bugs of ancient mkdir. + MKDIR_P='mkdir -p' fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 @@ -8087,12 +8208,14 @@ then : enableval=$enable_gil; if test "x$enable_gil" = xyes then : disable_gil=no -else $as_nop - disable_gil=yes +else case e in #( + e) disable_gil=yes ;; +esac fi -else $as_nop - disable_gil=no - +else case e in #( + e) disable_gil=no + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $disable_gil" >&5 @@ -8128,9 +8251,10 @@ printf "%s\n" "yes" >&6; }; else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; }; Py_DEBUG='false' fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } ;; +esac fi @@ -8143,9 +8267,10 @@ printf %s "checking for --with-trace-refs... " >&6; } if test ${with_trace_refs+y} then : withval=$with_trace_refs; -else $as_nop - with_trace_refs=no - +else case e in #( + e) with_trace_refs=no + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_trace_refs" >&5 @@ -8170,9 +8295,10 @@ printf %s "checking for --enable-pystats... " >&6; } if test ${enable_pystats+y} then : enableval=$enable_pystats; -else $as_nop - enable_pystats=no - +else case e in #( + e) enable_pystats=no + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_pystats" >&5 @@ -8222,8 +8348,9 @@ printf %s "checking for --enable-experimental-jit... " >&6; } if test ${enable_experimental_jit+y} then : enableval=$enable_experimental_jit; -else $as_nop - enable_experimental_jit=no +else case e in #( + e) enable_experimental_jit=no ;; +esac fi case $enable_experimental_jit in @@ -8237,20 +8364,22 @@ esac if ${tier2_flags:+false} : then : -else $as_nop - as_fn_append CFLAGS_NODIST " $tier2_flags" +else case e in #( + e) as_fn_append CFLAGS_NODIST " $tier2_flags" ;; +esac fi if ${jit_flags:+false} : then : -else $as_nop - as_fn_append CFLAGS_NODIST " $jit_flags" +else case e in #( + e) as_fn_append CFLAGS_NODIST " $jit_flags" REGEN_JIT_COMMAND="\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py $host" JIT_STENCILS_H="jit_stencils.h" if test "x$Py_DEBUG" = xtrue then : as_fn_append REGEN_JIT_COMMAND " --debug" -fi +fi ;; +esac fi @@ -8277,9 +8406,10 @@ else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; }; fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } ;; +esac fi @@ -8299,8 +8429,8 @@ printf %s "checking whether C compiler accepts -fno-semantic-interposition... " if test ${ax_cv_check_cflags__Werror__fno_semantic_interposition+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror -fno-semantic-interposition" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -8317,11 +8447,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags__Werror__fno_semantic_interposition=yes -else $as_nop - ax_cv_check_cflags__Werror__fno_semantic_interposition=no +else case e in #( + e) ax_cv_check_cflags__Werror__fno_semantic_interposition=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__fno_semantic_interposition" >&5 printf "%s\n" "$ax_cv_check_cflags__Werror__fno_semantic_interposition" >&6; } @@ -8331,8 +8463,9 @@ then : CFLAGS_NODIST="$CFLAGS_NODIST -fno-semantic-interposition" LDFLAGS_NODIST="$LDFLAGS_NODIST -fno-semantic-interposition" -else $as_nop - : +else case e in #( + e) : ;; +esac fi @@ -8419,9 +8552,10 @@ printf "%s\n" "no" >&6; } ;; esac -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } ;; +esac fi if test "$Py_LTO" = 'true' ; then @@ -8433,8 +8567,8 @@ printf %s "checking whether C compiler accepts -flto=thin... " >&6; } if test ${ax_cv_check_cflags___flto_thin+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -flto=thin" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -8451,19 +8585,22 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___flto_thin=yes -else $as_nop - ax_cv_check_cflags___flto_thin=no +else case e in #( + e) ax_cv_check_cflags___flto_thin=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___flto_thin" >&5 printf "%s\n" "$ax_cv_check_cflags___flto_thin" >&6; } if test "x$ax_cv_check_cflags___flto_thin" = xyes then : LDFLAGS_NOLTO="-flto=thin" -else $as_nop - LDFLAGS_NOLTO="-flto" +else case e in #( + e) LDFLAGS_NOLTO="-flto" ;; +esac fi @@ -8475,8 +8612,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_LLVM_AR+y} then : printf %s "(cached) " >&6 -else $as_nop - case $LLVM_AR in +else case e in #( + e) case $LLVM_AR in [\\/]* | ?:[\\/]*) ac_cv_path_LLVM_AR="$LLVM_AR" # Let the user override the test with a path. ;; @@ -8501,6 +8638,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi LLVM_AR=$ac_cv_path_LLVM_AR @@ -8523,8 +8661,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_LLVM_AR+y} then : printf %s "(cached) " >&6 -else $as_nop - case $ac_pt_LLVM_AR in +else case e in #( + e) case $ac_pt_LLVM_AR in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_LLVM_AR="$ac_pt_LLVM_AR" # Let the user override the test with a path. ;; @@ -8549,6 +8687,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi ac_pt_LLVM_AR=$ac_cv_path_ac_pt_LLVM_AR @@ -8613,8 +8752,8 @@ printf %s "checking whether C compiler accepts -flto=thin... " >&6; } if test ${ax_cv_check_cflags___flto_thin+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -flto=thin" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -8631,11 +8770,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___flto_thin=yes -else $as_nop - ax_cv_check_cflags___flto_thin=no +else case e in #( + e) ax_cv_check_cflags___flto_thin=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___flto_thin" >&5 printf "%s\n" "$ax_cv_check_cflags___flto_thin" >&6; } @@ -8645,12 +8786,13 @@ then : LTOFLAGS="-flto=thin -Wl,-export_dynamic -Wl,-object_path_lto,\"\$@\".lto" LTOCFLAGS="-flto=thin" -else $as_nop - +else case e in #( + e) LTOFLAGS="-flto -Wl,-export_dynamic -Wl,-object_path_lto,\"\$@\".lto" LTOCFLAGS="-flto" - + ;; +esac fi else @@ -8667,8 +8809,8 @@ printf %s "checking whether C compiler accepts -flto=thin... " >&6; } if test ${ax_cv_check_cflags___flto_thin+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -flto=thin" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -8685,19 +8827,22 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___flto_thin=yes -else $as_nop - ax_cv_check_cflags___flto_thin=no +else case e in #( + e) ax_cv_check_cflags___flto_thin=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___flto_thin" >&5 printf "%s\n" "$ax_cv_check_cflags___flto_thin" >&6; } if test "x$ax_cv_check_cflags___flto_thin" = xyes then : LTOFLAGS="-flto=thin" -else $as_nop - LTOFLAGS="-flto" +else case e in #( + e) LTOFLAGS="-flto" ;; +esac fi else @@ -8757,8 +8902,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_LLVM_PROFDATA+y} then : printf %s "(cached) " >&6 -else $as_nop - case $LLVM_PROFDATA in +else case e in #( + e) case $LLVM_PROFDATA in [\\/]* | ?:[\\/]*) ac_cv_path_LLVM_PROFDATA="$LLVM_PROFDATA" # Let the user override the test with a path. ;; @@ -8783,6 +8928,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi LLVM_PROFDATA=$ac_cv_path_LLVM_PROFDATA @@ -8805,8 +8951,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_LLVM_PROFDATA+y} then : printf %s "(cached) " >&6 -else $as_nop - case $ac_pt_LLVM_PROFDATA in +else case e in #( + e) case $ac_pt_LLVM_PROFDATA in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_LLVM_PROFDATA="$ac_pt_LLVM_PROFDATA" # Let the user override the test with a path. ;; @@ -8831,6 +8977,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi ac_pt_LLVM_PROFDATA=$ac_cv_path_ac_pt_LLVM_PROFDATA @@ -8927,9 +9074,10 @@ else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; }; fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } ;; +esac fi @@ -8946,8 +9094,8 @@ printf %s "checking whether C compiler accepts -fno-reorder-blocks-and-partition if test ${ax_cv_check_cflags___fno_reorder_blocks_and_partition+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -fno-reorder-blocks-and-partition" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -8964,11 +9112,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___fno_reorder_blocks_and_partition=yes -else $as_nop - ax_cv_check_cflags___fno_reorder_blocks_and_partition=no +else case e in #( + e) ax_cv_check_cflags___fno_reorder_blocks_and_partition=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___fno_reorder_blocks_and_partition" >&5 printf "%s\n" "$ax_cv_check_cflags___fno_reorder_blocks_and_partition" >&6; } @@ -8977,8 +9127,9 @@ then : CFLAGS_NODIST="$CFLAGS_NODIST -fno-reorder-blocks-and-partition" -else $as_nop - : +else case e in #( + e) : ;; +esac fi @@ -8998,8 +9149,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_LLVM_BOLT+y} then : printf %s "(cached) " >&6 -else $as_nop - case $LLVM_BOLT in +else case e in #( + e) case $LLVM_BOLT in [\\/]* | ?:[\\/]*) ac_cv_path_LLVM_BOLT="$LLVM_BOLT" # Let the user override the test with a path. ;; @@ -9024,6 +9175,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi LLVM_BOLT=$ac_cv_path_LLVM_BOLT @@ -9046,8 +9198,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_LLVM_BOLT+y} then : printf %s "(cached) " >&6 -else $as_nop - case $ac_pt_LLVM_BOLT in +else case e in #( + e) case $ac_pt_LLVM_BOLT in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_LLVM_BOLT="$ac_pt_LLVM_BOLT" # Let the user override the test with a path. ;; @@ -9072,6 +9224,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi ac_pt_LLVM_BOLT=$ac_cv_path_ac_pt_LLVM_BOLT @@ -9115,8 +9268,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_MERGE_FDATA+y} then : printf %s "(cached) " >&6 -else $as_nop - case $MERGE_FDATA in +else case e in #( + e) case $MERGE_FDATA in [\\/]* | ?:[\\/]*) ac_cv_path_MERGE_FDATA="$MERGE_FDATA" # Let the user override the test with a path. ;; @@ -9141,6 +9294,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi MERGE_FDATA=$ac_cv_path_MERGE_FDATA @@ -9163,8 +9317,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_MERGE_FDATA+y} then : printf %s "(cached) " >&6 -else $as_nop - case $ac_pt_MERGE_FDATA in +else case e in #( + e) case $ac_pt_MERGE_FDATA in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_MERGE_FDATA="$ac_pt_MERGE_FDATA" # Let the user override the test with a path. ;; @@ -9189,6 +9343,7 @@ done IFS=$as_save_IFS ;; +esac ;; esac fi ac_pt_MERGE_FDATA=$ac_cv_path_ac_pt_MERGE_FDATA @@ -9274,8 +9429,8 @@ printf %s "checking if $CC supports -fstrict-overflow and -fno-strict-overflow.. if test ${ac_cv_cc_supports_fstrict_overflow+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -9289,12 +9444,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_cc_supports_fstrict_overflow=yes -else $as_nop - ac_cv_cc_supports_fstrict_overflow=no - +else case e in #( + e) ac_cv_cc_supports_fstrict_overflow=no + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cc_supports_fstrict_overflow" >&5 printf "%s\n" "$ac_cv_cc_supports_fstrict_overflow" >&6; } @@ -9304,9 +9461,10 @@ if test "x$ac_cv_cc_supports_fstrict_overflow" = xyes then : STRICT_OVERFLOW_CFLAGS="-fstrict-overflow" NO_STRICT_OVERFLOW_CFLAGS="-fno-strict-overflow" -else $as_nop - STRICT_OVERFLOW_CFLAGS="" - NO_STRICT_OVERFLOW_CFLAGS="" +else case e in #( + e) STRICT_OVERFLOW_CFLAGS="" + NO_STRICT_OVERFLOW_CFLAGS="" ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-strict-overflow" >&5 @@ -9322,9 +9480,10 @@ then : printf "%s\n" "$as_me: WARNING: --with-strict-overflow=yes requires a compiler that supports -fstrict-overflow" >&2;} fi -else $as_nop - with_strict_overflow=no - +else case e in #( + e) with_strict_overflow=no + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_strict_overflow" >&5 @@ -9338,8 +9497,8 @@ printf %s "checking if $CC supports -Og optimization level... " >&6; } if test ${ac_cv_cc_supports_og+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -9357,13 +9516,15 @@ then : ac_cv_cc_supports_og=yes -else $as_nop - +else case e in #( + e) ac_cv_cc_supports_og=no - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cc_supports_og" >&5 printf "%s\n" "$ac_cv_cc_supports_og" >&6; } @@ -9429,8 +9590,9 @@ case $ac_sys_system in #( if test "x$Py_DEBUG" = xyes then : wasm_debug=yes -else $as_nop - wasm_debug=no +else case e in #( + e) wasm_debug=no ;; +esac fi as_fn_append LINKFORSHARED " -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=20971520" @@ -9466,10 +9628,11 @@ then : as_fn_append LDFLAGS_NODIST " -sASSERTIONS" as_fn_append LINKFORSHARED " $WASM_LINKFORSHARED_DEBUG" -else $as_nop - +else case e in #( + e) as_fn_append LINKFORSHARED " -O2 -g0" - + ;; +esac fi ;; #( WASI) : @@ -9542,8 +9705,9 @@ UNIVERSAL_ARCH_FLAGS= if test "x$with_strict_overflow" = xyes then : BASECFLAGS="$BASECFLAGS $STRICT_OVERFLOW_CFLAGS" -else $as_nop - BASECFLAGS="$BASECFLAGS $NO_STRICT_OVERFLOW_CFLAGS" +else case e in #( + e) BASECFLAGS="$BASECFLAGS $NO_STRICT_OVERFLOW_CFLAGS" ;; +esac fi # Enable flags that warn and protect for potential security vulnerabilities. @@ -9557,11 +9721,13 @@ then : enableval=$enable_safety; if test "x$disable_safety" = xyes then : enable_safety=no -else $as_nop - enable_safety=yes +else case e in #( + e) enable_safety=yes ;; +esac fi -else $as_nop - enable_safety=no +else case e in #( + e) enable_safety=no ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_safety" >&5 @@ -9574,8 +9740,8 @@ printf %s "checking whether C compiler accepts -fstack-protector-strong... " >&6 if test ${ax_cv_check_cflags__Werror__fstack_protector_strong+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror -fstack-protector-strong" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -9592,20 +9758,23 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags__Werror__fstack_protector_strong=yes -else $as_nop - ax_cv_check_cflags__Werror__fstack_protector_strong=no +else case e in #( + e) ax_cv_check_cflags__Werror__fstack_protector_strong=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__fstack_protector_strong" >&5 printf "%s\n" "$ax_cv_check_cflags__Werror__fstack_protector_strong" >&6; } if test "x$ax_cv_check_cflags__Werror__fstack_protector_strong" = xyes then : CFLAGS_NODIST="$CFLAGS_NODIST -fstack-protector-strong" -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: -fstack-protector-strong not supported" >&5 -printf "%s\n" "$as_me: WARNING: -fstack-protector-strong not supported" >&2;} +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: -fstack-protector-strong not supported" >&5 +printf "%s\n" "$as_me: WARNING: -fstack-protector-strong not supported" >&2;} ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wtrampolines" >&5 @@ -9613,8 +9782,8 @@ printf %s "checking whether C compiler accepts -Wtrampolines... " >&6; } if test ${ax_cv_check_cflags__Werror__Wtrampolines+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror -Wtrampolines" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -9631,20 +9800,23 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags__Werror__Wtrampolines=yes -else $as_nop - ax_cv_check_cflags__Werror__Wtrampolines=no +else case e in #( + e) ax_cv_check_cflags__Werror__Wtrampolines=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__Wtrampolines" >&5 printf "%s\n" "$ax_cv_check_cflags__Werror__Wtrampolines" >&6; } if test "x$ax_cv_check_cflags__Werror__Wtrampolines" = xyes then : CFLAGS_NODIST="$CFLAGS_NODIST -Wtrampolines" -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: -Wtrampolines not supported" >&5 -printf "%s\n" "$as_me: WARNING: -Wtrampolines not supported" >&2;} +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: -Wtrampolines not supported" >&5 +printf "%s\n" "$as_me: WARNING: -Wtrampolines not supported" >&2;} ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wimplicit-fallthrough" >&5 @@ -9652,8 +9824,8 @@ printf %s "checking whether C compiler accepts -Wimplicit-fallthrough... " >&6; if test ${ax_cv_check_cflags__Werror__Wimplicit_fallthrough+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror -Wimplicit-fallthrough" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -9670,20 +9842,23 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags__Werror__Wimplicit_fallthrough=yes -else $as_nop - ax_cv_check_cflags__Werror__Wimplicit_fallthrough=no +else case e in #( + e) ax_cv_check_cflags__Werror__Wimplicit_fallthrough=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__Wimplicit_fallthrough" >&5 printf "%s\n" "$ax_cv_check_cflags__Werror__Wimplicit_fallthrough" >&6; } if test "x$ax_cv_check_cflags__Werror__Wimplicit_fallthrough" = xyes then : CFLAGS_NODIST="$CFLAGS_NODIST -Wimplicit-fallthrough" -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: -Wimplicit-fallthrough not supported" >&5 -printf "%s\n" "$as_me: WARNING: -Wimplicit-fallthrough not supported" >&2;} +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: -Wimplicit-fallthrough not supported" >&5 +printf "%s\n" "$as_me: WARNING: -Wimplicit-fallthrough not supported" >&2;} ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Werror=format-security" >&5 @@ -9691,8 +9866,8 @@ printf %s "checking whether C compiler accepts -Werror=format-security... " >&6; if test ${ax_cv_check_cflags__Werror__Werror_format_security+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror -Werror=format-security" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -9709,20 +9884,23 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags__Werror__Werror_format_security=yes -else $as_nop - ax_cv_check_cflags__Werror__Werror_format_security=no +else case e in #( + e) ax_cv_check_cflags__Werror__Werror_format_security=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__Werror_format_security" >&5 printf "%s\n" "$ax_cv_check_cflags__Werror__Werror_format_security" >&6; } if test "x$ax_cv_check_cflags__Werror__Werror_format_security" = xyes then : CFLAGS_NODIST="$CFLAGS_NODIST -Werror=format-security" -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: -Werror=format-security not supported" >&5 -printf "%s\n" "$as_me: WARNING: -Werror=format-security not supported" >&2;} +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: -Werror=format-security not supported" >&5 +printf "%s\n" "$as_me: WARNING: -Werror=format-security not supported" >&2;} ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wbidi-chars=any" >&5 @@ -9730,8 +9908,8 @@ printf %s "checking whether C compiler accepts -Wbidi-chars=any... " >&6; } if test ${ax_cv_check_cflags__Werror__Wbidi_chars_any+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror -Wbidi-chars=any" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -9748,20 +9926,23 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags__Werror__Wbidi_chars_any=yes -else $as_nop - ax_cv_check_cflags__Werror__Wbidi_chars_any=no +else case e in #( + e) ax_cv_check_cflags__Werror__Wbidi_chars_any=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__Wbidi_chars_any" >&5 printf "%s\n" "$ax_cv_check_cflags__Werror__Wbidi_chars_any" >&6; } if test "x$ax_cv_check_cflags__Werror__Wbidi_chars_any" = xyes then : CFLAGS_NODIST="$CFLAGS_NODIST -Wbidi-chars=any" -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: -Wbidi-chars=any not supported" >&5 -printf "%s\n" "$as_me: WARNING: -Wbidi-chars=any not supported" >&2;} +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: -Wbidi-chars=any not supported" >&5 +printf "%s\n" "$as_me: WARNING: -Wbidi-chars=any not supported" >&2;} ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wall" >&5 @@ -9769,8 +9950,8 @@ printf %s "checking whether C compiler accepts -Wall... " >&6; } if test ${ax_cv_check_cflags__Werror__Wall+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror -Wall" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -9787,20 +9968,23 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags__Werror__Wall=yes -else $as_nop - ax_cv_check_cflags__Werror__Wall=no +else case e in #( + e) ax_cv_check_cflags__Werror__Wall=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__Wall" >&5 printf "%s\n" "$ax_cv_check_cflags__Werror__Wall" >&6; } if test "x$ax_cv_check_cflags__Werror__Wall" = xyes then : CFLAGS_NODIST="$CFLAGS_NODIST -Wall" -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: -Wall not supported" >&5 -printf "%s\n" "$as_me: WARNING: -Wall not supported" >&2;} +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: -Wall not supported" >&5 +printf "%s\n" "$as_me: WARNING: -Wall not supported" >&2;} ;; +esac fi fi @@ -9813,11 +9997,13 @@ then : enableval=$enable_slower_safety; if test "x$disable_slower_safety" = xyes then : enable_slower_safety=no -else $as_nop - enable_slower_safety=yes +else case e in #( + e) enable_slower_safety=yes ;; +esac fi -else $as_nop - enable_slower_safety=no +else case e in #( + e) enable_slower_safety=no ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_slower_safety" >&5 @@ -9830,8 +10016,8 @@ printf %s "checking whether C compiler accepts -D_FORTIFY_SOURCE=3... " >&6; } if test ${ax_cv_check_cflags__Werror__D_FORTIFY_SOURCE_3+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror -D_FORTIFY_SOURCE=3" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -9848,20 +10034,23 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags__Werror__D_FORTIFY_SOURCE_3=yes -else $as_nop - ax_cv_check_cflags__Werror__D_FORTIFY_SOURCE_3=no +else case e in #( + e) ax_cv_check_cflags__Werror__D_FORTIFY_SOURCE_3=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__D_FORTIFY_SOURCE_3" >&5 printf "%s\n" "$ax_cv_check_cflags__Werror__D_FORTIFY_SOURCE_3" >&6; } if test "x$ax_cv_check_cflags__Werror__D_FORTIFY_SOURCE_3" = xyes then : CFLAGS_NODIST="$CFLAGS_NODIST -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3" -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: -D_FORTIFY_SOURCE=3 not supported" >&5 -printf "%s\n" "$as_me: WARNING: -D_FORTIFY_SOURCE=3 not supported" >&2;} +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: -D_FORTIFY_SOURCE=3 not supported" >&5 +printf "%s\n" "$as_me: WARNING: -D_FORTIFY_SOURCE=3 not supported" >&2;} ;; +esac fi fi @@ -9878,8 +10067,8 @@ printf %s "checking if we can add -Wextra... " >&6; } if test ${ac_cv_enable_extra_warning+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) py_cflags=$CFLAGS as_fn_append CFLAGS " -Wextra -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -9896,12 +10085,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_enable_extra_warning=yes -else $as_nop - ac_cv_enable_extra_warning=no +else case e in #( + e) ac_cv_enable_extra_warning=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$py_cflags - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_enable_extra_warning" >&5 printf "%s\n" "$ac_cv_enable_extra_warning" >&6; } @@ -9924,8 +10115,8 @@ printf %s "checking whether $CC accepts and needs -fno-strict-aliasing... " >&6; if test ${ac_cv_no_strict_aliasing+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -9961,19 +10152,22 @@ then : ac_cv_no_strict_aliasing=no -else $as_nop - +else case e in #( + e) ac_cv_no_strict_aliasing=yes - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -else $as_nop - +else case e in #( + e) ac_cv_no_strict_aliasing=no - + ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_no_strict_aliasing" >&5 printf "%s\n" "$ac_cv_no_strict_aliasing" >&6; } @@ -9996,8 +10190,8 @@ printf %s "checking if we can disable $CC unused-result warning... " >&6; } if test ${ac_cv_disable_unused_result_warning+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) py_cflags=$CFLAGS as_fn_append CFLAGS " -Wunused-result -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -10014,12 +10208,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_disable_unused_result_warning=yes -else $as_nop - ac_cv_disable_unused_result_warning=no +else case e in #( + e) ac_cv_disable_unused_result_warning=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$py_cflags - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_disable_unused_result_warning" >&5 printf "%s\n" "$ac_cv_disable_unused_result_warning" >&6; } @@ -10041,8 +10237,8 @@ printf %s "checking if we can disable $CC unused-parameter warning... " >&6; } if test ${ac_cv_disable_unused_parameter_warning+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) py_cflags=$CFLAGS as_fn_append CFLAGS " -Wunused-parameter -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -10059,12 +10255,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_disable_unused_parameter_warning=yes -else $as_nop - ac_cv_disable_unused_parameter_warning=no +else case e in #( + e) ac_cv_disable_unused_parameter_warning=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$py_cflags - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_disable_unused_parameter_warning" >&5 printf "%s\n" "$ac_cv_disable_unused_parameter_warning" >&6; } @@ -10082,8 +10280,8 @@ printf %s "checking if we can disable $CC int-conversion warning... " >&6; } if test ${ac_cv_disable_int_conversion_warning+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) py_cflags=$CFLAGS as_fn_append CFLAGS " -Wint-conversion -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -10100,12 +10298,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_disable_int_conversion_warning=yes -else $as_nop - ac_cv_disable_int_conversion_warning=no +else case e in #( + e) ac_cv_disable_int_conversion_warning=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$py_cflags - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_disable_int_conversion_warning" >&5 printf "%s\n" "$ac_cv_disable_int_conversion_warning" >&6; } @@ -10123,8 +10323,8 @@ printf %s "checking if we can disable $CC missing-field-initializers warning... if test ${ac_cv_disable_missing_field_initializers_warning+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) py_cflags=$CFLAGS as_fn_append CFLAGS " -Wmissing-field-initializers -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -10141,12 +10341,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_disable_missing_field_initializers_warning=yes -else $as_nop - ac_cv_disable_missing_field_initializers_warning=no +else case e in #( + e) ac_cv_disable_missing_field_initializers_warning=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$py_cflags - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_disable_missing_field_initializers_warning" >&5 printf "%s\n" "$ac_cv_disable_missing_field_initializers_warning" >&6; } @@ -10164,8 +10366,8 @@ printf %s "checking if we can enable $CC sign-compare warning... " >&6; } if test ${ac_cv_enable_sign_compare_warning+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) py_cflags=$CFLAGS as_fn_append CFLAGS " -Wsign-compare -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -10182,12 +10384,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_enable_sign_compare_warning=yes -else $as_nop - ac_cv_enable_sign_compare_warning=no +else case e in #( + e) ac_cv_enable_sign_compare_warning=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$py_cflags - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_enable_sign_compare_warning" >&5 printf "%s\n" "$ac_cv_enable_sign_compare_warning" >&6; } @@ -10205,8 +10409,8 @@ printf %s "checking if we can enable $CC unreachable-code warning... " >&6; } if test ${ac_cv_enable_unreachable_code_warning+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) py_cflags=$CFLAGS as_fn_append CFLAGS " -Wunreachable-code -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -10223,12 +10427,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_enable_unreachable_code_warning=yes -else $as_nop - ac_cv_enable_unreachable_code_warning=no +else case e in #( + e) ac_cv_enable_unreachable_code_warning=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$py_cflags - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_enable_unreachable_code_warning" >&5 printf "%s\n" "$ac_cv_enable_unreachable_code_warning" >&6; } @@ -10257,8 +10463,8 @@ printf %s "checking if we can enable $CC strict-prototypes warning... " >&6; } if test ${ac_cv_enable_strict_prototypes_warning+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) py_cflags=$CFLAGS as_fn_append CFLAGS " -Wstrict-prototypes -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -10275,12 +10481,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_enable_strict_prototypes_warning=yes -else $as_nop - ac_cv_enable_strict_prototypes_warning=no +else case e in #( + e) ac_cv_enable_strict_prototypes_warning=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$py_cflags - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_enable_strict_prototypes_warning" >&5 printf "%s\n" "$ac_cv_enable_strict_prototypes_warning" >&6; } @@ -10298,8 +10506,8 @@ printf %s "checking if we can make implicit function declaration an error in $CC if test ${ac_cv_enable_implicit_function_declaration_error+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -10317,12 +10525,14 @@ then : ac_cv_enable_implicit_function_declaration_error=yes -else $as_nop - +else case e in #( + e) ac_cv_enable_implicit_function_declaration_error=no - + ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_enable_implicit_function_declaration_error" >&5 printf "%s\n" "$ac_cv_enable_implicit_function_declaration_error" >&6; } @@ -10340,8 +10550,8 @@ printf %s "checking if we can use visibility in $CC... " >&6; } if test ${ac_cv_enable_visibility+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -10359,12 +10569,14 @@ then : ac_cv_enable_visibility=yes -else $as_nop - +else case e in #( + e) ac_cv_enable_visibility=no - + ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_enable_visibility" >&5 printf "%s\n" "$ac_cv_enable_visibility" >&6; } @@ -10413,8 +10625,8 @@ printf %s "checking whether C compiler accepts -Wunguarded-availability... " >&6 if test ${ax_cv_check_cflags__Werror__Wunguarded_availability+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror -Wunguarded-availability" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -10431,19 +10643,22 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags__Werror__Wunguarded_availability=yes -else $as_nop - ax_cv_check_cflags__Werror__Wunguarded_availability=no +else case e in #( + e) ax_cv_check_cflags__Werror__Wunguarded_availability=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__Wunguarded_availability" >&5 printf "%s\n" "$ax_cv_check_cflags__Werror__Wunguarded_availability" >&6; } if test "x$ax_cv_check_cflags__Werror__Wunguarded_availability" = xyes then : as_fn_append CFLAGS_NODIST " -Werror=unguarded-availability" -else $as_nop - : +else case e in #( + e) : ;; +esac fi @@ -10579,11 +10794,12 @@ if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } as_fn_error $? "check config.log and use the '--with-universal-archs' option" "$LINENO" 5 - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -10592,8 +10808,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \ ;; esac -else $as_nop - +else case e in #( + e) case $ac_sys_system in OpenUNIX*|UnixWare*) BASECFLAGS="$BASECFLAGS -K pentium,host,inline,loop_unroll,alloca " @@ -10602,7 +10818,8 @@ else $as_nop BASECFLAGS="$BASECFLAGS -belf -Ki486 -DSCO5" ;; esac - + ;; +esac fi case "$ac_cv_cc_name" in @@ -10639,12 +10856,12 @@ printf %s "checking whether pthreads are available without options... " >&6; } if test ${ac_cv_pthread_is_default+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "$cross_compiling" = yes +else case e in #( + e) if test "$cross_compiling" = yes then : ac_cv_pthread_is_default=no -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -10668,14 +10885,17 @@ then : ac_cv_kthread=no ac_cv_pthread=no -else $as_nop - ac_cv_pthread_is_default=no +else case e in #( + e) ac_cv_pthread_is_default=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_pthread_is_default" >&5 printf "%s\n" "$ac_cv_pthread_is_default" >&6; } @@ -10695,14 +10915,14 @@ printf %s "checking whether $CC accepts -Kpthread... " >&6; } if test ${ac_cv_kpthread+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_save_cc="$CC" +else case e in #( + e) ac_save_cc="$CC" CC="$CC -Kpthread" if test "$cross_compiling" = yes then : ac_cv_kpthread=no -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -10722,14 +10942,17 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_kpthread=yes -else $as_nop - ac_cv_kpthread=no +else case e in #( + e) ac_cv_kpthread=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi -CC="$ac_save_cc" +CC="$ac_save_cc" ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_kpthread" >&5 printf "%s\n" "$ac_cv_kpthread" >&6; } @@ -10747,14 +10970,14 @@ printf %s "checking whether $CC accepts -Kthread... " >&6; } if test ${ac_cv_kthread+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_save_cc="$CC" +else case e in #( + e) ac_save_cc="$CC" CC="$CC -Kthread" if test "$cross_compiling" = yes then : ac_cv_kthread=no -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -10774,14 +10997,17 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_kthread=yes -else $as_nop - ac_cv_kthread=no +else case e in #( + e) ac_cv_kthread=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi -CC="$ac_save_cc" +CC="$ac_save_cc" ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_kthread" >&5 printf "%s\n" "$ac_cv_kthread" >&6; } @@ -10799,14 +11025,14 @@ printf %s "checking whether $CC accepts -pthread... " >&6; } if test ${ac_cv_pthread+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_save_cc="$CC" +else case e in #( + e) ac_save_cc="$CC" CC="$CC -pthread" if test "$cross_compiling" = yes then : ac_cv_pthread=no -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -10826,14 +11052,17 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_pthread=yes -else $as_nop - ac_cv_pthread=no +else case e in #( + e) ac_cv_pthread=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi -CC="$ac_save_cc" +CC="$ac_save_cc" ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_pthread" >&5 printf "%s\n" "$ac_cv_pthread" >&6; } @@ -10848,8 +11077,8 @@ printf %s "checking whether $CXX also accepts flags for thread support... " >&6; if test ${ac_cv_cxx_thread+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_save_cxx="$CXX" +else case e in #( + e) ac_save_cxx="$CXX" if test "$ac_cv_kpthread" = "yes" then @@ -10880,7 +11109,8 @@ then fi rm -fr conftest* fi -CXX="$ac_save_cxx" +CXX="$ac_save_cxx" ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_thread" >&5 printf "%s\n" "$ac_cv_cxx_thread" >&6; } @@ -11407,14 +11637,14 @@ fi ac_header_dirent=no for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do - as_ac_Header=`printf "%s\n" "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` + as_ac_Header=`printf "%s\n" "ac_cv_header_dirent_$ac_hdr" | sed "$as_sed_sh"` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5 printf %s "checking for $ac_hdr that defines DIR... " >&6; } if eval test \${$as_ac_Header+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include <$ac_hdr> @@ -11431,10 +11661,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$as_ac_Header=yes" -else $as_nop - eval "$as_ac_Header=no" +else case e in #( + e) eval "$as_ac_Header=no" ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi eval ac_res=\$$as_ac_Header { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 @@ -11442,7 +11674,7 @@ printf "%s\n" "$ac_res" >&6; } if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF -#define `printf "%s\n" "HAVE_$ac_hdr" | $as_tr_cpp` 1 +#define `printf "%s\n" "HAVE_$ac_hdr" | sed "$as_sed_cpp"` 1 _ACEOF ac_header_dirent=$ac_hdr; break @@ -11456,15 +11688,21 @@ printf %s "checking for library containing opendir... " >&6; } if test ${ac_cv_search_opendir+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_func_search_save_LIBS=$LIBS +else case e in #( + e) ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char opendir (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char opendir (void); int main (void) { @@ -11495,11 +11733,13 @@ done if test ${ac_cv_search_opendir+y} then : -else $as_nop - ac_cv_search_opendir=no +else case e in #( + e) ac_cv_search_opendir=no ;; +esac fi rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS +LIBS=$ac_func_search_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 printf "%s\n" "$ac_cv_search_opendir" >&6; } @@ -11516,15 +11756,21 @@ printf %s "checking for library containing opendir... " >&6; } if test ${ac_cv_search_opendir+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_func_search_save_LIBS=$LIBS +else case e in #( + e) ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char opendir (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char opendir (void); int main (void) { @@ -11555,11 +11801,13 @@ done if test ${ac_cv_search_opendir+y} then : -else $as_nop - ac_cv_search_opendir=no +else case e in #( + e) ac_cv_search_opendir=no ;; +esac fi rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS +LIBS=$ac_func_search_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 printf "%s\n" "$ac_cv_search_opendir" >&6; } @@ -11752,10 +12000,11 @@ then : printf "%s\n" "#define HAVE_CLOCK_T 1" >>confdefs.h -else $as_nop - +else case e in #( + e) printf "%s\n" "#define clock_t long" >>confdefs.h - + ;; +esac fi @@ -11764,8 +12013,8 @@ printf %s "checking for makedev... " >&6; } if test ${ac_cv_func_makedev+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -11790,12 +12039,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_func_makedev=yes -else $as_nop - ac_cv_func_makedev=no +else case e in #( + e) ac_cv_func_makedev=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_makedev" >&5 printf "%s\n" "$ac_cv_func_makedev" >&6; } @@ -11815,8 +12066,8 @@ printf %s "checking for le64toh... " >&6; } if test ${ac_cv_func_le64toh+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -11839,12 +12090,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_func_le64toh=yes -else $as_nop - ac_cv_func_le64toh=no +else case e in #( + e) ac_cv_func_le64toh=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_le64toh" >&5 printf "%s\n" "$ac_cv_func_le64toh" >&6; } @@ -11894,20 +12147,22 @@ ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default" if test "x$ac_cv_type_mode_t" = xyes then : -else $as_nop - +else case e in #( + e) printf "%s\n" "#define mode_t int" >>confdefs.h - + ;; +esac fi ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" if test "x$ac_cv_type_off_t" = xyes then : -else $as_nop - +else case e in #( + e) printf "%s\n" "#define off_t long int" >>confdefs.h - + ;; +esac fi @@ -11916,8 +12171,8 @@ fi if test "x$ac_cv_type_pid_t" = xyes then : -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if defined _WIN64 && !defined __CYGWIN__ @@ -11936,14 +12191,16 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_pid_type='int' -else $as_nop - ac_pid_type='__int64' +else case e in #( + e) ac_pid_type='__int64' ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf "%s\n" "#define pid_t $ac_pid_type" >>confdefs.h - + ;; +esac fi @@ -11954,42 +12211,33 @@ ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" if test "x$ac_cv_type_size_t" = xyes then : -else $as_nop - +else case e in #( + e) printf "%s\n" "#define size_t unsigned int" >>confdefs.h - + ;; +esac fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5 -printf %s "checking for uid_t in sys/types.h... " >&6; } -if test ${ac_cv_type_uid_t+y} +ac_fn_c_check_type "$LINENO" "uid_t" "ac_cv_type_uid_t" "$ac_includes_default" +if test "x$ac_cv_type_uid_t" = xyes then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "uid_t" >/dev/null 2>&1 -then : - ac_cv_type_uid_t=yes -else $as_nop - ac_cv_type_uid_t=no +else case e in #( + e) +printf "%s\n" "#define uid_t int" >>confdefs.h + ;; +esac fi -rm -rf conftest* - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5 -printf "%s\n" "$ac_cv_type_uid_t" >&6; } -if test $ac_cv_type_uid_t = no; then - -printf "%s\n" "#define uid_t int" >>confdefs.h +ac_fn_c_check_type "$LINENO" "gid_t" "ac_cv_type_gid_t" "$ac_includes_default" +if test "x$ac_cv_type_gid_t" = xyes +then : +else case e in #( + e) printf "%s\n" "#define gid_t int" >>confdefs.h - + ;; +esac fi @@ -12018,28 +12266,30 @@ fi # ANSI C requires sizeof(char) == 1, so no need to check it # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of int" >&5 printf %s "checking size of int... " >&6; } if test ${ac_cv_sizeof_int+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int" "$ac_includes_default" +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int" "$ac_includes_default" then : -else $as_nop - if test "$ac_cv_type_int" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_int" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (int) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_int=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5 printf "%s\n" "$ac_cv_sizeof_int" >&6; } @@ -12051,28 +12301,30 @@ printf "%s\n" "#define SIZEOF_INT $ac_cv_sizeof_int" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of long" >&5 printf %s "checking size of long... " >&6; } if test ${ac_cv_sizeof_long+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default" +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default" then : -else $as_nop - if test "$ac_cv_type_long" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_long" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (long) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_long=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5 printf "%s\n" "$ac_cv_sizeof_long" >&6; } @@ -12089,22 +12341,24 @@ printf %s "checking alignment of long... " >&6; } if test ${ac_cv_alignof_long+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) offsetof (ac__type_alignof_, y)" "ac_cv_alignof_long" "$ac_includes_default +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) offsetof (ac__type_alignof_, y)" "ac_cv_alignof_long" "$ac_includes_default typedef struct { char x; long y; } ac__type_alignof_;" then : -else $as_nop - if test "$ac_cv_type_long" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_long" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute alignment of long -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_alignof_long=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_alignof_long" >&5 printf "%s\n" "$ac_cv_alignof_long" >&6; } @@ -12116,28 +12370,30 @@ printf "%s\n" "#define ALIGNOF_LONG $ac_cv_alignof_long" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of long long" >&5 printf %s "checking size of long long... " >&6; } if test ${ac_cv_sizeof_long_long+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long" "$ac_includes_default" +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long" "$ac_includes_default" then : -else $as_nop - if test "$ac_cv_type_long_long" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_long_long" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (long long) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_long_long=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long" >&5 printf "%s\n" "$ac_cv_sizeof_long_long" >&6; } @@ -12149,28 +12405,30 @@ printf "%s\n" "#define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5 printf %s "checking size of void *... " >&6; } if test ${ac_cv_sizeof_void_p+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p" "$ac_includes_default" +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p" "$ac_includes_default" then : -else $as_nop - if test "$ac_cv_type_void_p" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_void_p" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (void *) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_void_p=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5 printf "%s\n" "$ac_cv_sizeof_void_p" >&6; } @@ -12182,28 +12440,30 @@ printf "%s\n" "#define SIZEOF_VOID_P $ac_cv_sizeof_void_p" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of short" >&5 printf %s "checking size of short... " >&6; } if test ${ac_cv_sizeof_short+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (short))" "ac_cv_sizeof_short" "$ac_includes_default" +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (short))" "ac_cv_sizeof_short" "$ac_includes_default" then : -else $as_nop - if test "$ac_cv_type_short" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_short" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (short) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_short=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_short" >&5 printf "%s\n" "$ac_cv_sizeof_short" >&6; } @@ -12215,28 +12475,30 @@ printf "%s\n" "#define SIZEOF_SHORT $ac_cv_sizeof_short" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of float" >&5 printf %s "checking size of float... " >&6; } if test ${ac_cv_sizeof_float+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (float))" "ac_cv_sizeof_float" "$ac_includes_default" +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (float))" "ac_cv_sizeof_float" "$ac_includes_default" then : -else $as_nop - if test "$ac_cv_type_float" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_float" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (float) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_float=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_float" >&5 printf "%s\n" "$ac_cv_sizeof_float" >&6; } @@ -12248,28 +12510,30 @@ printf "%s\n" "#define SIZEOF_FLOAT $ac_cv_sizeof_float" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of double" >&5 printf %s "checking size of double... " >&6; } if test ${ac_cv_sizeof_double+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (double))" "ac_cv_sizeof_double" "$ac_includes_default" +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (double))" "ac_cv_sizeof_double" "$ac_includes_default" then : -else $as_nop - if test "$ac_cv_type_double" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_double" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (double) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_double=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_double" >&5 printf "%s\n" "$ac_cv_sizeof_double" >&6; } @@ -12281,28 +12545,30 @@ printf "%s\n" "#define SIZEOF_DOUBLE $ac_cv_sizeof_double" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of fpos_t" >&5 printf %s "checking size of fpos_t... " >&6; } if test ${ac_cv_sizeof_fpos_t+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (fpos_t))" "ac_cv_sizeof_fpos_t" "$ac_includes_default" +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (fpos_t))" "ac_cv_sizeof_fpos_t" "$ac_includes_default" then : -else $as_nop - if test "$ac_cv_type_fpos_t" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_fpos_t" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (fpos_t) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_fpos_t=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_fpos_t" >&5 printf "%s\n" "$ac_cv_sizeof_fpos_t" >&6; } @@ -12314,28 +12580,30 @@ printf "%s\n" "#define SIZEOF_FPOS_T $ac_cv_sizeof_fpos_t" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of size_t" >&5 printf %s "checking size of size_t... " >&6; } if test ${ac_cv_sizeof_size_t+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (size_t))" "ac_cv_sizeof_size_t" "$ac_includes_default" +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (size_t))" "ac_cv_sizeof_size_t" "$ac_includes_default" then : -else $as_nop - if test "$ac_cv_type_size_t" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_size_t" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (size_t) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_size_t=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_size_t" >&5 printf "%s\n" "$ac_cv_sizeof_size_t" >&6; } @@ -12352,22 +12620,24 @@ printf %s "checking alignment of size_t... " >&6; } if test ${ac_cv_alignof_size_t+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) offsetof (ac__type_alignof_, y)" "ac_cv_alignof_size_t" "$ac_includes_default +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) offsetof (ac__type_alignof_, y)" "ac_cv_alignof_size_t" "$ac_includes_default typedef struct { char x; size_t y; } ac__type_alignof_;" then : -else $as_nop - if test "$ac_cv_type_size_t" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_size_t" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute alignment of size_t -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_alignof_size_t=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_alignof_size_t" >&5 printf "%s\n" "$ac_cv_alignof_size_t" >&6; } @@ -12379,28 +12649,30 @@ printf "%s\n" "#define ALIGNOF_SIZE_T $ac_cv_alignof_size_t" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of pid_t" >&5 printf %s "checking size of pid_t... " >&6; } if test ${ac_cv_sizeof_pid_t+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (pid_t))" "ac_cv_sizeof_pid_t" "$ac_includes_default" +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (pid_t))" "ac_cv_sizeof_pid_t" "$ac_includes_default" then : -else $as_nop - if test "$ac_cv_type_pid_t" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_pid_t" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (pid_t) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_pid_t=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_pid_t" >&5 printf "%s\n" "$ac_cv_sizeof_pid_t" >&6; } @@ -12412,28 +12684,30 @@ printf "%s\n" "#define SIZEOF_PID_T $ac_cv_sizeof_pid_t" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of uintptr_t" >&5 printf %s "checking size of uintptr_t... " >&6; } if test ${ac_cv_sizeof_uintptr_t+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (uintptr_t))" "ac_cv_sizeof_uintptr_t" "$ac_includes_default" +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (uintptr_t))" "ac_cv_sizeof_uintptr_t" "$ac_includes_default" then : -else $as_nop - if test "$ac_cv_type_uintptr_t" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_uintptr_t" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (uintptr_t) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_uintptr_t=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_uintptr_t" >&5 printf "%s\n" "$ac_cv_sizeof_uintptr_t" >&6; } @@ -12450,22 +12724,24 @@ printf %s "checking alignment of max_align_t... " >&6; } if test ${ac_cv_alignof_max_align_t+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) offsetof (ac__type_alignof_, y)" "ac_cv_alignof_max_align_t" "$ac_includes_default +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) offsetof (ac__type_alignof_, y)" "ac_cv_alignof_max_align_t" "$ac_includes_default typedef struct { char x; max_align_t y; } ac__type_alignof_;" then : -else $as_nop - if test "$ac_cv_type_max_align_t" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_max_align_t" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute alignment of max_align_t -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_alignof_max_align_t=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_alignof_max_align_t" >&5 printf "%s\n" "$ac_cv_alignof_max_align_t" >&6; } @@ -12482,8 +12758,8 @@ printf %s "checking for long double... " >&6; } if test ${ac_cv_type_long_double+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "$GCC" = yes; then +else case e in #( + e) if test "$GCC" = yes; then ac_cv_type_long_double=yes else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -12506,11 +12782,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_type_long_double=yes -else $as_nop - ac_cv_type_long_double=no +else case e in #( + e) ac_cv_type_long_double=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - fi + fi ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_long_double" >&5 printf "%s\n" "$ac_cv_type_long_double" >&6; } @@ -12522,28 +12800,30 @@ printf "%s\n" "#define HAVE_LONG_DOUBLE 1" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of long double" >&5 printf %s "checking size of long double... " >&6; } if test ${ac_cv_sizeof_long_double+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long double))" "ac_cv_sizeof_long_double" "$ac_includes_default" +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long double))" "ac_cv_sizeof_long_double" "$ac_includes_default" then : -else $as_nop - if test "$ac_cv_type_long_double" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_long_double" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (long double) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_long_double=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_double" >&5 printf "%s\n" "$ac_cv_sizeof_long_double" >&6; } @@ -12556,28 +12836,30 @@ printf "%s\n" "#define SIZEOF_LONG_DOUBLE $ac_cv_sizeof_long_double" >>confdefs. # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of _Bool" >&5 printf %s "checking size of _Bool... " >&6; } if test ${ac_cv_sizeof__Bool+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (_Bool))" "ac_cv_sizeof__Bool" "$ac_includes_default" +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (_Bool))" "ac_cv_sizeof__Bool" "$ac_includes_default" then : -else $as_nop - if test "$ac_cv_type__Bool" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type__Bool" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (_Bool) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof__Bool=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof__Bool" >&5 printf "%s\n" "$ac_cv_sizeof__Bool" >&6; } @@ -12590,15 +12872,15 @@ printf "%s\n" "#define SIZEOF__BOOL $ac_cv_sizeof__Bool" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of off_t" >&5 printf %s "checking size of off_t... " >&6; } if test ${ac_cv_sizeof_off_t+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (off_t))" "ac_cv_sizeof_off_t" " +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (off_t))" "ac_cv_sizeof_off_t" " #ifdef HAVE_SYS_TYPES_H #include #endif @@ -12606,17 +12888,19 @@ else $as_nop " then : -else $as_nop - if test "$ac_cv_type_off_t" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_off_t" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (off_t) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_off_t=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_off_t" >&5 printf "%s\n" "$ac_cv_sizeof_off_t" >&6; } @@ -12651,24 +12935,25 @@ printf "%s\n" "#define HAVE_LARGEFILE_SUPPORT 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of time_t" >&5 printf %s "checking size of time_t... " >&6; } if test ${ac_cv_sizeof_time_t+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (time_t))" "ac_cv_sizeof_time_t" " +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (time_t))" "ac_cv_sizeof_time_t" " #ifdef HAVE_SYS_TYPES_H #include #endif @@ -12679,17 +12964,19 @@ else $as_nop " then : -else $as_nop - if test "$ac_cv_type_time_t" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_time_t" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (time_t) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_time_t=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_time_t" >&5 printf "%s\n" "$ac_cv_sizeof_time_t" >&6; } @@ -12715,8 +13002,8 @@ printf %s "checking for pthread_t... " >&6; } if test ${ac_cv_have_pthread_t+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -12733,11 +13020,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_pthread_t=yes -else $as_nop - ac_cv_have_pthread_t=no +else case e in #( + e) ac_cv_have_pthread_t=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_pthread_t" >&5 printf "%s\n" "$ac_cv_have_pthread_t" >&6; } @@ -12746,15 +13035,15 @@ then : # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of pthread_t" >&5 printf %s "checking size of pthread_t... " >&6; } if test ${ac_cv_sizeof_pthread_t+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (pthread_t))" "ac_cv_sizeof_pthread_t" " +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (pthread_t))" "ac_cv_sizeof_pthread_t" " #ifdef HAVE_PTHREAD_H #include #endif @@ -12762,17 +13051,19 @@ else $as_nop " then : -else $as_nop - if test "$ac_cv_type_pthread_t" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_pthread_t" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (pthread_t) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_pthread_t=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_pthread_t" >&5 printf "%s\n" "$ac_cv_sizeof_pthread_t" >&6; } @@ -12789,29 +13080,31 @@ fi # This checking will be unnecessary after removing deprecated TLS API. # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of pthread_key_t" >&5 printf %s "checking size of pthread_key_t... " >&6; } if test ${ac_cv_sizeof_pthread_key_t+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (pthread_key_t))" "ac_cv_sizeof_pthread_key_t" "#include +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (pthread_key_t))" "ac_cv_sizeof_pthread_key_t" "#include " then : -else $as_nop - if test "$ac_cv_type_pthread_key_t" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_pthread_key_t" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (pthread_key_t) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_pthread_key_t=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_pthread_key_t" >&5 printf "%s\n" "$ac_cv_sizeof_pthread_key_t" >&6; } @@ -12826,8 +13119,8 @@ printf %s "checking whether pthread_key_t is compatible with int... " >&6; } if test ${ac_cv_pthread_key_t_is_arithmetic_type+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) if test "$ac_cv_sizeof_pthread_key_t" -eq "$ac_cv_sizeof_int" ; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -12843,15 +13136,17 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_pthread_key_t_is_arithmetic_type=yes -else $as_nop - ac_cv_pthread_key_t_is_arithmetic_type=no - +else case e in #( + e) ac_cv_pthread_key_t_is_arithmetic_type=no + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext else ac_cv_pthread_key_t_is_arithmetic_type=no fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_pthread_key_t_is_arithmetic_type" >&5 printf "%s\n" "$ac_cv_pthread_key_t_is_arithmetic_type" >&6; } @@ -12910,9 +13205,10 @@ printf "%s\n" "yes" >&6; }; else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; }; DSYMUTIL= fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } ;; +esac fi @@ -12924,8 +13220,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_DSYMUTIL_PATH+y} then : printf %s "(cached) " >&6 -else $as_nop - case $DSYMUTIL_PATH in +else case e in #( + e) case $DSYMUTIL_PATH in [\\/]* | ?:[\\/]*) ac_cv_path_DSYMUTIL_PATH="$DSYMUTIL_PATH" # Let the user override the test with a path. ;; @@ -12951,6 +13247,7 @@ IFS=$as_save_IFS test -z "$ac_cv_path_DSYMUTIL_PATH" && ac_cv_path_DSYMUTIL_PATH="not found" ;; +esac ;; esac fi DSYMUTIL_PATH=$ac_cv_path_DSYMUTIL_PATH @@ -12998,9 +13295,10 @@ LDFLAGS="-fsanitize=address $LDFLAGS" # ASan works by controlling memory allocation, our own malloc interferes. with_pymalloc="no" -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } ;; +esac fi @@ -13018,8 +13316,8 @@ printf %s "checking whether C compiler accepts -fsanitize=memory... " >&6; } if test ${ax_cv_check_cflags___fsanitize_memory+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -fsanitize=memory" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -13036,11 +13334,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___fsanitize_memory=yes -else $as_nop - ax_cv_check_cflags___fsanitize_memory=no +else case e in #( + e) ax_cv_check_cflags___fsanitize_memory=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___fsanitize_memory" >&5 printf "%s\n" "$ax_cv_check_cflags___fsanitize_memory" >&6; } @@ -13050,16 +13350,18 @@ then : BASECFLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer $BASECFLAGS" LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 $LDFLAGS" -else $as_nop - as_fn_error $? "The selected compiler doesn't support memory sanitizer" "$LINENO" 5 +else case e in #( + e) as_fn_error $? "The selected compiler doesn't support memory sanitizer" "$LINENO" 5 ;; +esac fi # MSan works by controlling memory allocation, our own malloc interferes. with_pymalloc="no" -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } ;; +esac fi @@ -13076,12 +13378,13 @@ BASECFLAGS="-fsanitize=undefined $BASECFLAGS" LDFLAGS="-fsanitize=undefined $LDFLAGS" with_ubsan="yes" -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } with_ubsan="no" - + ;; +esac fi @@ -13098,12 +13401,13 @@ BASECFLAGS="-fsanitize=thread $BASECFLAGS" LDFLAGS="-fsanitize=thread $LDFLAGS" with_tsan="yes" -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } with_tsan="no" - + ;; +esac fi @@ -13487,16 +13791,22 @@ printf %s "checking for sendfile in -lsendfile... " >&6; } if test ${ac_cv_lib_sendfile_sendfile+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsendfile $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sendfile (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sendfile (void); int main (void) { @@ -13508,12 +13818,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sendfile_sendfile=yes -else $as_nop - ac_cv_lib_sendfile_sendfile=no +else case e in #( + e) ac_cv_lib_sendfile_sendfile=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sendfile_sendfile" >&5 printf "%s\n" "$ac_cv_lib_sendfile_sendfile" >&6; } @@ -13530,16 +13842,22 @@ printf %s "checking for dlopen in -ldl... " >&6; } if test ${ac_cv_lib_dl_dlopen+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char dlopen (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (void); int main (void) { @@ -13551,12 +13869,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dl_dlopen=yes -else $as_nop - ac_cv_lib_dl_dlopen=no +else case e in #( + e) ac_cv_lib_dl_dlopen=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; } @@ -13573,16 +13893,22 @@ printf %s "checking for shl_load in -ldld... " >&6; } if test ${ac_cv_lib_dld_shl_load+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char shl_load (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char shl_load (void); int main (void) { @@ -13594,12 +13920,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dld_shl_load=yes -else $as_nop - ac_cv_lib_dld_shl_load=no +else case e in #( + e) ac_cv_lib_dld_shl_load=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 printf "%s\n" "$ac_cv_lib_dld_shl_load" >&6; } @@ -13627,12 +13955,12 @@ then : for ac_func in uuid_create uuid_enc_be do : - as_ac_var=`printf "%s\n" "ac_cv_func_$ac_func" | $as_tr_sh` + as_ac_var=`printf "%s\n" "ac_cv_func_$ac_func" | sed "$as_sed_sh"` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes" then : cat >>confdefs.h <<_ACEOF -#define `printf "%s\n" "HAVE_$ac_func" | $as_tr_cpp` 1 +#define `printf "%s\n" "HAVE_$ac_func" | sed "$as_sed_cpp"` 1 _ACEOF have_uuid=yes LIBUUID_CFLAGS=${LIBUUID_CFLAGS-""} @@ -13730,16 +14058,22 @@ printf %s "checking for uuid_generate_time in -luuid... " >&6; } if test ${ac_cv_lib_uuid_uuid_generate_time+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-luuid $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char uuid_generate_time (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char uuid_generate_time (void); int main (void) { @@ -13751,12 +14085,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_uuid_uuid_generate_time=yes -else $as_nop - ac_cv_lib_uuid_uuid_generate_time=no +else case e in #( + e) ac_cv_lib_uuid_uuid_generate_time=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_uuid_uuid_generate_time" >&5 printf "%s\n" "$ac_cv_lib_uuid_uuid_generate_time" >&6; } @@ -13773,16 +14109,22 @@ printf %s "checking for uuid_generate_time_safe in -luuid... " >&6; } if test ${ac_cv_lib_uuid_uuid_generate_time_safe+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-luuid $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char uuid_generate_time_safe (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char uuid_generate_time_safe (void); int main (void) { @@ -13794,12 +14136,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_uuid_uuid_generate_time_safe=yes -else $as_nop - ac_cv_lib_uuid_uuid_generate_time_safe=no +else case e in #( + e) ac_cv_lib_uuid_uuid_generate_time_safe=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_uuid_uuid_generate_time_safe" >&5 printf "%s\n" "$ac_cv_lib_uuid_uuid_generate_time_safe" >&6; } @@ -13856,16 +14200,22 @@ printf %s "checking for uuid_generate_time in -luuid... " >&6; } if test ${ac_cv_lib_uuid_uuid_generate_time+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-luuid $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char uuid_generate_time (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char uuid_generate_time (void); int main (void) { @@ -13877,12 +14227,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_uuid_uuid_generate_time=yes -else $as_nop - ac_cv_lib_uuid_uuid_generate_time=no +else case e in #( + e) ac_cv_lib_uuid_uuid_generate_time=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_uuid_uuid_generate_time" >&5 printf "%s\n" "$ac_cv_lib_uuid_uuid_generate_time" >&6; } @@ -13899,16 +14251,22 @@ printf %s "checking for uuid_generate_time_safe in -luuid... " >&6; } if test ${ac_cv_lib_uuid_uuid_generate_time_safe+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-luuid $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char uuid_generate_time_safe (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char uuid_generate_time_safe (void); int main (void) { @@ -13920,12 +14278,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_uuid_uuid_generate_time_safe=yes -else $as_nop - ac_cv_lib_uuid_uuid_generate_time_safe=no +else case e in #( + e) ac_cv_lib_uuid_uuid_generate_time_safe=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_uuid_uuid_generate_time_safe" >&5 printf "%s\n" "$ac_cv_lib_uuid_uuid_generate_time_safe" >&6; } @@ -14020,15 +14380,21 @@ printf %s "checking for library containing sem_init... " >&6; } if test ${ac_cv_search_sem_init+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_func_search_save_LIBS=$LIBS +else case e in #( + e) ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sem_init (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sem_init (void); int main (void) { @@ -14059,11 +14425,13 @@ done if test ${ac_cv_search_sem_init+y} then : -else $as_nop - ac_cv_search_sem_init=no +else case e in #( + e) ac_cv_search_sem_init=no ;; +esac fi rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS +LIBS=$ac_func_search_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_sem_init" >&5 printf "%s\n" "$ac_cv_search_sem_init" >&6; } @@ -14081,16 +14449,22 @@ printf %s "checking for textdomain in -lintl... " >&6; } if test ${ac_cv_lib_intl_textdomain+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lintl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char textdomain (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char textdomain (void); int main (void) { @@ -14102,12 +14476,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_intl_textdomain=yes -else $as_nop - ac_cv_lib_intl_textdomain=no +else case e in #( + e) ac_cv_lib_intl_textdomain=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_intl_textdomain" >&5 printf "%s\n" "$ac_cv_lib_intl_textdomain" >&6; } @@ -14146,11 +14522,12 @@ printf "%s\n" "#define AIX_GENUINE_CPLUSPLUS 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -14179,8 +14556,8 @@ esac if test "$cross_compiling" = yes then : ac_cv_c_complex_supported=no -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -14201,11 +14578,13 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_c_complex_supported=yes -else $as_nop - ac_cv_c_complex_supported=no +else case e in #( + e) ac_cv_c_complex_supported=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi if test "$ac_cv_c_complex_supported" = "yes"; then @@ -14220,8 +14599,8 @@ printf %s "checking aligned memory access is required... " >&6; } if test ${ac_cv_aligned_required+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "$cross_compiling" = yes +else case e in #( + e) if test "$cross_compiling" = yes then : # "yes" changes the hash function to FNV, which causes problems with Numba @@ -14231,8 +14610,8 @@ if test "$ac_sys_system" = "Linux-android"; then else ac_cv_aligned_required=yes fi -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void) @@ -14251,14 +14630,17 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_aligned_required=no -else $as_nop - ac_cv_aligned_required=yes +else case e in #( + e) ac_cv_aligned_required=yes ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_aligned_required" >&5 printf "%s\n" "$ac_cv_aligned_required" >&6; } @@ -14298,9 +14680,10 @@ case "$withval" in ;; esac -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: default" >&5 -printf "%s\n" "default" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: default" >&5 +printf "%s\n" "default" >&6; } ;; +esac fi @@ -14338,10 +14721,11 @@ printf "%s\n" "\"$withval\"" >&6; } ;; esac -else $as_nop - validate_tzpath "$TZPATH" +else case e in #( + e) validate_tzpath "$TZPATH" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: \"$TZPATH\"" >&5 -printf "%s\n" "\"$TZPATH\"" >&6; } +printf "%s\n" "\"$TZPATH\"" >&6; } ;; +esac fi @@ -14352,16 +14736,22 @@ printf %s "checking for t_open in -lnsl... " >&6; } if test ${ac_cv_lib_nsl_t_open+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lnsl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char t_open (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char t_open (void); int main (void) { @@ -14373,12 +14763,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_nsl_t_open=yes -else $as_nop - ac_cv_lib_nsl_t_open=no +else case e in #( + e) ac_cv_lib_nsl_t_open=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_t_open" >&5 printf "%s\n" "$ac_cv_lib_nsl_t_open" >&6; } @@ -14392,16 +14784,22 @@ printf %s "checking for socket in -lsocket... " >&6; } if test ${ac_cv_lib_socket_socket+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char socket (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char socket (void); int main (void) { @@ -14413,12 +14811,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_socket_socket=yes -else $as_nop - ac_cv_lib_socket_socket=no +else case e in #( + e) ac_cv_lib_socket_socket=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_socket" >&5 printf "%s\n" "$ac_cv_lib_socket_socket" >&6; } @@ -14435,16 +14835,22 @@ printf %s "checking for socket in -lnetwork... " >&6; } if test ${ac_cv_lib_network_socket+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lnetwork $LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char socket (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char socket (void); int main (void) { @@ -14456,12 +14862,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_network_socket=yes -else $as_nop - ac_cv_lib_network_socket=no +else case e in #( + e) ac_cv_lib_network_socket=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_network_socket" >&5 printf "%s\n" "$ac_cv_lib_network_socket" >&6; } @@ -14484,9 +14892,10 @@ then : printf "%s\n" "$withval" >&6; } LIBS="$withval $LIBS" -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } ;; +esac fi @@ -14498,8 +14907,9 @@ printf %s "checking for --with-system-expat... " >&6; } if test ${with_system_expat+y} then : withval=$with_system_expat; -else $as_nop - with_system_expat="no" +else case e in #( + e) with_system_expat="no" ;; +esac fi @@ -14513,12 +14923,13 @@ then : LIBEXPAT_LDFLAGS=${LIBEXPAT_LDFLAGS-"-lexpat"} LIBEXPAT_INTERNAL= -else $as_nop - +else case e in #( + e) LIBEXPAT_CFLAGS="-I\$(srcdir)/Modules/expat" LIBEXPAT_LDFLAGS="-lm \$(LIBEXPAT_A)" LIBEXPAT_INTERNAL="\$(LIBEXPAT_HEADERS) \$(LIBEXPAT_A)" - + ;; +esac fi @@ -14544,16 +14955,22 @@ printf %s "checking for ffi_call in -lffi... " >&6; } if test ${ac_cv_lib_ffi_ffi_call+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lffi $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char ffi_call (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char ffi_call (void); int main (void) { @@ -14565,12 +14982,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ffi_ffi_call=yes -else $as_nop - ac_cv_lib_ffi_ffi_call=no +else case e in #( + e) ac_cv_lib_ffi_ffi_call=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ffi_ffi_call" >&5 printf "%s\n" "$ac_cv_lib_ffi_ffi_call" >&6; } @@ -14675,17 +15094,23 @@ printf %s "checking for ffi_call in -lffi... " >&6; } if test ${ac_cv_lib_ffi_ffi_call+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lffi $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char ffi_call (); -int + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char ffi_call (void); +int main (void) { return ffi_call (); @@ -14696,12 +15121,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ffi_ffi_call=yes -else $as_nop - ac_cv_lib_ffi_ffi_call=no +else case e in #( + e) ac_cv_lib_ffi_ffi_call=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ffi_ffi_call" >&5 printf "%s\n" "$ac_cv_lib_ffi_ffi_call" >&6; } @@ -14712,8 +15139,9 @@ then : LIBFFI_CFLAGS=${LIBFFI_CFLAGS-""} LIBFFI_LIBS=${LIBFFI_LIBS-"-lffi"} -else $as_nop - have_libffi=no +else case e in #( + e) have_libffi=no ;; +esac fi @@ -14748,16 +15176,22 @@ printf %s "checking for ffi_call in -lffi... " >&6; } if test ${ac_cv_lib_ffi_ffi_call+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lffi $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char ffi_call (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char ffi_call (void); int main (void) { @@ -14769,12 +15203,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ffi_ffi_call=yes -else $as_nop - ac_cv_lib_ffi_ffi_call=no +else case e in #( + e) ac_cv_lib_ffi_ffi_call=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ffi_ffi_call" >&5 printf "%s\n" "$ac_cv_lib_ffi_ffi_call" >&6; } @@ -14785,8 +15221,9 @@ then : LIBFFI_CFLAGS=${LIBFFI_CFLAGS-""} LIBFFI_LIBS=${LIBFFI_LIBS-"-lffi"} -else $as_nop - have_libffi=no +else case e in #( + e) have_libffi=no ;; +esac fi @@ -14859,8 +15296,8 @@ printf %s "checking for ffi_prep_cif_var... " >&6; } if test ${ac_cv_func_ffi_prep_cif_var+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -14874,11 +15311,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_ffi_prep_cif_var=yes -else $as_nop - ac_cv_func_ffi_prep_cif_var=no +else case e in #( + e) ac_cv_func_ffi_prep_cif_var=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_ffi_prep_cif_var" >&5 printf "%s\n" "$ac_cv_func_ffi_prep_cif_var" >&6; } @@ -14898,8 +15337,8 @@ printf %s "checking for ffi_prep_closure_loc... " >&6; } if test ${ac_cv_func_ffi_prep_closure_loc+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -14913,11 +15352,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_ffi_prep_closure_loc=yes -else $as_nop - ac_cv_func_ffi_prep_closure_loc=no +else case e in #( + e) ac_cv_func_ffi_prep_closure_loc=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_ffi_prep_closure_loc" >&5 printf "%s\n" "$ac_cv_func_ffi_prep_closure_loc" >&6; } @@ -14937,8 +15378,8 @@ printf %s "checking for ffi_closure_alloc... " >&6; } if test ${ac_cv_func_ffi_closure_alloc+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -14952,11 +15393,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_ffi_closure_alloc=yes -else $as_nop - ac_cv_func_ffi_closure_alloc=no +else case e in #( + e) ac_cv_func_ffi_closure_alloc=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_ffi_closure_alloc" >&5 printf "%s\n" "$ac_cv_func_ffi_closure_alloc" >&6; } @@ -14989,8 +15432,8 @@ printf %s "checking libffi has complex type support... " >&6; } if test ${ac_cv_ffi_complex_double_supported+y} then : printf %s "(cached) " >&6 -else $as_nop - save_CFLAGS=$CFLAGS +else case e in #( + e) save_CFLAGS=$CFLAGS save_CPPFLAGS=$CPPFLAGS save_LDFLAGS=$LDFLAGS save_LIBS=$LIBS @@ -15002,8 +15445,8 @@ save_LIBS=$LIBS if test "$cross_compiling" = yes then : ac_cv_ffi_complex_double_supported=no -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -15033,11 +15476,13 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_ffi_complex_double_supported=yes -else $as_nop - ac_cv_ffi_complex_double_supported=no +else case e in #( + e) ac_cv_ffi_complex_double_supported=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi @@ -15046,7 +15491,8 @@ CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_ffi_complex_double_supported" >&5 printf "%s\n" "$ac_cv_ffi_complex_double_supported" >&6; } @@ -15064,8 +15510,9 @@ printf %s "checking for --with-system-libmpdec... " >&6; } if test ${with_system_libmpdec+y} then : withval=$with_system_libmpdec; -else $as_nop - with_system_libmpdec="yes" +else case e in #( + e) with_system_libmpdec="yes" ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_system_libmpdec" >&5 @@ -15150,12 +15597,13 @@ else printf "%s\n" "yes" >&6; } fi -else $as_nop - LIBMPDEC_CFLAGS="-I\$(srcdir)/Modules/_decimal/libmpdec" +else case e in #( + e) LIBMPDEC_CFLAGS="-I\$(srcdir)/Modules/_decimal/libmpdec" LIBMPDEC_LIBS="-lm \$(LIBMPDEC_A)" LIBMPDEC_INTERNAL="\$(LIBMPDEC_HEADERS) \$(LIBMPDEC_A)" have_mpdec=yes - with_system_libmpdec=no + with_system_libmpdec=no ;; +esac fi if test "x$with_system_libmpdec" = xyes @@ -15189,8 +15637,9 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : have_mpdec=yes -else $as_nop - have_mpdec=no +else case e in #( + e) have_mpdec=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -15201,9 +15650,10 @@ LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: the bundled copy of libmpdecimal is scheduled for removal in Python 3.15; consider using a system installed mpdecimal library." >&5 -printf "%s\n" "$as_me: WARNING: the bundled copy of libmpdecimal is scheduled for removal in Python 3.15; consider using a system installed mpdecimal library." >&2;} +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: the bundled copy of libmpdecimal is scheduled for removal in Python 3.15; consider using a system installed mpdecimal library." >&5 +printf "%s\n" "$as_me: WARNING: the bundled copy of libmpdecimal is scheduled for removal in Python 3.15; consider using a system installed mpdecimal library." >&2;} ;; +esac fi if test "$with_system_libmpdec" = "yes" && test "$have_mpdec" = "no" @@ -15231,8 +15681,9 @@ printf %s "checking for --with-decimal-contextvar... " >&6; } if test ${with_decimal_contextvar+y} then : withval=$with_decimal_contextvar; -else $as_nop - with_decimal_contextvar="yes" +else case e in #( + e) with_decimal_contextvar="yes" ;; +esac fi @@ -15470,16 +15921,22 @@ printf %s "checking for sqlite3_bind_double in -lsqlite3... " >&6; } if test ${ac_cv_lib_sqlite3_sqlite3_bind_double+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsqlite3 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sqlite3_bind_double (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sqlite3_bind_double (void); int main (void) { @@ -15491,12 +15948,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sqlite3_sqlite3_bind_double=yes -else $as_nop - ac_cv_lib_sqlite3_sqlite3_bind_double=no +else case e in #( + e) ac_cv_lib_sqlite3_sqlite3_bind_double=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_bind_double" >&5 printf "%s\n" "$ac_cv_lib_sqlite3_sqlite3_bind_double" >&6; } @@ -15506,10 +15965,11 @@ then : LIBS="-lsqlite3 $LIBS" -else $as_nop - +else case e in #( + e) have_supported_sqlite3=no - + ;; +esac fi @@ -15519,16 +15979,22 @@ printf %s "checking for sqlite3_column_decltype in -lsqlite3... " >&6; } if test ${ac_cv_lib_sqlite3_sqlite3_column_decltype+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsqlite3 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sqlite3_column_decltype (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sqlite3_column_decltype (void); int main (void) { @@ -15540,12 +16006,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sqlite3_sqlite3_column_decltype=yes -else $as_nop - ac_cv_lib_sqlite3_sqlite3_column_decltype=no +else case e in #( + e) ac_cv_lib_sqlite3_sqlite3_column_decltype=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_column_decltype" >&5 printf "%s\n" "$ac_cv_lib_sqlite3_sqlite3_column_decltype" >&6; } @@ -15555,10 +16023,11 @@ then : LIBS="-lsqlite3 $LIBS" -else $as_nop - +else case e in #( + e) have_supported_sqlite3=no - + ;; +esac fi @@ -15568,16 +16037,22 @@ printf %s "checking for sqlite3_column_double in -lsqlite3... " >&6; } if test ${ac_cv_lib_sqlite3_sqlite3_column_double+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsqlite3 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sqlite3_column_double (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sqlite3_column_double (void); int main (void) { @@ -15589,12 +16064,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sqlite3_sqlite3_column_double=yes -else $as_nop - ac_cv_lib_sqlite3_sqlite3_column_double=no +else case e in #( + e) ac_cv_lib_sqlite3_sqlite3_column_double=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_column_double" >&5 printf "%s\n" "$ac_cv_lib_sqlite3_sqlite3_column_double" >&6; } @@ -15604,10 +16081,11 @@ then : LIBS="-lsqlite3 $LIBS" -else $as_nop - +else case e in #( + e) have_supported_sqlite3=no - + ;; +esac fi @@ -15617,16 +16095,22 @@ printf %s "checking for sqlite3_complete in -lsqlite3... " >&6; } if test ${ac_cv_lib_sqlite3_sqlite3_complete+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsqlite3 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sqlite3_complete (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sqlite3_complete (void); int main (void) { @@ -15638,12 +16122,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sqlite3_sqlite3_complete=yes -else $as_nop - ac_cv_lib_sqlite3_sqlite3_complete=no +else case e in #( + e) ac_cv_lib_sqlite3_sqlite3_complete=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_complete" >&5 printf "%s\n" "$ac_cv_lib_sqlite3_sqlite3_complete" >&6; } @@ -15653,10 +16139,11 @@ then : LIBS="-lsqlite3 $LIBS" -else $as_nop - +else case e in #( + e) have_supported_sqlite3=no - + ;; +esac fi @@ -15666,16 +16153,22 @@ printf %s "checking for sqlite3_progress_handler in -lsqlite3... " >&6; } if test ${ac_cv_lib_sqlite3_sqlite3_progress_handler+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsqlite3 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sqlite3_progress_handler (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sqlite3_progress_handler (void); int main (void) { @@ -15687,12 +16180,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sqlite3_sqlite3_progress_handler=yes -else $as_nop - ac_cv_lib_sqlite3_sqlite3_progress_handler=no +else case e in #( + e) ac_cv_lib_sqlite3_sqlite3_progress_handler=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_progress_handler" >&5 printf "%s\n" "$ac_cv_lib_sqlite3_sqlite3_progress_handler" >&6; } @@ -15702,10 +16197,11 @@ then : LIBS="-lsqlite3 $LIBS" -else $as_nop - +else case e in #( + e) have_supported_sqlite3=no - + ;; +esac fi @@ -15715,16 +16211,22 @@ printf %s "checking for sqlite3_result_double in -lsqlite3... " >&6; } if test ${ac_cv_lib_sqlite3_sqlite3_result_double+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsqlite3 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sqlite3_result_double (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sqlite3_result_double (void); int main (void) { @@ -15736,12 +16238,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sqlite3_sqlite3_result_double=yes -else $as_nop - ac_cv_lib_sqlite3_sqlite3_result_double=no +else case e in #( + e) ac_cv_lib_sqlite3_sqlite3_result_double=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_result_double" >&5 printf "%s\n" "$ac_cv_lib_sqlite3_sqlite3_result_double" >&6; } @@ -15751,10 +16255,11 @@ then : LIBS="-lsqlite3 $LIBS" -else $as_nop - +else case e in #( + e) have_supported_sqlite3=no - + ;; +esac fi @@ -15764,16 +16269,22 @@ printf %s "checking for sqlite3_set_authorizer in -lsqlite3... " >&6; } if test ${ac_cv_lib_sqlite3_sqlite3_set_authorizer+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsqlite3 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sqlite3_set_authorizer (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sqlite3_set_authorizer (void); int main (void) { @@ -15785,12 +16296,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sqlite3_sqlite3_set_authorizer=yes -else $as_nop - ac_cv_lib_sqlite3_sqlite3_set_authorizer=no +else case e in #( + e) ac_cv_lib_sqlite3_sqlite3_set_authorizer=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_set_authorizer" >&5 printf "%s\n" "$ac_cv_lib_sqlite3_sqlite3_set_authorizer" >&6; } @@ -15800,10 +16313,11 @@ then : LIBS="-lsqlite3 $LIBS" -else $as_nop - +else case e in #( + e) have_supported_sqlite3=no - + ;; +esac fi @@ -15813,16 +16327,22 @@ printf %s "checking for sqlite3_trace_v2 in -lsqlite3... " >&6; } if test ${ac_cv_lib_sqlite3_sqlite3_trace_v2+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsqlite3 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sqlite3_trace_v2 (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sqlite3_trace_v2 (void); int main (void) { @@ -15834,12 +16354,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sqlite3_sqlite3_trace_v2=yes -else $as_nop - ac_cv_lib_sqlite3_sqlite3_trace_v2=no +else case e in #( + e) ac_cv_lib_sqlite3_sqlite3_trace_v2=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_trace_v2" >&5 printf "%s\n" "$ac_cv_lib_sqlite3_sqlite3_trace_v2" >&6; } @@ -15849,8 +16371,8 @@ then : LIBS="-lsqlite3 $LIBS" -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sqlite3_trace in -lsqlite3" >&5 @@ -15858,16 +16380,22 @@ printf %s "checking for sqlite3_trace in -lsqlite3... " >&6; } if test ${ac_cv_lib_sqlite3_sqlite3_trace+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsqlite3 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sqlite3_trace (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sqlite3_trace (void); int main (void) { @@ -15879,12 +16407,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sqlite3_sqlite3_trace=yes -else $as_nop - ac_cv_lib_sqlite3_sqlite3_trace=no +else case e in #( + e) ac_cv_lib_sqlite3_sqlite3_trace=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_trace" >&5 printf "%s\n" "$ac_cv_lib_sqlite3_sqlite3_trace" >&6; } @@ -15894,15 +16424,17 @@ then : LIBS="-lsqlite3 $LIBS" -else $as_nop - +else case e in #( + e) have_supported_sqlite3=no - + ;; +esac fi - + ;; +esac fi @@ -15912,16 +16444,22 @@ printf %s "checking for sqlite3_value_double in -lsqlite3... " >&6; } if test ${ac_cv_lib_sqlite3_sqlite3_value_double+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsqlite3 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sqlite3_value_double (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sqlite3_value_double (void); int main (void) { @@ -15933,12 +16471,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sqlite3_sqlite3_value_double=yes -else $as_nop - ac_cv_lib_sqlite3_sqlite3_value_double=no +else case e in #( + e) ac_cv_lib_sqlite3_sqlite3_value_double=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_value_double" >&5 printf "%s\n" "$ac_cv_lib_sqlite3_sqlite3_value_double" >&6; } @@ -15948,10 +16488,11 @@ then : LIBS="-lsqlite3 $LIBS" -else $as_nop - +else case e in #( + e) have_supported_sqlite3=no - + ;; +esac fi @@ -15960,16 +16501,22 @@ printf %s "checking for sqlite3_load_extension in -lsqlite3... " >&6; } if test ${ac_cv_lib_sqlite3_sqlite3_load_extension+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsqlite3 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sqlite3_load_extension (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sqlite3_load_extension (void); int main (void) { @@ -15981,21 +16528,24 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sqlite3_sqlite3_load_extension=yes -else $as_nop - ac_cv_lib_sqlite3_sqlite3_load_extension=no +else case e in #( + e) ac_cv_lib_sqlite3_sqlite3_load_extension=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_load_extension" >&5 printf "%s\n" "$ac_cv_lib_sqlite3_sqlite3_load_extension" >&6; } if test "x$ac_cv_lib_sqlite3_sqlite3_load_extension" = xyes then : have_sqlite3_load_extension=yes -else $as_nop - have_sqlite3_load_extension=no - +else case e in #( + e) have_sqlite3_load_extension=no + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sqlite3_serialize in -lsqlite3" >&5 @@ -16003,16 +16553,22 @@ printf %s "checking for sqlite3_serialize in -lsqlite3... " >&6; } if test ${ac_cv_lib_sqlite3_sqlite3_serialize+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsqlite3 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char sqlite3_serialize (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char sqlite3_serialize (void); int main (void) { @@ -16024,12 +16580,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sqlite3_sqlite3_serialize=yes -else $as_nop - ac_cv_lib_sqlite3_sqlite3_serialize=no +else case e in #( + e) ac_cv_lib_sqlite3_sqlite3_serialize=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_serialize" >&5 printf "%s\n" "$ac_cv_lib_sqlite3_sqlite3_serialize" >&6; } @@ -16043,10 +16601,11 @@ printf "%s\n" "#define PY_SQLITE_HAVE_SERIALIZE 1" >>confdefs.h fi -else $as_nop - +else case e in #( + e) have_supported_sqlite3=no - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -16074,22 +16633,24 @@ printf "%s\n" "n/a" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Your version of SQLite does not support loadable extensions" >&5 printf "%s\n" "$as_me: WARNING: Your version of SQLite does not support loadable extensions" >&2;} -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define PY_SQLITE_ENABLE_LOAD_EXTENSION 1" >>confdefs.h - + ;; +esac fi -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi @@ -16277,8 +16838,8 @@ See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. @@ -16288,7 +16849,7 @@ and X11_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else X11_CFLAGS=$pkg_cv_X11_CFLAGS X11_LIBS=$pkg_cv_X11_LIBS @@ -16356,10 +16917,11 @@ then : have_tcltk=yes as_fn_append TCLTK_CFLAGS " -Wno-strict-prototypes -DWITH_APPINIT=1" -else $as_nop - +else case e in #( + e) have_tcltk=no - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -16393,16 +16955,22 @@ printf %s "checking for gdbm_open in -lgdbm... " >&6; } if test ${ac_cv_lib_gdbm_gdbm_open+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lgdbm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char gdbm_open (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char gdbm_open (void); int main (void) { @@ -16414,12 +16982,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_gdbm_gdbm_open=yes -else $as_nop - ac_cv_lib_gdbm_gdbm_open=no +else case e in #( + e) ac_cv_lib_gdbm_gdbm_open=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gdbm_gdbm_open" >&5 printf "%s\n" "$ac_cv_lib_gdbm_gdbm_open" >&6; } @@ -16429,13 +16999,15 @@ then : have_gdbm=yes GDBM_LIBS=${GDBM_LIBS-"-lgdbm"} -else $as_nop - have_gdbm=no +else case e in #( + e) have_gdbm=no ;; +esac fi -else $as_nop - have_gdbm=no +else case e in #( + e) have_gdbm=no ;; +esac fi done @@ -16465,15 +17037,21 @@ printf %s "checking for library containing dbm_open... " >&6; } if test ${ac_cv_search_dbm_open+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_func_search_save_LIBS=$LIBS +else case e in #( + e) ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char dbm_open (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char dbm_open (void); int main (void) { @@ -16504,11 +17082,13 @@ done if test ${ac_cv_search_dbm_open+y} then : -else $as_nop - ac_cv_search_dbm_open=no +else case e in #( + e) ac_cv_search_dbm_open=no ;; +esac fi rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS +LIBS=$ac_func_search_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dbm_open" >&5 printf "%s\n" "$ac_cv_search_dbm_open" >&6; } @@ -16557,18 +17137,20 @@ printf "%s\n" "$have_ndbm ($dbm_ndbm)" >&6; } if test ${ac_cv_header_gdbm_slash_ndbm_h+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ac_fn_c_check_header_compile "$LINENO" "gdbm/ndbm.h" "ac_cv_header_gdbm_ndbm_h" "$ac_includes_default" if test "x$ac_cv_header_gdbm_ndbm_h" = xyes then : ac_cv_header_gdbm_slash_ndbm_h=yes -else $as_nop - ac_cv_header_gdbm_slash_ndbm_h=no - +else case e in #( + e) ac_cv_header_gdbm_slash_ndbm_h=no + ;; +esac fi - + ;; +esac fi if test "x$ac_cv_header_gdbm_slash_ndbm_h" = xyes @@ -16584,18 +17166,20 @@ fi if test ${ac_cv_header_gdbm_dash_ndbm_h+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ac_fn_c_check_header_compile "$LINENO" "gdbm-ndbm.h" "ac_cv_header_gdbm_ndbm_h" "$ac_includes_default" if test "x$ac_cv_header_gdbm_ndbm_h" = xyes then : ac_cv_header_gdbm_dash_ndbm_h=yes -else $as_nop - ac_cv_header_gdbm_dash_ndbm_h=no - +else case e in #( + e) ac_cv_header_gdbm_dash_ndbm_h=no + ;; +esac fi - + ;; +esac fi if test "x$ac_cv_header_gdbm_dash_ndbm_h" = xyes @@ -16621,15 +17205,21 @@ printf %s "checking for library containing dbm_open... " >&6; } if test ${ac_cv_search_dbm_open+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_func_search_save_LIBS=$LIBS +else case e in #( + e) ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char dbm_open (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char dbm_open (void); int main (void) { @@ -16660,11 +17250,13 @@ done if test ${ac_cv_search_dbm_open+y} then : -else $as_nop - ac_cv_search_dbm_open=no +else case e in #( + e) ac_cv_search_dbm_open=no ;; +esac fi rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS +LIBS=$ac_func_search_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dbm_open" >&5 printf "%s\n" "$ac_cv_search_dbm_open" >&6; } @@ -16673,8 +17265,9 @@ if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" have_gdbm_compat=yes -else $as_nop - have_gdbm_compat=no +else case e in #( + e) have_gdbm_compat=no ;; +esac fi @@ -16700,8 +17293,8 @@ printf %s "checking for libdb... " >&6; } if test ${ac_cv_have_libdb+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) save_CFLAGS=$CFLAGS save_CPPFLAGS=$CPPFLAGS save_LDFLAGS=$LDFLAGS @@ -16730,8 +17323,9 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_have_libdb=yes -else $as_nop - ac_cv_have_libdb=no +else case e in #( + e) ac_cv_have_libdb=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -16742,7 +17336,8 @@ LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_libdb" >&5 printf "%s\n" "$ac_cv_have_libdb" >&6; } @@ -16767,8 +17362,9 @@ printf %s "checking for --with-dbmliborder... " >&6; } if test ${with_dbmliborder+y} then : withval=$with_dbmliborder; -else $as_nop - with_dbmliborder=gdbm:ndbm:bdb +else case e in #( + e) with_dbmliborder=gdbm:ndbm:bdb ;; +esac fi @@ -16885,8 +17481,8 @@ printf %s "checking for _POSIX_THREADS defined in unistd.h... " >&6; } if test ${ac_cv_defined__POSIX_THREADS_unistd_h+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -16907,18 +17503,21 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_defined__POSIX_THREADS_unistd_h=yes -else $as_nop - ac_cv_defined__POSIX_THREADS_unistd_h=no +else case e in #( + e) ac_cv_defined__POSIX_THREADS_unistd_h=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_defined__POSIX_THREADS_unistd_h" >&5 printf "%s\n" "$ac_cv_defined__POSIX_THREADS_unistd_h" >&6; } if test $ac_cv_defined__POSIX_THREADS_unistd_h != "no" then : unistd_defines_pthreads=yes -else $as_nop - unistd_defines_pthreads=no +else case e in #( + e) unistd_defines_pthreads=no ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $unistd_defines_pthreads" >&5 printf "%s\n" "$unistd_defines_pthreads" >&6; } @@ -16956,8 +17555,8 @@ then : printf "%s\n" "yes" >&6; } posix_threads=yes -else $as_nop - +else case e in #( + e) LIBS=$_libs ac_fn_c_check_func "$LINENO" "pthread_detach" "ac_cv_func_pthread_detach" if test "x$ac_cv_func_pthread_detach" = xyes @@ -16965,23 +17564,29 @@ then : posix_threads=yes -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthreads" >&5 printf %s "checking for pthread_create in -lpthreads... " >&6; } if test ${ac_cv_lib_pthreads_pthread_create+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lpthreads $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char pthread_create (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_create (void); int main (void) { @@ -16993,12 +17598,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_pthreads_pthread_create=yes -else $as_nop - ac_cv_lib_pthreads_pthread_create=no +else case e in #( + e) ac_cv_lib_pthreads_pthread_create=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthreads_pthread_create" >&5 printf "%s\n" "$ac_cv_lib_pthreads_pthread_create" >&6; } @@ -17008,23 +17615,29 @@ then : posix_threads=yes LIBS="$LIBS -lpthreads" -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lc_r" >&5 printf %s "checking for pthread_create in -lc_r... " >&6; } if test ${ac_cv_lib_c_r_pthread_create+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lc_r $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char pthread_create (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_create (void); int main (void) { @@ -17036,12 +17649,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_c_r_pthread_create=yes -else $as_nop - ac_cv_lib_c_r_pthread_create=no +else case e in #( + e) ac_cv_lib_c_r_pthread_create=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_r_pthread_create" >&5 printf "%s\n" "$ac_cv_lib_c_r_pthread_create" >&6; } @@ -17051,23 +17666,29 @@ then : posix_threads=yes LIBS="$LIBS -lc_r" -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for __pthread_create_system in -lpthread" >&5 printf %s "checking for __pthread_create_system in -lpthread... " >&6; } if test ${ac_cv_lib_pthread___pthread_create_system+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char __pthread_create_system (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char __pthread_create_system (void); int main (void) { @@ -17079,12 +17700,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_pthread___pthread_create_system=yes -else $as_nop - ac_cv_lib_pthread___pthread_create_system=no +else case e in #( + e) ac_cv_lib_pthread___pthread_create_system=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread___pthread_create_system" >&5 printf "%s\n" "$ac_cv_lib_pthread___pthread_create_system" >&6; } @@ -17094,23 +17717,29 @@ then : posix_threads=yes LIBS="$LIBS -lpthread" -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lcma" >&5 printf %s "checking for pthread_create in -lcma... " >&6; } if test ${ac_cv_lib_cma_pthread_create+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lcma $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char pthread_create (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_create (void); int main (void) { @@ -17122,12 +17751,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_cma_pthread_create=yes -else $as_nop - ac_cv_lib_cma_pthread_create=no +else case e in #( + e) ac_cv_lib_cma_pthread_create=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cma_pthread_create" >&5 printf "%s\n" "$ac_cv_lib_cma_pthread_create" >&6; } @@ -17137,8 +17768,8 @@ then : posix_threads=yes LIBS="$LIBS -lcma" -else $as_nop - +else case e in #( + e) case $ac_sys_system in #( WASI) : posix_threads=stub ;; #( @@ -17146,17 +17777,23 @@ else $as_nop as_fn_error $? "could not find pthreads on your system" "$LINENO" 5 ;; esac - -fi - + ;; +esac fi - + ;; +esac fi - + ;; +esac fi - + ;; +esac fi - + ;; +esac +fi + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -17166,16 +17803,22 @@ printf %s "checking for usconfig in -lmpc... " >&6; } if test ${ac_cv_lib_mpc_usconfig+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lmpc $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char usconfig (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char usconfig (void); int main (void) { @@ -17187,12 +17830,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_mpc_usconfig=yes -else $as_nop - ac_cv_lib_mpc_usconfig=no +else case e in #( + e) ac_cv_lib_mpc_usconfig=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_mpc_usconfig" >&5 printf "%s\n" "$ac_cv_lib_mpc_usconfig" >&6; } @@ -17238,12 +17883,12 @@ printf %s "checking if PTHREAD_SCOPE_SYSTEM is supported... " >&6; } if test ${ac_cv_pthread_system_supported+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "$cross_compiling" = yes +else case e in #( + e) if test "$cross_compiling" = yes then : ac_cv_pthread_system_supported=no -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -17263,14 +17908,17 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_pthread_system_supported=yes -else $as_nop - ac_cv_pthread_system_supported=no +else case e in #( + e) ac_cv_pthread_system_supported=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_pthread_system_supported" >&5 printf "%s\n" "$ac_cv_pthread_system_supported" >&6; } @@ -17334,8 +17982,8 @@ printf "%s\n" "yes" >&6; } ipv6=yes ;; esac -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* AF_INET6 available check */ @@ -17354,10 +18002,11 @@ then : ipv6=yes -else $as_nop - +else case e in #( + e) ipv6=no - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext @@ -17397,12 +18046,13 @@ then : printf "%s\n" "yes" >&6; } ipv6=yes -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } ipv6=no - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi @@ -17411,7 +18061,8 @@ if test "$ipv6" = "yes"; then printf "%s\n" "#define ENABLE_IPV6 1" >>confdefs.h fi - + ;; +esac fi @@ -17430,8 +18081,8 @@ printf %s "checking for IPV6_INRIA_VERSION defined in netinet/in.h... " >&6; } if test ${ac_cv_defined_IPV6_INRIA_VERSION_netinet_in_h+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -17452,10 +18103,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_defined_IPV6_INRIA_VERSION_netinet_in_h=yes -else $as_nop - ac_cv_defined_IPV6_INRIA_VERSION_netinet_in_h=no +else case e in #( + e) ac_cv_defined_IPV6_INRIA_VERSION_netinet_in_h=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_defined_IPV6_INRIA_VERSION_netinet_in_h" >&5 printf "%s\n" "$ac_cv_defined_IPV6_INRIA_VERSION_netinet_in_h" >&6; } @@ -17471,8 +18124,8 @@ printf %s "checking for __KAME__ defined in netinet/in.h... " >&6; } if test ${ac_cv_defined___KAME___netinet_in_h+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -17493,10 +18146,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_defined___KAME___netinet_in_h=yes -else $as_nop - ac_cv_defined___KAME___netinet_in_h=no +else case e in #( + e) ac_cv_defined___KAME___netinet_in_h=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_defined___KAME___netinet_in_h" >&5 printf "%s\n" "$ac_cv_defined___KAME___netinet_in_h" >&6; } @@ -17515,8 +18170,8 @@ printf %s "checking for __GLIBC__ defined in features.h... " >&6; } if test ${ac_cv_defined___GLIBC___features_h+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -17537,10 +18192,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_defined___GLIBC___features_h=yes -else $as_nop - ac_cv_defined___GLIBC___features_h=no +else case e in #( + e) ac_cv_defined___GLIBC___features_h=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_defined___GLIBC___features_h" >&5 printf "%s\n" "$ac_cv_defined___GLIBC___features_h" >&6; } @@ -17573,8 +18230,8 @@ printf %s "checking for _TOSHIBA_INET6 defined in sys/param.h... " >&6; } if test ${ac_cv_defined__TOSHIBA_INET6_sys_param_h+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -17595,10 +18252,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_defined__TOSHIBA_INET6_sys_param_h=yes -else $as_nop - ac_cv_defined__TOSHIBA_INET6_sys_param_h=no +else case e in #( + e) ac_cv_defined__TOSHIBA_INET6_sys_param_h=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_defined__TOSHIBA_INET6_sys_param_h" >&5 printf "%s\n" "$ac_cv_defined__TOSHIBA_INET6_sys_param_h" >&6; } @@ -17616,8 +18275,8 @@ printf %s "checking for __V6D__ defined in /usr/local/v6/include/sys/v6config.h. if test ${ac_cv_defined___V6D____usr_local_v6_include_sys_v6config_h+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -17638,10 +18297,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_defined___V6D____usr_local_v6_include_sys_v6config_h=yes -else $as_nop - ac_cv_defined___V6D____usr_local_v6_include_sys_v6config_h=no +else case e in #( + e) ac_cv_defined___V6D____usr_local_v6_include_sys_v6config_h=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_defined___V6D____usr_local_v6_include_sys_v6config_h" >&5 printf "%s\n" "$ac_cv_defined___V6D____usr_local_v6_include_sys_v6config_h" >&6; } @@ -17660,8 +18321,8 @@ printf %s "checking for _ZETA_MINAMI_INET6 defined in sys/param.h... " >&6; } if test ${ac_cv_defined__ZETA_MINAMI_INET6_sys_param_h+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -17682,10 +18343,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_defined__ZETA_MINAMI_INET6_sys_param_h=yes -else $as_nop - ac_cv_defined__ZETA_MINAMI_INET6_sys_param_h=no +else case e in #( + e) ac_cv_defined__ZETA_MINAMI_INET6_sys_param_h=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_defined__ZETA_MINAMI_INET6_sys_param_h" >&5 printf "%s\n" "$ac_cv_defined__ZETA_MINAMI_INET6_sys_param_h" >&6; } @@ -17721,10 +18384,11 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: libc" >&5 printf "%s\n" "libc" >&6; } -else $as_nop - +else case e in #( + e) as_fn_error $? "No $ipv6lib library found; cannot continue. You need to fetch lib$ipv6lib.a from appropriate ipv6 kit and compile beforehand." "$LINENO" 5 - + ;; +esac fi fi fi @@ -17735,8 +18399,8 @@ printf %s "checking CAN_RAW_FD_FRAMES... " >&6; } if test ${ac_cv_can_raw_fd_frames+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* CAN_RAW_FD_FRAMES available check */ @@ -17752,11 +18416,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_can_raw_fd_frames=yes -else $as_nop - ac_cv_can_raw_fd_frames=no +else case e in #( + e) ac_cv_can_raw_fd_frames=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_can_raw_fd_frames" >&5 printf "%s\n" "$ac_cv_can_raw_fd_frames" >&6; } @@ -17774,8 +18440,8 @@ printf %s "checking for CAN_RAW_JOIN_FILTERS... " >&6; } if test ${ac_cv_can_raw_join_filters+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -17791,11 +18457,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_can_raw_join_filters=yes -else $as_nop - ac_cv_can_raw_join_filters=no +else case e in #( + e) ac_cv_can_raw_join_filters=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_can_raw_join_filters" >&5 printf "%s\n" "$ac_cv_can_raw_join_filters" >&6; } @@ -17837,8 +18505,8 @@ printf %s "checking for stdatomic.h... " >&6; } if test ${ac_cv_header_stdatomic_h+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -17858,12 +18526,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_header_stdatomic_h=yes -else $as_nop - ac_cv_header_stdatomic_h=no +else case e in #( + e) ac_cv_header_stdatomic_h=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdatomic_h" >&5 printf "%s\n" "$ac_cv_header_stdatomic_h" >&6; } @@ -17883,8 +18553,8 @@ printf %s "checking for builtin __atomic_load_n and __atomic_store_n functions.. if test ${ac_cv_builtin_atomic+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -17901,12 +18571,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_builtin_atomic=yes -else $as_nop - ac_cv_builtin_atomic=no +else case e in #( + e) ac_cv_builtin_atomic=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_builtin_atomic" >&5 printf "%s\n" "$ac_cv_builtin_atomic" >&6; } @@ -17928,9 +18600,10 @@ printf %s "checking for --with-mimalloc... " >&6; } if test ${with_mimalloc+y} then : withval=$with_mimalloc; -else $as_nop - with_mimalloc="$ac_cv_header_stdatomic_h" - +else case e in #( + e) with_mimalloc="$ac_cv_header_stdatomic_h" + ;; +esac fi @@ -18019,9 +18692,10 @@ printf %s "checking for --with-valgrind... " >&6; } if test ${with_valgrind+y} then : withval=$with_valgrind; -else $as_nop - with_valgrind=no - +else case e in #( + e) with_valgrind=no + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_valgrind" >&5 @@ -18033,9 +18707,10 @@ then : printf "%s\n" "#define WITH_VALGRIND 1" >>confdefs.h -else $as_nop - as_fn_error $? "Valgrind support requested but headers not available" "$LINENO" 5 - +else case e in #( + e) as_fn_error $? "Valgrind support requested but headers not available" "$LINENO" 5 + ;; +esac fi OPT="-DDYNAMIC_ANNOTATIONS_ENABLED=1 $OPT" @@ -18049,8 +18724,9 @@ printf %s "checking for --with-dtrace... " >&6; } if test ${with_dtrace+y} then : withval=$with_dtrace; -else $as_nop - with_dtrace=no +else case e in #( + e) with_dtrace=no ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_dtrace" >&5 @@ -18073,8 +18749,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_DTRACE+y} then : printf %s "(cached) " >&6 -else $as_nop - case $DTRACE in +else case e in #( + e) case $DTRACE in [\\/]* | ?:[\\/]*) ac_cv_path_DTRACE="$DTRACE" # Let the user override the test with a path. ;; @@ -18100,6 +18776,7 @@ IFS=$as_save_IFS test -z "$ac_cv_path_DTRACE" && ac_cv_path_DTRACE="not found" ;; +esac ;; esac fi DTRACE=$ac_cv_path_DTRACE @@ -18129,12 +18806,13 @@ printf %s "checking whether DTrace probes require linking... " >&6; } if test ${ac_cv_dtrace_link+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_cv_dtrace_link=no +else case e in #( + e) ac_cv_dtrace_link=no echo 'BEGIN{}' > conftest.d "$DTRACE" $DFLAGS -G -s conftest.d -o conftest.o > /dev/null 2>&1 && \ ac_cv_dtrace_link=yes - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_dtrace_link" >&5 printf "%s\n" "$ac_cv_dtrace_link" >&6; } @@ -18237,7 +18915,7 @@ if test "$ac_sys_system" = "Linux-android"; then fi for name in $blocked_funcs; do - as_func_var=`printf "%s\n" "ac_cv_func_$name" | $as_tr_sh` + as_func_var=`printf "%s\n" "ac_cv_func_$name" | sed "$as_sed_sh"` eval "$as_func_var=no" @@ -19449,8 +20127,8 @@ printf %s "checking for $CC options needed to detect all undeclared functions... if test ${ac_cv_c_undeclared_builtin_options+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_save_CFLAGS=$CFLAGS +else case e in #( + e) ac_save_CFLAGS=$CFLAGS ac_cv_c_undeclared_builtin_options='cannot detect' for ac_arg in '' -fno-builtin; do CFLAGS="$ac_save_CFLAGS $ac_arg" @@ -19469,8 +20147,8 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : -else $as_nop - # This test program should compile successfully. +else case e in #( + e) # This test program should compile successfully. # No library function is consistently available on # freestanding implementations, so test against a dummy # declaration. Include always-available headers on the @@ -19498,26 +20176,29 @@ then : if test x"$ac_arg" = x then : ac_cv_c_undeclared_builtin_options='none needed' -else $as_nop - ac_cv_c_undeclared_builtin_options=$ac_arg +else case e in #( + e) ac_cv_c_undeclared_builtin_options=$ac_arg ;; +esac fi break fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done CFLAGS=$ac_save_CFLAGS - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5 printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; } case $ac_cv_c_undeclared_builtin_options in #( 'cannot detect') : - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "cannot make $CC report undeclared builtins -See \`config.log' for more details" "$LINENO" 5; } ;; #( +See 'config.log' for more details" "$LINENO" 5; } ;; #( 'none needed') : ac_c_undeclared_builtin_options='' ;; #( *) : @@ -19543,8 +20224,8 @@ printf %s "checking for chroot... " >&6; } if test ${ac_cv_func_chroot+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -19558,11 +20239,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_chroot=yes -else $as_nop - ac_cv_func_chroot=no +else case e in #( + e) ac_cv_func_chroot=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_chroot" >&5 printf "%s\n" "$ac_cv_func_chroot" >&6; } @@ -19582,8 +20265,8 @@ printf %s "checking for link... " >&6; } if test ${ac_cv_func_link+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -19597,11 +20280,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_link=yes -else $as_nop - ac_cv_func_link=no +else case e in #( + e) ac_cv_func_link=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_link" >&5 printf "%s\n" "$ac_cv_func_link" >&6; } @@ -19621,8 +20306,8 @@ printf %s "checking for symlink... " >&6; } if test ${ac_cv_func_symlink+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -19636,11 +20321,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_symlink=yes -else $as_nop - ac_cv_func_symlink=no +else case e in #( + e) ac_cv_func_symlink=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_symlink" >&5 printf "%s\n" "$ac_cv_func_symlink" >&6; } @@ -19660,8 +20347,8 @@ printf %s "checking for fchdir... " >&6; } if test ${ac_cv_func_fchdir+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -19675,11 +20362,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_fchdir=yes -else $as_nop - ac_cv_func_fchdir=no +else case e in #( + e) ac_cv_func_fchdir=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fchdir" >&5 printf "%s\n" "$ac_cv_func_fchdir" >&6; } @@ -19699,8 +20388,8 @@ printf %s "checking for fsync... " >&6; } if test ${ac_cv_func_fsync+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -19714,11 +20403,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_fsync=yes -else $as_nop - ac_cv_func_fsync=no +else case e in #( + e) ac_cv_func_fsync=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fsync" >&5 printf "%s\n" "$ac_cv_func_fsync" >&6; } @@ -19738,8 +20429,8 @@ printf %s "checking for fdatasync... " >&6; } if test ${ac_cv_func_fdatasync+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -19753,11 +20444,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_fdatasync=yes -else $as_nop - ac_cv_func_fdatasync=no +else case e in #( + e) ac_cv_func_fdatasync=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fdatasync" >&5 printf "%s\n" "$ac_cv_func_fdatasync" >&6; } @@ -19777,8 +20470,8 @@ printf %s "checking for epoll_create... " >&6; } if test ${ac_cv_func_epoll_create+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -19792,11 +20485,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_epoll_create=yes -else $as_nop - ac_cv_func_epoll_create=no +else case e in #( + e) ac_cv_func_epoll_create=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_epoll_create" >&5 printf "%s\n" "$ac_cv_func_epoll_create" >&6; } @@ -19816,8 +20511,8 @@ printf %s "checking for epoll_create1... " >&6; } if test ${ac_cv_func_epoll_create1+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -19831,11 +20526,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_epoll_create1=yes -else $as_nop - ac_cv_func_epoll_create1=no +else case e in #( + e) ac_cv_func_epoll_create1=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_epoll_create1" >&5 printf "%s\n" "$ac_cv_func_epoll_create1" >&6; } @@ -19855,8 +20552,8 @@ printf %s "checking for kqueue... " >&6; } if test ${ac_cv_func_kqueue+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -19873,11 +20570,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_kqueue=yes -else $as_nop - ac_cv_func_kqueue=no +else case e in #( + e) ac_cv_func_kqueue=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_kqueue" >&5 printf "%s\n" "$ac_cv_func_kqueue" >&6; } @@ -19897,8 +20596,8 @@ printf %s "checking for prlimit... " >&6; } if test ${ac_cv_func_prlimit+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -19915,11 +20614,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_prlimit=yes -else $as_nop - ac_cv_func_prlimit=no +else case e in #( + e) ac_cv_func_prlimit=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_prlimit" >&5 printf "%s\n" "$ac_cv_func_prlimit" >&6; } @@ -19940,8 +20641,8 @@ printf %s "checking for _dyld_shared_cache_contains_path... " >&6; } if test ${ac_cv_func__dyld_shared_cache_contains_path+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -19955,11 +20656,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func__dyld_shared_cache_contains_path=yes -else $as_nop - ac_cv_func__dyld_shared_cache_contains_path=no +else case e in #( + e) ac_cv_func__dyld_shared_cache_contains_path=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func__dyld_shared_cache_contains_path" >&5 printf "%s\n" "$ac_cv_func__dyld_shared_cache_contains_path" >&6; } @@ -19980,8 +20683,8 @@ printf %s "checking for memfd_create... " >&6; } if test ${ac_cv_func_memfd_create+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef HAVE_SYS_MMAN_H @@ -20002,11 +20705,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_memfd_create=yes -else $as_nop - ac_cv_func_memfd_create=no +else case e in #( + e) ac_cv_func_memfd_create=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_memfd_create" >&5 printf "%s\n" "$ac_cv_func_memfd_create" >&6; } @@ -20027,8 +20732,8 @@ printf %s "checking for eventfd... " >&6; } if test ${ac_cv_func_eventfd+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef HAVE_SYS_EVENTFD_H @@ -20046,11 +20751,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_eventfd=yes -else $as_nop - ac_cv_func_eventfd=no +else case e in #( + e) ac_cv_func_eventfd=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_eventfd" >&5 printf "%s\n" "$ac_cv_func_eventfd" >&6; } @@ -20071,8 +20778,8 @@ printf %s "checking for timerfd_create... " >&6; } if test ${ac_cv_func_timerfd_create+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef HAVE_SYS_TIMERFD_H @@ -20090,11 +20797,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_timerfd_create=yes -else $as_nop - ac_cv_func_timerfd_create=no +else case e in #( + e) ac_cv_func_timerfd_create=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_timerfd_create" >&5 printf "%s\n" "$ac_cv_func_timerfd_create" >&6; } @@ -20121,8 +20830,8 @@ printf %s "checking for ctermid_r... " >&6; } if test ${ac_cv_func_ctermid_r+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -20136,11 +20845,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_ctermid_r=yes -else $as_nop - ac_cv_func_ctermid_r=no +else case e in #( + e) ac_cv_func_ctermid_r=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_ctermid_r" >&5 printf "%s\n" "$ac_cv_func_ctermid_r" >&6; } @@ -20159,8 +20870,8 @@ printf %s "checking for flock declaration... " >&6; } if test ${ac_cv_flock_decl+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -20175,12 +20886,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_flock_decl=yes -else $as_nop - ac_cv_flock_decl=no - +else case e in #( + e) ac_cv_flock_decl=no + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_flock_decl" >&5 printf "%s\n" "$ac_cv_flock_decl" >&6; } @@ -20194,22 +20907,28 @@ if test "x$ac_cv_func_flock" = xyes then : printf "%s\n" "#define HAVE_FLOCK 1" >>confdefs.h -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for flock in -lbsd" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for flock in -lbsd" >&5 printf %s "checking for flock in -lbsd... " >&6; } if test ${ac_cv_lib_bsd_flock+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lbsd $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char flock (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char flock (void); int main (void) { @@ -20221,12 +20940,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_bsd_flock=yes -else $as_nop - ac_cv_lib_bsd_flock=no +else case e in #( + e) ac_cv_lib_bsd_flock=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsd_flock" >&5 printf "%s\n" "$ac_cv_lib_bsd_flock" >&6; } @@ -20234,7 +20955,8 @@ if test "x$ac_cv_lib_bsd_flock" = xyes then : FCNTL_LIBS="-lbsd" fi - + ;; +esac fi done @@ -20247,8 +20969,8 @@ printf %s "checking for getpagesize... " >&6; } if test ${ac_cv_func_getpagesize+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -20262,11 +20984,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_getpagesize=yes -else $as_nop - ac_cv_func_getpagesize=no +else case e in #( + e) ac_cv_func_getpagesize=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_getpagesize" >&5 printf "%s\n" "$ac_cv_func_getpagesize" >&6; } @@ -20285,8 +21009,8 @@ printf %s "checking for broken unsetenv... " >&6; } if test ${ac_cv_broken_unsetenv+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -20300,12 +21024,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_broken_unsetenv=no -else $as_nop - ac_cv_broken_unsetenv=yes - +else case e in #( + e) ac_cv_broken_unsetenv=yes + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_broken_unsetenv" >&5 printf "%s\n" "$ac_cv_broken_unsetenv" >&6; } @@ -20327,8 +21053,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_TRUE+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$TRUE"; then +else case e in #( + e) if test -n "$TRUE"; then ac_cv_prog_TRUE="$TRUE" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -20350,7 +21076,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi TRUE=$ac_cv_prog_TRUE if test -n "$TRUE"; then @@ -20372,16 +21099,22 @@ printf %s "checking for inet_aton in -lc... " >&6; } if test ${ac_cv_lib_c_inet_aton+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lc $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char inet_aton (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char inet_aton (void); int main (void) { @@ -20393,34 +21126,42 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_c_inet_aton=yes -else $as_nop - ac_cv_lib_c_inet_aton=no +else case e in #( + e) ac_cv_lib_c_inet_aton=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_inet_aton" >&5 printf "%s\n" "$ac_cv_lib_c_inet_aton" >&6; } if test "x$ac_cv_lib_c_inet_aton" = xyes then : $ac_cv_prog_TRUE -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for inet_aton in -lresolv" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for inet_aton in -lresolv" >&5 printf %s "checking for inet_aton in -lresolv... " >&6; } if test ${ac_cv_lib_resolv_inet_aton+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lresolv $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char inet_aton (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char inet_aton (void); int main (void) { @@ -20432,12 +21173,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_resolv_inet_aton=yes -else $as_nop - ac_cv_lib_resolv_inet_aton=no +else case e in #( + e) ac_cv_lib_resolv_inet_aton=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_resolv_inet_aton" >&5 printf "%s\n" "$ac_cv_lib_resolv_inet_aton" >&6; } @@ -20446,7 +21189,8 @@ then : SOCKET_LIBS="-lresolv" fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for hstrerror in -lc" >&5 @@ -20454,16 +21198,22 @@ printf %s "checking for hstrerror in -lc... " >&6; } if test ${ac_cv_lib_c_hstrerror+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lc $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char hstrerror (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char hstrerror (void); int main (void) { @@ -20475,34 +21225,42 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_c_hstrerror=yes -else $as_nop - ac_cv_lib_c_hstrerror=no +else case e in #( + e) ac_cv_lib_c_hstrerror=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_hstrerror" >&5 printf "%s\n" "$ac_cv_lib_c_hstrerror" >&6; } if test "x$ac_cv_lib_c_hstrerror" = xyes then : $ac_cv_prog_TRUE -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for hstrerror in -lresolv" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for hstrerror in -lresolv" >&5 printf %s "checking for hstrerror in -lresolv... " >&6; } if test ${ac_cv_lib_resolv_hstrerror+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lresolv $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char hstrerror (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char hstrerror (void); int main (void) { @@ -20514,12 +21272,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_resolv_hstrerror=yes -else $as_nop - ac_cv_lib_resolv_hstrerror=no +else case e in #( + e) ac_cv_lib_resolv_hstrerror=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_resolv_hstrerror" >&5 printf "%s\n" "$ac_cv_lib_resolv_hstrerror" >&6; } @@ -20528,7 +21288,8 @@ then : SOCKET_LIBS="-lresolv" fi - + ;; +esac fi @@ -20539,12 +21300,12 @@ printf %s "checking for chflags... " >&6; } if test ${ac_cv_have_chflags+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "$cross_compiling" = yes +else case e in #( + e) if test "$cross_compiling" = yes then : ac_cv_have_chflags=cross -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -20560,14 +21321,17 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_have_chflags=yes -else $as_nop - ac_cv_have_chflags=no +else case e in #( + e) ac_cv_have_chflags=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_chflags" >&5 printf "%s\n" "$ac_cv_have_chflags" >&6; } @@ -20576,8 +21340,9 @@ if test "$ac_cv_have_chflags" = cross ; then if test "x$ac_cv_func_chflags" = xyes then : ac_cv_have_chflags="yes" -else $as_nop - ac_cv_have_chflags="no" +else case e in #( + e) ac_cv_have_chflags="no" ;; +esac fi fi @@ -20592,12 +21357,12 @@ printf %s "checking for lchflags... " >&6; } if test ${ac_cv_have_lchflags+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "$cross_compiling" = yes +else case e in #( + e) if test "$cross_compiling" = yes then : ac_cv_have_lchflags=cross -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -20613,14 +21378,17 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_have_lchflags=yes -else $as_nop - ac_cv_have_lchflags=no +else case e in #( + e) ac_cv_have_lchflags=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_lchflags" >&5 printf "%s\n" "$ac_cv_have_lchflags" >&6; } @@ -20629,8 +21397,9 @@ if test "$ac_cv_have_lchflags" = cross ; then if test "x$ac_cv_func_lchflags" = xyes then : ac_cv_have_lchflags="yes" -else $as_nop - ac_cv_have_lchflags="no" +else case e in #( + e) ac_cv_have_lchflags="no" ;; +esac fi fi @@ -20737,16 +21506,22 @@ printf %s "checking for gzread in -lz... " >&6; } if test ${ac_cv_lib_z_gzread+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lz $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char gzread (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char gzread (void); int main (void) { @@ -20758,27 +21533,31 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_z_gzread=yes -else $as_nop - ac_cv_lib_z_gzread=no +else case e in #( + e) ac_cv_lib_z_gzread=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_gzread" >&5 printf "%s\n" "$ac_cv_lib_z_gzread" >&6; } if test "x$ac_cv_lib_z_gzread" = xyes then : have_zlib=yes -else $as_nop - have_zlib=no +else case e in #( + e) have_zlib=no ;; +esac fi LIBS=$py_check_lib_save_LIBS -else $as_nop - have_zlib=no +else case e in #( + e) have_zlib=no ;; +esac fi done @@ -20793,16 +21572,22 @@ printf %s "checking for inflateCopy in -lz... " >&6; } if test ${ac_cv_lib_z_inflateCopy+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lz $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char inflateCopy (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char inflateCopy (void); int main (void) { @@ -20814,12 +21599,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_z_inflateCopy=yes -else $as_nop - ac_cv_lib_z_inflateCopy=no +else case e in #( + e) ac_cv_lib_z_inflateCopy=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_inflateCopy" >&5 printf "%s\n" "$ac_cv_lib_z_inflateCopy" >&6; } @@ -20866,16 +21653,22 @@ printf %s "checking for gzread in -lz... " >&6; } if test ${ac_cv_lib_z_gzread+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lz $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char gzread (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char gzread (void); int main (void) { @@ -20887,27 +21680,31 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_z_gzread=yes -else $as_nop - ac_cv_lib_z_gzread=no +else case e in #( + e) ac_cv_lib_z_gzread=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_gzread" >&5 printf "%s\n" "$ac_cv_lib_z_gzread" >&6; } if test "x$ac_cv_lib_z_gzread" = xyes then : have_zlib=yes -else $as_nop - have_zlib=no +else case e in #( + e) have_zlib=no ;; +esac fi LIBS=$py_check_lib_save_LIBS -else $as_nop - have_zlib=no +else case e in #( + e) have_zlib=no ;; +esac fi done @@ -20922,16 +21719,22 @@ printf %s "checking for inflateCopy in -lz... " >&6; } if test ${ac_cv_lib_z_inflateCopy+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lz $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char inflateCopy (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char inflateCopy (void); int main (void) { @@ -20943,12 +21746,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_z_inflateCopy=yes -else $as_nop - ac_cv_lib_z_inflateCopy=no +else case e in #( + e) ac_cv_lib_z_inflateCopy=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_inflateCopy" >&5 printf "%s\n" "$ac_cv_lib_z_inflateCopy" >&6; } @@ -21084,16 +21889,22 @@ printf %s "checking for BZ2_bzCompress in -lbz2... " >&6; } if test ${ac_cv_lib_bz2_BZ2_bzCompress+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lbz2 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char BZ2_bzCompress (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char BZ2_bzCompress (void); int main (void) { @@ -21105,25 +21916,29 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_bz2_BZ2_bzCompress=yes -else $as_nop - ac_cv_lib_bz2_BZ2_bzCompress=no +else case e in #( + e) ac_cv_lib_bz2_BZ2_bzCompress=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bz2_BZ2_bzCompress" >&5 printf "%s\n" "$ac_cv_lib_bz2_BZ2_bzCompress" >&6; } if test "x$ac_cv_lib_bz2_BZ2_bzCompress" = xyes then : have_bzip2=yes -else $as_nop - have_bzip2=no +else case e in #( + e) have_bzip2=no ;; +esac fi -else $as_nop - have_bzip2=no +else case e in #( + e) have_bzip2=no ;; +esac fi done @@ -21166,16 +21981,22 @@ printf %s "checking for BZ2_bzCompress in -lbz2... " >&6; } if test ${ac_cv_lib_bz2_BZ2_bzCompress+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lbz2 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char BZ2_bzCompress (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char BZ2_bzCompress (void); int main (void) { @@ -21187,25 +22008,29 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_bz2_BZ2_bzCompress=yes -else $as_nop - ac_cv_lib_bz2_BZ2_bzCompress=no +else case e in #( + e) ac_cv_lib_bz2_BZ2_bzCompress=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bz2_BZ2_bzCompress" >&5 printf "%s\n" "$ac_cv_lib_bz2_BZ2_bzCompress" >&6; } if test "x$ac_cv_lib_bz2_BZ2_bzCompress" = xyes then : have_bzip2=yes -else $as_nop - have_bzip2=no +else case e in #( + e) have_bzip2=no ;; +esac fi -else $as_nop - have_bzip2=no +else case e in #( + e) have_bzip2=no ;; +esac fi done @@ -21312,16 +22137,22 @@ printf %s "checking for lzma_easy_encoder in -llzma... " >&6; } if test ${ac_cv_lib_lzma_lzma_easy_encoder+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-llzma $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char lzma_easy_encoder (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char lzma_easy_encoder (void); int main (void) { @@ -21333,25 +22164,29 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_lzma_lzma_easy_encoder=yes -else $as_nop - ac_cv_lib_lzma_lzma_easy_encoder=no +else case e in #( + e) ac_cv_lib_lzma_lzma_easy_encoder=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lzma_lzma_easy_encoder" >&5 printf "%s\n" "$ac_cv_lib_lzma_lzma_easy_encoder" >&6; } if test "x$ac_cv_lib_lzma_lzma_easy_encoder" = xyes then : have_liblzma=yes -else $as_nop - have_liblzma=no +else case e in #( + e) have_liblzma=no ;; +esac fi -else $as_nop - have_liblzma=no +else case e in #( + e) have_liblzma=no ;; +esac fi done @@ -21394,16 +22229,22 @@ printf %s "checking for lzma_easy_encoder in -llzma... " >&6; } if test ${ac_cv_lib_lzma_lzma_easy_encoder+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-llzma $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char lzma_easy_encoder (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char lzma_easy_encoder (void); int main (void) { @@ -21415,25 +22256,29 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_lzma_lzma_easy_encoder=yes -else $as_nop - ac_cv_lib_lzma_lzma_easy_encoder=no +else case e in #( + e) ac_cv_lib_lzma_lzma_easy_encoder=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lzma_lzma_easy_encoder" >&5 printf "%s\n" "$ac_cv_lib_lzma_lzma_easy_encoder" >&6; } if test "x$ac_cv_lib_lzma_lzma_easy_encoder" = xyes then : have_liblzma=yes -else $as_nop - have_liblzma=no +else case e in #( + e) have_liblzma=no ;; +esac fi -else $as_nop - have_liblzma=no +else case e in #( + e) have_liblzma=no ;; +esac fi done @@ -21469,8 +22314,8 @@ printf %s "checking for hstrerror... " >&6; } if test ${ac_cv_func_hstrerror+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -21484,11 +22329,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_hstrerror=yes -else $as_nop - ac_cv_func_hstrerror=no +else case e in #( + e) ac_cv_func_hstrerror=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_hstrerror" >&5 printf "%s\n" "$ac_cv_func_hstrerror" >&6; } @@ -21508,8 +22355,8 @@ printf %s "checking for getservbyname... " >&6; } if test ${ac_cv_func_getservbyname+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -21523,11 +22370,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_getservbyname=yes -else $as_nop - ac_cv_func_getservbyname=no +else case e in #( + e) ac_cv_func_getservbyname=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_getservbyname" >&5 printf "%s\n" "$ac_cv_func_getservbyname" >&6; } @@ -21547,8 +22396,8 @@ printf %s "checking for getservbyport... " >&6; } if test ${ac_cv_func_getservbyport+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -21562,11 +22411,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_getservbyport=yes -else $as_nop - ac_cv_func_getservbyport=no +else case e in #( + e) ac_cv_func_getservbyport=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_getservbyport" >&5 printf "%s\n" "$ac_cv_func_getservbyport" >&6; } @@ -21586,8 +22437,8 @@ printf %s "checking for gethostbyname... " >&6; } if test ${ac_cv_func_gethostbyname+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -21601,11 +22452,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_gethostbyname=yes -else $as_nop - ac_cv_func_gethostbyname=no +else case e in #( + e) ac_cv_func_gethostbyname=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_gethostbyname" >&5 printf "%s\n" "$ac_cv_func_gethostbyname" >&6; } @@ -21625,8 +22478,8 @@ printf %s "checking for gethostbyaddr... " >&6; } if test ${ac_cv_func_gethostbyaddr+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -21640,11 +22493,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_gethostbyaddr=yes -else $as_nop - ac_cv_func_gethostbyaddr=no +else case e in #( + e) ac_cv_func_gethostbyaddr=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_gethostbyaddr" >&5 printf "%s\n" "$ac_cv_func_gethostbyaddr" >&6; } @@ -21664,8 +22519,8 @@ printf %s "checking for getprotobyname... " >&6; } if test ${ac_cv_func_getprotobyname+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -21679,11 +22534,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_getprotobyname=yes -else $as_nop - ac_cv_func_getprotobyname=no +else case e in #( + e) ac_cv_func_getprotobyname=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_getprotobyname" >&5 printf "%s\n" "$ac_cv_func_getprotobyname" >&6; } @@ -21706,8 +22563,8 @@ printf %s "checking for inet_aton... " >&6; } if test ${ac_cv_func_inet_aton+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -21726,11 +22583,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_inet_aton=yes -else $as_nop - ac_cv_func_inet_aton=no +else case e in #( + e) ac_cv_func_inet_aton=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_inet_aton" >&5 printf "%s\n" "$ac_cv_func_inet_aton" >&6; } @@ -21750,8 +22609,8 @@ printf %s "checking for inet_ntoa... " >&6; } if test ${ac_cv_func_inet_ntoa+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -21770,11 +22629,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_inet_ntoa=yes -else $as_nop - ac_cv_func_inet_ntoa=no +else case e in #( + e) ac_cv_func_inet_ntoa=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_inet_ntoa" >&5 printf "%s\n" "$ac_cv_func_inet_ntoa" >&6; } @@ -21794,8 +22655,8 @@ printf %s "checking for inet_pton... " >&6; } if test ${ac_cv_func_inet_pton+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -21814,11 +22675,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_inet_pton=yes -else $as_nop - ac_cv_func_inet_pton=no +else case e in #( + e) ac_cv_func_inet_pton=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_inet_pton" >&5 printf "%s\n" "$ac_cv_func_inet_pton" >&6; } @@ -21838,8 +22701,8 @@ printf %s "checking for getpeername... " >&6; } if test ${ac_cv_func_getpeername+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -21858,11 +22721,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_getpeername=yes -else $as_nop - ac_cv_func_getpeername=no +else case e in #( + e) ac_cv_func_getpeername=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_getpeername" >&5 printf "%s\n" "$ac_cv_func_getpeername" >&6; } @@ -21882,8 +22747,8 @@ printf %s "checking for getsockname... " >&6; } if test ${ac_cv_func_getsockname+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -21902,11 +22767,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_getsockname=yes -else $as_nop - ac_cv_func_getsockname=no +else case e in #( + e) ac_cv_func_getsockname=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_getsockname" >&5 printf "%s\n" "$ac_cv_func_getsockname" >&6; } @@ -21926,8 +22793,8 @@ printf %s "checking for accept... " >&6; } if test ${ac_cv_func_accept+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -21946,11 +22813,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_accept=yes -else $as_nop - ac_cv_func_accept=no +else case e in #( + e) ac_cv_func_accept=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_accept" >&5 printf "%s\n" "$ac_cv_func_accept" >&6; } @@ -21970,8 +22839,8 @@ printf %s "checking for bind... " >&6; } if test ${ac_cv_func_bind+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -21990,11 +22859,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_bind=yes -else $as_nop - ac_cv_func_bind=no +else case e in #( + e) ac_cv_func_bind=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_bind" >&5 printf "%s\n" "$ac_cv_func_bind" >&6; } @@ -22014,8 +22885,8 @@ printf %s "checking for connect... " >&6; } if test ${ac_cv_func_connect+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -22034,11 +22905,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_connect=yes -else $as_nop - ac_cv_func_connect=no +else case e in #( + e) ac_cv_func_connect=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_connect" >&5 printf "%s\n" "$ac_cv_func_connect" >&6; } @@ -22058,8 +22931,8 @@ printf %s "checking for listen... " >&6; } if test ${ac_cv_func_listen+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -22078,11 +22951,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_listen=yes -else $as_nop - ac_cv_func_listen=no +else case e in #( + e) ac_cv_func_listen=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_listen" >&5 printf "%s\n" "$ac_cv_func_listen" >&6; } @@ -22102,8 +22977,8 @@ printf %s "checking for recvfrom... " >&6; } if test ${ac_cv_func_recvfrom+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -22122,11 +22997,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_recvfrom=yes -else $as_nop - ac_cv_func_recvfrom=no +else case e in #( + e) ac_cv_func_recvfrom=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_recvfrom" >&5 printf "%s\n" "$ac_cv_func_recvfrom" >&6; } @@ -22146,8 +23023,8 @@ printf %s "checking for sendto... " >&6; } if test ${ac_cv_func_sendto+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -22166,11 +23043,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_sendto=yes -else $as_nop - ac_cv_func_sendto=no +else case e in #( + e) ac_cv_func_sendto=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_sendto" >&5 printf "%s\n" "$ac_cv_func_sendto" >&6; } @@ -22190,8 +23069,8 @@ printf %s "checking for setsockopt... " >&6; } if test ${ac_cv_func_setsockopt+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -22210,11 +23089,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_setsockopt=yes -else $as_nop - ac_cv_func_setsockopt=no +else case e in #( + e) ac_cv_func_setsockopt=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_setsockopt" >&5 printf "%s\n" "$ac_cv_func_setsockopt" >&6; } @@ -22234,8 +23115,8 @@ printf %s "checking for socket... " >&6; } if test ${ac_cv_func_socket+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -22254,11 +23135,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_socket=yes -else $as_nop - ac_cv_func_socket=no +else case e in #( + e) ac_cv_func_socket=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_socket" >&5 printf "%s\n" "$ac_cv_func_socket" >&6; } @@ -22280,8 +23163,8 @@ printf %s "checking for setgroups... " >&6; } if test ${ac_cv_func_setgroups+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -22300,11 +23183,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_setgroups=yes -else $as_nop - ac_cv_func_setgroups=no +else case e in #( + e) ac_cv_func_setgroups=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_setgroups" >&5 printf "%s\n" "$ac_cv_func_setgroups" >&6; } @@ -22328,22 +23213,28 @@ if test "x$ac_cv_func_openpty" = xyes then : printf "%s\n" "#define HAVE_OPENPTY 1" >>confdefs.h -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for openpty in -lutil" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for openpty in -lutil" >&5 printf %s "checking for openpty in -lutil... " >&6; } if test ${ac_cv_lib_util_openpty+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lutil $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char openpty (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char openpty (void); int main (void) { @@ -22355,12 +23246,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_util_openpty=yes -else $as_nop - ac_cv_lib_util_openpty=no +else case e in #( + e) ac_cv_lib_util_openpty=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_openpty" >&5 printf "%s\n" "$ac_cv_lib_util_openpty" >&6; } @@ -22368,22 +23261,28 @@ if test "x$ac_cv_lib_util_openpty" = xyes then : printf "%s\n" "#define HAVE_OPENPTY 1" >>confdefs.h LIBS="$LIBS -lutil" -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for openpty in -lbsd" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for openpty in -lbsd" >&5 printf %s "checking for openpty in -lbsd... " >&6; } if test ${ac_cv_lib_bsd_openpty+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lbsd $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char openpty (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char openpty (void); int main (void) { @@ -22395,12 +23294,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_bsd_openpty=yes -else $as_nop - ac_cv_lib_bsd_openpty=no +else case e in #( + e) ac_cv_lib_bsd_openpty=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsd_openpty" >&5 printf "%s\n" "$ac_cv_lib_bsd_openpty" >&6; } @@ -22409,9 +23310,11 @@ then : printf "%s\n" "#define HAVE_OPENPTY 1" >>confdefs.h LIBS="$LIBS -lbsd" fi - + ;; +esac fi - + ;; +esac fi done @@ -22420,15 +23323,21 @@ printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_func_search_save_LIBS=$LIBS +else case e in #( + e) ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char login_tty (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char login_tty (void); int main (void) { @@ -22459,11 +23368,13 @@ done if test ${ac_cv_search_login_tty+y} then : -else $as_nop - ac_cv_search_login_tty=no +else case e in #( + e) ac_cv_search_login_tty=no ;; +esac fi rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS +LIBS=$ac_func_search_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_login_tty" >&5 printf "%s\n" "$ac_cv_search_login_tty" >&6; } @@ -22485,22 +23396,28 @@ if test "x$ac_cv_func_forkpty" = xyes then : printf "%s\n" "#define HAVE_FORKPTY 1" >>confdefs.h -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for forkpty in -lutil" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for forkpty in -lutil" >&5 printf %s "checking for forkpty in -lutil... " >&6; } if test ${ac_cv_lib_util_forkpty+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lutil $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char forkpty (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char forkpty (void); int main (void) { @@ -22512,12 +23429,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_util_forkpty=yes -else $as_nop - ac_cv_lib_util_forkpty=no +else case e in #( + e) ac_cv_lib_util_forkpty=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_forkpty" >&5 printf "%s\n" "$ac_cv_lib_util_forkpty" >&6; } @@ -22525,22 +23444,28 @@ if test "x$ac_cv_lib_util_forkpty" = xyes then : printf "%s\n" "#define HAVE_FORKPTY 1" >>confdefs.h LIBS="$LIBS -lutil" -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for forkpty in -lbsd" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for forkpty in -lbsd" >&5 printf %s "checking for forkpty in -lbsd... " >&6; } if test ${ac_cv_lib_bsd_forkpty+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lbsd $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char forkpty (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char forkpty (void); int main (void) { @@ -22552,12 +23477,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_bsd_forkpty=yes -else $as_nop - ac_cv_lib_bsd_forkpty=no +else case e in #( + e) ac_cv_lib_bsd_forkpty=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsd_forkpty" >&5 printf "%s\n" "$ac_cv_lib_bsd_forkpty" >&6; } @@ -22566,9 +23493,11 @@ then : printf "%s\n" "#define HAVE_FORKPTY 1" >>confdefs.h LIBS="$LIBS -lbsd" fi - + ;; +esac fi - + ;; +esac fi done @@ -22617,13 +23546,14 @@ if test "x$ac_cv_func_dup2" = xyes then : printf "%s\n" "#define HAVE_DUP2 1" >>confdefs.h -else $as_nop - case " $LIBOBJS " in +else case e in #( + e) case " $LIBOBJS " in *" dup2.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS dup2.$ac_objext" ;; esac - + ;; +esac fi @@ -22706,23 +23636,29 @@ if test "x$ac_cv_func_clock_gettime" = xyes then : printf "%s\n" "#define HAVE_CLOCK_GETTIME 1" >>confdefs.h -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt" >&5 printf %s "checking for clock_gettime in -lrt... " >&6; } if test ${ac_cv_lib_rt_clock_gettime+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lrt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char clock_gettime (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char clock_gettime (void); int main (void) { @@ -22734,12 +23670,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_rt_clock_gettime=yes -else $as_nop - ac_cv_lib_rt_clock_gettime=no +else case e in #( + e) ac_cv_lib_rt_clock_gettime=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_gettime" >&5 printf "%s\n" "$ac_cv_lib_rt_clock_gettime" >&6; } @@ -22755,7 +23693,8 @@ printf "%s\n" "#define TIMEMODULE_LIB rt" >>confdefs.h fi - + ;; +esac fi done @@ -22768,23 +23707,29 @@ if test "x$ac_cv_func_clock_getres" = xyes then : printf "%s\n" "#define HAVE_CLOCK_GETRES 1" >>confdefs.h -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for clock_getres in -lrt" >&5 printf %s "checking for clock_getres in -lrt... " >&6; } if test ${ac_cv_lib_rt_clock_getres+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lrt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char clock_getres (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char clock_getres (void); int main (void) { @@ -22796,12 +23741,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_rt_clock_getres=yes -else $as_nop - ac_cv_lib_rt_clock_getres=no +else case e in #( + e) ac_cv_lib_rt_clock_getres=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_getres" >&5 printf "%s\n" "$ac_cv_lib_rt_clock_getres" >&6; } @@ -22813,7 +23760,8 @@ then : fi - + ;; +esac fi done @@ -22831,23 +23779,29 @@ if test "x$ac_cv_func_clock_settime" = xyes then : printf "%s\n" "#define HAVE_CLOCK_SETTIME 1" >>confdefs.h -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for clock_settime in -lrt" >&5 printf %s "checking for clock_settime in -lrt... " >&6; } if test ${ac_cv_lib_rt_clock_settime+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lrt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char clock_settime (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char clock_settime (void); int main (void) { @@ -22859,12 +23813,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_rt_clock_settime=yes -else $as_nop - ac_cv_lib_rt_clock_settime=no +else case e in #( + e) ac_cv_lib_rt_clock_settime=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_settime" >&5 printf "%s\n" "$ac_cv_lib_rt_clock_settime" >&6; } @@ -22876,7 +23832,8 @@ then : fi - + ;; +esac fi done @@ -22894,23 +23851,29 @@ if test "x$ac_cv_func_clock_nanosleep" = xyes then : printf "%s\n" "#define HAVE_CLOCK_NANOSLEEP 1" >>confdefs.h -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for clock_nanosleep in -lrt" >&5 printf %s "checking for clock_nanosleep in -lrt... " >&6; } if test ${ac_cv_lib_rt_clock_nanosleep+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lrt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char clock_nanosleep (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char clock_nanosleep (void); int main (void) { @@ -22922,12 +23885,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_rt_clock_nanosleep=yes -else $as_nop - ac_cv_lib_rt_clock_nanosleep=no +else case e in #( + e) ac_cv_lib_rt_clock_nanosleep=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_nanosleep" >&5 printf "%s\n" "$ac_cv_lib_rt_clock_nanosleep" >&6; } @@ -22939,7 +23904,8 @@ then : fi - + ;; +esac fi done @@ -22953,23 +23919,29 @@ if test "x$ac_cv_func_nanosleep" = xyes then : printf "%s\n" "#define HAVE_NANOSLEEP 1" >>confdefs.h -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for nanosleep in -lrt" >&5 printf %s "checking for nanosleep in -lrt... " >&6; } if test ${ac_cv_lib_rt_nanosleep+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lrt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char nanosleep (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char nanosleep (void); int main (void) { @@ -22981,12 +23953,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_rt_nanosleep=yes -else $as_nop - ac_cv_lib_rt_nanosleep=no +else case e in #( + e) ac_cv_lib_rt_nanosleep=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_nanosleep" >&5 printf "%s\n" "$ac_cv_lib_rt_nanosleep" >&6; } @@ -22998,7 +23972,8 @@ then : fi - + ;; +esac fi done @@ -23008,8 +23983,8 @@ printf %s "checking for major, minor, and makedev... " >&6; } if test ${ac_cv_device_macros+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -23035,12 +24010,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_device_macros=yes -else $as_nop - ac_cv_device_macros=no +else case e in #( + e) ac_cv_device_macros=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_device_macros" >&5 printf "%s\n" "$ac_cv_device_macros" >&6; } @@ -23064,8 +24041,8 @@ printf %s "checking for getaddrinfo... " >&6; } if test ${ac_cv_func_getaddrinfo+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -23085,12 +24062,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_func_getaddrinfo=yes -else $as_nop - ac_cv_func_getaddrinfo=no +else case e in #( + e) ac_cv_func_getaddrinfo=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_getaddrinfo" >&5 printf "%s\n" "$ac_cv_func_getaddrinfo" >&6; } @@ -23103,8 +24082,8 @@ printf %s "checking getaddrinfo bug... " >&6; } if test ${ac_cv_buggy_getaddrinfo+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "$cross_compiling" = yes +else case e in #( + e) if test "$cross_compiling" = yes then : if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then @@ -23114,8 +24093,8 @@ elif test "${enable_ipv6+set}" = set; then else ac_cv_buggy_getaddrinfo=yes fi -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -23211,13 +24190,16 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_buggy_getaddrinfo=no -else $as_nop - ac_cv_buggy_getaddrinfo=yes +else case e in #( + e) ac_cv_buggy_getaddrinfo=yes ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_buggy_getaddrinfo" >&5 printf "%s\n" "$ac_cv_buggy_getaddrinfo" >&6; } @@ -23253,8 +24235,8 @@ printf %s "checking whether struct tm is in sys/time.h or time.h... " >&6; } if test ${ac_cv_struct_tm+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include @@ -23272,10 +24254,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_struct_tm=time.h -else $as_nop - ac_cv_struct_tm=sys/time.h +else case e in #( + e) ac_cv_struct_tm=sys/time.h ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5 printf "%s\n" "$ac_cv_struct_tm" >&6; } @@ -23307,8 +24291,9 @@ else if test "x$ac_cv_have_decl_tzname" = xyes then : ac_have_decl=1 -else $as_nop - ac_have_decl=0 +else case e in #( + e) ac_have_decl=0 ;; +esac fi printf "%s\n" "#define HAVE_DECL_TZNAME $ac_have_decl" >>confdefs.h @@ -23317,8 +24302,8 @@ printf %s "checking for tzname... " >&6; } if test ${ac_cv_var_tzname+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #if !HAVE_DECL_TZNAME @@ -23336,11 +24321,13 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_var_tzname=yes -else $as_nop - ac_cv_var_tzname=no +else case e in #( + e) ac_cv_var_tzname=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext + conftest$ac_exeext conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_var_tzname" >&5 printf "%s\n" "$ac_cv_var_tzname" >&6; } @@ -23447,8 +24434,8 @@ printf %s "checking for time.h that defines altzone... " >&6; } if test ${ac_cv_header_time_altzone+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -23463,11 +24450,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_header_time_altzone=yes -else $as_nop - ac_cv_header_time_altzone=no +else case e in #( + e) ac_cv_header_time_altzone=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time_altzone" >&5 printf "%s\n" "$ac_cv_header_time_altzone" >&6; } @@ -23482,8 +24471,8 @@ printf %s "checking for addrinfo... " >&6; } if test ${ac_cv_struct_addrinfo+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -23497,10 +24486,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_struct_addrinfo=yes -else $as_nop - ac_cv_struct_addrinfo=no +else case e in #( + e) ac_cv_struct_addrinfo=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_addrinfo" >&5 printf "%s\n" "$ac_cv_struct_addrinfo" >&6; } @@ -23515,8 +24506,8 @@ printf %s "checking for sockaddr_storage... " >&6; } if test ${ac_cv_struct_sockaddr_storage+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ # include @@ -23532,10 +24523,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_struct_sockaddr_storage=yes -else $as_nop - ac_cv_struct_sockaddr_storage=no +else case e in #( + e) ac_cv_struct_sockaddr_storage=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_sockaddr_storage" >&5 printf "%s\n" "$ac_cv_struct_sockaddr_storage" >&6; } @@ -23550,8 +24543,8 @@ printf %s "checking for sockaddr_alg... " >&6; } if test ${ac_cv_struct_sockaddr_alg+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ # include @@ -23568,10 +24561,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_struct_sockaddr_alg=yes -else $as_nop - ac_cv_struct_sockaddr_alg=no +else case e in #( + e) ac_cv_struct_sockaddr_alg=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_sockaddr_alg" >&5 printf "%s\n" "$ac_cv_struct_sockaddr_alg" >&6; } @@ -23588,8 +24583,8 @@ printf %s "checking for an ANSI C-conforming const... " >&6; } if test ${ac_cv_c_const+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -23653,10 +24648,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_const=yes -else $as_nop - ac_cv_c_const=no +else case e in #( + e) ac_cv_c_const=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 printf "%s\n" "$ac_cv_c_const" >&6; } @@ -23672,8 +24669,8 @@ printf %s "checking for working signed char... " >&6; } if test ${ac_cv_working_signed_char_c+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -23688,11 +24685,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_working_signed_char_c=yes -else $as_nop - ac_cv_working_signed_char_c=no +else case e in #( + e) ac_cv_working_signed_char_c=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_working_signed_char_c" >&5 printf "%s\n" "$ac_cv_working_signed_char_c" >&6; } @@ -23710,8 +24709,8 @@ printf %s "checking for prototypes... " >&6; } if test ${ac_cv_function_prototypes+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int foo(int x) { return 0; } @@ -23726,11 +24725,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_function_prototypes=yes -else $as_nop - ac_cv_function_prototypes=no +else case e in #( + e) ac_cv_function_prototypes=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_function_prototypes" >&5 printf "%s\n" "$ac_cv_function_prototypes" >&6; } @@ -23752,8 +24753,8 @@ printf %s "checking for socketpair... " >&6; } if test ${ac_cv_func_socketpair+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -23770,11 +24771,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_socketpair=yes -else $as_nop - ac_cv_func_socketpair=no +else case e in #( + e) ac_cv_func_socketpair=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_socketpair" >&5 printf "%s\n" "$ac_cv_func_socketpair" >&6; } @@ -23794,8 +24797,8 @@ printf %s "checking if sockaddr has sa_len member... " >&6; } if test ${ac_cv_struct_sockaddr_sa_len+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -23812,11 +24815,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_struct_sockaddr_sa_len=yes -else $as_nop - ac_cv_struct_sockaddr_sa_len=no +else case e in #( + e) ac_cv_struct_sockaddr_sa_len=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_sockaddr_sa_len" >&5 printf "%s\n" "$ac_cv_struct_sockaddr_sa_len" >&6; } @@ -23873,8 +24878,8 @@ printf "%s\n" "#define HAVE_GETHOSTBYNAME_R_6_ARG 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking gethostbyname_r with 5 args" >&5 @@ -23911,8 +24916,8 @@ printf "%s\n" "#define HAVE_GETHOSTBYNAME_R_5_ARG 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking gethostbyname_r with 3 args" >&5 @@ -23947,23 +24952,26 @@ printf "%s\n" "#define HAVE_GETHOSTBYNAME_R_3_ARG 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$OLD_CFLAGS -else $as_nop - +else case e in #( + e) ac_fn_c_check_func "$LINENO" "gethostbyname" "ac_cv_func_gethostbyname" if test "x$ac_cv_func_gethostbyname" = xyes then : @@ -23971,7 +24979,8 @@ then : fi - + ;; +esac fi @@ -23988,22 +24997,28 @@ ac_fn_c_check_func "$LINENO" "__fpu_control" "ac_cv_func___fpu_control" if test "x$ac_cv_func___fpu_control" = xyes then : -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for __fpu_control in -lieee" >&5 +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for __fpu_control in -lieee" >&5 printf %s "checking for __fpu_control in -lieee... " >&6; } if test ${ac_cv_lib_ieee___fpu_control+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lieee $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char __fpu_control (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char __fpu_control (void); int main (void) { @@ -24015,12 +25030,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ieee___fpu_control=yes -else $as_nop - ac_cv_lib_ieee___fpu_control=no +else case e in #( + e) ac_cv_lib_ieee___fpu_control=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ieee___fpu_control" >&5 printf "%s\n" "$ac_cv_lib_ieee___fpu_control" >&6; } @@ -24032,7 +25049,8 @@ then : fi - + ;; +esac fi @@ -24059,9 +25077,10 @@ then LIBM=$withval printf "%s\n" "set LIBM=\"$withval\"" >&6; } else as_fn_error $? "proper usage is --with-libm=STRING" "$LINENO" 5 fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: default LIBM=\"$LIBM\"" >&5 -printf "%s\n" "default LIBM=\"$LIBM\"" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: default LIBM=\"$LIBM\"" >&5 +printf "%s\n" "default LIBM=\"$LIBM\"" >&6; } ;; +esac fi @@ -24084,9 +25103,10 @@ then LIBC=$withval printf "%s\n" "set LIBC=\"$withval\"" >&6; } else as_fn_error $? "proper usage is --with-libc=STRING" "$LINENO" 5 fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: default LIBC=\"$LIBC\"" >&5 -printf "%s\n" "default LIBC=\"$LIBC\"" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: default LIBC=\"$LIBC\"" >&5 +printf "%s\n" "default LIBC=\"$LIBC\"" >&6; } ;; +esac fi @@ -24100,8 +25120,8 @@ printf %s "checking for x64 gcc inline assembler... " >&6; } if test ${ac_cv_gcc_asm_for_x64+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -24118,12 +25138,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_gcc_asm_for_x64=yes -else $as_nop - ac_cv_gcc_asm_for_x64=no +else case e in #( + e) ac_cv_gcc_asm_for_x64=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_gcc_asm_for_x64" >&5 printf "%s\n" "$ac_cv_gcc_asm_for_x64" >&6; } @@ -24146,8 +25168,8 @@ printf %s "checking whether float word ordering is bigendian... " >&6; } if test ${ax_cv_c_float_words_bigendian+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_cv_c_float_words_bigendian=unknown cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -24184,7 +25206,8 @@ fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext + conftest$ac_exeext conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_c_float_words_bigendian" >&5 printf "%s\n" "$ax_cv_c_float_words_bigendian" >&6; } @@ -24232,8 +25255,8 @@ printf %s "checking whether we can use gcc inline assembler to get and set x87 c if test ${ac_cv_gcc_asm_for_x87+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -24252,12 +25275,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_gcc_asm_for_x87=yes -else $as_nop - ac_cv_gcc_asm_for_x87=no +else case e in #( + e) ac_cv_gcc_asm_for_x87=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_gcc_asm_for_x87" >&5 printf "%s\n" "$ac_cv_gcc_asm_for_x87" >&6; } @@ -24275,8 +25300,8 @@ printf %s "checking whether we can use gcc inline assembler to get and set mc688 if test ${ac_cv_gcc_asm_for_mc68881+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -24295,12 +25320,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_gcc_asm_for_mc68881=yes -else $as_nop - ac_cv_gcc_asm_for_mc68881=no +else case e in #( + e) ac_cv_gcc_asm_for_mc68881=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_gcc_asm_for_mc68881" >&5 printf "%s\n" "$ac_cv_gcc_asm_for_mc68881" >&6; } @@ -24323,16 +25350,16 @@ printf %s "checking for x87-style double rounding... " >&6; } if test ${ac_cv_x87_double_rounding+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) # $BASECFLAGS may affect the result ac_save_cc="$CC" CC="$CC $BASECFLAGS" if test "$cross_compiling" = yes then : ac_cv_x87_double_rounding=no -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -24358,15 +25385,18 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_x87_double_rounding=no -else $as_nop - ac_cv_x87_double_rounding=yes +else case e in #( + e) ac_cv_x87_double_rounding=yes ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi CC="$ac_save_cc" - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_x87_double_rounding" >&5 printf "%s\n" "$ac_cv_x87_double_rounding" >&6; } @@ -24390,17 +25420,18 @@ LIBS="$LIBS $LIBM" for ac_func in acosh asinh atanh erf erfc expm1 log1p log2 do : - as_ac_var=`printf "%s\n" "ac_cv_func_$ac_func" | $as_tr_sh` + as_ac_var=`printf "%s\n" "ac_cv_func_$ac_func" | sed "$as_sed_sh"` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes" then : cat >>confdefs.h <<_ACEOF -#define `printf "%s\n" "HAVE_$ac_func" | $as_tr_cpp` 1 +#define `printf "%s\n" "HAVE_$ac_func" | sed "$as_sed_cpp"` 1 _ACEOF -else $as_nop - as_fn_error $? "Python requires C99 compatible libm" "$LINENO" 5 - +else case e in #( + e) as_fn_error $? "Python requires C99 compatible libm" "$LINENO" 5 + ;; +esac fi done @@ -24411,12 +25442,12 @@ printf %s "checking whether POSIX semaphores are enabled... " >&6; } if test ${ac_cv_posix_semaphores_enabled+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "$cross_compiling" = yes +else case e in #( + e) if test "$cross_compiling" = yes then : ac_cv_posix_semaphores_enabled=yes -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -24442,14 +25473,17 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_posix_semaphores_enabled=yes -else $as_nop - ac_cv_posix_semaphores_enabled=no +else case e in #( + e) ac_cv_posix_semaphores_enabled=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_posix_semaphores_enabled" >&5 printf "%s\n" "$ac_cv_posix_semaphores_enabled" >&6; } @@ -24467,12 +25501,12 @@ printf %s "checking for broken sem_getvalue... " >&6; } if test ${ac_cv_broken_sem_getvalue+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "$cross_compiling" = yes +else case e in #( + e) if test "$cross_compiling" = yes then : ac_cv_broken_sem_getvalue=yes -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -24502,14 +25536,17 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_broken_sem_getvalue=no -else $as_nop - ac_cv_broken_sem_getvalue=yes +else case e in #( + e) ac_cv_broken_sem_getvalue=yes ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_broken_sem_getvalue" >&5 printf "%s\n" "$ac_cv_broken_sem_getvalue" >&6; } @@ -24527,8 +25564,9 @@ ac_fn_check_decl "$LINENO" "RTLD_LAZY" "ac_cv_have_decl_RTLD_LAZY" "#include
>confdefs.h ac_fn_check_decl "$LINENO" "RTLD_NOW" "ac_cv_have_decl_RTLD_NOW" "#include @@ -24536,8 +25574,9 @@ ac_fn_check_decl "$LINENO" "RTLD_NOW" "ac_cv_have_decl_RTLD_NOW" "#include >confdefs.h ac_fn_check_decl "$LINENO" "RTLD_GLOBAL" "ac_cv_have_decl_RTLD_GLOBAL" "#include @@ -24545,8 +25584,9 @@ ac_fn_check_decl "$LINENO" "RTLD_GLOBAL" "ac_cv_have_decl_RTLD_GLOBAL" "#include if test "x$ac_cv_have_decl_RTLD_GLOBAL" = xyes then : ac_have_decl=1 -else $as_nop - ac_have_decl=0 +else case e in #( + e) ac_have_decl=0 ;; +esac fi printf "%s\n" "#define HAVE_DECL_RTLD_GLOBAL $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "RTLD_LOCAL" "ac_cv_have_decl_RTLD_LOCAL" "#include @@ -24554,8 +25594,9 @@ ac_fn_check_decl "$LINENO" "RTLD_LOCAL" "ac_cv_have_decl_RTLD_LOCAL" "#include < if test "x$ac_cv_have_decl_RTLD_LOCAL" = xyes then : ac_have_decl=1 -else $as_nop - ac_have_decl=0 +else case e in #( + e) ac_have_decl=0 ;; +esac fi printf "%s\n" "#define HAVE_DECL_RTLD_LOCAL $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "RTLD_NODELETE" "ac_cv_have_decl_RTLD_NODELETE" "#include @@ -24563,8 +25604,9 @@ ac_fn_check_decl "$LINENO" "RTLD_NODELETE" "ac_cv_have_decl_RTLD_NODELETE" "#inc if test "x$ac_cv_have_decl_RTLD_NODELETE" = xyes then : ac_have_decl=1 -else $as_nop - ac_have_decl=0 +else case e in #( + e) ac_have_decl=0 ;; +esac fi printf "%s\n" "#define HAVE_DECL_RTLD_NODELETE $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "RTLD_NOLOAD" "ac_cv_have_decl_RTLD_NOLOAD" "#include @@ -24572,8 +25614,9 @@ ac_fn_check_decl "$LINENO" "RTLD_NOLOAD" "ac_cv_have_decl_RTLD_NOLOAD" "#include if test "x$ac_cv_have_decl_RTLD_NOLOAD" = xyes then : ac_have_decl=1 -else $as_nop - ac_have_decl=0 +else case e in #( + e) ac_have_decl=0 ;; +esac fi printf "%s\n" "#define HAVE_DECL_RTLD_NOLOAD $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "RTLD_DEEPBIND" "ac_cv_have_decl_RTLD_DEEPBIND" "#include @@ -24581,8 +25624,9 @@ ac_fn_check_decl "$LINENO" "RTLD_DEEPBIND" "ac_cv_have_decl_RTLD_DEEPBIND" "#inc if test "x$ac_cv_have_decl_RTLD_DEEPBIND" = xyes then : ac_have_decl=1 -else $as_nop - ac_have_decl=0 +else case e in #( + e) ac_have_decl=0 ;; +esac fi printf "%s\n" "#define HAVE_DECL_RTLD_DEEPBIND $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "RTLD_MEMBER" "ac_cv_have_decl_RTLD_MEMBER" "#include @@ -24590,8 +25634,9 @@ ac_fn_check_decl "$LINENO" "RTLD_MEMBER" "ac_cv_have_decl_RTLD_MEMBER" "#include if test "x$ac_cv_have_decl_RTLD_MEMBER" = xyes then : ac_have_decl=1 -else $as_nop - ac_have_decl=0 +else case e in #( + e) ac_have_decl=0 ;; +esac fi printf "%s\n" "#define HAVE_DECL_RTLD_MEMBER $ac_have_decl" >>confdefs.h @@ -24618,9 +25663,10 @@ printf "%s\n" "$enable_big_digits" >&6; } printf "%s\n" "#define PYLONG_BITS_IN_DIGIT $enable_big_digits" >>confdefs.h -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no value specified" >&5 -printf "%s\n" "no value specified" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no value specified" >&5 +printf "%s\n" "no value specified" >&6; } ;; +esac fi @@ -24634,9 +25680,10 @@ printf "%s\n" "#define HAVE_WCHAR_H 1" >>confdefs.h wchar_h="yes" -else $as_nop - wchar_h="no" - +else case e in #( + e) wchar_h="no" + ;; +esac fi @@ -24645,29 +25692,31 @@ if test "$wchar_h" = yes then # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of wchar_t" >&5 printf %s "checking size of wchar_t... " >&6; } if test ${ac_cv_sizeof_wchar_t+y} then : printf %s "(cached) " >&6 -else $as_nop - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (wchar_t))" "ac_cv_sizeof_wchar_t" "#include +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (wchar_t))" "ac_cv_sizeof_wchar_t" "#include " then : -else $as_nop - if test "$ac_cv_type_wchar_t" = yes; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +else case e in #( + e) if test "$ac_cv_type_wchar_t" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (wchar_t) -See \`config.log' for more details" "$LINENO" 5; } +See 'config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_wchar_t=0 - fi + fi ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_wchar_t" >&5 printf "%s\n" "$ac_cv_sizeof_wchar_t" >&6; } @@ -24688,13 +25737,13 @@ printf %s "checking whether wchar_t is signed... " >&6; } if test ${ac_cv_wchar_t_signed+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) if test "$cross_compiling" = yes then : ac_cv_wchar_t_signed=yes -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -24708,13 +25757,16 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_wchar_t_signed=yes -else $as_nop - ac_cv_wchar_t_signed=no +else case e in #( + e) ac_cv_wchar_t_signed=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_wchar_t_signed" >&5 printf "%s\n" "$ac_cv_wchar_t_signed" >&6; } @@ -24758,8 +25810,8 @@ printf %s "checking whether byte ordering is bigendian... " >&6; } if test ${ac_cv_c_bigendian+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_cv_c_bigendian=unknown +else case e in #( + e) ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -24805,8 +25857,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext int main (void) { -#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ - && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ +#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \\ + && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \\ && LITTLE_ENDIAN) bogus endian macros #endif @@ -24837,8 +25889,9 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_bigendian=yes -else $as_nop - ac_cv_c_bigendian=no +else case e in #( + e) ac_cv_c_bigendian=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi @@ -24882,8 +25935,9 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_bigendian=yes -else $as_nop - ac_cv_c_bigendian=no +else case e in #( + e) ac_cv_c_bigendian=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi @@ -24910,22 +25964,23 @@ unsigned short int ascii_mm[] = int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } - extern int foo; - -int -main (void) -{ -return use_ascii (foo) == use_ebcdic (foo); - ; - return 0; -} + int + main (int argc, char **argv) + { + /* Intimidate the compiler so that it does not + optimize the arrays away. */ + char *p = argv[0]; + ascii_mm[1] = *p++; ebcdic_mm[1] = *p++; + ascii_ii[1] = *p++; ebcdic_ii[1] = *p++; + return use_ascii (argc) == use_ebcdic (*p); + } _ACEOF -if ac_fn_c_try_compile "$LINENO" +if ac_fn_c_try_link "$LINENO" then : - if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then + if grep BIGenDianSyS conftest$ac_exeext >/dev/null; then ac_cv_c_bigendian=yes fi - if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if grep LiTTleEnDian conftest$ac_exeext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else @@ -24934,9 +25989,10 @@ then : fi fi fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int @@ -24959,14 +26015,17 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_c_bigendian=no -else $as_nop - ac_cv_c_bigendian=yes +else case e in #( + e) ac_cv_c_bigendian=yes ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - fi + fi ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 printf "%s\n" "$ac_cv_c_bigendian" >&6; } @@ -25084,9 +26143,10 @@ else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } ;; +esac fi @@ -25117,9 +26177,10 @@ else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } ;; +esac fi @@ -25130,13 +26191,13 @@ printf %s "checking whether right shift extends the sign bit... " >&6; } if test ${ac_cv_rshift_extends_sign+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) if test "$cross_compiling" = yes then : ac_cv_rshift_extends_sign=yes -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void) @@ -25148,13 +26209,16 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_rshift_extends_sign=yes -else $as_nop - ac_cv_rshift_extends_sign=no +else case e in #( + e) ac_cv_rshift_extends_sign=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_rshift_extends_sign" >&5 printf "%s\n" "$ac_cv_rshift_extends_sign" >&6; } @@ -25171,8 +26235,8 @@ printf %s "checking for getc_unlocked() and friends... " >&6; } if test ${ac_cv_have_getc_unlocked+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -25192,11 +26256,13 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_have_getc_unlocked=yes -else $as_nop - ac_cv_have_getc_unlocked=no +else case e in #( + e) ac_cv_have_getc_unlocked=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext + conftest$ac_exeext conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_getc_unlocked" >&5 printf "%s\n" "$ac_cv_have_getc_unlocked" >&6; } @@ -25227,9 +26293,10 @@ then : ;; esac -else $as_nop - with_readline=readline - +else case e in #( + e) with_readline=readline + ;; +esac fi @@ -25316,16 +26383,22 @@ printf %s "checking for readline in -lreadline... " >&6; } if test ${ac_cv_lib_readline_readline+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lreadline $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char readline (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char readline (void); int main (void) { @@ -25337,12 +26410,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_readline_readline=yes -else $as_nop - ac_cv_lib_readline_readline=no +else case e in #( + e) ac_cv_lib_readline_readline=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_readline_readline" >&5 printf "%s\n" "$ac_cv_lib_readline_readline" >&6; } @@ -25353,13 +26428,15 @@ then : READLINE_CFLAGS=${LIBREADLINE_CFLAGS-""} READLINE_LIBS=${LIBREADLINE_LIBS-"-lreadline"} -else $as_nop - with_readline=no +else case e in #( + e) with_readline=no ;; +esac fi -else $as_nop - with_readline=no +else case e in #( + e) with_readline=no ;; +esac fi done @@ -25395,16 +26472,22 @@ printf %s "checking for readline in -lreadline... " >&6; } if test ${ac_cv_lib_readline_readline+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-lreadline $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char readline (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char readline (void); int main (void) { @@ -25416,12 +26499,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_readline_readline=yes -else $as_nop - ac_cv_lib_readline_readline=no +else case e in #( + e) ac_cv_lib_readline_readline=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_readline_readline" >&5 printf "%s\n" "$ac_cv_lib_readline_readline" >&6; } @@ -25432,13 +26517,15 @@ then : READLINE_CFLAGS=${LIBREADLINE_CFLAGS-""} READLINE_LIBS=${LIBREADLINE_LIBS-"-lreadline"} -else $as_nop - with_readline=no +else case e in #( + e) with_readline=no ;; +esac fi -else $as_nop - with_readline=no +else case e in #( + e) with_readline=no ;; +esac fi done @@ -25547,16 +26634,22 @@ printf %s "checking for readline in -ledit... " >&6; } if test ${ac_cv_lib_edit_readline+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-ledit $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char readline (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char readline (void); int main (void) { @@ -25568,12 +26661,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_edit_readline=yes -else $as_nop - ac_cv_lib_edit_readline=no +else case e in #( + e) ac_cv_lib_edit_readline=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_edit_readline" >&5 printf "%s\n" "$ac_cv_lib_edit_readline" >&6; } @@ -25586,13 +26681,15 @@ then : READLINE_CFLAGS=${LIBEDIT_CFLAGS-""} READLINE_LIBS=${LIBEDIT_LIBS-"-ledit"} -else $as_nop - with_readline=no +else case e in #( + e) with_readline=no ;; +esac fi -else $as_nop - with_readline=no +else case e in #( + e) with_readline=no ;; +esac fi done @@ -25628,16 +26725,22 @@ printf %s "checking for readline in -ledit... " >&6; } if test ${ac_cv_lib_edit_readline+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS LIBS="-ledit $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char readline (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char readline (void); int main (void) { @@ -25649,12 +26752,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_edit_readline=yes -else $as_nop - ac_cv_lib_edit_readline=no +else case e in #( + e) ac_cv_lib_edit_readline=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +LIBS=$ac_check_lib_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_edit_readline" >&5 printf "%s\n" "$ac_cv_lib_edit_readline" >&6; } @@ -25667,13 +26772,15 @@ then : READLINE_CFLAGS=${LIBEDIT_CFLAGS-""} READLINE_LIBS=${LIBEDIT_LIBS-"-ledit"} -else $as_nop - with_readline=no +else case e in #( + e) with_readline=no ;; +esac fi -else $as_nop - with_readline=no +else case e in #( + e) with_readline=no ;; +esac fi done @@ -25711,8 +26818,8 @@ then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_readline (CFLAGS: $READLINE_CFLAGS, LIBS: $READLINE_LIBS)" >&5 printf "%s\n" "$with_readline (CFLAGS: $READLINE_CFLAGS, LIBS: $READLINE_LIBS)" >&6; } @@ -25773,8 +26880,8 @@ printf %s "checking for rl_pre_input_hook in -l$LIBREADLINE... " >&6; } if test ${ac_cv_readline_rl_pre_input_hook+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -25797,13 +26904,15 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_readline_rl_pre_input_hook=yes -else $as_nop - ac_cv_readline_rl_pre_input_hook=no - +else case e in #( + e) ac_cv_readline_rl_pre_input_hook=no + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_readline_rl_pre_input_hook" >&5 printf "%s\n" "$ac_cv_readline_rl_pre_input_hook" >&6; } @@ -25822,8 +26931,8 @@ printf %s "checking for rl_completion_display_matches_hook in -l$LIBREADLINE... if test ${ac_cv_readline_rl_completion_display_matches_hook+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -25846,13 +26955,15 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_readline_rl_completion_display_matches_hook=yes -else $as_nop - ac_cv_readline_rl_completion_display_matches_hook=no - +else case e in #( + e) ac_cv_readline_rl_completion_display_matches_hook=no + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_readline_rl_completion_display_matches_hook" >&5 printf "%s\n" "$ac_cv_readline_rl_completion_display_matches_hook" >&6; } @@ -25871,8 +26982,8 @@ printf %s "checking for rl_resize_terminal in -l$LIBREADLINE... " >&6; } if test ${ac_cv_readline_rl_resize_terminal+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -25895,13 +27006,15 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_readline_rl_resize_terminal=yes -else $as_nop - ac_cv_readline_rl_resize_terminal=no - +else case e in #( + e) ac_cv_readline_rl_resize_terminal=no + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_readline_rl_resize_terminal" >&5 printf "%s\n" "$ac_cv_readline_rl_resize_terminal" >&6; } @@ -25920,8 +27033,8 @@ printf %s "checking for rl_completion_matches in -l$LIBREADLINE... " >&6; } if test ${ac_cv_readline_rl_completion_matches+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -25944,13 +27057,15 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_readline_rl_completion_matches=yes -else $as_nop - ac_cv_readline_rl_completion_matches=no - +else case e in #( + e) ac_cv_readline_rl_completion_matches=no + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_readline_rl_completion_matches" >&5 printf "%s\n" "$ac_cv_readline_rl_completion_matches" >&6; } @@ -25988,8 +27103,8 @@ printf %s "checking for append_history in -l$LIBREADLINE... " >&6; } if test ${ac_cv_readline_append_history+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -26012,13 +27127,15 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_readline_append_history=yes -else $as_nop - ac_cv_readline_append_history=no - +else case e in #( + e) ac_cv_readline_append_history=no + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_readline_append_history" >&5 printf "%s\n" "$ac_cv_readline_append_history" >&6; } @@ -26058,8 +27175,8 @@ printf %s "checking if rl_startup_hook takes arguments... " >&6; } if test ${ac_cv_readline_rl_startup_hook_takes_args+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -26083,12 +27200,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_readline_rl_startup_hook_takes_args=yes -else $as_nop - ac_cv_readline_rl_startup_hook_takes_args=no - +else case e in #( + e) ac_cv_readline_rl_startup_hook_takes_args=no + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_readline_rl_startup_hook_takes_args" >&5 printf "%s\n" "$ac_cv_readline_rl_startup_hook_takes_args" >&6; } @@ -26108,7 +27227,8 @@ CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for broken nice()" >&5 @@ -26116,13 +27236,13 @@ printf %s "checking for broken nice()... " >&6; } if test ${ac_cv_broken_nice+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) if test "$cross_compiling" = yes then : ac_cv_broken_nice=no -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -26139,13 +27259,16 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_broken_nice=yes -else $as_nop - ac_cv_broken_nice=no +else case e in #( + e) ac_cv_broken_nice=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_broken_nice" >&5 printf "%s\n" "$ac_cv_broken_nice" >&6; } @@ -26161,12 +27284,12 @@ printf %s "checking for broken poll()... " >&6; } if test ${ac_cv_broken_poll+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "$cross_compiling" = yes +else case e in #( + e) if test "$cross_compiling" = yes then : ac_cv_broken_poll=no -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -26192,13 +27315,16 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_broken_poll=yes -else $as_nop - ac_cv_broken_poll=no +else case e in #( + e) ac_cv_broken_poll=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_broken_poll" >&5 printf "%s\n" "$ac_cv_broken_poll" >&6; } @@ -26215,13 +27341,13 @@ printf %s "checking for working tzset()... " >&6; } if test ${ac_cv_working_tzset+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) if test "$cross_compiling" = yes then : ac_cv_working_tzset=no -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -26291,13 +27417,16 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_working_tzset=yes -else $as_nop - ac_cv_working_tzset=no +else case e in #( + e) ac_cv_working_tzset=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_working_tzset" >&5 printf "%s\n" "$ac_cv_working_tzset" >&6; } @@ -26314,8 +27443,8 @@ printf %s "checking for tv_nsec in struct stat... " >&6; } if test ${ac_cv_stat_tv_nsec+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -26332,10 +27461,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_stat_tv_nsec=yes -else $as_nop - ac_cv_stat_tv_nsec=no +else case e in #( + e) ac_cv_stat_tv_nsec=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_stat_tv_nsec" >&5 printf "%s\n" "$ac_cv_stat_tv_nsec" >&6; } @@ -26352,8 +27483,8 @@ printf %s "checking for tv_nsec2 in struct stat... " >&6; } if test ${ac_cv_stat_tv_nsec2+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -26370,10 +27501,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_stat_tv_nsec2=yes -else $as_nop - ac_cv_stat_tv_nsec2=no +else case e in #( + e) ac_cv_stat_tv_nsec2=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_stat_tv_nsec2" >&5 printf "%s\n" "$ac_cv_stat_tv_nsec2" >&6; } @@ -26389,13 +27522,13 @@ printf %s "checking whether year with century should be normalized for strftime. if test ${ac_cv_normalize_century+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) if test "$cross_compiling" = yes then : ac_cv_normalize_century=yes -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -26419,13 +27552,16 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_normalize_century=yes -else $as_nop - ac_cv_normalize_century=no +else case e in #( + e) ac_cv_normalize_century=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_normalize_century" >&5 printf "%s\n" "$ac_cv_normalize_century" >&6; } @@ -26441,13 +27577,13 @@ printf %s "checking whether C99-compatible strftime specifiers are supported... if test ${ac_cv_strftime_c99_support+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) if test "$cross_compiling" = yes then : ac_cv_strftime_c99_support= -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -26471,13 +27607,16 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_strftime_c99_support=yes -else $as_nop - as_fn_error $? "Python requires C99-compatible strftime specifiers" "$LINENO" 5 +else case e in #( + e) as_fn_error $? "Python requires C99-compatible strftime specifiers" "$LINENO" 5 ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_strftime_c99_support" >&5 printf "%s\n" "$ac_cv_strftime_c99_support" >&6; } @@ -26868,15 +28007,21 @@ printf %s "checking for library containing initscr... " >&6; } if test ${ac_cv_search_initscr+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_func_search_save_LIBS=$LIBS +else case e in #( + e) ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char initscr (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char initscr (void); int main (void) { @@ -26907,11 +28052,13 @@ done if test ${ac_cv_search_initscr+y} then : -else $as_nop - ac_cv_search_initscr=no +else case e in #( + e) ac_cv_search_initscr=no ;; +esac fi rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS +LIBS=$ac_func_search_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_initscr" >&5 printf "%s\n" "$ac_cv_search_initscr" >&6; } @@ -26924,8 +28071,9 @@ then : have_curses=yes CURSES_LIBS=${CURSES_LIBS-"$ac_cv_search_initscr"} fi -else $as_nop - have_curses=no +else case e in #( + e) have_curses=no ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing update_panels" >&5 @@ -26933,15 +28081,21 @@ printf %s "checking for library containing update_panels... " >&6; } if test ${ac_cv_search_update_panels+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_func_search_save_LIBS=$LIBS +else case e in #( + e) ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char update_panels (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char update_panels (void); int main (void) { @@ -26972,11 +28126,13 @@ done if test ${ac_cv_search_update_panels+y} then : -else $as_nop - ac_cv_search_update_panels=no +else case e in #( + e) ac_cv_search_update_panels=no ;; +esac fi rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS +LIBS=$ac_func_search_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_update_panels" >&5 printf "%s\n" "$ac_cv_search_update_panels" >&6; } @@ -26989,8 +28145,9 @@ then : have_panel=yes PANEL_LIBS=${PANEL_LIBS-"$ac_cv_search_update_panels"} fi -else $as_nop - have_panel=no +else case e in #( + e) have_panel=no ;; +esac fi @@ -27042,8 +28199,8 @@ printf %s "checking whether mvwdelch is an expression... " >&6; } if test ${ac_cv_mvwdelch_is_expression+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define NCURSES_OPAQUE 0 @@ -27075,10 +28232,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_mvwdelch_is_expression=yes -else $as_nop - ac_cv_mvwdelch_is_expression=no +else case e in #( + e) ac_cv_mvwdelch_is_expression=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_mvwdelch_is_expression" >&5 printf "%s\n" "$ac_cv_mvwdelch_is_expression" >&6; } @@ -27095,8 +28254,8 @@ printf %s "checking whether WINDOW has _flags... " >&6; } if test ${ac_cv_window_has_flags+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define NCURSES_OPAQUE 0 @@ -27128,10 +28287,12 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_window_has_flags=yes -else $as_nop - ac_cv_window_has_flags=no +else case e in #( + e) ac_cv_window_has_flags=no ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_window_has_flags" >&5 printf "%s\n" "$ac_cv_window_has_flags" >&6; } @@ -27153,8 +28314,8 @@ printf %s "checking for curses function is_pad... " >&6; } if test ${ac_cv_lib_curses_is_pad+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define NCURSES_OPAQUE 0 @@ -27187,11 +28348,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_lib_curses_is_pad=yes -else $as_nop - ac_cv_lib_curses_is_pad=no +else case e in #( + e) ac_cv_lib_curses_is_pad=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_is_pad" >&5 printf "%s\n" "$ac_cv_lib_curses_is_pad" >&6; } @@ -27211,8 +28374,8 @@ printf %s "checking for curses function is_term_resized... " >&6; } if test ${ac_cv_lib_curses_is_term_resized+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define NCURSES_OPAQUE 0 @@ -27245,11 +28408,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_lib_curses_is_term_resized=yes -else $as_nop - ac_cv_lib_curses_is_term_resized=no +else case e in #( + e) ac_cv_lib_curses_is_term_resized=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_is_term_resized" >&5 printf "%s\n" "$ac_cv_lib_curses_is_term_resized" >&6; } @@ -27269,8 +28434,8 @@ printf %s "checking for curses function resize_term... " >&6; } if test ${ac_cv_lib_curses_resize_term+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define NCURSES_OPAQUE 0 @@ -27303,11 +28468,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_lib_curses_resize_term=yes -else $as_nop - ac_cv_lib_curses_resize_term=no +else case e in #( + e) ac_cv_lib_curses_resize_term=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_resize_term" >&5 printf "%s\n" "$ac_cv_lib_curses_resize_term" >&6; } @@ -27327,8 +28494,8 @@ printf %s "checking for curses function resizeterm... " >&6; } if test ${ac_cv_lib_curses_resizeterm+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define NCURSES_OPAQUE 0 @@ -27361,11 +28528,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_lib_curses_resizeterm=yes -else $as_nop - ac_cv_lib_curses_resizeterm=no +else case e in #( + e) ac_cv_lib_curses_resizeterm=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_resizeterm" >&5 printf "%s\n" "$ac_cv_lib_curses_resizeterm" >&6; } @@ -27385,8 +28554,8 @@ printf %s "checking for curses function immedok... " >&6; } if test ${ac_cv_lib_curses_immedok+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define NCURSES_OPAQUE 0 @@ -27419,11 +28588,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_lib_curses_immedok=yes -else $as_nop - ac_cv_lib_curses_immedok=no +else case e in #( + e) ac_cv_lib_curses_immedok=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_immedok" >&5 printf "%s\n" "$ac_cv_lib_curses_immedok" >&6; } @@ -27443,8 +28614,8 @@ printf %s "checking for curses function syncok... " >&6; } if test ${ac_cv_lib_curses_syncok+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define NCURSES_OPAQUE 0 @@ -27477,11 +28648,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_lib_curses_syncok=yes -else $as_nop - ac_cv_lib_curses_syncok=no +else case e in #( + e) ac_cv_lib_curses_syncok=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_syncok" >&5 printf "%s\n" "$ac_cv_lib_curses_syncok" >&6; } @@ -27501,8 +28674,8 @@ printf %s "checking for curses function wchgat... " >&6; } if test ${ac_cv_lib_curses_wchgat+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define NCURSES_OPAQUE 0 @@ -27535,11 +28708,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_lib_curses_wchgat=yes -else $as_nop - ac_cv_lib_curses_wchgat=no +else case e in #( + e) ac_cv_lib_curses_wchgat=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_wchgat" >&5 printf "%s\n" "$ac_cv_lib_curses_wchgat" >&6; } @@ -27559,8 +28734,8 @@ printf %s "checking for curses function filter... " >&6; } if test ${ac_cv_lib_curses_filter+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define NCURSES_OPAQUE 0 @@ -27593,11 +28768,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_lib_curses_filter=yes -else $as_nop - ac_cv_lib_curses_filter=no +else case e in #( + e) ac_cv_lib_curses_filter=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_filter" >&5 printf "%s\n" "$ac_cv_lib_curses_filter" >&6; } @@ -27617,8 +28794,8 @@ printf %s "checking for curses function has_key... " >&6; } if test ${ac_cv_lib_curses_has_key+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define NCURSES_OPAQUE 0 @@ -27651,11 +28828,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_lib_curses_has_key=yes -else $as_nop - ac_cv_lib_curses_has_key=no +else case e in #( + e) ac_cv_lib_curses_has_key=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_has_key" >&5 printf "%s\n" "$ac_cv_lib_curses_has_key" >&6; } @@ -27675,8 +28854,8 @@ printf %s "checking for curses function typeahead... " >&6; } if test ${ac_cv_lib_curses_typeahead+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define NCURSES_OPAQUE 0 @@ -27709,11 +28888,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_lib_curses_typeahead=yes -else $as_nop - ac_cv_lib_curses_typeahead=no +else case e in #( + e) ac_cv_lib_curses_typeahead=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_typeahead" >&5 printf "%s\n" "$ac_cv_lib_curses_typeahead" >&6; } @@ -27733,8 +28914,8 @@ printf %s "checking for curses function use_env... " >&6; } if test ${ac_cv_lib_curses_use_env+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define NCURSES_OPAQUE 0 @@ -27767,11 +28948,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_lib_curses_use_env=yes -else $as_nop - ac_cv_lib_curses_use_env=no +else case e in #( + e) ac_cv_lib_curses_use_env=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_use_env" >&5 printf "%s\n" "$ac_cv_lib_curses_use_env" >&6; } @@ -27822,14 +29005,15 @@ printf %s "checking for /dev/ptmx... " >&6; } if test ${ac_cv_file__dev_ptmx+y} then : printf %s "(cached) " >&6 -else $as_nop - test "$cross_compiling" = yes && +else case e in #( + e) test "$cross_compiling" = yes && as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r "/dev/ptmx"; then ac_cv_file__dev_ptmx=yes else ac_cv_file__dev_ptmx=no -fi +fi ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_file__dev_ptmx" >&5 printf "%s\n" "$ac_cv_file__dev_ptmx" >&6; } @@ -27848,14 +29032,15 @@ printf %s "checking for /dev/ptc... " >&6; } if test ${ac_cv_file__dev_ptc+y} then : printf %s "(cached) " >&6 -else $as_nop - test "$cross_compiling" = yes && +else case e in #( + e) test "$cross_compiling" = yes && as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r "/dev/ptc"; then ac_cv_file__dev_ptc=yes else ac_cv_file__dev_ptc=no -fi +fi ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_file__dev_ptc" >&5 printf "%s\n" "$ac_cv_file__dev_ptc" >&6; } @@ -27891,10 +29076,11 @@ then : printf "%s\n" "#define HAVE_SOCKLEN_T 1" >>confdefs.h -else $as_nop - +else case e in #( + e) printf "%s\n" "#define socklen_t int" >>confdefs.h - + ;; +esac fi @@ -27903,12 +29089,12 @@ printf %s "checking for broken mbstowcs... " >&6; } if test ${ac_cv_broken_mbstowcs+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "$cross_compiling" = yes +else case e in #( + e) if test "$cross_compiling" = yes then : ac_cv_broken_mbstowcs=no -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -27925,13 +29111,16 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_broken_mbstowcs=no -else $as_nop - ac_cv_broken_mbstowcs=yes +else case e in #( + e) ac_cv_broken_mbstowcs=yes ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_broken_mbstowcs" >&5 printf "%s\n" "$ac_cv_broken_mbstowcs" >&6; } @@ -27967,9 +29156,10 @@ printf "%s\n" "#define USE_COMPUTED_GOTOS 0" >>confdefs.h printf "%s\n" "no" >&6; } fi -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no value specified" >&5 -printf "%s\n" "no value specified" >&6; } +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no value specified" >&5 +printf "%s\n" "no value specified" >&6; } ;; +esac fi @@ -27978,16 +29168,16 @@ printf %s "checking whether $CC supports computed gotos... " >&6; } if test ${ac_cv_computed_gotos+y} then : printf %s "(cached) " >&6 -else $as_nop - if test "$cross_compiling" = yes +else case e in #( + e) if test "$cross_compiling" = yes then : if test "${with_computed_gotos+set}" = set; then ac_cv_computed_gotos="$with_computed_gotos -- configured --with(out)-computed-gotos" else ac_cv_computed_gotos=no fi -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(int argc, char **argv) @@ -28005,13 +29195,16 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_computed_gotos=yes -else $as_nop - ac_cv_computed_gotos=no +else case e in #( + e) ac_cv_computed_gotos=no ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_computed_gotos" >&5 printf "%s\n" "$ac_cv_computed_gotos" >&6; } @@ -28078,8 +29271,8 @@ printf %s "checking for -O2... " >&6; } if test ${ac_cv_compile_o2+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) saved_cflags="$CFLAGS" CFLAGS="-O2" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -28096,12 +29289,14 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_compile_o2=yes -else $as_nop - ac_cv_compile_o2=no +else case e in #( + e) ac_cv_compile_o2=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS="$saved_cflags" - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_compile_o2" >&5 printf "%s\n" "$ac_cv_compile_o2" >&6; } @@ -28118,8 +29313,8 @@ fi if test "$cross_compiling" = yes then : have_glibc_memmove_bug=undefined -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -28141,11 +29336,13 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : have_glibc_memmove_bug=no -else $as_nop - have_glibc_memmove_bug=yes +else case e in #( + e) have_glibc_memmove_bug=yes ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi CFLAGS="$saved_cflags" @@ -28170,8 +29367,8 @@ printf %s "checking for gcc ipa-pure-const bug... " >&6; } if test "$cross_compiling" = yes then : have_ipa_pure_const_bug=undefined -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ __attribute__((noinline)) int @@ -28194,11 +29391,13 @@ _ACEOF if ac_fn_c_try_run "$LINENO" then : have_ipa_pure_const_bug=no -else $as_nop - have_ipa_pure_const_bug=yes +else case e in #( + e) have_ipa_pure_const_bug=yes ;; +esac fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac fi CFLAGS="$saved_cflags" @@ -28221,8 +29420,8 @@ printf %s "checking for ensurepip... " >&6; } if test ${with_ensurepip+y} then : withval=$with_ensurepip; -else $as_nop - +else case e in #( + e) case $ac_sys_system in #( Emscripten) : with_ensurepip=no ;; #( @@ -28234,7 +29433,8 @@ else $as_nop with_ensurepip=upgrade ;; esac - + ;; +esac fi case $with_ensurepip in #( @@ -28257,8 +29457,8 @@ printf %s "checking if the dirent structure of a d_type field... " >&6; } if test ${ac_cv_dirent_d_type+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -28275,12 +29475,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_dirent_d_type=yes -else $as_nop - ac_cv_dirent_d_type=no +else case e in #( + e) ac_cv_dirent_d_type=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_dirent_d_type" >&5 printf "%s\n" "$ac_cv_dirent_d_type" >&6; } @@ -28300,8 +29502,8 @@ printf %s "checking for the Linux getrandom() syscall... " >&6; } if test ${ac_cv_getrandom_syscall+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -28325,12 +29527,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_getrandom_syscall=yes -else $as_nop - ac_cv_getrandom_syscall=no +else case e in #( + e) ac_cv_getrandom_syscall=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_getrandom_syscall" >&5 printf "%s\n" "$ac_cv_getrandom_syscall" >&6; } @@ -28351,8 +29555,8 @@ printf %s "checking for the getrandom() function... " >&6; } if test ${ac_cv_func_getrandom+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -28374,12 +29578,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_func_getrandom=yes -else $as_nop - ac_cv_func_getrandom=no +else case e in #( + e) ac_cv_func_getrandom=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_getrandom" >&5 printf "%s\n" "$ac_cv_func_getrandom" >&6; } @@ -28407,15 +29613,21 @@ printf %s "checking for library containing shm_open... " >&6; } if test ${ac_cv_search_shm_open+y} then : printf %s "(cached) " >&6 -else $as_nop - ac_func_search_save_LIBS=$LIBS +else case e in #( + e) ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char shm_open (); + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char shm_open (void); int main (void) { @@ -28446,11 +29658,13 @@ done if test ${ac_cv_search_shm_open+y} then : -else $as_nop - ac_cv_search_shm_open=no +else case e in #( + e) ac_cv_search_shm_open=no ;; +esac fi rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS +LIBS=$ac_func_search_save_LIBS ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_shm_open" >&5 printf "%s\n" "$ac_cv_search_shm_open" >&6; } @@ -28478,16 +29692,17 @@ fi for ac_func in shm_open shm_unlink do : - as_ac_var=`printf "%s\n" "ac_cv_func_$ac_func" | $as_tr_sh` + as_ac_var=`printf "%s\n" "ac_cv_func_$ac_func" | sed "$as_sed_sh"` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes" then : cat >>confdefs.h <<_ACEOF -#define `printf "%s\n" "HAVE_$ac_func" | $as_tr_cpp` 1 +#define `printf "%s\n" "HAVE_$ac_func" | sed "$as_sed_cpp"` 1 _ACEOF have_posix_shmem=yes -else $as_nop - have_posix_shmem=no +else case e in #( + e) have_posix_shmem=no ;; +esac fi done @@ -28516,8 +29731,8 @@ then : ;; esac -else $as_nop - +else case e in #( + e) # if pkg-config is installed and openssl has installed a .pc file, # then use that information and don't search ssldirs if test -n "$ac_tool_prefix"; then @@ -28528,8 +29743,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_PKG_CONFIG+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$PKG_CONFIG"; then +else case e in #( + e) if test -n "$PKG_CONFIG"; then ac_cv_prog_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -28551,7 +29766,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi PKG_CONFIG=$ac_cv_prog_PKG_CONFIG if test -n "$PKG_CONFIG"; then @@ -28573,8 +29789,8 @@ printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_PKG_CONFIG+y} then : printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_PKG_CONFIG"; then +else case e in #( + e) if test -n "$ac_ct_PKG_CONFIG"; then ac_cv_prog_ac_ct_PKG_CONFIG="$ac_ct_PKG_CONFIG" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -28596,7 +29812,8 @@ done done IFS=$as_save_IFS -fi +fi ;; +esac fi ac_ct_PKG_CONFIG=$ac_cv_prog_ac_ct_PKG_CONFIG if test -n "$ac_ct_PKG_CONFIG"; then @@ -28636,7 +29853,8 @@ fi ssldirs="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr" fi - + ;; +esac fi @@ -28699,12 +29917,13 @@ then : printf "%s\n" "yes" >&6; } have_openssl=yes -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } have_openssl=no - + ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext @@ -28723,15 +29942,16 @@ then : rpath_arg="-Wl,--enable-new-dtags,-rpath=" -else $as_nop - +else case e in #( + e) if test "$ac_sys_system" = "Darwin" then rpath_arg="-Wl,-rpath," else rpath_arg="-Wl,-rpath=" fi - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-openssl-rpath" >&5 @@ -28741,9 +29961,10 @@ printf %s "checking for --with-openssl-rpath... " >&6; } if test ${with_openssl_rpath+y} then : withval=$with_openssl_rpath; -else $as_nop - with_openssl_rpath=no - +else case e in #( + e) with_openssl_rpath=no + ;; +esac fi case $with_openssl_rpath in #( @@ -28769,8 +29990,9 @@ then : OPENSSL_RPATH="$with_openssl_rpath" OPENSSL_LDFLAGS_RPATH="${rpath_arg}$with_openssl_rpath" -else $as_nop - as_fn_error $? "--with-openssl-rpath \"$with_openssl_rpath\" is not a directory" "$LINENO" 5 +else case e in #( + e) as_fn_error $? "--with-openssl-rpath \"$with_openssl_rpath\" is not a directory" "$LINENO" 5 ;; +esac fi ;; @@ -28833,8 +30055,8 @@ printf %s "checking whether OpenSSL provides required ssl module APIs... " >&6; if test ${ac_cv_working_openssl_ssl+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -28864,12 +30086,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_working_openssl_ssl=yes -else $as_nop - ac_cv_working_openssl_ssl=no +else case e in #( + e) ac_cv_working_openssl_ssl=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_working_openssl_ssl" >&5 printf "%s\n" "$ac_cv_working_openssl_ssl" >&6; } @@ -28896,8 +30120,8 @@ printf %s "checking whether OpenSSL provides required hashlib module APIs... " > if test ${ac_cv_working_openssl_hashlib+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -28924,12 +30148,14 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_working_openssl_hashlib=yes -else $as_nop - ac_cv_working_openssl_hashlib=no +else case e in #( + e) ac_cv_working_openssl_hashlib=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - + ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_working_openssl_hashlib" >&5 printf "%s\n" "$ac_cv_working_openssl_hashlib" >&6; } @@ -28971,13 +30197,14 @@ case "$withval" in ;; esac -else $as_nop - +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: python" >&5 printf "%s\n" "python" >&6; } printf "%s\n" "#define PY_SSL_DEFAULT_CIPHERS 1" >>confdefs.h - + ;; +esac fi @@ -29000,8 +30227,9 @@ then : ;; esac -else $as_nop - with_builtin_hashlib_hashes=$default_hashlib_hashes +else case e in #( + e) with_builtin_hashlib_hashes=$default_hashlib_hashes ;; +esac fi @@ -29043,12 +30271,14 @@ then : if test "x$enable_test_modules" = xyes then : TEST_MODULES=yes -else $as_nop - TEST_MODULES=no +else case e in #( + e) TEST_MODULES=no ;; +esac fi -else $as_nop - TEST_MODULES=yes +else case e in #( + e) TEST_MODULES=yes ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $TEST_MODULES" >&5 @@ -29077,8 +30307,8 @@ printf %s "checking whether libatomic is needed by ... " >&6; } if test ${ac_cv_libatomic_needed+y} then : printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ // pyatomic.h needs uint64_t and Py_ssize_t types @@ -29121,11 +30351,13 @@ _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_libatomic_needed=no -else $as_nop - ac_cv_libatomic_needed=yes +else case e in #( + e) ac_cv_libatomic_needed=yes ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext + conftest$ac_exeext conftest.$ac_ext ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libatomic_needed" >&5 printf "%s\n" "$ac_cv_libatomic_needed" >&6; } @@ -29778,11 +31010,13 @@ then : if test "$ac_cv_func_sem_unlink" = "yes" then : py_cv_module__multiprocessing=yes -else $as_nop - py_cv_module__multiprocessing=missing +else case e in #( + e) py_cv_module__multiprocessing=missing ;; +esac fi -else $as_nop - py_cv_module__multiprocessing=disabled +else case e in #( + e) py_cv_module__multiprocessing=disabled ;; +esac fi fi @@ -29816,11 +31050,13 @@ then : if test "$have_posix_shmem" = "yes" then : py_cv_module__posixshmem=yes -else $as_nop - py_cv_module__posixshmem=missing +else case e in #( + e) py_cv_module__posixshmem=missing ;; +esac fi -else $as_nop - py_cv_module__posixshmem=disabled +else case e in #( + e) py_cv_module__posixshmem=disabled ;; +esac fi fi @@ -29945,11 +31181,13 @@ then : if test "$ac_cv_header_sys_ioctl_h" = "yes" -a "$ac_cv_header_fcntl_h" = "yes" then : py_cv_module_fcntl=yes -else $as_nop - py_cv_module_fcntl=missing +else case e in #( + e) py_cv_module_fcntl=missing ;; +esac fi -else $as_nop - py_cv_module_fcntl=disabled +else case e in #( + e) py_cv_module_fcntl=disabled ;; +esac fi fi @@ -29983,11 +31221,13 @@ then : if test "$ac_cv_header_sys_mman_h" = "yes" -a "$ac_cv_header_sys_stat_h" = "yes" then : py_cv_module_mmap=yes -else $as_nop - py_cv_module_mmap=missing +else case e in #( + e) py_cv_module_mmap=missing ;; +esac fi -else $as_nop - py_cv_module_mmap=disabled +else case e in #( + e) py_cv_module_mmap=disabled ;; +esac fi fi @@ -30021,11 +31261,13 @@ then : if test "$ac_cv_header_sys_socket_h" = "yes" -a "$ac_cv_header_sys_types_h" = "yes" -a "$ac_cv_header_netinet_in_h" = "yes" then : py_cv_module__socket=yes -else $as_nop - py_cv_module__socket=missing +else case e in #( + e) py_cv_module__socket=missing ;; +esac fi -else $as_nop - py_cv_module__socket=disabled +else case e in #( + e) py_cv_module__socket=disabled ;; +esac fi fi @@ -30061,11 +31303,13 @@ then : { test "$ac_cv_func_getgrgid" = "yes" || test "$ac_cv_func_getgrgid_r" = "yes"; } then : py_cv_module_grp=yes -else $as_nop - py_cv_module_grp=missing +else case e in #( + e) py_cv_module_grp=missing ;; +esac fi -else $as_nop - py_cv_module_grp=disabled +else case e in #( + e) py_cv_module_grp=disabled ;; +esac fi fi @@ -30099,11 +31343,13 @@ then : if test "$ac_cv_func_getpwuid" = yes -o "$ac_cv_func_getpwuid_r" = yes then : py_cv_module_pwd=yes -else $as_nop - py_cv_module_pwd=missing +else case e in #( + e) py_cv_module_pwd=missing ;; +esac fi -else $as_nop - py_cv_module_pwd=disabled +else case e in #( + e) py_cv_module_pwd=disabled ;; +esac fi fi @@ -30137,11 +31383,13 @@ then : if test "$ac_cv_header_sys_resource_h" = yes then : py_cv_module_resource=yes -else $as_nop - py_cv_module_resource=missing +else case e in #( + e) py_cv_module_resource=missing ;; +esac fi -else $as_nop - py_cv_module_resource=disabled +else case e in #( + e) py_cv_module_resource=disabled ;; +esac fi fi @@ -30175,11 +31423,13 @@ then : if true then : py_cv_module__scproxy=yes -else $as_nop - py_cv_module__scproxy=missing +else case e in #( + e) py_cv_module__scproxy=missing ;; +esac fi -else $as_nop - py_cv_module__scproxy=disabled +else case e in #( + e) py_cv_module__scproxy=disabled ;; +esac fi fi @@ -30213,11 +31463,13 @@ then : if test "$ac_cv_header_syslog_h" = yes then : py_cv_module_syslog=yes -else $as_nop - py_cv_module_syslog=missing +else case e in #( + e) py_cv_module_syslog=missing ;; +esac fi -else $as_nop - py_cv_module_syslog=disabled +else case e in #( + e) py_cv_module_syslog=disabled ;; +esac fi fi @@ -30251,11 +31503,13 @@ then : if test "$ac_cv_header_termios_h" = yes then : py_cv_module_termios=yes -else $as_nop - py_cv_module_termios=missing +else case e in #( + e) py_cv_module_termios=missing ;; +esac fi -else $as_nop - py_cv_module_termios=disabled +else case e in #( + e) py_cv_module_termios=disabled ;; +esac fi fi @@ -30290,11 +31544,13 @@ then : if test "$ac_cv_header_sys_time_h" = "yes" then : py_cv_module_pyexpat=yes -else $as_nop - py_cv_module_pyexpat=missing +else case e in #( + e) py_cv_module_pyexpat=missing ;; +esac fi -else $as_nop - py_cv_module_pyexpat=disabled +else case e in #( + e) py_cv_module_pyexpat=disabled ;; +esac fi fi @@ -30328,11 +31584,13 @@ then : if true then : py_cv_module__elementtree=yes -else $as_nop - py_cv_module__elementtree=missing +else case e in #( + e) py_cv_module__elementtree=missing ;; +esac fi -else $as_nop - py_cv_module__elementtree=disabled +else case e in #( + e) py_cv_module__elementtree=disabled ;; +esac fi fi @@ -30543,11 +31801,13 @@ then : if true then : py_cv_module__md5=yes -else $as_nop - py_cv_module__md5=missing +else case e in #( + e) py_cv_module__md5=missing ;; +esac fi -else $as_nop - py_cv_module__md5=disabled +else case e in #( + e) py_cv_module__md5=disabled ;; +esac fi fi @@ -30581,11 +31841,13 @@ then : if true then : py_cv_module__sha1=yes -else $as_nop - py_cv_module__sha1=missing +else case e in #( + e) py_cv_module__sha1=missing ;; +esac fi -else $as_nop - py_cv_module__sha1=disabled +else case e in #( + e) py_cv_module__sha1=disabled ;; +esac fi fi @@ -30619,11 +31881,13 @@ then : if true then : py_cv_module__sha2=yes -else $as_nop - py_cv_module__sha2=missing +else case e in #( + e) py_cv_module__sha2=missing ;; +esac fi -else $as_nop - py_cv_module__sha2=disabled +else case e in #( + e) py_cv_module__sha2=disabled ;; +esac fi fi @@ -30657,11 +31921,13 @@ then : if true then : py_cv_module__sha3=yes -else $as_nop - py_cv_module__sha3=missing +else case e in #( + e) py_cv_module__sha3=missing ;; +esac fi -else $as_nop - py_cv_module__sha3=disabled +else case e in #( + e) py_cv_module__sha3=disabled ;; +esac fi fi @@ -30695,11 +31961,13 @@ then : if true then : py_cv_module__blake2=yes -else $as_nop - py_cv_module__blake2=missing +else case e in #( + e) py_cv_module__blake2=missing ;; +esac fi -else $as_nop - py_cv_module__blake2=disabled +else case e in #( + e) py_cv_module__blake2=disabled ;; +esac fi fi @@ -30742,8 +32010,8 @@ printf %s "checking whether C compiler accepts -msse -msse2 -msse3 -msse4.1 -mss if test ${ax_cv_check_cflags__Werror__msse__msse2__msse3__msse4_1__msse4_2+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror -msse -msse2 -msse3 -msse4.1 -msse4.2" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -30760,11 +32028,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags__Werror__msse__msse2__msse3__msse4_1__msse4_2=yes -else $as_nop - ax_cv_check_cflags__Werror__msse__msse2__msse3__msse4_1__msse4_2=no +else case e in #( + e) ax_cv_check_cflags__Werror__msse__msse2__msse3__msse4_1__msse4_2=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__msse__msse2__msse3__msse4_1__msse4_2" >&5 printf "%s\n" "$ax_cv_check_cflags__Werror__msse__msse2__msse3__msse4_1__msse4_2" >&6; } @@ -30793,8 +32063,9 @@ printf "%s\n" "standard" >&6; } fi -else $as_nop - : +else case e in #( + e) : ;; +esac fi fi @@ -30814,8 +32085,8 @@ printf %s "checking whether C compiler accepts -mavx2... " >&6; } if test ${ax_cv_check_cflags__Werror__mavx2+y} then : printf %s "(cached) " >&6 -else $as_nop - +else case e in #( + e) ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror -mavx2" cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -30832,11 +32103,13 @@ _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags__Werror__mavx2=yes -else $as_nop - ax_cv_check_cflags__Werror__mavx2=no +else case e in #( + e) ax_cv_check_cflags__Werror__mavx2=no ;; +esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS=$ax_check_save_flags + CFLAGS=$ax_check_save_flags ;; +esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__mavx2" >&5 printf "%s\n" "$ax_cv_check_cflags__Werror__mavx2" >&6; } @@ -30864,8 +32137,9 @@ printf "%s\n" "universal2" >&6; } printf "%s\n" "standard" >&6; } fi -else $as_nop - : +else case e in #( + e) : ;; +esac fi fi @@ -30883,11 +32157,13 @@ then : if test "$have_libffi" = yes then : py_cv_module__ctypes=yes -else $as_nop - py_cv_module__ctypes=missing +else case e in #( + e) py_cv_module__ctypes=missing ;; +esac fi -else $as_nop - py_cv_module__ctypes=disabled +else case e in #( + e) py_cv_module__ctypes=disabled ;; +esac fi fi @@ -30921,11 +32197,13 @@ then : if test "$have_curses" = "yes" then : py_cv_module__curses=yes -else $as_nop - py_cv_module__curses=missing +else case e in #( + e) py_cv_module__curses=missing ;; +esac fi -else $as_nop - py_cv_module__curses=disabled +else case e in #( + e) py_cv_module__curses=disabled ;; +esac fi fi @@ -30960,11 +32238,13 @@ then : if test "$have_curses" = "yes" && test "$have_panel" = "yes" then : py_cv_module__curses_panel=yes -else $as_nop - py_cv_module__curses_panel=missing +else case e in #( + e) py_cv_module__curses_panel=missing ;; +esac fi -else $as_nop - py_cv_module__curses_panel=disabled +else case e in #( + e) py_cv_module__curses_panel=disabled ;; +esac fi fi @@ -30999,11 +32279,13 @@ then : if test "$have_mpdec" = "yes" then : py_cv_module__decimal=yes -else $as_nop - py_cv_module__decimal=missing +else case e in #( + e) py_cv_module__decimal=missing ;; +esac fi -else $as_nop - py_cv_module__decimal=disabled +else case e in #( + e) py_cv_module__decimal=disabled ;; +esac fi fi @@ -31037,11 +32319,13 @@ then : if test "$have_dbm" != "no" then : py_cv_module__dbm=yes -else $as_nop - py_cv_module__dbm=missing +else case e in #( + e) py_cv_module__dbm=missing ;; +esac fi -else $as_nop - py_cv_module__dbm=disabled +else case e in #( + e) py_cv_module__dbm=disabled ;; +esac fi fi @@ -31075,11 +32359,13 @@ then : if test "$have_gdbm" = yes then : py_cv_module__gdbm=yes -else $as_nop - py_cv_module__gdbm=missing +else case e in #( + e) py_cv_module__gdbm=missing ;; +esac fi -else $as_nop - py_cv_module__gdbm=disabled +else case e in #( + e) py_cv_module__gdbm=disabled ;; +esac fi fi @@ -31113,11 +32399,13 @@ then : if test "$with_readline" != "no" then : py_cv_module_readline=yes -else $as_nop - py_cv_module_readline=missing +else case e in #( + e) py_cv_module_readline=missing ;; +esac fi -else $as_nop - py_cv_module_readline=disabled +else case e in #( + e) py_cv_module_readline=disabled ;; +esac fi fi @@ -31151,11 +32439,13 @@ then : if test "$have_supported_sqlite3" = "yes" then : py_cv_module__sqlite3=yes -else $as_nop - py_cv_module__sqlite3=missing +else case e in #( + e) py_cv_module__sqlite3=missing ;; +esac fi -else $as_nop - py_cv_module__sqlite3=disabled +else case e in #( + e) py_cv_module__sqlite3=disabled ;; +esac fi fi @@ -31189,11 +32479,13 @@ then : if test "$have_tcltk" = "yes" then : py_cv_module__tkinter=yes -else $as_nop - py_cv_module__tkinter=missing +else case e in #( + e) py_cv_module__tkinter=missing ;; +esac fi -else $as_nop - py_cv_module__tkinter=disabled +else case e in #( + e) py_cv_module__tkinter=disabled ;; +esac fi fi @@ -31227,11 +32519,13 @@ then : if test "$have_uuid" = "yes" then : py_cv_module__uuid=yes -else $as_nop - py_cv_module__uuid=missing +else case e in #( + e) py_cv_module__uuid=missing ;; +esac fi -else $as_nop - py_cv_module__uuid=disabled +else case e in #( + e) py_cv_module__uuid=disabled ;; +esac fi fi @@ -31266,11 +32560,13 @@ then : if test "$have_zlib" = yes then : py_cv_module_zlib=yes -else $as_nop - py_cv_module_zlib=missing +else case e in #( + e) py_cv_module_zlib=missing ;; +esac fi -else $as_nop - py_cv_module_zlib=disabled +else case e in #( + e) py_cv_module_zlib=disabled ;; +esac fi fi @@ -31326,11 +32622,13 @@ then : if test "$have_bzip2" = yes then : py_cv_module__bz2=yes -else $as_nop - py_cv_module__bz2=missing +else case e in #( + e) py_cv_module__bz2=missing ;; +esac fi -else $as_nop - py_cv_module__bz2=disabled +else case e in #( + e) py_cv_module__bz2=disabled ;; +esac fi fi @@ -31364,11 +32662,13 @@ then : if test "$have_liblzma" = yes then : py_cv_module__lzma=yes -else $as_nop - py_cv_module__lzma=missing +else case e in #( + e) py_cv_module__lzma=missing ;; +esac fi -else $as_nop - py_cv_module__lzma=disabled +else case e in #( + e) py_cv_module__lzma=disabled ;; +esac fi fi @@ -31403,11 +32703,13 @@ then : if test "$ac_cv_working_openssl_ssl" = yes then : py_cv_module__ssl=yes -else $as_nop - py_cv_module__ssl=missing +else case e in #( + e) py_cv_module__ssl=missing ;; +esac fi -else $as_nop - py_cv_module__ssl=disabled +else case e in #( + e) py_cv_module__ssl=disabled ;; +esac fi fi @@ -31441,11 +32743,13 @@ then : if test "$ac_cv_working_openssl_hashlib" = yes then : py_cv_module__hashlib=yes -else $as_nop - py_cv_module__hashlib=missing +else case e in #( + e) py_cv_module__hashlib=missing ;; +esac fi -else $as_nop - py_cv_module__hashlib=disabled +else case e in #( + e) py_cv_module__hashlib=disabled ;; +esac fi fi @@ -31480,11 +32784,13 @@ then : if true then : py_cv_module__testcapi=yes -else $as_nop - py_cv_module__testcapi=missing +else case e in #( + e) py_cv_module__testcapi=missing ;; +esac fi -else $as_nop - py_cv_module__testcapi=disabled +else case e in #( + e) py_cv_module__testcapi=disabled ;; +esac fi fi @@ -31518,11 +32824,13 @@ then : if true then : py_cv_module__testclinic=yes -else $as_nop - py_cv_module__testclinic=missing +else case e in #( + e) py_cv_module__testclinic=missing ;; +esac fi -else $as_nop - py_cv_module__testclinic=disabled +else case e in #( + e) py_cv_module__testclinic=disabled ;; +esac fi fi @@ -31556,11 +32864,13 @@ then : if true then : py_cv_module__testclinic_limited=yes -else $as_nop - py_cv_module__testclinic_limited=missing +else case e in #( + e) py_cv_module__testclinic_limited=missing ;; +esac fi -else $as_nop - py_cv_module__testclinic_limited=disabled +else case e in #( + e) py_cv_module__testclinic_limited=disabled ;; +esac fi fi @@ -31594,11 +32904,13 @@ then : if true then : py_cv_module__testlimitedcapi=yes -else $as_nop - py_cv_module__testlimitedcapi=missing +else case e in #( + e) py_cv_module__testlimitedcapi=missing ;; +esac fi -else $as_nop - py_cv_module__testlimitedcapi=disabled +else case e in #( + e) py_cv_module__testlimitedcapi=disabled ;; +esac fi fi @@ -31632,11 +32944,13 @@ then : if true then : py_cv_module__testinternalcapi=yes -else $as_nop - py_cv_module__testinternalcapi=missing +else case e in #( + e) py_cv_module__testinternalcapi=missing ;; +esac fi -else $as_nop - py_cv_module__testinternalcapi=disabled +else case e in #( + e) py_cv_module__testinternalcapi=disabled ;; +esac fi fi @@ -31670,11 +32984,13 @@ then : if true then : py_cv_module__testbuffer=yes -else $as_nop - py_cv_module__testbuffer=missing +else case e in #( + e) py_cv_module__testbuffer=missing ;; +esac fi -else $as_nop - py_cv_module__testbuffer=disabled +else case e in #( + e) py_cv_module__testbuffer=disabled ;; +esac fi fi @@ -31708,11 +33024,13 @@ then : if test "$ac_cv_func_dlopen" = yes then : py_cv_module__testimportmultiple=yes -else $as_nop - py_cv_module__testimportmultiple=missing +else case e in #( + e) py_cv_module__testimportmultiple=missing ;; +esac fi -else $as_nop - py_cv_module__testimportmultiple=disabled +else case e in #( + e) py_cv_module__testimportmultiple=disabled ;; +esac fi fi @@ -31746,11 +33064,13 @@ then : if test "$ac_cv_func_dlopen" = yes then : py_cv_module__testmultiphase=yes -else $as_nop - py_cv_module__testmultiphase=missing +else case e in #( + e) py_cv_module__testmultiphase=missing ;; +esac fi -else $as_nop - py_cv_module__testmultiphase=disabled +else case e in #( + e) py_cv_module__testmultiphase=disabled ;; +esac fi fi @@ -31784,11 +33104,13 @@ then : if test "$ac_cv_func_dlopen" = yes then : py_cv_module__testsinglephase=yes -else $as_nop - py_cv_module__testsinglephase=missing +else case e in #( + e) py_cv_module__testsinglephase=missing ;; +esac fi -else $as_nop - py_cv_module__testsinglephase=disabled +else case e in #( + e) py_cv_module__testsinglephase=disabled ;; +esac fi fi @@ -31822,11 +33144,13 @@ then : if true then : py_cv_module__testexternalinspection=yes -else $as_nop - py_cv_module__testexternalinspection=missing +else case e in #( + e) py_cv_module__testexternalinspection=missing ;; +esac fi -else $as_nop - py_cv_module__testexternalinspection=disabled +else case e in #( + e) py_cv_module__testexternalinspection=disabled ;; +esac fi fi @@ -31860,11 +33184,13 @@ then : if true then : py_cv_module_xxsubtype=yes -else $as_nop - py_cv_module_xxsubtype=missing +else case e in #( + e) py_cv_module_xxsubtype=missing ;; +esac fi -else $as_nop - py_cv_module_xxsubtype=disabled +else case e in #( + e) py_cv_module_xxsubtype=disabled ;; +esac fi fi @@ -31898,11 +33224,13 @@ then : if true then : py_cv_module__xxtestfuzz=yes -else $as_nop - py_cv_module__xxtestfuzz=missing +else case e in #( + e) py_cv_module__xxtestfuzz=missing ;; +esac fi -else $as_nop - py_cv_module__xxtestfuzz=disabled +else case e in #( + e) py_cv_module__xxtestfuzz=disabled ;; +esac fi fi @@ -31936,11 +33264,13 @@ then : if test "$have_libffi" = yes -a "$ac_cv_func_dlopen" = yes then : py_cv_module__ctypes_test=yes -else $as_nop - py_cv_module__ctypes_test=missing +else case e in #( + e) py_cv_module__ctypes_test=missing ;; +esac fi -else $as_nop - py_cv_module__ctypes_test=disabled +else case e in #( + e) py_cv_module__ctypes_test=disabled ;; +esac fi fi @@ -31975,11 +33305,13 @@ then : if test "$ac_cv_func_dlopen" = yes then : py_cv_module_xxlimited=yes -else $as_nop - py_cv_module_xxlimited=missing +else case e in #( + e) py_cv_module_xxlimited=missing ;; +esac fi -else $as_nop - py_cv_module_xxlimited=disabled +else case e in #( + e) py_cv_module_xxlimited=disabled ;; +esac fi fi @@ -32013,11 +33345,13 @@ then : if test "$ac_cv_func_dlopen" = yes then : py_cv_module_xxlimited_35=yes -else $as_nop - py_cv_module_xxlimited_35=missing +else case e in #( + e) py_cv_module_xxlimited_35=missing ;; +esac fi -else $as_nop - py_cv_module_xxlimited_35=disabled +else case e in #( + e) py_cv_module_xxlimited_35=disabled ;; +esac fi fi @@ -32062,8 +33396,8 @@ cat >confcache <<\_ACEOF # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # -# `ac_cv_env_foo' variables (set or unset) will be overridden when -# loading this file, other *unset* `ac_cv_foo' will be assigned the +# 'ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* 'ac_cv_foo' will be assigned the # following values. _ACEOF @@ -32093,14 +33427,14 @@ printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) - # `set' does not quote correctly, so add quotes: double-quote + # 'set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) - # `set' quotes correctly as required by POSIX, so do not add quotes. + # 'set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | @@ -32519,7 +33853,6 @@ cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh -as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh @@ -32528,12 +33861,13 @@ then : # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST -else $as_nop - case `(set -o) 2>/dev/null` in #( +else case e in #( + e) case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; +esac ;; esac fi @@ -32605,7 +33939,7 @@ IFS=$as_save_IFS ;; esac -# We did not find ourselves, most probably we were run as `sh COMMAND' +# We did not find ourselves, most probably we were run as 'sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 @@ -32634,7 +33968,6 @@ as_fn_error () } # as_fn_error - # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. @@ -32674,11 +34007,12 @@ then : { eval $1+=\$2 }' -else $as_nop - as_fn_append () +else case e in #( + e) as_fn_append () { eval $1=\$$1\$2 - } + } ;; +esac fi # as_fn_append # as_fn_arith ARG... @@ -32692,11 +34026,12 @@ then : { as_val=$(( $* )) }' -else $as_nop - as_fn_arith () +else case e in #( + e) as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` - } + } ;; +esac fi # as_fn_arith @@ -32779,9 +34114,9 @@ if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. + # 1) On MSYS, both 'ln -s file dir' and 'ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; 'ln -s' creates a wrapper executable. + # In both cases, we have to default to 'cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then @@ -32862,10 +34197,12 @@ as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" +as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" +as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated # Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" +as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g" +as_tr_sh="eval sed '$as_sed_sh'" # deprecated exec 6>&1 @@ -32881,7 +34218,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # values after options handling. ac_log=" This file was extended by python $as_me 3.14, which was -generated by GNU Autoconf 2.71. Invocation command line was +generated by GNU Autoconf 2.72. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -32912,7 +34249,7 @@ _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ -\`$as_me' instantiates files and other configuration actions +'$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. @@ -32945,10 +34282,10 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ python config.status 3.14 -configured by $0, generated by GNU Autoconf 2.71, +configured by $0, generated by GNU Autoconf 2.72, with options \\"\$ac_cs_config\\" -Copyright (C) 2021 Free Software Foundation, Inc. +Copyright (C) 2023 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -33009,8 +34346,8 @@ do ac_need_defaults=false;; --he | --h) # Conflict between --help and --header - as_fn_error $? "ambiguous option: \`$1' -Try \`$0 --help' for more information.";; + as_fn_error $? "ambiguous option: '$1' +Try '$0 --help' for more information.";; --help | --hel | -h ) printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ @@ -33018,8 +34355,8 @@ Try \`$0 --help' for more information.";; ac_cs_silent=: ;; # This is an error. - -*) as_fn_error $? "unrecognized option: \`$1' -Try \`$0 --help' for more information." ;; + -*) as_fn_error $? "unrecognized option: '$1' +Try '$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; @@ -33081,7 +34418,7 @@ do "Modules/Setup.stdlib") CONFIG_FILES="$CONFIG_FILES Modules/Setup.stdlib" ;; "Modules/ld_so_aix") CONFIG_FILES="$CONFIG_FILES Modules/ld_so_aix" ;; - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + *) as_fn_error $? "invalid argument: '$ac_config_target'" "$LINENO" 5;; esac done @@ -33100,7 +34437,7 @@ fi # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: -# after its creation but before its name has been assigned to `$tmp'. +# after its creation but before its name has been assigned to '$tmp'. $debug || { tmp= ac_tmp= @@ -33124,7 +34461,7 @@ ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. -# This happens for instance with `./config.status config.h'. +# This happens for instance with './config.status config.h'. if test -n "$CONFIG_FILES"; then @@ -33282,13 +34619,13 @@ fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. -# This happens for instance with `./config.status Makefile'. +# This happens for instance with './config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF -# Transform confdefs.h into an awk script `defines.awk', embedded as +# Transform confdefs.h into an awk script 'defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. @@ -33398,7 +34735,7 @@ do esac case $ac_mode$ac_tag in :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :L* | :C*:*) as_fn_error $? "invalid tag '$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac @@ -33420,19 +34757,19 @@ do -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, - # because $ac_f cannot contain `:'. + # because $ac_f cannot contain ':'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + as_fn_error 1 "cannot find input file: '$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done - # Let's still pretend it is `configure' which instantiates (i.e., don't + # Let's still pretend it is 'configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` @@ -33565,7 +34902,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 esac _ACEOF -# Neutralize VPATH when `$srcdir' = `.'. +# Neutralize VPATH when '$srcdir' = '.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 @@ -33596,9 +34933,9 @@ test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable 'datarootdir' which seems to be undefined. Please make sure it is defined" >&5 -printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable 'datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" diff --git a/install-sh b/install-sh index ec298b53740270..7c56c9c0151036 100755 --- a/install-sh +++ b/install-sh @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2020-11-14.01; # UTC +scriptversion=2023-11-23.18; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -124,9 +124,9 @@ it's up to you to specify -f if you want it. If -S is not specified, no backups are attempted. -Email bug reports to bug-automake@gnu.org. -Automake home page: https://www.gnu.org/software/automake/ -" +Report bugs to . +GNU Automake home page: . +General help using GNU software: ." while test $# -ne 0; do case $1 in diff --git a/pyconfig.h.in b/pyconfig.h.in index ca08f087a85e9f..d862966b7de4c8 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -16,13 +16,13 @@ support for AIX C++ shared extension modules. */ #undef AIX_GENUINE_CPLUSPLUS -/* The normal alignment of `long', in bytes. */ +/* The normal alignment of 'long', in bytes. */ #undef ALIGNOF_LONG -/* The normal alignment of `max_align_t', in bytes. */ +/* The normal alignment of 'max_align_t', in bytes. */ #undef ALIGNOF_MAX_ALIGN_T -/* The normal alignment of `size_t', in bytes. */ +/* The normal alignment of 'size_t', in bytes. */ #undef ALIGNOF_SIZE_T /* Alternative SOABI used in debug build to load C extensions built in release @@ -59,16 +59,16 @@ /* Define if you have the 'accept' function. */ #undef HAVE_ACCEPT -/* Define to 1 if you have the `accept4' function. */ +/* Define to 1 if you have the 'accept4' function. */ #undef HAVE_ACCEPT4 -/* Define to 1 if you have the `acosh' function. */ +/* Define to 1 if you have the 'acosh' function. */ #undef HAVE_ACOSH /* struct addrinfo (netdb.h) */ #undef HAVE_ADDRINFO -/* Define to 1 if you have the `alarm' function. */ +/* Define to 1 if you have the 'alarm' function. */ #undef HAVE_ALARM /* Define if aligned memory access is required */ @@ -80,19 +80,19 @@ /* Define this if your time.h defines altzone. */ #undef HAVE_ALTZONE -/* Define to 1 if you have the `asinh' function. */ +/* Define to 1 if you have the 'asinh' function. */ #undef HAVE_ASINH /* Define to 1 if you have the header file. */ #undef HAVE_ASM_TYPES_H -/* Define to 1 if you have the `atanh' function. */ +/* Define to 1 if you have the 'atanh' function. */ #undef HAVE_ATANH /* Define if you have the 'bind' function. */ #undef HAVE_BIND -/* Define to 1 if you have the `bind_textdomain_codeset' function. */ +/* Define to 1 if you have the 'bind_textdomain_codeset' function. */ #undef HAVE_BIND_TEXTDOMAIN_CODESET /* Define to 1 if you have the header file. */ @@ -135,43 +135,43 @@ /* Define to 1 if you have the 'chflags' function. */ #undef HAVE_CHFLAGS -/* Define to 1 if you have the `chmod' function. */ +/* Define to 1 if you have the 'chmod' function. */ #undef HAVE_CHMOD -/* Define to 1 if you have the `chown' function. */ +/* Define to 1 if you have the 'chown' function. */ #undef HAVE_CHOWN /* Define if you have the 'chroot' function. */ #undef HAVE_CHROOT -/* Define to 1 if you have the `clock' function. */ +/* Define to 1 if you have the 'clock' function. */ #undef HAVE_CLOCK -/* Define to 1 if you have the `clock_getres' function. */ +/* Define to 1 if you have the 'clock_getres' function. */ #undef HAVE_CLOCK_GETRES -/* Define to 1 if you have the `clock_gettime' function. */ +/* Define to 1 if you have the 'clock_gettime' function. */ #undef HAVE_CLOCK_GETTIME -/* Define to 1 if you have the `clock_nanosleep' function. */ +/* Define to 1 if you have the 'clock_nanosleep' function. */ #undef HAVE_CLOCK_NANOSLEEP -/* Define to 1 if you have the `clock_settime' function. */ +/* Define to 1 if you have the 'clock_settime' function. */ #undef HAVE_CLOCK_SETTIME -/* Define to 1 if the system has the type `clock_t'. */ +/* Define to 1 if the system has the type 'clock_t'. */ #undef HAVE_CLOCK_T -/* Define to 1 if you have the `closefrom' function. */ +/* Define to 1 if you have the 'closefrom' function. */ #undef HAVE_CLOSEFROM -/* Define to 1 if you have the `close_range' function. */ +/* Define to 1 if you have the 'close_range' function. */ #undef HAVE_CLOSE_RANGE /* Define if the C compiler supports computed gotos. */ #undef HAVE_COMPUTED_GOTOS -/* Define to 1 if you have the `confstr' function. */ +/* Define to 1 if you have the 'confstr' function. */ #undef HAVE_CONFSTR /* Define to 1 if you have the header file. */ @@ -180,10 +180,10 @@ /* Define if you have the 'connect' function. */ #undef HAVE_CONNECT -/* Define to 1 if you have the `copy_file_range' function. */ +/* Define to 1 if you have the 'copy_file_range' function. */ #undef HAVE_COPY_FILE_RANGE -/* Define to 1 if you have the `ctermid' function. */ +/* Define to 1 if you have the 'ctermid' function. */ #undef HAVE_CTERMID /* Define if you have the 'ctermid_r' function. */ @@ -228,39 +228,39 @@ /* Define to 1 if you have the header file. */ #undef HAVE_DB_H -/* Define to 1 if you have the declaration of `RTLD_DEEPBIND', and to 0 if you +/* Define to 1 if you have the declaration of 'RTLD_DEEPBIND', and to 0 if you don't. */ #undef HAVE_DECL_RTLD_DEEPBIND -/* Define to 1 if you have the declaration of `RTLD_GLOBAL', and to 0 if you +/* Define to 1 if you have the declaration of 'RTLD_GLOBAL', and to 0 if you don't. */ #undef HAVE_DECL_RTLD_GLOBAL -/* Define to 1 if you have the declaration of `RTLD_LAZY', and to 0 if you +/* Define to 1 if you have the declaration of 'RTLD_LAZY', and to 0 if you don't. */ #undef HAVE_DECL_RTLD_LAZY -/* Define to 1 if you have the declaration of `RTLD_LOCAL', and to 0 if you +/* Define to 1 if you have the declaration of 'RTLD_LOCAL', and to 0 if you don't. */ #undef HAVE_DECL_RTLD_LOCAL -/* Define to 1 if you have the declaration of `RTLD_MEMBER', and to 0 if you +/* Define to 1 if you have the declaration of 'RTLD_MEMBER', and to 0 if you don't. */ #undef HAVE_DECL_RTLD_MEMBER -/* Define to 1 if you have the declaration of `RTLD_NODELETE', and to 0 if you +/* Define to 1 if you have the declaration of 'RTLD_NODELETE', and to 0 if you don't. */ #undef HAVE_DECL_RTLD_NODELETE -/* Define to 1 if you have the declaration of `RTLD_NOLOAD', and to 0 if you +/* Define to 1 if you have the declaration of 'RTLD_NOLOAD', and to 0 if you don't. */ #undef HAVE_DECL_RTLD_NOLOAD -/* Define to 1 if you have the declaration of `RTLD_NOW', and to 0 if you +/* Define to 1 if you have the declaration of 'RTLD_NOW', and to 0 if you don't. */ #undef HAVE_DECL_RTLD_NOW -/* Define to 1 if you have the declaration of `tzname', and to 0 if you don't. +/* Define to 1 if you have the declaration of 'tzname', and to 0 if you don't. */ #undef HAVE_DECL_TZNAME @@ -279,7 +279,7 @@ /* Define to 1 if the dirent structure has a d_type field */ #undef HAVE_DIRENT_D_TYPE -/* Define to 1 if you have the header file, and it defines `DIR'. +/* Define to 1 if you have the header file, and it defines 'DIR'. */ #undef HAVE_DIRENT_H @@ -289,16 +289,16 @@ /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H -/* Define to 1 if you have the `dlopen' function. */ +/* Define to 1 if you have the 'dlopen' function. */ #undef HAVE_DLOPEN -/* Define to 1 if you have the `dup' function. */ +/* Define to 1 if you have the 'dup' function. */ #undef HAVE_DUP -/* Define to 1 if you have the `dup2' function. */ +/* Define to 1 if you have the 'dup2' function. */ #undef HAVE_DUP2 -/* Define to 1 if you have the `dup3' function. */ +/* Define to 1 if you have the 'dup3' function. */ #undef HAVE_DUP3 /* Define if you have the '_dyld_shared_cache_contains_path' function. */ @@ -319,10 +319,10 @@ /* Define if you have the 'epoll_create1' function. */ #undef HAVE_EPOLL_CREATE1 -/* Define to 1 if you have the `erf' function. */ +/* Define to 1 if you have the 'erf' function. */ #undef HAVE_ERF -/* Define to 1 if you have the `erfc' function. */ +/* Define to 1 if you have the 'erfc' function. */ #undef HAVE_ERFC /* Define to 1 if you have the header file. */ @@ -331,34 +331,34 @@ /* Define if you have the 'eventfd' function. */ #undef HAVE_EVENTFD -/* Define to 1 if you have the `execv' function. */ +/* Define to 1 if you have the 'execv' function. */ #undef HAVE_EXECV -/* Define to 1 if you have the `explicit_bzero' function. */ +/* Define to 1 if you have the 'explicit_bzero' function. */ #undef HAVE_EXPLICIT_BZERO -/* Define to 1 if you have the `explicit_memset' function. */ +/* Define to 1 if you have the 'explicit_memset' function. */ #undef HAVE_EXPLICIT_MEMSET -/* Define to 1 if you have the `expm1' function. */ +/* Define to 1 if you have the 'expm1' function. */ #undef HAVE_EXPM1 -/* Define to 1 if you have the `faccessat' function. */ +/* Define to 1 if you have the 'faccessat' function. */ #undef HAVE_FACCESSAT /* Define if you have the 'fchdir' function. */ #undef HAVE_FCHDIR -/* Define to 1 if you have the `fchmod' function. */ +/* Define to 1 if you have the 'fchmod' function. */ #undef HAVE_FCHMOD -/* Define to 1 if you have the `fchmodat' function. */ +/* Define to 1 if you have the 'fchmodat' function. */ #undef HAVE_FCHMODAT -/* Define to 1 if you have the `fchown' function. */ +/* Define to 1 if you have the 'fchown' function. */ #undef HAVE_FCHOWN -/* Define to 1 if you have the `fchownat' function. */ +/* Define to 1 if you have the 'fchownat' function. */ #undef HAVE_FCHOWNAT /* Define to 1 if you have the header file. */ @@ -367,13 +367,13 @@ /* Define if you have the 'fdatasync' function. */ #undef HAVE_FDATASYNC -/* Define to 1 if you have the `fdopendir' function. */ +/* Define to 1 if you have the 'fdopendir' function. */ #undef HAVE_FDOPENDIR -/* Define to 1 if you have the `fdwalk' function. */ +/* Define to 1 if you have the 'fdwalk' function. */ #undef HAVE_FDWALK -/* Define to 1 if you have the `fexecve' function. */ +/* Define to 1 if you have the 'fexecve' function. */ #undef HAVE_FEXECVE /* Define if you have the 'ffi_closure_alloc' function. */ @@ -385,58 +385,58 @@ /* Define if you have the 'ffi_prep_closure_loc' function. */ #undef HAVE_FFI_PREP_CLOSURE_LOC -/* Define to 1 if you have the `flock' function. */ +/* Define to 1 if you have the 'flock' function. */ #undef HAVE_FLOCK -/* Define to 1 if you have the `fork' function. */ +/* Define to 1 if you have the 'fork' function. */ #undef HAVE_FORK -/* Define to 1 if you have the `fork1' function. */ +/* Define to 1 if you have the 'fork1' function. */ #undef HAVE_FORK1 -/* Define to 1 if you have the `forkpty' function. */ +/* Define to 1 if you have the 'forkpty' function. */ #undef HAVE_FORKPTY -/* Define to 1 if you have the `fpathconf' function. */ +/* Define to 1 if you have the 'fpathconf' function. */ #undef HAVE_FPATHCONF -/* Define to 1 if you have the `fseek64' function. */ +/* Define to 1 if you have the 'fseek64' function. */ #undef HAVE_FSEEK64 -/* Define to 1 if you have the `fseeko' function. */ +/* Define to 1 if you have the 'fseeko' function. */ #undef HAVE_FSEEKO -/* Define to 1 if you have the `fstatat' function. */ +/* Define to 1 if you have the 'fstatat' function. */ #undef HAVE_FSTATAT -/* Define to 1 if you have the `fstatvfs' function. */ +/* Define to 1 if you have the 'fstatvfs' function. */ #undef HAVE_FSTATVFS /* Define if you have the 'fsync' function. */ #undef HAVE_FSYNC -/* Define to 1 if you have the `ftell64' function. */ +/* Define to 1 if you have the 'ftell64' function. */ #undef HAVE_FTELL64 -/* Define to 1 if you have the `ftello' function. */ +/* Define to 1 if you have the 'ftello' function. */ #undef HAVE_FTELLO -/* Define to 1 if you have the `ftime' function. */ +/* Define to 1 if you have the 'ftime' function. */ #undef HAVE_FTIME -/* Define to 1 if you have the `ftruncate' function. */ +/* Define to 1 if you have the 'ftruncate' function. */ #undef HAVE_FTRUNCATE -/* Define to 1 if you have the `futimens' function. */ +/* Define to 1 if you have the 'futimens' function. */ #undef HAVE_FUTIMENS -/* Define to 1 if you have the `futimes' function. */ +/* Define to 1 if you have the 'futimes' function. */ #undef HAVE_FUTIMES -/* Define to 1 if you have the `futimesat' function. */ +/* Define to 1 if you have the 'futimesat' function. */ #undef HAVE_FUTIMESAT -/* Define to 1 if you have the `gai_strerror' function. */ +/* Define to 1 if you have the 'gai_strerror' function. */ #undef HAVE_GAI_STRERROR /* Define if we can use gcc inline assembler to get and set mc68881 fpcr */ @@ -467,40 +467,40 @@ /* Define this if you have flockfile(), getc_unlocked(), and funlockfile() */ #undef HAVE_GETC_UNLOCKED -/* Define to 1 if you have the `getegid' function. */ +/* Define to 1 if you have the 'getegid' function. */ #undef HAVE_GETEGID -/* Define to 1 if you have the `getentropy' function. */ +/* Define to 1 if you have the 'getentropy' function. */ #undef HAVE_GETENTROPY -/* Define to 1 if you have the `geteuid' function. */ +/* Define to 1 if you have the 'geteuid' function. */ #undef HAVE_GETEUID -/* Define to 1 if you have the `getgid' function. */ +/* Define to 1 if you have the 'getgid' function. */ #undef HAVE_GETGID -/* Define to 1 if you have the `getgrent' function. */ +/* Define to 1 if you have the 'getgrent' function. */ #undef HAVE_GETGRENT -/* Define to 1 if you have the `getgrgid' function. */ +/* Define to 1 if you have the 'getgrgid' function. */ #undef HAVE_GETGRGID -/* Define to 1 if you have the `getgrgid_r' function. */ +/* Define to 1 if you have the 'getgrgid_r' function. */ #undef HAVE_GETGRGID_R -/* Define to 1 if you have the `getgrnam_r' function. */ +/* Define to 1 if you have the 'getgrnam_r' function. */ #undef HAVE_GETGRNAM_R -/* Define to 1 if you have the `getgrouplist' function. */ +/* Define to 1 if you have the 'getgrouplist' function. */ #undef HAVE_GETGROUPLIST -/* Define to 1 if you have the `getgroups' function. */ +/* Define to 1 if you have the 'getgroups' function. */ #undef HAVE_GETGROUPS /* Define if you have the 'gethostbyaddr' function. */ #undef HAVE_GETHOSTBYADDR -/* Define to 1 if you have the `gethostbyname' function. */ +/* Define to 1 if you have the 'gethostbyname' function. */ #undef HAVE_GETHOSTBYNAME /* Define this if you have some version of gethostbyname_r() */ @@ -515,19 +515,19 @@ /* Define this if you have the 6-arg version of gethostbyname_r(). */ #undef HAVE_GETHOSTBYNAME_R_6_ARG -/* Define to 1 if you have the `gethostname' function. */ +/* Define to 1 if you have the 'gethostname' function. */ #undef HAVE_GETHOSTNAME -/* Define to 1 if you have the `getitimer' function. */ +/* Define to 1 if you have the 'getitimer' function. */ #undef HAVE_GETITIMER -/* Define to 1 if you have the `getloadavg' function. */ +/* Define to 1 if you have the 'getloadavg' function. */ #undef HAVE_GETLOADAVG -/* Define to 1 if you have the `getlogin' function. */ +/* Define to 1 if you have the 'getlogin' function. */ #undef HAVE_GETLOGIN -/* Define to 1 if you have the `getnameinfo' function. */ +/* Define to 1 if you have the 'getnameinfo' function. */ #undef HAVE_GETNAMEINFO /* Define if you have the 'getpagesize' function. */ @@ -536,34 +536,34 @@ /* Define if you have the 'getpeername' function. */ #undef HAVE_GETPEERNAME -/* Define to 1 if you have the `getpgid' function. */ +/* Define to 1 if you have the 'getpgid' function. */ #undef HAVE_GETPGID -/* Define to 1 if you have the `getpgrp' function. */ +/* Define to 1 if you have the 'getpgrp' function. */ #undef HAVE_GETPGRP -/* Define to 1 if you have the `getpid' function. */ +/* Define to 1 if you have the 'getpid' function. */ #undef HAVE_GETPID -/* Define to 1 if you have the `getppid' function. */ +/* Define to 1 if you have the 'getppid' function. */ #undef HAVE_GETPPID -/* Define to 1 if you have the `getpriority' function. */ +/* Define to 1 if you have the 'getpriority' function. */ #undef HAVE_GETPRIORITY /* Define if you have the 'getprotobyname' function. */ #undef HAVE_GETPROTOBYNAME -/* Define to 1 if you have the `getpwent' function. */ +/* Define to 1 if you have the 'getpwent' function. */ #undef HAVE_GETPWENT -/* Define to 1 if you have the `getpwnam_r' function. */ +/* Define to 1 if you have the 'getpwnam_r' function. */ #undef HAVE_GETPWNAM_R -/* Define to 1 if you have the `getpwuid' function. */ +/* Define to 1 if you have the 'getpwuid' function. */ #undef HAVE_GETPWUID -/* Define to 1 if you have the `getpwuid_r' function. */ +/* Define to 1 if you have the 'getpwuid_r' function. */ #undef HAVE_GETPWUID_R /* Define to 1 if the getrandom() function is available */ @@ -572,13 +572,13 @@ /* Define to 1 if the Linux getrandom() syscall is available */ #undef HAVE_GETRANDOM_SYSCALL -/* Define to 1 if you have the `getresgid' function. */ +/* Define to 1 if you have the 'getresgid' function. */ #undef HAVE_GETRESGID -/* Define to 1 if you have the `getresuid' function. */ +/* Define to 1 if you have the 'getresuid' function. */ #undef HAVE_GETRESUID -/* Define to 1 if you have the `getrusage' function. */ +/* Define to 1 if you have the 'getrusage' function. */ #undef HAVE_GETRUSAGE /* Define if you have the 'getservbyname' function. */ @@ -587,29 +587,29 @@ /* Define if you have the 'getservbyport' function. */ #undef HAVE_GETSERVBYPORT -/* Define to 1 if you have the `getsid' function. */ +/* Define to 1 if you have the 'getsid' function. */ #undef HAVE_GETSID /* Define if you have the 'getsockname' function. */ #undef HAVE_GETSOCKNAME -/* Define to 1 if you have the `getspent' function. */ +/* Define to 1 if you have the 'getspent' function. */ #undef HAVE_GETSPENT -/* Define to 1 if you have the `getspnam' function. */ +/* Define to 1 if you have the 'getspnam' function. */ #undef HAVE_GETSPNAM -/* Define to 1 if you have the `getuid' function. */ +/* Define to 1 if you have the 'getuid' function. */ #undef HAVE_GETUID -/* Define to 1 if you have the `getwd' function. */ +/* Define to 1 if you have the 'getwd' function. */ #undef HAVE_GETWD /* Define if glibc has incorrect _FORTIFY_SOURCE wrappers for memmove and bcopy. */ #undef HAVE_GLIBC_MEMMOVE_BUG -/* Define to 1 if you have the `grantpt' function. */ +/* Define to 1 if you have the 'grantpt' function. */ #undef HAVE_GRANTPT /* Define to 1 if you have the header file. */ @@ -621,7 +621,7 @@ /* Define this if you have le64toh() */ #undef HAVE_HTOLE64 -/* Define to 1 if you have the `if_nameindex' function. */ +/* Define to 1 if you have the 'if_nameindex' function. */ #undef HAVE_IF_NAMEINDEX /* Define if you have the 'inet_aton' function. */ @@ -633,7 +633,7 @@ /* Define if you have the 'inet_pton' function. */ #undef HAVE_INET_PTON -/* Define to 1 if you have the `initgroups' function. */ +/* Define to 1 if you have the 'initgroups' function. */ #undef HAVE_INITGROUPS /* Define to 1 if you have the header file. */ @@ -645,10 +645,10 @@ /* Define if gcc has the ipa-pure-const bug. */ #undef HAVE_IPA_PURE_CONST_BUG -/* Define to 1 if you have the `kill' function. */ +/* Define to 1 if you have the 'kill' function. */ #undef HAVE_KILL -/* Define to 1 if you have the `killpg' function. */ +/* Define to 1 if you have the 'killpg' function. */ #undef HAVE_KILLPG /* Define if you have the 'kqueue' function. */ @@ -666,31 +666,31 @@ /* Define to 1 if you have the 'lchflags' function. */ #undef HAVE_LCHFLAGS -/* Define to 1 if you have the `lchmod' function. */ +/* Define to 1 if you have the 'lchmod' function. */ #undef HAVE_LCHMOD -/* Define to 1 if you have the `lchown' function. */ +/* Define to 1 if you have the 'lchown' function. */ #undef HAVE_LCHOWN /* Define to 1 if you have the `db' library (-ldb). */ #undef HAVE_LIBDB -/* Define to 1 if you have the `dl' library (-ldl). */ +/* Define to 1 if you have the 'dl' library (-ldl). */ #undef HAVE_LIBDL -/* Define to 1 if you have the `dld' library (-ldld). */ +/* Define to 1 if you have the 'dld' library (-ldld). */ #undef HAVE_LIBDLD -/* Define to 1 if you have the `ieee' library (-lieee). */ +/* Define to 1 if you have the 'ieee' library (-lieee). */ #undef HAVE_LIBIEEE /* Define to 1 if you have the header file. */ #undef HAVE_LIBINTL_H -/* Define to 1 if you have the `sendfile' library (-lsendfile). */ +/* Define to 1 if you have the 'sendfile' library (-lsendfile). */ #undef HAVE_LIBSENDFILE -/* Define to 1 if you have the `sqlite3' library (-lsqlite3). */ +/* Define to 1 if you have the 'sqlite3' library (-lsqlite3). */ #undef HAVE_LIBSQLITE3 /* Define to 1 if you have the header file. */ @@ -699,7 +699,7 @@ /* Define if you have the 'link' function. */ #undef HAVE_LINK -/* Define to 1 if you have the `linkat' function. */ +/* Define to 1 if you have the 'linkat' function. */ #undef HAVE_LINKAT /* Define to 1 if you have the header file. */ @@ -762,73 +762,73 @@ /* Define if you have the 'listen' function. */ #undef HAVE_LISTEN -/* Define to 1 if you have the `lockf' function. */ +/* Define to 1 if you have the 'lockf' function. */ #undef HAVE_LOCKF -/* Define to 1 if you have the `log1p' function. */ +/* Define to 1 if you have the 'log1p' function. */ #undef HAVE_LOG1P -/* Define to 1 if you have the `log2' function. */ +/* Define to 1 if you have the 'log2' function. */ #undef HAVE_LOG2 /* Define to 1 if you have the `login_tty' function. */ #undef HAVE_LOGIN_TTY -/* Define to 1 if the system has the type `long double'. */ +/* Define to 1 if the system has the type 'long double'. */ #undef HAVE_LONG_DOUBLE -/* Define to 1 if you have the `lstat' function. */ +/* Define to 1 if you have the 'lstat' function. */ #undef HAVE_LSTAT -/* Define to 1 if you have the `lutimes' function. */ +/* Define to 1 if you have the 'lutimes' function. */ #undef HAVE_LUTIMES /* Define to 1 if you have the header file. */ #undef HAVE_LZMA_H -/* Define to 1 if you have the `madvise' function. */ +/* Define to 1 if you have the 'madvise' function. */ #undef HAVE_MADVISE /* Define this if you have the makedev macro. */ #undef HAVE_MAKEDEV -/* Define to 1 if you have the `mbrtowc' function. */ +/* Define to 1 if you have the 'mbrtowc' function. */ #undef HAVE_MBRTOWC /* Define if you have the 'memfd_create' function. */ #undef HAVE_MEMFD_CREATE -/* Define to 1 if you have the `memrchr' function. */ +/* Define to 1 if you have the 'memrchr' function. */ #undef HAVE_MEMRCHR /* Define to 1 if you have the header file. */ #undef HAVE_MINIX_CONFIG_H -/* Define to 1 if you have the `mkdirat' function. */ +/* Define to 1 if you have the 'mkdirat' function. */ #undef HAVE_MKDIRAT -/* Define to 1 if you have the `mkfifo' function. */ +/* Define to 1 if you have the 'mkfifo' function. */ #undef HAVE_MKFIFO -/* Define to 1 if you have the `mkfifoat' function. */ +/* Define to 1 if you have the 'mkfifoat' function. */ #undef HAVE_MKFIFOAT -/* Define to 1 if you have the `mknod' function. */ +/* Define to 1 if you have the 'mknod' function. */ #undef HAVE_MKNOD -/* Define to 1 if you have the `mknodat' function. */ +/* Define to 1 if you have the 'mknodat' function. */ #undef HAVE_MKNODAT -/* Define to 1 if you have the `mktime' function. */ +/* Define to 1 if you have the 'mktime' function. */ #undef HAVE_MKTIME -/* Define to 1 if you have the `mmap' function. */ +/* Define to 1 if you have the 'mmap' function. */ #undef HAVE_MMAP -/* Define to 1 if you have the `mremap' function. */ +/* Define to 1 if you have the 'mremap' function. */ #undef HAVE_MREMAP -/* Define to 1 if you have the `nanosleep' function. */ +/* Define to 1 if you have the 'nanosleep' function. */ #undef HAVE_NANOSLEEP /* Define if you have the 'ncurses' library */ @@ -861,7 +861,7 @@ /* Define to 1 if you have the header file. */ #undef HAVE_NDBM_H -/* Define to 1 if you have the header file, and it defines `DIR'. */ +/* Define to 1 if you have the header file, and it defines 'DIR'. */ #undef HAVE_NDIR_H /* Define to 1 if you have the header file. */ @@ -885,20 +885,20 @@ /* Define to 1 if you have the header file. */ #undef HAVE_NET_IF_H -/* Define to 1 if you have the `nice' function. */ +/* Define to 1 if you have the 'nice' function. */ #undef HAVE_NICE /* Define if the internal form of wchar_t in non-Unicode locales is not Unicode. */ #undef HAVE_NON_UNICODE_WCHAR_T_REPRESENTATION -/* Define to 1 if you have the `openat' function. */ +/* Define to 1 if you have the 'openat' function. */ #undef HAVE_OPENAT -/* Define to 1 if you have the `opendir' function. */ +/* Define to 1 if you have the 'opendir' function. */ #undef HAVE_OPENDIR -/* Define to 1 if you have the `openpty' function. */ +/* Define to 1 if you have the 'openpty' function. */ #undef HAVE_OPENPTY /* Define if you have the 'panel' library */ @@ -910,53 +910,53 @@ /* Define to 1 if you have the header file. */ #undef HAVE_PANEL_H -/* Define to 1 if you have the `pathconf' function. */ +/* Define to 1 if you have the 'pathconf' function. */ #undef HAVE_PATHCONF -/* Define to 1 if you have the `pause' function. */ +/* Define to 1 if you have the 'pause' function. */ #undef HAVE_PAUSE -/* Define to 1 if you have the `pipe' function. */ +/* Define to 1 if you have the 'pipe' function. */ #undef HAVE_PIPE -/* Define to 1 if you have the `pipe2' function. */ +/* Define to 1 if you have the 'pipe2' function. */ #undef HAVE_PIPE2 -/* Define to 1 if you have the `plock' function. */ +/* Define to 1 if you have the 'plock' function. */ #undef HAVE_PLOCK -/* Define to 1 if you have the `poll' function. */ +/* Define to 1 if you have the 'poll' function. */ #undef HAVE_POLL /* Define to 1 if you have the header file. */ #undef HAVE_POLL_H -/* Define to 1 if you have the `posix_fadvise' function. */ +/* Define to 1 if you have the 'posix_fadvise' function. */ #undef HAVE_POSIX_FADVISE -/* Define to 1 if you have the `posix_fallocate' function. */ +/* Define to 1 if you have the 'posix_fallocate' function. */ #undef HAVE_POSIX_FALLOCATE -/* Define to 1 if you have the `posix_openpt' function. */ +/* Define to 1 if you have the 'posix_openpt' function. */ #undef HAVE_POSIX_OPENPT -/* Define to 1 if you have the `posix_spawn' function. */ +/* Define to 1 if you have the 'posix_spawn' function. */ #undef HAVE_POSIX_SPAWN -/* Define to 1 if you have the `posix_spawnp' function. */ +/* Define to 1 if you have the 'posix_spawnp' function. */ #undef HAVE_POSIX_SPAWNP -/* Define to 1 if you have the `posix_spawn_file_actions_addclosefrom_np' +/* Define to 1 if you have the 'posix_spawn_file_actions_addclosefrom_np' function. */ #undef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP -/* Define to 1 if you have the `pread' function. */ +/* Define to 1 if you have the 'pread' function. */ #undef HAVE_PREAD -/* Define to 1 if you have the `preadv' function. */ +/* Define to 1 if you have the 'preadv' function. */ #undef HAVE_PREADV -/* Define to 1 if you have the `preadv2' function. */ +/* Define to 1 if you have the 'preadv2' function. */ #undef HAVE_PREADV2 /* Define if you have the 'prlimit' function. */ @@ -965,83 +965,83 @@ /* Define to 1 if you have the header file. */ #undef HAVE_PROCESS_H -/* Define to 1 if you have the `process_vm_readv' function. */ +/* Define to 1 if you have the 'process_vm_readv' function. */ #undef HAVE_PROCESS_VM_READV /* Define if your compiler supports function prototype */ #undef HAVE_PROTOTYPES -/* Define to 1 if you have the `pthread_condattr_setclock' function. */ +/* Define to 1 if you have the 'pthread_condattr_setclock' function. */ #undef HAVE_PTHREAD_CONDATTR_SETCLOCK -/* Define to 1 if you have the `pthread_cond_timedwait_relative_np' function. +/* Define to 1 if you have the 'pthread_cond_timedwait_relative_np' function. */ #undef HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP /* Defined for Solaris 2.6 bug in pthread header. */ #undef HAVE_PTHREAD_DESTRUCTOR -/* Define to 1 if you have the `pthread_getcpuclockid' function. */ +/* Define to 1 if you have the 'pthread_getcpuclockid' function. */ #undef HAVE_PTHREAD_GETCPUCLOCKID -/* Define to 1 if you have the `pthread_getname_np' function. */ +/* Define to 1 if you have the 'pthread_getname_np' function. */ #undef HAVE_PTHREAD_GETNAME_NP /* Define to 1 if you have the header file. */ #undef HAVE_PTHREAD_H -/* Define to 1 if you have the `pthread_init' function. */ +/* Define to 1 if you have the 'pthread_init' function. */ #undef HAVE_PTHREAD_INIT -/* Define to 1 if you have the `pthread_kill' function. */ +/* Define to 1 if you have the 'pthread_kill' function. */ #undef HAVE_PTHREAD_KILL -/* Define to 1 if you have the `pthread_setname_np' function. */ +/* Define to 1 if you have the 'pthread_setname_np' function. */ #undef HAVE_PTHREAD_SETNAME_NP -/* Define to 1 if you have the `pthread_sigmask' function. */ +/* Define to 1 if you have the 'pthread_sigmask' function. */ #undef HAVE_PTHREAD_SIGMASK /* Define if platform requires stubbed pthreads support */ #undef HAVE_PTHREAD_STUBS -/* Define to 1 if you have the `ptsname' function. */ +/* Define to 1 if you have the 'ptsname' function. */ #undef HAVE_PTSNAME -/* Define to 1 if you have the `ptsname_r' function. */ +/* Define to 1 if you have the 'ptsname_r' function. */ #undef HAVE_PTSNAME_R /* Define to 1 if you have the header file. */ #undef HAVE_PTY_H -/* Define to 1 if you have the `pwrite' function. */ +/* Define to 1 if you have the 'pwrite' function. */ #undef HAVE_PWRITE -/* Define to 1 if you have the `pwritev' function. */ +/* Define to 1 if you have the 'pwritev' function. */ #undef HAVE_PWRITEV -/* Define to 1 if you have the `pwritev2' function. */ +/* Define to 1 if you have the 'pwritev2' function. */ #undef HAVE_PWRITEV2 /* Define to 1 if you have the header file. */ #undef HAVE_READLINE_READLINE_H -/* Define to 1 if you have the `readlink' function. */ +/* Define to 1 if you have the 'readlink' function. */ #undef HAVE_READLINK -/* Define to 1 if you have the `readlinkat' function. */ +/* Define to 1 if you have the 'readlinkat' function. */ #undef HAVE_READLINKAT -/* Define to 1 if you have the `readv' function. */ +/* Define to 1 if you have the 'readv' function. */ #undef HAVE_READV -/* Define to 1 if you have the `realpath' function. */ +/* Define to 1 if you have the 'realpath' function. */ #undef HAVE_REALPATH /* Define if you have the 'recvfrom' function. */ #undef HAVE_RECVFROM -/* Define to 1 if you have the `renameat' function. */ +/* Define to 1 if you have the 'renameat' function. */ #undef HAVE_RENAMEAT /* Define if readline supports append_history */ @@ -1050,7 +1050,7 @@ /* Define if you can turn off readline's signal handling. */ #undef HAVE_RL_CATCH_SIGNAL -/* Define to 1 if the system has the type `rl_compdisp_func_t'. */ +/* Define to 1 if the system has the type 'rl_compdisp_func_t'. */ #undef HAVE_RL_COMPDISP_FUNC_T /* Define if you have readline 2.2 */ @@ -1071,154 +1071,154 @@ /* Define if you have readline 4.0 */ #undef HAVE_RL_RESIZE_TERMINAL -/* Define to 1 if you have the `rtpSpawn' function. */ +/* Define to 1 if you have the 'rtpSpawn' function. */ #undef HAVE_RTPSPAWN -/* Define to 1 if you have the `sched_get_priority_max' function. */ +/* Define to 1 if you have the 'sched_get_priority_max' function. */ #undef HAVE_SCHED_GET_PRIORITY_MAX /* Define to 1 if you have the header file. */ #undef HAVE_SCHED_H -/* Define to 1 if you have the `sched_rr_get_interval' function. */ +/* Define to 1 if you have the 'sched_rr_get_interval' function. */ #undef HAVE_SCHED_RR_GET_INTERVAL -/* Define to 1 if you have the `sched_setaffinity' function. */ +/* Define to 1 if you have the 'sched_setaffinity' function. */ #undef HAVE_SCHED_SETAFFINITY -/* Define to 1 if you have the `sched_setparam' function. */ +/* Define to 1 if you have the 'sched_setparam' function. */ #undef HAVE_SCHED_SETPARAM -/* Define to 1 if you have the `sched_setscheduler' function. */ +/* Define to 1 if you have the 'sched_setscheduler' function. */ #undef HAVE_SCHED_SETSCHEDULER -/* Define to 1 if you have the `sem_clockwait' function. */ +/* Define to 1 if you have the 'sem_clockwait' function. */ #undef HAVE_SEM_CLOCKWAIT -/* Define to 1 if you have the `sem_getvalue' function. */ +/* Define to 1 if you have the 'sem_getvalue' function. */ #undef HAVE_SEM_GETVALUE -/* Define to 1 if you have the `sem_open' function. */ +/* Define to 1 if you have the 'sem_open' function. */ #undef HAVE_SEM_OPEN -/* Define to 1 if you have the `sem_timedwait' function. */ +/* Define to 1 if you have the 'sem_timedwait' function. */ #undef HAVE_SEM_TIMEDWAIT -/* Define to 1 if you have the `sem_unlink' function. */ +/* Define to 1 if you have the 'sem_unlink' function. */ #undef HAVE_SEM_UNLINK -/* Define to 1 if you have the `sendfile' function. */ +/* Define to 1 if you have the 'sendfile' function. */ #undef HAVE_SENDFILE /* Define if you have the 'sendto' function. */ #undef HAVE_SENDTO -/* Define to 1 if you have the `setegid' function. */ +/* Define to 1 if you have the 'setegid' function. */ #undef HAVE_SETEGID -/* Define to 1 if you have the `seteuid' function. */ +/* Define to 1 if you have the 'seteuid' function. */ #undef HAVE_SETEUID -/* Define to 1 if you have the `setgid' function. */ +/* Define to 1 if you have the 'setgid' function. */ #undef HAVE_SETGID /* Define if you have the 'setgroups' function. */ #undef HAVE_SETGROUPS -/* Define to 1 if you have the `sethostname' function. */ +/* Define to 1 if you have the 'sethostname' function. */ #undef HAVE_SETHOSTNAME -/* Define to 1 if you have the `setitimer' function. */ +/* Define to 1 if you have the 'setitimer' function. */ #undef HAVE_SETITIMER /* Define to 1 if you have the header file. */ #undef HAVE_SETJMP_H -/* Define to 1 if you have the `setlocale' function. */ +/* Define to 1 if you have the 'setlocale' function. */ #undef HAVE_SETLOCALE -/* Define to 1 if you have the `setns' function. */ +/* Define to 1 if you have the 'setns' function. */ #undef HAVE_SETNS -/* Define to 1 if you have the `setpgid' function. */ +/* Define to 1 if you have the 'setpgid' function. */ #undef HAVE_SETPGID -/* Define to 1 if you have the `setpgrp' function. */ +/* Define to 1 if you have the 'setpgrp' function. */ #undef HAVE_SETPGRP -/* Define to 1 if you have the `setpriority' function. */ +/* Define to 1 if you have the 'setpriority' function. */ #undef HAVE_SETPRIORITY -/* Define to 1 if you have the `setregid' function. */ +/* Define to 1 if you have the 'setregid' function. */ #undef HAVE_SETREGID -/* Define to 1 if you have the `setresgid' function. */ +/* Define to 1 if you have the 'setresgid' function. */ #undef HAVE_SETRESGID -/* Define to 1 if you have the `setresuid' function. */ +/* Define to 1 if you have the 'setresuid' function. */ #undef HAVE_SETRESUID -/* Define to 1 if you have the `setreuid' function. */ +/* Define to 1 if you have the 'setreuid' function. */ #undef HAVE_SETREUID -/* Define to 1 if you have the `setsid' function. */ +/* Define to 1 if you have the 'setsid' function. */ #undef HAVE_SETSID /* Define if you have the 'setsockopt' function. */ #undef HAVE_SETSOCKOPT -/* Define to 1 if you have the `setuid' function. */ +/* Define to 1 if you have the 'setuid' function. */ #undef HAVE_SETUID -/* Define to 1 if you have the `setvbuf' function. */ +/* Define to 1 if you have the 'setvbuf' function. */ #undef HAVE_SETVBUF /* Define to 1 if you have the header file. */ #undef HAVE_SHADOW_H -/* Define to 1 if you have the `shm_open' function. */ +/* Define to 1 if you have the 'shm_open' function. */ #undef HAVE_SHM_OPEN -/* Define to 1 if you have the `shm_unlink' function. */ +/* Define to 1 if you have the 'shm_unlink' function. */ #undef HAVE_SHM_UNLINK -/* Define to 1 if you have the `shutdown' function. */ +/* Define to 1 if you have the 'shutdown' function. */ #undef HAVE_SHUTDOWN -/* Define to 1 if you have the `sigaction' function. */ +/* Define to 1 if you have the 'sigaction' function. */ #undef HAVE_SIGACTION -/* Define to 1 if you have the `sigaltstack' function. */ +/* Define to 1 if you have the 'sigaltstack' function. */ #undef HAVE_SIGALTSTACK -/* Define to 1 if you have the `sigfillset' function. */ +/* Define to 1 if you have the 'sigfillset' function. */ #undef HAVE_SIGFILLSET -/* Define to 1 if `si_band' is a member of `siginfo_t'. */ +/* Define to 1 if 'si_band' is a member of 'siginfo_t'. */ #undef HAVE_SIGINFO_T_SI_BAND -/* Define to 1 if you have the `siginterrupt' function. */ +/* Define to 1 if you have the 'siginterrupt' function. */ #undef HAVE_SIGINTERRUPT /* Define to 1 if you have the header file. */ #undef HAVE_SIGNAL_H -/* Define to 1 if you have the `sigpending' function. */ +/* Define to 1 if you have the 'sigpending' function. */ #undef HAVE_SIGPENDING -/* Define to 1 if you have the `sigrelse' function. */ +/* Define to 1 if you have the 'sigrelse' function. */ #undef HAVE_SIGRELSE -/* Define to 1 if you have the `sigtimedwait' function. */ +/* Define to 1 if you have the 'sigtimedwait' function. */ #undef HAVE_SIGTIMEDWAIT -/* Define to 1 if you have the `sigwait' function. */ +/* Define to 1 if you have the 'sigwait' function. */ #undef HAVE_SIGWAIT -/* Define to 1 if you have the `sigwaitinfo' function. */ +/* Define to 1 if you have the 'sigwaitinfo' function. */ #undef HAVE_SIGWAITINFO -/* Define to 1 if you have the `snprintf' function. */ +/* Define to 1 if you have the 'snprintf' function. */ #undef HAVE_SNPRINTF /* struct sockaddr_alg (linux/if_alg.h) */ @@ -1236,19 +1236,19 @@ /* Define if you have the 'socketpair' function. */ #undef HAVE_SOCKETPAIR -/* Define to 1 if the system has the type `socklen_t'. */ +/* Define to 1 if the system has the type 'socklen_t'. */ #undef HAVE_SOCKLEN_T /* Define to 1 if you have the header file. */ #undef HAVE_SPAWN_H -/* Define to 1 if you have the `splice' function. */ +/* Define to 1 if you have the 'splice' function. */ #undef HAVE_SPLICE -/* Define to 1 if the system has the type `ssize_t'. */ +/* Define to 1 if the system has the type 'ssize_t'. */ #undef HAVE_SSIZE_T -/* Define to 1 if you have the `statvfs' function. */ +/* Define to 1 if you have the 'statvfs' function. */ #undef HAVE_STATVFS /* Define if you have struct stat.st_mtim.tv_nsec */ @@ -1269,7 +1269,7 @@ /* Has stdatomic.h with atomic_int and atomic_uintptr_t */ #undef HAVE_STD_ATOMIC -/* Define to 1 if you have the `strftime' function. */ +/* Define to 1 if you have the 'strftime' function. */ #undef HAVE_STRFTIME /* Define to 1 if you have the header file. */ @@ -1278,52 +1278,52 @@ /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H -/* Define to 1 if you have the `strlcpy' function. */ +/* Define to 1 if you have the 'strlcpy' function. */ #undef HAVE_STRLCPY /* Define to 1 if you have the header file. */ #undef HAVE_STROPTS_H -/* Define to 1 if you have the `strsignal' function. */ +/* Define to 1 if you have the 'strsignal' function. */ #undef HAVE_STRSIGNAL -/* Define to 1 if `pw_gecos' is a member of `struct passwd'. */ +/* Define to 1 if 'pw_gecos' is a member of 'struct passwd'. */ #undef HAVE_STRUCT_PASSWD_PW_GECOS -/* Define to 1 if `pw_passwd' is a member of `struct passwd'. */ +/* Define to 1 if 'pw_passwd' is a member of 'struct passwd'. */ #undef HAVE_STRUCT_PASSWD_PW_PASSWD -/* Define to 1 if `st_birthtime' is a member of `struct stat'. */ +/* Define to 1 if 'st_birthtime' is a member of 'struct stat'. */ #undef HAVE_STRUCT_STAT_ST_BIRTHTIME -/* Define to 1 if `st_blksize' is a member of `struct stat'. */ +/* Define to 1 if 'st_blksize' is a member of 'struct stat'. */ #undef HAVE_STRUCT_STAT_ST_BLKSIZE -/* Define to 1 if `st_blocks' is a member of `struct stat'. */ +/* Define to 1 if 'st_blocks' is a member of 'struct stat'. */ #undef HAVE_STRUCT_STAT_ST_BLOCKS -/* Define to 1 if `st_flags' is a member of `struct stat'. */ +/* Define to 1 if 'st_flags' is a member of 'struct stat'. */ #undef HAVE_STRUCT_STAT_ST_FLAGS -/* Define to 1 if `st_gen' is a member of `struct stat'. */ +/* Define to 1 if 'st_gen' is a member of 'struct stat'. */ #undef HAVE_STRUCT_STAT_ST_GEN -/* Define to 1 if `st_rdev' is a member of `struct stat'. */ +/* Define to 1 if 'st_rdev' is a member of 'struct stat'. */ #undef HAVE_STRUCT_STAT_ST_RDEV -/* Define to 1 if `tm_zone' is a member of `struct tm'. */ +/* Define to 1 if 'tm_zone' is a member of 'struct tm'. */ #undef HAVE_STRUCT_TM_TM_ZONE /* Define if you have the 'symlink' function. */ #undef HAVE_SYMLINK -/* Define to 1 if you have the `symlinkat' function. */ +/* Define to 1 if you have the 'symlinkat' function. */ #undef HAVE_SYMLINKAT -/* Define to 1 if you have the `sync' function. */ +/* Define to 1 if you have the 'sync' function. */ #undef HAVE_SYNC -/* Define to 1 if you have the `sysconf' function. */ +/* Define to 1 if you have the 'sysconf' function. */ #undef HAVE_SYSCONF /* Define to 1 if you have the header file. */ @@ -1332,7 +1332,7 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYSLOG_H -/* Define to 1 if you have the `system' function. */ +/* Define to 1 if you have the 'system' function. */ #undef HAVE_SYSTEM /* Define to 1 if you have the header file. */ @@ -1347,7 +1347,7 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_DEVPOLL_H -/* Define to 1 if you have the header file, and it defines `DIR'. +/* Define to 1 if you have the header file, and it defines 'DIR'. */ #undef HAVE_SYS_DIR_H @@ -1390,7 +1390,7 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_MODEM_H -/* Define to 1 if you have the header file, and it defines `DIR'. +/* Define to 1 if you have the header file, and it defines 'DIR'. */ #undef HAVE_SYS_NDIR_H @@ -1466,13 +1466,13 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_XATTR_H -/* Define to 1 if you have the `tcgetpgrp' function. */ +/* Define to 1 if you have the 'tcgetpgrp' function. */ #undef HAVE_TCGETPGRP -/* Define to 1 if you have the `tcsetpgrp' function. */ +/* Define to 1 if you have the 'tcsetpgrp' function. */ #undef HAVE_TCSETPGRP -/* Define to 1 if you have the `tempnam' function. */ +/* Define to 1 if you have the 'tempnam' function. */ #undef HAVE_TEMPNAM /* Define to 1 if you have the header file. */ @@ -1481,54 +1481,54 @@ /* Define to 1 if you have the header file. */ #undef HAVE_TERM_H -/* Define to 1 if you have the `timegm' function. */ +/* Define to 1 if you have the 'timegm' function. */ #undef HAVE_TIMEGM /* Define if you have the 'timerfd_create' function. */ #undef HAVE_TIMERFD_CREATE -/* Define to 1 if you have the `times' function. */ +/* Define to 1 if you have the 'times' function. */ #undef HAVE_TIMES -/* Define to 1 if you have the `tmpfile' function. */ +/* Define to 1 if you have the 'tmpfile' function. */ #undef HAVE_TMPFILE -/* Define to 1 if you have the `tmpnam' function. */ +/* Define to 1 if you have the 'tmpnam' function. */ #undef HAVE_TMPNAM -/* Define to 1 if you have the `tmpnam_r' function. */ +/* Define to 1 if you have the 'tmpnam_r' function. */ #undef HAVE_TMPNAM_R -/* Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use - `HAVE_STRUCT_TM_TM_ZONE' instead. */ +/* Define to 1 if your 'struct tm' has 'tm_zone'. Deprecated, use + 'HAVE_STRUCT_TM_TM_ZONE' instead. */ #undef HAVE_TM_ZONE -/* Define to 1 if you have the `truncate' function. */ +/* Define to 1 if you have the 'truncate' function. */ #undef HAVE_TRUNCATE -/* Define to 1 if you have the `ttyname' function. */ +/* Define to 1 if you have the 'ttyname' function. */ #undef HAVE_TTYNAME -/* Define to 1 if you don't have `tm_zone' but do have the external array - `tzname'. */ +/* Define to 1 if you don't have 'tm_zone' but do have the external array + 'tzname'. */ #undef HAVE_TZNAME -/* Define to 1 if you have the `umask' function. */ +/* Define to 1 if you have the 'umask' function. */ #undef HAVE_UMASK -/* Define to 1 if you have the `uname' function. */ +/* Define to 1 if you have the 'uname' function. */ #undef HAVE_UNAME /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H -/* Define to 1 if you have the `unlinkat' function. */ +/* Define to 1 if you have the 'unlinkat' function. */ #undef HAVE_UNLINKAT -/* Define to 1 if you have the `unlockpt' function. */ +/* Define to 1 if you have the 'unlockpt' function. */ #undef HAVE_UNLOCKPT -/* Define to 1 if you have the `unshare' function. */ +/* Define to 1 if you have the 'unshare' function. */ #undef HAVE_UNSHARE /* Define if you have a useable wchar_t type defined in wchar.h; useable means @@ -1539,10 +1539,10 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UTIL_H -/* Define to 1 if you have the `utimensat' function. */ +/* Define to 1 if you have the 'utimensat' function. */ #undef HAVE_UTIMENSAT -/* Define to 1 if you have the `utimes' function. */ +/* Define to 1 if you have the 'utimes' function. */ #undef HAVE_UTIMES /* Define to 1 if you have the header file. */ @@ -1551,10 +1551,10 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UTMP_H -/* Define to 1 if you have the `uuid_create' function. */ +/* Define to 1 if you have the 'uuid_create' function. */ #undef HAVE_UUID_CREATE -/* Define to 1 if you have the `uuid_enc_be' function. */ +/* Define to 1 if you have the 'uuid_enc_be' function. */ #undef HAVE_UUID_ENC_BE /* Define if uuid_generate_time_safe() exists. */ @@ -1566,44 +1566,44 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UUID_UUID_H -/* Define to 1 if you have the `vfork' function. */ +/* Define to 1 if you have the 'vfork' function. */ #undef HAVE_VFORK -/* Define to 1 if you have the `wait' function. */ +/* Define to 1 if you have the 'wait' function. */ #undef HAVE_WAIT -/* Define to 1 if you have the `wait3' function. */ +/* Define to 1 if you have the 'wait3' function. */ #undef HAVE_WAIT3 -/* Define to 1 if you have the `wait4' function. */ +/* Define to 1 if you have the 'wait4' function. */ #undef HAVE_WAIT4 -/* Define to 1 if you have the `waitid' function. */ +/* Define to 1 if you have the 'waitid' function. */ #undef HAVE_WAITID -/* Define to 1 if you have the `waitpid' function. */ +/* Define to 1 if you have the 'waitpid' function. */ #undef HAVE_WAITPID /* Define if the compiler provides a wchar.h header file. */ #undef HAVE_WCHAR_H -/* Define to 1 if you have the `wcscoll' function. */ +/* Define to 1 if you have the 'wcscoll' function. */ #undef HAVE_WCSCOLL -/* Define to 1 if you have the `wcsftime' function. */ +/* Define to 1 if you have the 'wcsftime' function. */ #undef HAVE_WCSFTIME -/* Define to 1 if you have the `wcsxfrm' function. */ +/* Define to 1 if you have the 'wcsxfrm' function. */ #undef HAVE_WCSXFRM -/* Define to 1 if you have the `wmemcmp' function. */ +/* Define to 1 if you have the 'wmemcmp' function. */ #undef HAVE_WMEMCMP /* Define if tzset() actually switches the local timezone in a meaningful way. */ #undef HAVE_WORKING_TZSET -/* Define to 1 if you have the `writev' function. */ +/* Define to 1 if you have the 'writev' function. */ #undef HAVE_WRITEV /* Define if the zlib library has inflateCopy */ @@ -1612,17 +1612,17 @@ /* Define to 1 if you have the header file. */ #undef HAVE_ZLIB_H -/* Define to 1 if you have the `_getpty' function. */ +/* Define to 1 if you have the '_getpty' function. */ #undef HAVE__GETPTY -/* Define to 1 if the system has the type `__uint128_t'. */ +/* Define to 1 if the system has the type '__uint128_t'. */ #undef HAVE___UINT128_T -/* Define to 1 if `major', `minor', and `makedev' are declared in . +/* Define to 1 if 'major', 'minor', and 'makedev' are declared in . */ #undef MAJOR_IN_MKDEV -/* Define to 1 if `major', `minor', and `makedev' are declared in +/* Define to 1 if 'major', 'minor', and 'makedev' are declared in . */ #undef MAJOR_IN_SYSMACROS @@ -1730,58 +1730,58 @@ /* Define if i>>j for signed int i does not extend the sign bit when i < 0 */ #undef SIGNED_RIGHT_SHIFT_ZERO_FILLS -/* The size of `double', as computed by sizeof. */ +/* The size of 'double', as computed by sizeof. */ #undef SIZEOF_DOUBLE -/* The size of `float', as computed by sizeof. */ +/* The size of 'float', as computed by sizeof. */ #undef SIZEOF_FLOAT -/* The size of `fpos_t', as computed by sizeof. */ +/* The size of 'fpos_t', as computed by sizeof. */ #undef SIZEOF_FPOS_T -/* The size of `int', as computed by sizeof. */ +/* The size of 'int', as computed by sizeof. */ #undef SIZEOF_INT -/* The size of `long', as computed by sizeof. */ +/* The size of 'long', as computed by sizeof. */ #undef SIZEOF_LONG -/* The size of `long double', as computed by sizeof. */ +/* The size of 'long double', as computed by sizeof. */ #undef SIZEOF_LONG_DOUBLE -/* The size of `long long', as computed by sizeof. */ +/* The size of 'long long', as computed by sizeof. */ #undef SIZEOF_LONG_LONG -/* The size of `off_t', as computed by sizeof. */ +/* The size of 'off_t', as computed by sizeof. */ #undef SIZEOF_OFF_T -/* The size of `pid_t', as computed by sizeof. */ +/* The size of 'pid_t', as computed by sizeof. */ #undef SIZEOF_PID_T -/* The size of `pthread_key_t', as computed by sizeof. */ +/* The size of 'pthread_key_t', as computed by sizeof. */ #undef SIZEOF_PTHREAD_KEY_T -/* The size of `pthread_t', as computed by sizeof. */ +/* The size of 'pthread_t', as computed by sizeof. */ #undef SIZEOF_PTHREAD_T -/* The size of `short', as computed by sizeof. */ +/* The size of 'short', as computed by sizeof. */ #undef SIZEOF_SHORT -/* The size of `size_t', as computed by sizeof. */ +/* The size of 'size_t', as computed by sizeof. */ #undef SIZEOF_SIZE_T -/* The size of `time_t', as computed by sizeof. */ +/* The size of 'time_t', as computed by sizeof. */ #undef SIZEOF_TIME_T -/* The size of `uintptr_t', as computed by sizeof. */ +/* The size of 'uintptr_t', as computed by sizeof. */ #undef SIZEOF_UINTPTR_T -/* The size of `void *', as computed by sizeof. */ +/* The size of 'void *', as computed by sizeof. */ #undef SIZEOF_VOID_P -/* The size of `wchar_t', as computed by sizeof. */ +/* The size of 'wchar_t', as computed by sizeof. */ #undef SIZEOF_WCHAR_T -/* The size of `_Bool', as computed by sizeof. */ +/* The size of '_Bool', as computed by sizeof. */ #undef SIZEOF__BOOL /* Define to 1 if you have the ANSI C header files. */ @@ -1797,13 +1797,13 @@ /* Library needed by timemodule.c: librt may be needed for clock_gettime() */ #undef TIMEMODULE_LIB -/* Define to 1 if your declares `struct tm'. */ +/* Define to 1 if your declares 'struct tm'. */ #undef TM_IN_SYS_TIME /* Define if you want to use computed gotos in ceval.c. */ #undef USE_COMPUTED_GOTOS -/* Enable extensions on AIX 3, Interix. */ +/* Enable extensions on AIX, Interix, z/OS. */ #ifndef _ALL_SOURCE # undef _ALL_SOURCE #endif @@ -1864,11 +1864,15 @@ #ifndef __STDC_WANT_IEC_60559_DFP_EXT__ # undef __STDC_WANT_IEC_60559_DFP_EXT__ #endif +/* Enable extensions specified by C23 Annex F. */ +#ifndef __STDC_WANT_IEC_60559_EXT__ +# undef __STDC_WANT_IEC_60559_EXT__ +#endif /* Enable extensions specified by ISO/IEC TS 18661-4:2015. */ #ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__ # undef __STDC_WANT_IEC_60559_FUNCS_EXT__ #endif -/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */ +/* Enable extensions specified by C23 Annex H and ISO/IEC TS 18661-3:2015. */ #ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ # undef __STDC_WANT_IEC_60559_TYPES_EXT__ #endif @@ -1997,16 +2001,16 @@ /* Define to 'long' if does not define clock_t. */ #undef clock_t -/* Define to empty if `const' does not conform to ANSI C. */ +/* Define to empty if 'const' does not conform to ANSI C. */ #undef const -/* Define to `int' if doesn't define. */ +/* Define as 'int' if doesn't define. */ #undef gid_t -/* Define to `int' if does not define. */ +/* Define to 'int' if does not define. */ #undef mode_t -/* Define to `long int' if does not define. */ +/* Define to 'long int' if does not define. */ #undef off_t /* Define as a signed integer type capable of holding a process identifier. */ @@ -2015,13 +2019,13 @@ /* Define to empty if the keyword does not work. */ #undef signed -/* Define to `unsigned int' if does not define. */ +/* Define as 'unsigned int' if doesn't define. */ #undef size_t /* Define to 'int' if does not define. */ #undef socklen_t -/* Define to `int' if doesn't define. */ +/* Define as 'int' if doesn't define. */ #undef uid_t From fa985bee6189aabac1c329f2de32aa9a4e88e550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 3 Jan 2025 13:37:02 +0100 Subject: [PATCH 167/376] gh-127787: refactor helpers for `PyUnicodeErrorObject` internal interface (GH-127789) - Unify `get_unicode` and `get_string` in a single function. - Allow to retrieve the underlying `object` attribute, its size, and the adjusted 'start' and 'end', all at once. Add a new `_PyUnicodeError_GetParams` internal function for this. (In `exceptions.c`, it's somewhat common to not need all the attributes, but the compiler has opportunity to inline the function and optimize unneeded work away. Outside that file, we'll usually need all or most of them at once.) - Use a common implementation for the following functions: - `PyUnicode{Decode,Encode}Error_GetEncoding` - `PyUnicode{Decode,Encode,Translate}Error_GetObject` - `PyUnicode{Decode,Encode,Translate}Error_{Get,Set}Reason` - `PyUnicode{Decode,Encode,Translate}Error_{Get,Set}{Start,End}` --- Include/cpython/pyerrors.h | 6 + Objects/exceptions.c | 442 ++++++++++++++++++++++++------------- 2 files changed, 293 insertions(+), 155 deletions(-) diff --git a/Include/cpython/pyerrors.h b/Include/cpython/pyerrors.h index b36b4681f5dddb..49a6265e5eb02f 100644 --- a/Include/cpython/pyerrors.h +++ b/Include/cpython/pyerrors.h @@ -94,6 +94,12 @@ PyAPI_FUNC(void) _PyErr_ChainExceptions1(PyObject *); /* In exceptions.c */ +PyAPI_FUNC(int) _PyUnicodeError_GetParams( + PyObject *self, + PyObject **obj, Py_ssize_t *objlen, + Py_ssize_t *start, Py_ssize_t *end, + int as_bytes); + PyAPI_FUNC(PyObject*) PyUnstable_Exc_PrepReraiseStar( PyObject *orig, PyObject *excs); diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 6880c24196cbb8..714f8c828afbc1 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -2667,47 +2667,167 @@ SimpleExtendsException(PyExc_Exception, ValueError, SimpleExtendsException(PyExc_ValueError, UnicodeError, "Unicode related error."); + +/* + * Check the validity of 'attr' as a unicode or bytes object depending + * on 'as_bytes' and return a new reference on it if it is the case. + * + * The 'name' is the attribute name and is only used for error reporting. + * + * On success, this returns a strong reference on 'attr'. + * On failure, this sets a TypeError and returns NULL. + */ static PyObject * -get_bytes(PyObject *attr, const char *name) +as_unicode_error_attribute(PyObject *attr, const char *name, int as_bytes) { - if (!attr) { - PyErr_Format(PyExc_TypeError, "%.200s attribute not set", name); + assert(as_bytes == 0 || as_bytes == 1); + if (attr == NULL) { + PyErr_Format(PyExc_TypeError, "%s attribute not set", name); return NULL; } - - if (!PyBytes_Check(attr)) { - PyErr_Format(PyExc_TypeError, "%.200s attribute must be bytes", name); + if (!(as_bytes ? PyBytes_Check(attr) : PyUnicode_Check(attr))) { + PyErr_Format(PyExc_TypeError, + "%s attribute must be %s", + name, + as_bytes ? "bytes" : "unicode"); return NULL; } return Py_NewRef(attr); } -static PyObject * -get_unicode(PyObject *attr, const char *name) -{ - if (!attr) { - PyErr_Format(PyExc_TypeError, "%.200s attribute not set", name); - return NULL; - } - if (!PyUnicode_Check(attr)) { +#define PyUnicodeError_Check(PTR) \ + PyObject_TypeCheck((PTR), (PyTypeObject *)PyExc_UnicodeError) +#define PyUnicodeError_CAST(PTR) \ + (assert(PyUnicodeError_Check(PTR)), ((PyUnicodeErrorObject *)(PTR))) + + +/* class names to use when reporting errors */ +#define Py_UNICODE_ENCODE_ERROR_NAME "UnicodeEncodeError" +#define Py_UNICODE_DECODE_ERROR_NAME "UnicodeDecodeError" +#define Py_UNICODE_TRANSLATE_ERROR_NAME "UnicodeTranslateError" + + +/* + * Check that 'self' is a UnicodeError object. + * + * On success, this returns 0. + * On failure, this sets a TypeError exception and returns -1. + * + * The 'expect_type' is the name of the expected type, which is + * only used for error reporting. + * + * As an implementation detail, the `PyUnicode*Error_*` functions + * currently allow *any* subclass of UnicodeError as 'self'. + * + * Use one of the `Py_UNICODE_*_ERROR_NAME` macros to avoid typos. + */ +static inline int +check_unicode_error_type(PyObject *self, const char *expect_type) +{ + assert(self != NULL); + if (!PyUnicodeError_Check(self)) { PyErr_Format(PyExc_TypeError, - "%.200s attribute must be unicode", name); - return NULL; + "expecting a %s object, got %T", expect_type, self); + return -1; } - return Py_NewRef(attr); + return 0; } -static int -set_unicodefromstring(PyObject **attr, const char *value) + +// --- PyUnicodeEncodeObject: internal helpers -------------------------------- +// +// In the helpers below, the caller is responsible to ensure that 'self' +// is a PyUnicodeErrorObject, although this is verified on DEBUG builds +// through PyUnicodeError_CAST(). + +/* + * Return the underlying (str) 'encoding' attribute of a UnicodeError object. + */ +static inline PyObject * +unicode_error_get_encoding_impl(PyObject *self) +{ + assert(self != NULL); + PyUnicodeErrorObject *exc = PyUnicodeError_CAST(self); + return as_unicode_error_attribute(exc->encoding, "encoding", false); +} + + +/* + * Return the underlying 'object' attribute of a UnicodeError object + * as a bytes or a string instance, depending on the 'as_bytes' flag. + */ +static inline PyObject * +unicode_error_get_object_impl(PyObject *self, int as_bytes) { - PyObject *obj = PyUnicode_FromString(value); - if (!obj) + assert(self != NULL); + PyUnicodeErrorObject *exc = PyUnicodeError_CAST(self); + return as_unicode_error_attribute(exc->object, "object", as_bytes); +} + + +/* + * Return the underlying (str) 'reason' attribute of a UnicodeError object. + */ +static inline PyObject * +unicode_error_get_reason_impl(PyObject *self) +{ + assert(self != NULL); + PyUnicodeErrorObject *exc = PyUnicodeError_CAST(self); + return as_unicode_error_attribute(exc->reason, "reason", false); +} + + +/* + * Set the underlying (str) 'reason' attribute of a UnicodeError object. + * + * Return 0 on success and -1 on failure. + */ +static inline int +unicode_error_set_reason_impl(PyObject *self, const char *reason) +{ + assert(self != NULL); + PyObject *value = PyUnicode_FromString(reason); + if (value == NULL) { return -1; - Py_XSETREF(*attr, obj); + } + PyUnicodeErrorObject *exc = PyUnicodeError_CAST(self); + Py_XSETREF(exc->reason, value); return 0; } + +/* + * Set the 'start' attribute of a UnicodeError object. + * + * Return 0 on success and -1 on failure. + */ +static inline int +unicode_error_set_start_impl(PyObject *self, Py_ssize_t start) +{ + assert(self != NULL); + PyUnicodeErrorObject *exc = PyUnicodeError_CAST(self); + exc->start = start; + return 0; +} + + +/* + * Set the 'end' attribute of a UnicodeError object. + * + * Return 0 on success and -1 on failure. + */ +static inline int +unicode_error_set_end_impl(PyObject *self, Py_ssize_t end) +{ + assert(self != NULL); + PyUnicodeErrorObject *exc = PyUnicodeError_CAST(self); + exc->end = end; + return 0; +} + +// --- PyUnicodeEncodeObject: internal getters -------------------------------- + /* * Adjust the (inclusive) 'start' value of a UnicodeError object. * @@ -2728,6 +2848,7 @@ unicode_error_adjust_start(Py_ssize_t start, Py_ssize_t objlen) return start; } + /* * Adjust the (exclusive) 'end' value of a UnicodeError object. * @@ -2748,134 +2869,162 @@ unicode_error_adjust_end(Py_ssize_t end, Py_ssize_t objlen) return end; } -#define _PyUnicodeError_CAST(PTR) ((PyUnicodeErrorObject *)(PTR)) -#define PyUnicodeError_Check(PTR) \ - PyObject_TypeCheck((PTR), (PyTypeObject *)PyExc_UnicodeError) -#define PyUnicodeError_CAST(PTR) \ - (assert(PyUnicodeError_Check(PTR)), _PyUnicodeError_CAST(PTR)) - -static inline int -check_unicode_error_type(PyObject *self, const char *expect_type) +/* + * Get various common parameters of a UnicodeError object. + * + * The caller is responsible to ensure that 'self' is a PyUnicodeErrorObject, + * although this condition is verified by this function on DEBUG builds. + * + * Return 0 on success and -1 on failure. + * + * Output parameters: + * + * obj A strong reference to the 'object' attribute. + * objlen The 'object' length. + * start The clipped 'start' attribute. + * end The clipped 'end' attribute. + * + * An output parameter can be NULL to indicate that + * the corresponding value does not need to be stored. + * + * Input parameter: + * + * as_bytes If 1, the error's 'object' attribute must be a bytes object, + * i.e. the call is for a `UnicodeDecodeError`. Otherwise, the + * 'object' attribute must be a string. + * + * A TypeError is raised if the 'object' type is incompatible. + */ +int +_PyUnicodeError_GetParams(PyObject *self, + PyObject **obj, Py_ssize_t *objlen, + Py_ssize_t *start, Py_ssize_t *end, + int as_bytes) { - if (!PyUnicodeError_Check(self)) { - PyErr_Format(PyExc_TypeError, - "expecting a %s object, got %T", expect_type, self); + assert(self != NULL); + assert(as_bytes == 0 || as_bytes == 1); + PyUnicodeErrorObject *exc = PyUnicodeError_CAST(self); + PyObject *r = as_unicode_error_attribute(exc->object, "object", as_bytes); + if (r == NULL) { return -1; } + + Py_ssize_t n = as_bytes ? PyBytes_GET_SIZE(r) : PyUnicode_GET_LENGTH(r); + if (objlen != NULL) { + *objlen = n; + } + if (start != NULL) { + *start = unicode_error_adjust_start(exc->start, n); + assert(*start >= 0); + assert(*start <= n); + } + if (end != NULL) { + *end = unicode_error_adjust_end(exc->end, n); + assert(*end >= 0); + assert(*end <= n); + } + if (obj != NULL) { + *obj = r; + } + else { + Py_DECREF(r); + } return 0; } -static inline PyUnicodeErrorObject * -as_unicode_error(PyObject *self, const char *expect_type) -{ - int rc = check_unicode_error_type(self, expect_type); - return rc < 0 ? NULL : _PyUnicodeError_CAST(self); -} +// --- PyUnicodeEncodeObject: 'encoding' getters ------------------------------ +// Note: PyUnicodeTranslateError does not have an 'encoding' attribute. PyObject * PyUnicodeEncodeError_GetEncoding(PyObject *self) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeEncodeError"); - return exc == NULL ? NULL : get_unicode(exc->encoding, "encoding"); + int rc = check_unicode_error_type(self, Py_UNICODE_ENCODE_ERROR_NAME); + return rc < 0 ? NULL : unicode_error_get_encoding_impl(self); } + PyObject * PyUnicodeDecodeError_GetEncoding(PyObject *self) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeDecodeError"); - return exc == NULL ? NULL : get_unicode(exc->encoding, "encoding"); + int rc = check_unicode_error_type(self, Py_UNICODE_DECODE_ERROR_NAME); + return rc < 0 ? NULL : unicode_error_get_encoding_impl(self); } + +// --- PyUnicodeEncodeObject: 'object' getters -------------------------------- + PyObject * PyUnicodeEncodeError_GetObject(PyObject *self) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeEncodeError"); - return exc == NULL ? NULL : get_unicode(exc->object, "object"); + int rc = check_unicode_error_type(self, Py_UNICODE_ENCODE_ERROR_NAME); + return rc < 0 ? NULL : unicode_error_get_object_impl(self, false); } + PyObject * PyUnicodeDecodeError_GetObject(PyObject *self) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeDecodeError"); - return exc == NULL ? NULL : get_bytes(exc->object, "object"); + int rc = check_unicode_error_type(self, Py_UNICODE_DECODE_ERROR_NAME); + return rc < 0 ? NULL : unicode_error_get_object_impl(self, true); } + PyObject * PyUnicodeTranslateError_GetObject(PyObject *self) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeTranslateError"); - return exc == NULL ? NULL : get_unicode(exc->object, "object"); + int rc = check_unicode_error_type(self, Py_UNICODE_TRANSLATE_ERROR_NAME); + return rc < 0 ? NULL : unicode_error_get_object_impl(self, false); } + +// --- PyUnicodeEncodeObject: 'start' getters --------------------------------- + +/* + * Specialization of _PyUnicodeError_GetParams() for the 'start' attribute. + * + * The caller is responsible to ensure that 'self' is a PyUnicodeErrorObject, + * although this condition is verified by this function on DEBUG builds. + */ +static inline int +unicode_error_get_start_impl(PyObject *self, Py_ssize_t *start, int as_bytes) +{ + assert(self != NULL); + return _PyUnicodeError_GetParams(self, NULL, NULL, start, NULL, as_bytes); +} + + int PyUnicodeEncodeError_GetStart(PyObject *self, Py_ssize_t *start) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeEncodeError"); - if (exc == NULL) { - return -1; - } - PyObject *obj = get_unicode(exc->object, "object"); - if (obj == NULL) { - return -1; - } - Py_ssize_t size = PyUnicode_GET_LENGTH(obj); - Py_DECREF(obj); - *start = unicode_error_adjust_start(exc->start, size); - return 0; + int rc = check_unicode_error_type(self, Py_UNICODE_ENCODE_ERROR_NAME); + return rc < 0 ? -1 : unicode_error_get_start_impl(self, start, false); } int PyUnicodeDecodeError_GetStart(PyObject *self, Py_ssize_t *start) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeDecodeError"); - if (exc == NULL) { - return -1; - } - PyObject *obj = get_bytes(exc->object, "object"); - if (obj == NULL) { - return -1; - } - Py_ssize_t size = PyBytes_GET_SIZE(obj); - Py_DECREF(obj); - *start = unicode_error_adjust_start(exc->start, size); - return 0; + int rc = check_unicode_error_type(self, Py_UNICODE_DECODE_ERROR_NAME); + return rc < 0 ? -1 : unicode_error_get_start_impl(self, start, true); } int PyUnicodeTranslateError_GetStart(PyObject *self, Py_ssize_t *start) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeTranslateError"); - if (exc == NULL) { - return -1; - } - PyObject *obj = get_unicode(exc->object, "object"); - if (obj == NULL) { - return -1; - } - Py_ssize_t size = PyUnicode_GET_LENGTH(obj); - Py_DECREF(obj); - *start = unicode_error_adjust_start(exc->start, size); - return 0; + int rc = check_unicode_error_type(self, Py_UNICODE_TRANSLATE_ERROR_NAME); + return rc < 0 ? -1 : unicode_error_get_start_impl(self, start, false); } -static inline int -unicode_error_set_start_impl(PyObject *self, Py_ssize_t start) -{ - PyUnicodeErrorObject *exc = _PyUnicodeError_CAST(self); - exc->start = start; - return 0; -} - +// --- PyUnicodeEncodeObject: 'start' setters --------------------------------- int PyUnicodeEncodeError_SetStart(PyObject *self, Py_ssize_t start) { - int rc = check_unicode_error_type(self, "UnicodeEncodeError"); + int rc = check_unicode_error_type(self, Py_UNICODE_ENCODE_ERROR_NAME); return rc < 0 ? -1 : unicode_error_set_start_impl(self, start); } @@ -2883,7 +3032,7 @@ PyUnicodeEncodeError_SetStart(PyObject *self, Py_ssize_t start) int PyUnicodeDecodeError_SetStart(PyObject *self, Py_ssize_t start) { - int rc = check_unicode_error_type(self, "UnicodeDecodeError"); + int rc = check_unicode_error_type(self, Py_UNICODE_DECODE_ERROR_NAME); return rc < 0 ? -1 : unicode_error_set_start_impl(self, start); } @@ -2891,78 +3040,57 @@ PyUnicodeDecodeError_SetStart(PyObject *self, Py_ssize_t start) int PyUnicodeTranslateError_SetStart(PyObject *self, Py_ssize_t start) { - int rc = check_unicode_error_type(self, "UnicodeTranslateError"); + int rc = check_unicode_error_type(self, Py_UNICODE_TRANSLATE_ERROR_NAME); return rc < 0 ? -1 : unicode_error_set_start_impl(self, start); } +// --- PyUnicodeEncodeObject: 'end' getters ----------------------------------- + +/* + * Specialization of _PyUnicodeError_GetParams() for the 'end' attribute. + * + * The caller is responsible to ensure that 'self' is a PyUnicodeErrorObject, + * although this condition is verified by this function on DEBUG builds. + */ +static inline int +unicode_error_get_end_impl(PyObject *self, Py_ssize_t *end, int as_bytes) +{ + assert(self != NULL); + return _PyUnicodeError_GetParams(self, NULL, NULL, NULL, end, as_bytes); +} + + int PyUnicodeEncodeError_GetEnd(PyObject *self, Py_ssize_t *end) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeEncodeError"); - if (exc == NULL) { - return -1; - } - PyObject *obj = get_unicode(exc->object, "object"); - if (obj == NULL) { - return -1; - } - Py_ssize_t size = PyUnicode_GET_LENGTH(obj); - Py_DECREF(obj); - *end = unicode_error_adjust_end(exc->end, size); - return 0; + int rc = check_unicode_error_type(self, Py_UNICODE_ENCODE_ERROR_NAME); + return rc < 0 ? -1 : unicode_error_get_end_impl(self, end, false); } int PyUnicodeDecodeError_GetEnd(PyObject *self, Py_ssize_t *end) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeDecodeError"); - if (exc == NULL) { - return -1; - } - PyObject *obj = get_bytes(exc->object, "object"); - if (obj == NULL) { - return -1; - } - Py_ssize_t size = PyBytes_GET_SIZE(obj); - Py_DECREF(obj); - *end = unicode_error_adjust_end(exc->end, size); - return 0; + int rc = check_unicode_error_type(self, Py_UNICODE_DECODE_ERROR_NAME); + return rc < 0 ? -1 : unicode_error_get_end_impl(self, end, true); } int PyUnicodeTranslateError_GetEnd(PyObject *self, Py_ssize_t *end) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeTranslateError"); - if (exc == NULL) { - return -1; - } - PyObject *obj = get_unicode(exc->object, "object"); - if (obj == NULL) { - return -1; - } - Py_ssize_t size = PyUnicode_GET_LENGTH(obj); - Py_DECREF(obj); - *end = unicode_error_adjust_end(exc->end, size); - return 0; + int rc = check_unicode_error_type(self, Py_UNICODE_TRANSLATE_ERROR_NAME); + return rc < 0 ? -1 : unicode_error_get_end_impl(self, end, false); } -static inline int -unicode_error_set_end_impl(PyObject *self, Py_ssize_t end) -{ - PyUnicodeErrorObject *exc = _PyUnicodeError_CAST(self); - exc->end = end; - return 0; -} - +// --- PyUnicodeEncodeObject: 'end' setters ----------------------------------- int PyUnicodeEncodeError_SetEnd(PyObject *self, Py_ssize_t end) { - int rc = check_unicode_error_type(self, "UnicodeEncodeError"); + int rc = check_unicode_error_type(self, Py_UNICODE_ENCODE_ERROR_NAME); return rc < 0 ? -1 : unicode_error_set_end_impl(self, end); } @@ -2970,7 +3098,7 @@ PyUnicodeEncodeError_SetEnd(PyObject *self, Py_ssize_t end) int PyUnicodeDecodeError_SetEnd(PyObject *self, Py_ssize_t end) { - int rc = check_unicode_error_type(self, "UnicodeDecodeError"); + int rc = check_unicode_error_type(self, Py_UNICODE_DECODE_ERROR_NAME); return rc < 0 ? -1 : unicode_error_set_end_impl(self, end); } @@ -2978,56 +3106,60 @@ PyUnicodeDecodeError_SetEnd(PyObject *self, Py_ssize_t end) int PyUnicodeTranslateError_SetEnd(PyObject *self, Py_ssize_t end) { - int rc = check_unicode_error_type(self, "UnicodeTranslateError"); + int rc = check_unicode_error_type(self, Py_UNICODE_TRANSLATE_ERROR_NAME); return rc < 0 ? -1 : unicode_error_set_end_impl(self, end); } +// --- PyUnicodeEncodeObject: 'reason' getters -------------------------------- + PyObject * PyUnicodeEncodeError_GetReason(PyObject *self) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeEncodeError"); - return exc == NULL ? NULL : get_unicode(exc->reason, "reason"); + int rc = check_unicode_error_type(self, Py_UNICODE_ENCODE_ERROR_NAME); + return rc < 0 ? NULL : unicode_error_get_reason_impl(self); } PyObject * PyUnicodeDecodeError_GetReason(PyObject *self) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeDecodeError"); - return exc == NULL ? NULL : get_unicode(exc->reason, "reason"); + int rc = check_unicode_error_type(self, Py_UNICODE_DECODE_ERROR_NAME); + return rc < 0 ? NULL : unicode_error_get_reason_impl(self); } PyObject * PyUnicodeTranslateError_GetReason(PyObject *self) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeTranslateError"); - return exc == NULL ? NULL : get_unicode(exc->reason, "reason"); + int rc = check_unicode_error_type(self, Py_UNICODE_TRANSLATE_ERROR_NAME); + return rc < 0 ? NULL : unicode_error_get_reason_impl(self); } +// --- PyUnicodeEncodeObject: 'reason' setters -------------------------------- + int PyUnicodeEncodeError_SetReason(PyObject *self, const char *reason) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeEncodeError"); - return exc == NULL ? -1 : set_unicodefromstring(&exc->reason, reason); + int rc = check_unicode_error_type(self, Py_UNICODE_ENCODE_ERROR_NAME); + return rc < 0 ? -1 : unicode_error_set_reason_impl(self, reason); } int PyUnicodeDecodeError_SetReason(PyObject *self, const char *reason) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeDecodeError"); - return exc == NULL ? -1 : set_unicodefromstring(&exc->reason, reason); + int rc = check_unicode_error_type(self, Py_UNICODE_DECODE_ERROR_NAME); + return rc < 0 ? -1 : unicode_error_set_reason_impl(self, reason); } int PyUnicodeTranslateError_SetReason(PyObject *self, const char *reason) { - PyUnicodeErrorObject *exc = as_unicode_error(self, "UnicodeTranslateError"); - return exc == NULL ? -1 : set_unicodefromstring(&exc->reason, reason); + int rc = check_unicode_error_type(self, Py_UNICODE_TRANSLATE_ERROR_NAME); + return rc < 0 ? -1 : unicode_error_set_reason_impl(self, reason); } From f21af186bf21c1c554209ac67d78d3cf99f7d7c0 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:56:24 +0200 Subject: [PATCH 168/376] gh-128317: Highlight today in colour in calendar CLI output (#128318) Co-authored-by: Peter Bierma --- Doc/library/calendar.rst | 31 ++++++++++-- Doc/whatsnew/3.14.rst | 13 +++++ Lib/_colorize.py | 2 + Lib/calendar.py | 50 +++++++++++++++---- ...-12-29-00-33-34.gh-issue-128317.WgFina.rst | 2 + 5 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index 97ca34b6c6184c..ace8529d6e7e0c 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -146,26 +146,34 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is the specified width, representing an empty day. The *weekday* parameter is unused. - .. method:: formatweek(theweek, w=0) + .. method:: formatweek(theweek, w=0, highlight_day=None) Return a single week in a string with no newline. If *w* is provided, it specifies the width of the date columns, which are centered. Depends on the first weekday as specified in the constructor or set by the :meth:`setfirstweekday` method. + .. versionchanged:: next + If *highlight_day* is given, this date is highlighted in color. + This can be :ref:`controlled using environment variables + `. + + .. method:: formatweekday(weekday, width) Return a string representing the name of a single weekday formatted to the specified *width*. The *weekday* parameter is an integer representing the day of the week, where ``0`` is Monday and ``6`` is Sunday. + .. method:: formatweekheader(width) Return a string containing the header row of weekday names, formatted with the given *width* for each column. The names depend on the locale settings and are padded to the specified width. - .. method:: formatmonth(theyear, themonth, w=0, l=0) + + .. method:: formatmonth(theyear, themonth, w=0, l=0, highlight_day=None) Return a month's calendar in a multi-line string. If *w* is provided, it specifies the width of the date columns, which are centered. If *l* is @@ -173,6 +181,12 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is on the first weekday as specified in the constructor or set by the :meth:`setfirstweekday` method. + .. versionchanged:: next + If *highlight_day* is given, this date is highlighted in color. + This can be :ref:`controlled using environment variables + `. + + .. method:: formatmonthname(theyear, themonth, width=0, withyear=True) Return a string representing the month's name centered within the @@ -180,12 +194,13 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is output. The *theyear* and *themonth* parameters specify the year and month for the name to be formatted respectively. + .. method:: prmonth(theyear, themonth, w=0, l=0) Print a month's calendar as returned by :meth:`formatmonth`. - .. method:: formatyear(theyear, w=2, l=1, c=6, m=3) + .. method:: formatyear(theyear, w=2, l=1, c=6, m=3, highlight_day=None) Return a *m*-column calendar for an entire year as a multi-line string. Optional parameters *w*, *l*, and *c* are for date column width, lines per @@ -194,6 +209,11 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is :meth:`setfirstweekday` method. The earliest year for which a calendar can be generated is platform-dependent. + .. versionchanged:: next + If *highlight_day* is given, this date is highlighted in color. + This can be :ref:`controlled using environment variables + `. + .. method:: pryear(theyear, w=2, l=1, c=6, m=3) @@ -549,7 +569,7 @@ The :mod:`calendar` module defines the following exceptions: .. _calendar-cli: -Command-Line Usage +Command-line usage ------------------ .. versionadded:: 2.5 @@ -687,6 +707,9 @@ The following options are accepted: The number of months printed per row. Defaults to 3. +.. versionchanged:: next + By default, today's date is highlighted in color and can be + :ref:`controlled using environment variables `. *HTML-mode options:* diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 69c356ba60e6d4..cb9167300260cb 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -296,6 +296,19 @@ ast * The ``repr()`` output for AST nodes now includes more information. (Contributed by Tomas R in :gh:`116022`.) + +calendar +-------- + +* By default, today's date is highlighted in color in :mod:`calendar`'s + :ref:`command-line ` text output. + This can be controlled via the :envvar:`PYTHON_COLORS` environment + variable as well as the canonical |NO_COLOR|_ + and |FORCE_COLOR|_ environment variables. + See also :ref:`using-on-controlling-color`. + (Contributed by Hugo van Kemenade in :gh:`128317`.) + + concurrent.futures ------------------ diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 709081e25ec59b..f609901887a26b 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -6,9 +6,11 @@ class ANSIColors: + BACKGROUND_YELLOW = "\x1b[43m" BOLD_GREEN = "\x1b[1;32m" BOLD_MAGENTA = "\x1b[1;35m" BOLD_RED = "\x1b[1;31m" + BLACK = "\x1b[30m" GREEN = "\x1b[32m" GREY = "\x1b[90m" MAGENTA = "\x1b[35m" diff --git a/Lib/calendar.py b/Lib/calendar.py index 8c1c646da46a98..d2e5e08d02dbb9 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -349,11 +349,27 @@ def formatday(self, day, weekday, width): s = '%2i' % day # right-align single-digit days return s.center(width) - def formatweek(self, theweek, width): + def formatweek(self, theweek, width, *, highlight_day=None): """ Returns a single week in a string (no newline). """ - return ' '.join(self.formatday(d, wd, width) for (d, wd) in theweek) + if highlight_day: + from _colorize import get_colors + + ansi = get_colors() + highlight = f"{ansi.BLACK}{ansi.BACKGROUND_YELLOW}" + reset = ansi.RESET + else: + highlight = reset = "" + + return ' '.join( + ( + f"{highlight}{self.formatday(d, wd, width)}{reset}" + if d == highlight_day + else self.formatday(d, wd, width) + ) + for (d, wd) in theweek + ) def formatweekday(self, day, width): """ @@ -388,10 +404,11 @@ def prmonth(self, theyear, themonth, w=0, l=0): """ print(self.formatmonth(theyear, themonth, w, l), end='') - def formatmonth(self, theyear, themonth, w=0, l=0): + def formatmonth(self, theyear, themonth, w=0, l=0, *, highlight_day=None): """ Return a month's calendar string (multi-line). """ + highlight_day = highlight_day.day if highlight_day else None w = max(2, w) l = max(1, l) s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1) @@ -400,11 +417,11 @@ def formatmonth(self, theyear, themonth, w=0, l=0): s += self.formatweekheader(w).rstrip() s += '\n' * l for week in self.monthdays2calendar(theyear, themonth): - s += self.formatweek(week, w).rstrip() + s += self.formatweek(week, w, highlight_day=highlight_day).rstrip() s += '\n' * l return s - def formatyear(self, theyear, w=2, l=1, c=6, m=3): + def formatyear(self, theyear, w=2, l=1, c=6, m=3, *, highlight_day=None): """ Returns a year's calendar as a multi-line string. """ @@ -428,15 +445,24 @@ def formatyear(self, theyear, w=2, l=1, c=6, m=3): headers = (header for k in months) a(formatstring(headers, colwidth, c).rstrip()) a('\n'*l) + + if highlight_day and highlight_day.month in months: + month_pos = months.index(highlight_day.month) + else: + month_pos = None + # max number of weeks for this row height = max(len(cal) for cal in row) for j in range(height): weeks = [] - for cal in row: + for k, cal in enumerate(row): if j >= len(cal): weeks.append('') else: - weeks.append(self.formatweek(cal[j], w)) + day = highlight_day.day if k == month_pos else None + weeks.append( + self.formatweek(cal[j], w, highlight_day=day) + ) a(formatstring(weeks, colwidth, c).rstrip()) a('\n' * l) return ''.join(v) @@ -765,6 +791,7 @@ def main(args=None): sys.exit(1) locale = options.locale, options.encoding + today = datetime.date.today() if options.type == "html": if options.month: @@ -781,7 +808,7 @@ def main(args=None): optdict = dict(encoding=encoding, css=options.css) write = sys.stdout.buffer.write if options.year is None: - write(cal.formatyearpage(datetime.date.today().year, **optdict)) + write(cal.formatyearpage(today.year, **optdict)) else: write(cal.formatyearpage(options.year, **optdict)) else: @@ -797,10 +824,15 @@ def main(args=None): if options.month is not None: _validate_month(options.month) if options.year is None: - result = cal.formatyear(datetime.date.today().year, **optdict) + optdict["highlight_day"] = today + result = cal.formatyear(today.year, **optdict) elif options.month is None: + if options.year == today.year: + optdict["highlight_day"] = today result = cal.formatyear(options.year, **optdict) else: + if options.year == today.year and options.month == today.month: + optdict["highlight_day"] = today result = cal.formatmonth(options.year, options.month, **optdict) write = sys.stdout.write if options.encoding: diff --git a/Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst b/Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst new file mode 100644 index 00000000000000..4441108014569e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst @@ -0,0 +1,2 @@ +Highlight today in colour in :mod:`calendar`'s CLI output. Patch by Hugo van +Kemenade. From b4f799b1e78ede17b41de9a2bc51b437a7e6dd74 Mon Sep 17 00:00:00 2001 From: Rian Hunter Date: Fri, 3 Jan 2025 05:07:07 -0800 Subject: [PATCH 169/376] gh-112015: Implement `ctypes.memoryview_at()` (GH-112018) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Serhiy Storchaka Co-authored-by: Petr Viktorin Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/ctypes.rst | 22 +++++++ Doc/whatsnew/3.14.rst | 8 +++ Lib/ctypes/__init__.py | 9 +++ Lib/test/test_ctypes/test_memfunctions.py | 60 ++++++++++++++++++- ...-11-12-21-53-40.gh-issue-112015.2WPRxE.rst | 5 ++ Modules/_ctypes/_ctypes.c | 17 ++++++ 6 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-12-21-53-40.gh-issue-112015.2WPRxE.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 09692e56d29a39..398cb92bac809a 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -2182,6 +2182,28 @@ Utility functions .. audit-event:: ctypes.wstring_at ptr,size ctypes.wstring_at +.. function:: memoryview_at(ptr, size, readonly=False) + + Return a :class:`memoryview` object of length *size* that references memory + starting at *void \*ptr*. + + If *readonly* is true, the returned :class:`!memoryview` object can + not be used to modify the underlying memory. + (Changes made by other means will still be reflected in the returned + object.) + + This function is similar to :func:`string_at` with the key + difference of not making a copy of the specified memory. + It is a semantically equivalent (but more efficient) alternative to + ``memoryview((c_byte * size).from_address(ptr))``. + (While :meth:`~_CData.from_address` only takes integers, *ptr* can also + be given as a :class:`ctypes.POINTER` or a :func:`~ctypes.byref` object.) + + .. audit-event:: ctypes.memoryview_at address,size,readonly + + .. versionadded:: next + + .. _ctypes-data-types: Data types diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index cb9167300260cb..f365db37217e95 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -343,6 +343,14 @@ ctypes * On Windows, the :func:`~ctypes.CopyComPointer` function is now public. (Contributed by Jun Komoda in :gh:`127275`.) +* :func:`ctypes.memoryview_at` now exists to create a + :class:`memoryview` object that refers to the supplied pointer and + length. This works like :func:`ctypes.string_at` except it avoids a + buffer copy, and is typically useful when implementing pure Python + callback functions that are passed dynamically-sized buffers. + (Contributed by Rian Hunter in :gh:`112018`.) + + datetime -------- diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 2f2b0ca9f38633..8e2a2926f7a853 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -524,6 +524,7 @@ def WinError(code=None, descr=None): # functions from _ctypes import _memmove_addr, _memset_addr, _string_at_addr, _cast_addr +from _ctypes import _memoryview_at_addr ## void *memmove(void *, const void *, size_t); memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr) @@ -549,6 +550,14 @@ def string_at(ptr, size=-1): Return the byte string at void *ptr.""" return _string_at(ptr, size) +_memoryview_at = PYFUNCTYPE( + py_object, c_void_p, c_ssize_t, c_int)(_memoryview_at_addr) +def memoryview_at(ptr, size, readonly=False): + """memoryview_at(ptr, size[, readonly]) -> memoryview + + Return a memoryview representing the memory at void *ptr.""" + return _memoryview_at(ptr, size, bool(readonly)) + try: from _ctypes import _wstring_at_addr except ImportError: diff --git a/Lib/test/test_ctypes/test_memfunctions.py b/Lib/test/test_ctypes/test_memfunctions.py index 112b27ba48e07e..325487618137f6 100644 --- a/Lib/test/test_ctypes/test_memfunctions.py +++ b/Lib/test/test_ctypes/test_memfunctions.py @@ -5,7 +5,9 @@ create_string_buffer, string_at, create_unicode_buffer, wstring_at, memmove, memset, - c_char_p, c_byte, c_ubyte, c_wchar) + memoryview_at, c_void_p, + c_char_p, c_byte, c_ubyte, c_wchar, + addressof, byref) class MemFunctionsTest(unittest.TestCase): @@ -77,6 +79,62 @@ def test_wstring_at(self): self.assertEqual(wstring_at(a, 16), "Hello, World\0\0\0\0") self.assertEqual(wstring_at(a, 0), "") + def test_memoryview_at(self): + b = (c_byte * 10)() + + size = len(b) + for foreign_ptr in ( + b, + cast(b, c_void_p), + byref(b), + addressof(b), + ): + with self.subTest(foreign_ptr=type(foreign_ptr).__name__): + b[:] = b"initialval" + v = memoryview_at(foreign_ptr, size) + self.assertIsInstance(v, memoryview) + self.assertEqual(bytes(v), b"initialval") + + # test that writes to source buffer get reflected in memoryview + b[:] = b"0123456789" + self.assertEqual(bytes(v), b"0123456789") + + # test that writes to memoryview get reflected in source buffer + v[:] = b"9876543210" + self.assertEqual(bytes(b), b"9876543210") + + with self.assertRaises(ValueError): + memoryview_at(foreign_ptr, -1) + + with self.assertRaises(ValueError): + memoryview_at(foreign_ptr, sys.maxsize + 1) + + v0 = memoryview_at(foreign_ptr, 0) + self.assertEqual(bytes(v0), b'') + + def test_memoryview_at_readonly(self): + b = (c_byte * 10)() + + size = len(b) + for foreign_ptr in ( + b, + cast(b, c_void_p), + byref(b), + addressof(b), + ): + with self.subTest(foreign_ptr=type(foreign_ptr).__name__): + b[:] = b"initialval" + v = memoryview_at(foreign_ptr, size, readonly=True) + self.assertIsInstance(v, memoryview) + self.assertEqual(bytes(v), b"initialval") + + # test that writes to source buffer get reflected in memoryview + b[:] = b"0123456789" + self.assertEqual(bytes(v), b"0123456789") + + # test that writes to the memoryview are blocked + with self.assertRaises(TypeError): + v[:] = b"9876543210" if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2023-11-12-21-53-40.gh-issue-112015.2WPRxE.rst b/Misc/NEWS.d/next/Library/2023-11-12-21-53-40.gh-issue-112015.2WPRxE.rst new file mode 100644 index 00000000000000..4b58ec9d219eff --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-12-21-53-40.gh-issue-112015.2WPRxE.rst @@ -0,0 +1,5 @@ +:func:`ctypes.memoryview_at` now exists to create a +:class:`memoryview` object that refers to the supplied pointer and +length. This works like :func:`ctypes.string_at` except it avoids a +buffer copy, and is typically useful when implementing pure Python +callback functions that are passed dynamically-sized buffers. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index ac520ffaad6c90..ede95bdf98bf76 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5791,6 +5791,22 @@ wstring_at(const wchar_t *ptr, int size) return PyUnicode_FromWideChar(ptr, ssize); } +static PyObject * +memoryview_at(void *ptr, Py_ssize_t size, int readonly) +{ + if (PySys_Audit("ctypes.memoryview_at", "nni", + (Py_ssize_t)ptr, size, readonly) < 0) { + return NULL; + } + if (size < 0) { + PyErr_Format(PyExc_ValueError, + "memoryview_at: size is negative (or overflowed): %zd", + size); + return NULL; + } + return PyMemoryView_FromMemory(ptr, size, + readonly ? PyBUF_READ : PyBUF_WRITE); +} static int _ctypes_add_types(PyObject *mod) @@ -5919,6 +5935,7 @@ _ctypes_add_objects(PyObject *mod) MOD_ADD("_string_at_addr", PyLong_FromVoidPtr(string_at)); MOD_ADD("_cast_addr", PyLong_FromVoidPtr(cast)); MOD_ADD("_wstring_at_addr", PyLong_FromVoidPtr(wstring_at)); + MOD_ADD("_memoryview_at_addr", PyLong_FromVoidPtr(memoryview_at)); /* If RTLD_LOCAL is not defined (Windows!), set it to zero. */ #if !HAVE_DECL_RTLD_LOCAL From 8522f8bacb4e2db4b901472c254efad6057e9fd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:04:33 +0100 Subject: [PATCH 170/376] gh-111178: fix UBSan failures in `Modules/_csv.c` (GH-128243) Also: suppress unused return values --- Modules/_csv.c | 76 +++++++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/Modules/_csv.c b/Modules/_csv.c index 1a4dc3f1f55ace..7ca30e39e00c0c 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -77,7 +77,7 @@ _csv_traverse(PyObject *module, visitproc visit, void *arg) static void _csv_free(void *module) { - _csv_clear((PyObject *)module); + (void)_csv_clear((PyObject *)module); } typedef enum { @@ -151,6 +151,10 @@ typedef struct { PyObject *error_obj; /* cached error object */ } WriterObj; +#define _DialectObj_CAST(op) ((DialectObj *)(op)) +#define _ReaderObj_CAST(op) ((ReaderObj *)(op)) +#define _WriterObj_CAST(op) ((WriterObj *)(op)) + /* * DIALECT class */ @@ -176,32 +180,37 @@ get_char_or_None(Py_UCS4 c) } static PyObject * -Dialect_get_lineterminator(DialectObj *self, void *Py_UNUSED(ignored)) +Dialect_get_lineterminator(PyObject *op, void *Py_UNUSED(ignored)) { + DialectObj *self = _DialectObj_CAST(op); return Py_XNewRef(self->lineterminator); } static PyObject * -Dialect_get_delimiter(DialectObj *self, void *Py_UNUSED(ignored)) +Dialect_get_delimiter(PyObject *op, void *Py_UNUSED(ignored)) { + DialectObj *self = _DialectObj_CAST(op); return get_char_or_None(self->delimiter); } static PyObject * -Dialect_get_escapechar(DialectObj *self, void *Py_UNUSED(ignored)) +Dialect_get_escapechar(PyObject *op, void *Py_UNUSED(ignored)) { + DialectObj *self = _DialectObj_CAST(op); return get_char_or_None(self->escapechar); } static PyObject * -Dialect_get_quotechar(DialectObj *self, void *Py_UNUSED(ignored)) +Dialect_get_quotechar(PyObject *op, void *Py_UNUSED(ignored)) { + DialectObj *self = _DialectObj_CAST(op); return get_char_or_None(self->quotechar); } static PyObject * -Dialect_get_quoting(DialectObj *self, void *Py_UNUSED(ignored)) +Dialect_get_quoting(PyObject *op, void *Py_UNUSED(ignored)) { + DialectObj *self = _DialectObj_CAST(op); return PyLong_FromLong(self->quoting); } @@ -371,16 +380,16 @@ static struct PyMemberDef Dialect_memberlist[] = { #undef D_OFF static PyGetSetDef Dialect_getsetlist[] = { - { "delimiter", (getter)Dialect_get_delimiter}, - { "escapechar", (getter)Dialect_get_escapechar}, - { "lineterminator", (getter)Dialect_get_lineterminator}, - { "quotechar", (getter)Dialect_get_quotechar}, - { "quoting", (getter)Dialect_get_quoting}, + {"delimiter", Dialect_get_delimiter}, + {"escapechar", Dialect_get_escapechar}, + {"lineterminator", Dialect_get_lineterminator}, + {"quotechar", Dialect_get_quotechar}, + {"quoting", Dialect_get_quoting}, {NULL}, }; static void -Dialect_dealloc(DialectObj *self) +Dialect_dealloc(PyObject *self) { PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); @@ -594,15 +603,17 @@ PyDoc_STRVAR(Dialect_Type_doc, "The Dialect type records CSV parsing and generation options.\n"); static int -Dialect_clear(DialectObj *self) +Dialect_clear(PyObject *op) { + DialectObj *self = _DialectObj_CAST(op); Py_CLEAR(self->lineterminator); return 0; } static int -Dialect_traverse(DialectObj *self, visitproc visit, void *arg) +Dialect_traverse(PyObject *op, visitproc visit, void *arg) { + DialectObj *self = _DialectObj_CAST(op); Py_VISIT(self->lineterminator); Py_VISIT(Py_TYPE(self)); return 0; @@ -916,8 +927,10 @@ parse_reset(ReaderObj *self) } static PyObject * -Reader_iternext(ReaderObj *self) +Reader_iternext(PyObject *op) { + ReaderObj *self = _ReaderObj_CAST(op); + PyObject *fields = NULL; Py_UCS4 c; Py_ssize_t pos, linelen; @@ -982,11 +995,12 @@ Reader_iternext(ReaderObj *self) } static void -Reader_dealloc(ReaderObj *self) +Reader_dealloc(PyObject *op) { + ReaderObj *self = _ReaderObj_CAST(op); PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - tp->tp_clear((PyObject *)self); + (void)tp->tp_clear(op); if (self->field != NULL) { PyMem_Free(self->field); self->field = NULL; @@ -996,8 +1010,9 @@ Reader_dealloc(ReaderObj *self) } static int -Reader_traverse(ReaderObj *self, visitproc visit, void *arg) +Reader_traverse(PyObject *op, visitproc visit, void *arg) { + ReaderObj *self = _ReaderObj_CAST(op); Py_VISIT(self->dialect); Py_VISIT(self->input_iter); Py_VISIT(self->fields); @@ -1006,8 +1021,9 @@ Reader_traverse(ReaderObj *self, visitproc visit, void *arg) } static int -Reader_clear(ReaderObj *self) +Reader_clear(PyObject *op) { + ReaderObj *self = _ReaderObj_CAST(op); Py_CLEAR(self->dialect); Py_CLEAR(self->input_iter); Py_CLEAR(self->fields); @@ -1303,8 +1319,9 @@ PyDoc_STRVAR(csv_writerow_doc, "elements will be converted to string."); static PyObject * -csv_writerow(WriterObj *self, PyObject *seq) +csv_writerow(PyObject *op, PyObject *seq) { + WriterObj *self = _WriterObj_CAST(op); DialectObj *dialect = self->dialect; PyObject *iter, *field, *line, *result; bool null_field = false; @@ -1412,7 +1429,7 @@ PyDoc_STRVAR(csv_writerows_doc, "elements will be converted to string."); static PyObject * -csv_writerows(WriterObj *self, PyObject *seqseq) +csv_writerows(PyObject *self, PyObject *seqseq) { PyObject *row_iter, *row_obj, *result; @@ -1437,9 +1454,9 @@ csv_writerows(WriterObj *self, PyObject *seqseq) } static struct PyMethodDef Writer_methods[] = { - { "writerow", (PyCFunction)csv_writerow, METH_O, csv_writerow_doc}, - { "writerows", (PyCFunction)csv_writerows, METH_O, csv_writerows_doc}, - { NULL, NULL } + {"writerow", csv_writerow, METH_O, csv_writerow_doc}, + {"writerows", csv_writerows, METH_O, csv_writerows_doc}, + {NULL, NULL, 0, NULL} /* sentinel */ }; #define W_OFF(x) offsetof(WriterObj, x) @@ -1452,8 +1469,9 @@ static struct PyMemberDef Writer_memberlist[] = { #undef W_OFF static int -Writer_traverse(WriterObj *self, visitproc visit, void *arg) +Writer_traverse(PyObject *op, visitproc visit, void *arg) { + WriterObj *self = _WriterObj_CAST(op); Py_VISIT(self->dialect); Py_VISIT(self->write); Py_VISIT(self->error_obj); @@ -1462,8 +1480,9 @@ Writer_traverse(WriterObj *self, visitproc visit, void *arg) } static int -Writer_clear(WriterObj *self) +Writer_clear(PyObject *op) { + WriterObj *self = _WriterObj_CAST(op); Py_CLEAR(self->dialect); Py_CLEAR(self->write); Py_CLEAR(self->error_obj); @@ -1471,11 +1490,12 @@ Writer_clear(WriterObj *self) } static void -Writer_dealloc(WriterObj *self) +Writer_dealloc(PyObject *op) { + WriterObj *self = _WriterObj_CAST(op); PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - tp->tp_clear((PyObject *)self); + tp->tp_clear(op); if (self->rec != NULL) { PyMem_Free(self->rec); } From 621d4ff35ec099c1626f77955318c0bc1febc33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:12:40 +0100 Subject: [PATCH 171/376] gh-111178: fix UBSan failures in `Modules/curses*.c` (GH-128244) * fix UBSan failures in `_cursesmodule.c` * fix UBSan failures in `_curses_panel.c` * suppress an unused return value --- Modules/_curses_panel.c | 7 +- Modules/_cursesmodule.c | 178 ++++++++++++++++++++++++---------------- 2 files changed, 114 insertions(+), 71 deletions(-) diff --git a/Modules/_curses_panel.c b/Modules/_curses_panel.c index bbbb62c9066df0..eecf7a1c8a1e56 100644 --- a/Modules/_curses_panel.c +++ b/Modules/_curses_panel.c @@ -62,7 +62,7 @@ _curses_panel_traverse(PyObject *mod, visitproc visit, void *arg) static void _curses_panel_free(void *mod) { - _curses_panel_clear((PyObject *) mod); + (void)_curses_panel_clear((PyObject *)mod); } /* Utility Functions */ @@ -101,6 +101,8 @@ typedef struct { PyCursesWindowObject *wo; /* for reference counts */ } PyCursesPanelObject; +#define _PyCursesPanelObject_CAST(op) ((PyCursesPanelObject *)(op)) + /* Some helper functions. The problem is that there's always a window associated with a panel. To ensure that Python's GC doesn't pull this window from under our feet we need to keep track of references @@ -277,9 +279,10 @@ PyCursesPanel_New(_curses_panel_state *state, PANEL *pan, } static void -PyCursesPanel_Dealloc(PyCursesPanelObject *po) +PyCursesPanel_Dealloc(PyObject *self) { PyObject *tp, *obj; + PyCursesPanelObject *po = _PyCursesPanelObject_CAST(self); tp = (PyObject *) Py_TYPE(po); obj = (PyObject *) panel_userptr(po->pan); diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 92961af381b9cb..c6835738348ff9 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -187,6 +187,8 @@ get_cursesmodule_state_by_win(PyCursesWindowObject *win) return get_cursesmodule_state_by_cls(Py_TYPE(win)); } +#define _PyCursesWindowObject_CAST(op) ((PyCursesWindowObject *)(op)) + /*[clinic input] module _curses class _curses.window "PyCursesWindowObject *" "clinic_state()->window_type" @@ -654,53 +656,80 @@ class component_converter(CConverter): PARSESTR - format string for argument parsing */ -#define Window_NoArgNoReturnFunction(X) \ - static PyObject *PyCursesWindow_ ## X \ - (PyCursesWindowObject *self, PyObject *Py_UNUSED(ignored)) \ - { return PyCursesCheckERR_ForWin(self, X(self->win), # X); } +#define Window_NoArgNoReturnFunction(X) \ + static PyObject *PyCursesWindow_ ## X \ + (PyObject *op, PyObject *Py_UNUSED(ignored)) \ + { \ + PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); \ + int code = X(self->win); \ + return PyCursesCheckERR_ForWin(self, code, # X); \ + } #define Window_NoArgTrueFalseFunction(X) \ static PyObject * PyCursesWindow_ ## X \ - (PyCursesWindowObject *self, PyObject *Py_UNUSED(ignored)) \ + (PyObject *op, PyObject *Py_UNUSED(ignored)) \ { \ - return PyBool_FromLong(X(self->win)); } + PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); \ + return PyBool_FromLong(X(self->win)); \ + } -#define Window_NoArgNoReturnVoidFunction(X) \ - static PyObject * PyCursesWindow_ ## X \ - (PyCursesWindowObject *self, PyObject *Py_UNUSED(ignored)) \ - { \ - X(self->win); Py_RETURN_NONE; } +#define Window_NoArgNoReturnVoidFunction(X) \ + static PyObject * PyCursesWindow_ ## X \ + (PyObject *op, PyObject *Py_UNUSED(ignored)) \ + { \ + PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); \ + X(self->win); \ + Py_RETURN_NONE; \ + } #define Window_NoArg2TupleReturnFunction(X, TYPE, ERGSTR) \ static PyObject * PyCursesWindow_ ## X \ - (PyCursesWindowObject *self, PyObject *Py_UNUSED(ignored)) \ + (PyObject *op, PyObject *Py_UNUSED(ignored)) \ { \ TYPE arg1, arg2; \ - X(self->win,arg1,arg2); return Py_BuildValue(ERGSTR, arg1, arg2); } + PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); \ + X(self->win, arg1, arg2); \ + return Py_BuildValue(ERGSTR, arg1, arg2); \ + } #define Window_OneArgNoReturnVoidFunction(X, TYPE, PARSESTR) \ static PyObject * PyCursesWindow_ ## X \ - (PyCursesWindowObject *self, PyObject *args) \ + (PyObject *op, PyObject *args) \ { \ TYPE arg1; \ - if (!PyArg_ParseTuple(args, PARSESTR, &arg1)) return NULL; \ - X(self->win,arg1); Py_RETURN_NONE; } + if (!PyArg_ParseTuple(args, PARSESTR, &arg1)) { \ + return NULL; \ + } \ + PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); \ + X(self->win, arg1); \ + Py_RETURN_NONE; \ + } #define Window_OneArgNoReturnFunction(X, TYPE, PARSESTR) \ static PyObject * PyCursesWindow_ ## X \ - (PyCursesWindowObject *self, PyObject *args) \ + (PyObject *op, PyObject *args) \ { \ TYPE arg1; \ - if (!PyArg_ParseTuple(args,PARSESTR, &arg1)) return NULL; \ - return PyCursesCheckERR_ForWin(self, X(self->win, arg1), # X); } + if (!PyArg_ParseTuple(args, PARSESTR, &arg1)) { \ + return NULL; \ + } \ + PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); \ + int code = X(self->win, arg1); \ + return PyCursesCheckERR_ForWin(self, code, # X); \ + } #define Window_TwoArgNoReturnFunction(X, TYPE, PARSESTR) \ static PyObject * PyCursesWindow_ ## X \ - (PyCursesWindowObject *self, PyObject *args) \ + (PyObject *op, PyObject *args) \ { \ TYPE arg1, arg2; \ - if (!PyArg_ParseTuple(args,PARSESTR, &arg1, &arg2)) return NULL; \ - return PyCursesCheckERR_ForWin(self, X(self->win, arg1, arg2), # X); } + if (!PyArg_ParseTuple(args,PARSESTR, &arg1, &arg2)) { \ + return NULL; \ + } \ + PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); \ + int code = X(self->win, arg1, arg2); \ + return PyCursesCheckERR_ForWin(self, code, # X); \ + } /* ------------- WINDOW routines --------------- */ @@ -1302,8 +1331,10 @@ the touchline() method so that the contents will be redisplayed by the next window refresh. [-clinic start generated code]*/ static PyObject * -PyCursesWindow_ChgAt(PyCursesWindowObject *self, PyObject *args) +PyCursesWindow_ChgAt(PyObject *op, PyObject *args) { + PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); + int rtn; int x, y; int num = -1; @@ -1656,8 +1687,10 @@ Read a string from the user, with primitive line editing capacity. [-clinic start generated code]*/ static PyObject * -PyCursesWindow_GetStr(PyCursesWindowObject *self, PyObject *args) +PyCursesWindow_GetStr(PyObject *op, PyObject *args) { + PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); + int x, y, n; char rtn[1024]; /* This should be big enough.. I hope */ int rtn2; @@ -1860,8 +1893,10 @@ from the characters. If n is specified, instr() returns a string at most n characters long (exclusive of the trailing NUL). [-clinic start generated code]*/ static PyObject * -PyCursesWindow_InStr(PyCursesWindowObject *self, PyObject *args) +PyCursesWindow_InStr(PyObject *op, PyObject *args) { + PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); + int x, y, n; char rtn[1024]; /* This should be big enough.. I hope */ int rtn2; @@ -2557,14 +2592,17 @@ _curses_window_vline_impl(PyCursesWindowObject *self, int group_left_1, } static PyObject * -PyCursesWindow_get_encoding(PyCursesWindowObject *self, void *closure) +PyCursesWindow_get_encoding(PyObject *op, void *closure) { + PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); return PyUnicode_FromString(self->encoding); } static int -PyCursesWindow_set_encoding(PyCursesWindowObject *self, PyObject *value, void *Py_UNUSED(ignored)) +PyCursesWindow_set_encoding(PyObject *op, PyObject *value, void *Py_UNUSED(ignored)) { + PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op); + PyObject *ascii; char *encoding; @@ -2607,88 +2645,90 @@ static PyMethodDef PyCursesWindow_methods[] = { _CURSES_WINDOW_ATTRSET_METHODDEF _CURSES_WINDOW_BKGD_METHODDEF #ifdef HAVE_CURSES_WCHGAT - {"chgat", (PyCFunction)PyCursesWindow_ChgAt, METH_VARARGS}, + {"chgat", PyCursesWindow_ChgAt, METH_VARARGS}, #endif _CURSES_WINDOW_BKGDSET_METHODDEF _CURSES_WINDOW_BORDER_METHODDEF _CURSES_WINDOW_BOX_METHODDEF - {"clear", (PyCFunction)PyCursesWindow_wclear, METH_NOARGS}, - {"clearok", (PyCFunction)PyCursesWindow_clearok, METH_VARARGS}, - {"clrtobot", (PyCFunction)PyCursesWindow_wclrtobot, METH_NOARGS}, - {"clrtoeol", (PyCFunction)PyCursesWindow_wclrtoeol, METH_NOARGS}, - {"cursyncup", (PyCFunction)PyCursesWindow_wcursyncup, METH_NOARGS}, + {"clear", PyCursesWindow_wclear, METH_NOARGS}, + {"clearok", PyCursesWindow_clearok, METH_VARARGS}, + {"clrtobot", PyCursesWindow_wclrtobot, METH_NOARGS}, + {"clrtoeol", PyCursesWindow_wclrtoeol, METH_NOARGS}, + {"cursyncup", PyCursesWindow_wcursyncup, METH_NOARGS}, _CURSES_WINDOW_DELCH_METHODDEF - {"deleteln", (PyCFunction)PyCursesWindow_wdeleteln, METH_NOARGS}, + {"deleteln", PyCursesWindow_wdeleteln, METH_NOARGS}, _CURSES_WINDOW_DERWIN_METHODDEF _CURSES_WINDOW_ECHOCHAR_METHODDEF _CURSES_WINDOW_ENCLOSE_METHODDEF - {"erase", (PyCFunction)PyCursesWindow_werase, METH_NOARGS}, - {"getbegyx", (PyCFunction)PyCursesWindow_getbegyx, METH_NOARGS}, + {"erase", PyCursesWindow_werase, METH_NOARGS}, + {"getbegyx", PyCursesWindow_getbegyx, METH_NOARGS}, _CURSES_WINDOW_GETBKGD_METHODDEF _CURSES_WINDOW_GETCH_METHODDEF _CURSES_WINDOW_GETKEY_METHODDEF _CURSES_WINDOW_GET_WCH_METHODDEF - {"getmaxyx", (PyCFunction)PyCursesWindow_getmaxyx, METH_NOARGS}, - {"getparyx", (PyCFunction)PyCursesWindow_getparyx, METH_NOARGS}, - {"getstr", (PyCFunction)PyCursesWindow_GetStr, METH_VARARGS}, - {"getyx", (PyCFunction)PyCursesWindow_getyx, METH_NOARGS}, + {"getmaxyx", PyCursesWindow_getmaxyx, METH_NOARGS}, + {"getparyx", PyCursesWindow_getparyx, METH_NOARGS}, + {"getstr", PyCursesWindow_GetStr, METH_VARARGS}, + {"getyx", PyCursesWindow_getyx, METH_NOARGS}, _CURSES_WINDOW_HLINE_METHODDEF - {"idcok", (PyCFunction)PyCursesWindow_idcok, METH_VARARGS}, - {"idlok", (PyCFunction)PyCursesWindow_idlok, METH_VARARGS}, + {"idcok", PyCursesWindow_idcok, METH_VARARGS}, + {"idlok", PyCursesWindow_idlok, METH_VARARGS}, #ifdef HAVE_CURSES_IMMEDOK - {"immedok", (PyCFunction)PyCursesWindow_immedok, METH_VARARGS}, + {"immedok", PyCursesWindow_immedok, METH_VARARGS}, #endif _CURSES_WINDOW_INCH_METHODDEF _CURSES_WINDOW_INSCH_METHODDEF - {"insdelln", (PyCFunction)PyCursesWindow_winsdelln, METH_VARARGS}, - {"insertln", (PyCFunction)PyCursesWindow_winsertln, METH_NOARGS}, + {"insdelln", PyCursesWindow_winsdelln, METH_VARARGS}, + {"insertln", PyCursesWindow_winsertln, METH_NOARGS}, _CURSES_WINDOW_INSNSTR_METHODDEF _CURSES_WINDOW_INSSTR_METHODDEF - {"instr", (PyCFunction)PyCursesWindow_InStr, METH_VARARGS}, + {"instr", PyCursesWindow_InStr, METH_VARARGS}, _CURSES_WINDOW_IS_LINETOUCHED_METHODDEF - {"is_wintouched", (PyCFunction)PyCursesWindow_is_wintouched, METH_NOARGS}, - {"keypad", (PyCFunction)PyCursesWindow_keypad, METH_VARARGS}, - {"leaveok", (PyCFunction)PyCursesWindow_leaveok, METH_VARARGS}, - {"move", (PyCFunction)PyCursesWindow_wmove, METH_VARARGS}, - {"mvderwin", (PyCFunction)PyCursesWindow_mvderwin, METH_VARARGS}, - {"mvwin", (PyCFunction)PyCursesWindow_mvwin, METH_VARARGS}, - {"nodelay", (PyCFunction)PyCursesWindow_nodelay, METH_VARARGS}, - {"notimeout", (PyCFunction)PyCursesWindow_notimeout, METH_VARARGS}, + {"is_wintouched", PyCursesWindow_is_wintouched, METH_NOARGS}, + {"keypad", PyCursesWindow_keypad, METH_VARARGS}, + {"leaveok", PyCursesWindow_leaveok, METH_VARARGS}, + {"move", PyCursesWindow_wmove, METH_VARARGS}, + {"mvderwin", PyCursesWindow_mvderwin, METH_VARARGS}, + {"mvwin", PyCursesWindow_mvwin, METH_VARARGS}, + {"nodelay", PyCursesWindow_nodelay, METH_VARARGS}, + {"notimeout", PyCursesWindow_notimeout, METH_VARARGS}, _CURSES_WINDOW_NOUTREFRESH_METHODDEF _CURSES_WINDOW_OVERLAY_METHODDEF _CURSES_WINDOW_OVERWRITE_METHODDEF _CURSES_WINDOW_PUTWIN_METHODDEF _CURSES_WINDOW_REDRAWLN_METHODDEF - {"redrawwin", (PyCFunction)PyCursesWindow_redrawwin, METH_NOARGS}, + {"redrawwin", PyCursesWindow_redrawwin, METH_NOARGS}, _CURSES_WINDOW_REFRESH_METHODDEF #ifndef STRICT_SYSV_CURSES - {"resize", (PyCFunction)PyCursesWindow_wresize, METH_VARARGS}, + {"resize", PyCursesWindow_wresize, METH_VARARGS}, #endif _CURSES_WINDOW_SCROLL_METHODDEF - {"scrollok", (PyCFunction)PyCursesWindow_scrollok, METH_VARARGS}, + {"scrollok", PyCursesWindow_scrollok, METH_VARARGS}, _CURSES_WINDOW_SETSCRREG_METHODDEF - {"standend", (PyCFunction)PyCursesWindow_wstandend, METH_NOARGS}, - {"standout", (PyCFunction)PyCursesWindow_wstandout, METH_NOARGS}, + {"standend", PyCursesWindow_wstandend, METH_NOARGS}, + {"standout", PyCursesWindow_wstandout, METH_NOARGS}, {"subpad", (PyCFunction)_curses_window_subwin, METH_VARARGS, _curses_window_subwin__doc__}, _CURSES_WINDOW_SUBWIN_METHODDEF - {"syncdown", (PyCFunction)PyCursesWindow_wsyncdown, METH_NOARGS}, + {"syncdown", PyCursesWindow_wsyncdown, METH_NOARGS}, #ifdef HAVE_CURSES_SYNCOK - {"syncok", (PyCFunction)PyCursesWindow_syncok, METH_VARARGS}, + {"syncok", PyCursesWindow_syncok, METH_VARARGS}, #endif - {"syncup", (PyCFunction)PyCursesWindow_wsyncup, METH_NOARGS}, - {"timeout", (PyCFunction)PyCursesWindow_wtimeout, METH_VARARGS}, + {"syncup", PyCursesWindow_wsyncup, METH_NOARGS}, + {"timeout", PyCursesWindow_wtimeout, METH_VARARGS}, _CURSES_WINDOW_TOUCHLINE_METHODDEF - {"touchwin", (PyCFunction)PyCursesWindow_touchwin, METH_NOARGS}, - {"untouchwin", (PyCFunction)PyCursesWindow_untouchwin, METH_NOARGS}, + {"touchwin", PyCursesWindow_touchwin, METH_NOARGS}, + {"untouchwin", PyCursesWindow_untouchwin, METH_NOARGS}, _CURSES_WINDOW_VLINE_METHODDEF {NULL, NULL} /* sentinel */ }; static PyGetSetDef PyCursesWindow_getsets[] = { - {"encoding", - (getter)PyCursesWindow_get_encoding, - (setter)PyCursesWindow_set_encoding, - "the typecode character used to create the array"}, + { + "encoding", + PyCursesWindow_get_encoding, + PyCursesWindow_set_encoding, + "the typecode character used to create the array" + }, {NULL, NULL, NULL, NULL } /* sentinel */ }; From aad5ba4b6aafd3599d1737c3040b1719165e6e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:29:41 +0100 Subject: [PATCH 172/376] gh-111178: fix UBSan failures in `Objects/enumobject.c` (GH-128246) * fix UBSan failures for `enumobject` * fix UBSan failures for `reversedobject` --- Objects/enumobject.c | 57 +++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/Objects/enumobject.c b/Objects/enumobject.c index 556666779d8522..eb8952675269a2 100644 --- a/Objects/enumobject.c +++ b/Objects/enumobject.c @@ -23,6 +23,7 @@ typedef struct { PyObject* one; /* borrowed reference */ } enumobject; +#define _enumobject_CAST(op) ((enumobject *)(op)) /*[clinic input] @classmethod @@ -150,8 +151,9 @@ enumerate_vectorcall(PyObject *type, PyObject *const *args, } static void -enum_dealloc(enumobject *en) +enum_dealloc(PyObject *op) { + enumobject *en = _enumobject_CAST(op); PyObject_GC_UnTrack(en); Py_XDECREF(en->en_sit); Py_XDECREF(en->en_result); @@ -160,8 +162,9 @@ enum_dealloc(enumobject *en) } static int -enum_traverse(enumobject *en, visitproc visit, void *arg) +enum_traverse(PyObject *op, visitproc visit, void *arg) { + enumobject *en = _enumobject_CAST(op); Py_VISIT(en->en_sit); Py_VISIT(en->en_result); Py_VISIT(en->en_longindex); @@ -220,8 +223,9 @@ enum_next_long(enumobject *en, PyObject* next_item) } static PyObject * -enum_next(enumobject *en) +enum_next(PyObject *op) { + enumobject *en = _enumobject_CAST(op); PyObject *next_index; PyObject *next_item; PyObject *result = en->en_result; @@ -270,8 +274,9 @@ enum_next(enumobject *en) } static PyObject * -enum_reduce(enumobject *en, PyObject *Py_UNUSED(ignored)) +enum_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) { + enumobject *en = _enumobject_CAST(op); if (en->en_longindex != NULL) return Py_BuildValue("O(OO)", Py_TYPE(en), en->en_sit, en->en_longindex); else @@ -281,7 +286,7 @@ enum_reduce(enumobject *en, PyObject *Py_UNUSED(ignored)) PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyMethodDef enum_methods[] = { - {"__reduce__", (PyCFunction)enum_reduce, METH_NOARGS, reduce_doc}, + {"__reduce__", enum_reduce, METH_NOARGS, reduce_doc}, {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, {NULL, NULL} /* sentinel */ @@ -293,7 +298,7 @@ PyTypeObject PyEnum_Type = { sizeof(enumobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ - (destructor)enum_dealloc, /* tp_dealloc */ + enum_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -311,12 +316,12 @@ PyTypeObject PyEnum_Type = { Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ enum_new__doc__, /* tp_doc */ - (traverseproc)enum_traverse, /* tp_traverse */ + enum_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ - (iternextfunc)enum_next, /* tp_iternext */ + enum_next, /* tp_iternext */ enum_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ @@ -329,7 +334,7 @@ PyTypeObject PyEnum_Type = { PyType_GenericAlloc, /* tp_alloc */ enum_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ - .tp_vectorcall = (vectorcallfunc)enumerate_vectorcall + .tp_vectorcall = enumerate_vectorcall }; /* Reversed Object ***************************************************************/ @@ -340,6 +345,8 @@ typedef struct { PyObject* seq; } reversedobject; +#define _reversedobject_CAST(op) ((reversedobject *)(op)) + /*[clinic input] @classmethod reversed.__new__ as reversed_new @@ -411,23 +418,26 @@ reversed_vectorcall(PyObject *type, PyObject * const*args, } static void -reversed_dealloc(reversedobject *ro) +reversed_dealloc(PyObject *op) { + reversedobject *ro = _reversedobject_CAST(op); PyObject_GC_UnTrack(ro); Py_XDECREF(ro->seq); Py_TYPE(ro)->tp_free(ro); } static int -reversed_traverse(reversedobject *ro, visitproc visit, void *arg) +reversed_traverse(PyObject *op, visitproc visit, void *arg) { + reversedobject *ro = _reversedobject_CAST(op); Py_VISIT(ro->seq); return 0; } static PyObject * -reversed_next(reversedobject *ro) +reversed_next(PyObject *op) { + reversedobject *ro = _reversedobject_CAST(op); PyObject *item; Py_ssize_t index = ro->index; @@ -447,8 +457,9 @@ reversed_next(reversedobject *ro) } static PyObject * -reversed_len(reversedobject *ro, PyObject *Py_UNUSED(ignored)) +reversed_len(PyObject *op, PyObject *Py_UNUSED(ignored)) { + reversedobject *ro = _reversedobject_CAST(op); Py_ssize_t position, seqsize; if (ro->seq == NULL) @@ -463,8 +474,9 @@ reversed_len(reversedobject *ro, PyObject *Py_UNUSED(ignored)) PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyObject * -reversed_reduce(reversedobject *ro, PyObject *Py_UNUSED(ignored)) +reversed_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) { + reversedobject *ro = _reversedobject_CAST(op); if (ro->seq) return Py_BuildValue("O(O)n", Py_TYPE(ro), ro->seq, ro->index); else @@ -472,8 +484,9 @@ reversed_reduce(reversedobject *ro, PyObject *Py_UNUSED(ignored)) } static PyObject * -reversed_setstate(reversedobject *ro, PyObject *state) +reversed_setstate(PyObject *op, PyObject *state) { + reversedobject *ro = _reversedobject_CAST(op); Py_ssize_t index = PyLong_AsSsize_t(state); if (index == -1 && PyErr_Occurred()) return NULL; @@ -493,9 +506,9 @@ reversed_setstate(reversedobject *ro, PyObject *state) PyDoc_STRVAR(setstate_doc, "Set state information for unpickling."); static PyMethodDef reversediter_methods[] = { - {"__length_hint__", (PyCFunction)reversed_len, METH_NOARGS, length_hint_doc}, - {"__reduce__", (PyCFunction)reversed_reduce, METH_NOARGS, reduce_doc}, - {"__setstate__", (PyCFunction)reversed_setstate, METH_O, setstate_doc}, + {"__length_hint__", reversed_len, METH_NOARGS, length_hint_doc}, + {"__reduce__", reversed_reduce, METH_NOARGS, reduce_doc}, + {"__setstate__", reversed_setstate, METH_O, setstate_doc}, {NULL, NULL} /* sentinel */ }; @@ -505,7 +518,7 @@ PyTypeObject PyReversed_Type = { sizeof(reversedobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ - (destructor)reversed_dealloc, /* tp_dealloc */ + reversed_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -523,12 +536,12 @@ PyTypeObject PyReversed_Type = { Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ reversed_new__doc__, /* tp_doc */ - (traverseproc)reversed_traverse,/* tp_traverse */ + reversed_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ - (iternextfunc)reversed_next, /* tp_iternext */ + reversed_next, /* tp_iternext */ reversediter_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ @@ -541,5 +554,5 @@ PyTypeObject PyReversed_Type = { PyType_GenericAlloc, /* tp_alloc */ reversed_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ - .tp_vectorcall = (vectorcallfunc)reversed_vectorcall, + .tp_vectorcall = reversed_vectorcall, }; From 56430320536a753236aadde59984c3c8f8777aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:35:05 +0100 Subject: [PATCH 173/376] gh-111178: fix UBSan failures in `Objects/tupleobject.c` (GH-128251) fix UBSan failures for `_PyTupleIterObject` --- Objects/tupleobject.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 49977726eadca9..002002eb455556 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -988,26 +988,29 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize) /*********************** Tuple Iterator **************************/ +#define _PyTupleIterObject_CAST(op) ((_PyTupleIterObject *)(op)) static void -tupleiter_dealloc(_PyTupleIterObject *it) +tupleiter_dealloc(PyObject *self) { + _PyTupleIterObject *it = _PyTupleIterObject_CAST(self); _PyObject_GC_UNTRACK(it); Py_XDECREF(it->it_seq); PyObject_GC_Del(it); } static int -tupleiter_traverse(_PyTupleIterObject *it, visitproc visit, void *arg) +tupleiter_traverse(PyObject *self, visitproc visit, void *arg) { + _PyTupleIterObject *it = _PyTupleIterObject_CAST(self); Py_VISIT(it->it_seq); return 0; } static PyObject * -tupleiter_next(PyObject *obj) +tupleiter_next(PyObject *self) { - _PyTupleIterObject *it = (_PyTupleIterObject *)obj; + _PyTupleIterObject *it = _PyTupleIterObject_CAST(self); PyTupleObject *seq; PyObject *item; @@ -1029,8 +1032,9 @@ tupleiter_next(PyObject *obj) } static PyObject * -tupleiter_len(_PyTupleIterObject *it, PyObject *Py_UNUSED(ignored)) +tupleiter_len(PyObject *self, PyObject *Py_UNUSED(ignored)) { + _PyTupleIterObject *it = _PyTupleIterObject_CAST(self); Py_ssize_t len = 0; if (it->it_seq) len = PyTuple_GET_SIZE(it->it_seq) - it->it_index; @@ -1040,13 +1044,14 @@ tupleiter_len(_PyTupleIterObject *it, PyObject *Py_UNUSED(ignored)) PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyObject * -tupleiter_reduce(_PyTupleIterObject *it, PyObject *Py_UNUSED(ignored)) +tupleiter_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) { PyObject *iter = _PyEval_GetBuiltin(&_Py_ID(iter)); /* _PyEval_GetBuiltin can invoke arbitrary code, * call must be before access of iterator pointers. * see issue #101765 */ + _PyTupleIterObject *it = _PyTupleIterObject_CAST(self); if (it->it_seq) return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); @@ -1055,8 +1060,9 @@ tupleiter_reduce(_PyTupleIterObject *it, PyObject *Py_UNUSED(ignored)) } static PyObject * -tupleiter_setstate(_PyTupleIterObject *it, PyObject *state) +tupleiter_setstate(PyObject *self, PyObject *state) { + _PyTupleIterObject *it = _PyTupleIterObject_CAST(self); Py_ssize_t index = PyLong_AsSsize_t(state); if (index == -1 && PyErr_Occurred()) return NULL; @@ -1074,19 +1080,19 @@ PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); PyDoc_STRVAR(setstate_doc, "Set state information for unpickling."); static PyMethodDef tupleiter_methods[] = { - {"__length_hint__", (PyCFunction)tupleiter_len, METH_NOARGS, length_hint_doc}, - {"__reduce__", (PyCFunction)tupleiter_reduce, METH_NOARGS, reduce_doc}, - {"__setstate__", (PyCFunction)tupleiter_setstate, METH_O, setstate_doc}, - {NULL, NULL} /* sentinel */ + {"__length_hint__", tupleiter_len, METH_NOARGS, length_hint_doc}, + {"__reduce__", tupleiter_reduce, METH_NOARGS, reduce_doc}, + {"__setstate__", tupleiter_setstate, METH_O, setstate_doc}, + {NULL, NULL, 0, NULL} /* sentinel */ }; PyTypeObject PyTupleIter_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "tuple_iterator", /* tp_name */ - sizeof(_PyTupleIterObject), /* tp_basicsize */ + sizeof(_PyTupleIterObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ - (destructor)tupleiter_dealloc, /* tp_dealloc */ + tupleiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -1103,7 +1109,7 @@ PyTypeObject PyTupleIter_Type = { 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ - (traverseproc)tupleiter_traverse, /* tp_traverse */ + tupleiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ From 1c9b0204796ddeaee710646871a4404b4cda1f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:36:41 +0100 Subject: [PATCH 174/376] gh-111178: fix UBSan failures in `Modules/zlibmodule.c` (GH-128252) --- Modules/zlibmodule.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c index 78dcce73cdaade..b90665ae7ef64a 100644 --- a/Modules/zlibmodule.c +++ b/Modules/zlibmodule.c @@ -221,6 +221,8 @@ typedef struct PyThread_type_lock lock; } compobject; +#define _compobject_CAST(op) ((compobject *)op) + static void zlib_error(zlibstate *state, z_stream zst, int err, const char *msg) { @@ -706,7 +708,7 @@ zlib_decompressobj_impl(PyObject *module, int wbits, PyObject *zdict) static void Dealloc(compobject *self) { - PyObject *type = (PyObject *)Py_TYPE(self); + PyTypeObject *type = Py_TYPE(self); PyThread_free_lock(self->lock); Py_XDECREF(self->unused_data); Py_XDECREF(self->unconsumed_tail); @@ -716,18 +718,20 @@ Dealloc(compobject *self) } static void -Comp_dealloc(compobject *self) +Comp_dealloc(PyObject *op) { + compobject *self = _compobject_CAST(op); if (self->is_initialised) - deflateEnd(&self->zst); + (void)deflateEnd(&self->zst); Dealloc(self); } static void -Decomp_dealloc(compobject *self) +Decomp_dealloc(PyObject *op) { + compobject *self = _compobject_CAST(op); if (self->is_initialised) - inflateEnd(&self->zst); + (void)inflateEnd(&self->zst); Dealloc(self); } From 4c14f03495724f2c52de2d34f1bfa35dd94757c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:51:22 +0100 Subject: [PATCH 175/376] gh-89083: improve UUIDv8 uniqueness tests (GH-128149) improve UUIDv8 uniqueness tests --- Lib/test/test_uuid.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index 7bd26a8ca34b62..8f40dd97f42fdc 100755 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -707,12 +707,16 @@ def test_uuid8(self): equal(u.int & 0x3fffffffffffffff, lo) def test_uuid8_uniqueness(self): - # Test that UUIDv8-generated values are unique - # (up to a negligible probability of failure). - u1 = self.uuid.uuid8() - u2 = self.uuid.uuid8() - self.assertNotEqual(u1.int, u2.int) - self.assertEqual(u1.version, u2.version) + # Test that UUIDv8-generated values are unique (up to a negligible + # probability of failure). There are 122 bits of entropy and assuming + # that the underlying mt-19937-based random generator is sufficiently + # good, it is unlikely to have a collision of two UUIDs. + N = 1000 + uuids = {self.uuid.uuid8() for _ in range(N)} + self.assertEqual(len(uuids), N) + + versions = {u.version for u in uuids} + self.assertSetEqual(versions, {8}) @support.requires_fork() def testIssue8621(self): From 46cb6340d7bad955edfc0a20f6a52dabc03b0932 Mon Sep 17 00:00:00 2001 From: Alexander Shadchin Date: Fri, 3 Jan 2025 21:47:58 +0300 Subject: [PATCH 176/376] gh-127903: Fix a crash on debug builds when calling `Objects/unicodeobject::_copy_characters`` (#127876) --- Lib/test/test_str.py | 7 +++++++ .../2024-12-13-14-17-24.gh-issue-127903.vemHSl.rst | 2 ++ Objects/unicodeobject.c | 9 ++++++--- 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-14-17-24.gh-issue-127903.vemHSl.rst diff --git a/Lib/test/test_str.py b/Lib/test/test_str.py index 4de6c1cba152bd..d1c9542c7d1317 100644 --- a/Lib/test/test_str.py +++ b/Lib/test/test_str.py @@ -7,6 +7,7 @@ """ import _string import codecs +import datetime import itertools import operator import pickle @@ -1908,6 +1909,12 @@ def test_utf8_decode_invalid_sequences(self): self.assertRaises(UnicodeDecodeError, (b'\xF4'+cb+b'\xBF\xBF').decode, 'utf-8') + def test_issue127903(self): + # gh-127903: ``_copy_characters`` crashes on DEBUG builds when + # there is nothing to copy. + d = datetime.datetime(2013, 11, 10, 14, 20, 59) + self.assertEqual(d.strftime('%z'), '') + def test_issue8271(self): # Issue #8271: during the decoding of an invalid UTF-8 byte sequence, # only the start byte and the continuation byte(s) are now considered diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-14-17-24.gh-issue-127903.vemHSl.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-14-17-24.gh-issue-127903.vemHSl.rst new file mode 100644 index 00000000000000..ad479b52d1675c --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-14-17-24.gh-issue-127903.vemHSl.rst @@ -0,0 +1,2 @@ +``Objects/unicodeobject.c``: fix a crash on DEBUG builds in ``_copy_characters`` +when there is nothing to copy. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 9f0a4d4785eda6..5e532ce0f348c4 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -1463,11 +1463,14 @@ _copy_characters(PyObject *to, Py_ssize_t to_start, assert(PyUnicode_Check(from)); assert(from_start + how_many <= PyUnicode_GET_LENGTH(from)); - assert(PyUnicode_Check(to)); - assert(to_start + how_many <= PyUnicode_GET_LENGTH(to)); + assert(to == NULL || PyUnicode_Check(to)); - if (how_many == 0) + if (how_many == 0) { return 0; + } + + assert(to != NULL); + assert(to_start + how_many <= PyUnicode_GET_LENGTH(to)); from_kind = PyUnicode_KIND(from); from_data = PyUnicode_DATA(from); From befcfdfdaba15ecae38739ecabebd8046c1b1977 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Fri, 3 Jan 2025 14:14:57 -0500 Subject: [PATCH 177/376] gh-128400: Only show the current thread in `faulthandler` if the GIL is disabled (GH-128425) --- Doc/library/faulthandler.rst | 4 +++ Lib/test/test_faulthandler.py | 11 +++++-- ...-01-02-15-20-17.gh-issue-128400.UMiG4f.rst | 2 ++ Modules/faulthandler.c | 33 +++++++++++++++++-- 4 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-02-15-20-17.gh-issue-128400.UMiG4f.rst diff --git a/Doc/library/faulthandler.rst b/Doc/library/faulthandler.rst index 4067d7912b88b2..b81da4af3cff58 100644 --- a/Doc/library/faulthandler.rst +++ b/Doc/library/faulthandler.rst @@ -91,6 +91,10 @@ Fault handler state The dump now mentions if a garbage collector collection is running if *all_threads* is true. + .. versionchanged:: next + Only the current thread is dumped if the :term:`GIL` is disabled to + prevent the risk of data races. + .. function:: disable() Disable the fault handler: uninstall the signal handlers installed by diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index fd56dee5d842ac..2088793cbb9387 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -100,7 +100,12 @@ def check_error(self, code, lineno, fatal_error, *, Raise an error if the output doesn't match the expected format. """ - if all_threads: + all_threads_disabled = ( + (not py_fatal_error) + and all_threads + and (not sys._is_gil_enabled()) + ) + if all_threads and not all_threads_disabled: if know_current_thread: header = 'Current thread 0x[0-9a-f]+' else: @@ -111,8 +116,10 @@ def check_error(self, code, lineno, fatal_error, *, if py_fatal_error: regex.append("Python runtime state: initialized") regex.append('') + if all_threads_disabled: + regex.append("") regex.append(fr'{header} \(most recent call first\):') - if garbage_collecting: + if garbage_collecting and not all_threads_disabled: regex.append(' Garbage-collecting') regex.append(fr' File "", line {lineno} in {function}') regex = '\n'.join(regex) diff --git a/Misc/NEWS.d/next/Library/2025-01-02-15-20-17.gh-issue-128400.UMiG4f.rst b/Misc/NEWS.d/next/Library/2025-01-02-15-20-17.gh-issue-128400.UMiG4f.rst new file mode 100644 index 00000000000000..f9d5f84224c8dc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-02-15-20-17.gh-issue-128400.UMiG4f.rst @@ -0,0 +1,2 @@ +Only show the current thread in :mod:`faulthandler` on the :term:`free +threaded ` build to prevent races. diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index 2d16028a5232d0..b44b964b29484b 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -1,4 +1,5 @@ #include "Python.h" +#include "pycore_ceval.h" // _PyEval_IsGILEnabled #include "pycore_initconfig.h" // _PyStatus_ERR #include "pycore_pyerrors.h" // _Py_DumpExtensionModules #include "pycore_pystate.h" // _PyThreadState_GET() @@ -27,6 +28,8 @@ # include // getauxval() #endif +/* Sentinel to ignore all_threads on free-threading */ +#define FT_IGNORE_ALL_THREADS 2 /* Allocate at maximum 100 MiB of the stack to raise the stack overflow */ #define STACK_OVERFLOW_MAX_SIZE (100 * 1024 * 1024) @@ -201,10 +204,13 @@ faulthandler_dump_traceback(int fd, int all_threads, PyGILState_GetThisThreadState(). */ PyThreadState *tstate = PyGILState_GetThisThreadState(); - if (all_threads) { + if (all_threads == 1) { (void)_Py_DumpTracebackThreads(fd, NULL, tstate); } else { + if (all_threads == FT_IGNORE_ALL_THREADS) { + PUTS(fd, "\n"); + } if (tstate != NULL) _Py_DumpTraceback(fd, tstate); } @@ -271,6 +277,27 @@ faulthandler_disable_fatal_handler(fault_handler_t *handler) #endif } +static int +deduce_all_threads(void) +{ +#ifndef Py_GIL_DISABLED + return fatal_error.all_threads; +#else + if (fatal_error.all_threads == 0) { + return 0; + } + // We can't use _PyThreadState_GET, so use the stored GILstate one + PyThreadState *tstate = PyGILState_GetThisThreadState(); + if (tstate == NULL) { + return 0; + } + + /* In theory, it's safe to dump all threads if the GIL is enabled */ + return _PyEval_IsGILEnabled(tstate) + ? fatal_error.all_threads + : FT_IGNORE_ALL_THREADS; +#endif +} /* Handler for SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL signals. @@ -325,7 +352,7 @@ faulthandler_fatal_error(int signum) PUTS(fd, "\n\n"); } - faulthandler_dump_traceback(fd, fatal_error.all_threads, + faulthandler_dump_traceback(fd, deduce_all_threads(), fatal_error.interp); _Py_DumpExtensionModules(fd, fatal_error.interp); @@ -401,7 +428,7 @@ faulthandler_exc_handler(struct _EXCEPTION_POINTERS *exc_info) } } - faulthandler_dump_traceback(fd, fatal_error.all_threads, + faulthandler_dump_traceback(fd, deduce_all_threads(), fatal_error.interp); /* call the next exception handler */ From 4974bbdb290b61a2d8860de490fde1228c296753 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Fri, 3 Jan 2025 14:49:50 -0600 Subject: [PATCH 178/376] gh-128456: Use '-reorder-functions=cdsort' for BOLT builds (#128457) 'hfsort+' is deprecated in favor of 'cdsort'. --- configure | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 86b96d6208fe40..08fec03bef42dc 100755 --- a/configure +++ b/configure @@ -9403,7 +9403,7 @@ printf "%s\n" "$BOLT_INSTRUMENT_FLAGS" >&6; } printf %s "checking BOLT_APPLY_FLAGS... " >&6; } if test -z "${BOLT_APPLY_FLAGS}" then - BOLT_APPLY_FLAGS=" -update-debug-sections -reorder-blocks=ext-tsp -reorder-functions=hfsort+ -split-functions -icf=1 -inline-all -split-eh -reorder-functions-use-hot-size -peepholes=none -jump-tables=aggressive -inline-ap -indirect-call-promotion=all -dyno-stats -use-gnu-stack -frame-opt=hot " + BOLT_APPLY_FLAGS=" -update-debug-sections -reorder-blocks=ext-tsp -reorder-functions=cdsort -split-functions -icf=1 -inline-all -split-eh -reorder-functions-use-hot-size -peepholes=none -jump-tables=aggressive -inline-ap -indirect-call-promotion=all -dyno-stats -use-gnu-stack -frame-opt=hot " fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $BOLT_APPLY_FLAGS" >&5 diff --git a/configure.ac b/configure.ac index 50b130f2c802b5..ecaf0fa9c43d49 100644 --- a/configure.ac +++ b/configure.ac @@ -2183,7 +2183,7 @@ then [m4_normalize(" -update-debug-sections -reorder-blocks=ext-tsp - -reorder-functions=hfsort+ + -reorder-functions=cdsort -split-functions -icf=1 -inline-all From b75ed951d4de8ba85349d80c8e7f097b3cd6052f Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Fri, 3 Jan 2025 14:51:06 -0600 Subject: [PATCH 179/376] gh-128354: Consistently use LIBS over LDFLAGS in library build checks (#128359) --- configure | 49 ++++++++++++++++++++++++------------------------- configure.ac | 35 +++++++++++++++++------------------ 2 files changed, 41 insertions(+), 43 deletions(-) diff --git a/configure b/configure index 08fec03bef42dc..aa88c74c61156c 100755 --- a/configure +++ b/configure @@ -14044,7 +14044,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $LIBUUID_CFLAGS" - LDFLAGS="$LDFLAGS $LIBUUID_LIBS" + LIBS="$LIBS $LIBUUID_LIBS" for ac_header in uuid/uuid.h do : ac_fn_c_check_header_compile "$LINENO" "uuid/uuid.h" "ac_cv_header_uuid_uuid_h" "$ac_includes_default" @@ -14186,7 +14186,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $LIBUUID_CFLAGS" - LDFLAGS="$LDFLAGS $LIBUUID_LIBS" + LIBS="$LIBS $LIBUUID_LIBS" for ac_header in uuid/uuid.h do : ac_fn_c_check_header_compile "$LINENO" "uuid/uuid.h" "ac_cv_header_uuid_uuid_h" "$ac_includes_default" @@ -15084,7 +15084,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $LIBFFI_CFLAGS" - LDFLAGS="$LDFLAGS $LIBFFI_LIBS" + LIBS="$LIBS $LIBFFI_LIBS" ac_fn_c_check_header_compile "$LINENO" "ffi.h" "ac_cv_header_ffi_h" "$ac_includes_default" if test "x$ac_cv_header_ffi_h" = xyes then : @@ -15166,7 +15166,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $LIBFFI_CFLAGS" - LDFLAGS="$LDFLAGS $LIBFFI_LIBS" + LIBS="$LIBS $LIBFFI_LIBS" ac_fn_c_check_header_compile "$LINENO" "ffi.h" "ac_cv_header_ffi_h" "$ac_includes_default" if test "x$ac_cv_header_ffi_h" = xyes then : @@ -15286,8 +15286,8 @@ save_LDFLAGS=$LDFLAGS save_LIBS=$LIBS - CFLAGS="$LIBFFI_CFLAGS $CFLAGS" - LDFLAGS="$LIBFFI_LIBS $LDFLAGS" + CFLAGS="$CFLAGS $LIBFFI_CFLAGS" + LIBS="$LIBS $LIBFFI_LIBS" @@ -15439,9 +15439,8 @@ save_LDFLAGS=$LDFLAGS save_LIBS=$LIBS - CPPFLAGS="$LIBFFI_CFLAGS $CPPFLAGS" - LDFLAGS="$LIBFFI_LIBS $LDFLAGS" - LIBS="$LIBFFI_LIBS $LIBS" + CPPFLAGS="$CPPFLAGS $LIBFFI_CFLAGS" + LIBS="$LIBS $LIBFFI_LIBS" if test "$cross_compiling" = yes then : ac_cv_ffi_complex_double_supported=no @@ -15614,8 +15613,8 @@ save_LDFLAGS=$LDFLAGS save_LIBS=$LIBS - CPPFLAGS="$LIBMPDEC_CFLAGS $CPPFLAGS" - LIBS="$LIBMPDEC_LIBS $LIBS" + CPPFLAGS="$CPPFLAGS $LIBMPDEC_CFLAGS" + LIBS="$LIBS $LIBMPDEC_LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -15885,7 +15884,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $LIBSQLITE3_CFLAGS" - LIBS="$LIBSQLITE3_LIBS $LIBS" + LIBS="$LIBS $LIBSQLITE3_LIBS" ac_fn_c_check_header_compile "$LINENO" "sqlite3.h" "ac_cv_header_sqlite3_h" "$ac_includes_default" if test "x$ac_cv_header_sqlite3_h" = xyes @@ -16875,7 +16874,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $TCLTK_CFLAGS" - LIBS="$TCLTK_LIBS $LDFLAGS" + LIBS="$LIBS $TCLTK_LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -16942,7 +16941,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $GDBM_CFLAGS" - LDFLAGS="$GDBM_LIBS $LDFLAGS" + LIBS="$LIBS $GDBM_LIBS" for ac_header in gdbm.h do : ac_fn_c_check_header_compile "$LINENO" "gdbm.h" "ac_cv_header_gdbm_h" "$ac_includes_default" @@ -21492,7 +21491,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $ZLIB_CFLAGS" - LDFLAGS="$LDFLAGS $ZLIB_LIBS" + LIBS="$LIBS $ZLIB_LIBS" for ac_header in zlib.h do : ac_fn_c_check_header_compile "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default" @@ -21639,7 +21638,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $ZLIB_CFLAGS" - LDFLAGS="$LDFLAGS $ZLIB_LIBS" + LIBS="$LIBS $ZLIB_LIBS" for ac_header in zlib.h do : ac_fn_c_check_header_compile "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default" @@ -21876,7 +21875,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $BZIP2_CFLAGS" - LDFLAGS="$LDFLAGS $BZIP2_LIBS" + LIBS="$LIBS $BZIP2_LIBS" for ac_header in bzlib.h do : ac_fn_c_check_header_compile "$LINENO" "bzlib.h" "ac_cv_header_bzlib_h" "$ac_includes_default" @@ -21968,7 +21967,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $BZIP2_CFLAGS" - LDFLAGS="$LDFLAGS $BZIP2_LIBS" + LIBS="$LIBS $BZIP2_LIBS" for ac_header in bzlib.h do : ac_fn_c_check_header_compile "$LINENO" "bzlib.h" "ac_cv_header_bzlib_h" "$ac_includes_default" @@ -22124,7 +22123,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $LIBLZMA_CFLAGS" - LDFLAGS="$LDFLAGS $LIBLZMA_LIBS" + LIBS="$LIBS $LIBLZMA_LIBS" for ac_header in lzma.h do : ac_fn_c_check_header_compile "$LINENO" "lzma.h" "ac_cv_header_lzma_h" "$ac_includes_default" @@ -22216,7 +22215,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $LIBLZMA_CFLAGS" - LDFLAGS="$LDFLAGS $LIBLZMA_LIBS" + LIBS="$LIBS $LIBLZMA_LIBS" for ac_header in lzma.h do : ac_fn_c_check_header_compile "$LINENO" "lzma.h" "ac_cv_header_lzma_h" "$ac_includes_default" @@ -26370,7 +26369,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $LIBREADLINE_CFLAGS" - LDFLAGS="$LDFLAGS $LIBREADLINE_LIBS" + LIBS="$LIBS $LIBREADLINE_LIBS" for ac_header in readline/readline.h do : ac_fn_c_check_header_compile "$LINENO" "readline/readline.h" "ac_cv_header_readline_readline_h" "$ac_includes_default" @@ -26459,7 +26458,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $LIBREADLINE_CFLAGS" - LDFLAGS="$LDFLAGS $LIBREADLINE_LIBS" + LIBS="$LIBS $LIBREADLINE_LIBS" for ac_header in readline/readline.h do : ac_fn_c_check_header_compile "$LINENO" "readline/readline.h" "ac_cv_header_readline_readline_h" "$ac_includes_default" @@ -26621,7 +26620,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $LIBEDIT_CFLAGS" - LDFLAGS="$LDFLAGS $LIBEDIT_LIBS" + LIBS="$LIBS $LIBEDIT_LIBS" for ac_header in editline/readline.h do : ac_fn_c_check_header_compile "$LINENO" "editline/readline.h" "ac_cv_header_editline_readline_h" "$ac_includes_default" @@ -26712,7 +26711,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $LIBEDIT_CFLAGS" - LDFLAGS="$LDFLAGS $LIBEDIT_LIBS" + LIBS="$LIBS $LIBEDIT_LIBS" for ac_header in editline/readline.h do : ac_fn_c_check_header_compile "$LINENO" "editline/readline.h" "ac_cv_header_editline_readline_h" "$ac_includes_default" @@ -26830,7 +26829,7 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $READLINE_CFLAGS" - LIBS="$READLINE_LIBS $LIBS" + LIBS="$LIBS $READLINE_LIBS" LIBS_SAVE=$LIBS diff --git a/configure.ac b/configure.ac index ecaf0fa9c43d49..9e131ed1a2dc98 100644 --- a/configure.ac +++ b/configure.ac @@ -3701,7 +3701,7 @@ AS_VAR_IF([have_uuid], [missing], [ ], [ WITH_SAVE_ENV([ CPPFLAGS="$CPPFLAGS $LIBUUID_CFLAGS" - LDFLAGS="$LDFLAGS $LIBUUID_LIBS" + LIBS="$LIBS $LIBUUID_LIBS" AC_CHECK_HEADERS([uuid/uuid.h], [ PY_CHECK_LIB([uuid], [uuid_generate_time], [have_uuid=yes]) PY_CHECK_LIB([uuid], [uuid_generate_time_safe], @@ -3971,7 +3971,7 @@ AS_VAR_IF([have_libffi], [missing], [ PKG_CHECK_MODULES([LIBFFI], [libffi], [have_libffi=yes], [ WITH_SAVE_ENV([ CPPFLAGS="$CPPFLAGS $LIBFFI_CFLAGS" - LDFLAGS="$LDFLAGS $LIBFFI_LIBS" + LIBS="$LIBS $LIBFFI_LIBS" AC_CHECK_HEADER([ffi.h], [ AC_CHECK_LIB([ffi], [ffi_call], [ have_libffi=yes @@ -4005,8 +4005,8 @@ AS_VAR_IF([have_libffi], [yes], [ AS_VAR_IF([ac_cv_lib_dl_dlopen], [yes], [AS_VAR_APPEND([LIBFFI_LIBS], [" -ldl"])]) WITH_SAVE_ENV([ - CFLAGS="$LIBFFI_CFLAGS $CFLAGS" - LDFLAGS="$LIBFFI_LIBS $LDFLAGS" + CFLAGS="$CFLAGS $LIBFFI_CFLAGS" + LIBS="$LIBS $LIBFFI_LIBS" PY_CHECK_FUNC([ffi_prep_cif_var], [@%:@include ]) PY_CHECK_FUNC([ffi_prep_closure_loc], [@%:@include ]) @@ -4021,9 +4021,8 @@ AS_VAR_IF([have_libffi], [yes], [ # AC_CACHE_CHECK([libffi has complex type support], [ac_cv_ffi_complex_double_supported], [WITH_SAVE_ENV([ - CPPFLAGS="$LIBFFI_CFLAGS $CPPFLAGS" - LDFLAGS="$LIBFFI_LIBS $LDFLAGS" - LIBS="$LIBFFI_LIBS $LIBS" + CPPFLAGS="$CPPFLAGS $LIBFFI_CFLAGS" + LIBS="$LIBS $LIBFFI_LIBS" AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include @@ -4086,8 +4085,8 @@ AS_VAR_IF( AS_VAR_IF([with_system_libmpdec], [yes], [WITH_SAVE_ENV([ - CPPFLAGS="$LIBMPDEC_CFLAGS $CPPFLAGS" - LIBS="$LIBMPDEC_LIBS $LIBS" + CPPFLAGS="$CPPFLAGS $LIBMPDEC_CFLAGS" + LIBS="$LIBS $LIBMPDEC_LIBS" AC_LINK_IFELSE([ AC_LANG_PROGRAM([ @@ -4220,7 +4219,7 @@ WITH_SAVE_ENV([ dnl bpo-45774/GH-29507: The CPP check in AC_CHECK_HEADER can fail on FreeBSD, dnl hence CPPFLAGS instead of CFLAGS. CPPFLAGS="$CPPFLAGS $LIBSQLITE3_CFLAGS" - LIBS="$LIBSQLITE3_LIBS $LIBS" + LIBS="$LIBS $LIBSQLITE3_LIBS" AC_CHECK_HEADER([sqlite3.h], [ have_sqlite3=yes @@ -4323,7 +4322,7 @@ AS_CASE([$ac_sys_system], WITH_SAVE_ENV([ CPPFLAGS="$CPPFLAGS $TCLTK_CFLAGS" - LIBS="$TCLTK_LIBS $LDFLAGS" + LIBS="$LIBS $TCLTK_LIBS" AC_LINK_IFELSE([ AC_LANG_PROGRAM([ @@ -4365,7 +4364,7 @@ AC_ARG_VAR([GDBM_CFLAGS], [C compiler flags for gdbm]) AC_ARG_VAR([GDBM_LIBS], [additional linker flags for gdbm]) WITH_SAVE_ENV([ CPPFLAGS="$CPPFLAGS $GDBM_CFLAGS" - LDFLAGS="$GDBM_LIBS $LDFLAGS" + LIBS="$LIBS $GDBM_LIBS" AC_CHECK_HEADERS([gdbm.h], [ AC_CHECK_LIB([gdbm], [gdbm_open], [ have_gdbm=yes @@ -5310,7 +5309,7 @@ PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.0], [ ], [ WITH_SAVE_ENV([ CPPFLAGS="$CPPFLAGS $ZLIB_CFLAGS" - LDFLAGS="$LDFLAGS $ZLIB_LIBS" + LIBS="$LIBS $ZLIB_LIBS" AC_CHECK_HEADERS([zlib.h], [ PY_CHECK_LIB([z], [gzread], [have_zlib=yes], [have_zlib=no]) ], [have_zlib=no]) @@ -5334,7 +5333,7 @@ PY_CHECK_EMSCRIPTEN_PORT([BZIP2], [-sUSE_BZIP2]) PKG_CHECK_MODULES([BZIP2], [bzip2], [have_bzip2=yes], [ WITH_SAVE_ENV([ CPPFLAGS="$CPPFLAGS $BZIP2_CFLAGS" - LDFLAGS="$LDFLAGS $BZIP2_LIBS" + LIBS="$LIBS $BZIP2_LIBS" AC_CHECK_HEADERS([bzlib.h], [ AC_CHECK_LIB([bz2], [BZ2_bzCompress], [have_bzip2=yes], [have_bzip2=no]) ], [have_bzip2=no]) @@ -5348,7 +5347,7 @@ PKG_CHECK_MODULES([BZIP2], [bzip2], [have_bzip2=yes], [ PKG_CHECK_MODULES([LIBLZMA], [liblzma], [have_liblzma=yes], [ WITH_SAVE_ENV([ CPPFLAGS="$CPPFLAGS $LIBLZMA_CFLAGS" - LDFLAGS="$LDFLAGS $LIBLZMA_LIBS" + LIBS="$LIBS $LIBLZMA_LIBS" AC_CHECK_HEADERS([lzma.h], [ AC_CHECK_LIB([lzma], [lzma_easy_encoder], [have_liblzma=yes], [have_liblzma=no]) ], [have_liblzma=no]) @@ -6342,7 +6341,7 @@ AS_VAR_IF([with_readline], [readline], [ ], [ WITH_SAVE_ENV([ CPPFLAGS="$CPPFLAGS $LIBREADLINE_CFLAGS" - LDFLAGS="$LDFLAGS $LIBREADLINE_LIBS" + LIBS="$LIBS $LIBREADLINE_LIBS" AC_CHECK_HEADERS([readline/readline.h], [ AC_CHECK_LIB([readline], [readline], [ LIBREADLINE=readline @@ -6363,7 +6362,7 @@ AS_VAR_IF([with_readline], [edit], [ ], [ WITH_SAVE_ENV([ CPPFLAGS="$CPPFLAGS $LIBEDIT_CFLAGS" - LDFLAGS="$LDFLAGS $LIBEDIT_LIBS" + LIBS="$LIBS $LIBEDIT_LIBS" AC_CHECK_HEADERS([editline/readline.h], [ AC_CHECK_LIB([edit], [readline], [ LIBREADLINE=edit @@ -6387,7 +6386,7 @@ AS_VAR_IF([with_readline], [no], [ WITH_SAVE_ENV([ CPPFLAGS="$CPPFLAGS $READLINE_CFLAGS" - LIBS="$READLINE_LIBS $LIBS" + LIBS="$LIBS $READLINE_LIBS" LIBS_SAVE=$LIBS m4_define([readline_includes], [ From f1574859d7d6cd259f867194762f04b72ef2c340 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Fri, 3 Jan 2025 16:48:47 -0500 Subject: [PATCH 180/376] gh-125985: Fix `cmodule_function()` scaling benchmark (#128460) Add a separate benchmark that measures the effect of `_PyObject_LookupSpecial()` on scaling. In the process of cleaning up the scaling benchmarks for inclusion, I unintentionally changed the "cmodule_function" benchmark to pass an `int` to `math.floor()` instead of a `float`, which causes it to use the `_PyObject_LookupSpecial()` code path. `_PyObject_LookupSpecial()` has its own scaling issues that we want to measure separately from calling a function on a C module. --- Tools/ftscalingbench/ftscalingbench.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Tools/ftscalingbench/ftscalingbench.py b/Tools/ftscalingbench/ftscalingbench.py index 767aeae9349070..364c465bc91b0b 100644 --- a/Tools/ftscalingbench/ftscalingbench.py +++ b/Tools/ftscalingbench/ftscalingbench.py @@ -54,8 +54,16 @@ def object_cfunction(): @register_benchmark def cmodule_function(): - for i in range(1000 * WORK_SCALE): - math.floor(i * i) + N = 1000 * WORK_SCALE + for i in range(N): + math.cos(i / N) + +@register_benchmark +def object_lookup_special(): + # round() uses `_PyObject_LookupSpecial()` internally. + N = 1000 * WORK_SCALE + for i in range(N): + round(i / N) @register_benchmark def mult_constant(): @@ -206,7 +214,7 @@ def benchmark(func): color = "\x1b[33m" # yellow reset_color = "\x1b[0m" - print(f"{color}{func.__name__:<18} {round(factor, 1):>4}x {direction}{reset_color}") + print(f"{color}{func.__name__:<25} {round(factor, 1):>4}x {direction}{reset_color}") def determine_num_threads_and_affinity(): if sys.platform != "linux": From 513a4efa75bf78c9d629ddabc9516fb058787289 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Sat, 4 Jan 2025 14:18:22 +0530 Subject: [PATCH 181/376] gh-128002: fix many thread safety issues in asyncio (#128147) * Makes `_asyncio.Task` and `_asyncio.Future` thread-safe by adding critical sections * Add assertions to check for thread safety checking locking of object by critical sections in internal functions * Make `_asyncio.all_tasks` thread safe when eager tasks are used * Add a thread safety test --- Lib/test/test_asyncio/test_free_threading.py | 82 +++ Modules/_asynciomodule.c | 494 +++++++++++------ Modules/clinic/_asynciomodule.c.h | 550 ++++++++++++++++++- 3 files changed, 951 insertions(+), 175 deletions(-) create mode 100644 Lib/test/test_asyncio/test_free_threading.py diff --git a/Lib/test/test_asyncio/test_free_threading.py b/Lib/test/test_asyncio/test_free_threading.py new file mode 100644 index 00000000000000..90bddbf3a9dda1 --- /dev/null +++ b/Lib/test/test_asyncio/test_free_threading.py @@ -0,0 +1,82 @@ +import asyncio +import unittest +from threading import Thread +from unittest import TestCase + +from test.support import threading_helper + +threading_helper.requires_working_threading(module=True) + +def tearDownModule(): + asyncio._set_event_loop_policy(None) + + +class TestFreeThreading: + def test_all_tasks_race(self) -> None: + async def main(): + loop = asyncio.get_running_loop() + future = loop.create_future() + + async def coro(): + await future + + tasks = set() + + async with asyncio.TaskGroup() as tg: + for _ in range(100): + tasks.add(tg.create_task(coro())) + + all_tasks = self.all_tasks(loop) + self.assertEqual(len(all_tasks), 101) + + for task in all_tasks: + self.assertEqual(task.get_loop(), loop) + self.assertFalse(task.done()) + + current = self.current_task() + self.assertEqual(current.get_loop(), loop) + self.assertSetEqual(all_tasks, tasks | {current}) + future.set_result(None) + + def runner(): + with asyncio.Runner() as runner: + loop = runner.get_loop() + loop.set_task_factory(self.factory) + runner.run(main()) + + threads = [] + + for _ in range(10): + thread = Thread(target=runner) + threads.append(thread) + + with threading_helper.start_threads(threads): + pass + + +class TestPyFreeThreading(TestFreeThreading, TestCase): + all_tasks = staticmethod(asyncio.tasks._py_all_tasks) + current_task = staticmethod(asyncio.tasks._py_current_task) + + def factory(self, loop, coro, context=None): + return asyncio.tasks._PyTask(coro, loop=loop, context=context) + + +@unittest.skipUnless(hasattr(asyncio.tasks, "_c_all_tasks"), "requires _asyncio") +class TestCFreeThreading(TestFreeThreading, TestCase): + all_tasks = staticmethod(getattr(asyncio.tasks, "_c_all_tasks", None)) + current_task = staticmethod(getattr(asyncio.tasks, "_c_current_task", None)) + + def factory(self, loop, coro, context=None): + return asyncio.tasks._CTask(coro, loop=loop, context=context) + + +class TestEagerPyFreeThreading(TestPyFreeThreading): + def factory(self, loop, coro, context=None): + return asyncio.tasks._PyTask(coro, loop=loop, context=context, eager_start=True) + + +@unittest.skipUnless(hasattr(asyncio.tasks, "_c_all_tasks"), "requires _asyncio") +class TestEagerCFreeThreading(TestCFreeThreading, TestCase): + def factory(self, loop, coro, context=None): + return asyncio.tasks._CTask(coro, loop=loop, context=context, eager_start=True) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 74db4c74af905a..b8b184af04a7cb 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -364,6 +364,8 @@ future_ensure_alive(FutureObj *fut) static int future_schedule_callbacks(asyncio_state *state, FutureObj *fut) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut); + if (fut->fut_callback0 != NULL) { /* There's a 1st callback */ @@ -481,6 +483,8 @@ future_init(FutureObj *fut, PyObject *loop) static PyObject * future_set_result(asyncio_state *state, FutureObj *fut, PyObject *res) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut); + if (future_ensure_alive(fut)) { return NULL; } @@ -503,6 +507,8 @@ future_set_result(asyncio_state *state, FutureObj *fut, PyObject *res) static PyObject * future_set_exception(asyncio_state *state, FutureObj *fut, PyObject *exc) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut); + PyObject *exc_val = NULL; if (fut->fut_state != STATE_PENDING) { @@ -569,6 +575,8 @@ future_set_exception(asyncio_state *state, FutureObj *fut, PyObject *exc) static PyObject * create_cancelled_error(asyncio_state *state, FutureObj *fut) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut); + PyObject *exc; if (fut->fut_cancelled_exc != NULL) { /* transfer ownership */ @@ -588,6 +596,8 @@ create_cancelled_error(asyncio_state *state, FutureObj *fut) static void future_set_cancelled_error(asyncio_state *state, FutureObj *fut) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut); + PyObject *exc = create_cancelled_error(state, fut); if (exc == NULL) { return; @@ -599,6 +609,8 @@ future_set_cancelled_error(asyncio_state *state, FutureObj *fut) static int future_get_result(asyncio_state *state, FutureObj *fut, PyObject **result) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut); + if (fut->fut_state == STATE_CANCELLED) { future_set_cancelled_error(state, fut); return -1; @@ -632,6 +644,8 @@ static PyObject * future_add_done_callback(asyncio_state *state, FutureObj *fut, PyObject *arg, PyObject *ctx) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut); + if (!future_is_alive(fut)) { PyErr_SetString(PyExc_RuntimeError, "uninitialized Future object"); return NULL; @@ -706,6 +720,8 @@ future_add_done_callback(asyncio_state *state, FutureObj *fut, PyObject *arg, static PyObject * future_cancel(asyncio_state *state, FutureObj *fut, PyObject *msg) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut); + fut->fut_log_tb = 0; if (fut->fut_state != STATE_PENDING) { @@ -787,6 +803,7 @@ FutureObj_traverse(FutureObj *fut, visitproc visit, void *arg) } /*[clinic input] +@critical_section _asyncio.Future.result Return the result this future represents. @@ -798,7 +815,7 @@ the future is done and has an exception set, this exception is raised. static PyObject * _asyncio_Future_result_impl(FutureObj *self) -/*[clinic end generated code: output=f35f940936a4b1e5 input=49ecf9cf5ec50dc5]*/ +/*[clinic end generated code: output=f35f940936a4b1e5 input=61d89f48e4c8b670]*/ { asyncio_state *state = get_asyncio_state_by_def((PyObject *)self); PyObject *result; @@ -827,6 +844,7 @@ _asyncio_Future_result_impl(FutureObj *self) } /*[clinic input] +@critical_section _asyncio.Future.exception cls: defining_class @@ -842,7 +860,7 @@ InvalidStateError. static PyObject * _asyncio_Future_exception_impl(FutureObj *self, PyTypeObject *cls) -/*[clinic end generated code: output=ce75576b187c905b input=3faf15c22acdb60d]*/ +/*[clinic end generated code: output=ce75576b187c905b input=647d1fd1fc403301]*/ { if (!future_is_alive(self)) { asyncio_state *state = get_asyncio_state_by_cls(cls); @@ -873,6 +891,7 @@ _asyncio_Future_exception_impl(FutureObj *self, PyTypeObject *cls) } /*[clinic input] +@critical_section _asyncio.Future.set_result cls: defining_class @@ -888,7 +907,7 @@ InvalidStateError. static PyObject * _asyncio_Future_set_result_impl(FutureObj *self, PyTypeObject *cls, PyObject *result) -/*[clinic end generated code: output=99afbbe78f99c32d input=d5a41c1e353acc2e]*/ +/*[clinic end generated code: output=99afbbe78f99c32d input=4069306f03a3b6ee]*/ { asyncio_state *state = get_asyncio_state_by_cls(cls); ENSURE_FUTURE_ALIVE(state, self) @@ -896,6 +915,7 @@ _asyncio_Future_set_result_impl(FutureObj *self, PyTypeObject *cls, } /*[clinic input] +@critical_section _asyncio.Future.set_exception cls: defining_class @@ -911,7 +931,7 @@ InvalidStateError. static PyObject * _asyncio_Future_set_exception_impl(FutureObj *self, PyTypeObject *cls, PyObject *exception) -/*[clinic end generated code: output=0a5e8b5a52f058d6 input=a245cd49d3df939b]*/ +/*[clinic end generated code: output=0a5e8b5a52f058d6 input=b6eab43a389bc966]*/ { asyncio_state *state = get_asyncio_state_by_cls(cls); ENSURE_FUTURE_ALIVE(state, self) @@ -919,6 +939,7 @@ _asyncio_Future_set_exception_impl(FutureObj *self, PyTypeObject *cls, } /*[clinic input] +@critical_section _asyncio.Future.add_done_callback cls: defining_class @@ -937,7 +958,7 @@ scheduled with call_soon. static PyObject * _asyncio_Future_add_done_callback_impl(FutureObj *self, PyTypeObject *cls, PyObject *fn, PyObject *context) -/*[clinic end generated code: output=922e9a4cbd601167 input=599261c521458cc2]*/ +/*[clinic end generated code: output=922e9a4cbd601167 input=37d97f941beb7b3e]*/ { asyncio_state *state = get_asyncio_state_by_cls(cls); if (context == NULL) { @@ -953,6 +974,7 @@ _asyncio_Future_add_done_callback_impl(FutureObj *self, PyTypeObject *cls, } /*[clinic input] +@critical_section _asyncio.Future.remove_done_callback cls: defining_class @@ -967,7 +989,7 @@ Returns the number of callbacks removed. static PyObject * _asyncio_Future_remove_done_callback_impl(FutureObj *self, PyTypeObject *cls, PyObject *fn) -/*[clinic end generated code: output=2da35ccabfe41b98 input=c7518709b86fc747]*/ +/*[clinic end generated code: output=2da35ccabfe41b98 input=3afbc9f6a673091b]*/ { PyObject *newlist; Py_ssize_t len, i, j=0; @@ -1076,6 +1098,7 @@ _asyncio_Future_remove_done_callback_impl(FutureObj *self, PyTypeObject *cls, } /*[clinic input] +@critical_section _asyncio.Future.cancel cls: defining_class @@ -1092,7 +1115,7 @@ return True. static PyObject * _asyncio_Future_cancel_impl(FutureObj *self, PyTypeObject *cls, PyObject *msg) -/*[clinic end generated code: output=074956f35904b034 input=bba8f8b786941a94]*/ +/*[clinic end generated code: output=074956f35904b034 input=44ab4003da839970]*/ { asyncio_state *state = get_asyncio_state_by_cls(cls); ENSURE_FUTURE_ALIVE(state, self) @@ -1100,6 +1123,7 @@ _asyncio_Future_cancel_impl(FutureObj *self, PyTypeObject *cls, } /*[clinic input] +@critical_section _asyncio.Future.cancelled Return True if the future was cancelled. @@ -1107,7 +1131,7 @@ Return True if the future was cancelled. static PyObject * _asyncio_Future_cancelled_impl(FutureObj *self) -/*[clinic end generated code: output=145197ced586357d input=943ab8b7b7b17e45]*/ +/*[clinic end generated code: output=145197ced586357d input=9b8644819a675416]*/ { if (future_is_alive(self) && self->fut_state == STATE_CANCELLED) { Py_RETURN_TRUE; @@ -1118,6 +1142,7 @@ _asyncio_Future_cancelled_impl(FutureObj *self) } /*[clinic input] +@critical_section _asyncio.Future.done Return True if the future is done. @@ -1128,7 +1153,7 @@ future was cancelled. static PyObject * _asyncio_Future_done_impl(FutureObj *self) -/*[clinic end generated code: output=244c5ac351145096 input=28d7b23fdb65d2ac]*/ +/*[clinic end generated code: output=244c5ac351145096 input=7204d3cc63bef7f3]*/ { if (!future_is_alive(self) || self->fut_state == STATE_PENDING) { Py_RETURN_FALSE; @@ -1139,6 +1164,7 @@ _asyncio_Future_done_impl(FutureObj *self) } /*[clinic input] +@critical_section _asyncio.Future.get_loop cls: defining_class @@ -1149,17 +1175,24 @@ Return the event loop the Future is bound to. static PyObject * _asyncio_Future_get_loop_impl(FutureObj *self, PyTypeObject *cls) -/*[clinic end generated code: output=f50ea6c374d9ee97 input=163c2c498b45a1f0]*/ +/*[clinic end generated code: output=f50ea6c374d9ee97 input=f3ce629bfd9f45c1]*/ { asyncio_state *state = get_asyncio_state_by_cls(cls); ENSURE_FUTURE_ALIVE(state, self) return Py_NewRef(self->fut_loop); } +/*[clinic input] +@critical_section +@getter +_asyncio.Future._asyncio_future_blocking +[clinic start generated code]*/ + static PyObject * -FutureObj_get_blocking(FutureObj *fut, void *Py_UNUSED(ignored)) +_asyncio_Future__asyncio_future_blocking_get_impl(FutureObj *self) +/*[clinic end generated code: output=a558a2c51e38823b input=58da92efc03b617d]*/ { - if (future_is_alive(fut) && fut->fut_blocking) { + if (future_is_alive(self) && self->fut_blocking) { Py_RETURN_TRUE; } else { @@ -1167,31 +1200,47 @@ FutureObj_get_blocking(FutureObj *fut, void *Py_UNUSED(ignored)) } } +/*[clinic input] +@critical_section +@setter +_asyncio.Future._asyncio_future_blocking +[clinic start generated code]*/ + static int -FutureObj_set_blocking(FutureObj *fut, PyObject *val, void *Py_UNUSED(ignored)) +_asyncio_Future__asyncio_future_blocking_set_impl(FutureObj *self, + PyObject *value) +/*[clinic end generated code: output=0686d1cb024a7453 input=3fd4a5f95df788b7]*/ + { - if (future_ensure_alive(fut)) { + if (future_ensure_alive(self)) { return -1; } - if (val == NULL) { + if (value == NULL) { PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); return -1; } - int is_true = PyObject_IsTrue(val); + int is_true = PyObject_IsTrue(value); if (is_true < 0) { return -1; } - fut->fut_blocking = is_true; + self->fut_blocking = is_true; return 0; } +/*[clinic input] +@critical_section +@getter +_asyncio.Future._log_traceback +[clinic start generated code]*/ + static PyObject * -FutureObj_get_log_traceback(FutureObj *fut, void *Py_UNUSED(ignored)) +_asyncio_Future__log_traceback_get_impl(FutureObj *self) +/*[clinic end generated code: output=2724433b238593c7 input=91e5144ea4117d8e]*/ { - asyncio_state *state = get_asyncio_state_by_def((PyObject *)fut); - ENSURE_FUTURE_ALIVE(state, fut) - if (fut->fut_log_tb) { + asyncio_state *state = get_asyncio_state_by_def((PyObject *)self); + ENSURE_FUTURE_ALIVE(state, self) + if (self->fut_log_tb) { Py_RETURN_TRUE; } else { @@ -1199,14 +1248,21 @@ FutureObj_get_log_traceback(FutureObj *fut, void *Py_UNUSED(ignored)) } } +/*[clinic input] +@critical_section +@setter +_asyncio.Future._log_traceback +[clinic start generated code]*/ + static int -FutureObj_set_log_traceback(FutureObj *fut, PyObject *val, void *Py_UNUSED(ignored)) +_asyncio_Future__log_traceback_set_impl(FutureObj *self, PyObject *value) +/*[clinic end generated code: output=9ce8e19504f42f54 input=30ac8217754b08c2]*/ { - if (val == NULL) { + if (value == NULL) { PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); return -1; } - int is_true = PyObject_IsTrue(val); + int is_true = PyObject_IsTrue(value); if (is_true < 0) { return -1; } @@ -1215,31 +1271,44 @@ FutureObj_set_log_traceback(FutureObj *fut, PyObject *val, void *Py_UNUSED(ignor "_log_traceback can only be set to False"); return -1; } - fut->fut_log_tb = is_true; + self->fut_log_tb = is_true; return 0; } +/*[clinic input] +@critical_section +@getter +_asyncio.Future._loop +[clinic start generated code]*/ static PyObject * -FutureObj_get_loop(FutureObj *fut, void *Py_UNUSED(ignored)) +_asyncio_Future__loop_get_impl(FutureObj *self) +/*[clinic end generated code: output=5ba31563eecfeedf input=0337130bc5781670]*/ { - if (!future_is_alive(fut)) { + if (!future_is_alive(self)) { Py_RETURN_NONE; } - return Py_NewRef(fut->fut_loop); + return Py_NewRef(self->fut_loop); } +/*[clinic input] +@critical_section +@getter +_asyncio.Future._callbacks +[clinic start generated code]*/ + static PyObject * -FutureObj_get_callbacks(FutureObj *fut, void *Py_UNUSED(ignored)) +_asyncio_Future__callbacks_get_impl(FutureObj *self) +/*[clinic end generated code: output=b40d360505fcc583 input=7a466649530c01bb]*/ { - asyncio_state *state = get_asyncio_state_by_def((PyObject *)fut); - ENSURE_FUTURE_ALIVE(state, fut) + asyncio_state *state = get_asyncio_state_by_def((PyObject *)self); + ENSURE_FUTURE_ALIVE(state, self) Py_ssize_t len = 0; - if (fut->fut_callback0 != NULL) { + if (self->fut_callback0 != NULL) { len++; } - if (fut->fut_callbacks != NULL) { - len += PyList_GET_SIZE(fut->fut_callbacks); + if (self->fut_callbacks != NULL) { + len += PyList_GET_SIZE(self->fut_callbacks); } if (len == 0) { @@ -1252,22 +1321,22 @@ FutureObj_get_callbacks(FutureObj *fut, void *Py_UNUSED(ignored)) } Py_ssize_t i = 0; - if (fut->fut_callback0 != NULL) { + if (self->fut_callback0 != NULL) { PyObject *tup0 = PyTuple_New(2); if (tup0 == NULL) { Py_DECREF(callbacks); return NULL; } - PyTuple_SET_ITEM(tup0, 0, Py_NewRef(fut->fut_callback0)); - assert(fut->fut_context0 != NULL); - PyTuple_SET_ITEM(tup0, 1, Py_NewRef(fut->fut_context0)); + PyTuple_SET_ITEM(tup0, 0, Py_NewRef(self->fut_callback0)); + assert(self->fut_context0 != NULL); + PyTuple_SET_ITEM(tup0, 1, Py_NewRef(self->fut_context0)); PyList_SET_ITEM(callbacks, i, tup0); i++; } - if (fut->fut_callbacks != NULL) { - for (Py_ssize_t j = 0; j < PyList_GET_SIZE(fut->fut_callbacks); j++) { - PyObject *cb = PyList_GET_ITEM(fut->fut_callbacks, j); + if (self->fut_callbacks != NULL) { + for (Py_ssize_t j = 0; j < PyList_GET_SIZE(self->fut_callbacks); j++) { + PyObject *cb = PyList_GET_ITEM(self->fut_callbacks, j); Py_INCREF(cb); PyList_SET_ITEM(callbacks, i, cb); i++; @@ -1277,68 +1346,110 @@ FutureObj_get_callbacks(FutureObj *fut, void *Py_UNUSED(ignored)) return callbacks; } +/*[clinic input] +@critical_section +@getter +_asyncio.Future._result +[clinic start generated code]*/ + static PyObject * -FutureObj_get_result(FutureObj *fut, void *Py_UNUSED(ignored)) +_asyncio_Future__result_get_impl(FutureObj *self) +/*[clinic end generated code: output=6877e8ce97333873 input=624f8e28e67f2636]*/ + { - asyncio_state *state = get_asyncio_state_by_def((PyObject *)fut); - ENSURE_FUTURE_ALIVE(state, fut) - if (fut->fut_result == NULL) { + asyncio_state *state = get_asyncio_state_by_def((PyObject *)self); + ENSURE_FUTURE_ALIVE(state, self) + if (self->fut_result == NULL) { Py_RETURN_NONE; } - return Py_NewRef(fut->fut_result); + return Py_NewRef(self->fut_result); } +/*[clinic input] +@critical_section +@getter +_asyncio.Future._exception +[clinic start generated code]*/ + static PyObject * -FutureObj_get_exception(FutureObj *fut, void *Py_UNUSED(ignored)) +_asyncio_Future__exception_get_impl(FutureObj *self) +/*[clinic end generated code: output=32f2c93b9e021a9b input=1828a1fcac929710]*/ { - asyncio_state *state = get_asyncio_state_by_def((PyObject *)fut); - ENSURE_FUTURE_ALIVE(state, fut) - if (fut->fut_exception == NULL) { + asyncio_state *state = get_asyncio_state_by_def((PyObject *)self); + ENSURE_FUTURE_ALIVE(state, self) + if (self->fut_exception == NULL) { Py_RETURN_NONE; } - return Py_NewRef(fut->fut_exception); + return Py_NewRef(self->fut_exception); } +/*[clinic input] +@critical_section +@getter +_asyncio.Future._source_traceback +[clinic start generated code]*/ + static PyObject * -FutureObj_get_source_traceback(FutureObj *fut, void *Py_UNUSED(ignored)) +_asyncio_Future__source_traceback_get_impl(FutureObj *self) +/*[clinic end generated code: output=d4f12b09af22f61b input=3c831fbde5da90d0]*/ { - if (!future_is_alive(fut) || fut->fut_source_tb == NULL) { + if (!future_is_alive(self) || self->fut_source_tb == NULL) { Py_RETURN_NONE; } - return Py_NewRef(fut->fut_source_tb); + return Py_NewRef(self->fut_source_tb); } +/*[clinic input] +@critical_section +@getter +_asyncio.Future._cancel_message +[clinic start generated code]*/ + static PyObject * -FutureObj_get_cancel_message(FutureObj *fut, void *Py_UNUSED(ignored)) +_asyncio_Future__cancel_message_get_impl(FutureObj *self) +/*[clinic end generated code: output=52ef6444f92cedac input=54c12c67082e4eea]*/ { - if (fut->fut_cancel_msg == NULL) { + if (self->fut_cancel_msg == NULL) { Py_RETURN_NONE; } - return Py_NewRef(fut->fut_cancel_msg); + return Py_NewRef(self->fut_cancel_msg); } +/*[clinic input] +@critical_section +@setter +_asyncio.Future._cancel_message +[clinic start generated code]*/ + static int -FutureObj_set_cancel_message(FutureObj *fut, PyObject *msg, - void *Py_UNUSED(ignored)) +_asyncio_Future__cancel_message_set_impl(FutureObj *self, PyObject *value) +/*[clinic end generated code: output=0854b2f77bff2209 input=f461d17f2d891fad]*/ { - if (msg == NULL) { + if (value == NULL) { PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); return -1; } - Py_INCREF(msg); - Py_XSETREF(fut->fut_cancel_msg, msg); + Py_INCREF(value); + Py_XSETREF(self->fut_cancel_msg, value); return 0; } +/*[clinic input] +@critical_section +@getter +_asyncio.Future._state +[clinic start generated code]*/ + static PyObject * -FutureObj_get_state(FutureObj *fut, void *Py_UNUSED(ignored)) +_asyncio_Future__state_get_impl(FutureObj *self) +/*[clinic end generated code: output=622f560a3fa69c63 input=7c5ad023a93423ff]*/ { - asyncio_state *state = get_asyncio_state_by_def((PyObject *)fut); + asyncio_state *state = get_asyncio_state_by_def((PyObject *)self); PyObject *ret = NULL; - ENSURE_FUTURE_ALIVE(state, fut) + ENSURE_FUTURE_ALIVE(state, self) - switch (fut->fut_state) { + switch (self->fut_state) { case STATE_PENDING: ret = &_Py_ID(PENDING); break; @@ -1364,6 +1475,7 @@ FutureObj_repr(FutureObj *fut) } /*[clinic input] +@critical_section _asyncio.Future._make_cancelled_error Create the CancelledError to raise if the Future is cancelled. @@ -1374,7 +1486,7 @@ it erases the context exception value. static PyObject * _asyncio_Future__make_cancelled_error_impl(FutureObj *self) -/*[clinic end generated code: output=a5df276f6c1213de input=ac6effe4ba795ecc]*/ +/*[clinic end generated code: output=a5df276f6c1213de input=ccb90df8c3c18bcd]*/ { asyncio_state *state = get_asyncio_state_by_def((PyObject *)self); return create_cancelled_error(state, self); @@ -1455,23 +1567,16 @@ static PyMethodDef FutureType_methods[] = { {NULL, NULL} /* Sentinel */ }; -#define FUTURE_COMMON_GETSETLIST \ - {"_state", (getter)FutureObj_get_state, NULL, NULL}, \ - {"_asyncio_future_blocking", (getter)FutureObj_get_blocking, \ - (setter)FutureObj_set_blocking, NULL}, \ - {"_loop", (getter)FutureObj_get_loop, NULL, NULL}, \ - {"_callbacks", (getter)FutureObj_get_callbacks, NULL, NULL}, \ - {"_result", (getter)FutureObj_get_result, NULL, NULL}, \ - {"_exception", (getter)FutureObj_get_exception, NULL, NULL}, \ - {"_log_traceback", (getter)FutureObj_get_log_traceback, \ - (setter)FutureObj_set_log_traceback, NULL}, \ - {"_source_traceback", (getter)FutureObj_get_source_traceback, \ - NULL, NULL}, \ - {"_cancel_message", (getter)FutureObj_get_cancel_message, \ - (setter)FutureObj_set_cancel_message, NULL}, - static PyGetSetDef FutureType_getsetlist[] = { - FUTURE_COMMON_GETSETLIST + _ASYNCIO_FUTURE__STATE_GETSETDEF + _ASYNCIO_FUTURE__ASYNCIO_FUTURE_BLOCKING_GETSETDEF + _ASYNCIO_FUTURE__LOOP_GETSETDEF + _ASYNCIO_FUTURE__CALLBACKS_GETSETDEF + _ASYNCIO_FUTURE__RESULT_GETSETDEF + _ASYNCIO_FUTURE__EXCEPTION_GETSETDEF + _ASYNCIO_FUTURE__LOG_TRACEBACK_GETSETDEF + _ASYNCIO_FUTURE__SOURCE_TRACEBACK_GETSETDEF + _ASYNCIO_FUTURE__CANCEL_MESSAGE_GETSETDEF {NULL} /* Sentinel */ }; @@ -1550,19 +1655,13 @@ FutureIter_dealloc(futureiterobject *it) } static PySendResult -FutureIter_am_send(futureiterobject *it, - PyObject *Py_UNUSED(arg), - PyObject **result) +FutureIter_am_send_lock_held(futureiterobject *it, PyObject **result) { - /* arg is unused, see the comment on FutureIter_send for clarification */ - PyObject *res; FutureObj *fut = it->future; + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut); *result = NULL; - if (fut == NULL) { - return PYGEN_ERROR; - } if (fut->fut_state == STATE_PENDING) { if (!fut->fut_blocking) { @@ -1575,18 +1674,29 @@ FutureIter_am_send(futureiterobject *it, return PYGEN_ERROR; } - it->future = NULL; res = _asyncio_Future_result_impl(fut); if (res != NULL) { - Py_DECREF(fut); *result = res; return PYGEN_RETURN; } - Py_DECREF(fut); return PYGEN_ERROR; } +static PySendResult +FutureIter_am_send(futureiterobject *it, + PyObject *Py_UNUSED(arg), + PyObject **result) +{ + /* arg is unused, see the comment on FutureIter_send for clarification */ + PySendResult res; + Py_BEGIN_CRITICAL_SECTION(it->future); + res = FutureIter_am_send_lock_held(it, result); + Py_END_CRITICAL_SECTION(); + return res; +} + + static PyObject * FutureIter_iternext(futureiterobject *it) { @@ -1807,7 +1917,11 @@ TaskStepMethWrapper_call(TaskStepMethWrapper *o, return NULL; } asyncio_state *state = get_asyncio_state_by_def((PyObject *)o); - return task_step(state, o->sw_task, o->sw_arg); + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(o->sw_task); + res = task_step(state, o->sw_task, o->sw_arg); + Py_END_CRITICAL_SECTION(); + return res; } static int @@ -2150,10 +2264,17 @@ TaskObj_traverse(TaskObj *task, visitproc visit, void *arg) return 0; } +/*[clinic input] +@critical_section +@getter +_asyncio.Task._log_destroy_pending +[clinic start generated code]*/ + static PyObject * -TaskObj_get_log_destroy_pending(TaskObj *task, void *Py_UNUSED(ignored)) +_asyncio_Task__log_destroy_pending_get_impl(TaskObj *self) +/*[clinic end generated code: output=e6c2a47d029ac93b input=17127298cd4c720b]*/ { - if (task->task_log_destroy_pending) { + if (self->task_log_destroy_pending) { Py_RETURN_TRUE; } else { @@ -2161,25 +2282,40 @@ TaskObj_get_log_destroy_pending(TaskObj *task, void *Py_UNUSED(ignored)) } } +/*[clinic input] +@critical_section +@setter +_asyncio.Task._log_destroy_pending +[clinic start generated code]*/ + static int -TaskObj_set_log_destroy_pending(TaskObj *task, PyObject *val, void *Py_UNUSED(ignored)) +_asyncio_Task__log_destroy_pending_set_impl(TaskObj *self, PyObject *value) +/*[clinic end generated code: output=7ebc030bb92ec5ce input=49b759c97d1216a4]*/ { - if (val == NULL) { + if (value == NULL) { PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); return -1; } - int is_true = PyObject_IsTrue(val); + int is_true = PyObject_IsTrue(value); if (is_true < 0) { return -1; } - task->task_log_destroy_pending = is_true; + self->task_log_destroy_pending = is_true; return 0; } + +/*[clinic input] +@critical_section +@getter +_asyncio.Task._must_cancel +[clinic start generated code]*/ + static PyObject * -TaskObj_get_must_cancel(TaskObj *task, void *Py_UNUSED(ignored)) +_asyncio_Task__must_cancel_get_impl(TaskObj *self) +/*[clinic end generated code: output=70e79b900996c363 input=2d04529fb23feedf]*/ { - if (task->task_must_cancel) { + if (self->task_must_cancel) { Py_RETURN_TRUE; } else { @@ -2187,21 +2323,36 @@ TaskObj_get_must_cancel(TaskObj *task, void *Py_UNUSED(ignored)) } } +/*[clinic input] +@critical_section +@getter +_asyncio.Task._coro +[clinic start generated code]*/ + static PyObject * -TaskObj_get_coro(TaskObj *task, void *Py_UNUSED(ignored)) +_asyncio_Task__coro_get_impl(TaskObj *self) +/*[clinic end generated code: output=a2726012ab5fd531 input=323c31a272020624]*/ { - if (task->task_coro) { - return Py_NewRef(task->task_coro); + if (self->task_coro) { + return Py_NewRef(self->task_coro); } Py_RETURN_NONE; } + +/*[clinic input] +@critical_section +@getter +_asyncio.Task._fut_waiter +[clinic start generated code]*/ + static PyObject * -TaskObj_get_fut_waiter(TaskObj *task, void *Py_UNUSED(ignored)) +_asyncio_Task__fut_waiter_get_impl(TaskObj *self) +/*[clinic end generated code: output=c4f966b847fefcdf input=4d1005d725e72db7]*/ { - if (task->task_fut_waiter) { - return Py_NewRef(task->task_fut_waiter); + if (self->task_fut_waiter) { + return Py_NewRef(self->task_fut_waiter); } Py_RETURN_NONE; @@ -2217,6 +2368,7 @@ TaskObj_repr(TaskObj *task) /*[clinic input] +@critical_section _asyncio.Task._make_cancelled_error Create the CancelledError to raise if the Task is cancelled. @@ -2227,7 +2379,7 @@ it erases the context exception value. static PyObject * _asyncio_Task__make_cancelled_error_impl(TaskObj *self) -/*[clinic end generated code: output=55a819e8b4276fab input=52c0e32de8e2f840]*/ +/*[clinic end generated code: output=55a819e8b4276fab input=2d3213be0cb02390]*/ { FutureObj *fut = (FutureObj*)self; return _asyncio_Future__make_cancelled_error_impl(fut); @@ -2235,6 +2387,7 @@ _asyncio_Task__make_cancelled_error_impl(TaskObj *self) /*[clinic input] +@critical_section _asyncio.Task.cancel msg: object = None @@ -2263,7 +2416,7 @@ This also increases the task's count of cancellation requests. static PyObject * _asyncio_Task_cancel_impl(TaskObj *self, PyObject *msg) -/*[clinic end generated code: output=c66b60d41c74f9f1 input=7bb51bf25974c783]*/ +/*[clinic end generated code: output=c66b60d41c74f9f1 input=6125d45b9a6a5abd]*/ { self->task_log_tb = 0; @@ -2308,6 +2461,7 @@ _asyncio_Task_cancel_impl(TaskObj *self, PyObject *msg) } /*[clinic input] +@critical_section _asyncio.Task.cancelling Return the count of the task's cancellation requests. @@ -2318,13 +2472,14 @@ and may be decremented using .uncancel(). static PyObject * _asyncio_Task_cancelling_impl(TaskObj *self) -/*[clinic end generated code: output=803b3af96f917d7e input=b625224d310cbb17]*/ +/*[clinic end generated code: output=803b3af96f917d7e input=5ef89b1b38f080ee]*/ /*[clinic end generated code]*/ { return PyLong_FromLong(self->task_num_cancels_requested); } /*[clinic input] +@critical_section _asyncio.Task.uncancel Decrement the task's count of cancellation requests. @@ -2337,7 +2492,7 @@ Returns the remaining number of cancellation requests. static PyObject * _asyncio_Task_uncancel_impl(TaskObj *self) -/*[clinic end generated code: output=58184d236a817d3c input=68f81a4b90b46be2]*/ +/*[clinic end generated code: output=58184d236a817d3c input=cb3220b0e5afd61d]*/ /*[clinic end generated code]*/ { if (self->task_num_cancels_requested > 0) { @@ -2451,12 +2606,13 @@ _asyncio_Task_set_exception(TaskObj *self, PyObject *exception) } /*[clinic input] +@critical_section _asyncio.Task.get_coro [clinic start generated code]*/ static PyObject * _asyncio_Task_get_coro_impl(TaskObj *self) -/*[clinic end generated code: output=bcac27c8cc6c8073 input=d2e8606c42a7b403]*/ +/*[clinic end generated code: output=bcac27c8cc6c8073 input=a47f81427e39fe0c]*/ { if (self->task_coro) { return Py_NewRef(self->task_coro); @@ -2477,12 +2633,13 @@ _asyncio_Task_get_context_impl(TaskObj *self) } /*[clinic input] +@critical_section _asyncio.Task.get_name [clinic start generated code]*/ static PyObject * _asyncio_Task_get_name_impl(TaskObj *self) -/*[clinic end generated code: output=0ecf1570c3b37a8f input=a4a6595d12f4f0f8]*/ +/*[clinic end generated code: output=0ecf1570c3b37a8f input=92a8f30c85034249]*/ { if (self->task_name) { if (PyLong_CheckExact(self->task_name)) { @@ -2499,6 +2656,7 @@ _asyncio_Task_get_name_impl(TaskObj *self) } /*[clinic input] +@critical_section _asyncio.Task.set_name value: object @@ -2506,8 +2664,8 @@ _asyncio.Task.set_name [clinic start generated code]*/ static PyObject * -_asyncio_Task_set_name(TaskObj *self, PyObject *value) -/*[clinic end generated code: output=138a8d51e32057d6 input=a8359b6e65f8fd31]*/ +_asyncio_Task_set_name_impl(TaskObj *self, PyObject *value) +/*[clinic end generated code: output=f88ff4c0d64a9a6f input=e8d400ad64bad799]*/ { if (!PyUnicode_CheckExact(value)) { value = PyObject_Str(value); @@ -2618,12 +2776,10 @@ static PyMethodDef TaskType_methods[] = { }; static PyGetSetDef TaskType_getsetlist[] = { - FUTURE_COMMON_GETSETLIST - {"_log_destroy_pending", (getter)TaskObj_get_log_destroy_pending, - (setter)TaskObj_set_log_destroy_pending, NULL}, - {"_must_cancel", (getter)TaskObj_get_must_cancel, NULL, NULL}, - {"_coro", (getter)TaskObj_get_coro, NULL, NULL}, - {"_fut_waiter", (getter)TaskObj_get_fut_waiter, NULL, NULL}, + _ASYNCIO_TASK__LOG_DESTROY_PENDING_GETSETDEF + _ASYNCIO_TASK__MUST_CANCEL_GETSETDEF + _ASYNCIO_TASK__CORO_GETSETDEF + _ASYNCIO_TASK__FUT_WAITER_GETSETDEF {NULL} /* Sentinel */ }; @@ -2738,6 +2894,8 @@ gen_status_from_result(PyObject **result) static PyObject * task_step_impl(asyncio_state *state, TaskObj *task, PyObject *exc) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(task); + int clear_exc = 0; PyObject *result = NULL; PyObject *coro; @@ -2867,6 +3025,8 @@ task_step_impl(asyncio_state *state, TaskObj *task, PyObject *exc) static PyObject * task_step_handle_result_impl(asyncio_state *state, TaskObj *task, PyObject *result) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(task); + int res; PyObject *o; @@ -2897,8 +3057,10 @@ task_step_handle_result_impl(asyncio_state *state, TaskObj *task, PyObject *resu if (wrapper == NULL) { goto fail; } + Py_BEGIN_CRITICAL_SECTION(result); tmp = future_add_done_callback(state, (FutureObj*)result, wrapper, task->task_context); + Py_END_CRITICAL_SECTION(); Py_DECREF(wrapper); if (tmp == NULL) { goto fail; @@ -3140,7 +3302,10 @@ task_eager_start(asyncio_state *state, TaskObj *task) int retval = 0; - PyObject *stepres = task_step_impl(state, task, NULL); + PyObject *stepres; + Py_BEGIN_CRITICAL_SECTION(task); + stepres = task_step_impl(state, task, NULL); + Py_END_CRITICAL_SECTION(); if (stepres == NULL) { PyObject *exc = PyErr_GetRaisedException(); _PyErr_ChainExceptions1(exc); @@ -3177,16 +3342,20 @@ task_eager_start(asyncio_state *state, TaskObj *task) } static PyObject * -task_wakeup(TaskObj *task, PyObject *o) +task_wakeup_lock_held(TaskObj *task, PyObject *o) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(task); + PyObject *result; assert(o); asyncio_state *state = get_asyncio_state_by_def((PyObject *)task); if (Future_CheckExact(state, o) || Task_CheckExact(state, o)) { PyObject *fut_result = NULL; - int res = future_get_result(state, (FutureObj*)o, &fut_result); - + int res; + Py_BEGIN_CRITICAL_SECTION(o); + res = future_get_result(state, (FutureObj*)o, &fut_result); + Py_END_CRITICAL_SECTION(); switch(res) { case -1: assert(fut_result == NULL); @@ -3220,6 +3389,16 @@ task_wakeup(TaskObj *task, PyObject *o) return result; } +static PyObject * +task_wakeup(TaskObj *task, PyObject *o) +{ + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(task); + res = task_wakeup_lock_held(task, o); + Py_END_CRITICAL_SECTION(); + return res; +} + /*********************** Functions **************************/ @@ -3564,62 +3743,41 @@ static PyObject * _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) /*[clinic end generated code: output=0e107cbb7f72aa7b input=43a1b423c2d95bfa]*/ { - asyncio_state *state = get_asyncio_state(module); - PyObject *tasks = PySet_New(NULL); - if (tasks == NULL) { - return NULL; - } if (loop == Py_None) { loop = _asyncio_get_running_loop_impl(module); if (loop == NULL) { - Py_DECREF(tasks); return NULL; } } else { Py_INCREF(loop); } - // First add eager tasks to the set so that we don't miss + // First add eager tasks to the list so that we don't miss // any tasks which graduates from eager to non-eager - PyObject *eager_iter = PyObject_GetIter(state->eager_tasks); - if (eager_iter == NULL) { - Py_DECREF(tasks); + // We first add all the tasks to `tasks` list and then filter + // out the tasks which are done and return it as a set. + PyObject *tasks = PyList_New(0); + if (tasks == NULL) { Py_DECREF(loop); return NULL; } - PyObject *item; - while ((item = PyIter_Next(eager_iter)) != NULL) { - if (add_one_task(state, tasks, item, loop) < 0) { - Py_DECREF(tasks); - Py_DECREF(loop); - Py_DECREF(item); - Py_DECREF(eager_iter); - return NULL; - } - Py_DECREF(item); - } - Py_DECREF(eager_iter); - - if (PyErr_Occurred()) { + if (PyList_Extend(tasks, state->eager_tasks) < 0) { Py_DECREF(tasks); Py_DECREF(loop); return NULL; } - int err = 0; ASYNCIO_STATE_LOCK(state); struct llist_node *node; + llist_for_each_safe(node, &state->asyncio_tasks_head) { TaskObj *task = llist_data(node, TaskObj, task_node); - Py_INCREF(task); - if (add_one_task(state, tasks, (PyObject *)task, loop) < 0) { - Py_DECREF(task); + if (PyList_Append(tasks, (PyObject *)task) < 0) { Py_DECREF(tasks); Py_DECREF(loop); err = 1; break; } - Py_DECREF(task); } ASYNCIO_STATE_UNLOCK(state); if (err) { @@ -3631,8 +3789,9 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) Py_DECREF(loop); return NULL; } + PyObject *item; while ((item = PyIter_Next(scheduled_iter)) != NULL) { - if (add_one_task(state, tasks, item, loop) < 0) { + if (PyList_Append(tasks, item) < 0) { Py_DECREF(tasks); Py_DECREF(loop); Py_DECREF(item); @@ -3642,14 +3801,27 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) Py_DECREF(item); } Py_DECREF(scheduled_iter); - Py_DECREF(loop); - - if (PyErr_Occurred()) { + // All the tasks are now in the list, now filter the tasks which are done + PyObject *res = PySet_New(NULL); + if (res == NULL) { Py_DECREF(tasks); + Py_DECREF(loop); return NULL; } - return tasks; + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(tasks); i++) { + PyObject *task = PyList_GET_ITEM(tasks, i); + if (add_one_task(state, res, task, loop) < 0) { + Py_DECREF(res); + Py_DECREF(tasks); + Py_DECREF(loop); + return NULL; + } + } + + Py_DECREF(tasks); + Py_DECREF(loop); + return res; } static int diff --git a/Modules/clinic/_asynciomodule.c.h b/Modules/clinic/_asynciomodule.c.h index 32045804c35004..3a37cdd9b5fa83 100644 --- a/Modules/clinic/_asynciomodule.c.h +++ b/Modules/clinic/_asynciomodule.c.h @@ -6,6 +6,7 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(_asyncio_Future___init____doc__, @@ -98,7 +99,13 @@ _asyncio_Future_result_impl(FutureObj *self); static PyObject * _asyncio_Future_result(FutureObj *self, PyObject *Py_UNUSED(ignored)) { - return _asyncio_Future_result_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future_result_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_asyncio_Future_exception__doc__, @@ -121,11 +128,18 @@ _asyncio_Future_exception_impl(FutureObj *self, PyTypeObject *cls); static PyObject * _asyncio_Future_exception(FutureObj *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { + PyObject *return_value = NULL; + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { PyErr_SetString(PyExc_TypeError, "exception() takes no arguments"); - return NULL; + goto exit; } - return _asyncio_Future_exception_impl(self, cls); + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future_exception_impl(self, cls); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; } PyDoc_STRVAR(_asyncio_Future_set_result__doc__, @@ -170,7 +184,9 @@ _asyncio_Future_set_result(FutureObj *self, PyTypeObject *cls, PyObject *const * goto exit; } result = args[0]; + Py_BEGIN_CRITICAL_SECTION(self); return_value = _asyncio_Future_set_result_impl(self, cls, result); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -218,7 +234,9 @@ _asyncio_Future_set_exception(FutureObj *self, PyTypeObject *cls, PyObject *cons goto exit; } exception = args[0]; + Py_BEGIN_CRITICAL_SECTION(self); return_value = _asyncio_Future_set_exception_impl(self, cls, exception); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -286,7 +304,9 @@ _asyncio_Future_add_done_callback(FutureObj *self, PyTypeObject *cls, PyObject * } context = args[1]; skip_optional_kwonly: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _asyncio_Future_add_done_callback_impl(self, cls, fn, context); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -333,7 +353,9 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyTypeObject *cls, PyObjec goto exit; } fn = args[0]; + Py_BEGIN_CRITICAL_SECTION(self); return_value = _asyncio_Future_remove_done_callback_impl(self, cls, fn); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -399,7 +421,9 @@ _asyncio_Future_cancel(FutureObj *self, PyTypeObject *cls, PyObject *const *args } msg = args[0]; skip_optional_pos: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _asyncio_Future_cancel_impl(self, cls, msg); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -420,7 +444,13 @@ _asyncio_Future_cancelled_impl(FutureObj *self); static PyObject * _asyncio_Future_cancelled(FutureObj *self, PyObject *Py_UNUSED(ignored)) { - return _asyncio_Future_cancelled_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future_cancelled_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_asyncio_Future_done__doc__, @@ -441,7 +471,13 @@ _asyncio_Future_done_impl(FutureObj *self); static PyObject * _asyncio_Future_done(FutureObj *self, PyObject *Py_UNUSED(ignored)) { - return _asyncio_Future_done_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future_done_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_asyncio_Future_get_loop__doc__, @@ -459,11 +495,319 @@ _asyncio_Future_get_loop_impl(FutureObj *self, PyTypeObject *cls); static PyObject * _asyncio_Future_get_loop(FutureObj *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { + PyObject *return_value = NULL; + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { PyErr_SetString(PyExc_TypeError, "get_loop() takes no arguments"); - return NULL; + goto exit; } - return _asyncio_Future_get_loop_impl(self, cls); + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future_get_loop_impl(self, cls); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +#if !defined(_asyncio_Future__asyncio_future_blocking_DOCSTR) +# define _asyncio_Future__asyncio_future_blocking_DOCSTR NULL +#endif +#if defined(_ASYNCIO_FUTURE__ASYNCIO_FUTURE_BLOCKING_GETSETDEF) +# undef _ASYNCIO_FUTURE__ASYNCIO_FUTURE_BLOCKING_GETSETDEF +# define _ASYNCIO_FUTURE__ASYNCIO_FUTURE_BLOCKING_GETSETDEF {"_asyncio_future_blocking", (getter)_asyncio_Future__asyncio_future_blocking_get, (setter)_asyncio_Future__asyncio_future_blocking_set, _asyncio_Future__asyncio_future_blocking_DOCSTR}, +#else +# define _ASYNCIO_FUTURE__ASYNCIO_FUTURE_BLOCKING_GETSETDEF {"_asyncio_future_blocking", (getter)_asyncio_Future__asyncio_future_blocking_get, NULL, _asyncio_Future__asyncio_future_blocking_DOCSTR}, +#endif + +static PyObject * +_asyncio_Future__asyncio_future_blocking_get_impl(FutureObj *self); + +static PyObject * +_asyncio_Future__asyncio_future_blocking_get(FutureObj *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future__asyncio_future_blocking_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_asyncio_Future__asyncio_future_blocking_DOCSTR) +# define _asyncio_Future__asyncio_future_blocking_DOCSTR NULL +#endif +#if defined(_ASYNCIO_FUTURE__ASYNCIO_FUTURE_BLOCKING_GETSETDEF) +# undef _ASYNCIO_FUTURE__ASYNCIO_FUTURE_BLOCKING_GETSETDEF +# define _ASYNCIO_FUTURE__ASYNCIO_FUTURE_BLOCKING_GETSETDEF {"_asyncio_future_blocking", (getter)_asyncio_Future__asyncio_future_blocking_get, (setter)_asyncio_Future__asyncio_future_blocking_set, _asyncio_Future__asyncio_future_blocking_DOCSTR}, +#else +# define _ASYNCIO_FUTURE__ASYNCIO_FUTURE_BLOCKING_GETSETDEF {"_asyncio_future_blocking", NULL, (setter)_asyncio_Future__asyncio_future_blocking_set, NULL}, +#endif + +static int +_asyncio_Future__asyncio_future_blocking_set_impl(FutureObj *self, + PyObject *value); + +static int +_asyncio_Future__asyncio_future_blocking_set(FutureObj *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future__asyncio_future_blocking_set_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_asyncio_Future__log_traceback_DOCSTR) +# define _asyncio_Future__log_traceback_DOCSTR NULL +#endif +#if defined(_ASYNCIO_FUTURE__LOG_TRACEBACK_GETSETDEF) +# undef _ASYNCIO_FUTURE__LOG_TRACEBACK_GETSETDEF +# define _ASYNCIO_FUTURE__LOG_TRACEBACK_GETSETDEF {"_log_traceback", (getter)_asyncio_Future__log_traceback_get, (setter)_asyncio_Future__log_traceback_set, _asyncio_Future__log_traceback_DOCSTR}, +#else +# define _ASYNCIO_FUTURE__LOG_TRACEBACK_GETSETDEF {"_log_traceback", (getter)_asyncio_Future__log_traceback_get, NULL, _asyncio_Future__log_traceback_DOCSTR}, +#endif + +static PyObject * +_asyncio_Future__log_traceback_get_impl(FutureObj *self); + +static PyObject * +_asyncio_Future__log_traceback_get(FutureObj *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future__log_traceback_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_asyncio_Future__log_traceback_DOCSTR) +# define _asyncio_Future__log_traceback_DOCSTR NULL +#endif +#if defined(_ASYNCIO_FUTURE__LOG_TRACEBACK_GETSETDEF) +# undef _ASYNCIO_FUTURE__LOG_TRACEBACK_GETSETDEF +# define _ASYNCIO_FUTURE__LOG_TRACEBACK_GETSETDEF {"_log_traceback", (getter)_asyncio_Future__log_traceback_get, (setter)_asyncio_Future__log_traceback_set, _asyncio_Future__log_traceback_DOCSTR}, +#else +# define _ASYNCIO_FUTURE__LOG_TRACEBACK_GETSETDEF {"_log_traceback", NULL, (setter)_asyncio_Future__log_traceback_set, NULL}, +#endif + +static int +_asyncio_Future__log_traceback_set_impl(FutureObj *self, PyObject *value); + +static int +_asyncio_Future__log_traceback_set(FutureObj *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future__log_traceback_set_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_asyncio_Future__loop_DOCSTR) +# define _asyncio_Future__loop_DOCSTR NULL +#endif +#if defined(_ASYNCIO_FUTURE__LOOP_GETSETDEF) +# undef _ASYNCIO_FUTURE__LOOP_GETSETDEF +# define _ASYNCIO_FUTURE__LOOP_GETSETDEF {"_loop", (getter)_asyncio_Future__loop_get, (setter)_asyncio_Future__loop_set, _asyncio_Future__loop_DOCSTR}, +#else +# define _ASYNCIO_FUTURE__LOOP_GETSETDEF {"_loop", (getter)_asyncio_Future__loop_get, NULL, _asyncio_Future__loop_DOCSTR}, +#endif + +static PyObject * +_asyncio_Future__loop_get_impl(FutureObj *self); + +static PyObject * +_asyncio_Future__loop_get(FutureObj *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future__loop_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_asyncio_Future__callbacks_DOCSTR) +# define _asyncio_Future__callbacks_DOCSTR NULL +#endif +#if defined(_ASYNCIO_FUTURE__CALLBACKS_GETSETDEF) +# undef _ASYNCIO_FUTURE__CALLBACKS_GETSETDEF +# define _ASYNCIO_FUTURE__CALLBACKS_GETSETDEF {"_callbacks", (getter)_asyncio_Future__callbacks_get, (setter)_asyncio_Future__callbacks_set, _asyncio_Future__callbacks_DOCSTR}, +#else +# define _ASYNCIO_FUTURE__CALLBACKS_GETSETDEF {"_callbacks", (getter)_asyncio_Future__callbacks_get, NULL, _asyncio_Future__callbacks_DOCSTR}, +#endif + +static PyObject * +_asyncio_Future__callbacks_get_impl(FutureObj *self); + +static PyObject * +_asyncio_Future__callbacks_get(FutureObj *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future__callbacks_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_asyncio_Future__result_DOCSTR) +# define _asyncio_Future__result_DOCSTR NULL +#endif +#if defined(_ASYNCIO_FUTURE__RESULT_GETSETDEF) +# undef _ASYNCIO_FUTURE__RESULT_GETSETDEF +# define _ASYNCIO_FUTURE__RESULT_GETSETDEF {"_result", (getter)_asyncio_Future__result_get, (setter)_asyncio_Future__result_set, _asyncio_Future__result_DOCSTR}, +#else +# define _ASYNCIO_FUTURE__RESULT_GETSETDEF {"_result", (getter)_asyncio_Future__result_get, NULL, _asyncio_Future__result_DOCSTR}, +#endif + +static PyObject * +_asyncio_Future__result_get_impl(FutureObj *self); + +static PyObject * +_asyncio_Future__result_get(FutureObj *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future__result_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_asyncio_Future__exception_DOCSTR) +# define _asyncio_Future__exception_DOCSTR NULL +#endif +#if defined(_ASYNCIO_FUTURE__EXCEPTION_GETSETDEF) +# undef _ASYNCIO_FUTURE__EXCEPTION_GETSETDEF +# define _ASYNCIO_FUTURE__EXCEPTION_GETSETDEF {"_exception", (getter)_asyncio_Future__exception_get, (setter)_asyncio_Future__exception_set, _asyncio_Future__exception_DOCSTR}, +#else +# define _ASYNCIO_FUTURE__EXCEPTION_GETSETDEF {"_exception", (getter)_asyncio_Future__exception_get, NULL, _asyncio_Future__exception_DOCSTR}, +#endif + +static PyObject * +_asyncio_Future__exception_get_impl(FutureObj *self); + +static PyObject * +_asyncio_Future__exception_get(FutureObj *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future__exception_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_asyncio_Future__source_traceback_DOCSTR) +# define _asyncio_Future__source_traceback_DOCSTR NULL +#endif +#if defined(_ASYNCIO_FUTURE__SOURCE_TRACEBACK_GETSETDEF) +# undef _ASYNCIO_FUTURE__SOURCE_TRACEBACK_GETSETDEF +# define _ASYNCIO_FUTURE__SOURCE_TRACEBACK_GETSETDEF {"_source_traceback", (getter)_asyncio_Future__source_traceback_get, (setter)_asyncio_Future__source_traceback_set, _asyncio_Future__source_traceback_DOCSTR}, +#else +# define _ASYNCIO_FUTURE__SOURCE_TRACEBACK_GETSETDEF {"_source_traceback", (getter)_asyncio_Future__source_traceback_get, NULL, _asyncio_Future__source_traceback_DOCSTR}, +#endif + +static PyObject * +_asyncio_Future__source_traceback_get_impl(FutureObj *self); + +static PyObject * +_asyncio_Future__source_traceback_get(FutureObj *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future__source_traceback_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_asyncio_Future__cancel_message_DOCSTR) +# define _asyncio_Future__cancel_message_DOCSTR NULL +#endif +#if defined(_ASYNCIO_FUTURE__CANCEL_MESSAGE_GETSETDEF) +# undef _ASYNCIO_FUTURE__CANCEL_MESSAGE_GETSETDEF +# define _ASYNCIO_FUTURE__CANCEL_MESSAGE_GETSETDEF {"_cancel_message", (getter)_asyncio_Future__cancel_message_get, (setter)_asyncio_Future__cancel_message_set, _asyncio_Future__cancel_message_DOCSTR}, +#else +# define _ASYNCIO_FUTURE__CANCEL_MESSAGE_GETSETDEF {"_cancel_message", (getter)_asyncio_Future__cancel_message_get, NULL, _asyncio_Future__cancel_message_DOCSTR}, +#endif + +static PyObject * +_asyncio_Future__cancel_message_get_impl(FutureObj *self); + +static PyObject * +_asyncio_Future__cancel_message_get(FutureObj *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future__cancel_message_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_asyncio_Future__cancel_message_DOCSTR) +# define _asyncio_Future__cancel_message_DOCSTR NULL +#endif +#if defined(_ASYNCIO_FUTURE__CANCEL_MESSAGE_GETSETDEF) +# undef _ASYNCIO_FUTURE__CANCEL_MESSAGE_GETSETDEF +# define _ASYNCIO_FUTURE__CANCEL_MESSAGE_GETSETDEF {"_cancel_message", (getter)_asyncio_Future__cancel_message_get, (setter)_asyncio_Future__cancel_message_set, _asyncio_Future__cancel_message_DOCSTR}, +#else +# define _ASYNCIO_FUTURE__CANCEL_MESSAGE_GETSETDEF {"_cancel_message", NULL, (setter)_asyncio_Future__cancel_message_set, NULL}, +#endif + +static int +_asyncio_Future__cancel_message_set_impl(FutureObj *self, PyObject *value); + +static int +_asyncio_Future__cancel_message_set(FutureObj *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future__cancel_message_set_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_asyncio_Future__state_DOCSTR) +# define _asyncio_Future__state_DOCSTR NULL +#endif +#if defined(_ASYNCIO_FUTURE__STATE_GETSETDEF) +# undef _ASYNCIO_FUTURE__STATE_GETSETDEF +# define _ASYNCIO_FUTURE__STATE_GETSETDEF {"_state", (getter)_asyncio_Future__state_get, (setter)_asyncio_Future__state_set, _asyncio_Future__state_DOCSTR}, +#else +# define _ASYNCIO_FUTURE__STATE_GETSETDEF {"_state", (getter)_asyncio_Future__state_get, NULL, _asyncio_Future__state_DOCSTR}, +#endif + +static PyObject * +_asyncio_Future__state_get_impl(FutureObj *self); + +static PyObject * +_asyncio_Future__state_get(FutureObj *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future__state_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_asyncio_Future__make_cancelled_error__doc__, @@ -484,7 +828,13 @@ _asyncio_Future__make_cancelled_error_impl(FutureObj *self); static PyObject * _asyncio_Future__make_cancelled_error(FutureObj *self, PyObject *Py_UNUSED(ignored)) { - return _asyncio_Future__make_cancelled_error_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future__make_cancelled_error_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_asyncio_Task___init____doc__, @@ -575,6 +925,131 @@ _asyncio_Task___init__(PyObject *self, PyObject *args, PyObject *kwargs) return return_value; } +#if !defined(_asyncio_Task__log_destroy_pending_DOCSTR) +# define _asyncio_Task__log_destroy_pending_DOCSTR NULL +#endif +#if defined(_ASYNCIO_TASK__LOG_DESTROY_PENDING_GETSETDEF) +# undef _ASYNCIO_TASK__LOG_DESTROY_PENDING_GETSETDEF +# define _ASYNCIO_TASK__LOG_DESTROY_PENDING_GETSETDEF {"_log_destroy_pending", (getter)_asyncio_Task__log_destroy_pending_get, (setter)_asyncio_Task__log_destroy_pending_set, _asyncio_Task__log_destroy_pending_DOCSTR}, +#else +# define _ASYNCIO_TASK__LOG_DESTROY_PENDING_GETSETDEF {"_log_destroy_pending", (getter)_asyncio_Task__log_destroy_pending_get, NULL, _asyncio_Task__log_destroy_pending_DOCSTR}, +#endif + +static PyObject * +_asyncio_Task__log_destroy_pending_get_impl(TaskObj *self); + +static PyObject * +_asyncio_Task__log_destroy_pending_get(TaskObj *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Task__log_destroy_pending_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_asyncio_Task__log_destroy_pending_DOCSTR) +# define _asyncio_Task__log_destroy_pending_DOCSTR NULL +#endif +#if defined(_ASYNCIO_TASK__LOG_DESTROY_PENDING_GETSETDEF) +# undef _ASYNCIO_TASK__LOG_DESTROY_PENDING_GETSETDEF +# define _ASYNCIO_TASK__LOG_DESTROY_PENDING_GETSETDEF {"_log_destroy_pending", (getter)_asyncio_Task__log_destroy_pending_get, (setter)_asyncio_Task__log_destroy_pending_set, _asyncio_Task__log_destroy_pending_DOCSTR}, +#else +# define _ASYNCIO_TASK__LOG_DESTROY_PENDING_GETSETDEF {"_log_destroy_pending", NULL, (setter)_asyncio_Task__log_destroy_pending_set, NULL}, +#endif + +static int +_asyncio_Task__log_destroy_pending_set_impl(TaskObj *self, PyObject *value); + +static int +_asyncio_Task__log_destroy_pending_set(TaskObj *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Task__log_destroy_pending_set_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_asyncio_Task__must_cancel_DOCSTR) +# define _asyncio_Task__must_cancel_DOCSTR NULL +#endif +#if defined(_ASYNCIO_TASK__MUST_CANCEL_GETSETDEF) +# undef _ASYNCIO_TASK__MUST_CANCEL_GETSETDEF +# define _ASYNCIO_TASK__MUST_CANCEL_GETSETDEF {"_must_cancel", (getter)_asyncio_Task__must_cancel_get, (setter)_asyncio_Task__must_cancel_set, _asyncio_Task__must_cancel_DOCSTR}, +#else +# define _ASYNCIO_TASK__MUST_CANCEL_GETSETDEF {"_must_cancel", (getter)_asyncio_Task__must_cancel_get, NULL, _asyncio_Task__must_cancel_DOCSTR}, +#endif + +static PyObject * +_asyncio_Task__must_cancel_get_impl(TaskObj *self); + +static PyObject * +_asyncio_Task__must_cancel_get(TaskObj *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Task__must_cancel_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_asyncio_Task__coro_DOCSTR) +# define _asyncio_Task__coro_DOCSTR NULL +#endif +#if defined(_ASYNCIO_TASK__CORO_GETSETDEF) +# undef _ASYNCIO_TASK__CORO_GETSETDEF +# define _ASYNCIO_TASK__CORO_GETSETDEF {"_coro", (getter)_asyncio_Task__coro_get, (setter)_asyncio_Task__coro_set, _asyncio_Task__coro_DOCSTR}, +#else +# define _ASYNCIO_TASK__CORO_GETSETDEF {"_coro", (getter)_asyncio_Task__coro_get, NULL, _asyncio_Task__coro_DOCSTR}, +#endif + +static PyObject * +_asyncio_Task__coro_get_impl(TaskObj *self); + +static PyObject * +_asyncio_Task__coro_get(TaskObj *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Task__coro_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_asyncio_Task__fut_waiter_DOCSTR) +# define _asyncio_Task__fut_waiter_DOCSTR NULL +#endif +#if defined(_ASYNCIO_TASK__FUT_WAITER_GETSETDEF) +# undef _ASYNCIO_TASK__FUT_WAITER_GETSETDEF +# define _ASYNCIO_TASK__FUT_WAITER_GETSETDEF {"_fut_waiter", (getter)_asyncio_Task__fut_waiter_get, (setter)_asyncio_Task__fut_waiter_set, _asyncio_Task__fut_waiter_DOCSTR}, +#else +# define _ASYNCIO_TASK__FUT_WAITER_GETSETDEF {"_fut_waiter", (getter)_asyncio_Task__fut_waiter_get, NULL, _asyncio_Task__fut_waiter_DOCSTR}, +#endif + +static PyObject * +_asyncio_Task__fut_waiter_get_impl(TaskObj *self); + +static PyObject * +_asyncio_Task__fut_waiter_get(TaskObj *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Task__fut_waiter_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(_asyncio_Task__make_cancelled_error__doc__, "_make_cancelled_error($self, /)\n" "--\n" @@ -593,7 +1068,13 @@ _asyncio_Task__make_cancelled_error_impl(TaskObj *self); static PyObject * _asyncio_Task__make_cancelled_error(TaskObj *self, PyObject *Py_UNUSED(ignored)) { - return _asyncio_Task__make_cancelled_error_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Task__make_cancelled_error_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_asyncio_Task_cancel__doc__, @@ -670,7 +1151,9 @@ _asyncio_Task_cancel(TaskObj *self, PyObject *const *args, Py_ssize_t nargs, PyO } msg = args[0]; skip_optional_pos: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _asyncio_Task_cancel_impl(self, msg); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -694,7 +1177,13 @@ _asyncio_Task_cancelling_impl(TaskObj *self); static PyObject * _asyncio_Task_cancelling(TaskObj *self, PyObject *Py_UNUSED(ignored)) { - return _asyncio_Task_cancelling_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Task_cancelling_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_asyncio_Task_uncancel__doc__, @@ -717,7 +1206,13 @@ _asyncio_Task_uncancel_impl(TaskObj *self); static PyObject * _asyncio_Task_uncancel(TaskObj *self, PyObject *Py_UNUSED(ignored)) { - return _asyncio_Task_uncancel_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Task_uncancel_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_asyncio_Task_get_stack__doc__, @@ -905,7 +1400,13 @@ _asyncio_Task_get_coro_impl(TaskObj *self); static PyObject * _asyncio_Task_get_coro(TaskObj *self, PyObject *Py_UNUSED(ignored)) { - return _asyncio_Task_get_coro_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Task_get_coro_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_asyncio_Task_get_context__doc__, @@ -939,7 +1440,13 @@ _asyncio_Task_get_name_impl(TaskObj *self); static PyObject * _asyncio_Task_get_name(TaskObj *self, PyObject *Py_UNUSED(ignored)) { - return _asyncio_Task_get_name_impl(self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Task_get_name_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_asyncio_Task_set_name__doc__, @@ -950,6 +1457,21 @@ PyDoc_STRVAR(_asyncio_Task_set_name__doc__, #define _ASYNCIO_TASK_SET_NAME_METHODDEF \ {"set_name", (PyCFunction)_asyncio_Task_set_name, METH_O, _asyncio_Task_set_name__doc__}, +static PyObject * +_asyncio_Task_set_name_impl(TaskObj *self, PyObject *value); + +static PyObject * +_asyncio_Task_set_name(TaskObj *self, PyObject *value) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Task_set_name_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(_asyncio__get_running_loop__doc__, "_get_running_loop($module, /)\n" "--\n" @@ -1566,4 +2088,4 @@ _asyncio_all_tasks(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py exit: return return_value; } -/*[clinic end generated code: output=e5d95a0ec229ffcd input=a9049054013a1b77]*/ +/*[clinic end generated code: output=408e156476ced07f input=a9049054013a1b77]*/ From 03ede5afe2d10c04e05f159fd353ee5869ae2cdb Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sat, 4 Jan 2025 13:31:01 +0300 Subject: [PATCH 182/376] Add `check-readthedocs` pre-commit hook (#128453) --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 107f3b255735f4..af6accd89b5bd4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -55,6 +55,7 @@ repos: hooks: - id: check-dependabot - id: check-github-workflows + - id: check-readthedocs - repo: https://github.com/rhysd/actionlint rev: v1.7.4 From a4e773c540cfd3a9c2bb3b5033d2f79ef50962c8 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 4 Jan 2025 11:46:04 +0100 Subject: [PATCH 183/376] gh-128152: Argument Clinic: ignore pre-processor directives inside C comments (#128464) --- Lib/test/test_clinic.py | 10 ++++++++++ .../2025-01-03-23-51-07.gh-issue-128152.IhzElS.rst | 2 ++ Tools/clinic/libclinic/cpp.py | 3 +++ 3 files changed, 15 insertions(+) create mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-01-03-23-51-07.gh-issue-128152.IhzElS.rst diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 11054963b6ff03..b45b9ee89ee3de 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -740,6 +740,16 @@ def test_cloned_forced_text_signature_illegal(self): err = "Cannot use @text_signature when cloning a function" self.expect_failure(block, err, lineno=11) + def test_ignore_preprocessor_in_comments(self): + for dsl in "clinic", "python": + raw = dedent(f"""\ + /*[{dsl} input] + # CPP directives, valid or not, should be ignored in C comments. + # + [{dsl} start generated code]*/ + """) + self.clinic.parse(raw) + class ParseFileUnitTest(TestCase): def expect_parsing_failure( diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-01-03-23-51-07.gh-issue-128152.IhzElS.rst b/Misc/NEWS.d/next/Tools-Demos/2025-01-03-23-51-07.gh-issue-128152.IhzElS.rst new file mode 100644 index 00000000000000..9657e138e9911b --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2025-01-03-23-51-07.gh-issue-128152.IhzElS.rst @@ -0,0 +1,2 @@ +Fix a bug where Argument Clinic's C pre-processor parser tried to parse +pre-processor directives inside C comments. Patch by Erlend Aasland. diff --git a/Tools/clinic/libclinic/cpp.py b/Tools/clinic/libclinic/cpp.py index e115d65a88e1b6..3cfe99b712641d 100644 --- a/Tools/clinic/libclinic/cpp.py +++ b/Tools/clinic/libclinic/cpp.py @@ -132,6 +132,9 @@ def pop_stack() -> TokenAndCondition: if line_comment: line = before.rstrip() + if self.in_comment: + return + if not line.startswith('#'): return From 95352dcb9321423d0606ae0b01524ad61c2c2ec1 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sat, 4 Jan 2025 12:53:51 +0000 Subject: [PATCH 184/376] GH-127381: pathlib ABCs: remove `PathBase.move()` and `move_into()` (#128337) These methods combine `_delete()` and `copy()`, but `_delete()` isn't part of the public interface, and it's unlikely to be added until the pathlib ABCs are made official, or perhaps even later. --- Lib/pathlib/_abc.py | 27 ----- Lib/pathlib/_local.py | 38 +++++-- Lib/test/test_pathlib/test_pathlib.py | 118 ++++++++++++++++++++++ Lib/test/test_pathlib/test_pathlib_abc.py | 118 ---------------------- 4 files changed, 148 insertions(+), 153 deletions(-) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index e6ff3fe1187512..7de2bb066f8f99 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -573,30 +573,3 @@ def copy_into(self, target_dir, *, follow_symlinks=True, return self.copy(target, follow_symlinks=follow_symlinks, dirs_exist_ok=dirs_exist_ok, preserve_metadata=preserve_metadata) - - def _delete(self): - """ - Delete this file or directory (including all sub-directories). - """ - raise NotImplementedError - - def move(self, target): - """ - Recursively move this file or directory tree to the given destination. - """ - target = self.copy(target, follow_symlinks=False, preserve_metadata=True) - self._delete() - return target - - def move_into(self, target_dir): - """ - Move this file or directory tree into the given existing directory. - """ - name = self.name - if not name: - raise ValueError(f"{self!r} has an empty name") - elif isinstance(target_dir, PathBase): - target = target_dir / name - else: - target = self.with_segments(target_dir, name) - return self.move(target) diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index c5721a69d00470..1da85ddea24376 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -1128,16 +1128,38 @@ def move(self, target): """ Recursively move this file or directory tree to the given destination. """ - if not isinstance(target, PathBase): - target = self.with_segments(target) - target.copy._ensure_different_file(self) + # Use os.replace() if the target is os.PathLike and on the same FS. try: - return self.replace(target) - except OSError as err: - if err.errno != EXDEV: - raise + target_str = os.fspath(target) + except TypeError: + pass + else: + if not isinstance(target, PathBase): + target = self.with_segments(target_str) + target.copy._ensure_different_file(self) + try: + os.replace(self, target_str) + return target + except OSError as err: + if err.errno != EXDEV: + raise # Fall back to copy+delete. - return PathBase.move(self, target) + target = self.copy(target, follow_symlinks=False, preserve_metadata=True) + self._delete() + return target + + def move_into(self, target_dir): + """ + Move this file or directory tree into the given existing directory. + """ + name = self.name + if not name: + raise ValueError(f"{self!r} has an empty name") + elif isinstance(target_dir, PathBase): + target = target_dir / name + else: + target = self.with_segments(target_dir, name) + return self.move(target) if hasattr(os, "symlink"): def symlink_to(self, target, target_is_directory=False): diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index d13daf8ac8cb07..a67a1c531630a1 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -1423,26 +1423,97 @@ def test_move_dangling_symlink(self): self.assertTrue(target.is_symlink()) self.assertEqual(source_readlink, target.readlink()) + def test_move_file(self): + base = self.cls(self.base) + source = base / 'fileA' + source_text = source.read_text() + target = base / 'fileA_moved' + result = source.move(target) + self.assertEqual(result, target) + self.assertFalse(source.exists()) + self.assertTrue(target.exists()) + self.assertEqual(source_text, target.read_text()) + @patch_replace def test_move_file_other_fs(self): self.test_move_file() + def test_move_file_to_file(self): + base = self.cls(self.base) + source = base / 'fileA' + source_text = source.read_text() + target = base / 'dirB' / 'fileB' + result = source.move(target) + self.assertEqual(result, target) + self.assertFalse(source.exists()) + self.assertTrue(target.exists()) + self.assertEqual(source_text, target.read_text()) + @patch_replace def test_move_file_to_file_other_fs(self): self.test_move_file_to_file() + def test_move_file_to_dir(self): + base = self.cls(self.base) + source = base / 'fileA' + target = base / 'dirB' + self.assertRaises(OSError, source.move, target) + @patch_replace def test_move_file_to_dir_other_fs(self): self.test_move_file_to_dir() + def test_move_file_to_itself(self): + base = self.cls(self.base) + source = base / 'fileA' + self.assertRaises(OSError, source.move, source) + + def test_move_dir(self): + base = self.cls(self.base) + source = base / 'dirC' + target = base / 'dirC_moved' + result = source.move(target) + self.assertEqual(result, target) + self.assertFalse(source.exists()) + self.assertTrue(target.is_dir()) + self.assertTrue(target.joinpath('dirD').is_dir()) + self.assertTrue(target.joinpath('dirD', 'fileD').is_file()) + self.assertEqual(target.joinpath('dirD', 'fileD').read_text(), + "this is file D\n") + self.assertTrue(target.joinpath('fileC').is_file()) + self.assertTrue(target.joinpath('fileC').read_text(), + "this is file C\n") + @patch_replace def test_move_dir_other_fs(self): self.test_move_dir() + def test_move_dir_to_dir(self): + base = self.cls(self.base) + source = base / 'dirC' + target = base / 'dirB' + self.assertRaises(OSError, source.move, target) + self.assertTrue(source.exists()) + self.assertTrue(target.exists()) + @patch_replace def test_move_dir_to_dir_other_fs(self): self.test_move_dir_to_dir() + def test_move_dir_to_itself(self): + base = self.cls(self.base) + source = base / 'dirC' + self.assertRaises(OSError, source.move, source) + self.assertTrue(source.exists()) + + def test_move_dir_into_itself(self): + base = self.cls(self.base) + source = base / 'dirC' + target = base / 'dirC' / 'bar' + self.assertRaises(OSError, source.move, target) + self.assertTrue(source.exists()) + self.assertFalse(target.exists()) + @patch_replace def test_move_dir_into_itself_other_fs(self): self.test_move_dir_into_itself() @@ -1472,10 +1543,26 @@ def test_move_dir_symlink_to_itself_other_fs(self): def test_move_dangling_symlink_other_fs(self): self.test_move_dangling_symlink() + def test_move_into(self): + base = self.cls(self.base) + source = base / 'fileA' + source_text = source.read_text() + target_dir = base / 'dirA' + result = source.move_into(target_dir) + self.assertEqual(result, target_dir / 'fileA') + self.assertFalse(source.exists()) + self.assertTrue(result.exists()) + self.assertEqual(source_text, result.read_text()) + @patch_replace def test_move_into_other_os(self): self.test_move_into() + def test_move_into_empty_name(self): + source = self.cls('') + target_dir = self.base + self.assertRaises(ValueError, source.move_into, target_dir) + @patch_replace def test_move_into_empty_name_other_os(self): self.test_move_into_empty_name() @@ -1794,6 +1881,37 @@ def test_rmdir(self): self.assertFileNotFound(p.stat) self.assertFileNotFound(p.unlink) + def test_delete_file(self): + p = self.cls(self.base) / 'fileA' + p._delete() + self.assertFalse(p.exists()) + self.assertFileNotFound(p._delete) + + def test_delete_dir(self): + base = self.cls(self.base) + base.joinpath('dirA')._delete() + self.assertFalse(base.joinpath('dirA').exists()) + self.assertFalse(base.joinpath('dirA', 'linkC').exists( + follow_symlinks=False)) + base.joinpath('dirB')._delete() + self.assertFalse(base.joinpath('dirB').exists()) + self.assertFalse(base.joinpath('dirB', 'fileB').exists()) + self.assertFalse(base.joinpath('dirB', 'linkD').exists( + follow_symlinks=False)) + base.joinpath('dirC')._delete() + self.assertFalse(base.joinpath('dirC').exists()) + self.assertFalse(base.joinpath('dirC', 'dirD').exists()) + self.assertFalse(base.joinpath('dirC', 'dirD', 'fileD').exists()) + self.assertFalse(base.joinpath('dirC', 'fileC').exists()) + self.assertFalse(base.joinpath('dirC', 'novel.txt').exists()) + + def test_delete_missing(self): + tmp = self.cls(self.base, 'delete') + tmp.mkdir() + # filename is guaranteed not to exist + filename = tmp / 'foo' + self.assertRaises(FileNotFoundError, filename._delete) + @needs_symlinks def test_delete_symlink(self): tmp = self.cls(self.base, 'delete') diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index d588442bd11785..0762f224fc9ac4 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -1345,93 +1345,6 @@ def test_copy_into_empty_name(self): target_dir = self.base self.assertRaises(ValueError, source.copy_into, target_dir) - def test_move_file(self): - base = self.cls(self.base) - source = base / 'fileA' - source_text = source.read_text() - target = base / 'fileA_moved' - result = source.move(target) - self.assertEqual(result, target) - self.assertFalse(source.exists()) - self.assertTrue(target.exists()) - self.assertEqual(source_text, target.read_text()) - - def test_move_file_to_file(self): - base = self.cls(self.base) - source = base / 'fileA' - source_text = source.read_text() - target = base / 'dirB' / 'fileB' - result = source.move(target) - self.assertEqual(result, target) - self.assertFalse(source.exists()) - self.assertTrue(target.exists()) - self.assertEqual(source_text, target.read_text()) - - def test_move_file_to_dir(self): - base = self.cls(self.base) - source = base / 'fileA' - target = base / 'dirB' - self.assertRaises(OSError, source.move, target) - - def test_move_file_to_itself(self): - base = self.cls(self.base) - source = base / 'fileA' - self.assertRaises(OSError, source.move, source) - - def test_move_dir(self): - base = self.cls(self.base) - source = base / 'dirC' - target = base / 'dirC_moved' - result = source.move(target) - self.assertEqual(result, target) - self.assertFalse(source.exists()) - self.assertTrue(target.is_dir()) - self.assertTrue(target.joinpath('dirD').is_dir()) - self.assertTrue(target.joinpath('dirD', 'fileD').is_file()) - self.assertEqual(target.joinpath('dirD', 'fileD').read_text(), - "this is file D\n") - self.assertTrue(target.joinpath('fileC').is_file()) - self.assertTrue(target.joinpath('fileC').read_text(), - "this is file C\n") - - def test_move_dir_to_dir(self): - base = self.cls(self.base) - source = base / 'dirC' - target = base / 'dirB' - self.assertRaises(OSError, source.move, target) - self.assertTrue(source.exists()) - self.assertTrue(target.exists()) - - def test_move_dir_to_itself(self): - base = self.cls(self.base) - source = base / 'dirC' - self.assertRaises(OSError, source.move, source) - self.assertTrue(source.exists()) - - def test_move_dir_into_itself(self): - base = self.cls(self.base) - source = base / 'dirC' - target = base / 'dirC' / 'bar' - self.assertRaises(OSError, source.move, target) - self.assertTrue(source.exists()) - self.assertFalse(target.exists()) - - def test_move_into(self): - base = self.cls(self.base) - source = base / 'fileA' - source_text = source.read_text() - target_dir = base / 'dirA' - result = source.move_into(target_dir) - self.assertEqual(result, target_dir / 'fileA') - self.assertFalse(source.exists()) - self.assertTrue(result.exists()) - self.assertEqual(source_text, result.read_text()) - - def test_move_into_empty_name(self): - source = self.cls('') - target_dir = self.base - self.assertRaises(ValueError, source.move_into, target_dir) - def test_iterdir(self): P = self.cls p = P(self.base) @@ -1660,37 +1573,6 @@ def test_is_symlink(self): self.assertIs((P / 'linkA\udfff').is_file(), False) self.assertIs((P / 'linkA\x00').is_file(), False) - def test_delete_file(self): - p = self.cls(self.base) / 'fileA' - p._delete() - self.assertFalse(p.exists()) - self.assertFileNotFound(p._delete) - - def test_delete_dir(self): - base = self.cls(self.base) - base.joinpath('dirA')._delete() - self.assertFalse(base.joinpath('dirA').exists()) - self.assertFalse(base.joinpath('dirA', 'linkC').exists( - follow_symlinks=False)) - base.joinpath('dirB')._delete() - self.assertFalse(base.joinpath('dirB').exists()) - self.assertFalse(base.joinpath('dirB', 'fileB').exists()) - self.assertFalse(base.joinpath('dirB', 'linkD').exists( - follow_symlinks=False)) - base.joinpath('dirC')._delete() - self.assertFalse(base.joinpath('dirC').exists()) - self.assertFalse(base.joinpath('dirC', 'dirD').exists()) - self.assertFalse(base.joinpath('dirC', 'dirD', 'fileD').exists()) - self.assertFalse(base.joinpath('dirC', 'fileC').exists()) - self.assertFalse(base.joinpath('dirC', 'novel.txt').exists()) - - def test_delete_missing(self): - tmp = self.cls(self.base, 'delete') - tmp.mkdir() - # filename is guaranteed not to exist - filename = tmp / 'foo' - self.assertRaises(FileNotFoundError, filename._delete) - class DummyPathWalkTest(unittest.TestCase): cls = DummyPath From a0088b40bb212dc132e147d04f9287cabd72d163 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 4 Jan 2025 16:44:34 +0100 Subject: [PATCH 185/376] Docs: mark up json.dump() using parameter list (#128482) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/library/json.rst | 118 +++++++++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 48 deletions(-) diff --git a/Doc/library/json.rst b/Doc/library/json.rst index 758d47462b6e12..f11109fb0ecfed 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -151,69 +151,91 @@ Basic Usage sort_keys=False, **kw) Serialize *obj* as a JSON formatted stream to *fp* (a ``.write()``-supporting - :term:`file-like object`) using this :ref:`conversion table + :term:`file-like object`) using this :ref:`Python-to-JSON conversion table `. - If *skipkeys* is true (default: ``False``), then dict keys that are not - of a basic type (:class:`str`, :class:`int`, :class:`float`, :class:`bool`, - ``None``) will be skipped instead of raising a :exc:`TypeError`. - - The :mod:`json` module always produces :class:`str` objects, not - :class:`bytes` objects. Therefore, ``fp.write()`` must support :class:`str` - input. - - If *ensure_ascii* is true (the default), the output is guaranteed to - have all incoming non-ASCII characters escaped. If *ensure_ascii* is - false, these characters will be output as-is. + To use a custom :class:`JSONEncoder` subclass (for example, one that overrides the + :meth:`~JSONEncoder.default` method to serialize additional types), specify it with the + *cls* keyword argument; otherwise :class:`JSONEncoder` is used. - If *check_circular* is false (default: ``True``), then the circular - reference check for container types will be skipped and a circular reference - will result in a :exc:`RecursionError` (or worse). + .. note:: - If *allow_nan* is false (default: ``True``), then it will be a - :exc:`ValueError` to serialize out of range :class:`float` values (``nan``, - ``inf``, ``-inf``) in strict compliance of the JSON specification. - If *allow_nan* is true, their JavaScript equivalents (``NaN``, - ``Infinity``, ``-Infinity``) will be used. + Unlike :mod:`pickle` and :mod:`marshal`, JSON is not a framed protocol, + so trying to serialize multiple objects with repeated calls to + :func:`dump` using the same *fp* will result in an invalid JSON file. - If *indent* is a non-negative integer or string, then JSON array elements and - object members will be pretty-printed with that indent level. An indent level - of 0, negative, or ``""`` will only insert newlines. ``None`` (the default) - selects the most compact representation. Using a positive integer indent - indents that many spaces per level. If *indent* is a string (such as ``"\t"``), - that string is used to indent each level. + :param object obj: + The Python object to be serialized. + + :param fp: + The file-like object *obj* will be serialized to. + The :mod:`!json` module always produces :class:`str` objects, + not :class:`bytes` objects, + therefore ``fp.write()`` must support :class:`str` input. + :type fp: :term:`file-like object` + + :param bool skipkeys: + If ``True``, keys that are not of a basic type + (:class:`str`, :class:`int`, :class:`float`, :class:`bool`, ``None``) + will be skipped instead of raising a :exc:`TypeError`. + Default ``False``. + + :param bool ensure_ascii: + If ``True`` (the default), the output is guaranteed to + have all incoming non-ASCII characters escaped. + If ``False``, these characters will be outputted as-is. + + :param bool check_circular: + If ``False``, the circular reference check for container types is skipped + and a circular reference will result in a :exc:`RecursionError` (or worse). + Default ``True``. + + :param bool allow_nan: + If ``False``, serialization of out-of-range :class:`float` values + (``nan``, ``inf``, ``-inf``) will result in a :exc:`ValueError`, + in strict compliance with the JSON specification. + If ``True`` (the default), their JavaScript equivalents + (``NaN``, ``Infinity``, ``-Infinity``) are used. + + :param indent: + If a positive integer or string, JSON array elements and + object members will be pretty-printed with that indent level. + A positive integer indents that many spaces per level; + a string (such as ``"\t"``) is used to indent each level. + If zero, negative, or ``""`` (the empty string), + only newlines are inserted. + If ``None`` (the default), the most compact representation is used. + :type indent: int | str | None + + :param separators: + A two-tuple: ``(item_separator, key_separator)``. + If ``None`` (the default), *separators* defaults to + ``(', ', ': ')`` if *indent* is ``None``, + and ``(',', ': ')`` otherwise. + For the most compact JSON, + specify ``(',', ':')`` to eliminate whitespace. + :type separators: tuple | None + + :param default: + A function that is called for objects that can't otherwise be serialized. + It should return a JSON encodable version of the object + or raise a :exc:`TypeError`. + If ``None`` (the default), :exc:`!TypeError` is raised. + :type default: :term:`callable` | None + + :param sort_keys: + If ``True``, dictionaries will be outputted sorted by key. + Default ``False``. .. versionchanged:: 3.2 Allow strings for *indent* in addition to integers. - If specified, *separators* should be an ``(item_separator, key_separator)`` - tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and - ``(',', ': ')`` otherwise. To get the most compact JSON representation, - you should specify ``(',', ':')`` to eliminate whitespace. - .. versionchanged:: 3.4 Use ``(',', ': ')`` as default if *indent* is not ``None``. - If specified, *default* should be a function that gets called for objects that - can't otherwise be serialized. It should return a JSON encodable version of - the object or raise a :exc:`TypeError`. If not specified, :exc:`TypeError` - is raised. - - If *sort_keys* is true (default: ``False``), then the output of - dictionaries will be sorted by key. - - To use a custom :class:`JSONEncoder` subclass (e.g. one that overrides the - :meth:`~JSONEncoder.default` method to serialize additional types), specify it with the - *cls* kwarg; otherwise :class:`JSONEncoder` is used. - .. versionchanged:: 3.6 All optional parameters are now :ref:`keyword-only `. - .. note:: - - Unlike :mod:`pickle` and :mod:`marshal`, JSON is not a framed protocol, - so trying to serialize multiple objects with repeated calls to - :func:`dump` using the same *fp* will result in an invalid JSON file. .. function:: dumps(obj, *, skipkeys=False, ensure_ascii=True, \ check_circular=True, allow_nan=True, cls=None, \ From fd94c6a8032676d0659aa9e38cdaa7c17093119c Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sat, 4 Jan 2025 15:45:24 +0000 Subject: [PATCH 186/376] pathlib tests: create `walk()` test hierarchy without using class under test (#128338) In the tests for `pathlib.Path.walk()`, avoid using the path class under test (`self.cls`) in test setup. Instead we use `os` functions in `test_pathlib`, and direct manipulation of `DummyPath` internal data in `test_pathlib_abc`. --- Lib/test/test_pathlib/test_pathlib.py | 38 ++++++++++++++- Lib/test/test_pathlib/test_pathlib_abc.py | 59 ++++++++--------------- 2 files changed, 58 insertions(+), 39 deletions(-) diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index a67a1c531630a1..6548577f4de12c 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -3029,6 +3029,42 @@ def setUp(self): if name in _tests_needing_symlinks and not self.can_symlink: self.skipTest('requires symlinks') super().setUp() + + def createTestHierarchy(self): + # Build: + # TESTFN/ + # TEST1/ a file kid and two directory kids + # tmp1 + # SUB1/ a file kid and a directory kid + # tmp2 + # SUB11/ no kids + # SUB2/ a file kid and a dirsymlink kid + # tmp3 + # link/ a symlink to TEST2 + # broken_link + # broken_link2 + # TEST2/ + # tmp4 a lone file + t2_path = self.cls(self.base, "TEST2") + os.makedirs(self.sub11_path) + os.makedirs(self.sub2_path) + os.makedirs(t2_path) + + tmp1_path = self.walk_path / "tmp1" + tmp2_path = self.sub1_path / "tmp2" + tmp3_path = self.sub2_path / "tmp3" + tmp4_path = self.cls(self.base, "TEST2", "tmp4") + for path in tmp1_path, tmp2_path, tmp3_path, tmp4_path: + with open(path, "w", encoding='utf-8') as f: + f.write(f"I'm {path} and proud of it. Blame test_pathlib.\n") + + if self.can_symlink: + broken_link_path = self.sub2_path / "broken_link" + broken_link2_path = self.sub2_path / "broken_link2" + os.symlink(t2_path, self.link_path, target_is_directory=True) + os.symlink('broken', broken_link_path) + os.symlink(os.path.join('tmp3', 'broken'), broken_link2_path) + self.sub2_tree = (self.sub2_path, [], ["broken_link", "broken_link2", "link", "tmp3"]) sub21_path= self.sub2_path / "SUB21" tmp5_path = sub21_path / "tmp3" broken_link3_path = self.sub2_path / "broken_link3" @@ -3052,7 +3088,7 @@ def setUp(self): def tearDown(self): if 'SUB21' in self.sub2_tree[1]: os.chmod(self.sub2_path / "SUB21", stat.S_IRWXU) - super().tearDown() + os_helper.rmtree(self.base) def test_walk_bad_dir(self): errors = [] diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index 0762f224fc9ac4..87aef0c130cf9e 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -1580,52 +1580,35 @@ class DummyPathWalkTest(unittest.TestCase): can_symlink = False def setUp(self): - # Build: - # TESTFN/ - # TEST1/ a file kid and two directory kids - # tmp1 - # SUB1/ a file kid and a directory kid - # tmp2 - # SUB11/ no kids - # SUB2/ a file kid and a dirsymlink kid - # tmp3 - # link/ a symlink to TEST2 - # broken_link - # broken_link2 - # TEST2/ - # tmp4 a lone file self.walk_path = self.cls(self.base, "TEST1") self.sub1_path = self.walk_path / "SUB1" self.sub11_path = self.sub1_path / "SUB11" self.sub2_path = self.walk_path / "SUB2" - tmp1_path = self.walk_path / "tmp1" - tmp2_path = self.sub1_path / "tmp2" - tmp3_path = self.sub2_path / "tmp3" self.link_path = self.sub2_path / "link" - t2_path = self.cls(self.base, "TEST2") - tmp4_path = self.cls(self.base, "TEST2", "tmp4") - broken_link_path = self.sub2_path / "broken_link" - broken_link2_path = self.sub2_path / "broken_link2" - - self.sub11_path.mkdir(parents=True) - self.sub2_path.mkdir(parents=True) - t2_path.mkdir(parents=True) - - for path in tmp1_path, tmp2_path, tmp3_path, tmp4_path: - with path.open("w", encoding='utf-8') as f: - f.write(f"I'm {path} and proud of it. Blame test_pathlib.\n") + self.sub2_tree = (self.sub2_path, [], ["tmp3"]) + self.createTestHierarchy() - if self.can_symlink: - self.link_path.symlink_to(t2_path, target_is_directory=True) - broken_link_path.symlink_to('broken') - broken_link2_path.symlink_to(self.cls('tmp3', 'broken')) - self.sub2_tree = (self.sub2_path, [], ["broken_link", "broken_link2", "link", "tmp3"]) - else: - self.sub2_tree = (self.sub2_path, [], ["tmp3"]) + def createTestHierarchy(self): + cls = self.cls + cls._files = { + f'{self.base}/TEST1/tmp1': b'this is tmp1\n', + f'{self.base}/TEST1/SUB1/tmp2': b'this is tmp2\n', + f'{self.base}/TEST1/SUB2/tmp3': b'this is tmp3\n', + f'{self.base}/TEST2/tmp4': b'this is tmp4\n', + } + cls._directories = { + f'{self.base}': {'TEST1', 'TEST2'}, + f'{self.base}/TEST1': {'SUB1', 'SUB2', 'tmp1'}, + f'{self.base}/TEST1/SUB1': {'SUB11', 'tmp2'}, + f'{self.base}/TEST1/SUB1/SUB11': set(), + f'{self.base}/TEST1/SUB2': {'tmp3'}, + f'{self.base}/TEST2': {'tmp4'}, + } def tearDown(self): - base = self.cls(self.base) - base._delete() + cls = self.cls + cls._files.clear() + cls._directories.clear() def test_walk_topdown(self): walker = self.walk_path.walk() From f28d471fbe99f9eaac05d60ed40da47b0b56fe86 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Sat, 4 Jan 2025 17:38:57 +0000 Subject: [PATCH 187/376] gh-126719: Clarify math.fmod docs (#127741) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/math.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/math.rst b/Doc/library/math.rst index bf79b23a72bbf9..c78b313db5152d 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -248,7 +248,8 @@ Floating point arithmetic .. function:: fmod(x, y) - Return ``fmod(x, y)``, as defined by the platform C library. Note that the + Return the floating-point remainder of ``x / y``, + as defined by the platform C library function ``fmod(x, y)``. Note that the Python expression ``x % y`` may not return the same result. The intent of the C standard is that ``fmod(x, y)`` be exactly (mathematically; to infinite precision) equal to ``x - n*y`` for some integer *n* such that the result has From 87ee76062a7eb9c0fa2b94e36cfed21d86ae90ac Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 4 Jan 2025 19:57:59 +0100 Subject: [PATCH 188/376] Docs: amend json.dump() post gh-128482 (#128489) --- Doc/library/json.rst | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Doc/library/json.rst b/Doc/library/json.rst index f11109fb0ecfed..169291f74f44a5 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -154,10 +154,6 @@ Basic Usage :term:`file-like object`) using this :ref:`Python-to-JSON conversion table `. - To use a custom :class:`JSONEncoder` subclass (for example, one that overrides the - :meth:`~JSONEncoder.default` method to serialize additional types), specify it with the - *cls* keyword argument; otherwise :class:`JSONEncoder` is used. - .. note:: Unlike :mod:`pickle` and :mod:`marshal`, JSON is not a framed protocol, @@ -197,6 +193,13 @@ Basic Usage If ``True`` (the default), their JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``) are used. + :param cls: + If set, a custom JSON encoder with the + :meth:`~JSONEncoder.default` method overridden, + for serializing into custom datatypes. + If ``None`` (the default), :class:`!JSONEncoder` is used. + :type cls: a :class:`JSONEncoder` subclass + :param indent: If a positive integer or string, JSON array elements and object members will be pretty-printed with that indent level. @@ -223,7 +226,7 @@ Basic Usage If ``None`` (the default), :exc:`!TypeError` is raised. :type default: :term:`callable` | None - :param sort_keys: + :param bool sort_keys: If ``True``, dictionaries will be outputted sorted by key. Default ``False``. From 8ade15343d5daec3bf79ff7c47f03726fb2bcadf Mon Sep 17 00:00:00 2001 From: "RUANG (James Roy)" Date: Sun, 5 Jan 2025 04:48:20 +0800 Subject: [PATCH 189/376] gh-127954: Document PyObject_DelItemString (#127986) --- Doc/c-api/object.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index a137688fe07545..3a434a4173eafa 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -493,6 +493,13 @@ Object Protocol on failure. This is equivalent to the Python statement ``del o[key]``. +.. c:function:: int PyObject_DelItemString(PyObject *o, const char *key) + + This is the same as :c:func:`PyObject_DelItem`, but *key* is + specified as a :c:expr:`const char*` UTF-8 encoded bytes string, + rather than a :c:expr:`PyObject*`. + + .. c:function:: PyObject* PyObject_Dir(PyObject *o) This is equivalent to the Python expression ``dir(o)``, returning a (possibly From e8b6b39ff707378da654e15ccddde9c28cefdd30 Mon Sep 17 00:00:00 2001 From: Beomsoo Kim Date: Sun, 5 Jan 2025 07:38:49 +0900 Subject: [PATCH 190/376] gh-127553: Remove outdated TODO comment in _pydatetime (#127564) --- Lib/_pydatetime.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index ed01670cfece43..be90c9b1315d53 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -2392,7 +2392,6 @@ def __reduce__(self): def _isoweek1monday(year): # Helper to calculate the day number of the Monday starting week 1 - # XXX This could be done more efficiently THURSDAY = 3 firstday = _ymd2ord(year, 1, 1) firstweekday = (firstday + 6) % 7 # See weekday() above From 0cafa97932c6574734bbaa07180bbd5a762b01a6 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 5 Jan 2025 00:38:46 +0100 Subject: [PATCH 191/376] gh-115765: Document and enforce Autoconf 2.72 requirement (#128502) --- Doc/using/configure.rst | 5 ++++- configure.ac | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index e7733a6dc11451..82df8dfc948ed2 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -29,7 +29,7 @@ Features and minimum versions required to build CPython: * Tcl/Tk 8.5.12 for the :mod:`tkinter` module. -* Autoconf 2.71 and aclocal 1.16.5 are required to regenerate the +* Autoconf 2.72 and aclocal 1.16.5 are required to regenerate the :file:`configure` script. .. versionchanged:: 3.1 @@ -58,6 +58,9 @@ Features and minimum versions required to build CPython: .. versionchanged:: 3.13 Autoconf 2.71, aclocal 1.16.5 and SQLite 3.15.2 are now required. +.. versionchanged:: next + Autoconf 2.72 is now required. + See also :pep:`7` "Style Guide for C Code" and :pep:`11` "CPython platform support". diff --git a/configure.ac b/configure.ac index 9e131ed1a2dc98..1def41176337e3 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ dnl ************************************************************ dnl * Please run autoreconf -ivf -Werror to test your changes! * dnl ************************************************************ dnl -dnl Python's configure script requires autoconf 2.71, autoconf-archive, +dnl Python's configure script requires autoconf 2.72, autoconf-archive, dnl aclocal 1.16, and pkg-config. dnl dnl It is recommended to use the Tools/build/regen-configure.sh shell script @@ -12,7 +12,7 @@ dnl # Set VERSION so we only need to edit in one place (i.e., here) m4_define([PYTHON_VERSION], [3.14]) -AC_PREREQ([2.71]) +AC_PREREQ([2.72]) AC_INIT([python],[PYTHON_VERSION],[https://github.com/python/cpython/issues/]) From b60044b838f3ea97395cd6f3adbd5330356fc273 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Sat, 4 Jan 2025 22:16:30 -0600 Subject: [PATCH 192/376] gh-128437: Add `BOLT_COMMON_FLAGS` with `-update-debug-sections` (gh-128455) Add `BOLT_COMMON_FLAGS` with `-update-debug-sections` Co-authored-by: Gregory Szorc --- configure | 18 ++++++++++++++++-- configure.ac | 18 ++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/configure b/configure index aa88c74c61156c..61ee51c4b36473 100755 --- a/configure +++ b/configure @@ -907,6 +907,7 @@ CFLAGS_ALIASING OPT BOLT_APPLY_FLAGS BOLT_INSTRUMENT_FLAGS +BOLT_COMMON_FLAGS BOLT_BINARIES MERGE_FDATA LLVM_BOLT @@ -1142,6 +1143,7 @@ LIBS CPPFLAGS CPP PROFILE_TASK +BOLT_COMMON_FLAGS BOLT_INSTRUMENT_FLAGS BOLT_APPLY_FLAGS LIBUUID_CFLAGS @@ -1963,6 +1965,8 @@ Some influential environment variables: CPP C preprocessor PROFILE_TASK Python args for PGO generation task + BOLT_COMMON_FLAGS + Common arguments to llvm-bolt when instrumenting and applying BOLT_INSTRUMENT_FLAGS Arguments to llvm-bolt when instrumenting binaries BOLT_APPLY_FLAGS @@ -9389,11 +9393,21 @@ then : fi + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking BOLT_COMMON_FLAGS" >&5 +printf %s "checking BOLT_COMMON_FLAGS... " >&6; } +if test -z "${BOLT_COMMON_FLAGS}" +then + BOLT_COMMON_FLAGS=-update-debug-sections + +fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking BOLT_INSTRUMENT_FLAGS" >&5 printf %s "checking BOLT_INSTRUMENT_FLAGS... " >&6; } if test -z "${BOLT_INSTRUMENT_FLAGS}" then - BOLT_INSTRUMENT_FLAGS= + BOLT_INSTRUMENT_FLAGS="${BOLT_COMMON_FLAGS}" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $BOLT_INSTRUMENT_FLAGS" >&5 printf "%s\n" "$BOLT_INSTRUMENT_FLAGS" >&6; } @@ -9403,7 +9417,7 @@ printf "%s\n" "$BOLT_INSTRUMENT_FLAGS" >&6; } printf %s "checking BOLT_APPLY_FLAGS... " >&6; } if test -z "${BOLT_APPLY_FLAGS}" then - BOLT_APPLY_FLAGS=" -update-debug-sections -reorder-blocks=ext-tsp -reorder-functions=cdsort -split-functions -icf=1 -inline-all -split-eh -reorder-functions-use-hot-size -peepholes=none -jump-tables=aggressive -inline-ap -indirect-call-promotion=all -dyno-stats -use-gnu-stack -frame-opt=hot " + BOLT_APPLY_FLAGS=" ${BOLT_COMMON_FLAGS} -reorder-blocks=ext-tsp -reorder-functions=cdsort -split-functions -icf=1 -inline-all -split-eh -reorder-functions-use-hot-size -peepholes=none -jump-tables=aggressive -inline-ap -indirect-call-promotion=all -dyno-stats -use-gnu-stack -frame-opt=hot " fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $BOLT_APPLY_FLAGS" >&5 diff --git a/configure.ac b/configure.ac index 1def41176337e3..172e8a1a842010 100644 --- a/configure.ac +++ b/configure.ac @@ -2160,6 +2160,20 @@ AS_VAR_IF([enable_shared], [yes], [ BOLT_BINARIES="${BOLT_BINARIES} \$(INSTSONAME)" ]) +AC_ARG_VAR( + [BOLT_COMMON_FLAGS], + [Common arguments to llvm-bolt when instrumenting and applying] +) + +AC_MSG_CHECKING([BOLT_COMMON_FLAGS]) +if test -z "${BOLT_COMMON_FLAGS}" +then + AS_VAR_SET( + [BOLT_COMMON_FLAGS], + [-update-debug-sections] + ) +fi + AC_ARG_VAR( [BOLT_INSTRUMENT_FLAGS], [Arguments to llvm-bolt when instrumenting binaries] @@ -2167,7 +2181,7 @@ AC_ARG_VAR( AC_MSG_CHECKING([BOLT_INSTRUMENT_FLAGS]) if test -z "${BOLT_INSTRUMENT_FLAGS}" then - BOLT_INSTRUMENT_FLAGS= + BOLT_INSTRUMENT_FLAGS="${BOLT_COMMON_FLAGS}" fi AC_MSG_RESULT([$BOLT_INSTRUMENT_FLAGS]) @@ -2181,7 +2195,7 @@ then AS_VAR_SET( [BOLT_APPLY_FLAGS], [m4_normalize(" - -update-debug-sections + ${BOLT_COMMON_FLAGS} -reorder-blocks=ext-tsp -reorder-functions=cdsort -split-functions From ae23a012e6c8aadc4a588101cbe7bc86ede45627 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sun, 5 Jan 2025 18:17:06 +0900 Subject: [PATCH 193/376] gh-128137: Update PyASCIIObject to handle interned field with the atomic operation (gh-128196) --- Include/cpython/unicodeobject.h | 20 ++++++++++++------- ...-12-24-01-40-12.gh-issue-128137.gsTwr_.rst | 2 ++ Objects/unicodeobject.c | 6 +++--- 3 files changed, 18 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-24-01-40-12.gh-issue-128137.gsTwr_.rst diff --git a/Include/cpython/unicodeobject.h b/Include/cpython/unicodeobject.h index 91799137101280..46a01c8e591709 100644 --- a/Include/cpython/unicodeobject.h +++ b/Include/cpython/unicodeobject.h @@ -109,7 +109,7 @@ typedef struct { 3: Interned, Immortal, and Static This categorization allows the runtime to determine the right cleanup mechanism at runtime shutdown. */ - unsigned int interned:2; + uint16_t interned; /* Character size: - PyUnicode_1BYTE_KIND (1): @@ -132,21 +132,23 @@ typedef struct { * all characters are in the range U+0000-U+10FFFF * at least one character is in the range U+10000-U+10FFFF */ - unsigned int kind:3; + unsigned short kind:3; /* Compact is with respect to the allocation scheme. Compact unicode objects only require one memory block while non-compact objects use one block for the PyUnicodeObject struct and another for its data buffer. */ - unsigned int compact:1; + unsigned short compact:1; /* The string only contains characters in the range U+0000-U+007F (ASCII) and the kind is PyUnicode_1BYTE_KIND. If ascii is set and compact is set, use the PyASCIIObject structure. */ - unsigned int ascii:1; + unsigned short ascii:1; /* The object is statically allocated. */ - unsigned int statically_allocated:1; + unsigned short statically_allocated:1; /* Padding to ensure that PyUnicode_DATA() is always aligned to - 4 bytes (see issue #19537 on m68k). */ - unsigned int :24; + 4 bytes (see issue #19537 on m68k) and we use unsigned short to avoid + the extra four bytes on 32-bit Windows. This is restricted features + for specific compilers including GCC, MSVC, Clang and IBM's XL compiler. */ + unsigned short :10; } state; } PyASCIIObject; @@ -195,7 +197,11 @@ typedef struct { /* Use only if you know it's a string */ static inline unsigned int PyUnicode_CHECK_INTERNED(PyObject *op) { +#ifdef Py_GIL_DISABLED + return _Py_atomic_load_uint16_relaxed(&_PyASCIIObject_CAST(op)->state.interned); +#else return _PyASCIIObject_CAST(op)->state.interned; +#endif } #define PyUnicode_CHECK_INTERNED(op) PyUnicode_CHECK_INTERNED(_PyObject_CAST(op)) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-24-01-40-12.gh-issue-128137.gsTwr_.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-24-01-40-12.gh-issue-128137.gsTwr_.rst new file mode 100644 index 00000000000000..a3b7cde7f67676 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-24-01-40-12.gh-issue-128137.gsTwr_.rst @@ -0,0 +1,2 @@ +Update :c:type:`PyASCIIObject` layout to handle interned field with the +atomic operation. Patch by Donghee Na. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 5e532ce0f348c4..3eafa2381c1a4d 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -15729,7 +15729,7 @@ immortalize_interned(PyObject *s) _Py_DecRefTotal(_PyThreadState_GET()); } #endif - _PyUnicode_STATE(s).interned = SSTATE_INTERNED_IMMORTAL; + FT_ATOMIC_STORE_UINT16_RELAXED(_PyUnicode_STATE(s).interned, SSTATE_INTERNED_IMMORTAL); _Py_SetImmortal(s); } @@ -15848,7 +15848,7 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */, _Py_DecRefTotal(_PyThreadState_GET()); #endif } - _PyUnicode_STATE(s).interned = SSTATE_INTERNED_MORTAL; + FT_ATOMIC_STORE_UINT16_RELAXED(_PyUnicode_STATE(s).interned, SSTATE_INTERNED_MORTAL); /* INTERNED_MORTAL -> INTERNED_IMMORTAL (if needed) */ @@ -15984,7 +15984,7 @@ _PyUnicode_ClearInterned(PyInterpreterState *interp) Py_UNREACHABLE(); } if (!shared) { - _PyUnicode_STATE(s).interned = SSTATE_NOT_INTERNED; + FT_ATOMIC_STORE_UINT16_RELAXED(_PyUnicode_STATE(s).interned, SSTATE_NOT_INTERNED); } } #ifdef INTERNED_STATS From 2228e92da31ca7344a163498f848973a1b356597 Mon Sep 17 00:00:00 2001 From: Damien <81557462+Damien-Chen@users.noreply.github.com> Date: Sun, 5 Jan 2025 20:07:18 +0800 Subject: [PATCH 194/376] gh-128504: Upgrade doctest to ubuntu-24.04 (#128506) Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- .github/workflows/reusable-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-docs.yml b/.github/workflows/reusable-docs.yml index 3962d12403919a..88da55bf08b8fe 100644 --- a/.github/workflows/reusable-docs.yml +++ b/.github/workflows/reusable-docs.yml @@ -99,7 +99,7 @@ jobs: # Run "doctest" on HEAD as new syntax doesn't exist in the latest stable release doctest: name: 'Doctest' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 60 steps: - uses: actions/checkout@v4 From 3b231be8f000ae59faa04d5a2f1af11beafee866 Mon Sep 17 00:00:00 2001 From: Yuki Kobayashi Date: Mon, 6 Jan 2025 06:58:31 +0900 Subject: [PATCH 195/376] Docs: fix `MessageDefect` references in email.policy docs (#128468) --- Doc/library/email.policy.rst | 4 ++-- Doc/tools/.nitignore | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/library/email.policy.rst b/Doc/library/email.policy.rst index 314767d0802a08..6b997ee784f6e4 100644 --- a/Doc/library/email.policy.rst +++ b/Doc/library/email.policy.rst @@ -267,7 +267,7 @@ added matters. To illustrate:: Handle a *defect* found on *obj*. When the email package calls this method, *defect* will always be a subclass of - :class:`~email.errors.Defect`. + :class:`~email.errors.MessageDefect`. The default implementation checks the :attr:`raise_on_defect` flag. If it is ``True``, *defect* is raised as an exception. If it is ``False`` @@ -277,7 +277,7 @@ added matters. To illustrate:: .. method:: register_defect(obj, defect) Register a *defect* on *obj*. In the email package, *defect* will always - be a subclass of :class:`~email.errors.Defect`. + be a subclass of :class:`~email.errors.MessageDefect`. The default implementation calls the ``append`` method of the ``defects`` attribute of *obj*. When the email package calls :attr:`handle_defect`, diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index 7d50aec56a9bf7..6940c95ab2c9a1 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -23,7 +23,6 @@ Doc/library/email.charset.rst Doc/library/email.compat32-message.rst Doc/library/email.errors.rst Doc/library/email.parser.rst -Doc/library/email.policy.rst Doc/library/exceptions.rst Doc/library/functools.rst Doc/library/http.cookiejar.rst From a62ba52f1439c1f878a3ff9b8544caf9aeef9b90 Mon Sep 17 00:00:00 2001 From: RanKKI Date: Mon, 6 Jan 2025 12:32:16 +1100 Subject: [PATCH 196/376] gh-98188: Fix EmailMessage.get_payload to decode data when CTE value has extra text (#127547) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to this point message handling has been very strict with regards to content encoding values: mixed case was accepted, but trailing blanks or other text would cause decoding failure, even if the first token was a valid encoding. By Postel's Rule we should go ahead and decode as long as we can recognize that first token. We have not thought of any security or backward compatibility concerns with this fix. This fix does introduce a new technique/pattern to the Message code: we look to see if the header has a 'cte' attribute, and if so we use that. This effectively promotes the header API exposed by HeaderRegistry to an API that any header parser "should" support. This seems like a reasonable thing to do. It is not, however, a requirement, as the string value of the header is still used if there is no cte attribute. The full fix (ignore any trailing blanks or blank-separated trailing text) applies only to the non-compat32 API. compat32 is only fixed to the extent that it now ignores trailing spaces. Note that the HeaderRegistry parsing still records a HeaderDefect if there is extra text. Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/email/message.py | 8 +++- Lib/test/test_email/test_email.py | 44 +++++++++++++++++++ Lib/test/test_email/test_headerregistry.py | 5 +++ Misc/ACKS | 1 + ...4-12-03-14-45-16.gh-issue-98188.GX9i2b.rst | 3 ++ 5 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-03-14-45-16.gh-issue-98188.GX9i2b.rst diff --git a/Lib/email/message.py b/Lib/email/message.py index a58afc5fe5f68e..87fcab68868d46 100644 --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -286,8 +286,12 @@ def get_payload(self, i=None, decode=False): if i is not None and not isinstance(self._payload, list): raise TypeError('Expected list, got %s' % type(self._payload)) payload = self._payload - # cte might be a Header, so for now stringify it. - cte = str(self.get('content-transfer-encoding', '')).lower() + cte = self.get('content-transfer-encoding', '') + if hasattr(cte, 'cte'): + cte = cte.cte + else: + # cte might be a Header, so for now stringify it. + cte = str(cte).strip().lower() # payload may be bytes here. if not decode: if isinstance(payload, str) and utils._has_surrogates(payload): diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index abe9ef2e94409f..2deb35721576b8 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -810,6 +810,16 @@ def test_unicode_body_defaults_to_utf8_encoding(self): w4kgdGVzdGFiYwo= """)) + def test_string_payload_with_base64_cte(self): + msg = email.message_from_string(textwrap.dedent("""\ + Content-Transfer-Encoding: base64 + + SGVsbG8uIFRlc3Rpbmc= + """), policy=email.policy.default) + self.assertEqual(msg.get_payload(decode=True), b"Hello. Testing") + self.assertDefectsEqual(msg['content-transfer-encoding'].defects, []) + + # Test the email.encoders module class TestEncoders(unittest.TestCase): @@ -2352,6 +2362,40 @@ def test_missing_header_body_separator(self): self.assertDefectsEqual(msg.defects, [errors.MissingHeaderBodySeparatorDefect]) + def test_string_payload_with_extra_space_after_cte(self): + # https://github.com/python/cpython/issues/98188 + cte = "base64 " + msg = email.message_from_string(textwrap.dedent(f"""\ + Content-Transfer-Encoding: {cte} + + SGVsbG8uIFRlc3Rpbmc= + """), policy=email.policy.default) + self.assertEqual(msg.get_payload(decode=True), b"Hello. Testing") + self.assertDefectsEqual(msg['content-transfer-encoding'].defects, []) + + def test_string_payload_with_extra_text_after_cte(self): + msg = email.message_from_string(textwrap.dedent("""\ + Content-Transfer-Encoding: base64 some text + + SGVsbG8uIFRlc3Rpbmc= + """), policy=email.policy.default) + self.assertEqual(msg.get_payload(decode=True), b"Hello. Testing") + cte = msg['content-transfer-encoding'] + self.assertDefectsEqual(cte.defects, [email.errors.InvalidHeaderDefect]) + + def test_string_payload_with_extra_space_after_cte_compat32(self): + cte = "base64 " + msg = email.message_from_string(textwrap.dedent(f"""\ + Content-Transfer-Encoding: {cte} + + SGVsbG8uIFRlc3Rpbmc= + """), policy=email.policy.compat32) + pasted_cte = msg['content-transfer-encoding'] + self.assertEqual(pasted_cte, cte) + self.assertEqual(msg.get_payload(decode=True), b"Hello. Testing") + self.assertDefectsEqual(msg.defects, []) + + # Test RFC 2047 header encoding and decoding class TestRFC2047(TestEmailBase): diff --git a/Lib/test/test_email/test_headerregistry.py b/Lib/test/test_email/test_headerregistry.py index 4c0523f410332f..ff7a6da644d572 100644 --- a/Lib/test/test_email/test_headerregistry.py +++ b/Lib/test/test_email/test_headerregistry.py @@ -837,6 +837,11 @@ def cte_as_value(self, '7bit', [errors.InvalidHeaderDefect]), + 'extra_space_after_cte': ( + 'base64 ', + 'base64', + []), + } diff --git a/Misc/ACKS b/Misc/ACKS index c6e53317b37d78..d7585c16c8169c 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1129,6 +1129,7 @@ Gregor Lingl Everett Lipman Mirko Liss Alexander Liu +Hui Liu Yuan Liu Nick Lockwood Stephanie Lockwood diff --git a/Misc/NEWS.d/next/Library/2024-12-03-14-45-16.gh-issue-98188.GX9i2b.rst b/Misc/NEWS.d/next/Library/2024-12-03-14-45-16.gh-issue-98188.GX9i2b.rst new file mode 100644 index 00000000000000..30ab8cfc3f0bc6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-03-14-45-16.gh-issue-98188.GX9i2b.rst @@ -0,0 +1,3 @@ +Fix an issue in :meth:`email.message.Message.get_payload` where data +cannot be decoded if the Content Transfer Encoding mechanism contains +trailing whitespaces or additional junk text. Patch by Hui Liu. From aef52ca8b334ff90e8032da39f4d06e7b5130eb9 Mon Sep 17 00:00:00 2001 From: "Tomas R." Date: Mon, 6 Jan 2025 09:42:26 +0100 Subject: [PATCH 197/376] gh-128519: Align the docstring of untokenize() to match the docs (#128521) --- Lib/tokenize.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/Lib/tokenize.py b/Lib/tokenize.py index 7ece4e9b70d31b..1a60fd32a77ea4 100644 --- a/Lib/tokenize.py +++ b/Lib/tokenize.py @@ -318,16 +318,10 @@ def untokenize(iterable): with at least two elements, a token number and token value. If only two tokens are passed, the resulting output is poor. - Round-trip invariant for full input: - Untokenized source will match input source exactly - - Round-trip invariant for limited input: - # Output bytes will tokenize back to the input - t1 = [tok[:2] for tok in tokenize(f.readline)] - newcode = untokenize(t1) - readline = BytesIO(newcode).readline - t2 = [tok[:2] for tok in tokenize(readline)] - assert t1 == t2 + The result is guaranteed to tokenize back to match the input so + that the conversion is lossless and round-trips are assured. + The guarantee applies only to the token type and token string as + the spacing between tokens (column positions) may change. """ ut = Untokenizer() out = ut.untokenize(iterable) From 879d287f49edcd4fa68324265f8ba63758716540 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 6 Jan 2025 13:29:18 +0300 Subject: [PATCH 198/376] gh-102471: convert decimal module to use PyLong_Export API (PEP 757) (#128267) --- Modules/_decimal/_decimal.c | 51 ++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index c564813036e504..0def463c7d8b9e 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -30,7 +30,6 @@ #endif #include -#include "pycore_long.h" // _PyLong_IsZero() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_typeobject.h" #include "complexobject.h" @@ -2323,38 +2322,42 @@ static PyObject * dec_from_long(decimal_state *state, PyTypeObject *type, PyObject *v, const mpd_context_t *ctx, uint32_t *status) { - PyObject *dec; - PyLongObject *l = (PyLongObject *)v; + PyObject *dec = PyDecType_New(state, type); - dec = PyDecType_New(state, type); if (dec == NULL) { return NULL; } - if (_PyLong_IsZero(l)) { - _dec_settriple(dec, MPD_POS, 0, 0); - return dec; - } - - uint8_t sign = _PyLong_IsNegative(l) ? MPD_NEG : MPD_POS; + PyLongExport export_long; - if (_PyLong_IsCompact(l)) { - _dec_settriple(dec, sign, l->long_value.ob_digit[0], 0); - mpd_qfinalize(MPD(dec), ctx, status); - return dec; + if (PyLong_Export(v, &export_long) == -1) { + Py_DECREF(dec); + return NULL; } - size_t len = _PyLong_DigitCount(l); + if (export_long.digits) { + const PyLongLayout *layout = PyLong_GetNativeLayout(); + uint32_t base = (uint32_t)1 << layout->bits_per_digit; + uint8_t sign = export_long.negative ? MPD_NEG : MPD_POS; + Py_ssize_t len = export_long.ndigits; -#if PYLONG_BITS_IN_DIGIT == 30 - 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->long_value.ob_digit, len, sign, PyLong_BASE, - ctx, status); -#else - #error "PYLONG_BITS_IN_DIGIT should be 15 or 30" -#endif + assert(layout->bits_per_digit <= 32); + assert(layout->digits_order == -1); + assert(layout->digit_endianness == (PY_LITTLE_ENDIAN ? -1 : 1)); + assert(layout->digit_size == 2 || layout->digit_size == 4); + if (layout->digit_size == 4) { + mpd_qimport_u32(MPD(dec), export_long.digits, len, sign, + base, ctx, status); + } + else { + mpd_qimport_u16(MPD(dec), export_long.digits, len, sign, + base, ctx, status); + } + PyLong_FreeExport(&export_long); + } + else { + mpd_qset_i64(MPD(dec), export_long.value, ctx, status); + } return dec; } From d50fa0541b6d4f458d7ab16d3a11b1117c607f85 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 6 Jan 2025 02:30:42 -0800 Subject: [PATCH 199/376] gh-128089: Add PYC magic number for VALUE_WITH_FAKE_GLOBALS (#128097) Assign 3610 PYC magic number to VALUE_WITH_FAKE_GLOBALS format of annotationlib. --- Include/internal/pycore_magic_number.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_magic_number.h b/Include/internal/pycore_magic_number.h index 4c3b9c4c71da1b..ec3685d2034560 100644 --- a/Include/internal/pycore_magic_number.h +++ b/Include/internal/pycore_magic_number.h @@ -262,7 +262,7 @@ Known values: Python 3.14a1 3607 (Add pseudo instructions JUMP_IF_TRUE/FALSE) Python 3.14a1 3608 (Add support for slices) Python 3.14a2 3609 (Add LOAD_SMALL_INT and LOAD_CONST_IMMORTAL instructions, remove RETURN_CONST) - (3610 accidentally omitted) + Python 3.14a4 3610 (Add VALUE_WITH_FAKE_GLOBALS format to annotationlib) Python 3.14a4 3611 (Add NOT_TAKEN instruction) Python 3.15 will start with 3650 From 8d15058d61681e98579cf3fbd3c6fd13b3df6a72 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 6 Jan 2025 12:50:28 +0200 Subject: [PATCH 200/376] gh-77214: Update outdated documentation for numeric PyArg_Parse formats (GH-128454) --- Doc/c-api/arg.rst | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 3201bdc82691f4..41c0366d205086 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -229,12 +229,24 @@ There are three ways strings and buffers can be converted to C: Numbers ------- +These formats allow representing Python numbers or single characters as C numbers. +Formats that require :class:`int`, :class:`float` or :class:`complex` can +also use the corresponding special methods :meth:`~object.__index__`, +:meth:`~object.__float__` or :meth:`~object.__complex__` to convert +the Python object to the required type. + +For signed integer formats, :exc:`OverflowError` is raised if the value +is out of range for the C type. +For unsigned integer formats, no range checking is done --- the +most significant bits are silently truncated when the receiving field is too +small to receive the value. + ``b`` (:class:`int`) [unsigned char] - Convert a nonnegative Python integer to an unsigned tiny int, stored in a C + Convert a nonnegative Python integer to an unsigned tiny integer, stored in a C :c:expr:`unsigned char`. ``B`` (:class:`int`) [unsigned char] - Convert a Python integer to a tiny int without overflow checking, stored in a C + Convert a Python integer to a tiny integer without overflow checking, stored in a C :c:expr:`unsigned char`. ``h`` (:class:`int`) [short int] @@ -344,12 +356,6 @@ Other objects in *items*. The C arguments must correspond to the individual format units in *items*. Format units for sequences may be nested. -It is possible to pass "long" integers (integers whose value exceeds the -platform's :c:macro:`LONG_MAX`) however no proper range checking is done --- the -most significant bits are silently truncated when the receiving field is too -small to receive the value (actually, the semantics are inherited from downcasts -in C --- your mileage may vary). - A few other characters have a meaning in a format string. These may not occur inside nested parentheses. They are: From 1ef6bf4e29db43bbf06639923516838db2d5a5ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 6 Jan 2025 12:50:01 +0100 Subject: [PATCH 201/376] gh-111178: fix UBSan failures in `Objects/descrobject.c` (GH-128245) fix UBSan failures for `propertyobject` --- Objects/descrobject.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 4eccd1704eb95a..1852118359f014 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -1508,6 +1508,8 @@ PyWrapper_New(PyObject *d, PyObject *self) /* A built-in 'property' type */ +#define _propertyobject_CAST(op) ((propertyobject *)(op)) + /* class property(object): @@ -1911,8 +1913,9 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset, } static PyObject * -property_get__name__(propertyobject *prop, void *Py_UNUSED(ignored)) +property_get__name__(PyObject *op, void *Py_UNUSED(ignored)) { + propertyobject *prop = _propertyobject_CAST(op); PyObject *name; if (property_name(prop, &name) < 0) { return NULL; @@ -1925,16 +1928,17 @@ property_get__name__(propertyobject *prop, void *Py_UNUSED(ignored)) } static int -property_set__name__(propertyobject *prop, PyObject *value, - void *Py_UNUSED(ignored)) +property_set__name__(PyObject *op, PyObject *value, void *Py_UNUSED(ignored)) { + propertyobject *prop = _propertyobject_CAST(op); Py_XSETREF(prop->prop_name, Py_XNewRef(value)); return 0; } static PyObject * -property_get___isabstractmethod__(propertyobject *prop, void *closure) +property_get___isabstractmethod__(PyObject *op, void *closure) { + propertyobject *prop = _propertyobject_CAST(op); int res = _PyObject_IsAbstract(prop->prop_get); if (res == -1) { return NULL; @@ -1962,9 +1966,8 @@ property_get___isabstractmethod__(propertyobject *prop, void *closure) } static PyGetSetDef property_getsetlist[] = { - {"__name__", (getter)property_get__name__, (setter)property_set__name__}, - {"__isabstractmethod__", - (getter)property_get___isabstractmethod__, NULL, + {"__name__", property_get__name__, property_set__name__, NULL, NULL}, + {"__isabstractmethod__", property_get___isabstractmethod__, NULL, NULL, NULL}, {NULL} /* Sentinel */ From 657d7b77e5c69967e9c0000b986fa4872d13958c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 6 Jan 2025 14:28:50 +0200 Subject: [PATCH 202/376] gh-90241: Clarify documentation for PyUnicode_FSConverter and PyUnicode_FSDecoder (GH-128451) Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Co-authored-by: Erlend E. Aasland --- Doc/c-api/arg.rst | 12 +++++++++--- Doc/c-api/unicode.rst | 35 +++++++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 41c0366d205086..209056ef2f8bce 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -319,7 +319,7 @@ Other objects .. _o_ampersand: -``O&`` (object) [*converter*, *anything*] +``O&`` (object) [*converter*, *address*] Convert a Python object to a C variable through a *converter* function. This takes two arguments: the first is a function, the second is the address of a C variable (of arbitrary type), converted to :c:expr:`void *`. The *converter* @@ -333,14 +333,20 @@ Other objects the conversion has failed. When the conversion fails, the *converter* function should raise an exception and leave the content of *address* unmodified. - If the *converter* returns ``Py_CLEANUP_SUPPORTED``, it may get called a + .. c:macro:: Py_CLEANUP_SUPPORTED + :no-typesetting: + + If the *converter* returns :c:macro:`!Py_CLEANUP_SUPPORTED`, it may get called a second time if the argument parsing eventually fails, giving the converter a chance to release any memory that it had already allocated. In this second call, the *object* parameter will be ``NULL``; *address* will have the same value as in the original call. + Examples of converters: :c:func:`PyUnicode_FSConverter` and + :c:func:`PyUnicode_FSDecoder`. + .. versionchanged:: 3.1 - ``Py_CLEANUP_SUPPORTED`` was added. + :c:macro:`!Py_CLEANUP_SUPPORTED` was added. ``p`` (:class:`bool`) [int] Tests the value passed in for truth (a boolean **p**\ redicate) and converts diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index dcbc8804cd6b89..f19b86a8dbfb66 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -786,16 +786,25 @@ Functions encoding to and decoding from the :term:`filesystem encoding and error handler` (:pep:`383` and :pep:`529`). To encode file names to :class:`bytes` during argument parsing, the ``"O&"`` -converter should be used, passing :c:func:`PyUnicode_FSConverter` as the +converter should be used, passing :c:func:`!PyUnicode_FSConverter` as the conversion function: .. c:function:: int PyUnicode_FSConverter(PyObject* obj, void* result) - ParseTuple converter: encode :class:`str` objects -- obtained directly or + :ref:`PyArg_Parse\* converter `: encode :class:`str` objects -- obtained directly or through the :class:`os.PathLike` interface -- to :class:`bytes` using :c:func:`PyUnicode_EncodeFSDefault`; :class:`bytes` objects are output as-is. - *result* must be a :c:expr:`PyBytesObject*` which must be released when it is - no longer used. + *result* must be an address of a C variable of type :c:expr:`PyObject*` + (or :c:expr:`PyBytesObject*`). + On success, set the variable to a new :term:`strong reference` to + a :ref:`bytes object ` which must be released + when it is no longer used and return a non-zero value + (:c:macro:`Py_CLEANUP_SUPPORTED`). + Embedded null bytes are not allowed in the result. + On failure, return ``0`` with an exception set. + + If *obj* is ``NULL``, the function releases a strong reference + stored in the variable referred by *result* and returns ``1``. .. versionadded:: 3.1 @@ -803,16 +812,26 @@ conversion function: Accepts a :term:`path-like object`. To decode file names to :class:`str` during argument parsing, the ``"O&"`` -converter should be used, passing :c:func:`PyUnicode_FSDecoder` as the +converter should be used, passing :c:func:`!PyUnicode_FSDecoder` as the conversion function: .. c:function:: int PyUnicode_FSDecoder(PyObject* obj, void* result) - ParseTuple converter: decode :class:`bytes` objects -- obtained either + :ref:`PyArg_Parse\* converter `: decode :class:`bytes` objects -- obtained either directly or indirectly through the :class:`os.PathLike` interface -- to :class:`str` using :c:func:`PyUnicode_DecodeFSDefaultAndSize`; :class:`str` - objects are output as-is. *result* must be a :c:expr:`PyUnicodeObject*` which - must be released when it is no longer used. + objects are output as-is. + *result* must be an address of a C variable of type :c:expr:`PyObject*` + (or :c:expr:`PyUnicodeObject*`). + On success, set the variable to a new :term:`strong reference` to + a :ref:`Unicode object ` which must be released + when it is no longer used and return a non-zero value + (:c:macro:`Py_CLEANUP_SUPPORTED`). + Embedded null characters are not allowed in the result. + On failure, return ``0`` with an exception set. + + If *obj* is ``NULL``, release the strong reference + to the object referred to by *result* and return ``1``. .. versionadded:: 3.2 From 7e8c571604cd18e65cefd26bfc48082840264549 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Mon, 6 Jan 2025 18:05:11 +0530 Subject: [PATCH 203/376] gh-128340: add thread safe handle for `loop.call_soon_threadsafe` (#128369) Adds `_ThreadSafeHandle` to be used for callbacks scheduled with `loop.call_soon_threadsafe`. --- Lib/asyncio/base_events.py | 5 +- Lib/asyncio/events.py | 28 +++++ Lib/test/test_asyncio/test_events.py | 118 ++++++++++++++++++ ...-01-05-11-46-14.gh-issue-128340.gKI0uU.rst | 1 + 4 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-05-11-46-14.gh-issue-128340.gKI0uU.rst diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 5dbe4b28d236d3..9e6f6e3ee7e3ec 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -873,7 +873,10 @@ def call_soon_threadsafe(self, callback, *args, context=None): self._check_closed() if self._debug: self._check_callback(callback, 'call_soon_threadsafe') - handle = self._call_soon(callback, args, context) + handle = events._ThreadSafeHandle(callback, args, self, context) + self._ready.append(handle) + if handle._source_traceback: + del handle._source_traceback[-1] if handle._source_traceback: del handle._source_traceback[-1] self._write_to_self() diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index 6e291d28ec81ae..2ee9870e80f20b 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -113,6 +113,34 @@ def _run(self): self._loop.call_exception_handler(context) self = None # Needed to break cycles when an exception occurs. +# _ThreadSafeHandle is used for callbacks scheduled with call_soon_threadsafe +# and is thread safe unlike Handle which is not thread safe. +class _ThreadSafeHandle(Handle): + + __slots__ = ('_lock',) + + def __init__(self, callback, args, loop, context=None): + super().__init__(callback, args, loop, context) + self._lock = threading.RLock() + + def cancel(self): + with self._lock: + return super().cancel() + + def cancelled(self): + with self._lock: + return super().cancelled() + + def _run(self): + # The event loop checks for cancellation without holding the lock + # It is possible that the handle is cancelled after the check + # but before the callback is called so check it again after acquiring + # the lock and return without calling the callback if it is cancelled. + with self._lock: + if self._cancelled: + return + return super()._run() + class TimerHandle(Handle): """Object returned by timed callback registration methods.""" diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index c8439c9af5e6ba..ed75b909317357 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -353,6 +353,124 @@ def run_in_thread(): t.join() self.assertEqual(results, ['hello', 'world']) + def test_call_soon_threadsafe_handle_block_check_cancelled(self): + results = [] + + callback_started = threading.Event() + callback_finished = threading.Event() + def callback(arg): + callback_started.set() + results.append(arg) + time.sleep(1) + callback_finished.set() + + def run_in_thread(): + handle = self.loop.call_soon_threadsafe(callback, 'hello') + self.assertIsInstance(handle, events._ThreadSafeHandle) + callback_started.wait() + # callback started so it should block checking for cancellation + # until it finishes + self.assertFalse(handle.cancelled()) + self.assertTrue(callback_finished.is_set()) + self.loop.call_soon_threadsafe(self.loop.stop) + + t = threading.Thread(target=run_in_thread) + t.start() + + self.loop.run_forever() + t.join() + self.assertEqual(results, ['hello']) + + def test_call_soon_threadsafe_handle_block_cancellation(self): + results = [] + + callback_started = threading.Event() + callback_finished = threading.Event() + def callback(arg): + callback_started.set() + results.append(arg) + time.sleep(1) + callback_finished.set() + + def run_in_thread(): + handle = self.loop.call_soon_threadsafe(callback, 'hello') + self.assertIsInstance(handle, events._ThreadSafeHandle) + callback_started.wait() + # callback started so it cannot be cancelled from other thread until + # it finishes + handle.cancel() + self.assertTrue(callback_finished.is_set()) + self.loop.call_soon_threadsafe(self.loop.stop) + + t = threading.Thread(target=run_in_thread) + t.start() + + self.loop.run_forever() + t.join() + self.assertEqual(results, ['hello']) + + def test_call_soon_threadsafe_handle_cancel_same_thread(self): + results = [] + callback_started = threading.Event() + callback_finished = threading.Event() + + fut = concurrent.futures.Future() + def callback(arg): + callback_started.set() + handle = fut.result() + handle.cancel() + results.append(arg) + callback_finished.set() + self.loop.stop() + + def run_in_thread(): + handle = self.loop.call_soon_threadsafe(callback, 'hello') + fut.set_result(handle) + self.assertIsInstance(handle, events._ThreadSafeHandle) + callback_started.wait() + # callback cancels itself from same thread so it has no effect + # it runs to completion + self.assertTrue(handle.cancelled()) + self.assertTrue(callback_finished.is_set()) + self.loop.call_soon_threadsafe(self.loop.stop) + + t = threading.Thread(target=run_in_thread) + t.start() + + self.loop.run_forever() + t.join() + self.assertEqual(results, ['hello']) + + def test_call_soon_threadsafe_handle_cancel_other_thread(self): + results = [] + ev = threading.Event() + + callback_finished = threading.Event() + def callback(arg): + results.append(arg) + callback_finished.set() + self.loop.stop() + + def run_in_thread(): + handle = self.loop.call_soon_threadsafe(callback, 'hello') + # handle can be cancelled from other thread if not started yet + self.assertIsInstance(handle, events._ThreadSafeHandle) + handle.cancel() + self.assertTrue(handle.cancelled()) + self.assertFalse(callback_finished.is_set()) + ev.set() + self.loop.call_soon_threadsafe(self.loop.stop) + + # block the main loop until the callback is added and cancelled in the + # other thread + self.loop.call_soon(ev.wait) + t = threading.Thread(target=run_in_thread) + t.start() + self.loop.run_forever() + t.join() + self.assertEqual(results, []) + self.assertFalse(callback_finished.is_set()) + def test_call_soon_threadsafe_same_thread(self): results = [] diff --git a/Misc/NEWS.d/next/Library/2025-01-05-11-46-14.gh-issue-128340.gKI0uU.rst b/Misc/NEWS.d/next/Library/2025-01-05-11-46-14.gh-issue-128340.gKI0uU.rst new file mode 100644 index 00000000000000..790400a19f334b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-05-11-46-14.gh-issue-128340.gKI0uU.rst @@ -0,0 +1 @@ +Add internal thread safe handle to be used in :meth:`asyncio.loop.call_soon_threadsafe` for thread safe cancellation. From f89e5e20cb8964653ea7d6f53d3e40953b6548ce Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 6 Jan 2025 13:43:09 +0100 Subject: [PATCH 204/376] gh-127350: Add Py_fopen() and Py_fclose() functions (#127821) --- Doc/c-api/sys.rst | 32 ++++++++ Doc/whatsnew/3.14.rst | 6 ++ Include/cpython/fileutils.h | 10 ++- Lib/test/test_capi/test_file.py | 67 ++++++++++++++++ Lib/test/test_ssl.py | 3 +- ...-12-11-13-01-26.gh-issue-127350.uEBZZ4.rst | 5 ++ Modules/_ssl.c | 2 +- Modules/_ssl/debughelpers.c | 4 +- Modules/_testcapi/clinic/file.c.h | 48 +++++++++++ Modules/_testcapi/file.c | 35 ++++++++ Modules/_testcapi/object.c | 8 +- Modules/_testcapimodule.c | 12 +-- Modules/main.c | 4 +- Python/errors.c | 2 +- Python/fileutils.c | 79 ++++++++++++------- Python/import.c | 2 +- Python/pythonrun.c | 2 +- Python/sysmodule.c | 2 +- 18 files changed, 270 insertions(+), 53 deletions(-) create mode 100644 Lib/test/test_capi/test_file.py create mode 100644 Misc/NEWS.d/next/C_API/2024-12-11-13-01-26.gh-issue-127350.uEBZZ4.rst create mode 100644 Modules/_testcapi/clinic/file.c.h diff --git a/Doc/c-api/sys.rst b/Doc/c-api/sys.rst index c688afdca8231d..7a7d39aea20baf 100644 --- a/Doc/c-api/sys.rst +++ b/Doc/c-api/sys.rst @@ -216,6 +216,38 @@ Operating System Utilities The function now uses the UTF-8 encoding on Windows if :c:member:`PyPreConfig.legacy_windows_fs_encoding` is zero. +.. c:function:: FILE* Py_fopen(PyObject *path, const char *mode) + + Similar to :c:func:`!fopen`, but *path* is a Python object and + an exception is set on error. + + *path* must be a :class:`str` object, a :class:`bytes` object, + or a :term:`path-like object`. + + On success, return the new file pointer. + On error, set an exception and return ``NULL``. + + The file must be closed by :c:func:`Py_fclose` rather than calling directly + :c:func:`!fclose`. + + The file descriptor is created non-inheritable (:pep:`446`). + + The caller must hold the GIL. + + .. versionadded:: next + + +.. c:function:: int Py_fclose(FILE *file) + + Close a file that was opened by :c:func:`Py_fopen`. + + On success, return ``0``. + On error, return ``EOF`` and ``errno`` is set to indicate the error. + In either case, any further access (including another call to + :c:func:`Py_fclose`) to the stream results in undefined behavior. + + .. versionadded:: next + .. _systemfunctions: diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index f365db37217e95..16851b4e63ea2c 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1237,6 +1237,12 @@ New features :monitoring-event:`BRANCH_LEFT` and :monitoring-event:`BRANCH_RIGHT` events, respectively. +* Add :c:func:`Py_fopen` function to open a file. Similar to the + :c:func:`!fopen` function, but the *path* parameter is a Python object and an + exception is set on error. Add also :c:func:`Py_fclose` function to close a + file. + (Contributed by Victor Stinner in :gh:`127350`.) + Porting to Python 3.14 ---------------------- diff --git a/Include/cpython/fileutils.h b/Include/cpython/fileutils.h index b386ad107bde1f..702f89aca324c5 100644 --- a/Include/cpython/fileutils.h +++ b/Include/cpython/fileutils.h @@ -2,7 +2,13 @@ # error "this header file must not be included directly" #endif -// Used by _testcapi which must not use the internal C API -PyAPI_FUNC(FILE*) _Py_fopen_obj( +PyAPI_FUNC(FILE*) Py_fopen( PyObject *path, const char *mode); + +// Deprecated alias to Py_fopen() kept for backward compatibility +Py_DEPRECATED(3.14) PyAPI_FUNC(FILE*) _Py_fopen_obj( + PyObject *path, + const char *mode); + +PyAPI_FUNC(int) Py_fclose(FILE *file); diff --git a/Lib/test/test_capi/test_file.py b/Lib/test/test_capi/test_file.py new file mode 100644 index 00000000000000..8a08a0a93eb8b7 --- /dev/null +++ b/Lib/test/test_capi/test_file.py @@ -0,0 +1,67 @@ +import os +import unittest +from test import support +from test.support import import_helper, os_helper + +_testcapi = import_helper.import_module('_testcapi') + + +class CAPIFileTest(unittest.TestCase): + def test_py_fopen(self): + # Test Py_fopen() and Py_fclose() + + with open(__file__, "rb") as fp: + source = fp.read() + + for filename in (__file__, os.fsencode(__file__)): + with self.subTest(filename=filename): + data = _testcapi.py_fopen(filename, "rb") + self.assertEqual(data, source[:256]) + + data = _testcapi.py_fopen(os_helper.FakePath(filename), "rb") + self.assertEqual(data, source[:256]) + + filenames = [ + os_helper.TESTFN, + os.fsencode(os_helper.TESTFN), + ] + # TESTFN_UNDECODABLE cannot be used to create a file on macOS/WASI. + if os_helper.TESTFN_UNENCODABLE is not None: + filenames.append(os_helper.TESTFN_UNENCODABLE) + for filename in filenames: + with self.subTest(filename=filename): + try: + with open(filename, "wb") as fp: + fp.write(source) + + data = _testcapi.py_fopen(filename, "rb") + self.assertEqual(data, source[:256]) + finally: + os_helper.unlink(filename) + + # embedded null character/byte in the filename + with self.assertRaises(ValueError): + _testcapi.py_fopen("a\x00b", "rb") + with self.assertRaises(ValueError): + _testcapi.py_fopen(b"a\x00b", "rb") + + # non-ASCII mode failing with "Invalid argument" + with self.assertRaises(OSError): + _testcapi.py_fopen(__file__, "\xe9") + + # invalid filename type + for invalid_type in (123, object()): + with self.subTest(filename=invalid_type): + with self.assertRaises(TypeError): + _testcapi.py_fopen(invalid_type, "rb") + + if support.MS_WINDOWS: + with self.assertRaises(OSError): + # On Windows, the file mode is limited to 10 characters + _testcapi.py_fopen(__file__, "rt+, ccs=UTF-8") + + # CRASHES py_fopen(__file__, None) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index c16ef3f96f9a21..9863f3ffe97656 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -1325,8 +1325,7 @@ def test_load_verify_cadata(self): def test_load_dh_params(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) ctx.load_dh_params(DHFILE) - if os.name != 'nt': - ctx.load_dh_params(BYTES_DHFILE) + ctx.load_dh_params(BYTES_DHFILE) self.assertRaises(TypeError, ctx.load_dh_params) self.assertRaises(TypeError, ctx.load_dh_params, None) with self.assertRaises(FileNotFoundError) as cm: diff --git a/Misc/NEWS.d/next/C_API/2024-12-11-13-01-26.gh-issue-127350.uEBZZ4.rst b/Misc/NEWS.d/next/C_API/2024-12-11-13-01-26.gh-issue-127350.uEBZZ4.rst new file mode 100644 index 00000000000000..d1b528c673442f --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2024-12-11-13-01-26.gh-issue-127350.uEBZZ4.rst @@ -0,0 +1,5 @@ +Add :c:func:`Py_fopen` function to open a file. Similar to the :c:func:`!fopen` +function, but the *path* parameter is a Python object and an exception is set +on error. Add also :c:func:`Py_fclose` function to close a file, function +needed for Windows support. +Patch by Victor Stinner. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 74cf99957389e2..87739832fbf784 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -4377,7 +4377,7 @@ _ssl__SSLContext_load_dh_params_impl(PySSLContext *self, PyObject *filepath) FILE *f; DH *dh; - f = _Py_fopen_obj(filepath, "rb"); + f = Py_fopen(filepath, "rb"); if (f == NULL) return NULL; diff --git a/Modules/_ssl/debughelpers.c b/Modules/_ssl/debughelpers.c index 9c87f8b4d21e68..318c045a0eec3c 100644 --- a/Modules/_ssl/debughelpers.c +++ b/Modules/_ssl/debughelpers.c @@ -180,8 +180,8 @@ _PySSLContext_set_keylog_filename(PySSLContext *self, PyObject *arg, void *c) { return 0; } - /* _Py_fopen_obj() also checks that arg is of proper type. */ - fp = _Py_fopen_obj(arg, "a" PY_STDIOTEXTMODE); + /* Py_fopen() also checks that arg is of proper type. */ + fp = Py_fopen(arg, "a" PY_STDIOTEXTMODE); if (fp == NULL) return -1; diff --git a/Modules/_testcapi/clinic/file.c.h b/Modules/_testcapi/clinic/file.c.h new file mode 100644 index 00000000000000..2ca21fffcef680 --- /dev/null +++ b/Modules/_testcapi/clinic/file.c.h @@ -0,0 +1,48 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + +PyDoc_STRVAR(_testcapi_py_fopen__doc__, +"py_fopen($module, path, mode, /)\n" +"--\n" +"\n" +"Call Py_fopen(), fread(256) and Py_fclose(). Return read bytes."); + +#define _TESTCAPI_PY_FOPEN_METHODDEF \ + {"py_fopen", _PyCFunction_CAST(_testcapi_py_fopen), METH_FASTCALL, _testcapi_py_fopen__doc__}, + +static PyObject * +_testcapi_py_fopen_impl(PyObject *module, PyObject *path, const char *mode); + +static PyObject * +_testcapi_py_fopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *path; + const char *mode; + + if (!_PyArg_CheckPositional("py_fopen", nargs, 2, 2)) { + goto exit; + } + path = args[0]; + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("py_fopen", "argument 2", "str", args[1]); + goto exit; + } + Py_ssize_t mode_length; + mode = PyUnicode_AsUTF8AndSize(args[1], &mode_length); + if (mode == NULL) { + goto exit; + } + if (strlen(mode) != (size_t)mode_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } + return_value = _testcapi_py_fopen_impl(module, path, mode); + +exit: + return return_value; +} +/*[clinic end generated code: output=c9fe964c3e5a0c32 input=a9049054013a1b77]*/ diff --git a/Modules/_testcapi/file.c b/Modules/_testcapi/file.c index 634563f6ea12cb..4bad43010fd440 100644 --- a/Modules/_testcapi/file.c +++ b/Modules/_testcapi/file.c @@ -1,8 +1,43 @@ +// clinic/file.c.h uses internal pycore_modsupport.h API +#define PYTESTCAPI_NEED_INTERNAL_API + #include "parts.h" #include "util.h" +#include "clinic/file.c.h" + +/*[clinic input] +module _testcapi +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=6361033e795369fc]*/ + +/*[clinic input] +_testcapi.py_fopen + + path: object + mode: str + / + +Call Py_fopen(), fread(256) and Py_fclose(). Return read bytes. +[clinic start generated code]*/ +static PyObject * +_testcapi_py_fopen_impl(PyObject *module, PyObject *path, const char *mode) +/*[clinic end generated code: output=5a900af000f759de input=d7e7b8f0fd151953]*/ +{ + FILE *fp = Py_fopen(path, mode); + if (fp == NULL) { + return NULL; + } + + char buffer[256]; + size_t size = fread(buffer, 1, Py_ARRAY_LENGTH(buffer), fp); + Py_fclose(fp); + + return PyBytes_FromStringAndSize(buffer, size); +} static PyMethodDef test_methods[] = { + _TESTCAPI_PY_FOPEN_METHODDEF {NULL}, }; diff --git a/Modules/_testcapi/object.c b/Modules/_testcapi/object.c index 3af5429ef00985..841410c52b3ce2 100644 --- a/Modules/_testcapi/object.c +++ b/Modules/_testcapi/object.c @@ -15,7 +15,7 @@ call_pyobject_print(PyObject *self, PyObject * args) return NULL; } - fp = _Py_fopen_obj(filename, "w+"); + fp = Py_fopen(filename, "w+"); if (Py_IsTrue(print_raw)) { flags = Py_PRINT_RAW; @@ -41,7 +41,7 @@ pyobject_print_null(PyObject *self, PyObject *args) return NULL; } - fp = _Py_fopen_obj(filename, "w+"); + fp = Py_fopen(filename, "w+"); if (PyObject_Print(NULL, fp, 0) < 0) { fclose(fp); @@ -72,7 +72,7 @@ pyobject_print_noref_object(PyObject *self, PyObject *args) return NULL; } - fp = _Py_fopen_obj(filename, "w+"); + fp = Py_fopen(filename, "w+"); if (PyObject_Print(test_string, fp, 0) < 0){ fclose(fp); @@ -103,7 +103,7 @@ pyobject_print_os_error(PyObject *self, PyObject *args) } // open file in read mode to induce OSError - fp = _Py_fopen_obj(filename, "r"); + fp = Py_fopen(filename, "r"); if (PyObject_Print(test_string, fp, 0) < 0) { fclose(fp); diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index f737250ac29d57..cd9014118f2d7f 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1744,7 +1744,7 @@ pymarshal_write_long_to_file(PyObject* self, PyObject *args) &value, &filename, &version)) return NULL; - fp = _Py_fopen_obj(filename, "wb"); + fp = Py_fopen(filename, "wb"); if (fp == NULL) { PyErr_SetFromErrno(PyExc_OSError); return NULL; @@ -1769,7 +1769,7 @@ pymarshal_write_object_to_file(PyObject* self, PyObject *args) &obj, &filename, &version)) return NULL; - fp = _Py_fopen_obj(filename, "wb"); + fp = Py_fopen(filename, "wb"); if (fp == NULL) { PyErr_SetFromErrno(PyExc_OSError); return NULL; @@ -1793,7 +1793,7 @@ pymarshal_read_short_from_file(PyObject* self, PyObject *args) if (!PyArg_ParseTuple(args, "O:pymarshal_read_short_from_file", &filename)) return NULL; - fp = _Py_fopen_obj(filename, "rb"); + fp = Py_fopen(filename, "rb"); if (fp == NULL) { PyErr_SetFromErrno(PyExc_OSError); return NULL; @@ -1818,7 +1818,7 @@ pymarshal_read_long_from_file(PyObject* self, PyObject *args) if (!PyArg_ParseTuple(args, "O:pymarshal_read_long_from_file", &filename)) return NULL; - fp = _Py_fopen_obj(filename, "rb"); + fp = Py_fopen(filename, "rb"); if (fp == NULL) { PyErr_SetFromErrno(PyExc_OSError); return NULL; @@ -1840,7 +1840,7 @@ pymarshal_read_last_object_from_file(PyObject* self, PyObject *args) if (!PyArg_ParseTuple(args, "O:pymarshal_read_last_object_from_file", &filename)) return NULL; - FILE *fp = _Py_fopen_obj(filename, "rb"); + FILE *fp = Py_fopen(filename, "rb"); if (fp == NULL) { PyErr_SetFromErrno(PyExc_OSError); return NULL; @@ -1863,7 +1863,7 @@ pymarshal_read_object_from_file(PyObject* self, PyObject *args) if (!PyArg_ParseTuple(args, "O:pymarshal_read_object_from_file", &filename)) return NULL; - FILE *fp = _Py_fopen_obj(filename, "rb"); + FILE *fp = Py_fopen(filename, "rb"); if (fp == NULL) { PyErr_SetFromErrno(PyExc_OSError); return NULL; diff --git a/Modules/main.c b/Modules/main.c index 3bf2241f2837a3..5bb1de2d04d30c 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -370,7 +370,7 @@ pymain_run_file_obj(PyObject *program_name, PyObject *filename, return pymain_exit_err_print(); } - FILE *fp = _Py_fopen_obj(filename, "rb"); + FILE *fp = Py_fopen(filename, "rb"); if (fp == NULL) { // Ignore the OSError PyErr_Clear(); @@ -465,7 +465,7 @@ pymain_run_startup(PyConfig *config, int *exitcode) goto error; } - FILE *fp = _Py_fopen_obj(startup, "r"); + FILE *fp = Py_fopen(startup, "r"); if (fp == NULL) { int save_errno = errno; PyErr_Clear(); diff --git a/Python/errors.c b/Python/errors.c index 2d362c1864ffff..b6ac2f767a283b 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -1981,7 +1981,7 @@ _PyErr_ProgramDecodedTextObject(PyObject *filename, int lineno, const char* enco return NULL; } - FILE *fp = _Py_fopen_obj(filename, "r" PY_STDIOTEXTMODE); + FILE *fp = Py_fopen(filename, "r" PY_STDIOTEXTMODE); if (fp == NULL) { PyErr_Clear(); return NULL; diff --git a/Python/fileutils.c b/Python/fileutils.c index 81276651f6df44..6bc3a44c3c1313 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -1748,8 +1748,10 @@ _Py_wfopen(const wchar_t *path, const wchar_t *mode) } -/* Open a file. Call _wfopen() on Windows, or encode the path to the filesystem - encoding and call fopen() otherwise. +/* Open a file. + + On Windows, if 'path' is a Unicode string, call _wfopen(). Otherwise, encode + the path to the filesystem encoding and call fopen(). Return the new file object on success. Raise an exception and return NULL on error. @@ -1762,32 +1764,32 @@ _Py_wfopen(const wchar_t *path, const wchar_t *mode) Release the GIL to call _wfopen() or fopen(). The caller must hold the GIL. */ FILE* -_Py_fopen_obj(PyObject *path, const char *mode) +Py_fopen(PyObject *path, const char *mode) { - FILE *f; - int async_err = 0; -#ifdef MS_WINDOWS - wchar_t wmode[10]; - int usize; - assert(PyGILState_Check()); if (PySys_Audit("open", "Osi", path, mode, 0) < 0) { return NULL; } - if (!PyUnicode_Check(path)) { - PyErr_Format(PyExc_TypeError, - "str file path expected under Windows, got %R", - Py_TYPE(path)); + + FILE *f; + int async_err = 0; + int saved_errno; +#ifdef MS_WINDOWS + PyObject *unicode; + if (!PyUnicode_FSDecoder(path, &unicode)) { return NULL; } - wchar_t *wpath = PyUnicode_AsWideCharString(path, NULL); - if (wpath == NULL) + wchar_t *wpath = PyUnicode_AsWideCharString(unicode, NULL); + Py_DECREF(unicode); + if (wpath == NULL) { return NULL; + } - usize = MultiByteToWideChar(CP_ACP, 0, mode, -1, - wmode, Py_ARRAY_LENGTH(wmode)); + wchar_t wmode[10]; + int usize = MultiByteToWideChar(CP_ACP, 0, mode, -1, + wmode, Py_ARRAY_LENGTH(wmode)); if (usize == 0) { PyErr_SetFromWindowsErr(0); PyMem_Free(wpath); @@ -1796,26 +1798,20 @@ _Py_fopen_obj(PyObject *path, const char *mode) do { Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH f = _wfopen(wpath, wmode); + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS } while (f == NULL && errno == EINTR && !(async_err = PyErr_CheckSignals())); - int saved_errno = errno; + saved_errno = errno; PyMem_Free(wpath); #else PyObject *bytes; - const char *path_bytes; - - assert(PyGILState_Check()); - - if (!PyUnicode_FSConverter(path, &bytes)) - return NULL; - path_bytes = PyBytes_AS_STRING(bytes); - - if (PySys_Audit("open", "Osi", path, mode, 0) < 0) { - Py_DECREF(bytes); + if (!PyUnicode_FSConverter(path, &bytes)) { return NULL; } + const char *path_bytes = PyBytes_AS_STRING(bytes); do { Py_BEGIN_ALLOW_THREADS @@ -1823,11 +1819,13 @@ _Py_fopen_obj(PyObject *path, const char *mode) Py_END_ALLOW_THREADS } while (f == NULL && errno == EINTR && !(async_err = PyErr_CheckSignals())); - int saved_errno = errno; + saved_errno = errno; Py_DECREF(bytes); #endif - if (async_err) + + if (async_err) { return NULL; + } if (f == NULL) { errno = saved_errno; @@ -1842,6 +1840,27 @@ _Py_fopen_obj(PyObject *path, const char *mode) return f; } + +// Deprecated alias to Py_fopen() kept for backward compatibility +FILE* +_Py_fopen_obj(PyObject *path, const char *mode) +{ + return Py_fopen(path, mode); +} + + +// Call fclose(). +// +// On Windows, files opened by Py_fopen() in the Python DLL must be closed by +// the Python DLL to use the same C runtime version. Otherwise, calling +// fclose() directly can cause undefined behavior. +int +Py_fclose(FILE *file) +{ + return fclose(file); +} + + /* Read count bytes from fd into buf. On success, return the number of read bytes, it can be lower than count. diff --git a/Python/import.c b/Python/import.c index a9282dde633959..b3648e24d0e064 100644 --- a/Python/import.c +++ b/Python/import.c @@ -4688,7 +4688,7 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) * code relies on fp still being open. */ FILE *fp; if (file != NULL) { - fp = _Py_fopen_obj(info.filename, "r"); + fp = Py_fopen(info.filename, "r"); if (fp == NULL) { goto finally; } diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 31e065ff00d59a..0da26ad3f9b4bd 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -467,7 +467,7 @@ _PyRun_SimpleFileObject(FILE *fp, PyObject *filename, int closeit, fclose(fp); } - pyc_fp = _Py_fopen_obj(filename, "rb"); + pyc_fp = Py_fopen(filename, "rb"); if (pyc_fp == NULL) { fprintf(stderr, "python: Can't reopen .pyc file\n"); goto done; diff --git a/Python/sysmodule.c b/Python/sysmodule.c index d6719f9bb0af91..887591a681b25c 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2356,7 +2356,7 @@ static PyObject * sys__dump_tracelets_impl(PyObject *module, PyObject *outpath) /*[clinic end generated code: output=a7fe265e2bc3b674 input=5bff6880cd28ffd1]*/ { - FILE *out = _Py_fopen_obj(outpath, "wb"); + FILE *out = Py_fopen(outpath, "wb"); if (out == NULL) { return NULL; } From b9c693dcca01537eee1ef716ffebc632be37594b Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 6 Jan 2025 14:16:22 +0000 Subject: [PATCH 205/376] GH-128073: Include `EXIT_IF` when checking for escaping calls (GH-128537) --- Lib/test/test_generated_cases.py | 25 +++++++++++++++++++++++++ Tools/cases_generator/analyzer.py | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 9c65e81dfe4be1..75cbd8dd94e9cb 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -1713,6 +1713,31 @@ def test_pop_dead_inputs_with_output(self): """ self.run_cases_test(input, output) + def test_no_escaping_calls_in_branching_macros(self): + + input = """ + inst(OP, ( -- )) { + DEOPT_IF(escaping_call()); + } + """ + with self.assertRaises(SyntaxError): + self.run_cases_test(input, "") + + input = """ + inst(OP, ( -- )) { + EXIT_IF(escaping_call()); + } + """ + with self.assertRaises(SyntaxError): + self.run_cases_test(input, "") + + input = """ + inst(OP, ( -- )) { + ERROR_IF(escaping_call(), error); + } + """ + with self.assertRaises(SyntaxError): + self.run_cases_test(input, "") class TestGeneratedAbstractCases(unittest.TestCase): def setUp(self) -> None: diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index eca851e6de87ae..73c871759afbf5 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -668,7 +668,7 @@ def check_escaping_calls(instr: parser.InstDef, escapes: dict[lexer.Token, tuple if tkn.kind == "IF": next(tkn_iter) in_if = 1 - if tkn.kind == "IDENTIFIER" and tkn.text in ("DEOPT_IF", "ERROR_IF"): + if tkn.kind == "IDENTIFIER" and tkn.text in ("DEOPT_IF", "ERROR_IF", "EXIT_IF"): next(tkn_iter) in_if = 1 elif tkn.kind == "LPAREN" and in_if: From f826beca0cedb8e4b92896544c75fd0d9dcb0446 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 6 Jan 2025 17:54:47 +0000 Subject: [PATCH 206/376] GH-128375: Better instrument for `FOR_ITER` (GH-128445) --- Include/internal/pycore_instruments.h | 4 +- Include/internal/pycore_magic_number.h | 3 +- Include/internal/pycore_opcode_metadata.h | 39 +- Include/internal/pycore_uop_ids.h | 1 + Include/internal/pycore_uop_metadata.h | 4 + Include/opcode_ids.h | 182 ++++---- Lib/_opcode_metadata.py | 182 ++++---- Lib/test/test_code.py | 40 ++ Lib/test/test_compiler_codegen.py | 3 +- Lib/test/test_dis.py | 392 +++++++++--------- Lib/test/test_monitoring.py | 92 +++- Modules/_testcapimodule.c | 21 + Programs/test_frozenmain.h | 66 +-- Python/bytecodes.c | 57 +-- Python/ceval_macros.h | 2 +- Python/codegen.c | 6 +- Python/executor_cases.c.h | 14 + Python/generated_cases.c.h | 71 ++-- Python/instrumentation.c | 151 ++++--- Python/opcode_targets.h | 4 +- Python/optimizer.c | 12 +- Python/optimizer_cases.c.h | 6 + Tools/cases_generator/analyzer.py | 4 + Tools/cases_generator/generators_common.py | 2 + Tools/cases_generator/lexer.py | 1 + .../opcode_metadata_generator.py | 5 +- Tools/cases_generator/tier1_generator.py | 7 +- 27 files changed, 827 insertions(+), 544 deletions(-) diff --git a/Include/internal/pycore_instruments.h b/Include/internal/pycore_instruments.h index 4e5b374968ea98..92d8f056f402fc 100644 --- a/Include/internal/pycore_instruments.h +++ b/Include/internal/pycore_instruments.h @@ -48,8 +48,8 @@ _Py_call_instrumentation_instruction( _Py_CODEUNIT * _Py_call_instrumentation_jump( - PyThreadState *tstate, int event, - _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, _Py_CODEUNIT *target); + _Py_CODEUNIT *instr, PyThreadState *tstate, int event, + _PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest); extern int _Py_call_instrumentation_arg(PyThreadState *tstate, int event, diff --git a/Include/internal/pycore_magic_number.h b/Include/internal/pycore_magic_number.h index ec3685d2034560..f9f71d7453331e 100644 --- a/Include/internal/pycore_magic_number.h +++ b/Include/internal/pycore_magic_number.h @@ -264,6 +264,7 @@ Known values: Python 3.14a2 3609 (Add LOAD_SMALL_INT and LOAD_CONST_IMMORTAL instructions, remove RETURN_CONST) Python 3.14a4 3610 (Add VALUE_WITH_FAKE_GLOBALS format to annotationlib) Python 3.14a4 3611 (Add NOT_TAKEN instruction) + Python 3.14a4 3612 (Add POP_ITER and INSTRUMENTED_POP_ITER) Python 3.15 will start with 3650 @@ -276,7 +277,7 @@ PC/launcher.c must also be updated. */ -#define PYC_MAGIC_NUMBER 3611 +#define PYC_MAGIC_NUMBER 3612 /* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes (little-endian) and then appending b'\r\n'. */ #define PYC_MAGIC_NUMBER_TOKEN \ diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 5fb236836dccd9..90d5e277d8d6ce 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -245,6 +245,8 @@ int _PyOpcode_num_popped(int opcode, int oparg) { return 0; case INSTRUMENTED_NOT_TAKEN: return 0; + case INSTRUMENTED_POP_ITER: + return 1; case INSTRUMENTED_POP_JUMP_IF_FALSE: return 0; case INSTRUMENTED_POP_JUMP_IF_NONE: @@ -375,6 +377,8 @@ int _PyOpcode_num_popped(int opcode, int oparg) { return 0; case POP_EXCEPT: return 1; + case POP_ITER: + return 1; case POP_JUMP_IF_FALSE: return 1; case POP_JUMP_IF_NONE: @@ -708,6 +712,8 @@ int _PyOpcode_num_pushed(int opcode, int oparg) { return 0; case INSTRUMENTED_NOT_TAKEN: return 0; + case INSTRUMENTED_POP_ITER: + return 0; case INSTRUMENTED_POP_JUMP_IF_FALSE: return 0; case INSTRUMENTED_POP_JUMP_IF_NONE: @@ -838,6 +844,8 @@ int _PyOpcode_num_pushed(int opcode, int oparg) { return 0; case POP_EXCEPT: return 0; + case POP_ITER: + return 0; case POP_JUMP_IF_FALSE: return 0; case POP_JUMP_IF_NONE: @@ -1399,6 +1407,10 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) { *effect = 0; return 0; } + case INSTRUMENTED_POP_ITER: { + *effect = -1; + return 0; + } case INSTRUMENTED_POP_JUMP_IF_FALSE: { *effect = 0; return 0; @@ -1659,6 +1671,10 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) { *effect = -1; return 0; } + case POP_ITER: { + *effect = -1; + return 0; + } case POP_JUMP_IF_FALSE: { *effect = -1; return 0; @@ -1921,6 +1937,7 @@ enum InstructionFormat { #define HAS_PASSTHROUGH_FLAG (4096) #define HAS_OPARG_AND_1_FLAG (8192) #define HAS_ERROR_NO_POP_FLAG (16384) +#define HAS_NO_SAVE_IP_FLAG (32768) #define OPCODE_HAS_ARG(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_ARG_FLAG)) #define OPCODE_HAS_CONST(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_CONST_FLAG)) #define OPCODE_HAS_NAME(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_NAME_FLAG)) @@ -1936,6 +1953,7 @@ enum InstructionFormat { #define OPCODE_HAS_PASSTHROUGH(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_PASSTHROUGH_FLAG)) #define OPCODE_HAS_OPARG_AND_1(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_OPARG_AND_1_FLAG)) #define OPCODE_HAS_ERROR_NO_POP(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_ERROR_NO_POP_FLAG)) +#define OPCODE_HAS_NO_SAVE_IP(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_NO_SAVE_IP_FLAG)) #define OPARG_FULL 0 #define OPARG_CACHE_1 1 @@ -1948,8 +1966,8 @@ enum InstructionFormat { struct opcode_metadata { uint8_t valid_entry; - int8_t instr_format; - int16_t flags; + uint8_t instr_format; + uint16_t flags; }; extern const struct opcode_metadata _PyOpcode_opcode_metadata[266]; @@ -2028,7 +2046,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [DICT_MERGE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [DICT_UPDATE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [END_ASYNC_FOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, - [END_FOR] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, + [END_FOR] = { true, INSTR_FMT_IX, HAS_NO_SAVE_IP_FLAG }, [END_SEND] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, [ENTER_EXECUTOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, [EXIT_INIT_CHECK] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, @@ -2051,15 +2069,16 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [INSTRUMENTED_CALL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [INSTRUMENTED_CALL_FUNCTION_EX] = { true, INSTR_FMT_IX, 0 }, [INSTRUMENTED_CALL_KW] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [INSTRUMENTED_END_FOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, + [INSTRUMENTED_END_FOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG | HAS_NO_SAVE_IP_FLAG }, [INSTRUMENTED_END_SEND] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, - [INSTRUMENTED_FOR_ITER] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, + [INSTRUMENTED_FOR_ITER] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [INSTRUMENTED_INSTRUCTION] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [INSTRUMENTED_JUMP_BACKWARD] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [INSTRUMENTED_JUMP_FORWARD] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, [INSTRUMENTED_LINE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, [INSTRUMENTED_LOAD_SUPER_ATTR] = { true, INSTR_FMT_IXC, 0 }, [INSTRUMENTED_NOT_TAKEN] = { true, INSTR_FMT_IX, 0 }, + [INSTRUMENTED_POP_ITER] = { true, INSTR_FMT_IX, 0 }, [INSTRUMENTED_POP_JUMP_IF_FALSE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG }, [INSTRUMENTED_POP_JUMP_IF_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG }, [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG }, @@ -2119,6 +2138,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [NOP] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, [NOT_TAKEN] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, [POP_EXCEPT] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, + [POP_ITER] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, [POP_JUMP_IF_FALSE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [POP_JUMP_IF_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [POP_JUMP_IF_NOT_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, @@ -2261,7 +2281,7 @@ _PyOpcode_macro_expansion[256] = { [DELETE_SUBSCR] = { .nuops = 1, .uops = { { _DELETE_SUBSCR, 0, 0 } } }, [DICT_MERGE] = { .nuops = 1, .uops = { { _DICT_MERGE, 0, 0 } } }, [DICT_UPDATE] = { .nuops = 1, .uops = { { _DICT_UPDATE, 0, 0 } } }, - [END_FOR] = { .nuops = 1, .uops = { { _POP_TOP, 0, 0 } } }, + [END_FOR] = { .nuops = 1, .uops = { { _END_FOR, 0, 0 } } }, [END_SEND] = { .nuops = 1, .uops = { { _END_SEND, 0, 0 } } }, [EXIT_INIT_CHECK] = { .nuops = 1, .uops = { { _EXIT_INIT_CHECK, 0, 0 } } }, [FORMAT_SIMPLE] = { .nuops = 1, .uops = { { _FORMAT_SIMPLE, 0, 0 } } }, @@ -2324,6 +2344,7 @@ _PyOpcode_macro_expansion[256] = { [NOP] = { .nuops = 1, .uops = { { _NOP, 0, 0 } } }, [NOT_TAKEN] = { .nuops = 1, .uops = { { _NOP, 0, 0 } } }, [POP_EXCEPT] = { .nuops = 1, .uops = { { _POP_EXCEPT, 0, 0 } } }, + [POP_ITER] = { .nuops = 1, .uops = { { _POP_TOP, 0, 0 } } }, [POP_JUMP_IF_FALSE] = { .nuops = 1, .uops = { { _POP_JUMP_IF_FALSE, 9, 1 } } }, [POP_JUMP_IF_NONE] = { .nuops = 2, .uops = { { _IS_NONE, 0, 0 }, { _POP_JUMP_IF_TRUE, 9, 1 } } }, [POP_JUMP_IF_NOT_NONE] = { .nuops = 2, .uops = { { _IS_NONE, 0, 0 }, { _POP_JUMP_IF_FALSE, 9, 1 } } }, @@ -2482,6 +2503,7 @@ const char *_PyOpcode_OpName[266] = { [INSTRUMENTED_LINE] = "INSTRUMENTED_LINE", [INSTRUMENTED_LOAD_SUPER_ATTR] = "INSTRUMENTED_LOAD_SUPER_ATTR", [INSTRUMENTED_NOT_TAKEN] = "INSTRUMENTED_NOT_TAKEN", + [INSTRUMENTED_POP_ITER] = "INSTRUMENTED_POP_ITER", [INSTRUMENTED_POP_JUMP_IF_FALSE] = "INSTRUMENTED_POP_JUMP_IF_FALSE", [INSTRUMENTED_POP_JUMP_IF_NONE] = "INSTRUMENTED_POP_JUMP_IF_NONE", [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = "INSTRUMENTED_POP_JUMP_IF_NOT_NONE", @@ -2547,6 +2569,7 @@ const char *_PyOpcode_OpName[266] = { [NOT_TAKEN] = "NOT_TAKEN", [POP_BLOCK] = "POP_BLOCK", [POP_EXCEPT] = "POP_EXCEPT", + [POP_ITER] = "POP_ITER", [POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE", [POP_JUMP_IF_NONE] = "POP_JUMP_IF_NONE", [POP_JUMP_IF_NOT_NONE] = "POP_JUMP_IF_NOT_NONE", @@ -2740,6 +2763,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [INSTRUMENTED_LINE] = INSTRUMENTED_LINE, [INSTRUMENTED_LOAD_SUPER_ATTR] = INSTRUMENTED_LOAD_SUPER_ATTR, [INSTRUMENTED_NOT_TAKEN] = INSTRUMENTED_NOT_TAKEN, + [INSTRUMENTED_POP_ITER] = INSTRUMENTED_POP_ITER, [INSTRUMENTED_POP_JUMP_IF_FALSE] = INSTRUMENTED_POP_JUMP_IF_FALSE, [INSTRUMENTED_POP_JUMP_IF_NONE] = INSTRUMENTED_POP_JUMP_IF_NONE, [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = INSTRUMENTED_POP_JUMP_IF_NOT_NONE, @@ -2799,6 +2823,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [NOP] = NOP, [NOT_TAKEN] = NOT_TAKEN, [POP_EXCEPT] = POP_EXCEPT, + [POP_ITER] = POP_ITER, [POP_JUMP_IF_FALSE] = POP_JUMP_IF_FALSE, [POP_JUMP_IF_NONE] = POP_JUMP_IF_NONE, [POP_JUMP_IF_NOT_NONE] = POP_JUMP_IF_NOT_NONE, @@ -2856,7 +2881,6 @@ const uint8_t _PyOpcode_Deopt[256] = { #endif // NEED_OPCODE_METADATA #define EXTRA_CASES \ - case 117: \ case 118: \ case 119: \ case 120: \ @@ -2895,7 +2919,6 @@ const uint8_t _PyOpcode_Deopt[256] = { case 232: \ case 233: \ case 234: \ - case 235: \ ; struct pseudo_targets { uint8_t as_sequence; diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 92515b4230ccb4..21690a28839565 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -101,6 +101,7 @@ extern "C" { #define _DO_CALL_FUNCTION_EX 358 #define _DO_CALL_KW 359 #define _DYNAMIC_EXIT 360 +#define _END_FOR END_FOR #define _END_SEND END_SEND #define _ERROR_POP_N 361 #define _EXIT_INIT_CHECK EXIT_INIT_CHECK diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 73fc29eb78a7a4..83e578cdd76fbd 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -55,6 +55,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_STORE_FAST_STORE_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, [_POP_TOP] = HAS_PURE_FLAG, [_PUSH_NULL] = HAS_PURE_FLAG, + [_END_FOR] = HAS_NO_SAVE_IP_FLAG, [_END_SEND] = HAS_PURE_FLAG, [_UNARY_NEGATIVE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_UNARY_NOT] = HAS_PURE_FLAG, @@ -391,6 +392,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_DICT_MERGE] = "_DICT_MERGE", [_DICT_UPDATE] = "_DICT_UPDATE", [_DYNAMIC_EXIT] = "_DYNAMIC_EXIT", + [_END_FOR] = "_END_FOR", [_END_SEND] = "_END_SEND", [_ERROR_POP_N] = "_ERROR_POP_N", [_EXIT_INIT_CHECK] = "_EXIT_INIT_CHECK", @@ -655,6 +657,8 @@ int _PyUop_num_popped(int opcode, int oparg) return 1; case _PUSH_NULL: return 0; + case _END_FOR: + return 1; case _END_SEND: return 2; case _UNARY_NEGATIVE: diff --git a/Include/opcode_ids.h b/Include/opcode_ids.h index 3cd189b93dd9d6..09e261fadd5544 100644 --- a/Include/opcode_ids.h +++ b/Include/opcode_ids.h @@ -40,93 +40,94 @@ extern "C" { #define NOP 27 #define NOT_TAKEN 28 #define POP_EXCEPT 29 -#define POP_TOP 30 -#define PUSH_EXC_INFO 31 -#define PUSH_NULL 32 -#define RETURN_GENERATOR 33 -#define RETURN_VALUE 34 -#define SETUP_ANNOTATIONS 35 -#define STORE_SLICE 36 -#define STORE_SUBSCR 37 -#define TO_BOOL 38 -#define UNARY_INVERT 39 -#define UNARY_NEGATIVE 40 -#define UNARY_NOT 41 -#define WITH_EXCEPT_START 42 -#define BINARY_OP 43 -#define BUILD_LIST 44 -#define BUILD_MAP 45 -#define BUILD_SET 46 -#define BUILD_SLICE 47 -#define BUILD_STRING 48 -#define BUILD_TUPLE 49 -#define CALL 50 -#define CALL_FUNCTION_EX 51 -#define CALL_INTRINSIC_1 52 -#define CALL_INTRINSIC_2 53 -#define CALL_KW 54 -#define COMPARE_OP 55 -#define CONTAINS_OP 56 -#define CONVERT_VALUE 57 -#define COPY 58 -#define COPY_FREE_VARS 59 -#define DELETE_ATTR 60 -#define DELETE_DEREF 61 -#define DELETE_FAST 62 -#define DELETE_GLOBAL 63 -#define DELETE_NAME 64 -#define DICT_MERGE 65 -#define DICT_UPDATE 66 -#define EXTENDED_ARG 67 -#define FOR_ITER 68 -#define GET_AWAITABLE 69 -#define IMPORT_FROM 70 -#define IMPORT_NAME 71 -#define IS_OP 72 -#define JUMP_BACKWARD 73 -#define JUMP_BACKWARD_NO_INTERRUPT 74 -#define JUMP_FORWARD 75 -#define LIST_APPEND 76 -#define LIST_EXTEND 77 -#define LOAD_ATTR 78 -#define LOAD_COMMON_CONSTANT 79 -#define LOAD_CONST 80 -#define LOAD_DEREF 81 -#define LOAD_FAST 82 -#define LOAD_FAST_AND_CLEAR 83 -#define LOAD_FAST_CHECK 84 -#define LOAD_FAST_LOAD_FAST 85 -#define LOAD_FROM_DICT_OR_DEREF 86 -#define LOAD_FROM_DICT_OR_GLOBALS 87 -#define LOAD_GLOBAL 88 -#define LOAD_NAME 89 -#define LOAD_SMALL_INT 90 -#define LOAD_SPECIAL 91 -#define LOAD_SUPER_ATTR 92 -#define MAKE_CELL 93 -#define MAP_ADD 94 -#define MATCH_CLASS 95 -#define POP_JUMP_IF_FALSE 96 -#define POP_JUMP_IF_NONE 97 -#define POP_JUMP_IF_NOT_NONE 98 -#define POP_JUMP_IF_TRUE 99 -#define RAISE_VARARGS 100 -#define RERAISE 101 -#define SEND 102 -#define SET_ADD 103 -#define SET_FUNCTION_ATTRIBUTE 104 -#define SET_UPDATE 105 -#define STORE_ATTR 106 -#define STORE_DEREF 107 -#define STORE_FAST 108 -#define STORE_FAST_LOAD_FAST 109 -#define STORE_FAST_STORE_FAST 110 -#define STORE_GLOBAL 111 -#define STORE_NAME 112 -#define SWAP 113 -#define UNPACK_EX 114 -#define UNPACK_SEQUENCE 115 -#define YIELD_VALUE 116 +#define POP_ITER 30 +#define POP_TOP 31 +#define PUSH_EXC_INFO 32 +#define PUSH_NULL 33 +#define RETURN_GENERATOR 34 +#define RETURN_VALUE 35 +#define SETUP_ANNOTATIONS 36 +#define STORE_SLICE 37 +#define STORE_SUBSCR 38 +#define TO_BOOL 39 +#define UNARY_INVERT 40 +#define UNARY_NEGATIVE 41 +#define UNARY_NOT 42 +#define WITH_EXCEPT_START 43 +#define BINARY_OP 44 +#define BUILD_LIST 45 +#define BUILD_MAP 46 +#define BUILD_SET 47 +#define BUILD_SLICE 48 +#define BUILD_STRING 49 +#define BUILD_TUPLE 50 +#define CALL 51 +#define CALL_FUNCTION_EX 52 +#define CALL_INTRINSIC_1 53 +#define CALL_INTRINSIC_2 54 +#define CALL_KW 55 +#define COMPARE_OP 56 +#define CONTAINS_OP 57 +#define CONVERT_VALUE 58 +#define COPY 59 +#define COPY_FREE_VARS 60 +#define DELETE_ATTR 61 +#define DELETE_DEREF 62 +#define DELETE_FAST 63 +#define DELETE_GLOBAL 64 +#define DELETE_NAME 65 +#define DICT_MERGE 66 +#define DICT_UPDATE 67 +#define EXTENDED_ARG 68 +#define FOR_ITER 69 +#define GET_AWAITABLE 70 +#define IMPORT_FROM 71 +#define IMPORT_NAME 72 +#define IS_OP 73 +#define JUMP_BACKWARD 74 +#define JUMP_BACKWARD_NO_INTERRUPT 75 +#define JUMP_FORWARD 76 +#define LIST_APPEND 77 +#define LIST_EXTEND 78 +#define LOAD_ATTR 79 +#define LOAD_COMMON_CONSTANT 80 +#define LOAD_CONST 81 +#define LOAD_DEREF 82 +#define LOAD_FAST 83 +#define LOAD_FAST_AND_CLEAR 84 +#define LOAD_FAST_CHECK 85 +#define LOAD_FAST_LOAD_FAST 86 +#define LOAD_FROM_DICT_OR_DEREF 87 +#define LOAD_FROM_DICT_OR_GLOBALS 88 +#define LOAD_GLOBAL 89 +#define LOAD_NAME 90 +#define LOAD_SMALL_INT 91 +#define LOAD_SPECIAL 92 +#define LOAD_SUPER_ATTR 93 +#define MAKE_CELL 94 +#define MAP_ADD 95 +#define MATCH_CLASS 96 +#define POP_JUMP_IF_FALSE 97 +#define POP_JUMP_IF_NONE 98 +#define POP_JUMP_IF_NOT_NONE 99 +#define POP_JUMP_IF_TRUE 100 +#define RAISE_VARARGS 101 +#define RERAISE 102 +#define SEND 103 +#define SET_ADD 104 +#define SET_FUNCTION_ATTRIBUTE 105 +#define SET_UPDATE 106 +#define STORE_ATTR 107 +#define STORE_DEREF 108 +#define STORE_FAST 109 +#define STORE_FAST_LOAD_FAST 110 +#define STORE_FAST_STORE_FAST 111 +#define STORE_GLOBAL 112 +#define STORE_NAME 113 +#define SWAP 114 +#define UNPACK_EX 115 +#define UNPACK_SEQUENCE 116 +#define YIELD_VALUE 117 #define RESUME 149 #define BINARY_OP_ADD_FLOAT 150 #define BINARY_OP_ADD_INT 151 @@ -206,7 +207,8 @@ extern "C" { #define UNPACK_SEQUENCE_LIST 225 #define UNPACK_SEQUENCE_TUPLE 226 #define UNPACK_SEQUENCE_TWO_TUPLE 227 -#define INSTRUMENTED_END_FOR 236 +#define INSTRUMENTED_END_FOR 235 +#define INSTRUMENTED_POP_ITER 236 #define INSTRUMENTED_END_SEND 237 #define INSTRUMENTED_LOAD_SUPER_ATTR 238 #define INSTRUMENTED_FOR_ITER 239 @@ -237,9 +239,9 @@ extern "C" { #define SETUP_WITH 264 #define STORE_FAST_MAYBE_NULL 265 -#define HAVE_ARGUMENT 42 +#define HAVE_ARGUMENT 43 #define MIN_SPECIALIZED_OPCODE 150 -#define MIN_INSTRUMENTED_OPCODE 236 +#define MIN_INSTRUMENTED_OPCODE 235 #ifdef __cplusplus } diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py index dada2cb5fa033f..64ee56fd10556f 100644 --- a/Lib/_opcode_metadata.py +++ b/Lib/_opcode_metadata.py @@ -233,94 +233,96 @@ 'NOP': 27, 'NOT_TAKEN': 28, 'POP_EXCEPT': 29, - 'POP_TOP': 30, - 'PUSH_EXC_INFO': 31, - 'PUSH_NULL': 32, - 'RETURN_GENERATOR': 33, - 'RETURN_VALUE': 34, - 'SETUP_ANNOTATIONS': 35, - 'STORE_SLICE': 36, - 'STORE_SUBSCR': 37, - 'TO_BOOL': 38, - 'UNARY_INVERT': 39, - 'UNARY_NEGATIVE': 40, - 'UNARY_NOT': 41, - 'WITH_EXCEPT_START': 42, - 'BINARY_OP': 43, - 'BUILD_LIST': 44, - 'BUILD_MAP': 45, - 'BUILD_SET': 46, - 'BUILD_SLICE': 47, - 'BUILD_STRING': 48, - 'BUILD_TUPLE': 49, - 'CALL': 50, - 'CALL_FUNCTION_EX': 51, - 'CALL_INTRINSIC_1': 52, - 'CALL_INTRINSIC_2': 53, - 'CALL_KW': 54, - 'COMPARE_OP': 55, - 'CONTAINS_OP': 56, - 'CONVERT_VALUE': 57, - 'COPY': 58, - 'COPY_FREE_VARS': 59, - 'DELETE_ATTR': 60, - 'DELETE_DEREF': 61, - 'DELETE_FAST': 62, - 'DELETE_GLOBAL': 63, - 'DELETE_NAME': 64, - 'DICT_MERGE': 65, - 'DICT_UPDATE': 66, - 'EXTENDED_ARG': 67, - 'FOR_ITER': 68, - 'GET_AWAITABLE': 69, - 'IMPORT_FROM': 70, - 'IMPORT_NAME': 71, - 'IS_OP': 72, - 'JUMP_BACKWARD': 73, - 'JUMP_BACKWARD_NO_INTERRUPT': 74, - 'JUMP_FORWARD': 75, - 'LIST_APPEND': 76, - 'LIST_EXTEND': 77, - 'LOAD_ATTR': 78, - 'LOAD_COMMON_CONSTANT': 79, - 'LOAD_CONST': 80, - 'LOAD_DEREF': 81, - 'LOAD_FAST': 82, - 'LOAD_FAST_AND_CLEAR': 83, - 'LOAD_FAST_CHECK': 84, - 'LOAD_FAST_LOAD_FAST': 85, - 'LOAD_FROM_DICT_OR_DEREF': 86, - 'LOAD_FROM_DICT_OR_GLOBALS': 87, - 'LOAD_GLOBAL': 88, - 'LOAD_NAME': 89, - 'LOAD_SMALL_INT': 90, - 'LOAD_SPECIAL': 91, - 'LOAD_SUPER_ATTR': 92, - 'MAKE_CELL': 93, - 'MAP_ADD': 94, - 'MATCH_CLASS': 95, - 'POP_JUMP_IF_FALSE': 96, - 'POP_JUMP_IF_NONE': 97, - 'POP_JUMP_IF_NOT_NONE': 98, - 'POP_JUMP_IF_TRUE': 99, - 'RAISE_VARARGS': 100, - 'RERAISE': 101, - 'SEND': 102, - 'SET_ADD': 103, - 'SET_FUNCTION_ATTRIBUTE': 104, - 'SET_UPDATE': 105, - 'STORE_ATTR': 106, - 'STORE_DEREF': 107, - 'STORE_FAST': 108, - 'STORE_FAST_LOAD_FAST': 109, - 'STORE_FAST_STORE_FAST': 110, - 'STORE_GLOBAL': 111, - 'STORE_NAME': 112, - 'SWAP': 113, - 'UNPACK_EX': 114, - 'UNPACK_SEQUENCE': 115, - 'YIELD_VALUE': 116, - 'INSTRUMENTED_END_FOR': 236, + 'POP_ITER': 30, + 'POP_TOP': 31, + 'PUSH_EXC_INFO': 32, + 'PUSH_NULL': 33, + 'RETURN_GENERATOR': 34, + 'RETURN_VALUE': 35, + 'SETUP_ANNOTATIONS': 36, + 'STORE_SLICE': 37, + 'STORE_SUBSCR': 38, + 'TO_BOOL': 39, + 'UNARY_INVERT': 40, + 'UNARY_NEGATIVE': 41, + 'UNARY_NOT': 42, + 'WITH_EXCEPT_START': 43, + 'BINARY_OP': 44, + 'BUILD_LIST': 45, + 'BUILD_MAP': 46, + 'BUILD_SET': 47, + 'BUILD_SLICE': 48, + 'BUILD_STRING': 49, + 'BUILD_TUPLE': 50, + 'CALL': 51, + 'CALL_FUNCTION_EX': 52, + 'CALL_INTRINSIC_1': 53, + 'CALL_INTRINSIC_2': 54, + 'CALL_KW': 55, + 'COMPARE_OP': 56, + 'CONTAINS_OP': 57, + 'CONVERT_VALUE': 58, + 'COPY': 59, + 'COPY_FREE_VARS': 60, + 'DELETE_ATTR': 61, + 'DELETE_DEREF': 62, + 'DELETE_FAST': 63, + 'DELETE_GLOBAL': 64, + 'DELETE_NAME': 65, + 'DICT_MERGE': 66, + 'DICT_UPDATE': 67, + 'EXTENDED_ARG': 68, + 'FOR_ITER': 69, + 'GET_AWAITABLE': 70, + 'IMPORT_FROM': 71, + 'IMPORT_NAME': 72, + 'IS_OP': 73, + 'JUMP_BACKWARD': 74, + 'JUMP_BACKWARD_NO_INTERRUPT': 75, + 'JUMP_FORWARD': 76, + 'LIST_APPEND': 77, + 'LIST_EXTEND': 78, + 'LOAD_ATTR': 79, + 'LOAD_COMMON_CONSTANT': 80, + 'LOAD_CONST': 81, + 'LOAD_DEREF': 82, + 'LOAD_FAST': 83, + 'LOAD_FAST_AND_CLEAR': 84, + 'LOAD_FAST_CHECK': 85, + 'LOAD_FAST_LOAD_FAST': 86, + 'LOAD_FROM_DICT_OR_DEREF': 87, + 'LOAD_FROM_DICT_OR_GLOBALS': 88, + 'LOAD_GLOBAL': 89, + 'LOAD_NAME': 90, + 'LOAD_SMALL_INT': 91, + 'LOAD_SPECIAL': 92, + 'LOAD_SUPER_ATTR': 93, + 'MAKE_CELL': 94, + 'MAP_ADD': 95, + 'MATCH_CLASS': 96, + 'POP_JUMP_IF_FALSE': 97, + 'POP_JUMP_IF_NONE': 98, + 'POP_JUMP_IF_NOT_NONE': 99, + 'POP_JUMP_IF_TRUE': 100, + 'RAISE_VARARGS': 101, + 'RERAISE': 102, + 'SEND': 103, + 'SET_ADD': 104, + 'SET_FUNCTION_ATTRIBUTE': 105, + 'SET_UPDATE': 106, + 'STORE_ATTR': 107, + 'STORE_DEREF': 108, + 'STORE_FAST': 109, + 'STORE_FAST_LOAD_FAST': 110, + 'STORE_FAST_STORE_FAST': 111, + 'STORE_GLOBAL': 112, + 'STORE_NAME': 113, + 'SWAP': 114, + 'UNPACK_EX': 115, + 'UNPACK_SEQUENCE': 116, + 'YIELD_VALUE': 117, + 'INSTRUMENTED_END_FOR': 235, + 'INSTRUMENTED_POP_ITER': 236, 'INSTRUMENTED_END_SEND': 237, 'INSTRUMENTED_LOAD_SUPER_ATTR': 238, 'INSTRUMENTED_FOR_ITER': 239, @@ -350,5 +352,5 @@ 'STORE_FAST_MAYBE_NULL': 265, } -HAVE_ARGUMENT = 42 -MIN_INSTRUMENTED_OPCODE = 236 +HAVE_ARGUMENT = 43 +MIN_INSTRUMENTED_OPCODE = 235 diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 2a1b26e8a1ffd1..7ffa4eb8639add 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -215,6 +215,8 @@ from test.support import threading_helper, import_helper from test.support.bytecode_helper import instructions_with_positions from opcode import opmap, opname +from _testcapi import code_offset_to_line + COPY_FREE_VARS = opmap['COPY_FREE_VARS'] @@ -896,6 +898,44 @@ async def async_func(): rc, out, err = assert_python_ok('-OO', '-c', code) + def test_co_branches(self): + + def get_line_branches(func): + code = func.__code__ + base = code.co_firstlineno + return [ + ( + code_offset_to_line(code, src) - base, + code_offset_to_line(code, left) - base, + code_offset_to_line(code, right) - base + ) for (src, left, right) in + code.co_branches() + ] + + def simple(x): + if x: + A + else: + B + + self.assertEqual( + get_line_branches(simple), + [(1,2,4)]) + + def with_extended_args(x): + if x: + A.x; A.x; A.x; A.x; A.x; A.x; + A.x; A.x; A.x; A.x; A.x; A.x; + A.x; A.x; A.x; A.x; A.x; A.x; + A.x; A.x; A.x; A.x; A.x; A.x; + A.x; A.x; A.x; A.x; A.x; A.x; + else: + B + + self.assertEqual( + get_line_branches(with_extended_args), + [(1,2,8)]) + if check_impl_detail(cpython=True) and ctypes is not None: py = ctypes.pythonapi freefunc = ctypes.CFUNCTYPE(None,ctypes.c_voidp) diff --git a/Lib/test/test_compiler_codegen.py b/Lib/test/test_compiler_codegen.py index f8c4fc14c91ebe..5655e1b9cf196a 100644 --- a/Lib/test/test_compiler_codegen.py +++ b/Lib/test/test_compiler_codegen.py @@ -50,7 +50,6 @@ def test_for_loop(self): ('GET_ITER', None, 1), loop_lbl := self.Label(), ('FOR_ITER', exit_lbl := self.Label(), 1), - ('NOT_TAKEN', None, 1), ('NOP', None, 1, 1), ('STORE_NAME', 1, 1), ('LOAD_NAME', 2, 2), @@ -61,7 +60,7 @@ def test_for_loop(self): ('JUMP', loop_lbl), exit_lbl, ('END_FOR', None), - ('POP_TOP', None), + ('POP_ITER', None), ('LOAD_CONST', 0), ('RETURN_VALUE', None), ] diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 955a3e4cb9e4f7..e733a673d003e7 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -175,14 +175,13 @@ def bug708901(): %3d CALL 2 GET_ITER - L1: FOR_ITER 4 (to L2) - NOT_TAKEN + L1: FOR_ITER 3 (to L2) STORE_FAST 0 (res) -%3d JUMP_BACKWARD 6 (to L1) +%3d JUMP_BACKWARD 5 (to L1) %3d L2: END_FOR - POP_TOP + POP_ITER LOAD_CONST 0 (None) RETURN_VALUE """ % (bug708901.__code__.co_firstlineno, @@ -844,8 +843,7 @@ def foo(x): L1: RESUME 0 LOAD_FAST 0 (.0) GET_ITER - L2: FOR_ITER 11 (to L3) - NOT_TAKEN + L2: FOR_ITER 10 (to L3) STORE_FAST 1 (z) LOAD_DEREF 2 (x) LOAD_FAST 1 (z) @@ -853,9 +851,9 @@ def foo(x): YIELD_VALUE 0 RESUME 5 POP_TOP - JUMP_BACKWARD 13 (to L2) + JUMP_BACKWARD 12 (to L2) L3: END_FOR - POP_TOP + POP_ITER LOAD_CONST 0 (None) RETURN_VALUE @@ -899,18 +897,17 @@ def loop_test(): LOAD_SMALL_INT 3 BINARY_OP 5 (*) GET_ITER - L1: FOR_ITER_LIST 15 (to L2) - NOT_TAKEN + L1: FOR_ITER_LIST 14 (to L2) STORE_FAST 0 (i) %3d LOAD_GLOBAL_MODULE 1 (load_test + NULL) LOAD_FAST 0 (i) CALL_PY_GENERAL 1 POP_TOP - JUMP_BACKWARD 17 (to L1) + JUMP_BACKWARD 16 (to L1) %3d L2: END_FOR - POP_TOP + POP_ITER LOAD_CONST_IMMORTAL 1 (None) RETURN_VALUE """ % (loop_test.__code__.co_firstlineno, @@ -1706,214 +1703,213 @@ def _prepare_test_cases(): Instruction = dis.Instruction expected_opinfo_outer = [ - Instruction(opname='MAKE_CELL', opcode=93, arg=0, argval='a', argrepr='a', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='MAKE_CELL', opcode=93, arg=1, argval='b', argrepr='b', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='MAKE_CELL', opcode=94, arg=0, argval='a', argrepr='a', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='MAKE_CELL', opcode=94, arg=1, argval='b', argrepr='b', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=4, start_offset=4, starts_line=True, line_number=1, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=(3, 4), argrepr='(3, 4)', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='a', argrepr='a', offset=8, start_offset=8, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=82, arg=1, argval='b', argrepr='b', offset=10, start_offset=10, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='BUILD_TUPLE', opcode=49, arg=2, argval=2, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=(3, 4), argrepr='(3, 4)', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='a', argrepr='a', offset=8, start_offset=8, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=1, argval='b', argrepr='b', offset=10, start_offset=10, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='BUILD_TUPLE', opcode=50, arg=2, argval=2, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=0, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), Instruction(opname='MAKE_FUNCTION', opcode=23, arg=None, argval=None, argrepr='', offset=16, start_offset=16, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=8, argval=8, argrepr='closure', offset=18, start_offset=18, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=1, argval=1, argrepr='defaults', offset=20, start_offset=20, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='STORE_FAST', opcode=108, arg=2, argval='f', argrepr='f', offset=22, start_offset=22, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='print', argrepr='print + NULL', offset=24, start_offset=24, starts_line=True, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_DEREF', opcode=81, arg=0, argval='a', argrepr='a', offset=34, start_offset=34, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=81, arg=1, argval='b', argrepr='b', offset=36, start_offset=36, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=80, arg=1, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=1, argval=1, argrepr='', offset=40, start_offset=40, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='BUILD_LIST', opcode=44, arg=0, argval=0, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='BUILD_MAP', opcode=45, arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=80, arg=2, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=50, arg=7, argval=7, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=82, arg=2, argval='f', argrepr='f', offset=58, start_offset=58, starts_line=True, line_number=8, label=None, positions=None, cache_info=None), - Instruction(opname='RETURN_VALUE', opcode=34, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=8, label=None, positions=None, cache_info=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=105, arg=8, argval=8, argrepr='closure', offset=18, start_offset=18, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=105, arg=1, argval=1, argrepr='defaults', offset=20, start_offset=20, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='STORE_FAST', opcode=109, arg=2, argval='f', argrepr='f', offset=22, start_offset=22, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=1, argval='print', argrepr='print + NULL', offset=24, start_offset=24, starts_line=True, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_DEREF', opcode=82, arg=0, argval='a', argrepr='a', offset=34, start_offset=34, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=82, arg=1, argval='b', argrepr='b', offset=36, start_offset=36, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=1, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=1, argval=1, argrepr='', offset=40, start_offset=40, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='BUILD_LIST', opcode=45, arg=0, argval=0, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='BUILD_MAP', opcode=46, arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=2, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=7, argval=7, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=2, argval='f', argrepr='f', offset=58, start_offset=58, starts_line=True, line_number=8, label=None, positions=None, cache_info=None), + Instruction(opname='RETURN_VALUE', opcode=35, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=8, label=None, positions=None, cache_info=None), ] expected_opinfo_f = [ - Instruction(opname='COPY_FREE_VARS', opcode=59, arg=2, argval=2, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='MAKE_CELL', opcode=93, arg=0, argval='c', argrepr='c', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='MAKE_CELL', opcode=93, arg=1, argval='d', argrepr='d', offset=4, start_offset=4, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='COPY_FREE_VARS', opcode=60, arg=2, argval=2, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='MAKE_CELL', opcode=94, arg=0, argval='c', argrepr='c', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='MAKE_CELL', opcode=94, arg=1, argval='d', argrepr='d', offset=4, start_offset=4, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=80, arg=1, argval=(5, 6), argrepr='(5, 6)', offset=8, start_offset=8, starts_line=True, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=82, arg=3, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=82, arg=4, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='c', argrepr='c', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=82, arg=1, argval='d', argrepr='d', offset=16, start_offset=16, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='BUILD_TUPLE', opcode=49, arg=4, argval=4, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, start_offset=20, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=1, argval=(5, 6), argrepr='(5, 6)', offset=8, start_offset=8, starts_line=True, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=3, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=4, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='c', argrepr='c', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=1, argval='d', argrepr='d', offset=16, start_offset=16, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='BUILD_TUPLE', opcode=50, arg=4, argval=4, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=0, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, start_offset=20, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), Instruction(opname='MAKE_FUNCTION', opcode=23, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=8, argval=8, argrepr='closure', offset=24, start_offset=24, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=1, argval=1, argrepr='defaults', offset=26, start_offset=26, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='STORE_FAST', opcode=108, arg=2, argval='inner', argrepr='inner', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_DEREF', opcode=81, arg=3, argval='a', argrepr='a', offset=40, start_offset=40, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=81, arg=4, argval='b', argrepr='b', offset=42, start_offset=42, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=81, arg=0, argval='c', argrepr='c', offset=44, start_offset=44, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=81, arg=1, argval='d', argrepr='d', offset=46, start_offset=46, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=50, arg=4, argval=4, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=82, arg=2, argval='inner', argrepr='inner', offset=58, start_offset=58, starts_line=True, line_number=6, label=None, positions=None, cache_info=None), - Instruction(opname='RETURN_VALUE', opcode=34, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=6, label=None, positions=None, cache_info=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=105, arg=8, argval=8, argrepr='closure', offset=24, start_offset=24, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=105, arg=1, argval=1, argrepr='defaults', offset=26, start_offset=26, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='STORE_FAST', opcode=109, arg=2, argval='inner', argrepr='inner', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=1, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_DEREF', opcode=82, arg=3, argval='a', argrepr='a', offset=40, start_offset=40, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=82, arg=4, argval='b', argrepr='b', offset=42, start_offset=42, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=82, arg=0, argval='c', argrepr='c', offset=44, start_offset=44, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=82, arg=1, argval='d', argrepr='d', offset=46, start_offset=46, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=4, argval=4, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=2, argval='inner', argrepr='inner', offset=58, start_offset=58, starts_line=True, line_number=6, label=None, positions=None, cache_info=None), + Instruction(opname='RETURN_VALUE', opcode=35, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=6, label=None, positions=None, cache_info=None), ] expected_opinfo_inner = [ - Instruction(opname='COPY_FREE_VARS', opcode=59, arg=4, argval=4, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='COPY_FREE_VARS', opcode=60, arg=4, argval=4, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='print', argrepr='print + NULL', offset=4, start_offset=4, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_DEREF', opcode=81, arg=2, argval='a', argrepr='a', offset=14, start_offset=14, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=81, arg=3, argval='b', argrepr='b', offset=16, start_offset=16, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=81, arg=4, argval='c', argrepr='c', offset=18, start_offset=18, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=81, arg=5, argval='d', argrepr='d', offset=20, start_offset=20, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST_LOAD_FAST', opcode=85, arg=1, argval=('e', 'f'), argrepr='e, f', offset=22, start_offset=22, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=50, arg=6, argval=6, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=32, start_offset=32, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval=None, argrepr='None', offset=34, start_offset=34, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='RETURN_VALUE', opcode=34, arg=None, argval=None, argrepr='', offset=36, start_offset=36, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=1, argval='print', argrepr='print + NULL', offset=4, start_offset=4, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_DEREF', opcode=82, arg=2, argval='a', argrepr='a', offset=14, start_offset=14, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=82, arg=3, argval='b', argrepr='b', offset=16, start_offset=16, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=82, arg=4, argval='c', argrepr='c', offset=18, start_offset=18, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=82, arg=5, argval='d', argrepr='d', offset=20, start_offset=20, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST_LOAD_FAST', opcode=86, arg=1, argval=('e', 'f'), argrepr='e, f', offset=22, start_offset=22, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=6, argval=6, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=32, start_offset=32, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=0, argval=None, argrepr='None', offset=34, start_offset=34, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='RETURN_VALUE', opcode=35, arg=None, argval=None, argrepr='', offset=36, start_offset=36, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), ] expected_opinfo_jumpy = [ Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=1, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='range', argrepr='range + NULL', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=10, argval=10, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=1, argval='range', argrepr='range + NULL', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=10, argval=10, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), Instruction(opname='GET_ITER', opcode=16, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='FOR_ITER', opcode=68, arg=34, argval=96, argrepr='to L4', offset=24, start_offset=24, starts_line=False, line_number=3, label=1, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='STORE_FAST', opcode=108, arg=0, argval='i', argrepr='i', offset=30, start_offset=30, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=32, start_offset=32, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=42, start_offset=42, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=52, start_offset=52, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=54, start_offset=54, starts_line=True, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=4, argval=4, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=55, arg=18, argval='<', argrepr='bool(<)', offset=58, start_offset=58, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=3, argval=72, argrepr='to L2', offset=62, start_offset=62, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=66, start_offset=66, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD', opcode=73, arg=24, argval=24, argrepr='to L1', offset=68, start_offset=68, starts_line=True, line_number=6, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=72, start_offset=72, starts_line=True, line_number=7, label=2, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=6, argval=6, argrepr='', offset=74, start_offset=74, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=55, arg=148, argval='>', argrepr='bool(>)', offset=76, start_offset=76, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=99, arg=3, argval=90, argrepr='to L3', offset=80, start_offset=80, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=84, start_offset=84, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD', opcode=73, arg=33, argval=24, argrepr='to L1', offset=86, start_offset=86, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=90, start_offset=90, starts_line=True, line_number=None, label=3, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=92, start_offset=92, starts_line=True, line_number=8, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_FORWARD', opcode=75, arg=13, argval=122, argrepr='to L5', offset=94, start_offset=94, starts_line=False, line_number=8, label=None, positions=None, cache_info=None), - Instruction(opname='END_FOR', opcode=9, arg=None, argval=None, argrepr='', offset=96, start_offset=96, starts_line=True, line_number=3, label=4, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=98, start_offset=98, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=100, start_offset=100, starts_line=True, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=110, start_offset=110, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=112, start_offset=112, starts_line=False, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=120, start_offset=120, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST_CHECK', opcode=84, arg=0, argval='i', argrepr='i', offset=122, start_offset=122, starts_line=True, line_number=11, label=5, positions=None, cache_info=None), - Instruction(opname='TO_BOOL', opcode=38, arg=None, argval=None, argrepr='', offset=124, start_offset=124, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=37, argval=210, argrepr='to L8', offset=132, start_offset=132, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=136, start_offset=136, starts_line=False, line_number=11, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=138, start_offset=138, starts_line=True, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=148, start_offset=148, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=150, start_offset=150, starts_line=False, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=158, start_offset=158, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=160, start_offset=160, starts_line=True, line_number=13, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=1, argval=1, argrepr='', offset=162, start_offset=162, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), - Instruction(opname='BINARY_OP', opcode=43, arg=23, argval=23, argrepr='-=', offset=164, start_offset=164, starts_line=False, line_number=13, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='STORE_FAST', opcode=108, arg=0, argval='i', argrepr='i', offset=168, start_offset=168, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=170, start_offset=170, starts_line=True, line_number=14, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=6, argval=6, argrepr='', offset=172, start_offset=172, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=55, arg=148, argval='>', argrepr='bool(>)', offset=174, start_offset=174, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=3, argval=188, argrepr='to L6', offset=178, start_offset=178, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=182, start_offset=182, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD', opcode=73, arg=33, argval=122, argrepr='to L5', offset=184, start_offset=184, starts_line=True, line_number=15, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=188, start_offset=188, starts_line=True, line_number=16, label=6, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=4, argval=4, argrepr='', offset=190, start_offset=190, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=55, arg=18, argval='<', argrepr='bool(<)', offset=192, start_offset=192, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=99, arg=3, argval=206, argrepr='to L7', offset=196, start_offset=196, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=200, start_offset=200, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD', opcode=73, arg=42, argval=122, argrepr='to L5', offset=202, start_offset=202, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=206, start_offset=206, starts_line=True, line_number=None, label=7, positions=None, cache_info=None), - Instruction(opname='JUMP_FORWARD', opcode=75, arg=11, argval=232, argrepr='to L9', offset=208, start_offset=208, starts_line=True, line_number=17, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=210, start_offset=210, starts_line=True, line_number=19, label=8, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=80, arg=1, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=220, start_offset=220, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=222, start_offset=222, starts_line=False, line_number=19, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=230, start_offset=230, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), - Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=232, start_offset=232, starts_line=True, line_number=20, label=9, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=1, argval=1, argrepr='', offset=234, start_offset=234, starts_line=True, line_number=21, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=0, argval=0, argrepr='', offset=236, start_offset=236, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), - Instruction(opname='BINARY_OP', opcode=43, arg=11, argval=11, argrepr='/', offset=238, start_offset=238, starts_line=False, line_number=21, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=242, start_offset=242, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=244, start_offset=244, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=58, arg=1, argval=1, argrepr='', offset=246, start_offset=246, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SPECIAL', opcode=91, arg=1, argval=1, argrepr='__exit__', offset=248, start_offset=248, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='SWAP', opcode=113, arg=2, argval=2, argrepr='', offset=250, start_offset=250, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='SWAP', opcode=113, arg=3, argval=3, argrepr='', offset=252, start_offset=252, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SPECIAL', opcode=91, arg=0, argval=0, argrepr='__enter__', offset=254, start_offset=254, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=50, arg=0, argval=0, argrepr='', offset=256, start_offset=256, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='STORE_FAST', opcode=108, arg=1, argval='dodgy', argrepr='dodgy', offset=264, start_offset=264, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=266, start_offset=266, starts_line=True, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=80, arg=2, argval='Never reach this', argrepr="'Never reach this'", offset=276, start_offset=276, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=278, start_offset=278, starts_line=False, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=286, start_offset=286, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=None, argrepr='None', offset=288, start_offset=288, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=None, argrepr='None', offset=290, start_offset=290, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=None, argrepr='None', offset=292, start_offset=292, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=50, arg=3, argval=3, argrepr='', offset=294, start_offset=294, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=302, start_offset=302, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=304, start_offset=304, starts_line=True, line_number=28, label=10, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=80, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=314, start_offset=314, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=316, start_offset=316, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=324, start_offset=324, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=None, argrepr='None', offset=326, start_offset=326, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='RETURN_VALUE', opcode=34, arg=None, argval=None, argrepr='', offset=328, start_offset=328, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='PUSH_EXC_INFO', opcode=31, arg=None, argval=None, argrepr='', offset=330, start_offset=330, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='WITH_EXCEPT_START', opcode=42, arg=None, argval=None, argrepr='', offset=332, start_offset=332, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='TO_BOOL', opcode=38, arg=None, argval=None, argrepr='', offset=334, start_offset=334, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=99, arg=2, argval=350, argrepr='to L11', offset=342, start_offset=342, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=346, start_offset=346, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=101, arg=2, argval=2, argrepr='', offset=348, start_offset=348, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=350, start_offset=350, starts_line=False, line_number=25, label=11, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=352, start_offset=352, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=354, start_offset=354, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=356, start_offset=356, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=358, start_offset=358, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=74, arg=29, argval=304, argrepr='to L10', offset=360, start_offset=360, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=58, arg=3, argval=3, argrepr='', offset=362, start_offset=362, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=364, start_offset=364, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=101, arg=1, argval=1, argrepr='', offset=366, start_offset=366, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='PUSH_EXC_INFO', opcode=31, arg=None, argval=None, argrepr='', offset=368, start_offset=368, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=88, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=370, start_offset=370, starts_line=True, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='CHECK_EXC_MATCH', opcode=5, arg=None, argval=None, argrepr='', offset=380, start_offset=380, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=15, argval=416, argrepr='to L12', offset=382, start_offset=382, starts_line=False, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=386, start_offset=386, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=388, start_offset=388, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=390, start_offset=390, starts_line=True, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=80, arg=4, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=400, start_offset=400, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=402, start_offset=402, starts_line=False, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=410, start_offset=410, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=412, start_offset=412, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=74, arg=56, argval=304, argrepr='to L10', offset=414, start_offset=414, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=101, arg=0, argval=0, argrepr='', offset=416, start_offset=416, starts_line=True, line_number=22, label=12, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=58, arg=3, argval=3, argrepr='', offset=418, start_offset=418, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=420, start_offset=420, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=101, arg=1, argval=1, argrepr='', offset=422, start_offset=422, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='PUSH_EXC_INFO', opcode=31, arg=None, argval=None, argrepr='', offset=424, start_offset=424, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=426, start_offset=426, starts_line=True, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=80, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=436, start_offset=436, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=50, arg=1, argval=1, argrepr='', offset=438, start_offset=438, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=30, arg=None, argval=None, argrepr='', offset=446, start_offset=446, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=101, arg=0, argval=0, argrepr='', offset=448, start_offset=448, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=58, arg=3, argval=3, argrepr='', offset=450, start_offset=450, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=452, start_offset=452, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=101, arg=1, argval=1, argrepr='', offset=454, start_offset=454, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='FOR_ITER', opcode=69, arg=33, argval=94, argrepr='to L4', offset=24, start_offset=24, starts_line=False, line_number=3, label=1, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='STORE_FAST', opcode=109, arg=0, argval='i', argrepr='i', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=40, start_offset=40, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=50, start_offset=50, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=52, start_offset=52, starts_line=True, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=4, argval=4, argrepr='', offset=54, start_offset=54, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=56, arg=18, argval='<', argrepr='bool(<)', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=3, argval=70, argrepr='to L2', offset=60, start_offset=60, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=64, start_offset=64, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=74, arg=23, argval=24, argrepr='to L1', offset=66, start_offset=66, starts_line=True, line_number=6, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=70, start_offset=70, starts_line=True, line_number=7, label=2, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=6, argval=6, argrepr='', offset=72, start_offset=72, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=56, arg=148, argval='>', argrepr='bool(>)', offset=74, start_offset=74, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=3, argval=88, argrepr='to L3', offset=78, start_offset=78, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=82, start_offset=82, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=74, arg=32, argval=24, argrepr='to L1', offset=84, start_offset=84, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=88, start_offset=88, starts_line=True, line_number=None, label=3, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=90, start_offset=90, starts_line=True, line_number=8, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_FORWARD', opcode=76, arg=13, argval=120, argrepr='to L5', offset=92, start_offset=92, starts_line=False, line_number=8, label=None, positions=None, cache_info=None), + Instruction(opname='END_FOR', opcode=9, arg=None, argval=None, argrepr='', offset=94, start_offset=94, starts_line=True, line_number=3, label=4, positions=None, cache_info=None), + Instruction(opname='POP_ITER', opcode=30, arg=None, argval=None, argrepr='', offset=96, start_offset=96, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=98, start_offset=98, starts_line=True, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=0, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=108, start_offset=108, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=110, start_offset=110, starts_line=False, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=118, start_offset=118, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST_CHECK', opcode=85, arg=0, argval='i', argrepr='i', offset=120, start_offset=120, starts_line=True, line_number=11, label=5, positions=None, cache_info=None), + Instruction(opname='TO_BOOL', opcode=39, arg=None, argval=None, argrepr='', offset=122, start_offset=122, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=37, argval=208, argrepr='to L8', offset=130, start_offset=130, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=134, start_offset=134, starts_line=False, line_number=11, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=136, start_offset=136, starts_line=True, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=146, start_offset=146, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=148, start_offset=148, starts_line=False, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=156, start_offset=156, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=158, start_offset=158, starts_line=True, line_number=13, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=1, argval=1, argrepr='', offset=160, start_offset=160, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), + Instruction(opname='BINARY_OP', opcode=44, arg=23, argval=23, argrepr='-=', offset=162, start_offset=162, starts_line=False, line_number=13, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='STORE_FAST', opcode=109, arg=0, argval='i', argrepr='i', offset=166, start_offset=166, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=168, start_offset=168, starts_line=True, line_number=14, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=6, argval=6, argrepr='', offset=170, start_offset=170, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=56, arg=148, argval='>', argrepr='bool(>)', offset=172, start_offset=172, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=3, argval=186, argrepr='to L6', offset=176, start_offset=176, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=180, start_offset=180, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=74, arg=33, argval=120, argrepr='to L5', offset=182, start_offset=182, starts_line=True, line_number=15, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=186, start_offset=186, starts_line=True, line_number=16, label=6, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=4, argval=4, argrepr='', offset=188, start_offset=188, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=56, arg=18, argval='<', argrepr='bool(<)', offset=190, start_offset=190, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=3, argval=204, argrepr='to L7', offset=194, start_offset=194, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=198, start_offset=198, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=74, arg=42, argval=120, argrepr='to L5', offset=200, start_offset=200, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=204, start_offset=204, starts_line=True, line_number=None, label=7, positions=None, cache_info=None), + Instruction(opname='JUMP_FORWARD', opcode=76, arg=11, argval=230, argrepr='to L9', offset=206, start_offset=206, starts_line=True, line_number=17, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=208, start_offset=208, starts_line=True, line_number=19, label=8, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=1, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=218, start_offset=218, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=220, start_offset=220, starts_line=False, line_number=19, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=228, start_offset=228, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), + Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=230, start_offset=230, starts_line=True, line_number=20, label=9, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=1, argval=1, argrepr='', offset=232, start_offset=232, starts_line=True, line_number=21, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=0, argval=0, argrepr='', offset=234, start_offset=234, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), + Instruction(opname='BINARY_OP', opcode=44, arg=11, argval=11, argrepr='/', offset=236, start_offset=236, starts_line=False, line_number=21, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=240, start_offset=240, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=242, start_offset=242, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=59, arg=1, argval=1, argrepr='', offset=244, start_offset=244, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SPECIAL', opcode=92, arg=1, argval=1, argrepr='__exit__', offset=246, start_offset=246, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='SWAP', opcode=114, arg=2, argval=2, argrepr='', offset=248, start_offset=248, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='SWAP', opcode=114, arg=3, argval=3, argrepr='', offset=250, start_offset=250, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SPECIAL', opcode=92, arg=0, argval=0, argrepr='__enter__', offset=252, start_offset=252, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=0, argval=0, argrepr='', offset=254, start_offset=254, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='STORE_FAST', opcode=109, arg=1, argval='dodgy', argrepr='dodgy', offset=262, start_offset=262, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=264, start_offset=264, starts_line=True, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=2, argval='Never reach this', argrepr="'Never reach this'", offset=274, start_offset=274, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=276, start_offset=276, starts_line=False, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=284, start_offset=284, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=286, start_offset=286, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=288, start_offset=288, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=290, start_offset=290, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=3, argval=3, argrepr='', offset=292, start_offset=292, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=300, start_offset=300, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=302, start_offset=302, starts_line=True, line_number=28, label=10, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=312, start_offset=312, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=314, start_offset=314, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=322, start_offset=322, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=324, start_offset=324, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='RETURN_VALUE', opcode=35, arg=None, argval=None, argrepr='', offset=326, start_offset=326, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=328, start_offset=328, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='WITH_EXCEPT_START', opcode=43, arg=None, argval=None, argrepr='', offset=330, start_offset=330, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='TO_BOOL', opcode=39, arg=None, argval=None, argrepr='', offset=332, start_offset=332, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=2, argval=348, argrepr='to L11', offset=340, start_offset=340, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=344, start_offset=344, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=2, argval=2, argrepr='', offset=346, start_offset=346, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=348, start_offset=348, starts_line=False, line_number=25, label=11, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=350, start_offset=350, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=352, start_offset=352, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=354, start_offset=354, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=356, start_offset=356, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=75, arg=29, argval=302, argrepr='to L10', offset=358, start_offset=358, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=360, start_offset=360, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=364, start_offset=364, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=366, start_offset=366, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=368, start_offset=368, starts_line=True, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='CHECK_EXC_MATCH', opcode=5, arg=None, argval=None, argrepr='', offset=378, start_offset=378, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=15, argval=414, argrepr='to L12', offset=380, start_offset=380, starts_line=False, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=384, start_offset=384, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=386, start_offset=386, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=388, start_offset=388, starts_line=True, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=4, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=398, start_offset=398, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=400, start_offset=400, starts_line=False, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=408, start_offset=408, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=410, start_offset=410, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=75, arg=56, argval=302, argrepr='to L10', offset=412, start_offset=412, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=414, start_offset=414, starts_line=True, line_number=22, label=12, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=416, start_offset=416, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=418, start_offset=418, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=420, start_offset=420, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=422, start_offset=422, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=424, start_offset=424, starts_line=True, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=434, start_offset=434, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=436, start_offset=436, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=444, start_offset=444, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=446, start_offset=446, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=448, start_offset=448, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=450, start_offset=450, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=452, start_offset=452, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), ] # One last piece of inspect fodder to check the default line number handling def simple(): pass expected_opinfo_simple = [ Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=simple.__code__.co_firstlineno, label=None, positions=None), - Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval=None, argrepr='None', offset=2, start_offset=2, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None), - Instruction(opname='RETURN_VALUE', opcode=34, arg=None, argval=None, argrepr='', offset=4, start_offset=4, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=0, argval=None, argrepr='None', offset=2, start_offset=2, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None), + Instruction(opname='RETURN_VALUE', opcode=35, arg=None, argval=None, argrepr='', offset=4, start_offset=4, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None), ] diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py index 32b3a6ac049e28..43e3e56639db62 100644 --- a/Lib/test/test_monitoring.py +++ b/Lib/test/test_monitoring.py @@ -1589,11 +1589,11 @@ def whilefunc(n=0): ('branch right', 'whilefunc', 1, 3)]) self.check_events(func, recorders = BRANCH_OFFSET_RECORDERS, expected = [ - ('branch left', 'func', 28, 34), - ('branch right', 'func', 46, 60), - ('branch left', 'func', 28, 34), - ('branch left', 'func', 46, 52), - ('branch right', 'func', 28, 72)]) + ('branch left', 'func', 28, 32), + ('branch right', 'func', 44, 58), + ('branch left', 'func', 28, 32), + ('branch left', 'func', 44, 50), + ('branch right', 'func', 28, 70)]) def test_except_star(self): @@ -1658,6 +1658,88 @@ def foo(n=0): exit_loop]) +class TestBranchConsistency(MonitoringTestBase, unittest.TestCase): + + def check_branches(self, func, tool=TEST_TOOL, recorders=BRANCH_OFFSET_RECORDERS): + try: + self.assertEqual(sys.monitoring._all_events(), {}) + event_list = [] + all_events = 0 + for recorder in recorders: + ev = recorder.event_type + sys.monitoring.register_callback(tool, ev, recorder(event_list)) + all_events |= ev + sys.monitoring.set_local_events(tool, func.__code__, all_events) + func() + sys.monitoring.set_local_events(tool, func.__code__, 0) + for recorder in recorders: + sys.monitoring.register_callback(tool, recorder.event_type, None) + lefts = set() + rights = set() + for (src, left, right) in func.__code__.co_branches(): + lefts.add((src, left)) + rights.add((src, right)) + for event in event_list: + way, _, src, dest = event + if "left" in way: + self.assertIn((src, dest), lefts) + else: + self.assertIn("right", way) + self.assertIn((src, dest), rights) + finally: + sys.monitoring.set_local_events(tool, func.__code__, 0) + for recorder in recorders: + sys.monitoring.register_callback(tool, recorder.event_type, None) + + def test_simple(self): + + def func(): + x = 1 + for a in range(2): + if a: + x = 4 + else: + x = 6 + 7 + + self.check_branches(func) + + def whilefunc(n=0): + while n < 3: + n += 1 # line 2 + 3 + + self.check_branches(whilefunc) + + def test_except_star(self): + + class Foo: + def meth(self): + pass + + def func(): + try: + try: + raise KeyError + except* Exception as e: + f = Foo(); f.meth() + except KeyError: + pass + + + self.check_branches(func) + + def test4(self): + + def foo(n=0): + while n<4: + pass + n += 1 + return None + + self.check_branches(foo) + + class TestLoadSuperAttr(CheckEvents): RECORDERS = CallRecorder, LineRecorder, CRaiseRecorder, CReturnRecorder diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index cd9014118f2d7f..a0a1f8af6710a3 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3415,6 +3415,26 @@ test_atexit(PyObject *self, PyObject *Py_UNUSED(args)) Py_RETURN_NONE; } +static PyObject* +code_offset_to_line(PyObject* self, PyObject* const* args, Py_ssize_t nargsf) +{ + Py_ssize_t nargs = _PyVectorcall_NARGS(nargsf); + if (nargs != 2) { + PyErr_SetString(PyExc_TypeError, "code_offset_to_line takes 2 arguments"); + return NULL; + } + int offset; + if (PyLong_AsInt32(args[1], &offset) < 0) { + return NULL; + } + PyCodeObject *code = (PyCodeObject *)args[0]; + if (!PyCode_Check(code)) { + PyErr_SetString(PyExc_TypeError, "first arg must be a code object"); + return NULL; + } + return PyLong_FromInt32(PyCode_Addr2Line(code, offset)); +} + static PyMethodDef TestMethods[] = { {"set_errno", set_errno, METH_VARARGS}, {"test_config", test_config, METH_NOARGS}, @@ -3557,6 +3577,7 @@ static PyMethodDef TestMethods[] = { {"finalize_thread_hang", finalize_thread_hang, METH_O, NULL}, {"type_freeze", type_freeze, METH_VARARGS}, {"test_atexit", test_atexit, METH_NOARGS}, + {"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL}, {NULL, NULL} /* sentinel */ }; diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index a0007830e8cbc0..4f6933ac0ddcd6 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -1,37 +1,37 @@ // Auto-generated by Programs/freeze_test_frozenmain.py unsigned char M_test_frozenmain[] = { 227,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, - 0,0,0,0,0,243,170,0,0,0,149,0,90,0,80,0, - 71,0,112,0,90,0,80,0,71,1,112,1,89,2,32,0, - 80,1,50,1,0,0,0,0,0,0,30,0,89,2,32,0, - 80,2,89,0,78,6,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,50,2,0,0,0,0,0,0, - 30,0,89,1,78,8,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,32,0,50,0,0,0,0,0, - 0,0,80,3,2,0,0,0,112,5,80,4,16,0,68,21, - 0,0,28,0,112,6,89,2,32,0,80,5,89,6,12,0, - 80,6,89,5,89,6,2,0,0,0,12,0,48,4,50,1, - 0,0,0,0,0,0,30,0,73,23,0,0,9,0,30,0, - 80,0,34,0,41,7,78,122,18,70,114,111,122,101,110,32, - 72,101,108,108,111,32,87,111,114,108,100,122,8,115,121,115, - 46,97,114,103,118,218,6,99,111,110,102,105,103,41,5,218, - 12,112,114,111,103,114,97,109,95,110,97,109,101,218,10,101, - 120,101,99,117,116,97,98,108,101,218,15,117,115,101,95,101, - 110,118,105,114,111,110,109,101,110,116,218,17,99,111,110,102, - 105,103,117,114,101,95,99,95,115,116,100,105,111,218,14,98, - 117,102,102,101,114,101,100,95,115,116,100,105,111,122,7,99, - 111,110,102,105,103,32,122,2,58,32,41,7,218,3,115,121, - 115,218,17,95,116,101,115,116,105,110,116,101,114,110,97,108, - 99,97,112,105,218,5,112,114,105,110,116,218,4,97,114,103, - 118,218,11,103,101,116,95,99,111,110,102,105,103,115,114,2, - 0,0,0,218,3,107,101,121,169,0,243,0,0,0,0,218, - 18,116,101,115,116,95,102,114,111,122,101,110,109,97,105,110, - 46,112,121,218,8,60,109,111,100,117,108,101,62,114,17,0, - 0,0,1,0,0,0,115,94,0,0,0,240,3,1,1,1, - 243,8,0,1,11,219,0,24,225,0,5,208,6,26,212,0, - 27,217,0,5,128,106,144,35,151,40,145,40,212,0,27,216, - 9,26,215,9,38,210,9,38,211,9,40,168,24,209,9,50, - 128,6,244,2,6,12,2,128,67,241,14,0,5,10,136,71, - 144,67,144,53,152,2,152,54,160,35,153,59,152,45,208,10, - 40,214,4,41,243,15,6,12,2,114,15,0,0,0, + 0,0,0,0,0,243,168,0,0,0,149,0,91,0,81,0, + 72,0,113,0,91,0,81,0,72,1,113,1,90,2,33,0, + 81,1,51,1,0,0,0,0,0,0,31,0,90,2,33,0, + 81,2,90,0,79,6,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,51,2,0,0,0,0,0,0, + 31,0,90,1,79,8,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,33,0,51,0,0,0,0,0, + 0,0,81,3,2,0,0,0,113,5,81,4,16,0,69,20, + 0,0,113,6,90,2,33,0,81,5,90,6,12,0,81,6, + 90,5,90,6,2,0,0,0,12,0,49,4,51,1,0,0, + 0,0,0,0,31,0,74,22,0,0,9,0,30,0,81,0, + 35,0,41,7,78,122,18,70,114,111,122,101,110,32,72,101, + 108,108,111,32,87,111,114,108,100,122,8,115,121,115,46,97, + 114,103,118,218,6,99,111,110,102,105,103,41,5,218,12,112, + 114,111,103,114,97,109,95,110,97,109,101,218,10,101,120,101, + 99,117,116,97,98,108,101,218,15,117,115,101,95,101,110,118, + 105,114,111,110,109,101,110,116,218,17,99,111,110,102,105,103, + 117,114,101,95,99,95,115,116,100,105,111,218,14,98,117,102, + 102,101,114,101,100,95,115,116,100,105,111,122,7,99,111,110, + 102,105,103,32,122,2,58,32,41,7,218,3,115,121,115,218, + 17,95,116,101,115,116,105,110,116,101,114,110,97,108,99,97, + 112,105,218,5,112,114,105,110,116,218,4,97,114,103,118,218, + 11,103,101,116,95,99,111,110,102,105,103,115,114,2,0,0, + 0,218,3,107,101,121,169,0,243,0,0,0,0,218,18,116, + 101,115,116,95,102,114,111,122,101,110,109,97,105,110,46,112, + 121,218,8,60,109,111,100,117,108,101,62,114,17,0,0,0, + 1,0,0,0,115,94,0,0,0,240,3,1,1,1,243,8, + 0,1,11,219,0,24,225,0,5,208,6,26,212,0,27,217, + 0,5,128,106,144,35,151,40,145,40,212,0,27,216,9,26, + 215,9,38,210,9,38,211,9,40,168,24,209,9,50,128,6, + 243,2,6,12,2,128,67,241,14,0,5,10,136,71,144,67, + 144,53,152,2,152,54,160,35,153,59,152,45,208,10,40,214, + 4,41,243,15,6,12,2,114,15,0,0,0, }; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 602cf7f47b812b..4961693c7e654a 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -60,6 +60,8 @@ #define specializing #define split #define replicate(TIMES) +#define tier1 +#define no_save_ip // Dummy variables for stack effects. static PyObject *value, *value1, *value2, *left, *right, *res, *sum, *prod, *sub; @@ -336,9 +338,18 @@ dummy_func( res = PyStackRef_NULL; } - macro(END_FOR) = POP_TOP; + no_save_ip inst(END_FOR, (value -- )) { + /* Don't update instr_ptr, so that POP_ITER sees + * the FOR_ITER as the previous instruction. + * This has the benign side effect that if value is + * finalized it will see the location as the FOR_ITER's. + */ + PyStackRef_CLOSE(value); + } + + macro(POP_ITER) = POP_TOP; - tier1 inst(INSTRUMENTED_END_FOR, (receiver, value -- receiver)) { + no_save_ip tier1 inst(INSTRUMENTED_END_FOR, (receiver, value -- receiver)) { /* Need to create a fake StopIteration error here, * to conform to PEP 380 */ if (PyStackRef_GenCheck(receiver)) { @@ -350,6 +361,11 @@ dummy_func( DECREF_INPUTS(); } + tier1 inst(INSTRUMENTED_POP_ITER, (iter -- )) { + INSTRUMENTED_JUMP(prev_instr, this_instr+1, PY_MONITORING_EVENT_BRANCH_RIGHT); + PyStackRef_CLOSE(iter); + } + pure inst(END_SEND, (receiver, value -- val)) { (void)receiver; val = value; @@ -2924,10 +2940,8 @@ dummy_func( /* iterator ended normally */ assert(next_instr[oparg].op.code == END_FOR || next_instr[oparg].op.code == INSTRUMENTED_END_FOR); - PyStackRef_CLOSE(iter); - STACK_SHRINK(1); - /* Jump forward oparg, then skip following END_FOR and POP_TOP instruction */ - JUMPBY(oparg + 2); + /* Jump forward oparg, then skip following END_FOR */ + JUMPBY(oparg + 1); DISPATCH(); } next = PyStackRef_FromPyObjectSteal(next_o); @@ -2957,12 +2971,14 @@ dummy_func( macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _FOR_ITER; + inst(INSTRUMENTED_FOR_ITER, (unused/1 -- )) { _PyStackRef iter_stackref = TOP(); PyObject *iter = PyStackRef_AsPyObjectBorrow(iter_stackref); PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); if (next != NULL) { PUSH(PyStackRef_FromPyObjectSteal(next)); + INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT); } else { if (_PyErr_Occurred(tstate)) { @@ -2976,14 +2992,12 @@ dummy_func( /* iterator ended normally */ assert(next_instr[oparg].op.code == END_FOR || next_instr[oparg].op.code == INSTRUMENTED_END_FOR); - STACK_SHRINK(1); - PyStackRef_CLOSE(iter_stackref); - /* Skip END_FOR and POP_TOP */ - _Py_CODEUNIT *target = next_instr + oparg + 2; - INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH_RIGHT); + /* Skip END_FOR */ + JUMPBY(oparg + 1); } } + op(_ITER_CHECK_LIST, (iter -- iter)) { EXIT_IF(Py_TYPE(PyStackRef_AsPyObjectBorrow(iter)) != &PyListIter_Type); } @@ -3002,10 +3016,8 @@ dummy_func( Py_DECREF(seq); } #endif - PyStackRef_CLOSE(iter); - STACK_SHRINK(1); - /* Jump forward oparg, then skip following END_FOR and POP_TOP instructions */ - JUMPBY(oparg + 2); + /* Jump forward oparg, then skip following END_FOR instruction */ + JUMPBY(oparg + 1); DISPATCH(); } } @@ -3054,10 +3066,8 @@ dummy_func( it->it_seq = NULL; Py_DECREF(seq); } - PyStackRef_CLOSE(iter); - STACK_SHRINK(1); - /* Jump forward oparg, then skip following END_FOR and POP_TOP instructions */ - JUMPBY(oparg + 2); + /* Jump forward oparg, then skip following END_FOR instruction */ + JUMPBY(oparg + 1); DISPATCH(); } } @@ -3098,10 +3108,8 @@ dummy_func( assert(Py_TYPE(r) == &PyRangeIter_Type); STAT_INC(FOR_ITER, hit); if (r->len <= 0) { - STACK_SHRINK(1); - PyStackRef_CLOSE(iter); - // Jump over END_FOR and POP_TOP instructions. - JUMPBY(oparg + 2); + // Jump over END_FOR instruction. + JUMPBY(oparg + 1); DISPATCH(); } } @@ -4779,7 +4787,8 @@ dummy_func( } inst(INSTRUMENTED_NOT_TAKEN, ( -- )) { - INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT); + (void)this_instr; // INSTRUMENTED_JUMP requires this_instr + INSTRUMENTED_JUMP(prev_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT); } macro(INSTRUMENTED_JUMP_BACKWARD) = diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index f15633fa467376..c37e1cf3afa60e 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -363,7 +363,7 @@ do { \ next_instr = dest; \ } else { \ _PyFrame_SetStackPointer(frame, stack_pointer); \ - next_instr = _Py_call_instrumentation_jump(tstate, event, frame, src, dest); \ + next_instr = _Py_call_instrumentation_jump(this_instr, tstate, event, frame, src, dest); \ stack_pointer = _PyFrame_GetStackPointer(frame); \ if (next_instr == NULL) { \ next_instr = (dest)+1; \ diff --git a/Python/codegen.c b/Python/codegen.c index 7432415b17414e..14f9f5ad1254f5 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -409,7 +409,7 @@ codegen_addop_j(instr_sequence *seq, location loc, if (_PyInstructionSequence_Addop(seq, opcode, target.id, loc) != SUCCESS) { return ERROR; } - if (IS_CONDITIONAL_JUMP_OPCODE(opcode) || opcode == FOR_ITER) { + if (IS_CONDITIONAL_JUMP_OPCODE(opcode)) { return _PyInstructionSequence_Addop(seq, NOT_TAKEN, 0, NO_LOCATION); } return SUCCESS; @@ -2018,7 +2018,7 @@ codegen_for(compiler *c, stmt_ty s) * but a non-generator will jump to a later instruction. */ ADDOP(c, NO_LOCATION, END_FOR); - ADDOP(c, NO_LOCATION, POP_TOP); + ADDOP(c, NO_LOCATION, POP_ITER); _PyCompile_PopFBlock(c, COMPILE_FBLOCK_FOR_LOOP, start); @@ -4283,7 +4283,7 @@ codegen_sync_comprehension_generator(compiler *c, location loc, * but a non-generator will jump to a later instruction. */ ADDOP(c, NO_LOCATION, END_FOR); - ADDOP(c, NO_LOCATION, POP_TOP); + ADDOP(c, NO_LOCATION, POP_ITER); } return SUCCESS; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index f7374d52705960..ac2f69b7e98dc3 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -411,6 +411,20 @@ break; } + case _END_FOR: { + _PyStackRef value; + value = stack_pointer[-1]; + /* Don't update instr_ptr, so that POP_ITER sees + * the FOR_ITER as the previous instruction. + * This has the benign side effect that if value is + * finalized it will see the location as the FOR_ITER's. + */ + PyStackRef_CLOSE(value); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + break; + } + case _END_SEND: { _PyStackRef value; _PyStackRef receiver; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 98743c27c38524..b73844ca2d9542 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3768,11 +3768,15 @@ } TARGET(END_FOR) { - frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(END_FOR); _PyStackRef value; value = stack_pointer[-1]; + /* Don't update instr_ptr, so that POP_ITER sees + * the FOR_ITER as the previous instruction. + * This has the benign side effect that if value is + * finalized it will see the location as the FOR_ITER's. + */ PyStackRef_CLOSE(value); stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -3957,10 +3961,8 @@ /* iterator ended normally */ assert(next_instr[oparg].op.code == END_FOR || next_instr[oparg].op.code == INSTRUMENTED_END_FOR); - PyStackRef_CLOSE(iter); - STACK_SHRINK(1); - /* Jump forward oparg, then skip following END_FOR and POP_TOP instruction */ - JUMPBY(oparg + 2); + /* Jump forward oparg, then skip following END_FOR */ + JUMPBY(oparg + 1); DISPATCH(); } next = PyStackRef_FromPyObjectSteal(next_o); @@ -4048,10 +4050,8 @@ Py_DECREF(seq); } #endif - PyStackRef_CLOSE(iter); - STACK_SHRINK(1); - /* Jump forward oparg, then skip following END_FOR and POP_TOP instructions */ - JUMPBY(oparg + 2); + /* Jump forward oparg, then skip following END_FOR instruction */ + JUMPBY(oparg + 1); DISPATCH(); } } @@ -4091,10 +4091,8 @@ assert(Py_TYPE(r) == &PyRangeIter_Type); STAT_INC(FOR_ITER, hit); if (r->len <= 0) { - STACK_SHRINK(1); - PyStackRef_CLOSE(iter); - // Jump over END_FOR and POP_TOP instructions. - JUMPBY(oparg + 2); + // Jump over END_FOR instruction. + JUMPBY(oparg + 1); DISPATCH(); } } @@ -4141,10 +4139,8 @@ it->it_seq = NULL; Py_DECREF(seq); } - PyStackRef_CLOSE(iter); - STACK_SHRINK(1); - /* Jump forward oparg, then skip following END_FOR and POP_TOP instructions */ - JUMPBY(oparg + 2); + /* Jump forward oparg, then skip following END_FOR instruction */ + JUMPBY(oparg + 1); DISPATCH(); } } @@ -4572,7 +4568,7 @@ } TARGET(INSTRUMENTED_END_FOR) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_END_FOR); @@ -4636,6 +4632,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); if (next != NULL) { PUSH(PyStackRef_FromPyObjectSteal(next)); + INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT); } else { if (_PyErr_Occurred(tstate)) { @@ -4653,11 +4650,8 @@ /* iterator ended normally */ assert(next_instr[oparg].op.code == END_FOR || next_instr[oparg].op.code == INSTRUMENTED_END_FOR); - STACK_SHRINK(1); - PyStackRef_CLOSE(iter_stackref); - /* Skip END_FOR and POP_TOP */ - _Py_CODEUNIT *target = next_instr + oparg + 2; - INSTRUMENTED_JUMP(this_instr, target, PY_MONITORING_EVENT_BRANCH_RIGHT); + /* Skip END_FOR */ + JUMPBY(oparg + 1); } DISPATCH(); } @@ -4764,11 +4758,28 @@ } TARGET(INSTRUMENTED_NOT_TAKEN) { + _Py_CODEUNIT* const prev_instr = frame->instr_ptr; _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; (void)this_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_NOT_TAKEN); - INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT); + (void)this_instr; // INSTRUMENTED_JUMP requires this_instr + INSTRUMENTED_JUMP(prev_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT); + DISPATCH(); + } + + TARGET(INSTRUMENTED_POP_ITER) { + _Py_CODEUNIT* const prev_instr = frame->instr_ptr; + _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + (void)this_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_POP_ITER); + _PyStackRef iter; + iter = stack_pointer[-1]; + INSTRUMENTED_JUMP(prev_instr, this_instr+1, PY_MONITORING_EVENT_BRANCH_RIGHT); + PyStackRef_CLOSE(iter); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -6693,6 +6704,18 @@ DISPATCH(); } + TARGET(POP_ITER) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(POP_ITER); + _PyStackRef value; + value = stack_pointer[-1]; + PyStackRef_CLOSE(value); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + DISPATCH(); + } + TARGET(POP_JUMP_IF_FALSE) { _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; (void)this_instr; diff --git a/Python/instrumentation.c b/Python/instrumentation.c index e4255bfad8c41a..17e5346be5ed3d 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -14,6 +14,7 @@ #include "pycore_namespace.h" #include "pycore_object.h" #include "pycore_opcode_metadata.h" // IS_VALID_OPCODE, _PyOpcode_Caches +#include "pycore_opcode_utils.h" // IS_CONDITIONAL_JUMP_OPCODE #include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_STORE_UINTPTR_RELEASE #include "pycore_pyerrors.h" #include "pycore_pystate.h" // _PyInterpreterState_GET() @@ -95,8 +96,10 @@ static const int8_t EVENT_FOR_OPCODE[256] = { [INSTRUMENTED_POP_JUMP_IF_TRUE] = PY_MONITORING_EVENT_BRANCH_RIGHT, [INSTRUMENTED_POP_JUMP_IF_NONE] = PY_MONITORING_EVENT_BRANCH_RIGHT, [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = PY_MONITORING_EVENT_BRANCH_RIGHT, - [FOR_ITER] = PY_MONITORING_EVENT_BRANCH_RIGHT, - [INSTRUMENTED_FOR_ITER] = PY_MONITORING_EVENT_BRANCH_RIGHT, + [FOR_ITER] = PY_MONITORING_EVENT_BRANCH_LEFT, + [INSTRUMENTED_FOR_ITER] = PY_MONITORING_EVENT_BRANCH_LEFT, + [POP_ITER] = PY_MONITORING_EVENT_BRANCH_RIGHT, + [INSTRUMENTED_POP_ITER] = PY_MONITORING_EVENT_BRANCH_RIGHT, [END_FOR] = PY_MONITORING_EVENT_STOP_ITERATION, [INSTRUMENTED_END_FOR] = PY_MONITORING_EVENT_STOP_ITERATION, [END_SEND] = PY_MONITORING_EVENT_STOP_ITERATION, @@ -119,6 +122,7 @@ static const uint8_t DE_INSTRUMENT[256] = { [INSTRUMENTED_POP_JUMP_IF_NONE] = POP_JUMP_IF_NONE, [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = POP_JUMP_IF_NOT_NONE, [INSTRUMENTED_FOR_ITER] = FOR_ITER, + [INSTRUMENTED_POP_ITER] = POP_ITER, [INSTRUMENTED_END_FOR] = END_FOR, [INSTRUMENTED_END_SEND] = END_SEND, [INSTRUMENTED_LOAD_SUPER_ATTR] = LOAD_SUPER_ATTR, @@ -156,6 +160,8 @@ static const uint8_t INSTRUMENTED_OPCODES[256] = { [INSTRUMENTED_END_SEND] = INSTRUMENTED_END_SEND, [FOR_ITER] = INSTRUMENTED_FOR_ITER, [INSTRUMENTED_FOR_ITER] = INSTRUMENTED_FOR_ITER, + [POP_ITER] = INSTRUMENTED_POP_ITER, + [INSTRUMENTED_POP_ITER] = INSTRUMENTED_POP_ITER, [LOAD_SUPER_ATTR] = INSTRUMENTED_LOAD_SUPER_ATTR, [INSTRUMENTED_LOAD_SUPER_ATTR] = INSTRUMENTED_LOAD_SUPER_ATTR, [NOT_TAKEN] = INSTRUMENTED_NOT_TAKEN, @@ -1077,8 +1083,8 @@ static const char *const event_names [] = { static int call_instrumentation_vector( - PyThreadState *tstate, int event, - _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, Py_ssize_t nargs, PyObject *args[]) + _Py_CODEUNIT *instr, PyThreadState *tstate, int event, + _PyInterpreterFrame *frame, _Py_CODEUNIT *arg2, Py_ssize_t nargs, PyObject *args[]) { if (tstate->tracing) { return 0; @@ -1091,17 +1097,13 @@ call_instrumentation_vector( int offset = (int)(instr - _PyFrame_GetBytecode(frame)); /* Offset visible to user should be the offset in bytes, as that is the * convention for APIs involving code offsets. */ - int bytes_offset = offset * (int)sizeof(_Py_CODEUNIT); - if (event == PY_MONITORING_EVENT_BRANCH_LEFT) { - assert(EVENT_FOR_OPCODE[_Py_GetBaseCodeUnit(code, offset-2).op.code] == PY_MONITORING_EVENT_BRANCH_RIGHT); - bytes_offset -= 4; - } - PyObject *offset_obj = PyLong_FromLong(bytes_offset); - if (offset_obj == NULL) { + int bytes_arg2 = (int)(arg2 - _PyFrame_GetBytecode(frame)) * (int)sizeof(_Py_CODEUNIT); + PyObject *arg2_obj = PyLong_FromLong(bytes_arg2); + if (arg2_obj == NULL) { return -1; } assert(args[2] == NULL); - args[2] = offset_obj; + args[2] = arg2_obj; PyInterpreterState *interp = tstate->interp; uint8_t tools = get_tools_for_instruction(code, interp, offset, event); size_t nargsf = (size_t) nargs | PY_VECTORCALL_ARGUMENTS_OFFSET; @@ -1139,7 +1141,7 @@ call_instrumentation_vector( } } } - Py_DECREF(offset_obj); + Py_DECREF(arg2_obj); return err; } @@ -1149,7 +1151,7 @@ _Py_call_instrumentation( _PyInterpreterFrame *frame, _Py_CODEUNIT *instr) { PyObject *args[3] = { NULL, NULL, NULL }; - return call_instrumentation_vector(tstate, event, frame, instr, 2, args); + return call_instrumentation_vector(instr, tstate, event, frame, instr, 2, args); } int @@ -1158,7 +1160,7 @@ _Py_call_instrumentation_arg( _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg) { PyObject *args[4] = { NULL, NULL, NULL, arg }; - return call_instrumentation_vector(tstate, event, frame, instr, 3, args); + return call_instrumentation_vector(instr, tstate, event, frame, instr, 3, args); } int @@ -1167,34 +1169,34 @@ _Py_call_instrumentation_2args( _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg0, PyObject *arg1) { PyObject *args[5] = { NULL, NULL, NULL, arg0, arg1 }; - return call_instrumentation_vector(tstate, event, frame, instr, 4, args); + return call_instrumentation_vector(instr, tstate, event, frame, instr, 4, args); } _Py_CODEUNIT * _Py_call_instrumentation_jump( - PyThreadState *tstate, int event, - _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, _Py_CODEUNIT *target) + _Py_CODEUNIT *instr, PyThreadState *tstate, int event, + _PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest) { assert(event == PY_MONITORING_EVENT_JUMP || event == PY_MONITORING_EVENT_BRANCH_RIGHT || event == PY_MONITORING_EVENT_BRANCH_LEFT); - assert(frame->instr_ptr == instr); - int to = (int)(target - _PyFrame_GetBytecode(frame)); + int to = (int)(dest - _PyFrame_GetBytecode(frame)); PyObject *to_obj = PyLong_FromLong(to * (int)sizeof(_Py_CODEUNIT)); if (to_obj == NULL) { return NULL; } PyObject *args[4] = { NULL, NULL, NULL, to_obj }; - int err = call_instrumentation_vector(tstate, event, frame, instr, 3, args); + _Py_CODEUNIT *instr_ptr = frame->instr_ptr; + int err = call_instrumentation_vector(instr, tstate, event, frame, src, 3, args); Py_DECREF(to_obj); if (err) { return NULL; } - if (frame->instr_ptr != instr) { + if (frame->instr_ptr != instr_ptr) { /* The callback has caused a jump (by setting the line number) */ return frame->instr_ptr; } - return target; + return dest; } static void @@ -1204,7 +1206,7 @@ call_instrumentation_vector_protected( { assert(_PyErr_Occurred(tstate)); PyObject *exc = _PyErr_GetRaisedException(tstate); - int err = call_instrumentation_vector(tstate, event, frame, instr, nargs, args); + int err = call_instrumentation_vector(instr, tstate, event, frame, instr, nargs, args); if (err) { Py_XDECREF(exc); } @@ -1496,9 +1498,10 @@ initialize_lines(PyCodeObject *code) case END_FOR: case END_SEND: case RESUME: + case POP_ITER: /* END_FOR cannot start a line, as it is skipped by FOR_ITER * END_SEND cannot start a line, as it is skipped by SEND - * RESUME must not be instrumented with INSTRUMENT_LINE */ + * RESUME and POP_ITER must not be instrumented with INSTRUMENT_LINE */ line_data[i].original_opcode = 0; break; default: @@ -1570,11 +1573,14 @@ initialize_lines(PyCodeObject *code) } assert(target >= 0); if (line_data[target].line_delta != NO_LINE) { - line_data[target].original_opcode = _Py_GetBaseCodeUnit(code, target).op.code; - if (line_data[target].line_delta == COMPUTED_LINE_LINENO_CHANGE) { - // If the line is a jump target, we are not sure if the line - // number changes, so we set it to COMPUTED_LINE. - line_data[target].line_delta = COMPUTED_LINE; + int opcode = _Py_GetBaseCodeUnit(code, target).op.code; + if (opcode != POP_ITER) { + line_data[target].original_opcode = opcode; + if (line_data[target].line_delta == COMPUTED_LINE_LINENO_CHANGE) { + // If the line is a jump target, we are not sure if the line + // number changes, so we set it to COMPUTED_LINE. + line_data[target].line_delta = COMPUTED_LINE; + } } } } @@ -2887,30 +2893,52 @@ branch_handler( _PyLegacyBranchEventHandler *self, PyObject *const *args, size_t nargsf, PyObject *kwnames ) { + // Find the other instrumented instruction and remove tool + // The spec (PEP 669) allows spurious events after a DISABLE, + // so a best effort is good enough. + assert(PyVectorcall_NARGS(nargsf) >= 3); + PyCodeObject *code = (PyCodeObject *)args[0]; + int src_offset = PyLong_AsLong(args[1]); + if (PyErr_Occurred()) { + return NULL; + } + _Py_CODEUNIT instr = _PyCode_CODE(code)[src_offset/2]; + if (!is_instrumented(instr.op.code)) { + /* Already disabled */ + return &_PyInstrumentation_DISABLE; + } PyObject *res = PyObject_Vectorcall(self->handler, args, nargsf, kwnames); if (res == &_PyInstrumentation_DISABLE) { - // Find the other instrumented instruction and remove tool - assert(PyVectorcall_NARGS(nargsf) >= 2); - PyObject *offset_obj = args[1]; - int bytes_offset = PyLong_AsLong(offset_obj); - if (PyErr_Occurred()) { - return NULL; - } - PyCodeObject *code = (PyCodeObject *)args[0]; - if (!PyCode_Check(code) || (bytes_offset & 1)) { - return res; - } - int offset = bytes_offset / 2; /* We need FOR_ITER and POP_JUMP_ to be the same size */ assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1); - if (self->right) { - offset += 2; + int offset; + int other_event; + if (instr.op.code == FOR_ITER) { + if (self->right) { + offset = src_offset/2; + other_event = PY_MONITORING_EVENT_BRANCH_LEFT; + } + else { + // We don't know where the POP_ITER is, so + // we cannot de-instrument it. + return res; + } + } + else if (IS_CONDITIONAL_JUMP_OPCODE(instr.op.code)) { + if (self->right) { + offset = src_offset/2 + 2; + other_event = PY_MONITORING_EVENT_BRANCH_LEFT; + assert(_Py_GetBaseCodeUnit(code, offset).op.code == NOT_TAKEN); + } + else { + offset = src_offset/2; + other_event = PY_MONITORING_EVENT_BRANCH_RIGHT; + } } - if (offset >= Py_SIZE(code)) { + else { + // Orphaned NOT_TAKEN -- Jump removed by the compiler return res; } - int other_event = self->right ? - PY_MONITORING_EVENT_BRANCH_LEFT : PY_MONITORING_EVENT_BRANCH_RIGHT; LOCK_CODE(code); remove_tools(code, offset, other_event, 1 << self->tool_id); UNLOCK_CODE(); @@ -3013,15 +3041,30 @@ static PyObject * branchesiter_next(branchesiterator *bi) { int offset = bi->bi_offset; + int oparg = 0; while (offset < Py_SIZE(bi->bi_code)) { _Py_CODEUNIT inst = _Py_GetBaseCodeUnit(bi->bi_code, offset); - int next_offset = offset + _PyInstruction_GetLength(bi->bi_code, offset); - int event = EVENT_FOR_OPCODE[inst.op.code]; - if (event == PY_MONITORING_EVENT_BRANCH_RIGHT) { - /* Skip NOT_TAKEN */ - int not_taken = next_offset + 1; - bi->bi_offset = not_taken; - return int_triple(offset*2, not_taken*2, (next_offset + inst.op.arg)*2); + int next_offset = offset + 1 + _PyOpcode_Caches[inst.op.code]; + switch(inst.op.code) { + case EXTENDED_ARG: + oparg = (oparg << 8) | inst.op.arg; + break; + case FOR_ITER: + oparg = (oparg << 8) | inst.op.arg; + bi->bi_offset = next_offset; + int target = next_offset + oparg+2; // Skips END_FOR and POP_ITER + return int_triple(offset*2, next_offset*2, target*2); + case POP_JUMP_IF_FALSE: + case POP_JUMP_IF_TRUE: + case POP_JUMP_IF_NONE: + case POP_JUMP_IF_NOT_NONE: + oparg = (oparg << 8) | inst.op.arg; + /* Skip NOT_TAKEN */ + int not_taken = next_offset + 1; + bi->bi_offset = not_taken; + return int_triple(offset*2, not_taken*2, (next_offset + oparg)*2); + default: + oparg = 0; } offset = next_offset; } diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 7f3fb9c9a63dd1..00d55d3a82edc1 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -29,6 +29,7 @@ static void *opcode_targets[256] = { &&TARGET_NOP, &&TARGET_NOT_TAKEN, &&TARGET_POP_EXCEPT, + &&TARGET_POP_ITER, &&TARGET_POP_TOP, &&TARGET_PUSH_EXC_INFO, &&TARGET_PUSH_NULL, @@ -147,7 +148,6 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, - &&_unknown_opcode, &&TARGET_RESUME, &&TARGET_BINARY_OP_ADD_FLOAT, &&TARGET_BINARY_OP_ADD_INT, @@ -234,8 +234,8 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, - &&_unknown_opcode, &&TARGET_INSTRUMENTED_END_FOR, + &&TARGET_INSTRUMENTED_POP_ITER, &&TARGET_INSTRUMENTED_END_SEND, &&TARGET_INSTRUMENTED_LOAD_SUPER_ATTR, &&TARGET_INSTRUMENTED_FOR_ITER, diff --git a/Python/optimizer.c b/Python/optimizer.c index 6a4d20fad76c15..52b3f0a84afedf 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -622,8 +622,14 @@ translate_bytecode_to_trace( goto done; } assert(opcode != ENTER_EXECUTOR && opcode != EXTENDED_ARG); - RESERVE_RAW(2, "_CHECK_VALIDITY_AND_SET_IP"); - ADD_TO_TRACE(_CHECK_VALIDITY_AND_SET_IP, 0, (uintptr_t)instr, target); + if (OPCODE_HAS_NO_SAVE_IP(opcode)) { + RESERVE_RAW(2, "_CHECK_VALIDITY"); + ADD_TO_TRACE(_CHECK_VALIDITY, 0, 0, target); + } + else { + RESERVE_RAW(2, "_CHECK_VALIDITY_AND_SET_IP"); + ADD_TO_TRACE(_CHECK_VALIDITY_AND_SET_IP, 0, (uintptr_t)instr, target); + } /* Special case the first instruction, * so that we can guarantee forward progress */ @@ -771,7 +777,7 @@ translate_bytecode_to_trace( uint32_t next_inst = target + 1 + INLINE_CACHE_ENTRIES_FOR_ITER + (oparg > 255); uint32_t jump_target = next_inst + oparg; assert(_Py_GetBaseCodeUnit(code, jump_target).op.code == END_FOR); - assert(_Py_GetBaseCodeUnit(code, jump_target+1).op.code == POP_TOP); + assert(_Py_GetBaseCodeUnit(code, jump_target+1).op.code == POP_ITER); } #endif break; diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 0fcf5e18ed5808..be3e06108aec92 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -115,6 +115,12 @@ break; } + case _END_FOR: { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + break; + } + case _END_SEND: { _Py_UopsSymbol *val; val = sym_new_not_null(ctx); diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 73c871759afbf5..c0a370a936aa94 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -27,6 +27,7 @@ class Properties: oparg_and_1: bool = False const_oparg: int = -1 needs_prev: bool = False + no_save_ip: bool = False def dump(self, indent: str) -> None: simple_properties = self.__dict__.copy() @@ -60,6 +61,7 @@ def from_list(properties: list["Properties"]) -> "Properties": side_exit=any(p.side_exit for p in properties), pure=all(p.pure for p in properties), needs_prev=any(p.needs_prev for p in properties), + no_save_ip=all(p.no_save_ip for p in properties), ) @property @@ -87,6 +89,7 @@ def escapes(self) -> bool: has_free=False, side_exit=False, pure=True, + no_save_ip=False, ) @@ -829,6 +832,7 @@ def compute_properties(op: parser.InstDef) -> Properties: and not has_free, has_free=has_free, pure="pure" in op.annotations, + no_save_ip="no_save_ip" in op.annotations, tier=tier_variable(op), needs_prev=variable_used(op, "prev_instr"), ) diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index d17617cab0266b..8df9a9cada92ae 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -583,6 +583,8 @@ def cflags(p: Properties) -> str: flags.append("HAS_ESCAPES_FLAG") if p.pure: flags.append("HAS_PURE_FLAG") + if p.no_save_ip: + flags.append("HAS_NO_SAVE_IP_FLAG") if p.oparg_and_1: flags.append("HAS_OPARG_AND_1_FLAG") if flags: diff --git a/Tools/cases_generator/lexer.py b/Tools/cases_generator/lexer.py index 37f96398ff175f..bee2a185745f4d 100644 --- a/Tools/cases_generator/lexer.py +++ b/Tools/cases_generator/lexer.py @@ -226,6 +226,7 @@ def choice(*opts: str) -> str: "replicate", "tier1", "tier2", + "no_save_ip", } __all__ = [] diff --git a/Tools/cases_generator/opcode_metadata_generator.py b/Tools/cases_generator/opcode_metadata_generator.py index 1a9849c0cbbb25..453db6905d6842 100644 --- a/Tools/cases_generator/opcode_metadata_generator.py +++ b/Tools/cases_generator/opcode_metadata_generator.py @@ -53,6 +53,7 @@ "PASSTHROUGH", "OPARG_AND_1", "ERROR_NO_POP", + "NO_SAVE_IP", ] @@ -285,8 +286,8 @@ def generate_metadata_table(analysis: Analysis, out: CWriter) -> None: table_size = 256 + len(analysis.pseudos) out.emit("struct opcode_metadata {\n") out.emit("uint8_t valid_entry;\n") - out.emit("int8_t instr_format;\n") - out.emit("int16_t flags;\n") + out.emit("uint8_t instr_format;\n") + out.emit("uint16_t flags;\n") out.emit("};\n\n") out.emit( f"extern const struct opcode_metadata _PyOpcode_opcode_metadata[{table_size}];\n" diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py index fcdd3bdacd0e7a..40562da99b20ea 100644 --- a/Tools/cases_generator/tier1_generator.py +++ b/Tools/cases_generator/tier1_generator.py @@ -151,9 +151,12 @@ def generate_tier1( if inst.properties.needs_prev: out.emit(f"_Py_CODEUNIT* const prev_instr = frame->instr_ptr;\n") if needs_this and not inst.is_target: - out.emit(f"_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;\n") + if inst.properties.no_save_ip: + out.emit(f"_Py_CODEUNIT* const this_instr = next_instr;\n") + else: + out.emit(f"_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;\n") out.emit(unused_guard) - else: + elif not inst.properties.no_save_ip: out.emit(f"frame->instr_ptr = next_instr;\n") out.emit(f"next_instr += {inst.size};\n") out.emit(f"INSTRUCTION_STATS({name});\n") From b6c919b674549d519efbbc378588ca6d3efc7242 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 6 Jan 2025 20:45:14 +0100 Subject: [PATCH 207/376] gh-127146: Fix test_sysconfigdata_json for Emscripten (#128556) --- Lib/sysconfig/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index ed7b6a335d01d4..20d506bcd45abc 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -485,10 +485,10 @@ def _init_config_vars(): _init_posix(_CONFIG_VARS) # If we are cross-compiling, load the prefixes from the Makefile instead. if '_PYTHON_PROJECT_BASE' in os.environ: - prefix = _CONFIG_VARS['prefix'] - exec_prefix = _CONFIG_VARS['exec_prefix'] - base_prefix = _CONFIG_VARS['prefix'] - base_exec_prefix = _CONFIG_VARS['exec_prefix'] + prefix = _CONFIG_VARS['host_prefix'] + exec_prefix = _CONFIG_VARS['host_exec_prefix'] + base_prefix = _CONFIG_VARS['host_prefix'] + base_exec_prefix = _CONFIG_VARS['host_exec_prefix'] abiflags = _CONFIG_VARS['ABIFLAGS'] # Normalized versions of prefix and exec_prefix are handy to have; From 2434fd2d50b8b770585ad5949a664e4bbab4bde1 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 6 Jan 2025 22:01:07 +0000 Subject: [PATCH 208/376] GH-128533: Add `NOT_TAKEN` instruction after bytecode optimization. (GH-128554) --- Lib/test/test_compiler_codegen.py | 1 - Lib/test/test_dis.py | 248 +++++++++++++++--------------- Python/codegen.c | 8 +- Python/flowgraph.c | 9 +- 4 files changed, 127 insertions(+), 139 deletions(-) diff --git a/Lib/test/test_compiler_codegen.py b/Lib/test/test_compiler_codegen.py index 5655e1b9cf196a..cf5e2d901db4de 100644 --- a/Lib/test/test_compiler_codegen.py +++ b/Lib/test/test_compiler_codegen.py @@ -29,7 +29,6 @@ def test_if_expression(self): ('LOAD_CONST', 0, 1), ('TO_BOOL', 0, 1), ('POP_JUMP_IF_FALSE', false_lbl := self.Label(), 1), - ('NOT_TAKEN', None, 1), ('LOAD_SMALL_INT', 42, 1), ('JUMP_NO_INTERRUPT', exit_lbl := self.Label()), false_lbl, diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index e733a673d003e7..76d9b5401d7d8e 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -200,8 +200,7 @@ def bug1333982(x=[]): dis_bug1333982 = """\ %3d RESUME 0 -%3d NOT_TAKEN - LOAD_COMMON_CONSTANT 0 (AssertionError) +%3d LOAD_COMMON_CONSTANT 0 (AssertionError) LOAD_CONST 0 ( at 0x..., file "%s", line %d>) MAKE_FUNCTION LOAD_FAST 0 (x) @@ -433,7 +432,7 @@ def foo(a: int, b: str) -> str: 1 LOAD_SMALL_INT 0 STORE_NAME 0 (x) - 2 L1: NOT_TAKEN + 2 L1: NOP 3 LOAD_NAME 0 (x) LOAD_SMALL_INT 1 @@ -648,11 +647,11 @@ async def _asyncwith(c): L20: CLEANUP_THROW L21: END_SEND TO_BOOL - POP_JUMP_IF_TRUE 2 (to L22) - NOT_TAKEN - RERAISE 2 - L22: POP_TOP - L23: POP_EXCEPT + POP_JUMP_IF_TRUE 2 (to L24) + L22: NOT_TAKEN + L23: RERAISE 2 + L24: POP_TOP + L25: POP_EXCEPT POP_TOP POP_TOP POP_TOP @@ -662,24 +661,25 @@ async def _asyncwith(c): LOAD_CONST 0 (None) RETURN_VALUE - -- L24: COPY 3 + -- L26: COPY 3 POP_EXCEPT RERAISE 1 - L25: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR) + L27: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR) RERAISE 1 ExceptionTable: - L1 to L3 -> L25 [0] lasti + L1 to L3 -> L27 [0] lasti L3 to L4 -> L12 [4] - L4 to L6 -> L25 [0] lasti + L4 to L6 -> L27 [0] lasti L6 to L7 -> L16 [2] lasti - L7 to L9 -> L25 [0] lasti + L7 to L9 -> L27 [0] lasti L9 to L10 -> L14 [2] - L10 to L13 -> L25 [0] lasti - L14 to L15 -> L25 [0] lasti - L16 to L18 -> L24 [4] lasti + L10 to L13 -> L27 [0] lasti + L14 to L15 -> L27 [0] lasti + L16 to L18 -> L26 [4] lasti L18 to L19 -> L20 [7] - L19 to L23 -> L24 [4] lasti - L23 to L25 -> L25 [0] lasti + L19 to L22 -> L26 [4] lasti + L23 to L25 -> L26 [4] lasti + L25 to L27 -> L27 [0] lasti """ % (_asyncwith.__code__.co_firstlineno, _asyncwith.__code__.co_firstlineno + 1, _asyncwith.__code__.co_firstlineno + 2, @@ -1777,7 +1777,7 @@ def _prepare_test_cases(): Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=10, argval=10, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), Instruction(opname='GET_ITER', opcode=16, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='FOR_ITER', opcode=69, arg=33, argval=94, argrepr='to L4', offset=24, start_offset=24, starts_line=False, line_number=3, label=1, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='FOR_ITER', opcode=69, arg=32, argval=92, argrepr='to L4', offset=24, start_offset=24, starts_line=False, line_number=3, label=1, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), Instruction(opname='STORE_FAST', opcode=109, arg=0, argval='i', argrepr='i', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=40, start_offset=40, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), @@ -1795,113 +1795,111 @@ def _prepare_test_cases(): Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=3, argval=88, argrepr='to L3', offset=78, start_offset=78, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=82, start_offset=82, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), Instruction(opname='JUMP_BACKWARD', opcode=74, arg=32, argval=24, argrepr='to L1', offset=84, start_offset=84, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=88, start_offset=88, starts_line=True, line_number=None, label=3, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=90, start_offset=90, starts_line=True, line_number=8, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_FORWARD', opcode=76, arg=13, argval=120, argrepr='to L5', offset=92, start_offset=92, starts_line=False, line_number=8, label=None, positions=None, cache_info=None), - Instruction(opname='END_FOR', opcode=9, arg=None, argval=None, argrepr='', offset=94, start_offset=94, starts_line=True, line_number=3, label=4, positions=None, cache_info=None), - Instruction(opname='POP_ITER', opcode=30, arg=None, argval=None, argrepr='', offset=96, start_offset=96, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=98, start_offset=98, starts_line=True, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=0, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=108, start_offset=108, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=110, start_offset=110, starts_line=False, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=118, start_offset=118, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST_CHECK', opcode=85, arg=0, argval='i', argrepr='i', offset=120, start_offset=120, starts_line=True, line_number=11, label=5, positions=None, cache_info=None), - Instruction(opname='TO_BOOL', opcode=39, arg=None, argval=None, argrepr='', offset=122, start_offset=122, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=37, argval=208, argrepr='to L8', offset=130, start_offset=130, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=134, start_offset=134, starts_line=False, line_number=11, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=136, start_offset=136, starts_line=True, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=146, start_offset=146, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=148, start_offset=148, starts_line=False, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=156, start_offset=156, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=158, start_offset=158, starts_line=True, line_number=13, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=1, argval=1, argrepr='', offset=160, start_offset=160, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), - Instruction(opname='BINARY_OP', opcode=44, arg=23, argval=23, argrepr='-=', offset=162, start_offset=162, starts_line=False, line_number=13, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='STORE_FAST', opcode=109, arg=0, argval='i', argrepr='i', offset=166, start_offset=166, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=168, start_offset=168, starts_line=True, line_number=14, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=6, argval=6, argrepr='', offset=170, start_offset=170, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=56, arg=148, argval='>', argrepr='bool(>)', offset=172, start_offset=172, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=3, argval=186, argrepr='to L6', offset=176, start_offset=176, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=180, start_offset=180, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD', opcode=74, arg=33, argval=120, argrepr='to L5', offset=182, start_offset=182, starts_line=True, line_number=15, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=186, start_offset=186, starts_line=True, line_number=16, label=6, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=4, argval=4, argrepr='', offset=188, start_offset=188, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=56, arg=18, argval='<', argrepr='bool(<)', offset=190, start_offset=190, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=3, argval=204, argrepr='to L7', offset=194, start_offset=194, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=198, start_offset=198, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD', opcode=74, arg=42, argval=120, argrepr='to L5', offset=200, start_offset=200, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=204, start_offset=204, starts_line=True, line_number=None, label=7, positions=None, cache_info=None), - Instruction(opname='JUMP_FORWARD', opcode=76, arg=11, argval=230, argrepr='to L9', offset=206, start_offset=206, starts_line=True, line_number=17, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=208, start_offset=208, starts_line=True, line_number=19, label=8, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=1, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=218, start_offset=218, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=220, start_offset=220, starts_line=False, line_number=19, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=228, start_offset=228, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), - Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=230, start_offset=230, starts_line=True, line_number=20, label=9, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=1, argval=1, argrepr='', offset=232, start_offset=232, starts_line=True, line_number=21, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=0, argval=0, argrepr='', offset=234, start_offset=234, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), - Instruction(opname='BINARY_OP', opcode=44, arg=11, argval=11, argrepr='/', offset=236, start_offset=236, starts_line=False, line_number=21, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=240, start_offset=240, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=242, start_offset=242, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=59, arg=1, argval=1, argrepr='', offset=244, start_offset=244, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SPECIAL', opcode=92, arg=1, argval=1, argrepr='__exit__', offset=246, start_offset=246, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='SWAP', opcode=114, arg=2, argval=2, argrepr='', offset=248, start_offset=248, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='SWAP', opcode=114, arg=3, argval=3, argrepr='', offset=250, start_offset=250, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SPECIAL', opcode=92, arg=0, argval=0, argrepr='__enter__', offset=252, start_offset=252, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=0, argval=0, argrepr='', offset=254, start_offset=254, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='STORE_FAST', opcode=109, arg=1, argval='dodgy', argrepr='dodgy', offset=262, start_offset=262, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=264, start_offset=264, starts_line=True, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=2, argval='Never reach this', argrepr="'Never reach this'", offset=274, start_offset=274, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=276, start_offset=276, starts_line=False, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=284, start_offset=284, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=286, start_offset=286, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=288, start_offset=288, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=290, start_offset=290, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=3, argval=3, argrepr='', offset=292, start_offset=292, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=300, start_offset=300, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=302, start_offset=302, starts_line=True, line_number=28, label=10, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=312, start_offset=312, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=314, start_offset=314, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=322, start_offset=322, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=324, start_offset=324, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='RETURN_VALUE', opcode=35, arg=None, argval=None, argrepr='', offset=326, start_offset=326, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=328, start_offset=328, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='WITH_EXCEPT_START', opcode=43, arg=None, argval=None, argrepr='', offset=330, start_offset=330, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='TO_BOOL', opcode=39, arg=None, argval=None, argrepr='', offset=332, start_offset=332, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=2, argval=348, argrepr='to L11', offset=340, start_offset=340, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=344, start_offset=344, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=2, argval=2, argrepr='', offset=346, start_offset=346, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=348, start_offset=348, starts_line=False, line_number=25, label=11, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=350, start_offset=350, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=88, start_offset=88, starts_line=True, line_number=8, label=3, positions=None, cache_info=None), + Instruction(opname='JUMP_FORWARD', opcode=76, arg=13, argval=118, argrepr='to L5', offset=90, start_offset=90, starts_line=False, line_number=8, label=None, positions=None, cache_info=None), + Instruction(opname='END_FOR', opcode=9, arg=None, argval=None, argrepr='', offset=92, start_offset=92, starts_line=True, line_number=3, label=4, positions=None, cache_info=None), + Instruction(opname='POP_ITER', opcode=30, arg=None, argval=None, argrepr='', offset=94, start_offset=94, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=96, start_offset=96, starts_line=True, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=0, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=106, start_offset=106, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=108, start_offset=108, starts_line=False, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=116, start_offset=116, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST_CHECK', opcode=85, arg=0, argval='i', argrepr='i', offset=118, start_offset=118, starts_line=True, line_number=11, label=5, positions=None, cache_info=None), + Instruction(opname='TO_BOOL', opcode=39, arg=None, argval=None, argrepr='', offset=120, start_offset=120, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=36, argval=204, argrepr='to L8', offset=128, start_offset=128, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=132, start_offset=132, starts_line=False, line_number=11, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=134, start_offset=134, starts_line=True, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=144, start_offset=144, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=146, start_offset=146, starts_line=False, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=154, start_offset=154, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=156, start_offset=156, starts_line=True, line_number=13, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=1, argval=1, argrepr='', offset=158, start_offset=158, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), + Instruction(opname='BINARY_OP', opcode=44, arg=23, argval=23, argrepr='-=', offset=160, start_offset=160, starts_line=False, line_number=13, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='STORE_FAST', opcode=109, arg=0, argval='i', argrepr='i', offset=164, start_offset=164, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=166, start_offset=166, starts_line=True, line_number=14, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=6, argval=6, argrepr='', offset=168, start_offset=168, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=56, arg=148, argval='>', argrepr='bool(>)', offset=170, start_offset=170, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=3, argval=184, argrepr='to L6', offset=174, start_offset=174, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=178, start_offset=178, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=74, arg=33, argval=118, argrepr='to L5', offset=180, start_offset=180, starts_line=True, line_number=15, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=184, start_offset=184, starts_line=True, line_number=16, label=6, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=4, argval=4, argrepr='', offset=186, start_offset=186, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=56, arg=18, argval='<', argrepr='bool(<)', offset=188, start_offset=188, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=3, argval=202, argrepr='to L7', offset=192, start_offset=192, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=196, start_offset=196, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=74, arg=42, argval=118, argrepr='to L5', offset=198, start_offset=198, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='JUMP_FORWARD', opcode=76, arg=11, argval=226, argrepr='to L9', offset=202, start_offset=202, starts_line=True, line_number=17, label=7, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=204, start_offset=204, starts_line=True, line_number=19, label=8, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=1, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=214, start_offset=214, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=216, start_offset=216, starts_line=False, line_number=19, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=224, start_offset=224, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), + Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=226, start_offset=226, starts_line=True, line_number=20, label=9, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=1, argval=1, argrepr='', offset=228, start_offset=228, starts_line=True, line_number=21, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=0, argval=0, argrepr='', offset=230, start_offset=230, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), + Instruction(opname='BINARY_OP', opcode=44, arg=11, argval=11, argrepr='/', offset=232, start_offset=232, starts_line=False, line_number=21, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=236, start_offset=236, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=238, start_offset=238, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=59, arg=1, argval=1, argrepr='', offset=240, start_offset=240, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SPECIAL', opcode=92, arg=1, argval=1, argrepr='__exit__', offset=242, start_offset=242, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='SWAP', opcode=114, arg=2, argval=2, argrepr='', offset=244, start_offset=244, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='SWAP', opcode=114, arg=3, argval=3, argrepr='', offset=246, start_offset=246, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SPECIAL', opcode=92, arg=0, argval=0, argrepr='__enter__', offset=248, start_offset=248, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=0, argval=0, argrepr='', offset=250, start_offset=250, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='STORE_FAST', opcode=109, arg=1, argval='dodgy', argrepr='dodgy', offset=258, start_offset=258, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=260, start_offset=260, starts_line=True, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=2, argval='Never reach this', argrepr="'Never reach this'", offset=270, start_offset=270, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=272, start_offset=272, starts_line=False, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=280, start_offset=280, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=282, start_offset=282, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=284, start_offset=284, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=286, start_offset=286, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=3, argval=3, argrepr='', offset=288, start_offset=288, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=296, start_offset=296, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=298, start_offset=298, starts_line=True, line_number=28, label=10, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=308, start_offset=308, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=310, start_offset=310, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=318, start_offset=318, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=320, start_offset=320, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='RETURN_VALUE', opcode=35, arg=None, argval=None, argrepr='', offset=322, start_offset=322, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=324, start_offset=324, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='WITH_EXCEPT_START', opcode=43, arg=None, argval=None, argrepr='', offset=326, start_offset=326, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='TO_BOOL', opcode=39, arg=None, argval=None, argrepr='', offset=328, start_offset=328, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=2, argval=344, argrepr='to L11', offset=336, start_offset=336, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=340, start_offset=340, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=2, argval=2, argrepr='', offset=342, start_offset=342, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=344, start_offset=344, starts_line=False, line_number=25, label=11, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=346, start_offset=346, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=348, start_offset=348, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=350, start_offset=350, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=352, start_offset=352, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=354, start_offset=354, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=356, start_offset=356, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=75, arg=29, argval=302, argrepr='to L10', offset=358, start_offset=358, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=360, start_offset=360, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=364, start_offset=364, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=366, start_offset=366, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=368, start_offset=368, starts_line=True, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='CHECK_EXC_MATCH', opcode=5, arg=None, argval=None, argrepr='', offset=378, start_offset=378, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=15, argval=414, argrepr='to L12', offset=380, start_offset=380, starts_line=False, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=384, start_offset=384, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=386, start_offset=386, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=388, start_offset=388, starts_line=True, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=4, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=398, start_offset=398, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=400, start_offset=400, starts_line=False, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=408, start_offset=408, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=410, start_offset=410, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=75, arg=56, argval=302, argrepr='to L10', offset=412, start_offset=412, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=414, start_offset=414, starts_line=True, line_number=22, label=12, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=416, start_offset=416, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=418, start_offset=418, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=420, start_offset=420, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=422, start_offset=422, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=424, start_offset=424, starts_line=True, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=434, start_offset=434, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=436, start_offset=436, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=444, start_offset=444, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=446, start_offset=446, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=448, start_offset=448, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=450, start_offset=450, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=452, start_offset=452, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=75, arg=29, argval=298, argrepr='to L10', offset=354, start_offset=354, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=356, start_offset=356, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=358, start_offset=358, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=360, start_offset=360, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=364, start_offset=364, starts_line=True, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='CHECK_EXC_MATCH', opcode=5, arg=None, argval=None, argrepr='', offset=374, start_offset=374, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=15, argval=410, argrepr='to L12', offset=376, start_offset=376, starts_line=False, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=380, start_offset=380, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=382, start_offset=382, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=384, start_offset=384, starts_line=True, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=4, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=394, start_offset=394, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=396, start_offset=396, starts_line=False, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=404, start_offset=404, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=406, start_offset=406, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=75, arg=56, argval=298, argrepr='to L10', offset=408, start_offset=408, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=410, start_offset=410, starts_line=True, line_number=22, label=12, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=412, start_offset=412, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=414, start_offset=414, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=416, start_offset=416, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=418, start_offset=418, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=420, start_offset=420, starts_line=True, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=430, start_offset=430, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=432, start_offset=432, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=440, start_offset=440, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=442, start_offset=442, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=444, start_offset=444, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=446, start_offset=446, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=448, start_offset=448, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), ] # One last piece of inspect fodder to check the default line number handling diff --git a/Python/codegen.c b/Python/codegen.c index 14f9f5ad1254f5..61707ba677097c 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -406,13 +406,7 @@ codegen_addop_j(instr_sequence *seq, location loc, assert(IS_JUMP_TARGET_LABEL(target)); assert(OPCODE_HAS_JUMP(opcode) || IS_BLOCK_PUSH_OPCODE(opcode)); assert(!IS_ASSEMBLER_OPCODE(opcode)); - if (_PyInstructionSequence_Addop(seq, opcode, target.id, loc) != SUCCESS) { - return ERROR; - } - if (IS_CONDITIONAL_JUMP_OPCODE(opcode)) { - return _PyInstructionSequence_Addop(seq, NOT_TAKEN, 0, NO_LOCATION); - } - return SUCCESS; + return _PyInstructionSequence_Addop(seq, opcode, target.id, loc); } #define ADDOP_JUMP(C, LOC, OP, O) \ diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 64df6290de06ba..017216aadd1f01 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -522,14 +522,15 @@ no_redundant_jumps(cfg_builder *g) { static int normalize_jumps_in_block(cfg_builder *g, basicblock *b) { cfg_instr *last = basicblock_last_instr(b); - if (last == NULL || !is_jump(last) || - IS_UNCONDITIONAL_JUMP_OPCODE(last->i_opcode)) { + if (last == NULL || !IS_CONDITIONAL_JUMP_OPCODE(last->i_opcode)) { return SUCCESS; } assert(!IS_ASSEMBLER_OPCODE(last->i_opcode)); bool is_forward = last->i_target->b_visited == 0; if (is_forward) { + RETURN_IF_ERROR( + basicblock_addop(b, NOT_TAKEN, 0, last->i_loc)); return SUCCESS; } @@ -557,10 +558,6 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) { if (backwards_jump == NULL) { return ERROR; } - assert(b->b_next->b_iused > 0); - assert(b->b_next->b_instr[0].i_opcode == NOT_TAKEN); - b->b_next->b_instr[0].i_opcode = NOP; - b->b_next->b_instr[0].i_loc = NO_LOCATION; RETURN_IF_ERROR( basicblock_addop(backwards_jump, NOT_TAKEN, 0, last->i_loc)); RETURN_IF_ERROR( From 616692d465c4ef10baa592e41f3d9daeae4dddad Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 6 Jan 2025 23:25:29 +0100 Subject: [PATCH 209/376] gh-127146: Skip Emscripten tests with pending fixes (#128549) Marks some additional tests as skipped on Emscripten. Many of these skips can be reverted when the next Emscripten release is available. --- Lib/test/test_io.py | 1 + Lib/test/test_os.py | 1 + Lib/test/test_shutil.py | 1 + Lib/test/test_tarfile.py | 1 + Lib/test/test_zipfile/test_core.py | 4 +++- Lib/test/test_zipimport.py | 1 + 6 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 81c17b2731cc58..33e0161241e87e 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -3933,6 +3933,7 @@ def test_issue35928(self): self.assertEqual(res + f.readline(), 'foo\nbar\n') @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + @unittest.skipIf(support.is_emscripten, "Would be fixed by emscripten-core/emscripten#23306") def test_read_non_blocking(self): import os r, w = os.pipe() diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index d688a225538c11..d2c4dff3c9a0e5 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -4979,6 +4979,7 @@ def test_unpickable(self): self.assertRaises(TypeError, pickle.dumps, scandir_iter, filename) scandir_iter.close() + @unittest.skipIf(support.is_emscripten, "Fixed by emscripten-core/emscripten#23139, remove when next Emscripten release comes out") def check_entry(self, entry, name, is_dir, is_file, is_symlink): self.assertIsInstance(entry, os.DirEntry) self.assertEqual(entry.name, name) diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 1f18b1f09b5858..2d79d2ffede461 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1587,6 +1587,7 @@ def test_copyfile_same_file(self): # the path as a directory, but on AIX the trailing slash has no effect # and is considered as a file. @unittest.skipIf(AIX, 'Not valid on AIX, see gh-92670') + @unittest.skipIf(support.is_emscripten, 'Fixed by emscripten-core/emscripten#23218, remove when next Emscripten release comes out') def test_copyfile_nonexistent_dir(self): # Issue 43219 src_dir = self.mkdtemp() diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 54d329a15d4d25..2549b6b35adc29 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -3800,6 +3800,7 @@ def test_absolute_hardlink(self): "'parent' is a link to an absolute path") @symlink_test + @unittest.skipIf(support.is_emscripten, "Fixed by emscripten-core/emscripten#23136, remove when next Emscripten release comes out") def test_sly_relative0(self): # Inspired by 'relative0' in jwilk/traversal-archives with ArchiveMaker() as arc: diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py index 49f39b9337df85..79e7337606b4bc 100644 --- a/Lib/test/test_zipfile/test_core.py +++ b/Lib/test/test_zipfile/test_core.py @@ -22,7 +22,8 @@ from test.support import script_helper from test.support import ( findfile, requires_zlib, requires_bz2, requires_lzma, - captured_stdout, captured_stderr, requires_subprocess + captured_stdout, captured_stderr, requires_subprocess, + is_emscripten ) from test.support.os_helper import ( TESTFN, unlink, rmtree, temp_dir, temp_cwd, fd_count, FakePath @@ -622,6 +623,7 @@ def test_write_to_readonly(self): with self.assertRaises(ValueError): zipfp.open(TESTFN, mode='w') + @unittest.skipIf(is_emscripten, "Fixed by emscripten-core/emscripten#23310") def test_add_file_before_1980(self): # Set atime and mtime to 1970-01-01 os.utime(TESTFN, (0, 0)) diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py index 1f288c8b45d589..65f8b17f2f88c0 100644 --- a/Lib/test/test_zipimport.py +++ b/Lib/test/test_zipimport.py @@ -1038,6 +1038,7 @@ def testEmptyFile(self): self.assertZipFailure(TESTMOD) @unittest.skipIf(support.is_wasi, "mode 000 not supported.") + @unittest.skipIf(support.is_emscripten, "Fixed by emscripten-core/emscripten#23137, remove when next Emscripten release comes out") def testFileUnreadable(self): os_helper.unlink(TESTMOD) fd = os.open(TESTMOD, os.O_CREAT, 000) From 61c1a2478e6da8dc6dbdce4d6ac66b03d5963710 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 6 Jan 2025 23:26:35 +0100 Subject: [PATCH 210/376] gh-127146: Strip dash from Emscripten compiler version (#128557) `emcc -dumpversion` will sometimes say e.g., `4.0.0-git` but in this case uname does not include `-git` in the version string. Use cut to delete everything after the dash. --- configure | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 61ee51c4b36473..9024d22d4df5cd 100755 --- a/configure +++ b/configure @@ -4595,7 +4595,7 @@ printf "%s\n" "$IPHONEOS_DEPLOYMENT_TARGET" >&6; } _host_ident=$host_cpu ;; *-*-emscripten) - _host_ident=$(emcc -dumpversion)-$host_cpu + _host_ident=$(emcc -dumpversion | cut -f1 -d-)-$host_cpu ;; wasm32-*-* | wasm64-*-*) _host_ident=$host_cpu diff --git a/configure.ac b/configure.ac index 172e8a1a842010..86775e03e73d5a 100644 --- a/configure.ac +++ b/configure.ac @@ -794,7 +794,7 @@ if test "$cross_compiling" = yes; then _host_ident=$host_cpu ;; *-*-emscripten) - _host_ident=$(emcc -dumpversion)-$host_cpu + _host_ident=$(emcc -dumpversion | cut -f1 -d-)-$host_cpu ;; wasm32-*-* | wasm64-*-*) _host_ident=$host_cpu From 7363476b6405e3d288a61282aa7bc6aca9c2114d Mon Sep 17 00:00:00 2001 From: Michael H Date: Mon, 6 Jan 2025 18:03:27 -0500 Subject: [PATCH 211/376] gh-128559: Remove typing import from asyncio.timeouts (#128560) --- Lib/asyncio/timeouts.py | 24 +++++++++---------- ...-01-06-21-35-00.gh-issue-128559.6fxcDM.rst | 1 + 2 files changed, 12 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-06-21-35-00.gh-issue-128559.6fxcDM.rst diff --git a/Lib/asyncio/timeouts.py b/Lib/asyncio/timeouts.py index e6f5100691d362..09342dc7c1310b 100644 --- a/Lib/asyncio/timeouts.py +++ b/Lib/asyncio/timeouts.py @@ -1,7 +1,6 @@ import enum from types import TracebackType -from typing import final, Optional, Type from . import events from . import exceptions @@ -23,14 +22,13 @@ class _State(enum.Enum): EXITED = "finished" -@final class Timeout: """Asynchronous context manager for cancelling overdue coroutines. Use `timeout()` or `timeout_at()` rather than instantiating this class directly. """ - def __init__(self, when: Optional[float]) -> None: + def __init__(self, when: float | None) -> None: """Schedule a timeout that will trigger at a given loop time. - If `when` is `None`, the timeout will never trigger. @@ -39,15 +37,15 @@ def __init__(self, when: Optional[float]) -> None: """ self._state = _State.CREATED - self._timeout_handler: Optional[events.TimerHandle] = None - self._task: Optional[tasks.Task] = None + self._timeout_handler: events.TimerHandle | None = None + self._task: tasks.Task | None = None self._when = when - def when(self) -> Optional[float]: + def when(self) -> float | None: """Return the current deadline.""" return self._when - def reschedule(self, when: Optional[float]) -> None: + def reschedule(self, when: float | None) -> None: """Reschedule the timeout.""" if self._state is not _State.ENTERED: if self._state is _State.CREATED: @@ -96,10 +94,10 @@ async def __aenter__(self) -> "Timeout": async def __aexit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], - ) -> Optional[bool]: + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: assert self._state in (_State.ENTERED, _State.EXPIRING) if self._timeout_handler is not None: @@ -142,7 +140,7 @@ def _insert_timeout_error(exc_val: BaseException) -> None: exc_val = exc_val.__context__ -def timeout(delay: Optional[float]) -> Timeout: +def timeout(delay: float | None) -> Timeout: """Timeout async context manager. Useful in cases when you want to apply timeout logic around block @@ -162,7 +160,7 @@ def timeout(delay: Optional[float]) -> Timeout: return Timeout(loop.time() + delay if delay is not None else None) -def timeout_at(when: Optional[float]) -> Timeout: +def timeout_at(when: float | None) -> Timeout: """Schedule the timeout at absolute time. Like timeout() but argument gives absolute time in the same clock system diff --git a/Misc/NEWS.d/next/Library/2025-01-06-21-35-00.gh-issue-128559.6fxcDM.rst b/Misc/NEWS.d/next/Library/2025-01-06-21-35-00.gh-issue-128559.6fxcDM.rst new file mode 100644 index 00000000000000..7f9380de17761b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-06-21-35-00.gh-issue-128559.6fxcDM.rst @@ -0,0 +1 @@ +Improved import time of :mod:`asyncio`. From 953b49e5468d02afaddadc2307f4763422078603 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Mon, 6 Jan 2025 23:38:45 +0000 Subject: [PATCH 212/376] gh-108202: Document `calendar.Calendar` methods `getfirstweekday` and `setfirstweekday` (#127579) --- Doc/library/calendar.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index ace8529d6e7e0c..dadd301ebc795b 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -40,6 +40,14 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is :class:`Calendar` instances have the following methods: + .. method:: getfirstweekday() + + Return an :class:`int` for the current first weekday (0-6). + + .. method:: setfirstweekday(firstweekday) + + Set the first weekday to *firstweekday*, passed as an :class:`int` where Monday is 0 and Sunday is 6. + .. method:: iterweekdays() Return an iterator for the week day numbers that will be used for one From 24b147a19b360c49cb1740aa46211d342aaa071f Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Mon, 6 Jan 2025 21:02:55 -0600 Subject: [PATCH 213/376] gh-128472: Add `-skip-funcs` to BOLT options to fix computed goto errors (gh-128511) * Add `-skip-funcs` to BOLT options to fix computed goto errors Co-authored-by: Gregory Szorc * NEWS --------- Co-authored-by: Gregory Szorc --- .../Build/2025-01-04-22-39-10.gh-issue-128472.Wt5E6M.rst | 2 ++ configure | 2 +- configure.ac | 9 ++++++++- 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2025-01-04-22-39-10.gh-issue-128472.Wt5E6M.rst diff --git a/Misc/NEWS.d/next/Build/2025-01-04-22-39-10.gh-issue-128472.Wt5E6M.rst b/Misc/NEWS.d/next/Build/2025-01-04-22-39-10.gh-issue-128472.Wt5E6M.rst new file mode 100644 index 00000000000000..c6233e1f2d8693 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-01-04-22-39-10.gh-issue-128472.Wt5E6M.rst @@ -0,0 +1,2 @@ +Skip BOLT optimization of functions using computed gotos, fixing errors on +build with LLVM 19. diff --git a/configure b/configure index 9024d22d4df5cd..08c10ff52ce8d5 100755 --- a/configure +++ b/configure @@ -9398,7 +9398,7 @@ fi printf %s "checking BOLT_COMMON_FLAGS... " >&6; } if test -z "${BOLT_COMMON_FLAGS}" then - BOLT_COMMON_FLAGS=-update-debug-sections + BOLT_COMMON_FLAGS=" -update-debug-sections -skip-funcs=_PyEval_EvalFrameDefault,sre_ucs1_match/1,sre_ucs2_match/1,sre_ucs4_match/1 " fi diff --git a/configure.ac b/configure.ac index 86775e03e73d5a..fb1dd77be167b9 100644 --- a/configure.ac +++ b/configure.ac @@ -2170,7 +2170,14 @@ if test -z "${BOLT_COMMON_FLAGS}" then AS_VAR_SET( [BOLT_COMMON_FLAGS], - [-update-debug-sections] + [m4_normalize(" + [-update-debug-sections] + + dnl At least LLVM 19.x doesn't support computed gotos in PIC compiled code. + dnl Exclude functions containing computed gotos. + dnl TODO this may be fixed in LLVM 20.x via https://github.com/llvm/llvm-project/pull/120267. + [-skip-funcs=_PyEval_EvalFrameDefault,sre_ucs1_match/1,sre_ucs2_match/1,sre_ucs4_match/1] + ")] ) fi From e837a1f71e832ce8f551a6fac05e346f654457e0 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 7 Jan 2025 13:12:58 +0800 Subject: [PATCH 214/376] gh-128146: Exclude os/log.h import on older macOS versions. (#128165) Reworks the handling of Apple system log handling to account for older macOS versions that don't provide os-log. --- ...-12-22-08-54-30.gh-issue-127592.iyuFCC.rst | 2 ++ Python/pylifecycle.c | 33 ++++++++++++------- 2 files changed, 24 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/macOS/2024-12-22-08-54-30.gh-issue-127592.iyuFCC.rst diff --git a/Misc/NEWS.d/next/macOS/2024-12-22-08-54-30.gh-issue-127592.iyuFCC.rst b/Misc/NEWS.d/next/macOS/2024-12-22-08-54-30.gh-issue-127592.iyuFCC.rst new file mode 100644 index 00000000000000..dfe659294c712e --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2024-12-22-08-54-30.gh-issue-127592.iyuFCC.rst @@ -0,0 +1,2 @@ +Usage of the unified Apple System Log APIs was disabled when the minimum +macOS version is earlier than 10.12. diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 06418123d6dd9b..8a15a09a01dbf7 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -46,8 +46,25 @@ #if defined(__APPLE__) # include +# include # include -# include +// The os_log unified logging APIs were introduced in macOS 10.12, iOS 10.0, +// tvOS 10.0, and watchOS 3.0; +# if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +# define HAS_APPLE_SYSTEM_LOG 1 +# elif defined(TARGET_OS_OSX) && TARGET_OS_OSX +# if defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12 +# define HAS_APPLE_SYSTEM_LOG 1 +# else +# define HAS_APPLE_SYSTEM_LOG 0 +# endif +# else +# define HAS_APPLE_SYSTEM_LOG 0 +# endif + +# if HAS_APPLE_SYSTEM_LOG +# include +# endif #endif #ifdef HAVE_SIGNAL_H @@ -77,7 +94,7 @@ static PyStatus init_sys_streams(PyThreadState *tstate); #ifdef __ANDROID__ static PyStatus init_android_streams(PyThreadState *tstate); #endif -#if defined(__APPLE__) +#if defined(__APPLE__) && HAS_APPLE_SYSTEM_LOG static PyStatus init_apple_streams(PyThreadState *tstate); #endif static void wait_for_thread_shutdown(PyThreadState *tstate); @@ -1262,7 +1279,7 @@ init_interp_main(PyThreadState *tstate) return status; } #endif -#if defined(__APPLE__) +#if defined(__APPLE__) && HAS_APPLE_SYSTEM_LOG if (config->use_system_logger) { status = init_apple_streams(tstate); if (_PyStatus_EXCEPTION(status)) { @@ -2946,7 +2963,7 @@ init_android_streams(PyThreadState *tstate) #endif // __ANDROID__ -#if defined(__APPLE__) +#if defined(__APPLE__) && HAS_APPLE_SYSTEM_LOG static PyObject * apple_log_write_impl(PyObject *self, PyObject *args) @@ -2957,14 +2974,9 @@ apple_log_write_impl(PyObject *self, PyObject *args) return NULL; } - // Call the underlying Apple logging API. The os_log unified logging APIs - // were introduced in macOS 10.12, iOS 10.0, tvOS 10.0, and watchOS 3.0; - // this call is a no-op on older versions. - #if TARGET_OS_IPHONE || (TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) // Pass the user-provided text through explicit %s formatting // to avoid % literals being interpreted as a formatting directive. os_log_with_type(OS_LOG_DEFAULT, logtype, "%s", text); - #endif Py_RETURN_NONE; } @@ -2999,7 +3011,6 @@ init_apple_streams(PyThreadState *tstate) if (result == NULL) { goto error; } - goto done; error: @@ -3013,7 +3024,7 @@ init_apple_streams(PyThreadState *tstate) return status; } -#endif // __APPLE__ +#endif // __APPLE__ && HAS_APPLE_SYSTEM_LOG static void From b3cbd8f1b58b9e71df20bdda9960e7d3190c509e Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 7 Jan 2025 08:59:24 +0100 Subject: [PATCH 215/376] gh-108202: Document calendar.Calendar.firstweekday (#128566) --- Doc/library/calendar.rst | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index dadd301ebc795b..f25930eb0d873e 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -38,21 +38,33 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is itself. This is the job of subclasses. - :class:`Calendar` instances have the following methods: + :class:`Calendar` instances have the following methods and attributes: + + .. attribute:: firstweekday + + The first weekday as an integer (0--6). + + This property can also be set and read using + :meth:`~Calendar.setfirstweekday` and + :meth:`~Calendar.getfirstweekday` respectively. .. method:: getfirstweekday() - Return an :class:`int` for the current first weekday (0-6). + Return an :class:`int` for the current first weekday (0--6). + + Identical to reading the :attr:`~Calendar.firstweekday` property. .. method:: setfirstweekday(firstweekday) - Set the first weekday to *firstweekday*, passed as an :class:`int` where Monday is 0 and Sunday is 6. + Set the first weekday to *firstweekday*, passed as an :class:`int` (0--6) + + Identical to setting the :attr:`~Calendar.firstweekday` property. .. method:: iterweekdays() Return an iterator for the week day numbers that will be used for one week. The first value from the iterator will be the same as the value of - the :attr:`firstweekday` property. + the :attr:`~Calendar.firstweekday` property. .. method:: itermonthdates(year, month) From 145276a072ae058bac42ee43a4235cd4eda2726b Mon Sep 17 00:00:00 2001 From: Jun Komoda <45822440+junkmd@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:36:27 +0900 Subject: [PATCH 216/376] Add `.. availability:: Windows` directive to COM-related function prototype (GH-127436) Add a directive to prototype that "returns a foreign function that will call a COM method" --- Doc/library/ctypes.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 398cb92bac809a..f25bf94417c198 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1812,6 +1812,8 @@ different ways, depending on the type and number of the parameters in the call: the COM interface as first argument, in addition to those parameters that are specified in the :attr:`!argtypes` tuple. + .. availability:: Windows + The optional *paramflags* parameter creates foreign function wrappers with much more functionality than the features described above. From 6ea04da27036eaa69d65150148bb8c537d9beacf Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Tue, 7 Jan 2025 02:40:41 -0800 Subject: [PATCH 217/376] gh-128302: Fix bugs in xml.dom.xmlbuilder (GH-128284) * Allow DOMParser.parse() to correctly handle DOMInputSource instances that only have a systemId attribute set. * Fix DOMEntityResolver.resolveEntity(), which was broken by the Python 3.0 transition. * Add Lib/test/test_xml_dom_xmlbuilder.py with few tests. --- Lib/test/test_xml_dom_xmlbuilder.py | 88 +++++++++++++++++++ Lib/xml/dom/xmlbuilder.py | 12 +-- ...-12-27-16-28-57.gh-issue-128302.2GMvyl.rst | 3 + ...-12-29-13-49-46.gh-issue-128302.psRpPN.rst | 2 + 4 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 Lib/test/test_xml_dom_xmlbuilder.py create mode 100644 Misc/NEWS.d/next/Library/2024-12-27-16-28-57.gh-issue-128302.2GMvyl.rst create mode 100644 Misc/NEWS.d/next/Library/2024-12-29-13-49-46.gh-issue-128302.psRpPN.rst diff --git a/Lib/test/test_xml_dom_xmlbuilder.py b/Lib/test/test_xml_dom_xmlbuilder.py new file mode 100644 index 00000000000000..5f5f2eb328df9f --- /dev/null +++ b/Lib/test/test_xml_dom_xmlbuilder.py @@ -0,0 +1,88 @@ +import io +import unittest +from http import client +from test.test_httplib import FakeSocket +from unittest import mock +from xml.dom import getDOMImplementation, minidom, xmlbuilder + +SMALL_SAMPLE = b""" + + +Introduction to XSL +
+

A. Namespace

+""" + + +class XMLBuilderTest(unittest.TestCase): + def test_entity_resolver(self): + body = ( + b"HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset=utf-8\r\n\r\n" + + SMALL_SAMPLE + ) + + sock = FakeSocket(body) + response = client.HTTPResponse(sock) + response.begin() + attrs = {"open.return_value": response} + opener = mock.Mock(**attrs) + + resolver = xmlbuilder.DOMEntityResolver() + + with mock.patch("urllib.request.build_opener") as mock_build: + mock_build.return_value = opener + source = resolver.resolveEntity(None, "http://example.com/2000/svg") + + self.assertIsInstance(source, xmlbuilder.DOMInputSource) + self.assertIsNone(source.publicId) + self.assertEqual(source.systemId, "http://example.com/2000/svg") + self.assertEqual(source.baseURI, "http://example.com/2000/") + self.assertEqual(source.encoding, "utf-8") + self.assertIs(source.byteStream, response) + + self.assertIsNone(source.characterStream) + self.assertIsNone(source.stringData) + + def test_builder(self): + imp = getDOMImplementation() + self.assertIsInstance(imp, xmlbuilder.DOMImplementationLS) + + builder = imp.createDOMBuilder(imp.MODE_SYNCHRONOUS, None) + self.assertIsInstance(builder, xmlbuilder.DOMBuilder) + + def test_parse_uri(self): + body = ( + b"HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset=utf-8\r\n\r\n" + + SMALL_SAMPLE + ) + + sock = FakeSocket(body) + response = client.HTTPResponse(sock) + response.begin() + attrs = {"open.return_value": response} + opener = mock.Mock(**attrs) + + with mock.patch("urllib.request.build_opener") as mock_build: + mock_build.return_value = opener + + imp = getDOMImplementation() + builder = imp.createDOMBuilder(imp.MODE_SYNCHRONOUS, None) + document = builder.parseURI("http://example.com/2000/svg") + + self.assertIsInstance(document, minidom.Document) + self.assertEqual(len(document.childNodes), 1) + + def test_parse_with_systemId(self): + response = io.BytesIO(SMALL_SAMPLE) + + with mock.patch("urllib.request.urlopen") as mock_open: + mock_open.return_value = response + + imp = getDOMImplementation() + source = imp.createDOMInputSource() + builder = imp.createDOMBuilder(imp.MODE_SYNCHRONOUS, None) + source.systemId = "http://example.com/2000/svg" + document = builder.parse(source) + + self.assertIsInstance(document, minidom.Document) + self.assertEqual(len(document.childNodes), 1) diff --git a/Lib/xml/dom/xmlbuilder.py b/Lib/xml/dom/xmlbuilder.py index 8a200263497b89..a8852625a2f9a2 100644 --- a/Lib/xml/dom/xmlbuilder.py +++ b/Lib/xml/dom/xmlbuilder.py @@ -189,7 +189,7 @@ def parse(self, input): options.filter = self.filter options.errorHandler = self.errorHandler fp = input.byteStream - if fp is None and options.systemId: + if fp is None and input.systemId: import urllib.request fp = urllib.request.urlopen(input.systemId) return self._parse_bytestream(fp, options) @@ -247,10 +247,12 @@ def _create_opener(self): def _guess_media_encoding(self, source): info = source.byteStream.info() - if "Content-Type" in info: - for param in info.getplist(): - if param.startswith("charset="): - return param.split("=", 1)[1].lower() + # import email.message + # assert isinstance(info, email.message.Message) + charset = info.get_param('charset') + if charset is not None: + return charset.lower() + return None class DOMInputSource(object): diff --git a/Misc/NEWS.d/next/Library/2024-12-27-16-28-57.gh-issue-128302.2GMvyl.rst b/Misc/NEWS.d/next/Library/2024-12-27-16-28-57.gh-issue-128302.2GMvyl.rst new file mode 100644 index 00000000000000..56e2fe6f85f4bf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-27-16-28-57.gh-issue-128302.2GMvyl.rst @@ -0,0 +1,3 @@ +Allow :meth:`!xml.dom.xmlbuilder.DOMParser.parse` to correctly handle +:class:`!xml.dom.xmlbuilder.DOMInputSource` instances that only have a +:attr:`!systemId` attribute set. diff --git a/Misc/NEWS.d/next/Library/2024-12-29-13-49-46.gh-issue-128302.psRpPN.rst b/Misc/NEWS.d/next/Library/2024-12-29-13-49-46.gh-issue-128302.psRpPN.rst new file mode 100644 index 00000000000000..98c07297b06f8a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-29-13-49-46.gh-issue-128302.psRpPN.rst @@ -0,0 +1,2 @@ +Fix :meth:`!xml.dom.xmlbuilder.DOMEntityResolver.resolveEntity`, which was +broken by the Python 3.0 transition. From 61b9811ac6843e22b5896ef96030d421b79cd892 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Tue, 7 Jan 2025 11:44:57 +0000 Subject: [PATCH 218/376] gh-128552: fix refcycles in eager task creation (#128553) --- Lib/asyncio/base_events.py | 7 ++- Lib/asyncio/taskgroups.py | 7 ++- Lib/test/test_asyncio/test_taskgroups.py | 62 +++++++++++++++++-- ...-01-06-18-41-08.gh-issue-128552.fV-f8j.rst | 1 + 4 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-06-18-41-08.gh-issue-128552.fV-f8j.rst diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 9e6f6e3ee7e3ec..6e6e5aaac15caf 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -477,7 +477,12 @@ def create_task(self, coro, *, name=None, context=None): task.set_name(name) - return task + try: + return task + finally: + # gh-128552: prevent a refcycle of + # task.exception().__traceback__->BaseEventLoop.create_task->task + del task def set_task_factory(self, factory): """Set a task factory that will be used by loop.create_task(). diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py index 9fa772ca9d02cc..8af199d6dcc41a 100644 --- a/Lib/asyncio/taskgroups.py +++ b/Lib/asyncio/taskgroups.py @@ -205,7 +205,12 @@ def create_task(self, coro, *, name=None, context=None): else: self._tasks.add(task) task.add_done_callback(self._on_task_done) - return task + try: + return task + finally: + # gh-128552: prevent a refcycle of + # task.exception().__traceback__->TaskGroup.create_task->task + del task # Since Python 3.8 Tasks propagate all exceptions correctly, # except for KeyboardInterrupt and SystemExit which are diff --git a/Lib/test/test_asyncio/test_taskgroups.py b/Lib/test/test_asyncio/test_taskgroups.py index c47bf4ec9ed64b..870fa8dbbf2714 100644 --- a/Lib/test/test_asyncio/test_taskgroups.py +++ b/Lib/test/test_asyncio/test_taskgroups.py @@ -1,6 +1,7 @@ # Adapted with permission from the EdgeDB project; # license: PSFL. +import weakref import sys import gc import asyncio @@ -38,7 +39,25 @@ def no_other_refs(): return [coro] -class TestTaskGroup(unittest.IsolatedAsyncioTestCase): +def set_gc_state(enabled): + was_enabled = gc.isenabled() + if enabled: + gc.enable() + else: + gc.disable() + return was_enabled + + +@contextlib.contextmanager +def disable_gc(): + was_enabled = set_gc_state(enabled=False) + try: + yield + finally: + set_gc_state(enabled=was_enabled) + + +class BaseTestTaskGroup: async def test_taskgroup_01(self): @@ -832,15 +851,15 @@ async def test_taskgroup_without_parent_task(self): with self.assertRaisesRegex(RuntimeError, "has not been entered"): tg.create_task(coro) - def test_coro_closed_when_tg_closed(self): + async def test_coro_closed_when_tg_closed(self): async def run_coro_after_tg_closes(): async with taskgroups.TaskGroup() as tg: pass coro = asyncio.sleep(0) with self.assertRaisesRegex(RuntimeError, "is finished"): tg.create_task(coro) - loop = asyncio.get_event_loop() - loop.run_until_complete(run_coro_after_tg_closes()) + + await run_coro_after_tg_closes() async def test_cancelling_level_preserved(self): async def raise_after(t, e): @@ -965,6 +984,30 @@ async def coro_fn(): self.assertIsInstance(exc, _Done) self.assertListEqual(gc.get_referrers(exc), no_other_refs()) + + async def test_exception_refcycles_parent_task_wr(self): + """Test that TaskGroup deletes self._parent_task and create_task() deletes task""" + tg = asyncio.TaskGroup() + exc = None + + class _Done(Exception): + pass + + async def coro_fn(): + async with tg: + raise _Done + + with disable_gc(): + try: + async with asyncio.TaskGroup() as tg2: + task_wr = weakref.ref(tg2.create_task(coro_fn())) + except* _Done as excs: + exc = excs.exceptions[0].exceptions[0] + + self.assertIsNone(task_wr()) + self.assertIsInstance(exc, _Done) + self.assertListEqual(gc.get_referrers(exc), no_other_refs()) + async def test_exception_refcycles_propagate_cancellation_error(self): """Test that TaskGroup deletes propagate_cancellation_error""" tg = asyncio.TaskGroup() @@ -998,5 +1041,16 @@ class MyKeyboardInterrupt(KeyboardInterrupt): self.assertListEqual(gc.get_referrers(exc), no_other_refs()) +class TestTaskGroup(BaseTestTaskGroup, unittest.IsolatedAsyncioTestCase): + loop_factory = asyncio.EventLoop + +class TestEagerTaskTaskGroup(BaseTestTaskGroup, unittest.IsolatedAsyncioTestCase): + @staticmethod + def loop_factory(): + loop = asyncio.EventLoop() + loop.set_task_factory(asyncio.eager_task_factory) + return loop + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2025-01-06-18-41-08.gh-issue-128552.fV-f8j.rst b/Misc/NEWS.d/next/Library/2025-01-06-18-41-08.gh-issue-128552.fV-f8j.rst new file mode 100644 index 00000000000000..83816f775da9c5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-06-18-41-08.gh-issue-128552.fV-f8j.rst @@ -0,0 +1 @@ +Fix cyclic garbage introduced by :meth:`asyncio.loop.create_task` and :meth:`asyncio.TaskGroup.create_task` holding a reference to the created task if it is eager. From bcdf654c8a9d8a661e32e6a89f043197de19ad53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 7 Jan 2025 12:45:29 +0100 Subject: [PATCH 219/376] gh-111178: fix UBSan failures in `Modules/_abc.c` (GH-128253) * fix UBSan failures in `_abc.c` * suppress unused return values --- Modules/_abc.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Modules/_abc.c b/Modules/_abc.c index 4f4b24b035db4a..d6a953b336025d 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -67,6 +67,8 @@ typedef struct { uint64_t _abc_negative_cache_version; } _abc_data; +#define _abc_data_CAST(op) ((_abc_data *)(op)) + static inline uint64_t get_cache_version(_abc_data *impl) { @@ -88,8 +90,9 @@ set_cache_version(_abc_data *impl, uint64_t version) } static int -abc_data_traverse(_abc_data *self, visitproc visit, void *arg) +abc_data_traverse(PyObject *op, visitproc visit, void *arg) { + _abc_data *self = _abc_data_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->_abc_registry); Py_VISIT(self->_abc_cache); @@ -98,8 +101,9 @@ abc_data_traverse(_abc_data *self, visitproc visit, void *arg) } static int -abc_data_clear(_abc_data *self) +abc_data_clear(PyObject *op) { + _abc_data *self = _abc_data_CAST(op); Py_CLEAR(self->_abc_registry); Py_CLEAR(self->_abc_cache); Py_CLEAR(self->_abc_negative_cache); @@ -107,7 +111,7 @@ abc_data_clear(_abc_data *self) } static void -abc_data_dealloc(_abc_data *self) +abc_data_dealloc(PyObject *self) { PyObject_GC_UnTrack(self); PyTypeObject *tp = Py_TYPE(self); @@ -212,7 +216,7 @@ _destroy(PyObject *setweakref, PyObject *objweakref) } static PyMethodDef _destroy_def = { - "_destroy", (PyCFunction) _destroy, METH_O + "_destroy", _destroy, METH_O }; static int @@ -964,7 +968,7 @@ _abcmodule_clear(PyObject *module) static void _abcmodule_free(void *module) { - _abcmodule_clear((PyObject *)module); + (void)_abcmodule_clear((PyObject *)module); } static PyModuleDef_Slot _abcmodule_slots[] = { From a734c1e304ab459fb34a88f6dd2dbd944a1b57c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 7 Jan 2025 12:47:18 +0100 Subject: [PATCH 220/376] gh-111178: fix UBSan failures in `Modules/_sre/sre.c` (GH-128250) fix UBSan failures for `PatternObject`, `MatchObject`, `TemplateObject`, `ScannerObject` --- Modules/_sre/sre.c | 91 ++++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 35 deletions(-) diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c index 36f542ddb4df2b..d0025dd21e045b 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -395,6 +395,11 @@ static struct PyModuleDef sremodule; static PyObject*pattern_new_match(_sremodulestate *, PatternObject*, SRE_STATE*, Py_ssize_t); static PyObject *pattern_scanner(_sremodulestate *, PatternObject *, PyObject *, Py_ssize_t, Py_ssize_t); +#define _PatternObject_CAST(op) ((PatternObject *)(op)) +#define _MatchObject_CAST(op) ((MatchObject *)(op)) +#define _TemplateObject_CAST(op) ((TemplateObject *)(op)) +#define _ScannerObject_CAST(op) ((ScannerObject *)(op)) + /*[clinic input] module _sre class _sre.SRE_Pattern "PatternObject *" "get_sre_module_state_by_class(tp)->Pattern_Type" @@ -699,8 +704,9 @@ pattern_error(Py_ssize_t status) } static int -pattern_traverse(PatternObject *self, visitproc visit, void *arg) +pattern_traverse(PyObject *op, visitproc visit, void *arg) { + PatternObject *self = _PatternObject_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->groupindex); Py_VISIT(self->indexgroup); @@ -712,8 +718,9 @@ pattern_traverse(PatternObject *self, visitproc visit, void *arg) } static int -pattern_clear(PatternObject *self) +pattern_clear(PyObject *op) { + PatternObject *self = _PatternObject_CAST(op); Py_CLEAR(self->groupindex); Py_CLEAR(self->indexgroup); Py_CLEAR(self->pattern); @@ -724,13 +731,13 @@ pattern_clear(PatternObject *self) } static void -pattern_dealloc(PatternObject* self) +pattern_dealloc(PyObject *self) { PyTypeObject *tp = Py_TYPE(self); - PyObject_GC_UnTrack(self); - if (self->weakreflist != NULL) { - PyObject_ClearWeakRefs((PyObject *) self); + PatternObject *obj = _PatternObject_CAST(self); + if (obj->weakreflist != NULL) { + PyObject_ClearWeakRefs(self); } (void)pattern_clear(self); tp->tp_free(self); @@ -1497,7 +1504,7 @@ _sre_SRE_Pattern__fail_after_impl(PatternObject *self, int count, #endif /* Py_DEBUG */ static PyObject * -pattern_repr(PatternObject *obj) +pattern_repr(PyObject *self) { static const struct { const char *name; @@ -1512,6 +1519,8 @@ pattern_repr(PatternObject *obj) {"re.DEBUG", SRE_FLAG_DEBUG}, {"re.ASCII", SRE_FLAG_ASCII}, }; + + PatternObject *obj = _PatternObject_CAST(self); PyObject *result = NULL; PyObject *flag_items; size_t i; @@ -1579,8 +1588,9 @@ PyDoc_STRVAR(pattern_doc, "Compiled regular expression object."); /* PatternObject's 'groupindex' method. */ static PyObject * -pattern_groupindex(PatternObject *self, void *Py_UNUSED(ignored)) +pattern_groupindex(PyObject *op, void *Py_UNUSED(ignored)) { + PatternObject *self = _PatternObject_CAST(op); if (self->groupindex == NULL) return PyDict_New(); return PyDictProxy_New(self->groupindex); @@ -2245,8 +2255,9 @@ _validate(PatternObject *self) /* match methods */ static int -match_traverse(MatchObject *self, visitproc visit, void *arg) +match_traverse(PyObject *op, visitproc visit, void *arg) { + MatchObject *self = _MatchObject_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->string); Py_VISIT(self->regs); @@ -2255,8 +2266,9 @@ match_traverse(MatchObject *self, visitproc visit, void *arg) } static int -match_clear(MatchObject *self) +match_clear(PyObject *op) { + MatchObject *self = _MatchObject_CAST(op); Py_CLEAR(self->string); Py_CLEAR(self->regs); Py_CLEAR(self->pattern); @@ -2264,10 +2276,9 @@ match_clear(MatchObject *self) } static void -match_dealloc(MatchObject* self) +match_dealloc(PyObject *self) { PyTypeObject *tp = Py_TYPE(self); - PyObject_GC_UnTrack(self); (void)match_clear(self); tp->tp_free(self); @@ -2376,8 +2387,9 @@ _sre_SRE_Match_expand_impl(MatchObject *self, PyObject *template) } static PyObject* -match_group(MatchObject* self, PyObject* args) +match_group(PyObject *op, PyObject* args) { + MatchObject *self = _MatchObject_CAST(op); PyObject* result; Py_ssize_t i, size; @@ -2411,8 +2423,9 @@ match_group(MatchObject* self, PyObject* args) } static PyObject* -match_getitem(MatchObject* self, PyObject* name) +match_getitem(PyObject *op, PyObject* name) { + MatchObject *self = _MatchObject_CAST(op); return match_getslice(self, name, Py_None); } @@ -2654,16 +2667,18 @@ PyDoc_STRVAR(match_group_doc, For 0 returns the entire match."); static PyObject * -match_lastindex_get(MatchObject *self, void *Py_UNUSED(ignored)) +match_lastindex_get(PyObject *op, void *Py_UNUSED(ignored)) { + MatchObject *self = _MatchObject_CAST(op); if (self->lastindex >= 0) return PyLong_FromSsize_t(self->lastindex); Py_RETURN_NONE; } static PyObject * -match_lastgroup_get(MatchObject *self, void *Py_UNUSED(ignored)) +match_lastgroup_get(PyObject *op, void *Py_UNUSED(ignored)) { + MatchObject *self = _MatchObject_CAST(op); if (self->pattern->indexgroup && self->lastindex >= 0 && self->lastindex < PyTuple_GET_SIZE(self->pattern->indexgroup)) @@ -2676,8 +2691,9 @@ match_lastgroup_get(MatchObject *self, void *Py_UNUSED(ignored)) } static PyObject * -match_regs_get(MatchObject *self, void *Py_UNUSED(ignored)) +match_regs_get(PyObject *op, void *Py_UNUSED(ignored)) { + MatchObject *self = _MatchObject_CAST(op); if (self->regs) { return Py_NewRef(self->regs); } else @@ -2780,27 +2796,29 @@ pattern_new_match(_sremodulestate* module_state, /* scanner methods (experimental) */ static int -scanner_traverse(ScannerObject *self, visitproc visit, void *arg) +scanner_traverse(PyObject *op, visitproc visit, void *arg) { + ScannerObject *self = _ScannerObject_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->pattern); return 0; } static int -scanner_clear(ScannerObject *self) +scanner_clear(PyObject *op) { + ScannerObject *self = _ScannerObject_CAST(op); Py_CLEAR(self->pattern); return 0; } static void -scanner_dealloc(ScannerObject* self) +scanner_dealloc(PyObject *self) { PyTypeObject *tp = Py_TYPE(self); - PyObject_GC_UnTrack(self); - state_fini(&self->state); + ScannerObject *scanner = _ScannerObject_CAST(self); + state_fini(&scanner->state); (void)scanner_clear(self); tp->tp_free(self); Py_DECREF(tp); @@ -2957,8 +2975,9 @@ pattern_scanner(_sremodulestate *module_state, /* template methods */ static int -template_traverse(TemplateObject *self, visitproc visit, void *arg) +template_traverse(PyObject *op, visitproc visit, void *arg) { + TemplateObject *self = _TemplateObject_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->literal); for (Py_ssize_t i = 0, n = Py_SIZE(self); i < n; i++) { @@ -2968,8 +2987,9 @@ template_traverse(TemplateObject *self, visitproc visit, void *arg) } static int -template_clear(TemplateObject *self) +template_clear(PyObject *op) { + TemplateObject *self = _TemplateObject_CAST(op); Py_CLEAR(self->literal); for (Py_ssize_t i = 0, n = Py_SIZE(self); i < n; i++) { Py_CLEAR(self->items[i].literal); @@ -2978,10 +2998,9 @@ template_clear(TemplateObject *self) } static void -template_dealloc(TemplateObject *self) +template_dealloc(PyObject *self) { PyTypeObject *tp = Py_TYPE(self); - PyObject_GC_UnTrack(self); (void)template_clear(self); tp->tp_free(self); @@ -3056,8 +3075,10 @@ expand_template(TemplateObject *self, MatchObject *match) static Py_hash_t -pattern_hash(PatternObject *self) +pattern_hash(PyObject *op) { + PatternObject *self = _PatternObject_CAST(op); + Py_hash_t hash, hash2; hash = PyObject_Hash(self->pattern); @@ -3148,7 +3169,7 @@ static PyMethodDef pattern_methods[] = { }; static PyGetSetDef pattern_getset[] = { - {"groupindex", (getter)pattern_groupindex, (setter)NULL, + {"groupindex", pattern_groupindex, NULL, "A dictionary mapping group names to group numbers."}, {NULL} /* Sentinel */ }; @@ -3166,9 +3187,9 @@ static PyMemberDef pattern_members[] = { }; static PyType_Slot pattern_slots[] = { - {Py_tp_dealloc, (destructor)pattern_dealloc}, - {Py_tp_repr, (reprfunc)pattern_repr}, - {Py_tp_hash, (hashfunc)pattern_hash}, + {Py_tp_dealloc, pattern_dealloc}, + {Py_tp_repr, pattern_repr}, + {Py_tp_hash, pattern_hash}, {Py_tp_doc, (void *)pattern_doc}, {Py_tp_richcompare, pattern_richcompare}, {Py_tp_methods, pattern_methods}, @@ -3189,7 +3210,7 @@ static PyType_Spec pattern_spec = { }; static PyMethodDef match_methods[] = { - {"group", (PyCFunction) match_group, METH_VARARGS, match_group_doc}, + {"group", match_group, METH_VARARGS, match_group_doc}, _SRE_SRE_MATCH_START_METHODDEF _SRE_SRE_MATCH_END_METHODDEF _SRE_SRE_MATCH_SPAN_METHODDEF @@ -3204,11 +3225,11 @@ static PyMethodDef match_methods[] = { }; static PyGetSetDef match_getset[] = { - {"lastindex", (getter)match_lastindex_get, (setter)NULL, + {"lastindex", match_lastindex_get, NULL, "The integer index of the last matched capturing group."}, - {"lastgroup", (getter)match_lastgroup_get, (setter)NULL, + {"lastgroup", match_lastgroup_get, NULL, "The name of the last matched capturing group."}, - {"regs", (getter)match_regs_get, (setter)NULL}, + {"regs", match_regs_get, NULL, NULL}, {NULL} }; From 8f93dd8a8f237b277abad20d566df90c5cbd7f1e Mon Sep 17 00:00:00 2001 From: "T. Wouters" Date: Tue, 7 Jan 2025 06:41:01 -0800 Subject: [PATCH 221/376] gh-115999: Add free-threaded specialization for COMPARE_OP (#126410) Add free-threaded specialization for COMPARE_OP, and tests for COMPARE_OP specialization in general. Co-authored-by: Donghee Na --- Lib/test/test_opcache.py | 33 +++++++++++++++++++++++++++++++++ Python/bytecodes.c | 2 +- Python/generated_cases.c.h | 2 +- Python/specialize.c | 17 +++++++---------- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index 79f452f8068c7f..c7cd4c2e8a3146 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -1598,6 +1598,39 @@ def __getitem__(self, item): self.assert_specialized(binary_subscr_getitems, "BINARY_SUBSCR_GETITEM") self.assert_no_opcode(binary_subscr_getitems, "BINARY_SUBSCR") + @cpython_only + @requires_specialization_ft + def test_compare_op(self): + def compare_op_int(): + for _ in range(100): + a, b = 1, 2 + c = a == b + self.assertFalse(c) + + compare_op_int() + self.assert_specialized(compare_op_int, "COMPARE_OP_INT") + self.assert_no_opcode(compare_op_int, "COMPARE_OP") + + def compare_op_float(): + for _ in range(100): + a, b = 1.0, 2.0 + c = a == b + self.assertFalse(c) + + compare_op_float() + self.assert_specialized(compare_op_float, "COMPARE_OP_FLOAT") + self.assert_no_opcode(compare_op_float, "COMPARE_OP") + + def compare_op_str(): + for _ in range(100): + a, b = "spam", "ham" + c = a == b + self.assertFalse(c) + + compare_op_str() + self.assert_specialized(compare_op_str, "COMPARE_OP_STR") + self.assert_no_opcode(compare_op_str, "COMPARE_OP") + if __name__ == "__main__": unittest.main() diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 4961693c7e654a..ec1cd00962ac0a 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2464,7 +2464,7 @@ dummy_func( }; specializing op(_SPECIALIZE_COMPARE_OP, (counter/1, left, right -- left, right)) { - #if ENABLE_SPECIALIZATION + #if ENABLE_SPECIALIZATION_FT if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _Py_Specialize_CompareOp(left, right, next_instr, oparg); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index b73844ca2d9542..eaa8a563464068 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3229,7 +3229,7 @@ left = stack_pointer[-2]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION + #if ENABLE_SPECIALIZATION_FT if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _PyFrame_SetStackPointer(frame, stack_pointer); diff --git a/Python/specialize.c b/Python/specialize.c index c918c77779d20d..c9325c39210874 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -2480,23 +2480,23 @@ _Py_Specialize_CompareOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *i { PyObject *lhs = PyStackRef_AsPyObjectBorrow(lhs_st); PyObject *rhs = PyStackRef_AsPyObjectBorrow(rhs_st); + uint8_t specialized_op; - assert(ENABLE_SPECIALIZATION); + assert(ENABLE_SPECIALIZATION_FT); assert(_PyOpcode_Caches[COMPARE_OP] == INLINE_CACHE_ENTRIES_COMPARE_OP); // All of these specializations compute boolean values, so they're all valid // regardless of the fifth-lowest oparg bit. - _PyCompareOpCache *cache = (_PyCompareOpCache *)(instr + 1); if (Py_TYPE(lhs) != Py_TYPE(rhs)) { SPECIALIZATION_FAIL(COMPARE_OP, compare_op_fail_kind(lhs, rhs)); goto failure; } if (PyFloat_CheckExact(lhs)) { - instr->op.code = COMPARE_OP_FLOAT; + specialized_op = COMPARE_OP_FLOAT; goto success; } if (PyLong_CheckExact(lhs)) { if (_PyLong_IsCompact((PyLongObject *)lhs) && _PyLong_IsCompact((PyLongObject *)rhs)) { - instr->op.code = COMPARE_OP_INT; + specialized_op = COMPARE_OP_INT; goto success; } else { @@ -2511,19 +2511,16 @@ _Py_Specialize_CompareOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *i goto failure; } else { - instr->op.code = COMPARE_OP_STR; + specialized_op = COMPARE_OP_STR; goto success; } } SPECIALIZATION_FAIL(COMPARE_OP, compare_op_fail_kind(lhs, rhs)); failure: - STAT_INC(COMPARE_OP, failure); - instr->op.code = COMPARE_OP; - cache->counter = adaptive_counter_backoff(cache->counter); + unspecialize(instr); return; success: - STAT_INC(COMPARE_OP, success); - cache->counter = adaptive_counter_cooldown(); + specialize(instr, specialized_op); } #ifdef Py_STATS From a21e31ec5417374497b0f969ee2b3dc84a0944ff Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 7 Jan 2025 22:07:17 +0100 Subject: [PATCH 222/376] Docs: mark up json.load() using parameter list (#128488) --- Doc/library/json.rst | 113 ++++++++++++++++++++++++++----------------- 1 file changed, 69 insertions(+), 44 deletions(-) diff --git a/Doc/library/json.rst b/Doc/library/json.rst index 169291f74f44a5..cf516cf3dc1d29 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -258,36 +258,82 @@ Basic Usage the original one. That is, ``loads(dumps(x)) != x`` if x has non-string keys. -.. function:: load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw) +.. function:: load(fp, *, cls=None, object_hook=None, parse_float=None, \ + parse_int=None, parse_constant=None, \ + object_pairs_hook=None, **kw) - Deserialize *fp* (a ``.read()``-supporting :term:`text file` or - :term:`binary file` containing a JSON document) to a Python object using - this :ref:`conversion table `. + Deserialize *fp* to a Python object + using the :ref:`JSON-to-Python conversion table `. - *object_hook* is an optional function that will be called with the result of - any object literal decoded (a :class:`dict`). The return value of - *object_hook* will be used instead of the :class:`dict`. This feature can - be used to implement custom decoders (e.g. `JSON-RPC - `_ class hinting). + :param fp: + A ``.read()``-supporting :term:`text file` or :term:`binary file` + containing the JSON document to be deserialized. + :type fp: :term:`file-like object` - *object_pairs_hook* is an optional function that will be called with the - result of any object literal decoded with an ordered list of pairs. The - return value of *object_pairs_hook* will be used instead of the - :class:`dict`. This feature can be used to implement custom decoders. If - *object_hook* is also defined, the *object_pairs_hook* takes priority. + :param cls: + If set, a custom JSON decoder. + Additional keyword arguments to :func:`!load` + will be passed to the constructor of *cls*. + If ``None`` (the default), :class:`!JSONDecoder` is used. + :type cls: a :class:`JSONDecoder` subclass + + :param object_hook: + If set, a function that is called with the result of + any object literal decoded (a :class:`dict`). + The return value of this function will be used + instead of the :class:`dict`. + This feature can be used to implement custom decoders, + for example `JSON-RPC `_ class hinting. + Default ``None``. + :type object_hook: :term:`callable` | None + + :param object_pairs_hook: + If set, a function that is called with the result of + any object literal decoded with an ordered list of pairs. + The return value of this function will be used + instead of the :class:`dict`. + This feature can be used to implement custom decoders. + If *object_hook* is also set, *object_pairs_hook* takes priority. + Default ``None``. + :type object_pairs_hook: :term:`callable` | None + + :param parse_float: + If set, a function that is called with + the string of every JSON float to be decoded. + If ``None`` (the default), it is equivalent to ``float(num_str)``. + This can be used to parse JSON floats into custom datatypes, + for example :class:`decimal.Decimal`. + :type parse_float: :term:`callable` | None + + :param parse_int: + If set, a function that is called with + the string of every JSON int to be decoded. + If ``None`` (the default), it is equivalent to ``int(num_str)``. + This can be used to parse JSON integers into custom datatypes, + for example :class:`float`. + :type parse_int: :term:`callable` | None + + :param parse_constant: + If set, a function that is called with one of the following strings: + ``'-Infinity'``, ``'Infinity'``, or ``'NaN'``. + This can be used to raise an exception + if invalid JSON numbers are encountered. + Default ``None``. + :type parse_constant: :term:`callable` | None + + :raises JSONDecodeError: + When the data being deserialized is not a valid JSON document. .. versionchanged:: 3.1 - Added support for *object_pairs_hook*. - *parse_float* is an optional function that will be called with the string of - every JSON float to be decoded. By default, this is equivalent to - ``float(num_str)``. This can be used to use another datatype or parser for - JSON floats (e.g. :class:`decimal.Decimal`). + * Added the optional *object_pairs_hook* parameter. + * *parse_constant* doesn't get called on 'null', 'true', 'false' anymore. - *parse_int* is an optional function that will be called with the string of - every JSON int to be decoded. By default, this is equivalent to - ``int(num_str)``. This can be used to use another datatype or parser for - JSON integers (e.g. :class:`float`). + .. versionchanged:: 3.6 + + * All optional parameters are now :ref:`keyword-only `. + * *fp* can now be a :term:`binary file`. + The input encoding should be UTF-8, UTF-16 or UTF-32. .. versionchanged:: 3.11 The default *parse_int* of :func:`int` now limits the maximum length of @@ -295,27 +341,6 @@ Basic Usage conversion length limitation ` to help avoid denial of service attacks. - *parse_constant* is an optional function that will be called with one of the - following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. This can be - used to raise an exception if invalid JSON numbers are encountered. - - .. versionchanged:: 3.1 - *parse_constant* doesn't get called on 'null', 'true', 'false' anymore. - - To use a custom :class:`JSONDecoder` subclass, specify it with the ``cls`` - kwarg; otherwise :class:`JSONDecoder` is used. Additional keyword arguments - will be passed to the constructor of the class. - - If the data being deserialized is not a valid JSON document, a - :exc:`JSONDecodeError` will be raised. - - .. versionchanged:: 3.6 - All optional parameters are now :ref:`keyword-only `. - - .. versionchanged:: 3.6 - *fp* can now be a :term:`binary file`. The input encoding should be - UTF-8, UTF-16 or UTF-32. - .. function:: loads(s, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw) Deserialize *s* (a :class:`str`, :class:`bytes` or :class:`bytearray` From 07e6aa2efc5b2c7ecbbdb41636013c64e335a2ba Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 7 Jan 2025 23:13:08 +0200 Subject: [PATCH 223/376] gh-127350: Add more tests for Py_fopen() (GH-128587) --- Lib/test/test_capi/test_file.py | 25 +++++++++++++++++++++---- Modules/_testcapi/clinic/file.c.h | 25 +++++++------------------ Modules/_testcapi/file.c | 8 +++++--- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/Lib/test/test_capi/test_file.py b/Lib/test/test_capi/test_file.py index 8a08a0a93eb8b7..a67a5121c4588b 100644 --- a/Lib/test/test_capi/test_file.py +++ b/Lib/test/test_capi/test_file.py @@ -5,6 +5,8 @@ _testcapi = import_helper.import_module('_testcapi') +NULL = None + class CAPIFileTest(unittest.TestCase): def test_py_fopen(self): @@ -25,7 +27,9 @@ def test_py_fopen(self): os_helper.TESTFN, os.fsencode(os_helper.TESTFN), ] - # TESTFN_UNDECODABLE cannot be used to create a file on macOS/WASI. + if os_helper.TESTFN_UNDECODABLE is not None: + filenames.append(os_helper.TESTFN_UNDECODABLE) + filenames.append(os.fsdecode(os_helper.TESTFN_UNDECODABLE)) if os_helper.TESTFN_UNENCODABLE is not None: filenames.append(os_helper.TESTFN_UNENCODABLE) for filename in filenames: @@ -33,7 +37,12 @@ def test_py_fopen(self): try: with open(filename, "wb") as fp: fp.write(source) - + except OSError: + # TESTFN_UNDECODABLE cannot be used to create a file + # on macOS/WASI. + filename = None + continue + try: data = _testcapi.py_fopen(filename, "rb") self.assertEqual(data, source[:256]) finally: @@ -47,7 +56,14 @@ def test_py_fopen(self): # non-ASCII mode failing with "Invalid argument" with self.assertRaises(OSError): - _testcapi.py_fopen(__file__, "\xe9") + _testcapi.py_fopen(__file__, b"\xc2\x80") + with self.assertRaises(OSError): + # \x98 is invalid in cp1250, cp1251, cp1257 + # \x9d is invalid in cp1252-cp1255, cp1258 + _testcapi.py_fopen(__file__, b"\xc2\x98\xc2\x9d") + # UnicodeDecodeError can come from the audit hook code + with self.assertRaises((UnicodeDecodeError, OSError)): + _testcapi.py_fopen(__file__, b"\x98\x9d") # invalid filename type for invalid_type in (123, object()): @@ -60,7 +76,8 @@ def test_py_fopen(self): # On Windows, the file mode is limited to 10 characters _testcapi.py_fopen(__file__, "rt+, ccs=UTF-8") - # CRASHES py_fopen(__file__, None) + # CRASHES _testcapi.py_fopen(NULL, 'rb') + # CRASHES _testcapi.py_fopen(__file__, NULL) if __name__ == "__main__": diff --git a/Modules/_testcapi/clinic/file.c.h b/Modules/_testcapi/clinic/file.c.h index 2ca21fffcef680..fddbf48071bd3b 100644 --- a/Modules/_testcapi/clinic/file.c.h +++ b/Modules/_testcapi/clinic/file.c.h @@ -14,7 +14,8 @@ PyDoc_STRVAR(_testcapi_py_fopen__doc__, {"py_fopen", _PyCFunction_CAST(_testcapi_py_fopen), METH_FASTCALL, _testcapi_py_fopen__doc__}, static PyObject * -_testcapi_py_fopen_impl(PyObject *module, PyObject *path, const char *mode); +_testcapi_py_fopen_impl(PyObject *module, PyObject *path, const char *mode, + Py_ssize_t mode_length); static PyObject * _testcapi_py_fopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) @@ -22,27 +23,15 @@ _testcapi_py_fopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; PyObject *path; const char *mode; - - if (!_PyArg_CheckPositional("py_fopen", nargs, 2, 2)) { - goto exit; - } - path = args[0]; - if (!PyUnicode_Check(args[1])) { - _PyArg_BadArgument("py_fopen", "argument 2", "str", args[1]); - goto exit; - } Py_ssize_t mode_length; - mode = PyUnicode_AsUTF8AndSize(args[1], &mode_length); - if (mode == NULL) { - goto exit; - } - if (strlen(mode) != (size_t)mode_length) { - PyErr_SetString(PyExc_ValueError, "embedded null character"); + + if (!_PyArg_ParseStack(args, nargs, "Oz#:py_fopen", + &path, &mode, &mode_length)) { goto exit; } - return_value = _testcapi_py_fopen_impl(module, path, mode); + return_value = _testcapi_py_fopen_impl(module, path, mode, mode_length); exit: return return_value; } -/*[clinic end generated code: output=c9fe964c3e5a0c32 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c4dc92400306c3eb input=a9049054013a1b77]*/ diff --git a/Modules/_testcapi/file.c b/Modules/_testcapi/file.c index 4bad43010fd440..d15173fc7959e5 100644 --- a/Modules/_testcapi/file.c +++ b/Modules/_testcapi/file.c @@ -14,16 +14,18 @@ module _testcapi _testcapi.py_fopen path: object - mode: str + mode: str(zeroes=True, accept={robuffer, str, NoneType}) / Call Py_fopen(), fread(256) and Py_fclose(). Return read bytes. [clinic start generated code]*/ static PyObject * -_testcapi_py_fopen_impl(PyObject *module, PyObject *path, const char *mode) -/*[clinic end generated code: output=5a900af000f759de input=d7e7b8f0fd151953]*/ +_testcapi_py_fopen_impl(PyObject *module, PyObject *path, const char *mode, + Py_ssize_t mode_length) +/*[clinic end generated code: output=69840d0cfd8b7fbb input=f3a579dd7eb60926]*/ { + NULLABLE(path); FILE *fp = Py_fopen(path, mode); if (fp == NULL) { return NULL; From e08b28235a863323ca3a7e444776bb7803e77caf Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 7 Jan 2025 22:42:36 +0100 Subject: [PATCH 224/376] gh-127614: Correctly check for ttyname_r() in configure (#128503) PR #14868 replaced the ttyname() call with ttyname_r(), but the old check remained. --- Modules/clinic/posixmodule.c.h | 6 +++--- Modules/posixmodule.c | 2 +- configure | 6 +++--- configure.ac | 2 +- pyconfig.h.in | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 554299b8598299..4e6c5b068c42b7 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -309,7 +309,7 @@ os_access(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k return return_value; } -#if defined(HAVE_TTYNAME) +#if defined(HAVE_TTYNAME_R) PyDoc_STRVAR(os_ttyname__doc__, "ttyname($module, fd, /)\n" @@ -342,7 +342,7 @@ os_ttyname(PyObject *module, PyObject *arg) return return_value; } -#endif /* defined(HAVE_TTYNAME) */ +#endif /* defined(HAVE_TTYNAME_R) */ #if defined(HAVE_CTERMID) @@ -13140,4 +13140,4 @@ os__emscripten_debugger(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef OS__EMSCRIPTEN_DEBUGGER_METHODDEF #define OS__EMSCRIPTEN_DEBUGGER_METHODDEF #endif /* !defined(OS__EMSCRIPTEN_DEBUGGER_METHODDEF) */ -/*[clinic end generated code: output=9c2ca1dbf986c62c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=39b69b279fd637f7 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 151d469983fafb..bb8d698bfed375 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -3347,7 +3347,7 @@ os_access_impl(PyObject *module, path_t *path, int mode, int dir_fd, #endif -#ifdef HAVE_TTYNAME +#ifdef HAVE_TTYNAME_R /*[clinic input] os.ttyname diff --git a/configure b/configure index 08c10ff52ce8d5..6e1b393a3ece68 100755 --- a/configure +++ b/configure @@ -19986,10 +19986,10 @@ then : printf "%s\n" "#define HAVE_TRUNCATE 1" >>confdefs.h fi -ac_fn_c_check_func "$LINENO" "ttyname" "ac_cv_func_ttyname" -if test "x$ac_cv_func_ttyname" = xyes +ac_fn_c_check_func "$LINENO" "ttyname_r" "ac_cv_func_ttyname_r" +if test "x$ac_cv_func_ttyname_r" = xyes then : - printf "%s\n" "#define HAVE_TTYNAME 1" >>confdefs.h + printf "%s\n" "#define HAVE_TTYNAME_R 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "umask" "ac_cv_func_umask" diff --git a/configure.ac b/configure.ac index fb1dd77be167b9..6d44be8959865d 100644 --- a/configure.ac +++ b/configure.ac @@ -5152,7 +5152,7 @@ AC_CHECK_FUNCS([ \ sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ - tmpnam tmpnam_r truncate ttyname umask uname unlinkat unlockpt utimensat utimes vfork \ + tmpnam tmpnam_r truncate ttyname_r umask uname unlinkat unlockpt utimensat utimes vfork \ wait wait3 wait4 waitid waitpid wcscoll wcsftime wcsxfrm wmemcmp writev \ ]) diff --git a/pyconfig.h.in b/pyconfig.h.in index d862966b7de4c8..874b98dc96585a 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1506,8 +1506,8 @@ /* Define to 1 if you have the 'truncate' function. */ #undef HAVE_TRUNCATE -/* Define to 1 if you have the 'ttyname' function. */ -#undef HAVE_TTYNAME +/* Define to 1 if you have the 'ttyname_r' function. */ +#undef HAVE_TTYNAME_R /* Define to 1 if you don't have 'tm_zone' but do have the external array 'tzname'. */ From 65ae3d5a73ca3c53a0c6b601dddb8e9b3b6e3f51 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Tue, 7 Jan 2025 17:25:48 -0800 Subject: [PATCH 225/376] GH-127809: Fix the JIT's understanding of ** (GH-127844) --- Lib/test/test_capi/test_opt.py | 44 +++++++++++ ...-12-11-14-32-22.gh-issue-127809.0W8khe.rst | 2 + Python/bytecodes.c | 16 ++++ Python/executor_cases.c.h | 16 ++++ Python/generated_cases.c.h | 16 ++++ Python/optimizer_bytecodes.c | 57 ++++++++++++--- Python/optimizer_cases.c.h | 73 +++++++++++++++---- Tools/cases_generator/analyzer.py | 1 + 8 files changed, 199 insertions(+), 26 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-11-14-32-22.gh-issue-127809.0W8khe.rst diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 4cf9b66170c055..d84702411afe41 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -1,4 +1,5 @@ import contextlib +import itertools import sys import textwrap import unittest @@ -1511,6 +1512,49 @@ def test_jit_error_pops(self): with self.assertRaises(TypeError): {item for item in items} + def test_power_type_depends_on_input_values(self): + template = textwrap.dedent(""" + import _testinternalcapi + + L, R, X, Y = {l}, {r}, {x}, {y} + + def check(actual: complex, expected: complex) -> None: + assert actual == expected, (actual, expected) + assert type(actual) is type(expected), (actual, expected) + + def f(l: complex, r: complex) -> None: + expected_local_local = pow(l, r) + pow(l, r) + expected_const_local = pow(L, r) + pow(L, r) + expected_local_const = pow(l, R) + pow(l, R) + expected_const_const = pow(L, R) + pow(L, R) + for _ in range(_testinternalcapi.TIER2_THRESHOLD): + # Narrow types: + l + l, r + r + # The powers produce results, and the addition is unguarded: + check(l ** r + l ** r, expected_local_local) + check(L ** r + L ** r, expected_const_local) + check(l ** R + l ** R, expected_local_const) + check(L ** R + L ** R, expected_const_const) + + # JIT for one pair of values... + f(L, R) + # ...then run with another: + f(X, Y) + """) + interesting = [ + (1, 1), # int ** int -> int + (1, -1), # int ** int -> float + (1.0, 1), # float ** int -> float + (1, 1.0), # int ** float -> float + (-1, 0.5), # int ** float -> complex + (1.0, 1.0), # float ** float -> float + (-1.0, 0.5), # float ** float -> complex + ] + for (l, r), (x, y) in itertools.product(interesting, repeat=2): + s = template.format(l=l, r=r, x=x, y=y) + with self.subTest(l=l, r=r, x=x, y=y): + script_helper.assert_python_ok("-c", s) + def global_identity(x): return x diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-11-14-32-22.gh-issue-127809.0W8khe.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-11-14-32-22.gh-issue-127809.0W8khe.rst new file mode 100644 index 00000000000000..19c8cc6e99c8c5 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-11-14-32-22.gh-issue-127809.0W8khe.rst @@ -0,0 +1,2 @@ +Fix an issue where the experimental JIT may infer an incorrect result type +for exponentiation (``**`` and ``**=``), leading to bugs or crashes. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index ec1cd00962ac0a..8bab4ea16b629b 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -530,6 +530,8 @@ dummy_func( pure op(_BINARY_OP_MULTIPLY_INT, (left, right -- res)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Multiply((PyLongObject *)left_o, (PyLongObject *)right_o); @@ -543,6 +545,8 @@ dummy_func( pure op(_BINARY_OP_ADD_INT, (left, right -- res)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Add((PyLongObject *)left_o, (PyLongObject *)right_o); @@ -556,6 +560,8 @@ dummy_func( pure op(_BINARY_OP_SUBTRACT_INT, (left, right -- res)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); @@ -593,6 +599,8 @@ dummy_func( pure op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); double dres = @@ -607,6 +615,8 @@ dummy_func( pure op(_BINARY_OP_ADD_FLOAT, (left, right -- res)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); double dres = @@ -621,6 +631,8 @@ dummy_func( pure op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); double dres = @@ -650,6 +662,8 @@ dummy_func( pure op(_BINARY_OP_ADD_UNICODE, (left, right -- res)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyUnicode_CheckExact(left_o)); + assert(PyUnicode_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = PyUnicode_Concat(left_o, right_o); @@ -672,6 +686,8 @@ dummy_func( op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right --)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyUnicode_CheckExact(left_o)); + assert(PyUnicode_CheckExact(right_o)); int next_oparg; #if TIER_ONE diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index ac2f69b7e98dc3..e40fa88be89172 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -638,6 +638,8 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Multiply((PyLongObject *)left_o, (PyLongObject *)right_o); PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); @@ -658,6 +660,8 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Add((PyLongObject *)left_o, (PyLongObject *)right_o); PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); @@ -678,6 +682,8 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); @@ -738,6 +744,8 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); double dres = ((PyFloatObject *)left_o)->ob_fval * @@ -759,6 +767,8 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); double dres = ((PyFloatObject *)left_o)->ob_fval + @@ -780,6 +790,8 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); double dres = ((PyFloatObject *)left_o)->ob_fval - @@ -819,6 +831,8 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyUnicode_CheckExact(left_o)); + assert(PyUnicode_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = PyUnicode_Concat(left_o, right_o); PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); @@ -838,6 +852,8 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyUnicode_CheckExact(left_o)); + assert(PyUnicode_CheckExact(right_o)); int next_oparg; #if TIER_ONE assert(next_instr->op.code == STORE_FAST); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index eaa8a563464068..7028ba52faae96 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -80,6 +80,8 @@ { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); double dres = ((PyFloatObject *)left_o)->ob_fval + @@ -116,6 +118,8 @@ { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Add((PyLongObject *)left_o, (PyLongObject *)right_o); PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); @@ -151,6 +155,8 @@ { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyUnicode_CheckExact(left_o)); + assert(PyUnicode_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = PyUnicode_Concat(left_o, right_o); PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); @@ -185,6 +191,8 @@ { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyUnicode_CheckExact(left_o)); + assert(PyUnicode_CheckExact(right_o)); int next_oparg; #if TIER_ONE assert(next_instr->op.code == STORE_FAST); @@ -247,6 +255,8 @@ { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); double dres = ((PyFloatObject *)left_o)->ob_fval * @@ -283,6 +293,8 @@ { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Multiply((PyLongObject *)left_o, (PyLongObject *)right_o); PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); @@ -318,6 +330,8 @@ { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); double dres = ((PyFloatObject *)left_o)->ob_fval - @@ -354,6 +368,8 @@ { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = _PyLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index a14d119b7a1dec..86394480f76bb8 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -167,23 +167,56 @@ dummy_func(void) { } op(_BINARY_OP, (left, right -- res)) { - PyTypeObject *ltype = sym_get_type(left); - PyTypeObject *rtype = sym_get_type(right); - if (ltype != NULL && (ltype == &PyLong_Type || ltype == &PyFloat_Type) && - rtype != NULL && (rtype == &PyLong_Type || rtype == &PyFloat_Type)) - { - if (oparg != NB_TRUE_DIVIDE && oparg != NB_INPLACE_TRUE_DIVIDE && - ltype == &PyLong_Type && rtype == &PyLong_Type) { - /* If both inputs are ints and the op is not division the result is an int */ - res = sym_new_type(ctx, &PyLong_Type); + bool lhs_int = sym_matches_type(left, &PyLong_Type); + bool rhs_int = sym_matches_type(right, &PyLong_Type); + bool lhs_float = sym_matches_type(left, &PyFloat_Type); + bool rhs_float = sym_matches_type(right, &PyFloat_Type); + if (!((lhs_int || lhs_float) && (rhs_int || rhs_float))) { + // There's something other than an int or float involved: + res = sym_new_unknown(ctx); + } + else if (oparg == NB_POWER || oparg == NB_INPLACE_POWER) { + // This one's fun... the *type* of the result depends on the + // *values* being exponentiated. However, exponents with one + // constant part are reasonably common, so it's probably worth + // trying to infer some simple cases: + // - A: 1 ** 1 -> 1 (int ** int -> int) + // - B: 1 ** -1 -> 1.0 (int ** int -> float) + // - C: 1.0 ** 1 -> 1.0 (float ** int -> float) + // - D: 1 ** 1.0 -> 1.0 (int ** float -> float) + // - E: -1 ** 0.5 ~> 1j (int ** float -> complex) + // - F: 1.0 ** 1.0 -> 1.0 (float ** float -> float) + // - G: -1.0 ** 0.5 ~> 1j (float ** float -> complex) + if (rhs_float) { + // Case D, E, F, or G... can't know without the sign of the LHS + // or whether the RHS is whole, which isn't worth the effort: + res = sym_new_unknown(ctx); } - else { - /* For any other op combining ints/floats the result is a float */ + else if (lhs_float) { + // Case C: res = sym_new_type(ctx, &PyFloat_Type); } + else if (!sym_is_const(right)) { + // Case A or B... can't know without the sign of the RHS: + res = sym_new_unknown(ctx); + } + else if (_PyLong_IsNegative((PyLongObject *)sym_get_const(right))) { + // Case B: + res = sym_new_type(ctx, &PyFloat_Type); + } + else { + // Case A: + res = sym_new_type(ctx, &PyLong_Type); + } + } + else if (oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE) { + res = sym_new_type(ctx, &PyFloat_Type); + } + else if (lhs_int && rhs_int) { + res = sym_new_type(ctx, &PyLong_Type); } else { - res = sym_new_unknown(ctx); + res = sym_new_type(ctx, &PyFloat_Type); } } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index be3e06108aec92..c72ae7b6281e80 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -2307,24 +2307,69 @@ _Py_UopsSymbol *res; right = stack_pointer[-1]; left = stack_pointer[-2]; - PyTypeObject *ltype = sym_get_type(left); - PyTypeObject *rtype = sym_get_type(right); - if (ltype != NULL && (ltype == &PyLong_Type || ltype == &PyFloat_Type) && - rtype != NULL && (rtype == &PyLong_Type || rtype == &PyFloat_Type)) - { - if (oparg != NB_TRUE_DIVIDE && oparg != NB_INPLACE_TRUE_DIVIDE && - ltype == &PyLong_Type && rtype == &PyLong_Type) { - /* If both inputs are ints and the op is not division the result is an int */ - res = sym_new_type(ctx, &PyLong_Type); + bool lhs_int = sym_matches_type(left, &PyLong_Type); + bool rhs_int = sym_matches_type(right, &PyLong_Type); + bool lhs_float = sym_matches_type(left, &PyFloat_Type); + bool rhs_float = sym_matches_type(right, &PyFloat_Type); + if (!((lhs_int || lhs_float) && (rhs_int || rhs_float))) { + // There's something other than an int or float involved: + res = sym_new_unknown(ctx); + } + else { + if (oparg == NB_POWER || oparg == NB_INPLACE_POWER) { + // This one's fun... the *type* of the result depends on the + // *values* being exponentiated. However, exponents with one + // constant part are reasonably common, so it's probably worth + // trying to infer some simple cases: + // - A: 1 ** 1 -> 1 (int ** int -> int) + // - B: 1 ** -1 -> 1.0 (int ** int -> float) + // - C: 1.0 ** 1 -> 1.0 (float ** int -> float) + // - D: 1 ** 1.0 -> 1.0 (int ** float -> float) + // - E: -1 ** 0.5 ~> 1j (int ** float -> complex) + // - F: 1.0 ** 1.0 -> 1.0 (float ** float -> float) + // - G: -1.0 ** 0.5 ~> 1j (float ** float -> complex) + if (rhs_float) { + // Case D, E, F, or G... can't know without the sign of the LHS + // or whether the RHS is whole, which isn't worth the effort: + res = sym_new_unknown(ctx); + } + else { + if (lhs_float) { + // Case C: + res = sym_new_type(ctx, &PyFloat_Type); + } + else { + if (!sym_is_const(right)) { + // Case A or B... can't know without the sign of the RHS: + res = sym_new_unknown(ctx); + } + else { + if (_PyLong_IsNegative((PyLongObject *)sym_get_const(right))) { + // Case B: + res = sym_new_type(ctx, &PyFloat_Type); + } + else { + // Case A: + res = sym_new_type(ctx, &PyLong_Type); + } + } + } + } } else { - /* For any other op combining ints/floats the result is a float */ - res = sym_new_type(ctx, &PyFloat_Type); + if (oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE) { + res = sym_new_type(ctx, &PyFloat_Type); + } + else { + if (lhs_int && rhs_int) { + res = sym_new_type(ctx, &PyLong_Type); + } + else { + res = sym_new_type(ctx, &PyFloat_Type); + } + } } } - else { - res = sym_new_unknown(ctx); - } stack_pointer[-2] = res; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index c0a370a936aa94..679beca3ec3a9d 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -599,6 +599,7 @@ def has_error_without_pop(op: parser.InstDef) -> bool: "_PyLong_CompactValue", "_PyLong_DigitCount", "_PyLong_IsCompact", + "_PyLong_IsNegative", "_PyLong_IsNonNegativeCompact", "_PyLong_IsZero", "_PyLong_Multiply", From 15372d0112ac7fbbbe313725dd87d5a45af2f6d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Srinivas=20Reddy=20Thatiparthy=20=28=E0=B0=A4=E0=B0=BE?= =?UTF-8?q?=E0=B0=9F=E0=B0=BF=E0=B0=AA=E0=B0=B0=E0=B1=8D=E0=B0=A4=E0=B0=BF?= =?UTF-8?q?=20=E0=B0=B6=E0=B1=8D=E0=B0=B0=E0=B1=80=E0=B0=A8=E0=B0=BF?= =?UTF-8?q?=E0=B0=B5=E0=B0=BE=E0=B0=B8=E0=B1=8D=20=20=E0=B0=B0=E0=B1=86?= =?UTF-8?q?=E0=B0=A1=E0=B1=8D=E0=B0=A1=E0=B0=BF=29?= Date: Wed, 8 Jan 2025 13:05:43 +0530 Subject: [PATCH 226/376] gh-87506: Document that json.load*() can raise UnicodeDecodeError (#127355) Co-authored-by: Erlend Aasland --- Doc/library/json.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/library/json.rst b/Doc/library/json.rst index cf516cf3dc1d29..41a09b40d7e88b 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -324,6 +324,10 @@ Basic Usage :raises JSONDecodeError: When the data being deserialized is not a valid JSON document. + :raises UnicodeDecodeError: + When the data being deserialized does not contain + UTF-8, UTF-16 or UTF-32 encoded data. + .. versionchanged:: 3.1 * Added the optional *object_pairs_hook* parameter. From cdfb8bc93a4d8c06d2404ba2d243937ba209438c Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 8 Jan 2025 09:36:44 +0100 Subject: [PATCH 227/376] gh-87506: Amend json.loads() post PR #127355 (#128609) --- Doc/library/json.rst | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Doc/library/json.rst b/Doc/library/json.rst index 41a09b40d7e88b..4e7046d6d8f6ac 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -347,15 +347,11 @@ Basic Usage .. function:: loads(s, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw) - Deserialize *s* (a :class:`str`, :class:`bytes` or :class:`bytearray` + Identical to :func:`load`, but instead of a file-like object, + deserialize *s* (a :class:`str`, :class:`bytes` or :class:`bytearray` instance containing a JSON document) to a Python object using this :ref:`conversion table `. - The other arguments have the same meaning as in :func:`load`. - - If the data being deserialized is not a valid JSON document, a - :exc:`JSONDecodeError` will be raised. - .. versionchanged:: 3.6 *s* can now be of type :class:`bytes` or :class:`bytearray`. The input encoding should be UTF-8, UTF-16 or UTF-32. From 474e419792484d1c16e7d9c99b7bf144136b9307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Srinivas=20Reddy=20Thatiparthy=20=28=E0=B0=A4=E0=B0=BE?= =?UTF-8?q?=E0=B0=9F=E0=B0=BF=E0=B0=AA=E0=B0=B0=E0=B1=8D=E0=B0=A4=E0=B0=BF?= =?UTF-8?q?=20=E0=B0=B6=E0=B1=8D=E0=B0=B0=E0=B1=80=E0=B0=A8=E0=B0=BF?= =?UTF-8?q?=E0=B0=B5=E0=B0=BE=E0=B0=B8=E0=B1=8D=20=20=E0=B0=B0=E0=B1=86?= =?UTF-8?q?=E0=B0=A1=E0=B1=8D=E0=B0=A1=E0=B0=BF=29?= Date: Wed, 8 Jan 2025 16:02:07 +0530 Subject: [PATCH 228/376] gh-41872: Fix quick extraction of module docstrings from a file in pydoc (GH-127520) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It now supports docstrings with single quotes, escape sequences, raw string literals, and other Python syntax. Co-authored-by: Éric Co-authored-by: Serhiy Storchaka --- Lib/pydoc.py | 39 ++++++---- Lib/test/test_pydoc/test_pydoc.py | 77 +++++++++++++++++++ ...4-12-17-15-23-40.gh-issue-41872.31LjKY.rst | 3 + 3 files changed, 104 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-17-15-23-40.gh-issue-41872.31LjKY.rst diff --git a/Lib/pydoc.py b/Lib/pydoc.py index c863794ea14ef9..9e84292aaf825f 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -53,6 +53,7 @@ class or function within a module or module in a package. If the # the current directory is changed with os.chdir(), an incorrect # path will be displayed. +import ast import __future__ import builtins import importlib._bootstrap @@ -384,21 +385,29 @@ def ispackage(path): return False def source_synopsis(file): - line = file.readline() - while line[:1] == '#' or not line.strip(): - line = file.readline() - if not line: break - line = line.strip() - if line[:4] == 'r"""': line = line[1:] - if line[:3] == '"""': - line = line[3:] - if line[-1:] == '\\': line = line[:-1] - while not line.strip(): - line = file.readline() - if not line: break - result = line.split('"""')[0].strip() - else: result = None - return result + """Return the one-line summary of a file object, if present""" + + string = '' + try: + tokens = tokenize.generate_tokens(file.readline) + for tok_type, tok_string, _, _, _ in tokens: + if tok_type == tokenize.STRING: + string += tok_string + elif tok_type == tokenize.NEWLINE: + with warnings.catch_warnings(): + # Ignore the "invalid escape sequence" warning. + warnings.simplefilter("ignore", SyntaxWarning) + docstring = ast.literal_eval(string) + if not isinstance(docstring, str): + return None + return docstring.strip().split('\n')[0].strip() + elif tok_type == tokenize.OP and tok_string in ('(', ')'): + string += tok_string + elif tok_type not in (tokenize.COMMENT, tokenize.NL, tokenize.ENCODING): + return None + except (tokenize.TokenError, UnicodeDecodeError, SyntaxError): + return None + return None def synopsis(filename, cache={}): """Get the one-line summary out of a module file.""" diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index c798b11f5aa56e..cec18aa9440c9e 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -4,6 +4,7 @@ import contextlib import importlib.util import inspect +import io import pydoc import py_compile import keyword @@ -899,6 +900,82 @@ def test_synopsis(self): synopsis = pydoc.synopsis(TESTFN, {}) self.assertEqual(synopsis, 'line 1: h\xe9') + def test_source_synopsis(self): + def check(source, expected, encoding=None): + if isinstance(source, str): + source_file = StringIO(source) + else: + source_file = io.TextIOWrapper(io.BytesIO(source), encoding=encoding) + with source_file: + result = pydoc.source_synopsis(source_file) + self.assertEqual(result, expected) + + check('"""Single line docstring."""', + 'Single line docstring.') + check('"""First line of docstring.\nSecond line.\nThird line."""', + 'First line of docstring.') + check('"""First line of docstring.\\nSecond line.\\nThird line."""', + 'First line of docstring.') + check('""" Whitespace around docstring. """', + 'Whitespace around docstring.') + check('import sys\n"""No docstring"""', + None) + check(' \n"""Docstring after empty line."""', + 'Docstring after empty line.') + check('# Comment\n"""Docstring after comment."""', + 'Docstring after comment.') + check(' # Indented comment\n"""Docstring after comment."""', + 'Docstring after comment.') + check('""""""', # Empty docstring + '') + check('', # Empty file + None) + check('"""Embedded\0null byte"""', + None) + check('"""Embedded null byte"""\0', + None) + check('"""Café and résumé."""', + 'Café and résumé.') + check("'''Triple single quotes'''", + 'Triple single quotes') + check('"Single double quotes"', + 'Single double quotes') + check("'Single single quotes'", + 'Single single quotes') + check('"""split\\\nline"""', + 'splitline') + check('"""Unrecognized escape \\sequence"""', + 'Unrecognized escape \\sequence') + check('"""Invalid escape seq\\uence"""', + None) + check('r"""Raw \\stri\\ng"""', + 'Raw \\stri\\ng') + check('b"""Bytes literal"""', + None) + check('f"""f-string"""', + None) + check('"""Concatenated""" \\\n"string" \'literals\'', + 'Concatenatedstringliterals') + check('"""String""" + """expression"""', + None) + check('("""In parentheses""")', + 'In parentheses') + check('("""Multiple lines """\n"""in parentheses""")', + 'Multiple lines in parentheses') + check('()', # tuple + None) + check(b'# coding: iso-8859-15\n"""\xa4uro sign"""', + '€uro sign', encoding='iso-8859-15') + check(b'"""\xa4"""', # Decoding error + None, encoding='utf-8') + + with tempfile.NamedTemporaryFile(mode='w+', encoding='utf-8') as temp_file: + temp_file.write('"""Real file test."""\n') + temp_file.flush() + temp_file.seek(0) + result = pydoc.source_synopsis(temp_file) + self.assertEqual(result, "Real file test.") + @requires_docstrings def test_synopsis_sourceless(self): os = import_helper.import_fresh_module('os') diff --git a/Misc/NEWS.d/next/Library/2024-12-17-15-23-40.gh-issue-41872.31LjKY.rst b/Misc/NEWS.d/next/Library/2024-12-17-15-23-40.gh-issue-41872.31LjKY.rst new file mode 100644 index 00000000000000..b807dcb284c248 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-17-15-23-40.gh-issue-41872.31LjKY.rst @@ -0,0 +1,3 @@ +Fix quick extraction of module docstrings from a file in :mod:`pydoc`. +It now supports docstrings with single quotes, escape sequences, +raw string literals, and other Python syntax. From 971a52b5495e3d596e599faa1f31d4671897026d Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 8 Jan 2025 14:19:41 +0300 Subject: [PATCH 229/376] gh-128617: Fix `test_typing.test_readonly_inheritance` (#128618) --- Lib/test/test_typing.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index ef3cfc9517085e..0bb9ada221e985 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -8912,13 +8912,13 @@ class Child1(Base1): self.assertEqual(Child1.__mutable_keys__, frozenset({'b'})) class Base2(TypedDict): - a: ReadOnly[int] + a: int class Child2(Base2): - b: str + b: ReadOnly[str] - self.assertEqual(Child1.__readonly_keys__, frozenset({'a'})) - self.assertEqual(Child1.__mutable_keys__, frozenset({'b'})) + self.assertEqual(Child2.__readonly_keys__, frozenset({'b'})) + self.assertEqual(Child2.__mutable_keys__, frozenset({'a'})) def test_cannot_make_mutable_key_readonly(self): class Base(TypedDict): From eb26e170695f15714b5e2ae0c0b83aa790c97869 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 8 Jan 2025 15:02:47 +0300 Subject: [PATCH 230/376] gh-128613: Increase `typing.Concatenate` coverage (#128614) --- Lib/test/test_typing.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 0bb9ada221e985..a75dac4a6102bf 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -10129,6 +10129,18 @@ def test_valid_uses(self): self.assertEqual(C4.__args__, (Concatenate[int, T, P], T)) self.assertEqual(C4.__parameters__, (T, P)) + def test_invalid_uses(self): + with self.assertRaisesRegex(TypeError, 'Concatenate of no types'): + Concatenate[()] + with self.assertRaisesRegex( + TypeError, + ( + 'The last parameter to Concatenate should be a ' + 'ParamSpec variable or ellipsis' + ), + ): + Concatenate[int] + def test_var_substitution(self): T = TypeVar('T') P = ParamSpec('P') From 95cd9c669cdc7718198addb1abb49941a2c61fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns=20=F0=9F=87=B5=F0=9F=87=B8?= Date: Wed, 8 Jan 2025 12:03:21 +0000 Subject: [PATCH 231/376] GH-127970: find the runtime library when dladdr is available (#127972) --- ...-12-15-19-51-54.gh-issue-127970.vdUp-y.rst | 6 +++ Modules/getpath.c | 38 ++++++++----------- configure | 6 +++ configure.ac | 2 +- pyconfig.h.in | 3 ++ 5 files changed, 31 insertions(+), 24 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-19-51-54.gh-issue-127970.vdUp-y.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-19-51-54.gh-issue-127970.vdUp-y.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-19-51-54.gh-issue-127970.vdUp-y.rst new file mode 100644 index 00000000000000..e4dc7b5fe032d6 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-19-51-54.gh-issue-127970.vdUp-y.rst @@ -0,0 +1,6 @@ +We now use the location of the ``libpython`` runtime library used in the current +proccess to determine :data:`sys.base_prefix` on all platforms implementing the +`dladdr `_ +function defined by the UNIX standard — this includes Linux, Android, macOS, +iOS, FreeBSD, etc. This was already the case on Windows and macOS Framework +builds. diff --git a/Modules/getpath.c b/Modules/getpath.c index 18ddfaf8dbce1a..2d3c9757298d16 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -17,10 +17,13 @@ #endif #ifdef __APPLE__ -# include # include #endif +#ifdef HAVE_DLFCN_H +# include +#endif + /* Reference the precompiled getpath.py */ #include "Python/frozen_modules/getpath.h" @@ -803,36 +806,25 @@ progname_to_dict(PyObject *dict, const char *key) static int library_to_dict(PyObject *dict, const char *key) { +/* macOS framework builds do not link against a libpython dynamic library, but + instead link against a macOS Framework. */ +#if defined(Py_ENABLE_SHARED) || defined(WITH_NEXT_FRAMEWORK) + #ifdef MS_WINDOWS -#ifdef Py_ENABLE_SHARED extern HMODULE PyWin_DLLhModule; if (PyWin_DLLhModule) { return winmodule_to_dict(dict, key, PyWin_DLLhModule); } #endif -#elif defined(WITH_NEXT_FRAMEWORK) - static char modPath[MAXPATHLEN + 1]; - static int modPathInitialized = -1; - if (modPathInitialized < 0) { - modPathInitialized = 0; - - /* On Mac OS X we have a special case if we're running from a framework. - This is because the python home should be set relative to the library, - which is in the framework, not relative to the executable, which may - be outside of the framework. Except when we're in the build - directory... */ - Dl_info pythonInfo; - if (dladdr(&Py_Initialize, &pythonInfo)) { - if (pythonInfo.dli_fname) { - strncpy(modPath, pythonInfo.dli_fname, MAXPATHLEN); - modPathInitialized = 1; - } - } - } - if (modPathInitialized > 0) { - return decode_to_dict(dict, key, modPath); + +#if HAVE_DLADDR + Dl_info libpython_info; + if (dladdr(&Py_Initialize, &libpython_info) && libpython_info.dli_fname) { + return decode_to_dict(dict, key, libpython_info.dli_fname); } #endif +#endif + return PyDict_SetItemString(dict, key, Py_None) == 0; } diff --git a/configure b/configure index 6e1b393a3ece68..bb77c558abda5a 100755 --- a/configure +++ b/configure @@ -19001,6 +19001,12 @@ if test "x$ac_cv_func_ctermid" = xyes then : printf "%s\n" "#define HAVE_CTERMID 1" >>confdefs.h +fi +ac_fn_c_check_func "$LINENO" "dladdr" "ac_cv_func_dladdr" +if test "x$ac_cv_func_dladdr" = xyes +then : + printf "%s\n" "#define HAVE_DLADDR 1" >>confdefs.h + fi ac_fn_c_check_func "$LINENO" "dup" "ac_cv_func_dup" if test "x$ac_cv_func_dup" = xyes diff --git a/configure.ac b/configure.ac index 6d44be8959865d..653cd3f6c531b6 100644 --- a/configure.ac +++ b/configure.ac @@ -5128,7 +5128,7 @@ fi # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ - copy_file_range ctermid dup dup3 execv explicit_bzero explicit_memset \ + copy_file_range ctermid dladdr dup dup3 execv explicit_bzero explicit_memset \ faccessat fchmod fchmodat fchown fchownat fdopendir fdwalk fexecve \ fork fork1 fpathconf fstatat ftime ftruncate futimens futimes futimesat \ gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ diff --git a/pyconfig.h.in b/pyconfig.h.in index 874b98dc96585a..aaf52168c3d39d 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -286,6 +286,9 @@ /* Define if you have the 'dirfd' function or macro. */ #undef HAVE_DIRFD +/* Define to 1 if you have the 'dladdr' function. */ +#undef HAVE_DLADDR + /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H From 74a517181a9bb65a1f6da149af7427a9fcb3add3 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 8 Jan 2025 15:04:54 +0300 Subject: [PATCH 232/376] gh-128615: Cover pickling of `ParamSpecArgs` and `ParamSpecKwargs` (#128616) --- Lib/test/test_typing.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index a75dac4a6102bf..45ba7611059e43 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -5182,6 +5182,18 @@ class C(B[int]): x = pickle.loads(z) self.assertEqual(s, x) + # Test ParamSpec args and kwargs + global PP + PP = ParamSpec('PP') + for thing in [PP.args, PP.kwargs]: + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(thing=thing, proto=proto): + self.assertEqual( + pickle.loads(pickle.dumps(thing, proto)), + thing, + ) + del PP + def test_copy_and_deepcopy(self): T = TypeVar('T') class Node(Generic[T]): ... From c22302ecea8c22379a41cd77158748d5fcd91352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:50:40 +0100 Subject: [PATCH 233/376] gh-111178: fix UBSan failures in `Python/hamt.c` (GH-128247) * fix UBSan failures for `PyHamtObject` * fix UBSan failures for `PyHamtNode_Array` * fix UBSan failures for `PyHamtNode_Collision` * fix UBSan failures for `PyHamtNode_Bitmap` --- Python/hamt.c | 174 ++++++++++++++++++++++++-------------------------- 1 file changed, 84 insertions(+), 90 deletions(-) diff --git a/Python/hamt.c b/Python/hamt.c index cfd211f4541446..ed43a0449d7a01 100644 --- a/Python/hamt.c +++ b/Python/hamt.c @@ -319,6 +319,8 @@ typedef struct { Py_ssize_t a_count; } PyHamtNode_Array; +#define _PyHamtNode_Array_CAST(op) ((PyHamtNode_Array *)(op)) + typedef struct { PyObject_VAR_HEAD @@ -326,6 +328,8 @@ typedef struct { PyObject *c_array[1]; } PyHamtNode_Collision; +#define _PyHamtNode_Collision_CAST(op) ((PyHamtNode_Collision *)(op)) + static PyHamtObject * hamt_alloc(void); @@ -479,6 +483,8 @@ _hamt_dump_ident(PyUnicodeWriter *writer, int level) #endif /* Py_DEBUG */ /////////////////////////////////// Bitmap Node +#define _PyHamtNode_Bitmap_CAST(op) ((PyHamtNode_Bitmap *)(op)) + static PyHamtNode * hamt_node_bitmap_new(Py_ssize_t size) @@ -1083,30 +1089,27 @@ hamt_node_bitmap_find(PyHamtNode_Bitmap *self, } static int -hamt_node_bitmap_traverse(PyHamtNode_Bitmap *self, visitproc visit, void *arg) +hamt_node_bitmap_traverse(PyObject *op, visitproc visit, void *arg) { /* Bitmap's tp_traverse */ - - Py_ssize_t i; - - for (i = Py_SIZE(self); --i >= 0; ) { + PyHamtNode_Bitmap *self = _PyHamtNode_Bitmap_CAST(op); + for (Py_ssize_t i = Py_SIZE(self); --i >= 0;) { Py_VISIT(self->b_array[i]); } - return 0; } static void -hamt_node_bitmap_dealloc(PyHamtNode_Bitmap *self) +hamt_node_bitmap_dealloc(PyObject *self) { /* Bitmap's tp_dealloc */ - Py_ssize_t len = Py_SIZE(self); - Py_ssize_t i; + PyHamtNode_Bitmap *node = _PyHamtNode_Bitmap_CAST(self); + Py_ssize_t i, len = Py_SIZE(self); - if (Py_SIZE(self) == 0) { + if (len == 0) { /* The empty node is statically allocated. */ - assert(self == &_Py_SINGLETON(hamt_bitmap_node_empty)); + assert(node == &_Py_SINGLETON(hamt_bitmap_node_empty)); #ifdef Py_DEBUG _Py_FatalRefcountError("deallocating the empty hamt node bitmap singleton"); #else @@ -1120,11 +1123,11 @@ hamt_node_bitmap_dealloc(PyHamtNode_Bitmap *self) if (len > 0) { i = len; while (--i >= 0) { - Py_XDECREF(self->b_array[i]); + Py_XDECREF(node->b_array[i]); } } - Py_TYPE(self)->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free(self); Py_TRASHCAN_END } @@ -1489,38 +1492,30 @@ hamt_node_collision_find(PyHamtNode_Collision *self, static int -hamt_node_collision_traverse(PyHamtNode_Collision *self, - visitproc visit, void *arg) +hamt_node_collision_traverse(PyObject *op, visitproc visit, void *arg) { /* Collision's tp_traverse */ - - Py_ssize_t i; - - for (i = Py_SIZE(self); --i >= 0; ) { + PyHamtNode_Collision *self = _PyHamtNode_Collision_CAST(op); + for (Py_ssize_t i = Py_SIZE(self); --i >= 0; ) { Py_VISIT(self->c_array[i]); } - return 0; } static void -hamt_node_collision_dealloc(PyHamtNode_Collision *self) +hamt_node_collision_dealloc(PyObject *self) { /* Collision's tp_dealloc */ - Py_ssize_t len = Py_SIZE(self); - PyObject_GC_UnTrack(self); Py_TRASHCAN_BEGIN(self, hamt_node_collision_dealloc) - if (len > 0) { - + PyHamtNode_Collision *node = _PyHamtNode_Collision_CAST(self); while (--len >= 0) { - Py_XDECREF(self->c_array[len]); + Py_XDECREF(node->c_array[len]); } } - - Py_TYPE(self)->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free(self); Py_TRASHCAN_END } @@ -1868,35 +1863,27 @@ hamt_node_array_find(PyHamtNode_Array *self, } static int -hamt_node_array_traverse(PyHamtNode_Array *self, - visitproc visit, void *arg) +hamt_node_array_traverse(PyObject *op, visitproc visit, void *arg) { /* Array's tp_traverse */ - - Py_ssize_t i; - - for (i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) { + PyHamtNode_Array *self = _PyHamtNode_Array_CAST(op); + for (Py_ssize_t i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) { Py_VISIT(self->a_array[i]); } - return 0; } static void -hamt_node_array_dealloc(PyHamtNode_Array *self) +hamt_node_array_dealloc(PyObject *self) { /* Array's tp_dealloc */ - - Py_ssize_t i; - PyObject_GC_UnTrack(self); Py_TRASHCAN_BEGIN(self, hamt_node_array_dealloc) - - for (i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) { - Py_XDECREF(self->a_array[i]); + PyHamtNode_Array *obj = _PyHamtNode_Array_CAST(self); + for (Py_ssize_t i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) { + Py_XDECREF(obj->a_array[i]); } - - Py_TYPE(self)->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free(self); Py_TRASHCAN_END } @@ -2605,6 +2592,8 @@ static PyObject * hamt_dump(PyHamtObject *self); #endif +#define _PyHamtObject_CAST(op) ((PyHamtObject *)(op)) + static PyObject * hamt_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) @@ -2613,24 +2602,27 @@ hamt_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } static int -hamt_tp_clear(PyHamtObject *self) +hamt_tp_clear(PyObject *op) { + PyHamtObject *self = _PyHamtObject_CAST(op); Py_CLEAR(self->h_root); return 0; } static int -hamt_tp_traverse(PyHamtObject *self, visitproc visit, void *arg) +hamt_tp_traverse(PyObject *op, visitproc visit, void *arg) { + PyHamtObject *self = _PyHamtObject_CAST(op); Py_VISIT(self->h_root); return 0; } static void -hamt_tp_dealloc(PyHamtObject *self) +hamt_tp_dealloc(PyObject *self) { - if (self == _empty_hamt) { + PyHamtObject *obj = _PyHamtObject_CAST(self); + if (obj == _empty_hamt) { /* The empty one is statically allocated. */ #ifdef Py_DEBUG _Py_FatalRefcountError("deallocating the empty hamt singleton"); @@ -2640,8 +2632,8 @@ hamt_tp_dealloc(PyHamtObject *self) } PyObject_GC_UnTrack(self); - if (self->h_weakreflist != NULL) { - PyObject_ClearWeakRefs((PyObject*)self); + if (obj->h_weakreflist != NULL) { + PyObject_ClearWeakRefs(self); } (void)hamt_tp_clear(self); Py_TYPE(self)->tp_free(self); @@ -2673,16 +2665,18 @@ hamt_tp_richcompare(PyObject *v, PyObject *w, int op) } static int -hamt_tp_contains(PyHamtObject *self, PyObject *key) +hamt_tp_contains(PyObject *op, PyObject *key) { PyObject *val; + PyHamtObject *self = _PyHamtObject_CAST(op); return _PyHamt_Find(self, key, &val); } static PyObject * -hamt_tp_subscript(PyHamtObject *self, PyObject *key) +hamt_tp_subscript(PyObject *op, PyObject *key) { PyObject *val; + PyHamtObject *self = _PyHamtObject_CAST(op); hamt_find_t res = hamt_find(self, key, &val); switch (res) { case F_ERROR: @@ -2698,19 +2692,21 @@ hamt_tp_subscript(PyHamtObject *self, PyObject *key) } static Py_ssize_t -hamt_tp_len(PyHamtObject *self) +hamt_tp_len(PyObject *op) { + PyHamtObject *self = _PyHamtObject_CAST(op); return _PyHamt_Len(self); } static PyObject * -hamt_tp_iter(PyHamtObject *self) +hamt_tp_iter(PyObject *op) { + PyHamtObject *self = _PyHamtObject_CAST(op); return _PyHamt_NewIterKeys(self); } static PyObject * -hamt_py_set(PyHamtObject *self, PyObject *args) +hamt_py_set(PyObject *op, PyObject *args) { PyObject *key; PyObject *val; @@ -2719,11 +2715,12 @@ hamt_py_set(PyHamtObject *self, PyObject *args) return NULL; } + PyHamtObject *self = _PyHamtObject_CAST(op); return (PyObject *)_PyHamt_Assoc(self, key, val); } static PyObject * -hamt_py_get(PyHamtObject *self, PyObject *args) +hamt_py_get(PyObject *op, PyObject *args) { PyObject *key; PyObject *def = NULL; @@ -2733,6 +2730,7 @@ hamt_py_get(PyHamtObject *self, PyObject *args) } PyObject *val = NULL; + PyHamtObject *self = _PyHamtObject_CAST(op); hamt_find_t res = hamt_find(self, key, &val); switch (res) { case F_ERROR: @@ -2750,67 +2748,63 @@ hamt_py_get(PyHamtObject *self, PyObject *args) } static PyObject * -hamt_py_delete(PyHamtObject *self, PyObject *key) +hamt_py_delete(PyObject *op, PyObject *key) { + PyHamtObject *self = _PyHamtObject_CAST(op); return (PyObject *)_PyHamt_Without(self, key); } static PyObject * -hamt_py_items(PyHamtObject *self, PyObject *args) +hamt_py_items(PyObject *op, PyObject *args) { + PyHamtObject *self = _PyHamtObject_CAST(op); return _PyHamt_NewIterItems(self); } static PyObject * -hamt_py_values(PyHamtObject *self, PyObject *args) +hamt_py_values(PyObject *op, PyObject *args) { + PyHamtObject *self = _PyHamtObject_CAST(op); return _PyHamt_NewIterValues(self); } static PyObject * -hamt_py_keys(PyHamtObject *self, PyObject *Py_UNUSED(args)) +hamt_py_keys(PyObject *op, PyObject *Py_UNUSED(args)) { + PyHamtObject *self = _PyHamtObject_CAST(op); return _PyHamt_NewIterKeys(self); } #ifdef Py_DEBUG static PyObject * -hamt_py_dump(PyHamtObject *self, PyObject *Py_UNUSED(args)) +hamt_py_dump(PyObject *op, PyObject *Py_UNUSED(args)) { + PyHamtObject *self = _PyHamtObject_CAST(op); return hamt_dump(self); } #endif static PyMethodDef PyHamt_methods[] = { - {"set", _PyCFunction_CAST(hamt_py_set), METH_VARARGS, NULL}, - {"get", _PyCFunction_CAST(hamt_py_get), METH_VARARGS, NULL}, - {"delete", _PyCFunction_CAST(hamt_py_delete), METH_O, NULL}, - {"items", _PyCFunction_CAST(hamt_py_items), METH_NOARGS, NULL}, - {"keys", _PyCFunction_CAST(hamt_py_keys), METH_NOARGS, NULL}, - {"values", _PyCFunction_CAST(hamt_py_values), METH_NOARGS, NULL}, + {"set", hamt_py_set, METH_VARARGS, NULL}, + {"get", hamt_py_get, METH_VARARGS, NULL}, + {"delete", hamt_py_delete, METH_O, NULL}, + {"items", hamt_py_items, METH_NOARGS, NULL}, + {"keys", hamt_py_keys, METH_NOARGS, NULL}, + {"values", hamt_py_values, METH_NOARGS, NULL}, #ifdef Py_DEBUG - {"__dump__", _PyCFunction_CAST(hamt_py_dump), METH_NOARGS, NULL}, + {"__dump__", hamt_py_dump, METH_NOARGS, NULL}, #endif {NULL, NULL} }; static PySequenceMethods PyHamt_as_sequence = { - 0, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - 0, /* sq_item */ - 0, /* sq_slice */ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - (objobjproc)hamt_tp_contains, /* sq_contains */ - 0, /* sq_inplace_concat */ - 0, /* sq_inplace_repeat */ + .sq_contains = hamt_tp_contains, }; static PyMappingMethods PyHamt_as_mapping = { - (lenfunc)hamt_tp_len, /* mp_length */ - (binaryfunc)hamt_tp_subscript, /* mp_subscript */ + .mp_length = hamt_tp_len, + .mp_subscript = hamt_tp_subscript, }; PyTypeObject _PyHamt_Type = { @@ -2820,13 +2814,13 @@ PyTypeObject _PyHamt_Type = { .tp_methods = PyHamt_methods, .tp_as_mapping = &PyHamt_as_mapping, .tp_as_sequence = &PyHamt_as_sequence, - .tp_iter = (getiterfunc)hamt_tp_iter, - .tp_dealloc = (destructor)hamt_tp_dealloc, + .tp_iter = hamt_tp_iter, + .tp_dealloc = hamt_tp_dealloc, .tp_getattro = PyObject_GenericGetAttr, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_richcompare = hamt_tp_richcompare, - .tp_traverse = (traverseproc)hamt_tp_traverse, - .tp_clear = (inquiry)hamt_tp_clear, + .tp_traverse = hamt_tp_traverse, + .tp_clear = hamt_tp_clear, .tp_new = hamt_tp_new, .tp_weaklistoffset = offsetof(PyHamtObject, h_weakreflist), .tp_hash = PyObject_HashNotImplemented, @@ -2841,10 +2835,10 @@ PyTypeObject _PyHamt_ArrayNode_Type = { "hamt_array_node", sizeof(PyHamtNode_Array), 0, - .tp_dealloc = (destructor)hamt_node_array_dealloc, + .tp_dealloc = hamt_node_array_dealloc, .tp_getattro = PyObject_GenericGetAttr, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, - .tp_traverse = (traverseproc)hamt_node_array_traverse, + .tp_traverse = hamt_node_array_traverse, .tp_free = PyObject_GC_Del, .tp_hash = PyObject_HashNotImplemented, }; @@ -2854,10 +2848,10 @@ PyTypeObject _PyHamt_BitmapNode_Type = { "hamt_bitmap_node", sizeof(PyHamtNode_Bitmap) - sizeof(PyObject *), sizeof(PyObject *), - .tp_dealloc = (destructor)hamt_node_bitmap_dealloc, + .tp_dealloc = hamt_node_bitmap_dealloc, .tp_getattro = PyObject_GenericGetAttr, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, - .tp_traverse = (traverseproc)hamt_node_bitmap_traverse, + .tp_traverse = hamt_node_bitmap_traverse, .tp_free = PyObject_GC_Del, .tp_hash = PyObject_HashNotImplemented, }; @@ -2867,10 +2861,10 @@ PyTypeObject _PyHamt_CollisionNode_Type = { "hamt_collision_node", sizeof(PyHamtNode_Collision) - sizeof(PyObject *), sizeof(PyObject *), - .tp_dealloc = (destructor)hamt_node_collision_dealloc, + .tp_dealloc = hamt_node_collision_dealloc, .tp_getattro = PyObject_GenericGetAttr, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, - .tp_traverse = (traverseproc)hamt_node_collision_traverse, + .tp_traverse = hamt_node_collision_traverse, .tp_free = PyObject_GC_Del, .tp_hash = PyObject_HashNotImplemented, }; From 1da0901894d7c0d56ebce97cd0c16aeffb64adcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:52:27 +0100 Subject: [PATCH 234/376] gh-111178: fix UBSan failures in `Python/context.c` (GH-128242) * fix UBSan failures for `PyContext` * fix UBSan failures for `PyContextVar` * fix UBSan failures for `PyContextToken` * fix UBSan failures for `_PyContextTokenMissing` --- Python/context.c | 121 ++++++++++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 53 deletions(-) diff --git a/Python/context.c b/Python/context.c index 95aa82206270f9..f30b59b9443bbf 100644 --- a/Python/context.c +++ b/Python/context.c @@ -419,6 +419,9 @@ class _contextvars.Context "PyContext *" "&PyContext_Type" /*[clinic end generated code: output=da39a3ee5e6b4b0d input=bdf87f8e0cb580e8]*/ +#define _PyContext_CAST(op) ((PyContext *)(op)) + + static inline PyContext * _context_alloc(void) { @@ -513,28 +516,30 @@ context_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } static int -context_tp_clear(PyContext *self) +context_tp_clear(PyObject *op) { + PyContext *self = _PyContext_CAST(op); Py_CLEAR(self->ctx_prev); Py_CLEAR(self->ctx_vars); return 0; } static int -context_tp_traverse(PyContext *self, visitproc visit, void *arg) +context_tp_traverse(PyObject *op, visitproc visit, void *arg) { + PyContext *self = _PyContext_CAST(op); Py_VISIT(self->ctx_prev); Py_VISIT(self->ctx_vars); return 0; } static void -context_tp_dealloc(PyContext *self) +context_tp_dealloc(PyObject *self) { _PyObject_GC_UNTRACK(self); - - if (self->ctx_weakreflist != NULL) { - PyObject_ClearWeakRefs((PyObject*)self); + PyContext *ctx = _PyContext_CAST(self); + if (ctx->ctx_weakreflist != NULL) { + PyObject_ClearWeakRefs(self); } (void)context_tp_clear(self); @@ -542,8 +547,9 @@ context_tp_dealloc(PyContext *self) } static PyObject * -context_tp_iter(PyContext *self) +context_tp_iter(PyObject *op) { + PyContext *self = _PyContext_CAST(op); return _PyHamt_NewIterKeys(self->ctx_vars); } @@ -575,18 +581,20 @@ context_tp_richcompare(PyObject *v, PyObject *w, int op) } static Py_ssize_t -context_tp_len(PyContext *self) +context_tp_len(PyObject *op) { + PyContext *self = _PyContext_CAST(op); return _PyHamt_Len(self->ctx_vars); } static PyObject * -context_tp_subscript(PyContext *self, PyObject *key) +context_tp_subscript(PyObject *op, PyObject *key) { if (context_check_key_type(key)) { return NULL; } PyObject *val = NULL; + PyContext *self = _PyContext_CAST(op); int found = _PyHamt_Find(self->ctx_vars, key, &val); if (found < 0) { return NULL; @@ -599,12 +607,13 @@ context_tp_subscript(PyContext *self, PyObject *key) } static int -context_tp_contains(PyContext *self, PyObject *key) +context_tp_contains(PyObject *op, PyObject *key) { if (context_check_key_type(key)) { return -1; } PyObject *val = NULL; + PyContext *self = _PyContext_CAST(op); return _PyHamt_Find(self->ctx_vars, key, &val); } @@ -701,7 +710,7 @@ _contextvars_Context_copy_impl(PyContext *self) static PyObject * -context_run(PyContext *self, PyObject *const *args, +context_run(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyThreadState *ts = _PyThreadState_GET(); @@ -712,14 +721,14 @@ context_run(PyContext *self, PyObject *const *args, return NULL; } - if (_PyContext_Enter(ts, (PyObject *)self)) { + if (_PyContext_Enter(ts, self)) { return NULL; } PyObject *call_result = _PyObject_VectorcallTstate( ts, args[0], args + 1, nargs - 1, kwnames); - if (_PyContext_Exit(ts, (PyObject *)self)) { + if (_PyContext_Exit(ts, self)) { Py_XDECREF(call_result); return NULL; } @@ -739,21 +748,12 @@ static PyMethodDef PyContext_methods[] = { }; static PySequenceMethods PyContext_as_sequence = { - 0, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - 0, /* sq_item */ - 0, /* sq_slice */ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - (objobjproc)context_tp_contains, /* sq_contains */ - 0, /* sq_inplace_concat */ - 0, /* sq_inplace_repeat */ + .sq_contains = context_tp_contains }; static PyMappingMethods PyContext_as_mapping = { - (lenfunc)context_tp_len, /* mp_length */ - (binaryfunc)context_tp_subscript, /* mp_subscript */ + .mp_length = context_tp_len, + .mp_subscript = context_tp_subscript }; PyTypeObject PyContext_Type = { @@ -763,13 +763,13 @@ PyTypeObject PyContext_Type = { .tp_methods = PyContext_methods, .tp_as_mapping = &PyContext_as_mapping, .tp_as_sequence = &PyContext_as_sequence, - .tp_iter = (getiterfunc)context_tp_iter, - .tp_dealloc = (destructor)context_tp_dealloc, + .tp_iter = context_tp_iter, + .tp_dealloc = context_tp_dealloc, .tp_getattro = PyObject_GenericGetAttr, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_richcompare = context_tp_richcompare, - .tp_traverse = (traverseproc)context_tp_traverse, - .tp_clear = (inquiry)context_tp_clear, + .tp_traverse = context_tp_traverse, + .tp_clear = context_tp_clear, .tp_new = context_tp_new, .tp_weaklistoffset = offsetof(PyContext, ctx_weakreflist), .tp_hash = PyObject_HashNotImplemented, @@ -909,6 +909,9 @@ class _contextvars.ContextVar "PyContextVar *" "&PyContextVar_Type" /*[clinic end generated code: output=da39a3ee5e6b4b0d input=445da935fa8883c3]*/ +#define _PyContextVar_CAST(op) ((PyContextVar *)(op)) + + static PyObject * contextvar_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -926,8 +929,9 @@ contextvar_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } static int -contextvar_tp_clear(PyContextVar *self) +contextvar_tp_clear(PyObject *op) { + PyContextVar *self = _PyContextVar_CAST(op); Py_CLEAR(self->var_name); Py_CLEAR(self->var_default); #ifndef Py_GIL_DISABLED @@ -939,15 +943,16 @@ contextvar_tp_clear(PyContextVar *self) } static int -contextvar_tp_traverse(PyContextVar *self, visitproc visit, void *arg) +contextvar_tp_traverse(PyObject *op, visitproc visit, void *arg) { + PyContextVar *self = _PyContextVar_CAST(op); Py_VISIT(self->var_name); Py_VISIT(self->var_default); return 0; } static void -contextvar_tp_dealloc(PyContextVar *self) +contextvar_tp_dealloc(PyObject *self) { PyObject_GC_UnTrack(self); (void)contextvar_tp_clear(self); @@ -955,14 +960,16 @@ contextvar_tp_dealloc(PyContextVar *self) } static Py_hash_t -contextvar_tp_hash(PyContextVar *self) +contextvar_tp_hash(PyObject *op) { + PyContextVar *self = _PyContextVar_CAST(op); return self->var_hash; } static PyObject * -contextvar_tp_repr(PyContextVar *self) +contextvar_tp_repr(PyObject *op) { + PyContextVar *self = _PyContextVar_CAST(op); // Estimation based on the shortest name and default value, // but maximize the pointer size. // "" @@ -1106,15 +1113,15 @@ PyTypeObject PyContextVar_Type = { sizeof(PyContextVar), .tp_methods = PyContextVar_methods, .tp_members = PyContextVar_members, - .tp_dealloc = (destructor)contextvar_tp_dealloc, + .tp_dealloc = contextvar_tp_dealloc, .tp_getattro = PyObject_GenericGetAttr, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, - .tp_traverse = (traverseproc)contextvar_tp_traverse, - .tp_clear = (inquiry)contextvar_tp_clear, + .tp_traverse = contextvar_tp_traverse, + .tp_clear = contextvar_tp_clear, .tp_new = contextvar_tp_new, .tp_free = PyObject_GC_Del, - .tp_hash = (hashfunc)contextvar_tp_hash, - .tp_repr = (reprfunc)contextvar_tp_repr, + .tp_hash = contextvar_tp_hash, + .tp_repr = contextvar_tp_repr, }; @@ -1129,6 +1136,9 @@ class _contextvars.Token "PyContextToken *" "&PyContextToken_Type" /*[clinic end generated code: output=da39a3ee5e6b4b0d input=338a5e2db13d3f5b]*/ +#define _PyContextToken_CAST(op) ((PyContextToken *)(op)) + + static PyObject * token_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -1138,8 +1148,9 @@ token_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } static int -token_tp_clear(PyContextToken *self) +token_tp_clear(PyObject *op) { + PyContextToken *self = _PyContextToken_CAST(op); Py_CLEAR(self->tok_ctx); Py_CLEAR(self->tok_var); Py_CLEAR(self->tok_oldval); @@ -1147,8 +1158,9 @@ token_tp_clear(PyContextToken *self) } static int -token_tp_traverse(PyContextToken *self, visitproc visit, void *arg) +token_tp_traverse(PyObject *op, visitproc visit, void *arg) { + PyContextToken *self = _PyContextToken_CAST(op); Py_VISIT(self->tok_ctx); Py_VISIT(self->tok_var); Py_VISIT(self->tok_oldval); @@ -1156,7 +1168,7 @@ token_tp_traverse(PyContextToken *self, visitproc visit, void *arg) } static void -token_tp_dealloc(PyContextToken *self) +token_tp_dealloc(PyObject *self) { PyObject_GC_UnTrack(self); (void)token_tp_clear(self); @@ -1164,8 +1176,9 @@ token_tp_dealloc(PyContextToken *self) } static PyObject * -token_tp_repr(PyContextToken *self) +token_tp_repr(PyObject *op) { + PyContextToken *self = _PyContextToken_CAST(op); PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); if (writer == NULL) { return NULL; @@ -1195,14 +1208,16 @@ token_tp_repr(PyContextToken *self) } static PyObject * -token_get_var(PyContextToken *self, void *Py_UNUSED(ignored)) +token_get_var(PyObject *op, void *Py_UNUSED(ignored)) { + PyContextToken *self = _PyContextToken_CAST(op); return Py_NewRef(self->tok_var);; } static PyObject * -token_get_old_value(PyContextToken *self, void *Py_UNUSED(ignored)) +token_get_old_value(PyObject *op, void *Py_UNUSED(ignored)) { + PyContextToken *self = _PyContextToken_CAST(op); if (self->tok_oldval == NULL) { return get_token_missing(); } @@ -1211,8 +1226,8 @@ token_get_old_value(PyContextToken *self, void *Py_UNUSED(ignored)) } static PyGetSetDef PyContextTokenType_getsetlist[] = { - {"var", (getter)token_get_var, NULL, NULL}, - {"old_value", (getter)token_get_old_value, NULL, NULL}, + {"var", token_get_var, NULL, NULL}, + {"old_value", token_get_old_value, NULL, NULL}, {NULL} }; @@ -1228,15 +1243,15 @@ PyTypeObject PyContextToken_Type = { sizeof(PyContextToken), .tp_methods = PyContextTokenType_methods, .tp_getset = PyContextTokenType_getsetlist, - .tp_dealloc = (destructor)token_tp_dealloc, + .tp_dealloc = token_tp_dealloc, .tp_getattro = PyObject_GenericGetAttr, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, - .tp_traverse = (traverseproc)token_tp_traverse, - .tp_clear = (inquiry)token_tp_clear, + .tp_traverse = token_tp_traverse, + .tp_clear = token_tp_clear, .tp_new = token_tp_new, .tp_free = PyObject_GC_Del, .tp_hash = PyObject_HashNotImplemented, - .tp_repr = (reprfunc)token_tp_repr, + .tp_repr = token_tp_repr, }; static PyContextToken * @@ -1270,7 +1285,7 @@ context_token_missing_tp_repr(PyObject *self) } static void -context_token_missing_tp_dealloc(_PyContextTokenMissing *Py_UNUSED(self)) +context_token_missing_tp_dealloc(PyObject *Py_UNUSED(self)) { #ifdef Py_DEBUG /* The singleton is statically allocated. */ @@ -1285,7 +1300,7 @@ PyTypeObject _PyContextTokenMissing_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "Token.MISSING", sizeof(_PyContextTokenMissing), - .tp_dealloc = (destructor)context_token_missing_tp_dealloc, + .tp_dealloc = context_token_missing_tp_dealloc, .tp_getattro = PyObject_GenericGetAttr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_repr = context_token_missing_tp_repr, From 845d924efb2a144120421260e62b9c4c9726fc69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:55:04 +0100 Subject: [PATCH 235/376] gh-111178: fix UBSan failures in `Objects/capsule.c` (GH-128239) fix UBSan failures for `PyCapsule` --- Objects/capsule.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Objects/capsule.c b/Objects/capsule.c index 28965e0f21b7a0..16ae65905ef5ac 100644 --- a/Objects/capsule.c +++ b/Objects/capsule.c @@ -18,6 +18,8 @@ typedef struct { } PyCapsule; +#define _PyCapsule_CAST(op) ((PyCapsule *)(op)) + static int _is_legal_capsule(PyObject *op, const char *invalid_capsule) @@ -284,7 +286,7 @@ PyCapsule_Import(const char *name, int no_block) static void capsule_dealloc(PyObject *op) { - PyCapsule *capsule = (PyCapsule *)op; + PyCapsule *capsule = _PyCapsule_CAST(op); PyObject_GC_UnTrack(op); if (capsule->destructor) { capsule->destructor(op); @@ -296,7 +298,7 @@ capsule_dealloc(PyObject *op) static PyObject * capsule_repr(PyObject *o) { - PyCapsule *capsule = (PyCapsule *)o; + PyCapsule *capsule = _PyCapsule_CAST(o); const char *name; const char *quote; @@ -314,28 +316,27 @@ capsule_repr(PyObject *o) static int -capsule_traverse(PyCapsule *capsule, visitproc visit, void *arg) +capsule_traverse(PyObject *self, visitproc visit, void *arg) { // Capsule object is only tracked by the GC // if _PyCapsule_SetTraverse() is called, but // this can still be manually triggered by gc.get_referents() - + PyCapsule *capsule = _PyCapsule_CAST(self); if (capsule->traverse_func != NULL) { - return capsule->traverse_func((PyObject*)capsule, visit, arg); + return capsule->traverse_func(self, visit, arg); } - return 0; } static int -capsule_clear(PyCapsule *capsule) +capsule_clear(PyObject *self) { // Capsule object is only tracked by the GC // if _PyCapsule_SetTraverse() is called + PyCapsule *capsule = _PyCapsule_CAST(self); assert(capsule->clear_func != NULL); - - return capsule->clear_func((PyObject*)capsule); + return capsule->clear_func(self); } @@ -358,8 +359,8 @@ PyTypeObject PyCapsule_Type = { .tp_dealloc = capsule_dealloc, .tp_repr = capsule_repr, .tp_doc = PyCapsule_Type__doc__, - .tp_traverse = (traverseproc)capsule_traverse, - .tp_clear = (inquiry)capsule_clear, + .tp_traverse = capsule_traverse, + .tp_clear = capsule_clear, }; From 004f9fd1f22643100aa8163cc9f7bcde7df54973 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 8 Jan 2025 09:00:11 -0800 Subject: [PATCH 236/376] Remove unnecessary LIST_TO_TUPLE conversions (GH-126558) --- Lib/test/test_peepholer.py | 51 ++++++++++++++++++++++++++++++++++++++ Python/flowgraph.c | 7 ++++++ 2 files changed, 58 insertions(+) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index c7da151dce3b37..b5b2b350e77a3b 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -1193,5 +1193,56 @@ def get_insts(lno1, lno2, op1, op2): ] self.cfg_optimization_test(insts, expected_insts, consts=list(range(5))) + def test_list_to_tuple_get_iter(self): + # for _ in (*foo, *bar) -> for _ in [*foo, *bar] + INTRINSIC_LIST_TO_TUPLE = 6 + insts = [ + ("BUILD_LIST", 0, 1), + ("LOAD_FAST", 0, 2), + ("LIST_EXTEND", 1, 3), + ("LOAD_FAST", 1, 4), + ("LIST_EXTEND", 1, 5), + ("CALL_INTRINSIC_1", INTRINSIC_LIST_TO_TUPLE, 6), + ("GET_ITER", None, 7), + top := self.Label(), + ("FOR_ITER", end := self.Label(), 8), + ("STORE_FAST", 2, 9), + ("JUMP", top, 10), + end, + ("END_FOR", None, 11), + ("POP_TOP", None, 12), + ("LOAD_CONST", 0, 13), + ("RETURN_VALUE", None, 14), + ] + expected_insts = [ + ("BUILD_LIST", 0, 1), + ("LOAD_FAST", 0, 2), + ("LIST_EXTEND", 1, 3), + ("LOAD_FAST", 1, 4), + ("LIST_EXTEND", 1, 5), + ("NOP", None, 6), # ("CALL_INTRINSIC_1", INTRINSIC_LIST_TO_TUPLE, 6), + ("GET_ITER", None, 7), + top := self.Label(), + ("FOR_ITER", end := self.Label(), 8), + ("STORE_FAST", 2, 9), + ("JUMP", top, 10), + end, + ("END_FOR", None, 11), + ("POP_TOP", None, 12), + ("LOAD_CONST", 0, 13), + ("RETURN_VALUE", None, 14), + ] + self.cfg_optimization_test(insts, expected_insts, consts=[None]) + + def test_list_to_tuple_get_iter_is_safe(self): + a, b = [], [] + for item in (*(items := [0, 1, 2, 3]),): + a.append(item) + b.append(items.pop()) + self.assertEqual(a, [0, 1, 2, 3]) + self.assertEqual(b, [3, 2, 1, 0]) + self.assertEqual(items, []) + + if __name__ == "__main__": unittest.main() diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 017216aadd1f01..24561c1ee04db9 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -4,6 +4,7 @@ #include "Python.h" #include "pycore_flowgraph.h" #include "pycore_compile.h" +#include "pycore_intrinsics.h" #include "pycore_pymem.h" // _PyMem_IsPtrFreed() #include "pycore_opcode_utils.h" @@ -1874,6 +1875,12 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) continue; } break; + case CALL_INTRINSIC_1: + // for _ in (*foo, *bar) -> for _ in [*foo, *bar] + if (oparg == INTRINSIC_LIST_TO_TUPLE && nextop == GET_ITER) { + INSTR_SET_OP0(inst, NOP); + } + break; } } From 34e840f9ddd9c04991cf004e2594c6a1e0e278d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns=20=F0=9F=87=B5=F0=9F=87=B8?= Date: Wed, 8 Jan 2025 20:23:16 +0000 Subject: [PATCH 237/376] GH-66409: check if exec_prefix is the same as prefix before searching executable_dir (#127974) --- .../2024-12-15-21-11-26.gh-issue-66409.wv109z.rst | 3 +++ Modules/getpath.py | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-21-11-26.gh-issue-66409.wv109z.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-21-11-26.gh-issue-66409.wv109z.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-21-11-26.gh-issue-66409.wv109z.rst new file mode 100644 index 00000000000000..0d70ad06c97968 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-21-11-26.gh-issue-66409.wv109z.rst @@ -0,0 +1,3 @@ +During the :ref:`path initialization `, we now check if +``base_exec_prefix`` is the same as ``base_prefix`` before falling back to +searching the Python interpreter directory. diff --git a/Modules/getpath.py b/Modules/getpath.py index c34101e720851d..be2210345afbda 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -625,6 +625,8 @@ def search_up(prefix, *landmarks, test=isfile): # 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 prefix and isdir(joinpath(prefix, PLATSTDLIB_LANDMARK)): + 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: From 58a9133fc2caea15d11116f0b5bd6832374cb88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns=20=F0=9F=87=B5=F0=9F=87=B8?= Date: Wed, 8 Jan 2025 21:10:43 +0000 Subject: [PATCH 238/376] acks: add myself (#128652) --- Misc/ACKS | 1 + 1 file changed, 1 insertion(+) diff --git a/Misc/ACKS b/Misc/ACKS index d7585c16c8169c..deda334bee7417 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1038,6 +1038,7 @@ Erno Kuusela Kabir Kwatra Ross Lagerwall Cameron Laird +Filipe Laíns Loïc Lajeanne Alexander Lakeev David Lam From a1284e97979ff73ad72ad06c796b904137950576 Mon Sep 17 00:00:00 2001 From: "Tomas R." Date: Thu, 9 Jan 2025 00:38:02 +0100 Subject: [PATCH 239/376] gh-97850: Remove the mention of removal from `ResourceReader` docs (#128602) Remove the mention of removal from ResourceReader docs --- Doc/library/importlib.resources.abc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/importlib.resources.abc.rst b/Doc/library/importlib.resources.abc.rst index 54995ddbfbca12..4085bdf6598d98 100644 --- a/Doc/library/importlib.resources.abc.rst +++ b/Doc/library/importlib.resources.abc.rst @@ -43,7 +43,7 @@ :const:`None`. An object compatible with this ABC should only be returned when the specified module is a package. - .. deprecated-removed:: 3.12 3.14 + .. deprecated:: 3.12 Use :class:`importlib.resources.abc.TraversableResources` instead. .. abstractmethod:: open_resource(resource) From 4685401845ba3e2ab8c9f4a9a10aa2969b11985f Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 9 Jan 2025 14:49:05 +0530 Subject: [PATCH 240/376] gh-79149: document reentrant safety of `loop.call_soon_threadsafe` (#128662) Co-authored-by: Thomas Grainger --- Doc/library/asyncio-eventloop.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index ccb362d8c31ddf..bfc0d16f023e5e 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -246,6 +246,9 @@ Scheduling callbacks another thread, this function *must* be used, since :meth:`call_soon` is not thread-safe. + This function is safe to be called from a reentrant context or signal handler, + however, it is not safe or fruitful to use the returned handle in such contexts. + Raises :exc:`RuntimeError` if called on a loop that's been closed. This can happen on a secondary thread when the main application is shutting down. From 1439b81928f1b52c5a0ac7fd81fdd66afd5f72da Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 9 Jan 2025 11:10:28 +0100 Subject: [PATCH 241/376] gh-128629: Add Py_PACK_VERSION and Py_PACK_FULL_VERSION (GH-128630) --- Doc/c-api/apiabiversion.rst | 102 +++++++++++++----- Doc/data/stable_abi.dat | 2 + Doc/whatsnew/3.14.rst | 4 + Include/patchlevel.h | 26 +++-- Include/pymacro.h | 9 ++ Lib/test/test_capi/test_misc.py | 43 ++++++++ Lib/test/test_stable_abi_ctypes.py | 2 + ...-01-08-13-13-18.gh-issue-128629.gSmzyl.rst | 2 + Misc/stable_abi.toml | 4 + Modules/Setup.stdlib.in | 2 +- Modules/_testlimitedcapi.c | 3 + Modules/_testlimitedcapi/clinic/version.c.h | 93 ++++++++++++++++ Modules/_testlimitedcapi/parts.h | 1 + Modules/_testlimitedcapi/version.c | 77 +++++++++++++ PC/python3dll.c | 2 + PCbuild/_testlimitedcapi.vcxproj | 1 + PCbuild/_testlimitedcapi.vcxproj.filters | 1 + Python/modsupport.c | 17 +++ 18 files changed, 358 insertions(+), 33 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-01-08-13-13-18.gh-issue-128629.gSmzyl.rst create mode 100644 Modules/_testlimitedcapi/clinic/version.c.h create mode 100644 Modules/_testlimitedcapi/version.c diff --git a/Doc/c-api/apiabiversion.rst b/Doc/c-api/apiabiversion.rst index f6c8284daeacb0..96050f59bd5250 100644 --- a/Doc/c-api/apiabiversion.rst +++ b/Doc/c-api/apiabiversion.rst @@ -6,9 +6,13 @@ API and ABI Versioning *********************** + +Build-time version constants +---------------------------- + CPython exposes its version number in the following macros. -Note that these correspond to the version code is **built** with, -not necessarily the version used at **run time**. +Note that these correspond to the version code is **built** with. +See :c:var:`Py_Version` for the version used at **run time**. See :ref:`stable` for a discussion of API and ABI stability across versions. @@ -37,37 +41,83 @@ See :ref:`stable` for a discussion of API and ABI stability across versions. .. c:macro:: PY_VERSION_HEX The Python version number encoded in a single integer. + See :c:func:`Py_PACK_FULL_VERSION` for the encoding details. - The underlying version information can be found by treating it as a 32 bit - number in the following manner: - - +-------+-------------------------+-------------------------+--------------------------+ - | Bytes | Bits (big endian order) | Meaning | Value for ``3.4.1a2`` | - +=======+=========================+=========================+==========================+ - | 1 | 1-8 | ``PY_MAJOR_VERSION`` | ``0x03`` | - +-------+-------------------------+-------------------------+--------------------------+ - | 2 | 9-16 | ``PY_MINOR_VERSION`` | ``0x04`` | - +-------+-------------------------+-------------------------+--------------------------+ - | 3 | 17-24 | ``PY_MICRO_VERSION`` | ``0x01`` | - +-------+-------------------------+-------------------------+--------------------------+ - | 4 | 25-28 | ``PY_RELEASE_LEVEL`` | ``0xA`` | - + +-------------------------+-------------------------+--------------------------+ - | | 29-32 | ``PY_RELEASE_SERIAL`` | ``0x2`` | - +-------+-------------------------+-------------------------+--------------------------+ + Use this for numeric comparisons, for example, + ``#if PY_VERSION_HEX >= ...``. - Thus ``3.4.1a2`` is hexversion ``0x030401a2`` and ``3.10.0`` is - hexversion ``0x030a00f0``. - Use this for numeric comparisons, e.g. ``#if PY_VERSION_HEX >= ...``. - - This version is also available via the symbol :c:var:`Py_Version`. +Run-time version +---------------- .. c:var:: const unsigned long Py_Version - The Python runtime version number encoded in a single constant integer, with - the same format as the :c:macro:`PY_VERSION_HEX` macro. + The Python runtime version number encoded in a single constant integer. + See :c:func:`Py_PACK_FULL_VERSION` for the encoding details. This contains the Python version used at run time. + Use this for numeric comparisons, for example, ``if (Py_Version >= ...)``. + .. versionadded:: 3.11 -All the given macros are defined in :source:`Include/patchlevel.h`. + +Bit-packing macros +------------------ + +.. c:function:: uint32_t Py_PACK_FULL_VERSION(int major, int minor, int micro, int release_level, int release_serial) + + Return the given version, encoded as a single 32-bit integer with + the following structure: + + +------------------+-------+----------------+-----------+--------------------------+ + | | No. | | | Example values | + | | of | | +-------------+------------+ + | Argument | bits | Bit mask | Bit shift | ``3.4.1a2`` | ``3.10.0`` | + +==================+=======+================+===========+=============+============+ + | *major* | 8 | ``0xFF000000`` | 24 | ``0x03`` | ``0x03`` | + +------------------+-------+----------------+-----------+-------------+------------+ + | *minor* | 8 | ``0x00FF0000`` | 16 | ``0x04`` | ``0x0A`` | + +------------------+-------+----------------+-----------+-------------+------------+ + | *micro* | 8 | ``0x0000FF00`` | 8 | ``0x01`` | ``0x00`` | + +------------------+-------+----------------+-----------+-------------+------------+ + | *release_level* | 4 | ``0x000000F0`` | 4 | ``0xA`` | ``0xF`` | + +------------------+-------+----------------+-----------+-------------+------------+ + | *release_serial* | 4 | ``0x0000000F`` | 0 | ``0x2`` | ``0x0`` | + +------------------+-------+----------------+-----------+-------------+------------+ + + For example: + + +-------------+------------------------------------+-----------------+ + | Version | ``Py_PACK_FULL_VERSION`` arguments | Encoded version | + +=============+====================================+=================+ + | ``3.4.1a2`` | ``(3, 4, 1, 0xA, 2)`` | ``0x030401a2`` | + +-------------+------------------------------------+-----------------+ + | ``3.10.0`` | ``(3, 10, 0, 0xF, 0)`` | ``0x030a00f0`` | + +-------------+------------------------------------+-----------------+ + + Out-of range bits in the arguments are ignored. + That is, the macro can be defined as: + + .. code-block:: c + + #ifndef Py_PACK_FULL_VERSION + #define Py_PACK_FULL_VERSION(X, Y, Z, LEVEL, SERIAL) ( \ + (((X) & 0xff) << 24) | \ + (((Y) & 0xff) << 16) | \ + (((Z) & 0xff) << 8) | \ + (((LEVEL) & 0xf) << 4) | \ + (((SERIAL) & 0xf) << 0)) + #endif + + ``Py_PACK_FULL_VERSION`` is primarily a macro, intended for use in + ``#if`` directives, but it is also available as an exported function. + + .. versionadded:: 3.14 + +.. c:function:: uint32_t Py_PACK_VERSION(int major, int minor) + + Equivalent to ``Py_PACK_FULL_VERSION(major, minor, 0, 0, 0)``. + The result does not correspond to any Python release, but is useful + in numeric comparisons. + + .. versionadded:: 3.14 diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 6f9d27297e8f65..c15f82603aa944 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -883,6 +883,8 @@ func,Py_Main,3.2,, func,Py_MakePendingCalls,3.2,, func,Py_NewInterpreter,3.2,, func,Py_NewRef,3.10,, +func,Py_PACK_FULL_VERSION,3.14,, +func,Py_PACK_VERSION,3.14,, func,Py_REFCNT,3.14,, func,Py_ReprEnter,3.2,, func,Py_ReprLeave,3.2,, diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 16851b4e63ea2c..72abfebd46f2b9 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1243,6 +1243,10 @@ New features file. (Contributed by Victor Stinner in :gh:`127350`.) +* Add macros :c:func:`Py_PACK_VERSION` and :c:func:`Py_PACK_FULL_VERSION` for + bit-packing Python version numbers. + (Contributed by Petr Viktorin in :gh:`128629`.) + Porting to Python 3.14 ---------------------- diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 6d4f719fcde5a8..eca2ca08a4337c 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -1,4 +1,5 @@ - +#ifndef _Py_PATCHLEVEL_H +#define _Py_PATCHLEVEL_H /* Python version identification scheme. When the major or minor version changes, the VERSION variable in @@ -26,10 +27,23 @@ #define PY_VERSION "3.14.0a3+" /*--end constants--*/ + +#define _Py_PACK_FULL_VERSION(X, Y, Z, LEVEL, SERIAL) ( \ + (((X) & 0xff) << 24) | \ + (((Y) & 0xff) << 16) | \ + (((Z) & 0xff) << 8) | \ + (((LEVEL) & 0xf) << 4) | \ + (((SERIAL) & 0xf) << 0)) + /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. Use this for numeric comparisons, e.g. #if PY_VERSION_HEX >= ... */ -#define PY_VERSION_HEX ((PY_MAJOR_VERSION << 24) | \ - (PY_MINOR_VERSION << 16) | \ - (PY_MICRO_VERSION << 8) | \ - (PY_RELEASE_LEVEL << 4) | \ - (PY_RELEASE_SERIAL << 0)) +#define PY_VERSION_HEX _Py_PACK_FULL_VERSION( \ + PY_MAJOR_VERSION, \ + PY_MINOR_VERSION, \ + PY_MICRO_VERSION, \ + PY_RELEASE_LEVEL, \ + PY_RELEASE_SERIAL) + +// Public Py_PACK_VERSION is declared in pymacro.h; it needs . + +#endif //_Py_PATCHLEVEL_H diff --git a/Include/pymacro.h b/Include/pymacro.h index e0378f9d27a048..a82f347866e8d0 100644 --- a/Include/pymacro.h +++ b/Include/pymacro.h @@ -190,4 +190,13 @@ // "comparison of unsigned expression in '< 0' is always false". #define _Py_IS_TYPE_SIGNED(type) ((type)(-1) <= 0) +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030E0000 // 3.14 +// Version helpers. These are primarily macros, but have exported equivalents. +PyAPI_FUNC(uint32_t) Py_PACK_FULL_VERSION(int x, int y, int z, int level, int serial); +PyAPI_FUNC(uint32_t) Py_PACK_VERSION(int x, int y); +#define Py_PACK_FULL_VERSION _Py_PACK_FULL_VERSION +#define Py_PACK_VERSION(X, Y) Py_PACK_FULL_VERSION(X, Y, 0, 0, 0) +#endif // Py_LIMITED_API < 3.14 + + #endif /* Py_PYMACRO_H */ diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index ada30181aeeca9..b62bc4c2ecd980 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -3335,6 +3335,49 @@ def run(self): self.assertEqual(len(set(py_thread_ids)), len(py_thread_ids), py_thread_ids) +class TestVersions(unittest.TestCase): + full_cases = ( + (3, 4, 1, 0xA, 2, 0x030401a2), + (3, 10, 0, 0xF, 0, 0x030a00f0), + (0x103, 0x10B, 0xFF00, -1, 0xF0, 0x030b00f0), # test masking + ) + xy_cases = ( + (3, 4, 0x03040000), + (3, 10, 0x030a0000), + (0x103, 0x10B, 0x030b0000), # test masking + ) + + def test_pack_full_version(self): + for *args, expected in self.full_cases: + with self.subTest(hexversion=hex(expected)): + result = _testlimitedcapi.pack_full_version(*args) + self.assertEqual(result, expected) + + def test_pack_version(self): + for *args, expected in self.xy_cases: + with self.subTest(hexversion=hex(expected)): + result = _testlimitedcapi.pack_version(*args) + self.assertEqual(result, expected) + + def test_pack_full_version_ctypes(self): + ctypes = import_helper.import_module('ctypes') + ctypes_func = ctypes.pythonapi.Py_PACK_FULL_VERSION + ctypes_func.restype = ctypes.c_uint32 + ctypes_func.argtypes = [ctypes.c_int] * 5 + for *args, expected in self.full_cases: + with self.subTest(hexversion=hex(expected)): + result = ctypes_func(*args) + self.assertEqual(result, expected) + + def test_pack_version_ctypes(self): + ctypes = import_helper.import_module('ctypes') + ctypes_func = ctypes.pythonapi.Py_PACK_VERSION + ctypes_func.restype = ctypes.c_uint32 + ctypes_func.argtypes = [ctypes.c_int] * 2 + for *args, expected in self.xy_cases: + with self.subTest(hexversion=hex(expected)): + result = ctypes_func(*args) + self.assertEqual(result, expected) if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index fa08dc6a25b0ea..f3724ce6d4d15a 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -901,6 +901,8 @@ def test_windows_feature_macros(self): "Py_MakePendingCalls", "Py_NewInterpreter", "Py_NewRef", + "Py_PACK_FULL_VERSION", + "Py_PACK_VERSION", "Py_REFCNT", "Py_ReprEnter", "Py_ReprLeave", diff --git a/Misc/NEWS.d/next/C_API/2025-01-08-13-13-18.gh-issue-128629.gSmzyl.rst b/Misc/NEWS.d/next/C_API/2025-01-08-13-13-18.gh-issue-128629.gSmzyl.rst new file mode 100644 index 00000000000000..cde5bf38f754b6 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-01-08-13-13-18.gh-issue-128629.gSmzyl.rst @@ -0,0 +1,2 @@ +Add macros :c:func:`Py_PACK_VERSION` and :c:func:`Py_PACK_FULL_VERSION` for +bit-packing Python version numbers. diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index f9e51f0683c965..276526a1b6908e 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -2540,3 +2540,7 @@ added = '3.14' [function.PyType_Freeze] added = '3.14' +[function.Py_PACK_FULL_VERSION] + added = '3.14' +[function.Py_PACK_VERSION] + added = '3.14' diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 52c0f883d383db..b7357f41768a2f 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -163,7 +163,7 @@ @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c @MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c -@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c +@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c diff --git a/Modules/_testlimitedcapi.c b/Modules/_testlimitedcapi.c index ba83a23117b2a5..bcc69a339ec5c4 100644 --- a/Modules/_testlimitedcapi.c +++ b/Modules/_testlimitedcapi.c @@ -83,5 +83,8 @@ PyInit__testlimitedcapi(void) if (_PyTestLimitedCAPI_Init_VectorcallLimited(mod) < 0) { return NULL; } + if (_PyTestLimitedCAPI_Init_Version(mod) < 0) { + return NULL; + } return mod; } diff --git a/Modules/_testlimitedcapi/clinic/version.c.h b/Modules/_testlimitedcapi/clinic/version.c.h new file mode 100644 index 00000000000000..096c7dd528b332 --- /dev/null +++ b/Modules/_testlimitedcapi/clinic/version.c.h @@ -0,0 +1,93 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +PyDoc_STRVAR(_testlimitedcapi_pack_full_version__doc__, +"pack_full_version($module, major, minor, micro, level, serial, /)\n" +"--\n" +"\n"); + +#define _TESTLIMITEDCAPI_PACK_FULL_VERSION_METHODDEF \ + {"pack_full_version", (PyCFunction)(void(*)(void))_testlimitedcapi_pack_full_version, METH_FASTCALL, _testlimitedcapi_pack_full_version__doc__}, + +static PyObject * +_testlimitedcapi_pack_full_version_impl(PyObject *module, int major, + int minor, int micro, int level, + int serial); + +static PyObject * +_testlimitedcapi_pack_full_version(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int major; + int minor; + int micro; + int level; + int serial; + + if (nargs != 5) { + PyErr_Format(PyExc_TypeError, "pack_full_version expected 5 arguments, got %zd", nargs); + goto exit; + } + major = PyLong_AsInt(args[0]); + if (major == -1 && PyErr_Occurred()) { + goto exit; + } + minor = PyLong_AsInt(args[1]); + if (minor == -1 && PyErr_Occurred()) { + goto exit; + } + micro = PyLong_AsInt(args[2]); + if (micro == -1 && PyErr_Occurred()) { + goto exit; + } + level = PyLong_AsInt(args[3]); + if (level == -1 && PyErr_Occurred()) { + goto exit; + } + serial = PyLong_AsInt(args[4]); + if (serial == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = _testlimitedcapi_pack_full_version_impl(module, major, minor, micro, level, serial); + +exit: + return return_value; +} + +PyDoc_STRVAR(_testlimitedcapi_pack_version__doc__, +"pack_version($module, major, minor, /)\n" +"--\n" +"\n"); + +#define _TESTLIMITEDCAPI_PACK_VERSION_METHODDEF \ + {"pack_version", (PyCFunction)(void(*)(void))_testlimitedcapi_pack_version, METH_FASTCALL, _testlimitedcapi_pack_version__doc__}, + +static PyObject * +_testlimitedcapi_pack_version_impl(PyObject *module, int major, int minor); + +static PyObject * +_testlimitedcapi_pack_version(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int major; + int minor; + + if (nargs != 2) { + PyErr_Format(PyExc_TypeError, "pack_version expected 2 arguments, got %zd", nargs); + goto exit; + } + major = PyLong_AsInt(args[0]); + if (major == -1 && PyErr_Occurred()) { + goto exit; + } + minor = PyLong_AsInt(args[1]); + if (minor == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = _testlimitedcapi_pack_version_impl(module, major, minor); + +exit: + return return_value; +} +/*[clinic end generated code: output=aed3e226da77f2d2 input=a9049054013a1b77]*/ diff --git a/Modules/_testlimitedcapi/parts.h b/Modules/_testlimitedcapi/parts.h index 4107b150c5b4e0..56d566b66565a3 100644 --- a/Modules/_testlimitedcapi/parts.h +++ b/Modules/_testlimitedcapi/parts.h @@ -40,5 +40,6 @@ int _PyTestLimitedCAPI_Init_Sys(PyObject *module); int _PyTestLimitedCAPI_Init_Tuple(PyObject *module); int _PyTestLimitedCAPI_Init_Unicode(PyObject *module); int _PyTestLimitedCAPI_Init_VectorcallLimited(PyObject *module); +int _PyTestLimitedCAPI_Init_Version(PyObject *module); #endif // Py_TESTLIMITEDCAPI_PARTS_H diff --git a/Modules/_testlimitedcapi/version.c b/Modules/_testlimitedcapi/version.c new file mode 100644 index 00000000000000..57cd6e4e928ea3 --- /dev/null +++ b/Modules/_testlimitedcapi/version.c @@ -0,0 +1,77 @@ +/* Test version macros in the limited API */ + +#include "pyconfig.h" // Py_GIL_DISABLED +#ifndef Py_GIL_DISABLED +# define Py_LIMITED_API 0x030e0000 // Added in 3.14 +#endif + +#include "parts.h" +#include "clinic/version.c.h" +#include + +/*[clinic input] +module _testlimitedcapi +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=2700057f9c1135ba]*/ + +/*[clinic input] +_testlimitedcapi.pack_full_version + + major: int + minor: int + micro: int + level: int + serial: int + / +[clinic start generated code]*/ + +static PyObject * +_testlimitedcapi_pack_full_version_impl(PyObject *module, int major, + int minor, int micro, int level, + int serial) +/*[clinic end generated code: output=b87a1e9805648861 input=2a304423be61d2ac]*/ +{ + uint32_t macro_result = Py_PACK_FULL_VERSION( + major, minor, micro, level, serial); +#undef Py_PACK_FULL_VERSION + uint32_t func_result = Py_PACK_FULL_VERSION( + major, minor, micro, level, serial); + + assert(macro_result == func_result); + return PyLong_FromUnsignedLong((unsigned long)func_result); +} + +/*[clinic input] +_testlimitedcapi.pack_version + + major: int + minor: int + / +[clinic start generated code]*/ + +static PyObject * +_testlimitedcapi_pack_version_impl(PyObject *module, int major, int minor) +/*[clinic end generated code: output=771247bbd06e7883 input=3e39e9dcbc09e86a]*/ +{ + uint32_t macro_result = Py_PACK_VERSION(major, minor); +#undef Py_PACK_VERSION + uint32_t func_result = Py_PACK_VERSION(major, minor); + + assert(macro_result == func_result); + return PyLong_FromUnsignedLong((unsigned long)func_result); +} + +static PyMethodDef TestMethods[] = { + _TESTLIMITEDCAPI_PACK_FULL_VERSION_METHODDEF + _TESTLIMITEDCAPI_PACK_VERSION_METHODDEF + {NULL}, +}; + +int +_PyTestLimitedCAPI_Init_Version(PyObject *m) +{ + if (PyModule_AddFunctions(m, TestMethods) < 0) { + return -1; + } + return 0; +} diff --git a/PC/python3dll.c b/PC/python3dll.c index 8657ddb9fa5155..84b3c735240b73 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -81,6 +81,8 @@ EXPORT_FUNC(Py_Main) EXPORT_FUNC(Py_MakePendingCalls) EXPORT_FUNC(Py_NewInterpreter) EXPORT_FUNC(Py_NewRef) +EXPORT_FUNC(Py_PACK_FULL_VERSION) +EXPORT_FUNC(Py_PACK_VERSION) EXPORT_FUNC(Py_REFCNT) EXPORT_FUNC(Py_ReprEnter) EXPORT_FUNC(Py_ReprLeave) diff --git a/PCbuild/_testlimitedcapi.vcxproj b/PCbuild/_testlimitedcapi.vcxproj index 846e027e10c7fa..0ea5edba3aa9a7 100644 --- a/PCbuild/_testlimitedcapi.vcxproj +++ b/PCbuild/_testlimitedcapi.vcxproj @@ -112,6 +112,7 @@ + diff --git a/PCbuild/_testlimitedcapi.vcxproj.filters b/PCbuild/_testlimitedcapi.vcxproj.filters index 57be2e2fc5b950..b379090eb599f5 100644 --- a/PCbuild/_testlimitedcapi.vcxproj.filters +++ b/PCbuild/_testlimitedcapi.vcxproj.filters @@ -28,6 +28,7 @@ + diff --git a/Python/modsupport.c b/Python/modsupport.c index 0fb7783345c78e..517dc971f88c87 100644 --- a/Python/modsupport.c +++ b/Python/modsupport.c @@ -648,3 +648,20 @@ PyModule_AddType(PyObject *module, PyTypeObject *type) return PyModule_AddObjectRef(module, name, (PyObject *)type); } + + +/* Exported functions for version helper macros */ + +#undef Py_PACK_FULL_VERSION +uint32_t +Py_PACK_FULL_VERSION(int x, int y, int z, int level, int serial) +{ + return _Py_PACK_FULL_VERSION(x, y, z, level, serial); +} + +#undef Py_PACK_VERSION +uint32_t +Py_PACK_VERSION(int x, int y) +{ + return Py_PACK_FULL_VERSION(x, y, 0, 0, 0); +} From b2adf556747d080f04b53ba4063b627c2dbe41d1 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 9 Jan 2025 15:40:45 +0530 Subject: [PATCH 242/376] gh-126137: improve docs for `loop.add_reader` and `loop.add_writer` (#128666) --- Doc/library/asyncio-eventloop.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index bfc0d16f023e5e..072ab206f25e4f 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -970,6 +970,9 @@ Watching file descriptors invoke *callback* with the specified arguments once *fd* is available for reading. + Any preexisting callback registered for *fd* is cancelled and replaced by + *callback*. + .. method:: loop.remove_reader(fd) Stop monitoring the *fd* file descriptor for read availability. Returns @@ -981,6 +984,9 @@ Watching file descriptors invoke *callback* with the specified arguments once *fd* is available for writing. + Any preexisting callback registered for *fd* is cancelled and replaced by + *callback*. + Use :func:`functools.partial` :ref:`to pass keyword arguments ` to *callback*. From 4322a318ea98ceeb95d88b7ae6b5cfa3572d2069 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 9 Jan 2025 17:50:12 +0530 Subject: [PATCH 243/376] gh-124433: fix docs for `asyncio.Queue.task_done` (#128669) --- Doc/library/asyncio-queue.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/asyncio-queue.rst b/Doc/library/asyncio-queue.rst index 61991bf2f4ed1d..066edd424d150e 100644 --- a/Doc/library/asyncio-queue.rst +++ b/Doc/library/asyncio-queue.rst @@ -115,11 +115,11 @@ Queue .. method:: task_done() - Indicate that a formerly enqueued task is complete. + Indicate that a formerly enqueued work item is complete. Used by queue consumers. For each :meth:`~Queue.get` used to - fetch a task, a subsequent call to :meth:`task_done` tells the - queue that the processing on the task is complete. + fetch a work item, a subsequent call to :meth:`task_done` tells the + queue that the processing on the work item is complete. If a :meth:`join` is currently blocking, it will resume when all items have been processed (meaning that a :meth:`task_done` From ea39c8b08d8f025273bfa5b7a95f7b5984dc1e86 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 9 Jan 2025 08:54:44 -0500 Subject: [PATCH 244/376] gh-118915: Document `PyUnstable_InterpreterState_GetMainModule` (GH-128483) --- Doc/c-api/init.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index dd63dd013e32dc..97996a6f69dd22 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1492,6 +1492,17 @@ All of the following functions must be called after :c:func:`Py_Initialize`. .. versionadded:: 3.8 + +.. c:function:: PyObject* PyUnstable_InterpreterState_GetMainModule(PyInterpreterState *interp) + + Return a :term:`strong reference` to the ``__main__`` `module object `_ + for the given interpreter. + + The caller must hold the GIL. + + .. versionadded:: 3.13 + + .. c:type:: PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) Type of a frame evaluation function. From 43ac9f505903ba806aa6a5d93e6a67beb04bebc4 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 9 Jan 2025 17:25:03 +0300 Subject: [PATCH 245/376] gh-128673: Increase coverage of `typing.get_type_hints` (#128674) --- Lib/test/test_typing.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 45ba7611059e43..1c86b95e8e5c29 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -7152,6 +7152,25 @@ class C: self.assertEqual(get_type_hints(C, format=annotationlib.Format.STRING), {'x': 'undefined'}) + def test_get_type_hints_format_function(self): + def func(x: undefined) -> undefined: ... + + # VALUE + with self.assertRaises(NameError): + get_type_hints(func) + with self.assertRaises(NameError): + get_type_hints(func, format=annotationlib.Format.VALUE) + + # FORWARDREF + self.assertEqual( + get_type_hints(func, format=annotationlib.Format.FORWARDREF), + {'x': ForwardRef('undefined'), 'return': ForwardRef('undefined')}, + ) + + # STRING + self.assertEqual(get_type_hints(func, format=annotationlib.Format.STRING), + {'x': 'undefined', 'return': 'undefined'}) + class GetUtilitiesTestCase(TestCase): def test_get_origin(self): From b725297cee9e5608b709f3c7291d974c97f68fff Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 9 Jan 2025 18:15:13 +0300 Subject: [PATCH 246/376] gh-128661: Fix `typing.evaluate_forward_ref` not showing deprecation (#128663) gh-128661: Fix `typing.evaluate_forward_ref` not showing deprecataion --- Lib/test/test_typing.py | 46 +++++++++++++++++++ Lib/typing.py | 2 +- ...-01-09-12-06-52.gh-issue-128661.ixx_0z.rst | 2 + 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-09-12-06-52.gh-issue-128661.ixx_0z.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 1c86b95e8e5c29..c51ee763890af2 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -45,6 +45,7 @@ import textwrap import typing import weakref +import warnings import types from test.support import captured_stderr, cpython_only, infinite_recursion, requires_docstrings, import_helper, run_code @@ -7273,6 +7274,51 @@ class C(Generic[T]): pass self.assertEqual(get_args(Unpack[tuple[Unpack[Ts]]]), (tuple[Unpack[Ts]],)) +class EvaluateForwardRefTests(BaseTestCase): + def test_evaluate_forward_ref(self): + int_ref = ForwardRef('int') + missing = ForwardRef('missing') + self.assertIs( + typing.evaluate_forward_ref(int_ref, type_params=()), + int, + ) + self.assertIs( + typing.evaluate_forward_ref( + int_ref, type_params=(), format=annotationlib.Format.FORWARDREF, + ), + int, + ) + self.assertIs( + typing.evaluate_forward_ref( + missing, type_params=(), format=annotationlib.Format.FORWARDREF, + ), + missing, + ) + self.assertEqual( + typing.evaluate_forward_ref( + int_ref, type_params=(), format=annotationlib.Format.STRING, + ), + 'int', + ) + + def test_evaluate_forward_ref_no_type_params(self): + ref = ForwardRef('int') + with self.assertWarnsRegex( + DeprecationWarning, + ( + "Failing to pass a value to the 'type_params' parameter " + "of 'typing.evaluate_forward_ref' is deprecated, " + "as it leads to incorrect behaviour" + ), + ): + typing.evaluate_forward_ref(ref) + + # No warnings when `type_params` is passed: + with warnings.catch_warnings(record=True) as w: + typing.evaluate_forward_ref(ref, type_params=()) + self.assertEqual(w, []) + + class CollectionsAbcTests(BaseTestCase): def test_hashable(self): diff --git a/Lib/typing.py b/Lib/typing.py index e69b485422cbd2..66570db7a5bd74 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1024,7 +1024,7 @@ def evaluate_forward_ref( owner=None, globals=None, locals=None, - type_params=None, + type_params=_sentinel, format=annotationlib.Format.VALUE, _recursive_guard=frozenset(), ): diff --git a/Misc/NEWS.d/next/Library/2025-01-09-12-06-52.gh-issue-128661.ixx_0z.rst b/Misc/NEWS.d/next/Library/2025-01-09-12-06-52.gh-issue-128661.ixx_0z.rst new file mode 100644 index 00000000000000..6c52b3dcc0ed00 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-09-12-06-52.gh-issue-128661.ixx_0z.rst @@ -0,0 +1,2 @@ +Fixes :func:`typing.evaluate_forward_ref` not showing deprecation when +``type_params`` arg is not passed. From 7dc41ad6a7826ffc675f088972de96624917696e Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 9 Jan 2025 21:26:00 +0530 Subject: [PATCH 247/376] gh-128002: fix `asyncio.all_tasks` against concurrent deallocations of tasks (#128541) --- Include/internal/pycore_object.h | 2 +- Modules/_asynciomodule.c | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index d7d68f938a9f0a..e26cb7673f939c 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -120,7 +120,7 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalRefcountErrorFunc( PyAPI_DATA(Py_ssize_t) _Py_RefTotal; extern void _Py_AddRefTotal(PyThreadState *, Py_ssize_t); -extern void _Py_IncRefTotal(PyThreadState *); +extern PyAPI_FUNC(void) _Py_IncRefTotal(PyThreadState *); extern void _Py_DecRefTotal(PyThreadState *); # define _Py_DEC_REFTOTAL(interp) \ diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index b8b184af04a7cb..48f0ef95934fa4 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -3772,11 +3772,20 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) llist_for_each_safe(node, &state->asyncio_tasks_head) { TaskObj *task = llist_data(node, TaskObj, task_node); - if (PyList_Append(tasks, (PyObject *)task) < 0) { - Py_DECREF(tasks); - Py_DECREF(loop); - err = 1; - break; + // The linked list holds borrowed references to task + // as such it is possible that the task is concurrently + // deallocated while added to this list. + // To protect against concurrent deallocations, + // we first try to incref the task which would fail + // if it is concurrently getting deallocated in another thread, + // otherwise it gets added to the list. + if (_Py_TryIncref((PyObject *)task)) { + if (_PyList_AppendTakeRef((PyListObject *)tasks, (PyObject *)task) < 0) { + Py_DECREF(tasks); + Py_DECREF(loop); + err = 1; + break; + } } } ASYNCIO_STATE_UNLOCK(state); From 8af57810946c216b3e18c94c8f0ee3c0c96566a9 Mon Sep 17 00:00:00 2001 From: dgpb <3577712+dg-pb@users.noreply.github.com> Date: Thu, 9 Jan 2025 21:32:00 +0200 Subject: [PATCH 248/376] gh-128650: Fix incorrect statement in partial documentation (gh-128651) --- Doc/library/functools.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index 69d9d81c848124..8ad5f48c9e5286 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -368,8 +368,8 @@ The :mod:`functools` module defines the following functions: If :data:`Placeholder` sentinels are present in *args*, they will be filled first when :func:`!partial` is called. This makes it possible to pre-fill any positional - argument with a call to :func:`!partial`; without :data:`!Placeholder`, only the - first positional argument can be pre-filled. + argument with a call to :func:`!partial`; without :data:`!Placeholder`, + only the chosen number of leading positional arguments can be pre-filled. If any :data:`!Placeholder` sentinels are present, all must be filled at call time: From 087bb48acac997c06e69dae25bae2dd75194b980 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 9 Jan 2025 22:45:35 +0300 Subject: [PATCH 249/376] gh-127196: Fix crash in `_interpreters`, when `shared` had invalid encodings (#127220) --- Lib/test/test__interpreters.py | 13 ++++++++++++- .../2024-11-24-14-53-35.gh-issue-127196.8CBkUa.rst | 2 ++ Modules/_interpretersmodule.c | 7 ++++++- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-24-14-53-35.gh-issue-127196.8CBkUa.rst diff --git a/Lib/test/test__interpreters.py b/Lib/test/test__interpreters.py index bf3165e2341949..fd444f1f06ce48 100644 --- a/Lib/test/test__interpreters.py +++ b/Lib/test/test__interpreters.py @@ -557,7 +557,7 @@ def setUp(self): self.id = _interpreters.create() def test_signatures(self): - # for method in ['exec', 'run_string', 'run_func']: + # See https://github.com/python/cpython/issues/126654 msg = "expected 'shared' to be a dict" with self.assertRaisesRegex(TypeError, msg): _interpreters.exec(self.id, 'a', 1) @@ -568,6 +568,17 @@ def test_signatures(self): with self.assertRaisesRegex(TypeError, msg): _interpreters.run_func(self.id, lambda: None, shared=1) + def test_invalid_shared_encoding(self): + # See https://github.com/python/cpython/issues/127196 + bad_shared = {"\uD82A": 0} + msg = 'surrogates not allowed' + with self.assertRaisesRegex(UnicodeEncodeError, msg): + _interpreters.exec(self.id, 'a', shared=bad_shared) + with self.assertRaisesRegex(UnicodeEncodeError, msg): + _interpreters.run_string(self.id, 'a', shared=bad_shared) + with self.assertRaisesRegex(UnicodeEncodeError, msg): + _interpreters.run_func(self.id, lambda: None, shared=bad_shared) + class RunStringTests(TestBase): diff --git a/Misc/NEWS.d/next/Library/2024-11-24-14-53-35.gh-issue-127196.8CBkUa.rst b/Misc/NEWS.d/next/Library/2024-11-24-14-53-35.gh-issue-127196.8CBkUa.rst new file mode 100644 index 00000000000000..471f64d185deab --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-24-14-53-35.gh-issue-127196.8CBkUa.rst @@ -0,0 +1,2 @@ +Fix crash when dict with keys in invalid encoding were passed to several +functions in ``_interpreters`` module. diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index a36823c4bb982b..fcd0baf696f943 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -459,7 +459,12 @@ _run_in_interpreter(PyInterpreterState *interp, // Prep and switch interpreters. if (_PyXI_Enter(&session, interp, shareables) < 0) { - assert(!PyErr_Occurred()); + if (PyErr_Occurred()) { + // If an error occured at this step, it means that interp + // was not prepared and switched. + return -1; + } + // Now, apply the error from another interpreter: PyObject *excinfo = _PyXI_ApplyError(session.error); if (excinfo != NULL) { *p_excinfo = excinfo; From c1417487e98e270d614965ed78ff9439044b65a6 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 9 Jan 2025 19:59:10 -0500 Subject: [PATCH 250/376] gh-128691: Use deferred reference counting on `_thread._local` (#128693) This change, along with the LOAD_ATTR specializations, makes the "thread_local_read" micro benchmark in Tools/ftscalingbench/ftscalingbench.py scale well to multiple threads. --- Modules/_threadmodule.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 2cbdfeb09b95ae..d19ae326bd6b48 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1414,6 +1414,10 @@ local_new(PyTypeObject *type, PyObject *args, PyObject *kw) return NULL; } + // gh-128691: Use deferred reference counting for thread-locals to avoid + // contention on the shared object. + _PyObject_SetDeferredRefcount((PyObject *)self); + self->args = Py_XNewRef(args); self->kw = Py_XNewRef(kw); From 2fcdc8488c32d18f4567f797094068a994777f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 10 Jan 2025 04:32:53 +0100 Subject: [PATCH 251/376] gh-126862: Use `Py_ssize_t` instead of `int` when processing the number of super-classes (#127523) --- .../2024-12-02-18-15-37.gh-issue-126862.fdIK7T.rst | 2 ++ Objects/typeobject.c | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-02-18-15-37.gh-issue-126862.fdIK7T.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-02-18-15-37.gh-issue-126862.fdIK7T.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-02-18-15-37.gh-issue-126862.fdIK7T.rst new file mode 100644 index 00000000000000..d930c2963e3632 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-02-18-15-37.gh-issue-126862.fdIK7T.rst @@ -0,0 +1,2 @@ +Fix a possible overflow when a class inherits from an absurd number of +super-classes. Reported by Valery Fedorenko. Patch by Bénédikt Tran. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 7f95b519561e68..680846f1c0b865 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2860,7 +2860,7 @@ vectorcall_maybe(PyThreadState *tstate, PyObject *name, */ static int -tail_contains(PyObject *tuple, int whence, PyObject *o) +tail_contains(PyObject *tuple, Py_ssize_t whence, PyObject *o) { Py_ssize_t j, size; size = PyTuple_GET_SIZE(tuple); @@ -2923,7 +2923,7 @@ check_duplicates(PyObject *tuple) */ static void -set_mro_error(PyObject **to_merge, Py_ssize_t to_merge_size, int *remain) +set_mro_error(PyObject **to_merge, Py_ssize_t to_merge_size, Py_ssize_t *remain) { Py_ssize_t i, n, off; char buf[1000]; @@ -2978,13 +2978,13 @@ pmerge(PyObject *acc, PyObject **to_merge, Py_ssize_t to_merge_size) { int res = 0; Py_ssize_t i, j, empty_cnt; - int *remain; + Py_ssize_t *remain; /* remain stores an index into each sublist of to_merge. remain[i] is the index of the next base in to_merge[i] that is not included in acc. */ - remain = PyMem_New(int, to_merge_size); + remain = PyMem_New(Py_ssize_t, to_merge_size); if (remain == NULL) { PyErr_NoMemory(); return -1; From 65b484db97b170af0f0e50655c4dcc6b9b1af8e9 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 10 Jan 2025 13:25:02 +0300 Subject: [PATCH 252/376] gh-128694: Fix `(env changed)` error in `test_inspect` (#128702) --- Lib/test/test_inspect/test_inspect.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 345a57a5cfee2d..6457bc523de460 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -1,4 +1,5 @@ from annotationlib import Format, ForwardRef +import asyncio import builtins import collections import copy @@ -2791,6 +2792,10 @@ async def number_asyncgen(): async def asyncTearDown(self): await self.asyncgen.aclose() + @classmethod + def tearDownClass(cls): + asyncio._set_event_loop_policy(None) + def _asyncgenstate(self): return inspect.getasyncgenstate(self.asyncgen) From 6cf31750e0eba0b0e70ac10bf0dfa3db268a4dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:46:36 +0100 Subject: [PATCH 253/376] gh-111178: fix UBSan failures in `Objects/complexobject.c` (GH-128241) fix UBSan failures for `PyComplexObject` --- Objects/complexobject.c | 54 ++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/Objects/complexobject.c b/Objects/complexobject.c index bf6187efac941f..5d9b3c9f0e3e76 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -14,6 +14,8 @@ #include "pycore_pymath.h" // _Py_ADJUST_ERANGE2() +#define _PyComplexObject_CAST(op) ((PyComplexObject *)(op)) + /*[clinic input] class complex "PyComplexObject *" "&PyComplex_Type" @@ -553,11 +555,12 @@ PyComplex_AsCComplex(PyObject *op) } static PyObject * -complex_repr(PyComplexObject *v) +complex_repr(PyObject *op) { int precision = 0; char format_code = 'r'; PyObject *result = NULL; + PyComplexObject *v = _PyComplexObject_CAST(op); /* If these are non-NULL, they'll need to be freed. */ char *pre = NULL; @@ -609,13 +612,14 @@ complex_repr(PyComplexObject *v) } static Py_hash_t -complex_hash(PyComplexObject *v) +complex_hash(PyObject *op) { Py_uhash_t hashreal, hashimag, combined; - hashreal = (Py_uhash_t)_Py_HashDouble((PyObject *) v, v->cval.real); + PyComplexObject *v = _PyComplexObject_CAST(op); + hashreal = (Py_uhash_t)_Py_HashDouble(op, v->cval.real); if (hashreal == (Py_uhash_t)-1) return -1; - hashimag = (Py_uhash_t)_Py_HashDouble((PyObject *)v, v->cval.imag); + hashimag = (Py_uhash_t)_Py_HashDouble(op, v->cval.imag); if (hashimag == (Py_uhash_t)-1) return -1; /* Note: if the imaginary part is 0, hashimag is 0 now, @@ -753,8 +757,9 @@ complex_pow(PyObject *v, PyObject *w, PyObject *z) } static PyObject * -complex_neg(PyComplexObject *v) +complex_neg(PyObject *op) { + PyComplexObject *v = _PyComplexObject_CAST(op); Py_complex neg; neg.real = -v->cval.real; neg.imag = -v->cval.imag; @@ -762,22 +767,20 @@ complex_neg(PyComplexObject *v) } static PyObject * -complex_pos(PyComplexObject *v) +complex_pos(PyObject *op) { + PyComplexObject *v = _PyComplexObject_CAST(op); if (PyComplex_CheckExact(v)) { return Py_NewRef(v); } - else - return PyComplex_FromCComplex(v->cval); + return PyComplex_FromCComplex(v->cval); } static PyObject * -complex_abs(PyComplexObject *v) +complex_abs(PyObject *op) { - double result; - - result = _Py_c_abs(v->cval); - + PyComplexObject *v = _PyComplexObject_CAST(op); + double result = _Py_c_abs(v->cval); if (errno == ERANGE) { PyErr_SetString(PyExc_OverflowError, "absolute value too large"); @@ -787,8 +790,9 @@ complex_abs(PyComplexObject *v) } static int -complex_bool(PyComplexObject *v) +complex_bool(PyObject *op) { + PyComplexObject *v = _PyComplexObject_CAST(op); return v->cval.real != 0.0 || v->cval.imag != 0.0; } @@ -1339,16 +1343,16 @@ static PyMemberDef complex_members[] = { }; static PyNumberMethods complex_as_number = { - (binaryfunc)complex_add, /* nb_add */ - (binaryfunc)complex_sub, /* nb_subtract */ - (binaryfunc)complex_mul, /* nb_multiply */ + complex_add, /* nb_add */ + complex_sub, /* nb_subtract */ + complex_mul, /* nb_multiply */ 0, /* nb_remainder */ 0, /* nb_divmod */ - (ternaryfunc)complex_pow, /* nb_power */ - (unaryfunc)complex_neg, /* nb_negative */ - (unaryfunc)complex_pos, /* nb_positive */ - (unaryfunc)complex_abs, /* nb_absolute */ - (inquiry)complex_bool, /* nb_bool */ + complex_pow, /* nb_power */ + complex_neg, /* nb_negative */ + complex_pos, /* nb_positive */ + complex_abs, /* nb_absolute */ + complex_bool, /* nb_bool */ 0, /* nb_invert */ 0, /* nb_lshift */ 0, /* nb_rshift */ @@ -1369,7 +1373,7 @@ static PyNumberMethods complex_as_number = { 0, /* nb_inplace_xor */ 0, /* nb_inplace_or */ 0, /* nb_floor_divide */ - (binaryfunc)complex_div, /* nb_true_divide */ + complex_div, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ }; @@ -1384,11 +1388,11 @@ PyTypeObject PyComplex_Type = { 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ - (reprfunc)complex_repr, /* tp_repr */ + complex_repr, /* tp_repr */ &complex_as_number, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ - (hashfunc)complex_hash, /* tp_hash */ + complex_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ From 49d78158c058ff2dece8c0a1acec384f9c413ab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:47:19 +0100 Subject: [PATCH 254/376] gh-111178: fix UBSan failures in `Modules/_bz2module.c` (GH-128238) --- Modules/_bz2module.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Modules/_bz2module.c b/Modules/_bz2module.c index 661847ad26702e..9e85e0de42cd8d 100644 --- a/Modules/_bz2module.c +++ b/Modules/_bz2module.c @@ -129,6 +129,9 @@ typedef struct { PyThread_type_lock lock; } BZ2Decompressor; +#define _BZ2Compressor_CAST(op) ((BZ2Compressor *)(op)) +#define _BZ2Decompressor_CAST(op) ((BZ2Decompressor *)(op)) + /* Helper functions. */ static int @@ -376,8 +379,9 @@ _bz2_BZ2Compressor_impl(PyTypeObject *type, int compresslevel) } static void -BZ2Compressor_dealloc(BZ2Compressor *self) +BZ2Compressor_dealloc(PyObject *op) { + BZ2Compressor *self = _BZ2Compressor_CAST(op); BZ2_bzCompressEnd(&self->bzs); if (self->lock != NULL) { PyThread_free_lock(self->lock); @@ -388,7 +392,7 @@ BZ2Compressor_dealloc(BZ2Compressor *self) } static int -BZ2Compressor_traverse(BZ2Compressor *self, visitproc visit, void *arg) +BZ2Compressor_traverse(PyObject *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); return 0; @@ -680,8 +684,10 @@ _bz2_BZ2Decompressor_impl(PyTypeObject *type) } static void -BZ2Decompressor_dealloc(BZ2Decompressor *self) +BZ2Decompressor_dealloc(PyObject *op) { + BZ2Decompressor *self = _BZ2Decompressor_CAST(op); + if(self->input_buffer != NULL) { PyMem_Free(self->input_buffer); } @@ -697,7 +703,7 @@ BZ2Decompressor_dealloc(BZ2Decompressor *self) } static int -BZ2Decompressor_traverse(BZ2Decompressor *self, visitproc visit, void *arg) +BZ2Decompressor_traverse(PyObject *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); return 0; From 295776c7f3778756e4bdbfa06bded731ad022047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:48:06 +0100 Subject: [PATCH 255/376] gh-111178: fix UBSan failures in `Objects/bytesobject.c` (GH-128237) * remove redundant casts for `bytesobject` * fix UBSan failures for `striterobject` --- Objects/bytesobject.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 90e8a9af88b4a3..024653546563e6 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -3085,7 +3085,7 @@ PyTypeObject PyBytes_Type = { bytes_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ - (richcmpfunc)bytes_richcompare, /* tp_richcompare */ + bytes_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ bytes_iter, /* tp_iter */ 0, /* tp_iternext */ @@ -3245,24 +3245,29 @@ typedef struct { PyBytesObject *it_seq; /* Set to NULL when iterator is exhausted */ } striterobject; +#define _striterobject_CAST(op) ((striterobject *)(op)) + static void -striter_dealloc(striterobject *it) +striter_dealloc(PyObject *op) { + striterobject *it = _striterobject_CAST(op); _PyObject_GC_UNTRACK(it); Py_XDECREF(it->it_seq); PyObject_GC_Del(it); } static int -striter_traverse(striterobject *it, visitproc visit, void *arg) +striter_traverse(PyObject *op, visitproc visit, void *arg) { + striterobject *it = _striterobject_CAST(op); Py_VISIT(it->it_seq); return 0; } static PyObject * -striter_next(striterobject *it) +striter_next(PyObject *op) { + striterobject *it = _striterobject_CAST(op); PyBytesObject *seq; assert(it != NULL); @@ -3282,8 +3287,9 @@ striter_next(striterobject *it) } static PyObject * -striter_len(striterobject *it, PyObject *Py_UNUSED(ignored)) +striter_len(PyObject *op, PyObject *Py_UNUSED(ignored)) { + striterobject *it = _striterobject_CAST(op); Py_ssize_t len = 0; if (it->it_seq) len = PyBytes_GET_SIZE(it->it_seq) - it->it_index; @@ -3294,14 +3300,14 @@ PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyObject * -striter_reduce(striterobject *it, PyObject *Py_UNUSED(ignored)) +striter_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) { PyObject *iter = _PyEval_GetBuiltin(&_Py_ID(iter)); /* _PyEval_GetBuiltin can invoke arbitrary code, * call must be before access of iterator pointers. * see issue #101765 */ - + striterobject *it = _striterobject_CAST(op); if (it->it_seq != NULL) { return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); } else { @@ -3312,11 +3318,12 @@ striter_reduce(striterobject *it, PyObject *Py_UNUSED(ignored)) PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyObject * -striter_setstate(striterobject *it, PyObject *state) +striter_setstate(PyObject *op, PyObject *state) { Py_ssize_t index = PyLong_AsSsize_t(state); if (index == -1 && PyErr_Occurred()) return NULL; + striterobject *it = _striterobject_CAST(op); if (it->it_seq != NULL) { if (index < 0) index = 0; @@ -3330,12 +3337,9 @@ striter_setstate(striterobject *it, PyObject *state) PyDoc_STRVAR(setstate_doc, "Set state information for unpickling."); static PyMethodDef striter_methods[] = { - {"__length_hint__", (PyCFunction)striter_len, METH_NOARGS, - length_hint_doc}, - {"__reduce__", (PyCFunction)striter_reduce, METH_NOARGS, - reduce_doc}, - {"__setstate__", (PyCFunction)striter_setstate, METH_O, - setstate_doc}, + {"__length_hint__", striter_len, METH_NOARGS, length_hint_doc}, + {"__reduce__", striter_reduce, METH_NOARGS, reduce_doc}, + {"__setstate__", striter_setstate, METH_O, setstate_doc}, {NULL, NULL} /* sentinel */ }; @@ -3345,7 +3349,7 @@ PyTypeObject PyBytesIter_Type = { sizeof(striterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ - (destructor)striter_dealloc, /* tp_dealloc */ + striter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -3362,12 +3366,12 @@ PyTypeObject PyBytesIter_Type = { 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ - (traverseproc)striter_traverse, /* tp_traverse */ + striter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ - (iternextfunc)striter_next, /* tp_iternext */ + striter_next, /* tp_iternext */ striter_methods, /* tp_methods */ 0, }; From 613240bf03a44323f78f02359b2acf928a4093f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:50:02 +0100 Subject: [PATCH 256/376] gh-111178: fix UBSan failures in `Objects/bytearrayobject.c` (GH-128236) * fix UBSan failures for `bytesiterobject` * fix UBSan failures for `PyByteArrayObject` --- Objects/bytearrayobject.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 871f99b6f885ba..21584332e0e443 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -2113,8 +2113,9 @@ PyDoc_STRVAR(alloc_doc, Return the number of bytes actually allocated."); static PyObject * -bytearray_alloc(PyByteArrayObject *self, PyObject *Py_UNUSED(ignored)) +bytearray_alloc(PyObject *op, PyObject *Py_UNUSED(ignored)) { + PyByteArrayObject *self = _PyByteArray_CAST(op); return PyLong_FromSsize_t(self->ob_alloc); } @@ -2313,7 +2314,7 @@ static PyBufferProcs bytearray_as_buffer = { }; static PyMethodDef bytearray_methods[] = { - {"__alloc__", (PyCFunction)bytearray_alloc, METH_NOARGS, alloc_doc}, + {"__alloc__", bytearray_alloc, METH_NOARGS, alloc_doc}, BYTEARRAY_REDUCE_METHODDEF BYTEARRAY_REDUCE_EX_METHODDEF BYTEARRAY_SIZEOF_METHODDEF @@ -2464,24 +2465,29 @@ typedef struct { PyByteArrayObject *it_seq; /* Set to NULL when iterator is exhausted */ } bytesiterobject; +#define _bytesiterobject_CAST(op) ((bytesiterobject *)(op)) + static void -bytearrayiter_dealloc(bytesiterobject *it) +bytearrayiter_dealloc(PyObject *self) { + bytesiterobject *it = _bytesiterobject_CAST(self); _PyObject_GC_UNTRACK(it); Py_XDECREF(it->it_seq); PyObject_GC_Del(it); } static int -bytearrayiter_traverse(bytesiterobject *it, visitproc visit, void *arg) +bytearrayiter_traverse(PyObject *self, visitproc visit, void *arg) { + bytesiterobject *it = _bytesiterobject_CAST(self); Py_VISIT(it->it_seq); return 0; } static PyObject * -bytearrayiter_next(bytesiterobject *it) +bytearrayiter_next(PyObject *self) { + bytesiterobject *it = _bytesiterobject_CAST(self); PyByteArrayObject *seq; assert(it != NULL); @@ -2501,8 +2507,9 @@ bytearrayiter_next(bytesiterobject *it) } static PyObject * -bytearrayiter_length_hint(bytesiterobject *it, PyObject *Py_UNUSED(ignored)) +bytearrayiter_length_hint(PyObject *self, PyObject *Py_UNUSED(ignored)) { + bytesiterobject *it = _bytesiterobject_CAST(self); Py_ssize_t len = 0; if (it->it_seq) { len = PyByteArray_GET_SIZE(it->it_seq) - it->it_index; @@ -2517,14 +2524,14 @@ PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyObject * -bytearrayiter_reduce(bytesiterobject *it, PyObject *Py_UNUSED(ignored)) +bytearrayiter_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) { PyObject *iter = _PyEval_GetBuiltin(&_Py_ID(iter)); /* _PyEval_GetBuiltin can invoke arbitrary code, * call must be before access of iterator pointers. * see issue #101765 */ - + bytesiterobject *it = _bytesiterobject_CAST(self); if (it->it_seq != NULL) { return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); } else { @@ -2533,11 +2540,13 @@ bytearrayiter_reduce(bytesiterobject *it, PyObject *Py_UNUSED(ignored)) } static PyObject * -bytearrayiter_setstate(bytesiterobject *it, PyObject *state) +bytearrayiter_setstate(PyObject *self, PyObject *state) { Py_ssize_t index = PyLong_AsSsize_t(state); if (index == -1 && PyErr_Occurred()) return NULL; + + bytesiterobject *it = _bytesiterobject_CAST(self); if (it->it_seq != NULL) { if (index < 0) index = 0; @@ -2551,11 +2560,11 @@ bytearrayiter_setstate(bytesiterobject *it, PyObject *state) PyDoc_STRVAR(setstate_doc, "Set state information for unpickling."); static PyMethodDef bytearrayiter_methods[] = { - {"__length_hint__", (PyCFunction)bytearrayiter_length_hint, METH_NOARGS, + {"__length_hint__", bytearrayiter_length_hint, METH_NOARGS, length_hint_doc}, - {"__reduce__", (PyCFunction)bytearrayiter_reduce, METH_NOARGS, + {"__reduce__", bytearrayiter_reduce, METH_NOARGS, bytearray_reduce__doc__}, - {"__setstate__", (PyCFunction)bytearrayiter_setstate, METH_O, + {"__setstate__", bytearrayiter_setstate, METH_O, setstate_doc}, {NULL, NULL} /* sentinel */ }; @@ -2566,7 +2575,7 @@ PyTypeObject PyByteArrayIter_Type = { sizeof(bytesiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ - (destructor)bytearrayiter_dealloc, /* tp_dealloc */ + bytearrayiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -2583,12 +2592,12 @@ PyTypeObject PyByteArrayIter_Type = { 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ - (traverseproc)bytearrayiter_traverse, /* tp_traverse */ + bytearrayiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ - (iternextfunc)bytearrayiter_next, /* tp_iternext */ + bytearrayiter_next, /* tp_iternext */ bytearrayiter_methods, /* tp_methods */ 0, }; From 28ffdc5702e8e5d71d7b06e389d1619c0ae9a4ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:51:24 +0100 Subject: [PATCH 257/376] gh-111178: fix UBSan failures in `Python/bltinmodule.c` (GH-128235) * fix UBSan failures for `filterobject` * fix UBSan failures for `mapobject` * fix UBSan failures for `zipobject` --- Python/bltinmodule.c | 81 ++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index fb9868b3740b8c..46a6fd9a8ef017 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -494,6 +494,8 @@ typedef struct { PyObject *it; } filterobject; +#define _filterobject_CAST(op) ((filterobject *)(op)) + static PyObject * filter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -559,8 +561,9 @@ filter_vectorcall(PyObject *type, PyObject * const*args, } static void -filter_dealloc(filterobject *lz) +filter_dealloc(PyObject *self) { + filterobject *lz = _filterobject_CAST(self); PyObject_GC_UnTrack(lz); Py_TRASHCAN_BEGIN(lz, filter_dealloc) Py_XDECREF(lz->func); @@ -570,16 +573,18 @@ filter_dealloc(filterobject *lz) } static int -filter_traverse(filterobject *lz, visitproc visit, void *arg) +filter_traverse(PyObject *self, visitproc visit, void *arg) { + filterobject *lz = _filterobject_CAST(self); Py_VISIT(lz->it); Py_VISIT(lz->func); return 0; } static PyObject * -filter_next(filterobject *lz) +filter_next(PyObject *self) { + filterobject *lz = _filterobject_CAST(self); PyObject *item; PyObject *it = lz->it; long ok; @@ -613,15 +618,16 @@ filter_next(filterobject *lz) } static PyObject * -filter_reduce(filterobject *lz, PyObject *Py_UNUSED(ignored)) +filter_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) { + filterobject *lz = _filterobject_CAST(self); return Py_BuildValue("O(OO)", Py_TYPE(lz), lz->func, lz->it); } PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyMethodDef filter_methods[] = { - {"__reduce__", _PyCFunction_CAST(filter_reduce), METH_NOARGS, reduce_doc}, + {"__reduce__", filter_reduce, METH_NOARGS, reduce_doc}, {NULL, NULL} /* sentinel */ }; @@ -638,7 +644,7 @@ PyTypeObject PyFilter_Type = { sizeof(filterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ - (destructor)filter_dealloc, /* tp_dealloc */ + filter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -656,12 +662,12 @@ PyTypeObject PyFilter_Type = { Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ filter_doc, /* tp_doc */ - (traverseproc)filter_traverse, /* tp_traverse */ + filter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ - (iternextfunc)filter_next, /* tp_iternext */ + filter_next, /* tp_iternext */ filter_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ @@ -674,7 +680,7 @@ PyTypeObject PyFilter_Type = { PyType_GenericAlloc, /* tp_alloc */ filter_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ - .tp_vectorcall = (vectorcallfunc)filter_vectorcall + .tp_vectorcall = filter_vectorcall }; @@ -1319,6 +1325,8 @@ typedef struct { int strict; } mapobject; +#define _mapobject_CAST(op) ((mapobject *)(op)) + static PyObject * map_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -1422,8 +1430,9 @@ map_vectorcall(PyObject *type, PyObject * const*args, } static void -map_dealloc(mapobject *lz) +map_dealloc(PyObject *self) { + mapobject *lz = _mapobject_CAST(self); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->iters); Py_XDECREF(lz->func); @@ -1431,16 +1440,18 @@ map_dealloc(mapobject *lz) } static int -map_traverse(mapobject *lz, visitproc visit, void *arg) +map_traverse(PyObject *self, visitproc visit, void *arg) { + mapobject *lz = _mapobject_CAST(self); Py_VISIT(lz->iters); Py_VISIT(lz->func); return 0; } static PyObject * -map_next(mapobject *lz) +map_next(PyObject *self) { + mapobject *lz = _mapobject_CAST(self); Py_ssize_t i; PyObject *small_stack[_PY_FASTCALL_SMALL_STACK]; PyObject **stack; @@ -1523,8 +1534,9 @@ map_next(mapobject *lz) } static PyObject * -map_reduce(mapobject *lz, PyObject *Py_UNUSED(ignored)) +map_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) { + mapobject *lz = _mapobject_CAST(self); Py_ssize_t numargs = PyTuple_GET_SIZE(lz->iters); PyObject *args = PyTuple_New(numargs+1); Py_ssize_t i; @@ -1545,19 +1557,20 @@ map_reduce(mapobject *lz, PyObject *Py_UNUSED(ignored)) PyDoc_STRVAR(setstate_doc, "Set state information for unpickling."); static PyObject * -map_setstate(mapobject *lz, PyObject *state) +map_setstate(PyObject *self, PyObject *state) { int strict = PyObject_IsTrue(state); if (strict < 0) { return NULL; } + mapobject *lz = _mapobject_CAST(self); lz->strict = strict; Py_RETURN_NONE; } static PyMethodDef map_methods[] = { - {"__reduce__", _PyCFunction_CAST(map_reduce), METH_NOARGS, reduce_doc}, - {"__setstate__", _PyCFunction_CAST(map_setstate), METH_O, setstate_doc}, + {"__reduce__", map_reduce, METH_NOARGS, reduce_doc}, + {"__setstate__", map_setstate, METH_O, setstate_doc}, {NULL, NULL} /* sentinel */ }; @@ -1578,7 +1591,7 @@ PyTypeObject PyMap_Type = { sizeof(mapobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ - (destructor)map_dealloc, /* tp_dealloc */ + map_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -1596,12 +1609,12 @@ PyTypeObject PyMap_Type = { Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ map_doc, /* tp_doc */ - (traverseproc)map_traverse, /* tp_traverse */ + map_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ - (iternextfunc)map_next, /* tp_iternext */ + map_next, /* tp_iternext */ map_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ @@ -1614,7 +1627,7 @@ PyTypeObject PyMap_Type = { PyType_GenericAlloc, /* tp_alloc */ map_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ - .tp_vectorcall = (vectorcallfunc)map_vectorcall + .tp_vectorcall = map_vectorcall }; @@ -2965,6 +2978,8 @@ typedef struct { int strict; } zipobject; +#define _zipobject_CAST(op) ((zipobject *)(op)) + static PyObject * zip_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -3033,8 +3048,9 @@ zip_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } static void -zip_dealloc(zipobject *lz) +zip_dealloc(PyObject *self) { + zipobject *lz = _zipobject_CAST(self); PyObject_GC_UnTrack(lz); Py_XDECREF(lz->ittuple); Py_XDECREF(lz->result); @@ -3042,16 +3058,19 @@ zip_dealloc(zipobject *lz) } static int -zip_traverse(zipobject *lz, visitproc visit, void *arg) +zip_traverse(PyObject *self, visitproc visit, void *arg) { + zipobject *lz = _zipobject_CAST(self); Py_VISIT(lz->ittuple); Py_VISIT(lz->result); return 0; } static PyObject * -zip_next(zipobject *lz) +zip_next(PyObject *self) { + zipobject *lz = _zipobject_CAST(self); + Py_ssize_t i; Py_ssize_t tuplesize = lz->tuplesize; PyObject *result = lz->result; @@ -3141,8 +3160,9 @@ zip_next(zipobject *lz) } static PyObject * -zip_reduce(zipobject *lz, PyObject *Py_UNUSED(ignored)) +zip_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) { + zipobject *lz = _zipobject_CAST(self); /* Just recreate the zip with the internal iterator tuple */ if (lz->strict) { return PyTuple_Pack(3, Py_TYPE(lz), lz->ittuple, Py_True); @@ -3151,19 +3171,20 @@ zip_reduce(zipobject *lz, PyObject *Py_UNUSED(ignored)) } static PyObject * -zip_setstate(zipobject *lz, PyObject *state) +zip_setstate(PyObject *self, PyObject *state) { int strict = PyObject_IsTrue(state); if (strict < 0) { return NULL; } + zipobject *lz = _zipobject_CAST(self); lz->strict = strict; Py_RETURN_NONE; } static PyMethodDef zip_methods[] = { - {"__reduce__", _PyCFunction_CAST(zip_reduce), METH_NOARGS, reduce_doc}, - {"__setstate__", _PyCFunction_CAST(zip_setstate), METH_O, setstate_doc}, + {"__reduce__", zip_reduce, METH_NOARGS, reduce_doc}, + {"__setstate__", zip_setstate, METH_O, setstate_doc}, {NULL} /* sentinel */ }; @@ -3188,7 +3209,7 @@ PyTypeObject PyZip_Type = { sizeof(zipobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ - (destructor)zip_dealloc, /* tp_dealloc */ + zip_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -3206,12 +3227,12 @@ PyTypeObject PyZip_Type = { Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ zip_doc, /* tp_doc */ - (traverseproc)zip_traverse, /* tp_traverse */ + zip_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ - (iternextfunc)zip_next, /* tp_iternext */ + zip_next, /* tp_iternext */ zip_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ From baf65715fc9002e43cd0e1010b8dba9b4c84d503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:52:36 +0100 Subject: [PATCH 258/376] gh-111178: fix UBSan failures for `gdbmobject` (GH-128178) * fix UBSan failures for `gdbmobject` * suppress unused return values --- Modules/_gdbmmodule.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index ea4fe247987e9d..ab2ebdba9249bf 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -76,6 +76,8 @@ typedef struct { GDBM_FILE di_dbm; } gdbmobject; +#define _gdbmobject_CAST(op) ((gdbmobject *)(op)) + #include "clinic/_gdbmmodule.c.h" #define check_gdbmobject_open(v, err) \ @@ -120,27 +122,29 @@ newgdbmobject(_gdbm_state *state, const char *file, int flags, int mode) /* Methods */ static int -gdbm_traverse(gdbmobject *dp, visitproc visit, void *arg) +gdbm_traverse(PyObject *op, visitproc visit, void *arg) { - Py_VISIT(Py_TYPE(dp)); + Py_VISIT(Py_TYPE(op)); return 0; } static void -gdbm_dealloc(gdbmobject *dp) +gdbm_dealloc(PyObject *op) { + gdbmobject *dp = _gdbmobject_CAST(op); + PyTypeObject *tp = Py_TYPE(dp); PyObject_GC_UnTrack(dp); if (dp->di_dbm) { gdbm_close(dp->di_dbm); } - PyTypeObject *tp = Py_TYPE(dp); tp->tp_free(dp); Py_DECREF(tp); } static Py_ssize_t -gdbm_length(gdbmobject *dp) +gdbm_length(PyObject *op) { + gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); if (dp->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); @@ -185,8 +189,9 @@ gdbm_length(gdbmobject *dp) } static int -gdbm_bool(gdbmobject *dp) +gdbm_bool(PyObject *op) { + gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); if (dp->di_dbm == NULL) { PyErr_SetString(state->gdbm_error, "GDBM object has already been closed"); @@ -235,10 +240,11 @@ parse_datum(PyObject *o, datum *d, const char *failmsg) } static PyObject * -gdbm_subscript(gdbmobject *dp, PyObject *key) +gdbm_subscript(PyObject *op, PyObject *key) { PyObject *v; datum drec, krec; + gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); if (!parse_datum(key, &krec, NULL)) { @@ -275,7 +281,7 @@ _gdbm_gdbm_get_impl(gdbmobject *self, PyObject *key, PyObject *default_value) { PyObject *res; - res = gdbm_subscript(self, key); + res = gdbm_subscript((PyObject *)self, key); if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) { PyErr_Clear(); return Py_NewRef(default_value); @@ -284,10 +290,11 @@ _gdbm_gdbm_get_impl(gdbmobject *self, PyObject *key, PyObject *default_value) } static int -gdbm_ass_sub(gdbmobject *dp, PyObject *v, PyObject *w) +gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w) { datum krec, drec; const char *failmsg = "gdbm mappings have bytes or string indices only"; + gdbmobject *dp = _gdbmobject_CAST(op); _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); if (!parse_datum(v, &krec, failmsg)) { @@ -345,12 +352,12 @@ _gdbm_gdbm_setdefault_impl(gdbmobject *self, PyObject *key, { PyObject *res; - res = gdbm_subscript(self, key); + res = gdbm_subscript((PyObject *)self, key); if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) { PyErr_Clear(); - if (gdbm_ass_sub(self, key, default_value) < 0) + if (gdbm_ass_sub((PyObject *)self, key, default_value) < 0) return NULL; - return gdbm_subscript(self, key); + return gdbm_subscript((PyObject *)self, key); } return res; } @@ -841,7 +848,7 @@ _gdbm_module_clear(PyObject *module) static void _gdbm_module_free(void *module) { - _gdbm_module_clear((PyObject *)module); + (void)_gdbm_module_clear((PyObject *)module); } static PyModuleDef_Slot _gdbm_module_slots[] = { From 688f3a0d4b94874ff6d72af3baafd8bbf911153e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulie=20Pe=C3=B1a?= <203125+paulie4@users.noreply.github.com> Date: Fri, 10 Jan 2025 07:52:19 -0500 Subject: [PATCH 259/376] gh-128388: pyrepl on Windows: add meta and ctrl+arrow keybindings (GH-128389) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix `Lib/_pyrepl/windows_console.py` to support more keybindings, like the `Ctrl`+`←` and `Ctrl`+`→` word-skipping keybindings and those with meta (i.e. Alt), e.g. to `kill-word` or `backward-kill-word`. Specifics: if Ctrl is pressed, emit "ctrl left" and "ctrl right" instead of just "left" or "right," and if Meta/Alt is pressed, emit the special key code for meta before emitting the other key that was pressed. Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Pieter Eendebak --- Lib/_pyrepl/windows_console.py | 42 ++++++++++++------- ...-01-01-19-24-43.gh-issue-128388.8UdMz_.rst | 1 + 2 files changed, 27 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-01-19-24-43.gh-issue-128388.8UdMz_.rst diff --git a/Lib/_pyrepl/windows_console.py b/Lib/_pyrepl/windows_console.py index d457d2b5a338eb..e738fd09c65758 100644 --- a/Lib/_pyrepl/windows_console.py +++ b/Lib/_pyrepl/windows_console.py @@ -102,6 +102,10 @@ def __init__(self, err: int | None, descr: str | None = None) -> None: MOVE_DOWN = "\x1b[{}B" CLEAR = "\x1b[H\x1b[J" +# State of control keys: https://learn.microsoft.com/en-us/windows/console/key-event-record-str +ALT_ACTIVE = 0x01 | 0x02 +CTRL_ACTIVE = 0x04 | 0x08 + class _error(Exception): pass @@ -407,31 +411,37 @@ def get_event(self, block: bool = True) -> Event | None: continue return None - key = rec.Event.KeyEvent.uChar.UnicodeChar + key_event = rec.Event.KeyEvent + raw_key = key = key_event.uChar.UnicodeChar - if rec.Event.KeyEvent.uChar.UnicodeChar == "\r": - # Make enter make unix-like + if key == "\r": + # Make enter unix-like return Event(evt="key", data="\n", raw=b"\n") - elif rec.Event.KeyEvent.wVirtualKeyCode == 8: + elif key_event.wVirtualKeyCode == 8: # Turn backspace directly into the command - return Event( - evt="key", - data="backspace", - raw=rec.Event.KeyEvent.uChar.UnicodeChar, - ) - elif rec.Event.KeyEvent.uChar.UnicodeChar == "\x00": + key = "backspace" + elif key == "\x00": # Handle special keys like arrow keys and translate them into the appropriate command - code = VK_MAP.get(rec.Event.KeyEvent.wVirtualKeyCode) - if code: - return Event( - evt="key", data=code, raw=rec.Event.KeyEvent.uChar.UnicodeChar - ) + key = VK_MAP.get(key_event.wVirtualKeyCode) + if key: + if key_event.dwControlKeyState & CTRL_ACTIVE: + key = f"ctrl {key}" + elif key_event.dwControlKeyState & ALT_ACTIVE: + # queue the key, return the meta command + self.event_queue.insert(0, Event(evt="key", data=key, raw=key)) + return Event(evt="key", data="\033") # keymap.py uses this for meta + return Event(evt="key", data=key, raw=key) if block: continue return None - return Event(evt="key", data=key, raw=rec.Event.KeyEvent.uChar.UnicodeChar) + if key_event.dwControlKeyState & ALT_ACTIVE: + # queue the key, return the meta command + self.event_queue.insert(0, Event(evt="key", data=key, raw=raw_key)) + return Event(evt="key", data="\033") # keymap.py uses this for meta + + return Event(evt="key", data=key, raw=raw_key) def push_char(self, char: int | bytes) -> None: """ diff --git a/Misc/NEWS.d/next/Library/2025-01-01-19-24-43.gh-issue-128388.8UdMz_.rst b/Misc/NEWS.d/next/Library/2025-01-01-19-24-43.gh-issue-128388.8UdMz_.rst new file mode 100644 index 00000000000000..5bef0fd6bcac17 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-01-19-24-43.gh-issue-128388.8UdMz_.rst @@ -0,0 +1 @@ +Fix ``PyREPL`` on Windows to support more keybindings, like the :kbd:`Control-←` and :kbd:`Control-→` word-skipping keybindings and those with meta (i.e. :kbd:`Alt`), e.g. :kbd:`Alt-d` to ``kill-word`` or :kbd:`Alt-Backspace` ``backward-kill-word``. From 802556abfa008abe0bdd78e6f9e18bef71db90c1 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 10 Jan 2025 13:59:51 +0100 Subject: [PATCH 260/376] gh-127257: Add hex code to ssl "unknown error" message (GH-127360) * ssl: Add hex error code to "unknown error" messages To make it easier to vary the individual parts of the message, replace the if-ladder with constant format strings by building the string piece-wise with PyUnicodeWriter. Use "unknown error (0x%x)" rather than just "unknown error" if we can't get a better error message. (Hex makes sense as the error includes two packed parts.) --- ...-11-28-14-24-12.gh-issue-127360.HVKt-c.rst | 4 ++ Modules/_ssl.c | 63 ++++++++++++++----- 2 files changed, 51 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-28-14-24-12.gh-issue-127360.HVKt-c.rst diff --git a/Misc/NEWS.d/next/Library/2024-11-28-14-24-12.gh-issue-127360.HVKt-c.rst b/Misc/NEWS.d/next/Library/2024-11-28-14-24-12.gh-issue-127360.HVKt-c.rst new file mode 100644 index 00000000000000..4772aef71e658f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-28-14-24-12.gh-issue-127360.HVKt-c.rst @@ -0,0 +1,4 @@ +When a descriptive error message cannot be provided for an +:exc:`ssl.SSLError`, the "unknown error" message now shows the internal +error code (as retrieved by ``ERR_get_error`` and similar OpenSSL +functions). diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 87739832fbf784..a7d0f509aed3d1 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -473,6 +473,7 @@ fill_and_set_sslerror(_sslmodulestate *state, PyObject *err_value = NULL, *reason_obj = NULL, *lib_obj = NULL; PyObject *verify_obj = NULL, *verify_code_obj = NULL; PyObject *init_value, *msg, *key; + PyUnicodeWriter *writer = NULL; if (errcode != 0) { int lib, reason; @@ -495,11 +496,10 @@ fill_and_set_sslerror(_sslmodulestate *state, if (lib_obj == NULL && PyErr_Occurred()) { goto fail; } - if (errstr == NULL) + if (errstr == NULL) { errstr = ERR_reason_error_string(errcode); + } } - if (errstr == NULL) - errstr = "unknown error"; /* verify code for cert validation error */ if ((sslsock != NULL) && (type == state->PySSLCertVerificationErrorObject)) { @@ -539,20 +539,50 @@ fill_and_set_sslerror(_sslmodulestate *state, } } - if (verify_obj && reason_obj && lib_obj) - msg = PyUnicode_FromFormat("[%S: %S] %s: %S (_ssl.c:%d)", - lib_obj, reason_obj, errstr, verify_obj, - lineno); - else if (reason_obj && lib_obj) - msg = PyUnicode_FromFormat("[%S: %S] %s (_ssl.c:%d)", - lib_obj, reason_obj, errstr, lineno); - else if (lib_obj) - msg = PyUnicode_FromFormat("[%S] %s (_ssl.c:%d)", - lib_obj, errstr, lineno); - else - msg = PyUnicode_FromFormat("%s (_ssl.c:%d)", errstr, lineno); - if (msg == NULL) + // Format message roughly as: + // [lib_obj: reason_obj] errstr: verify_obj (_ssl.c:lineno) + // with parts missing/replaced if unavailable + writer = PyUnicodeWriter_Create(64); + if (!writer) { + goto fail; + } + if (lib_obj) { + if (PyUnicodeWriter_Format(writer, "[%S", lib_obj) < 0) { + goto fail; + } + if (reason_obj) { + if (PyUnicodeWriter_Format(writer, ": %S", reason_obj) < 0) { + goto fail; + } + } + if (PyUnicodeWriter_WriteUTF8(writer, "] ", 2) < 0) { + goto fail; + } + } + if (errstr) { + if (PyUnicodeWriter_Format(writer, "%s", errstr) < 0) { + goto fail; + } + } + else { + if (PyUnicodeWriter_Format( + writer, "unknown error (0x%x)", errcode) < 0) { + goto fail; + } + } + if (verify_obj) { + if (PyUnicodeWriter_Format(writer, ": %S", verify_obj) < 0) { + goto fail; + } + } + if (PyUnicodeWriter_Format(writer, " (_ssl.c:%d)", lineno) < 0) { goto fail; + } + msg = PyUnicodeWriter_Finish(writer); + writer = NULL; + if (!msg) { + goto fail; + } init_value = Py_BuildValue("iN", ERR_GET_REASON(ssl_errno), msg); if (init_value == NULL) @@ -587,6 +617,7 @@ fill_and_set_sslerror(_sslmodulestate *state, Py_XDECREF(err_value); Py_XDECREF(verify_code_obj); Py_XDECREF(verify_obj); + PyUnicodeWriter_Discard(writer); } static int From 1b39b502d33c68f52fd775c4e6c2174baddd40bd Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Fri, 10 Jan 2025 15:54:45 +0000 Subject: [PATCH 261/376] gh-128409: Document that pdb.post_mortem() accepts exceptions (#128410) Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> --- Doc/library/pdb.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index 6c099b22b38c21..f9d1213fb6d29d 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -179,13 +179,15 @@ slightly different way: .. versionadded:: 3.14 The *commands* argument. -.. function:: post_mortem(traceback=None) +.. function:: post_mortem(t=None) - Enter post-mortem debugging of the given *traceback* object. If no - *traceback* is given, it uses the one of the exception that is currently - being handled (an exception must be being handled if the default is to be - used). + Enter post-mortem debugging of the given exception or + :ref:`traceback object `. If no value is given, it uses + the exception that is currently being handled, or raises ``ValueError`` if + there isn’t one. + .. versionchanged:: 3.13 + Support for exception objects was added. .. function:: pm() From 553cdc6d6856c1b4539a45eb90d0769f7c629355 Mon Sep 17 00:00:00 2001 From: "RUANG (James Roy)" Date: Sat, 11 Jan 2025 09:03:12 +0800 Subject: [PATCH 262/376] gh-128696: Add arm64 to the get_platform return val description (#128701) --- Doc/library/sysconfig.rst | 3 ++- Lib/sysconfig/__init__.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Doc/library/sysconfig.rst b/Doc/library/sysconfig.rst index 3921908b7c7bfc..9f018f9c8f0e50 100644 --- a/Doc/library/sysconfig.rst +++ b/Doc/library/sysconfig.rst @@ -388,7 +388,8 @@ Other functions Windows will return one of: - - win-amd64 (64bit Windows on AMD64, aka x86_64, Intel64, and EM64T) + - win-amd64 (64-bit Windows on AMD64, aka x86_64, Intel64, and EM64T) + - win-arm64 (64-bit Windows on ARM64, aka AArch64) - win32 (all others - specifically, sys.platform is returned) macOS can return: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index 20d506bcd45abc..7a4a8f65a5eb3e 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -616,7 +616,8 @@ def get_platform(): solaris-2.6-sun4u Windows will return one of: - win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + win-amd64 (64-bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + win-arm64 (64-bit Windows on ARM64 (aka AArch64) win32 (all others - specifically, sys.platform is returned) For other non-POSIX platforms, currently just returns 'sys.platform'. From 3a570c6d58bd5ad7d7c13b24dafccc2701081d9f Mon Sep 17 00:00:00 2001 From: 9cel <9based@gmail.com> Date: Sat, 11 Jan 2025 05:17:35 -0400 Subject: [PATCH 263/376] Make the Python CLI error message style more consistent (GH-128129) --- Lib/test/test_cmd_line.py | 2 +- Python/getopt.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index 634efda354407f..dfdbb80b94d66f 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -1012,7 +1012,7 @@ def test_parsing_error(self): stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - err_msg = "unknown option --unknown-option\nusage: " + err_msg = "Unknown option: --unknown-option\nusage: " self.assertTrue(proc.stderr.startswith(err_msg), proc.stderr) self.assertNotEqual(proc.returncode, 0) diff --git a/Python/getopt.c b/Python/getopt.c index f64c89fa22734a..39a6938dec7663 100644 --- a/Python/getopt.c +++ b/Python/getopt.c @@ -102,7 +102,7 @@ int _PyOS_GetOpt(Py_ssize_t argc, wchar_t * const *argv, int *longindex) // Parse long option. if (*opt_ptr == L'\0') { if (_PyOS_opterr) { - fprintf(stderr, "expected long option\n"); + fprintf(stderr, "Expected long option\n"); } return -1; } @@ -114,7 +114,7 @@ int _PyOS_GetOpt(Py_ssize_t argc, wchar_t * const *argv, int *longindex) } if (!opt->name) { if (_PyOS_opterr) { - fprintf(stderr, "unknown option %ls\n", argv[_PyOS_optind - 1]); + fprintf(stderr, "Unknown option: %ls\n", argv[_PyOS_optind - 1]); } return '_'; } From 0946ed25b53dddfa4eb040513720353b7214d71b Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Sat, 11 Jan 2025 17:19:29 +0100 Subject: [PATCH 264/376] gh-128438: Add `EnvironmentVarGuard` for `test_pdb.py` (#128522) --- Lib/test/test_pdb.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index c5ee8c5fb25350..ace5544f4106d0 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -3152,16 +3152,12 @@ def run_pdb_script(self, script, commands, self.addCleanup(os_helper.unlink, '.pdbrc') self.addCleanup(os_helper.unlink, filename) - homesave = None - if remove_home: - homesave = os.environ.pop('HOME', None) - try: + with os_helper.EnvironmentVarGuard() as env: + if remove_home: + env.unset('HOME') if script_args is None: script_args = [] stdout, stderr = self._run_pdb([filename] + script_args, commands, expected_returncode, extra_env) - finally: - if homesave is not None: - os.environ['HOME'] = homesave return stdout, stderr def run_pdb_module(self, script, commands): @@ -3585,17 +3581,14 @@ def test_readrc_kwarg(self): self.assertIn("NameError: name 'invalid' is not defined", stdout) def test_readrc_homedir(self): - save_home = os.environ.pop("HOME", None) - with os_helper.temp_dir() as temp_dir, patch("os.path.expanduser"): - rc_path = os.path.join(temp_dir, ".pdbrc") - os.path.expanduser.return_value = rc_path - try: + with os_helper.EnvironmentVarGuard() as env: + env.unset("HOME") + with os_helper.temp_dir() as temp_dir, patch("os.path.expanduser"): + rc_path = os.path.join(temp_dir, ".pdbrc") + os.path.expanduser.return_value = rc_path with open(rc_path, "w") as f: f.write("invalid") self.assertEqual(pdb.Pdb().rcLines[0], "invalid") - finally: - if save_home is not None: - os.environ["HOME"] = save_home def test_header(self): stdout = StringIO() From 22a442181d5f1ac496dac08e24fd852de404882f Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sat, 11 Jan 2025 19:27:47 +0000 Subject: [PATCH 265/376] GH-128520: Divide pathlib ABCs into three classes (#128523) In the private pathlib ABCs, rename `PurePathBase` to `JoinablePath`, and split `PathBase` into `ReadablePath` and `WritablePath`. This improves the API fit for read-only virtual filesystems. The split of `PathBase` entails a similar split of `CopyWorker` (implements copying) and the test cases in `test_pathlib_abc`. In a later patch, we'll make `WritablePath` inherit directly from `JoinablePath` rather than `ReadablePath`. For a couple of reasons, this isn't quite possible yet. --- Lib/pathlib/_abc.py | 110 ++--- Lib/pathlib/_local.py | 22 +- Lib/pathlib/_types.py | 2 +- Lib/test/test_pathlib/test_pathlib.py | 14 +- Lib/test/test_pathlib/test_pathlib_abc.py | 476 +++++++++++----------- 5 files changed, 317 insertions(+), 307 deletions(-) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 7de2bb066f8f99..38bc660e0aeb30 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -7,8 +7,8 @@ it's developed alongside pathlib. If it finds success and maturity as a PyPI package, it could become a public part of the standard library. -Two base classes are defined here -- PurePathBase and PathBase -- that -resemble pathlib's PurePath and Path respectively. +Three base classes are defined here -- JoinablePath, ReadablePath and +WritablePath. """ import functools @@ -56,13 +56,13 @@ def concat_path(path, text): return path.with_segments(str(path) + text) -class CopyWorker: +class CopyReader: """ Class that implements copying between path objects. An instance of this - class is available from the PathBase.copy property; it's made callable so - that PathBase.copy() can be treated as a method. + class is available from the ReadablePath.copy property; it's made callable + so that ReadablePath.copy() can be treated as a method. - The target path's CopyWorker drives the process from its _create() method. + The target path's CopyWriter drives the process from its _create() method. Files and directories are exchanged by calling methods on the source and target paths, and metadata is exchanged by calling source.copy._read_metadata() and target.copy._write_metadata(). @@ -77,11 +77,15 @@ def __call__(self, target, follow_symlinks=True, dirs_exist_ok=False, """ Recursively copy this file or directory tree to the given destination. """ - if not isinstance(target, PathBase): + if not isinstance(target, ReadablePath): target = self._path.with_segments(target) - # Delegate to the target path's CopyWorker object. - return target.copy._create(self._path, follow_symlinks, dirs_exist_ok, preserve_metadata) + # Delegate to the target path's CopyWriter object. + try: + create = target.copy._create + except AttributeError: + raise TypeError(f"Target is not writable: {target}") from None + return create(self._path, follow_symlinks, dirs_exist_ok, preserve_metadata) _readable_metakeys = frozenset() @@ -91,6 +95,10 @@ def _read_metadata(self, metakeys, *, follow_symlinks=True): """ raise NotImplementedError + +class CopyWriter(CopyReader): + __slots__ = () + _writable_metakeys = frozenset() def _write_metadata(self, metadata, *, follow_symlinks=True): @@ -182,7 +190,7 @@ def _ensure_distinct_path(self, source): raise err -class PurePathBase: +class JoinablePath: """Base class for pure path objects. This class *does not* provide several magic methods that are defined in @@ -334,7 +342,7 @@ def match(self, path_pattern, *, case_sensitive=None): is matched. The recursive wildcard '**' is *not* supported by this method. """ - if not isinstance(path_pattern, PurePathBase): + if not isinstance(path_pattern, JoinablePath): path_pattern = self.with_segments(path_pattern) if case_sensitive is None: case_sensitive = _is_case_sensitive(self.parser) @@ -359,7 +367,7 @@ def full_match(self, pattern, *, case_sensitive=None): Return True if this path matches the given glob-style pattern. The pattern is matched against the entire path. """ - if not isinstance(pattern, PurePathBase): + if not isinstance(pattern, JoinablePath): pattern = self.with_segments(pattern) if case_sensitive is None: case_sensitive = _is_case_sensitive(self.parser) @@ -369,7 +377,7 @@ def full_match(self, pattern, *, case_sensitive=None): -class PathBase(PurePathBase): +class ReadablePath(JoinablePath): """Base class for concrete path objects. This class provides dummy implementations for many methods that derived @@ -434,25 +442,6 @@ def read_text(self, encoding=None, errors=None, newline=None): with self.open(mode='r', encoding=encoding, errors=errors, newline=newline) as f: return f.read() - def write_bytes(self, data): - """ - Open the file in bytes mode, write to it, and close the file. - """ - # type-check for the buffer interface before truncating the file - view = memoryview(data) - with self.open(mode='wb') as f: - return f.write(view) - - def write_text(self, data, encoding=None, errors=None, newline=None): - """ - Open the file in text mode, write to it, and close the file. - """ - if not isinstance(data, str): - raise TypeError('data must be str, not %s' % - data.__class__.__name__) - with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f: - return f.write(data) - def _scandir(self): """Yield os.DirEntry-like objects of the directory contents. @@ -474,7 +463,7 @@ def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=True): """Iterate over this subtree and yield all existing files (of any kind, including directories) matching the given relative pattern. """ - if not isinstance(pattern, PurePathBase): + if not isinstance(pattern, JoinablePath): pattern = self.with_segments(pattern) anchor, parts = _explode_path(pattern) if anchor: @@ -496,7 +485,7 @@ def rglob(self, pattern, *, case_sensitive=None, recurse_symlinks=True): directories) matching the given relative pattern, anywhere in this subtree. """ - if not isinstance(pattern, PurePathBase): + if not isinstance(pattern, JoinablePath): pattern = self.with_segments(pattern) pattern = '**' / pattern return self.glob(pattern, case_sensitive=case_sensitive, recurse_symlinks=recurse_symlinks) @@ -543,6 +532,28 @@ def readlink(self): """ raise NotImplementedError + copy = property(CopyReader, doc=CopyReader.__call__.__doc__) + + def copy_into(self, target_dir, *, follow_symlinks=True, + dirs_exist_ok=False, preserve_metadata=False): + """ + Copy this file or directory tree into the given existing directory. + """ + name = self.name + if not name: + raise ValueError(f"{self!r} has an empty name") + elif isinstance(target_dir, ReadablePath): + target = target_dir / name + else: + target = self.with_segments(target_dir, name) + return self.copy(target, follow_symlinks=follow_symlinks, + dirs_exist_ok=dirs_exist_ok, + preserve_metadata=preserve_metadata) + + +class WritablePath(ReadablePath): + __slots__ = () + def symlink_to(self, target, target_is_directory=False): """ Make this path a symlink pointing to the target path. @@ -556,20 +567,23 @@ def mkdir(self, mode=0o777, parents=False, exist_ok=False): """ raise NotImplementedError - copy = property(CopyWorker, doc=CopyWorker.__call__.__doc__) + def write_bytes(self, data): + """ + Open the file in bytes mode, write to it, and close the file. + """ + # type-check for the buffer interface before truncating the file + view = memoryview(data) + with self.open(mode='wb') as f: + return f.write(view) - def copy_into(self, target_dir, *, follow_symlinks=True, - dirs_exist_ok=False, preserve_metadata=False): + def write_text(self, data, encoding=None, errors=None, newline=None): """ - Copy this file or directory tree into the given existing directory. + Open the file in text mode, write to it, and close the file. """ - name = self.name - if not name: - raise ValueError(f"{self!r} has an empty name") - elif isinstance(target_dir, PathBase): - target = target_dir / name - else: - target = self.with_segments(target_dir, name) - return self.copy(target, follow_symlinks=follow_symlinks, - dirs_exist_ok=dirs_exist_ok, - preserve_metadata=preserve_metadata) + if not isinstance(data, str): + raise TypeError('data must be str, not %s' % + data.__class__.__name__) + with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f: + return f.write(data) + + copy = property(CopyWriter, doc=CopyWriter.__call__.__doc__) diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index 1da85ddea24376..d6afb31424265c 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -20,7 +20,7 @@ grp = None from pathlib._os import copyfile -from pathlib._abc import CopyWorker, PurePathBase, PathBase +from pathlib._abc import CopyWriter, JoinablePath, WritablePath __all__ = [ @@ -65,7 +65,7 @@ def __repr__(self): return "<{}.parents>".format(type(self._path).__name__) -class _LocalCopyWorker(CopyWorker): +class _LocalCopyWriter(CopyWriter): """This object implements the Path.copy callable. Don't try to construct it yourself.""" __slots__ = () @@ -158,7 +158,7 @@ def _create_file(self, source, metakeys): try: source = os.fspath(source) except TypeError: - if not isinstance(source, PathBase): + if not isinstance(source, WritablePath): raise super()._create_file(source, metakeys) else: @@ -190,7 +190,7 @@ def _ensure_different_file(self, source): raise err -class PurePath(PurePathBase): +class PurePath(JoinablePath): """Base class for manipulating paths without I/O. PurePath represents a filesystem path and offers operations which @@ -646,7 +646,7 @@ def full_match(self, pattern, *, case_sensitive=None): Return True if this path matches the given glob-style pattern. The pattern is matched against the entire path. """ - if not isinstance(pattern, PurePathBase): + if not isinstance(pattern, PurePath): pattern = self.with_segments(pattern) if case_sensitive is None: case_sensitive = self.parser is posixpath @@ -683,7 +683,7 @@ class PureWindowsPath(PurePath): __slots__ = () -class Path(PathBase, PurePath): +class Path(WritablePath, PurePath): """PurePath subclass that can make system calls. Path represents a filesystem path but unlike PurePath, also offers @@ -830,7 +830,7 @@ def read_text(self, encoding=None, errors=None, newline=None): # Call io.text_encoding() here to ensure any warning is raised at an # appropriate stack level. encoding = io.text_encoding(encoding) - return PathBase.read_text(self, encoding, errors, newline) + return super().read_text(encoding, errors, newline) def write_text(self, data, encoding=None, errors=None, newline=None): """ @@ -839,7 +839,7 @@ def write_text(self, data, encoding=None, errors=None, newline=None): # Call io.text_encoding() here to ensure any warning is raised at an # appropriate stack level. encoding = io.text_encoding(encoding) - return PathBase.write_text(self, data, encoding, errors, newline) + return super().write_text(data, encoding, errors, newline) _remove_leading_dot = operator.itemgetter(slice(2, None)) _remove_trailing_slash = operator.itemgetter(slice(-1)) @@ -1122,7 +1122,7 @@ def replace(self, target): os.replace(self, target) return self.with_segments(target) - copy = property(_LocalCopyWorker, doc=_LocalCopyWorker.__call__.__doc__) + copy = property(_LocalCopyWriter, doc=_LocalCopyWriter.__call__.__doc__) def move(self, target): """ @@ -1134,7 +1134,7 @@ def move(self, target): except TypeError: pass else: - if not isinstance(target, PathBase): + if not isinstance(target, WritablePath): target = self.with_segments(target_str) target.copy._ensure_different_file(self) try: @@ -1155,7 +1155,7 @@ def move_into(self, target_dir): name = self.name if not name: raise ValueError(f"{self!r} has an empty name") - elif isinstance(target_dir, PathBase): + elif isinstance(target_dir, WritablePath): target = target_dir / name else: target = self.with_segments(target_dir, name) diff --git a/Lib/pathlib/_types.py b/Lib/pathlib/_types.py index 72dac2e276fce0..84032bb5b4ff1a 100644 --- a/Lib/pathlib/_types.py +++ b/Lib/pathlib/_types.py @@ -9,7 +9,7 @@ class Parser(Protocol): """Protocol for path parsers, which do low-level path manipulation. Path parsers provide a subset of the os.path API, specifically those - functions needed to provide PurePathBase functionality. Each PurePathBase + functions needed to provide JoinablePath functionality. Each JoinablePath subclass references its path parser via a 'parser' class attribute. """ diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index 6548577f4de12c..ad5a9f9c8de9d6 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -75,7 +75,7 @@ def test_is_notimplemented(self): # Tests for the pure classes. # -class PurePathTest(test_pathlib_abc.DummyPurePathTest): +class PurePathTest(test_pathlib_abc.DummyJoinablePathTest): cls = pathlib.PurePath # Make sure any symbolic links in the base test path are resolved. @@ -924,7 +924,7 @@ class cls(pathlib.PurePath): # Tests for the concrete classes. # -class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest): +class PathTest(test_pathlib_abc.DummyWritablePathTest, PurePathTest): """Tests for the FS-accessing functionalities of the Path classes.""" cls = pathlib.Path can_symlink = os_helper.can_symlink() @@ -980,15 +980,15 @@ def tempdir(self): self.addCleanup(os_helper.rmtree, d) return d - def test_matches_pathbase_docstrings(self): - path_names = {name for name in dir(pathlib._abc.PathBase) if name[0] != '_'} + def test_matches_writablepath_docstrings(self): + path_names = {name for name in dir(pathlib._abc.WritablePath) if name[0] != '_'} for attr_name in path_names: if attr_name == 'parser': - # On Windows, Path.parser is ntpath, but PathBase.parser is + # On Windows, Path.parser is ntpath, but WritablePath.parser is # posixpath, and so their docstrings differ. continue our_attr = getattr(self.cls, attr_name) - path_attr = getattr(pathlib._abc.PathBase, attr_name) + path_attr = getattr(pathlib._abc.WritablePath, attr_name) self.assertEqual(our_attr.__doc__, path_attr.__doc__) def test_concrete_class(self): @@ -3019,7 +3019,7 @@ def test_group_windows(self): P('c:/').group() -class PathWalkTest(test_pathlib_abc.DummyPathWalkTest): +class PathWalkTest(test_pathlib_abc.DummyReadablePathWalkTest): cls = pathlib.Path base = PathTest.base can_symlink = PathTest.can_symlink diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index 87aef0c130cf9e..6ba012e0208a53 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -4,7 +4,7 @@ import errno import unittest -from pathlib._abc import PurePathBase, PathBase +from pathlib._abc import JoinablePath, ReadablePath, WritablePath from pathlib._types import Parser import posixpath @@ -31,8 +31,8 @@ def needs_windows(fn): # -class PurePathBaseTest(unittest.TestCase): - cls = PurePathBase +class JoinablePathTest(unittest.TestCase): + cls = JoinablePath def test_magic_methods(self): P = self.cls @@ -51,7 +51,7 @@ def test_parser(self): self.assertIs(self.cls.parser, posixpath) -class DummyPurePath(PurePathBase): +class DummyJoinablePath(JoinablePath): __slots__ = ('_segments',) def __init__(self, *segments): @@ -63,7 +63,7 @@ def __str__(self): return '' def __eq__(self, other): - if not isinstance(other, DummyPurePath): + if not isinstance(other, DummyJoinablePath): return NotImplemented return str(self) == str(other) @@ -77,8 +77,8 @@ def with_segments(self, *pathsegments): return type(self)(*pathsegments) -class DummyPurePathTest(unittest.TestCase): - cls = DummyPurePath +class DummyJoinablePathTest(unittest.TestCase): + cls = DummyJoinablePath # Use a base path that's unrelated to any real filesystem path. base = f'/this/path/kills/fascists/{TESTFN}' @@ -916,9 +916,9 @@ def test_with_suffix_invalid(self): # -class DummyPathIO(io.BytesIO): +class DummyWritablePathIO(io.BytesIO): """ - Used by DummyPath to implement `open('w')` + Used by DummyWritablePath to implement `open('w')` """ def __init__(self, files, path): @@ -931,10 +931,10 @@ def close(self): super().close() -class DummyPath(PathBase): +class DummyReadablePath(ReadablePath): """ - Simple implementation of PathBase that keeps files and directories in - memory. + Simple implementation of DummyReadablePath that keeps files and + directories in memory. """ __slots__ = ('_segments') @@ -950,7 +950,7 @@ def __str__(self): return '' def __eq__(self, other): - if not isinstance(other, DummyPath): + if not isinstance(other, DummyReadablePath): return NotImplemented return str(self) == str(other) @@ -990,10 +990,11 @@ def open(self, mode='r', buffering=-1, encoding=None, raise FileNotFoundError(errno.ENOENT, "File not found", path) stream = io.BytesIO(self._files[path]) elif mode == 'w': + # FIXME: move to DummyWritablePath parent, name = posixpath.split(path) if parent not in self._directories: raise FileNotFoundError(errno.ENOENT, "File not found", parent) - stream = DummyPathIO(self._files, path) + stream = DummyWritablePathIO(self._files, path) self._files[path] = b'' self._directories[parent].add(name) else: @@ -1011,6 +1012,10 @@ def iterdir(self): else: raise FileNotFoundError(errno.ENOENT, "File not found", path) + +class DummyWritablePath(DummyReadablePath, WritablePath): + __slots__ = () + def mkdir(self, mode=0o777, parents=False, exist_ok=False): path = str(self) parent = str(self.parent) @@ -1029,24 +1034,11 @@ def mkdir(self, mode=0o777, parents=False, exist_ok=False): self.parent.mkdir(parents=True, exist_ok=True) self.mkdir(mode, parents=False, exist_ok=exist_ok) - def _delete(self): - path = str(self) - if path in self._files: - del self._files[path] - elif path in self._directories: - for name in list(self._directories[path]): - self.joinpath(name)._delete() - del self._directories[path] - else: - raise FileNotFoundError(errno.ENOENT, "File not found", path) - parent = str(self.parent) - self._directories[parent].remove(self.name) - -class DummyPathTest(DummyPurePathTest): - """Tests for PathBase methods that use stat(), open() and iterdir().""" +class DummyReadablePathTest(DummyJoinablePathTest): + """Tests for ReadablePathTest methods that use stat(), open() and iterdir().""" - cls = DummyPath + cls = DummyReadablePath can_symlink = False # (self.base) @@ -1138,213 +1130,6 @@ def test_open_common(self): self.assertIsInstance(f, io.BufferedIOBase) self.assertEqual(f.read().strip(), b"this is file A") - def test_read_write_bytes(self): - p = self.cls(self.base) - (p / 'fileA').write_bytes(b'abcdefg') - self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') - # Check that trying to write str does not truncate the file. - self.assertRaises(TypeError, (p / 'fileA').write_bytes, 'somestr') - self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') - - def test_read_write_text(self): - p = self.cls(self.base) - (p / 'fileA').write_text('äbcdefg', encoding='latin-1') - self.assertEqual((p / 'fileA').read_text( - encoding='utf-8', errors='ignore'), 'bcdefg') - # Check that trying to write bytes does not truncate the file. - self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes') - self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), 'äbcdefg') - - def test_read_text_with_newlines(self): - p = self.cls(self.base) - # Check that `\n` character change nothing - (p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq') - self.assertEqual((p / 'fileA').read_text(newline='\n'), - 'abcde\r\nfghlk\n\rmnopq') - # Check that `\r` character replaces `\n` - (p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq') - self.assertEqual((p / 'fileA').read_text(newline='\r'), - 'abcde\r\nfghlk\n\rmnopq') - # Check that `\r\n` character replaces `\n` - (p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq') - self.assertEqual((p / 'fileA').read_text(newline='\r\n'), - 'abcde\r\nfghlk\n\rmnopq') - - def test_write_text_with_newlines(self): - p = self.cls(self.base) - # Check that `\n` character change nothing - (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\n') - self.assertEqual((p / 'fileA').read_bytes(), - b'abcde\r\nfghlk\n\rmnopq') - # Check that `\r` character replaces `\n` - (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r') - self.assertEqual((p / 'fileA').read_bytes(), - b'abcde\r\rfghlk\r\rmnopq') - # Check that `\r\n` character replaces `\n` - (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r\n') - self.assertEqual((p / 'fileA').read_bytes(), - b'abcde\r\r\nfghlk\r\n\rmnopq') - # Check that no argument passed will change `\n` to `os.linesep` - os_linesep_byte = bytes(os.linesep, encoding='ascii') - (p / 'fileA').write_text('abcde\nfghlk\n\rmnopq') - self.assertEqual((p / 'fileA').read_bytes(), - b'abcde' + os_linesep_byte + b'fghlk' + os_linesep_byte + b'\rmnopq') - - def test_copy_file(self): - base = self.cls(self.base) - source = base / 'fileA' - target = base / 'copyA' - result = source.copy(target) - self.assertEqual(result, target) - self.assertTrue(target.exists()) - self.assertEqual(source.read_text(), target.read_text()) - - def test_copy_file_to_existing_file(self): - base = self.cls(self.base) - source = base / 'fileA' - target = base / 'dirB' / 'fileB' - result = source.copy(target) - self.assertEqual(result, target) - self.assertTrue(target.exists()) - self.assertEqual(source.read_text(), target.read_text()) - - def test_copy_file_to_existing_directory(self): - base = self.cls(self.base) - source = base / 'fileA' - target = base / 'dirA' - self.assertRaises(OSError, source.copy, target) - - def test_copy_file_empty(self): - base = self.cls(self.base) - source = base / 'empty' - target = base / 'copyA' - source.write_bytes(b'') - result = source.copy(target) - self.assertEqual(result, target) - self.assertTrue(target.exists()) - self.assertEqual(target.read_bytes(), b'') - - def test_copy_file_to_itself(self): - base = self.cls(self.base) - source = base / 'empty' - source.write_bytes(b'') - self.assertRaises(OSError, source.copy, source) - self.assertRaises(OSError, source.copy, source, follow_symlinks=False) - - def test_copy_dir_simple(self): - base = self.cls(self.base) - source = base / 'dirC' - target = base / 'copyC' - result = source.copy(target) - self.assertEqual(result, target) - self.assertTrue(target.is_dir()) - self.assertTrue(target.joinpath('dirD').is_dir()) - self.assertTrue(target.joinpath('dirD', 'fileD').is_file()) - self.assertEqual(target.joinpath('dirD', 'fileD').read_text(), - "this is file D\n") - self.assertTrue(target.joinpath('fileC').is_file()) - self.assertTrue(target.joinpath('fileC').read_text(), - "this is file C\n") - - def test_copy_dir_complex(self, follow_symlinks=True): - def ordered_walk(path): - for dirpath, dirnames, filenames in path.walk(follow_symlinks=follow_symlinks): - dirnames.sort() - filenames.sort() - yield dirpath, dirnames, filenames - base = self.cls(self.base) - source = base / 'dirC' - - if self.can_symlink: - # Add some symlinks - source.joinpath('linkC').symlink_to('fileC') - source.joinpath('linkD').symlink_to('dirD', target_is_directory=True) - - # Perform the copy - target = base / 'copyC' - result = source.copy(target, follow_symlinks=follow_symlinks) - self.assertEqual(result, target) - - # Compare the source and target trees - source_walk = ordered_walk(source) - target_walk = ordered_walk(target) - for source_item, target_item in zip(source_walk, target_walk, strict=True): - self.assertEqual(source_item[0].parts[len(source.parts):], - target_item[0].parts[len(target.parts):]) # dirpath - self.assertEqual(source_item[1], target_item[1]) # dirnames - self.assertEqual(source_item[2], target_item[2]) # filenames - # Compare files and symlinks - for filename in source_item[2]: - source_file = source_item[0].joinpath(filename) - target_file = target_item[0].joinpath(filename) - if follow_symlinks or not source_file.is_symlink(): - # Regular file. - self.assertEqual(source_file.read_bytes(), target_file.read_bytes()) - elif source_file.is_dir(): - # Symlink to directory. - self.assertTrue(target_file.is_dir()) - self.assertEqual(source_file.readlink(), target_file.readlink()) - else: - # Symlink to file. - self.assertEqual(source_file.read_bytes(), target_file.read_bytes()) - self.assertEqual(source_file.readlink(), target_file.readlink()) - - def test_copy_dir_complex_follow_symlinks_false(self): - self.test_copy_dir_complex(follow_symlinks=False) - - def test_copy_dir_to_existing_directory(self): - base = self.cls(self.base) - source = base / 'dirC' - target = base / 'copyC' - target.mkdir() - target.joinpath('dirD').mkdir() - self.assertRaises(FileExistsError, source.copy, target) - - def test_copy_dir_to_existing_directory_dirs_exist_ok(self): - base = self.cls(self.base) - source = base / 'dirC' - target = base / 'copyC' - target.mkdir() - target.joinpath('dirD').mkdir() - result = source.copy(target, dirs_exist_ok=True) - self.assertEqual(result, target) - self.assertTrue(target.is_dir()) - self.assertTrue(target.joinpath('dirD').is_dir()) - self.assertTrue(target.joinpath('dirD', 'fileD').is_file()) - self.assertEqual(target.joinpath('dirD', 'fileD').read_text(), - "this is file D\n") - self.assertTrue(target.joinpath('fileC').is_file()) - self.assertTrue(target.joinpath('fileC').read_text(), - "this is file C\n") - - def test_copy_dir_to_itself(self): - base = self.cls(self.base) - source = base / 'dirC' - self.assertRaises(OSError, source.copy, source) - self.assertRaises(OSError, source.copy, source, follow_symlinks=False) - - def test_copy_dir_into_itself(self): - base = self.cls(self.base) - source = base / 'dirC' - target = base / 'dirC' / 'dirD' / 'copyC' - self.assertRaises(OSError, source.copy, target) - self.assertRaises(OSError, source.copy, target, follow_symlinks=False) - self.assertFalse(target.exists()) - - def test_copy_into(self): - base = self.cls(self.base) - source = base / 'fileA' - target_dir = base / 'dirA' - result = source.copy_into(target_dir) - self.assertEqual(result, target_dir / 'fileA') - self.assertTrue(result.exists()) - self.assertEqual(source.read_text(), result.read_text()) - - def test_copy_into_empty_name(self): - source = self.cls('') - target_dir = self.base - self.assertRaises(ValueError, source.copy_into, target_dir) - def test_iterdir(self): P = self.cls p = P(self.base) @@ -1574,9 +1359,220 @@ def test_is_symlink(self): self.assertIs((P / 'linkA\x00').is_file(), False) -class DummyPathWalkTest(unittest.TestCase): - cls = DummyPath - base = DummyPathTest.base +class DummyWritablePathTest(DummyReadablePathTest): + cls = DummyWritablePath + + def test_read_write_bytes(self): + p = self.cls(self.base) + (p / 'fileA').write_bytes(b'abcdefg') + self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') + # Check that trying to write str does not truncate the file. + self.assertRaises(TypeError, (p / 'fileA').write_bytes, 'somestr') + self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') + + def test_read_write_text(self): + p = self.cls(self.base) + (p / 'fileA').write_text('äbcdefg', encoding='latin-1') + self.assertEqual((p / 'fileA').read_text( + encoding='utf-8', errors='ignore'), 'bcdefg') + # Check that trying to write bytes does not truncate the file. + self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes') + self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), 'äbcdefg') + + def test_read_text_with_newlines(self): + p = self.cls(self.base) + # Check that `\n` character change nothing + (p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq') + self.assertEqual((p / 'fileA').read_text(newline='\n'), + 'abcde\r\nfghlk\n\rmnopq') + # Check that `\r` character replaces `\n` + (p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq') + self.assertEqual((p / 'fileA').read_text(newline='\r'), + 'abcde\r\nfghlk\n\rmnopq') + # Check that `\r\n` character replaces `\n` + (p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq') + self.assertEqual((p / 'fileA').read_text(newline='\r\n'), + 'abcde\r\nfghlk\n\rmnopq') + + def test_write_text_with_newlines(self): + p = self.cls(self.base) + # Check that `\n` character change nothing + (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\n') + self.assertEqual((p / 'fileA').read_bytes(), + b'abcde\r\nfghlk\n\rmnopq') + # Check that `\r` character replaces `\n` + (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r') + self.assertEqual((p / 'fileA').read_bytes(), + b'abcde\r\rfghlk\r\rmnopq') + # Check that `\r\n` character replaces `\n` + (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r\n') + self.assertEqual((p / 'fileA').read_bytes(), + b'abcde\r\r\nfghlk\r\n\rmnopq') + # Check that no argument passed will change `\n` to `os.linesep` + os_linesep_byte = bytes(os.linesep, encoding='ascii') + (p / 'fileA').write_text('abcde\nfghlk\n\rmnopq') + self.assertEqual((p / 'fileA').read_bytes(), + b'abcde' + os_linesep_byte + b'fghlk' + os_linesep_byte + b'\rmnopq') + + def test_copy_file(self): + base = self.cls(self.base) + source = base / 'fileA' + target = base / 'copyA' + result = source.copy(target) + self.assertEqual(result, target) + self.assertTrue(target.exists()) + self.assertEqual(source.read_text(), target.read_text()) + + def test_copy_file_to_existing_file(self): + base = self.cls(self.base) + source = base / 'fileA' + target = base / 'dirB' / 'fileB' + result = source.copy(target) + self.assertEqual(result, target) + self.assertTrue(target.exists()) + self.assertEqual(source.read_text(), target.read_text()) + + def test_copy_file_to_existing_directory(self): + base = self.cls(self.base) + source = base / 'fileA' + target = base / 'dirA' + self.assertRaises(OSError, source.copy, target) + + def test_copy_file_empty(self): + base = self.cls(self.base) + source = base / 'empty' + target = base / 'copyA' + source.write_bytes(b'') + result = source.copy(target) + self.assertEqual(result, target) + self.assertTrue(target.exists()) + self.assertEqual(target.read_bytes(), b'') + + def test_copy_file_to_itself(self): + base = self.cls(self.base) + source = base / 'empty' + source.write_bytes(b'') + self.assertRaises(OSError, source.copy, source) + self.assertRaises(OSError, source.copy, source, follow_symlinks=False) + + def test_copy_dir_simple(self): + base = self.cls(self.base) + source = base / 'dirC' + target = base / 'copyC' + result = source.copy(target) + self.assertEqual(result, target) + self.assertTrue(target.is_dir()) + self.assertTrue(target.joinpath('dirD').is_dir()) + self.assertTrue(target.joinpath('dirD', 'fileD').is_file()) + self.assertEqual(target.joinpath('dirD', 'fileD').read_text(), + "this is file D\n") + self.assertTrue(target.joinpath('fileC').is_file()) + self.assertTrue(target.joinpath('fileC').read_text(), + "this is file C\n") + + def test_copy_dir_complex(self, follow_symlinks=True): + def ordered_walk(path): + for dirpath, dirnames, filenames in path.walk(follow_symlinks=follow_symlinks): + dirnames.sort() + filenames.sort() + yield dirpath, dirnames, filenames + base = self.cls(self.base) + source = base / 'dirC' + + if self.can_symlink: + # Add some symlinks + source.joinpath('linkC').symlink_to('fileC') + source.joinpath('linkD').symlink_to('dirD', target_is_directory=True) + + # Perform the copy + target = base / 'copyC' + result = source.copy(target, follow_symlinks=follow_symlinks) + self.assertEqual(result, target) + + # Compare the source and target trees + source_walk = ordered_walk(source) + target_walk = ordered_walk(target) + for source_item, target_item in zip(source_walk, target_walk, strict=True): + self.assertEqual(source_item[0].parts[len(source.parts):], + target_item[0].parts[len(target.parts):]) # dirpath + self.assertEqual(source_item[1], target_item[1]) # dirnames + self.assertEqual(source_item[2], target_item[2]) # filenames + # Compare files and symlinks + for filename in source_item[2]: + source_file = source_item[0].joinpath(filename) + target_file = target_item[0].joinpath(filename) + if follow_symlinks or not source_file.is_symlink(): + # Regular file. + self.assertEqual(source_file.read_bytes(), target_file.read_bytes()) + elif source_file.is_dir(): + # Symlink to directory. + self.assertTrue(target_file.is_dir()) + self.assertEqual(source_file.readlink(), target_file.readlink()) + else: + # Symlink to file. + self.assertEqual(source_file.read_bytes(), target_file.read_bytes()) + self.assertEqual(source_file.readlink(), target_file.readlink()) + + def test_copy_dir_complex_follow_symlinks_false(self): + self.test_copy_dir_complex(follow_symlinks=False) + + def test_copy_dir_to_existing_directory(self): + base = self.cls(self.base) + source = base / 'dirC' + target = base / 'copyC' + target.mkdir() + target.joinpath('dirD').mkdir() + self.assertRaises(FileExistsError, source.copy, target) + + def test_copy_dir_to_existing_directory_dirs_exist_ok(self): + base = self.cls(self.base) + source = base / 'dirC' + target = base / 'copyC' + target.mkdir() + target.joinpath('dirD').mkdir() + result = source.copy(target, dirs_exist_ok=True) + self.assertEqual(result, target) + self.assertTrue(target.is_dir()) + self.assertTrue(target.joinpath('dirD').is_dir()) + self.assertTrue(target.joinpath('dirD', 'fileD').is_file()) + self.assertEqual(target.joinpath('dirD', 'fileD').read_text(), + "this is file D\n") + self.assertTrue(target.joinpath('fileC').is_file()) + self.assertTrue(target.joinpath('fileC').read_text(), + "this is file C\n") + + def test_copy_dir_to_itself(self): + base = self.cls(self.base) + source = base / 'dirC' + self.assertRaises(OSError, source.copy, source) + self.assertRaises(OSError, source.copy, source, follow_symlinks=False) + + def test_copy_dir_into_itself(self): + base = self.cls(self.base) + source = base / 'dirC' + target = base / 'dirC' / 'dirD' / 'copyC' + self.assertRaises(OSError, source.copy, target) + self.assertRaises(OSError, source.copy, target, follow_symlinks=False) + self.assertFalse(target.exists()) + + def test_copy_into(self): + base = self.cls(self.base) + source = base / 'fileA' + target_dir = base / 'dirA' + result = source.copy_into(target_dir) + self.assertEqual(result, target_dir / 'fileA') + self.assertTrue(result.exists()) + self.assertEqual(source.read_text(), result.read_text()) + + def test_copy_into_empty_name(self): + source = self.cls('') + target_dir = self.base + self.assertRaises(ValueError, source.copy_into, target_dir) + + +class DummyReadablePathWalkTest(unittest.TestCase): + cls = DummyReadablePath + base = DummyReadablePathTest.base can_symlink = False def setUp(self): From 30268b5d2fbb1a5e6c876f0cdc4cbdb5d93315e6 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Sun, 12 Jan 2025 09:27:28 +0100 Subject: [PATCH 266/376] gh-128377: Skip `test_cmd_line.test_non_interactive_output_buffering` when `PYTHONUNBUFFERED=1` (#128378) The `test_cmd_line.test_non_interactive_output_buffering` test assumes a buffered `stdio` without checking the `PYTHONUNBUFFERED` value. Instead of changing the environment variable for the duration of the test, it is better to simply skip it. --- Lib/test/test_cmd_line.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index dfdbb80b94d66f..24cf357c581096 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -336,6 +336,8 @@ def test_osx_android_utf8(self): self.assertEqual(stdout, expected) self.assertEqual(p.returncode, 0) + @unittest.skipIf(os.environ.get("PYTHONUNBUFFERED", "0") != "0", + "Python stdio buffering is disabled.") def test_non_interactive_output_buffering(self): code = textwrap.dedent(""" import sys From cb72feb8a30edb448bc8dc31330ed7420279d7c2 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 12 Jan 2025 12:22:43 +0200 Subject: [PATCH 267/376] gh-128729: Fix RuntimeWarning in test_unittest (GH-128730) --- Lib/test/test_unittest/test_case.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/test/test_unittest/test_case.py b/Lib/test/test_unittest/test_case.py index b4b2194a09cf9f..621f8269a177ce 100644 --- a/Lib/test/test_unittest/test_case.py +++ b/Lib/test/test_unittest/test_case.py @@ -347,7 +347,10 @@ async def test1(self): return 1 with self.assertWarns(DeprecationWarning) as w: + warnings.filterwarnings('ignore', + 'coroutine .* was never awaited', RuntimeWarning) Foo('test1').run() + support.gc_collect() self.assertIn('It is deprecated to return a value that is not None', str(w.warning)) self.assertIn('test1', str(w.warning)) self.assertEqual(w.filename, __file__) From 5ace71713b03cb37d829f50c849a8bb8a518738d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 12 Jan 2025 12:53:17 +0200 Subject: [PATCH 268/376] gh-128734: Fix ResourceWarning in urllib tests (GH-128735) --- Lib/test/test_urllib.py | 25 ++++++++++++++++--------- Lib/test/test_urllib2.py | 16 ++++++++++++---- Lib/test/test_urllib2_localnet.py | 16 +++++++++------- Lib/test/test_urllib_response.py | 2 ++ Lib/test/test_urllibnet.py | 1 + 5 files changed, 40 insertions(+), 20 deletions(-) diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 042d3b35b77022..4842428d6fd103 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -419,7 +419,9 @@ def test_read_bogus(self): Content-Type: text/html; charset=iso-8859-1 ''', mock_close=True) try: - self.assertRaises(OSError, urllib.request.urlopen, "http://python.org/") + with self.assertRaises(urllib.error.HTTPError) as cm: + urllib.request.urlopen("http://python.org/") + cm.exception.close() finally: self.unfakehttp() @@ -434,8 +436,9 @@ def test_invalid_redirect(self): ''', mock_close=True) try: msg = "Redirection to url 'file:" - with self.assertRaisesRegex(urllib.error.HTTPError, msg): + with self.assertRaisesRegex(urllib.error.HTTPError, msg) as cm: urllib.request.urlopen("http://python.org/") + cm.exception.close() finally: self.unfakehttp() @@ -448,8 +451,9 @@ def test_redirect_limit_independent(self): Connection: close ''', mock_close=True) try: - self.assertRaises(urllib.error.HTTPError, urllib.request.urlopen, - "http://something") + with self.assertRaises(urllib.error.HTTPError) as cm: + urllib.request.urlopen("http://something") + cm.exception.close() finally: self.unfakehttp() @@ -529,10 +533,11 @@ def setUp(self): "QOjdAAAAAXNSR0IArs4c6QAAAA9JREFUCNdj%0AYGBg%2BP//PwAGAQL%2BCm8 " "vHgAAAABJRU5ErkJggg%3D%3D%0A%20") - self.text_url_resp = urllib.request.urlopen(self.text_url) - self.text_url_base64_resp = urllib.request.urlopen( - self.text_url_base64) - self.image_url_resp = urllib.request.urlopen(self.image_url) + self.text_url_resp = self.enterContext( + urllib.request.urlopen(self.text_url)) + self.text_url_base64_resp = self.enterContext( + urllib.request.urlopen(self.text_url_base64)) + self.image_url_resp = self.enterContext(urllib.request.urlopen(self.image_url)) def test_interface(self): # Make sure object returned by urlopen() has the specified methods @@ -548,8 +553,10 @@ def test_info(self): [('text/plain', ''), ('charset', 'ISO-8859-1')]) self.assertEqual(self.image_url_resp.info()['content-length'], str(len(self.image))) - self.assertEqual(urllib.request.urlopen("data:,").info().get_params(), + r = urllib.request.urlopen("data:,") + self.assertEqual(r.info().get_params(), [('text/plain', ''), ('charset', 'US-ASCII')]) + r.close() def test_geturl(self): self.assertEqual(self.text_url_resp.geturl(), self.text_url) diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 085b24c25b2daa..44e6af8c6b6868 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -782,6 +782,7 @@ def connect_ftp(self, user, passwd, host, port, dirs, headers = r.info() self.assertEqual(headers.get("Content-type"), mimetype) self.assertEqual(int(headers["Content-length"]), len(data)) + r.close() @support.requires_resource("network") def test_ftp_error(self): @@ -1247,10 +1248,11 @@ def test_redirect(self): try: method(req, MockFile(), code, "Blah", MockHeaders({"location": to_url})) - except urllib.error.HTTPError: + except urllib.error.HTTPError as err: # 307 and 308 in response to POST require user OK self.assertIn(code, (307, 308)) self.assertIsNotNone(data) + err.close() self.assertEqual(o.req.get_full_url(), to_url) try: self.assertEqual(o.req.get_method(), "GET") @@ -1286,9 +1288,10 @@ def redirect(h, req, url=to_url): while 1: redirect(h, req, "http://example.com/") count = count + 1 - except urllib.error.HTTPError: + except urllib.error.HTTPError as err: # don't stop until max_repeats, because cookies may introduce state self.assertEqual(count, urllib.request.HTTPRedirectHandler.max_repeats) + err.close() # detect endless non-repeating chain of redirects req = Request(from_url, origin_req_host="example.com") @@ -1298,9 +1301,10 @@ def redirect(h, req, url=to_url): while 1: redirect(h, req, "http://example.com/%d" % count) count = count + 1 - except urllib.error.HTTPError: + except urllib.error.HTTPError as err: self.assertEqual(count, urllib.request.HTTPRedirectHandler.max_redirections) + err.close() def test_invalid_redirect(self): from_url = "http://example.com/a.html" @@ -1314,9 +1318,11 @@ def test_invalid_redirect(self): for scheme in invalid_schemes: invalid_url = scheme + '://' + schemeless_url - self.assertRaises(urllib.error.HTTPError, h.http_error_302, + with self.assertRaises(urllib.error.HTTPError) as cm: + h.http_error_302( req, MockFile(), 302, "Security Loophole", MockHeaders({"location": invalid_url})) + cm.exception.close() for scheme in valid_schemes: valid_url = scheme + '://' + schemeless_url @@ -1912,11 +1918,13 @@ def test_HTTPError_interface(self): self.assertEqual(str(err), expected_errmsg) expected_errmsg = '' % (err.code, err.msg) self.assertEqual(repr(err), expected_errmsg) + err.close() def test_gh_98778(self): x = urllib.error.HTTPError("url", 405, "METHOD NOT ALLOWED", None, None) self.assertEqual(getattr(x, "__notes__", ()), ()) self.assertIsInstance(x.fp.read(), bytes) + x.close() def test_parse_proxy(self): parse_proxy_test_cases = [ diff --git a/Lib/test/test_urllib2_localnet.py b/Lib/test/test_urllib2_localnet.py index 50c491a3cfd3d0..9cb15d61c2ad4d 100644 --- a/Lib/test/test_urllib2_localnet.py +++ b/Lib/test/test_urllib2_localnet.py @@ -316,7 +316,9 @@ def test_basic_auth_httperror(self): ah = urllib.request.HTTPBasicAuthHandler() ah.add_password(self.REALM, self.server_url, self.USER, self.INCORRECT_PASSWD) urllib.request.install_opener(urllib.request.build_opener(ah)) - self.assertRaises(urllib.error.HTTPError, urllib.request.urlopen, self.server_url) + with self.assertRaises(urllib.error.HTTPError) as cm: + urllib.request.urlopen(self.server_url) + cm.exception.close() @hashlib_helper.requires_hashdigest("md5", openssl=True) @@ -362,15 +364,15 @@ def test_proxy_with_bad_password_raises_httperror(self): self.proxy_digest_handler.add_password(self.REALM, self.URL, self.USER, self.PASSWD+"bad") self.digest_auth_handler.set_qop("auth") - self.assertRaises(urllib.error.HTTPError, - self.opener.open, - self.URL) + with self.assertRaises(urllib.error.HTTPError) as cm: + self.opener.open(self.URL) + cm.exception.close() def test_proxy_with_no_password_raises_httperror(self): self.digest_auth_handler.set_qop("auth") - self.assertRaises(urllib.error.HTTPError, - self.opener.open, - self.URL) + with self.assertRaises(urllib.error.HTTPError) as cm: + self.opener.open(self.URL) + cm.exception.close() def test_proxy_qop_auth_works(self): self.proxy_digest_handler.add_password(self.REALM, self.URL, diff --git a/Lib/test/test_urllib_response.py b/Lib/test/test_urllib_response.py index b76763f4ed824f..d949fa38bfc42f 100644 --- a/Lib/test/test_urllib_response.py +++ b/Lib/test/test_urllib_response.py @@ -48,6 +48,7 @@ def test_addinfo(self): info = urllib.response.addinfo(self.fp, self.test_headers) self.assertEqual(info.info(), self.test_headers) self.assertEqual(info.headers, self.test_headers) + info.close() def test_addinfourl(self): url = "http://www.python.org" @@ -60,6 +61,7 @@ def test_addinfourl(self): self.assertEqual(infourl.headers, self.test_headers) self.assertEqual(infourl.url, url) self.assertEqual(infourl.status, code) + infourl.close() def tearDown(self): self.sock.close() diff --git a/Lib/test/test_urllibnet.py b/Lib/test/test_urllibnet.py index f824dddf711761..ce4e60e3a8011d 100644 --- a/Lib/test/test_urllibnet.py +++ b/Lib/test/test_urllibnet.py @@ -106,6 +106,7 @@ def test_getcode(self): with urllib.request.urlopen(URL): pass self.assertEqual(e.exception.code, 404) + e.exception.close() @support.requires_resource('walltime') def test_bad_address(self): From ff39e3ff7bebc9d700d89c5cd22145db2c879cf2 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sun, 12 Jan 2025 14:01:49 +0100 Subject: [PATCH 269/376] gh-126703: Add freelist for `PyMethodObject` (#128594) --- Include/internal/pycore_freelist_state.h | 2 ++ .../2025-01-07-19-48-56.gh-issue-126703.0ISs-7.rst | 1 + Objects/classobject.c | 11 ++++++++--- Objects/object.c | 1 + 4 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-07-19-48-56.gh-issue-126703.0ISs-7.rst diff --git a/Include/internal/pycore_freelist_state.h b/Include/internal/pycore_freelist_state.h index a1a94c1f2dc880..2ccd1ac055b747 100644 --- a/Include/internal/pycore_freelist_state.h +++ b/Include/internal/pycore_freelist_state.h @@ -22,6 +22,7 @@ extern "C" { # define Py_futureiters_MAXFREELIST 255 # define Py_object_stack_chunks_MAXFREELIST 4 # define Py_unicode_writers_MAXFREELIST 1 +# define Py_pymethodobjects_MAXFREELIST 20 // A generic freelist of either PyObjects or other data structures. struct _Py_freelist { @@ -48,6 +49,7 @@ struct _Py_freelists { struct _Py_freelist futureiters; struct _Py_freelist object_stack_chunks; struct _Py_freelist unicode_writers; + struct _Py_freelist pymethodobjects; }; #ifdef __cplusplus diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-07-19-48-56.gh-issue-126703.0ISs-7.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-07-19-48-56.gh-issue-126703.0ISs-7.rst new file mode 100644 index 00000000000000..ecb8eddb13c649 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-07-19-48-56.gh-issue-126703.0ISs-7.rst @@ -0,0 +1 @@ +Improve performance of class methods by using a freelist. diff --git a/Objects/classobject.c b/Objects/classobject.c index 775894ad5a7166..58e1d17977322e 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -3,6 +3,7 @@ #include "Python.h" #include "pycore_call.h" // _PyObject_VectorcallTstate() #include "pycore_ceval.h" // _PyEval_GetBuiltin() +#include "pycore_freelist.h" #include "pycore_object.h" #include "pycore_pyerrors.h" #include "pycore_pystate.h" // _PyThreadState_GET() @@ -112,9 +113,12 @@ PyMethod_New(PyObject *func, PyObject *self) PyErr_BadInternalCall(); return NULL; } - PyMethodObject *im = PyObject_GC_New(PyMethodObject, &PyMethod_Type); + PyMethodObject *im = _Py_FREELIST_POP(PyMethodObject, pymethodobjects); if (im == NULL) { - return NULL; + im = PyObject_GC_New(PyMethodObject, &PyMethod_Type); + if (im == NULL) { + return NULL; + } } im->im_weakreflist = NULL; im->im_func = Py_NewRef(func); @@ -245,7 +249,8 @@ method_dealloc(PyObject *self) PyObject_ClearWeakRefs((PyObject *)im); Py_DECREF(im->im_func); Py_XDECREF(im->im_self); - PyObject_GC_Del(im); + assert(Py_IS_TYPE(self, &PyMethod_Type)); + _Py_FREELIST_FREE(pymethodobjects, (PyObject *)im, PyObject_GC_Del); } static PyObject * diff --git a/Objects/object.c b/Objects/object.c index 4c30257ca26938..9befd92e3231c8 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -937,6 +937,7 @@ _PyObject_ClearFreeLists(struct _Py_freelists *freelists, int is_finalization) } clear_freelist(&freelists->unicode_writers, is_finalization, PyMem_Free); clear_freelist(&freelists->ints, is_finalization, free_object); + clear_freelist(&freelists->pymethodobjects, is_finalization, free_object); } /* From f6c61bf2d7d8b66ccd9f16e723546bdcc251a3d0 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Sun, 12 Jan 2025 08:04:30 -0500 Subject: [PATCH 270/376] gh-128717: Stop-the-world when setting the recursion limit (#128741) --- Lib/test/test_free_threading/test_races.py | 15 +++++++++++++++ ...2025-01-11-12-39-17.gh-issue-128717.i65d06.rst | 2 ++ Python/ceval.c | 2 ++ 3 files changed, 19 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-11-12-39-17.gh-issue-128717.i65d06.rst diff --git a/Lib/test/test_free_threading/test_races.py b/Lib/test/test_free_threading/test_races.py index 69982558a067a5..85aa69c8cd494f 100644 --- a/Lib/test/test_free_threading/test_races.py +++ b/Lib/test/test_free_threading/test_races.py @@ -270,6 +270,21 @@ def mutate(): do_race(set_value, mutate) + def test_racing_recursion_limit(self): + def something_recursive(): + def count(n): + if n > 0: + return count(n - 1) + 1 + return 0 + + count(50) + + def set_recursion_limit(): + for limit in range(100, 200): + sys.setrecursionlimit(limit) + + do_race(something_recursive, set_recursion_limit) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-11-12-39-17.gh-issue-128717.i65d06.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-11-12-39-17.gh-issue-128717.i65d06.rst new file mode 100644 index 00000000000000..212c6d3cb97216 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-11-12-39-17.gh-issue-128717.i65d06.rst @@ -0,0 +1,2 @@ +Fix a crash when setting the recursion limit while other threads are active +on the :term:`free threaded ` build. diff --git a/Python/ceval.c b/Python/ceval.c index e92a11b16cec81..e0362c3c89fe6a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -294,6 +294,7 @@ void Py_SetRecursionLimit(int new_limit) { PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyEval_StopTheWorld(interp); interp->ceval.recursion_limit = new_limit; _Py_FOR_EACH_TSTATE_BEGIN(interp, p) { int depth = p->py_recursion_limit - p->py_recursion_remaining; @@ -301,6 +302,7 @@ Py_SetRecursionLimit(int new_limit) p->py_recursion_remaining = new_limit - depth; } _Py_FOR_EACH_TSTATE_END(interp); + _PyEval_StartTheWorld(interp); } /* The function _Py_EnterRecursiveCallTstate() only calls _Py_CheckRecursiveCall() From 5e65a1acc0b630397f1d190aed279114e6e99612 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 12 Jan 2025 15:14:46 +0200 Subject: [PATCH 271/376] gh-128731: Fix ResourceWarning in robotparser.RobotFileParser.read() (GH-128733) --- Lib/urllib/robotparser.py | 1 + .../next/Library/2025-01-11-13-40-12.gh-issue-128731.qpKlai.rst | 1 + 2 files changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-01-11-13-40-12.gh-issue-128731.qpKlai.rst diff --git a/Lib/urllib/robotparser.py b/Lib/urllib/robotparser.py index c58565e3945146..24ee198c355f56 100644 --- a/Lib/urllib/robotparser.py +++ b/Lib/urllib/robotparser.py @@ -65,6 +65,7 @@ def read(self): self.disallow_all = True elif err.code >= 400 and err.code < 500: self.allow_all = True + err.close() else: raw = f.read() self.parse(raw.decode("utf-8").splitlines()) diff --git a/Misc/NEWS.d/next/Library/2025-01-11-13-40-12.gh-issue-128731.qpKlai.rst b/Misc/NEWS.d/next/Library/2025-01-11-13-40-12.gh-issue-128731.qpKlai.rst new file mode 100644 index 00000000000000..b23499ca2fea3a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-11-13-40-12.gh-issue-128731.qpKlai.rst @@ -0,0 +1 @@ +Fix :exc:`ResourceWarning` in :meth:`urllib.robotparser.RobotFileParser.read`. From d0ecbdd838034c1f061e9b4e9b54a900141458c3 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 13 Jan 2025 00:09:39 +0100 Subject: [PATCH 272/376] gh-128627: Emscripten: Use wasm-gc based call adaptor if available (#128628) Replaces the trampoline mechanism in Emscripten with an implementation that uses a recently added feature of wasm-gc instead of JS type reflection, when that feature is available. --- .../internal/pycore_emscripten_trampoline.h | 24 +- Include/internal/pycore_runtime.h | 2 +- ...-01-09-19-44-00.gh-issue-128627.mHzsEd.rst | 3 + Python/emscripten_trampoline.c | 229 ++++++++++++++---- 4 files changed, 187 insertions(+), 71 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2025-01-09-19-44-00.gh-issue-128627.mHzsEd.rst diff --git a/Include/internal/pycore_emscripten_trampoline.h b/Include/internal/pycore_emscripten_trampoline.h index e519c99ad86cce..5546ebbbfcb5c1 100644 --- a/Include/internal/pycore_emscripten_trampoline.h +++ b/Include/internal/pycore_emscripten_trampoline.h @@ -27,24 +27,14 @@ #if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) -void _Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime); +void +_Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime); PyObject* -_PyEM_TrampolineCall_JavaScript(PyCFunctionWithKeywords func, - PyObject* self, - PyObject* args, - PyObject* kw); - -PyObject* -_PyEM_TrampolineCall_Reflection(PyCFunctionWithKeywords func, - PyObject* self, - PyObject* args, - PyObject* kw); - -#define _PyEM_TrampolineCall(meth, self, args, kw) \ - ((_PyRuntime.wasm_type_reflection_available) ? \ - (_PyEM_TrampolineCall_Reflection((PyCFunctionWithKeywords)(meth), (self), (args), (kw))) : \ - (_PyEM_TrampolineCall_JavaScript((PyCFunctionWithKeywords)(meth), (self), (args), (kw)))) +_PyEM_TrampolineCall(PyCFunctionWithKeywords func, + PyObject* self, + PyObject* args, + PyObject* kw); #define _PyCFunction_TrampolineCall(meth, self, args) \ _PyEM_TrampolineCall( \ @@ -62,8 +52,6 @@ _PyEM_TrampolineCall_Reflection(PyCFunctionWithKeywords func, #else // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) -#define _Py_EmscriptenTrampoline_Init(runtime) - #define _PyCFunction_TrampolineCall(meth, self, args) \ (meth)((self), (args)) diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 86d024535fdda8..cf123791eba9ac 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -172,7 +172,7 @@ typedef struct pyruntimestate { #if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) // Used in "Python/emscripten_trampoline.c" to choose between type // reflection trampoline and EM_JS trampoline. - bool wasm_type_reflection_available; + int (*emscripten_count_args_function)(PyCFunctionWithKeywords func); #endif /* All the objects that are shared by the runtime's interpreters. */ diff --git a/Misc/NEWS.d/next/Build/2025-01-09-19-44-00.gh-issue-128627.mHzsEd.rst b/Misc/NEWS.d/next/Build/2025-01-09-19-44-00.gh-issue-128627.mHzsEd.rst new file mode 100644 index 00000000000000..a8c80ab6804b02 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-01-09-19-44-00.gh-issue-128627.mHzsEd.rst @@ -0,0 +1,3 @@ +For Emscripten builds the function pointer cast call trampoline now uses the +wasm-gc ref.test instruction if it's available instead of Wasm JS type +reflection. diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c index 960c6b4a2ef995..2f9648a12ef2e4 100644 --- a/Python/emscripten_trampoline.c +++ b/Python/emscripten_trampoline.c @@ -1,79 +1,205 @@ #if defined(PY_CALL_TRAMPOLINE) -#include // EM_JS +#include // EM_JS, EM_JS_DEPS #include #include "pycore_runtime.h" // _PyRuntime +typedef int (*CountArgsFunc)(PyCFunctionWithKeywords func); -/** - * This is the GoogleChromeLabs approved way to feature detect type-reflection: - * https://github.com/GoogleChromeLabs/wasm-feature-detect/blob/main/src/detectors/type-reflection/index.js - */ -EM_JS(int, _PyEM_detect_type_reflection, (), { - if (!("Function" in WebAssembly)) { - return false; - } - if (WebAssembly.Function.type) { - // Node v20 - Module.PyEM_CountArgs = (func) => WebAssembly.Function.type(wasmTable.get(func)).parameters.length; - } else { - // Node >= 22, v8-based browsers - Module.PyEM_CountArgs = (func) => wasmTable.get(func).type().parameters.length; +// Offset of emscripten_count_args_function in _PyRuntimeState. There's a couple +// of alternatives: +// 1. Just make emscripten_count_args_function a real C global variable instead +// of a field of _PyRuntimeState. This would violate our rule against mutable +// globals. +// 2. #define a preprocessor constant equal to a hard coded number and make a +// _Static_assert(offsetof(_PyRuntimeState, emscripten_count_args_function) +// == OURCONSTANT) This has the disadvantage that we have to update the hard +// coded constant when _PyRuntimeState changes +// +// So putting the mutable constant in _PyRuntime and using a immutable global to +// record the offset so we can access it from JS is probably the best way. +EMSCRIPTEN_KEEPALIVE const int _PyEM_EMSCRIPTEN_COUNT_ARGS_OFFSET = offsetof(_PyRuntimeState, emscripten_count_args_function); + +EM_JS(CountArgsFunc, _PyEM_GetCountArgsPtr, (), { + return Module._PyEM_CountArgsPtr; // initialized below +} +// Binary module for the checks. It has to be done in web assembly because +// clang/llvm have no support yet for the reference types yet. In fact, the wasm +// binary toolkit doesn't yet support the ref.test instruction either. To +// convert the following module to the binary, my approach is to find and +// replace "ref.test $type" -> "drop i32.const n" on the source text. This +// results in the bytes "0x1a, 0x41, n" where we need the bytes "0xfb, 0x14, n" +// so doing a find and replace on the output from "0x1a, 0x41" -> "0xfb, 0x14" +// gets us the output we need. +// +// (module +// (type $type0 (func (param) (result i32))) +// (type $type1 (func (param i32) (result i32))) +// (type $type2 (func (param i32 i32) (result i32))) +// (type $type3 (func (param i32 i32 i32) (result i32))) +// (type $blocktype (func (param i32) (result))) +// (table $funcs (import "e" "t") 0 funcref) +// (export "f" (func $f)) +// (func $f (param $fptr i32) (result i32) +// (local $fref funcref) +// local.get $fptr +// table.get $funcs +// local.tee $fref +// ref.test $type3 +// (block $b (type $blocktype) +// i32.eqz +// br_if $b +// i32.const 3 +// return +// ) +// local.get $fref +// ref.test $type2 +// (block $b (type $blocktype) +// i32.eqz +// br_if $b +// i32.const 2 +// return +// ) +// local.get $fref +// ref.test $type1 +// (block $b (type $blocktype) +// i32.eqz +// br_if $b +// i32.const 1 +// return +// ) +// local.get $fref +// ref.test $type0 +// (block $b (type $blocktype) +// i32.eqz +// br_if $b +// i32.const 0 +// return +// ) +// i32.const -1 +// ) +// ) +addOnPreRun(() => { + // Try to initialize countArgsFunc + const code = new Uint8Array([ + 0x00, 0x61, 0x73, 0x6d, // \0asm magic number + 0x01, 0x00, 0x00, 0x00, // version 1 + 0x01, 0x1b, // Type section, body is 0x1b bytes + 0x05, // 6 entries + 0x60, 0x00, 0x01, 0x7f, // (type $type0 (func (param) (result i32))) + 0x60, 0x01, 0x7f, 0x01, 0x7f, // (type $type1 (func (param i32) (result i32))) + 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, // (type $type2 (func (param i32 i32) (result i32))) + 0x60, 0x03, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, // (type $type3 (func (param i32 i32 i32) (result i32))) + 0x60, 0x01, 0x7f, 0x00, // (type $blocktype (func (param i32) (result))) + 0x02, 0x09, // Import section, 0x9 byte body + 0x01, // 1 import (table $funcs (import "e" "t") 0 funcref) + 0x01, 0x65, // "e" + 0x01, 0x74, // "t" + 0x01, // importing a table + 0x70, // of entry type funcref + 0x00, 0x00, // table limits: no max, min of 0 + 0x03, 0x02, // Function section + 0x01, 0x01, // We're going to define one function of type 1 (func (param i32) (result i32)) + 0x07, 0x05, // export section + 0x01, // 1 export + 0x01, 0x66, // called "f" + 0x00, // a function + 0x00, // at index 0 + + 0x0a, 0x44, // Code section, + 0x01, 0x42, // one entry of length 50 + 0x01, 0x01, 0x70, // one local of type funcref + // Body of the function + 0x20, 0x00, // local.get $fptr + 0x25, 0x00, // table.get $funcs + 0x22, 0x01, // local.tee $fref + 0xfb, 0x14, 0x03, // ref.test $type3 + 0x02, 0x04, // block $b (type $blocktype) + 0x45, // i32.eqz + 0x0d, 0x00, // br_if $b + 0x41, 0x03, // i32.const 3 + 0x0f, // return + 0x0b, // end block + + 0x20, 0x01, // local.get $fref + 0xfb, 0x14, 0x02, // ref.test $type2 + 0x02, 0x04, // block $b (type $blocktype) + 0x45, // i32.eqz + 0x0d, 0x00, // br_if $b + 0x41, 0x02, // i32.const 2 + 0x0f, // return + 0x0b, // end block + + 0x20, 0x01, // local.get $fref + 0xfb, 0x14, 0x01, // ref.test $type1 + 0x02, 0x04, // block $b (type $blocktype) + 0x45, // i32.eqz + 0x0d, 0x00, // br_if $b + 0x41, 0x01, // i32.const 1 + 0x0f, // return + 0x0b, // end block + + 0x20, 0x01, // local.get $fref + 0xfb, 0x14, 0x00, // ref.test $type0 + 0x02, 0x04, // block $b (type $blocktype) + 0x45, // i32.eqz + 0x0d, 0x00, // br_if $b + 0x41, 0x00, // i32.const 0 + 0x0f, // return + 0x0b, // end block + + 0x41, 0x7f, // i32.const -1 + 0x0b // end function + ]); + let ptr = 0; + try { + const mod = new WebAssembly.Module(code); + const inst = new WebAssembly.Instance(mod, {e: {t: wasmTable}}); + ptr = addFunction(inst.exports.f); + } catch(e) { + // If something goes wrong, we'll null out _PyEM_CountFuncParams and fall + // back to the JS trampoline. } - return true; + Module._PyEM_CountArgsPtr = ptr; + const offset = HEAP32[__PyEM_EMSCRIPTEN_COUNT_ARGS_OFFSET/4]; + HEAP32[__PyRuntime/4 + offset] = ptr; }); +); void _Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime) { - runtime->wasm_type_reflection_available = _PyEM_detect_type_reflection(); + runtime->emscripten_count_args_function = _PyEM_GetCountArgsPtr(); } +// We have to be careful to work correctly with memory snapshots. Even if we are +// loading a memory snapshot, we need to perform the JS initialization work. +// That means we can't call the initialization code from C. Instead, we export +// this function pointer to JS and then fill it in a preRun function which runs +// unconditionally. /** * Backwards compatible trampoline works with all JS runtimes */ -EM_JS(PyObject*, -_PyEM_TrampolineCall_JavaScript, (PyCFunctionWithKeywords func, - PyObject *arg1, - PyObject *arg2, - PyObject *arg3), -{ +EM_JS(PyObject*, _PyEM_TrampolineCall_JS, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), { return wasmTable.get(func)(arg1, arg2, arg3); -} -); - -/** - * In runtimes with WebAssembly type reflection, count the number of parameters - * and cast to the appropriate signature - */ -EM_JS(int, _PyEM_CountFuncParams, (PyCFunctionWithKeywords func), -{ - let n = _PyEM_CountFuncParams.cache.get(func); - - if (n !== undefined) { - return n; - } - n = Module.PyEM_CountArgs(func); - _PyEM_CountFuncParams.cache.set(func, n); - return n; -} -_PyEM_CountFuncParams.cache = new Map(); -) - +}); typedef PyObject* (*zero_arg)(void); typedef PyObject* (*one_arg)(PyObject*); typedef PyObject* (*two_arg)(PyObject*, PyObject*); typedef PyObject* (*three_arg)(PyObject*, PyObject*, PyObject*); - PyObject* -_PyEM_TrampolineCall_Reflection(PyCFunctionWithKeywords func, - PyObject* self, - PyObject* args, - PyObject* kw) +_PyEM_TrampolineCall(PyCFunctionWithKeywords func, + PyObject* self, + PyObject* args, + PyObject* kw) { - switch (_PyEM_CountFuncParams(func)) { + CountArgsFunc count_args = _PyRuntime.emscripten_count_args_function; + if (count_args == 0) { + return _PyEM_TrampolineCall_JS(func, self, args, kw); + } + switch (count_args(func)) { case 0: return ((zero_arg)func)(); case 1: @@ -83,8 +209,7 @@ _PyEM_TrampolineCall_Reflection(PyCFunctionWithKeywords func, case 3: return ((three_arg)func)(self, args, kw); default: - PyErr_SetString(PyExc_SystemError, - "Handler takes too many arguments"); + PyErr_SetString(PyExc_SystemError, "Handler takes too many arguments"); return NULL; } } From 6e1e78054060ad326f26dd8dbf12adfedbb52883 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 13 Jan 2025 10:40:52 +0300 Subject: [PATCH 273/376] gh-128759: fix data race in `type_modified_unlocked` (#128764) --- Objects/typeobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 680846f1c0b865..0f5ebc6f90773d 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1038,7 +1038,7 @@ type_modified_unlocked(PyTypeObject *type) We don't assign new version tags eagerly, but only as needed. */ - if (type->tp_version_tag == 0) { + if (FT_ATOMIC_LOAD_UINT_RELAXED(type->tp_version_tag) == 0) { return; } // Cannot modify static builtin types. From 6ecb620a0cae1b0b6183d1e6d0ae722013ca8cd2 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Mon, 13 Jan 2025 02:38:28 -0600 Subject: [PATCH 274/376] gh-90905: Allow cross-compilation on macOS (#128385) --- .../2024-12-31-17-09-37.gh-issue-90905.PjLNai.rst | 1 + configure | 12 ++++++++++++ configure.ac | 12 ++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 Misc/NEWS.d/next/Build/2024-12-31-17-09-37.gh-issue-90905.PjLNai.rst diff --git a/Misc/NEWS.d/next/Build/2024-12-31-17-09-37.gh-issue-90905.PjLNai.rst b/Misc/NEWS.d/next/Build/2024-12-31-17-09-37.gh-issue-90905.PjLNai.rst new file mode 100644 index 00000000000000..31bb5fe005379a --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-12-31-17-09-37.gh-issue-90905.PjLNai.rst @@ -0,0 +1 @@ +Add support for cross-compiling to x86_64 on aarch64/arm64 macOS. diff --git a/configure b/configure index bb77c558abda5a..70581e11b60682 100755 --- a/configure +++ b/configure @@ -4097,6 +4097,9 @@ then *-apple-ios*) ac_sys_system=iOS ;; + *-*-darwin*) + ac_sys_system=Darwin + ;; *-*-vxworks*) ac_sys_system=VxWorks ;; @@ -4591,6 +4594,15 @@ printf "%s\n" "$IPHONEOS_DEPLOYMENT_TARGET" >&6; } ;; esac ;; + *-*-darwin*) + case "$host_cpu" in + arm*) + _host_ident=arm + ;; + *) + _host_ident=$host_cpu + esac + ;; *-*-vxworks*) _host_ident=$host_cpu ;; diff --git a/configure.ac b/configure.ac index 653cd3f6c531b6..d7c3920d049d67 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,9 @@ then *-apple-ios*) ac_sys_system=iOS ;; + *-*-darwin*) + ac_sys_system=Darwin + ;; *-*-vxworks*) ac_sys_system=VxWorks ;; @@ -790,6 +793,15 @@ if test "$cross_compiling" = yes; then ;; esac ;; + *-*-darwin*) + case "$host_cpu" in + arm*) + _host_ident=arm + ;; + *) + _host_ident=$host_cpu + esac + ;; *-*-vxworks*) _host_ident=$host_cpu ;; From b00e1254fc00941bf91e41138940e73fd22e1cbf Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Mon, 13 Jan 2025 04:02:18 -0500 Subject: [PATCH 275/376] gh-128690: temporarily do not use test_embed in PGO profile builds (#128771) Temporarily do not use test_embed in PGO profile builds until the problem with test_init_pyvenv_cfg failing in some configurations is resolved. --- Lib/test/libregrtest/pgo.py | 1 - .../next/Tests/2025-01-13-01-29-08.gh-issue-128690.cPFVDb.rst | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Tests/2025-01-13-01-29-08.gh-issue-128690.cPFVDb.rst diff --git a/Lib/test/libregrtest/pgo.py b/Lib/test/libregrtest/pgo.py index f762345c88cde3..04803ddf64453c 100644 --- a/Lib/test/libregrtest/pgo.py +++ b/Lib/test/libregrtest/pgo.py @@ -19,7 +19,6 @@ 'test_datetime', 'test_decimal', 'test_difflib', - 'test_embed', 'test_float', 'test_fstring', 'test_functools', diff --git a/Misc/NEWS.d/next/Tests/2025-01-13-01-29-08.gh-issue-128690.cPFVDb.rst b/Misc/NEWS.d/next/Tests/2025-01-13-01-29-08.gh-issue-128690.cPFVDb.rst new file mode 100644 index 00000000000000..fb84334ca77214 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2025-01-13-01-29-08.gh-issue-128690.cPFVDb.rst @@ -0,0 +1,2 @@ +Temporarily do not use test_embed in PGO profile builds until the problem +with test_init_pyvenv_cfg failing in some configurations is resolved. From 29fe8072cf404b891dde9c1d415095edddbe19de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:21:22 +0100 Subject: [PATCH 276/376] gh-123345: improve `fnmatch` docs (#123346) --- Doc/library/fnmatch.rst | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Doc/library/fnmatch.rst b/Doc/library/fnmatch.rst index fda44923f204fc..5cb47777ae527d 100644 --- a/Doc/library/fnmatch.rst +++ b/Doc/library/fnmatch.rst @@ -46,9 +46,15 @@ module. See module :mod:`glob` for pathname expansion (:mod:`glob` uses a period are not special for this module, and are matched by the ``*`` and ``?`` patterns. -Also note that :func:`functools.lru_cache` with the *maxsize* of 32768 is used to -cache the compiled regex patterns in the following functions: :func:`fnmatch`, -:func:`fnmatchcase`, :func:`.filter`. +Unless stated otherwise, "filename string" and "pattern string" either refer to +:class:`str` or ``ISO-8859-1`` encoded :class:`bytes` objects. Note that the +functions documented below do not allow to mix a :class:`!bytes` pattern with +a :class:`!str` filename, and vice-versa. + +Finally, note that :func:`functools.lru_cache` with a *maxsize* of 32768 +is used to cache the (typed) compiled regex patterns in the following +functions: :func:`fnmatch`, :func:`fnmatchcase`, :func:`.filter`. + .. function:: fnmatch(name, pat) @@ -78,8 +84,8 @@ cache the compiled regex patterns in the following functions: :func:`fnmatch`, .. function:: filter(names, pat) - Construct a list from those elements of the :term:`iterable` *names* - that match pattern *pat*. + Construct a list from those elements of the :term:`iterable` of filename + strings *names* that match the pattern string *pat*. It is the same as ``[n for n in names if fnmatch(n, pat)]``, but implemented more efficiently. @@ -87,7 +93,7 @@ cache the compiled regex patterns in the following functions: :func:`fnmatch`, .. function:: translate(pat) Return the shell-style pattern *pat* converted to a regular expression for - using with :func:`re.match`. + using with :func:`re.match`. The pattern is expected to be a :class:`str`. Example: From ddd959987c557beaf823b681bf5e5e573ad657ac Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 13 Jan 2025 10:30:28 +0000 Subject: [PATCH 277/376] GH-128685: Specialize (rather than quicken) LOAD_CONST into LOAD_CONST_[IM]MORTAL (GH-128708) --- Include/internal/pycore_magic_number.h | 3 +- Include/internal/pycore_opcode_metadata.h | 16 +++++++-- Include/internal/pycore_uop_ids.h | 1 + Include/internal/pycore_uop_metadata.h | 6 ++-- Include/opcode_ids.h | 41 +++++++++++----------- Lib/_opcode_metadata.py | 42 ++++++++++++----------- Lib/test/test_dis.py | 4 +-- Python/bytecodes.c | 18 ++++++++-- Python/executor_cases.c.h | 7 ++-- Python/generated_cases.c.h | 26 +++++++++++++- Python/opcode_targets.h | 2 +- Python/optimizer_bytecodes.c | 7 ++++ Python/optimizer_cases.c.h | 4 ++- Python/specialize.c | 9 ----- 14 files changed, 121 insertions(+), 65 deletions(-) diff --git a/Include/internal/pycore_magic_number.h b/Include/internal/pycore_magic_number.h index f9f71d7453331e..8b3d6285c1e4e7 100644 --- a/Include/internal/pycore_magic_number.h +++ b/Include/internal/pycore_magic_number.h @@ -265,6 +265,7 @@ Known values: Python 3.14a4 3610 (Add VALUE_WITH_FAKE_GLOBALS format to annotationlib) Python 3.14a4 3611 (Add NOT_TAKEN instruction) Python 3.14a4 3612 (Add POP_ITER and INSTRUMENTED_POP_ITER) + Python 3.14a4 3613 (Add LOAD_CONST_MORTAL instruction) Python 3.15 will start with 3650 @@ -277,7 +278,7 @@ PC/launcher.c must also be updated. */ -#define PYC_MAGIC_NUMBER 3612 +#define PYC_MAGIC_NUMBER 3613 /* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes (little-endian) and then appending b'\r\n'. */ #define PYC_MAGIC_NUMBER_TOKEN \ diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 90d5e277d8d6ce..51d6e435948225 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -321,6 +321,8 @@ int _PyOpcode_num_popped(int opcode, int oparg) { return 0; case LOAD_CONST_IMMORTAL: return 0; + case LOAD_CONST_MORTAL: + return 0; case LOAD_DEREF: return 0; case LOAD_FAST: @@ -788,6 +790,8 @@ int _PyOpcode_num_pushed(int opcode, int oparg) { return 1; case LOAD_CONST_IMMORTAL: return 1; + case LOAD_CONST_MORTAL: + return 1; case LOAD_DEREF: return 1; case LOAD_FAST: @@ -1559,6 +1563,10 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) { *effect = 1; return 0; } + case LOAD_CONST_MORTAL: { + *effect = 1; + return 0; + } case LOAD_DEREF: { *effect = 1; return 0; @@ -2109,8 +2117,9 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [LOAD_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, [LOAD_BUILD_CLASS] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [LOAD_COMMON_CONSTANT] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, - [LOAD_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG | HAS_PURE_FLAG }, + [LOAD_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG }, [LOAD_CONST_IMMORTAL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG }, + [LOAD_CONST_MORTAL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG }, [LOAD_DEREF] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [LOAD_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_PURE_FLAG }, [LOAD_FAST_AND_CLEAR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG }, @@ -2317,8 +2326,8 @@ _PyOpcode_macro_expansion[256] = { [LOAD_ATTR_WITH_HINT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_WITH_HINT, 0, 0 }, { _LOAD_ATTR_WITH_HINT, 1, 3 } } }, [LOAD_BUILD_CLASS] = { .nuops = 1, .uops = { { _LOAD_BUILD_CLASS, 0, 0 } } }, [LOAD_COMMON_CONSTANT] = { .nuops = 1, .uops = { { _LOAD_COMMON_CONSTANT, 0, 0 } } }, - [LOAD_CONST] = { .nuops = 1, .uops = { { _LOAD_CONST, 0, 0 } } }, [LOAD_CONST_IMMORTAL] = { .nuops = 1, .uops = { { _LOAD_CONST_IMMORTAL, 0, 0 } } }, + [LOAD_CONST_MORTAL] = { .nuops = 1, .uops = { { _LOAD_CONST_MORTAL, 0, 0 } } }, [LOAD_DEREF] = { .nuops = 1, .uops = { { _LOAD_DEREF, 0, 0 } } }, [LOAD_FAST] = { .nuops = 1, .uops = { { _LOAD_FAST, 0, 0 } } }, [LOAD_FAST_AND_CLEAR] = { .nuops = 1, .uops = { { _LOAD_FAST_AND_CLEAR, 0, 0 } } }, @@ -2541,6 +2550,7 @@ const char *_PyOpcode_OpName[266] = { [LOAD_COMMON_CONSTANT] = "LOAD_COMMON_CONSTANT", [LOAD_CONST] = "LOAD_CONST", [LOAD_CONST_IMMORTAL] = "LOAD_CONST_IMMORTAL", + [LOAD_CONST_MORTAL] = "LOAD_CONST_MORTAL", [LOAD_DEREF] = "LOAD_DEREF", [LOAD_FAST] = "LOAD_FAST", [LOAD_FAST_AND_CLEAR] = "LOAD_FAST_AND_CLEAR", @@ -2796,6 +2806,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [LOAD_COMMON_CONSTANT] = LOAD_COMMON_CONSTANT, [LOAD_CONST] = LOAD_CONST, [LOAD_CONST_IMMORTAL] = LOAD_CONST, + [LOAD_CONST_MORTAL] = LOAD_CONST, [LOAD_DEREF] = LOAD_DEREF, [LOAD_FAST] = LOAD_FAST, [LOAD_FAST_AND_CLEAR] = LOAD_FAST_AND_CLEAR, @@ -2912,7 +2923,6 @@ const uint8_t _PyOpcode_Deopt[256] = { case 146: \ case 147: \ case 148: \ - case 228: \ case 229: \ case 230: \ case 231: \ diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 21690a28839565..3841363b411eed 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -206,6 +206,7 @@ extern "C" { #define _LOAD_CONST_INLINE_BORROW 430 #define _LOAD_CONST_INLINE_BORROW_WITH_NULL 431 #define _LOAD_CONST_INLINE_WITH_NULL 432 +#define _LOAD_CONST_MORTAL LOAD_CONST_MORTAL #define _LOAD_DEREF LOAD_DEREF #define _LOAD_FAST 433 #define _LOAD_FAST_0 434 diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 83e578cdd76fbd..942e5eb25b76b4 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -35,7 +35,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_PURE_FLAG, [_LOAD_FAST_AND_CLEAR] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, [_LOAD_FAST_LOAD_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, - [_LOAD_CONST] = HAS_ARG_FLAG | HAS_CONST_FLAG | HAS_PURE_FLAG, + [_LOAD_CONST_MORTAL] = HAS_ARG_FLAG | HAS_CONST_FLAG, [_LOAD_CONST_IMMORTAL] = HAS_ARG_FLAG | HAS_CONST_FLAG, [_LOAD_SMALL_INT_0] = 0, [_LOAD_SMALL_INT_1] = 0, @@ -474,12 +474,12 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_LOAD_ATTR_WITH_HINT] = "_LOAD_ATTR_WITH_HINT", [_LOAD_BUILD_CLASS] = "_LOAD_BUILD_CLASS", [_LOAD_COMMON_CONSTANT] = "_LOAD_COMMON_CONSTANT", - [_LOAD_CONST] = "_LOAD_CONST", [_LOAD_CONST_IMMORTAL] = "_LOAD_CONST_IMMORTAL", [_LOAD_CONST_INLINE] = "_LOAD_CONST_INLINE", [_LOAD_CONST_INLINE_BORROW] = "_LOAD_CONST_INLINE_BORROW", [_LOAD_CONST_INLINE_BORROW_WITH_NULL] = "_LOAD_CONST_INLINE_BORROW_WITH_NULL", [_LOAD_CONST_INLINE_WITH_NULL] = "_LOAD_CONST_INLINE_WITH_NULL", + [_LOAD_CONST_MORTAL] = "_LOAD_CONST_MORTAL", [_LOAD_DEREF] = "_LOAD_DEREF", [_LOAD_FAST] = "_LOAD_FAST", [_LOAD_FAST_0] = "_LOAD_FAST_0", @@ -617,7 +617,7 @@ int _PyUop_num_popped(int opcode, int oparg) return 0; case _LOAD_FAST_LOAD_FAST: return 0; - case _LOAD_CONST: + case _LOAD_CONST_MORTAL: return 0; case _LOAD_CONST_IMMORTAL: return 0; diff --git a/Include/opcode_ids.h b/Include/opcode_ids.h index 09e261fadd5544..f2d8963a1813c5 100644 --- a/Include/opcode_ids.h +++ b/Include/opcode_ids.h @@ -187,26 +187,27 @@ extern "C" { #define LOAD_ATTR_SLOT 205 #define LOAD_ATTR_WITH_HINT 206 #define LOAD_CONST_IMMORTAL 207 -#define LOAD_GLOBAL_BUILTIN 208 -#define LOAD_GLOBAL_MODULE 209 -#define LOAD_SUPER_ATTR_ATTR 210 -#define LOAD_SUPER_ATTR_METHOD 211 -#define RESUME_CHECK 212 -#define SEND_GEN 213 -#define STORE_ATTR_INSTANCE_VALUE 214 -#define STORE_ATTR_SLOT 215 -#define STORE_ATTR_WITH_HINT 216 -#define STORE_SUBSCR_DICT 217 -#define STORE_SUBSCR_LIST_INT 218 -#define TO_BOOL_ALWAYS_TRUE 219 -#define TO_BOOL_BOOL 220 -#define TO_BOOL_INT 221 -#define TO_BOOL_LIST 222 -#define TO_BOOL_NONE 223 -#define TO_BOOL_STR 224 -#define UNPACK_SEQUENCE_LIST 225 -#define UNPACK_SEQUENCE_TUPLE 226 -#define UNPACK_SEQUENCE_TWO_TUPLE 227 +#define LOAD_CONST_MORTAL 208 +#define LOAD_GLOBAL_BUILTIN 209 +#define LOAD_GLOBAL_MODULE 210 +#define LOAD_SUPER_ATTR_ATTR 211 +#define LOAD_SUPER_ATTR_METHOD 212 +#define RESUME_CHECK 213 +#define SEND_GEN 214 +#define STORE_ATTR_INSTANCE_VALUE 215 +#define STORE_ATTR_SLOT 216 +#define STORE_ATTR_WITH_HINT 217 +#define STORE_SUBSCR_DICT 218 +#define STORE_SUBSCR_LIST_INT 219 +#define TO_BOOL_ALWAYS_TRUE 220 +#define TO_BOOL_BOOL 221 +#define TO_BOOL_INT 222 +#define TO_BOOL_LIST 223 +#define TO_BOOL_NONE 224 +#define TO_BOOL_STR 225 +#define UNPACK_SEQUENCE_LIST 226 +#define UNPACK_SEQUENCE_TUPLE 227 +#define UNPACK_SEQUENCE_TWO_TUPLE 228 #define INSTRUMENTED_END_FOR 235 #define INSTRUMENTED_POP_ITER 236 #define INSTRUMENTED_END_SEND 237 diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py index 64ee56fd10556f..d93e8d8df8fe4b 100644 --- a/Lib/_opcode_metadata.py +++ b/Lib/_opcode_metadata.py @@ -7,6 +7,7 @@ "RESUME_CHECK", ], "LOAD_CONST": [ + "LOAD_CONST_MORTAL", "LOAD_CONST_IMMORTAL", ], "TO_BOOL": [ @@ -178,26 +179,27 @@ 'LOAD_ATTR_SLOT': 205, 'LOAD_ATTR_WITH_HINT': 206, 'LOAD_CONST_IMMORTAL': 207, - 'LOAD_GLOBAL_BUILTIN': 208, - 'LOAD_GLOBAL_MODULE': 209, - 'LOAD_SUPER_ATTR_ATTR': 210, - 'LOAD_SUPER_ATTR_METHOD': 211, - 'RESUME_CHECK': 212, - 'SEND_GEN': 213, - 'STORE_ATTR_INSTANCE_VALUE': 214, - 'STORE_ATTR_SLOT': 215, - 'STORE_ATTR_WITH_HINT': 216, - 'STORE_SUBSCR_DICT': 217, - 'STORE_SUBSCR_LIST_INT': 218, - 'TO_BOOL_ALWAYS_TRUE': 219, - 'TO_BOOL_BOOL': 220, - 'TO_BOOL_INT': 221, - 'TO_BOOL_LIST': 222, - 'TO_BOOL_NONE': 223, - 'TO_BOOL_STR': 224, - 'UNPACK_SEQUENCE_LIST': 225, - 'UNPACK_SEQUENCE_TUPLE': 226, - 'UNPACK_SEQUENCE_TWO_TUPLE': 227, + 'LOAD_CONST_MORTAL': 208, + 'LOAD_GLOBAL_BUILTIN': 209, + 'LOAD_GLOBAL_MODULE': 210, + 'LOAD_SUPER_ATTR_ATTR': 211, + 'LOAD_SUPER_ATTR_METHOD': 212, + 'RESUME_CHECK': 213, + 'SEND_GEN': 214, + 'STORE_ATTR_INSTANCE_VALUE': 215, + 'STORE_ATTR_SLOT': 216, + 'STORE_ATTR_WITH_HINT': 217, + 'STORE_SUBSCR_DICT': 218, + 'STORE_SUBSCR_LIST_INT': 219, + 'TO_BOOL_ALWAYS_TRUE': 220, + 'TO_BOOL_BOOL': 221, + 'TO_BOOL_INT': 222, + 'TO_BOOL_LIST': 223, + 'TO_BOOL_NONE': 224, + 'TO_BOOL_STR': 225, + 'UNPACK_SEQUENCE_LIST': 226, + 'UNPACK_SEQUENCE_TUPLE': 227, + 'UNPACK_SEQUENCE_TWO_TUPLE': 228, } opmap = { diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 76d9b5401d7d8e..ed8bd6fa20880b 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -892,7 +892,7 @@ def loop_test(): %3d RESUME_CHECK 0 %3d BUILD_LIST 0 - LOAD_CONST 0 ((1, 2, 3)) + LOAD_CONST_MORTAL 0 ((1, 2, 3)) LIST_EXTEND 1 LOAD_SMALL_INT 3 BINARY_OP 5 (*) @@ -2548,7 +2548,7 @@ def test_specialized_code(self): expect = ''' 0 RESUME 0 - 1 LOAD_CONST_IMMORTAL 0 (None) + 1 LOAD_CONST 0 (None) RETURN_VALUE ''' for flag in ['-S', '--specialized']: diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 8bab4ea16b629b..657e2fb4e1c850 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -285,11 +285,25 @@ dummy_func( } family(LOAD_CONST, 0) = { + LOAD_CONST_MORTAL, LOAD_CONST_IMMORTAL, }; - pure inst(LOAD_CONST, (-- value)) { - value = PyStackRef_FromPyObjectNew(GETITEM(FRAME_CO_CONSTS, oparg)); + inst(LOAD_CONST, (-- value)) { + /* We can't do this in the bytecode compiler as + * marshalling can intern strings and make them immortal. */ + PyObject *obj = GETITEM(FRAME_CO_CONSTS, oparg); + value = PyStackRef_FromPyObjectNew(obj); +#if ENABLE_SPECIALIZATION + if (this_instr->op.code == LOAD_CONST) { + this_instr->op.code = _Py_IsImmortal(obj) ? LOAD_CONST_IMMORTAL : LOAD_CONST_MORTAL; + } +#endif + } + + inst(LOAD_CONST_MORTAL, (-- value)) { + PyObject *obj = GETITEM(FRAME_CO_CONSTS, oparg); + value = PyStackRef_FromPyObjectNew(obj); } inst(LOAD_CONST_IMMORTAL, (-- value)) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index e40fa88be89172..e90d6b5ec103ae 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -209,10 +209,13 @@ break; } - case _LOAD_CONST: { + /* _LOAD_CONST is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ + + case _LOAD_CONST_MORTAL: { _PyStackRef value; oparg = CURRENT_OPARG(); - value = PyStackRef_FromPyObjectNew(GETITEM(FRAME_CO_CONSTS, oparg)); + PyObject *obj = GETITEM(FRAME_CO_CONSTS, oparg); + value = PyStackRef_FromPyObjectNew(obj); stack_pointer[0] = value; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 7028ba52faae96..70d0814d682205 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -5913,8 +5913,18 @@ next_instr += 1; INSTRUCTION_STATS(LOAD_CONST); PREDICTED(LOAD_CONST); + _Py_CODEUNIT* const this_instr = next_instr - 1; + (void)this_instr; _PyStackRef value; - value = PyStackRef_FromPyObjectNew(GETITEM(FRAME_CO_CONSTS, oparg)); + /* We can't do this in the bytecode compiler as + * marshalling can intern strings and make them immortal. */ + PyObject *obj = GETITEM(FRAME_CO_CONSTS, oparg); + value = PyStackRef_FromPyObjectNew(obj); + #if ENABLE_SPECIALIZATION + if (this_instr->op.code == LOAD_CONST) { + this_instr->op.code = _Py_IsImmortal(obj) ? LOAD_CONST_IMMORTAL : LOAD_CONST_MORTAL; + } + #endif stack_pointer[0] = value; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); @@ -5936,6 +5946,20 @@ DISPATCH(); } + TARGET(LOAD_CONST_MORTAL) { + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_CONST_MORTAL); + static_assert(0 == 0, "incorrect cache size"); + _PyStackRef value; + PyObject *obj = GETITEM(FRAME_CO_CONSTS, oparg); + value = PyStackRef_FromPyObjectNew(obj); + stack_pointer[0] = value; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); + DISPATCH(); + } + TARGET(LOAD_DEREF) { frame->instr_ptr = next_instr; next_instr += 1; diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 00d55d3a82edc1..c5c008fcbe574e 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -207,6 +207,7 @@ static void *opcode_targets[256] = { &&TARGET_LOAD_ATTR_SLOT, &&TARGET_LOAD_ATTR_WITH_HINT, &&TARGET_LOAD_CONST_IMMORTAL, + &&TARGET_LOAD_CONST_MORTAL, &&TARGET_LOAD_GLOBAL_BUILTIN, &&TARGET_LOAD_GLOBAL_MODULE, &&TARGET_LOAD_SUPER_ATTR_ATTR, @@ -233,7 +234,6 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, - &&_unknown_opcode, &&TARGET_INSTRUMENTED_END_FOR, &&TARGET_INSTRUMENTED_POP_ITER, &&TARGET_INSTRUMENTED_END_SEND, diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 86394480f76bb8..788adecca8af80 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -479,6 +479,13 @@ dummy_func(void) { value = sym_new_const(ctx, val); } + op(_LOAD_CONST_MORTAL, (-- value)) { + PyObject *val = PyTuple_GET_ITEM(co->co_consts, this_instr->oparg); + int opcode = _Py_IsImmortal(val) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)val); + value = sym_new_const(ctx, val); + } + op(_LOAD_CONST_IMMORTAL, (-- value)) { PyObject *val = PyTuple_GET_ITEM(co->co_consts, this_instr->oparg); REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index c72ae7b6281e80..a64dd465467122 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -58,7 +58,9 @@ break; } - case _LOAD_CONST: { + /* _LOAD_CONST is not a viable micro-op for tier 2 */ + + case _LOAD_CONST_MORTAL: { _Py_UopsSymbol *value; PyObject *val = PyTuple_GET_ITEM(co->co_consts, this_instr->oparg); int opcode = _Py_IsImmortal(val) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; diff --git a/Python/specialize.c b/Python/specialize.c index c9325c39210874..897005c4f1078d 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -478,15 +478,6 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, PyObject *consts, } i += caches; } - else if (opcode == LOAD_CONST) { - /* We can't do this in the bytecode compiler as - * marshalling can intern strings and make them immortal. */ - - PyObject *obj = PyTuple_GET_ITEM(consts, oparg); - if (_Py_IsImmortal(obj)) { - instructions[i].op.code = LOAD_CONST_IMMORTAL; - } - } if (opcode != EXTENDED_ARG) { oparg = 0; } From afb9dc887c6e8ae17b6a54c6124399e8bdc82253 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 13 Jan 2025 13:05:02 +0200 Subject: [PATCH 278/376] gh-128595: Add test class helper to force no terminal colour (#128687) Co-authored-by: Erlend E. Aasland --- Lib/test/support/__init__.py | 47 ++++++++++++++++++--------- Lib/test/test_code_module.py | 3 +- Lib/test/test_exceptions.py | 1 + Lib/test/test_traceback.py | 9 ++++- Lib/test/test_unittest/test_result.py | 11 ++++--- 5 files changed, 49 insertions(+), 22 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 42e7b876594fa7..ee9520a8838625 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -60,6 +60,7 @@ "skip_on_s390x", "without_optimizer", "force_not_colorized", + "force_not_colorized_test_class", "BrokenIter", "in_systemd_nspawn_sync_suppressed", "run_no_yield_async_fn", "run_yielding_async_fn", "async_yield", @@ -2832,30 +2833,44 @@ def is_slot_wrapper(name, value): yield name, True +@contextlib.contextmanager +def no_color(): + import _colorize + from .os_helper import EnvironmentVarGuard + + with ( + swap_attr(_colorize, "can_colorize", lambda: False), + EnvironmentVarGuard() as env, + ): + for var in {"FORCE_COLOR", "NO_COLOR", "PYTHON_COLORS"}: + env.unset(var) + env.set("NO_COLOR", "1") + yield + + def force_not_colorized(func): """Force the terminal not to be colorized.""" @functools.wraps(func) def wrapper(*args, **kwargs): - import _colorize - original_fn = _colorize.can_colorize - variables: dict[str, str | None] = { - "PYTHON_COLORS": None, "FORCE_COLOR": None, "NO_COLOR": None - } - try: - for key in variables: - variables[key] = os.environ.pop(key, None) - os.environ["NO_COLOR"] = "1" - _colorize.can_colorize = lambda: False + with no_color(): return func(*args, **kwargs) - finally: - _colorize.can_colorize = original_fn - del os.environ["NO_COLOR"] - for key, value in variables.items(): - if value is not None: - os.environ[key] = value return wrapper +def force_not_colorized_test_class(cls): + """Force the terminal not to be colorized for the entire test class.""" + original_setUpClass = cls.setUpClass + + @classmethod + @functools.wraps(cls.setUpClass) + def new_setUpClass(cls): + cls.enterClassContext(no_color()) + original_setUpClass() + + cls.setUpClass = new_setUpClass + return cls + + def initialized_with_pyrepl(): """Detect whether PyREPL was used during Python initialization.""" # If the main module has a __file__ attribute it's a Python module, which means PyREPL. diff --git a/Lib/test/test_code_module.py b/Lib/test/test_code_module.py index 37c7bc772ed8c7..20b960ce8d1e02 100644 --- a/Lib/test/test_code_module.py +++ b/Lib/test/test_code_module.py @@ -5,9 +5,9 @@ from textwrap import dedent from contextlib import ExitStack from unittest import mock +from test.support import force_not_colorized_test_class from test.support import import_helper - code = import_helper.import_module('code') @@ -30,6 +30,7 @@ def mock_sys(self): del self.sysmod.ps2 +@force_not_colorized_test_class class TestInteractiveConsole(unittest.TestCase, MockSys): maxDiff = None diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 6ccfa9575f8569..206e22e791e02a 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2274,6 +2274,7 @@ def test_range_of_offsets(self): self.assertIn(expected, err.getvalue()) the_exception = exc + @force_not_colorized def test_subclass(self): class MySyntaxError(SyntaxError): pass diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 31f0a61d6a9d59..abdfc4638f2e9c 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -21,7 +21,7 @@ from test.support.os_helper import TESTFN, unlink from test.support.script_helper import assert_python_ok, assert_python_failure from test.support.import_helper import forget -from test.support import force_not_colorized +from test.support import force_not_colorized, force_not_colorized_test_class import json import textwrap @@ -1712,6 +1712,7 @@ def f(): @requires_debug_ranges() +@force_not_colorized_test_class class PurePythonTracebackErrorCaretTests( PurePythonExceptionFormattingMixin, TracebackErrorLocationCaretTestBase, @@ -1725,6 +1726,7 @@ class PurePythonTracebackErrorCaretTests( @cpython_only @requires_debug_ranges() +@force_not_colorized_test_class class CPythonTracebackErrorCaretTests( CAPIExceptionFormattingMixin, TracebackErrorLocationCaretTestBase, @@ -1736,6 +1738,7 @@ class CPythonTracebackErrorCaretTests( @cpython_only @requires_debug_ranges() +@force_not_colorized_test_class class CPythonTracebackLegacyErrorCaretTests( CAPIExceptionFormattingLegacyMixin, TracebackErrorLocationCaretTestBase, @@ -2149,10 +2152,12 @@ def test_print_exception_bad_type_python(self): boundaries = re.compile( '(%s|%s)' % (re.escape(cause_message), re.escape(context_message))) +@force_not_colorized_test_class class TestTracebackFormat(unittest.TestCase, TracebackFormatMixin): pass @cpython_only +@force_not_colorized_test_class class TestFallbackTracebackFormat(unittest.TestCase, TracebackFormatMixin): DEBUG_RANGES = False def setUp(self) -> None: @@ -2940,6 +2945,7 @@ def f(): self.assertEqual(report, expected) +@force_not_colorized_test_class class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase): # # This checks reporting through the 'traceback' module, with both @@ -2956,6 +2962,7 @@ def get_report(self, e): return s +@force_not_colorized_test_class class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase): # # This checks built-in reporting by the interpreter. diff --git a/Lib/test/test_unittest/test_result.py b/Lib/test/test_unittest/test_result.py index 746b9fa2677717..ad6f52d7e0260e 100644 --- a/Lib/test/test_unittest/test_result.py +++ b/Lib/test/test_unittest/test_result.py @@ -1,13 +1,15 @@ import io import sys import textwrap - -from test.support import warnings_helper, captured_stdout - import traceback import unittest from unittest.util import strclass -from test.support import force_not_colorized +from test.support import warnings_helper +from test.support import ( + captured_stdout, + force_not_colorized, + force_not_colorized_test_class, +) from test.test_unittest.support import BufferedWriter @@ -772,6 +774,7 @@ def testFoo(self): runner.run(Test('testFoo')) +@force_not_colorized_test_class class TestOutputBuffering(unittest.TestCase): def setUp(self): From 39fc7ef4fe211e8f7d3b5a6e392e475ecdfbce72 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 13 Jan 2025 11:42:45 +0000 Subject: [PATCH 279/376] GH-124483: Mark `Py_DECREF`, etc. as escaping for the JIT (GH-128678) --- Include/internal/pycore_opcode_metadata.h | 44 ++++++------- Include/internal/pycore_uop_metadata.h | 64 +++++++++---------- Lib/test/test_capi/test_opt.py | 19 ++++++ ...-01-09-11-46-57.gh-issue-124483.KRtBeQ.rst | 3 + Tools/cases_generator/analyzer.py | 18 ++++-- 5 files changed, 90 insertions(+), 58 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-09-11-46-57.gh-issue-124483.KRtBeQ.rst diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 51d6e435948225..8c4182ae384e5f 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1985,7 +1985,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [BINARY_OP_ADD_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_ADD_INT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_ADD_UNICODE] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, - [BINARY_OP_INPLACE_ADD_UNICODE] = { true, INSTR_FMT_IXC, HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG }, + [BINARY_OP_INPLACE_ADD_UNICODE] = { true, INSTR_FMT_IXC, HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [BINARY_OP_MULTIPLY_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_MULTIPLY_INT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_SUBTRACT_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, @@ -1995,8 +1995,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [BINARY_SUBSCR_DICT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [BINARY_SUBSCR_GETITEM] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG }, [BINARY_SUBSCR_LIST_INT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, - [BINARY_SUBSCR_STR_INT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG }, - [BINARY_SUBSCR_TUPLE_INT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG }, + [BINARY_SUBSCR_STR_INT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, + [BINARY_SUBSCR_TUPLE_INT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [BUILD_LIST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG }, [BUILD_MAP] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [BUILD_SET] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -2006,7 +2006,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [CACHE] = { true, INSTR_FMT_IX, 0 }, [CALL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [CALL_ALLOC_AND_ENTER_INIT] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, - [CALL_BOUND_METHOD_EXACT_ARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, + [CALL_BOUND_METHOD_EXACT_ARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG }, [CALL_BOUND_METHOD_GENERAL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [CALL_BUILTIN_CLASS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_BUILTIN_FAST] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -2021,7 +2021,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [CALL_KW_NON_PY] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_KW_PY] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [CALL_LEN] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, - [CALL_LIST_APPEND] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG }, + [CALL_LIST_APPEND] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_METHOD_DESCRIPTOR_FAST] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_METHOD_DESCRIPTOR_NOARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -2031,7 +2031,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [CALL_PY_GENERAL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [CALL_STR_1] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_TUPLE_1] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [CALL_TYPE_1] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, + [CALL_TYPE_1] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [CHECK_EG_MATCH] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CHECK_EXC_MATCH] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CLEANUP_THROW] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, @@ -2054,8 +2054,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [DICT_MERGE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [DICT_UPDATE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [END_ASYNC_FOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, - [END_FOR] = { true, INSTR_FMT_IX, HAS_NO_SAVE_IP_FLAG }, - [END_SEND] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, + [END_FOR] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG | HAS_NO_SAVE_IP_FLAG }, + [END_SEND] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG | HAS_PURE_FLAG }, [ENTER_EXECUTOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, [EXIT_INIT_CHECK] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [EXTENDED_ARG] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, @@ -2063,9 +2063,9 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [FORMAT_WITH_SPEC] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [FOR_ITER] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [FOR_ITER_GEN] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, - [FOR_ITER_LIST] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG }, + [FOR_ITER_LIST] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG }, [FOR_ITER_RANGE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG }, - [FOR_ITER_TUPLE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG }, + [FOR_ITER_TUPLE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG }, [GET_AITER] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [GET_ANEXT] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [GET_AWAITABLE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -2086,10 +2086,10 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [INSTRUMENTED_LINE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, [INSTRUMENTED_LOAD_SUPER_ATTR] = { true, INSTR_FMT_IXC, 0 }, [INSTRUMENTED_NOT_TAKEN] = { true, INSTR_FMT_IX, 0 }, - [INSTRUMENTED_POP_ITER] = { true, INSTR_FMT_IX, 0 }, + [INSTRUMENTED_POP_ITER] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, [INSTRUMENTED_POP_JUMP_IF_FALSE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG }, - [INSTRUMENTED_POP_JUMP_IF_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG }, - [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG }, + [INSTRUMENTED_POP_JUMP_IF_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_ESCAPES_FLAG }, + [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_ESCAPES_FLAG }, [INSTRUMENTED_POP_JUMP_IF_TRUE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG }, [INSTRUMENTED_RESUME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [INSTRUMENTED_RETURN_VALUE] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -2109,7 +2109,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [LOAD_ATTR_METHOD_LAZY_DICT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, [LOAD_ATTR_METHOD_NO_DICT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_EXIT_FLAG }, [LOAD_ATTR_METHOD_WITH_VALUES] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, - [LOAD_ATTR_MODULE] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, + [LOAD_ATTR_MODULE] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_EXIT_FLAG }, [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, [LOAD_ATTR_PROPERTY] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, @@ -2137,7 +2137,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [LOAD_SUPER_ATTR] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [LOAD_SUPER_ATTR_ATTR] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [LOAD_SUPER_ATTR_METHOD] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [MAKE_CELL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG }, + [MAKE_CELL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [MAKE_FUNCTION] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [MAP_ADD] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [MATCH_CLASS] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -2169,19 +2169,19 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [SET_FUNCTION_ATTRIBUTE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, [SET_UPDATE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [STORE_ATTR] = { true, INSTR_FMT_IBC000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [STORE_ATTR_INSTANCE_VALUE] = { true, INSTR_FMT_IXC000, HAS_EXIT_FLAG }, - [STORE_ATTR_SLOT] = { true, INSTR_FMT_IXC000, HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, + [STORE_ATTR_INSTANCE_VALUE] = { true, INSTR_FMT_IXC000, HAS_EXIT_FLAG | HAS_ESCAPES_FLAG }, + [STORE_ATTR_SLOT] = { true, INSTR_FMT_IXC000, HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG }, [STORE_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG }, [STORE_DEREF] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ESCAPES_FLAG }, - [STORE_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG }, - [STORE_FAST_LOAD_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG }, - [STORE_FAST_STORE_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG }, + [STORE_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ESCAPES_FLAG }, + [STORE_FAST_LOAD_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ESCAPES_FLAG }, + [STORE_FAST_STORE_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ESCAPES_FLAG }, [STORE_GLOBAL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [STORE_NAME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [STORE_SLICE] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [STORE_SUBSCR] = { true, INSTR_FMT_IXC, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [STORE_SUBSCR_DICT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [STORE_SUBSCR_LIST_INT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG }, + [STORE_SUBSCR_LIST_INT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [SWAP] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_PURE_FLAG }, [TO_BOOL] = { true, INSTR_FMT_IXC00, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [TO_BOOL_ALWAYS_TRUE] = { true, INSTR_FMT_IXC00, HAS_EXIT_FLAG }, @@ -2209,7 +2209,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [SETUP_CLEANUP] = { true, -1, HAS_PURE_FLAG | HAS_ARG_FLAG }, [SETUP_FINALLY] = { true, -1, HAS_PURE_FLAG | HAS_ARG_FLAG }, [SETUP_WITH] = { true, -1, HAS_PURE_FLAG | HAS_ARG_FLAG }, - [STORE_FAST_MAYBE_NULL] = { true, -1, HAS_ARG_FLAG | HAS_LOCAL_FLAG }, + [STORE_FAST_MAYBE_NULL] = { true, -1, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ESCAPES_FLAG }, }; #endif diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 942e5eb25b76b4..70328a0194fd8c 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -42,21 +42,21 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_SMALL_INT_2] = 0, [_LOAD_SMALL_INT_3] = 0, [_LOAD_SMALL_INT] = HAS_ARG_FLAG, - [_STORE_FAST_0] = HAS_LOCAL_FLAG, - [_STORE_FAST_1] = HAS_LOCAL_FLAG, - [_STORE_FAST_2] = HAS_LOCAL_FLAG, - [_STORE_FAST_3] = HAS_LOCAL_FLAG, - [_STORE_FAST_4] = HAS_LOCAL_FLAG, - [_STORE_FAST_5] = HAS_LOCAL_FLAG, - [_STORE_FAST_6] = HAS_LOCAL_FLAG, - [_STORE_FAST_7] = HAS_LOCAL_FLAG, - [_STORE_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, - [_STORE_FAST_LOAD_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, - [_STORE_FAST_STORE_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, + [_STORE_FAST_0] = HAS_LOCAL_FLAG | HAS_ESCAPES_FLAG, + [_STORE_FAST_1] = HAS_LOCAL_FLAG | HAS_ESCAPES_FLAG, + [_STORE_FAST_2] = HAS_LOCAL_FLAG | HAS_ESCAPES_FLAG, + [_STORE_FAST_3] = HAS_LOCAL_FLAG | HAS_ESCAPES_FLAG, + [_STORE_FAST_4] = HAS_LOCAL_FLAG | HAS_ESCAPES_FLAG, + [_STORE_FAST_5] = HAS_LOCAL_FLAG | HAS_ESCAPES_FLAG, + [_STORE_FAST_6] = HAS_LOCAL_FLAG | HAS_ESCAPES_FLAG, + [_STORE_FAST_7] = HAS_LOCAL_FLAG | HAS_ESCAPES_FLAG, + [_STORE_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ESCAPES_FLAG, + [_STORE_FAST_LOAD_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ESCAPES_FLAG, + [_STORE_FAST_STORE_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ESCAPES_FLAG, [_POP_TOP] = HAS_PURE_FLAG, [_PUSH_NULL] = HAS_PURE_FLAG, - [_END_FOR] = HAS_NO_SAVE_IP_FLAG, - [_END_SEND] = HAS_PURE_FLAG, + [_END_FOR] = HAS_ESCAPES_FLAG | HAS_NO_SAVE_IP_FLAG, + [_END_SEND] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG, [_UNARY_NEGATIVE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_UNARY_NOT] = HAS_PURE_FLAG, [_TO_BOOL] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -81,20 +81,20 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_BINARY_OP_SUBTRACT_FLOAT] = HAS_ERROR_FLAG | HAS_PURE_FLAG, [_GUARD_BOTH_UNICODE] = HAS_EXIT_FLAG, [_BINARY_OP_ADD_UNICODE] = HAS_ERROR_FLAG | HAS_PURE_FLAG, - [_BINARY_OP_INPLACE_ADD_UNICODE] = HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG, + [_BINARY_OP_INPLACE_ADD_UNICODE] = HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_BINARY_SUBSCR] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_BINARY_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_STORE_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_BINARY_SUBSCR_LIST_INT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, - [_BINARY_SUBSCR_STR_INT] = HAS_DEOPT_FLAG, - [_BINARY_SUBSCR_TUPLE_INT] = HAS_DEOPT_FLAG, + [_BINARY_SUBSCR_STR_INT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, + [_BINARY_SUBSCR_TUPLE_INT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, [_BINARY_SUBSCR_DICT] = HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_BINARY_SUBSCR_CHECK_FUNC] = HAS_DEOPT_FLAG, [_BINARY_SUBSCR_INIT_CALL] = 0, [_LIST_APPEND] = HAS_ARG_FLAG | HAS_ERROR_FLAG, [_SET_ADD] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_STORE_SUBSCR] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_STORE_SUBSCR_LIST_INT] = HAS_DEOPT_FLAG, + [_STORE_SUBSCR_LIST_INT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, [_STORE_SUBSCR_DICT] = HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_DELETE_SUBSCR] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_INTRINSIC_1] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -128,7 +128,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_GLOBAL_MODULE_FROM_KEYS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_LOAD_GLOBAL_BUILTINS_FROM_KEYS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_DELETE_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_MAKE_CELL] = HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG, + [_MAKE_CELL] = HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_DELETE_DEREF] = HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_LOAD_FROM_DICT_OR_DEREF] = HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_LOAD_DEREF] = HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -155,7 +155,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_ATTR_INSTANCE_VALUE_1] = HAS_DEOPT_FLAG, [_LOAD_ATTR_INSTANCE_VALUE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_OPARG_AND_1_FLAG, [_CHECK_ATTR_MODULE_PUSH_KEYS] = HAS_DEOPT_FLAG, - [_LOAD_ATTR_MODULE_FROM_KEYS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, + [_LOAD_ATTR_MODULE_FROM_KEYS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, [_CHECK_ATTR_WITH_HINT] = HAS_EXIT_FLAG, [_LOAD_ATTR_WITH_HINT] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG, [_LOAD_ATTR_SLOT_0] = HAS_DEOPT_FLAG, @@ -167,9 +167,9 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_ATTR_CLASS] = HAS_ARG_FLAG | HAS_OPARG_AND_1_FLAG, [_LOAD_ATTR_PROPERTY_FRAME] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_GUARD_DORV_NO_DICT] = HAS_EXIT_FLAG, - [_STORE_ATTR_INSTANCE_VALUE] = 0, + [_STORE_ATTR_INSTANCE_VALUE] = HAS_ESCAPES_FLAG, [_STORE_ATTR_WITH_HINT] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, - [_STORE_ATTR_SLOT] = HAS_DEOPT_FLAG, + [_STORE_ATTR_SLOT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, [_COMPARE_OP] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_COMPARE_OP_FLOAT] = HAS_ARG_FLAG, [_COMPARE_OP_INT] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, @@ -212,16 +212,16 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = HAS_ARG_FLAG, [_CHECK_ATTR_METHOD_LAZY_DICT] = HAS_DEOPT_FLAG, [_LOAD_ATTR_METHOD_LAZY_DICT] = HAS_ARG_FLAG, - [_MAYBE_EXPAND_METHOD] = HAS_ARG_FLAG, + [_MAYBE_EXPAND_METHOD] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_PY_FRAME_GENERAL] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_CHECK_FUNCTION_VERSION] = HAS_ARG_FLAG | HAS_EXIT_FLAG, [_CHECK_FUNCTION_VERSION_INLINE] = HAS_EXIT_FLAG, [_CHECK_METHOD_VERSION] = HAS_ARG_FLAG | HAS_EXIT_FLAG, - [_EXPAND_METHOD] = HAS_ARG_FLAG, + [_EXPAND_METHOD] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_CHECK_IS_NOT_PY_CALLABLE] = HAS_ARG_FLAG | HAS_EXIT_FLAG, [_CALL_NON_PY_GENERAL] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CHECK_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG | HAS_EXIT_FLAG, - [_INIT_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG, + [_INIT_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_CHECK_PEP_523] = HAS_DEOPT_FLAG, [_CHECK_FUNCTION_EXACT_ARGS] = HAS_ARG_FLAG | HAS_EXIT_FLAG, [_CHECK_STACK_SPACE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, @@ -232,7 +232,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_INIT_CALL_PY_EXACT_ARGS_4] = HAS_PURE_FLAG, [_INIT_CALL_PY_EXACT_ARGS] = HAS_ARG_FLAG | HAS_PURE_FLAG, [_PUSH_FRAME] = 0, - [_CALL_TYPE_1] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, + [_CALL_TYPE_1] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, [_CALL_STR_1] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_TUPLE_1] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CHECK_AND_ALLOCATE_OBJECT] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, @@ -244,16 +244,16 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_CALL_BUILTIN_FAST_WITH_KEYWORDS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_LEN] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_CALL_ISINSTANCE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, - [_CALL_LIST_APPEND] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG, + [_CALL_LIST_APPEND] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_METHOD_DESCRIPTOR_O] = HAS_ARG_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = HAS_ARG_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_METHOD_DESCRIPTOR_NOARGS] = HAS_ARG_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_METHOD_DESCRIPTOR_FAST] = HAS_ARG_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_MAYBE_EXPAND_METHOD_KW] = HAS_ARG_FLAG, + [_MAYBE_EXPAND_METHOD_KW] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_PY_FRAME_KW] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_CHECK_FUNCTION_VERSION_KW] = HAS_ARG_FLAG | HAS_EXIT_FLAG, [_CHECK_METHOD_VERSION_KW] = HAS_ARG_FLAG | HAS_EXIT_FLAG, - [_EXPAND_METHOD_KW] = HAS_ARG_FLAG, + [_EXPAND_METHOD_KW] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_CHECK_IS_NOT_PY_CALLABLE_KW] = HAS_ARG_FLAG | HAS_EXIT_FLAG, [_CALL_KW_NON_PY] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_MAKE_CALLARGS_A_TUPLE] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, @@ -269,8 +269,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_SWAP] = HAS_ARG_FLAG | HAS_PURE_FLAG, [_GUARD_IS_TRUE_POP] = HAS_EXIT_FLAG, [_GUARD_IS_FALSE_POP] = HAS_EXIT_FLAG, - [_GUARD_IS_NONE_POP] = HAS_EXIT_FLAG, - [_GUARD_IS_NOT_NONE_POP] = HAS_EXIT_FLAG, + [_GUARD_IS_NONE_POP] = HAS_EXIT_FLAG | HAS_ESCAPES_FLAG, + [_GUARD_IS_NOT_NONE_POP] = HAS_EXIT_FLAG | HAS_ESCAPES_FLAG, [_JUMP_TO_TOP] = 0, [_SET_IP] = 0, [_CHECK_STACK_SPACE_OPERAND] = HAS_DEOPT_FLAG, @@ -279,7 +279,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_CHECK_VALIDITY] = HAS_DEOPT_FLAG, [_LOAD_CONST_INLINE] = HAS_PURE_FLAG, [_LOAD_CONST_INLINE_BORROW] = HAS_PURE_FLAG, - [_POP_TOP_LOAD_CONST_INLINE_BORROW] = HAS_PURE_FLAG, + [_POP_TOP_LOAD_CONST_INLINE_BORROW] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG, [_LOAD_CONST_INLINE_WITH_NULL] = HAS_PURE_FLAG, [_LOAD_CONST_INLINE_BORROW_WITH_NULL] = HAS_PURE_FLAG, [_CHECK_FUNCTION] = HAS_DEOPT_FLAG, @@ -288,7 +288,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_ATTR_MODULE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_INTERNAL_INCREMENT_OPT_COUNTER] = 0, [_DYNAMIC_EXIT] = HAS_ESCAPES_FLAG, - [_START_EXECUTOR] = 0, + [_START_EXECUTOR] = HAS_ESCAPES_FLAG, [_MAKE_WARM] = 0, [_FATAL_ERROR] = 0, [_CHECK_VALIDITY_AND_SET_IP] = HAS_DEOPT_FLAG, diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index d84702411afe41..12542d8b7fa62e 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -1555,6 +1555,25 @@ def f(l: complex, r: complex) -> None: with self.subTest(l=l, r=r, x=x, y=y): script_helper.assert_python_ok("-c", s) + def test_decref_escapes(self): + class Convert9999ToNone: + def __del__(self): + ns = sys._getframe(1).f_locals + if ns["i"] == 9999: + ns["i"] = None + + def crash_addition(): + try: + for i in range(10000): + n = Convert9999ToNone() + i + i # Remove guards for i. + n = None # Change i. + i + i # This crashed when we didn't treat DECREF as escaping (gh-124483) + except TypeError: + pass + + crash_addition() + def global_identity(x): return x diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-09-11-46-57.gh-issue-124483.KRtBeQ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-09-11-46-57.gh-issue-124483.KRtBeQ.rst new file mode 100644 index 00000000000000..6f2e7265d62321 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-09-11-46-57.gh-issue-124483.KRtBeQ.rst @@ -0,0 +1,3 @@ +Treat ``Py_DECREF`` and variants as escaping when generating opcode and uop +metadata. This prevents the possibility of a ``__del__`` method causing the JIT +to behave incorrectly. diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 679beca3ec3a9d..4e1d47281fdbbf 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -8,6 +8,7 @@ @dataclass class Properties: escaping_calls: dict[lexer.Token, tuple[lexer.Token, lexer.Token]] + escapes: bool error_with_pop: bool error_without_pop: bool deopts: bool @@ -45,6 +46,7 @@ def from_list(properties: list["Properties"]) -> "Properties": escaping_calls.update(p.escaping_calls) return Properties( escaping_calls=escaping_calls, + escapes = any(p.escapes for p in properties), error_with_pop=any(p.error_with_pop for p in properties), error_without_pop=any(p.error_without_pop for p in properties), deopts=any(p.deopts for p in properties), @@ -68,12 +70,9 @@ def from_list(properties: list["Properties"]) -> "Properties": def infallible(self) -> bool: return not self.error_with_pop and not self.error_without_pop - @property - def escapes(self) -> bool: - return bool(self.escaping_calls) - SKIP_PROPERTIES = Properties( escaping_calls={}, + escapes=False, error_with_pop=False, error_without_pop=False, deopts=False, @@ -815,8 +814,19 @@ def compute_properties(op: parser.InstDef) -> Properties: ) error_with_pop = has_error_with_pop(op) error_without_pop = has_error_without_pop(op) + escapes = ( + bool(escaping_calls) or + variable_used(op, "Py_DECREF") or + variable_used(op, "Py_XDECREF") or + variable_used(op, "Py_CLEAR") or + variable_used(op, "PyStackRef_CLOSE") or + variable_used(op, "PyStackRef_XCLOSE") or + variable_used(op, "PyStackRef_CLEAR") or + variable_used(op, "SETLOCAL") + ) return Properties( escaping_calls=escaping_calls, + escapes=escapes, error_with_pop=error_with_pop, error_without_pop=error_without_pop, deopts=deopts_if, From 6ff8f82f92a8af363b2bdd8bbaba5845eef430fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 13 Jan 2025 12:46:13 +0100 Subject: [PATCH 280/376] gh-128150: Improve performances of `uuid.uuid*` constructor functions. (#128151) We introduce a private constructor `UUID._from_int()` for RFC 4122/9562 UUIDs, which takes the integral UUID value as input. The latter must have correctly set its variant and version bits. We also make `UUID.__init__()` slightly more efficient. --- Doc/whatsnew/3.14.rst | 16 ++++ Lib/uuid.py | 85 +++++++++++++------ ...-12-21-11-12-50.gh-issue-128151.aq7vpG.rst | 2 + 3 files changed, 76 insertions(+), 27 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-21-11-12-50.gh-issue-128151.aq7vpG.rst diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 72abfebd46f2b9..474bd6a24b8ece 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -717,6 +717,22 @@ io file's bytes in full. (Contributed by Cody Maloney and Victor Stinner in :gh:`120754` and :gh:`90102`.) + +uuid +---- + +* Improve generation of :class:`~uuid.UUID` objects via their dedicated + functions: + + * :func:`~uuid.uuid3` and :func:`~uuid.uuid5` are both roughly 40% faster + for 16-byte names and 20% faster for 1024-byte names. Performance for + longer names remains unchanged. + * :func:`~uuid.uuid4` and :func:`~uuid.uuid8` are 30% and 40% faster + respectively. + + (Contributed by Bénédikt Tran in :gh:`128150`.) + + Deprecated ========== diff --git a/Lib/uuid.py b/Lib/uuid.py index 9c6ad9643cf6d5..cd1f3530ab63e1 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -85,6 +85,17 @@ class SafeUUID: unknown = None +_UINT_128_MAX = (1 << 128) - 1 +# 128-bit mask to clear the variant and version bits of a UUID integral value +_RFC_4122_CLEARFLAGS_MASK = ~((0xf000 << 64) | (0xc000 << 48)) +# RFC 4122 variant bits and version bits to activate on a UUID integral value. +_RFC_4122_VERSION_1_FLAGS = ((1 << 76) | (0x8000 << 48)) +_RFC_4122_VERSION_3_FLAGS = ((3 << 76) | (0x8000 << 48)) +_RFC_4122_VERSION_4_FLAGS = ((4 << 76) | (0x8000 << 48)) +_RFC_4122_VERSION_5_FLAGS = ((5 << 76) | (0x8000 << 48)) +_RFC_4122_VERSION_8_FLAGS = ((8 << 76) | (0x8000 << 48)) + + class UUID: """Instances of the UUID class represent UUIDs as specified in RFC 4122. UUID objects are immutable, hashable, and usable as dictionary keys. @@ -174,57 +185,69 @@ def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None, if [hex, bytes, bytes_le, fields, int].count(None) != 4: raise TypeError('one of the hex, bytes, bytes_le, fields, ' 'or int arguments must be given') - if hex is not None: + if int is not None: + pass + elif hex is not None: hex = hex.replace('urn:', '').replace('uuid:', '') hex = hex.strip('{}').replace('-', '') if len(hex) != 32: raise ValueError('badly formed hexadecimal UUID string') int = int_(hex, 16) - if bytes_le is not None: + elif bytes_le is not None: if len(bytes_le) != 16: raise ValueError('bytes_le is not a 16-char string') + assert isinstance(bytes_le, bytes_), repr(bytes_le) bytes = (bytes_le[4-1::-1] + bytes_le[6-1:4-1:-1] + bytes_le[8-1:6-1:-1] + bytes_le[8:]) - if bytes is not None: + int = int_.from_bytes(bytes) # big endian + elif bytes is not None: if len(bytes) != 16: raise ValueError('bytes is not a 16-char string') assert isinstance(bytes, bytes_), repr(bytes) int = int_.from_bytes(bytes) # big endian - if fields is not None: + elif fields is not None: if len(fields) != 6: raise ValueError('fields is not a 6-tuple') (time_low, time_mid, time_hi_version, clock_seq_hi_variant, clock_seq_low, node) = fields - if not 0 <= time_low < 1<<32: + if not 0 <= time_low < (1 << 32): raise ValueError('field 1 out of range (need a 32-bit value)') - if not 0 <= time_mid < 1<<16: + if not 0 <= time_mid < (1 << 16): raise ValueError('field 2 out of range (need a 16-bit value)') - if not 0 <= time_hi_version < 1<<16: + if not 0 <= time_hi_version < (1 << 16): raise ValueError('field 3 out of range (need a 16-bit value)') - if not 0 <= clock_seq_hi_variant < 1<<8: + if not 0 <= clock_seq_hi_variant < (1 << 8): raise ValueError('field 4 out of range (need an 8-bit value)') - if not 0 <= clock_seq_low < 1<<8: + if not 0 <= clock_seq_low < (1 << 8): raise ValueError('field 5 out of range (need an 8-bit value)') - if not 0 <= node < 1<<48: + if not 0 <= node < (1 << 48): raise ValueError('field 6 out of range (need a 48-bit value)') clock_seq = (clock_seq_hi_variant << 8) | clock_seq_low int = ((time_low << 96) | (time_mid << 80) | (time_hi_version << 64) | (clock_seq << 48) | node) - if int is not None: - if not 0 <= int < 1<<128: - raise ValueError('int is out of range (need a 128-bit value)') + if not 0 <= int <= _UINT_128_MAX: + raise ValueError('int is out of range (need a 128-bit value)') if version is not None: if not 1 <= version <= 8: raise ValueError('illegal version number') + # clear the variant and the version number bits + int &= _RFC_4122_CLEARFLAGS_MASK # Set the variant to RFC 4122/9562. - int &= ~(0xc000 << 48) - int |= 0x8000 << 48 + int |= 0x8000_0000_0000_0000 # (0x8000 << 48) # Set the version number. - int &= ~(0xf000 << 64) int |= version << 76 object.__setattr__(self, 'int', int) object.__setattr__(self, 'is_safe', is_safe) + @classmethod + def _from_int(cls, value): + """Create a UUID from an integer *value*. Internal use only.""" + assert 0 <= value <= _UINT_128_MAX, repr(value) + self = object.__new__(cls) + object.__setattr__(self, 'int', value) + object.__setattr__(self, 'is_safe', SafeUUID.unknown) + return self + def __getstate__(self): d = {'int': self.int} if self.is_safe != SafeUUID.unknown: @@ -700,24 +723,30 @@ def uuid3(namespace, name): """Generate a UUID from the MD5 hash of a namespace UUID and a name.""" if isinstance(name, str): name = bytes(name, "utf-8") - from hashlib import md5 - digest = md5( - namespace.bytes + name, - usedforsecurity=False - ).digest() - return UUID(bytes=digest[:16], version=3) + import hashlib + h = hashlib.md5(namespace.bytes + name, usedforsecurity=False) + int_uuid_3 = int.from_bytes(h.digest()) + int_uuid_3 &= _RFC_4122_CLEARFLAGS_MASK + int_uuid_3 |= _RFC_4122_VERSION_3_FLAGS + return UUID._from_int(int_uuid_3) def uuid4(): """Generate a random UUID.""" - return UUID(bytes=os.urandom(16), version=4) + int_uuid_4 = int.from_bytes(os.urandom(16)) + int_uuid_4 &= _RFC_4122_CLEARFLAGS_MASK + int_uuid_4 |= _RFC_4122_VERSION_4_FLAGS + return UUID._from_int(int_uuid_4) def uuid5(namespace, name): """Generate a UUID from the SHA-1 hash of a namespace UUID and a name.""" if isinstance(name, str): name = bytes(name, "utf-8") - from hashlib import sha1 - hash = sha1(namespace.bytes + name).digest() - return UUID(bytes=hash[:16], version=5) + import hashlib + h = hashlib.sha1(namespace.bytes + name, usedforsecurity=False) + int_uuid_5 = int.from_bytes(h.digest()[:16]) + int_uuid_5 &= _RFC_4122_CLEARFLAGS_MASK + int_uuid_5 |= _RFC_4122_VERSION_5_FLAGS + return UUID._from_int(int_uuid_5) def uuid8(a=None, b=None, c=None): """Generate a UUID from three custom blocks. @@ -740,7 +769,9 @@ def uuid8(a=None, b=None, c=None): int_uuid_8 = (a & 0xffff_ffff_ffff) << 80 int_uuid_8 |= (b & 0xfff) << 64 int_uuid_8 |= c & 0x3fff_ffff_ffff_ffff - return UUID(int=int_uuid_8, version=8) + # by construction, the variant and version bits are already cleared + int_uuid_8 |= _RFC_4122_VERSION_8_FLAGS + return UUID._from_int(int_uuid_8) def main(): """Run the uuid command line interface.""" diff --git a/Misc/NEWS.d/next/Library/2024-12-21-11-12-50.gh-issue-128151.aq7vpG.rst b/Misc/NEWS.d/next/Library/2024-12-21-11-12-50.gh-issue-128151.aq7vpG.rst new file mode 100644 index 00000000000000..04c744fb2ba54f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-21-11-12-50.gh-issue-128151.aq7vpG.rst @@ -0,0 +1,2 @@ +Improve generation of :class:`~uuid.UUID` objects version 3, 4, 5, and 8 +via their dedicated functions by 30%. Patch by Bénédikt Tran. From 517dc65ffcea8413e1a60c4cb5d63e5fa39e7f72 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 13 Jan 2025 12:37:48 +0000 Subject: [PATCH 281/376] GH-128682: Stronger checking of `PyStackRef_CLOSE` and `DEAD`. (GH-128683) --- Include/internal/pycore_opcode_metadata.h | 6 +- Include/internal/pycore_uop_metadata.h | 6 +- Lib/test/test_generated_cases.py | 12 +++ Python/bytecodes.c | 74 ++++++++------- Python/executor_cases.c.h | 67 +++++++------- Python/generated_cases.c.h | 75 ++++++++------- Tools/cases_generator/generators_common.py | 103 ++++++++++++++++++--- 7 files changed, 220 insertions(+), 123 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 8c4182ae384e5f..5365e2a5c6b8cd 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1985,7 +1985,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [BINARY_OP_ADD_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_ADD_INT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_ADD_UNICODE] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, - [BINARY_OP_INPLACE_ADD_UNICODE] = { true, INSTR_FMT_IXC, HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, + [BINARY_OP_INPLACE_ADD_UNICODE] = { true, INSTR_FMT_IXC, HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_MULTIPLY_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_MULTIPLY_INT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_SUBTRACT_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, @@ -2055,7 +2055,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [DICT_UPDATE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [END_ASYNC_FOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [END_FOR] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG | HAS_NO_SAVE_IP_FLAG }, - [END_SEND] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG | HAS_PURE_FLAG }, + [END_SEND] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, [ENTER_EXECUTOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, [EXIT_INIT_CHECK] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [EXTENDED_ARG] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, @@ -2136,7 +2136,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [LOAD_SPECIAL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [LOAD_SUPER_ATTR] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [LOAD_SUPER_ATTR_ATTR] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [LOAD_SUPER_ATTR_METHOD] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, + [LOAD_SUPER_ATTR_METHOD] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [MAKE_CELL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [MAKE_FUNCTION] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [MAP_ADD] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 70328a0194fd8c..721fa94da19615 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -56,7 +56,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_POP_TOP] = HAS_PURE_FLAG, [_PUSH_NULL] = HAS_PURE_FLAG, [_END_FOR] = HAS_ESCAPES_FLAG | HAS_NO_SAVE_IP_FLAG, - [_END_SEND] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG, + [_END_SEND] = HAS_PURE_FLAG, [_UNARY_NEGATIVE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_UNARY_NOT] = HAS_PURE_FLAG, [_TO_BOOL] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -81,7 +81,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_BINARY_OP_SUBTRACT_FLOAT] = HAS_ERROR_FLAG | HAS_PURE_FLAG, [_GUARD_BOTH_UNICODE] = HAS_EXIT_FLAG, [_BINARY_OP_ADD_UNICODE] = HAS_ERROR_FLAG | HAS_PURE_FLAG, - [_BINARY_OP_INPLACE_ADD_UNICODE] = HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, + [_BINARY_OP_INPLACE_ADD_UNICODE] = HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG, [_BINARY_SUBSCR] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_BINARY_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_STORE_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -146,7 +146,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_DICT_MERGE] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_MAP_ADD] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_LOAD_SUPER_ATTR_ATTR] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_LOAD_SUPER_ATTR_METHOD] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, + [_LOAD_SUPER_ATTR_METHOD] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_LOAD_ATTR] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GUARD_TYPE_VERSION] = HAS_EXIT_FLAG, [_GUARD_TYPE_VERSION_AND_LOCK] = HAS_EXIT_FLAG, diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 75cbd8dd94e9cb..4a1c99edacb71a 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -1739,6 +1739,18 @@ def test_no_escaping_calls_in_branching_macros(self): with self.assertRaises(SyntaxError): self.run_cases_test(input, "") + def test_kill_in_wrong_order(self): + input = """ + inst(OP, (a, b -- c)) { + c = b; + PyStackRef_CLOSE(a); + PyStackRef_CLOSE(b); + } + """ + with self.assertRaises(SyntaxError): + self.run_cases_test(input, "") + + class TestGeneratedAbstractCases(unittest.TestCase): def setUp(self) -> None: super().setUp() diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 657e2fb4e1c850..ef7086c3c020b0 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -384,7 +384,7 @@ dummy_func( (void)receiver; val = value; DEAD(value); - PyStackRef_CLOSE(receiver); + DECREF_INPUTS(); } tier1 inst(INSTRUMENTED_END_SEND, (receiver, value -- val)) { @@ -681,8 +681,8 @@ dummy_func( STAT_INC(BINARY_OP, hit); PyObject *res_o = PyUnicode_Concat(left_o, right_o); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); INPUTS_DEAD(); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); @@ -725,7 +725,7 @@ dummy_func( * that the string is safe to mutate. */ assert(Py_REFCNT(left_o) >= 2); - PyStackRef_CLOSE(left); + PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); DEAD(left); PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local); PyUnicode_Append(&temp, right_o); @@ -822,8 +822,7 @@ dummy_func( err = PyObject_SetItem(PyStackRef_AsPyObjectBorrow(container), slice, PyStackRef_AsPyObjectBorrow(v)); Py_DECREF(slice); } - PyStackRef_CLOSE(v); - PyStackRef_CLOSE(container); + DECREF_INPUTS(); ERROR_IF(err, error); } @@ -2082,11 +2081,8 @@ dummy_func( int method_found = 0; PyObject *attr_o = _PySuper_Lookup(cls, self, name, Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); - PyStackRef_CLOSE(global_super_st); - PyStackRef_CLOSE(class_st); if (attr_o == NULL) { - PyStackRef_CLOSE(self_st); - ERROR_IF(true, error); + ERROR_NO_POP(); } if (method_found) { self_or_null = self_st; // transfer ownership @@ -2095,6 +2091,8 @@ dummy_func( PyStackRef_CLOSE(self_st); self_or_null = PyStackRef_NULL; } + PyStackRef_CLOSE(class_st); + PyStackRef_CLOSE(global_super_st); attr = PyStackRef_FromPyObjectSteal(attr_o); } @@ -2920,7 +2918,6 @@ dummy_func( else { /* `iterable` is not a generator. */ PyObject *iter_o = PyObject_GetIter(iterable_o); - DEAD(iterable); if (iter_o == NULL) { ERROR_NO_POP(); } @@ -3466,11 +3463,11 @@ dummy_func( /* Callable is not a normal Python function */ STACKREFS_TO_PYOBJECTS(args, total_args, args_o); if (CONVERSION_FAILED(args_o)) { - PyStackRef_CLOSE(callable[0]); for (int i = 0; i < total_args; i++) { PyStackRef_CLOSE(args[i]); } DEAD(self_or_null); + PyStackRef_CLOSE(callable[0]); ERROR_IF(true, error); } PyObject *res_o = PyObject_Vectorcall( @@ -3496,11 +3493,11 @@ dummy_func( } } assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(callable[0]); for (int i = 0; i < total_args; i++) { PyStackRef_CLOSE(args[i]); } DEAD(self_or_null); + PyStackRef_CLOSE(callable[0]); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -3636,11 +3633,11 @@ dummy_func( NULL); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(callable[0]); for (int i = 0; i < total_args; i++) { PyStackRef_CLOSE(args[i]); } DEAD(self_or_null); + PyStackRef_CLOSE(callable[0]); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -3865,28 +3862,29 @@ dummy_func( op(_CALL_BUILTIN_CLASS, (callable[1], self_or_null[1], args[oparg] -- res)) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - + DEOPT_IF(!PyType_Check(callable_o)); + PyTypeObject *tp = (PyTypeObject *)callable_o; int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } - DEAD(self_or_null); - DEOPT_IF(!PyType_Check(callable_o)); - PyTypeObject *tp = (PyTypeObject *)callable_o; DEOPT_IF(tp->tp_vectorcall == NULL); STAT_INC(CALL, hit); - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { DECREF_INPUTS(); ERROR_IF(true, error); } + DEAD(self_or_null); PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); + PyStackRef_CLOSE(arguments[i]); } + DEAD(args); PyStackRef_CLOSE(callable[0]); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); @@ -3939,21 +3937,22 @@ dummy_func( PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } - DEAD(self_or_null); DEOPT_IF(!PyCFunction_CheckExact(callable_o)); DEOPT_IF(PyCFunction_GET_FLAGS(callable_o) != METH_FASTCALL); STAT_INC(CALL, hit); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o); /* res = func(self, args, nargs) */ - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { DECREF_INPUTS(); ERROR_IF(true, error); } + DEAD(self_or_null); PyObject *res_o = ((PyCFunctionFast)(void(*)(void))cfunc)( PyCFunction_GET_SELF(callable_o), args_o, @@ -3963,8 +3962,9 @@ dummy_func( /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); + PyStackRef_CLOSE(arguments[i]); } + DEAD(args); PyStackRef_CLOSE(callable[0]); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); @@ -4043,8 +4043,10 @@ dummy_func( if (res_o == NULL) { GOTO_ERROR(error); } - PyStackRef_CLOSE(callable[0]); PyStackRef_CLOSE(arg_stackref); + DEAD(args); + DEAD(self_or_null); + PyStackRef_CLOSE(callable[0]); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4053,25 +4055,24 @@ dummy_func( PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } DEOPT_IF(total_args != 2); PyInterpreterState *interp = tstate->interp; DEOPT_IF(callable_o != interp->callable_cache.isinstance); STAT_INC(CALL, hit); - _PyStackRef cls_stackref = args[1]; - _PyStackRef inst_stackref = args[0]; + _PyStackRef cls_stackref = arguments[1]; + _PyStackRef inst_stackref = arguments[0]; int retval = PyObject_IsInstance(PyStackRef_AsPyObjectBorrow(inst_stackref), PyStackRef_AsPyObjectBorrow(cls_stackref)); if (retval < 0) { ERROR_NO_POP(); } res = retval ? PyStackRef_True : PyStackRef_False; assert((!PyStackRef_IsNull(res)) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(inst_stackref); - PyStackRef_CLOSE(cls_stackref); - PyStackRef_CLOSE(callable[0]); + DECREF_INPUTS(); } // This is secretly a super-instruction @@ -4370,11 +4371,11 @@ dummy_func( } PyStackRef_CLOSE(kwnames); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(callable[0]); for (int i = 0; i < total_args; i++) { PyStackRef_CLOSE(args[i]); } DEAD(self_or_null); + PyStackRef_CLOSE(callable[0]); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4529,6 +4530,8 @@ dummy_func( PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); if (PyTuple_CheckExact(callargs_o)) { tuple = callargs; + kwargs_out = kwargs_in; + DEAD(kwargs_in); DEAD(callargs); } else { @@ -4540,11 +4543,11 @@ dummy_func( if (tuple_o == NULL) { ERROR_NO_POP(); } + kwargs_out = kwargs_in; + DEAD(kwargs_in); PyStackRef_CLOSE(callargs); tuple = PyStackRef_FromPyObjectSteal(tuple_o); } - kwargs_out = kwargs_in; - DEAD(kwargs_in); } op(_DO_CALL_FUNCTION_EX, (func_st, unused, callargs_st, kwargs_st if (oparg & 1) -- result)) { @@ -4720,8 +4723,7 @@ dummy_func( inst(FORMAT_WITH_SPEC, (value, fmt_spec -- res)) { PyObject *res_o = PyObject_Format(PyStackRef_AsPyObjectBorrow(value), PyStackRef_AsPyObjectBorrow(fmt_spec)); - PyStackRef_CLOSE(value); - PyStackRef_CLOSE(fmt_spec); + DECREF_INPUTS(); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index e90d6b5ec103ae..0d3b9b269ab247 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -838,8 +838,8 @@ assert(PyUnicode_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = PyUnicode_Concat(left_o, right_o); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); if (res_o == NULL) JUMP_TO_ERROR(); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; @@ -882,7 +882,7 @@ * that the string is safe to mutate. */ assert(Py_REFCNT(left_o) >= 2); - PyStackRef_CLOSE(left); + PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local); PyUnicode_Append(&temp, right_o); *target_local = PyStackRef_FromPyObjectSteal(temp); @@ -2529,11 +2529,8 @@ PyObject *attr_o = _PySuper_Lookup(cls, self, name, Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); stack_pointer = _PyFrame_GetStackPointer(frame); - PyStackRef_CLOSE(global_super_st); - PyStackRef_CLOSE(class_st); if (attr_o == NULL) { - PyStackRef_CLOSE(self_st); - if (true) JUMP_TO_ERROR(); + JUMP_TO_ERROR(); } if (method_found) { self_or_null = self_st; // transfer ownership @@ -2541,6 +2538,8 @@ PyStackRef_CLOSE(self_st); self_or_null = PyStackRef_NULL; } + PyStackRef_CLOSE(class_st); + PyStackRef_CLOSE(global_super_st); attr = PyStackRef_FromPyObjectSteal(attr_o); stack_pointer[-3] = attr; stack_pointer[-2] = self_or_null; @@ -4215,7 +4214,7 @@ STACKREFS_TO_PYOBJECTS(args, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); - PyStackRef_CLOSE(self_or_null[0]); + PyStackRef_XCLOSE(self_or_null[0]); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } @@ -4229,10 +4228,10 @@ stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(callable[0]); for (int i = 0; i < total_args; i++) { PyStackRef_CLOSE(args[i]); } + PyStackRef_CLOSE(callable[0]); if (res_o == NULL) JUMP_TO_ERROR(); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; @@ -4695,25 +4694,26 @@ self_or_null = &stack_pointer[-1 - oparg]; callable = &stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - int total_args = oparg; - if (!PyStackRef_IsNull(self_or_null[0])) { - args--; - total_args++; - } if (!PyType_Check(callable_o)) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } PyTypeObject *tp = (PyTypeObject *)callable_o; + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null[0])) { + arguments--; + total_args++; + } if (tp->tp_vectorcall == NULL) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } STAT_INC(CALL, hit); - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); - PyStackRef_CLOSE(self_or_null[0]); + PyStackRef_XCLOSE(self_or_null[0]); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } @@ -4725,7 +4725,7 @@ STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); + PyStackRef_CLOSE(arguments[i]); } PyStackRef_CLOSE(callable[0]); if (res_o == NULL) JUMP_TO_ERROR(); @@ -4800,8 +4800,9 @@ /* Builtin METH_FASTCALL functions, without keywords */ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } if (!PyCFunction_CheckExact(callable_o)) { @@ -4815,10 +4816,10 @@ STAT_INC(CALL, hit); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o); /* res = func(self, args, nargs) */ - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); - PyStackRef_CLOSE(self_or_null[0]); + PyStackRef_XCLOSE(self_or_null[0]); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } @@ -4834,7 +4835,7 @@ assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); + PyStackRef_CLOSE(arguments[i]); } PyStackRef_CLOSE(callable[0]); if (res_o == NULL) JUMP_TO_ERROR(); @@ -4879,7 +4880,7 @@ STACKREFS_TO_PYOBJECTS(args, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); - PyStackRef_CLOSE(self_or_null[0]); + PyStackRef_XCLOSE(self_or_null[0]); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } @@ -4942,8 +4943,8 @@ if (res_o == NULL) { GOTO_ERROR(error); } - PyStackRef_CLOSE(callable[0]); PyStackRef_CLOSE(arg_stackref); + PyStackRef_CLOSE(callable[0]); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -4963,8 +4964,9 @@ /* isinstance(o, o2) */ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } if (total_args != 2) { @@ -4977,8 +4979,8 @@ JUMP_TO_JUMP_TARGET(); } STAT_INC(CALL, hit); - _PyStackRef cls_stackref = args[1]; - _PyStackRef inst_stackref = args[0]; + _PyStackRef cls_stackref = arguments[1]; + _PyStackRef inst_stackref = arguments[0]; _PyFrame_SetStackPointer(frame, stack_pointer); int retval = PyObject_IsInstance(PyStackRef_AsPyObjectBorrow(inst_stackref), PyStackRef_AsPyObjectBorrow(cls_stackref)); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -4987,9 +4989,11 @@ } res = retval ? PyStackRef_True : PyStackRef_False; assert((!PyStackRef_IsNull(res)) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(inst_stackref); - PyStackRef_CLOSE(cls_stackref); PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -5136,7 +5140,7 @@ STACKREFS_TO_PYOBJECTS(args, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); - PyStackRef_CLOSE(self_or_null[0]); + PyStackRef_XCLOSE(self_or_null[0]); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } @@ -5257,7 +5261,7 @@ STACKREFS_TO_PYOBJECTS(args, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); - PyStackRef_CLOSE(self_or_null[0]); + PyStackRef_XCLOSE(self_or_null[0]); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } @@ -5467,7 +5471,7 @@ STACKREFS_TO_PYOBJECTS(args, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); - PyStackRef_CLOSE(self_or_null[0]); + PyStackRef_XCLOSE(self_or_null[0]); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } @@ -5512,6 +5516,7 @@ PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); if (PyTuple_CheckExact(callargs_o)) { tuple = callargs; + kwargs_out = kwargs_in; } else { _PyFrame_SetStackPointer(frame, stack_pointer); @@ -5526,10 +5531,10 @@ if (tuple_o == NULL) { JUMP_TO_ERROR(); } + kwargs_out = kwargs_in; PyStackRef_CLOSE(callargs); tuple = PyStackRef_FromPyObjectSteal(tuple_o); } - kwargs_out = kwargs_in; stack_pointer[-1 - (oparg & 1)] = tuple; if (oparg & 1) stack_pointer[-(oparg & 1)] = kwargs_out; break; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 70d0814d682205..45423a2565c76d 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -159,8 +159,8 @@ assert(PyUnicode_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = PyUnicode_Concat(left_o, right_o); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); if (res_o == NULL) goto pop_2_error; res = PyStackRef_FromPyObjectSteal(res_o); } @@ -215,7 +215,7 @@ * that the string is safe to mutate. */ assert(Py_REFCNT(left_o) >= 2); - PyStackRef_CLOSE(left); + PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local); PyUnicode_Append(&temp, right_o); *target_local = PyStackRef_FromPyObjectSteal(temp); @@ -962,10 +962,10 @@ /* Callable is not a normal Python function */ STACKREFS_TO_PYOBJECTS(args, total_args, args_o); if (CONVERSION_FAILED(args_o)) { - PyStackRef_CLOSE(callable[0]); for (int i = 0; i < total_args; i++) { PyStackRef_CLOSE(args[i]); } + PyStackRef_CLOSE(callable[0]); { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -1001,10 +1001,10 @@ } } assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(callable[0]); for (int i = 0; i < total_args; i++) { PyStackRef_CLOSE(args[i]); } + PyStackRef_CLOSE(callable[0]); if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -1351,19 +1351,20 @@ self_or_null = &stack_pointer[-1 - oparg]; callable = &stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); + DEOPT_IF(!PyType_Check(callable_o), CALL); + PyTypeObject *tp = (PyTypeObject *)callable_o; int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } - DEOPT_IF(!PyType_Check(callable_o), CALL); - PyTypeObject *tp = (PyTypeObject *)callable_o; DEOPT_IF(tp->tp_vectorcall == NULL, CALL); STAT_INC(CALL, hit); - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); - PyStackRef_CLOSE(self_or_null[0]); + PyStackRef_XCLOSE(self_or_null[0]); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } @@ -1379,7 +1380,7 @@ STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); + PyStackRef_CLOSE(arguments[i]); } PyStackRef_CLOSE(callable[0]); if (res_o == NULL) { @@ -1430,8 +1431,9 @@ /* Builtin METH_FASTCALL functions, without keywords */ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } DEOPT_IF(!PyCFunction_CheckExact(callable_o), CALL); @@ -1439,10 +1441,10 @@ STAT_INC(CALL, hit); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o); /* res = func(self, args, nargs) */ - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); - PyStackRef_CLOSE(self_or_null[0]); + PyStackRef_XCLOSE(self_or_null[0]); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } @@ -1462,7 +1464,7 @@ assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); /* Free the arguments. */ for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); + PyStackRef_CLOSE(arguments[i]); } PyStackRef_CLOSE(callable[0]); if (res_o == NULL) { @@ -1529,7 +1531,7 @@ STACKREFS_TO_PYOBJECTS(args, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); - PyStackRef_CLOSE(self_or_null[0]); + PyStackRef_XCLOSE(self_or_null[0]); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } @@ -1670,6 +1672,7 @@ PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); if (PyTuple_CheckExact(callargs_o)) { tuple = callargs; + kwargs_out = kwargs_in; } else { _PyFrame_SetStackPointer(frame, stack_pointer); @@ -1684,10 +1687,10 @@ if (tuple_o == NULL) { goto error; } + kwargs_out = kwargs_in; PyStackRef_CLOSE(callargs); tuple = PyStackRef_FromPyObjectSteal(tuple_o); } - kwargs_out = kwargs_in; } // _DO_CALL_FUNCTION_EX { @@ -1872,16 +1875,17 @@ /* isinstance(o, o2) */ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } DEOPT_IF(total_args != 2, CALL); PyInterpreterState *interp = tstate->interp; DEOPT_IF(callable_o != interp->callable_cache.isinstance, CALL); STAT_INC(CALL, hit); - _PyStackRef cls_stackref = args[1]; - _PyStackRef inst_stackref = args[0]; + _PyStackRef cls_stackref = arguments[1]; + _PyStackRef inst_stackref = arguments[0]; _PyFrame_SetStackPointer(frame, stack_pointer); int retval = PyObject_IsInstance(PyStackRef_AsPyObjectBorrow(inst_stackref), PyStackRef_AsPyObjectBorrow(cls_stackref)); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -1890,9 +1894,11 @@ } res = retval ? PyStackRef_True : PyStackRef_False; assert((!PyStackRef_IsNull(res)) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(inst_stackref); - PyStackRef_CLOSE(cls_stackref); PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -1997,7 +2003,7 @@ STACKREFS_TO_PYOBJECTS(args, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); - PyStackRef_CLOSE(self_or_null[0]); + PyStackRef_XCLOSE(self_or_null[0]); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } @@ -2039,10 +2045,10 @@ } PyStackRef_CLOSE(kwnames); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(callable[0]); for (int i = 0; i < total_args; i++) { PyStackRef_CLOSE(args[i]); } + PyStackRef_CLOSE(callable[0]); if (res_o == NULL) { stack_pointer += -3 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -2198,7 +2204,7 @@ STACKREFS_TO_PYOBJECTS(args, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); - PyStackRef_CLOSE(self_or_null[0]); + PyStackRef_XCLOSE(self_or_null[0]); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } @@ -2375,8 +2381,8 @@ if (res_o == NULL) { GOTO_ERROR(error); } - PyStackRef_CLOSE(callable[0]); PyStackRef_CLOSE(arg_stackref); + PyStackRef_CLOSE(callable[0]); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -2456,7 +2462,7 @@ STACKREFS_TO_PYOBJECTS(args, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); - PyStackRef_CLOSE(self_or_null[0]); + PyStackRef_XCLOSE(self_or_null[0]); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } @@ -2541,7 +2547,7 @@ STACKREFS_TO_PYOBJECTS(args, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); - PyStackRef_CLOSE(self_or_null[0]); + PyStackRef_XCLOSE(self_or_null[0]); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } @@ -2774,7 +2780,7 @@ STACKREFS_TO_PYOBJECTS(args, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); - PyStackRef_CLOSE(self_or_null[0]); + PyStackRef_XCLOSE(self_or_null[0]); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } @@ -2792,10 +2798,10 @@ stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(callable[0]); for (int i = 0; i < total_args; i++) { PyStackRef_CLOSE(args[i]); } + PyStackRef_CLOSE(callable[0]); if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -4480,10 +4486,10 @@ /* Callable is not a normal Python function */ STACKREFS_TO_PYOBJECTS(args, total_args, args_o); if (CONVERSION_FAILED(args_o)) { - PyStackRef_CLOSE(callable[0]); for (int i = 0; i < total_args; i++) { PyStackRef_CLOSE(args[i]); } + PyStackRef_CLOSE(callable[0]); { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -4519,10 +4525,10 @@ } } assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(callable[0]); for (int i = 0; i < total_args; i++) { PyStackRef_CLOSE(args[i]); } + PyStackRef_CLOSE(callable[0]); if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -6540,11 +6546,8 @@ PyObject *attr_o = _PySuper_Lookup(cls, self, name, Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); stack_pointer = _PyFrame_GetStackPointer(frame); - PyStackRef_CLOSE(global_super_st); - PyStackRef_CLOSE(class_st); if (attr_o == NULL) { - PyStackRef_CLOSE(self_st); - goto pop_3_error; + goto error; } if (method_found) { self_or_null = self_st; // transfer ownership @@ -6552,6 +6555,8 @@ PyStackRef_CLOSE(self_st); self_or_null = PyStackRef_NULL; } + PyStackRef_CLOSE(class_st); + PyStackRef_CLOSE(global_super_st); attr = PyStackRef_FromPyObjectSteal(attr_o); stack_pointer[-3] = attr; stack_pointer[-2] = self_or_null; diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index 8df9a9cada92ae..0035deb02ffdf0 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -98,6 +98,11 @@ def always_true(tkn: Token | None) -> bool: return False return tkn.text in {"true", "1"} +NON_ESCAPING_DEALLOCS = { + "_PyFloat_ExactDealloc", + "_PyLong_ExactDealloc", + "_PyUnicode_ExactDealloc", +} class Emitter: out: CWriter @@ -116,7 +121,7 @@ def __init__(self, out: CWriter): "SAVE_STACK": self.save_stack, "RELOAD_STACK": self.reload_stack, "PyStackRef_CLOSE": self.stackref_close, - "PyStackRef_CLOSE_SPECIALIZED": self.stackref_close, + "PyStackRef_CLOSE_SPECIALIZED": self.stackref_close_specialized, "PyStackRef_AsPyObjectSteal": self.stackref_steal, "DISPATCH": self.dispatch, "INSTRUCTION_SIZE": self.instruction_size, @@ -234,23 +239,26 @@ def decref_inputs( next(tkn_iter) next(tkn_iter) self.out.emit_at("", tkn) - for var in uop.stack.inputs: - if var.name == "unused" or var.name == "null" or var.peek: + for var in storage.inputs: + if not var.defined: + continue + if var.name == "null": continue + close = "PyStackRef_CLOSE" + if "null" in var.name or var.condition and var.condition != "1": + close = "PyStackRef_XCLOSE" if var.size: if var.size == "1": - self.out.emit(f"PyStackRef_CLOSE({var.name}[0]);\n") + self.out.emit(f"{close}({var.name}[0]);\n") else: self.out.emit(f"for (int _i = {var.size}; --_i >= 0;) {{\n") - self.out.emit(f"PyStackRef_CLOSE({var.name}[_i]);\n") + self.out.emit(f"{close}({var.name}[_i]);\n") self.out.emit("}\n") elif var.condition: - if var.condition == "1": - self.out.emit(f"PyStackRef_CLOSE({var.name});\n") - elif var.condition != "0": - self.out.emit(f"PyStackRef_XCLOSE({var.name});\n") + if var.condition != "0": + self.out.emit(f"{close}({var.name});\n") else: - self.out.emit(f"PyStackRef_CLOSE({var.name});\n") + self.out.emit(f"{close}({var.name});\n") for input in storage.inputs: input.defined = False return True @@ -291,6 +299,25 @@ def kill( raise analysis_error(f"'{name}' is not a live input-only variable", name_tkn) return True + def stackref_kill( + self, + name: Token, + storage: Storage, + escapes: bool + ) -> bool: + live = "" + for var in reversed(storage.inputs): + if var.name == name.text: + if live and escapes: + raise analysis_error( + f"Cannot close '{name.text}' when " + f"'{live}' is still live", name) + var.defined = False + break + if var.defined: + live = var.name + return True + def stackref_close( self, tkn: Token, @@ -306,14 +333,60 @@ def stackref_close( name = next(tkn_iter) self.out.emit(name) if name.kind == "IDENTIFIER": - for var in storage.inputs: - if var.name == name.text: - var.defined = False + return self.stackref_kill(name, storage, True) rparen = emit_to(self.out, tkn_iter, "RPAREN") self.emit(rparen) return True - stackref_steal = stackref_close + def stackref_close_specialized( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: Uop, + storage: Storage, + inst: Instruction | None, + ) -> bool: + + self.out.emit(tkn) + tkn = next(tkn_iter) + assert tkn.kind == "LPAREN" + self.out.emit(tkn) + name = next(tkn_iter) + self.out.emit(name) + comma = next(tkn_iter) + if comma.kind != "COMMA": + raise analysis_error("Expected comma", comma) + self.out.emit(comma) + dealloc = next(tkn_iter) + if dealloc.kind != "IDENTIFIER": + raise analysis_error("Expected identifier", dealloc) + self.out.emit(dealloc) + if name.kind == "IDENTIFIER": + escapes = dealloc.text not in NON_ESCAPING_DEALLOCS + return self.stackref_kill(name, storage, escapes) + rparen = emit_to(self.out, tkn_iter, "RPAREN") + self.emit(rparen) + return True + + def stackref_steal( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: Uop, + storage: Storage, + inst: Instruction | None, + ) -> bool: + self.out.emit(tkn) + tkn = next(tkn_iter) + assert tkn.kind == "LPAREN" + self.out.emit(tkn) + name = next(tkn_iter) + self.out.emit(name) + if name.kind == "IDENTIFIER": + return self.stackref_kill(name, storage, False) + rparen = emit_to(self.out, tkn_iter, "RPAREN") + self.emit(rparen) + return True def sync_sp( self, @@ -548,7 +621,7 @@ def emit_tokens( storage.push_outputs() self._print_storage(storage) except StackError as ex: - raise analysis_error(ex.args[0], rbrace) + raise analysis_error(ex.args[0], rbrace) from None return storage def emit(self, txt: str | Token) -> None: From 76ffaef729c91bb79da6df2ade48f3ec51118300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 13 Jan 2025 13:55:09 +0100 Subject: [PATCH 282/376] gh-128078: Clear exception in `anext` before calling `_PyGen_SetStopIterationValue` (#128780) Co-authored-by: Kumar Aditya --- Lib/test/test_asyncgen.py | 17 +++++++++++++++++ ...25-01-13-12-48-30.gh-issue-128078.qOsl9B.rst | 2 ++ Objects/genobject.c | 1 + Objects/iterobject.c | 2 ++ 4 files changed, 22 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-13-12-48-30.gh-issue-128078.qOsl9B.rst diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py index 5bfd789185c675..3f631a03c9bf72 100644 --- a/Lib/test/test_asyncgen.py +++ b/Lib/test/test_asyncgen.py @@ -1152,6 +1152,23 @@ async def run(): self.loop.run_until_complete(run()) + def test_async_gen_asyncio_anext_tuple_no_exceptions(self): + # StopAsyncIteration exceptions should be cleared. + # See: https://github.com/python/cpython/issues/128078. + + async def foo(): + if False: + yield (1, 2) + + async def run(): + it = foo().__aiter__() + with self.assertRaises(StopAsyncIteration): + await it.__anext__() + res = await anext(it, ('a', 'b')) + self.assertEqual(res, ('a', 'b')) + + self.loop.run_until_complete(run()) + def test_async_gen_asyncio_anext_stopiteration(self): async def foo(): try: diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-13-12-48-30.gh-issue-128078.qOsl9B.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-13-12-48-30.gh-issue-128078.qOsl9B.rst new file mode 100644 index 00000000000000..498864a0aa3145 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-13-12-48-30.gh-issue-128078.qOsl9B.rst @@ -0,0 +1,2 @@ +Fix a :exc:`SystemError` when using :func:`anext` with a default tuple +value. Patch by Bénédikt Tran. diff --git a/Objects/genobject.c b/Objects/genobject.c index e87f199c2504ba..a4872de296e2cf 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -633,6 +633,7 @@ gen_iternext(PyObject *self) int _PyGen_SetStopIterationValue(PyObject *value) { + assert(!PyErr_Occurred()); PyObject *e; if (value == NULL || diff --git a/Objects/iterobject.c b/Objects/iterobject.c index 135ced9ea1f268..ebb342ff109222 100644 --- a/Objects/iterobject.c +++ b/Objects/iterobject.c @@ -384,6 +384,7 @@ anextawaitable_iternext(anextawaitableobject *obj) return result; } if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)) { + PyErr_Clear(); _PyGen_SetStopIterationValue(obj->default_value); } return NULL; @@ -407,6 +408,7 @@ anextawaitable_proxy(anextawaitableobject *obj, char *meth, PyObject *arg) { * exception we replace it with a `StopIteration(default)`, as if * it was the return value of `__anext__()` coroutine. */ + PyErr_Clear(); _PyGen_SetStopIterationValue(obj->default_value); } return NULL; From aa6579cb60b4fcd652d7bc83fed2668e4ae84db3 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 13 Jan 2025 14:10:41 +0100 Subject: [PATCH 283/376] gh-127773: Disable attribute cache on incompatible MRO entries (GH-127924) --- Include/cpython/object.h | 12 ++++++++- Lib/test/test_metaclass.py | 27 +++++++++++++++++++ ...-12-13-15-21-45.gh-issue-127773.E-DZR4.rst | 1 + Objects/typeobject.c | 12 ++++++++- 4 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-15-21-45.gh-issue-127773.E-DZR4.rst diff --git a/Include/cpython/object.h b/Include/cpython/object.h index e4797029da431e..c8c6bc97fa32ee 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -221,7 +221,9 @@ struct _typeobject { PyObject *tp_weaklist; /* not used for static builtin types */ destructor tp_del; - /* Type attribute cache version tag. Added in version 2.6 */ + /* Type attribute cache version tag. Added in version 2.6. + * If zero, the cache is invalid and must be initialized. + */ unsigned int tp_version_tag; destructor tp_finalize; @@ -229,9 +231,17 @@ struct _typeobject { /* bitset of which type-watchers care about this type */ unsigned char tp_watched; + + /* Number of tp_version_tag values used. + * Set to _Py_ATTR_CACHE_UNUSED if the attribute cache is + * disabled for this type (e.g. due to custom MRO entries). + * Otherwise, limited to MAX_VERSIONS_PER_CLASS (defined elsewhere). + */ uint16_t tp_versions_used; }; +#define _Py_ATTR_CACHE_UNUSED (30000) // (see tp_versions_used) + /* This struct is used by the specializer * It should be treated as an opaque blob * by code other than the specializer and interpreter. */ diff --git a/Lib/test/test_metaclass.py b/Lib/test/test_metaclass.py index b37b7defe84d1c..07a333f98fa0a9 100644 --- a/Lib/test/test_metaclass.py +++ b/Lib/test/test_metaclass.py @@ -254,6 +254,33 @@ [...] test.test_metaclass.ObscureException +Test setting attributes with a non-base type in mro() (gh-127773). + + >>> class Base: + ... value = 1 + ... + >>> class Meta(type): + ... def mro(cls): + ... return (cls, Base, object) + ... + >>> class WeirdClass(metaclass=Meta): + ... pass + ... + >>> Base.value + 1 + >>> WeirdClass.value + 1 + >>> Base.value = 2 + >>> Base.value + 2 + >>> WeirdClass.value + 2 + >>> Base.value = 3 + >>> Base.value + 3 + >>> WeirdClass.value + 3 + """ import sys diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-15-21-45.gh-issue-127773.E-DZR4.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-15-21-45.gh-issue-127773.E-DZR4.rst new file mode 100644 index 00000000000000..7e68b3fecd1c08 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-15-21-45.gh-issue-127773.E-DZR4.rst @@ -0,0 +1 @@ +Do not use the type attribute cache for types with incompatible :term:`MRO`. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 0f5ebc6f90773d..d8f5f6d9cb2366 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -992,6 +992,7 @@ static void set_version_unlocked(PyTypeObject *tp, unsigned int version) { ASSERT_TYPE_LOCK_HELD(); + assert(version == 0 || (tp->tp_versions_used != _Py_ATTR_CACHE_UNUSED)); #ifndef Py_GIL_DISABLED PyInterpreterState *interp = _PyInterpreterState_GET(); // lookup the old version and set to null @@ -1148,6 +1149,10 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) { PyObject *b = PyTuple_GET_ITEM(bases, i); PyTypeObject *cls = _PyType_CAST(b); + if (cls->tp_versions_used >= _Py_ATTR_CACHE_UNUSED) { + goto clear; + } + if (!is_subtype_with_mro(lookup_tp_mro(type), type, cls)) { goto clear; } @@ -1156,7 +1161,8 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) { clear: assert(!(type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN)); - set_version_unlocked(type, 0); /* 0 is not a valid version tag */ + set_version_unlocked(type, 0); /* 0 is not a valid version tag */ + type->tp_versions_used = _Py_ATTR_CACHE_UNUSED; if (PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) { // This field *must* be invalidated if the type is modified (see the // comment on struct _specialization_cache): @@ -1208,6 +1214,9 @@ _PyType_GetVersionForCurrentState(PyTypeObject *tp) #define MAX_VERSIONS_PER_CLASS 1000 +#if _Py_ATTR_CACHE_UNUSED < MAX_VERSIONS_PER_CLASS +#error "_Py_ATTR_CACHE_UNUSED must be bigger than max" +#endif static int assign_version_tag(PyInterpreterState *interp, PyTypeObject *type) @@ -1225,6 +1234,7 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type) return 0; } if (type->tp_versions_used >= MAX_VERSIONS_PER_CLASS) { + /* (this includes `tp_versions_used == _Py_ATTR_CACHE_UNUSED`) */ return 0; } From 5044c2245c1519e117ba3977a4c8f608785326e7 Mon Sep 17 00:00:00 2001 From: Cheryl Sabella Date: Mon, 13 Jan 2025 05:14:59 -0800 Subject: [PATCH 284/376] gh-67748: DOC:Add summary table for str methods in stdtypes.rst (GH-1709) Co-authored-by: Ezio Melotti Co-authored-by: Oleg Iarygin Co-authored-by: Carol Willing --- Doc/library/stdtypes.rst | 94 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 191827526e890f..6050784264707b 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1548,6 +1548,100 @@ objects that compare equal might have different :attr:`~range.start`, single: str (built-in class); (see also string) pair: object; string +.. _text-methods-summary: + +Text and Binary Sequence Type Methods Summary +============================================= +The following table summarizes the text and binary sequence types methods by +category. + + ++--------------------------+-------------------------------------------+---------------------------------------------------+ +| Category | :class:`str` methods | :class:`bytes` and :class:`bytearray` methods | ++==========================+===========================================+===================================================+ +| Formatting | :meth:`str.format` | | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.format_map` | | +| +-------------------------------------------+---------------------------------------------------+ +| | :ref:`f-strings` | | +| +-------------------------------------------+---------------------------------------------------+ +| | :ref:`old-string-formatting` | :ref:`bytes-formatting` | ++--------------------------+------------------+------------------------+--------------------+------------------------------+ +| Searching and Replacing | :meth:`str.find` | :meth:`str.rfind` | :meth:`bytes.find` | :meth:`bytes.rfind` | +| +------------------+------------------------+--------------------+------------------------------+ +| | :meth:`str.index`| :meth:`str.rindex` | :meth:`bytes.index`| :meth:`bytes.rindex` | +| +------------------+------------------------+--------------------+------------------------------+ +| | :meth:`str.startswith` | :meth:`bytes.startswith` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.endswith` | :meth:`bytes.endswith` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.count` | :meth:`bytes.count` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.replace` | :meth:`bytes.replace` | ++--------------------------+-------------------+-----------------------+---------------------+-----------------------------+ +| Splitting and Joining | :meth:`str.split` | :meth:`str.rsplit` | :meth:`bytes.split` | :meth:`bytes.rsplit` | +| +-------------------+-----------------------+---------------------+-----------------------------+ +| | :meth:`str.splitlines` | :meth:`bytes.splitlines` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.partition` | :meth:`bytes.partition` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.rpartition` | :meth:`bytes.rpartition` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.join` | :meth:`bytes.join` | ++--------------------------+-------------------------------------------+---------------------------------------------------+ +| String Classification | :meth:`str.isalpha` | :meth:`bytes.isalpha` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.isdecimal` | | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.isdigit` | :meth:`bytes.isdigit` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.isnumeric` | | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.isalnum` | :meth:`bytes.isalnum` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.isidentifier` | | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.islower` | :meth:`bytes.islower` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.isupper` | :meth:`bytes.isupper` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.istitle` | :meth:`bytes.istitle` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.isspace` | :meth:`bytes.isspace` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.isprintable` | | ++--------------------------+-------------------------------------------+---------------------------------------------------+ +| Case Manipulation | :meth:`str.lower` | :meth:`bytes.lower` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.upper` | :meth:`bytes.upper` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.casefold` | | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.capitalize` | :meth:`bytes.capitalize` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.title` | :meth:`bytes.title` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.swapcase` | :meth:`bytes.swapcase` | ++--------------------------+-------------------+-----------------------+---------------------+-----------------------------+ +| Padding and Stripping | :meth:`str.ljust` | :meth:`str.rjust` | :meth:`bytes.ljust` | :meth:`bytes.rjust` | +| +-------------------+-----------------------+---------------------+-----------------------------+ +| | :meth:`str.center` | :meth:`bytes.center` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.expandtabs` | :meth:`bytes.expandtabs` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.strip` | :meth:`bytes.strip` | +| +--------------------+----------------------+----------------------+----------------------------+ +| | :meth:`str.lstrip` | :meth:`str.rstrip` | :meth:`bytes.lstrip` | :meth:`bytes.rstrip` | ++--------------------------+--------------------+----------------------+----------------------+----------------------------+ +| Translation and Encoding | :meth:`str.translate` | :meth:`bytes.translate` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.maketrans` | :meth:`bytes.maketrans` | +| +-------------------------------------------+---------------------------------------------------+ +| | :meth:`str.encode` | | +| +-------------------------------------------+---------------------------------------------------+ +| | | :meth:`bytes.decode` | ++--------------------------+-------------------------------------------+---------------------------------------------------+ + .. _textseq: Text Sequence Type --- :class:`str` From 8dfc743a1dae499d3b116b81ef6aad78708c45bc Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Mon, 13 Jan 2025 08:17:38 -0500 Subject: [PATCH 285/376] gh-128182: Add per-object memory access synchronization to `ctypes` (GH-128490) --- Doc/library/ctypes.rst | 30 +++ Lib/test/test_ctypes/test_arrays.py | 22 ++- ...-01-04-11-32-46.gh-issue-128182.SJ2Zsa.rst | 2 + Modules/_ctypes/_ctypes.c | 183 +++++++++++++----- Modules/_ctypes/ctypes.h | 44 +++++ 5 files changed, 234 insertions(+), 47 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-04-11-32-46.gh-issue-128182.SJ2Zsa.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index f25bf94417c198..077a27919fc873 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -870,6 +870,36 @@ invalid non-\ ``NULL`` pointers would crash Python):: ValueError: NULL pointer access >>> +.. _ctypes-thread-safety: + +Thread safety without the GIL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In Python 3.13, the :term:`GIL` may be disabled on :term:`experimental free threaded ` builds. +In ctypes, reads and writes to a single object concurrently is safe, but not across multiple objects: + + .. code-block:: pycon + + >>> number = c_int(42) + >>> pointer_a = pointer(number) + >>> pointer_b = pointer(number) + +In the above, it's only safe for one object to read and write to the address at once if the GIL is disabled. +So, ``pointer_a`` can be shared and written to across multiple threads, but only if ``pointer_b`` +is not also attempting to do the same. If this is an issue, consider using a :class:`threading.Lock` +to synchronize access to memory: + + .. code-block:: pycon + + >>> import threading + >>> lock = threading.Lock() + >>> # Thread 1 + >>> with lock: + ... pointer_a.contents = 24 + >>> # Thread 2 + >>> with lock: + ... pointer_b.contents = 42 + .. _ctypes-type-conversions: diff --git a/Lib/test/test_ctypes/test_arrays.py b/Lib/test/test_ctypes/test_arrays.py index c80fdff5de685d..7f1f6cf58402c9 100644 --- a/Lib/test/test_ctypes/test_arrays.py +++ b/Lib/test/test_ctypes/test_arrays.py @@ -5,7 +5,7 @@ create_string_buffer, create_unicode_buffer, c_char, c_wchar, c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint, c_long, c_ulonglong, c_float, c_double, c_longdouble) -from test.support import bigmemtest, _2G +from test.support import bigmemtest, _2G, threading_helper, Py_GIL_DISABLED from ._support import (_CData, PyCArrayType, Py_TPFLAGS_DISALLOW_INSTANTIATION, Py_TPFLAGS_IMMUTABLETYPE) @@ -267,6 +267,26 @@ def test_bpo36504_signed_int_overflow(self): def test_large_array(self, size): c_char * size + @threading_helper.requires_working_threading() + @unittest.skipUnless(Py_GIL_DISABLED, "only meaningful if the GIL is disabled") + def test_thread_safety(self): + from threading import Thread + + buffer = (ctypes.c_char_p * 10)() + + def run(): + for i in range(100): + buffer.value = b"hello" + buffer[0] = b"j" + + with threading_helper.catch_threading_exception() as cm: + threads = (Thread(target=run) for _ in range(25)) + with threading_helper.start_threads(threads): + pass + + if cm.exc_value: + raise cm.exc_value + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2025-01-04-11-32-46.gh-issue-128182.SJ2Zsa.rst b/Misc/NEWS.d/next/Library/2025-01-04-11-32-46.gh-issue-128182.SJ2Zsa.rst new file mode 100644 index 00000000000000..038fecb5710436 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-04-11-32-46.gh-issue-128182.SJ2Zsa.rst @@ -0,0 +1,2 @@ +Fix crash when using :mod:`ctypes` pointers concurrently on the :term:`free +threaded ` build. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index ede95bdf98bf76..c4d130a5ec1d52 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -598,7 +598,7 @@ StructUnionType_paramfunc(ctypes_state *st, CDataObject *self) if (ptr == NULL) { return NULL; } - memcpy(ptr, self->b_ptr, self->b_size); + locked_memcpy_from(ptr, self, self->b_size); /* Create a Python object which calls PyMem_Free(ptr) in its deallocator. The object will be destroyed @@ -907,8 +907,7 @@ CDataType_from_buffer_copy_impl(PyObject *type, PyTypeObject *cls, result = generic_pycdata_new(st, (PyTypeObject *)type, NULL, NULL); if (result != NULL) { - memcpy(((CDataObject *)result)->b_ptr, - (char *)buffer->buf + offset, info->size); + locked_memcpy_to((CDataObject *) result, (char *)buffer->buf + offset, info->size); } return result; } @@ -1195,7 +1194,7 @@ PyCPointerType_paramfunc(ctypes_state *st, CDataObject *self) parg->tag = 'P'; parg->pffi_type = &ffi_type_pointer; parg->obj = Py_NewRef(self); - parg->value.p = *(void **)self->b_ptr; + parg->value.p = locked_deref(self); return parg; } @@ -1432,7 +1431,7 @@ CharArray_set_raw(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored)) goto fail; } - memcpy(self->b_ptr, ptr, size); + locked_memcpy_to(self, ptr, size); PyBuffer_Release(&view); return 0; @@ -1444,18 +1443,26 @@ CharArray_set_raw(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored)) static PyObject * CharArray_get_raw(CDataObject *self, void *Py_UNUSED(ignored)) { - return PyBytes_FromStringAndSize(self->b_ptr, self->b_size); + PyObject *res; + LOCK_PTR(self); + res = PyBytes_FromStringAndSize(self->b_ptr, self->b_size); + UNLOCK_PTR(self); + return res; } static PyObject * CharArray_get_value(CDataObject *self, void *Py_UNUSED(ignored)) { Py_ssize_t i; + PyObject *res; + LOCK_PTR(self); char *ptr = self->b_ptr; for (i = 0; i < self->b_size; ++i) if (*ptr++ == '\0') break; - return PyBytes_FromStringAndSize(self->b_ptr, i); + res = PyBytes_FromStringAndSize(self->b_ptr, i); + UNLOCK_PTR(self); + return res; } static int @@ -1486,9 +1493,11 @@ CharArray_set_value(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored) } ptr = PyBytes_AS_STRING(value); + LOCK_PTR(self); memcpy(self->b_ptr, ptr, size); if (size < self->b_size) self->b_ptr[size] = '\0'; + UNLOCK_PTR(self); Py_DECREF(value); return 0; @@ -1506,11 +1515,15 @@ static PyObject * WCharArray_get_value(CDataObject *self, void *Py_UNUSED(ignored)) { Py_ssize_t i; + PyObject *res; wchar_t *ptr = (wchar_t *)self->b_ptr; + LOCK_PTR(self); for (i = 0; i < self->b_size/(Py_ssize_t)sizeof(wchar_t); ++i) if (*ptr++ == (wchar_t)0) break; - return PyUnicode_FromWideChar((wchar_t *)self->b_ptr, i); + res = PyUnicode_FromWideChar((wchar_t *)self->b_ptr, i); + UNLOCK_PTR(self); + return res; } static int @@ -1540,10 +1553,11 @@ WCharArray_set_value(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored PyErr_SetString(PyExc_ValueError, "string too long"); return -1; } - if (PyUnicode_AsWideChar(value, (wchar_t *)self->b_ptr, size) < 0) { - return -1; - } - return 0; + Py_ssize_t rc; + LOCK_PTR(self); + rc = PyUnicode_AsWideChar(value, (wchar_t *)self->b_ptr, size); + UNLOCK_PTR(self); + return rc < 0 ? -1 : 0; } static PyGetSetDef WCharArray_getsets[] = { @@ -2053,6 +2067,7 @@ c_void_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) parg->pffi_type = &ffi_type_pointer; parg->tag = 'P'; Py_INCREF(value); + // Function pointers don't change their contents, no need to lock parg->value.p = *(void **)func->b_ptr; parg->obj = value; return (PyObject *)parg; @@ -2079,7 +2094,7 @@ c_void_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) parg->tag = 'Z'; parg->obj = Py_NewRef(value); /* Remember: b_ptr points to where the pointer is stored! */ - parg->value.p = *(void **)(((CDataObject *)value)->b_ptr); + parg->value.p = locked_deref((CDataObject *)value); return (PyObject *)parg; } } @@ -2196,7 +2211,7 @@ PyCSimpleType_paramfunc(ctypes_state *st, CDataObject *self) parg->tag = fmt[0]; parg->pffi_type = fd->pffi_type; parg->obj = Py_NewRef(self); - memcpy(&parg->value, self->b_ptr, self->b_size); + locked_memcpy_from(&parg->value, self, self->b_size); return parg; } @@ -2697,7 +2712,7 @@ PyCFuncPtrType_paramfunc(ctypes_state *st, CDataObject *self) parg->tag = 'P'; parg->pffi_type = &ffi_type_pointer; parg->obj = Py_NewRef(self); - parg->value.p = *(void **)self->b_ptr; + parg->value.p = locked_deref(self); return parg; } @@ -3017,8 +3032,12 @@ PyCData_reduce_impl(PyObject *myself, PyTypeObject *cls) if (dict == NULL) { return NULL; } + PyObject *bytes; + LOCK_PTR(self); + bytes = PyBytes_FromStringAndSize(self->b_ptr, self->b_size); + UNLOCK_PTR(self); return Py_BuildValue("O(O(NN))", st->_unpickle, Py_TYPE(myself), dict, - PyBytes_FromStringAndSize(self->b_ptr, self->b_size)); + bytes); } static PyObject * @@ -3036,7 +3055,10 @@ PyCData_setstate(PyObject *myself, PyObject *args) } if (len > self->b_size) len = self->b_size; + // XXX Can we use locked_memcpy_to()? + LOCK_PTR(self); memmove(self->b_ptr, data, len); + UNLOCK_PTR(self); mydict = PyObject_GetAttrString(myself, "__dict__"); if (mydict == NULL) { return NULL; @@ -3094,6 +3116,12 @@ static PyType_Spec pycdata_spec = { static int PyCData_MallocBuffer(CDataObject *obj, StgInfo *info) { + /* We don't have to lock in this function, because it's only + * used in constructors and therefore does not have concurrent + * access. + */ + assert (Py_REFCNT(obj) == 1); + if ((size_t)info->size <= sizeof(obj->b_value)) { /* No need to call malloc, can use the default buffer */ obj->b_ptr = (char *)&obj->b_value; @@ -3219,15 +3247,28 @@ PyObject * PyCData_get(ctypes_state *st, PyObject *type, GETFUNC getfunc, PyObject *src, Py_ssize_t index, Py_ssize_t size, char *adr) { - if (getfunc) - return getfunc(adr, size); +#ifdef Py_GIL_DISABLED + // This isn't used if the GIL is enabled, so it causes a compiler warning. + CDataObject *cdata = (CDataObject *)src; +#endif + if (getfunc) { + PyObject *res; + LOCK_PTR(cdata); + res = getfunc(adr, size); + UNLOCK_PTR(cdata); + return res; + } assert(type); StgInfo *info; if (PyStgInfo_FromType(st, type, &info) < 0) { return NULL; } if (info && info->getfunc && !_ctypes_simple_instance(st, type)) { - return info->getfunc(adr, size); + PyObject *res; + LOCK_PTR(cdata); + res = info->getfunc(adr, size); + UNLOCK_PTR(cdata); + return res; } return PyCData_FromBaseObj(st, type, src, index, adr); } @@ -3244,15 +3285,24 @@ _PyCData_set(ctypes_state *st, int err; if (setfunc) { - return setfunc(ptr, value, size); + PyObject *res; + LOCK_PTR(dst); + res = setfunc(ptr, value, size); + UNLOCK_PTR(dst); + return res; } if (!CDataObject_Check(st, value)) { StgInfo *info; if (PyStgInfo_FromType(st, type, &info) < 0) { return NULL; } - if (info && info->setfunc) - return info->setfunc(ptr, value, size); + if (info && info->setfunc) { + PyObject *res; + LOCK_PTR(dst); + res = info->setfunc(ptr, value, size); + UNLOCK_PTR(dst); + return res; + } /* If value is a tuple, we try to call the type with the tuple and use the result! @@ -3272,7 +3322,9 @@ _PyCData_set(ctypes_state *st, Py_DECREF(ob); return result; } else if (value == Py_None && PyCPointerTypeObject_Check(st, type)) { + LOCK_PTR(dst); *(void **)ptr = NULL; + UNLOCK_PTR(dst); Py_RETURN_NONE; } else { PyErr_Format(PyExc_TypeError, @@ -3288,9 +3340,7 @@ _PyCData_set(ctypes_state *st, if (err == -1) return NULL; if (err) { - memcpy(ptr, - src->b_ptr, - size); + locked_memcpy_from(ptr, src, size); if (PyCPointerTypeObject_Check(st, type)) { /* XXX */ @@ -3324,7 +3374,9 @@ _PyCData_set(ctypes_state *st, ((PyTypeObject *)type)->tp_name); return NULL; } + LOCK_PTR(src); *(void **)ptr = src->b_ptr; + UNLOCK_PTR(src); keep = GetKeepedObjects(src); if (keep == NULL) @@ -3891,6 +3943,8 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) self->paramflags = Py_XNewRef(paramflags); + // No other threads can have this object, no need to + // lock it. *(void **)self->b_ptr = address; Py_INCREF(dll); Py_DECREF(ftuple); @@ -4823,18 +4877,24 @@ Array_subscript(PyObject *myself, PyObject *item) if (slicelen <= 0) return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); if (step == 1) { - return PyBytes_FromStringAndSize(ptr + start, - slicelen); + PyObject *res; + LOCK_PTR(self); + res = PyBytes_FromStringAndSize(ptr + start, + slicelen); + UNLOCK_PTR(self); + return res; } dest = (char *)PyMem_Malloc(slicelen); if (dest == NULL) return PyErr_NoMemory(); + LOCK_PTR(self); for (cur = start, i = 0; i < slicelen; cur += step, i++) { dest[i] = ptr[cur]; } + UNLOCK_PTR(self); np = PyBytes_FromStringAndSize(dest, slicelen); PyMem_Free(dest); @@ -4847,8 +4907,12 @@ Array_subscript(PyObject *myself, PyObject *item) if (slicelen <= 0) return Py_GetConstant(Py_CONSTANT_EMPTY_STR); if (step == 1) { - return PyUnicode_FromWideChar(ptr + start, - slicelen); + PyObject *res; + LOCK_PTR(self); + res = PyUnicode_FromWideChar(ptr + start, + slicelen); + UNLOCK_PTR(self); + return res; } dest = PyMem_New(wchar_t, slicelen); @@ -4857,10 +4921,12 @@ Array_subscript(PyObject *myself, PyObject *item) return NULL; } + LOCK_PTR(self); for (cur = start, i = 0; i < slicelen; cur += step, i++) { dest[i] = ptr[cur]; } + UNLOCK_PTR(self); np = PyUnicode_FromWideChar(dest, slicelen); PyMem_Free(dest); @@ -5118,7 +5184,9 @@ Simple_set_value(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored)) assert(info); /* Cannot be NULL for CDataObject instances */ assert(info->setfunc); + LOCK_PTR(self); result = info->setfunc(self->b_ptr, value, info->size); + UNLOCK_PTR(self); if (!result) return -1; @@ -5147,7 +5215,11 @@ Simple_get_value(CDataObject *self, void *Py_UNUSED(ignored)) } assert(info); /* Cannot be NULL for CDataObject instances */ assert(info->getfunc); - return info->getfunc(self->b_ptr, self->b_size); + PyObject *res; + LOCK_PTR(self); + res = info->getfunc(self->b_ptr, self->b_size); + UNLOCK_PTR(self); + return res; } static PyGetSetDef Simple_getsets[] = { @@ -5183,7 +5255,11 @@ static PyMethodDef Simple_methods[] = { static int Simple_bool(CDataObject *self) { - return memcmp(self->b_ptr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", self->b_size); + int cmp; + LOCK_PTR(self); + cmp = memcmp(self->b_ptr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", self->b_size); + UNLOCK_PTR(self); + return cmp; } /* "%s(%s)" % (self.__class__.__name__, self.value) */ @@ -5239,8 +5315,9 @@ Pointer_item(PyObject *myself, Py_ssize_t index) Py_ssize_t size; Py_ssize_t offset; PyObject *proto; + void *deref = locked_deref(self); - if (*(void **)self->b_ptr == NULL) { + if (deref == NULL) { PyErr_SetString(PyExc_ValueError, "NULL pointer access"); return NULL; @@ -5267,7 +5344,7 @@ Pointer_item(PyObject *myself, Py_ssize_t index) offset = index * iteminfo->size; return PyCData_get(st, proto, stginfo->getfunc, (PyObject *)self, - index, size, (*(char **)self->b_ptr) + offset); + index, size, (char *)((char *)deref + offset)); } static int @@ -5284,7 +5361,8 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value) return -1; } - if (*(void **)self->b_ptr == NULL) { + void *deref = locked_deref(self); + if (deref == NULL) { PyErr_SetString(PyExc_ValueError, "NULL pointer access"); return -1; @@ -5311,13 +5389,14 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value) offset = index * iteminfo->size; return PyCData_set(st, (PyObject *)self, proto, stginfo->setfunc, value, - index, size, (*(char **)self->b_ptr) + offset); + index, size, ((char *)deref + offset)); } static PyObject * Pointer_get_contents(CDataObject *self, void *closure) { - if (*(void **)self->b_ptr == NULL) { + void *deref = locked_deref(self); + if (deref == NULL) { PyErr_SetString(PyExc_ValueError, "NULL pointer access"); return NULL; @@ -5332,7 +5411,7 @@ Pointer_get_contents(CDataObject *self, void *closure) return PyCData_FromBaseObj(st, stginfo->proto, (PyObject *)self, 0, - *(void **)self->b_ptr); + deref); } static int @@ -5367,7 +5446,7 @@ Pointer_set_contents(CDataObject *self, PyObject *value, void *closure) } dst = (CDataObject *)value; - *(void **)self->b_ptr = dst->b_ptr; + locked_deref_assign(self, dst->b_ptr); /* A Pointer instance must keep the value it points to alive. So, a @@ -5502,41 +5581,53 @@ Pointer_subscript(PyObject *myself, PyObject *item) } assert(iteminfo); if (iteminfo->getfunc == _ctypes_get_fielddesc("c")->getfunc) { - char *ptr = *(char **)self->b_ptr; + char *ptr = locked_deref(self); char *dest; if (len <= 0) return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); if (step == 1) { - return PyBytes_FromStringAndSize(ptr + start, - len); + PyObject *res; + LOCK_PTR(self); + res = PyBytes_FromStringAndSize(ptr + start, + len); + UNLOCK_PTR(self); + return res; } dest = (char *)PyMem_Malloc(len); if (dest == NULL) return PyErr_NoMemory(); + LOCK_PTR(self); for (cur = start, i = 0; i < len; cur += step, i++) { dest[i] = ptr[cur]; } + UNLOCK_PTR(self); np = PyBytes_FromStringAndSize(dest, len); PyMem_Free(dest); return np; } if (iteminfo->getfunc == _ctypes_get_fielddesc("u")->getfunc) { - wchar_t *ptr = *(wchar_t **)self->b_ptr; + wchar_t *ptr = locked_deref(self); wchar_t *dest; if (len <= 0) return Py_GetConstant(Py_CONSTANT_EMPTY_STR); if (step == 1) { - return PyUnicode_FromWideChar(ptr + start, - len); + PyObject *res; + LOCK_PTR(self); + res = PyUnicode_FromWideChar(ptr + start, + len); + UNLOCK_PTR(self); + return res; } dest = PyMem_New(wchar_t, len); if (dest == NULL) return PyErr_NoMemory(); + LOCK_PTR(self); for (cur = start, i = 0; i < len; cur += step, i++) { dest[i] = ptr[cur]; } + UNLOCK_PTR(self); np = PyUnicode_FromWideChar(dest, len); PyMem_Free(dest); return np; @@ -5562,7 +5653,7 @@ Pointer_subscript(PyObject *myself, PyObject *item) static int Pointer_bool(CDataObject *self) { - return (*(void **)self->b_ptr != NULL); + return locked_deref(self) != NULL; } static PyType_Slot pycpointer_slots[] = { @@ -5770,7 +5861,7 @@ cast(void *ptr, PyObject *src, PyObject *ctype) } } /* Should we assert that result is a pointer type? */ - memcpy(result->b_ptr, &ptr, sizeof(void *)); + locked_memcpy_to(result, &ptr, sizeof(void *)); return (PyObject *)result; failed: diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 45e00a538fb5a5..cc09639e21f7c2 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -543,3 +543,47 @@ PyStgInfo_Init(ctypes_state *state, PyTypeObject *type) info->initialized = 1; return info; } + +/* See discussion in gh-128490. The plan here is to eventually use a per-object + * lock rather than a critical section, but that work is for later. */ +#ifdef Py_GIL_DISABLED +# define LOCK_PTR(self) Py_BEGIN_CRITICAL_SECTION(self) +# define UNLOCK_PTR(self) Py_END_CRITICAL_SECTION() +#else +# define LOCK_PTR(self) +# define UNLOCK_PTR(self) +#endif + +static inline void +locked_memcpy_to(CDataObject *self, void *buf, Py_ssize_t size) +{ + LOCK_PTR(self); + (void)memcpy(self->b_ptr, buf, size); + UNLOCK_PTR(self); +} + +static inline void +locked_memcpy_from(void *buf, CDataObject *self, Py_ssize_t size) +{ + LOCK_PTR(self); + (void)memcpy(buf, self->b_ptr, size); + UNLOCK_PTR(self); +} + +static inline void * +locked_deref(CDataObject *self) +{ + void *ptr; + LOCK_PTR(self); + ptr = *(void **)self->b_ptr; + UNLOCK_PTR(self); + return ptr; +} + +static inline void +locked_deref_assign(CDataObject *self, void *new_ptr) +{ + LOCK_PTR(self); + *(void **)self->b_ptr = new_ptr; + UNLOCK_PTR(self); +} From 4533036e50f1e604e8c06710d9e19dd700f89c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 13 Jan 2025 14:25:04 +0100 Subject: [PATCH 286/376] gh-111178: fix UBSan failures in `Objects/codeobject.c` (GH-128240) --- Objects/codeobject.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index ae232cae86799b..15b36a868a47df 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -108,6 +108,8 @@ PyCode_ClearWatcher(int watcher_id) * generic helpers ******************/ +#define _PyCodeObject_CAST(op) (assert(PyCode_Check(op)), (PyCodeObject *)(op)) + static int should_intern_string(PyObject *o) { @@ -1865,11 +1867,12 @@ free_monitoring_data(_PyCoMonitoringData *data) } static void -code_dealloc(PyCodeObject *co) +code_dealloc(PyObject *self) { - _PyObject_ResurrectStart((PyObject *)co); + PyCodeObject *co = _PyCodeObject_CAST(self); + _PyObject_ResurrectStart(self); notify_code_watchers(PY_CODE_EVENT_DESTROY, co); - if (_PyObject_ResurrectEnd((PyObject *)co)) { + if (_PyObject_ResurrectEnd(self)) { return; } @@ -1918,7 +1921,7 @@ code_dealloc(PyCodeObject *co) PyMem_Free(co->_co_cached); } if (co->co_weakreflist != NULL) { - PyObject_ClearWeakRefs((PyObject*)co); + PyObject_ClearWeakRefs(self); } free_monitoring_data(co->_co_monitoring); #ifdef Py_GIL_DISABLED @@ -1939,7 +1942,7 @@ code_dealloc(PyCodeObject *co) static int code_traverse(PyObject *self, visitproc visit, void *arg) { - PyCodeObject *co = (PyCodeObject*)self; + PyCodeObject *co = _PyCodeObject_CAST(self); Py_VISIT(co->co_consts); return 0; } @@ -1948,7 +1951,7 @@ code_traverse(PyObject *self, visitproc visit, void *arg) static PyObject * code_repr(PyObject *self) { - PyCodeObject *co = (PyCodeObject*)self; + PyCodeObject *co = _PyCodeObject_CAST(self); int lineno; if (co->co_firstlineno != 0) lineno = co->co_firstlineno; @@ -2057,7 +2060,7 @@ code_richcompare(PyObject *self, PyObject *other, int op) static Py_hash_t code_hash(PyObject *self) { - PyCodeObject *co = (PyCodeObject*)self; + PyCodeObject *co = _PyCodeObject_CAST(self); Py_uhash_t uhash = 20221211; #define SCRAMBLE_IN(H) do { \ uhash ^= (Py_uhash_t)(H); \ @@ -2120,7 +2123,7 @@ static PyMemberDef code_memberlist[] = { static PyObject * code_getlnotab(PyObject *self, void *closure) { - PyCodeObject *code = (PyCodeObject*)self; + PyCodeObject *code = _PyCodeObject_CAST(self); if (PyErr_WarnEx(PyExc_DeprecationWarning, "co_lnotab is deprecated, use co_lines instead.", 1) < 0) { @@ -2132,28 +2135,28 @@ code_getlnotab(PyObject *self, void *closure) static PyObject * code_getvarnames(PyObject *self, void *closure) { - PyCodeObject *code = (PyCodeObject*)self; + PyCodeObject *code = _PyCodeObject_CAST(self); return _PyCode_GetVarnames(code); } static PyObject * code_getcellvars(PyObject *self, void *closure) { - PyCodeObject *code = (PyCodeObject*)self; + PyCodeObject *code = _PyCodeObject_CAST(self); return _PyCode_GetCellvars(code); } static PyObject * code_getfreevars(PyObject *self, void *closure) { - PyCodeObject *code = (PyCodeObject*)self; + PyCodeObject *code = _PyCodeObject_CAST(self); return _PyCode_GetFreevars(code); } static PyObject * code_getcodeadaptive(PyObject *self, void *closure) { - PyCodeObject *code = (PyCodeObject*)self; + PyCodeObject *code = _PyCodeObject_CAST(self); return PyBytes_FromStringAndSize(code->co_code_adaptive, _PyCode_NBYTES(code)); } @@ -2161,7 +2164,7 @@ code_getcodeadaptive(PyObject *self, void *closure) static PyObject * code_getcode(PyObject *self, void *closure) { - PyCodeObject *code = (PyCodeObject*)self; + PyCodeObject *code = _PyCodeObject_CAST(self); return _PyCode_GetCode(code); } @@ -2180,7 +2183,7 @@ static PyGetSetDef code_getsetlist[] = { static PyObject * code_sizeof(PyObject *self, PyObject *Py_UNUSED(args)) { - PyCodeObject *co = (PyCodeObject*)self; + PyCodeObject *co = _PyCodeObject_CAST(self); size_t res = _PyObject_VAR_SIZE(Py_TYPE(co), Py_SIZE(co)); _PyCodeObjectExtra *co_extra = (_PyCodeObjectExtra*) co->co_extra; if (co_extra != NULL) { @@ -2193,13 +2196,14 @@ code_sizeof(PyObject *self, PyObject *Py_UNUSED(args)) static PyObject * code_linesiterator(PyObject *self, PyObject *Py_UNUSED(args)) { - PyCodeObject *code = (PyCodeObject*)self; + PyCodeObject *code = _PyCodeObject_CAST(self); return (PyObject *)new_linesiterator(code); } static PyObject * -code_branchesiterator(PyCodeObject *code, PyObject *Py_UNUSED(args)) +code_branchesiterator(PyObject *self, PyObject *Py_UNUSED(args)) { + PyCodeObject *code = _PyCodeObject_CAST(self); return _PyInstrumentation_BranchesIterator(code); } @@ -2343,7 +2347,7 @@ code__varname_from_oparg_impl(PyCodeObject *self, int oparg) static struct PyMethodDef code_methods[] = { {"__sizeof__", code_sizeof, METH_NOARGS}, {"co_lines", code_linesiterator, METH_NOARGS}, - {"co_branches", (PyCFunction)code_branchesiterator, METH_NOARGS}, + {"co_branches", code_branchesiterator, METH_NOARGS}, {"co_positions", code_positionsiterator, METH_NOARGS}, CODE_REPLACE_METHODDEF CODE__VARNAME_FROM_OPARG_METHODDEF @@ -2358,7 +2362,7 @@ PyTypeObject PyCode_Type = { "code", offsetof(PyCodeObject, co_code_adaptive), sizeof(_Py_CODEUNIT), - (destructor)code_dealloc, /* tp_dealloc */ + code_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ From bf64a582f00a030fee11b7f89c6a02ea967990ca Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Mon, 13 Jan 2025 09:36:54 -0500 Subject: [PATCH 287/376] gh-128400: Only show the current thread in `Py_FatalError` on the free-threaded build (#128758) --- Lib/test/test_capi/test_misc.py | 14 ++++++++------ Lib/test/test_faulthandler.py | 14 ++++++++------ .../2025-01-12-12-19-51.gh-issue-128400.OwoIDw.rst | 2 ++ Python/pylifecycle.c | 4 ++++ 4 files changed, 22 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-01-12-12-19-51.gh-issue-128400.OwoIDw.rst diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index b62bc4c2ecd980..99d9a757759dcd 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -75,6 +75,8 @@ class InstanceMethod: id = _testcapi.instancemethod(id) testfunction = _testcapi.instancemethod(testfunction) +CURRENT_THREAD_REGEX = r'Current thread.*:\n' if not support.Py_GIL_DISABLED else r'Stack .*:\n' + class CAPITest(unittest.TestCase): def test_instancemethod(self): @@ -234,8 +236,8 @@ def test_return_null_without_error(self): r'Python runtime state: initialized\n' r'SystemError: ' r'returned NULL without setting an exception\n' - r'\n' - r'Current thread.*:\n' + r'\n' + + CURRENT_THREAD_REGEX + r' File .*", line 6 in \n') else: with self.assertRaises(SystemError) as cm: @@ -268,8 +270,8 @@ def test_return_result_with_error(self): r'SystemError: ' r'returned a result with an exception set\n' - r'\n' - r'Current thread.*:\n' + r'\n' + + CURRENT_THREAD_REGEX + r' File .*, line 6 in \n') else: with self.assertRaises(SystemError) as cm: @@ -298,8 +300,8 @@ def test_getitem_with_error(self): r'with an exception set\n' r'Python runtime state: initialized\n' r'ValueError: bug\n' - r'\n' - r'Current thread .* \(most recent call first\):\n' + r'\n' + + CURRENT_THREAD_REGEX + r' File .*, line 6 in \n' r'\n' r'Extension modules: _testcapi \(total: 1\)\n') diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 2088793cbb9387..75d303cd212c82 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -101,8 +101,7 @@ def check_error(self, code, lineno, fatal_error, *, Raise an error if the output doesn't match the expected format. """ all_threads_disabled = ( - (not py_fatal_error) - and all_threads + all_threads and (not sys._is_gil_enabled()) ) if all_threads and not all_threads_disabled: @@ -116,12 +115,15 @@ def check_error(self, code, lineno, fatal_error, *, if py_fatal_error: regex.append("Python runtime state: initialized") regex.append('') - if all_threads_disabled: + if all_threads_disabled and not py_fatal_error: regex.append("") regex.append(fr'{header} \(most recent call first\):') - if garbage_collecting and not all_threads_disabled: - regex.append(' Garbage-collecting') - regex.append(fr' File "", line {lineno} in {function}') + if support.Py_GIL_DISABLED and py_fatal_error and not know_current_thread: + regex.append(" ") + else: + if garbage_collecting and not all_threads_disabled: + regex.append(' Garbage-collecting') + regex.append(fr' File "", line {lineno} in {function}') regex = '\n'.join(regex) if other_regex: diff --git a/Misc/NEWS.d/next/C_API/2025-01-12-12-19-51.gh-issue-128400.OwoIDw.rst b/Misc/NEWS.d/next/C_API/2025-01-12-12-19-51.gh-issue-128400.OwoIDw.rst new file mode 100644 index 00000000000000..b9c117b269434c --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-01-12-12-19-51.gh-issue-128400.OwoIDw.rst @@ -0,0 +1,2 @@ +:c:func:`Py_FatalError` no longer shows all threads on the :term:`free +threaded ` build to prevent crashes. diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 8a15a09a01dbf7..9a8a92aaceb0be 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -3034,7 +3034,11 @@ _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp, PUTS(fd, "\n"); /* display the current Python stack */ +#ifndef Py_GIL_DISABLED _Py_DumpTracebackThreads(fd, interp, tstate); +#else + _Py_DumpTraceback(fd, tstate); +#endif } /* Print the current exception (if an exception is set) with its traceback, From 75214f87f1ddd1d7b9a5b663a9c688b1bd41c098 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Mon, 13 Jan 2025 20:08:33 +0530 Subject: [PATCH 288/376] gh-128421: make getters and setters of `BaseException` thread safe (#128728) --- Objects/clinic/exceptions.c.h | 311 ++++++++++++++++++++++++++++++++++ Objects/exceptions.c | 298 ++++++++++++++++++++++---------- 2 files changed, 520 insertions(+), 89 deletions(-) create mode 100644 Objects/clinic/exceptions.c.h diff --git a/Objects/clinic/exceptions.c.h b/Objects/clinic/exceptions.c.h new file mode 100644 index 00000000000000..caa5b0c63e53c5 --- /dev/null +++ b/Objects/clinic/exceptions.c.h @@ -0,0 +1,311 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() +#include "pycore_modsupport.h" // _PyArg_BadArgument() + +PyDoc_STRVAR(BaseException___reduce____doc__, +"__reduce__($self, /)\n" +"--\n" +"\n"); + +#define BASEEXCEPTION___REDUCE___METHODDEF \ + {"__reduce__", (PyCFunction)BaseException___reduce__, METH_NOARGS, BaseException___reduce____doc__}, + +static PyObject * +BaseException___reduce___impl(PyBaseExceptionObject *self); + +static PyObject * +BaseException___reduce__(PyBaseExceptionObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = BaseException___reduce___impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(BaseException___setstate____doc__, +"__setstate__($self, state, /)\n" +"--\n" +"\n"); + +#define BASEEXCEPTION___SETSTATE___METHODDEF \ + {"__setstate__", (PyCFunction)BaseException___setstate__, METH_O, BaseException___setstate____doc__}, + +static PyObject * +BaseException___setstate___impl(PyBaseExceptionObject *self, PyObject *state); + +static PyObject * +BaseException___setstate__(PyBaseExceptionObject *self, PyObject *state) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = BaseException___setstate___impl(self, state); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(BaseException_with_traceback__doc__, +"with_traceback($self, tb, /)\n" +"--\n" +"\n" +"Set self.__traceback__ to tb and return self."); + +#define BASEEXCEPTION_WITH_TRACEBACK_METHODDEF \ + {"with_traceback", (PyCFunction)BaseException_with_traceback, METH_O, BaseException_with_traceback__doc__}, + +static PyObject * +BaseException_with_traceback_impl(PyBaseExceptionObject *self, PyObject *tb); + +static PyObject * +BaseException_with_traceback(PyBaseExceptionObject *self, PyObject *tb) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = BaseException_with_traceback_impl(self, tb); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(BaseException_add_note__doc__, +"add_note($self, note, /)\n" +"--\n" +"\n" +"Add a note to the exception"); + +#define BASEEXCEPTION_ADD_NOTE_METHODDEF \ + {"add_note", (PyCFunction)BaseException_add_note, METH_O, BaseException_add_note__doc__}, + +static PyObject * +BaseException_add_note_impl(PyBaseExceptionObject *self, PyObject *note); + +static PyObject * +BaseException_add_note(PyBaseExceptionObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *note; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("add_note", "argument", "str", arg); + goto exit; + } + note = arg; + Py_BEGIN_CRITICAL_SECTION(self); + return_value = BaseException_add_note_impl(self, note); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +#if !defined(BaseException_args_DOCSTR) +# define BaseException_args_DOCSTR NULL +#endif +#if defined(BASEEXCEPTION_ARGS_GETSETDEF) +# undef BASEEXCEPTION_ARGS_GETSETDEF +# define BASEEXCEPTION_ARGS_GETSETDEF {"args", (getter)BaseException_args_get, (setter)BaseException_args_set, BaseException_args_DOCSTR}, +#else +# define BASEEXCEPTION_ARGS_GETSETDEF {"args", (getter)BaseException_args_get, NULL, BaseException_args_DOCSTR}, +#endif + +static PyObject * +BaseException_args_get_impl(PyBaseExceptionObject *self); + +static PyObject * +BaseException_args_get(PyBaseExceptionObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = BaseException_args_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(BaseException_args_DOCSTR) +# define BaseException_args_DOCSTR NULL +#endif +#if defined(BASEEXCEPTION_ARGS_GETSETDEF) +# undef BASEEXCEPTION_ARGS_GETSETDEF +# define BASEEXCEPTION_ARGS_GETSETDEF {"args", (getter)BaseException_args_get, (setter)BaseException_args_set, BaseException_args_DOCSTR}, +#else +# define BASEEXCEPTION_ARGS_GETSETDEF {"args", NULL, (setter)BaseException_args_set, NULL}, +#endif + +static int +BaseException_args_set_impl(PyBaseExceptionObject *self, PyObject *value); + +static int +BaseException_args_set(PyBaseExceptionObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = BaseException_args_set_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(BaseException___traceback___DOCSTR) +# define BaseException___traceback___DOCSTR NULL +#endif +#if defined(BASEEXCEPTION___TRACEBACK___GETSETDEF) +# undef BASEEXCEPTION___TRACEBACK___GETSETDEF +# define BASEEXCEPTION___TRACEBACK___GETSETDEF {"__traceback__", (getter)BaseException___traceback___get, (setter)BaseException___traceback___set, BaseException___traceback___DOCSTR}, +#else +# define BASEEXCEPTION___TRACEBACK___GETSETDEF {"__traceback__", (getter)BaseException___traceback___get, NULL, BaseException___traceback___DOCSTR}, +#endif + +static PyObject * +BaseException___traceback___get_impl(PyBaseExceptionObject *self); + +static PyObject * +BaseException___traceback___get(PyBaseExceptionObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = BaseException___traceback___get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(BaseException___traceback___DOCSTR) +# define BaseException___traceback___DOCSTR NULL +#endif +#if defined(BASEEXCEPTION___TRACEBACK___GETSETDEF) +# undef BASEEXCEPTION___TRACEBACK___GETSETDEF +# define BASEEXCEPTION___TRACEBACK___GETSETDEF {"__traceback__", (getter)BaseException___traceback___get, (setter)BaseException___traceback___set, BaseException___traceback___DOCSTR}, +#else +# define BASEEXCEPTION___TRACEBACK___GETSETDEF {"__traceback__", NULL, (setter)BaseException___traceback___set, NULL}, +#endif + +static int +BaseException___traceback___set_impl(PyBaseExceptionObject *self, + PyObject *value); + +static int +BaseException___traceback___set(PyBaseExceptionObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = BaseException___traceback___set_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(BaseException___context___DOCSTR) +# define BaseException___context___DOCSTR NULL +#endif +#if defined(BASEEXCEPTION___CONTEXT___GETSETDEF) +# undef BASEEXCEPTION___CONTEXT___GETSETDEF +# define BASEEXCEPTION___CONTEXT___GETSETDEF {"__context__", (getter)BaseException___context___get, (setter)BaseException___context___set, BaseException___context___DOCSTR}, +#else +# define BASEEXCEPTION___CONTEXT___GETSETDEF {"__context__", (getter)BaseException___context___get, NULL, BaseException___context___DOCSTR}, +#endif + +static PyObject * +BaseException___context___get_impl(PyBaseExceptionObject *self); + +static PyObject * +BaseException___context___get(PyBaseExceptionObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = BaseException___context___get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(BaseException___context___DOCSTR) +# define BaseException___context___DOCSTR NULL +#endif +#if defined(BASEEXCEPTION___CONTEXT___GETSETDEF) +# undef BASEEXCEPTION___CONTEXT___GETSETDEF +# define BASEEXCEPTION___CONTEXT___GETSETDEF {"__context__", (getter)BaseException___context___get, (setter)BaseException___context___set, BaseException___context___DOCSTR}, +#else +# define BASEEXCEPTION___CONTEXT___GETSETDEF {"__context__", NULL, (setter)BaseException___context___set, NULL}, +#endif + +static int +BaseException___context___set_impl(PyBaseExceptionObject *self, + PyObject *value); + +static int +BaseException___context___set(PyBaseExceptionObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = BaseException___context___set_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(BaseException___cause___DOCSTR) +# define BaseException___cause___DOCSTR NULL +#endif +#if defined(BASEEXCEPTION___CAUSE___GETSETDEF) +# undef BASEEXCEPTION___CAUSE___GETSETDEF +# define BASEEXCEPTION___CAUSE___GETSETDEF {"__cause__", (getter)BaseException___cause___get, (setter)BaseException___cause___set, BaseException___cause___DOCSTR}, +#else +# define BASEEXCEPTION___CAUSE___GETSETDEF {"__cause__", (getter)BaseException___cause___get, NULL, BaseException___cause___DOCSTR}, +#endif + +static PyObject * +BaseException___cause___get_impl(PyBaseExceptionObject *self); + +static PyObject * +BaseException___cause___get(PyBaseExceptionObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = BaseException___cause___get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(BaseException___cause___DOCSTR) +# define BaseException___cause___DOCSTR NULL +#endif +#if defined(BASEEXCEPTION___CAUSE___GETSETDEF) +# undef BASEEXCEPTION___CAUSE___GETSETDEF +# define BASEEXCEPTION___CAUSE___GETSETDEF {"__cause__", (getter)BaseException___cause___get, (setter)BaseException___cause___set, BaseException___cause___DOCSTR}, +#else +# define BASEEXCEPTION___CAUSE___GETSETDEF {"__cause__", NULL, (setter)BaseException___cause___set, NULL}, +#endif + +static int +BaseException___cause___set_impl(PyBaseExceptionObject *self, + PyObject *value); + +static int +BaseException___cause___set(PyBaseExceptionObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = BaseException___cause___set_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} +/*[clinic end generated code: output=58afcfd60057fc39 input=a9049054013a1b77]*/ diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 714f8c828afbc1..4df89edfaf3953 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -16,6 +16,13 @@ #include "osdefs.h" // SEP +#include "clinic/exceptions.c.h" + +/*[clinic input] +class BaseException "PyBaseExceptionObject *" "&PyExc_BaseException" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=90558eb0fbf8a3d0]*/ + /* Compatibility aliases */ PyObject *PyExc_EnvironmentError = NULL; // borrowed ref @@ -152,30 +159,50 @@ BaseException_traverse(PyBaseExceptionObject *self, visitproc visit, void *arg) static PyObject * BaseException_str(PyBaseExceptionObject *self) { + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(self); switch (PyTuple_GET_SIZE(self->args)) { case 0: - return Py_GetConstant(Py_CONSTANT_EMPTY_STR); + res = Py_GetConstant(Py_CONSTANT_EMPTY_STR); + break; case 1: - return PyObject_Str(PyTuple_GET_ITEM(self->args, 0)); + res = PyObject_Str(PyTuple_GET_ITEM(self->args, 0)); + break; default: - return PyObject_Str(self->args); + res = PyObject_Str(self->args); + break; } + Py_END_CRITICAL_SECTION(); + return res; } static PyObject * BaseException_repr(PyBaseExceptionObject *self) { + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(self); const char *name = _PyType_Name(Py_TYPE(self)); - if (PyTuple_GET_SIZE(self->args) == 1) - return PyUnicode_FromFormat("%s(%R)", name, + if (PyTuple_GET_SIZE(self->args) == 1) { + res = PyUnicode_FromFormat("%s(%R)", name, PyTuple_GET_ITEM(self->args, 0)); - else - return PyUnicode_FromFormat("%s%R", name, self->args); + } + else { + res = PyUnicode_FromFormat("%s%R", name, self->args); + } + Py_END_CRITICAL_SECTION(); + return res; } /* Pickling support */ + +/*[clinic input] +@critical_section +BaseException.__reduce__ +[clinic start generated code]*/ + static PyObject * -BaseException_reduce(PyBaseExceptionObject *self, PyObject *Py_UNUSED(ignored)) +BaseException___reduce___impl(PyBaseExceptionObject *self) +/*[clinic end generated code: output=af87c1247ef98748 input=283be5a10d9c964f]*/ { if (self->args && self->dict) return PyTuple_Pack(3, Py_TYPE(self), self->args, self->dict); @@ -188,8 +215,17 @@ BaseException_reduce(PyBaseExceptionObject *self, PyObject *Py_UNUSED(ignored)) * all their attributes in the __dict__. Code is taken from cPickle's * load_build function. */ + +/*[clinic input] +@critical_section +BaseException.__setstate__ + state: object + / +[clinic start generated code]*/ + static PyObject * -BaseException_setstate(PyObject *self, PyObject *state) +BaseException___setstate___impl(PyBaseExceptionObject *self, PyObject *state) +/*[clinic end generated code: output=f3834889950453ab input=5524b61cfe9b9856]*/ { PyObject *d_key, *d_value; Py_ssize_t i = 0; @@ -202,7 +238,7 @@ BaseException_setstate(PyObject *self, PyObject *state) while (PyDict_Next(state, &i, &d_key, &d_value)) { Py_INCREF(d_key); Py_INCREF(d_value); - int res = PyObject_SetAttr(self, d_key, d_value); + int res = PyObject_SetAttr((PyObject *)self, d_key, d_value); Py_DECREF(d_value); Py_DECREF(d_key); if (res < 0) { @@ -213,18 +249,26 @@ BaseException_setstate(PyObject *self, PyObject *state) Py_RETURN_NONE; } + +/*[clinic input] +@critical_section +BaseException.with_traceback + tb: object + / + +Set self.__traceback__ to tb and return self. +[clinic start generated code]*/ + static PyObject * -BaseException_with_traceback(PyObject *self, PyObject *tb) { - if (PyException_SetTraceback(self, tb)) +BaseException_with_traceback_impl(PyBaseExceptionObject *self, PyObject *tb) +/*[clinic end generated code: output=81e92f2387927f10 input=b5fb64d834717e36]*/ +{ + if (BaseException___traceback___set_impl(self, tb) < 0){ return NULL; - + } return Py_NewRef(self); } -PyDoc_STRVAR(with_traceback_doc, -"Exception.with_traceback(tb) --\n\ - set self.__traceback__ to tb and return self."); - static inline PyBaseExceptionObject* _PyBaseExceptionObject_cast(PyObject *exc) { @@ -232,18 +276,21 @@ _PyBaseExceptionObject_cast(PyObject *exc) return (PyBaseExceptionObject *)exc; } +/*[clinic input] +@critical_section +BaseException.add_note + note: object(subclass_of="&PyUnicode_Type") + / + +Add a note to the exception +[clinic start generated code]*/ + static PyObject * -BaseException_add_note(PyObject *self, PyObject *note) +BaseException_add_note_impl(PyBaseExceptionObject *self, PyObject *note) +/*[clinic end generated code: output=fb7cbcba611c187b input=e60a6b6e9596acaf]*/ { - if (!PyUnicode_Check(note)) { - PyErr_Format(PyExc_TypeError, - "note must be a str, not '%s'", - Py_TYPE(note)->tp_name); - return NULL; - } - PyObject *notes; - if (PyObject_GetOptionalAttr(self, &_Py_ID(__notes__), ¬es) < 0) { + if (PyObject_GetOptionalAttr((PyObject *)self, &_Py_ID(__notes__), ¬es) < 0) { return NULL; } if (notes == NULL) { @@ -251,7 +298,7 @@ BaseException_add_note(PyObject *self, PyObject *note) if (notes == NULL) { return NULL; } - if (PyObject_SetAttr(self, &_Py_ID(__notes__), notes) < 0) { + if (PyObject_SetAttr((PyObject *)self, &_Py_ID(__notes__), notes) < 0) { Py_DECREF(notes); return NULL; } @@ -269,22 +316,23 @@ BaseException_add_note(PyObject *self, PyObject *note) Py_RETURN_NONE; } -PyDoc_STRVAR(add_note_doc, -"Exception.add_note(note) --\n\ - add a note to the exception"); - static PyMethodDef BaseException_methods[] = { - {"__reduce__", (PyCFunction)BaseException_reduce, METH_NOARGS }, - {"__setstate__", (PyCFunction)BaseException_setstate, METH_O }, - {"with_traceback", (PyCFunction)BaseException_with_traceback, METH_O, - with_traceback_doc}, - {"add_note", (PyCFunction)BaseException_add_note, METH_O, - add_note_doc}, - {NULL, NULL, 0, NULL}, + BASEEXCEPTION___REDUCE___METHODDEF + BASEEXCEPTION___SETSTATE___METHODDEF + BASEEXCEPTION_WITH_TRACEBACK_METHODDEF + BASEEXCEPTION_ADD_NOTE_METHODDEF + {NULL, NULL, 0, NULL}, }; +/*[clinic input] +@critical_section +@getter +BaseException.args +[clinic start generated code]*/ + static PyObject * -BaseException_get_args(PyBaseExceptionObject *self, void *Py_UNUSED(ignored)) +BaseException_args_get_impl(PyBaseExceptionObject *self) +/*[clinic end generated code: output=e02e34e35cf4d677 input=64282386e4d7822d]*/ { if (self->args == NULL) { Py_RETURN_NONE; @@ -292,23 +340,37 @@ BaseException_get_args(PyBaseExceptionObject *self, void *Py_UNUSED(ignored)) return Py_NewRef(self->args); } +/*[clinic input] +@critical_section +@setter +BaseException.args +[clinic start generated code]*/ + static int -BaseException_set_args(PyBaseExceptionObject *self, PyObject *val, void *Py_UNUSED(ignored)) +BaseException_args_set_impl(PyBaseExceptionObject *self, PyObject *value) +/*[clinic end generated code: output=331137e11d8f9e80 input=2400047ea5970a84]*/ { PyObject *seq; - if (val == NULL) { + if (value == NULL) { PyErr_SetString(PyExc_TypeError, "args may not be deleted"); return -1; } - seq = PySequence_Tuple(val); + seq = PySequence_Tuple(value); if (!seq) return -1; Py_XSETREF(self->args, seq); return 0; } +/*[clinic input] +@critical_section +@getter +BaseException.__traceback__ +[clinic start generated code]*/ + static PyObject * -BaseException_get_tb(PyBaseExceptionObject *self, void *Py_UNUSED(ignored)) +BaseException___traceback___get_impl(PyBaseExceptionObject *self) +/*[clinic end generated code: output=17cf874a52339398 input=a2277f0de62170cf]*/ { if (self->traceback == NULL) { Py_RETURN_NONE; @@ -316,17 +378,26 @@ BaseException_get_tb(PyBaseExceptionObject *self, void *Py_UNUSED(ignored)) return Py_NewRef(self->traceback); } + +/*[clinic input] +@critical_section +@setter +BaseException.__traceback__ +[clinic start generated code]*/ + static int -BaseException_set_tb(PyBaseExceptionObject *self, PyObject *tb, void *Py_UNUSED(ignored)) +BaseException___traceback___set_impl(PyBaseExceptionObject *self, + PyObject *value) +/*[clinic end generated code: output=a82c86d9f29f48f0 input=12676035676badad]*/ { - if (tb == NULL) { + if (value == NULL) { PyErr_SetString(PyExc_TypeError, "__traceback__ may not be deleted"); return -1; } - if (PyTraceBack_Check(tb)) { - Py_XSETREF(self->traceback, Py_NewRef(tb)); + if (PyTraceBack_Check(value)) { + Py_XSETREF(self->traceback, Py_NewRef(value)); } - else if (tb == Py_None) { + else if (value == Py_None) { Py_CLEAR(self->traceback); } else { @@ -337,73 +408,100 @@ BaseException_set_tb(PyBaseExceptionObject *self, PyObject *tb, void *Py_UNUSED( return 0; } +/*[clinic input] +@critical_section +@getter +BaseException.__context__ +[clinic start generated code]*/ + static PyObject * -BaseException_get_context(PyObject *self, void *Py_UNUSED(ignored)) +BaseException___context___get_impl(PyBaseExceptionObject *self) +/*[clinic end generated code: output=6ec5d296ce8d1c93 input=b2d22687937e66ab]*/ { - PyObject *res = PyException_GetContext(self); - if (res) - return res; /* new reference already returned above */ - Py_RETURN_NONE; + if (self->context == NULL) { + Py_RETURN_NONE; + } + return Py_NewRef(self->context); } +/*[clinic input] +@critical_section +@setter +BaseException.__context__ +[clinic start generated code]*/ + static int -BaseException_set_context(PyObject *self, PyObject *arg, void *Py_UNUSED(ignored)) +BaseException___context___set_impl(PyBaseExceptionObject *self, + PyObject *value) +/*[clinic end generated code: output=b4cb52dcca1da3bd input=c0971adf47fa1858]*/ { - if (arg == NULL) { + if (value == NULL) { PyErr_SetString(PyExc_TypeError, "__context__ may not be deleted"); return -1; - } else if (arg == Py_None) { - arg = NULL; - } else if (!PyExceptionInstance_Check(arg)) { + } else if (value == Py_None) { + value = NULL; + } else if (!PyExceptionInstance_Check(value)) { PyErr_SetString(PyExc_TypeError, "exception context must be None " "or derive from BaseException"); return -1; } else { - /* PyException_SetContext steals this reference */ - Py_INCREF(arg); + Py_INCREF(value); } - PyException_SetContext(self, arg); + Py_XSETREF(self->context, value); return 0; } +/*[clinic input] +@critical_section +@getter +BaseException.__cause__ +[clinic start generated code]*/ + static PyObject * -BaseException_get_cause(PyObject *self, void *Py_UNUSED(ignored)) +BaseException___cause___get_impl(PyBaseExceptionObject *self) +/*[clinic end generated code: output=987f6c4d8a0bdbab input=40e0eac427b6e602]*/ { - PyObject *res = PyException_GetCause(self); - if (res) - return res; /* new reference already returned above */ - Py_RETURN_NONE; + if (self->cause == NULL) { + Py_RETURN_NONE; + } + return Py_NewRef(self->cause); } +/*[clinic input] +@critical_section +@setter +BaseException.__cause__ +[clinic start generated code]*/ + static int -BaseException_set_cause(PyObject *self, PyObject *arg, void *Py_UNUSED(ignored)) +BaseException___cause___set_impl(PyBaseExceptionObject *self, + PyObject *value) +/*[clinic end generated code: output=6161315398aaf541 input=e1b403c0bde3f62a]*/ { - if (arg == NULL) { + if (value == NULL) { PyErr_SetString(PyExc_TypeError, "__cause__ may not be deleted"); return -1; - } else if (arg == Py_None) { - arg = NULL; - } else if (!PyExceptionInstance_Check(arg)) { + } else if (value == Py_None) { + value = NULL; + } else if (!PyExceptionInstance_Check(value)) { PyErr_SetString(PyExc_TypeError, "exception cause must be None " "or derive from BaseException"); return -1; } else { /* PyException_SetCause steals this reference */ - Py_INCREF(arg); + Py_INCREF(value); } - PyException_SetCause(self, arg); + PyException_SetCause((PyObject *)self, value); return 0; } static PyGetSetDef BaseException_getset[] = { {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict}, - {"args", (getter)BaseException_get_args, (setter)BaseException_set_args}, - {"__traceback__", (getter)BaseException_get_tb, (setter)BaseException_set_tb}, - {"__context__", BaseException_get_context, - BaseException_set_context, PyDoc_STR("exception context")}, - {"__cause__", BaseException_get_cause, - BaseException_set_cause, PyDoc_STR("exception cause")}, + BASEEXCEPTION_ARGS_GETSETDEF + BASEEXCEPTION___TRACEBACK___GETSETDEF + BASEEXCEPTION___CONTEXT___GETSETDEF + BASEEXCEPTION___CAUSE___GETSETDEF {NULL}, }; @@ -411,59 +509,81 @@ static PyGetSetDef BaseException_getset[] = { PyObject * PyException_GetTraceback(PyObject *self) { - PyBaseExceptionObject *base_self = _PyBaseExceptionObject_cast(self); - return Py_XNewRef(base_self->traceback); + PyObject *traceback; + Py_BEGIN_CRITICAL_SECTION(self); + traceback = Py_XNewRef(_PyBaseExceptionObject_cast(self)->traceback); + Py_END_CRITICAL_SECTION(); + return traceback; } int PyException_SetTraceback(PyObject *self, PyObject *tb) { - return BaseException_set_tb(_PyBaseExceptionObject_cast(self), tb, NULL); + int res; + Py_BEGIN_CRITICAL_SECTION(self); + res = BaseException___traceback___set_impl(_PyBaseExceptionObject_cast(self), tb); + Py_END_CRITICAL_SECTION(); + return res; } PyObject * PyException_GetCause(PyObject *self) { - PyObject *cause = _PyBaseExceptionObject_cast(self)->cause; - return Py_XNewRef(cause); + PyObject *cause; + Py_BEGIN_CRITICAL_SECTION(self); + cause = Py_XNewRef(_PyBaseExceptionObject_cast(self)->cause); + Py_END_CRITICAL_SECTION(); + return cause; } /* Steals a reference to cause */ void PyException_SetCause(PyObject *self, PyObject *cause) { + Py_BEGIN_CRITICAL_SECTION(self); PyBaseExceptionObject *base_self = _PyBaseExceptionObject_cast(self); base_self->suppress_context = 1; Py_XSETREF(base_self->cause, cause); + Py_END_CRITICAL_SECTION(); } PyObject * PyException_GetContext(PyObject *self) { - PyObject *context = _PyBaseExceptionObject_cast(self)->context; - return Py_XNewRef(context); + PyObject *context; + Py_BEGIN_CRITICAL_SECTION(self); + context = Py_XNewRef(_PyBaseExceptionObject_cast(self)->context); + Py_END_CRITICAL_SECTION(); + return context; } /* Steals a reference to context */ void PyException_SetContext(PyObject *self, PyObject *context) { + Py_BEGIN_CRITICAL_SECTION(self); Py_XSETREF(_PyBaseExceptionObject_cast(self)->context, context); + Py_END_CRITICAL_SECTION(); } PyObject * PyException_GetArgs(PyObject *self) { - PyObject *args = _PyBaseExceptionObject_cast(self)->args; - return Py_NewRef(args); + PyObject *args; + Py_BEGIN_CRITICAL_SECTION(self); + args = Py_NewRef(_PyBaseExceptionObject_cast(self)->args); + Py_END_CRITICAL_SECTION(); + return args; } void PyException_SetArgs(PyObject *self, PyObject *args) { + Py_BEGIN_CRITICAL_SECTION(self); Py_INCREF(args); Py_XSETREF(_PyBaseExceptionObject_cast(self)->args, args); + Py_END_CRITICAL_SECTION(); } const char * @@ -4136,7 +4256,7 @@ _PyException_AddNote(PyObject *exc, PyObject *note) Py_TYPE(exc)->tp_name); return -1; } - PyObject *r = BaseException_add_note(exc, note); + PyObject *r = BaseException_add_note(_PyBaseExceptionObject_cast(exc), note); int res = r == NULL ? -1 : 0; Py_XDECREF(r); return res; From 3efe28a40b136164f0d33c4f84dfcef7e123d1a0 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Mon, 13 Jan 2025 21:06:55 +0530 Subject: [PATCH 289/376] gh-128002: add more thread safety tests for asyncio (#128480) --- Lib/test/test_asyncio/test_free_threading.py | 54 ++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/Lib/test/test_asyncio/test_free_threading.py b/Lib/test/test_asyncio/test_free_threading.py index 90bddbf3a9dda1..8f4bba5f3b97d9 100644 --- a/Lib/test/test_asyncio/test_free_threading.py +++ b/Lib/test/test_asyncio/test_free_threading.py @@ -7,6 +7,11 @@ threading_helper.requires_working_threading(module=True) + +class MyException(Exception): + pass + + def tearDownModule(): asyncio._set_event_loop_policy(None) @@ -53,6 +58,55 @@ def runner(): with threading_helper.start_threads(threads): pass + def test_run_coroutine_threadsafe(self) -> None: + results = [] + + def in_thread(loop: asyncio.AbstractEventLoop): + coro = asyncio.sleep(0.1, result=42) + fut = asyncio.run_coroutine_threadsafe(coro, loop) + result = fut.result() + self.assertEqual(result, 42) + results.append(result) + + async def main(): + loop = asyncio.get_running_loop() + async with asyncio.TaskGroup() as tg: + for _ in range(10): + tg.create_task(asyncio.to_thread(in_thread, loop)) + self.assertEqual(results, [42] * 10) + + with asyncio.Runner() as r: + loop = r.get_loop() + loop.set_task_factory(self.factory) + r.run(main()) + + def test_run_coroutine_threadsafe_exception(self) -> None: + async def coro(): + await asyncio.sleep(0) + raise MyException("test") + + def in_thread(loop: asyncio.AbstractEventLoop): + fut = asyncio.run_coroutine_threadsafe(coro(), loop) + return fut.result() + + async def main(): + loop = asyncio.get_running_loop() + tasks = [] + for _ in range(10): + task = loop.create_task(asyncio.to_thread(in_thread, loop)) + tasks.append(task) + results = await asyncio.gather(*tasks, return_exceptions=True) + + self.assertEqual(len(results), 10) + for result in results: + self.assertIsInstance(result, MyException) + self.assertEqual(str(result), "test") + + with asyncio.Runner() as r: + loop = r.get_loop() + loop.set_task_factory(self.factory) + r.run(main()) + class TestPyFreeThreading(TestFreeThreading, TestCase): all_tasks = staticmethod(asyncio.tasks._py_all_tasks) From 402b91da87052878b4e7e8946ba91bdf4ee4bebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 13 Jan 2025 16:54:13 +0100 Subject: [PATCH 290/376] gh-128078: Use `PyErr_SetRaisedException` in `_PyGen_SetStopIterationValue` (#128287) Co-authored-by: Kumar Aditya --- Lib/test/test_asyncgen.py | 2 +- Objects/genobject.c | 32 ++++++++++---------------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py index 3f631a03c9bf72..b81187871753b9 100644 --- a/Lib/test/test_asyncgen.py +++ b/Lib/test/test_asyncgen.py @@ -1165,7 +1165,7 @@ async def run(): with self.assertRaises(StopAsyncIteration): await it.__anext__() res = await anext(it, ('a', 'b')) - self.assertEqual(res, ('a', 'b')) + self.assertTupleEqual(res, ('a', 'b')) self.loop.run_until_complete(run()) diff --git a/Objects/genobject.c b/Objects/genobject.c index a4872de296e2cf..b32140766c4a38 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -634,30 +634,18 @@ int _PyGen_SetStopIterationValue(PyObject *value) { assert(!PyErr_Occurred()); - PyObject *e; - - if (value == NULL || - (!PyTuple_Check(value) && !PyExceptionInstance_Check(value))) - { - /* Delay exception instantiation if we can */ - PyErr_SetObject(PyExc_StopIteration, value); - return 0; - } - /* Construct an exception instance manually with - * PyObject_CallOneArg and pass it to PyErr_SetObject. - * - * We do this to handle a situation when "value" is a tuple, in which - * case PyErr_SetObject would set the value of StopIteration to - * the first element of the tuple. - * - * (See PyErr_SetObject/_PyErr_CreateException code for details.) - */ - e = PyObject_CallOneArg(PyExc_StopIteration, value); - if (e == NULL) { + // Construct an exception instance manually with PyObject_CallOneArg() + // but use PyErr_SetRaisedException() instead of PyErr_SetObject() as + // PyErr_SetObject(exc_type, value) has a fast path when 'value' + // is a tuple, where the value of the StopIteration exception would be + // set to 'value[0]' instead of 'value'. + PyObject *exc = value == NULL + ? PyObject_CallNoArgs(PyExc_StopIteration) + : PyObject_CallOneArg(PyExc_StopIteration, value); + if (exc == NULL) { return -1; } - PyErr_SetObject(PyExc_StopIteration, e); - Py_DECREF(e); + PyErr_SetRaisedException(exc /* stolen */); return 0; } From da8825ea95a7096bb4f933d33b212a94ade10f6e Mon Sep 17 00:00:00 2001 From: Zhikang Yan <2951256653@qq.com> Date: Mon, 13 Jan 2025 23:54:46 +0800 Subject: [PATCH 291/376] gh-128562: Fix generation of the tkinter widget names (GH-128604) There were possible conflicts if the widget class name ends with a digit. --- Lib/test/test_tkinter/test_misc.py | 10 +++++++++- Lib/tkinter/__init__.py | 2 ++ .../2025-01-08-03-09-29.gh-issue-128562.Mlv-yO.rst | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-08-03-09-29.gh-issue-128562.Mlv-yO.rst diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py index 3362169391818b..96ea3f0117ca03 100644 --- a/Lib/test/test_tkinter/test_misc.py +++ b/Lib/test/test_tkinter/test_misc.py @@ -31,12 +31,20 @@ def test_repr(self): self.assertEqual(repr(f), '') def test_generated_names(self): + class Button2(tkinter.Button): + pass + t = tkinter.Toplevel(self.root) f = tkinter.Frame(t) f2 = tkinter.Frame(t) + self.assertNotEqual(str(f), str(f2)) b = tkinter.Button(f2) - for name in str(b).split('.'): + b2 = Button2(f2) + for name in str(b).split('.') + str(b2).split('.'): self.assertFalse(name.isidentifier(), msg=repr(name)) + b3 = tkinter.Button(f2) + b4 = Button2(f2) + self.assertEqual(len({str(b), str(b2), str(b3), str(b4)}), 4) @requires_tk(8, 6, 6) def test_tk_busy(self): diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index d494c0c9687cd1..0baed8b569e40f 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -2741,6 +2741,8 @@ def _setup(self, master, cnf): del cnf['name'] if not name: name = self.__class__.__name__.lower() + if name[-1].isdigit(): + name += "!" # Avoid duplication when calculating names below if master._last_child_ids is None: master._last_child_ids = {} count = master._last_child_ids.get(name, 0) + 1 diff --git a/Misc/NEWS.d/next/Library/2025-01-08-03-09-29.gh-issue-128562.Mlv-yO.rst b/Misc/NEWS.d/next/Library/2025-01-08-03-09-29.gh-issue-128562.Mlv-yO.rst new file mode 100644 index 00000000000000..eb50dded67bea8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-08-03-09-29.gh-issue-128562.Mlv-yO.rst @@ -0,0 +1 @@ +Fix possible conflicts in generated :mod:`tkinter` widget names if the widget class name ends with a digit. From 53e8942e6938df3a32b783815f1bd4b76eed3dd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 13 Jan 2025 17:14:59 +0100 Subject: [PATCH 292/376] Explicitly import `urllib.error` in `urllib.robotparser` (#128737) --- Lib/urllib/robotparser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/urllib/robotparser.py b/Lib/urllib/robotparser.py index 24ee198c355f56..409f2b2e48de6e 100644 --- a/Lib/urllib/robotparser.py +++ b/Lib/urllib/robotparser.py @@ -11,6 +11,7 @@ """ import collections +import urllib.error import urllib.parse import urllib.request From b70a567575db37846beecbe8b40fb56b875274db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 13 Jan 2025 17:58:11 +0100 Subject: [PATCH 293/376] gh-125997: Increase test coverage for `time.sleep()` (#128751) - Add tests for durations of invalid types. - Add tests for `int` and `float` durations, including signed zeroes durations. - Add tests for nonzero very small durations and durations close to the clock resolution. --------- Co-authored-by: Victor Stinner --- Lib/test/test_time.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index 1c540bed33c71e..1147997d8d86bf 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -158,10 +158,19 @@ def test_conversions(self): self.assertEqual(int(time.mktime(time.localtime(self.t))), int(self.t)) - def test_sleep(self): + def test_sleep_exceptions(self): + self.assertRaises(TypeError, time.sleep, []) + self.assertRaises(TypeError, time.sleep, "a") + self.assertRaises(TypeError, time.sleep, complex(0, 0)) + self.assertRaises(ValueError, time.sleep, -2) self.assertRaises(ValueError, time.sleep, -1) - time.sleep(1.2) + self.assertRaises(ValueError, time.sleep, -0.1) + + def test_sleep(self): + for value in [-0.0, 0, 0.0, 1e-100, 1e-9, 1e-6, 1, 1.2]: + with self.subTest(value=value): + time.sleep(value) def test_epoch(self): # bpo-43869: Make sure that Python use the same Epoch on all platforms: From 41f73501eca2ff8b42fa4811d918a81c052a758b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jan 2025 08:44:20 +0100 Subject: [PATCH 294/376] gh-106320: Document private C APIs promoted to public C API (#128788) --- Doc/whatsnew/3.14.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 474bd6a24b8ece..1dbd871f3e3e5e 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1272,6 +1272,30 @@ Porting to Python 3.14 implementation details. (Contributed by Victor Stinner in :gh:`120600` and :gh:`124127`.) +* Private functions promoted to public C APIs: + + * ``_PyBytes_Join()``: :c:func:`PyBytes_Join`; + * ``_PyLong_IsNegative()``: :c:func:`PyLong_IsNegative`; + * ``_PyLong_IsPositive()``: :c:func:`PyLong_IsPositive`; + * ``_PyLong_IsZero()``: :c:func:`PyLong_IsZero`; + * ``_PyLong_Sign()``: :c:func:`PyLong_GetSign`; + * ``_PyUnicodeWriter_Dealloc()``: :c:func:`PyUnicodeWriter_Discard`; + * ``_PyUnicodeWriter_Finish()``: :c:func:`PyUnicodeWriter_Finish`; + * ``_PyUnicodeWriter_Init()``: :c:func:`PyUnicodeWriter_Create`; + * ``_PyUnicodeWriter_WriteChar()``: :c:func:`PyUnicodeWriter_WriteChar`; + * ``_PyUnicodeWriter_WriteStr()``: :c:func:`PyUnicodeWriter_WriteStr`; + * ``_PyUnicodeWriter_WriteSubstring()``: :c:func:`PyUnicodeWriter_WriteSubstring`; + * ``_PyUnicode_EQ()``: :c:func:`PyUnicode_Equal`; + * ``_PyUnicode_Equal()``: :c:func:`PyUnicode_Equal`; + * ``_Py_GetConfig()``: :c:func:`PyConfig_Get` and :c:func:`PyConfig_GetInt`; + * ``_Py_HashBytes()``: :c:func:`Py_HashBuffer`; + * ``_Py_fopen_obj()``: :c:func:`Py_fopen`. + + The `pythoncapi-compat project`_ can be used to get most of these new + functions on Python 3.13 and older. + +.. _pythoncapi-compat project: https://github.com/python/pythoncapi-compat/ + Deprecated ---------- From 06cad77a5b345adde88609be9c3c470c5cd9f417 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 14 Jan 2025 10:02:38 +0200 Subject: [PATCH 295/376] gh-71339: Add additional assertion methods for unittest (GH-128707) Add the following methods: * assertHasAttr() and assertNotHasAttr() * assertIsSubclass() and assertNotIsSubclass() * assertStartsWith() and assertNotStartsWith() * assertEndsWith() and assertNotEndsWith() Also improve error messages for assertIsInstance() and assertNotIsInstance(). --- Doc/library/unittest.rst | 61 ++++ Doc/whatsnew/3.14.rst | 17 + Lib/test/test_descr.py | 8 - Lib/test/test_gdb/util.py | 5 - .../resources/test_functional.py | 6 - Lib/test/test_pyclbr.py | 10 +- Lib/test/test_typing.py | 18 - Lib/test/test_unittest/test_case.py | 307 +++++++++++++++++- Lib/test/test_unittest/test_loader.py | 2 +- Lib/test/test_unittest/test_program.py | 10 +- Lib/test/test_unittest/test_result.py | 2 +- Lib/test/test_unittest/testmock/testasync.py | 14 +- .../test_unittest/testmock/testcallable.py | 14 +- .../test_unittest/testmock/testhelpers.py | 2 +- .../testmock/testmagicmethods.py | 12 +- Lib/test/test_unittest/testmock/testmock.py | 16 +- Lib/test/test_unittest/testmock/testpatch.py | 6 +- Lib/test/test_venv.py | 4 - Lib/unittest/case.py | 132 +++++++- ...5-01-10-15-06-45.gh-issue-71339.EKnpzw.rst | 9 + 20 files changed, 555 insertions(+), 100 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 7f8b710f611002..0eead59a337ef9 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -883,6 +883,12 @@ Test cases | :meth:`assertNotIsInstance(a, b) | ``not isinstance(a, b)`` | 3.2 | | ` | | | +-----------------------------------------+-----------------------------+---------------+ + | :meth:`assertIsSubclass(a, b) | ``issubclass(a, b)`` | 3.14 | + | ` | | | + +-----------------------------------------+-----------------------------+---------------+ + | :meth:`assertNotIsSubclass(a, b) | ``not issubclass(a, b)`` | 3.14 | + | ` | | | + +-----------------------------------------+-----------------------------+---------------+ All the assert methods accept a *msg* argument that, if specified, is used as the error message on failure (see also :data:`longMessage`). @@ -961,6 +967,15 @@ Test cases .. versionadded:: 3.2 + .. method:: assertIsSubclass(cls, superclass, msg=None) + assertNotIsSubclass(cls, superclass, msg=None) + + Test that *cls* is (or is not) a subclass of *superclass* (which can be a + class or a tuple of classes, as supported by :func:`issubclass`). + To check for the exact type, use :func:`assertIs(cls, superclass) `. + + .. versionadded:: next + It is also possible to check the production of exceptions, warnings, and log messages using the following methods: @@ -1210,6 +1225,24 @@ Test cases | ` | elements in the same number, | | | | regardless of their order. | | +---------------------------------------+--------------------------------+--------------+ + | :meth:`assertStartsWith(a, b) | ``a.startswith(b)`` | 3.14 | + | ` | | | + +---------------------------------------+--------------------------------+--------------+ + | :meth:`assertNotStartsWith(a, b) | ``not a.startswith(b)`` | 3.14 | + | ` | | | + +---------------------------------------+--------------------------------+--------------+ + | :meth:`assertEndsWith(a, b) | ``a.endswith(b)`` | 3.14 | + | ` | | | + +---------------------------------------+--------------------------------+--------------+ + | :meth:`assertNotEndsWith(a, b) | ``not a.endswith(b)`` | 3.14 | + | ` | | | + +---------------------------------------+--------------------------------+--------------+ + | :meth:`assertHasAttr(a, b) | ``hastattr(a, b)`` | 3.14 | + | ` | | | + +---------------------------------------+--------------------------------+--------------+ + | :meth:`assertNotHasAttr(a, b) | ``not hastattr(a, b)`` | 3.14 | + | ` | | | + +---------------------------------------+--------------------------------+--------------+ .. method:: assertAlmostEqual(first, second, places=7, msg=None, delta=None) @@ -1279,6 +1312,34 @@ Test cases .. versionadded:: 3.2 + .. method:: assertStartsWith(s, prefix, msg=None) + .. method:: assertNotStartsWith(s, prefix, msg=None) + + Test that the Unicode or byte string *s* starts (or does not start) + with a *prefix*. + *prefix* can also be a tuple of strings to try. + + .. versionadded:: next + + + .. method:: assertEndsWith(s, suffix, msg=None) + .. method:: assertNotEndsWith(s, suffix, msg=None) + + Test that the Unicode or byte string *s* ends (or does not end) + with a *suffix*. + *suffix* can also be a tuple of strings to try. + + .. versionadded:: next + + + .. method:: assertHasAttr(obj, name, msg=None) + .. method:: assertNotHasAttr(obj, name, msg=None) + + Test that the object *obj* has (or has not) an attribute *name*. + + .. versionadded:: next + + .. _type-specific-methods: The :meth:`assertEqual` method dispatches the equality check for objects of diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 1dbd871f3e3e5e..eedcc621c3c688 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -670,6 +670,23 @@ unittest directory again. It was removed in Python 3.11. (Contributed by Jacob Walls in :gh:`80958`.) +* A number of new methods were added in the :class:`~unittest.TestCase` class + that provide more specialized tests. + + - :meth:`~unittest.TestCase.assertHasAttr` and + :meth:`~unittest.TestCase.assertNotHasAttr` check whether the object + has a particular attribute. + - :meth:`~unittest.TestCase.assertIsSubclass` and + :meth:`~unittest.TestCase.assertNotIsSubclass` check whether the object + is a subclass of a particular class, or of one of a tuple of classes. + - :meth:`~unittest.TestCase.assertStartsWith`, + :meth:`~unittest.TestCase.assertNotStartsWith`, + :meth:`~unittest.TestCase.assertEndsWith` and + :meth:`~unittest.TestCase.assertNotEndsWith` check whether the Unicode + or byte string starts or ends with particular string(s). + + (Contributed by Serhiy Storchaka in :gh:`71339`.) + urllib ------ diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 168b78a477ee9c..51f97bb51f7bd2 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -405,14 +405,6 @@ def test_wrap_lenfunc_bad_cast(self): class ClassPropertiesAndMethods(unittest.TestCase): - def assertHasAttr(self, obj, name): - self.assertTrue(hasattr(obj, name), - '%r has no attribute %r' % (obj, name)) - - def assertNotHasAttr(self, obj, name): - self.assertFalse(hasattr(obj, name), - '%r has unexpected attribute %r' % (obj, name)) - def test_python_dicts(self): # Testing Python subclass of dict... self.assertTrue(issubclass(dict, dict)) diff --git a/Lib/test/test_gdb/util.py b/Lib/test/test_gdb/util.py index 8fe9cfc543395e..8097fd52ababe6 100644 --- a/Lib/test/test_gdb/util.py +++ b/Lib/test/test_gdb/util.py @@ -280,11 +280,6 @@ def get_stack_trace(self, source=None, script=None, return out - def assertEndsWith(self, actual, exp_end): - '''Ensure that the given "actual" string ends with "exp_end"''' - self.assertTrue(actual.endswith(exp_end), - msg='%r did not end with %r' % (actual, exp_end)) - def assertMultilineMatches(self, actual, pattern): m = re.match(pattern, actual, re.DOTALL) if not m: diff --git a/Lib/test/test_importlib/resources/test_functional.py b/Lib/test/test_importlib/resources/test_functional.py index 4317abf3162c52..e8d25fa4d9faf0 100644 --- a/Lib/test/test_importlib/resources/test_functional.py +++ b/Lib/test/test_importlib/resources/test_functional.py @@ -43,12 +43,6 @@ def _gen_resourcetxt_path_parts(self): with self.subTest(path_parts=path_parts): yield path_parts - def assertEndsWith(self, string, suffix): - """Assert that `string` ends with `suffix`. - - Used to ignore an architecture-specific UTF-16 byte-order mark.""" - self.assertEqual(string[-len(suffix) :], suffix) - def test_read_text(self): self.assertEqual( resources.read_text(self.anchor01, 'utf-8.file'), diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py index 4bf0576586cca5..25b313f6c25a4e 100644 --- a/Lib/test/test_pyclbr.py +++ b/Lib/test/test_pyclbr.py @@ -31,14 +31,6 @@ def assertListEq(self, l1, l2, ignore): print("l1=%r\nl2=%r\nignore=%r" % (l1, l2, ignore), file=sys.stderr) self.fail("%r missing" % missing.pop()) - def assertHasattr(self, obj, attr, ignore): - ''' succeed iff hasattr(obj,attr) or attr in ignore. ''' - if attr in ignore: return - if not hasattr(obj, attr): print("???", attr) - self.assertTrue(hasattr(obj, attr), - 'expected hasattr(%r, %r)' % (obj, attr)) - - def assertHaskey(self, obj, key, ignore): ''' succeed iff key in obj or key in ignore. ''' if key in ignore: return @@ -86,7 +78,7 @@ def ismethod(oclass, obj, name): for name, value in dict.items(): if name in ignore: continue - self.assertHasattr(module, name, ignore) + self.assertHasAttr(module, name, ignore) py_item = getattr(module, name) if isinstance(value, pyclbr.Function): self.assertIsInstance(py_item, (FunctionType, BuiltinFunctionType)) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index c51ee763890af2..c98e6f820e8cf7 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -59,20 +59,6 @@ class BaseTestCase(TestCase): - def assertIsSubclass(self, cls, class_or_tuple, msg=None): - if not issubclass(cls, class_or_tuple): - message = '%r is not a subclass of %r' % (cls, class_or_tuple) - if msg is not None: - message += ' : %s' % msg - raise self.failureException(message) - - def assertNotIsSubclass(self, cls, class_or_tuple, msg=None): - if issubclass(cls, class_or_tuple): - message = '%r is a subclass of %r' % (cls, class_or_tuple) - if msg is not None: - message += ' : %s' % msg - raise self.failureException(message) - def clear_caches(self): for f in typing._cleanups: f() @@ -1252,10 +1238,6 @@ class Gen[*Ts]: ... class TypeVarTupleTests(BaseTestCase): - def assertEndsWith(self, string, tail): - if not string.endswith(tail): - self.fail(f"String {string!r} does not end with {tail!r}") - def test_name(self): Ts = TypeVarTuple('Ts') self.assertEqual(Ts.__name__, 'Ts') diff --git a/Lib/test/test_unittest/test_case.py b/Lib/test/test_unittest/test_case.py index 621f8269a177ce..cd366496eedca3 100644 --- a/Lib/test/test_unittest/test_case.py +++ b/Lib/test/test_unittest/test_case.py @@ -10,6 +10,7 @@ import inspect import types +from collections import UserString from copy import deepcopy from test import support @@ -54,6 +55,10 @@ def tearDown(self): self.events.append('tearDown') +class List(list): + pass + + class Test_TestCase(unittest.TestCase, TestEquality, TestHashing): ### Set up attributes used by inherited tests @@ -85,7 +90,7 @@ class Test(unittest.TestCase): def runTest(self): raise MyException() def test(self): pass - self.assertEqual(Test().id()[-13:], '.Test.runTest') + self.assertEndsWith(Test().id(), '.Test.runTest') # test that TestCase can be instantiated with no args # primarily for use at the interactive interpreter @@ -106,7 +111,7 @@ class Test(unittest.TestCase): def runTest(self): raise MyException() def test(self): pass - self.assertEqual(Test('test').id()[-10:], '.Test.test') + self.assertEndsWith(Test('test').id(), '.Test.test') # "class TestCase([methodName])" # ... @@ -700,16 +705,120 @@ def testAssertIsNot(self): self.assertRaises(self.failureException, self.assertIsNot, thing, thing) def testAssertIsInstance(self): - thing = [] + thing = List() self.assertIsInstance(thing, list) - self.assertRaises(self.failureException, self.assertIsInstance, - thing, dict) + self.assertIsInstance(thing, (int, list)) + with self.assertRaises(self.failureException) as cm: + self.assertIsInstance(thing, int) + self.assertEqual(str(cm.exception), + "[] is not an instance of ") + with self.assertRaises(self.failureException) as cm: + self.assertIsInstance(thing, (int, float)) + self.assertEqual(str(cm.exception), + "[] is not an instance of any of (, )") + + with self.assertRaises(self.failureException) as cm: + self.assertIsInstance(thing, int, 'ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertIsInstance(thing, int, msg='ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) def testAssertNotIsInstance(self): - thing = [] - self.assertNotIsInstance(thing, dict) - self.assertRaises(self.failureException, self.assertNotIsInstance, - thing, list) + thing = List() + self.assertNotIsInstance(thing, int) + self.assertNotIsInstance(thing, (int, float)) + with self.assertRaises(self.failureException) as cm: + self.assertNotIsInstance(thing, list) + self.assertEqual(str(cm.exception), + "[] is an instance of ") + with self.assertRaises(self.failureException) as cm: + self.assertNotIsInstance(thing, (int, list)) + self.assertEqual(str(cm.exception), + "[] is an instance of ") + + with self.assertRaises(self.failureException) as cm: + self.assertNotIsInstance(thing, list, 'ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertNotIsInstance(thing, list, msg='ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + + def testAssertIsSubclass(self): + self.assertIsSubclass(List, list) + self.assertIsSubclass(List, (int, list)) + with self.assertRaises(self.failureException) as cm: + self.assertIsSubclass(List, int) + self.assertEqual(str(cm.exception), + f"{List!r} is not a subclass of ") + with self.assertRaises(self.failureException) as cm: + self.assertIsSubclass(List, (int, float)) + self.assertEqual(str(cm.exception), + f"{List!r} is not a subclass of any of (, )") + with self.assertRaises(self.failureException) as cm: + self.assertIsSubclass(1, int) + self.assertEqual(str(cm.exception), "1 is not a class") + + with self.assertRaises(self.failureException) as cm: + self.assertIsSubclass(List, int, 'ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertIsSubclass(List, int, msg='ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + + def testAssertNotIsSubclass(self): + self.assertNotIsSubclass(List, int) + self.assertNotIsSubclass(List, (int, float)) + with self.assertRaises(self.failureException) as cm: + self.assertNotIsSubclass(List, list) + self.assertEqual(str(cm.exception), + f"{List!r} is a subclass of ") + with self.assertRaises(self.failureException) as cm: + self.assertNotIsSubclass(List, (int, list)) + self.assertEqual(str(cm.exception), + f"{List!r} is a subclass of ") + with self.assertRaises(self.failureException) as cm: + self.assertNotIsSubclass(1, int) + self.assertEqual(str(cm.exception), "1 is not a class") + + with self.assertRaises(self.failureException) as cm: + self.assertNotIsSubclass(List, list, 'ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertNotIsSubclass(List, list, msg='ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + + def testAssertHasAttr(self): + a = List() + a.x = 1 + self.assertHasAttr(a, 'x') + with self.assertRaises(self.failureException) as cm: + self.assertHasAttr(a, 'y') + self.assertEqual(str(cm.exception), + "List instance has no attribute 'y'") + + with self.assertRaises(self.failureException) as cm: + self.assertHasAttr(a, 'y', 'ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertHasAttr(a, 'y', msg='ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + + def testAssertNotHasAttr(self): + a = List() + a.x = 1 + self.assertNotHasAttr(a, 'y') + with self.assertRaises(self.failureException) as cm: + self.assertNotHasAttr(a, 'x') + self.assertEqual(str(cm.exception), + "List instance has unexpected attribute 'x'") + + with self.assertRaises(self.failureException) as cm: + self.assertNotHasAttr(a, 'x', 'ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertNotHasAttr(a, 'x', msg='ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) def testAssertIn(self): animals = {'monkey': 'banana', 'cow': 'grass', 'seal': 'fish'} @@ -1864,6 +1973,186 @@ def testAssertNoLogsYieldsNone(self): pass self.assertIsNone(value) + def testAssertStartswith(self): + self.assertStartsWith('ababahalamaha', 'ababa') + self.assertStartsWith('ababahalamaha', ('x', 'ababa', 'y')) + self.assertStartsWith(UserString('ababahalamaha'), 'ababa') + self.assertStartsWith(UserString('ababahalamaha'), ('x', 'ababa', 'y')) + self.assertStartsWith(bytearray(b'ababahalamaha'), b'ababa') + self.assertStartsWith(bytearray(b'ababahalamaha'), (b'x', b'ababa', b'y')) + self.assertStartsWith(b'ababahalamaha', bytearray(b'ababa')) + self.assertStartsWith(b'ababahalamaha', + (bytearray(b'x'), bytearray(b'ababa'), bytearray(b'y'))) + + with self.assertRaises(self.failureException) as cm: + self.assertStartsWith('ababahalamaha', 'amaha') + self.assertEqual(str(cm.exception), + "'ababahalamaha' doesn't start with 'amaha'") + with self.assertRaises(self.failureException) as cm: + self.assertStartsWith('ababahalamaha', ('x', 'y')) + self.assertEqual(str(cm.exception), + "'ababahalamaha' doesn't start with any of ('x', 'y')") + + with self.assertRaises(self.failureException) as cm: + self.assertStartsWith(b'ababahalamaha', 'ababa') + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertStartsWith(b'ababahalamaha', ('amaha', 'ababa')) + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertStartsWith([], 'ababa') + self.assertEqual(str(cm.exception), 'Expected str, not list') + with self.assertRaises(self.failureException) as cm: + self.assertStartsWith('ababahalamaha', b'ababa') + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(self.failureException) as cm: + self.assertStartsWith('ababahalamaha', (b'amaha', b'ababa')) + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(TypeError): + self.assertStartsWith('ababahalamaha', ord('a')) + + with self.assertRaises(self.failureException) as cm: + self.assertStartsWith('ababahalamaha', 'amaha', 'abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertStartsWith('ababahalamaha', 'amaha', msg='abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + + def testAssertNotStartswith(self): + self.assertNotStartsWith('ababahalamaha', 'amaha') + self.assertNotStartsWith('ababahalamaha', ('x', 'amaha', 'y')) + self.assertNotStartsWith(UserString('ababahalamaha'), 'amaha') + self.assertNotStartsWith(UserString('ababahalamaha'), ('x', 'amaha', 'y')) + self.assertNotStartsWith(bytearray(b'ababahalamaha'), b'amaha') + self.assertNotStartsWith(bytearray(b'ababahalamaha'), (b'x', b'amaha', b'y')) + self.assertNotStartsWith(b'ababahalamaha', bytearray(b'amaha')) + self.assertNotStartsWith(b'ababahalamaha', + (bytearray(b'x'), bytearray(b'amaha'), bytearray(b'y'))) + + with self.assertRaises(self.failureException) as cm: + self.assertNotStartsWith('ababahalamaha', 'ababa') + self.assertEqual(str(cm.exception), + "'ababahalamaha' starts with 'ababa'") + with self.assertRaises(self.failureException) as cm: + self.assertNotStartsWith('ababahalamaha', ('x', 'ababa', 'y')) + self.assertEqual(str(cm.exception), + "'ababahalamaha' starts with 'ababa'") + + with self.assertRaises(self.failureException) as cm: + self.assertNotStartsWith(b'ababahalamaha', 'ababa') + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertNotStartsWith(b'ababahalamaha', ('amaha', 'ababa')) + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertNotStartsWith([], 'ababa') + self.assertEqual(str(cm.exception), 'Expected str, not list') + with self.assertRaises(self.failureException) as cm: + self.assertNotStartsWith('ababahalamaha', b'ababa') + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(self.failureException) as cm: + self.assertNotStartsWith('ababahalamaha', (b'amaha', b'ababa')) + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(TypeError): + self.assertNotStartsWith('ababahalamaha', ord('a')) + + with self.assertRaises(self.failureException) as cm: + self.assertNotStartsWith('ababahalamaha', 'ababa', 'abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertNotStartsWith('ababahalamaha', 'ababa', msg='abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + + def testAssertEndswith(self): + self.assertEndsWith('ababahalamaha', 'amaha') + self.assertEndsWith('ababahalamaha', ('x', 'amaha', 'y')) + self.assertEndsWith(UserString('ababahalamaha'), 'amaha') + self.assertEndsWith(UserString('ababahalamaha'), ('x', 'amaha', 'y')) + self.assertEndsWith(bytearray(b'ababahalamaha'), b'amaha') + self.assertEndsWith(bytearray(b'ababahalamaha'), (b'x', b'amaha', b'y')) + self.assertEndsWith(b'ababahalamaha', bytearray(b'amaha')) + self.assertEndsWith(b'ababahalamaha', + (bytearray(b'x'), bytearray(b'amaha'), bytearray(b'y'))) + + with self.assertRaises(self.failureException) as cm: + self.assertEndsWith('ababahalamaha', 'ababa') + self.assertEqual(str(cm.exception), + "'ababahalamaha' doesn't end with 'ababa'") + with self.assertRaises(self.failureException) as cm: + self.assertEndsWith('ababahalamaha', ('x', 'y')) + self.assertEqual(str(cm.exception), + "'ababahalamaha' doesn't end with any of ('x', 'y')") + + with self.assertRaises(self.failureException) as cm: + self.assertEndsWith(b'ababahalamaha', 'amaha') + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertEndsWith(b'ababahalamaha', ('ababa', 'amaha')) + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertEndsWith([], 'amaha') + self.assertEqual(str(cm.exception), 'Expected str, not list') + with self.assertRaises(self.failureException) as cm: + self.assertEndsWith('ababahalamaha', b'amaha') + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(self.failureException) as cm: + self.assertEndsWith('ababahalamaha', (b'ababa', b'amaha')) + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(TypeError): + self.assertEndsWith('ababahalamaha', ord('a')) + + with self.assertRaises(self.failureException) as cm: + self.assertEndsWith('ababahalamaha', 'ababa', 'abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertEndsWith('ababahalamaha', 'ababa', msg='abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + + def testAssertNotEndswith(self): + self.assertNotEndsWith('ababahalamaha', 'ababa') + self.assertNotEndsWith('ababahalamaha', ('x', 'ababa', 'y')) + self.assertNotEndsWith(UserString('ababahalamaha'), 'ababa') + self.assertNotEndsWith(UserString('ababahalamaha'), ('x', 'ababa', 'y')) + self.assertNotEndsWith(bytearray(b'ababahalamaha'), b'ababa') + self.assertNotEndsWith(bytearray(b'ababahalamaha'), (b'x', b'ababa', b'y')) + self.assertNotEndsWith(b'ababahalamaha', bytearray(b'ababa')) + self.assertNotEndsWith(b'ababahalamaha', + (bytearray(b'x'), bytearray(b'ababa'), bytearray(b'y'))) + + with self.assertRaises(self.failureException) as cm: + self.assertNotEndsWith('ababahalamaha', 'amaha') + self.assertEqual(str(cm.exception), + "'ababahalamaha' ends with 'amaha'") + with self.assertRaises(self.failureException) as cm: + self.assertNotEndsWith('ababahalamaha', ('x', 'amaha', 'y')) + self.assertEqual(str(cm.exception), + "'ababahalamaha' ends with 'amaha'") + + with self.assertRaises(self.failureException) as cm: + self.assertNotEndsWith(b'ababahalamaha', 'amaha') + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertNotEndsWith(b'ababahalamaha', ('ababa', 'amaha')) + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertNotEndsWith([], 'amaha') + self.assertEqual(str(cm.exception), 'Expected str, not list') + with self.assertRaises(self.failureException) as cm: + self.assertNotEndsWith('ababahalamaha', b'amaha') + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(self.failureException) as cm: + self.assertNotEndsWith('ababahalamaha', (b'ababa', b'amaha')) + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(TypeError): + self.assertNotEndsWith('ababahalamaha', ord('a')) + + with self.assertRaises(self.failureException) as cm: + self.assertNotEndsWith('ababahalamaha', 'amaha', 'abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertNotEndsWith('ababahalamaha', 'amaha', msg='abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + def testDeprecatedFailMethods(self): """Test that the deprecated fail* methods get removed in 3.12""" deprecated_names = [ diff --git a/Lib/test/test_unittest/test_loader.py b/Lib/test/test_unittest/test_loader.py index 83dd25ca54623f..cdff6d1a20c8df 100644 --- a/Lib/test/test_unittest/test_loader.py +++ b/Lib/test/test_unittest/test_loader.py @@ -76,7 +76,7 @@ def runTest(self): loader = unittest.TestLoader() # This has to be false for the test to succeed - self.assertFalse('runTest'.startswith(loader.testMethodPrefix)) + self.assertNotStartsWith('runTest', loader.testMethodPrefix) suite = loader.loadTestsFromTestCase(Foo) self.assertIsInstance(suite, loader.suiteClass) diff --git a/Lib/test/test_unittest/test_program.py b/Lib/test/test_unittest/test_program.py index 0b46f338ac77e1..58d0cef9708c95 100644 --- a/Lib/test/test_unittest/test_program.py +++ b/Lib/test/test_unittest/test_program.py @@ -128,14 +128,14 @@ def test_NonExit(self): argv=["foobar"], testRunner=unittest.TextTestRunner(stream=stream), testLoader=self.TestLoader(self.FooBar)) - self.assertTrue(hasattr(program, 'result')) + self.assertHasAttr(program, 'result') out = stream.getvalue() self.assertIn('\nFAIL: testFail ', out) self.assertIn('\nERROR: testError ', out) self.assertIn('\nUNEXPECTED SUCCESS: testUnexpectedSuccess ', out) expected = ('\n\nFAILED (failures=1, errors=1, skipped=1, ' 'expected failures=1, unexpected successes=1)\n') - self.assertTrue(out.endswith(expected)) + self.assertEndsWith(out, expected) @force_not_colorized def test_Exit(self): @@ -153,7 +153,7 @@ def test_Exit(self): self.assertIn('\nUNEXPECTED SUCCESS: testUnexpectedSuccess ', out) expected = ('\n\nFAILED (failures=1, errors=1, skipped=1, ' 'expected failures=1, unexpected successes=1)\n') - self.assertTrue(out.endswith(expected)) + self.assertEndsWith(out, expected) @force_not_colorized def test_ExitAsDefault(self): @@ -169,7 +169,7 @@ def test_ExitAsDefault(self): self.assertIn('\nUNEXPECTED SUCCESS: testUnexpectedSuccess ', out) expected = ('\n\nFAILED (failures=1, errors=1, skipped=1, ' 'expected failures=1, unexpected successes=1)\n') - self.assertTrue(out.endswith(expected)) + self.assertEndsWith(out, expected) @force_not_colorized def test_ExitSkippedSuite(self): @@ -182,7 +182,7 @@ def test_ExitSkippedSuite(self): self.assertEqual(cm.exception.code, 0) out = stream.getvalue() expected = '\n\nOK (skipped=1)\n' - self.assertTrue(out.endswith(expected)) + self.assertEndsWith(out, expected) @force_not_colorized def test_ExitEmptySuite(self): diff --git a/Lib/test/test_unittest/test_result.py b/Lib/test/test_unittest/test_result.py index ad6f52d7e0260e..327b246452bedf 100644 --- a/Lib/test/test_unittest/test_result.py +++ b/Lib/test/test_unittest/test_result.py @@ -462,7 +462,7 @@ def test(result): self.assertTrue(result.failfast) result = runner.run(test) stream.flush() - self.assertTrue(stream.getvalue().endswith('\n\nOK\n')) + self.assertEndsWith(stream.getvalue(), '\n\nOK\n') class Test_TextTestResult(unittest.TestCase): diff --git a/Lib/test/test_unittest/testmock/testasync.py b/Lib/test/test_unittest/testmock/testasync.py index afc9d1f11da1e2..0791675b5401ca 100644 --- a/Lib/test/test_unittest/testmock/testasync.py +++ b/Lib/test/test_unittest/testmock/testasync.py @@ -586,16 +586,16 @@ def test_sync_magic_methods_return_magic_mocks(self): def test_magicmock_has_async_magic_methods(self): m_mock = MagicMock() - self.assertTrue(hasattr(m_mock, "__aenter__")) - self.assertTrue(hasattr(m_mock, "__aexit__")) - self.assertTrue(hasattr(m_mock, "__anext__")) + self.assertHasAttr(m_mock, "__aenter__") + self.assertHasAttr(m_mock, "__aexit__") + self.assertHasAttr(m_mock, "__anext__") def test_asyncmock_has_sync_magic_methods(self): a_mock = AsyncMock() - self.assertTrue(hasattr(a_mock, "__enter__")) - self.assertTrue(hasattr(a_mock, "__exit__")) - self.assertTrue(hasattr(a_mock, "__next__")) - self.assertTrue(hasattr(a_mock, "__len__")) + self.assertHasAttr(a_mock, "__enter__") + self.assertHasAttr(a_mock, "__exit__") + self.assertHasAttr(a_mock, "__next__") + self.assertHasAttr(a_mock, "__len__") def test_magic_methods_are_async_functions(self): m_mock = MagicMock() diff --git a/Lib/test/test_unittest/testmock/testcallable.py b/Lib/test/test_unittest/testmock/testcallable.py index ca88511f63959d..03cb983e447c70 100644 --- a/Lib/test/test_unittest/testmock/testcallable.py +++ b/Lib/test/test_unittest/testmock/testcallable.py @@ -23,21 +23,21 @@ def assertNotCallable(self, mock): def test_non_callable(self): for mock in NonCallableMagicMock(), NonCallableMock(): self.assertRaises(TypeError, mock) - self.assertFalse(hasattr(mock, '__call__')) + self.assertNotHasAttr(mock, '__call__') self.assertIn(mock.__class__.__name__, repr(mock)) def test_hierarchy(self): - self.assertTrue(issubclass(MagicMock, Mock)) - self.assertTrue(issubclass(NonCallableMagicMock, NonCallableMock)) + self.assertIsSubclass(MagicMock, Mock) + self.assertIsSubclass(NonCallableMagicMock, NonCallableMock) def test_attributes(self): one = NonCallableMock() - self.assertTrue(issubclass(type(one.one), Mock)) + self.assertIsSubclass(type(one.one), Mock) two = NonCallableMagicMock() - self.assertTrue(issubclass(type(two.two), MagicMock)) + self.assertIsSubclass(type(two.two), MagicMock) def test_subclasses(self): @@ -45,13 +45,13 @@ class MockSub(Mock): pass one = MockSub() - self.assertTrue(issubclass(type(one.one), MockSub)) + self.assertIsSubclass(type(one.one), MockSub) class MagicSub(MagicMock): pass two = MagicSub() - self.assertTrue(issubclass(type(two.two), MagicSub)) + self.assertIsSubclass(type(two.two), MagicSub) def test_patch_spec(self): diff --git a/Lib/test/test_unittest/testmock/testhelpers.py b/Lib/test/test_unittest/testmock/testhelpers.py index f260769eb8c35e..8d0f3ebc5cba88 100644 --- a/Lib/test/test_unittest/testmock/testhelpers.py +++ b/Lib/test/test_unittest/testmock/testhelpers.py @@ -951,7 +951,7 @@ def __getattr__(self, attribute): proxy = Foo() autospec = create_autospec(proxy) - self.assertFalse(hasattr(autospec, '__name__')) + self.assertNotHasAttr(autospec, '__name__') def test_autospec_signature_staticmethod(self): diff --git a/Lib/test/test_unittest/testmock/testmagicmethods.py b/Lib/test/test_unittest/testmock/testmagicmethods.py index 2a8aa11b3284f6..acdbd699d18134 100644 --- a/Lib/test/test_unittest/testmock/testmagicmethods.py +++ b/Lib/test/test_unittest/testmock/testmagicmethods.py @@ -10,13 +10,13 @@ class TestMockingMagicMethods(unittest.TestCase): def test_deleting_magic_methods(self): mock = Mock() - self.assertFalse(hasattr(mock, '__getitem__')) + self.assertNotHasAttr(mock, '__getitem__') mock.__getitem__ = Mock() - self.assertTrue(hasattr(mock, '__getitem__')) + self.assertHasAttr(mock, '__getitem__') del mock.__getitem__ - self.assertFalse(hasattr(mock, '__getitem__')) + self.assertNotHasAttr(mock, '__getitem__') def test_magicmock_del(self): @@ -252,12 +252,12 @@ def test_magicmock(self): self.assertEqual(list(mock), [1, 2, 3]) getattr(mock, '__bool__').return_value = False - self.assertFalse(hasattr(mock, '__nonzero__')) + self.assertNotHasAttr(mock, '__nonzero__') self.assertFalse(bool(mock)) for entry in _magics: - self.assertTrue(hasattr(mock, entry)) - self.assertFalse(hasattr(mock, '__imaginary__')) + self.assertHasAttr(mock, entry) + self.assertNotHasAttr(mock, '__imaginary__') def test_magic_mock_equality(self): diff --git a/Lib/test/test_unittest/testmock/testmock.py b/Lib/test/test_unittest/testmock/testmock.py index e1b108f81e513c..5d1bf4258afacd 100644 --- a/Lib/test/test_unittest/testmock/testmock.py +++ b/Lib/test/test_unittest/testmock/testmock.py @@ -2215,13 +2215,13 @@ def test_attach_mock_patch_autospec_signature(self): def test_attribute_deletion(self): for mock in (Mock(), MagicMock(), NonCallableMagicMock(), NonCallableMock()): - self.assertTrue(hasattr(mock, 'm')) + self.assertHasAttr(mock, 'm') del mock.m - self.assertFalse(hasattr(mock, 'm')) + self.assertNotHasAttr(mock, 'm') del mock.f - self.assertFalse(hasattr(mock, 'f')) + self.assertNotHasAttr(mock, 'f') self.assertRaises(AttributeError, getattr, mock, 'f') @@ -2230,18 +2230,18 @@ def test_mock_does_not_raise_on_repeated_attribute_deletion(self): for mock in (Mock(), MagicMock(), NonCallableMagicMock(), NonCallableMock()): mock.foo = 3 - self.assertTrue(hasattr(mock, 'foo')) + self.assertHasAttr(mock, 'foo') self.assertEqual(mock.foo, 3) del mock.foo - self.assertFalse(hasattr(mock, 'foo')) + self.assertNotHasAttr(mock, 'foo') mock.foo = 4 - self.assertTrue(hasattr(mock, 'foo')) + self.assertHasAttr(mock, 'foo') self.assertEqual(mock.foo, 4) del mock.foo - self.assertFalse(hasattr(mock, 'foo')) + self.assertNotHasAttr(mock, 'foo') def test_mock_raises_when_deleting_nonexistent_attribute(self): @@ -2259,7 +2259,7 @@ def test_reset_mock_does_not_raise_on_attr_deletion(self): mock.child = True del mock.child mock.reset_mock() - self.assertFalse(hasattr(mock, 'child')) + self.assertNotHasAttr(mock, 'child') def test_class_assignable(self): diff --git a/Lib/test/test_unittest/testmock/testpatch.py b/Lib/test/test_unittest/testmock/testpatch.py index 037c021e6eafcf..7c5fc3deed2ca2 100644 --- a/Lib/test/test_unittest/testmock/testpatch.py +++ b/Lib/test/test_unittest/testmock/testpatch.py @@ -366,7 +366,7 @@ def test(): self.assertEqual(SomeClass.frooble, sentinel.Frooble) test() - self.assertFalse(hasattr(SomeClass, 'frooble')) + self.assertNotHasAttr(SomeClass, 'frooble') def test_patch_wont_create_by_default(self): @@ -383,7 +383,7 @@ def test_patchobject_wont_create_by_default(self): @patch.object(SomeClass, 'ord', sentinel.Frooble) def test(): pass test() - self.assertFalse(hasattr(SomeClass, 'ord')) + self.assertNotHasAttr(SomeClass, 'ord') def test_patch_builtins_without_create(self): @@ -1477,7 +1477,7 @@ def test_patch_multiple_create(self): finally: patcher.stop() - self.assertFalse(hasattr(Foo, 'blam')) + self.assertNotHasAttr(Foo, 'blam') def test_patch_multiple_spec_set(self): diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 0b09010c69d4ea..6e23097deaf221 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -111,10 +111,6 @@ def get_text_file_contents(self, *args, encoding='utf-8'): result = f.read() return result - def assertEndsWith(self, string, tail): - if not string.endswith(tail): - self.fail(f"String {string!r} does not end with {tail!r}") - class BasicTest(BaseTest): """Test venv module functionality.""" diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index 55c79d353539ca..e9ef551d0b3ded 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -1321,13 +1321,67 @@ def assertIsInstance(self, obj, cls, msg=None): """Same as self.assertTrue(isinstance(obj, cls)), with a nicer default message.""" if not isinstance(obj, cls): - standardMsg = '%s is not an instance of %r' % (safe_repr(obj), cls) + if isinstance(cls, tuple): + standardMsg = f'{safe_repr(obj)} is not an instance of any of {cls!r}' + else: + standardMsg = f'{safe_repr(obj)} is not an instance of {cls!r}' self.fail(self._formatMessage(msg, standardMsg)) def assertNotIsInstance(self, obj, cls, msg=None): """Included for symmetry with assertIsInstance.""" if isinstance(obj, cls): - standardMsg = '%s is an instance of %r' % (safe_repr(obj), cls) + if isinstance(cls, tuple): + for x in cls: + if isinstance(obj, x): + cls = x + break + standardMsg = f'{safe_repr(obj)} is an instance of {cls!r}' + self.fail(self._formatMessage(msg, standardMsg)) + + def assertIsSubclass(self, cls, superclass, msg=None): + try: + if issubclass(cls, superclass): + return + except TypeError: + if not isinstance(cls, type): + self.fail(self._formatMessage(msg, f'{cls!r} is not a class')) + raise + if isinstance(superclass, tuple): + standardMsg = f'{cls!r} is not a subclass of any of {superclass!r}' + else: + standardMsg = f'{cls!r} is not a subclass of {superclass!r}' + self.fail(self._formatMessage(msg, standardMsg)) + + def assertNotIsSubclass(self, cls, superclass, msg=None): + try: + if not issubclass(cls, superclass): + return + except TypeError: + if not isinstance(cls, type): + self.fail(self._formatMessage(msg, f'{cls!r} is not a class')) + raise + if isinstance(superclass, tuple): + for x in superclass: + if issubclass(cls, x): + superclass = x + break + standardMsg = f'{cls!r} is a subclass of {superclass!r}' + self.fail(self._formatMessage(msg, standardMsg)) + + def assertHasAttr(self, obj, name, msg=None): + if not hasattr(obj, name): + if isinstance(obj, types.ModuleType): + standardMsg = f'module {obj.__name__!r} has no attribute {name!r}' + else: + standardMsg = f'{type(obj).__name__} instance has no attribute {name!r}' + self.fail(self._formatMessage(msg, standardMsg)) + + def assertNotHasAttr(self, obj, name, msg=None): + if hasattr(obj, name): + if isinstance(obj, types.ModuleType): + standardMsg = f'module {obj.__name__!r} has unexpected attribute {name!r}' + else: + standardMsg = f'{type(obj).__name__} instance has unexpected attribute {name!r}' self.fail(self._formatMessage(msg, standardMsg)) def assertRaisesRegex(self, expected_exception, expected_regex, @@ -1391,6 +1445,80 @@ def assertNotRegex(self, text, unexpected_regex, msg=None): msg = self._formatMessage(msg, standardMsg) raise self.failureException(msg) + def _tail_type_check(self, s, tails, msg): + if not isinstance(tails, tuple): + tails = (tails,) + for tail in tails: + if isinstance(tail, str): + if not isinstance(s, str): + self.fail(self._formatMessage(msg, + f'Expected str, not {type(s).__name__}')) + elif isinstance(tail, (bytes, bytearray)): + if not isinstance(s, (bytes, bytearray)): + self.fail(self._formatMessage(msg, + f'Expected bytes, not {type(s).__name__}')) + + def assertStartsWith(self, s, prefix, msg=None): + try: + if s.startswith(prefix): + return + except (AttributeError, TypeError): + self._tail_type_check(s, prefix, msg) + raise + a = safe_repr(s, short=True) + b = safe_repr(prefix) + if isinstance(prefix, tuple): + standardMsg = f"{a} doesn't start with any of {b}" + else: + standardMsg = f"{a} doesn't start with {b}" + self.fail(self._formatMessage(msg, standardMsg)) + + def assertNotStartsWith(self, s, prefix, msg=None): + try: + if not s.startswith(prefix): + return + except (AttributeError, TypeError): + self._tail_type_check(s, prefix, msg) + raise + if isinstance(prefix, tuple): + for x in prefix: + if s.startswith(x): + prefix = x + break + a = safe_repr(s, short=True) + b = safe_repr(prefix) + self.fail(self._formatMessage(msg, f"{a} starts with {b}")) + + def assertEndsWith(self, s, suffix, msg=None): + try: + if s.endswith(suffix): + return + except (AttributeError, TypeError): + self._tail_type_check(s, suffix, msg) + raise + a = safe_repr(s, short=True) + b = safe_repr(suffix) + if isinstance(suffix, tuple): + standardMsg = f"{a} doesn't end with any of {b}" + else: + standardMsg = f"{a} doesn't end with {b}" + self.fail(self._formatMessage(msg, standardMsg)) + + def assertNotEndsWith(self, s, suffix, msg=None): + try: + if not s.endswith(suffix): + return + except (AttributeError, TypeError): + self._tail_type_check(s, suffix, msg) + raise + if isinstance(suffix, tuple): + for x in suffix: + if s.endswith(x): + suffix = x + break + a = safe_repr(s, short=True) + b = safe_repr(suffix) + self.fail(self._formatMessage(msg, f"{a} ends with {b}")) class FunctionTestCase(TestCase): diff --git a/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst b/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst new file mode 100644 index 00000000000000..5f33a30bd5eae1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst @@ -0,0 +1,9 @@ +Add new assertion methods for :mod:`unittest`: +:meth:`~unittest.TestCase.assertHasAttr`, +:meth:`~unittest.TestCase.assertNotHasAttr`, +:meth:`~unittest.TestCase.assertIsSubclass`, +:meth:`~unittest.TestCase.assertNotIsSubclass` +:meth:`~unittest.TestCase.assertStartsWith`, +:meth:`~unittest.TestCase.assertNotStartsWith`, +:meth:`~unittest.TestCase.assertEndsWith` and +:meth:`~unittest.TestCase.assertNotEndsWith`. From 99327d124145d22cde8ff324754026c87f01a1f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 14 Jan 2025 09:40:59 +0100 Subject: [PATCH 296/376] Skip CI expensive checks on `CODEOWNERS` update (#128754) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- .github/workflows/reusable-change-detection.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/reusable-change-detection.yml b/.github/workflows/reusable-change-detection.yml index 964bd87e815f42..c08c0cb8873f12 100644 --- a/.github/workflows/reusable-change-detection.yml +++ b/.github/workflows/reusable-change-detection.yml @@ -83,7 +83,22 @@ jobs: # into the PR branch anyway. # # https://github.com/python/core-workflow/issues/373 - git diff --name-only "origin/$GITHUB_BASE_REF.." | grep -qvE '(\.rst$|^Doc|^Misc|^\.pre-commit-config\.yaml$|\.ruff\.toml$|\.md$|mypy\.ini$)' && echo "run-tests=true" >> "$GITHUB_OUTPUT" || true + grep_ignore_args=( + # file extensions + -e '\.md$' + -e '\.rst$' + # top-level folders + -e '^Doc/' + -e '^Misc/' + # configuration files + -e '^\.github/CODEOWNERS$' + -e '^\.pre-commit-config\.yaml$' + -e '\.ruff\.toml$' + -e 'mypy\.ini$' + ) + git diff --name-only "origin/$GITHUB_BASE_REF.." \ + | grep -qvE "${grep_ignore_args[@]}" \ + && echo "run-tests=true" >> "$GITHUB_OUTPUT" || true fi # Check if we should run hypothesis tests From 98301326e4de507ae507b7c8eed76b964e4a2afc Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 14 Jan 2025 10:53:03 +0200 Subject: [PATCH 297/376] gh-123299: Copyedit "What's New in Python 3.14" (#128814) --- Doc/deprecations/pending-removal-in-3.16.rst | 10 +++++----- Doc/whatsnew/3.14.rst | 21 ++++++++++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/Doc/deprecations/pending-removal-in-3.16.rst b/Doc/deprecations/pending-removal-in-3.16.rst index d093deb648baf7..41b30defdba0bc 100644 --- a/Doc/deprecations/pending-removal-in-3.16.rst +++ b/Doc/deprecations/pending-removal-in-3.16.rst @@ -57,6 +57,11 @@ Pending removal in Python 3.16 In the rare case that you need the bitwise inversion of the underlying integer, convert to ``int`` explicitly (``~int(x)``). +* :mod:`functools`: + + * Calling the Python implementation of :func:`functools.reduce` with *function* + or *sequence* as keyword arguments has been deprecated since Python 3.14. + * :mod:`shutil`: * The :class:`!ExecError` exception @@ -79,8 +84,3 @@ Pending removal in Python 3.16 * The undocumented and unused :attr:`!TarFile.tarfile` attribute has been deprecated since Python 3.13. - -* :mod:`functools`: - - * Calling the Python implementation of :func:`functools.reduce` with *function* - or *sequence* as keyword arguments has been deprecated since Python 3.14. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index eedcc621c3c688..1152c7a90b7712 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -67,6 +67,7 @@ Summary -- release highlights * :ref:`PEP 649: deferred evaluation of annotations ` * :ref:`PEP 741: Python Configuration C API ` +* :ref:`PEP 761: Discontinuation of PGP signatures ` New features @@ -583,13 +584,6 @@ pdb command when :mod:`pdb` is in ``inline`` mode. (Contributed by Tian Gao in :gh:`123757`.) -platform --------- - -* Add :func:`platform.invalidate_caches` to invalidate the cached results. - - (Contributed by Bénédikt Tran in :gh:`122549`.) - pickle ------ @@ -601,6 +595,15 @@ pickle of the error. (Contributed by Serhiy Storchaka in :gh:`122213`.) + +platform +-------- + +* Add :func:`platform.invalidate_caches` to invalidate the cached results. + + (Contributed by Bénédikt Tran in :gh:`122549`.) + + pydoc ----- @@ -1123,9 +1126,11 @@ Changes in the Python API Build changes ============= -* GNU Autoconf 2.72 is now required to generate :file:`!configure`. +* GNU Autoconf 2.72 is now required to generate :file:`configure`. (Contributed by Erlend Aasland in :gh:`115765`.) +.. _whatsnew314-pep761: + PEP 761: Discontinuation of PGP signatures ------------------------------------------ From eefd4a0bc764c0272c560f26dd10fb8fba0fb7d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 14 Jan 2025 10:16:43 +0100 Subject: [PATCH 298/376] Update cryptographic primitives code owners. (#128747) --- .github/CODEOWNERS | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9e190d43b28ef9..1abcd455eca11b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -96,13 +96,14 @@ Doc/library/site.rst @FFY00 Lib/test/test_except*.py @iritkatriel Objects/exceptions.c @iritkatriel -# Hashing -**/*hashlib* @gpshead @tiran +# Hashing & cryptographic primitives +**/*hashlib* @gpshead @tiran @picnixz **/*pyhash* @gpshead @tiran -**/sha* @gpshead @tiran -Modules/md5* @gpshead @tiran -**/*blake* @gpshead @tiran +**/sha* @gpshead @tiran @picnixz +Modules/md5* @gpshead @tiran @picnixz +**/*blake* @gpshead @tiran @picnixz Modules/_hacl/** @gpshead +**/*hmac* @gpshead @picnixz # logging **/*logging* @vsajip From 24a8d920c1ec251a389fca6dedfb860d9f4bbc76 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jan 2025 11:17:20 +0100 Subject: [PATCH 299/376] gh-127787: Move _PyUnicodeError_GetParams() to the internal C API (#128803) --- Include/cpython/pyerrors.h | 6 ------ Include/internal/pycore_pyerrors.h | 9 +++++++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Include/cpython/pyerrors.h b/Include/cpython/pyerrors.h index 49a6265e5eb02f..b36b4681f5dddb 100644 --- a/Include/cpython/pyerrors.h +++ b/Include/cpython/pyerrors.h @@ -94,12 +94,6 @@ PyAPI_FUNC(void) _PyErr_ChainExceptions1(PyObject *); /* In exceptions.c */ -PyAPI_FUNC(int) _PyUnicodeError_GetParams( - PyObject *self, - PyObject **obj, Py_ssize_t *objlen, - Py_ssize_t *start, Py_ssize_t *end, - int as_bytes); - PyAPI_FUNC(PyObject*) PyUnstable_Exc_PrepReraiseStar( PyObject *orig, PyObject *excs); diff --git a/Include/internal/pycore_pyerrors.h b/Include/internal/pycore_pyerrors.h index 6f2fdda9a9f12f..8dea2d34117430 100644 --- a/Include/internal/pycore_pyerrors.h +++ b/Include/internal/pycore_pyerrors.h @@ -190,6 +190,15 @@ Py_DEPRECATED(3.12) extern void _PyErr_ChainExceptions(PyObject *, PyObject *, P PyAPI_DATA(PyTypeObject) _PyExc_IncompleteInputError; #define PyExc_IncompleteInputError ((PyObject *)(&_PyExc_IncompleteInputError)) +extern int _PyUnicodeError_GetParams( + PyObject *self, + PyObject **obj, + Py_ssize_t *objlen, + Py_ssize_t *start, + Py_ssize_t *end, + int as_bytes); + + #ifdef __cplusplus } #endif From 1153e66e20124b8f3484bcaddbc0e252d31161a6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jan 2025 11:18:52 +0100 Subject: [PATCH 300/376] gh-109959: Skip test_glob.test_selflink() flaky test (#128812) --- Lib/test/test_glob.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py index 00187a3fb3537d..1a836e34e8712f 100644 --- a/Lib/test/test_glob.py +++ b/Lib/test/test_glob.py @@ -511,6 +511,10 @@ def fn(pat): @skip_unless_symlink class SymlinkLoopGlobTests(unittest.TestCase): + # gh-109959: On Linux, glob._isdir() and glob._lexists() can return False + # randomly when checking the "link/" symbolic link. + # https://github.com/python/cpython/issues/109959#issuecomment-2577550700 + @unittest.skip("flaky test") def test_selflink(self): tempdir = TESTFN + "_dir" os.makedirs(tempdir) From ff3e145b2770ffe86c905b1092747ce3d8381319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 14 Jan 2025 12:26:26 +0100 Subject: [PATCH 301/376] gh-118761: Improve import time of the `pickle` module. (#128732) Importing `pickle` is now roughly 25% faster. Importing the `re` module is no longer needed and thus `re` is no more implicitly exposed as `pickle.re`. --------- Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Lib/pickle.py | 3 +-- .../Library/2025-01-10-13-34-33.gh-issue-118761.qRB8nS.rst | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-10-13-34-33.gh-issue-118761.qRB8nS.rst diff --git a/Lib/pickle.py b/Lib/pickle.py index 1920973e3f83e9..8afb4aa4285f37 100644 --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -31,7 +31,6 @@ import sys from sys import maxsize from struct import pack, unpack -import re import io import codecs import _compat_pickle @@ -188,7 +187,7 @@ def __init__(self, value): NEXT_BUFFER = b'\x97' # push next out-of-band buffer READONLY_BUFFER = b'\x98' # make top of stack readonly -__all__.extend([x for x in dir() if re.match("[A-Z][A-Z0-9_]+$", x)]) +__all__.extend(x for x in dir() if x.isupper() and not x.startswith('_')) class _Framer: diff --git a/Misc/NEWS.d/next/Library/2025-01-10-13-34-33.gh-issue-118761.qRB8nS.rst b/Misc/NEWS.d/next/Library/2025-01-10-13-34-33.gh-issue-118761.qRB8nS.rst new file mode 100644 index 00000000000000..a0a0f891ca55d9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-10-13-34-33.gh-issue-118761.qRB8nS.rst @@ -0,0 +1,3 @@ +Improve import time of :mod:`pickle` by 25% by removing an unnecessary +regular expression. As such, :mod:`re` is no more implicitly available +as ``pickle.re``. Patch by Bénédikt Tran. From f26daa9470925120f6336ca508f7ea193b00a3f4 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:41:39 +0200 Subject: [PATCH 302/376] Python 3.14.0a4 --- Doc/c-api/sys.rst | 4 +- Doc/library/asyncio-eventloop.rst | 2 +- Doc/library/asyncio-policy.rst | 12 +- Doc/library/calendar.rst | 8 +- Doc/library/ctypes.rst | 2 +- Doc/library/faulthandler.rst | 2 +- Doc/library/os.rst | 4 +- Doc/library/pyexpat.rst | 2 +- Doc/library/re.rst | 2 +- Doc/library/socket.rst | 2 +- Doc/library/ssl.rst | 2 +- Doc/library/turtle.rst | 2 +- Doc/library/unittest.rst | 8 +- Doc/using/configure.rst | 2 +- Include/patchlevel.h | 4 +- Lib/pydoc_data/topics.py | 33 +- Misc/NEWS.d/3.14.0a4.rst | 830 ++++++++++++++++++ ...-12-16-16-16-35.gh-issue-127951.lpE13-.rst | 1 - ...-12-20-09-03-22.gh-issue-128104.m_SoVx.rst | 3 - ...-12-21-09-56-37.gh-issue-100384.Ib-XrN.rst | 2 - ...-12-28-21-05-19.gh-issue-128321.0UvbXw.rst | 3 - ...4-12-31-17-09-37.gh-issue-90905.PjLNai.rst | 1 - ...-01-02-11-02-45.gh-issue-123925.TLlyUi.rst | 2 - ...-01-02-12-50-46.gh-issue-115765.jko7Fg.rst | 2 - ...-01-04-22-39-10.gh-issue-128472.Wt5E6M.rst | 2 - ...-01-09-19-44-00.gh-issue-128627.mHzsEd.rst | 3 - ...-12-11-13-01-26.gh-issue-127350.uEBZZ4.rst | 5 - ...-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst | 2 - ...-01-08-13-13-18.gh-issue-128629.gSmzyl.rst | 2 - ...-01-12-12-19-51.gh-issue-128400.OwoIDw.rst | 2 - ...-12-02-18-15-37.gh-issue-126862.fdIK7T.rst | 2 - ...-12-11-14-32-22.gh-issue-127809.0W8khe.rst | 2 - ...-12-13-14-17-24.gh-issue-127903.vemHSl.rst | 2 - ...-12-13-15-21-45.gh-issue-127773.E-DZR4.rst | 1 - ...-12-15-19-51-54.gh-issue-127970.vdUp-y.rst | 6 - ...4-12-15-21-11-26.gh-issue-66409.wv109z.rst | 3 - ...-12-17-13-45-33.gh-issue-127274.deNxNC.rst | 3 - ...-12-17-18-20-37.gh-issue-128035.JwqHdB.rst | 1 - ...-12-17-22-28-15.gh-issue-128030.H1ptOD.rst | 1 - ...-12-18-14-22-48.gh-issue-128079.SUD5le.rst | 5 - ...-12-20-12-25-16.gh-issue-127705.WmCz1z.rst | 4 - ...-12-20-23-07-33.gh-issue-114203.84NgoW.rst | 1 - ...-12-22-15-47-44.gh-issue-126868.RpjKez.rst | 1 - ...-12-23-11-14-07.gh-issue-128192.02mEhD.rst | 2 - ...-12-24-01-40-12.gh-issue-128137.gsTwr_.rst | 2 - ...-01-07-19-48-56.gh-issue-126703.0ISs-7.rst | 1 - ...-01-09-11-46-57.gh-issue-124483.KRtBeQ.rst | 3 - ...-01-11-12-39-17.gh-issue-128717.i65d06.rst | 2 - ...-01-13-12-48-30.gh-issue-128078.qOsl9B.rst | 2 - ...2-07-28-12-32-59.gh-issue-95371.F24IFC.rst | 1 - ...-11-12-21-53-40.gh-issue-112015.2WPRxE.rst | 5 - ...-07-13-13-25-31.gh-issue-121676.KDLS11.rst | 3 - ...-08-28-16-10-37.gh-issue-123424.u96_i6.rst | 1 - ...-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst | 1 - ...-10-04-09-56-45.gh-issue-124761.N4pSD6.rst | 1 - ...-10-31-14-31-36.gh-issue-126225.vTxGXm.rst | 6 - ...-11-09-15-59-51.gh-issue-126624.bN53Va.rst | 2 - ...-11-11-07-56-03.gh-issue-126639.AmVSt-.rst | 1 - ...-11-19-10-46-57.gh-issue-124130.OZ_vR5.rst | 4 - ...-11-24-14-53-35.gh-issue-127196.8CBkUa.rst | 2 - ...-11-28-14-24-12.gh-issue-127360.HVKt-c.rst | 4 - ...-12-02-19-13-19.gh-issue-127529.Pj1Xtf.rst | 4 - ...4-12-03-14-45-16.gh-issue-98188.GX9i2b.rst | 3 - ...-12-03-20-28-08.gh-issue-127586.zgotYF.rst | 3 - ...4-12-04-10-39-29.gh-issue-83662.CG1s3m.rst | 5 - ...-12-06-21-03-11.gh-issue-127688.NJqtc-.rst | 2 - ...-12-12-07-27-51.gh-issue-127847.ksfNKM.rst | 1 - ...-12-13-14-21-04.gh-issue-122548.hq3Vud.rst | 4 - ...-12-17-12-41-07.gh-issue-126742.l07qvT.rst | 3 - ...-12-17-13-21-52.gh-issue-127060.mv2bX6.rst | 2 - ...4-12-17-15-23-40.gh-issue-41872.31LjKY.rst | 3 - ...-12-18-00-07-50.gh-issue-128014.F3aUbz.rst | 2 - ...-12-18-10-18-55.gh-issue-128062.E9oU7-.rst | 2 - ...-12-19-20-46-01.gh-issue-127946.4lM3Op.rst | 2 - ...-12-20-10-57-10.gh-issue-128118.mYak8i.rst | 2 - ...-12-21-11-12-50.gh-issue-128151.aq7vpG.rst | 2 - ...-12-27-16-28-57.gh-issue-128302.2GMvyl.rst | 3 - ...-12-29-00-33-34.gh-issue-128317.WgFina.rst | 2 - ...-12-29-13-49-46.gh-issue-128302.psRpPN.rst | 2 - ...4-12-30-20-48-28.gh-issue-88834.RIvgwc.rst | 3 - ...-01-01-19-24-43.gh-issue-128388.8UdMz_.rst | 1 - ...-01-02-13-05-16.gh-issue-128400.5N43fF.rst | 2 - ...-01-02-15-20-17.gh-issue-128400.UMiG4f.rst | 2 - ...-01-04-11-32-46.gh-issue-128182.SJ2Zsa.rst | 2 - ...-01-05-11-46-14.gh-issue-128340.gKI0uU.rst | 1 - ...-01-06-18-41-08.gh-issue-128552.fV-f8j.rst | 1 - ...-01-06-21-35-00.gh-issue-128559.6fxcDM.rst | 1 - ...-01-08-03-09-29.gh-issue-128562.Mlv-yO.rst | 1 - ...-01-09-12-06-52.gh-issue-128661.ixx_0z.rst | 2 - ...-01-10-13-34-33.gh-issue-118761.qRB8nS.rst | 3 - ...5-01-10-15-06-45.gh-issue-71339.EKnpzw.rst | 9 - ...-01-11-13-40-12.gh-issue-128731.qpKlai.rst | 1 - ...-01-13-01-29-08.gh-issue-128690.cPFVDb.rst | 2 - ...-01-03-23-51-07.gh-issue-128152.IhzElS.rst | 2 - ...-12-22-08-54-30.gh-issue-127592.iyuFCC.rst | 2 - README.rst | 2 +- 96 files changed, 880 insertions(+), 230 deletions(-) create mode 100644 Misc/NEWS.d/3.14.0a4.rst delete mode 100644 Misc/NEWS.d/next/Build/2024-12-16-16-16-35.gh-issue-127951.lpE13-.rst delete mode 100644 Misc/NEWS.d/next/Build/2024-12-20-09-03-22.gh-issue-128104.m_SoVx.rst delete mode 100644 Misc/NEWS.d/next/Build/2024-12-21-09-56-37.gh-issue-100384.Ib-XrN.rst delete mode 100644 Misc/NEWS.d/next/Build/2024-12-28-21-05-19.gh-issue-128321.0UvbXw.rst delete mode 100644 Misc/NEWS.d/next/Build/2024-12-31-17-09-37.gh-issue-90905.PjLNai.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-01-02-11-02-45.gh-issue-123925.TLlyUi.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-01-02-12-50-46.gh-issue-115765.jko7Fg.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-01-04-22-39-10.gh-issue-128472.Wt5E6M.rst delete mode 100644 Misc/NEWS.d/next/Build/2025-01-09-19-44-00.gh-issue-128627.mHzsEd.rst delete mode 100644 Misc/NEWS.d/next/C_API/2024-12-11-13-01-26.gh-issue-127350.uEBZZ4.rst delete mode 100644 Misc/NEWS.d/next/C_API/2024-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-01-08-13-13-18.gh-issue-128629.gSmzyl.rst delete mode 100644 Misc/NEWS.d/next/C_API/2025-01-12-12-19-51.gh-issue-128400.OwoIDw.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-02-18-15-37.gh-issue-126862.fdIK7T.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-11-14-32-22.gh-issue-127809.0W8khe.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-14-17-24.gh-issue-127903.vemHSl.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-15-21-45.gh-issue-127773.E-DZR4.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-19-51-54.gh-issue-127970.vdUp-y.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-21-11-26.gh-issue-66409.wv109z.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-13-45-33.gh-issue-127274.deNxNC.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-18-20-37.gh-issue-128035.JwqHdB.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-22-28-15.gh-issue-128030.H1ptOD.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-18-14-22-48.gh-issue-128079.SUD5le.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-12-25-16.gh-issue-127705.WmCz1z.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-23-07-33.gh-issue-114203.84NgoW.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-22-15-47-44.gh-issue-126868.RpjKez.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-23-11-14-07.gh-issue-128192.02mEhD.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-24-01-40-12.gh-issue-128137.gsTwr_.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-07-19-48-56.gh-issue-126703.0ISs-7.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-09-11-46-57.gh-issue-124483.KRtBeQ.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-11-12-39-17.gh-issue-128717.i65d06.rst delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-13-12-48-30.gh-issue-128078.qOsl9B.rst delete mode 100644 Misc/NEWS.d/next/Library/2022-07-28-12-32-59.gh-issue-95371.F24IFC.rst delete mode 100644 Misc/NEWS.d/next/Library/2023-11-12-21-53-40.gh-issue-112015.2WPRxE.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-07-13-13-25-31.gh-issue-121676.KDLS11.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-08-28-16-10-37.gh-issue-123424.u96_i6.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-10-04-09-56-45.gh-issue-124761.N4pSD6.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-10-31-14-31-36.gh-issue-126225.vTxGXm.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-09-15-59-51.gh-issue-126624.bN53Va.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-11-07-56-03.gh-issue-126639.AmVSt-.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-19-10-46-57.gh-issue-124130.OZ_vR5.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-24-14-53-35.gh-issue-127196.8CBkUa.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-11-28-14-24-12.gh-issue-127360.HVKt-c.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-02-19-13-19.gh-issue-127529.Pj1Xtf.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-03-14-45-16.gh-issue-98188.GX9i2b.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-04-10-39-29.gh-issue-83662.CG1s3m.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-06-21-03-11.gh-issue-127688.NJqtc-.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-12-07-27-51.gh-issue-127847.ksfNKM.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-13-14-21-04.gh-issue-122548.hq3Vud.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-17-12-41-07.gh-issue-126742.l07qvT.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-17-13-21-52.gh-issue-127060.mv2bX6.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-17-15-23-40.gh-issue-41872.31LjKY.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-18-00-07-50.gh-issue-128014.F3aUbz.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-18-10-18-55.gh-issue-128062.E9oU7-.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-19-20-46-01.gh-issue-127946.4lM3Op.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-20-10-57-10.gh-issue-128118.mYak8i.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-21-11-12-50.gh-issue-128151.aq7vpG.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-27-16-28-57.gh-issue-128302.2GMvyl.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-29-13-49-46.gh-issue-128302.psRpPN.rst delete mode 100644 Misc/NEWS.d/next/Library/2024-12-30-20-48-28.gh-issue-88834.RIvgwc.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-01-01-19-24-43.gh-issue-128388.8UdMz_.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-01-02-13-05-16.gh-issue-128400.5N43fF.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-01-02-15-20-17.gh-issue-128400.UMiG4f.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-01-04-11-32-46.gh-issue-128182.SJ2Zsa.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-01-05-11-46-14.gh-issue-128340.gKI0uU.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-01-06-18-41-08.gh-issue-128552.fV-f8j.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-01-06-21-35-00.gh-issue-128559.6fxcDM.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-01-08-03-09-29.gh-issue-128562.Mlv-yO.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-01-09-12-06-52.gh-issue-128661.ixx_0z.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-01-10-13-34-33.gh-issue-118761.qRB8nS.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst delete mode 100644 Misc/NEWS.d/next/Library/2025-01-11-13-40-12.gh-issue-128731.qpKlai.rst delete mode 100644 Misc/NEWS.d/next/Tests/2025-01-13-01-29-08.gh-issue-128690.cPFVDb.rst delete mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-01-03-23-51-07.gh-issue-128152.IhzElS.rst delete mode 100644 Misc/NEWS.d/next/macOS/2024-12-22-08-54-30.gh-issue-127592.iyuFCC.rst diff --git a/Doc/c-api/sys.rst b/Doc/c-api/sys.rst index 7a7d39aea20baf..4ab5df4ccccdbb 100644 --- a/Doc/c-api/sys.rst +++ b/Doc/c-api/sys.rst @@ -234,7 +234,7 @@ Operating System Utilities The caller must hold the GIL. - .. versionadded:: next + .. versionadded:: 3.14 .. c:function:: int Py_fclose(FILE *file) @@ -246,7 +246,7 @@ Operating System Utilities In either case, any further access (including another call to :c:func:`Py_fclose`) to the stream results in undefined behavior. - .. versionadded:: next + .. versionadded:: 3.14 .. _systemfunctions: diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 072ab206f25e4f..3bf38a2212c0e0 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -73,7 +73,7 @@ an event loop: Set *loop* as the current event loop for the current OS thread. - .. deprecated:: next + .. deprecated:: 3.14 The :func:`set_event_loop` function is deprecated and will be removed in Python 3.16. diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst index 9f86234ce941d1..57f964912dd6ea 100644 --- a/Doc/library/asyncio-policy.rst +++ b/Doc/library/asyncio-policy.rst @@ -48,7 +48,7 @@ for the current process: Return the current process-wide policy. - .. deprecated:: next + .. deprecated:: 3.14 The :func:`get_event_loop_policy` function is deprecated and will be removed in Python 3.16. @@ -58,7 +58,7 @@ for the current process: If *policy* is set to ``None``, the default policy is restored. - .. deprecated:: next + .. deprecated:: 3.14 The :func:`set_event_loop_policy` function is deprecated and will be removed in Python 3.16. @@ -95,7 +95,7 @@ The abstract event loop policy base class is defined as follows: This method should never return ``None``. - .. deprecated:: next + .. deprecated:: 3.14 The :class:`AbstractEventLoopPolicy` class is deprecated and will be removed in Python 3.16. @@ -121,7 +121,7 @@ asyncio ships with the following built-in policies: The :meth:`get_event_loop` method of the default asyncio policy now raises a :exc:`RuntimeError` if there is no set event loop. - .. deprecated:: next + .. deprecated:: 3.14 The :class:`DefaultEventLoopPolicy` class is deprecated and will be removed in Python 3.16. @@ -133,7 +133,7 @@ asyncio ships with the following built-in policies: .. availability:: Windows. - .. deprecated:: next + .. deprecated:: 3.14 The :class:`WindowsSelectorEventLoopPolicy` class is deprecated and will be removed in Python 3.16. @@ -145,7 +145,7 @@ asyncio ships with the following built-in policies: .. availability:: Windows. - .. deprecated:: next + .. deprecated:: 3.14 The :class:`WindowsProactorEventLoopPolicy` class is deprecated and will be removed in Python 3.16. diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index f25930eb0d873e..1c6b5e03af3560 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -173,7 +173,7 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is on the first weekday as specified in the constructor or set by the :meth:`setfirstweekday` method. - .. versionchanged:: next + .. versionchanged:: 3.14 If *highlight_day* is given, this date is highlighted in color. This can be :ref:`controlled using environment variables `. @@ -201,7 +201,7 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is on the first weekday as specified in the constructor or set by the :meth:`setfirstweekday` method. - .. versionchanged:: next + .. versionchanged:: 3.14 If *highlight_day* is given, this date is highlighted in color. This can be :ref:`controlled using environment variables `. @@ -229,7 +229,7 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is :meth:`setfirstweekday` method. The earliest year for which a calendar can be generated is platform-dependent. - .. versionchanged:: next + .. versionchanged:: 3.14 If *highlight_day* is given, this date is highlighted in color. This can be :ref:`controlled using environment variables `. @@ -727,7 +727,7 @@ The following options are accepted: The number of months printed per row. Defaults to 3. -.. versionchanged:: next +.. versionchanged:: 3.14 By default, today's date is highlighted in color and can be :ref:`controlled using environment variables `. diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 077a27919fc873..164d706e7738e2 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -2233,7 +2233,7 @@ Utility functions .. audit-event:: ctypes.memoryview_at address,size,readonly - .. versionadded:: next + .. versionadded:: 3.14 .. _ctypes-data-types: diff --git a/Doc/library/faulthandler.rst b/Doc/library/faulthandler.rst index b81da4af3cff58..b7df9f6b9bcf96 100644 --- a/Doc/library/faulthandler.rst +++ b/Doc/library/faulthandler.rst @@ -91,7 +91,7 @@ Fault handler state The dump now mentions if a garbage collector collection is running if *all_threads* is true. - .. versionchanged:: next + .. versionchanged:: 3.14 Only the current thread is dumped if the :term:`GIL` is disabled to prevent the risk of data races. diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 69e6192038ab2b..6fec27bc6f68a2 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -5424,7 +5424,7 @@ operating system. Scheduling policy for tasks with deadline constraints. - .. versionadded:: next + .. versionadded:: 3.14 .. data:: SCHED_IDLE @@ -5434,7 +5434,7 @@ operating system. Alias for :data:`SCHED_OTHER`. - .. versionadded:: next + .. versionadded:: 3.14 .. data:: SCHED_SPORADIC diff --git a/Doc/library/pyexpat.rst b/Doc/library/pyexpat.rst index 0f3b58ef6ea5af..2d57cff10a9278 100644 --- a/Doc/library/pyexpat.rst +++ b/Doc/library/pyexpat.rst @@ -945,7 +945,7 @@ The ``errors`` module has the following attributes: The parser was tried to be stopped or suspended before it started. - .. versionadded:: next + .. versionadded:: 3.14 .. rubric:: Footnotes diff --git a/Doc/library/re.rst b/Doc/library/re.rst index 29387a429b844c..e2a78dc95d4ae1 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -572,7 +572,7 @@ character ``'$'``. Word boundaries are determined by the current locale if the :py:const:`~re.LOCALE` flag is used. - .. versionchanged:: next + .. versionchanged:: 3.14 ``\B`` now matches empty input string. .. index:: single: \d; in regular expressions diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 8ba2bd1dcce8cc..b36acad29ecb00 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -678,7 +678,7 @@ Constants Constant to enable duplicate address and port bindings with load balancing. - .. versionadded:: next + .. versionadded:: 3.14 .. availability:: FreeBSD >= 12.0 diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 9d7b6aa66cd443..37ea32dc8a56e5 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -938,7 +938,7 @@ Constants Whether the OpenSSL library has built-in support for TLS-PHA. - .. versionadded:: next + .. versionadded:: 3.14 .. data:: CHANNEL_BINDING_TYPES diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst index 512647f5f6e01f..61e2d125445b5a 100644 --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -2235,7 +2235,7 @@ Settings and special methods Add a turtle shape to TurtleScreen's shapelist. Only thusly registered shapes can be used by issuing the command ``shape(shapename)``. - .. versionchanged:: next + .. versionchanged:: 3.14 Added support for PNG, PGM, and PPM image formats. Both a shape name and an image file name can be specified. diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 0eead59a337ef9..5bb7f88d585e3b 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -974,7 +974,7 @@ Test cases class or a tuple of classes, as supported by :func:`issubclass`). To check for the exact type, use :func:`assertIs(cls, superclass) `. - .. versionadded:: next + .. versionadded:: 3.14 It is also possible to check the production of exceptions, warnings, and @@ -1319,7 +1319,7 @@ Test cases with a *prefix*. *prefix* can also be a tuple of strings to try. - .. versionadded:: next + .. versionadded:: 3.14 .. method:: assertEndsWith(s, suffix, msg=None) @@ -1329,7 +1329,7 @@ Test cases with a *suffix*. *suffix* can also be a tuple of strings to try. - .. versionadded:: next + .. versionadded:: 3.14 .. method:: assertHasAttr(obj, name, msg=None) @@ -1337,7 +1337,7 @@ Test cases Test that the object *obj* has (or has not) an attribute *name*. - .. versionadded:: next + .. versionadded:: 3.14 .. _type-specific-methods: diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 82df8dfc948ed2..b58532acc4b322 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -58,7 +58,7 @@ Features and minimum versions required to build CPython: .. versionchanged:: 3.13 Autoconf 2.71, aclocal 1.16.5 and SQLite 3.15.2 are now required. -.. versionchanged:: next +.. versionchanged:: 3.14 Autoconf 2.72 is now required. See also :pep:`7` "Style Guide for C Code" and :pep:`11` "CPython platform diff --git a/Include/patchlevel.h b/Include/patchlevel.h index eca2ca08a4337c..55af2c1e39cf40 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -21,10 +21,10 @@ #define PY_MINOR_VERSION 14 #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.14.0a3+" +#define PY_VERSION "3.14.0a4" /*--end constants--*/ diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index aebcef2b81d43d..97fed45d5a998c 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 17 11:49:52 2024 +# Autogenerated by Sphinx on Tue Jan 14 13:41:56 2025 # as part of the release process. topics = {'assert': 'The "assert" statement\n' '**********************\n' @@ -2822,15 +2822,18 @@ ' enter = type(manager).__enter__\n' ' exit = type(manager).__exit__\n' ' value = enter(manager)\n' + ' hit_except = False\n' '\n' ' try:\n' ' TARGET = value\n' ' SUITE\n' ' except:\n' + ' hit_except = True\n' ' if not exit(manager, *sys.exc_info()):\n' ' raise\n' - ' else:\n' - ' exit(manager, None, None, None)\n' + ' finally:\n' + ' if not hit_except:\n' + ' exit(manager, None, None, None)\n' '\n' 'With more than one item, the context managers are processed as ' 'if\n' @@ -5286,15 +5289,16 @@ '\n' ' Added in version 3.14: The *commands* argument.\n' '\n' - 'pdb.post_mortem(traceback=None)\n' + 'pdb.post_mortem(t=None)\n' '\n' - ' Enter post-mortem debugging of the given *traceback* object. ' - 'If no\n' - ' *traceback* is given, it uses the one of the exception that ' - 'is\n' - ' currently being handled (an exception must be being handled ' - 'if the\n' - ' default is to be used).\n' + ' Enter post-mortem debugging of the given exception or ' + 'traceback\n' + ' object. If no value is given, it uses the exception that is\n' + ' currently being handled, or raises "ValueError" if there ' + 'isn’t one.\n' + '\n' + ' Changed in version 3.13: Support for exception objects was ' + 'added.\n' '\n' 'pdb.pm()\n' '\n' @@ -17403,15 +17407,18 @@ ' enter = type(manager).__enter__\n' ' exit = type(manager).__exit__\n' ' value = enter(manager)\n' + ' hit_except = False\n' '\n' ' try:\n' ' TARGET = value\n' ' SUITE\n' ' except:\n' + ' hit_except = True\n' ' if not exit(manager, *sys.exc_info()):\n' ' raise\n' - ' else:\n' - ' exit(manager, None, None, None)\n' + ' finally:\n' + ' if not hit_except:\n' + ' exit(manager, None, None, None)\n' '\n' 'With more than one item, the context managers are processed as if\n' 'multiple "with" statements were nested:\n' diff --git a/Misc/NEWS.d/3.14.0a4.rst b/Misc/NEWS.d/3.14.0a4.rst new file mode 100644 index 00000000000000..1e08b36020386c --- /dev/null +++ b/Misc/NEWS.d/3.14.0a4.rst @@ -0,0 +1,830 @@ +.. date: 2024-12-22-08-54-30 +.. gh-issue: 127592 +.. nonce: iyuFCC +.. release date: 2025-01-14 +.. section: macOS + +Usage of the unified Apple System Log APIs was disabled when the minimum +macOS version is earlier than 10.12. + +.. + +.. date: 2025-01-03-23-51-07 +.. gh-issue: 128152 +.. nonce: IhzElS +.. section: Tools/Demos + +Fix a bug where Argument Clinic's C pre-processor parser tried to parse +pre-processor directives inside C comments. Patch by Erlend Aasland. + +.. + +.. date: 2025-01-13-01-29-08 +.. gh-issue: 128690 +.. nonce: cPFVDb +.. section: Tests + +Temporarily do not use test_embed in PGO profile builds until the problem +with test_init_pyvenv_cfg failing in some configurations is resolved. + +.. + +.. date: 2025-01-11-13-40-12 +.. gh-issue: 128731 +.. nonce: qpKlai +.. section: Library + +Fix :exc:`ResourceWarning` in +:meth:`urllib.robotparser.RobotFileParser.read`. + +.. + +.. date: 2025-01-10-15-06-45 +.. gh-issue: 71339 +.. nonce: EKnpzw +.. section: Library + +Add new assertion methods for :mod:`unittest`: +:meth:`~unittest.TestCase.assertHasAttr`, +:meth:`~unittest.TestCase.assertNotHasAttr`, +:meth:`~unittest.TestCase.assertIsSubclass`, +:meth:`~unittest.TestCase.assertNotIsSubclass` +:meth:`~unittest.TestCase.assertStartsWith`, +:meth:`~unittest.TestCase.assertNotStartsWith`, +:meth:`~unittest.TestCase.assertEndsWith` and +:meth:`~unittest.TestCase.assertNotEndsWith`. + +.. + +.. date: 2025-01-10-13-34-33 +.. gh-issue: 118761 +.. nonce: qRB8nS +.. section: Library + +Improve import time of :mod:`pickle` by 25% by removing an unnecessary +regular expression. As such, :mod:`re` is no more implicitly available as +``pickle.re``. Patch by Bénédikt Tran. + +.. + +.. date: 2025-01-09-12-06-52 +.. gh-issue: 128661 +.. nonce: ixx_0z +.. section: Library + +Fixes :func:`typing.evaluate_forward_ref` not showing deprecation when +``type_params`` arg is not passed. + +.. + +.. date: 2025-01-08-03-09-29 +.. gh-issue: 128562 +.. nonce: Mlv-yO +.. section: Library + +Fix possible conflicts in generated :mod:`tkinter` widget names if the +widget class name ends with a digit. + +.. + +.. date: 2025-01-06-21-35-00 +.. gh-issue: 128559 +.. nonce: 6fxcDM +.. section: Library + +Improved import time of :mod:`asyncio`. + +.. + +.. date: 2025-01-06-18-41-08 +.. gh-issue: 128552 +.. nonce: fV-f8j +.. section: Library + +Fix cyclic garbage introduced by :meth:`asyncio.loop.create_task` and +:meth:`asyncio.TaskGroup.create_task` holding a reference to the created +task if it is eager. + +.. + +.. date: 2025-01-05-11-46-14 +.. gh-issue: 128340 +.. nonce: gKI0uU +.. section: Library + +Add internal thread safe handle to be used in +:meth:`asyncio.loop.call_soon_threadsafe` for thread safe cancellation. + +.. + +.. date: 2025-01-04-11-32-46 +.. gh-issue: 128182 +.. nonce: SJ2Zsa +.. section: Library + +Fix crash when using :mod:`ctypes` pointers concurrently on the :term:`free +threaded ` build. + +.. + +.. date: 2025-01-02-15-20-17 +.. gh-issue: 128400 +.. nonce: UMiG4f +.. section: Library + +Only show the current thread in :mod:`faulthandler` on the :term:`free +threaded ` build to prevent races. + +.. + +.. date: 2025-01-02-13-05-16 +.. gh-issue: 128400 +.. nonce: 5N43fF +.. section: Library + +Fix crash when using :func:`faulthandler.dump_traceback` while other threads +are active on the :term:`free threaded ` build. + +.. + +.. date: 2025-01-01-19-24-43 +.. gh-issue: 128388 +.. nonce: 8UdMz_ +.. section: Library + +Fix ``PyREPL`` on Windows to support more keybindings, like the +:kbd:`Control-←` and :kbd:`Control-→` word-skipping keybindings and those +with meta (i.e. :kbd:`Alt`), e.g. :kbd:`Alt-d` to ``kill-word`` or +:kbd:`Alt-Backspace` ``backward-kill-word``. + +.. + +.. date: 2024-12-30-20-48-28 +.. gh-issue: 88834 +.. nonce: RIvgwc +.. section: Library + +Unify the instance check for :class:`typing.Union` and +:class:`types.UnionType`: :class:`!Union` now uses the instance checks +against its parameters instead of the subclass checks. + +.. + +.. date: 2024-12-29-13-49-46 +.. gh-issue: 128302 +.. nonce: psRpPN +.. section: Library + +Fix :meth:`!xml.dom.xmlbuilder.DOMEntityResolver.resolveEntity`, which was +broken by the Python 3.0 transition. + +.. + +.. date: 2024-12-29-00-33-34 +.. gh-issue: 128317 +.. nonce: WgFina +.. section: Library + +Highlight today in colour in :mod:`calendar`'s CLI output. Patch by Hugo van +Kemenade. + +.. + +.. date: 2024-12-27-16-28-57 +.. gh-issue: 128302 +.. nonce: 2GMvyl +.. section: Library + +Allow :meth:`!xml.dom.xmlbuilder.DOMParser.parse` to correctly handle +:class:`!xml.dom.xmlbuilder.DOMInputSource` instances that only have a +:attr:`!systemId` attribute set. + +.. + +.. date: 2024-12-21-11-12-50 +.. gh-issue: 128151 +.. nonce: aq7vpG +.. section: Library + +Improve generation of :class:`~uuid.UUID` objects version 3, 4, 5, and 8 via +their dedicated functions by 30%. Patch by Bénédikt Tran. + +.. + +.. date: 2024-12-20-10-57-10 +.. gh-issue: 128118 +.. nonce: mYak8i +.. section: Library + +Improve performance of :func:`copy.copy` by 30% via a fast path for atomic +types and container types. + +.. + +.. date: 2024-12-19-20-46-01 +.. gh-issue: 127946 +.. nonce: 4lM3Op +.. section: Library + +Fix crash when modifying :class:`ctypes._CFuncPtr` objects concurrently on +the :term:`free threaded ` build. + +.. + +.. date: 2024-12-18-10-18-55 +.. gh-issue: 128062 +.. nonce: E9oU7- +.. section: Library + +Revert the font of :mod:`turtledemo`'s menu bar to its default value and +display the shortcut keys in the correct position. + +.. + +.. date: 2024-12-18-00-07-50 +.. gh-issue: 128014 +.. nonce: F3aUbz +.. section: Library + +Fix resetting the default window icon by passing ``default=''`` to the +:mod:`tkinter` method :meth:`!wm_iconbitmap`. + +.. + +.. date: 2024-12-17-15-23-40 +.. gh-issue: 41872 +.. nonce: 31LjKY +.. section: Library + +Fix quick extraction of module docstrings from a file in :mod:`pydoc`. It +now supports docstrings with single quotes, escape sequences, raw string +literals, and other Python syntax. + +.. + +.. date: 2024-12-17-13-21-52 +.. gh-issue: 127060 +.. nonce: mv2bX6 +.. section: Library + +Set TERM environment variable to "dumb" to disable traceback colors in IDLE, +since IDLE doesn't understand ANSI escape sequences. Patch by Victor +Stinner. + +.. + +.. date: 2024-12-17-12-41-07 +.. gh-issue: 126742 +.. nonce: l07qvT +.. section: Library + +Fix support of localized error messages reported by :manpage:`dlerror(3)` +and :manpage:`gdbm_strerror ` in :mod:`ctypes` and :mod:`dbm.gnu` +functions respectively. Patch by Bénédikt Tran. + +.. + +.. date: 2024-12-13-14-21-04 +.. gh-issue: 122548 +.. nonce: hq3Vud +.. section: Library + +Adds two new local events to sys.monitoring, ``BRANCH_LEFT`` and +``BRANCH_RIGHT``. This allows the two arms of the branch to be disabled +independently, which should hugely improve performance of branch-level +coverage tools. The old branch event, ``BRANCH`` is now deprecated. + +.. + +.. date: 2024-12-12-07-27-51 +.. gh-issue: 127847 +.. nonce: ksfNKM +.. section: Library + +Fix the position when doing interleaved seeks and reads in uncompressed, +unencrypted zip files returned by :meth:`zipfile.ZipFile.open`. + +.. + +.. date: 2024-12-06-21-03-11 +.. gh-issue: 127688 +.. nonce: NJqtc- +.. section: Library + +Add the :data:`~os.SCHED_DEADLINE` and :data:`~os.SCHED_NORMAL` constants to +the :mod:`os` module. + +.. + +.. date: 2024-12-04-10-39-29 +.. gh-issue: 83662 +.. nonce: CG1s3m +.. section: Library + +Add missing ``__class_getitem__`` method to the Python implementation of +:func:`functools.partial`, to make it compatible with the C version. This is +mainly relevant for alternative Python implementations like PyPy and +GraalPy, because CPython will usually use the C-implementation of that +function. + +.. + +.. date: 2024-12-03-20-28-08 +.. gh-issue: 127586 +.. nonce: zgotYF +.. section: Library + +:class:`multiprocessing.pool.Pool` now properly restores blocked signal +handlers of the parent thread when creating processes via either *spawn* or +*forkserver*. + +.. + +.. date: 2024-12-03-14-45-16 +.. gh-issue: 98188 +.. nonce: GX9i2b +.. section: Library + +Fix an issue in :meth:`email.message.Message.get_payload` where data cannot +be decoded if the Content Transfer Encoding mechanism contains trailing +whitespaces or additional junk text. Patch by Hui Liu. + +.. + +.. date: 2024-12-02-19-13-19 +.. gh-issue: 127529 +.. nonce: Pj1Xtf +.. section: Library + +Correct behavior of +:func:`!asyncio.selector_events.BaseSelectorEventLoop._accept_connection` in +handling :exc:`ConnectionAbortedError` in a loop. This improves performance +on OpenBSD. + +.. + +.. date: 2024-11-28-14-24-12 +.. gh-issue: 127360 +.. nonce: HVKt-c +.. section: Library + +When a descriptive error message cannot be provided for an +:exc:`ssl.SSLError`, the "unknown error" message now shows the internal +error code (as retrieved by ``ERR_get_error`` and similar OpenSSL +functions). + +.. + +.. date: 2024-11-24-14-53-35 +.. gh-issue: 127196 +.. nonce: 8CBkUa +.. section: Library + +Fix crash when dict with keys in invalid encoding were passed to several +functions in ``_interpreters`` module. + +.. + +.. date: 2024-11-19-10-46-57 +.. gh-issue: 124130 +.. nonce: OZ_vR5 +.. section: Library + +Fix a bug in matching regular expression ``\B`` in empty input string. Now +it is always the opposite of ``\b``. To get an old behavior, use +``(?!\A\Z)\B``. To get a new behavior in old Python versions, use +``(?!\b)``. + +.. + +.. date: 2024-11-11-07-56-03 +.. gh-issue: 126639 +.. nonce: AmVSt- +.. section: Library + +:class:`tempfile.NamedTemporaryFile` will now issue a :exc:`ResourceWarning` +when it is finalized by the garbage collector without being explicitly +closed. + +.. + +.. date: 2024-11-09-15-59-51 +.. gh-issue: 126624 +.. nonce: bN53Va +.. section: Library + +Expose error code :data:`~xml.parsers.expat.errors.XML_ERROR_NOT_STARTED` of +Expat >=2.6.4 in :mod:`xml.parsers.expat.errors`. + +.. + +.. date: 2024-10-31-14-31-36 +.. gh-issue: 126225 +.. nonce: vTxGXm +.. section: Library + +:mod:`getopt` and :mod:`optparse` are no longer marked as deprecated. There +are legitimate reasons to use one of these modules in preference to +:mod:`argparse`, and none of these modules are at risk of being removed from +the standard library. Of the three, ``argparse`` remains the recommended +default choice, *unless* one of the concerns noted at the top of the +``optparse`` module documentation applies. + +.. + +.. date: 2024-10-04-09-56-45 +.. gh-issue: 124761 +.. nonce: N4pSD6 +.. section: Library + +Add :data:`~socket.SO_REUSEPORT_LB` constant to :mod:`socket` for FreeBSD. + +.. + +.. date: 2024-09-04-14-13-14 +.. gh-issue: 121720 +.. nonce: z9hhXQ +.. section: Library + +:class:`enum.EnumDict` can now be used without resorting to private API. + +.. + +.. date: 2024-08-28-16-10-37 +.. gh-issue: 123424 +.. nonce: u96_i6 +.. section: Library + +Add :meth:`zipfile.ZipInfo._for_archive` setting default properties on +:class:`~zipfile.ZipInfo` objects. Patch by Bénédikt Tran and Jason R. +Coombs. + +.. + +.. date: 2024-07-13-13-25-31 +.. gh-issue: 121676 +.. nonce: KDLS11 +.. section: Library + +Deprecate calling the Python implementation of :meth:`functools.reduce` with +a ``function`` or ``sequence`` as a :term:`keyword argument`. This will be +forbidden in Python 3.16 in order to match the C implementation. + +.. + +.. date: 2023-11-12-21-53-40 +.. gh-issue: 112015 +.. nonce: 2WPRxE +.. section: Library + +:func:`ctypes.memoryview_at` now exists to create a :class:`memoryview` +object that refers to the supplied pointer and length. This works like +:func:`ctypes.string_at` except it avoids a buffer copy, and is typically +useful when implementing pure Python callback functions that are passed +dynamically-sized buffers. + +.. + +.. date: 2022-07-28-12-32-59 +.. gh-issue: 95371 +.. nonce: F24IFC +.. section: Library + +Added support for other image formats (PNG, PGM, and PPM) to the turtle +module. Patch by Shin-myoung-serp. + +.. + +.. date: 2025-01-13-12-48-30 +.. gh-issue: 128078 +.. nonce: qOsl9B +.. section: Core and Builtins + +Fix a :exc:`SystemError` when using :func:`anext` with a default tuple +value. Patch by Bénédikt Tran. + +.. + +.. date: 2025-01-11-12-39-17 +.. gh-issue: 128717 +.. nonce: i65d06 +.. section: Core and Builtins + +Fix a crash when setting the recursion limit while other threads are active +on the :term:`free threaded ` build. + +.. + +.. date: 2025-01-09-11-46-57 +.. gh-issue: 124483 +.. nonce: KRtBeQ +.. section: Core and Builtins + +Treat ``Py_DECREF`` and variants as escaping when generating opcode and uop +metadata. This prevents the possibility of a ``__del__`` method causing the +JIT to behave incorrectly. + +.. + +.. date: 2025-01-07-19-48-56 +.. gh-issue: 126703 +.. nonce: 0ISs-7 +.. section: Core and Builtins + +Improve performance of class methods by using a freelist. + +.. + +.. date: 2024-12-24-01-40-12 +.. gh-issue: 128137 +.. nonce: gsTwr_ +.. section: Core and Builtins + +Update :c:type:`PyASCIIObject` layout to handle interned field with the +atomic operation. Patch by Donghee Na. + +.. + +.. date: 2024-12-23-11-14-07 +.. gh-issue: 128192 +.. nonce: 02mEhD +.. section: Core and Builtins + +Upgrade HTTP digest authentication algorithm for :mod:`urllib.request` by +supporting SHA-256 digest authentication as specified in :rfc:`7616`. + +.. + +.. date: 2024-12-22-15-47-44 +.. gh-issue: 126868 +.. nonce: RpjKez +.. section: Core and Builtins + +Increase usage of freelist for :class:`int` allocation. + +.. + +.. date: 2024-12-20-23-07-33 +.. gh-issue: 114203 +.. nonce: 84NgoW +.. section: Core and Builtins + +Optimize ``Py_BEGIN_CRITICAL_SECTION`` for simple recursive calls. + +.. + +.. date: 2024-12-20-12-25-16 +.. gh-issue: 127705 +.. nonce: WmCz1z +.. section: Core and Builtins + +Adds stackref debugging when ``Py_STACKREF_DEBUG`` is set. Finds all +double-closes and leaks, logging the origin and last borrow. + +Inspired by HPy's debug mode. +https://docs.hpyproject.org/en/latest/debug-mode.html + +.. + +.. date: 2024-12-18-14-22-48 +.. gh-issue: 128079 +.. nonce: SUD5le +.. section: Core and Builtins + +Fix a bug where :keyword:`except* ` does not properly check the +return value of an :exc:`ExceptionGroup`'s :meth:`~BaseExceptionGroup.split` +function, leading to a crash in some cases. Now when +:meth:`~BaseExceptionGroup.split` returns an invalid object, +:keyword:`except* ` raises a :exc:`TypeError` with the original +raised :exc:`ExceptionGroup` object chained to it. + +.. + +.. date: 2024-12-17-22-28-15 +.. gh-issue: 128030 +.. nonce: H1ptOD +.. section: Core and Builtins + +Avoid error from calling ``PyModule_GetFilenameObject`` on a non-module +object when importing a non-existent symbol from a non-module object. + +.. + +.. date: 2024-12-17-18-20-37 +.. gh-issue: 128035 +.. nonce: JwqHdB +.. section: Core and Builtins + +Indicate through :data:`ssl.HAS_PHA` whether the :mod:`ssl` module supports +TLSv1.3 post-handshake client authentication (PHA). Patch by Will +Childs-Klein. + +.. + +.. date: 2024-12-17-13-45-33 +.. gh-issue: 127274 +.. nonce: deNxNC +.. section: Core and Builtins + +Add a new flag, ``CO_METHOD``, to :attr:`~codeobject.co_flags` that +indicates whether the code object belongs to a function defined in class +scope. + +.. + +.. date: 2024-12-15-21-11-26 +.. gh-issue: 66409 +.. nonce: wv109z +.. section: Core and Builtins + +During the :ref:`path initialization `, we now check if +``base_exec_prefix`` is the same as ``base_prefix`` before falling back to +searching the Python interpreter directory. + +.. + +.. date: 2024-12-15-19-51-54 +.. gh-issue: 127970 +.. nonce: vdUp-y +.. section: Core and Builtins + +We now use the location of the ``libpython`` runtime library used in the +current proccess to determine :data:`sys.base_prefix` on all platforms +implementing the `dladdr +`_ +function defined by the UNIX standard — this includes Linux, Android, macOS, +iOS, FreeBSD, etc. This was already the case on Windows and macOS Framework +builds. + +.. + +.. date: 2024-12-13-15-21-45 +.. gh-issue: 127773 +.. nonce: E-DZR4 +.. section: Core and Builtins + +Do not use the type attribute cache for types with incompatible :term:`MRO`. + +.. + +.. date: 2024-12-13-14-17-24 +.. gh-issue: 127903 +.. nonce: vemHSl +.. section: Core and Builtins + +``Objects/unicodeobject.c``: fix a crash on DEBUG builds in +``_copy_characters`` when there is nothing to copy. + +.. + +.. date: 2024-12-11-14-32-22 +.. gh-issue: 127809 +.. nonce: 0W8khe +.. section: Core and Builtins + +Fix an issue where the experimental JIT may infer an incorrect result type +for exponentiation (``**`` and ``**=``), leading to bugs or crashes. + +.. + +.. date: 2024-12-02-18-15-37 +.. gh-issue: 126862 +.. nonce: fdIK7T +.. section: Core and Builtins + +Fix a possible overflow when a class inherits from an absurd number of +super-classes. Reported by Valery Fedorenko. Patch by Bénédikt Tran. + +.. + +.. date: 2025-01-12-12-19-51 +.. gh-issue: 128400 +.. nonce: OwoIDw +.. section: C API + +:c:func:`Py_FatalError` no longer shows all threads on the :term:`free +threaded ` build to prevent crashes. + +.. + +.. date: 2025-01-08-13-13-18 +.. gh-issue: 128629 +.. nonce: gSmzyl +.. section: C API + +Add macros :c:func:`Py_PACK_VERSION` and :c:func:`Py_PACK_FULL_VERSION` for +bit-packing Python version numbers. + +.. + +.. date: 2024-12-16-21-59-06 +.. gh-issue: 128008 +.. nonce: fa9Jt0 +.. section: C API + +Add :c:func:`PyWeakref_IsDead` function, which tests if a weak reference is +dead. + +.. + +.. date: 2024-12-11-13-01-26 +.. gh-issue: 127350 +.. nonce: uEBZZ4 +.. section: C API + +Add :c:func:`Py_fopen` function to open a file. Similar to the +:c:func:`!fopen` function, but the *path* parameter is a Python object and +an exception is set on error. Add also :c:func:`Py_fclose` function to close +a file, function needed for Windows support. Patch by Victor Stinner. + +.. + +.. date: 2025-01-09-19-44-00 +.. gh-issue: 128627 +.. nonce: mHzsEd +.. section: Build + +For Emscripten builds the function pointer cast call trampoline now uses the +wasm-gc ref.test instruction if it's available instead of Wasm JS type +reflection. + +.. + +.. date: 2025-01-04-22-39-10 +.. gh-issue: 128472 +.. nonce: Wt5E6M +.. section: Build + +Skip BOLT optimization of functions using computed gotos, fixing errors on +build with LLVM 19. + +.. + +.. date: 2025-01-02-12-50-46 +.. gh-issue: 115765 +.. nonce: jko7Fg +.. section: Build + +GNU Autoconf 2.72 is now required to generate :file:`!configure`. Patch by +Erlend Aasland. + +.. + +.. date: 2025-01-02-11-02-45 +.. gh-issue: 123925 +.. nonce: TLlyUi +.. section: Build + +Fix building the :mod:`curses` module on platforms with libncurses but +without libncursesw. + +.. + +.. date: 2024-12-31-17-09-37 +.. gh-issue: 90905 +.. nonce: PjLNai +.. section: Build + +Add support for cross-compiling to x86_64 on aarch64/arm64 macOS. + +.. + +.. date: 2024-12-28-21-05-19 +.. gh-issue: 128321 +.. nonce: 0UvbXw +.. section: Build + +Set ``LIBS`` instead of ``LDFLAGS`` when checking if :mod:`sqlite3` library +functions are available. This fixes the ordering of linked libraries during +checks, which was incorrect when using a statically linked ``libsqlite3``. + +.. + +.. date: 2024-12-21-09-56-37 +.. gh-issue: 100384 +.. nonce: Ib-XrN +.. section: Build + +Error on ``unguarded-availability`` in macOS builds, preventing invalid use +of symbols that are not available in older versions of the OS. + +.. + +.. date: 2024-12-20-09-03-22 +.. gh-issue: 128104 +.. nonce: m_SoVx +.. section: Build + +Remove ``Py_STRFTIME_C99_SUPPORT`` conditions in favor of requiring C99 +:manpage:`strftime(3)` specifier support at build time. When +cross-compiling, there is no build time check and support is assumed. + +.. + +.. date: 2024-12-16-16-16-35 +.. gh-issue: 127951 +.. nonce: lpE13- +.. section: Build + +Add option ``--pystats`` to the Windows build to enable performance +statistics collection. diff --git a/Misc/NEWS.d/next/Build/2024-12-16-16-16-35.gh-issue-127951.lpE13-.rst b/Misc/NEWS.d/next/Build/2024-12-16-16-16-35.gh-issue-127951.lpE13-.rst deleted file mode 100644 index 0c1df0e6bd7baa..00000000000000 --- a/Misc/NEWS.d/next/Build/2024-12-16-16-16-35.gh-issue-127951.lpE13-.rst +++ /dev/null @@ -1 +0,0 @@ -Add option ``--pystats`` to the Windows build to enable performance statistics collection. diff --git a/Misc/NEWS.d/next/Build/2024-12-20-09-03-22.gh-issue-128104.m_SoVx.rst b/Misc/NEWS.d/next/Build/2024-12-20-09-03-22.gh-issue-128104.m_SoVx.rst deleted file mode 100644 index c3a47fbecd1dad..00000000000000 --- a/Misc/NEWS.d/next/Build/2024-12-20-09-03-22.gh-issue-128104.m_SoVx.rst +++ /dev/null @@ -1,3 +0,0 @@ -Remove ``Py_STRFTIME_C99_SUPPORT`` conditions in favor of requiring C99 -:manpage:`strftime(3)` specifier support at build time. When cross-compiling, -there is no build time check and support is assumed. diff --git a/Misc/NEWS.d/next/Build/2024-12-21-09-56-37.gh-issue-100384.Ib-XrN.rst b/Misc/NEWS.d/next/Build/2024-12-21-09-56-37.gh-issue-100384.Ib-XrN.rst deleted file mode 100644 index 75c19fe3d8cef9..00000000000000 --- a/Misc/NEWS.d/next/Build/2024-12-21-09-56-37.gh-issue-100384.Ib-XrN.rst +++ /dev/null @@ -1,2 +0,0 @@ -Error on ``unguarded-availability`` in macOS builds, preventing invalid -use of symbols that are not available in older versions of the OS. diff --git a/Misc/NEWS.d/next/Build/2024-12-28-21-05-19.gh-issue-128321.0UvbXw.rst b/Misc/NEWS.d/next/Build/2024-12-28-21-05-19.gh-issue-128321.0UvbXw.rst deleted file mode 100644 index ed72cc8ab1449a..00000000000000 --- a/Misc/NEWS.d/next/Build/2024-12-28-21-05-19.gh-issue-128321.0UvbXw.rst +++ /dev/null @@ -1,3 +0,0 @@ -Set ``LIBS`` instead of ``LDFLAGS`` when checking if :mod:`sqlite3` library -functions are available. This fixes the ordering of linked libraries during -checks, which was incorrect when using a statically linked ``libsqlite3``. diff --git a/Misc/NEWS.d/next/Build/2024-12-31-17-09-37.gh-issue-90905.PjLNai.rst b/Misc/NEWS.d/next/Build/2024-12-31-17-09-37.gh-issue-90905.PjLNai.rst deleted file mode 100644 index 31bb5fe005379a..00000000000000 --- a/Misc/NEWS.d/next/Build/2024-12-31-17-09-37.gh-issue-90905.PjLNai.rst +++ /dev/null @@ -1 +0,0 @@ -Add support for cross-compiling to x86_64 on aarch64/arm64 macOS. diff --git a/Misc/NEWS.d/next/Build/2025-01-02-11-02-45.gh-issue-123925.TLlyUi.rst b/Misc/NEWS.d/next/Build/2025-01-02-11-02-45.gh-issue-123925.TLlyUi.rst deleted file mode 100644 index a2a9c6fc7680b5..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-01-02-11-02-45.gh-issue-123925.TLlyUi.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix building the :mod:`curses` module on platforms with libncurses but -without libncursesw. diff --git a/Misc/NEWS.d/next/Build/2025-01-02-12-50-46.gh-issue-115765.jko7Fg.rst b/Misc/NEWS.d/next/Build/2025-01-02-12-50-46.gh-issue-115765.jko7Fg.rst deleted file mode 100644 index 34618c2c1288bc..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-01-02-12-50-46.gh-issue-115765.jko7Fg.rst +++ /dev/null @@ -1,2 +0,0 @@ -GNU Autoconf 2.72 is now required to generate :file:`!configure`. -Patch by Erlend Aasland. diff --git a/Misc/NEWS.d/next/Build/2025-01-04-22-39-10.gh-issue-128472.Wt5E6M.rst b/Misc/NEWS.d/next/Build/2025-01-04-22-39-10.gh-issue-128472.Wt5E6M.rst deleted file mode 100644 index c6233e1f2d8693..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-01-04-22-39-10.gh-issue-128472.Wt5E6M.rst +++ /dev/null @@ -1,2 +0,0 @@ -Skip BOLT optimization of functions using computed gotos, fixing errors on -build with LLVM 19. diff --git a/Misc/NEWS.d/next/Build/2025-01-09-19-44-00.gh-issue-128627.mHzsEd.rst b/Misc/NEWS.d/next/Build/2025-01-09-19-44-00.gh-issue-128627.mHzsEd.rst deleted file mode 100644 index a8c80ab6804b02..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-01-09-19-44-00.gh-issue-128627.mHzsEd.rst +++ /dev/null @@ -1,3 +0,0 @@ -For Emscripten builds the function pointer cast call trampoline now uses the -wasm-gc ref.test instruction if it's available instead of Wasm JS type -reflection. diff --git a/Misc/NEWS.d/next/C_API/2024-12-11-13-01-26.gh-issue-127350.uEBZZ4.rst b/Misc/NEWS.d/next/C_API/2024-12-11-13-01-26.gh-issue-127350.uEBZZ4.rst deleted file mode 100644 index d1b528c673442f..00000000000000 --- a/Misc/NEWS.d/next/C_API/2024-12-11-13-01-26.gh-issue-127350.uEBZZ4.rst +++ /dev/null @@ -1,5 +0,0 @@ -Add :c:func:`Py_fopen` function to open a file. Similar to the :c:func:`!fopen` -function, but the *path* parameter is a Python object and an exception is set -on error. Add also :c:func:`Py_fclose` function to close a file, function -needed for Windows support. -Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C_API/2024-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst b/Misc/NEWS.d/next/C_API/2024-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst deleted file mode 100644 index 2349eccac8fedc..00000000000000 --- a/Misc/NEWS.d/next/C_API/2024-12-16-21-59-06.gh-issue-128008.fa9Jt0.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :c:func:`PyWeakref_IsDead` function, which tests if a weak reference is -dead. diff --git a/Misc/NEWS.d/next/C_API/2025-01-08-13-13-18.gh-issue-128629.gSmzyl.rst b/Misc/NEWS.d/next/C_API/2025-01-08-13-13-18.gh-issue-128629.gSmzyl.rst deleted file mode 100644 index cde5bf38f754b6..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-01-08-13-13-18.gh-issue-128629.gSmzyl.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add macros :c:func:`Py_PACK_VERSION` and :c:func:`Py_PACK_FULL_VERSION` for -bit-packing Python version numbers. diff --git a/Misc/NEWS.d/next/C_API/2025-01-12-12-19-51.gh-issue-128400.OwoIDw.rst b/Misc/NEWS.d/next/C_API/2025-01-12-12-19-51.gh-issue-128400.OwoIDw.rst deleted file mode 100644 index b9c117b269434c..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-01-12-12-19-51.gh-issue-128400.OwoIDw.rst +++ /dev/null @@ -1,2 +0,0 @@ -:c:func:`Py_FatalError` no longer shows all threads on the :term:`free -threaded ` build to prevent crashes. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-02-18-15-37.gh-issue-126862.fdIK7T.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-02-18-15-37.gh-issue-126862.fdIK7T.rst deleted file mode 100644 index d930c2963e3632..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-02-18-15-37.gh-issue-126862.fdIK7T.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a possible overflow when a class inherits from an absurd number of -super-classes. Reported by Valery Fedorenko. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-11-14-32-22.gh-issue-127809.0W8khe.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-11-14-32-22.gh-issue-127809.0W8khe.rst deleted file mode 100644 index 19c8cc6e99c8c5..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-11-14-32-22.gh-issue-127809.0W8khe.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix an issue where the experimental JIT may infer an incorrect result type -for exponentiation (``**`` and ``**=``), leading to bugs or crashes. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-14-17-24.gh-issue-127903.vemHSl.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-14-17-24.gh-issue-127903.vemHSl.rst deleted file mode 100644 index ad479b52d1675c..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-14-17-24.gh-issue-127903.vemHSl.rst +++ /dev/null @@ -1,2 +0,0 @@ -``Objects/unicodeobject.c``: fix a crash on DEBUG builds in ``_copy_characters`` -when there is nothing to copy. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-15-21-45.gh-issue-127773.E-DZR4.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-15-21-45.gh-issue-127773.E-DZR4.rst deleted file mode 100644 index 7e68b3fecd1c08..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-13-15-21-45.gh-issue-127773.E-DZR4.rst +++ /dev/null @@ -1 +0,0 @@ -Do not use the type attribute cache for types with incompatible :term:`MRO`. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-19-51-54.gh-issue-127970.vdUp-y.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-19-51-54.gh-issue-127970.vdUp-y.rst deleted file mode 100644 index e4dc7b5fe032d6..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-19-51-54.gh-issue-127970.vdUp-y.rst +++ /dev/null @@ -1,6 +0,0 @@ -We now use the location of the ``libpython`` runtime library used in the current -proccess to determine :data:`sys.base_prefix` on all platforms implementing the -`dladdr `_ -function defined by the UNIX standard — this includes Linux, Android, macOS, -iOS, FreeBSD, etc. This was already the case on Windows and macOS Framework -builds. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-21-11-26.gh-issue-66409.wv109z.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-21-11-26.gh-issue-66409.wv109z.rst deleted file mode 100644 index 0d70ad06c97968..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-15-21-11-26.gh-issue-66409.wv109z.rst +++ /dev/null @@ -1,3 +0,0 @@ -During the :ref:`path initialization `, we now check if -``base_exec_prefix`` is the same as ``base_prefix`` before falling back to -searching the Python interpreter directory. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-13-45-33.gh-issue-127274.deNxNC.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-13-45-33.gh-issue-127274.deNxNC.rst deleted file mode 100644 index a4608fbbbf19ec..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-13-45-33.gh-issue-127274.deNxNC.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add a new flag, ``CO_METHOD``, to :attr:`~codeobject.co_flags` that -indicates whether the code object belongs to a function defined in class -scope. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-18-20-37.gh-issue-128035.JwqHdB.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-18-20-37.gh-issue-128035.JwqHdB.rst deleted file mode 100644 index 27815d48425334..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-18-20-37.gh-issue-128035.JwqHdB.rst +++ /dev/null @@ -1 +0,0 @@ -Indicate through :data:`ssl.HAS_PHA` whether the :mod:`ssl` module supports TLSv1.3 post-handshake client authentication (PHA). Patch by Will Childs-Klein. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-22-28-15.gh-issue-128030.H1ptOD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-22-28-15.gh-issue-128030.H1ptOD.rst deleted file mode 100644 index 93d78632355b76..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-22-28-15.gh-issue-128030.H1ptOD.rst +++ /dev/null @@ -1 +0,0 @@ -Avoid error from calling ``PyModule_GetFilenameObject`` on a non-module object when importing a non-existent symbol from a non-module object. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-18-14-22-48.gh-issue-128079.SUD5le.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-18-14-22-48.gh-issue-128079.SUD5le.rst deleted file mode 100644 index 8da4e677f068a3..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-18-14-22-48.gh-issue-128079.SUD5le.rst +++ /dev/null @@ -1,5 +0,0 @@ -Fix a bug where :keyword:`except* ` does not properly check the -return value of an :exc:`ExceptionGroup`'s :meth:`~BaseExceptionGroup.split` -function, leading to a crash in some cases. Now when :meth:`~BaseExceptionGroup.split` -returns an invalid object, :keyword:`except* ` raises a :exc:`TypeError` -with the original raised :exc:`ExceptionGroup` object chained to it. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-12-25-16.gh-issue-127705.WmCz1z.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-12-25-16.gh-issue-127705.WmCz1z.rst deleted file mode 100644 index fde12b78ce0444..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-12-25-16.gh-issue-127705.WmCz1z.rst +++ /dev/null @@ -1,4 +0,0 @@ -Adds stackref debugging when ``Py_STACKREF_DEBUG`` is set. Finds all -double-closes and leaks, logging the origin and last borrow. - -Inspired by HPy's debug mode. https://docs.hpyproject.org/en/latest/debug-mode.html diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-23-07-33.gh-issue-114203.84NgoW.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-23-07-33.gh-issue-114203.84NgoW.rst deleted file mode 100644 index 6a9856e90c32bc..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-20-23-07-33.gh-issue-114203.84NgoW.rst +++ /dev/null @@ -1 +0,0 @@ -Optimize ``Py_BEGIN_CRITICAL_SECTION`` for simple recursive calls. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-22-15-47-44.gh-issue-126868.RpjKez.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-22-15-47-44.gh-issue-126868.RpjKez.rst deleted file mode 100644 index ede383deb4ad31..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-22-15-47-44.gh-issue-126868.RpjKez.rst +++ /dev/null @@ -1 +0,0 @@ -Increase usage of freelist for :class:`int` allocation. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-23-11-14-07.gh-issue-128192.02mEhD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-23-11-14-07.gh-issue-128192.02mEhD.rst deleted file mode 100644 index b80ab715ffc7db..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-23-11-14-07.gh-issue-128192.02mEhD.rst +++ /dev/null @@ -1,2 +0,0 @@ -Upgrade HTTP digest authentication algorithm for :mod:`urllib.request` by -supporting SHA-256 digest authentication as specified in :rfc:`7616`. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-24-01-40-12.gh-issue-128137.gsTwr_.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-24-01-40-12.gh-issue-128137.gsTwr_.rst deleted file mode 100644 index a3b7cde7f67676..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-24-01-40-12.gh-issue-128137.gsTwr_.rst +++ /dev/null @@ -1,2 +0,0 @@ -Update :c:type:`PyASCIIObject` layout to handle interned field with the -atomic operation. Patch by Donghee Na. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-07-19-48-56.gh-issue-126703.0ISs-7.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-07-19-48-56.gh-issue-126703.0ISs-7.rst deleted file mode 100644 index ecb8eddb13c649..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-07-19-48-56.gh-issue-126703.0ISs-7.rst +++ /dev/null @@ -1 +0,0 @@ -Improve performance of class methods by using a freelist. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-09-11-46-57.gh-issue-124483.KRtBeQ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-09-11-46-57.gh-issue-124483.KRtBeQ.rst deleted file mode 100644 index 6f2e7265d62321..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-09-11-46-57.gh-issue-124483.KRtBeQ.rst +++ /dev/null @@ -1,3 +0,0 @@ -Treat ``Py_DECREF`` and variants as escaping when generating opcode and uop -metadata. This prevents the possibility of a ``__del__`` method causing the JIT -to behave incorrectly. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-11-12-39-17.gh-issue-128717.i65d06.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-11-12-39-17.gh-issue-128717.i65d06.rst deleted file mode 100644 index 212c6d3cb97216..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-11-12-39-17.gh-issue-128717.i65d06.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a crash when setting the recursion limit while other threads are active -on the :term:`free threaded ` build. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-13-12-48-30.gh-issue-128078.qOsl9B.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-13-12-48-30.gh-issue-128078.qOsl9B.rst deleted file mode 100644 index 498864a0aa3145..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-13-12-48-30.gh-issue-128078.qOsl9B.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a :exc:`SystemError` when using :func:`anext` with a default tuple -value. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2022-07-28-12-32-59.gh-issue-95371.F24IFC.rst b/Misc/NEWS.d/next/Library/2022-07-28-12-32-59.gh-issue-95371.F24IFC.rst deleted file mode 100644 index 4a62aaed78b425..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-07-28-12-32-59.gh-issue-95371.F24IFC.rst +++ /dev/null @@ -1 +0,0 @@ -Added support for other image formats (PNG, PGM, and PPM) to the turtle module. Patch by Shin-myoung-serp. diff --git a/Misc/NEWS.d/next/Library/2023-11-12-21-53-40.gh-issue-112015.2WPRxE.rst b/Misc/NEWS.d/next/Library/2023-11-12-21-53-40.gh-issue-112015.2WPRxE.rst deleted file mode 100644 index 4b58ec9d219eff..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-11-12-21-53-40.gh-issue-112015.2WPRxE.rst +++ /dev/null @@ -1,5 +0,0 @@ -:func:`ctypes.memoryview_at` now exists to create a -:class:`memoryview` object that refers to the supplied pointer and -length. This works like :func:`ctypes.string_at` except it avoids a -buffer copy, and is typically useful when implementing pure Python -callback functions that are passed dynamically-sized buffers. diff --git a/Misc/NEWS.d/next/Library/2024-07-13-13-25-31.gh-issue-121676.KDLS11.rst b/Misc/NEWS.d/next/Library/2024-07-13-13-25-31.gh-issue-121676.KDLS11.rst deleted file mode 100644 index be589b727a1968..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-07-13-13-25-31.gh-issue-121676.KDLS11.rst +++ /dev/null @@ -1,3 +0,0 @@ -Deprecate calling the Python implementation of :meth:`functools.reduce` -with a ``function`` or ``sequence`` as a :term:`keyword argument`. -This will be forbidden in Python 3.16 in order to match the C implementation. diff --git a/Misc/NEWS.d/next/Library/2024-08-28-16-10-37.gh-issue-123424.u96_i6.rst b/Misc/NEWS.d/next/Library/2024-08-28-16-10-37.gh-issue-123424.u96_i6.rst deleted file mode 100644 index 4df4bbf2ba2b73..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-08-28-16-10-37.gh-issue-123424.u96_i6.rst +++ /dev/null @@ -1 +0,0 @@ -Add :meth:`zipfile.ZipInfo._for_archive` setting default properties on :class:`~zipfile.ZipInfo` objects. Patch by Bénédikt Tran and Jason R. Coombs. diff --git a/Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst b/Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst deleted file mode 100644 index 96da94a9f211af..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-09-04-14-13-14.gh-issue-121720.z9hhXQ.rst +++ /dev/null @@ -1 +0,0 @@ -:class:`enum.EnumDict` can now be used without resorting to private API. diff --git a/Misc/NEWS.d/next/Library/2024-10-04-09-56-45.gh-issue-124761.N4pSD6.rst b/Misc/NEWS.d/next/Library/2024-10-04-09-56-45.gh-issue-124761.N4pSD6.rst deleted file mode 100644 index 797dd31b368548..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-10-04-09-56-45.gh-issue-124761.N4pSD6.rst +++ /dev/null @@ -1 +0,0 @@ -Add :data:`~socket.SO_REUSEPORT_LB` constant to :mod:`socket` for FreeBSD. diff --git a/Misc/NEWS.d/next/Library/2024-10-31-14-31-36.gh-issue-126225.vTxGXm.rst b/Misc/NEWS.d/next/Library/2024-10-31-14-31-36.gh-issue-126225.vTxGXm.rst deleted file mode 100644 index 13a1f213c7a58e..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-10-31-14-31-36.gh-issue-126225.vTxGXm.rst +++ /dev/null @@ -1,6 +0,0 @@ -:mod:`getopt` and :mod:`optparse` are no longer marked as deprecated. -There are legitimate reasons to use one of these modules in preference to -:mod:`argparse`, and none of these modules are at risk of being removed -from the standard library. Of the three, ``argparse`` remains the -recommended default choice, *unless* one of the concerns noted at the top of -the ``optparse`` module documentation applies. diff --git a/Misc/NEWS.d/next/Library/2024-11-09-15-59-51.gh-issue-126624.bN53Va.rst b/Misc/NEWS.d/next/Library/2024-11-09-15-59-51.gh-issue-126624.bN53Va.rst deleted file mode 100644 index 468840a651c253..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-09-15-59-51.gh-issue-126624.bN53Va.rst +++ /dev/null @@ -1,2 +0,0 @@ -Expose error code :data:`~xml.parsers.expat.errors.XML_ERROR_NOT_STARTED` -of Expat >=2.6.4 in :mod:`xml.parsers.expat.errors`. diff --git a/Misc/NEWS.d/next/Library/2024-11-11-07-56-03.gh-issue-126639.AmVSt-.rst b/Misc/NEWS.d/next/Library/2024-11-11-07-56-03.gh-issue-126639.AmVSt-.rst deleted file mode 100644 index 0b75e5858de731..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-11-07-56-03.gh-issue-126639.AmVSt-.rst +++ /dev/null @@ -1 +0,0 @@ -:class:`tempfile.NamedTemporaryFile` will now issue a :exc:`ResourceWarning` when it is finalized by the garbage collector without being explicitly closed. diff --git a/Misc/NEWS.d/next/Library/2024-11-19-10-46-57.gh-issue-124130.OZ_vR5.rst b/Misc/NEWS.d/next/Library/2024-11-19-10-46-57.gh-issue-124130.OZ_vR5.rst deleted file mode 100644 index a1d4fc8ff4c22f..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-19-10-46-57.gh-issue-124130.OZ_vR5.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix a bug in matching regular expression ``\B`` in empty input string. -Now it is always the opposite of ``\b``. -To get an old behavior, use ``(?!\A\Z)\B``. -To get a new behavior in old Python versions, use ``(?!\b)``. diff --git a/Misc/NEWS.d/next/Library/2024-11-24-14-53-35.gh-issue-127196.8CBkUa.rst b/Misc/NEWS.d/next/Library/2024-11-24-14-53-35.gh-issue-127196.8CBkUa.rst deleted file mode 100644 index 471f64d185deab..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-24-14-53-35.gh-issue-127196.8CBkUa.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix crash when dict with keys in invalid encoding were passed to several -functions in ``_interpreters`` module. diff --git a/Misc/NEWS.d/next/Library/2024-11-28-14-24-12.gh-issue-127360.HVKt-c.rst b/Misc/NEWS.d/next/Library/2024-11-28-14-24-12.gh-issue-127360.HVKt-c.rst deleted file mode 100644 index 4772aef71e658f..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-28-14-24-12.gh-issue-127360.HVKt-c.rst +++ /dev/null @@ -1,4 +0,0 @@ -When a descriptive error message cannot be provided for an -:exc:`ssl.SSLError`, the "unknown error" message now shows the internal -error code (as retrieved by ``ERR_get_error`` and similar OpenSSL -functions). diff --git a/Misc/NEWS.d/next/Library/2024-12-02-19-13-19.gh-issue-127529.Pj1Xtf.rst b/Misc/NEWS.d/next/Library/2024-12-02-19-13-19.gh-issue-127529.Pj1Xtf.rst deleted file mode 100644 index 26f2fd5923ab7b..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-02-19-13-19.gh-issue-127529.Pj1Xtf.rst +++ /dev/null @@ -1,4 +0,0 @@ -Correct behavior of -:func:`!asyncio.selector_events.BaseSelectorEventLoop._accept_connection` -in handling :exc:`ConnectionAbortedError` in a loop. This improves -performance on OpenBSD. diff --git a/Misc/NEWS.d/next/Library/2024-12-03-14-45-16.gh-issue-98188.GX9i2b.rst b/Misc/NEWS.d/next/Library/2024-12-03-14-45-16.gh-issue-98188.GX9i2b.rst deleted file mode 100644 index 30ab8cfc3f0bc6..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-03-14-45-16.gh-issue-98188.GX9i2b.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix an issue in :meth:`email.message.Message.get_payload` where data -cannot be decoded if the Content Transfer Encoding mechanism contains -trailing whitespaces or additional junk text. Patch by Hui Liu. diff --git a/Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst b/Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst deleted file mode 100644 index 80217bd4a10503..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst +++ /dev/null @@ -1,3 +0,0 @@ -:class:`multiprocessing.pool.Pool` now properly restores blocked signal handlers -of the parent thread when creating processes via either *spawn* or -*forkserver*. diff --git a/Misc/NEWS.d/next/Library/2024-12-04-10-39-29.gh-issue-83662.CG1s3m.rst b/Misc/NEWS.d/next/Library/2024-12-04-10-39-29.gh-issue-83662.CG1s3m.rst deleted file mode 100644 index 5e39933047993c..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-04-10-39-29.gh-issue-83662.CG1s3m.rst +++ /dev/null @@ -1,5 +0,0 @@ -Add missing ``__class_getitem__`` method to the Python implementation of -:func:`functools.partial`, to make it compatible with the C version. This is -mainly relevant for alternative Python implementations like PyPy and -GraalPy, because CPython will usually use the C-implementation of that -function. diff --git a/Misc/NEWS.d/next/Library/2024-12-06-21-03-11.gh-issue-127688.NJqtc-.rst b/Misc/NEWS.d/next/Library/2024-12-06-21-03-11.gh-issue-127688.NJqtc-.rst deleted file mode 100644 index a22b136da72faf..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-06-21-03-11.gh-issue-127688.NJqtc-.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add the :data:`~os.SCHED_DEADLINE` and :data:`~os.SCHED_NORMAL` constants -to the :mod:`os` module. diff --git a/Misc/NEWS.d/next/Library/2024-12-12-07-27-51.gh-issue-127847.ksfNKM.rst b/Misc/NEWS.d/next/Library/2024-12-12-07-27-51.gh-issue-127847.ksfNKM.rst deleted file mode 100644 index 3d6e36fb538bca..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-12-07-27-51.gh-issue-127847.ksfNKM.rst +++ /dev/null @@ -1 +0,0 @@ -Fix the position when doing interleaved seeks and reads in uncompressed, unencrypted zip files returned by :meth:`zipfile.ZipFile.open`. diff --git a/Misc/NEWS.d/next/Library/2024-12-13-14-21-04.gh-issue-122548.hq3Vud.rst b/Misc/NEWS.d/next/Library/2024-12-13-14-21-04.gh-issue-122548.hq3Vud.rst deleted file mode 100644 index 6cd13572ff1893..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-13-14-21-04.gh-issue-122548.hq3Vud.rst +++ /dev/null @@ -1,4 +0,0 @@ -Adds two new local events to sys.monitoring, ``BRANCH_LEFT`` and -``BRANCH_RIGHT``. This allows the two arms of the branch to be disabled -independently, which should hugely improve performance of branch-level -coverage tools. The old branch event, ``BRANCH`` is now deprecated. diff --git a/Misc/NEWS.d/next/Library/2024-12-17-12-41-07.gh-issue-126742.l07qvT.rst b/Misc/NEWS.d/next/Library/2024-12-17-12-41-07.gh-issue-126742.l07qvT.rst deleted file mode 100644 index 70f7cc129f66e3..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-17-12-41-07.gh-issue-126742.l07qvT.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix support of localized error messages reported by :manpage:`dlerror(3)` and -:manpage:`gdbm_strerror ` in :mod:`ctypes` and :mod:`dbm.gnu` -functions respectively. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2024-12-17-13-21-52.gh-issue-127060.mv2bX6.rst b/Misc/NEWS.d/next/Library/2024-12-17-13-21-52.gh-issue-127060.mv2bX6.rst deleted file mode 100644 index 1da89e7a282147..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-17-13-21-52.gh-issue-127060.mv2bX6.rst +++ /dev/null @@ -1,2 +0,0 @@ -Set TERM environment variable to "dumb" to disable traceback colors in IDLE, -since IDLE doesn't understand ANSI escape sequences. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2024-12-17-15-23-40.gh-issue-41872.31LjKY.rst b/Misc/NEWS.d/next/Library/2024-12-17-15-23-40.gh-issue-41872.31LjKY.rst deleted file mode 100644 index b807dcb284c248..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-17-15-23-40.gh-issue-41872.31LjKY.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix quick extraction of module docstrings from a file in :mod:`pydoc`. -It now supports docstrings with single quotes, escape sequences, -raw string literals, and other Python syntax. diff --git a/Misc/NEWS.d/next/Library/2024-12-18-00-07-50.gh-issue-128014.F3aUbz.rst b/Misc/NEWS.d/next/Library/2024-12-18-00-07-50.gh-issue-128014.F3aUbz.rst deleted file mode 100644 index ef339a291f0ddd..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-18-00-07-50.gh-issue-128014.F3aUbz.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix resetting the default window icon by passing ``default=''`` to the -:mod:`tkinter` method :meth:`!wm_iconbitmap`. diff --git a/Misc/NEWS.d/next/Library/2024-12-18-10-18-55.gh-issue-128062.E9oU7-.rst b/Misc/NEWS.d/next/Library/2024-12-18-10-18-55.gh-issue-128062.E9oU7-.rst deleted file mode 100644 index d8e262e0848077..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-18-10-18-55.gh-issue-128062.E9oU7-.rst +++ /dev/null @@ -1,2 +0,0 @@ -Revert the font of :mod:`turtledemo`'s menu bar to its default value and -display the shortcut keys in the correct position. diff --git a/Misc/NEWS.d/next/Library/2024-12-19-20-46-01.gh-issue-127946.4lM3Op.rst b/Misc/NEWS.d/next/Library/2024-12-19-20-46-01.gh-issue-127946.4lM3Op.rst deleted file mode 100644 index faf1ec042bc2b9..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-19-20-46-01.gh-issue-127946.4lM3Op.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix crash when modifying :class:`ctypes._CFuncPtr` objects concurrently on -the :term:`free threaded ` build. diff --git a/Misc/NEWS.d/next/Library/2024-12-20-10-57-10.gh-issue-128118.mYak8i.rst b/Misc/NEWS.d/next/Library/2024-12-20-10-57-10.gh-issue-128118.mYak8i.rst deleted file mode 100644 index bc2898edfda721..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-20-10-57-10.gh-issue-128118.mYak8i.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve performance of :func:`copy.copy` by 30% via -a fast path for atomic types and container types. diff --git a/Misc/NEWS.d/next/Library/2024-12-21-11-12-50.gh-issue-128151.aq7vpG.rst b/Misc/NEWS.d/next/Library/2024-12-21-11-12-50.gh-issue-128151.aq7vpG.rst deleted file mode 100644 index 04c744fb2ba54f..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-21-11-12-50.gh-issue-128151.aq7vpG.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve generation of :class:`~uuid.UUID` objects version 3, 4, 5, and 8 -via their dedicated functions by 30%. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2024-12-27-16-28-57.gh-issue-128302.2GMvyl.rst b/Misc/NEWS.d/next/Library/2024-12-27-16-28-57.gh-issue-128302.2GMvyl.rst deleted file mode 100644 index 56e2fe6f85f4bf..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-27-16-28-57.gh-issue-128302.2GMvyl.rst +++ /dev/null @@ -1,3 +0,0 @@ -Allow :meth:`!xml.dom.xmlbuilder.DOMParser.parse` to correctly handle -:class:`!xml.dom.xmlbuilder.DOMInputSource` instances that only have a -:attr:`!systemId` attribute set. diff --git a/Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst b/Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst deleted file mode 100644 index 4441108014569e..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-29-00-33-34.gh-issue-128317.WgFina.rst +++ /dev/null @@ -1,2 +0,0 @@ -Highlight today in colour in :mod:`calendar`'s CLI output. Patch by Hugo van -Kemenade. diff --git a/Misc/NEWS.d/next/Library/2024-12-29-13-49-46.gh-issue-128302.psRpPN.rst b/Misc/NEWS.d/next/Library/2024-12-29-13-49-46.gh-issue-128302.psRpPN.rst deleted file mode 100644 index 98c07297b06f8a..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-29-13-49-46.gh-issue-128302.psRpPN.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :meth:`!xml.dom.xmlbuilder.DOMEntityResolver.resolveEntity`, which was -broken by the Python 3.0 transition. diff --git a/Misc/NEWS.d/next/Library/2024-12-30-20-48-28.gh-issue-88834.RIvgwc.rst b/Misc/NEWS.d/next/Library/2024-12-30-20-48-28.gh-issue-88834.RIvgwc.rst deleted file mode 100644 index ca43f914880ba3..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-12-30-20-48-28.gh-issue-88834.RIvgwc.rst +++ /dev/null @@ -1,3 +0,0 @@ -Unify the instance check for :class:`typing.Union` and -:class:`types.UnionType`: :class:`!Union` now uses the instance checks -against its parameters instead of the subclass checks. diff --git a/Misc/NEWS.d/next/Library/2025-01-01-19-24-43.gh-issue-128388.8UdMz_.rst b/Misc/NEWS.d/next/Library/2025-01-01-19-24-43.gh-issue-128388.8UdMz_.rst deleted file mode 100644 index 5bef0fd6bcac17..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-01-01-19-24-43.gh-issue-128388.8UdMz_.rst +++ /dev/null @@ -1 +0,0 @@ -Fix ``PyREPL`` on Windows to support more keybindings, like the :kbd:`Control-←` and :kbd:`Control-→` word-skipping keybindings and those with meta (i.e. :kbd:`Alt`), e.g. :kbd:`Alt-d` to ``kill-word`` or :kbd:`Alt-Backspace` ``backward-kill-word``. diff --git a/Misc/NEWS.d/next/Library/2025-01-02-13-05-16.gh-issue-128400.5N43fF.rst b/Misc/NEWS.d/next/Library/2025-01-02-13-05-16.gh-issue-128400.5N43fF.rst deleted file mode 100644 index 4033dea4eaf7bf..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-01-02-13-05-16.gh-issue-128400.5N43fF.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix crash when using :func:`faulthandler.dump_traceback` while other threads -are active on the :term:`free threaded ` build. diff --git a/Misc/NEWS.d/next/Library/2025-01-02-15-20-17.gh-issue-128400.UMiG4f.rst b/Misc/NEWS.d/next/Library/2025-01-02-15-20-17.gh-issue-128400.UMiG4f.rst deleted file mode 100644 index f9d5f84224c8dc..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-01-02-15-20-17.gh-issue-128400.UMiG4f.rst +++ /dev/null @@ -1,2 +0,0 @@ -Only show the current thread in :mod:`faulthandler` on the :term:`free -threaded ` build to prevent races. diff --git a/Misc/NEWS.d/next/Library/2025-01-04-11-32-46.gh-issue-128182.SJ2Zsa.rst b/Misc/NEWS.d/next/Library/2025-01-04-11-32-46.gh-issue-128182.SJ2Zsa.rst deleted file mode 100644 index 038fecb5710436..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-01-04-11-32-46.gh-issue-128182.SJ2Zsa.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix crash when using :mod:`ctypes` pointers concurrently on the :term:`free -threaded ` build. diff --git a/Misc/NEWS.d/next/Library/2025-01-05-11-46-14.gh-issue-128340.gKI0uU.rst b/Misc/NEWS.d/next/Library/2025-01-05-11-46-14.gh-issue-128340.gKI0uU.rst deleted file mode 100644 index 790400a19f334b..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-01-05-11-46-14.gh-issue-128340.gKI0uU.rst +++ /dev/null @@ -1 +0,0 @@ -Add internal thread safe handle to be used in :meth:`asyncio.loop.call_soon_threadsafe` for thread safe cancellation. diff --git a/Misc/NEWS.d/next/Library/2025-01-06-18-41-08.gh-issue-128552.fV-f8j.rst b/Misc/NEWS.d/next/Library/2025-01-06-18-41-08.gh-issue-128552.fV-f8j.rst deleted file mode 100644 index 83816f775da9c5..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-01-06-18-41-08.gh-issue-128552.fV-f8j.rst +++ /dev/null @@ -1 +0,0 @@ -Fix cyclic garbage introduced by :meth:`asyncio.loop.create_task` and :meth:`asyncio.TaskGroup.create_task` holding a reference to the created task if it is eager. diff --git a/Misc/NEWS.d/next/Library/2025-01-06-21-35-00.gh-issue-128559.6fxcDM.rst b/Misc/NEWS.d/next/Library/2025-01-06-21-35-00.gh-issue-128559.6fxcDM.rst deleted file mode 100644 index 7f9380de17761b..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-01-06-21-35-00.gh-issue-128559.6fxcDM.rst +++ /dev/null @@ -1 +0,0 @@ -Improved import time of :mod:`asyncio`. diff --git a/Misc/NEWS.d/next/Library/2025-01-08-03-09-29.gh-issue-128562.Mlv-yO.rst b/Misc/NEWS.d/next/Library/2025-01-08-03-09-29.gh-issue-128562.Mlv-yO.rst deleted file mode 100644 index eb50dded67bea8..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-01-08-03-09-29.gh-issue-128562.Mlv-yO.rst +++ /dev/null @@ -1 +0,0 @@ -Fix possible conflicts in generated :mod:`tkinter` widget names if the widget class name ends with a digit. diff --git a/Misc/NEWS.d/next/Library/2025-01-09-12-06-52.gh-issue-128661.ixx_0z.rst b/Misc/NEWS.d/next/Library/2025-01-09-12-06-52.gh-issue-128661.ixx_0z.rst deleted file mode 100644 index 6c52b3dcc0ed00..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-01-09-12-06-52.gh-issue-128661.ixx_0z.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixes :func:`typing.evaluate_forward_ref` not showing deprecation when -``type_params`` arg is not passed. diff --git a/Misc/NEWS.d/next/Library/2025-01-10-13-34-33.gh-issue-118761.qRB8nS.rst b/Misc/NEWS.d/next/Library/2025-01-10-13-34-33.gh-issue-118761.qRB8nS.rst deleted file mode 100644 index a0a0f891ca55d9..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-01-10-13-34-33.gh-issue-118761.qRB8nS.rst +++ /dev/null @@ -1,3 +0,0 @@ -Improve import time of :mod:`pickle` by 25% by removing an unnecessary -regular expression. As such, :mod:`re` is no more implicitly available -as ``pickle.re``. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst b/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst deleted file mode 100644 index 5f33a30bd5eae1..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst +++ /dev/null @@ -1,9 +0,0 @@ -Add new assertion methods for :mod:`unittest`: -:meth:`~unittest.TestCase.assertHasAttr`, -:meth:`~unittest.TestCase.assertNotHasAttr`, -:meth:`~unittest.TestCase.assertIsSubclass`, -:meth:`~unittest.TestCase.assertNotIsSubclass` -:meth:`~unittest.TestCase.assertStartsWith`, -:meth:`~unittest.TestCase.assertNotStartsWith`, -:meth:`~unittest.TestCase.assertEndsWith` and -:meth:`~unittest.TestCase.assertNotEndsWith`. diff --git a/Misc/NEWS.d/next/Library/2025-01-11-13-40-12.gh-issue-128731.qpKlai.rst b/Misc/NEWS.d/next/Library/2025-01-11-13-40-12.gh-issue-128731.qpKlai.rst deleted file mode 100644 index b23499ca2fea3a..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-01-11-13-40-12.gh-issue-128731.qpKlai.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :exc:`ResourceWarning` in :meth:`urllib.robotparser.RobotFileParser.read`. diff --git a/Misc/NEWS.d/next/Tests/2025-01-13-01-29-08.gh-issue-128690.cPFVDb.rst b/Misc/NEWS.d/next/Tests/2025-01-13-01-29-08.gh-issue-128690.cPFVDb.rst deleted file mode 100644 index fb84334ca77214..00000000000000 --- a/Misc/NEWS.d/next/Tests/2025-01-13-01-29-08.gh-issue-128690.cPFVDb.rst +++ /dev/null @@ -1,2 +0,0 @@ -Temporarily do not use test_embed in PGO profile builds until the problem -with test_init_pyvenv_cfg failing in some configurations is resolved. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-01-03-23-51-07.gh-issue-128152.IhzElS.rst b/Misc/NEWS.d/next/Tools-Demos/2025-01-03-23-51-07.gh-issue-128152.IhzElS.rst deleted file mode 100644 index 9657e138e9911b..00000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-01-03-23-51-07.gh-issue-128152.IhzElS.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug where Argument Clinic's C pre-processor parser tried to parse -pre-processor directives inside C comments. Patch by Erlend Aasland. diff --git a/Misc/NEWS.d/next/macOS/2024-12-22-08-54-30.gh-issue-127592.iyuFCC.rst b/Misc/NEWS.d/next/macOS/2024-12-22-08-54-30.gh-issue-127592.iyuFCC.rst deleted file mode 100644 index dfe659294c712e..00000000000000 --- a/Misc/NEWS.d/next/macOS/2024-12-22-08-54-30.gh-issue-127592.iyuFCC.rst +++ /dev/null @@ -1,2 +0,0 @@ -Usage of the unified Apple System Log APIs was disabled when the minimum -macOS version is earlier than 10.12. diff --git a/README.rst b/README.rst index 02776205e6dcc9..6f74bc3f9ed7ef 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.14.0 alpha 3 +This is Python version 3.14.0 alpha 4 ===================================== .. image:: https://github.com/python/cpython/actions/workflows/build.yml/badge.svg?branch=main&event=push From 75bd42c73718ca54ed70d5bf90cbc76c2c1d5ddc Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 14 Jan 2025 15:18:25 +0200 Subject: [PATCH 303/376] gh-71339: Use new assertion methods in test_sqlite3 (GH-128830) --- Lib/test/test_sqlite3/test_cli.py | 16 ++++++------ Lib/test/test_sqlite3/test_dbapi.py | 37 ++++++++++----------------- Lib/test/test_sqlite3/test_factory.py | 2 +- 3 files changed, 22 insertions(+), 33 deletions(-) diff --git a/Lib/test/test_sqlite3/test_cli.py b/Lib/test/test_sqlite3/test_cli.py index d014a9ce841607..dcd90d11d46819 100644 --- a/Lib/test/test_sqlite3/test_cli.py +++ b/Lib/test/test_sqlite3/test_cli.py @@ -90,14 +90,14 @@ def test_interact(self): out, err = self.run_cli() self.assertIn(self.MEMORY_DB_MSG, err) self.assertIn(self.MEMORY_DB_MSG, err) - self.assertTrue(out.endswith(self.PS1)) + self.assertEndsWith(out, self.PS1) self.assertEqual(out.count(self.PS1), 1) self.assertEqual(out.count(self.PS2), 0) def test_interact_quit(self): out, err = self.run_cli(commands=(".quit",)) self.assertIn(self.MEMORY_DB_MSG, err) - self.assertTrue(out.endswith(self.PS1)) + self.assertEndsWith(out, self.PS1) self.assertEqual(out.count(self.PS1), 1) self.assertEqual(out.count(self.PS2), 0) @@ -105,7 +105,7 @@ def test_interact_version(self): out, err = self.run_cli(commands=(".version",)) self.assertIn(self.MEMORY_DB_MSG, err) self.assertIn(sqlite3.sqlite_version + "\n", out) - self.assertTrue(out.endswith(self.PS1)) + self.assertEndsWith(out, self.PS1) self.assertEqual(out.count(self.PS1), 2) self.assertEqual(out.count(self.PS2), 0) self.assertIn(sqlite3.sqlite_version, out) @@ -114,14 +114,14 @@ def test_interact_valid_sql(self): out, err = self.run_cli(commands=("SELECT 1;",)) self.assertIn(self.MEMORY_DB_MSG, err) self.assertIn("(1,)\n", out) - self.assertTrue(out.endswith(self.PS1)) + self.assertEndsWith(out, self.PS1) self.assertEqual(out.count(self.PS1), 2) self.assertEqual(out.count(self.PS2), 0) def test_interact_incomplete_multiline_sql(self): out, err = self.run_cli(commands=("SELECT 1",)) self.assertIn(self.MEMORY_DB_MSG, err) - self.assertTrue(out.endswith(self.PS2)) + self.assertEndsWith(out, self.PS2) self.assertEqual(out.count(self.PS1), 1) self.assertEqual(out.count(self.PS2), 1) @@ -130,7 +130,7 @@ def test_interact_valid_multiline_sql(self): self.assertIn(self.MEMORY_DB_MSG, err) self.assertIn(self.PS2, out) self.assertIn("(1,)\n", out) - self.assertTrue(out.endswith(self.PS1)) + self.assertEndsWith(out, self.PS1) self.assertEqual(out.count(self.PS1), 2) self.assertEqual(out.count(self.PS2), 1) @@ -138,7 +138,7 @@ def test_interact_invalid_sql(self): out, err = self.run_cli(commands=("sel;",)) self.assertIn(self.MEMORY_DB_MSG, err) self.assertIn("OperationalError (SQLITE_ERROR)", err) - self.assertTrue(out.endswith(self.PS1)) + self.assertEndsWith(out, self.PS1) self.assertEqual(out.count(self.PS1), 2) self.assertEqual(out.count(self.PS2), 0) @@ -147,7 +147,7 @@ def test_interact_on_disk_file(self): out, err = self.run_cli(TESTFN, commands=("CREATE TABLE t(t);",)) self.assertIn(TESTFN, err) - self.assertTrue(out.endswith(self.PS1)) + self.assertEndsWith(out, self.PS1) out, _ = self.run_cli(TESTFN, commands=("SELECT count(t) FROM t;",)) self.assertIn("(0,)\n", out) diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index 488b401fb0054d..f5ffe2427430e2 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -59,45 +59,34 @@ def test_param_style(self): sqlite.paramstyle) def test_warning(self): - self.assertTrue(issubclass(sqlite.Warning, Exception), - "Warning is not a subclass of Exception") + self.assertIsSubclass(sqlite.Warning, Exception) def test_error(self): - self.assertTrue(issubclass(sqlite.Error, Exception), - "Error is not a subclass of Exception") + self.assertIsSubclass(sqlite.Error, Exception) def test_interface_error(self): - self.assertTrue(issubclass(sqlite.InterfaceError, sqlite.Error), - "InterfaceError is not a subclass of Error") + self.assertIsSubclass(sqlite.InterfaceError, sqlite.Error) def test_database_error(self): - self.assertTrue(issubclass(sqlite.DatabaseError, sqlite.Error), - "DatabaseError is not a subclass of Error") + self.assertIsSubclass(sqlite.DatabaseError, sqlite.Error) def test_data_error(self): - self.assertTrue(issubclass(sqlite.DataError, sqlite.DatabaseError), - "DataError is not a subclass of DatabaseError") + self.assertIsSubclass(sqlite.DataError, sqlite.DatabaseError) def test_operational_error(self): - self.assertTrue(issubclass(sqlite.OperationalError, sqlite.DatabaseError), - "OperationalError is not a subclass of DatabaseError") + self.assertIsSubclass(sqlite.OperationalError, sqlite.DatabaseError) def test_integrity_error(self): - self.assertTrue(issubclass(sqlite.IntegrityError, sqlite.DatabaseError), - "IntegrityError is not a subclass of DatabaseError") + self.assertIsSubclass(sqlite.IntegrityError, sqlite.DatabaseError) def test_internal_error(self): - self.assertTrue(issubclass(sqlite.InternalError, sqlite.DatabaseError), - "InternalError is not a subclass of DatabaseError") + self.assertIsSubclass(sqlite.InternalError, sqlite.DatabaseError) def test_programming_error(self): - self.assertTrue(issubclass(sqlite.ProgrammingError, sqlite.DatabaseError), - "ProgrammingError is not a subclass of DatabaseError") + self.assertIsSubclass(sqlite.ProgrammingError, sqlite.DatabaseError) def test_not_supported_error(self): - self.assertTrue(issubclass(sqlite.NotSupportedError, - sqlite.DatabaseError), - "NotSupportedError is not a subclass of DatabaseError") + self.assertIsSubclass(sqlite.NotSupportedError, sqlite.DatabaseError) def test_module_constants(self): consts = [ @@ -274,7 +263,7 @@ def test_module_constants(self): consts.append("SQLITE_IOERR_CORRUPTFS") for const in consts: with self.subTest(const=const): - self.assertTrue(hasattr(sqlite, const)) + self.assertHasAttr(sqlite, const) def test_error_code_on_exception(self): err_msg = "unable to open database file" @@ -288,7 +277,7 @@ def test_error_code_on_exception(self): sqlite.connect(db) e = cm.exception self.assertEqual(e.sqlite_errorcode, err_code) - self.assertTrue(e.sqlite_errorname.startswith("SQLITE_CANTOPEN")) + self.assertStartsWith(e.sqlite_errorname, "SQLITE_CANTOPEN") def test_extended_error_code_on_exception(self): with memory_database() as con: @@ -425,7 +414,7 @@ def test_connection_exceptions(self): ] for exc in exceptions: with self.subTest(exc=exc): - self.assertTrue(hasattr(self.cx, exc)) + self.assertHasAttr(self.cx, exc) self.assertIs(getattr(sqlite, exc), getattr(self.cx, exc)) def test_interrupt_on_closed_db(self): diff --git a/Lib/test/test_sqlite3/test_factory.py b/Lib/test/test_sqlite3/test_factory.py index 48d35b54a2e239..cc9f1ec5c4bec5 100644 --- a/Lib/test/test_sqlite3/test_factory.py +++ b/Lib/test/test_sqlite3/test_factory.py @@ -280,7 +280,7 @@ def test_custom(self): austria = "Österreich" row = self.con.execute("select ?", (austria,)).fetchone() self.assertEqual(type(row[0]), str, "type of row[0] must be unicode") - self.assertTrue(row[0].endswith("reich"), "column must contain original data") + self.assertEndsWith(row[0], "reich", "column must contain original data") class TextFactoryTestsWithEmbeddedZeroBytes(unittest.TestCase): From 859db49029b1021e24b5c99d4a9a0f57327d1b57 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 14 Jan 2025 15:19:32 +0200 Subject: [PATCH 304/376] gh-71339: Use new assertion methods in test_typing (GH-128825) --- Lib/test/test_typing.py | 103 ++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index c98e6f820e8cf7..f002d28df60e9c 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -115,18 +115,18 @@ def test_errors(self): def test_can_subclass(self): class Mock(Any): pass - self.assertTrue(issubclass(Mock, Any)) + self.assertIsSubclass(Mock, Any) self.assertIsInstance(Mock(), Mock) class Something: pass - self.assertFalse(issubclass(Something, Any)) + self.assertNotIsSubclass(Something, Any) self.assertNotIsInstance(Something(), Mock) class MockSomething(Something, Mock): pass - self.assertTrue(issubclass(MockSomething, Any)) - self.assertTrue(issubclass(MockSomething, MockSomething)) - self.assertTrue(issubclass(MockSomething, Something)) - self.assertTrue(issubclass(MockSomething, Mock)) + self.assertIsSubclass(MockSomething, Any) + self.assertIsSubclass(MockSomething, MockSomething) + self.assertIsSubclass(MockSomething, Something) + self.assertIsSubclass(MockSomething, Mock) ms = MockSomething() self.assertIsInstance(ms, MockSomething) self.assertIsInstance(ms, Something) @@ -1997,11 +1997,11 @@ def test_basics(self): self.assertNotEqual(u, Union) def test_union_isinstance(self): - self.assertTrue(isinstance(42, Union[int, str])) - self.assertTrue(isinstance('abc', Union[int, str])) - self.assertFalse(isinstance(3.14, Union[int, str])) - self.assertTrue(isinstance(42, Union[int, list[int]])) - self.assertTrue(isinstance(42, Union[int, Any])) + self.assertIsInstance(42, Union[int, str]) + self.assertIsInstance('abc', Union[int, str]) + self.assertNotIsInstance(3.14, Union[int, str]) + self.assertIsInstance(42, Union[int, list[int]]) + self.assertIsInstance(42, Union[int, Any]) def test_union_isinstance_type_error(self): with self.assertRaises(TypeError): @@ -2018,9 +2018,9 @@ def test_union_isinstance_type_error(self): isinstance(42, Union[Any, str]) def test_optional_isinstance(self): - self.assertTrue(isinstance(42, Optional[int])) - self.assertTrue(isinstance(None, Optional[int])) - self.assertFalse(isinstance('abc', Optional[int])) + self.assertIsInstance(42, Optional[int]) + self.assertIsInstance(None, Optional[int]) + self.assertNotIsInstance('abc', Optional[int]) def test_optional_isinstance_type_error(self): with self.assertRaises(TypeError): @@ -2033,14 +2033,14 @@ def test_optional_isinstance_type_error(self): isinstance(None, Optional[Any]) def test_union_issubclass(self): - self.assertTrue(issubclass(int, Union[int, str])) - self.assertTrue(issubclass(str, Union[int, str])) - self.assertFalse(issubclass(float, Union[int, str])) - self.assertTrue(issubclass(int, Union[int, list[int]])) - self.assertTrue(issubclass(int, Union[int, Any])) - self.assertFalse(issubclass(int, Union[str, Any])) - self.assertTrue(issubclass(int, Union[Any, int])) - self.assertFalse(issubclass(int, Union[Any, str])) + self.assertIsSubclass(int, Union[int, str]) + self.assertIsSubclass(str, Union[int, str]) + self.assertNotIsSubclass(float, Union[int, str]) + self.assertIsSubclass(int, Union[int, list[int]]) + self.assertIsSubclass(int, Union[int, Any]) + self.assertNotIsSubclass(int, Union[str, Any]) + self.assertIsSubclass(int, Union[Any, int]) + self.assertNotIsSubclass(int, Union[Any, str]) def test_union_issubclass_type_error(self): with self.assertRaises(TypeError): @@ -2057,12 +2057,12 @@ def test_union_issubclass_type_error(self): issubclass(int, Union[list[int], str]) def test_optional_issubclass(self): - self.assertTrue(issubclass(int, Optional[int])) - self.assertTrue(issubclass(type(None), Optional[int])) - self.assertFalse(issubclass(str, Optional[int])) - self.assertTrue(issubclass(Any, Optional[Any])) - self.assertTrue(issubclass(type(None), Optional[Any])) - self.assertFalse(issubclass(int, Optional[Any])) + self.assertIsSubclass(int, Optional[int]) + self.assertIsSubclass(type(None), Optional[int]) + self.assertNotIsSubclass(str, Optional[int]) + self.assertIsSubclass(Any, Optional[Any]) + self.assertIsSubclass(type(None), Optional[Any]) + self.assertNotIsSubclass(int, Optional[Any]) def test_optional_issubclass_type_error(self): with self.assertRaises(TypeError): @@ -4050,8 +4050,8 @@ def test_generic_protocols_repr(self): class P(Protocol[T, S]): pass - self.assertTrue(repr(P[T, S]).endswith('P[~T, ~S]')) - self.assertTrue(repr(P[int, str]).endswith('P[int, str]')) + self.assertEndsWith(repr(P[T, S]), 'P[~T, ~S]') + self.assertEndsWith(repr(P[int, str]), 'P[int, str]') def test_generic_protocols_eq(self): T = TypeVar('T') @@ -4641,8 +4641,7 @@ class C(Generic[T]): self.assertNotEqual(Z, Y[int]) self.assertNotEqual(Z, Y[T]) - self.assertTrue(str(Z).endswith( - '.C[typing.Tuple[str, int]]')) + self.assertEndsWith(str(Z), '.C[typing.Tuple[str, int]]') def test_new_repr(self): T = TypeVar('T') @@ -4870,12 +4869,12 @@ class A(Generic[T]): self.assertNotEqual(typing.FrozenSet[A[str]], typing.FrozenSet[mod_generics_cache.B.A[str]]) - self.assertTrue(repr(Tuple[A[str]]).endswith('.A[str]]')) - self.assertTrue(repr(Tuple[B.A[str]]).endswith('.B.A[str]]')) - self.assertTrue(repr(Tuple[mod_generics_cache.A[str]]) - .endswith('mod_generics_cache.A[str]]')) - self.assertTrue(repr(Tuple[mod_generics_cache.B.A[str]]) - .endswith('mod_generics_cache.B.A[str]]')) + self.assertEndsWith(repr(Tuple[A[str]]), '.A[str]]') + self.assertEndsWith(repr(Tuple[B.A[str]]), '.B.A[str]]') + self.assertEndsWith(repr(Tuple[mod_generics_cache.A[str]]), + 'mod_generics_cache.A[str]]') + self.assertEndsWith(repr(Tuple[mod_generics_cache.B.A[str]]), + 'mod_generics_cache.B.A[str]]') def test_extended_generic_rules_eq(self): T = TypeVar('T') @@ -5817,7 +5816,7 @@ def __call__(self, *args, **kwargs): @Wrapper def wrapped(): ... self.assertIsInstance(wrapped, Wrapper) - self.assertIs(False, hasattr(wrapped, "__final__")) + self.assertNotHasAttr(wrapped, "__final__") class Meta(type): @property @@ -5829,7 +5828,7 @@ class WithMeta(metaclass=Meta): ... # Builtin classes throw TypeError if you try to set an # attribute. final(int) - self.assertIs(False, hasattr(int, "__final__")) + self.assertNotHasAttr(int, "__final__") # Make sure it works with common builtin decorators class Methods: @@ -5910,19 +5909,19 @@ def static_method_bad_order(): self.assertEqual(Derived.class_method_good_order(), 42) self.assertIs(True, Derived.class_method_good_order.__override__) self.assertEqual(Derived.class_method_bad_order(), 42) - self.assertIs(False, hasattr(Derived.class_method_bad_order, "__override__")) + self.assertNotHasAttr(Derived.class_method_bad_order, "__override__") self.assertEqual(Derived.static_method_good_order(), 42) self.assertIs(True, Derived.static_method_good_order.__override__) self.assertEqual(Derived.static_method_bad_order(), 42) - self.assertIs(False, hasattr(Derived.static_method_bad_order, "__override__")) + self.assertNotHasAttr(Derived.static_method_bad_order, "__override__") # Base object is not changed: - self.assertIs(False, hasattr(Base.normal_method, "__override__")) - self.assertIs(False, hasattr(Base.class_method_good_order, "__override__")) - self.assertIs(False, hasattr(Base.class_method_bad_order, "__override__")) - self.assertIs(False, hasattr(Base.static_method_good_order, "__override__")) - self.assertIs(False, hasattr(Base.static_method_bad_order, "__override__")) + self.assertNotHasAttr(Base.normal_method, "__override__") + self.assertNotHasAttr(Base.class_method_good_order, "__override__") + self.assertNotHasAttr(Base.class_method_bad_order, "__override__") + self.assertNotHasAttr(Base.static_method_good_order, "__override__") + self.assertNotHasAttr(Base.static_method_bad_order, "__override__") def test_property(self): class Base: @@ -5947,8 +5946,8 @@ def wrong(self) -> int: self.assertEqual(instance.correct, 2) self.assertTrue(Child.correct.fget.__override__) self.assertEqual(instance.wrong, 2) - self.assertFalse(hasattr(Child.wrong, "__override__")) - self.assertFalse(hasattr(Child.wrong.fset, "__override__")) + self.assertNotHasAttr(Child.wrong, "__override__") + self.assertNotHasAttr(Child.wrong.fset, "__override__") def test_silent_failure(self): class CustomProp: @@ -5965,7 +5964,7 @@ def some(self): return 1 self.assertEqual(WithOverride.some, 1) - self.assertFalse(hasattr(WithOverride.some, "__override__")) + self.assertNotHasAttr(WithOverride.some, "__override__") def test_multiple_decorators(self): def with_wraps(f): # similar to `lru_cache` definition @@ -10474,7 +10473,7 @@ def test_special_attrs2(self): # to the variable name to which it is assigned". Thus, providing # __qualname__ is unnecessary. self.assertEqual(SpecialAttrsT.__name__, 'SpecialAttrsT') - self.assertFalse(hasattr(SpecialAttrsT, '__qualname__')) + self.assertNotHasAttr(SpecialAttrsT, '__qualname__') self.assertEqual(SpecialAttrsT.__module__, __name__) # Module-level type variables are picklable. for proto in range(pickle.HIGHEST_PROTOCOL + 1): @@ -10483,7 +10482,7 @@ def test_special_attrs2(self): self.assertIs(SpecialAttrsT, loaded) self.assertEqual(SpecialAttrsP.__name__, 'SpecialAttrsP') - self.assertFalse(hasattr(SpecialAttrsP, '__qualname__')) + self.assertNotHasAttr(SpecialAttrsP, '__qualname__') self.assertEqual(SpecialAttrsP.__module__, __name__) # Module-level ParamSpecs are picklable. for proto in range(pickle.HIGHEST_PROTOCOL + 1): From bbd3300ae82a71da483dbb0c345175ba090263c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 14 Jan 2025 14:25:33 +0100 Subject: [PATCH 305/376] gh-118761: substitute `re` import in `base64.b16decode` for a more efficient alternative (#128736) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Chris Markiewicz Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/whatsnew/3.14.rst | 9 +++++++++ Lib/base64.py | 3 +-- .../2025-01-10-13-06-54.gh-issue-118761.f8oADD.rst | 5 +++++ 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-10-13-06-54.gh-issue-118761.f8oADD.rst diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 1152c7a90b7712..ac203b208f4a8a 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -728,6 +728,15 @@ asyncio reduces memory usage. (Contributed by Kumar Aditya in :gh:`107803`.) + +base64 +------ + +* Improve the performance of :func:`base64.b16decode` by up to ten times, + and reduce the import time of :mod:`base64` by up to six times. + (Contributed by Bénédikt Tran, Chris Markiewicz, and Adam Turner in :gh:`118761`.) + + io --- * :mod:`io` which provides the built-in :func:`open` makes less system calls diff --git a/Lib/base64.py b/Lib/base64.py index 61be4fb856e92c..5d78cc09f40cd3 100644 --- a/Lib/base64.py +++ b/Lib/base64.py @@ -4,7 +4,6 @@ # Modified 30-Dec-2003 by Barry Warsaw to add full RFC 3548 support # Modified 22-May-2007 by Guido van Rossum to use bytes everywhere -import re import struct import binascii @@ -284,7 +283,7 @@ def b16decode(s, casefold=False): s = _bytes_from_decode_data(s) if casefold: s = s.upper() - if re.search(b'[^0-9A-F]', s): + if s.translate(None, delete=b'0123456789ABCDEF'): raise binascii.Error('Non-base16 digit found') return binascii.unhexlify(s) diff --git a/Misc/NEWS.d/next/Library/2025-01-10-13-06-54.gh-issue-118761.f8oADD.rst b/Misc/NEWS.d/next/Library/2025-01-10-13-06-54.gh-issue-118761.f8oADD.rst new file mode 100644 index 00000000000000..37c25cb2efd034 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-10-13-06-54.gh-issue-118761.f8oADD.rst @@ -0,0 +1,5 @@ +Improve the performance of :func:`base64.b16decode` by up to ten times +by more efficiently checking the byte-string for hexadecimal digits. +Reduce the import time of :mod:`base64` by up to six times, +by no longer importing :mod:`re`. +Patch by Bénédikt Tran, Chris Markiewicz, and Adam Turner. From f49a1df6f3b1249bcaedfda8e000e27c6cdffd99 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 14 Jan 2025 15:08:56 +0000 Subject: [PATCH 306/376] GH-128682: Convert explicit loops closing arrays into `DECREF_INPUTS`. (GH-128822) * Mark Py_DECREF and Py_XDECREF as escaping * Remove explicit loops for clearing array inputs --- Python/bytecodes.c | 139 +++++-------- Python/executor_cases.c.h | 132 +++++++----- Python/generated_cases.c.h | 226 ++++++++++++--------- Python/optimizer_cases.c.h | 69 ++++--- Tools/cases_generator/analyzer.py | 3 - Tools/cases_generator/generators_common.py | 1 + 6 files changed, 305 insertions(+), 265 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index ef7086c3c020b0..cec530fefffefb 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1004,10 +1004,10 @@ dummy_func( PyList_SET_ITEM(list, index, PyStackRef_AsPyObjectSteal(value)); assert(old_value != NULL); UNLOCK_OBJECT(list); // unlock before decrefs! - Py_DECREF(old_value); PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); DEAD(sub_st); PyStackRef_CLOSE(list_st); + Py_DECREF(old_value); } inst(STORE_SUBSCR_DICT, (unused/1, value, dict_st, sub -- )) { @@ -1885,9 +1885,8 @@ dummy_func( if (err == 0) { err = PySet_Add(set_o, PyStackRef_AsPyObjectBorrow(values[i])); } - PyStackRef_CLOSE(values[i]); } - DEAD(values); + DECREF_INPUTS(); if (err != 0) { Py_DECREF(set_o); ERROR_IF(true, error); @@ -2412,8 +2411,8 @@ dummy_func( _PyDictValues_AddToInsertionOrder(values, index); } UNLOCK_OBJECT(owner_o); - Py_XDECREF(old_value); PyStackRef_CLOSE(owner); + Py_XDECREF(old_value); } macro(STORE_ATTR_INSTANCE_VALUE) = @@ -2457,9 +2456,9 @@ dummy_func( // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault, // when dict only holds the strong reference to value in ep->me_value. - Py_XDECREF(old_value); STAT_INC(STORE_ATTR, hit); PyStackRef_CLOSE(owner); + Py_XDECREF(old_value); } macro(STORE_ATTR_WITH_HINT) = @@ -2476,8 +2475,8 @@ dummy_func( PyObject *old_value = *(PyObject **)addr; FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, PyStackRef_AsPyObjectSteal(value)); UNLOCK_OBJECT(owner_o); - Py_XDECREF(old_value); PyStackRef_CLOSE(owner); + Py_XDECREF(old_value); } macro(STORE_ATTR_SLOT) = @@ -3435,8 +3434,9 @@ dummy_func( // oparg counts all of the args, but *not* self: int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } // Check if the call can be inlined or not @@ -3448,7 +3448,7 @@ dummy_func( PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( tstate, callable[0], locals, - args, total_args, NULL, frame + arguments, total_args, NULL, frame ); // Manipulate stack directly since we leave using DISPATCH_INLINED(). SYNC_SP(); @@ -3461,13 +3461,9 @@ dummy_func( DISPATCH_INLINED(new_frame); } /* Callable is not a normal Python function */ - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } - DEAD(self_or_null); - PyStackRef_CLOSE(callable[0]); + DECREF_INPUTS(); ERROR_IF(true, error); } PyObject *res_o = PyObject_Vectorcall( @@ -3477,7 +3473,7 @@ dummy_func( STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); if (opcode == INSTRUMENTED_CALL) { PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(args[0]); + &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(arguments[0]); if (res_o == NULL) { _Py_call_instrumentation_exc2( tstate, PY_MONITORING_EVENT_C_RAISE, @@ -3493,11 +3489,7 @@ dummy_func( } } assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } - DEAD(self_or_null); - PyStackRef_CLOSE(callable[0]); + DECREF_INPUTS(); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -3617,12 +3609,13 @@ dummy_func( PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } /* Callable is not a normal Python function */ - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { DECREF_INPUTS(); ERROR_IF(true, error); @@ -3633,11 +3626,7 @@ dummy_func( NULL); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } - DEAD(self_or_null); - PyStackRef_CLOSE(callable[0]); + DECREF_INPUTS(); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -3877,15 +3866,9 @@ dummy_func( DECREF_INPUTS(); ERROR_IF(true, error); } - DEAD(self_or_null); PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(arguments[i]); - } - DEAD(args); - PyStackRef_CLOSE(callable[0]); + DECREF_INPUTS(); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -3952,20 +3935,13 @@ dummy_func( DECREF_INPUTS(); ERROR_IF(true, error); } - DEAD(self_or_null); PyObject *res_o = ((PyCFunctionFast)(void(*)(void))cfunc)( PyCFunction_GET_SELF(callable_o), args_o, total_args); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(arguments[i]); - } - DEAD(args); - PyStackRef_CLOSE(callable[0]); + DECREF_INPUTS(); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -3981,34 +3957,28 @@ dummy_func( PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } DEOPT_IF(!PyCFunction_CheckExact(callable_o)); DEOPT_IF(PyCFunction_GET_FLAGS(callable_o) != (METH_FASTCALL | METH_KEYWORDS)); STAT_INC(CALL, hit); - /* res = func(self, args, nargs, kwnames) */ + /* res = func(self, arguments, nargs, kwnames) */ PyCFunctionFastWithKeywords cfunc = (PyCFunctionFastWithKeywords)(void(*)(void)) PyCFunction_GET_FUNCTION(callable_o); - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { DECREF_INPUTS(); ERROR_IF(true, error); } PyObject *res_o = cfunc(PyCFunction_GET_SELF(callable_o), args_o, total_args, NULL); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } - DEAD(self_or_null); - PyStackRef_CLOSE(callable[0]); + DECREF_INPUTS(); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4147,8 +4117,9 @@ dummy_func( PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; @@ -4156,12 +4127,12 @@ dummy_func( PyMethodDef *meth = method->d_method; EXIT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS)); PyTypeObject *d_type = method->d_common.d_type; - PyObject *self = PyStackRef_AsPyObjectBorrow(args[0]); + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); EXIT_IF(!Py_IS_TYPE(self, d_type)); STAT_INC(CALL, hit); int nargs = total_args - 1; - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { DECREF_INPUTS(); ERROR_IF(true, error); @@ -4171,13 +4142,7 @@ dummy_func( PyObject *res_o = cfunc(self, (args_o + 1), nargs, NULL); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } - DEAD(self_or_null); - PyStackRef_CLOSE(callable[0]); + DECREF_INPUTS(); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4231,8 +4196,9 @@ dummy_func( PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; @@ -4240,12 +4206,12 @@ dummy_func( EXIT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type)); PyMethodDef *meth = method->d_method; EXIT_IF(meth->ml_flags != METH_FASTCALL); - PyObject *self = PyStackRef_AsPyObjectBorrow(args[0]); + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); EXIT_IF(!Py_IS_TYPE(self, method->d_common.d_type)); STAT_INC(CALL, hit); int nargs = total_args - 1; - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { DECREF_INPUTS(); ERROR_IF(true, error); @@ -4255,13 +4221,7 @@ dummy_func( PyObject *res_o = cfunc(self, (args_o + 1), nargs); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - - /* Clear the stack of the arguments. */ - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } - DEAD(self_or_null); - PyStackRef_CLOSE(callable[0]); + DECREF_INPUTS(); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4313,8 +4273,9 @@ dummy_func( // oparg counts all of the args, but *not* self: int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); @@ -4327,7 +4288,7 @@ dummy_func( PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( tstate, callable[0], locals, - args, positional_args, kwnames_o, frame + arguments, positional_args, kwnames_o, frame ); PyStackRef_CLOSE(kwnames); // Sync stack explicitly since we leave using DISPATCH_INLINED(). @@ -4342,7 +4303,7 @@ dummy_func( DISPATCH_INLINED(new_frame); } /* Callable is not a normal Python function */ - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { DECREF_INPUTS(); ERROR_IF(true, error); @@ -4354,7 +4315,7 @@ dummy_func( STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); if (opcode == INSTRUMENTED_CALL_KW) { PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(args[0]); + &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(arguments[0]); if (res_o == NULL) { _Py_call_instrumentation_exc2( tstate, PY_MONITORING_EVENT_C_RAISE, @@ -4369,13 +4330,7 @@ dummy_func( } } } - PyStackRef_CLOSE(kwnames); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } - DEAD(self_or_null); - PyStackRef_CLOSE(callable[0]); + DECREF_INPUTS(); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4385,8 +4340,9 @@ dummy_func( // oparg counts all of the args, but *not* self: int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); @@ -4396,7 +4352,7 @@ dummy_func( PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( tstate, callable[0], locals, - args, positional_args, kwnames_o, frame + arguments, positional_args, kwnames_o, frame ); PyStackRef_CLOSE(kwnames); // The frame has stolen all the arguments from the stack, @@ -4487,12 +4443,13 @@ dummy_func( PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } /* Callable is not a normal Python function */ - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { DECREF_INPUTS(); ERROR_IF(true, error); @@ -4506,11 +4463,7 @@ dummy_func( PyStackRef_CLOSE(kwnames); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } - DEAD(self_or_null); - PyStackRef_CLOSE(callable[0]); + DECREF_INPUTS(); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 0d3b9b269ab247..1aa80f398d7470 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -943,8 +943,8 @@ assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); res_o = PyObject_GetItem(PyStackRef_AsPyObjectBorrow(container), slice); - stack_pointer = _PyFrame_GetStackPointer(frame); Py_DECREF(slice); + stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += 2; assert(WITHIN_STACK_BOUNDS()); } @@ -979,8 +979,8 @@ assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); err = PyObject_SetItem(PyStackRef_AsPyObjectBorrow(container), slice, PyStackRef_AsPyObjectBorrow(v)); - stack_pointer = _PyFrame_GetStackPointer(frame); Py_DECREF(slice); + stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += 2; assert(WITHIN_STACK_BOUNDS()); } @@ -1300,11 +1300,13 @@ PyList_SET_ITEM(list, index, PyStackRef_AsPyObjectSteal(value)); assert(old_value != NULL); UNLOCK_OBJECT(list); // unlock before decrefs! - Py_DECREF(old_value); PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); PyStackRef_CLOSE(list_st); stack_pointer += -3; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -1452,8 +1454,8 @@ "'async for' received an object from __aiter__ " "that does not implement __anext__: %.100s", Py_TYPE(iter_o)->tp_name); - stack_pointer = _PyFrame_GetStackPointer(frame); Py_DECREF(iter_o); + stack_pointer = _PyFrame_GetStackPointer(frame); if (true) JUMP_TO_ERROR(); } iter = PyStackRef_FromPyObjectSteal(iter_o); @@ -2092,7 +2094,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); JUMP_TO_ERROR(); } + _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(oldobj); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -2302,10 +2306,16 @@ err = PySet_Add(set_o, PyStackRef_AsPyObjectBorrow(values[i])); stack_pointer = _PyFrame_GetStackPointer(frame); } - PyStackRef_CLOSE(values[i]); + } + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(values[_i]); } if (err != 0) { + stack_pointer += -oparg; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(set_o); + stack_pointer = _PyFrame_GetStackPointer(frame); if (true) JUMP_TO_ERROR(); } set = PyStackRef_FromPyObjectSteal(set_o); @@ -2367,12 +2377,14 @@ _PyFrame_SetStackPointer(frame, stack_pointer); err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__), ann_dict); - stack_pointer = _PyFrame_GetStackPointer(frame); Py_DECREF(ann_dict); + stack_pointer = _PyFrame_GetStackPointer(frame); if (err) JUMP_TO_ERROR(); } else { + _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(ann_dict); + stack_pointer = _PyFrame_GetStackPointer(frame); } break; } @@ -2992,10 +3004,12 @@ _PyDictValues_AddToInsertionOrder(values, index); } UNLOCK_OBJECT(owner_o); - Py_XDECREF(old_value); PyStackRef_CLOSE(owner); stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_XDECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -3059,11 +3073,13 @@ UNLOCK_OBJECT(dict); // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault, // when dict only holds the strong reference to value in ep->me_value. - Py_XDECREF(old_value); STAT_INC(STORE_ATTR, hit); PyStackRef_CLOSE(owner); stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_XDECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -3083,10 +3099,12 @@ PyObject *old_value = *(PyObject **)addr; FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, PyStackRef_AsPyObjectSteal(value)); UNLOCK_OBJECT(owner_o); - Py_XDECREF(old_value); PyStackRef_CLOSE(owner); stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_XDECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -3111,8 +3129,8 @@ assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); int res_bool = PyObject_IsTrue(res_o); - stack_pointer = _PyFrame_GetStackPointer(frame); Py_DECREF(res_o); + stack_pointer = _PyFrame_GetStackPointer(frame); if (res_bool < 0) JUMP_TO_ERROR(); res = res_bool ? PyStackRef_True : PyStackRef_False; } @@ -3859,7 +3877,9 @@ tb = Py_None; } else { + _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(tb); + stack_pointer = _PyFrame_GetStackPointer(frame); } assert(PyStackRef_LongCheck(lasti)); (void)lasti; // Shut up compiler warning if asserts are off @@ -4206,12 +4226,13 @@ #endif PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } /* Callable is not a normal Python function */ - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); PyStackRef_XCLOSE(self_or_null[0]); @@ -4228,10 +4249,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) JUMP_TO_ERROR(); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; @@ -4723,11 +4745,11 @@ PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL); stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(arguments[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) JUMP_TO_ERROR(); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; @@ -4833,11 +4855,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(arguments[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) JUMP_TO_ERROR(); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; @@ -4858,8 +4880,9 @@ /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } if (!PyCFunction_CheckExact(callable_o)) { @@ -4871,13 +4894,13 @@ JUMP_TO_JUMP_TARGET(); } STAT_INC(CALL, hit); - /* res = func(self, args, nargs, kwnames) */ + /* res = func(self, arguments, nargs, kwnames) */ _PyFrame_SetStackPointer(frame, stack_pointer); PyCFunctionFastWithKeywords cfunc = (PyCFunctionFastWithKeywords)(void(*)(void)) PyCFunction_GET_FUNCTION(callable_o); stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); PyStackRef_XCLOSE(self_or_null[0]); @@ -4891,11 +4914,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) JUMP_TO_ERROR(); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; @@ -5115,8 +5138,9 @@ callable = &stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; @@ -5130,14 +5154,14 @@ JUMP_TO_JUMP_TARGET(); } PyTypeObject *d_type = method->d_common.d_type; - PyObject *self = PyStackRef_AsPyObjectBorrow(args[0]); + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); if (!Py_IS_TYPE(self, d_type)) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } STAT_INC(CALL, hit); int nargs = total_args - 1; - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); PyStackRef_XCLOSE(self_or_null[0]); @@ -5153,11 +5177,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) JUMP_TO_ERROR(); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; @@ -5236,8 +5260,9 @@ callable = &stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; @@ -5251,14 +5276,14 @@ UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - PyObject *self = PyStackRef_AsPyObjectBorrow(args[0]); + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); if (!Py_IS_TYPE(self, method->d_common.d_type)) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } STAT_INC(CALL, hit); int nargs = total_args - 1; - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); PyStackRef_XCLOSE(self_or_null[0]); @@ -5274,11 +5299,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - /* Clear the stack of the arguments. */ - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) JUMP_TO_ERROR(); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; @@ -5334,8 +5359,9 @@ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); // oparg counts all of the args, but *not* self: int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); @@ -5346,7 +5372,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( tstate, callable[0], locals, - args, positional_args, kwnames_o, frame + arguments, positional_args, kwnames_o, frame ); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(kwnames); @@ -5463,12 +5489,13 @@ #endif PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } /* Callable is not a normal Python function */ - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); PyStackRef_XCLOSE(self_or_null[0]); @@ -5489,10 +5516,11 @@ PyStackRef_CLOSE(kwnames); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) JUMP_TO_ERROR(); res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-3 - oparg] = res; @@ -6128,7 +6156,9 @@ case _START_EXECUTOR: { PyObject *executor = (PyObject *)CURRENT_OPERAND0(); + _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(tstate->previous_executor); + stack_pointer = _PyFrame_GetStackPointer(frame); tstate->previous_executor = NULL; #ifndef _Py_JIT current_executor = (_PyExecutorObject*)executor; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 45423a2565c76d..810beb61d0db5e 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -418,8 +418,8 @@ assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); res_o = PyObject_GetItem(PyStackRef_AsPyObjectBorrow(container), slice); - stack_pointer = _PyFrame_GetStackPointer(frame); Py_DECREF(slice); + stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += 2; assert(WITHIN_STACK_BOUNDS()); } @@ -765,15 +765,17 @@ err = PySet_Add(set_o, PyStackRef_AsPyObjectBorrow(values[i])); stack_pointer = _PyFrame_GetStackPointer(frame); } - PyStackRef_CLOSE(values[i]); + } + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(values[_i]); } if (err != 0) { + stack_pointer += -oparg; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(set_o); - { - stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); - goto error; - } + stack_pointer = _PyFrame_GetStackPointer(frame); + goto error; } set = PyStackRef_FromPyObjectSteal(set_o); stack_pointer[-oparg] = set; @@ -931,8 +933,9 @@ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); // oparg counts all of the args, but *not* self: int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } // Check if the call can be inlined or not @@ -945,7 +948,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( tstate, callable[0], locals, - args, total_args, NULL, frame + arguments, total_args, NULL, frame ); stack_pointer = _PyFrame_GetStackPointer(frame); // Manipulate stack directly since we leave using DISPATCH_INLINED(). @@ -960,12 +963,13 @@ DISPATCH_INLINED(new_frame); } /* Callable is not a normal Python function */ - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -981,7 +985,7 @@ STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); if (opcode == INSTRUMENTED_CALL) { PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(args[0]); + &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(arguments[0]); if (res_o == NULL) { _PyFrame_SetStackPointer(frame, stack_pointer); _Py_call_instrumentation_exc2( @@ -1001,10 +1005,11 @@ } } assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -1378,11 +1383,11 @@ PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL); stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(arguments[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -1462,11 +1467,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(arguments[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -1515,20 +1520,21 @@ /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } DEOPT_IF(!PyCFunction_CheckExact(callable_o), CALL); DEOPT_IF(PyCFunction_GET_FLAGS(callable_o) != (METH_FASTCALL | METH_KEYWORDS), CALL); STAT_INC(CALL, hit); - /* res = func(self, args, nargs, kwnames) */ + /* res = func(self, arguments, nargs, kwnames) */ _PyFrame_SetStackPointer(frame, stack_pointer); PyCFunctionFastWithKeywords cfunc = (PyCFunctionFastWithKeywords)(void(*)(void)) PyCFunction_GET_FUNCTION(callable_o); stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); PyStackRef_XCLOSE(self_or_null[0]); @@ -1546,11 +1552,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -1967,8 +1973,9 @@ PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); // oparg counts all of the args, but *not* self: int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); @@ -1983,7 +1990,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( tstate, callable[0], locals, - args, positional_args, kwnames_o, frame + arguments, positional_args, kwnames_o, frame ); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(kwnames); @@ -2000,7 +2007,7 @@ DISPATCH_INLINED(new_frame); } /* Callable is not a normal Python function */ - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); PyStackRef_XCLOSE(self_or_null[0]); @@ -2024,7 +2031,7 @@ STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); if (opcode == INSTRUMENTED_CALL_KW) { PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(args[0]); + &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(arguments[0]); if (res_o == NULL) { _PyFrame_SetStackPointer(frame, stack_pointer); _Py_call_instrumentation_exc2( @@ -2043,12 +2050,12 @@ } } } - PyStackRef_CLOSE(kwnames); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } + PyStackRef_CLOSE(kwnames); if (res_o == NULL) { stack_pointer += -3 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -2115,8 +2122,9 @@ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); // oparg counts all of the args, but *not* self: int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); @@ -2127,7 +2135,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( tstate, callable[0], locals, - args, positional_args, kwnames_o, frame + arguments, positional_args, kwnames_o, frame ); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(kwnames); @@ -2196,12 +2204,13 @@ #endif PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } /* Callable is not a normal Python function */ - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); PyStackRef_XCLOSE(self_or_null[0]); @@ -2226,10 +2235,11 @@ PyStackRef_CLOSE(kwnames); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) { stack_pointer += -3 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -2291,8 +2301,9 @@ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); // oparg counts all of the args, but *not* self: int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); @@ -2303,7 +2314,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( tstate, callable[0], locals, - args, positional_args, kwnames_o, frame + arguments, positional_args, kwnames_o, frame ); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(kwnames); @@ -2446,8 +2457,9 @@ callable = &stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; @@ -2455,11 +2467,11 @@ DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); PyMethodDef *meth = method->d_method; DEOPT_IF(meth->ml_flags != METH_FASTCALL, CALL); - PyObject *self = PyStackRef_AsPyObjectBorrow(args[0]); + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); STAT_INC(CALL, hit); int nargs = total_args - 1; - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); PyStackRef_XCLOSE(self_or_null[0]); @@ -2479,11 +2491,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - /* Clear the stack of the arguments. */ - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -2531,8 +2543,9 @@ callable = &stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; @@ -2540,11 +2553,11 @@ PyMethodDef *meth = method->d_method; DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), CALL); PyTypeObject *d_type = method->d_common.d_type; - PyObject *self = PyStackRef_AsPyObjectBorrow(args[0]); + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL); STAT_INC(CALL, hit); int nargs = total_args - 1; - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); PyStackRef_XCLOSE(self_or_null[0]); @@ -2564,11 +2577,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - /* Free the arguments. */ - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -2772,12 +2785,13 @@ #endif PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } /* Callable is not a normal Python function */ - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { PyStackRef_CLOSE(callable[0]); PyStackRef_XCLOSE(self_or_null[0]); @@ -2798,10 +2812,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -3279,8 +3294,8 @@ assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); int res_bool = PyObject_IsTrue(res_o); - stack_pointer = _PyFrame_GetStackPointer(frame); Py_DECREF(res_o); + stack_pointer = _PyFrame_GetStackPointer(frame); if (res_bool < 0) goto error; res = res_bool ? PyStackRef_True : PyStackRef_False; } @@ -3602,7 +3617,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); goto error; } + _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(oldobj); + stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } @@ -4069,7 +4086,9 @@ #ifndef Py_GIL_DISABLED if (seq != NULL) { it->it_seq = NULL; + _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(seq); + stack_pointer = _PyFrame_GetStackPointer(frame); } #endif /* Jump forward oparg, then skip following END_FOR instruction */ @@ -4159,7 +4178,9 @@ if (seq == NULL || it->it_index >= PyTuple_GET_SIZE(seq)) { if (seq != NULL) { it->it_seq = NULL; + _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(seq); + stack_pointer = _PyFrame_GetStackPointer(frame); } /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(oparg + 1); @@ -4220,8 +4241,8 @@ "'async for' received an object from __aiter__ " "that does not implement __anext__: %.100s", Py_TYPE(iter_o)->tp_name); - stack_pointer = _PyFrame_GetStackPointer(frame); Py_DECREF(iter_o); + stack_pointer = _PyFrame_GetStackPointer(frame); goto error; } iter = PyStackRef_FromPyObjectSteal(iter_o); @@ -4455,8 +4476,9 @@ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); // oparg counts all of the args, but *not* self: int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } // Check if the call can be inlined or not @@ -4469,7 +4491,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( tstate, callable[0], locals, - args, total_args, NULL, frame + arguments, total_args, NULL, frame ); stack_pointer = _PyFrame_GetStackPointer(frame); // Manipulate stack directly since we leave using DISPATCH_INLINED(). @@ -4484,12 +4506,13 @@ DISPATCH_INLINED(new_frame); } /* Callable is not a normal Python function */ - STACKREFS_TO_PYOBJECTS(args, total_args, args_o); + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -4505,7 +4528,7 @@ STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); if (opcode == INSTRUMENTED_CALL) { PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(args[0]); + &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(arguments[0]); if (res_o == NULL) { _PyFrame_SetStackPointer(frame, stack_pointer); _Py_call_instrumentation_exc2( @@ -4525,10 +4548,11 @@ } } assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - for (int i = 0; i < total_args; i++) { - PyStackRef_CLOSE(args[i]); - } PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -6470,8 +6494,8 @@ assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *attr_o = PyObject_GetAttr(super, name); - stack_pointer = _PyFrame_GetStackPointer(frame); Py_DECREF(super); + stack_pointer = _PyFrame_GetStackPointer(frame); if (attr_o == NULL) goto error; attr = PyStackRef_FromPyObjectSteal(attr_o); null = PyStackRef_NULL; @@ -6961,8 +6985,8 @@ assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); _PyErr_SetString(tstate, PyExc_SystemError, "lasti is not an int"); - stack_pointer = _PyFrame_GetStackPointer(frame); Py_DECREF(exc); + stack_pointer = _PyFrame_GetStackPointer(frame); goto error; } stack_pointer += 1; @@ -7307,12 +7331,14 @@ _PyFrame_SetStackPointer(frame, stack_pointer); err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__), ann_dict); - stack_pointer = _PyFrame_GetStackPointer(frame); Py_DECREF(ann_dict); + stack_pointer = _PyFrame_GetStackPointer(frame); if (err) goto error; } else { + _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(ann_dict); + stack_pointer = _PyFrame_GetStackPointer(frame); } DISPATCH(); } @@ -7472,11 +7498,13 @@ _PyDictValues_AddToInsertionOrder(values, index); } UNLOCK_OBJECT(owner_o); - Py_XDECREF(old_value); PyStackRef_CLOSE(owner); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_XDECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); } - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -7507,11 +7535,13 @@ PyObject *old_value = *(PyObject **)addr; FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, PyStackRef_AsPyObjectSteal(value)); UNLOCK_OBJECT(owner_o); - Py_XDECREF(old_value); PyStackRef_CLOSE(owner); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_XDECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); } - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -7570,12 +7600,14 @@ UNLOCK_OBJECT(dict); // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault, // when dict only holds the strong reference to value in ep->me_value. - Py_XDECREF(old_value); STAT_INC(STORE_ATTR, hit); PyStackRef_CLOSE(owner); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_XDECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); } - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -7723,8 +7755,8 @@ assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); err = PyObject_SetItem(PyStackRef_AsPyObjectBorrow(container), slice, PyStackRef_AsPyObjectBorrow(v)); - stack_pointer = _PyFrame_GetStackPointer(frame); Py_DECREF(slice); + stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += 2; assert(WITHIN_STACK_BOUNDS()); } @@ -7839,11 +7871,13 @@ PyList_SET_ITEM(list, index, PyStackRef_AsPyObjectSteal(value)); assert(old_value != NULL); UNLOCK_OBJECT(list); // unlock before decrefs! - Py_DECREF(old_value); PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); PyStackRef_CLOSE(list_st); stack_pointer += -3; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } @@ -8254,7 +8288,9 @@ tb = Py_None; } else { + _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(tb); + stack_pointer = _PyFrame_GetStackPointer(frame); } assert(PyStackRef_LongCheck(lasti)); (void)lasti; // Shut up compiler warning if asserts are off diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index a64dd465467122..1a7cc6becfefb6 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -279,16 +279,19 @@ goto error; } res = sym_new_const(ctx, temp); + stack_pointer[-2] = res; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); Py_DECREF(temp); // TODO gh-115506: // replace opcode with constant propagated one and add tests! } else { res = sym_new_type(ctx, &PyLong_Type); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); } - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = res; break; } @@ -309,16 +312,19 @@ goto error; } res = sym_new_const(ctx, temp); + stack_pointer[-2] = res; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); Py_DECREF(temp); // TODO gh-115506: // replace opcode with constant propagated one and add tests! } else { res = sym_new_type(ctx, &PyLong_Type); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); } - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = res; break; } @@ -339,16 +345,19 @@ goto error; } res = sym_new_const(ctx, temp); + stack_pointer[-2] = res; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); Py_DECREF(temp); // TODO gh-115506: // replace opcode with constant propagated one and add tests! } else { res = sym_new_type(ctx, &PyLong_Type); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); } - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = res; break; } @@ -401,16 +410,19 @@ goto error; } res = sym_new_const(ctx, temp); + stack_pointer[-2] = res; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); Py_DECREF(temp); // TODO gh-115506: // replace opcode with constant propagated one and update tests! } else { res = sym_new_type(ctx, &PyFloat_Type); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); } - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = res; break; } @@ -432,16 +444,19 @@ goto error; } res = sym_new_const(ctx, temp); + stack_pointer[-2] = res; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); Py_DECREF(temp); // TODO gh-115506: // replace opcode with constant propagated one and update tests! } else { res = sym_new_type(ctx, &PyFloat_Type); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); } - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = res; break; } @@ -463,16 +478,19 @@ goto error; } res = sym_new_const(ctx, temp); + stack_pointer[-2] = res; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); Py_DECREF(temp); // TODO gh-115506: // replace opcode with constant propagated one and update tests! } else { res = sym_new_type(ctx, &PyFloat_Type); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); } - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = res; break; } @@ -503,14 +521,17 @@ goto error; } res = sym_new_const(ctx, temp); + stack_pointer[-2] = res; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyUnicode_Type); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); } - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = res; break; } @@ -527,15 +548,17 @@ goto error; } res = sym_new_const(ctx, temp); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyUnicode_Type); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); } // _STORE_FAST: GETLOCAL(this_instr->operand0) = res; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); break; } diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 4e1d47281fdbbf..99896f32fd2b08 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -567,7 +567,6 @@ def has_error_without_pop(op: parser.InstDef) -> bool: "PyUnicode_READ_CHAR", "Py_ARRAY_LENGTH", "Py_CLEAR", - "Py_DECREF", "Py_FatalError", "Py_INCREF", "Py_IS_TYPE", @@ -577,7 +576,6 @@ def has_error_without_pop(op: parser.InstDef) -> bool: "Py_TYPE", "Py_UNREACHABLE", "Py_Unicode_GET_LENGTH", - "Py_XDECREF", "_PyCode_CODE", "_PyDictValues_AddToInsertionOrder", "_PyErr_Occurred", @@ -620,7 +618,6 @@ def has_error_without_pop(op: parser.InstDef) -> bool: "_PyUnicode_JoinArray", "_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY", "_Py_DECREF_NO_DEALLOC", - "_Py_DECREF_SPECIALIZED", "_Py_EnterRecursiveCallTstateUnchecked", "_Py_ID", "_Py_IsImmortal", diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index 0035deb02ffdf0..69d84183f1c7e6 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -121,6 +121,7 @@ def __init__(self, out: CWriter): "SAVE_STACK": self.save_stack, "RELOAD_STACK": self.reload_stack, "PyStackRef_CLOSE": self.stackref_close, + "PyStackRef_XCLOSE": self.stackref_close, "PyStackRef_CLOSE_SPECIALIZED": self.stackref_close_specialized, "PyStackRef_AsPyObjectSteal": self.stackref_steal, "DISPATCH": self.dispatch, From aa805887cf23e5bff1598615d4f91369835f016f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 14 Jan 2025 17:10:53 +0200 Subject: [PATCH 307/376] Post 3.14.0a4 --- Include/patchlevel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 55af2c1e39cf40..40e7d73b7a6634 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -24,7 +24,7 @@ #define PY_RELEASE_SERIAL 4 /* Version as a string */ -#define PY_VERSION "3.14.0a4" +#define PY_VERSION "3.14.0a4+" /*--end constants--*/ From 1598e18a659ef3a533caa0a79a489a3ed1d80bdd Mon Sep 17 00:00:00 2001 From: Rafael Fontenelle Date: Tue, 14 Jan 2025 12:17:24 -0300 Subject: [PATCH 308/376] Fix a "doctest" block in `Doc/library/turtle.rst` (#128831) --- Doc/library/turtle.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst index 61e2d125445b5a..548b9d3543fb94 100644 --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -993,8 +993,8 @@ Settings for measurement >>> turtle.heading() 90.0 - Change angle measurement unit to grad (also known as gon, - grade, or gradian and equals 1/100-th of the right angle.) + >>> # Change angle measurement unit to grad (also known as gon, + >>> # grade, or gradian and equals 1/100-th of the right angle.) >>> turtle.degrees(400.0) >>> turtle.heading() 100.0 From 43ef9587ae869fd11711a3bb126e65afe9177342 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jan 2025 16:29:21 +0100 Subject: [PATCH 309/376] gh-106320: Document replacement for removed C API (#128787) --- Doc/whatsnew/3.13.rst | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 6a0e483bd895d6..c2cfdf3186f86d 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -2706,6 +2706,33 @@ Changes in the C API Calling this function is redundant now that :c:func:`PyFrame_GetLocals` returns a write-through proxy for :term:`optimized scopes `. +* Python 3.13 removed many private functions. Some of them can be replaced using these + alternatives: + + * ``_PyDict_Pop()``: :c:func:`PyDict_Pop` or :c:func:`PyDict_PopString`; + * ``_PyDict_GetItemWithError()``: :c:func:`PyDict_GetItemRef`; + * ``_PyErr_WriteUnraisableMsg()``: :c:func:`PyErr_FormatUnraisable`; + * ``_PyEval_SetTrace()``: :c:func:`PyEval_SetTrace` or :c:func:`PyEval_SetTraceAllThreads`; + * ``_PyList_Extend()``: :c:func:`PyList_Extend`; + * ``_PyLong_AsInt()``: :c:func:`PyLong_AsInt`; + * ``_PyMem_RawStrdup()``: ``strdup()``; + * ``_PyMem_Strdup()``: ``strdup()``; + * ``_PyObject_ClearManagedDict()``: :c:func:`PyObject_ClearManagedDict`; + * ``_PyObject_VisitManagedDict()``: :c:func:`PyObject_VisitManagedDict`; + * ``_PyThreadState_UncheckedGet()``: :c:func:`PyThreadState_GetUnchecked()`; + * ``_PyTime_AsSecondsDouble()``: :c:func:`PyTime_AsSecondsDouble`; + * ``_PyTime_GetMonotonicClock()``: :c:func:`PyTime_Monotonic` or :c:func:`PyTime_MonotonicRaw`; + * ``_PyTime_GetPerfCounter()``: :c:func:`PyTime_PerfCounter` or :c:func:`PyTime_PerfCounterRaw`; + * ``_PyTime_GetSystemClock()``: :c:func:`PyTime_Time` or :c:func:`PyTime_TimeRaw`; + * ``_PyTime_MAX``: :c:var:`PyTime_MAX`; + * ``_PyTime_MIN``: :c:var:`PyTime_MIN`; + * ``_PyTime_t``: :c:type:`PyTime_t`; + * ``_Py_HashPointer()``: :c:func:`Py_HashPointer`; + * ``_Py_IsFinalizing()``: :c:func:`Py_IsFinalizing`. + + The `pythoncapi-compat project`_ can be used to get most of these new + functions on Python 3.12 and older. + Regression Test Changes ======================= From d786ac7f584f23c3206f4c86032bdabe83c17b51 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jan 2025 16:31:13 +0100 Subject: [PATCH 310/376] gh-59705: Document OS thread name change (#128800) --- Doc/library/threading.rst | 10 +++++++--- Doc/whatsnew/3.14.rst | 7 +++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index f183f3f535c4cb..00511df32e4388 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -380,6 +380,13 @@ since it is impossible to detect the termination of alien threads. This method will raise a :exc:`RuntimeError` if called more than once on the same thread object. + If supported, set the operating system thread name to + :attr:`threading.Thread.name`. The name can be truncated depending on the + operating system thread name limits. + + .. versionchanged:: 3.14 + Set the operating system thread name. + .. method:: run() Method representing the thread's activity. @@ -443,9 +450,6 @@ since it is impossible to detect the termination of alien threads. running thread is renamed. (Setting the *name* attribute of a different thread only updates the Python Thread object.) - .. versionchanged:: 3.14 - Set the operating system thread name. - .. method:: getName() setName() diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index ac203b208f4a8a..9f7ef101e56478 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -645,6 +645,13 @@ sys.monitoring * Two new events are added: :monitoring-event:`BRANCH_LEFT` and :monitoring-event:`BRANCH_RIGHT`. The ``BRANCH`` event is deprecated. +threading +--------- + +* :meth:`threading.Thread.start` now sets the operating system thread name + to :attr:`threading.Thread.name`. + (Contributed by Victor Stinner in :gh:`59705`.) + tkinter ------- From d906bde250d59c396d8dab92285b832c66cdec27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 14 Jan 2025 17:07:37 +0100 Subject: [PATCH 311/376] gh-67206: Document that `string.printable` is not printable in the POSIX sense (#128820) --- Doc/library/string.rst | 13 ++++++++++--- .../2025-01-14-11-06-41.gh-issue-67206.LYKmi5.rst | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Documentation/2025-01-14-11-06-41.gh-issue-67206.LYKmi5.rst diff --git a/Doc/library/string.rst b/Doc/library/string.rst index 913672a3ff2270..09165c481b246e 100644 --- a/Doc/library/string.rst +++ b/Doc/library/string.rst @@ -59,11 +59,18 @@ The constants defined in this module are: String of ASCII characters which are considered punctuation characters in the ``C`` locale: ``!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~``. + .. data:: printable - String of ASCII characters which are considered printable. This is a - combination of :const:`digits`, :const:`ascii_letters`, :const:`punctuation`, - and :const:`whitespace`. + String of ASCII characters which are considered printable by Python. + This is a combination of :const:`digits`, :const:`ascii_letters`, + :const:`punctuation`, and :const:`whitespace`. + + .. note:: + + By design, :meth:`string.printable.isprintable() ` + returns :const:`False`. In particular, ``string.printable`` is not + printable in the POSIX sense (see :manpage:`LC_CTYPE `). .. data:: whitespace diff --git a/Misc/NEWS.d/next/Documentation/2025-01-14-11-06-41.gh-issue-67206.LYKmi5.rst b/Misc/NEWS.d/next/Documentation/2025-01-14-11-06-41.gh-issue-67206.LYKmi5.rst new file mode 100644 index 00000000000000..11fb617e98edc3 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2025-01-14-11-06-41.gh-issue-67206.LYKmi5.rst @@ -0,0 +1,3 @@ +Document that :const:`string.printable` is not printable in the POSIX sense. +In particular, :meth:`string.printable.isprintable() ` returns +:const:`False`. Patch by Bénédikt Tran. From 1c13c56a34fc4c4d8969f0b6dc93d5208a50d61b Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Tue, 14 Jan 2025 11:43:42 -0800 Subject: [PATCH 312/376] gh-128384: Add locking to warnings.py. (gh-128386) Co-authored-by: Kumar Aditya --- Include/internal/pycore_warnings.h | 2 +- Lib/test/test_warnings/__init__.py | 2 +- Lib/warnings.py | 188 ++++++++++-------- ...-01-06-10-37-27.gh-issue-128384.V0xzwH.rst | 5 + Python/_warnings.c | 118 +++++++---- Python/clinic/_warnings.c.h | 50 ++++- 6 files changed, 237 insertions(+), 128 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-06-10-37-27.gh-issue-128384.V0xzwH.rst diff --git a/Include/internal/pycore_warnings.h b/Include/internal/pycore_warnings.h index f9f6559312f4ef..672228cd6fbd19 100644 --- a/Include/internal/pycore_warnings.h +++ b/Include/internal/pycore_warnings.h @@ -14,7 +14,7 @@ struct _warnings_runtime_state { PyObject *filters; /* List */ PyObject *once_registry; /* Dict */ PyObject *default_action; /* String */ - PyMutex mutex; + _PyRecursiveMutex lock; long filters_version; }; diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index 4e3c877896f295..4bd164b8a9a82b 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -1521,7 +1521,7 @@ def test_late_resource_warning(self): self.assertTrue(err.startswith(expected), ascii(err)) -class DeprecatedTests(unittest.TestCase): +class DeprecatedTests(PyPublicAPITests): def test_dunder_deprecated(self): @deprecated("A will go away soon") class A: diff --git a/Lib/warnings.py b/Lib/warnings.py index e83cde37ab2d1a..f20b01372dd7a4 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -185,24 +185,32 @@ def simplefilter(action, category=Warning, lineno=0, append=False): raise ValueError("lineno must be an int >= 0") _add_filter(action, None, category, None, lineno, append=append) +def _filters_mutated(): + # Even though this function is not part of the public API, it's used by + # a fair amount of user code. + with _lock: + _filters_mutated_lock_held() + def _add_filter(*item, append): - # Remove possible duplicate filters, so new one will be placed - # in correct place. If append=True and duplicate exists, do nothing. - if not append: - try: - filters.remove(item) - except ValueError: - pass - filters.insert(0, item) - else: - if item not in filters: - filters.append(item) - _filters_mutated() + with _lock: + if not append: + # Remove possible duplicate filters, so new one will be placed + # in correct place. If append=True and duplicate exists, do nothing. + try: + filters.remove(item) + except ValueError: + pass + filters.insert(0, item) + else: + if item not in filters: + filters.append(item) + _filters_mutated_lock_held() def resetwarnings(): """Clear the list of warning filters, so that no filters are active.""" - filters[:] = [] - _filters_mutated() + with _lock: + filters[:] = [] + _filters_mutated_lock_held() class _OptionError(Exception): """Exception used by option processing helpers.""" @@ -353,11 +361,6 @@ def warn_explicit(message, category, filename, lineno, module = filename or "" if module[-3:].lower() == ".py": module = module[:-3] # XXX What about leading pathname? - if registry is None: - registry = {} - if registry.get('version', 0) != _filters_version: - registry.clear() - registry['version'] = _filters_version if isinstance(message, Warning): text = str(message) category = message.__class__ @@ -365,52 +368,59 @@ def warn_explicit(message, category, filename, lineno, text = message message = category(message) key = (text, category, lineno) - # Quick test for common case - if registry.get(key): - return - # Search the filters - for item in filters: - action, msg, cat, mod, ln = item - if ((msg is None or msg.match(text)) and - issubclass(category, cat) and - (mod is None or mod.match(module)) and - (ln == 0 or lineno == ln)): - break - else: - action = defaultaction - # Early exit actions - if action == "ignore": - return + with _lock: + if registry is None: + registry = {} + if registry.get('version', 0) != _filters_version: + registry.clear() + registry['version'] = _filters_version + # Quick test for common case + if registry.get(key): + return + # Search the filters + for item in filters: + action, msg, cat, mod, ln = item + if ((msg is None or msg.match(text)) and + issubclass(category, cat) and + (mod is None or mod.match(module)) and + (ln == 0 or lineno == ln)): + break + else: + action = defaultaction + # Early exit actions + if action == "ignore": + return + + if action == "error": + raise message + # Other actions + if action == "once": + registry[key] = 1 + oncekey = (text, category) + if onceregistry.get(oncekey): + return + onceregistry[oncekey] = 1 + elif action in {"always", "all"}: + pass + elif action == "module": + registry[key] = 1 + altkey = (text, category, 0) + if registry.get(altkey): + return + registry[altkey] = 1 + elif action == "default": + registry[key] = 1 + else: + # Unrecognized actions are errors + raise RuntimeError( + "Unrecognized action (%r) in warnings.filters:\n %s" % + (action, item)) # Prime the linecache for formatting, in case the # "file" is actually in a zipfile or something. import linecache linecache.getlines(filename, module_globals) - if action == "error": - raise message - # Other actions - if action == "once": - registry[key] = 1 - oncekey = (text, category) - if onceregistry.get(oncekey): - return - onceregistry[oncekey] = 1 - elif action in {"always", "all"}: - pass - elif action == "module": - registry[key] = 1 - altkey = (text, category, 0) - if registry.get(altkey): - return - registry[altkey] = 1 - elif action == "default": - registry[key] = 1 - else: - # Unrecognized actions are errors - raise RuntimeError( - "Unrecognized action (%r) in warnings.filters:\n %s" % - (action, item)) # Print message and context msg = WarningMessage(message, category, filename, lineno, source) _showwarnmsg(msg) @@ -488,30 +498,32 @@ def __enter__(self): if self._entered: raise RuntimeError("Cannot enter %r twice" % self) self._entered = True - self._filters = self._module.filters - self._module.filters = self._filters[:] - self._module._filters_mutated() - self._showwarning = self._module.showwarning - self._showwarnmsg_impl = self._module._showwarnmsg_impl + with _lock: + self._filters = self._module.filters + self._module.filters = self._filters[:] + self._module._filters_mutated_lock_held() + self._showwarning = self._module.showwarning + self._showwarnmsg_impl = self._module._showwarnmsg_impl + if self._record: + log = [] + self._module._showwarnmsg_impl = log.append + # Reset showwarning() to the default implementation to make sure + # that _showwarnmsg() calls _showwarnmsg_impl() + self._module.showwarning = self._module._showwarning_orig + else: + log = None if self._filter is not None: simplefilter(*self._filter) - if self._record: - log = [] - self._module._showwarnmsg_impl = log.append - # Reset showwarning() to the default implementation to make sure - # that _showwarnmsg() calls _showwarnmsg_impl() - self._module.showwarning = self._module._showwarning_orig - return log - else: - return None + return log def __exit__(self, *exc_info): if not self._entered: raise RuntimeError("Cannot exit %r without entering first" % self) - self._module.filters = self._filters - self._module._filters_mutated() - self._module.showwarning = self._showwarning - self._module._showwarnmsg_impl = self._showwarnmsg_impl + with _lock: + self._module.filters = self._filters + self._module._filters_mutated_lock_held() + self._module.showwarning = self._showwarning + self._module._showwarnmsg_impl = self._showwarnmsg_impl class deprecated: @@ -701,18 +713,36 @@ def extract(): # If either if the compiled regexs are None, match anything. try: from _warnings import (filters, _defaultaction, _onceregistry, - warn, warn_explicit, _filters_mutated) + warn, warn_explicit, + _filters_mutated_lock_held, + _acquire_lock, _release_lock, + ) defaultaction = _defaultaction onceregistry = _onceregistry _warnings_defaults = True + + class _Lock: + def __enter__(self): + _acquire_lock() + return self + + def __exit__(self, *args): + _release_lock() + + _lock = _Lock() + except ImportError: filters = [] defaultaction = "default" onceregistry = {} + import _thread + + _lock = _thread.RLock() + _filters_version = 1 - def _filters_mutated(): + def _filters_mutated_lock_held(): global _filters_version _filters_version += 1 diff --git a/Misc/NEWS.d/next/Library/2025-01-06-10-37-27.gh-issue-128384.V0xzwH.rst b/Misc/NEWS.d/next/Library/2025-01-06-10-37-27.gh-issue-128384.V0xzwH.rst new file mode 100644 index 00000000000000..2ca592be20b681 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-06-10-37-27.gh-issue-128384.V0xzwH.rst @@ -0,0 +1,5 @@ +Add locking to :mod:`warnings` to avoid some data races when free-threading +is used. Change ``_warnings_runtime_state.mutex`` to be a recursive mutex +and expose it to :mod:`warnings`, via the :func:`!_acquire_lock` and +:func:`!_release_lock` functions. The lock is held when ``filters`` and +``_filters_version`` are updated. diff --git a/Python/_warnings.c b/Python/_warnings.c index e05ba99e8eaec4..283f203c72c9bf 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -232,6 +232,61 @@ get_warnings_attr(PyInterpreterState *interp, PyObject *attr, int try_import) return obj; } +static inline void +warnings_lock(PyInterpreterState *interp) +{ + WarningsState *st = warnings_get_state(interp); + assert(st != NULL); + _PyRecursiveMutex_Lock(&st->lock); +} + +static inline void +warnings_unlock(PyInterpreterState *interp) +{ + WarningsState *st = warnings_get_state(interp); + assert(st != NULL); + _PyRecursiveMutex_Unlock(&st->lock); +} + +static inline bool +warnings_lock_held(WarningsState *st) +{ + return PyMutex_IsLocked(&st->lock.mutex); +} + +/*[clinic input] +_acquire_lock as warnings_acquire_lock + +[clinic start generated code]*/ + +static PyObject * +warnings_acquire_lock_impl(PyObject *module) +/*[clinic end generated code: output=594313457d1bf8e1 input=46ec20e55acca52f]*/ +{ + PyInterpreterState *interp = get_current_interp(); + if (interp == NULL) { + return NULL; + } + warnings_lock(interp); + Py_RETURN_NONE; +} + +/*[clinic input] +_release_lock as warnings_release_lock + +[clinic start generated code]*/ + +static PyObject * +warnings_release_lock_impl(PyObject *module) +/*[clinic end generated code: output=d73d5a8789396750 input=ea01bb77870c5693]*/ +{ + PyInterpreterState *interp = get_current_interp(); + if (interp == NULL) { + return NULL; + } + warnings_unlock(interp); + Py_RETURN_NONE; +} static PyObject * get_once_registry(PyInterpreterState *interp) @@ -239,7 +294,7 @@ get_once_registry(PyInterpreterState *interp) WarningsState *st = warnings_get_state(interp); assert(st != NULL); - _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(&st->mutex); + assert(warnings_lock_held(st)); PyObject *registry = GET_WARNINGS_ATTR(interp, onceregistry, 0); if (registry == NULL) { @@ -267,7 +322,7 @@ get_default_action(PyInterpreterState *interp) WarningsState *st = warnings_get_state(interp); assert(st != NULL); - _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(&st->mutex); + assert(warnings_lock_held(st)); PyObject *default_action = GET_WARNINGS_ATTR(interp, defaultaction, 0); if (default_action == NULL) { @@ -299,7 +354,7 @@ get_filter(PyInterpreterState *interp, PyObject *category, WarningsState *st = warnings_get_state(interp); assert(st != NULL); - _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(&st->mutex); + assert(warnings_lock_held(st)); PyObject *warnings_filters = GET_WARNINGS_ATTR(interp, filters, 0); if (warnings_filters == NULL) { @@ -399,7 +454,7 @@ already_warned(PyInterpreterState *interp, PyObject *registry, PyObject *key, WarningsState *st = warnings_get_state(interp); assert(st != NULL); - _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(&st->mutex); + assert(warnings_lock_held(st)); PyObject *version_obj; if (PyDict_GetItemRef(registry, &_Py_ID(version), &version_obj) < 0) { @@ -994,15 +1049,10 @@ do_warn(PyObject *message, PyObject *category, Py_ssize_t stack_level, &filename, &lineno, &module, ®istry)) return NULL; -#ifdef Py_GIL_DISABLED - WarningsState *st = warnings_get_state(tstate->interp); - assert(st != NULL); -#endif - - Py_BEGIN_CRITICAL_SECTION_MUT(&st->mutex); + warnings_lock(tstate->interp); res = warn_explicit(tstate, category, message, filename, lineno, module, registry, NULL, source); - Py_END_CRITICAL_SECTION(); + warnings_unlock(tstate->interp); Py_DECREF(filename); Py_DECREF(registry); Py_DECREF(module); @@ -1151,27 +1201,22 @@ warnings_warn_explicit_impl(PyObject *module, PyObject *message, } } -#ifdef Py_GIL_DISABLED - WarningsState *st = warnings_get_state(tstate->interp); - assert(st != NULL); -#endif - - Py_BEGIN_CRITICAL_SECTION_MUT(&st->mutex); + warnings_lock(tstate->interp); returned = warn_explicit(tstate, category, message, filename, lineno, mod, registry, source_line, sourceobj); - Py_END_CRITICAL_SECTION(); + warnings_unlock(tstate->interp); Py_XDECREF(source_line); return returned; } /*[clinic input] -_filters_mutated as warnings_filters_mutated +_filters_mutated_lock_held as warnings_filters_mutated_lock_held [clinic start generated code]*/ static PyObject * -warnings_filters_mutated_impl(PyObject *module) -/*[clinic end generated code: output=8ce517abd12b88f4 input=35ecbf08ee2491b2]*/ +warnings_filters_mutated_lock_held_impl(PyObject *module) +/*[clinic end generated code: output=df5c84f044e856ec input=34208bf03d70e432]*/ { PyInterpreterState *interp = get_current_interp(); if (interp == NULL) { @@ -1181,14 +1226,17 @@ warnings_filters_mutated_impl(PyObject *module) WarningsState *st = warnings_get_state(interp); assert(st != NULL); - Py_BEGIN_CRITICAL_SECTION_MUT(&st->mutex); + // Note that the lock must be held by the caller. + if (!warnings_lock_held(st)) { + PyErr_SetString(PyExc_RuntimeError, "warnings lock is not held"); + return NULL; + } + st->filters_version++; - Py_END_CRITICAL_SECTION(); Py_RETURN_NONE; } - /* Function to issue a warning message; may raise an exception. */ static int @@ -1303,15 +1351,10 @@ PyErr_WarnExplicitObject(PyObject *category, PyObject *message, return -1; } -#ifdef Py_GIL_DISABLED - WarningsState *st = warnings_get_state(tstate->interp); - assert(st != NULL); -#endif - - Py_BEGIN_CRITICAL_SECTION_MUT(&st->mutex); + warnings_lock(tstate->interp); res = warn_explicit(tstate, category, message, filename, lineno, module, registry, NULL, NULL); - Py_END_CRITICAL_SECTION(); + warnings_unlock(tstate->interp); if (res == NULL) return -1; Py_DECREF(res); @@ -1376,15 +1419,10 @@ PyErr_WarnExplicitFormat(PyObject *category, PyObject *res; PyThreadState *tstate = get_current_tstate(); if (tstate != NULL) { -#ifdef Py_GIL_DISABLED - WarningsState *st = warnings_get_state(tstate->interp); - assert(st != NULL); -#endif - - Py_BEGIN_CRITICAL_SECTION_MUT(&st->mutex); + warnings_lock(tstate->interp); res = warn_explicit(tstate, category, message, filename, lineno, module, registry, NULL, NULL); - Py_END_CRITICAL_SECTION(); + warnings_unlock(tstate->interp); Py_DECREF(message); if (res != NULL) { Py_DECREF(res); @@ -1464,7 +1502,9 @@ _PyErr_WarnUnawaitedCoroutine(PyObject *coro) static PyMethodDef warnings_functions[] = { WARNINGS_WARN_METHODDEF WARNINGS_WARN_EXPLICIT_METHODDEF - WARNINGS_FILTERS_MUTATED_METHODDEF + WARNINGS_FILTERS_MUTATED_LOCK_HELD_METHODDEF + WARNINGS_ACQUIRE_LOCK_METHODDEF + WARNINGS_RELEASE_LOCK_METHODDEF /* XXX(brett.cannon): add showwarning? */ /* XXX(brett.cannon): Reasonable to add formatwarning? */ {NULL, NULL} /* sentinel */ diff --git a/Python/clinic/_warnings.c.h b/Python/clinic/_warnings.c.h index 9a2c33f2ea8169..bcb4b344fa4370 100644 --- a/Python/clinic/_warnings.c.h +++ b/Python/clinic/_warnings.c.h @@ -9,6 +9,40 @@ preserve #include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() +PyDoc_STRVAR(warnings_acquire_lock__doc__, +"_acquire_lock($module, /)\n" +"--\n" +"\n"); + +#define WARNINGS_ACQUIRE_LOCK_METHODDEF \ + {"_acquire_lock", (PyCFunction)warnings_acquire_lock, METH_NOARGS, warnings_acquire_lock__doc__}, + +static PyObject * +warnings_acquire_lock_impl(PyObject *module); + +static PyObject * +warnings_acquire_lock(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return warnings_acquire_lock_impl(module); +} + +PyDoc_STRVAR(warnings_release_lock__doc__, +"_release_lock($module, /)\n" +"--\n" +"\n"); + +#define WARNINGS_RELEASE_LOCK_METHODDEF \ + {"_release_lock", (PyCFunction)warnings_release_lock, METH_NOARGS, warnings_release_lock__doc__}, + +static PyObject * +warnings_release_lock_impl(PyObject *module); + +static PyObject * +warnings_release_lock(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return warnings_release_lock_impl(module); +} + PyDoc_STRVAR(warnings_warn__doc__, "warn($module, /, message, category=None, stacklevel=1, source=None, *,\n" " skip_file_prefixes=)\n" @@ -230,20 +264,20 @@ warnings_warn_explicit(PyObject *module, PyObject *const *args, Py_ssize_t nargs return return_value; } -PyDoc_STRVAR(warnings_filters_mutated__doc__, -"_filters_mutated($module, /)\n" +PyDoc_STRVAR(warnings_filters_mutated_lock_held__doc__, +"_filters_mutated_lock_held($module, /)\n" "--\n" "\n"); -#define WARNINGS_FILTERS_MUTATED_METHODDEF \ - {"_filters_mutated", (PyCFunction)warnings_filters_mutated, METH_NOARGS, warnings_filters_mutated__doc__}, +#define WARNINGS_FILTERS_MUTATED_LOCK_HELD_METHODDEF \ + {"_filters_mutated_lock_held", (PyCFunction)warnings_filters_mutated_lock_held, METH_NOARGS, warnings_filters_mutated_lock_held__doc__}, static PyObject * -warnings_filters_mutated_impl(PyObject *module); +warnings_filters_mutated_lock_held_impl(PyObject *module); static PyObject * -warnings_filters_mutated(PyObject *module, PyObject *Py_UNUSED(ignored)) +warnings_filters_mutated_lock_held(PyObject *module, PyObject *Py_UNUSED(ignored)) { - return warnings_filters_mutated_impl(module); + return warnings_filters_mutated_lock_held_impl(module); } -/*[clinic end generated code: output=ed02c0f521a03a37 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d9d32a8b59a30683 input=a9049054013a1b77]*/ From b5ee0258bf5bb60a5a5a65c64717853e06b64808 Mon Sep 17 00:00:00 2001 From: mpage Date: Tue, 14 Jan 2025 11:56:11 -0800 Subject: [PATCH 313/376] gh-115999: Specialize `LOAD_ATTR` for instance and class receivers in free-threaded builds (#128164) Finish specialization for LOAD_ATTR in the free-threaded build by adding support for class and instance receivers. --- Include/cpython/pystats.h | 2 +- Include/internal/pycore_dict.h | 10 + Include/internal/pycore_opcode_metadata.h | 2 +- Include/internal/pycore_uop_metadata.h | 2 +- Lib/test/test_capi/test_type.py | 5 +- Lib/test/test_descr.py | 32 ++- Lib/test/test_generated_cases.py | 74 ++++--- Lib/test/test_opcache.py | 90 +++++++- Objects/dictobject.c | 67 ++++-- Python/bytecodes.c | 85 +++++--- Python/executor_cases.c.h | 141 +++++++++---- Python/generated_cases.c.h | 81 +++++--- Python/optimizer_bytecodes.c | 8 +- Python/optimizer_cases.c.h | 19 +- Python/specialize.c | 229 +++++++++++++-------- Tools/cases_generator/analyzer.py | 2 +- Tools/cases_generator/generators_common.py | 15 +- Tools/cases_generator/stack.py | 4 - 18 files changed, 608 insertions(+), 260 deletions(-) diff --git a/Include/cpython/pystats.h b/Include/cpython/pystats.h index 29ef0c0e4d4e72..ee8885cda7b60d 100644 --- a/Include/cpython/pystats.h +++ b/Include/cpython/pystats.h @@ -31,7 +31,7 @@ #define PYSTATS_MAX_UOP_ID 512 -#define SPECIALIZATION_FAILURE_KINDS 36 +#define SPECIALIZATION_FAILURE_KINDS 37 /* Stats for determining who is calling PyEval_EvalFrame */ #define EVAL_CALL_TOTAL 0 diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index 71927006d1cd48..74ac8f2148174c 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -114,6 +114,16 @@ extern Py_ssize_t _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject extern Py_ssize_t _PyDict_LookupIndex(PyDictObject *, PyObject *); extern Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject *key); + +/* Look up a string key in an all unicode dict keys, assign the keys object a version, and + * store it in version. + * + * Returns DKIX_ERROR if key is not a string or if the keys object is not all + * strings. + * + * Returns DKIX_EMPTY if the key is not present. + */ +extern Py_ssize_t _PyDictKeys_StringLookupAndVersion(PyDictKeysObject* dictkeys, PyObject *key, uint32_t *version); extern Py_ssize_t _PyDictKeys_StringLookupSplit(PyDictKeysObject* dictkeys, PyObject *key); PyAPI_FUNC(PyObject *)_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *); PyAPI_FUNC(void) _PyDict_LoadGlobalStackRef(PyDictObject *, PyDictObject *, PyObject *, _PyStackRef *); diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 5365e2a5c6b8cd..0c0a6145bdbb27 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1540,7 +1540,7 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) { return 0; } case LOAD_ATTR_WITH_HINT: { - *effect = Py_MAX(0, (oparg & 1)); + *effect = Py_MAX(1, (oparg & 1)); return 0; } case LOAD_BUILD_CLASS: { diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 721fa94da19615..5670fe26f72071 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -862,7 +862,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _CHECK_ATTR_WITH_HINT: return 0; case _LOAD_ATTR_WITH_HINT: - return 1; + return 2; case _LOAD_ATTR_SLOT_0: return 1; case _LOAD_ATTR_SLOT_1: diff --git a/Lib/test/test_capi/test_type.py b/Lib/test/test_capi/test_type.py index 54c83e09f892a0..92d056e802eeed 100644 --- a/Lib/test/test_capi/test_type.py +++ b/Lib/test/test_capi/test_type.py @@ -1,4 +1,4 @@ -from test.support import import_helper +from test.support import import_helper, Py_GIL_DISABLED, refleak_helper import unittest _testcapi = import_helper.import_module('_testcapi') @@ -37,6 +37,9 @@ class D(A, C): pass # as well type_freeze(D) + @unittest.skipIf( + Py_GIL_DISABLED and refleak_helper.hunting_for_refleaks(), + "Specialization failure triggers gh-127773") def test_freeze_meta(self): """test PyType_Freeze() with overridden MRO""" type_freeze = _testcapi.type_freeze diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 51f97bb51f7bd2..a7ebc9e8be0294 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -7,6 +7,7 @@ import random import string import sys +import textwrap import types import unittest import warnings @@ -15,6 +16,7 @@ from copy import deepcopy from contextlib import redirect_stdout from test import support +from test.support.script_helper import assert_python_ok try: import _testcapi @@ -5222,6 +5224,7 @@ def test_type_lookup_mro_reference(self): # Issue #14199: _PyType_Lookup() has to keep a strong reference to # the type MRO because it may be modified during the lookup, if # __bases__ is set during the lookup for example. + code = textwrap.dedent(""" class MyKey(object): def __hash__(self): return hash('mykey') @@ -5237,12 +5240,29 @@ class Base2(object): mykey = 'from Base2' mykey2 = 'from Base2' - with self.assertWarnsRegex(RuntimeWarning, 'X'): - X = type('X', (Base,), {MyKey(): 5}) - # mykey is read from Base - self.assertEqual(X.mykey, 'from Base') - # mykey2 is read from Base2 because MyKey.__eq__ has set __bases__ - self.assertEqual(X.mykey2, 'from Base2') + X = type('X', (Base,), {MyKey(): 5}) + + bases_before = ",".join([c.__name__ for c in X.__bases__]) + print(f"before={bases_before}") + + # mykey is initially read from Base, however, the lookup will be perfomed + # again if specialization fails. The second lookup will use the new + # mro set by __eq__. + print(X.mykey) + + bases_after = ",".join([c.__name__ for c in X.__bases__]) + print(f"after={bases_after}") + + # mykey2 is read from Base2 because MyKey.__eq__ has set __bases_ + print(f"mykey2={X.mykey2}") + """) + _, out, err = assert_python_ok("-c", code) + err = err.decode() + self.assertRegex(err, "RuntimeWarning: .*X") + out = out.decode() + self.assertRegex(out, "before=Base") + self.assertRegex(out, "after=Base2") + self.assertRegex(out, "mykey2=from Base2") class PicklingTests(unittest.TestCase): diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 4a1c99edacb71a..7a50a29bb0126c 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -1639,12 +1639,16 @@ def test_escaping_call_next_to_cmacro(self): """ self.run_cases_test(input, output) - def test_pop_dead_inputs_all_live(self): + def test_pystackref_frompyobject_new_next_to_cmacro(self): input = """ - inst(OP, (a, b --)) { - POP_DEAD_INPUTS(); - HAM(a, b); - INPUTS_DEAD(); + inst(OP, (-- out1, out2)) { + PyObject *obj = SPAM(); + #ifdef Py_GIL_DISABLED + out1 = PyStackRef_FromPyObjectNew(obj); + #else + out1 = PyStackRef_FromPyObjectNew(obj); + #endif + out2 = PyStackRef_FromPyObjectNew(obj); } """ output = """ @@ -1652,22 +1656,28 @@ def test_pop_dead_inputs_all_live(self): frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); - _PyStackRef a; - _PyStackRef b; - b = stack_pointer[-1]; - a = stack_pointer[-2]; - HAM(a, b); - stack_pointer += -2; + _PyStackRef out1; + _PyStackRef out2; + PyObject *obj = SPAM(); + #ifdef Py_GIL_DISABLED + out1 = PyStackRef_FromPyObjectNew(obj); + #else + out1 = PyStackRef_FromPyObjectNew(obj); + #endif + out2 = PyStackRef_FromPyObjectNew(obj); + stack_pointer[0] = out1; + stack_pointer[1] = out2; + stack_pointer += 2; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } """ self.run_cases_test(input, output) - def test_pop_dead_inputs_some_live(self): + def test_pop_input(self): input = """ - inst(OP, (a, b, c --)) { - POP_DEAD_INPUTS(); + inst(OP, (a, b --)) { + POP_INPUT(b); HAM(a); INPUTS_DEAD(); } @@ -1678,8 +1688,10 @@ def test_pop_dead_inputs_some_live(self): next_instr += 1; INSTRUCTION_STATS(OP); _PyStackRef a; - a = stack_pointer[-3]; - stack_pointer += -2; + _PyStackRef b; + b = stack_pointer[-1]; + a = stack_pointer[-2]; + stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); HAM(a); stack_pointer += -1; @@ -1689,29 +1701,23 @@ def test_pop_dead_inputs_some_live(self): """ self.run_cases_test(input, output) - def test_pop_dead_inputs_with_output(self): + def test_pop_input_with_empty_stack(self): input = """ - inst(OP, (a, b -- c)) { - POP_DEAD_INPUTS(); - c = SPAM(); + inst(OP, (--)) { + POP_INPUT(foo); } """ - output = """ - TARGET(OP) { - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(OP); - _PyStackRef c; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - c = SPAM(); - stack_pointer[0] = c; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - DISPATCH(); + with self.assertRaises(SyntaxError): + self.run_cases_test(input, "") + + def test_pop_input_with_non_tos(self): + input = """ + inst(OP, (a, b --)) { + POP_INPUT(a); } """ - self.run_cases_test(input, output) + with self.assertRaises(SyntaxError): + self.run_cases_test(input, "") def test_no_escaping_calls_in_branching_macros(self): diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index c7cd4c2e8a3146..b80ccbf17f1ee6 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -564,6 +564,16 @@ def instantiate(): instantiate() +def make_deferred_ref_count_obj(): + """Create an object that uses deferred reference counting. + + Only objects that use deferred refence counting may be stored in inline + caches in free-threaded builds. This constructs a new class named Foo, + which uses deferred reference counting. + """ + return type("Foo", (object,), {}) + + @threading_helper.requires_working_threading() class TestRacesDoNotCrash(TestBase): # Careful with these. Bigger numbers have a higher chance of catching bugs, @@ -714,11 +724,11 @@ def write(items): opname = "FOR_ITER_LIST" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization + @requires_specialization_ft def test_load_attr_class(self): def get_items(): class C: - a = object() + a = make_deferred_ref_count_obj() items = [] for _ in range(self.ITEMS): @@ -739,12 +749,45 @@ def write(items): del item.a except AttributeError: pass - item.a = object() + item.a = make_deferred_ref_count_obj() opname = "LOAD_ATTR_CLASS" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization + @requires_specialization_ft + def test_load_attr_class_with_metaclass_check(self): + def get_items(): + class Meta(type): + pass + + class C(metaclass=Meta): + a = make_deferred_ref_count_obj() + + items = [] + for _ in range(self.ITEMS): + item = C + items.append(item) + return items + + def read(items): + for item in items: + try: + item.a + except AttributeError: + pass + + def write(items): + for item in items: + try: + del item.a + except AttributeError: + pass + item.a = make_deferred_ref_count_obj() + + opname = "LOAD_ATTR_CLASS_WITH_METACLASS_CHECK" + self.assert_races_do_not_crash(opname, get_items, read, write) + + @requires_specialization_ft def test_load_attr_getattribute_overridden(self): def get_items(): class C: @@ -774,7 +817,7 @@ def write(items): opname = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization + @requires_specialization_ft def test_load_attr_instance_value(self): def get_items(): class C: @@ -798,7 +841,7 @@ def write(items): opname = "LOAD_ATTR_INSTANCE_VALUE" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization + @requires_specialization_ft def test_load_attr_method_lazy_dict(self): def get_items(): class C(Exception): @@ -828,7 +871,7 @@ def write(items): opname = "LOAD_ATTR_METHOD_LAZY_DICT" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization + @requires_specialization_ft def test_load_attr_method_no_dict(self): def get_items(): class C: @@ -859,7 +902,7 @@ def write(items): opname = "LOAD_ATTR_METHOD_NO_DICT" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization + @requires_specialization_ft def test_load_attr_method_with_values(self): def get_items(): class C: @@ -914,7 +957,7 @@ def write(items): opname = "LOAD_ATTR_MODULE" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization + @requires_specialization_ft def test_load_attr_property(self): def get_items(): class C: @@ -944,7 +987,34 @@ def write(items): opname = "LOAD_ATTR_PROPERTY" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization + @requires_specialization_ft + def test_load_attr_slot(self): + def get_items(): + class C: + __slots__ = ["a", "b"] + + items = [] + for i in range(self.ITEMS): + item = C() + item.a = i + item.b = i + self.ITEMS + items.append(item) + return items + + def read(items): + for item in items: + item.a + item.b + + def write(items): + for item in items: + item.a = 100 + item.b = 200 + + opname = "LOAD_ATTR_SLOT" + self.assert_races_do_not_crash(opname, get_items, read, write) + + @requires_specialization_ft def test_load_attr_with_hint(self): def get_items(): class C: diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 2a054c3f2ae0ff..82789d5e56f523 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1129,6 +1129,24 @@ dictkeys_generic_lookup(PyDictObject *mp, PyDictKeysObject* dk, PyObject *key, P return do_lookup(mp, dk, key, hash, compare_generic); } +static bool +check_keys_unicode(PyDictKeysObject *dk, PyObject *key) +{ + return PyUnicode_CheckExact(key) && (dk->dk_kind != DICT_KEYS_GENERAL); +} + +static Py_ssize_t +hash_unicode_key(PyObject *key) +{ + assert(PyUnicode_CheckExact(key)); + Py_hash_t hash = unicode_get_hash(key); + if (hash == -1) { + hash = PyUnicode_Type.tp_hash(key); + assert(hash != -1); + } + return hash; +} + #ifdef Py_GIL_DISABLED static Py_ssize_t unicodekeys_lookup_unicode_threadsafe(PyDictKeysObject* dk, PyObject *key, @@ -1167,21 +1185,28 @@ unicodekeys_lookup_split(PyDictKeysObject* dk, PyObject *key, Py_hash_t hash) Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dk, PyObject *key) { - DictKeysKind kind = dk->dk_kind; - if (!PyUnicode_CheckExact(key) || kind == DICT_KEYS_GENERAL) { + if (!check_keys_unicode(dk, key)) { return DKIX_ERROR; } - Py_hash_t hash = unicode_get_hash(key); - if (hash == -1) { - hash = PyUnicode_Type.tp_hash(key); - if (hash == -1) { - PyErr_Clear(); - return DKIX_ERROR; - } - } + Py_hash_t hash = hash_unicode_key(key); return unicodekeys_lookup_unicode(dk, key, hash); } +Py_ssize_t +_PyDictKeys_StringLookupAndVersion(PyDictKeysObject *dk, PyObject *key, uint32_t *version) +{ + if (!check_keys_unicode(dk, key)) { + return DKIX_ERROR; + } + Py_ssize_t ix; + Py_hash_t hash = hash_unicode_key(key); + LOCK_KEYS(dk); + ix = unicodekeys_lookup_unicode(dk, key, hash); + *version = _PyDictKeys_GetVersionForCurrentState(_PyInterpreterState_GET(), dk); + UNLOCK_KEYS(dk); + return ix; +} + /* Like _PyDictKeys_StringLookup() but only works on split keys. Note * that in free-threaded builds this locks the keys object as required. */ @@ -1926,6 +1951,16 @@ build_indices_unicode(PyDictKeysObject *keys, PyDictUnicodeEntry *ep, Py_ssize_t } } +static void +invalidate_and_clear_inline_values(PyDictValues *values) +{ + assert(values->embedded); + FT_ATOMIC_STORE_UINT8(values->valid, 0); + for (int i = 0; i < values->capacity; i++) { + FT_ATOMIC_STORE_PTR_RELEASE(values->values[i], NULL); + } +} + /* Restructure the table by allocating a new table and reinserting all items again. When entries have been deleted, the new table may @@ -2017,7 +2052,7 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp, if (oldvalues->embedded) { assert(oldvalues->embedded == 1); assert(oldvalues->valid == 1); - FT_ATOMIC_STORE_UINT8(oldvalues->valid, 0); + invalidate_and_clear_inline_values(oldvalues); } else { free_values(oldvalues, IS_DICT_SHARED(mp)); @@ -7007,7 +7042,13 @@ _PyObject_TryGetInstanceAttribute(PyObject *obj, PyObject *name, PyObject **attr #ifdef Py_GIL_DISABLED PyObject *value = _Py_atomic_load_ptr_acquire(&values->values[ix]); - if (value == NULL || _Py_TryIncrefCompare(&values->values[ix], value)) { + if (value == NULL) { + if (FT_ATOMIC_LOAD_UINT8(values->valid)) { + *attr = NULL; + return true; + } + } + else if (_Py_TryIncrefCompare(&values->values[ix], value)) { *attr = value; return true; } @@ -7345,7 +7386,7 @@ _PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj) } mp->ma_values = values; - FT_ATOMIC_STORE_UINT8(_PyObject_InlineValues(obj)->valid, 0); + invalidate_and_clear_inline_values(_PyObject_InlineValues(obj)); assert(_PyObject_InlineValuesConsistencyCheck(obj)); ASSERT_CONSISTENT(mp); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index cec530fefffefb..a906ded365650c 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2190,18 +2190,23 @@ dummy_func( PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - DEOPT_IF(!_PyObject_InlineValues(owner_o)->valid); + DEOPT_IF(!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)); } split op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr, null if (oparg & 1))) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); - PyObject *attr_o = *value_ptr; + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); DEOPT_IF(attr_o == NULL); + #ifdef Py_GIL_DISABLED + if (!_Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr)) { + DEOPT_IF(true); + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif STAT_INC(LOAD_ATTR, hit); - Py_INCREF(attr_o); null = PyStackRef_NULL; - attr = PyStackRef_FromPyObjectSteal(attr_o); DECREF_INPUTS(); } @@ -2227,9 +2232,8 @@ dummy_func( assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(mod_keys->dk_nentries)); PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(mod_keys) + index; PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value); - DEAD(mod_keys); // Clear mod_keys from stack in case we need to deopt - POP_DEAD_INPUTS(); + POP_INPUT(mod_keys); DEOPT_IF(attr_o == NULL); #ifdef Py_GIL_DISABLED int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr); @@ -2251,30 +2255,50 @@ dummy_func( _LOAD_ATTR_MODULE_FROM_KEYS + unused/5; - op(_CHECK_ATTR_WITH_HINT, (owner -- owner)) { + op(_CHECK_ATTR_WITH_HINT, (owner -- owner, dict: PyDictObject *)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictObject *dict = _PyObject_GetManagedDict(owner_o); - EXIT_IF(dict == NULL); - assert(PyDict_CheckExact((PyObject *)dict)); + PyDictObject *dict_o = _PyObject_GetManagedDict(owner_o); + EXIT_IF(dict_o == NULL); + assert(PyDict_CheckExact((PyObject *)dict_o)); + dict = dict_o; } - op(_LOAD_ATTR_WITH_HINT, (hint/1, owner -- attr, null if (oparg & 1))) { - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + op(_LOAD_ATTR_WITH_HINT, (hint/1, owner, dict: PyDictObject * -- attr, null if (oparg & 1))) { PyObject *attr_o; + if (!LOCK_OBJECT(dict)) { + POP_INPUT(dict); + DEOPT_IF(true); + } - PyDictObject *dict = _PyObject_GetManagedDict(owner_o); - DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries); + if (hint >= (size_t)dict->ma_keys->dk_nentries) { + UNLOCK_OBJECT(dict); + POP_INPUT(dict); + DEOPT_IF(true); + } PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); - DEOPT_IF(!DK_IS_UNICODE(dict->ma_keys)); + if (dict->ma_keys->dk_kind != DICT_KEYS_UNICODE) { + UNLOCK_OBJECT(dict); + POP_INPUT(dict); + DEOPT_IF(true); + } PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; - DEOPT_IF(ep->me_key != name); + if (ep->me_key != name) { + UNLOCK_OBJECT(dict); + POP_INPUT(dict); + DEOPT_IF(true); + } attr_o = ep->me_value; - DEOPT_IF(attr_o == NULL); + if (attr_o == NULL) { + UNLOCK_OBJECT(dict); + POP_INPUT(dict); + DEOPT_IF(true); + } STAT_INC(LOAD_ATTR, hit); - Py_INCREF(attr_o); - attr = PyStackRef_FromPyObjectSteal(attr_o); + attr = PyStackRef_FromPyObjectNew(attr_o); + UNLOCK_OBJECT(dict); + DEAD(dict); null = PyStackRef_NULL; DECREF_INPUTS(); } @@ -2289,12 +2313,17 @@ dummy_func( split op(_LOAD_ATTR_SLOT, (index/1, owner -- attr, null if (oparg & 1))) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - char *addr = (char *)owner_o + index; - PyObject *attr_o = *(PyObject **)addr; + PyObject **addr = (PyObject **)((char *)owner_o + index); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR(*addr); DEOPT_IF(attr_o == NULL); + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(addr, attr_o, &attr); + DEOPT_IF(!increfed); + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif STAT_INC(LOAD_ATTR, hit); null = PyStackRef_NULL; - attr = PyStackRef_FromPyObjectNew(attr_o); DECREF_INPUTS(); } @@ -2309,7 +2338,7 @@ dummy_func( EXIT_IF(!PyType_Check(owner_o)); assert(type_version != 0); - EXIT_IF(((PyTypeObject *)owner_o)->tp_version_tag != type_version); + EXIT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version); } split op(_LOAD_ATTR_CLASS, (descr/4, owner -- attr, null if (oparg & 1))) { @@ -2363,7 +2392,7 @@ dummy_func( DEOPT_IF(tstate->interp->eval_frame); PyTypeObject *cls = Py_TYPE(owner_o); assert(type_version != 0); - DEOPT_IF(cls->tp_version_tag != type_version); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(cls->tp_version_tag) != type_version); assert(Py_IS_TYPE(getattribute, &PyFunction_Type)); PyFunctionObject *f = (PyFunctionObject *)getattribute; assert(func_version != 0); @@ -3281,13 +3310,15 @@ dummy_func( op(_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, (owner -- owner)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - DEOPT_IF(!_PyObject_InlineValues(owner_o)->valid); + PyDictValues *ivs = _PyObject_InlineValues(owner_o); + DEOPT_IF(!FT_ATOMIC_LOAD_UINT8(ivs->valid)); } op(_GUARD_KEYS_VERSION, (keys_version/2, owner -- owner)) { PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version); + PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; + DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version); } split op(_LOAD_ATTR_METHOD_WITH_VALUES, (descr/4, owner -- attr, self if (1))) { @@ -3357,7 +3388,7 @@ dummy_func( op(_CHECK_ATTR_METHOD_LAZY_DICT, (dictoffset/1, owner -- owner)) { char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset; - PyObject *dict = *(PyObject **)ptr; + PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr); /* This object has a __dict__, just not yet created */ DEOPT_IF(dict != NULL); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 1aa80f398d7470..cda01bb768c269 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2652,7 +2652,7 @@ PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (!_PyObject_InlineValues(owner_o)->valid) { + if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } @@ -2668,15 +2668,23 @@ uint16_t offset = (uint16_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); - PyObject *attr_o = *value_ptr; + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); if (attr_o == NULL) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } + #ifdef Py_GIL_DISABLED + if (!_Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr)) { + if (true) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif STAT_INC(LOAD_ATTR, hit); - Py_INCREF(attr_o); null = PyStackRef_NULL; - attr = PyStackRef_FromPyObjectSteal(attr_o); PyStackRef_CLOSE(owner); stack_pointer[-1] = attr; break; @@ -2691,15 +2699,23 @@ uint16_t offset = (uint16_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); - PyObject *attr_o = *value_ptr; + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); if (attr_o == NULL) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } + #ifdef Py_GIL_DISABLED + if (!_Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr)) { + if (true) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif STAT_INC(LOAD_ATTR, hit); - Py_INCREF(attr_o); null = PyStackRef_NULL; - attr = PyStackRef_FromPyObjectSteal(attr_o); PyStackRef_CLOSE(owner); stack_pointer[-1] = attr; stack_pointer[0] = null; @@ -2778,55 +2794,88 @@ case _CHECK_ATTR_WITH_HINT: { _PyStackRef owner; + PyDictObject *dict; owner = stack_pointer[-1]; PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictObject *dict = _PyObject_GetManagedDict(owner_o); - if (dict == NULL) { + PyDictObject *dict_o = _PyObject_GetManagedDict(owner_o); + if (dict_o == NULL) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - assert(PyDict_CheckExact((PyObject *)dict)); + assert(PyDict_CheckExact((PyObject *)dict_o)); + dict = dict_o; + stack_pointer[0].bits = (uintptr_t)dict; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); break; } case _LOAD_ATTR_WITH_HINT: { + PyDictObject *dict; _PyStackRef owner; _PyStackRef attr; _PyStackRef null = PyStackRef_NULL; oparg = CURRENT_OPARG(); - owner = stack_pointer[-1]; + dict = (PyDictObject *)stack_pointer[-1].bits; + owner = stack_pointer[-2]; uint16_t hint = (uint16_t)CURRENT_OPERAND0(); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject *attr_o; - PyDictObject *dict = _PyObject_GetManagedDict(owner_o); + if (!LOCK_OBJECT(dict)) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + if (true) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + } if (hint >= (size_t)dict->ma_keys->dk_nentries) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + UNLOCK_OBJECT(dict); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + if (true) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } } PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); - if (!DK_IS_UNICODE(dict->ma_keys)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + if (dict->ma_keys->dk_kind != DICT_KEYS_UNICODE) { + UNLOCK_OBJECT(dict); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + if (true) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } } PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; if (ep->me_key != name) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + UNLOCK_OBJECT(dict); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + if (true) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } } attr_o = ep->me_value; if (attr_o == NULL) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + UNLOCK_OBJECT(dict); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + if (true) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } } STAT_INC(LOAD_ATTR, hit); - Py_INCREF(attr_o); - attr = PyStackRef_FromPyObjectSteal(attr_o); + attr = PyStackRef_FromPyObjectNew(attr_o); + UNLOCK_OBJECT(dict); null = PyStackRef_NULL; PyStackRef_CLOSE(owner); - stack_pointer[-1] = attr; - if (oparg & 1) stack_pointer[0] = null; - stack_pointer += (oparg & 1); + stack_pointer[-2] = attr; + if (oparg & 1) stack_pointer[-1] = null; + stack_pointer += -1 + (oparg & 1); assert(WITHIN_STACK_BOUNDS()); break; } @@ -2839,15 +2888,23 @@ owner = stack_pointer[-1]; uint16_t index = (uint16_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - char *addr = (char *)owner_o + index; - PyObject *attr_o = *(PyObject **)addr; + PyObject **addr = (PyObject **)((char *)owner_o + index); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR(*addr); if (attr_o == NULL) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(addr, attr_o, &attr); + if (!increfed) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif STAT_INC(LOAD_ATTR, hit); null = PyStackRef_NULL; - attr = PyStackRef_FromPyObjectNew(attr_o); PyStackRef_CLOSE(owner); stack_pointer[-1] = attr; break; @@ -2861,15 +2918,23 @@ owner = stack_pointer[-1]; uint16_t index = (uint16_t)CURRENT_OPERAND0(); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - char *addr = (char *)owner_o + index; - PyObject *attr_o = *(PyObject **)addr; + PyObject **addr = (PyObject **)((char *)owner_o + index); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR(*addr); if (attr_o == NULL) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(addr, attr_o, &attr); + if (!increfed) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif STAT_INC(LOAD_ATTR, hit); null = PyStackRef_NULL; - attr = PyStackRef_FromPyObjectNew(attr_o); PyStackRef_CLOSE(owner); stack_pointer[-1] = attr; stack_pointer[0] = null; @@ -2890,7 +2955,7 @@ JUMP_TO_JUMP_TARGET(); } assert(type_version != 0); - if (((PyTypeObject *)owner_o)->tp_version_tag != type_version) { + if (FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } @@ -3924,7 +3989,8 @@ owner = stack_pointer[-1]; PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (!_PyObject_InlineValues(owner_o)->valid) { + PyDictValues *ivs = _PyObject_InlineValues(owner_o); + if (!FT_ATOMIC_LOAD_UINT8(ivs->valid)) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } @@ -3937,7 +4003,8 @@ uint32_t keys_version = (uint32_t)CURRENT_OPERAND0(); PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - if (owner_heap_type->ht_cached_keys->dk_version != keys_version) { + PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } @@ -4022,7 +4089,7 @@ owner = stack_pointer[-1]; uint16_t dictoffset = (uint16_t)CURRENT_OPERAND0(); char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset; - PyObject *dict = *(PyObject **)ptr; + PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr); /* This object has a __dict__, just not yet created */ if (dict != NULL) { UOP_STAT_INC(uopcode, miss); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 810beb61d0db5e..81408380d6b2b8 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -5345,7 +5345,7 @@ PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); DEOPT_IF(!PyType_Check(owner_o), LOAD_ATTR); assert(type_version != 0); - DEOPT_IF(((PyTypeObject *)owner_o)->tp_version_tag != type_version, LOAD_ATTR); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version, LOAD_ATTR); } /* Skip 2 cache entries */ // _LOAD_ATTR_CLASS @@ -5380,7 +5380,7 @@ PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); DEOPT_IF(!PyType_Check(owner_o), LOAD_ATTR); assert(type_version != 0); - DEOPT_IF(((PyTypeObject *)owner_o)->tp_version_tag != type_version, LOAD_ATTR); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version, LOAD_ATTR); } // _GUARD_TYPE_VERSION { @@ -5421,7 +5421,7 @@ DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); PyTypeObject *cls = Py_TYPE(owner_o); assert(type_version != 0); - DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR); + DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(cls->tp_version_tag) != type_version, LOAD_ATTR); assert(Py_IS_TYPE(getattribute, &PyFunction_Type)); PyFunctionObject *f = (PyFunctionObject *)getattribute; assert(func_version != 0); @@ -5463,19 +5463,24 @@ PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - DEOPT_IF(!_PyObject_InlineValues(owner_o)->valid, LOAD_ATTR); + DEOPT_IF(!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid), LOAD_ATTR); } // _LOAD_ATTR_INSTANCE_VALUE { uint16_t offset = read_u16(&this_instr[4].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); - PyObject *attr_o = *value_ptr; + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); DEOPT_IF(attr_o == NULL, LOAD_ATTR); + #ifdef Py_GIL_DISABLED + if (!_Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr)) { + DEOPT_IF(true, LOAD_ATTR); + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif STAT_INC(LOAD_ATTR, hit); - Py_INCREF(attr_o); null = PyStackRef_NULL; - attr = PyStackRef_FromPyObjectSteal(attr_o); PyStackRef_CLOSE(owner); } /* Skip 5 cache entries */ @@ -5507,7 +5512,7 @@ { uint16_t dictoffset = read_u16(&this_instr[4].cache); char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset; - PyObject *dict = *(PyObject **)ptr; + PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr); /* This object has a __dict__, just not yet created */ DEOPT_IF(dict != NULL, LOAD_ATTR); } @@ -5586,14 +5591,16 @@ { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - DEOPT_IF(!_PyObject_InlineValues(owner_o)->valid, LOAD_ATTR); + PyDictValues *ivs = _PyObject_InlineValues(owner_o); + DEOPT_IF(!FT_ATOMIC_LOAD_UINT8(ivs->valid), LOAD_ATTR); } // _GUARD_KEYS_VERSION { uint32_t keys_version = read_u32(&this_instr[4].cache); PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version, LOAD_ATTR); + PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; + DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version, LOAD_ATTR); } // _LOAD_ATTR_METHOD_WITH_VALUES { @@ -5716,14 +5723,16 @@ { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - DEOPT_IF(!_PyObject_InlineValues(owner_o)->valid, LOAD_ATTR); + PyDictValues *ivs = _PyObject_InlineValues(owner_o); + DEOPT_IF(!FT_ATOMIC_LOAD_UINT8(ivs->valid), LOAD_ATTR); } // _GUARD_KEYS_VERSION { uint32_t keys_version = read_u32(&this_instr[4].cache); PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version, LOAD_ATTR); + PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; + DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version, LOAD_ATTR); } // _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES { @@ -5824,12 +5833,17 @@ { uint16_t index = read_u16(&this_instr[4].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - char *addr = (char *)owner_o + index; - PyObject *attr_o = *(PyObject **)addr; + PyObject **addr = (PyObject **)((char *)owner_o + index); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR(*addr); DEOPT_IF(attr_o == NULL, LOAD_ATTR); + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(addr, attr_o, &attr); + DEOPT_IF(!increfed, LOAD_ATTR); + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif STAT_INC(LOAD_ATTR, hit); null = PyStackRef_NULL; - attr = PyStackRef_FromPyObjectNew(attr_o); PyStackRef_CLOSE(owner); } /* Skip 5 cache entries */ @@ -5846,6 +5860,7 @@ INSTRUCTION_STATS(LOAD_ATTR_WITH_HINT); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); _PyStackRef owner; + PyDictObject *dict; _PyStackRef attr; _PyStackRef null = PyStackRef_NULL; /* Skip 1 cache entry */ @@ -5861,26 +5876,40 @@ { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictObject *dict = _PyObject_GetManagedDict(owner_o); - DEOPT_IF(dict == NULL, LOAD_ATTR); - assert(PyDict_CheckExact((PyObject *)dict)); + PyDictObject *dict_o = _PyObject_GetManagedDict(owner_o); + DEOPT_IF(dict_o == NULL, LOAD_ATTR); + assert(PyDict_CheckExact((PyObject *)dict_o)); + dict = dict_o; } // _LOAD_ATTR_WITH_HINT { uint16_t hint = read_u16(&this_instr[4].cache); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject *attr_o; - PyDictObject *dict = _PyObject_GetManagedDict(owner_o); - DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR); + if (!LOCK_OBJECT(dict)) { + DEOPT_IF(true, LOAD_ATTR); + } + if (hint >= (size_t)dict->ma_keys->dk_nentries) { + UNLOCK_OBJECT(dict); + DEOPT_IF(true, LOAD_ATTR); + } PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); - DEOPT_IF(!DK_IS_UNICODE(dict->ma_keys), LOAD_ATTR); + if (dict->ma_keys->dk_kind != DICT_KEYS_UNICODE) { + UNLOCK_OBJECT(dict); + DEOPT_IF(true, LOAD_ATTR); + } PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; - DEOPT_IF(ep->me_key != name, LOAD_ATTR); + if (ep->me_key != name) { + UNLOCK_OBJECT(dict); + DEOPT_IF(true, LOAD_ATTR); + } attr_o = ep->me_value; - DEOPT_IF(attr_o == NULL, LOAD_ATTR); + if (attr_o == NULL) { + UNLOCK_OBJECT(dict); + DEOPT_IF(true, LOAD_ATTR); + } STAT_INC(LOAD_ATTR, hit); - Py_INCREF(attr_o); - attr = PyStackRef_FromPyObjectSteal(attr_o); + attr = PyStackRef_FromPyObjectNew(attr_o); + UNLOCK_OBJECT(dict); null = PyStackRef_NULL; PyStackRef_CLOSE(owner); } diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 788adecca8af80..4d96ada5acf00f 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -582,11 +582,17 @@ dummy_func(void) { } } - op(_LOAD_ATTR_WITH_HINT, (hint/1, owner -- attr, null if (oparg & 1))) { + op(_CHECK_ATTR_WITH_HINT, (owner -- owner, dict)) { + dict = sym_new_not_null(ctx); + (void)owner; + } + + op(_LOAD_ATTR_WITH_HINT, (hint/1, owner, dict -- attr, null if (oparg & 1))) { attr = sym_new_not_null(ctx); null = sym_new_null(ctx); (void)hint; (void)owner; + (void)dict; } op(_LOAD_ATTR_SLOT, (index/1, owner -- attr, null if (oparg & 1))) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 1a7cc6becfefb6..aff4493fdc4dd7 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1250,22 +1250,33 @@ } case _CHECK_ATTR_WITH_HINT: { + _Py_UopsSymbol *owner; + _Py_UopsSymbol *dict; + owner = stack_pointer[-1]; + dict = sym_new_not_null(ctx); + (void)owner; + stack_pointer[0] = dict; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); break; } case _LOAD_ATTR_WITH_HINT: { + _Py_UopsSymbol *dict; _Py_UopsSymbol *owner; _Py_UopsSymbol *attr; _Py_UopsSymbol *null = NULL; - owner = stack_pointer[-1]; + dict = stack_pointer[-1]; + owner = stack_pointer[-2]; uint16_t hint = (uint16_t)this_instr->operand0; attr = sym_new_not_null(ctx); null = sym_new_null(ctx); (void)hint; (void)owner; - stack_pointer[-1] = attr; - if (oparg & 1) stack_pointer[0] = null; - stack_pointer += (oparg & 1); + (void)dict; + stack_pointer[-2] = attr; + if (oparg & 1) stack_pointer[-1] = null; + stack_pointer += -1 + (oparg & 1); assert(WITHIN_STACK_BOUNDS()); break; } diff --git a/Python/specialize.c b/Python/specialize.c index 897005c4f1078d..8d9f19c8895187 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -3,6 +3,7 @@ #include "opcode.h" #include "pycore_code.h" +#include "pycore_critical_section.h" #include "pycore_descrobject.h" // _PyMethodWrapper_Type #include "pycore_dict.h" // DICT_KEYS_UNICODE #include "pycore_function.h" // _PyFunction_GetVersionForCurrentState() @@ -537,6 +538,7 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, PyObject *consts, #define SPEC_FAIL_ATTR_BUILTIN_CLASS_METHOD_OBJ 33 #define SPEC_FAIL_ATTR_METACLASS_OVERRIDDEN 34 #define SPEC_FAIL_ATTR_SPLIT_DICT 35 +#define SPEC_FAIL_ATTR_DESCR_NOT_DEFERRED 36 /* Binary subscr and store subscr */ @@ -729,11 +731,8 @@ unspecialize(_Py_CODEUNIT *instr) } static int function_kind(PyCodeObject *code); -#ifndef Py_GIL_DISABLED static bool function_check_args(PyObject *o, int expected_argcount, int opcode); static uint32_t function_get_version(PyObject *o, int opcode); -static uint32_t type_get_version(PyTypeObject *t, int opcode); -#endif static int specialize_module_load_attr_lock_held(PyDictObject *dict, _Py_CODEUNIT *instr, PyObject *name) @@ -879,10 +878,11 @@ descriptor_is_class(PyObject *descriptor, PyObject *name) (descriptor == _PyType_Lookup(&PyBaseObject_Type, name))); } -#ifndef Py_GIL_DISABLED static DescriptorClassification -analyze_descriptor_load(PyTypeObject *type, PyObject *name, PyObject **descr) { +analyze_descriptor_load(PyTypeObject *type, PyObject *name, PyObject **descr, unsigned int *tp_version) { bool has_getattr = false; + bool have_ga_version = false; + unsigned int ga_version; getattrofunc getattro_slot = type->tp_getattro; if (getattro_slot == PyObject_GenericGetAttr) { /* Normal attribute lookup; */ @@ -892,24 +892,27 @@ analyze_descriptor_load(PyTypeObject *type, PyObject *name, PyObject **descr) { getattro_slot == _Py_slot_tp_getattro) { /* One or both of __getattribute__ or __getattr__ may have been overridden See typeobject.c for why these functions are special. */ - PyObject *getattribute = _PyType_LookupRef(type, &_Py_ID(__getattribute__)); + PyObject *getattribute = _PyType_LookupRefAndVersion(type, + &_Py_ID(__getattribute__), &ga_version); + have_ga_version = true; PyInterpreterState *interp = _PyInterpreterState_GET(); bool has_custom_getattribute = getattribute != NULL && getattribute != interp->callable_cache.object__getattribute__; - PyObject *getattr = _PyType_LookupRef(type, &_Py_ID(__getattr__)); + PyObject *getattr = _PyType_Lookup(type, &_Py_ID(__getattr__)); has_getattr = getattr != NULL; - Py_XDECREF(getattr); if (has_custom_getattribute) { if (getattro_slot == _Py_slot_tp_getattro && !has_getattr && Py_IS_TYPE(getattribute, &PyFunction_Type)) { *descr = getattribute; + *tp_version = ga_version; return GETATTRIBUTE_IS_PYTHON_FUNCTION; } /* Potentially both __getattr__ and __getattribute__ are set. Too complicated */ Py_DECREF(getattribute); *descr = NULL; + *tp_version = ga_version; return GETSET_OVERRIDDEN; } /* Potentially has __getattr__ but no custom __getattribute__. @@ -923,16 +926,18 @@ analyze_descriptor_load(PyTypeObject *type, PyObject *name, PyObject **descr) { } else { *descr = NULL; + *tp_version = FT_ATOMIC_LOAD_UINT_RELAXED(type->tp_version_tag); return GETSET_OVERRIDDEN; } - PyObject *descriptor = _PyType_LookupRef(type, name); + unsigned int descr_version; + PyObject *descriptor = _PyType_LookupRefAndVersion(type, name, &descr_version); *descr = descriptor; + *tp_version = have_ga_version ? ga_version : descr_version; if (descriptor_is_class(descriptor, name)) { return DUNDER_CLASS; } return classify_descriptor(descriptor, has_getattr); } -#endif //!Py_GIL_DISABLED static DescriptorClassification analyze_descriptor_store(PyTypeObject *type, PyObject *name, PyObject **descr, unsigned int *tp_version) @@ -952,12 +957,13 @@ analyze_descriptor_store(PyTypeObject *type, PyObject *name, PyObject **descr, u static int specialize_dict_access_inline( PyObject *owner, _Py_CODEUNIT *instr, PyTypeObject *type, - DescriptorClassification kind, PyObject *name, unsigned int tp_version, + PyObject *name, unsigned int tp_version, int base_op, int values_op) { _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); PyDictKeysObject *keys = ((PyHeapTypeObject *)type)->ht_cached_keys; assert(PyUnicode_CheckExact(name)); + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(owner); Py_ssize_t index = _PyDictKeys_StringLookupSplit(keys, name); assert (index != DKIX_ERROR); if (index == DKIX_EMPTY) { @@ -965,6 +971,7 @@ specialize_dict_access_inline( return 0; } assert(index >= 0); + assert(_PyObject_InlineValues(owner)->valid); char *value_addr = (char *)&_PyObject_InlineValues(owner)->values[index]; Py_ssize_t offset = value_addr - (char *)owner; if (offset != (uint16_t)offset) { @@ -980,10 +987,13 @@ specialize_dict_access_inline( static int specialize_dict_access_hint( PyDictObject *dict, _Py_CODEUNIT *instr, PyTypeObject *type, - DescriptorClassification kind, PyObject *name, unsigned int tp_version, + PyObject *name, unsigned int tp_version, int base_op, int hint_op) { _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); + + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(dict); + // We found an instance with a __dict__. if (_PyDict_HasSplitTable(dict)) { SPECIALIZATION_FAIL(base_op, SPEC_FAIL_ATTR_SPLIT_DICT); @@ -1027,7 +1037,7 @@ specialize_dict_access( PyDictObject *dict = _PyObject_GetManagedDict(owner); if (dict == NULL) { // managed dict, not materialized, inline values valid - res = specialize_dict_access_inline(owner, instr, type, kind, name, + res = specialize_dict_access_inline(owner, instr, type, name, tp_version, base_op, values_op); } else { @@ -1047,16 +1057,19 @@ specialize_dict_access( int res; Py_BEGIN_CRITICAL_SECTION(dict); // materialized managed dict - res = specialize_dict_access_hint(dict, instr, type, kind, name, + res = specialize_dict_access_hint(dict, instr, type, name, tp_version, base_op, hint_op); Py_END_CRITICAL_SECTION(); return res; } } -#ifndef Py_GIL_DISABLED -static int specialize_attr_loadclassattr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* name, - PyObject* descr, DescriptorClassification kind, bool is_method); +static int +specialize_attr_loadclassattr(PyObject *owner, _Py_CODEUNIT *instr, + PyObject *name, PyObject *descr, + unsigned int tp_version, + DescriptorClassification kind, bool is_method, + uint32_t shared_keys_version); static int specialize_class_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* name); /* Returns true if instances of obj's class are @@ -1065,7 +1078,7 @@ static int specialize_class_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyOb * For other objects, we check their actual dictionary. */ static bool -instance_has_key(PyObject *obj, PyObject* name) +instance_has_key(PyObject *obj, PyObject *name, uint32_t *shared_keys_version) { PyTypeObject *cls = Py_TYPE(obj); if ((cls->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { @@ -1073,36 +1086,38 @@ instance_has_key(PyObject *obj, PyObject* name) } if (cls->tp_flags & Py_TPFLAGS_INLINE_VALUES) { PyDictKeysObject *keys = ((PyHeapTypeObject *)cls)->ht_cached_keys; - Py_ssize_t index = _PyDictKeys_StringLookup(keys, name); + Py_ssize_t index = + _PyDictKeys_StringLookupAndVersion(keys, name, shared_keys_version); return index >= 0; } PyDictObject *dict = _PyObject_GetManagedDict(obj); if (dict == NULL || !PyDict_CheckExact(dict)) { return false; } + bool result; + Py_BEGIN_CRITICAL_SECTION(dict); if (dict->ma_values) { - return false; + result = false; } - Py_ssize_t index = _PyDict_LookupIndex(dict, name); - if (index < 0) { - return false; + else { + result = (_PyDict_LookupIndex(dict, name) >= 0); } - return true; + Py_END_CRITICAL_SECTION(); + return result; } static int -specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* name) +do_specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* name, + bool shadow, uint32_t shared_keys_version, + DescriptorClassification kind, PyObject *descr, unsigned int tp_version) { _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); PyTypeObject *type = Py_TYPE(owner); - bool shadow = instance_has_key(owner, name); - PyObject *descr = NULL; - DescriptorClassification kind = analyze_descriptor_load(type, name, &descr); - Py_XDECREF(descr); // turn strong ref into a borrowed ref - assert(descr != NULL || kind == ABSENT || kind == GETSET_OVERRIDDEN); - if (type_get_version(type, LOAD_ATTR) == 0) { + if (tp_version == 0) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS); return -1; } + uint8_t oparg = FT_ATOMIC_LOAD_UINT8_RELAXED(instr->op.arg); switch(kind) { case OVERRIDING: SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_OVERRIDING_DESCRIPTOR); @@ -1112,9 +1127,10 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na if (shadow) { goto try_instance; } - int oparg = instr->op.arg; if (oparg & 1) { - if (specialize_attr_loadclassattr(owner, instr, name, descr, kind, true)) { + if (specialize_attr_loadclassattr(owner, instr, name, descr, + tp_version, kind, true, + shared_keys_version)) { return 0; } else { @@ -1140,7 +1156,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na if (!function_check_args(fget, 1, LOAD_ATTR)) { return -1; } - if (instr->op.arg & 1) { + if (oparg & 1) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_METHOD); return -1; } @@ -1149,8 +1165,14 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER); return -1; } - assert(type->tp_version_tag != 0); - write_u32(lm_cache->type_version, type->tp_version_tag); + #ifdef Py_GIL_DISABLED + if (!_PyObject_HasDeferredRefcount(fget)) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_DESCR_NOT_DEFERRED); + return -1; + } + #endif + assert(tp_version != 0); + write_u32(lm_cache->type_version, tp_version); /* borrowed */ write_obj(lm_cache->descr, fget); specialize(instr, LOAD_ATTR_PROPERTY); @@ -1176,7 +1198,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na assert(dmem->type == Py_T_OBJECT_EX || dmem->type == _Py_T_OBJECT); assert(offset > 0); cache->index = (uint16_t)offset; - write_u32(cache->version, type->tp_version_tag); + write_u32(cache->version, tp_version); specialize(instr, LOAD_ATTR_SLOT); return 0; } @@ -1185,7 +1207,7 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na Py_ssize_t offset = offsetof(PyObject, ob_type); assert(offset == (uint16_t)offset); cache->index = (uint16_t)offset; - write_u32(cache->version, type->tp_version_tag); + write_u32(cache->version, tp_version); specialize(instr, LOAD_ATTR_SLOT); return 0; } @@ -1200,13 +1222,18 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na return -1; case GETATTRIBUTE_IS_PYTHON_FUNCTION: { + #ifndef Py_GIL_DISABLED + // In free-threaded builds it's possible for tp_getattro to change + // after the call to analyze_descriptor. That is fine: the version + // guard will fail. assert(type->tp_getattro == _Py_slot_tp_getattro); + #endif assert(Py_IS_TYPE(descr, &PyFunction_Type)); _PyLoadMethodCache *lm_cache = (_PyLoadMethodCache *)(instr + 1); if (!function_check_args(descr, 2, LOAD_ATTR)) { return -1; } - if (instr->op.arg & 1) { + if (oparg & 1) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_METHOD); return -1; } @@ -1219,10 +1246,16 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER); return -1; } + #ifdef Py_GIL_DISABLED + if (!_PyObject_HasDeferredRefcount(descr)) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_DESCR_NOT_DEFERRED); + return -1; + } + #endif write_u32(lm_cache->keys_version, version); /* borrowed */ write_obj(lm_cache->descr, descr); - write_u32(lm_cache->type_version, type->tp_version_tag); + write_u32(lm_cache->type_version, tp_version); specialize(instr, LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN); return 0; } @@ -1237,8 +1270,10 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na if (shadow) { goto try_instance; } - if ((instr->op.arg & 1) == 0) { - if (specialize_attr_loadclassattr(owner, instr, name, descr, kind, false)) { + if ((oparg & 1) == 0) { + if (specialize_attr_loadclassattr(owner, instr, name, descr, + tp_version, kind, false, + shared_keys_version)) { return 0; } } @@ -1252,14 +1287,28 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na } Py_UNREACHABLE(); try_instance: - if (specialize_dict_access(owner, instr, type, kind, name, type->tp_version_tag, + if (specialize_dict_access(owner, instr, type, kind, name, tp_version, LOAD_ATTR, LOAD_ATTR_INSTANCE_VALUE, LOAD_ATTR_WITH_HINT)) { return 0; } return -1; } -#endif // Py_GIL_DISABLED + +static int +specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* name) +{ + // 0 is not a valid version + uint32_t shared_keys_version = 0; + bool shadow = instance_has_key(owner, name, &shared_keys_version); + PyObject *descr = NULL; + unsigned int tp_version = 0; + PyTypeObject *type = Py_TYPE(owner); + DescriptorClassification kind = analyze_descriptor_load(type, name, &descr, &tp_version); + int result = do_specialize_instance_load_attr(owner, instr, name, shadow, shared_keys_version, kind, descr, tp_version); + Py_XDECREF(descr); + return result; +} void _Py_Specialize_LoadAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *name) @@ -1281,20 +1330,10 @@ _Py_Specialize_LoadAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *nam fail = specialize_module_load_attr(owner, instr, name); } else if (PyType_Check(owner)) { - #ifdef Py_GIL_DISABLED - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_EXPECTED_ERROR); - fail = true; - #else fail = specialize_class_load_attr(owner, instr, name); - #endif } else { - #ifdef Py_GIL_DISABLED - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_EXPECTED_ERROR); - fail = true; - #else fail = specialize_instance_load_attr(owner, instr, name); - #endif } if (fail) { @@ -1402,8 +1441,6 @@ _Py_Specialize_StoreAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *na return; } -#ifndef Py_GIL_DISABLED - #ifdef Py_STATS static int load_attr_fail_kind(DescriptorClassification kind) @@ -1452,8 +1489,10 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_METACLASS_OVERRIDDEN); return -1; } - PyObject *metadescriptor = _PyType_Lookup(Py_TYPE(cls), name); + unsigned int meta_version = 0; + PyObject *metadescriptor = _PyType_LookupRefAndVersion(Py_TYPE(cls), name, &meta_version); DescriptorClassification metakind = classify_descriptor(metadescriptor, false); + Py_XDECREF(metadescriptor); switch (metakind) { case METHOD: case NON_DESCRIPTOR: @@ -1468,38 +1507,52 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, } PyObject *descr = NULL; DescriptorClassification kind = 0; - kind = analyze_descriptor_load(cls, name, &descr); - Py_XDECREF(descr); // turn strong ref into a borrowed ref - if (type_get_version(cls, LOAD_ATTR) == 0) { + unsigned int tp_version = 0; + kind = analyze_descriptor_load(cls, name, &descr, &tp_version); + if (tp_version == 0) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS); + Py_XDECREF(descr); return -1; } bool metaclass_check = false; if ((Py_TYPE(cls)->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) == 0) { metaclass_check = true; - if (type_get_version(Py_TYPE(cls), LOAD_ATTR) == 0) { + if (meta_version == 0) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS); + Py_XDECREF(descr); return -1; } } switch (kind) { case METHOD: case NON_DESCRIPTOR: - write_u32(cache->type_version, cls->tp_version_tag); + #ifdef Py_GIL_DISABLED + if (!_PyObject_HasDeferredRefcount(descr)) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_DESCR_NOT_DEFERRED); + Py_XDECREF(descr); + return -1; + } + #endif + write_u32(cache->type_version, tp_version); write_obj(cache->descr, descr); if (metaclass_check) { - write_u32(cache->keys_version, Py_TYPE(cls)->tp_version_tag); + write_u32(cache->keys_version, meta_version); specialize(instr, LOAD_ATTR_CLASS_WITH_METACLASS_CHECK); } else { specialize(instr, LOAD_ATTR_CLASS); } + Py_XDECREF(descr); return 0; #ifdef Py_STATS case ABSENT: SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_EXPECTED_ERROR); + Py_XDECREF(descr); return -1; #endif default: SPECIALIZATION_FAIL(LOAD_ATTR, load_attr_fail_kind(kind)); + Py_XDECREF(descr); return -1; } } @@ -1508,29 +1561,41 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, // can cause a significant drop in cache hits. A possible test is // python.exe -m test_typing test_re test_dis test_zlib. static int -specialize_attr_loadclassattr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, -PyObject *descr, DescriptorClassification kind, bool is_method) +specialize_attr_loadclassattr(PyObject *owner, _Py_CODEUNIT *instr, + PyObject *name, PyObject *descr, + unsigned int tp_version, + DescriptorClassification kind, bool is_method, + uint32_t shared_keys_version) { _PyLoadMethodCache *cache = (_PyLoadMethodCache *)(instr + 1); PyTypeObject *owner_cls = Py_TYPE(owner); assert(descr != NULL); assert((is_method && kind == METHOD) || (!is_method && kind == NON_DESCRIPTOR)); - if (owner_cls->tp_flags & Py_TPFLAGS_INLINE_VALUES) { - PyDictKeysObject *keys = ((PyHeapTypeObject *)owner_cls)->ht_cached_keys; - assert(_PyDictKeys_StringLookup(keys, name) < 0); - uint32_t keys_version = _PyDictKeys_GetVersionForCurrentState( - _PyInterpreterState_GET(), keys); - if (keys_version == 0) { + + #ifdef Py_GIL_DISABLED + if (!_PyObject_HasDeferredRefcount(descr)) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_DESCR_NOT_DEFERRED); + return 0; + } + #endif + + unsigned long tp_flags = PyType_GetFlags(owner_cls); + if (tp_flags & Py_TPFLAGS_INLINE_VALUES) { + #ifndef Py_GIL_DISABLED + assert(_PyDictKeys_StringLookup( + ((PyHeapTypeObject *)owner_cls)->ht_cached_keys, name) < 0); + #endif + if (shared_keys_version == 0) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS); return 0; } - write_u32(cache->keys_version, keys_version); + write_u32(cache->keys_version, shared_keys_version); specialize(instr, is_method ? LOAD_ATTR_METHOD_WITH_VALUES : LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES); } else { Py_ssize_t dictoffset; - if (owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT) { + if (tp_flags & Py_TPFLAGS_MANAGED_DICT) { dictoffset = MANAGED_DICT_OFFSET; } else { @@ -1576,13 +1641,11 @@ PyObject *descr, DescriptorClassification kind, bool is_method) * PyType_Modified usages in typeobject.c). The MCACHE has been * working since Python 2.6 and it's battle-tested. */ - write_u32(cache->type_version, owner_cls->tp_version_tag); + write_u32(cache->type_version, tp_version); write_obj(cache->descr, descr); return 1; } -#endif // Py_GIL_DISABLED - static void specialize_load_global_lock_held( @@ -1729,7 +1792,6 @@ function_kind(PyCodeObject *code) { return SIMPLE_FUNCTION; } -#ifndef Py_GIL_DISABLED /* Returning false indicates a failure. */ static bool function_check_args(PyObject *o, int expected_argcount, int opcode) @@ -1763,19 +1825,6 @@ function_get_version(PyObject *o, int opcode) return version; } -/* Returning 0 indicates a failure. */ -static uint32_t -type_get_version(PyTypeObject *t, int opcode) -{ - uint32_t version = t->tp_version_tag; - if (version == 0) { - SPECIALIZATION_FAIL(opcode, SPEC_FAIL_OUT_OF_VERSIONS); - return 0; - } - return version; -} -#endif // Py_GIL_DISABLED - void _Py_Specialize_BinarySubscr( _PyStackRef container_st, _PyStackRef sub_st, _Py_CODEUNIT *instr) diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 99896f32fd2b08..4013b503502df6 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -386,7 +386,7 @@ def find_assignment_target(node: parser.InstDef, idx: int) -> list[lexer.Token]: """Find the tokens that make up the left-hand side of an assignment""" offset = 0 for tkn in reversed(node.block.tokens[: idx]): - if tkn.kind in {"SEMI", "LBRACE", "RBRACE"}: + if tkn.kind in {"SEMI", "LBRACE", "RBRACE", "CMACRO"}: return node.block.tokens[idx - offset : idx] offset += 1 return [] diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index 69d84183f1c7e6..f54afbb880d2fa 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -126,7 +126,7 @@ def __init__(self, out: CWriter): "PyStackRef_AsPyObjectSteal": self.stackref_steal, "DISPATCH": self.dispatch, "INSTRUCTION_SIZE": self.instruction_size, - "POP_DEAD_INPUTS": self.pop_dead_inputs, + "POP_INPUT": self.pop_input, } self.out = out @@ -423,7 +423,7 @@ def save_stack( self.emit_save(storage) return True - def pop_dead_inputs( + def pop_input( self, tkn: Token, tkn_iter: TokenIterator, @@ -432,9 +432,18 @@ def pop_dead_inputs( inst: Instruction | None, ) -> bool: next(tkn_iter) + name_tkn = next(tkn_iter) + name = name_tkn.text next(tkn_iter) next(tkn_iter) - storage.pop_dead_inputs(self.out) + if not storage.inputs: + raise analysis_error("stack is empty", tkn) + tos = storage.inputs[-1] + if tos.name != name: + raise analysis_error(f"'{name} is not top of stack", name_tkn) + tos.defined = False + storage.clear_dead_inputs() + storage.flush(self.out) return True def emit_reload(self, storage: Storage) -> None: diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 9471fe0e56f7d8..286f47d0cfb11b 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -512,10 +512,6 @@ def flush(self, out: CWriter, cast_type: str = "uintptr_t", extract_bits: bool = self._push_defined_outputs() self.stack.flush(out, cast_type, extract_bits) - def pop_dead_inputs(self, out: CWriter, cast_type: str = "uintptr_t", extract_bits: bool = True) -> None: - self.clear_dead_inputs() - self.stack.flush(out, cast_type, extract_bits) - def save(self, out: CWriter) -> None: assert self.spilled >= 0 if self.spilled == 0: From f7ceb317aec498823555885a4b7fed5e0244f300 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 14 Jan 2025 22:40:45 +0200 Subject: [PATCH 314/376] gh-71339: Use new assertion methods in test_logging (GH-128828) --- Lib/test/test_logging.py | 51 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 44c854f02a73c6..2e5f6475ae3b1e 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1134,7 +1134,7 @@ def test_basic(self): self.assertEqual(mailfrom, 'me') self.assertEqual(rcpttos, ['you']) self.assertIn('\nSubject: Log\n', data) - self.assertTrue(data.endswith('\n\nHello \u2713')) + self.assertEndsWith(data, '\n\nHello \u2713') h.close() def process_message(self, *args): @@ -3524,7 +3524,7 @@ def test_config14_ok(self): self.assertEqual(h.foo, 'bar') self.assertEqual(h.terminator, '!\n') logging.warning('Exclamation') - self.assertTrue(output.getvalue().endswith('Exclamation!\n')) + self.assertEndsWith(output.getvalue(), 'Exclamation!\n') def test_config15_ok(self): @@ -4281,7 +4281,7 @@ def test_queue_handler(self): msg = self.next_message() self.que_logger.warning(msg) data = self.queue.get_nowait() - self.assertTrue(isinstance(data, logging.LogRecord)) + self.assertIsInstance(data, logging.LogRecord) self.assertEqual(data.name, self.que_logger.name) self.assertEqual((data.msg, data.args), (msg, None)) @@ -4879,14 +4879,14 @@ def test_formatting(self): r.removeHandler(h) h.close() r = h.records[0] - self.assertTrue(r.exc_text.startswith('Traceback (most recent ' - 'call last):\n')) - self.assertTrue(r.exc_text.endswith('\nRuntimeError: ' - 'deliberate mistake')) - self.assertTrue(r.stack_info.startswith('Stack (most recent ' - 'call last):\n')) - self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', ' - 'stack_info=True)')) + self.assertStartsWith(r.exc_text, + 'Traceback (most recent call last):\n') + self.assertEndsWith(r.exc_text, + '\nRuntimeError: deliberate mistake') + self.assertStartsWith(r.stack_info, + 'Stack (most recent call last):\n') + self.assertEndsWith(r.stack_info, + "logging.exception('failed', stack_info=True)") class LastResortTest(BaseTest): @@ -5229,8 +5229,8 @@ class LogRecordTest(BaseTest): def test_str_rep(self): r = logging.makeLogRecord({}) s = str(r) - self.assertTrue(s.startswith('')) + self.assertStartsWith(s, '') def test_dict_arg(self): h = RecordingHandler() @@ -5880,14 +5880,14 @@ def test_extra_in_records(self): self.adapter.critical('foo should be here') self.assertEqual(len(self.recording.records), 1) record = self.recording.records[0] - self.assertTrue(hasattr(record, 'foo')) + self.assertHasAttr(record, 'foo') self.assertEqual(record.foo, '1') def test_extra_not_merged_by_default(self): self.adapter.critical('foo should NOT be here', extra={'foo': 'nope'}) self.assertEqual(len(self.recording.records), 1) record = self.recording.records[0] - self.assertFalse(hasattr(record, 'foo')) + self.assertNotHasAttr(record, 'foo') def test_extra_merged(self): self.adapter = logging.LoggerAdapter(logger=self.logger, @@ -5897,8 +5897,8 @@ def test_extra_merged(self): self.adapter.critical('foo and bar should be here', extra={'bar': '2'}) self.assertEqual(len(self.recording.records), 1) record = self.recording.records[0] - self.assertTrue(hasattr(record, 'foo')) - self.assertTrue(hasattr(record, 'bar')) + self.assertHasAttr(record, 'foo') + self.assertHasAttr(record, 'bar') self.assertEqual(record.foo, '1') self.assertEqual(record.bar, '2') @@ -5910,7 +5910,7 @@ def test_extra_merged_log_call_has_precedence(self): self.adapter.critical('foo shall be min', extra={'foo': '2'}) self.assertEqual(len(self.recording.records), 1) record = self.recording.records[0] - self.assertTrue(hasattr(record, 'foo')) + self.assertHasAttr(record, 'foo') self.assertEqual(record.foo, '2') @@ -6624,18 +6624,19 @@ def namer(filename): p = '%s.log.' % prefix for c in candidates: d, fn = os.path.split(c) - self.assertTrue(fn.startswith(p)) + self.assertStartsWith(fn, p) elif prefix.startswith('d.e'): for c in candidates: d, fn = os.path.split(c) - self.assertTrue(fn.endswith('.log'), fn) - self.assertTrue(fn.startswith(prefix + '.') and - fn[len(prefix) + 2].isdigit()) + self.assertEndsWith(fn, '.log') + self.assertStartsWith(fn, prefix + '.') + self.assertTrue(fn[len(prefix) + 2].isdigit()) elif prefix == 'g': for c in candidates: d, fn = os.path.split(c) - self.assertTrue(fn.endswith('.oldlog')) - self.assertTrue(fn.startswith('g') and fn[1].isdigit()) + self.assertEndsWith(fn, '.oldlog') + self.assertStartsWith(fn, 'g') + self.assertTrue(fn[1].isdigit()) def test_compute_files_to_delete_same_filename_different_extensions(self): # See GH-93205 for background @@ -6673,7 +6674,7 @@ def test_compute_files_to_delete_same_filename_different_extensions(self): matcher = re.compile(r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}\Z") for c in candidates: d, fn = os.path.split(c) - self.assertTrue(fn.startswith(prefix+'.')) + self.assertStartsWith(fn, prefix+'.') suffix = fn[(len(prefix)+1):] self.assertRegex(suffix, matcher) From b52de22ac345ad8583bcc57f963e26b35c2ee527 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 15 Jan 2025 01:17:11 +0200 Subject: [PATCH 315/376] gh-71339: Use new assertion methods in the multiprocessing tests (GH-128847) --- Lib/test/_test_multiprocessing.py | 53 ++++++++++++++----------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 38a03f3391d31d..4b7c3e7fa8bdd7 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -319,7 +319,7 @@ def test_current(self): authkey = current.authkey self.assertTrue(current.is_alive()) - self.assertTrue(not current.daemon) + self.assertFalse(current.daemon) self.assertIsInstance(authkey, bytes) self.assertTrue(len(authkey) > 0) self.assertEqual(current.ident, os.getpid()) @@ -463,7 +463,7 @@ def test_process(self): self.assertEqual(p.is_alive(), False) self.assertEqual(p.daemon, True) self.assertNotIn(p, self.active_children()) - self.assertTrue(type(self.active_children()) is list) + self.assertIs(type(self.active_children()), list) self.assertEqual(p.exitcode, None) p.start() @@ -583,8 +583,8 @@ def test_cpu_count(self): cpus = multiprocessing.cpu_count() except NotImplementedError: cpus = 1 - self.assertTrue(type(cpus) is int) - self.assertTrue(cpus >= 1) + self.assertIsInstance(cpus, int) + self.assertGreaterEqual(cpus, 1) def test_active_children(self): self.assertEqual(type(self.active_children()), list) @@ -2382,14 +2382,14 @@ def test_getobj_getlock(self): self.assertEqual(lock, lock3) arr4 = self.Value('i', 5, lock=False) - self.assertFalse(hasattr(arr4, 'get_lock')) - self.assertFalse(hasattr(arr4, 'get_obj')) + self.assertNotHasAttr(arr4, 'get_lock') + self.assertNotHasAttr(arr4, 'get_obj') self.assertRaises(AttributeError, self.Value, 'i', 5, lock='navalue') arr5 = self.RawValue('i', 5) - self.assertFalse(hasattr(arr5, 'get_lock')) - self.assertFalse(hasattr(arr5, 'get_obj')) + self.assertNotHasAttr(arr5, 'get_lock') + self.assertNotHasAttr(arr5, 'get_obj') class _TestArray(BaseTestCase): @@ -2462,14 +2462,14 @@ def test_getobj_getlock_obj(self): self.assertEqual(lock, lock3) arr4 = self.Array('i', range(10), lock=False) - self.assertFalse(hasattr(arr4, 'get_lock')) - self.assertFalse(hasattr(arr4, 'get_obj')) + self.assertNotHasAttr(arr4, 'get_lock') + self.assertNotHasAttr(arr4, 'get_obj') self.assertRaises(AttributeError, self.Array, 'i', range(10), lock='notalock') arr5 = self.RawArray('i', range(10)) - self.assertFalse(hasattr(arr5, 'get_lock')) - self.assertFalse(hasattr(arr5, 'get_obj')) + self.assertNotHasAttr(arr5, 'get_lock') + self.assertNotHasAttr(arr5, 'get_obj') # # @@ -2657,8 +2657,8 @@ def test_namespace(self): self.assertEqual((n.name, n.job), ('Bob', 'Builder')) del n.job self.assertEqual(str(n), "Namespace(name='Bob')") - self.assertTrue(hasattr(n, 'name')) - self.assertTrue(not hasattr(n, 'job')) + self.assertHasAttr(n, 'name') + self.assertNotHasAttr(n, 'job') # # @@ -4938,13 +4938,9 @@ def test_import(self): for name in modules: __import__(name) mod = sys.modules[name] - self.assertTrue(hasattr(mod, '__all__'), name) - + self.assertHasAttr(mod, '__all__', name) for attr in mod.__all__: - self.assertTrue( - hasattr(mod, attr), - '%r does not have attribute %r' % (mod, attr) - ) + self.assertHasAttr(mod, attr) # # Quick test that logging works -- does not test logging output @@ -4957,7 +4953,7 @@ class _TestLogging(BaseTestCase): def test_enable_logging(self): logger = multiprocessing.get_logger() logger.setLevel(util.SUBWARNING) - self.assertTrue(logger is not None) + self.assertIsNotNone(logger) logger.debug('this will not be printed') logger.info('nor will this') logger.setLevel(LOG_LEVEL) @@ -5753,9 +5749,8 @@ def test_set_get(self): self.assertEqual(multiprocessing.get_start_method(), method) ctx = multiprocessing.get_context() self.assertEqual(ctx.get_start_method(), method) - self.assertTrue(type(ctx).__name__.lower().startswith(method)) - self.assertTrue( - ctx.Process.__name__.lower().startswith(method)) + self.assertStartsWith(type(ctx).__name__.lower(), method) + self.assertStartsWith(ctx.Process.__name__.lower(), method) self.check_context(multiprocessing) count += 1 finally: @@ -5956,9 +5951,9 @@ def check_resource_tracker_death(self, signum, should_die): if should_die: self.assertEqual(len(all_warn), 1) the_warn = all_warn[0] - self.assertTrue(issubclass(the_warn.category, UserWarning)) - self.assertTrue("resource_tracker: process died" - in str(the_warn.message)) + self.assertIsSubclass(the_warn.category, UserWarning) + self.assertIn("resource_tracker: process died", + str(the_warn.message)) else: self.assertEqual(len(all_warn), 0) @@ -6163,8 +6158,8 @@ def is_alive(self): Process=FailingForkProcess)) p.close() p.join() - self.assertFalse( - any(process.is_alive() for process in forked_processes)) + for process in forked_processes: + self.assertFalse(process.is_alive(), process) @hashlib_helper.requires_hashdigest('sha256') From bd3baa8b1a7755f17b2fc98c7fb7b872fec43af3 Mon Sep 17 00:00:00 2001 From: "Tomas R." Date: Wed, 15 Jan 2025 01:48:46 +0100 Subject: [PATCH 316/376] gh-121604: Make sure all deprecated items in importlib raise DeprecationWarning (#128007) Co-authored-by: rashansmith Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Co-authored-by: Brett Cannon --- Lib/importlib/_bootstrap_external.py | 6 ++++ Lib/importlib/abc.py | 13 ++++++++ Lib/importlib/machinery.py | 27 ++++++++++++++-- Lib/inspect.py | 3 +- Lib/test/test_importlib/test_abc.py | 32 +++++++++++++++++++ Lib/test/test_importlib/test_api.py | 13 ++++++++ Lib/test/test_importlib/test_windows.py | 6 ++++ ...-12-16-22-20-38.gh-issue-121604.m3Xn4G.rst | 1 + 8 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-16-22-20-38.gh-issue-121604.m3Xn4G.rst diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index fa36159711846f..697f7c55218a8f 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -716,6 +716,12 @@ def _search_registry(cls, fullname): @classmethod def find_spec(cls, fullname, path=None, target=None): + _warnings.warn('importlib.machinery.WindowsRegistryFinder is ' + 'deprecated; use site configuration instead. ' + 'Future versions of Python may not enable this ' + 'finder by default.', + DeprecationWarning, stacklevel=2) + filepath = cls._search_registry(fullname) if filepath is None: return None diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py index eea6b38af6fa13..bb2837d38d83f1 100644 --- a/Lib/importlib/abc.py +++ b/Lib/importlib/abc.py @@ -70,6 +70,15 @@ class ResourceLoader(Loader): """ + def __init__(self): + import warnings + warnings.warn('importlib.abc.ResourceLoader is deprecated in ' + 'favour of supporting resource loading through ' + 'importlib.resources.abc.ResourceReader.', + DeprecationWarning, stacklevel=2) + super().__init__() + + @abc.abstractmethod def get_data(self, path): """Abstract method which when implemented should return the bytes for @@ -199,6 +208,10 @@ class SourceLoader(_bootstrap_external.SourceLoader, ResourceLoader, ExecutionLo def path_mtime(self, path): """Return the (int) modification time for the path (str).""" + import warnings + warnings.warn('SourceLoader.path_mtime is deprecated in favour of ' + 'SourceLoader.path_stats().', + DeprecationWarning, stacklevel=2) if self.path_stats.__func__ is SourceLoader.path_stats: raise OSError return int(self.path_stats(path)['mtime']) diff --git a/Lib/importlib/machinery.py b/Lib/importlib/machinery.py index 6e294d59bfdcb9..63d726445c3d96 100644 --- a/Lib/importlib/machinery.py +++ b/Lib/importlib/machinery.py @@ -3,9 +3,11 @@ from ._bootstrap import ModuleSpec from ._bootstrap import BuiltinImporter from ._bootstrap import FrozenImporter -from ._bootstrap_external import (SOURCE_SUFFIXES, DEBUG_BYTECODE_SUFFIXES, - OPTIMIZED_BYTECODE_SUFFIXES, BYTECODE_SUFFIXES, - EXTENSION_SUFFIXES) +from ._bootstrap_external import ( + SOURCE_SUFFIXES, BYTECODE_SUFFIXES, EXTENSION_SUFFIXES, + DEBUG_BYTECODE_SUFFIXES as _DEBUG_BYTECODE_SUFFIXES, + OPTIMIZED_BYTECODE_SUFFIXES as _OPTIMIZED_BYTECODE_SUFFIXES +) from ._bootstrap_external import WindowsRegistryFinder from ._bootstrap_external import PathFinder from ._bootstrap_external import FileFinder @@ -27,3 +29,22 @@ def all_suffixes(): 'NamespaceLoader', 'OPTIMIZED_BYTECODE_SUFFIXES', 'PathFinder', 'SOURCE_SUFFIXES', 'SourceFileLoader', 'SourcelessFileLoader', 'WindowsRegistryFinder', 'all_suffixes'] + + +def __getattr__(name): + import warnings + + if name == 'DEBUG_BYTECODE_SUFFIXES': + warnings.warn('importlib.machinery.DEBUG_BYTECODE_SUFFIXES is ' + 'deprecated; use importlib.machinery.BYTECODE_SUFFIXES ' + 'instead.', + DeprecationWarning, stacklevel=2) + return _DEBUG_BYTECODE_SUFFIXES + elif name == 'OPTIMIZED_BYTECODE_SUFFIXES': + warnings.warn('importlib.machinery.OPTIMIZED_BYTECODE_SUFFIXES is ' + 'deprecated; use importlib.machinery.BYTECODE_SUFFIXES ' + 'instead.', + DeprecationWarning, stacklevel=2) + return _OPTIMIZED_BYTECODE_SUFFIXES + + raise AttributeError(f'module {__name__!r} has no attribute {name!r}') diff --git a/Lib/inspect.py b/Lib/inspect.py index 5b7c4df8927c87..facad478103668 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -858,8 +858,7 @@ def getsourcefile(object): Return None if no way can be identified to get the source. """ filename = getfile(object) - all_bytecode_suffixes = importlib.machinery.DEBUG_BYTECODE_SUFFIXES[:] - all_bytecode_suffixes += importlib.machinery.OPTIMIZED_BYTECODE_SUFFIXES[:] + all_bytecode_suffixes = importlib.machinery.BYTECODE_SUFFIXES[:] if any(filename.endswith(s) for s in all_bytecode_suffixes): filename = (os.path.splitext(filename)[0] + importlib.machinery.SOURCE_SUFFIXES[0]) diff --git a/Lib/test/test_importlib/test_abc.py b/Lib/test/test_importlib/test_abc.py index 603125f6d926f6..00af2dd712425a 100644 --- a/Lib/test/test_importlib/test_abc.py +++ b/Lib/test/test_importlib/test_abc.py @@ -913,5 +913,37 @@ def test_universal_newlines(self): SourceOnlyLoaderMock=SPLIT_SOL) +class SourceLoaderDeprecationWarningsTests(unittest.TestCase): + """Tests SourceLoader deprecation warnings.""" + + def test_deprecated_path_mtime(self): + from importlib.abc import SourceLoader + class DummySourceLoader(SourceLoader): + def get_data(self, path): + return b'' + + def get_filename(self, fullname): + return 'foo.py' + + def path_stats(self, path): + return {'mtime': 1} + + loader = DummySourceLoader() + with self.assertWarns(DeprecationWarning): + loader.path_mtime('foo.py') + + +class ResourceLoaderDeprecationWarningsTests(unittest.TestCase): + """Tests ResourceLoader deprecation warnings.""" + + def test_deprecated_resource_loader(self): + from importlib.abc import ResourceLoader + class DummyLoader(ResourceLoader): + def get_data(self, path): + return b'' + + with self.assertWarns(DeprecationWarning): + DummyLoader() + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_importlib/test_api.py b/Lib/test/test_importlib/test_api.py index 51ea5270b1a928..6035b2ca72efb9 100644 --- a/Lib/test/test_importlib/test_api.py +++ b/Lib/test/test_importlib/test_api.py @@ -492,5 +492,18 @@ def test_util(self): support.check__all__(self, util['Source'], extra=extra) +class TestDeprecations(unittest.TestCase): + def test_machinery_deprecated_attributes(self): + from importlib import machinery + attributes = ( + 'DEBUG_BYTECODE_SUFFIXES', + 'OPTIMIZED_BYTECODE_SUFFIXES', + ) + for attr in attributes: + with self.subTest(attr=attr): + with self.assertWarns(DeprecationWarning): + getattr(machinery, attr) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_importlib/test_windows.py b/Lib/test/test_importlib/test_windows.py index 8a9a8fffcd10d4..f32680bdbeb9e3 100644 --- a/Lib/test/test_importlib/test_windows.py +++ b/Lib/test/test_importlib/test_windows.py @@ -104,6 +104,12 @@ def test_module_not_found(self): spec = self.machinery.WindowsRegistryFinder.find_spec(self.test_module) self.assertIsNone(spec) + def test_raises_deprecation_warning(self): + # WindowsRegistryFinder is not meant to be instantiated, so the + # deprecation warning is raised in the 'find_spec' method instead. + with self.assertWarns(DeprecationWarning): + self.machinery.WindowsRegistryFinder.find_spec('spam') + (Frozen_WindowsRegistryFinderTests, Source_WindowsRegistryFinderTests ) = test_util.test_both(WindowsRegistryFinderTests, machinery=machinery) diff --git a/Misc/NEWS.d/next/Library/2024-12-16-22-20-38.gh-issue-121604.m3Xn4G.rst b/Misc/NEWS.d/next/Library/2024-12-16-22-20-38.gh-issue-121604.m3Xn4G.rst new file mode 100644 index 00000000000000..9a6fce8647cc6b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-16-22-20-38.gh-issue-121604.m3Xn4G.rst @@ -0,0 +1 @@ +Add missing Deprecation warnings for :attr:`importlib.machinery.DEBUG_BYTECODE_SUFFIXES`, :attr:`importlib.machinery.OPTIMIZED_BYTECODE_SUFFIXES`, :class:`importlib.machinery.WindowsRegistryFinder`, :class:`importlib.abc.ResourceLoader`, :meth:`importlib.abc.SourceLoader.path_mtime`. From 6e4f64109b0eb6c9f1b50eb7dc5f647a1d901ff4 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Tue, 14 Jan 2025 19:49:02 -0600 Subject: [PATCH 317/376] gh-128473: Skip segfaulting `test_embed` tests when BOLT instrumented (gh-128474) * Skip segfaulting `test_embed` tests when BOLT instrumented Co-authored-by: Gregory Szorc * NEWS --------- Co-authored-by: Gregory Szorc --- Lib/test/test_embed.py | 4 ++++ .../next/Tests/2025-01-04-02-41-41.gh-issue-128474.0b-tl4.rst | 2 ++ 2 files changed, 6 insertions(+) create mode 100644 Misc/NEWS.d/next/Tests/2025-01-04-02-41-41.gh-issue-128474.0b-tl4.rst diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 7110fb889f3c8e..1b55cd156d759d 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -940,6 +940,7 @@ def check_all_configs(self, testname, expected_config=None, self.check_global_config(configs) return configs + @unittest.skipIf(support.check_bolt_optimized, "segfaults on BOLT instrumented binaries") def test_init_default_config(self): self.check_all_configs("test_init_initialize_config", api=API_COMPAT) @@ -1039,6 +1040,7 @@ def test_init_from_config(self): self.check_all_configs("test_init_from_config", config, preconfig, api=API_COMPAT) + @unittest.skipIf(support.check_bolt_optimized, "segfaults on BOLT instrumented binaries") def test_init_compat_env(self): preconfig = { 'allocator': ALLOCATOR_FOR_CONFIG, @@ -1074,6 +1076,7 @@ def test_init_compat_env(self): self.check_all_configs("test_init_compat_env", config, preconfig, api=API_COMPAT) + @unittest.skipIf(support.check_bolt_optimized, "segfaults on BOLT instrumented binaries") def test_init_python_env(self): preconfig = { 'allocator': ALLOCATOR_FOR_CONFIG, @@ -1772,6 +1775,7 @@ def test_init_set_config(self): self.check_all_configs("test_init_set_config", config, api=API_ISOLATED) + @unittest.skipIf(support.check_bolt_optimized, "segfaults on BOLT instrumented binaries") def test_initconfig_api(self): preconfig = { 'configure_locale': True, diff --git a/Misc/NEWS.d/next/Tests/2025-01-04-02-41-41.gh-issue-128474.0b-tl4.rst b/Misc/NEWS.d/next/Tests/2025-01-04-02-41-41.gh-issue-128474.0b-tl4.rst new file mode 100644 index 00000000000000..7dc807757c5ec0 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2025-01-04-02-41-41.gh-issue-128474.0b-tl4.rst @@ -0,0 +1,2 @@ +Disable ``test_embed`` test cases that segfault on BOLT instrument binaries. +The tests are only disabled when BOLT is enabled. From ae7f621c33c854334c69cf97260c7516170dcf0d Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Wed, 15 Jan 2025 10:38:43 +0100 Subject: [PATCH 318/376] gh-128438: Use `EnvironmentVarGuard` for `test_{builtin,io,locale}.py` (#128476) Modifying locale-related environment variables in `Lib/test/test_builtin.py`, `Lib/test/test_io.py` and `Lib/test/test_locale.py` is now achieved by using an `EnvironmentVarGuard` context instead of an explicit `try-finally` block. --- Lib/test/test_builtin.py | 9 ++------- Lib/test/test_io.py | 9 ++------- Lib/test/test_locale.py | 23 +++++++---------------- 3 files changed, 11 insertions(+), 30 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 5f4eac5267622f..73b139e405ae59 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1567,14 +1567,12 @@ def test_open(self): @unittest.skipIf(sys.flags.utf8_mode, "utf-8 mode is enabled") def test_open_default_encoding(self): - old_environ = dict(os.environ) - try: + with EnvironmentVarGuard() as env: # try to get a user preferred encoding different than the current # locale encoding to check that open() uses the current locale # encoding and not the user preferred encoding for key in ('LC_ALL', 'LANG', 'LC_CTYPE'): - if key in os.environ: - del os.environ[key] + env.unset(key) self.write_testfile() current_locale_encoding = locale.getencoding() @@ -1583,9 +1581,6 @@ def test_open_default_encoding(self): fp = open(TESTFN, 'w') with fp: self.assertEqual(fp.encoding, current_locale_encoding) - finally: - os.environ.clear() - os.environ.update(old_environ) @support.requires_subprocess() def test_open_non_inheritable(self): diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 33e0161241e87e..8c79d2c24a140a 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -2892,14 +2892,12 @@ def test_reconfigure_line_buffering(self): @unittest.skipIf(sys.flags.utf8_mode, "utf-8 mode is enabled") def test_default_encoding(self): - old_environ = dict(os.environ) - try: + with os_helper.EnvironmentVarGuard() as env: # try to get a user preferred encoding different than the current # locale encoding to check that TextIOWrapper() uses the current # locale encoding and not the user preferred encoding for key in ('LC_ALL', 'LANG', 'LC_CTYPE'): - if key in os.environ: - del os.environ[key] + env.unset(key) current_locale_encoding = locale.getencoding() b = self.BytesIO() @@ -2907,9 +2905,6 @@ def test_default_encoding(self): warnings.simplefilter("ignore", EncodingWarning) t = self.TextIOWrapper(b) self.assertEqual(t.encoding, current_locale_encoding) - finally: - os.environ.clear() - os.environ.update(old_environ) def test_encoding(self): # Check the encoding attribute is always set, and valid diff --git a/Lib/test/test_locale.py b/Lib/test/test_locale.py index 00e93d8e78443d..c025ed4108fb58 100644 --- a/Lib/test/test_locale.py +++ b/Lib/test/test_locale.py @@ -1,5 +1,5 @@ from decimal import Decimal -from test.support import verbose, is_android, is_emscripten, is_wasi +from test.support import verbose, is_android, is_emscripten, is_wasi, os_helper from test.support.warnings_helper import check_warnings from test.support.import_helper import import_fresh_module from unittest import mock @@ -499,25 +499,16 @@ def test_defaults_UTF8(self): else: orig_getlocale = None - orig_env = {} try: - for key in ('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE'): - if key in os.environ: - orig_env[key] = os.environ[key] - del os.environ[key] + with os_helper.EnvironmentVarGuard() as env: + for key in ('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE'): + env.unset(key) - os.environ['LC_CTYPE'] = 'UTF-8' - - with check_warnings(('', DeprecationWarning)): - self.assertEqual(locale.getdefaultlocale(), (None, 'UTF-8')) + env.set('LC_CTYPE', 'UTF-8') + with check_warnings(('', DeprecationWarning)): + self.assertEqual(locale.getdefaultlocale(), (None, 'UTF-8')) finally: - for k in orig_env: - os.environ[k] = orig_env[k] - - if 'LC_CTYPE' not in orig_env: - del os.environ['LC_CTYPE'] - if orig_getlocale is not None: _locale._getdefaultlocale = orig_getlocale From 1a1056d394a489d229b26c06c7c79aa9c06696f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wang=20Ran=20=28=E6=B1=AA=E7=84=B6=29?= Date: Wed, 15 Jan 2025 20:24:31 +0800 Subject: [PATCH 319/376] Fix typo in `Lib/asyncio/futures.py` (#128819) --- Lib/asyncio/futures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index c95fce035cd548..359b7a5e3f9eea 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -62,7 +62,7 @@ class Future: # that it is not compatible by setting this to None. # - It is set by __iter__() below so that Task.__step() can tell # the difference between - # `await Future()` or`yield from Future()` (correct) vs. + # `await Future()` or `yield from Future()` (correct) vs. # `yield Future()` (incorrect). _asyncio_future_blocking = False From 599be687ec7327c30c6469cf743aa4ee9e82232d Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 15 Jan 2025 13:05:59 +0000 Subject: [PATCH 320/376] gh-128816: Fix warnings in test_doctest (GH-128817) * Fix a deprecation warning for using importlib.resources.abc.ResourceReader. * Fix an import warning when importing readline (if it has not yet been imported). --- Lib/test/test_doctest/test_doctest.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_doctest/test_doctest.py b/Lib/test/test_doctest/test_doctest.py index b1e165fe16b54f..a4a49298bab3be 100644 --- a/Lib/test/test_doctest/test_doctest.py +++ b/Lib/test/test_doctest/test_doctest.py @@ -2860,7 +2860,7 @@ def test_testfile(): r""" >>> _colorize.COLORIZE = save_colorize """ -class TestImporter(importlib.abc.MetaPathFinder, importlib.abc.ResourceLoader): +class TestImporter(importlib.abc.MetaPathFinder): def find_spec(self, fullname, path, target=None): return importlib.util.spec_from_file_location(fullname, path, loader=self) @@ -2869,6 +2869,12 @@ def get_data(self, path): with open(path, mode='rb') as f: return f.read() + def exec_module(self, module): + raise ImportError + + def create_module(self, spec): + return None + class TestHook: def __init__(self, pathdir): From 40a4d88a14c741172a158683c39d232c587c6f11 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 15 Jan 2025 15:12:40 +0100 Subject: [PATCH 321/376] gh-128874: Fix the documentation for blurb 2.0 (#128875) --- Misc/NEWS.d/3.10.0b1.rst | 2 +- Misc/NEWS.d/3.11.0b1.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/3.10.0b1.rst b/Misc/NEWS.d/3.10.0b1.rst index 25c6b827146e82..406a5d7853edc0 100644 --- a/Misc/NEWS.d/3.10.0b1.rst +++ b/Misc/NEWS.d/3.10.0b1.rst @@ -941,7 +941,7 @@ result from ``entry_points()`` as deprecated. .. -.. gh: 47383 +.. gh-issue: 47383 .. date: 2021-04-08-19-32-26 .. nonce: YI1hdL .. section: Library diff --git a/Misc/NEWS.d/3.11.0b1.rst b/Misc/NEWS.d/3.11.0b1.rst index 85cb0f1b5cffbd..87442dbbbd17f5 100644 --- a/Misc/NEWS.d/3.11.0b1.rst +++ b/Misc/NEWS.d/3.11.0b1.rst @@ -570,7 +570,7 @@ planned). Patch by Alex Waygood. .. -.. gh: 78157 +.. gh-issue: 78157 .. date: 2022-05-05-20-40-45 .. nonce: IA_9na .. section: Library @@ -1289,7 +1289,7 @@ Deprecate the chunk module. .. -.. gh: 91498 +.. gh-issue: 91498 .. date: 2022-04-10-08-39-44 .. nonce: 8oII92 .. section: Library From 8d8b854824c4723d7c5924f1d5c6a397ea7214a5 Mon Sep 17 00:00:00 2001 From: Umar Butler <8473183+umarbutler@users.noreply.github.com> Date: Thu, 16 Jan 2025 04:00:54 +1100 Subject: [PATCH 322/376] gh-128016: Improved invalid escape sequence warning message (#128020) --- Lib/test/test_cmd_line_script.py | 3 +- Lib/test/test_codeop.py | 2 +- Lib/test/test_string_literals.py | 29 +++++++++++++------ Lib/test/test_unparse.py | 4 ++- ...-12-17-09-28-17.gh-issue-128016.DPqhah.rst | 1 + Objects/bytesobject.c | 7 +++-- Objects/unicodeobject.c | 6 ++-- Parser/string_parser.c | 24 +++++++++++---- Parser/tokenizer/helpers.c | 12 ++++++-- 9 files changed, 63 insertions(+), 25 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-09-28-17.gh-issue-128016.DPqhah.rst diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py index f30107225ff612..527d51857fc904 100644 --- a/Lib/test/test_cmd_line_script.py +++ b/Lib/test/test_cmd_line_script.py @@ -659,7 +659,8 @@ def test_syntaxerror_invalid_escape_sequence_multi_line(self): stderr.splitlines()[-3:], [ b' foo = """\\q"""', b' ^^^^^^^^', - b'SyntaxError: invalid escape sequence \'\\q\'' + b'SyntaxError: "\\q" is an invalid escape sequence. ' + b'Did you mean "\\\\q"? A raw string is also an option.' ], ) diff --git a/Lib/test/test_codeop.py b/Lib/test/test_codeop.py index 787bd1b6a79e20..0eefc22d11bce0 100644 --- a/Lib/test/test_codeop.py +++ b/Lib/test/test_codeop.py @@ -282,7 +282,7 @@ def test_warning(self): # Test that the warning is only returned once. with warnings_helper.check_warnings( ('"is" with \'str\' literal', SyntaxWarning), - ("invalid escape sequence", SyntaxWarning), + ('"\\\\e" is an invalid escape sequence', SyntaxWarning), ) as w: compile_command(r"'\e' is 0") self.assertEqual(len(w.warnings), 2) diff --git a/Lib/test/test_string_literals.py b/Lib/test/test_string_literals.py index c7c6f684cd33f0..f56195ca27672c 100644 --- a/Lib/test/test_string_literals.py +++ b/Lib/test/test_string_literals.py @@ -116,7 +116,9 @@ def test_eval_str_invalid_escape(self): warnings.simplefilter('always', category=SyntaxWarning) eval("'''\n\\z'''") self.assertEqual(len(w), 1) - self.assertEqual(str(w[0].message), r"invalid escape sequence '\z'") + self.assertEqual(str(w[0].message), r'"\z" is an invalid escape sequence. ' + r'Such sequences will not work in the future. ' + r'Did you mean "\\z"? A raw string is also an option.') self.assertEqual(w[0].filename, '') self.assertEqual(w[0].lineno, 1) @@ -126,7 +128,8 @@ def test_eval_str_invalid_escape(self): eval("'''\n\\z'''") exc = cm.exception self.assertEqual(w, []) - self.assertEqual(exc.msg, r"invalid escape sequence '\z'") + self.assertEqual(exc.msg, r'"\z" is an invalid escape sequence. ' + r'Did you mean "\\z"? A raw string is also an option.') self.assertEqual(exc.filename, '') self.assertEqual(exc.lineno, 1) self.assertEqual(exc.offset, 1) @@ -153,7 +156,9 @@ def test_eval_str_invalid_octal_escape(self): eval("'''\n\\407'''") self.assertEqual(len(w), 1) self.assertEqual(str(w[0].message), - r"invalid octal escape sequence '\407'") + r'"\407" is an invalid octal escape sequence. ' + r'Such sequences will not work in the future. ' + r'Did you mean "\\407"? A raw string is also an option.') self.assertEqual(w[0].filename, '') self.assertEqual(w[0].lineno, 1) @@ -163,7 +168,8 @@ def test_eval_str_invalid_octal_escape(self): eval("'''\n\\407'''") exc = cm.exception self.assertEqual(w, []) - self.assertEqual(exc.msg, r"invalid octal escape sequence '\407'") + self.assertEqual(exc.msg, r'"\407" is an invalid octal escape sequence. ' + r'Did you mean "\\407"? A raw string is also an option.') self.assertEqual(exc.filename, '') self.assertEqual(exc.lineno, 1) self.assertEqual(exc.offset, 1) @@ -205,7 +211,9 @@ def test_eval_bytes_invalid_escape(self): warnings.simplefilter('always', category=SyntaxWarning) eval("b'''\n\\z'''") self.assertEqual(len(w), 1) - self.assertEqual(str(w[0].message), r"invalid escape sequence '\z'") + self.assertEqual(str(w[0].message), r'"\z" is an invalid escape sequence. ' + r'Such sequences will not work in the future. ' + r'Did you mean "\\z"? A raw string is also an option.') self.assertEqual(w[0].filename, '') self.assertEqual(w[0].lineno, 1) @@ -215,7 +223,8 @@ def test_eval_bytes_invalid_escape(self): eval("b'''\n\\z'''") exc = cm.exception self.assertEqual(w, []) - self.assertEqual(exc.msg, r"invalid escape sequence '\z'") + self.assertEqual(exc.msg, r'"\z" is an invalid escape sequence. ' + r'Did you mean "\\z"? A raw string is also an option.') self.assertEqual(exc.filename, '') self.assertEqual(exc.lineno, 1) @@ -228,8 +237,9 @@ def test_eval_bytes_invalid_octal_escape(self): warnings.simplefilter('always', category=SyntaxWarning) eval("b'''\n\\407'''") self.assertEqual(len(w), 1) - self.assertEqual(str(w[0].message), - r"invalid octal escape sequence '\407'") + self.assertEqual(str(w[0].message), r'"\407" is an invalid octal escape sequence. ' + r'Such sequences will not work in the future. ' + r'Did you mean "\\407"? A raw string is also an option.') self.assertEqual(w[0].filename, '') self.assertEqual(w[0].lineno, 1) @@ -239,7 +249,8 @@ def test_eval_bytes_invalid_octal_escape(self): eval("b'''\n\\407'''") exc = cm.exception self.assertEqual(w, []) - self.assertEqual(exc.msg, r"invalid octal escape sequence '\407'") + self.assertEqual(exc.msg, r'"\407" is an invalid octal escape sequence. ' + r'Did you mean "\\407"? A raw string is also an option.') self.assertEqual(exc.filename, '') self.assertEqual(exc.lineno, 1) diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py index 35394f29fbe49d..332919540da4d6 100644 --- a/Lib/test/test_unparse.py +++ b/Lib/test/test_unparse.py @@ -651,7 +651,9 @@ def test_multiquote_joined_string(self): def test_backslash_in_format_spec(self): import re - msg = re.escape("invalid escape sequence '\\ '") + msg = re.escape('"\\ " is an invalid escape sequence. ' + 'Such sequences will not work in the future. ' + 'Did you mean "\\\\ "? A raw string is also an option.') with self.assertWarnsRegex(SyntaxWarning, msg): self.check_ast_roundtrip("""f"{x:\\ }" """) self.check_ast_roundtrip("""f"{x:\\n}" """) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-09-28-17.gh-issue-128016.DPqhah.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-09-28-17.gh-issue-128016.DPqhah.rst new file mode 100644 index 00000000000000..0832d777bc3251 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-09-28-17.gh-issue-128016.DPqhah.rst @@ -0,0 +1 @@ +Improved the ``SyntaxWarning`` message for invalid escape sequences to clarify that such sequences will raise a ``SyntaxError`` in future Python releases. The new message also suggests a potential fix, i.e., ``Did you mean "\\e"?``. diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 024653546563e6..b3d1c425ad18b7 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -1205,7 +1205,8 @@ PyObject *PyBytes_DecodeEscape(const char *s, unsigned char c = *first_invalid_escape; if ('4' <= c && c <= '7') { if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "invalid octal escape sequence '\\%.3s'", + "b\"\\%.3s\" is an invalid octal escape sequence. " + "Such sequences will not work in the future. ", first_invalid_escape) < 0) { Py_DECREF(result); @@ -1214,7 +1215,8 @@ PyObject *PyBytes_DecodeEscape(const char *s, } else { if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "invalid escape sequence '\\%c'", + "b\"\\%c\" is an invalid escape sequence. " + "Such sequences will not work in the future. ", c) < 0) { Py_DECREF(result); @@ -1223,7 +1225,6 @@ PyObject *PyBytes_DecodeEscape(const char *s, } } return result; - } /* -------------------------------------------------------------------- */ /* object api */ diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 3eafa2381c1a4d..d9952f764bb178 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -6853,7 +6853,8 @@ _PyUnicode_DecodeUnicodeEscapeStateful(const char *s, unsigned char c = *first_invalid_escape; if ('4' <= c && c <= '7') { if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "invalid octal escape sequence '\\%.3s'", + "\"\\%.3s\" is an invalid octal escape sequence. " + "Such sequences will not work in the future. ", first_invalid_escape) < 0) { Py_DECREF(result); @@ -6862,7 +6863,8 @@ _PyUnicode_DecodeUnicodeEscapeStateful(const char *s, } else { if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "invalid escape sequence '\\%c'", + "\"\\%c\" is an invalid escape sequence. " + "Such sequences will not work in the future. ", c) < 0) { Py_DECREF(result); diff --git a/Parser/string_parser.c b/Parser/string_parser.c index 9537c543b0eb93..9dd8f9ef28bd4f 100644 --- a/Parser/string_parser.c +++ b/Parser/string_parser.c @@ -28,9 +28,16 @@ warn_invalid_escape_sequence(Parser *p, const char *first_invalid_escape, Token int octal = ('4' <= c && c <= '7'); PyObject *msg = octal - ? PyUnicode_FromFormat("invalid octal escape sequence '\\%.3s'", - first_invalid_escape) - : PyUnicode_FromFormat("invalid escape sequence '\\%c'", c); + ? PyUnicode_FromFormat( + "\"\\%.3s\" is an invalid octal escape sequence. " + "Such sequences will not work in the future. " + "Did you mean \"\\\\%.3s\"? A raw string is also an option.", + first_invalid_escape, first_invalid_escape) + : PyUnicode_FromFormat( + "\"\\%c\" is an invalid escape sequence. " + "Such sequences will not work in the future. " + "Did you mean \"\\\\%c\"? A raw string is also an option.", + c, c); if (msg == NULL) { return -1; } @@ -53,11 +60,16 @@ warn_invalid_escape_sequence(Parser *p, const char *first_invalid_escape, Token error location, if p->known_err_token is not set. */ p->known_err_token = t; if (octal) { - RAISE_SYNTAX_ERROR("invalid octal escape sequence '\\%.3s'", - first_invalid_escape); + RAISE_SYNTAX_ERROR( + "\"\\%.3s\" is an invalid octal escape sequence. " + "Did you mean \"\\\\%.3s\"? A raw string is also an option.", + first_invalid_escape, first_invalid_escape); } else { - RAISE_SYNTAX_ERROR("invalid escape sequence '\\%c'", c); + RAISE_SYNTAX_ERROR( + "\"\\%c\" is an invalid escape sequence. " + "Did you mean \"\\\\%c\"? A raw string is also an option.", + c, c); } } Py_DECREF(msg); diff --git a/Parser/tokenizer/helpers.c b/Parser/tokenizer/helpers.c index 9c9d05bbef0f1a..5a416adb875aa1 100644 --- a/Parser/tokenizer/helpers.c +++ b/Parser/tokenizer/helpers.c @@ -113,7 +113,10 @@ _PyTokenizer_warn_invalid_escape_sequence(struct tok_state *tok, int first_inval } PyObject *msg = PyUnicode_FromFormat( - "invalid escape sequence '\\%c'", + "\"\\%c\" is an invalid escape sequence. " + "Such sequences will not work in the future. " + "Did you mean \"\\\\%c\"? A raw string is also an option.", + (char) first_invalid_escape_char, (char) first_invalid_escape_char ); @@ -129,7 +132,12 @@ _PyTokenizer_warn_invalid_escape_sequence(struct tok_state *tok, int first_inval /* Replace the SyntaxWarning exception with a SyntaxError to get a more accurate error report */ PyErr_Clear(); - return _PyTokenizer_syntaxerror(tok, "invalid escape sequence '\\%c'", (char) first_invalid_escape_char); + + return _PyTokenizer_syntaxerror(tok, + "\"\\%c\" is an invalid escape sequence. " + "Did you mean \"\\\\%c\"? A raw string is also an option.", + (char) first_invalid_escape_char, + (char) first_invalid_escape_char); } return -1; From 080f444a58dae1bb76a87ce746a09fd499792cfb Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Wed, 15 Jan 2025 11:27:28 -0800 Subject: [PATCH 323/376] gh-128807: Add marking phase for free-threaded cyclic GC (gh-128808) --- Include/internal/pycore_gc.h | 16 +- ...-01-13-17-03-49.gh-issue-128807.BGGBxD.rst | 6 + Python/gc_free_threading.c | 336 +++++++++++++++++- 3 files changed, 336 insertions(+), 22 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-13-17-03-49.gh-issue-128807.BGGBxD.rst diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index 4ff34bf8ead7d0..b1806df2706097 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -45,12 +45,13 @@ static inline PyObject* _Py_FROM_GC(PyGC_Head *gc) { * the per-object lock. */ #ifdef Py_GIL_DISABLED -# define _PyGC_BITS_TRACKED (1) // Tracked by the GC -# define _PyGC_BITS_FINALIZED (2) // tp_finalize was called -# define _PyGC_BITS_UNREACHABLE (4) -# define _PyGC_BITS_FROZEN (8) -# define _PyGC_BITS_SHARED (16) -# define _PyGC_BITS_DEFERRED (64) // Use deferred reference counting +# define _PyGC_BITS_TRACKED (1<<0) // Tracked by the GC +# define _PyGC_BITS_FINALIZED (1<<1) // tp_finalize was called +# define _PyGC_BITS_UNREACHABLE (1<<2) +# define _PyGC_BITS_FROZEN (1<<3) +# define _PyGC_BITS_SHARED (1<<4) +# define _PyGC_BITS_ALIVE (1<<5) // Reachable from a known root. +# define _PyGC_BITS_DEFERRED (1<<6) // Use deferred reference counting #endif #ifdef Py_GIL_DISABLED @@ -330,6 +331,9 @@ struct _gc_runtime_state { collections, and are awaiting to undergo a full collection for the first time. */ Py_ssize_t long_lived_pending; + + /* True if gc.freeze() has been used. */ + int freeze_active; #endif }; diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-13-17-03-49.gh-issue-128807.BGGBxD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-13-17-03-49.gh-issue-128807.BGGBxD.rst new file mode 100644 index 00000000000000..34952e9abb66e5 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-13-17-03-49.gh-issue-128807.BGGBxD.rst @@ -0,0 +1,6 @@ +Add a marking phase to the free-threaded GC. This is similar to what was +done in GH-126491. Since the free-threaded GC does not have generations and +is not incremental, the marking phase looks for all objects reachable from +known roots. The roots are objects known to not be garbage, like the module +dictionary for :mod:`sys`. For most programs, this marking phase should +make the GC a bit faster since typically less work is done per object. diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index f7f44407494e51..d1023d9351086f 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -17,6 +17,17 @@ #include "pydtrace.h" #include "pycore_uniqueid.h" // _PyObject_MergeThreadLocalRefcounts() + +// enable the "mark alive" pass of GC +#define GC_ENABLE_MARK_ALIVE 1 + +// include additional roots in "mark alive" pass +#define GC_MARK_ALIVE_EXTRA_ROOTS 1 + +// include Python stacks as set of known roots +#define GC_MARK_ALIVE_STACKS 1 + + #ifdef Py_GIL_DISABLED typedef struct _gc_runtime_state GCState; @@ -113,28 +124,66 @@ worklist_remove(struct worklist_iter *iter) iter->next = iter->ptr; } +static inline int +gc_has_bit(PyObject *op, uint8_t bit) +{ + return (op->ob_gc_bits & bit) != 0; +} + +static inline void +gc_set_bit(PyObject *op, uint8_t bit) +{ + op->ob_gc_bits |= bit; +} + +static inline void +gc_clear_bit(PyObject *op, uint8_t bit) +{ + op->ob_gc_bits &= ~bit; +} + static inline int gc_is_frozen(PyObject *op) { - return (op->ob_gc_bits & _PyGC_BITS_FROZEN) != 0; + return gc_has_bit(op, _PyGC_BITS_FROZEN); } static inline int gc_is_unreachable(PyObject *op) { - return (op->ob_gc_bits & _PyGC_BITS_UNREACHABLE) != 0; + return gc_has_bit(op, _PyGC_BITS_UNREACHABLE); } -static void +static inline void gc_set_unreachable(PyObject *op) { - op->ob_gc_bits |= _PyGC_BITS_UNREACHABLE; + gc_set_bit(op, _PyGC_BITS_UNREACHABLE); } -static void +static inline void gc_clear_unreachable(PyObject *op) { - op->ob_gc_bits &= ~_PyGC_BITS_UNREACHABLE; + gc_clear_bit(op, _PyGC_BITS_UNREACHABLE); +} + +static inline int +gc_is_alive(PyObject *op) +{ + return gc_has_bit(op, _PyGC_BITS_ALIVE); +} + +#ifdef GC_ENABLE_MARK_ALIVE +static inline void +gc_set_alive(PyObject *op) +{ + gc_set_bit(op, _PyGC_BITS_ALIVE); +} +#endif + +static inline void +gc_clear_alive(PyObject *op) +{ + gc_clear_bit(op, _PyGC_BITS_ALIVE); } // Initialize the `ob_tid` field to zero if the object is not already @@ -143,6 +192,7 @@ static void gc_maybe_init_refs(PyObject *op) { if (!gc_is_unreachable(op)) { + assert(!gc_is_alive(op)); gc_set_unreachable(op); op->ob_tid = 0; } @@ -264,9 +314,13 @@ static void gc_restore_refs(PyObject *op) { if (gc_is_unreachable(op)) { + assert(!gc_is_alive(op)); gc_restore_tid(op); gc_clear_unreachable(op); } + else { + gc_clear_alive(op); + } } // Given a mimalloc memory block return the PyObject stored in it or NULL if @@ -392,6 +446,119 @@ gc_visit_thread_stacks(PyInterpreterState *interp) _Py_FOR_EACH_TSTATE_END(interp); } +// Untrack objects that can never create reference cycles. +// Return true if the object was untracked. +static bool +gc_maybe_untrack(PyObject *op) +{ + // Currently we only check for tuples containing only non-GC objects. In + // theory we could check other immutable objects that contain references + // to non-GC objects. + if (PyTuple_CheckExact(op)) { + _PyTuple_MaybeUntrack(op); + if (!_PyObject_GC_IS_TRACKED(op)) { + return true; + } + } + return false; +} + +#ifdef GC_ENABLE_MARK_ALIVE +static int +mark_alive_stack_push(PyObject *op, _PyObjectStack *stack) +{ + if (op == NULL) { + return 0; + } + if (!_PyObject_GC_IS_TRACKED(op)) { + return 0; + } + if (gc_is_alive(op)) { + return 0; // already visited this object + } + if (gc_maybe_untrack(op)) { + return 0; // was untracked, don't visit it + } + + // Need to call tp_traverse on this object. Add to stack and mark it + // alive so we don't traverse it a second time. + gc_set_alive(op); + if (_PyObjectStack_Push(stack, op) < 0) { + return -1; + } + return 0; +} + +static bool +gc_clear_alive_bits(const mi_heap_t *heap, const mi_heap_area_t *area, + void *block, size_t block_size, void *args) +{ + PyObject *op = op_from_block(block, args, false); + if (op == NULL) { + return true; + } + if (gc_is_alive(op)) { + gc_clear_alive(op); + } + return true; +} + +static void +gc_abort_mark_alive(PyInterpreterState *interp, + struct collection_state *state, + _PyObjectStack *stack) +{ + // We failed to allocate memory for "stack" while doing the "mark + // alive" phase. In that case, free the object stack and make sure + // that no objects have the alive bit set. + _PyObjectStack_Clear(stack); + gc_visit_heaps(interp, &gc_clear_alive_bits, &state->base); +} + +#ifdef GC_MARK_ALIVE_STACKS +static int +gc_visit_stackref_mark_alive(_PyObjectStack *stack, _PyStackRef stackref) +{ + // Note: we MUST check that it is deferred before checking the rest. + // Otherwise we might read into invalid memory due to non-deferred references + // being dead already. + if (PyStackRef_IsDeferred(stackref) && !PyStackRef_IsNull(stackref)) { + PyObject *op = PyStackRef_AsPyObjectBorrow(stackref); + if (mark_alive_stack_push(op, stack) < 0) { + return -1; + } + } + return 0; +} + +static int +gc_visit_thread_stacks_mark_alive(PyInterpreterState *interp, _PyObjectStack *stack) +{ + _Py_FOR_EACH_TSTATE_BEGIN(interp, p) { + for (_PyInterpreterFrame *f = p->current_frame; f != NULL; f = f->previous) { + PyObject *executable = PyStackRef_AsPyObjectBorrow(f->f_executable); + if (executable == NULL || !PyCode_Check(executable)) { + continue; + } + + PyCodeObject *co = (PyCodeObject *)executable; + int max_stack = co->co_nlocalsplus + co->co_stacksize; + if (gc_visit_stackref_mark_alive(stack, f->f_executable) < 0) { + return -1; + } + for (int i = 0; i < max_stack; i++) { + if (gc_visit_stackref_mark_alive(stack, f->localsplus[i]) < 0) { + return -1; + } + } + } + } + _Py_FOR_EACH_TSTATE_END(interp); + return 0; +} +#endif // GC_MARK_ALIVE_STACKS +#endif // GC_ENABLE_MARK_ALIVE + static void queue_untracked_obj_decref(PyObject *op, struct collection_state *state) { @@ -460,7 +627,8 @@ visit_decref(PyObject *op, void *arg) { if (_PyObject_GC_IS_TRACKED(op) && !_Py_IsImmortal(op) - && !gc_is_frozen(op)) + && !gc_is_frozen(op) + && !gc_is_alive(op)) { // If update_refs hasn't reached this object yet, mark it // as (tentatively) unreachable and initialize ob_tid to zero. @@ -482,6 +650,10 @@ update_refs(const mi_heap_t *heap, const mi_heap_area_t *area, return true; } + if (gc_is_alive(op)) { + return true; + } + // Exclude immortal objects from garbage collection if (_Py_IsImmortal(op)) { op->ob_tid = 0; @@ -497,14 +669,9 @@ update_refs(const mi_heap_t *heap, const mi_heap_area_t *area, _PyObject_ASSERT(op, refcount >= 0); if (refcount > 0 && !_PyObject_HasDeferredRefcount(op)) { - // Untrack tuples and dicts as necessary in this pass, but not objects - // with zero refcount, which we will want to collect. - if (PyTuple_CheckExact(op)) { - _PyTuple_MaybeUntrack(op); - if (!_PyObject_GC_IS_TRACKED(op)) { - gc_restore_refs(op); - return true; - } + if (gc_maybe_untrack(op)) { + gc_restore_refs(op); + return true; } } @@ -553,6 +720,21 @@ mark_reachable(PyObject *op) } #ifdef GC_DEBUG +static bool +validate_alive_bits(const mi_heap_t *heap, const mi_heap_area_t *area, + void *block, size_t block_size, void *args) +{ + PyObject *op = op_from_block(block, args, false); + if (op == NULL) { + return true; + } + + _PyObject_ASSERT_WITH_MSG(op, !gc_is_alive(op), + "object should not be marked as alive yet"); + + return true; +} + static bool validate_refcounts(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) @@ -586,6 +768,11 @@ validate_gc_objects(const mi_heap_t *heap, const mi_heap_area_t *area, return true; } + if (gc_is_alive(op)) { + _PyObject_ASSERT(op, !gc_is_unreachable(op)); + return true; + } + _PyObject_ASSERT(op, gc_is_unreachable(op)); _PyObject_ASSERT_WITH_MSG(op, gc_get_refs(op) >= 0, "refcount is too small"); @@ -602,6 +789,10 @@ mark_heap_visitor(const mi_heap_t *heap, const mi_heap_area_t *area, return true; } + if (gc_is_alive(op)) { + return true; + } + _PyObject_ASSERT_WITH_MSG(op, gc_get_refs(op) >= 0, "refcount is too small"); @@ -630,6 +821,7 @@ restore_refs(const mi_heap_t *heap, const mi_heap_area_t *area, } gc_restore_tid(op); gc_clear_unreachable(op); + gc_clear_alive(op); return true; } @@ -679,6 +871,7 @@ scan_heap_visitor(const mi_heap_t *heap, const mi_heap_area_t *area, // object is reachable, restore `ob_tid`; we're done with these objects gc_restore_tid(op); + gc_clear_alive(op); state->long_lived_total++; return true; } @@ -686,6 +879,89 @@ scan_heap_visitor(const mi_heap_t *heap, const mi_heap_area_t *area, static int move_legacy_finalizer_reachable(struct collection_state *state); +#ifdef GC_ENABLE_MARK_ALIVE +static int +propagate_alive_bits(_PyObjectStack *stack) +{ + for (;;) { + PyObject *op = _PyObjectStack_Pop(stack); + if (op == NULL) { + break; + } + assert(_PyObject_GC_IS_TRACKED(op)); + assert(gc_is_alive(op)); + traverseproc traverse = Py_TYPE(op)->tp_traverse; + if (traverse(op, (visitproc)&mark_alive_stack_push, stack) < 0) { + return -1; + } + } + return 0; +} + +// Using tp_traverse, mark everything reachable from known root objects +// (which must be non-garbage) as alive (_PyGC_BITS_ALIVE is set). In +// most programs, this marks nearly all objects that are not actually +// unreachable. +// +// Actually alive objects can be missed in this pass if they are alive +// due to being referenced from an unknown root (e.g. an extension +// module global), some tp_traverse methods are either missing or not +// accurate, or objects that have been untracked. Objects that are only +// reachable from the aforementioned are also missed. +// +// If gc.freeze() is used, this pass is disabled since it is unlikely to +// help much. The next stages of cyclic GC will ignore objects with the +// alive bit set. +// +// Returns -1 on failure (out of memory). +static int +mark_alive_from_roots(PyInterpreterState *interp, + struct collection_state *state) +{ +#ifdef GC_DEBUG + // Check that all objects don't have alive bit set + gc_visit_heaps(interp, &validate_alive_bits, &state->base); +#endif + _PyObjectStack stack = { NULL }; + + #define STACK_PUSH(op) \ + if (mark_alive_stack_push(op, &stack) < 0) { \ + gc_abort_mark_alive(interp, state, &stack); \ + return -1; \ + } + STACK_PUSH(interp->sysdict); +#ifdef GC_MARK_ALIVE_EXTRA_ROOTS + STACK_PUSH(interp->builtins); + STACK_PUSH(interp->dict); + struct types_state *types = &interp->types; + for (int i = 0; i < _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES; i++) { + STACK_PUSH(types->builtins.initialized[i].tp_dict); + STACK_PUSH(types->builtins.initialized[i].tp_subclasses); + } + for (int i = 0; i < _Py_MAX_MANAGED_STATIC_EXT_TYPES; i++) { + STACK_PUSH(types->for_extensions.initialized[i].tp_dict); + STACK_PUSH(types->for_extensions.initialized[i].tp_subclasses); + } +#endif +#ifdef GC_MARK_ALIVE_STACKS + if (gc_visit_thread_stacks_mark_alive(interp, &stack) < 0) { + gc_abort_mark_alive(interp, state, &stack); + return -1; + } +#endif + #undef STACK_PUSH + + // Use tp_traverse to find everything reachable from roots. + if (propagate_alive_bits(&stack) < 0) { + gc_abort_mark_alive(interp, state, &stack); + return -1; + } + + return 0; +} +#endif // GC_ENABLE_MARK_ALIVE + + static int deduce_unreachable_heap(PyInterpreterState *interp, struct collection_state *state) @@ -1245,6 +1521,25 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state, process_delayed_frees(interp, state); + #ifdef GC_ENABLE_MARK_ALIVE + // If gc.freeze() was used, it seems likely that doing this "mark alive" + // pass will not be a performance win. Typically the majority of alive + // objects will be marked as frozen and will be skipped anyhow, without + // doing this extra work. Doing this pass also defeats one of the + // purposes of using freeze: avoiding writes to objects that are frozen. + // So, we just skip this if gc.freeze() was used. + if (!state->gcstate->freeze_active) { + // Mark objects reachable from known roots as "alive". These will + // be ignored for rest of the GC pass. + int err = mark_alive_from_roots(interp, state); + if (err < 0) { + _PyEval_StartTheWorld(interp); + PyErr_NoMemory(); + return; + } + } + #endif + // Find unreachable objects int err = deduce_unreachable_heap(interp, state); if (err < 0) { @@ -1253,6 +1548,11 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state, return; } +#ifdef GC_DEBUG + // At this point, no object should have the alive bit set + gc_visit_heaps(interp, &validate_alive_bits, &state->base); +#endif + // Print debugging information. if (interp->gc.debug & _PyGC_DEBUG_COLLECTABLE) { PyObject *op; @@ -1564,6 +1864,8 @@ _PyGC_Freeze(PyInterpreterState *interp) { struct visitor_args args; _PyEval_StopTheWorld(interp); + GCState *gcstate = get_gc_state(); + gcstate->freeze_active = true; gc_visit_heaps(interp, &visit_freeze, &args); _PyEval_StartTheWorld(interp); } @@ -1574,7 +1876,7 @@ visit_unfreeze(const mi_heap_t *heap, const mi_heap_area_t *area, { PyObject *op = op_from_block(block, args, true); if (op != NULL) { - op->ob_gc_bits &= ~_PyGC_BITS_FROZEN; + gc_clear_bit(op, _PyGC_BITS_FROZEN); } return true; } @@ -1584,6 +1886,8 @@ _PyGC_Unfreeze(PyInterpreterState *interp) { struct visitor_args args; _PyEval_StopTheWorld(interp); + GCState *gcstate = get_gc_state(); + gcstate->freeze_active = false; gc_visit_heaps(interp, &visit_unfreeze, &args); _PyEval_StartTheWorld(interp); } From 36c5e3bcc25700645d19eba65dcdf22acd99a7a8 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 15 Jan 2025 21:22:44 +0100 Subject: [PATCH 324/376] gh-128679: Redesign tracemalloc locking (#128888) * Use TABLES_LOCK() to protect 'tracemalloc_config.tracing'. * Hold TABLES_LOCK() longer while accessing tables. * tracemalloc_realloc() and tracemalloc_free() no longer remove the trace on reentrant call. * _PyTraceMalloc_Stop() unregisters _PyTraceMalloc_TraceRef(). * _PyTraceMalloc_GetTraces() sets the reentrant flag. * tracemalloc_clear_traces_unlocked() sets the reentrant flag. --- Include/internal/pycore_object.h | 6 - Include/internal/pycore_tracemalloc.h | 10 +- Modules/_tracemalloc.c | 16 +- Python/pylifecycle.c | 7 +- Python/tracemalloc.c | 611 ++++++++++++-------------- 5 files changed, 294 insertions(+), 356 deletions(-) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index e26cb7673f939c..322305bc8c664a 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -299,12 +299,6 @@ Py_ssize_t _Py_ExplicitMergeRefcount(PyObject *op, Py_ssize_t extra); extern int _PyType_CheckConsistency(PyTypeObject *type); extern int _PyDict_CheckConsistency(PyObject *mp, int check_content); -/* Update the Python traceback of an object. This function must be called - when a memory block is reused from a free list. - - Internal function called by _Py_NewReference(). */ -extern int _PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event, void*); - // Fast inlined version of PyType_HasFeature() static inline int _PyType_HasFeature(PyTypeObject *type, unsigned long feature) { diff --git a/Include/internal/pycore_tracemalloc.h b/Include/internal/pycore_tracemalloc.h index 7ddc5bac5d10af..9fed393ac9e60e 100644 --- a/Include/internal/pycore_tracemalloc.h +++ b/Include/internal/pycore_tracemalloc.h @@ -25,7 +25,7 @@ struct _PyTraceMalloc_Config { } initialized; /* Is tracemalloc tracing memory allocations? - Variable protected by the GIL */ + Variable protected by the TABLES_LOCK(). */ int tracing; /* limit of the number of frames in a traceback, 1 by default. @@ -85,14 +85,14 @@ struct _tracemalloc_runtime_state { size_t peak_traced_memory; /* Hash table used as a set to intern filenames: PyObject* => PyObject*. - Protected by the GIL */ + Protected by the TABLES_LOCK(). */ _Py_hashtable_t *filenames; /* Buffer to store a new traceback in traceback_new(). - Protected by the GIL. */ + Protected by the TABLES_LOCK(). */ struct tracemalloc_traceback *traceback; /* Hash table used as a set to intern tracebacks: traceback_t* => traceback_t* - Protected by the GIL */ + Protected by the TABLES_LOCK(). */ _Py_hashtable_t *tracebacks; /* pointer (void*) => trace (trace_t*). Protected by TABLES_LOCK(). */ @@ -144,7 +144,7 @@ extern PyObject* _PyTraceMalloc_GetTraces(void); extern PyObject* _PyTraceMalloc_GetObjectTraceback(PyObject *obj); /* Initialize tracemalloc */ -extern int _PyTraceMalloc_Init(void); +extern PyStatus _PyTraceMalloc_Init(void); /* Start tracemalloc */ extern int _PyTraceMalloc_Start(int max_nframe); diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index 887a1e820e250e..be71fc9fc9c341 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -215,18 +215,14 @@ static struct PyModuleDef module_def = { PyMODINIT_FUNC PyInit__tracemalloc(void) { - PyObject *m; - m = PyModule_Create(&module_def); - if (m == NULL) + PyObject *mod = PyModule_Create(&module_def); + if (mod == NULL) { return NULL; + } + #ifdef Py_GIL_DISABLED - PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); + PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED); #endif - if (_PyTraceMalloc_Init() < 0) { - Py_DECREF(m); - return NULL; - } - - return m; + return mod; } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 9a8a92aaceb0be..f1ecee6a92e5a1 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -707,7 +707,12 @@ pycore_create_interpreter(_PyRuntimeState *runtime, // the settings are loaded (so that feature_flags are set) but before // any calls are made to obmalloc functions. if (_PyMem_init_obmalloc(interp) < 0) { - return _PyStatus_NO_MEMORY(); + return _PyStatus_NO_MEMORY(); + } + + status = _PyTraceMalloc_Init(); + if (_PyStatus_EXCEPTION(status)) { + return status; } PyThreadState *tstate = _PyThreadState_New(interp, diff --git a/Python/tracemalloc.c b/Python/tracemalloc.c index f661d69c0312fa..68b641c51868b9 100644 --- a/Python/tracemalloc.c +++ b/Python/tracemalloc.c @@ -2,6 +2,7 @@ #include "pycore_fileutils.h" // _Py_write_noraise() #include "pycore_gc.h" // PyGC_Head #include "pycore_hashtable.h" // _Py_hashtable_t +#include "pycore_initconfig.h" // _PyStatus_NO_MEMORY() #include "pycore_object.h" // _PyType_PreHeaderSize() #include "pycore_pymem.h" // _Py_tracemalloc_config #include "pycore_runtime.h" // _Py_ID() @@ -19,6 +20,8 @@ _Py_DECLARE_STR(anon_unknown, ""); /* Forward declaration */ static void* raw_malloc(size_t size); static void raw_free(void *ptr); +static int _PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event, + void* Py_UNUSED(ignore)); #ifdef Py_DEBUG # define TRACE_DEBUG @@ -106,16 +109,16 @@ tracemalloc_error(const char *format, ...) static int get_reentrant(void) { - void *ptr; - assert(PyThread_tss_is_created(&tracemalloc_reentrant_key)); - ptr = PyThread_tss_get(&tracemalloc_reentrant_key); + + void *ptr = PyThread_tss_get(&tracemalloc_reentrant_key); if (ptr != NULL) { assert(ptr == REENTRANT); return 1; } - else + else { return 0; + } } static void @@ -252,6 +255,7 @@ tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame) { assert(PyStackRef_CodeCheck(pyframe->f_executable)); frame->filename = &_Py_STR(anon_unknown); + int lineno = PyUnstable_InterpreterFrame_GetLine(pyframe); if (lineno < 0) { lineno = 0; @@ -259,7 +263,6 @@ tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame) frame->lineno = (unsigned int)lineno; PyObject *filename = filename = _PyFrame_GetCode(pyframe)->co_filename; - if (filename == NULL) { #ifdef TRACE_DEBUG tracemalloc_error("failed to get the filename of the code object"); @@ -275,7 +278,7 @@ tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame) } if (!PyUnicode_IS_READY(filename)) { /* Don't make a Unicode string ready to avoid reentrant calls - to tracemalloc_malloc() or tracemalloc_realloc() */ + to tracemalloc_alloc() or tracemalloc_realloc() */ #ifdef TRACE_DEBUG tracemalloc_error("filename is not a ready unicode string"); #endif @@ -309,7 +312,7 @@ tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame) static Py_uhash_t traceback_hash(traceback_t *traceback) { - /* code based on tuplehash() of Objects/tupleobject.c */ + /* code based on tuple_hash() of Objects/tupleobject.c */ Py_uhash_t x, y; /* Unsigned for defined overflow behavior. */ int len = traceback->nframe; Py_uhash_t mult = PyHASH_MULTIPLIER; @@ -440,7 +443,7 @@ tracemalloc_get_traces_table(unsigned int domain) static void -tracemalloc_remove_trace(unsigned int domain, uintptr_t ptr) +tracemalloc_remove_trace_unlocked(unsigned int domain, uintptr_t ptr) { assert(tracemalloc_config.tracing); @@ -459,12 +462,12 @@ tracemalloc_remove_trace(unsigned int domain, uintptr_t ptr) } #define REMOVE_TRACE(ptr) \ - tracemalloc_remove_trace(DEFAULT_DOMAIN, (uintptr_t)(ptr)) + tracemalloc_remove_trace_unlocked(DEFAULT_DOMAIN, (uintptr_t)(ptr)) static int -tracemalloc_add_trace(unsigned int domain, uintptr_t ptr, - size_t size) +tracemalloc_add_trace_unlocked(unsigned int domain, uintptr_t ptr, + size_t size) { assert(tracemalloc_config.tracing); @@ -519,82 +522,138 @@ tracemalloc_add_trace(unsigned int domain, uintptr_t ptr, } #define ADD_TRACE(ptr, size) \ - tracemalloc_add_trace(DEFAULT_DOMAIN, (uintptr_t)(ptr), size) + tracemalloc_add_trace_unlocked(DEFAULT_DOMAIN, (uintptr_t)(ptr), size) static void* -tracemalloc_alloc(int use_calloc, void *ctx, size_t nelem, size_t elsize) +tracemalloc_alloc(int need_gil, int use_calloc, + void *ctx, size_t nelem, size_t elsize) { - PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; - void *ptr; - assert(elsize == 0 || nelem <= SIZE_MAX / elsize); - if (use_calloc) + int reentrant = get_reentrant(); + + // Ignore reentrant call. + // + // For example, PyObjet_Malloc() calls + // PyMem_Malloc() for allocations larger than 512 bytes: don't trace the + // same memory allocation twice. + // + // If reentrant calls are not ignored, PyGILState_Ensure() can call + // PyMem_RawMalloc() which would call PyGILState_Ensure() again in a loop. + if (!reentrant) { + set_reentrant(1); + } + + PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; + void *ptr; + if (use_calloc) { ptr = alloc->calloc(alloc->ctx, nelem, elsize); - else + } + else { ptr = alloc->malloc(alloc->ctx, nelem * elsize); - if (ptr == NULL) - return NULL; + } + if (ptr == NULL) { + goto done; + } + if (reentrant) { + goto done; + } + + PyGILState_STATE gil_state; + if (need_gil) { + gil_state = PyGILState_Ensure(); + } TABLES_LOCK(); + if (ADD_TRACE(ptr, nelem * elsize) < 0) { - /* Failed to allocate a trace for the new memory block */ - TABLES_UNLOCK(); + // Failed to allocate a trace for the new memory block alloc->free(alloc->ctx, ptr); - return NULL; + ptr = NULL; } + TABLES_UNLOCK(); + if (need_gil) { + PyGILState_Release(gil_state); + } + +done: + if (!reentrant) { + set_reentrant(0); + } return ptr; } static void* -tracemalloc_realloc(void *ctx, void *ptr, size_t new_size) +tracemalloc_realloc(int need_gil, void *ctx, void *ptr, size_t new_size) { + int reentrant = get_reentrant(); + + // Ignore reentrant call. PyObjet_Realloc() calls PyMem_Realloc() for + // allocations larger than 512 bytes: don't trace the same memory block + // twice. + if (!reentrant) { + set_reentrant(1); + } + PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; - void *ptr2; + void *ptr2 = alloc->realloc(alloc->ctx, ptr, new_size); - ptr2 = alloc->realloc(alloc->ctx, ptr, new_size); - if (ptr2 == NULL) - return NULL; + if (ptr2 == NULL) { + goto done; + } + if (reentrant) { + goto done; + } - if (ptr != NULL) { - /* an existing memory block has been resized */ + PyGILState_STATE gil_state; + if (need_gil) { + gil_state = PyGILState_Ensure(); + } + TABLES_LOCK(); - TABLES_LOCK(); + if (ptr != NULL) { + // An existing memory block has been resized - /* tracemalloc_add_trace() updates the trace if there is already - a trace at address ptr2 */ + // tracemalloc_add_trace_unlocked() updates the trace if there is + // already a trace at address ptr2. if (ptr2 != ptr) { REMOVE_TRACE(ptr); } if (ADD_TRACE(ptr2, new_size) < 0) { - /* Memory allocation failed. The error cannot be reported to - the caller, because realloc() may already have shrunk the - memory block and so removed bytes. - - This case is very unlikely: a hash entry has just been - released, so the hash table should have at least one free entry. - - The GIL and the table lock ensures that only one thread is - allocating memory. */ + // Memory allocation failed. The error cannot be reported to the + // caller, because realloc() already have shrunk the memory block + // and so removed bytes. + // + // This case is very unlikely: a hash entry has just been released, + // so the hash table should have at least one free entry. + // + // The GIL and the table lock ensures that only one thread is + // allocating memory. Py_FatalError("tracemalloc_realloc() failed to allocate a trace"); } - TABLES_UNLOCK(); } else { - /* new allocation */ + // New allocation - TABLES_LOCK(); if (ADD_TRACE(ptr2, new_size) < 0) { - /* Failed to allocate a trace for the new memory block */ - TABLES_UNLOCK(); + // Failed to allocate a trace for the new memory block alloc->free(alloc->ctx, ptr2); - return NULL; + ptr2 = NULL; } - TABLES_UNLOCK(); + } + + TABLES_UNLOCK(); + if (need_gil) { + PyGILState_Release(gil_state); + } + +done: + if (!reentrant) { + set_reentrant(0); } return ptr2; } @@ -603,168 +662,63 @@ tracemalloc_realloc(void *ctx, void *ptr, size_t new_size) static void tracemalloc_free(void *ctx, void *ptr) { - PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; - - if (ptr == NULL) + if (ptr == NULL) { return; + } - /* GIL cannot be locked in PyMem_RawFree() because it would introduce - a deadlock in _PyThreadState_DeleteCurrent(). */ - + PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; alloc->free(alloc->ctx, ptr); - TABLES_LOCK(); - REMOVE_TRACE(ptr); - TABLES_UNLOCK(); -} - - -static void* -tracemalloc_alloc_gil(int use_calloc, void *ctx, size_t nelem, size_t elsize) -{ - void *ptr; - if (get_reentrant()) { - PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; - if (use_calloc) - return alloc->calloc(alloc->ctx, nelem, elsize); - else - return alloc->malloc(alloc->ctx, nelem * elsize); + return; } - /* Ignore reentrant call. PyObjet_Malloc() calls PyMem_Malloc() for - allocations larger than 512 bytes, don't trace the same memory - allocation twice. */ - set_reentrant(1); - - ptr = tracemalloc_alloc(use_calloc, ctx, nelem, elsize); - - set_reentrant(0); - return ptr; + TABLES_LOCK(); + REMOVE_TRACE(ptr); + TABLES_UNLOCK(); } static void* tracemalloc_malloc_gil(void *ctx, size_t size) { - return tracemalloc_alloc_gil(0, ctx, 1, size); + return tracemalloc_alloc(0, 0, ctx, 1, size); } static void* tracemalloc_calloc_gil(void *ctx, size_t nelem, size_t elsize) { - return tracemalloc_alloc_gil(1, ctx, nelem, elsize); + return tracemalloc_alloc(0, 1, ctx, nelem, elsize); } static void* tracemalloc_realloc_gil(void *ctx, void *ptr, size_t new_size) { - void *ptr2; - - if (get_reentrant()) { - /* Reentrant call to PyMem_Realloc() and PyMem_RawRealloc(). - Example: PyMem_RawRealloc() is called internally by pymalloc - (_PyObject_Malloc() and _PyObject_Realloc()) to allocate a new - arena (new_arena()). */ - PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; - - ptr2 = alloc->realloc(alloc->ctx, ptr, new_size); - if (ptr2 != NULL && ptr != NULL) { - TABLES_LOCK(); - REMOVE_TRACE(ptr); - TABLES_UNLOCK(); - } - return ptr2; - } - - /* Ignore reentrant call. PyObjet_Realloc() calls PyMem_Realloc() for - allocations larger than 512 bytes. Don't trace the same memory - allocation twice. */ - set_reentrant(1); - - ptr2 = tracemalloc_realloc(ctx, ptr, new_size); - - set_reentrant(0); - return ptr2; + return tracemalloc_realloc(0, ctx, ptr, new_size); } #ifdef TRACE_RAW_MALLOC -static void* -tracemalloc_raw_alloc(int use_calloc, void *ctx, size_t nelem, size_t elsize) -{ - PyGILState_STATE gil_state; - void *ptr; - - if (get_reentrant()) { - PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; - if (use_calloc) - return alloc->calloc(alloc->ctx, nelem, elsize); - else - return alloc->malloc(alloc->ctx, nelem * elsize); - } - - /* Ignore reentrant call. PyGILState_Ensure() may call PyMem_RawMalloc() - indirectly which would call PyGILState_Ensure() if reentrant are not - disabled. */ - set_reentrant(1); - - gil_state = PyGILState_Ensure(); - ptr = tracemalloc_alloc(use_calloc, ctx, nelem, elsize); - PyGILState_Release(gil_state); - - set_reentrant(0); - return ptr; -} - - static void* tracemalloc_raw_malloc(void *ctx, size_t size) { - return tracemalloc_raw_alloc(0, ctx, 1, size); + return tracemalloc_alloc(1, 0, ctx, 1, size); } static void* tracemalloc_raw_calloc(void *ctx, size_t nelem, size_t elsize) { - return tracemalloc_raw_alloc(1, ctx, nelem, elsize); + return tracemalloc_alloc(1, 1, ctx, nelem, elsize); } static void* tracemalloc_raw_realloc(void *ctx, void *ptr, size_t new_size) { - PyGILState_STATE gil_state; - void *ptr2; - - if (get_reentrant()) { - /* Reentrant call to PyMem_RawRealloc(). */ - PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; - - ptr2 = alloc->realloc(alloc->ctx, ptr, new_size); - - if (ptr2 != NULL && ptr != NULL) { - TABLES_LOCK(); - REMOVE_TRACE(ptr); - TABLES_UNLOCK(); - } - return ptr2; - } - - /* Ignore reentrant call. PyGILState_Ensure() may call PyMem_RawMalloc() - indirectly which would call PyGILState_Ensure() if reentrant calls are - not disabled. */ - set_reentrant(1); - - gil_state = PyGILState_Ensure(); - ptr2 = tracemalloc_realloc(ctx, ptr, new_size); - PyGILState_Release(gil_state); - - set_reentrant(0); - return ptr2; + return tracemalloc_realloc(1, ctx, ptr, new_size); } #endif /* TRACE_RAW_MALLOC */ @@ -777,48 +731,36 @@ tracemalloc_clear_filename(void *value) } -/* reentrant flag must be set to call this function and GIL must be held */ static void -tracemalloc_clear_traces(void) +tracemalloc_clear_traces_unlocked(void) { - /* The GIL protects variables against concurrent access */ + // Clearing tracemalloc_filenames requires the GIL to call Py_DECREF() assert(PyGILState_Check()); - TABLES_LOCK(); + set_reentrant(1); + _Py_hashtable_clear(tracemalloc_traces); _Py_hashtable_clear(tracemalloc_domains); + _Py_hashtable_clear(tracemalloc_tracebacks); + _Py_hashtable_clear(tracemalloc_filenames); + tracemalloc_traced_memory = 0; tracemalloc_peak_traced_memory = 0; - TABLES_UNLOCK(); - - _Py_hashtable_clear(tracemalloc_tracebacks); - _Py_hashtable_clear(tracemalloc_filenames); + set_reentrant(0); } -int +PyStatus _PyTraceMalloc_Init(void) { - if (tracemalloc_config.initialized == TRACEMALLOC_FINALIZED) { - PyErr_SetString(PyExc_RuntimeError, - "the tracemalloc module has been unloaded"); - return -1; - } - - if (tracemalloc_config.initialized == TRACEMALLOC_INITIALIZED) - return 0; + assert(tracemalloc_config.initialized == TRACEMALLOC_NOT_INITIALIZED); PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw); #ifdef REENTRANT_THREADLOCAL if (PyThread_tss_create(&tracemalloc_reentrant_key) != 0) { -#ifdef MS_WINDOWS - PyErr_SetFromWindowsErr(0); -#else - PyErr_SetFromErrno(PyExc_OSError); -#endif - return -1; + return _PyStatus_NO_MEMORY(); } #endif @@ -826,8 +768,7 @@ _PyTraceMalloc_Init(void) if (tables_lock == NULL) { tables_lock = PyThread_allocate_lock(); if (tables_lock == NULL) { - PyErr_SetString(PyExc_RuntimeError, "cannot allocate lock"); - return -1; + return _PyStatus_NO_MEMORY(); } } #endif @@ -844,9 +785,9 @@ _PyTraceMalloc_Init(void) tracemalloc_domains = tracemalloc_create_domains_table(); if (tracemalloc_filenames == NULL || tracemalloc_tracebacks == NULL - || tracemalloc_traces == NULL || tracemalloc_domains == NULL) { - PyErr_NoMemory(); - return -1; + || tracemalloc_traces == NULL || tracemalloc_domains == NULL) + { + return _PyStatus_NO_MEMORY(); } tracemalloc_empty_traceback.nframe = 1; @@ -857,7 +798,7 @@ _PyTraceMalloc_Init(void) tracemalloc_empty_traceback.hash = traceback_hash(&tracemalloc_empty_traceback); tracemalloc_config.initialized = TRACEMALLOC_INITIALIZED; - return 0; + return _PyStatus_OK(); } @@ -892,9 +833,6 @@ tracemalloc_deinit(void) int _PyTraceMalloc_Start(int max_nframe) { - PyMemAllocatorEx alloc; - size_t size; - if (max_nframe < 1 || (unsigned long) max_nframe > MAX_NFRAME) { PyErr_Format(PyExc_ValueError, "the number of frames must be in range [1; %lu]", @@ -902,23 +840,15 @@ _PyTraceMalloc_Start(int max_nframe) return -1; } - if (_PyTraceMalloc_Init() < 0) { - return -1; - } - - if (PyRefTracer_SetTracer(_PyTraceMalloc_TraceRef, NULL) < 0) { - return -1; - } - - if (tracemalloc_config.tracing) { - /* hook already installed: do nothing */ + if (_PyTraceMalloc_IsTracing()) { + /* hooks already installed: do nothing */ return 0; } tracemalloc_config.max_nframe = max_nframe; /* allocate a buffer to store a new traceback */ - size = TRACEBACK_SIZE(max_nframe); + size_t size = TRACEBACK_SIZE(max_nframe); assert(tracemalloc_traceback == NULL); tracemalloc_traceback = raw_malloc(size); if (tracemalloc_traceback == NULL) { @@ -926,6 +856,7 @@ _PyTraceMalloc_Start(int max_nframe) return -1; } + PyMemAllocatorEx alloc; #ifdef TRACE_RAW_MALLOC alloc.malloc = tracemalloc_raw_malloc; alloc.calloc = tracemalloc_raw_calloc; @@ -950,8 +881,14 @@ _PyTraceMalloc_Start(int max_nframe) PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, &allocators.obj); PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc); + if (PyRefTracer_SetTracer(_PyTraceMalloc_TraceRef, NULL) < 0) { + return -1; + } + /* everything is ready: start tracing Python memory allocations */ + TABLES_LOCK(); tracemalloc_config.tracing = 1; + TABLES_UNLOCK(); return 0; } @@ -960,8 +897,11 @@ _PyTraceMalloc_Start(int max_nframe) void _PyTraceMalloc_Stop(void) { - if (!tracemalloc_config.tracing) - return; + TABLES_LOCK(); + + if (!tracemalloc_config.tracing) { + goto done; + } /* stop tracing Python memory allocations */ tracemalloc_config.tracing = 0; @@ -973,11 +913,16 @@ _PyTraceMalloc_Stop(void) PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &allocators.mem); PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &allocators.obj); - tracemalloc_clear_traces(); + tracemalloc_clear_traces_unlocked(); /* release memory */ raw_free(tracemalloc_traceback); tracemalloc_traceback = NULL; + + (void)PyRefTracer_SetTracer(NULL, NULL); + +done: + TABLES_UNLOCK(); } @@ -985,15 +930,16 @@ _PyTraceMalloc_Stop(void) static PyObject* frame_to_pyobject(frame_t *frame) { - PyObject *frame_obj, *lineno_obj; + assert(get_reentrant()); - frame_obj = PyTuple_New(2); - if (frame_obj == NULL) + PyObject *frame_obj = PyTuple_New(2); + if (frame_obj == NULL) { return NULL; + } PyTuple_SET_ITEM(frame_obj, 0, Py_NewRef(frame->filename)); - lineno_obj = PyLong_FromUnsignedLong(frame->lineno); + PyObject *lineno_obj = PyLong_FromUnsignedLong(frame->lineno); if (lineno_obj == NULL) { Py_DECREF(frame_obj); return NULL; @@ -1008,7 +954,6 @@ static PyObject* traceback_to_pyobject(traceback_t *traceback, _Py_hashtable_t *intern_table) { PyObject *frames; - if (intern_table != NULL) { frames = _Py_hashtable_get(intern_table, (const void *)traceback); if (frames) { @@ -1017,8 +962,9 @@ traceback_to_pyobject(traceback_t *traceback, _Py_hashtable_t *intern_table) } frames = PyTuple_New(traceback->nframe); - if (frames == NULL) + if (frames == NULL) { return NULL; + } for (int i=0; i < traceback->nframe; i++) { PyObject *frame = frame_to_pyobject(&traceback->frames[i]); @@ -1046,14 +992,14 @@ static PyObject* trace_to_pyobject(unsigned int domain, const trace_t *trace, _Py_hashtable_t *intern_tracebacks) { - PyObject *trace_obj = NULL; - PyObject *obj; + assert(get_reentrant()); - trace_obj = PyTuple_New(4); - if (trace_obj == NULL) + PyObject *trace_obj = PyTuple_New(4); + if (trace_obj == NULL) { return NULL; + } - obj = PyLong_FromSize_t(domain); + PyObject *obj = PyLong_FromSize_t(domain); if (obj == NULL) { Py_DECREF(trace_obj); return NULL; @@ -1100,7 +1046,6 @@ tracemalloc_copy_trace(_Py_hashtable_t *traces, void *user_data) { _Py_hashtable_t *traces2 = (_Py_hashtable_t *)user_data; - trace_t *trace = (trace_t *)value; trace_t *trace2 = raw_malloc(sizeof(trace_t)); @@ -1141,7 +1086,6 @@ tracemalloc_copy_domain(_Py_hashtable_t *domains, void *user_data) { _Py_hashtable_t *domains2 = (_Py_hashtable_t *)user_data; - unsigned int domain = (unsigned int)FROM_PTR(key); _Py_hashtable_t *traces = (_Py_hashtable_t *)value; @@ -1182,7 +1126,6 @@ tracemalloc_get_traces_fill(_Py_hashtable_t *traces, void *user_data) { get_traces_t *get_traces = user_data; - const trace_t *trace = (const trace_t *)value; PyObject *tuple = trace_to_pyobject(get_traces->domain, trace, @@ -1196,7 +1139,6 @@ tracemalloc_get_traces_fill(_Py_hashtable_t *traces, if (res < 0) { return 1; } - return 0; } @@ -1207,7 +1149,6 @@ tracemalloc_get_traces_domain(_Py_hashtable_t *domains, void *user_data) { get_traces_t *get_traces = user_data; - unsigned int domain = (unsigned int)FROM_PTR(key); _Py_hashtable_t *traces = (_Py_hashtable_t *)value; @@ -1227,27 +1168,21 @@ tracemalloc_pyobject_decref(void *value) static traceback_t* -tracemalloc_get_traceback(unsigned int domain, uintptr_t ptr) +tracemalloc_get_traceback_unlocked(unsigned int domain, uintptr_t ptr) { - - if (!tracemalloc_config.tracing) + if (!tracemalloc_config.tracing) { return NULL; + } - trace_t *trace; - TABLES_LOCK(); _Py_hashtable_t *traces = tracemalloc_get_traces_table(domain); - if (traces) { - trace = _Py_hashtable_get(traces, TO_PTR(ptr)); - } - else { - trace = NULL; + if (!traces) { + return NULL; } - TABLES_UNLOCK(); + trace_t *trace = _Py_hashtable_get(traces, TO_PTR(ptr)); if (!trace) { return NULL; } - return trace->traceback; } @@ -1269,24 +1204,28 @@ _PyMem_DumpFrame(int fd, frame_t * frame) void _PyMem_DumpTraceback(int fd, const void *ptr) { - traceback_t *traceback; - int i; - + TABLES_LOCK(); if (!tracemalloc_config.tracing) { PUTS(fd, "Enable tracemalloc to get the memory block " "allocation traceback\n\n"); - return; + goto done; } - traceback = tracemalloc_get_traceback(DEFAULT_DOMAIN, (uintptr_t)ptr); - if (traceback == NULL) - return; + traceback_t *traceback; + traceback = tracemalloc_get_traceback_unlocked(DEFAULT_DOMAIN, + (uintptr_t)ptr); + if (traceback == NULL) { + goto done; + } PUTS(fd, "Memory block allocated at (most recent call first):\n"); - for (i=0; i < traceback->nframe; i++) { + for (int i=0; i < traceback->nframe; i++) { _PyMem_DumpFrame(fd, &traceback->frames[i]); } PUTS(fd, "\n"); + +done: + TABLES_UNLOCK(); } #undef PUTS @@ -1307,38 +1246,42 @@ int PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr, size_t size) { - int res; - PyGILState_STATE gil_state; + PyGILState_STATE gil_state = PyGILState_Ensure(); + TABLES_LOCK(); - if (!tracemalloc_config.tracing) { + int result; + if (tracemalloc_config.tracing) { + result = tracemalloc_add_trace_unlocked(domain, ptr, size); + } + else { /* tracemalloc is not tracing: do nothing */ - return -2; + result = -2; } - gil_state = PyGILState_Ensure(); - - TABLES_LOCK(); - res = tracemalloc_add_trace(domain, ptr, size); TABLES_UNLOCK(); - PyGILState_Release(gil_state); - return res; + + return result; } int PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr) { - if (!tracemalloc_config.tracing) { + TABLES_LOCK(); + + int result; + if (tracemalloc_config.tracing) { + tracemalloc_remove_trace_unlocked(domain, ptr); + result = 0; + } + else { /* tracemalloc is not tracing: do nothing */ - return -2; + result = -2; } - TABLES_LOCK(); - tracemalloc_remove_trace(domain, ptr); TABLES_UNLOCK(); - - return 0; + return result; } @@ -1355,87 +1298,101 @@ _PyTraceMalloc_Fini(void) Do nothing if tracemalloc is not tracing memory allocations or if the object memory block is not already traced. */ -int -_PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event, void* Py_UNUSED(ignore)) +static int +_PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event, + void* Py_UNUSED(ignore)) { if (event != PyRefTracer_CREATE) { return 0; } + if (get_reentrant()) { + return 0; + } assert(PyGILState_Check()); + TABLES_LOCK(); - if (!tracemalloc_config.tracing) { - /* tracemalloc is not tracing: do nothing */ - return -1; - } + int result = -1; + assert(tracemalloc_config.tracing); PyTypeObject *type = Py_TYPE(op); const size_t presize = _PyType_PreHeaderSize(type); uintptr_t ptr = (uintptr_t)((char *)op - presize); - int res = -1; - - TABLES_LOCK(); trace_t *trace = _Py_hashtable_get(tracemalloc_traces, TO_PTR(ptr)); if (trace != NULL) { /* update the traceback of the memory block */ traceback_t *traceback = traceback_new(); if (traceback != NULL) { trace->traceback = traceback; - res = 0; + result = 0; } } /* else: cannot track the object, its memory block size is unknown */ - TABLES_UNLOCK(); - return res; + TABLES_UNLOCK(); + return result; } PyObject* _PyTraceMalloc_GetTraceback(unsigned int domain, uintptr_t ptr) { - traceback_t *traceback; + TABLES_LOCK(); - traceback = tracemalloc_get_traceback(domain, ptr); - if (traceback == NULL) - Py_RETURN_NONE; + traceback_t *traceback = tracemalloc_get_traceback_unlocked(domain, ptr); + PyObject *result; + if (traceback) { + set_reentrant(1); + result = traceback_to_pyobject(traceback, NULL); + set_reentrant(0); + } + else { + result = Py_NewRef(Py_None); + } - return traceback_to_pyobject(traceback, NULL); + TABLES_UNLOCK(); + return result; } int _PyTraceMalloc_IsTracing(void) { - return tracemalloc_config.tracing; + TABLES_LOCK(); + int tracing = tracemalloc_config.tracing; + TABLES_UNLOCK(); + return tracing; } void _PyTraceMalloc_ClearTraces(void) { - - if (!tracemalloc_config.tracing) { - return; + TABLES_LOCK(); + if (tracemalloc_config.tracing) { + tracemalloc_clear_traces_unlocked(); } - set_reentrant(1); - tracemalloc_clear_traces(); - set_reentrant(0); + TABLES_UNLOCK(); } PyObject * _PyTraceMalloc_GetTraces(void) { + TABLES_LOCK(); + set_reentrant(1); + get_traces_t get_traces; get_traces.domain = DEFAULT_DOMAIN; get_traces.traces = NULL; get_traces.domains = NULL; get_traces.tracebacks = NULL; get_traces.list = PyList_New(0); - if (get_traces.list == NULL) - goto error; + if (get_traces.list == NULL) { + goto finally; + } - if (!tracemalloc_config.tracing) - return get_traces.list; + if (!tracemalloc_config.tracing) { + goto finally; + } /* the traceback hash table is used temporarily to intern traceback tuple of (filename, lineno) tuples */ @@ -1449,24 +1406,17 @@ _PyTraceMalloc_GetTraces(void) // Copy all traces so tracemalloc_get_traces_fill() doesn't have to disable // temporarily tracemalloc which would impact other threads and so would // miss allocations while get_traces() is called. - TABLES_LOCK(); get_traces.traces = tracemalloc_copy_traces(tracemalloc_traces); - TABLES_UNLOCK(); - if (get_traces.traces == NULL) { goto no_memory; } - TABLES_LOCK(); get_traces.domains = tracemalloc_copy_domains(tracemalloc_domains); - TABLES_UNLOCK(); - if (get_traces.domains == NULL) { goto no_memory; } // Convert traces to a list of tuples - set_reentrant(1); int err = _Py_hashtable_foreach(get_traces.traces, tracemalloc_get_traces_fill, &get_traces); @@ -1475,20 +1425,22 @@ _PyTraceMalloc_GetTraces(void) tracemalloc_get_traces_domain, &get_traces); } - set_reentrant(0); + if (err) { - goto error; + Py_CLEAR(get_traces.list); + goto finally; } - goto finally; no_memory: PyErr_NoMemory(); - -error: Py_CLEAR(get_traces.list); + goto finally; finally: + set_reentrant(0); + TABLES_UNLOCK(); + if (get_traces.tracebacks != NULL) { _Py_hashtable_destroy(get_traces.tracebacks); } @@ -1506,31 +1458,21 @@ PyObject * _PyTraceMalloc_GetObjectTraceback(PyObject *obj) /*[clinic end generated code: output=41ee0553a658b0aa input=29495f1b21c53212]*/ { - PyTypeObject *type; - traceback_t *traceback; - - type = Py_TYPE(obj); + PyTypeObject *type = Py_TYPE(obj); const size_t presize = _PyType_PreHeaderSize(type); uintptr_t ptr = (uintptr_t)((char *)obj - presize); - - traceback = tracemalloc_get_traceback(DEFAULT_DOMAIN, ptr); - if (traceback == NULL) { - Py_RETURN_NONE; - } - - return traceback_to_pyobject(traceback, NULL); + return _PyTraceMalloc_GetTraceback(DEFAULT_DOMAIN, ptr); } -int _PyTraceMalloc_GetTracebackLimit(void) { +int _PyTraceMalloc_GetTracebackLimit(void) +{ return tracemalloc_config.max_nframe; } size_t -_PyTraceMalloc_GetMemory(void) { - - size_t size; - - size = _Py_hashtable_size(tracemalloc_tracebacks); +_PyTraceMalloc_GetMemory(void) +{ + size_t size = _Py_hashtable_size(tracemalloc_tracebacks); size += _Py_hashtable_size(tracemalloc_filenames); TABLES_LOCK(); @@ -1545,26 +1487,27 @@ _PyTraceMalloc_GetMemory(void) { PyObject * _PyTraceMalloc_GetTracedMemory(void) { - Py_ssize_t size, peak_size; - - if (!tracemalloc_config.tracing) - return Py_BuildValue("ii", 0, 0); - TABLES_LOCK(); - size = tracemalloc_traced_memory; - peak_size = tracemalloc_peak_traced_memory; + Py_ssize_t traced, peak; + if (tracemalloc_config.tracing) { + traced = tracemalloc_traced_memory; + peak = tracemalloc_peak_traced_memory; + } + else { + traced = 0; + peak = 0; + } TABLES_UNLOCK(); - return Py_BuildValue("nn", size, peak_size); + return Py_BuildValue("nn", traced, peak); } void _PyTraceMalloc_ResetPeak(void) { - if (!tracemalloc_config.tracing) { - return; - } TABLES_LOCK(); - tracemalloc_peak_traced_memory = tracemalloc_traced_memory; + if (tracemalloc_config.tracing) { + tracemalloc_peak_traced_memory = tracemalloc_traced_memory; + } TABLES_UNLOCK(); } From 256d6d2131541b3ff8f06f42c8157f808fde464c Mon Sep 17 00:00:00 2001 From: "Tomas R." Date: Wed, 15 Jan 2025 21:47:36 +0100 Subject: [PATCH 325/376] gh-97850: Suggest `TraversableResources` as the alternative for `ResourceLoader` (GH-128601) Suggest TraversableResources as the alternative for ResourceLoader. Previously, ResourceReader was the suggested alternative, but it is itself deprecated in favour of TraversableResources. --- Doc/library/importlib.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 9e088a598a6c08..b935fc0e42a4bd 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -380,13 +380,15 @@ ABC hierarchy:: .. class:: ResourceLoader + *Superseded by TraversableResources* + An abstract base class for a :term:`loader` which implements the optional :pep:`302` protocol for loading arbitrary resources from the storage back-end. .. deprecated:: 3.7 This ABC is deprecated in favour of supporting resource loading - through :class:`importlib.resources.abc.ResourceReader`. + through :class:`importlib.resources.abc.TraversableResources`. .. abstractmethod:: get_data(path) From 5eee2fe2b05c1a2836184e047fbd4176945cbf10 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:02:32 +0000 Subject: [PATCH 326/376] gh-128891: add specialized opcodes to opcode.opname (#128892) --- Lib/opcode.py | 5 +++-- Lib/test/test__opcode.py | 7 +++++++ Lib/test/test_dis.py | 6 ++++-- .../Library/2025-01-15-19-32-23.gh-issue-128891.ojUxKo.rst | 1 + 4 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-15-19-32-23.gh-issue-128891.ojUxKo.rst diff --git a/Lib/opcode.py b/Lib/opcode.py index 974f4d35e2a524..aba66153af1b2e 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -17,8 +17,9 @@ EXTENDED_ARG = opmap['EXTENDED_ARG'] opname = ['<%r>' % (op,) for op in range(max(opmap.values()) + 1)] -for op, i in opmap.items(): - opname[i] = op +for m in (opmap, _specialized_opmap): + for op, i in m.items(): + opname[i] = op cmp_op = ('<', '<=', '==', '!=', '>', '>=') diff --git a/Lib/test/test__opcode.py b/Lib/test/test__opcode.py index d5cf014d40daf8..95e09500df51d0 100644 --- a/Lib/test/test__opcode.py +++ b/Lib/test/test__opcode.py @@ -38,6 +38,13 @@ def test_is_valid(self): opcodes = [dis.opmap[opname] for opname in names] self.check_bool_function_result(_opcode.is_valid, opcodes, True) + def test_opmaps(self): + def check_roundtrip(name, map): + return self.assertEqual(opcode.opname[map[name]], name) + + check_roundtrip('BINARY_OP', opcode.opmap) + check_roundtrip('BINARY_OP_ADD_INT', opcode._specialized_opmap) + def test_oplists(self): def check_function(self, func, expected): for op in [-10, 520]: diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index ed8bd6fa20880b..8afe9653f19f6e 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -999,12 +999,14 @@ def test_boundaries(self): def test_widths(self): long_opcodes = set(['JUMP_BACKWARD_NO_INTERRUPT', 'INSTRUMENTED_CALL_FUNCTION_EX']) - for opcode, opname in enumerate(dis.opname): + for op, opname in enumerate(dis.opname): if opname in long_opcodes or opname.startswith("INSTRUMENTED"): continue + if opname in opcode._specialized_opmap: + continue with self.subTest(opname=opname): width = dis._OPNAME_WIDTH - if opcode in dis.hasarg: + if op in dis.hasarg: width += 1 + dis._OPARG_WIDTH self.assertLessEqual(len(opname), width) diff --git a/Misc/NEWS.d/next/Library/2025-01-15-19-32-23.gh-issue-128891.ojUxKo.rst b/Misc/NEWS.d/next/Library/2025-01-15-19-32-23.gh-issue-128891.ojUxKo.rst new file mode 100644 index 00000000000000..79d845bbab7cfc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-15-19-32-23.gh-issue-128891.ojUxKo.rst @@ -0,0 +1 @@ +Add specialized opcodes to ``opcode.opname``. From d05140f9f77d7dfc753dd1e5ac3a5962aaa03eff Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 15 Jan 2025 21:13:59 +0000 Subject: [PATCH 327/376] gh-121604: fix ResourceLoader deprecation warning message (GH-128859) --- Lib/importlib/abc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py index bb2837d38d83f1..29f01f77eff4a0 100644 --- a/Lib/importlib/abc.py +++ b/Lib/importlib/abc.py @@ -74,7 +74,7 @@ def __init__(self): import warnings warnings.warn('importlib.abc.ResourceLoader is deprecated in ' 'favour of supporting resource loading through ' - 'importlib.resources.abc.ResourceReader.', + 'importlib.resources.abc.TraversableResources.', DeprecationWarning, stacklevel=2) super().__init__() From 313b96eb8b8d0ad3bac58d633822a0a3705ce60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Srinivas=20Reddy=20Thatiparthy=20=28=E0=B0=A4=E0=B0=BE?= =?UTF-8?q?=E0=B0=9F=E0=B0=BF=E0=B0=AA=E0=B0=B0=E0=B1=8D=E0=B0=A4=E0=B0=BF?= =?UTF-8?q?=20=E0=B0=B6=E0=B1=8D=E0=B0=B0=E0=B1=80=E0=B0=A8=E0=B0=BF?= =?UTF-8?q?=E0=B0=B5=E0=B0=BE=E0=B0=B8=E0=B1=8D=20=20=E0=B0=B0=E0=B1=86?= =?UTF-8?q?=E0=B0=A1=E0=B1=8D=E0=B0=A1=E0=B0=BF=29?= Date: Thu, 16 Jan 2025 15:47:03 +0530 Subject: [PATCH 328/376] gh-128017: Make a note that sys variables are read-only (#128887) --- Doc/library/sys.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index dd6293c722e7ad..151fd60532048a 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -8,7 +8,7 @@ This module provides access to some variables used or maintained by the interpreter and to functions that interact strongly with the interpreter. It is -always available. +always available. Unless explicitly noted otherwise, all variables are read-only. .. data:: abiflags From 3193cb5ef87b77158d5c9bef99ae445cb82727de Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 16 Jan 2025 13:53:18 +0100 Subject: [PATCH 329/376] gh-128679: Fix tracemalloc.stop() race conditions (#128893) tracemalloc_alloc(), tracemalloc_realloc(), tracemalloc_free(), _PyTraceMalloc_TraceRef() and _PyTraceMalloc_GetMemory() now check 'tracemalloc_config.tracing' after calling TABLES_LOCK(). _PyTraceMalloc_TraceRef() now always returns 0. --- Lib/test/test_tracemalloc.py | 10 +- ...-01-15-21-41-51.gh-issue-128679.tq10F2.rst | 3 + Modules/_testcapimodule.c | 99 +++++++++++++++++++ Python/tracemalloc.c | 51 +++++++--- 4 files changed, 146 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-15-21-41-51.gh-issue-128679.tq10F2.rst diff --git a/Lib/test/test_tracemalloc.py b/Lib/test/test_tracemalloc.py index 5755f7697de91a..da2db28775578a 100644 --- a/Lib/test/test_tracemalloc.py +++ b/Lib/test/test_tracemalloc.py @@ -7,8 +7,9 @@ from test.support.script_helper import (assert_python_ok, assert_python_failure, interpreter_requires_environment) from test import support -from test.support import os_helper from test.support import force_not_colorized +from test.support import os_helper +from test.support import threading_helper try: import _testcapi @@ -952,7 +953,6 @@ def check_env_var_invalid(self, nframe): return self.fail(f"unexpected output: {stderr!a}") - def test_env_var_invalid(self): for nframe in INVALID_NFRAME: with self.subTest(nframe=nframe): @@ -1101,6 +1101,12 @@ def test_stop_untrack(self): with self.assertRaises(RuntimeError): self.untrack() + @unittest.skipIf(_testcapi is None, 'need _testcapi') + @threading_helper.requires_working_threading() + def test_tracemalloc_track_race(self): + # gh-128679: Test fix for tracemalloc.stop() race condition + _testcapi.tracemalloc_track_race() + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2025-01-15-21-41-51.gh-issue-128679.tq10F2.rst b/Misc/NEWS.d/next/Library/2025-01-15-21-41-51.gh-issue-128679.tq10F2.rst new file mode 100644 index 00000000000000..5c108da5703c00 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-15-21-41-51.gh-issue-128679.tq10F2.rst @@ -0,0 +1,3 @@ +:mod:`tracemalloc`: Fix race conditions when :func:`tracemalloc.stop` is +called by a thread, while other threads are tracing memory allocations. +Patch by Victor Stinner. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index a0a1f8af6710a3..7d304add5999d1 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3435,6 +3435,104 @@ code_offset_to_line(PyObject* self, PyObject* const* args, Py_ssize_t nargsf) return PyLong_FromInt32(PyCode_Addr2Line(code, offset)); } + +static void +tracemalloc_track_race_thread(void *data) +{ + PyTraceMalloc_Track(123, 10, 1); + + PyThread_type_lock lock = (PyThread_type_lock)data; + PyThread_release_lock(lock); +} + +// gh-128679: Test fix for tracemalloc.stop() race condition +static PyObject * +tracemalloc_track_race(PyObject *self, PyObject *args) +{ +#define NTHREAD 50 + PyObject *tracemalloc = NULL; + PyObject *stop = NULL; + PyThread_type_lock locks[NTHREAD]; + memset(locks, 0, sizeof(locks)); + + // Call tracemalloc.start() + tracemalloc = PyImport_ImportModule("tracemalloc"); + if (tracemalloc == NULL) { + goto error; + } + PyObject *start = PyObject_GetAttrString(tracemalloc, "start"); + if (start == NULL) { + goto error; + } + PyObject *res = PyObject_CallNoArgs(start); + Py_DECREF(start); + if (res == NULL) { + goto error; + } + Py_DECREF(res); + + stop = PyObject_GetAttrString(tracemalloc, "stop"); + Py_CLEAR(tracemalloc); + if (stop == NULL) { + goto error; + } + + // Start threads + for (size_t i = 0; i < NTHREAD; i++) { + PyThread_type_lock lock = PyThread_allocate_lock(); + if (!lock) { + PyErr_NoMemory(); + goto error; + } + locks[i] = lock; + PyThread_acquire_lock(lock, 1); + + unsigned long thread; + thread = PyThread_start_new_thread(tracemalloc_track_race_thread, + (void*)lock); + if (thread == (unsigned long)-1) { + PyErr_SetString(PyExc_RuntimeError, "can't start new thread"); + goto error; + } + } + + // Call tracemalloc.stop() while threads are running + res = PyObject_CallNoArgs(stop); + Py_CLEAR(stop); + if (res == NULL) { + goto error; + } + Py_DECREF(res); + + // Wait until threads complete with the GIL released + Py_BEGIN_ALLOW_THREADS + for (size_t i = 0; i < NTHREAD; i++) { + PyThread_type_lock lock = locks[i]; + PyThread_acquire_lock(lock, 1); + PyThread_release_lock(lock); + } + Py_END_ALLOW_THREADS + + // Free threads locks + for (size_t i=0; i < NTHREAD; i++) { + PyThread_type_lock lock = locks[i]; + PyThread_free_lock(lock); + } + Py_RETURN_NONE; + +error: + Py_CLEAR(tracemalloc); + Py_CLEAR(stop); + for (size_t i=0; i < NTHREAD; i++) { + PyThread_type_lock lock = locks[i]; + if (lock) { + PyThread_free_lock(lock); + } + } + return NULL; +#undef NTHREAD +} + static PyMethodDef TestMethods[] = { {"set_errno", set_errno, METH_VARARGS}, {"test_config", test_config, METH_NOARGS}, @@ -3578,6 +3676,7 @@ static PyMethodDef TestMethods[] = { {"type_freeze", type_freeze, METH_VARARGS}, {"test_atexit", test_atexit, METH_NOARGS}, {"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL}, + {"tracemalloc_track_race", tracemalloc_track_race, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; diff --git a/Python/tracemalloc.c b/Python/tracemalloc.c index 68b641c51868b9..919c564ee72967 100644 --- a/Python/tracemalloc.c +++ b/Python/tracemalloc.c @@ -567,11 +567,14 @@ tracemalloc_alloc(int need_gil, int use_calloc, } TABLES_LOCK(); - if (ADD_TRACE(ptr, nelem * elsize) < 0) { - // Failed to allocate a trace for the new memory block - alloc->free(alloc->ctx, ptr); - ptr = NULL; + if (tracemalloc_config.tracing) { + if (ADD_TRACE(ptr, nelem * elsize) < 0) { + // Failed to allocate a trace for the new memory block + alloc->free(alloc->ctx, ptr); + ptr = NULL; + } } + // else: gh-128679: tracemalloc.stop() was called by another thread TABLES_UNLOCK(); if (need_gil) { @@ -614,6 +617,11 @@ tracemalloc_realloc(int need_gil, void *ctx, void *ptr, size_t new_size) } TABLES_LOCK(); + if (!tracemalloc_config.tracing) { + // gh-128679: tracemalloc.stop() was called by another thread + goto unlock; + } + if (ptr != NULL) { // An existing memory block has been resized @@ -646,6 +654,7 @@ tracemalloc_realloc(int need_gil, void *ctx, void *ptr, size_t new_size) } } +unlock: TABLES_UNLOCK(); if (need_gil) { PyGILState_Release(gil_state); @@ -674,7 +683,12 @@ tracemalloc_free(void *ctx, void *ptr) } TABLES_LOCK(); - REMOVE_TRACE(ptr); + + if (tracemalloc_config.tracing) { + REMOVE_TRACE(ptr); + } + // else: gh-128679: tracemalloc.stop() was called by another thread + TABLES_UNLOCK(); } @@ -1312,8 +1326,9 @@ _PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event, assert(PyGILState_Check()); TABLES_LOCK(); - int result = -1; - assert(tracemalloc_config.tracing); + if (!tracemalloc_config.tracing) { + goto done; + } PyTypeObject *type = Py_TYPE(op); const size_t presize = _PyType_PreHeaderSize(type); @@ -1325,13 +1340,13 @@ _PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event, traceback_t *traceback = traceback_new(); if (traceback != NULL) { trace->traceback = traceback; - result = 0; } } /* else: cannot track the object, its memory block size is unknown */ +done: TABLES_UNLOCK(); - return result; + return 0; } @@ -1472,13 +1487,19 @@ int _PyTraceMalloc_GetTracebackLimit(void) size_t _PyTraceMalloc_GetMemory(void) { - size_t size = _Py_hashtable_size(tracemalloc_tracebacks); - size += _Py_hashtable_size(tracemalloc_filenames); - TABLES_LOCK(); - size += _Py_hashtable_size(tracemalloc_traces); - _Py_hashtable_foreach(tracemalloc_domains, - tracemalloc_get_tracemalloc_memory_cb, &size); + size_t size; + if (tracemalloc_config.tracing) { + size = _Py_hashtable_size(tracemalloc_tracebacks); + size += _Py_hashtable_size(tracemalloc_filenames); + + size += _Py_hashtable_size(tracemalloc_traces); + _Py_hashtable_foreach(tracemalloc_domains, + tracemalloc_get_tracemalloc_memory_cb, &size); + } + else { + size = 0; + } TABLES_UNLOCK(); return size; } From e81fe940c9bd092f6de558fa965100502b78da0f Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Thu, 16 Jan 2025 13:15:52 +0000 Subject: [PATCH 330/376] gh-119786: added InternalDocs/generators.md (#128524) --- InternalDocs/README.md | 2 +- InternalDocs/generators.md | 108 +++++++++++++++++++++++++++++++++++-- 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/InternalDocs/README.md b/InternalDocs/README.md index 794b4f3c6aad42..4502902307cd5c 100644 --- a/InternalDocs/README.md +++ b/InternalDocs/README.md @@ -25,7 +25,7 @@ Runtime Objects - [Code Objects](code_objects.md) -- [Generators (coming soon)](generators.md) +- [Generators](generators.md) - [Frames](frames.md) diff --git a/InternalDocs/generators.md b/InternalDocs/generators.md index afa8b8f4bb8040..608bd215aae65a 100644 --- a/InternalDocs/generators.md +++ b/InternalDocs/generators.md @@ -1,8 +1,106 @@ + +Generators and Coroutines +========================= + Generators -========== +---------- + +Generators in CPython are implemented with the struct `PyGenObject`. +They consist of a [`frame`](frames.md) and metadata about the generator's +execution state. + +A generator object resumes execution in its frame when its `send()` +method is called. This is analogous to a function executing in its own +fram when it is called, but a function returns to the calling frame only once, +while a generator "returns" execution to the caller's frame every time +it emits a new item with a +[`yield` expression](https://docs.python.org/dev/reference/expressions.html#yield-expressions). +This is implemented by the +[`YIELD_VALUE`](https://docs.python.org/dev/library/dis.html#opcode-YIELD_VALUE) +bytecode, which is similar to +[`RETURN_VALUE`](https://docs.python.org/dev/library/dis.html#opcode-RETURN_VALUE) +in the sense that it puts a value on the stack and returns execution to the +calling frame, but it also needs to perform additional work to leave the generator +frame in a state that allows it to be resumed. In particular, it updates the frame's +instruction pointer and stores the interpreter's exception state on the generator +object. When the generator is resumed, this exception state is copied back to the +interpreter state. + +The `frame` of a generator is embedded in the generator object struct as a +[`_PyInterpreterFrame`](frames.md) (see `_PyGenObject_HEAD` in +[`pycore_genobject.h`](../Include/internal/pycore_genobject.h)). +This means that we can get the frame from the generator or the generator +from the frame (see `_PyGen_GetGeneratorFromFrame` in the same file). +Other fields of the generator struct include metadata (such as the name of +the generator function) and runtime state information (such as whether its +frame is executing, suspended, cleared, etc.). + +Generator Object Creation and Destruction +----------------------------------------- + +The bytecode of a generator function begins with a +[`RETURN_GENERATOR`](https://docs.python.org/dev/library/dis.html#opcode-RETURN_GENERATOR) +instruction, which creates a generator object, including its embedded frame. +The generator's frame is initialized as a copy of the frame in which +`RETURN_GENERATOR` is executing, but its `owner` field is overwritten to indicate +that it is owned by a generator. Finally, `RETURN_GENERATOR` pushes the new generator +object to the stack and returns to the caller of the generator function (at +which time its frame is destroyed). When the generator is next resumed by +[`gen_send_ex2()`](../Objects/genobject.c), `_PyEval_EvalFrame()` is called +to continue executing the generator function, in the frame that is embedded in +the generator object. + +When a generator object is destroyed in [`gen_dealloc`](../Objects/genobject.c), +its embedded `_PyInterpreterFrame` field may need to be preserved, if it is exposed +to Python as part of a [`PyFrameObject`](frames.md#frame-objects). This is detected +in [`_PyFrame_ClearExceptCode`](../Python/frame.c) by the fact that the interpreter +frame's `frame_obj` field is set, and the frame object it points to has refcount +greater than 1. If so, the `take_ownership()` function is called to create a new +copy of the interpreter frame and transfer ownership of it from the generator to +the frame object. + +Iteration +--------- + +The [`FOR_ITER`](https://docs.python.org/dev/library/dis.html#opcode-FOR_ITER) +instruction calls `__next__` on the iterator which is on the top of the stack, +and pushes the result to the stack. It has [`specializations`](adaptive.md) +for a few common iterator types, including `FOR_ITER_GEN`, for iterating over +a generator. `FOR_ITER_GEN` bypasses the call to `__next__`, and instead +directly pushes the generator stack and resumes its execution from the +instruction that follows the last yield. + +Chained Generators +------------------ + +A `yield from` expression creates a generator that efficiently yields the +sequence created by another generator. This is implemented with the +[`SEND` instruction](https://docs.python.org/dev/library/dis.html#opcode-SEND), +which pushes the value of its arg to the stack of the generator's frame, sets +the exception state on this frame, and resumes execution of the chained generator. +On return from `SEND`, the value at the top of the stack is sent back up +the generator chain with a `YIELD_VALUE`. This sequence of `SEND` followed by +`YIELD_VALUE` is repeated in a loop, until a `StopIteration` exception is +raised to indicate that the generator has no more values to emit. + +The [`CLEANUP_THROW`](https://docs.python.org/dev/library/dis.html#opcode-CLEANUP_THROW) +instruction is used to handle exceptions raised from the send-yield loop. +Exceptions of type `StopIteration` is handled, their `value` field hold the +value to be returned by the generator's `close()` function. Any other +exception is re-raised by `CLEANUP_THROW`. + +Coroutines +---------- -Coming soon. +Coroutines are generators that use the value returned from a `yield` expression, +i.e., the argument that was passed to the `.send()` call that resumed it after +it yielded. This makes it possible for data to flow in both directions: from +the generator to the caller via the argument of the `yield` expression, and +from the caller to the generator via the send argument to the `send()` call. +A `yield from` expression passes the `send` argument to the chained generator, +so this data flow works along the chain (see `gen_send_ex2()` in +[`genobject.c`](../Objects/genobject.c)). - +Recall that a generator's `__next__` function simply calls `self.send(None)`, +so all this works the same in generators and coroutines, but only coroutines +use the value of the argument to `send`. From 3893a92d956363fa2443bc5e47d4bae3deddacef Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Thu, 16 Jan 2025 15:22:13 +0000 Subject: [PATCH 331/376] gh-100239: specialize long tail of binary operations (#128722) --- Include/internal/pycore_code.h | 13 +- Include/internal/pycore_magic_number.h | 3 +- Include/internal/pycore_opcode_metadata.h | 45 ++- Include/internal/pycore_uop_ids.h | 370 +++++++++--------- Include/internal/pycore_uop_metadata.h | 8 + Include/opcode_ids.h | 153 ++++---- Lib/_opcode_metadata.py | 154 ++++---- Lib/opcode.py | 1 + Lib/test/test_code.py | 10 +- Lib/test/test_dis.py | 182 ++++----- Lib/test/test_monitoring.py | 2 +- Lib/test/test_opcache.py | 25 ++ ...-01-10-23-54-16.gh-issue-100239.ijOOUs.rst | 2 + Python/bytecodes.c | 45 ++- Python/executor_cases.c.h | 45 +++ Python/generated_cases.c.h | 99 +++-- Python/opcode_targets.h | 2 +- Python/optimizer_cases.c.h | 13 + Python/specialize.c | 109 +++++- Tools/c-analyzer/cpython/ignored.tsv | 2 + Tools/cases_generator/parsing.py | 5 +- 21 files changed, 796 insertions(+), 492 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-10-23-54-16.gh-issue-100239.ijOOUs.rst diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index d97fe81a2fc54a..01d41446fdb0cf 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -100,6 +100,7 @@ typedef struct { typedef struct { _Py_BackoffCounter counter; + uint16_t external_cache[4]; } _PyBinaryOpCache; #define INLINE_CACHE_ENTRIES_BINARY_OP CACHE_ENTRIES(_PyBinaryOpCache) @@ -438,7 +439,7 @@ write_u64(uint16_t *p, uint64_t val) } static inline void -write_obj(uint16_t *p, PyObject *val) +write_ptr(uint16_t *p, void *val) { memcpy(p, &val, sizeof(val)); } @@ -576,6 +577,16 @@ adaptive_counter_backoff(_Py_BackoffCounter counter) { return restart_backoff_counter(counter); } +/* Specialization Extensions */ + +/* callbacks for an external specialization */ +typedef int (*binaryopguardfunc)(PyObject *lhs, PyObject *rhs); +typedef PyObject *(*binaryopactionfunc)(PyObject *lhs, PyObject *rhs); + +typedef struct { + binaryopguardfunc guard; + binaryopactionfunc action; +} _PyBinaryOpSpecializationDescr; /* Comparison bit masks. */ diff --git a/Include/internal/pycore_magic_number.h b/Include/internal/pycore_magic_number.h index 8b3d6285c1e4e7..1dd155abf3babf 100644 --- a/Include/internal/pycore_magic_number.h +++ b/Include/internal/pycore_magic_number.h @@ -266,6 +266,7 @@ Known values: Python 3.14a4 3611 (Add NOT_TAKEN instruction) Python 3.14a4 3612 (Add POP_ITER and INSTRUMENTED_POP_ITER) Python 3.14a4 3613 (Add LOAD_CONST_MORTAL instruction) + Python 3.14a5 3614 (Add BINARY_OP_EXTEND) Python 3.15 will start with 3650 @@ -278,7 +279,7 @@ PC/launcher.c must also be updated. */ -#define PYC_MAGIC_NUMBER 3613 +#define PYC_MAGIC_NUMBER 3614 /* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes (little-endian) and then appending b'\r\n'. */ #define PYC_MAGIC_NUMBER_TOKEN \ diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 0c0a6145bdbb27..f7c23ad634d6be 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -43,6 +43,8 @@ int _PyOpcode_num_popped(int opcode, int oparg) { return 2; case BINARY_OP_ADD_UNICODE: return 2; + case BINARY_OP_EXTEND: + return 2; case BINARY_OP_INPLACE_ADD_UNICODE: return 2; case BINARY_OP_MULTIPLY_FLOAT: @@ -512,6 +514,8 @@ int _PyOpcode_num_pushed(int opcode, int oparg) { return 1; case BINARY_OP_ADD_UNICODE: return 1; + case BINARY_OP_EXTEND: + return 1; case BINARY_OP_INPLACE_ADD_UNICODE: return 0; case BINARY_OP_MULTIPLY_FLOAT: @@ -989,6 +993,10 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) { *effect = 0; return 0; } + case BINARY_OP_EXTEND: { + *effect = 0; + return 0; + } case BINARY_OP_INPLACE_ADD_UNICODE: { *effect = 0; return 0; @@ -1919,11 +1927,13 @@ enum InstructionFormat { INSTR_FMT_IBC = 2, INSTR_FMT_IBC00 = 3, INSTR_FMT_IBC000 = 4, - INSTR_FMT_IBC00000000 = 5, - INSTR_FMT_IX = 6, - INSTR_FMT_IXC = 7, - INSTR_FMT_IXC00 = 8, - INSTR_FMT_IXC000 = 9, + INSTR_FMT_IBC0000 = 5, + INSTR_FMT_IBC00000000 = 6, + INSTR_FMT_IX = 7, + INSTR_FMT_IXC = 8, + INSTR_FMT_IXC00 = 9, + INSTR_FMT_IXC000 = 10, + INSTR_FMT_IXC0000 = 11, }; #define IS_VALID_OPCODE(OP) \ @@ -1981,15 +1991,16 @@ struct opcode_metadata { extern const struct opcode_metadata _PyOpcode_opcode_metadata[266]; #ifdef NEED_OPCODE_METADATA const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { - [BINARY_OP] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [BINARY_OP_ADD_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, - [BINARY_OP_ADD_INT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, - [BINARY_OP_ADD_UNICODE] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, - [BINARY_OP_INPLACE_ADD_UNICODE] = { true, INSTR_FMT_IXC, HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG }, - [BINARY_OP_MULTIPLY_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, - [BINARY_OP_MULTIPLY_INT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, - [BINARY_OP_SUBTRACT_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, - [BINARY_OP_SUBTRACT_INT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, + [BINARY_OP] = { true, INSTR_FMT_IBC0000, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, + [BINARY_OP_ADD_FLOAT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, + [BINARY_OP_ADD_INT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, + [BINARY_OP_ADD_UNICODE] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, + [BINARY_OP_EXTEND] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ESCAPES_FLAG }, + [BINARY_OP_INPLACE_ADD_UNICODE] = { true, INSTR_FMT_IXC0000, HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG }, + [BINARY_OP_MULTIPLY_FLOAT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, + [BINARY_OP_MULTIPLY_INT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, + [BINARY_OP_SUBTRACT_FLOAT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, + [BINARY_OP_SUBTRACT_INT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_SLICE] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [BINARY_SUBSCR] = { true, INSTR_FMT_IXC, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [BINARY_SUBSCR_DICT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -2227,6 +2238,7 @@ _PyOpcode_macro_expansion[256] = { [BINARY_OP_ADD_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_ADD_FLOAT, 0, 0 } } }, [BINARY_OP_ADD_INT] = { .nuops = 2, .uops = { { _GUARD_BOTH_INT, 0, 0 }, { _BINARY_OP_ADD_INT, 0, 0 } } }, [BINARY_OP_ADD_UNICODE] = { .nuops = 2, .uops = { { _GUARD_BOTH_UNICODE, 0, 0 }, { _BINARY_OP_ADD_UNICODE, 0, 0 } } }, + [BINARY_OP_EXTEND] = { .nuops = 2, .uops = { { _GUARD_BINARY_OP_EXTEND, 4, 1 }, { _BINARY_OP_EXTEND, 4, 1 } } }, [BINARY_OP_INPLACE_ADD_UNICODE] = { .nuops = 2, .uops = { { _GUARD_BOTH_UNICODE, 0, 0 }, { _BINARY_OP_INPLACE_ADD_UNICODE, 0, 0 } } }, [BINARY_OP_MULTIPLY_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_MULTIPLY_FLOAT, 0, 0 } } }, [BINARY_OP_MULTIPLY_INT] = { .nuops = 2, .uops = { { _GUARD_BOTH_INT, 0, 0 }, { _BINARY_OP_MULTIPLY_INT, 0, 0 } } }, @@ -2411,6 +2423,7 @@ const char *_PyOpcode_OpName[266] = { [BINARY_OP_ADD_FLOAT] = "BINARY_OP_ADD_FLOAT", [BINARY_OP_ADD_INT] = "BINARY_OP_ADD_INT", [BINARY_OP_ADD_UNICODE] = "BINARY_OP_ADD_UNICODE", + [BINARY_OP_EXTEND] = "BINARY_OP_EXTEND", [BINARY_OP_INPLACE_ADD_UNICODE] = "BINARY_OP_INPLACE_ADD_UNICODE", [BINARY_OP_MULTIPLY_FLOAT] = "BINARY_OP_MULTIPLY_FLOAT", [BINARY_OP_MULTIPLY_INT] = "BINARY_OP_MULTIPLY_INT", @@ -2661,7 +2674,7 @@ const uint8_t _PyOpcode_Caches[256] = { [FOR_ITER] = 1, [CALL] = 3, [CALL_KW] = 3, - [BINARY_OP] = 1, + [BINARY_OP] = 5, }; #endif @@ -2672,6 +2685,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [BINARY_OP_ADD_FLOAT] = BINARY_OP, [BINARY_OP_ADD_INT] = BINARY_OP, [BINARY_OP_ADD_UNICODE] = BINARY_OP, + [BINARY_OP_EXTEND] = BINARY_OP, [BINARY_OP_INPLACE_ADD_UNICODE] = BINARY_OP, [BINARY_OP_MULTIPLY_FLOAT] = BINARY_OP, [BINARY_OP_MULTIPLY_INT] = BINARY_OP, @@ -2923,7 +2937,6 @@ const uint8_t _PyOpcode_Deopt[256] = { case 146: \ case 147: \ case 148: \ - case 229: \ case 230: \ case 231: \ case 232: \ diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 3841363b411eed..f95defbc364aed 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -15,16 +15,17 @@ extern "C" { #define _BINARY_OP_ADD_FLOAT 303 #define _BINARY_OP_ADD_INT 304 #define _BINARY_OP_ADD_UNICODE 305 -#define _BINARY_OP_INPLACE_ADD_UNICODE 306 -#define _BINARY_OP_MULTIPLY_FLOAT 307 -#define _BINARY_OP_MULTIPLY_INT 308 -#define _BINARY_OP_SUBTRACT_FLOAT 309 -#define _BINARY_OP_SUBTRACT_INT 310 -#define _BINARY_SLICE 311 -#define _BINARY_SUBSCR 312 -#define _BINARY_SUBSCR_CHECK_FUNC 313 +#define _BINARY_OP_EXTEND 306 +#define _BINARY_OP_INPLACE_ADD_UNICODE 307 +#define _BINARY_OP_MULTIPLY_FLOAT 308 +#define _BINARY_OP_MULTIPLY_INT 309 +#define _BINARY_OP_SUBTRACT_FLOAT 310 +#define _BINARY_OP_SUBTRACT_INT 311 +#define _BINARY_SLICE 312 +#define _BINARY_SUBSCR 313 +#define _BINARY_SUBSCR_CHECK_FUNC 314 #define _BINARY_SUBSCR_DICT BINARY_SUBSCR_DICT -#define _BINARY_SUBSCR_INIT_CALL 314 +#define _BINARY_SUBSCR_INIT_CALL 315 #define _BINARY_SUBSCR_LIST_INT BINARY_SUBSCR_LIST_INT #define _BINARY_SUBSCR_STR_INT BINARY_SUBSCR_STR_INT #define _BINARY_SUBSCR_TUPLE_INT BINARY_SUBSCR_TUPLE_INT @@ -34,122 +35,123 @@ extern "C" { #define _BUILD_SLICE BUILD_SLICE #define _BUILD_STRING BUILD_STRING #define _BUILD_TUPLE BUILD_TUPLE -#define _CALL_BUILTIN_CLASS 315 -#define _CALL_BUILTIN_FAST 316 -#define _CALL_BUILTIN_FAST_WITH_KEYWORDS 317 -#define _CALL_BUILTIN_O 318 +#define _CALL_BUILTIN_CLASS 316 +#define _CALL_BUILTIN_FAST 317 +#define _CALL_BUILTIN_FAST_WITH_KEYWORDS 318 +#define _CALL_BUILTIN_O 319 #define _CALL_INTRINSIC_1 CALL_INTRINSIC_1 #define _CALL_INTRINSIC_2 CALL_INTRINSIC_2 #define _CALL_ISINSTANCE CALL_ISINSTANCE -#define _CALL_KW_NON_PY 319 +#define _CALL_KW_NON_PY 320 #define _CALL_LEN CALL_LEN #define _CALL_LIST_APPEND CALL_LIST_APPEND -#define _CALL_METHOD_DESCRIPTOR_FAST 320 -#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 321 -#define _CALL_METHOD_DESCRIPTOR_NOARGS 322 -#define _CALL_METHOD_DESCRIPTOR_O 323 -#define _CALL_NON_PY_GENERAL 324 -#define _CALL_STR_1 325 -#define _CALL_TUPLE_1 326 +#define _CALL_METHOD_DESCRIPTOR_FAST 321 +#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 322 +#define _CALL_METHOD_DESCRIPTOR_NOARGS 323 +#define _CALL_METHOD_DESCRIPTOR_O 324 +#define _CALL_NON_PY_GENERAL 325 +#define _CALL_STR_1 326 +#define _CALL_TUPLE_1 327 #define _CALL_TYPE_1 CALL_TYPE_1 -#define _CHECK_AND_ALLOCATE_OBJECT 327 -#define _CHECK_ATTR_CLASS 328 -#define _CHECK_ATTR_METHOD_LAZY_DICT 329 -#define _CHECK_ATTR_MODULE_PUSH_KEYS 330 -#define _CHECK_ATTR_WITH_HINT 331 -#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 332 +#define _CHECK_AND_ALLOCATE_OBJECT 328 +#define _CHECK_ATTR_CLASS 329 +#define _CHECK_ATTR_METHOD_LAZY_DICT 330 +#define _CHECK_ATTR_MODULE_PUSH_KEYS 331 +#define _CHECK_ATTR_WITH_HINT 332 +#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 333 #define _CHECK_EG_MATCH CHECK_EG_MATCH #define _CHECK_EXC_MATCH CHECK_EXC_MATCH -#define _CHECK_FUNCTION 333 -#define _CHECK_FUNCTION_EXACT_ARGS 334 -#define _CHECK_FUNCTION_VERSION 335 -#define _CHECK_FUNCTION_VERSION_INLINE 336 -#define _CHECK_FUNCTION_VERSION_KW 337 -#define _CHECK_IS_NOT_PY_CALLABLE 338 -#define _CHECK_IS_NOT_PY_CALLABLE_KW 339 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES 340 -#define _CHECK_METHOD_VERSION 341 -#define _CHECK_METHOD_VERSION_KW 342 -#define _CHECK_PEP_523 343 -#define _CHECK_PERIODIC 344 -#define _CHECK_PERIODIC_IF_NOT_YIELD_FROM 345 -#define _CHECK_STACK_SPACE 346 -#define _CHECK_STACK_SPACE_OPERAND 347 -#define _CHECK_VALIDITY 348 -#define _CHECK_VALIDITY_AND_SET_IP 349 -#define _COMPARE_OP 350 -#define _COMPARE_OP_FLOAT 351 -#define _COMPARE_OP_INT 352 -#define _COMPARE_OP_STR 353 -#define _CONTAINS_OP 354 +#define _CHECK_FUNCTION 334 +#define _CHECK_FUNCTION_EXACT_ARGS 335 +#define _CHECK_FUNCTION_VERSION 336 +#define _CHECK_FUNCTION_VERSION_INLINE 337 +#define _CHECK_FUNCTION_VERSION_KW 338 +#define _CHECK_IS_NOT_PY_CALLABLE 339 +#define _CHECK_IS_NOT_PY_CALLABLE_KW 340 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES 341 +#define _CHECK_METHOD_VERSION 342 +#define _CHECK_METHOD_VERSION_KW 343 +#define _CHECK_PEP_523 344 +#define _CHECK_PERIODIC 345 +#define _CHECK_PERIODIC_IF_NOT_YIELD_FROM 346 +#define _CHECK_STACK_SPACE 347 +#define _CHECK_STACK_SPACE_OPERAND 348 +#define _CHECK_VALIDITY 349 +#define _CHECK_VALIDITY_AND_SET_IP 350 +#define _COMPARE_OP 351 +#define _COMPARE_OP_FLOAT 352 +#define _COMPARE_OP_INT 353 +#define _COMPARE_OP_STR 354 +#define _CONTAINS_OP 355 #define _CONTAINS_OP_DICT CONTAINS_OP_DICT #define _CONTAINS_OP_SET CONTAINS_OP_SET #define _CONVERT_VALUE CONVERT_VALUE #define _COPY COPY #define _COPY_FREE_VARS COPY_FREE_VARS -#define _CREATE_INIT_FRAME 355 +#define _CREATE_INIT_FRAME 356 #define _DELETE_ATTR DELETE_ATTR #define _DELETE_DEREF DELETE_DEREF #define _DELETE_FAST DELETE_FAST #define _DELETE_GLOBAL DELETE_GLOBAL #define _DELETE_NAME DELETE_NAME #define _DELETE_SUBSCR DELETE_SUBSCR -#define _DEOPT 356 +#define _DEOPT 357 #define _DICT_MERGE DICT_MERGE #define _DICT_UPDATE DICT_UPDATE -#define _DO_CALL 357 -#define _DO_CALL_FUNCTION_EX 358 -#define _DO_CALL_KW 359 -#define _DYNAMIC_EXIT 360 +#define _DO_CALL 358 +#define _DO_CALL_FUNCTION_EX 359 +#define _DO_CALL_KW 360 +#define _DYNAMIC_EXIT 361 #define _END_FOR END_FOR #define _END_SEND END_SEND -#define _ERROR_POP_N 361 +#define _ERROR_POP_N 362 #define _EXIT_INIT_CHECK EXIT_INIT_CHECK -#define _EXPAND_METHOD 362 -#define _EXPAND_METHOD_KW 363 -#define _FATAL_ERROR 364 +#define _EXPAND_METHOD 363 +#define _EXPAND_METHOD_KW 364 +#define _FATAL_ERROR 365 #define _FORMAT_SIMPLE FORMAT_SIMPLE #define _FORMAT_WITH_SPEC FORMAT_WITH_SPEC -#define _FOR_ITER 365 -#define _FOR_ITER_GEN_FRAME 366 -#define _FOR_ITER_TIER_TWO 367 +#define _FOR_ITER 366 +#define _FOR_ITER_GEN_FRAME 367 +#define _FOR_ITER_TIER_TWO 368 #define _GET_AITER GET_AITER #define _GET_ANEXT GET_ANEXT #define _GET_AWAITABLE GET_AWAITABLE #define _GET_ITER GET_ITER #define _GET_LEN GET_LEN #define _GET_YIELD_FROM_ITER GET_YIELD_FROM_ITER -#define _GUARD_BOTH_FLOAT 368 -#define _GUARD_BOTH_INT 369 -#define _GUARD_BOTH_UNICODE 370 -#define _GUARD_BUILTINS_VERSION_PUSH_KEYS 371 -#define _GUARD_DORV_NO_DICT 372 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 373 -#define _GUARD_GLOBALS_VERSION 374 -#define _GUARD_GLOBALS_VERSION_PUSH_KEYS 375 -#define _GUARD_IS_FALSE_POP 376 -#define _GUARD_IS_NONE_POP 377 -#define _GUARD_IS_NOT_NONE_POP 378 -#define _GUARD_IS_TRUE_POP 379 -#define _GUARD_KEYS_VERSION 380 -#define _GUARD_NOS_FLOAT 381 -#define _GUARD_NOS_INT 382 -#define _GUARD_NOT_EXHAUSTED_LIST 383 -#define _GUARD_NOT_EXHAUSTED_RANGE 384 -#define _GUARD_NOT_EXHAUSTED_TUPLE 385 -#define _GUARD_TOS_FLOAT 386 -#define _GUARD_TOS_INT 387 -#define _GUARD_TYPE_VERSION 388 -#define _GUARD_TYPE_VERSION_AND_LOCK 389 +#define _GUARD_BINARY_OP_EXTEND 369 +#define _GUARD_BOTH_FLOAT 370 +#define _GUARD_BOTH_INT 371 +#define _GUARD_BOTH_UNICODE 372 +#define _GUARD_BUILTINS_VERSION_PUSH_KEYS 373 +#define _GUARD_DORV_NO_DICT 374 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 375 +#define _GUARD_GLOBALS_VERSION 376 +#define _GUARD_GLOBALS_VERSION_PUSH_KEYS 377 +#define _GUARD_IS_FALSE_POP 378 +#define _GUARD_IS_NONE_POP 379 +#define _GUARD_IS_NOT_NONE_POP 380 +#define _GUARD_IS_TRUE_POP 381 +#define _GUARD_KEYS_VERSION 382 +#define _GUARD_NOS_FLOAT 383 +#define _GUARD_NOS_INT 384 +#define _GUARD_NOT_EXHAUSTED_LIST 385 +#define _GUARD_NOT_EXHAUSTED_RANGE 386 +#define _GUARD_NOT_EXHAUSTED_TUPLE 387 +#define _GUARD_TOS_FLOAT 388 +#define _GUARD_TOS_INT 389 +#define _GUARD_TYPE_VERSION 390 +#define _GUARD_TYPE_VERSION_AND_LOCK 391 #define _IMPORT_FROM IMPORT_FROM #define _IMPORT_NAME IMPORT_NAME -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 390 -#define _INIT_CALL_PY_EXACT_ARGS 391 -#define _INIT_CALL_PY_EXACT_ARGS_0 392 -#define _INIT_CALL_PY_EXACT_ARGS_1 393 -#define _INIT_CALL_PY_EXACT_ARGS_2 394 -#define _INIT_CALL_PY_EXACT_ARGS_3 395 -#define _INIT_CALL_PY_EXACT_ARGS_4 396 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 392 +#define _INIT_CALL_PY_EXACT_ARGS 393 +#define _INIT_CALL_PY_EXACT_ARGS_0 394 +#define _INIT_CALL_PY_EXACT_ARGS_1 395 +#define _INIT_CALL_PY_EXACT_ARGS_2 396 +#define _INIT_CALL_PY_EXACT_ARGS_3 397 +#define _INIT_CALL_PY_EXACT_ARGS_4 398 #define _INSTRUMENTED_CALL_FUNCTION_EX INSTRUMENTED_CALL_FUNCTION_EX #define _INSTRUMENTED_CALL_KW INSTRUMENTED_CALL_KW #define _INSTRUMENTED_FOR_ITER INSTRUMENTED_FOR_ITER @@ -162,144 +164,144 @@ extern "C" { #define _INSTRUMENTED_POP_JUMP_IF_NONE INSTRUMENTED_POP_JUMP_IF_NONE #define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE INSTRUMENTED_POP_JUMP_IF_NOT_NONE #define _INSTRUMENTED_POP_JUMP_IF_TRUE INSTRUMENTED_POP_JUMP_IF_TRUE -#define _INTERNAL_INCREMENT_OPT_COUNTER 397 -#define _IS_NONE 398 +#define _INTERNAL_INCREMENT_OPT_COUNTER 399 +#define _IS_NONE 400 #define _IS_OP IS_OP -#define _ITER_CHECK_LIST 399 -#define _ITER_CHECK_RANGE 400 -#define _ITER_CHECK_TUPLE 401 -#define _ITER_JUMP_LIST 402 -#define _ITER_JUMP_RANGE 403 -#define _ITER_JUMP_TUPLE 404 -#define _ITER_NEXT_LIST 405 -#define _ITER_NEXT_RANGE 406 -#define _ITER_NEXT_TUPLE 407 -#define _JUMP_TO_TOP 408 +#define _ITER_CHECK_LIST 401 +#define _ITER_CHECK_RANGE 402 +#define _ITER_CHECK_TUPLE 403 +#define _ITER_JUMP_LIST 404 +#define _ITER_JUMP_RANGE 405 +#define _ITER_JUMP_TUPLE 406 +#define _ITER_NEXT_LIST 407 +#define _ITER_NEXT_RANGE 408 +#define _ITER_NEXT_TUPLE 409 +#define _JUMP_TO_TOP 410 #define _LIST_APPEND LIST_APPEND #define _LIST_EXTEND LIST_EXTEND -#define _LOAD_ATTR 409 -#define _LOAD_ATTR_CLASS 410 -#define _LOAD_ATTR_CLASS_0 411 -#define _LOAD_ATTR_CLASS_1 412 +#define _LOAD_ATTR 411 +#define _LOAD_ATTR_CLASS 412 +#define _LOAD_ATTR_CLASS_0 413 +#define _LOAD_ATTR_CLASS_1 414 #define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN -#define _LOAD_ATTR_INSTANCE_VALUE 413 -#define _LOAD_ATTR_INSTANCE_VALUE_0 414 -#define _LOAD_ATTR_INSTANCE_VALUE_1 415 -#define _LOAD_ATTR_METHOD_LAZY_DICT 416 -#define _LOAD_ATTR_METHOD_NO_DICT 417 -#define _LOAD_ATTR_METHOD_WITH_VALUES 418 -#define _LOAD_ATTR_MODULE 419 -#define _LOAD_ATTR_MODULE_FROM_KEYS 420 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 421 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 422 -#define _LOAD_ATTR_PROPERTY_FRAME 423 -#define _LOAD_ATTR_SLOT 424 -#define _LOAD_ATTR_SLOT_0 425 -#define _LOAD_ATTR_SLOT_1 426 -#define _LOAD_ATTR_WITH_HINT 427 +#define _LOAD_ATTR_INSTANCE_VALUE 415 +#define _LOAD_ATTR_INSTANCE_VALUE_0 416 +#define _LOAD_ATTR_INSTANCE_VALUE_1 417 +#define _LOAD_ATTR_METHOD_LAZY_DICT 418 +#define _LOAD_ATTR_METHOD_NO_DICT 419 +#define _LOAD_ATTR_METHOD_WITH_VALUES 420 +#define _LOAD_ATTR_MODULE 421 +#define _LOAD_ATTR_MODULE_FROM_KEYS 422 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 423 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 424 +#define _LOAD_ATTR_PROPERTY_FRAME 425 +#define _LOAD_ATTR_SLOT 426 +#define _LOAD_ATTR_SLOT_0 427 +#define _LOAD_ATTR_SLOT_1 428 +#define _LOAD_ATTR_WITH_HINT 429 #define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS -#define _LOAD_BYTECODE 428 +#define _LOAD_BYTECODE 430 #define _LOAD_COMMON_CONSTANT LOAD_COMMON_CONSTANT #define _LOAD_CONST LOAD_CONST #define _LOAD_CONST_IMMORTAL LOAD_CONST_IMMORTAL -#define _LOAD_CONST_INLINE 429 -#define _LOAD_CONST_INLINE_BORROW 430 -#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 431 -#define _LOAD_CONST_INLINE_WITH_NULL 432 +#define _LOAD_CONST_INLINE 431 +#define _LOAD_CONST_INLINE_BORROW 432 +#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 433 +#define _LOAD_CONST_INLINE_WITH_NULL 434 #define _LOAD_CONST_MORTAL LOAD_CONST_MORTAL #define _LOAD_DEREF LOAD_DEREF -#define _LOAD_FAST 433 -#define _LOAD_FAST_0 434 -#define _LOAD_FAST_1 435 -#define _LOAD_FAST_2 436 -#define _LOAD_FAST_3 437 -#define _LOAD_FAST_4 438 -#define _LOAD_FAST_5 439 -#define _LOAD_FAST_6 440 -#define _LOAD_FAST_7 441 +#define _LOAD_FAST 435 +#define _LOAD_FAST_0 436 +#define _LOAD_FAST_1 437 +#define _LOAD_FAST_2 438 +#define _LOAD_FAST_3 439 +#define _LOAD_FAST_4 440 +#define _LOAD_FAST_5 441 +#define _LOAD_FAST_6 442 +#define _LOAD_FAST_7 443 #define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR #define _LOAD_FAST_CHECK LOAD_FAST_CHECK #define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST #define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF #define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS -#define _LOAD_GLOBAL 442 -#define _LOAD_GLOBAL_BUILTINS 443 -#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 444 -#define _LOAD_GLOBAL_MODULE 445 -#define _LOAD_GLOBAL_MODULE_FROM_KEYS 446 +#define _LOAD_GLOBAL 444 +#define _LOAD_GLOBAL_BUILTINS 445 +#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 446 +#define _LOAD_GLOBAL_MODULE 447 +#define _LOAD_GLOBAL_MODULE_FROM_KEYS 448 #define _LOAD_LOCALS LOAD_LOCALS #define _LOAD_NAME LOAD_NAME -#define _LOAD_SMALL_INT 447 -#define _LOAD_SMALL_INT_0 448 -#define _LOAD_SMALL_INT_1 449 -#define _LOAD_SMALL_INT_2 450 -#define _LOAD_SMALL_INT_3 451 +#define _LOAD_SMALL_INT 449 +#define _LOAD_SMALL_INT_0 450 +#define _LOAD_SMALL_INT_1 451 +#define _LOAD_SMALL_INT_2 452 +#define _LOAD_SMALL_INT_3 453 #define _LOAD_SPECIAL LOAD_SPECIAL #define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR #define _LOAD_SUPER_ATTR_METHOD LOAD_SUPER_ATTR_METHOD -#define _MAKE_CALLARGS_A_TUPLE 452 +#define _MAKE_CALLARGS_A_TUPLE 454 #define _MAKE_CELL MAKE_CELL #define _MAKE_FUNCTION MAKE_FUNCTION -#define _MAKE_WARM 453 +#define _MAKE_WARM 455 #define _MAP_ADD MAP_ADD #define _MATCH_CLASS MATCH_CLASS #define _MATCH_KEYS MATCH_KEYS #define _MATCH_MAPPING MATCH_MAPPING #define _MATCH_SEQUENCE MATCH_SEQUENCE -#define _MAYBE_EXPAND_METHOD 454 -#define _MAYBE_EXPAND_METHOD_KW 455 -#define _MONITOR_CALL 456 -#define _MONITOR_JUMP_BACKWARD 457 -#define _MONITOR_RESUME 458 +#define _MAYBE_EXPAND_METHOD 456 +#define _MAYBE_EXPAND_METHOD_KW 457 +#define _MONITOR_CALL 458 +#define _MONITOR_JUMP_BACKWARD 459 +#define _MONITOR_RESUME 460 #define _NOP NOP #define _POP_EXCEPT POP_EXCEPT -#define _POP_JUMP_IF_FALSE 459 -#define _POP_JUMP_IF_TRUE 460 +#define _POP_JUMP_IF_FALSE 461 +#define _POP_JUMP_IF_TRUE 462 #define _POP_TOP POP_TOP -#define _POP_TOP_LOAD_CONST_INLINE_BORROW 461 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW 463 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 462 +#define _PUSH_FRAME 464 #define _PUSH_NULL PUSH_NULL -#define _PY_FRAME_GENERAL 463 -#define _PY_FRAME_KW 464 -#define _QUICKEN_RESUME 465 -#define _REPLACE_WITH_TRUE 466 +#define _PY_FRAME_GENERAL 465 +#define _PY_FRAME_KW 466 +#define _QUICKEN_RESUME 467 +#define _REPLACE_WITH_TRUE 468 #define _RESUME_CHECK RESUME_CHECK #define _RETURN_GENERATOR RETURN_GENERATOR #define _RETURN_VALUE RETURN_VALUE -#define _SAVE_RETURN_OFFSET 467 -#define _SEND 468 -#define _SEND_GEN_FRAME 469 +#define _SAVE_RETURN_OFFSET 469 +#define _SEND 470 +#define _SEND_GEN_FRAME 471 #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _START_EXECUTOR 470 -#define _STORE_ATTR 471 -#define _STORE_ATTR_INSTANCE_VALUE 472 -#define _STORE_ATTR_SLOT 473 -#define _STORE_ATTR_WITH_HINT 474 +#define _START_EXECUTOR 472 +#define _STORE_ATTR 473 +#define _STORE_ATTR_INSTANCE_VALUE 474 +#define _STORE_ATTR_SLOT 475 +#define _STORE_ATTR_WITH_HINT 476 #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 475 -#define _STORE_FAST_0 476 -#define _STORE_FAST_1 477 -#define _STORE_FAST_2 478 -#define _STORE_FAST_3 479 -#define _STORE_FAST_4 480 -#define _STORE_FAST_5 481 -#define _STORE_FAST_6 482 -#define _STORE_FAST_7 483 +#define _STORE_FAST 477 +#define _STORE_FAST_0 478 +#define _STORE_FAST_1 479 +#define _STORE_FAST_2 480 +#define _STORE_FAST_3 481 +#define _STORE_FAST_4 482 +#define _STORE_FAST_5 483 +#define _STORE_FAST_6 484 +#define _STORE_FAST_7 485 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME -#define _STORE_SLICE 484 -#define _STORE_SUBSCR 485 +#define _STORE_SLICE 486 +#define _STORE_SUBSCR 487 #define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT #define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT #define _SWAP SWAP -#define _TIER2_RESUME_CHECK 486 -#define _TO_BOOL 487 +#define _TIER2_RESUME_CHECK 488 +#define _TO_BOOL 489 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT #define _TO_BOOL_LIST TO_BOOL_LIST @@ -309,13 +311,13 @@ extern "C" { #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 488 +#define _UNPACK_SEQUENCE 490 #define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START #define _YIELD_VALUE YIELD_VALUE -#define MAX_UOP_ID 488 +#define MAX_UOP_ID 490 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 5670fe26f72071..298e918b872c62 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -82,6 +82,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_GUARD_BOTH_UNICODE] = HAS_EXIT_FLAG, [_BINARY_OP_ADD_UNICODE] = HAS_ERROR_FLAG | HAS_PURE_FLAG, [_BINARY_OP_INPLACE_ADD_UNICODE] = HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG, + [_GUARD_BINARY_OP_EXTEND] = HAS_EXIT_FLAG | HAS_ESCAPES_FLAG, + [_BINARY_OP_EXTEND] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG, [_BINARY_SUBSCR] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_BINARY_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_STORE_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -309,6 +311,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_BINARY_OP_ADD_FLOAT] = "_BINARY_OP_ADD_FLOAT", [_BINARY_OP_ADD_INT] = "_BINARY_OP_ADD_INT", [_BINARY_OP_ADD_UNICODE] = "_BINARY_OP_ADD_UNICODE", + [_BINARY_OP_EXTEND] = "_BINARY_OP_EXTEND", [_BINARY_OP_INPLACE_ADD_UNICODE] = "_BINARY_OP_INPLACE_ADD_UNICODE", [_BINARY_OP_MULTIPLY_FLOAT] = "_BINARY_OP_MULTIPLY_FLOAT", [_BINARY_OP_MULTIPLY_INT] = "_BINARY_OP_MULTIPLY_INT", @@ -410,6 +413,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_GET_ITER] = "_GET_ITER", [_GET_LEN] = "_GET_LEN", [_GET_YIELD_FROM_ITER] = "_GET_YIELD_FROM_ITER", + [_GUARD_BINARY_OP_EXTEND] = "_GUARD_BINARY_OP_EXTEND", [_GUARD_BOTH_FLOAT] = "_GUARD_BOTH_FLOAT", [_GUARD_BOTH_INT] = "_GUARD_BOTH_INT", [_GUARD_BOTH_UNICODE] = "_GUARD_BOTH_UNICODE", @@ -711,6 +715,10 @@ int _PyUop_num_popped(int opcode, int oparg) return 2; case _BINARY_OP_INPLACE_ADD_UNICODE: return 2; + case _GUARD_BINARY_OP_EXTEND: + return 0; + case _BINARY_OP_EXTEND: + return 2; case _BINARY_SUBSCR: return 2; case _BINARY_SLICE: diff --git a/Include/opcode_ids.h b/Include/opcode_ids.h index f2d8963a1813c5..06b207b347e504 100644 --- a/Include/opcode_ids.h +++ b/Include/opcode_ids.h @@ -132,82 +132,83 @@ extern "C" { #define BINARY_OP_ADD_FLOAT 150 #define BINARY_OP_ADD_INT 151 #define BINARY_OP_ADD_UNICODE 152 -#define BINARY_OP_MULTIPLY_FLOAT 153 -#define BINARY_OP_MULTIPLY_INT 154 -#define BINARY_OP_SUBTRACT_FLOAT 155 -#define BINARY_OP_SUBTRACT_INT 156 -#define BINARY_SUBSCR_DICT 157 -#define BINARY_SUBSCR_GETITEM 158 -#define BINARY_SUBSCR_LIST_INT 159 -#define BINARY_SUBSCR_STR_INT 160 -#define BINARY_SUBSCR_TUPLE_INT 161 -#define CALL_ALLOC_AND_ENTER_INIT 162 -#define CALL_BOUND_METHOD_EXACT_ARGS 163 -#define CALL_BOUND_METHOD_GENERAL 164 -#define CALL_BUILTIN_CLASS 165 -#define CALL_BUILTIN_FAST 166 -#define CALL_BUILTIN_FAST_WITH_KEYWORDS 167 -#define CALL_BUILTIN_O 168 -#define CALL_ISINSTANCE 169 -#define CALL_KW_BOUND_METHOD 170 -#define CALL_KW_NON_PY 171 -#define CALL_KW_PY 172 -#define CALL_LEN 173 -#define CALL_LIST_APPEND 174 -#define CALL_METHOD_DESCRIPTOR_FAST 175 -#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 176 -#define CALL_METHOD_DESCRIPTOR_NOARGS 177 -#define CALL_METHOD_DESCRIPTOR_O 178 -#define CALL_NON_PY_GENERAL 179 -#define CALL_PY_EXACT_ARGS 180 -#define CALL_PY_GENERAL 181 -#define CALL_STR_1 182 -#define CALL_TUPLE_1 183 -#define CALL_TYPE_1 184 -#define COMPARE_OP_FLOAT 185 -#define COMPARE_OP_INT 186 -#define COMPARE_OP_STR 187 -#define CONTAINS_OP_DICT 188 -#define CONTAINS_OP_SET 189 -#define FOR_ITER_GEN 190 -#define FOR_ITER_LIST 191 -#define FOR_ITER_RANGE 192 -#define FOR_ITER_TUPLE 193 -#define LOAD_ATTR_CLASS 194 -#define LOAD_ATTR_CLASS_WITH_METACLASS_CHECK 195 -#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 196 -#define LOAD_ATTR_INSTANCE_VALUE 197 -#define LOAD_ATTR_METHOD_LAZY_DICT 198 -#define LOAD_ATTR_METHOD_NO_DICT 199 -#define LOAD_ATTR_METHOD_WITH_VALUES 200 -#define LOAD_ATTR_MODULE 201 -#define LOAD_ATTR_NONDESCRIPTOR_NO_DICT 202 -#define LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 203 -#define LOAD_ATTR_PROPERTY 204 -#define LOAD_ATTR_SLOT 205 -#define LOAD_ATTR_WITH_HINT 206 -#define LOAD_CONST_IMMORTAL 207 -#define LOAD_CONST_MORTAL 208 -#define LOAD_GLOBAL_BUILTIN 209 -#define LOAD_GLOBAL_MODULE 210 -#define LOAD_SUPER_ATTR_ATTR 211 -#define LOAD_SUPER_ATTR_METHOD 212 -#define RESUME_CHECK 213 -#define SEND_GEN 214 -#define STORE_ATTR_INSTANCE_VALUE 215 -#define STORE_ATTR_SLOT 216 -#define STORE_ATTR_WITH_HINT 217 -#define STORE_SUBSCR_DICT 218 -#define STORE_SUBSCR_LIST_INT 219 -#define TO_BOOL_ALWAYS_TRUE 220 -#define TO_BOOL_BOOL 221 -#define TO_BOOL_INT 222 -#define TO_BOOL_LIST 223 -#define TO_BOOL_NONE 224 -#define TO_BOOL_STR 225 -#define UNPACK_SEQUENCE_LIST 226 -#define UNPACK_SEQUENCE_TUPLE 227 -#define UNPACK_SEQUENCE_TWO_TUPLE 228 +#define BINARY_OP_EXTEND 153 +#define BINARY_OP_MULTIPLY_FLOAT 154 +#define BINARY_OP_MULTIPLY_INT 155 +#define BINARY_OP_SUBTRACT_FLOAT 156 +#define BINARY_OP_SUBTRACT_INT 157 +#define BINARY_SUBSCR_DICT 158 +#define BINARY_SUBSCR_GETITEM 159 +#define BINARY_SUBSCR_LIST_INT 160 +#define BINARY_SUBSCR_STR_INT 161 +#define BINARY_SUBSCR_TUPLE_INT 162 +#define CALL_ALLOC_AND_ENTER_INIT 163 +#define CALL_BOUND_METHOD_EXACT_ARGS 164 +#define CALL_BOUND_METHOD_GENERAL 165 +#define CALL_BUILTIN_CLASS 166 +#define CALL_BUILTIN_FAST 167 +#define CALL_BUILTIN_FAST_WITH_KEYWORDS 168 +#define CALL_BUILTIN_O 169 +#define CALL_ISINSTANCE 170 +#define CALL_KW_BOUND_METHOD 171 +#define CALL_KW_NON_PY 172 +#define CALL_KW_PY 173 +#define CALL_LEN 174 +#define CALL_LIST_APPEND 175 +#define CALL_METHOD_DESCRIPTOR_FAST 176 +#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 177 +#define CALL_METHOD_DESCRIPTOR_NOARGS 178 +#define CALL_METHOD_DESCRIPTOR_O 179 +#define CALL_NON_PY_GENERAL 180 +#define CALL_PY_EXACT_ARGS 181 +#define CALL_PY_GENERAL 182 +#define CALL_STR_1 183 +#define CALL_TUPLE_1 184 +#define CALL_TYPE_1 185 +#define COMPARE_OP_FLOAT 186 +#define COMPARE_OP_INT 187 +#define COMPARE_OP_STR 188 +#define CONTAINS_OP_DICT 189 +#define CONTAINS_OP_SET 190 +#define FOR_ITER_GEN 191 +#define FOR_ITER_LIST 192 +#define FOR_ITER_RANGE 193 +#define FOR_ITER_TUPLE 194 +#define LOAD_ATTR_CLASS 195 +#define LOAD_ATTR_CLASS_WITH_METACLASS_CHECK 196 +#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 197 +#define LOAD_ATTR_INSTANCE_VALUE 198 +#define LOAD_ATTR_METHOD_LAZY_DICT 199 +#define LOAD_ATTR_METHOD_NO_DICT 200 +#define LOAD_ATTR_METHOD_WITH_VALUES 201 +#define LOAD_ATTR_MODULE 202 +#define LOAD_ATTR_NONDESCRIPTOR_NO_DICT 203 +#define LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 204 +#define LOAD_ATTR_PROPERTY 205 +#define LOAD_ATTR_SLOT 206 +#define LOAD_ATTR_WITH_HINT 207 +#define LOAD_CONST_IMMORTAL 208 +#define LOAD_CONST_MORTAL 209 +#define LOAD_GLOBAL_BUILTIN 210 +#define LOAD_GLOBAL_MODULE 211 +#define LOAD_SUPER_ATTR_ATTR 212 +#define LOAD_SUPER_ATTR_METHOD 213 +#define RESUME_CHECK 214 +#define SEND_GEN 215 +#define STORE_ATTR_INSTANCE_VALUE 216 +#define STORE_ATTR_SLOT 217 +#define STORE_ATTR_WITH_HINT 218 +#define STORE_SUBSCR_DICT 219 +#define STORE_SUBSCR_LIST_INT 220 +#define TO_BOOL_ALWAYS_TRUE 221 +#define TO_BOOL_BOOL 222 +#define TO_BOOL_INT 223 +#define TO_BOOL_LIST 224 +#define TO_BOOL_NONE 225 +#define TO_BOOL_STR 226 +#define UNPACK_SEQUENCE_LIST 227 +#define UNPACK_SEQUENCE_TUPLE 228 +#define UNPACK_SEQUENCE_TWO_TUPLE 229 #define INSTRUMENTED_END_FOR 235 #define INSTRUMENTED_POP_ITER 236 #define INSTRUMENTED_END_SEND 237 diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py index d93e8d8df8fe4b..7dd528ef74df33 100644 --- a/Lib/_opcode_metadata.py +++ b/Lib/_opcode_metadata.py @@ -26,6 +26,7 @@ "BINARY_OP_ADD_FLOAT", "BINARY_OP_SUBTRACT_FLOAT", "BINARY_OP_ADD_UNICODE", + "BINARY_OP_EXTEND", "BINARY_OP_INPLACE_ADD_UNICODE", ], "BINARY_SUBSCR": [ @@ -123,83 +124,84 @@ 'BINARY_OP_ADD_FLOAT': 150, 'BINARY_OP_ADD_INT': 151, 'BINARY_OP_ADD_UNICODE': 152, + 'BINARY_OP_EXTEND': 153, 'BINARY_OP_INPLACE_ADD_UNICODE': 3, - 'BINARY_OP_MULTIPLY_FLOAT': 153, - 'BINARY_OP_MULTIPLY_INT': 154, - 'BINARY_OP_SUBTRACT_FLOAT': 155, - 'BINARY_OP_SUBTRACT_INT': 156, - 'BINARY_SUBSCR_DICT': 157, - 'BINARY_SUBSCR_GETITEM': 158, - 'BINARY_SUBSCR_LIST_INT': 159, - 'BINARY_SUBSCR_STR_INT': 160, - 'BINARY_SUBSCR_TUPLE_INT': 161, - 'CALL_ALLOC_AND_ENTER_INIT': 162, - 'CALL_BOUND_METHOD_EXACT_ARGS': 163, - 'CALL_BOUND_METHOD_GENERAL': 164, - 'CALL_BUILTIN_CLASS': 165, - 'CALL_BUILTIN_FAST': 166, - 'CALL_BUILTIN_FAST_WITH_KEYWORDS': 167, - 'CALL_BUILTIN_O': 168, - 'CALL_ISINSTANCE': 169, - 'CALL_KW_BOUND_METHOD': 170, - 'CALL_KW_NON_PY': 171, - 'CALL_KW_PY': 172, - 'CALL_LEN': 173, - 'CALL_LIST_APPEND': 174, - 'CALL_METHOD_DESCRIPTOR_FAST': 175, - 'CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS': 176, - 'CALL_METHOD_DESCRIPTOR_NOARGS': 177, - 'CALL_METHOD_DESCRIPTOR_O': 178, - 'CALL_NON_PY_GENERAL': 179, - 'CALL_PY_EXACT_ARGS': 180, - 'CALL_PY_GENERAL': 181, - 'CALL_STR_1': 182, - 'CALL_TUPLE_1': 183, - 'CALL_TYPE_1': 184, - 'COMPARE_OP_FLOAT': 185, - 'COMPARE_OP_INT': 186, - 'COMPARE_OP_STR': 187, - 'CONTAINS_OP_DICT': 188, - 'CONTAINS_OP_SET': 189, - 'FOR_ITER_GEN': 190, - 'FOR_ITER_LIST': 191, - 'FOR_ITER_RANGE': 192, - 'FOR_ITER_TUPLE': 193, - 'LOAD_ATTR_CLASS': 194, - 'LOAD_ATTR_CLASS_WITH_METACLASS_CHECK': 195, - 'LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN': 196, - 'LOAD_ATTR_INSTANCE_VALUE': 197, - 'LOAD_ATTR_METHOD_LAZY_DICT': 198, - 'LOAD_ATTR_METHOD_NO_DICT': 199, - 'LOAD_ATTR_METHOD_WITH_VALUES': 200, - 'LOAD_ATTR_MODULE': 201, - 'LOAD_ATTR_NONDESCRIPTOR_NO_DICT': 202, - 'LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES': 203, - 'LOAD_ATTR_PROPERTY': 204, - 'LOAD_ATTR_SLOT': 205, - 'LOAD_ATTR_WITH_HINT': 206, - 'LOAD_CONST_IMMORTAL': 207, - 'LOAD_CONST_MORTAL': 208, - 'LOAD_GLOBAL_BUILTIN': 209, - 'LOAD_GLOBAL_MODULE': 210, - 'LOAD_SUPER_ATTR_ATTR': 211, - 'LOAD_SUPER_ATTR_METHOD': 212, - 'RESUME_CHECK': 213, - 'SEND_GEN': 214, - 'STORE_ATTR_INSTANCE_VALUE': 215, - 'STORE_ATTR_SLOT': 216, - 'STORE_ATTR_WITH_HINT': 217, - 'STORE_SUBSCR_DICT': 218, - 'STORE_SUBSCR_LIST_INT': 219, - 'TO_BOOL_ALWAYS_TRUE': 220, - 'TO_BOOL_BOOL': 221, - 'TO_BOOL_INT': 222, - 'TO_BOOL_LIST': 223, - 'TO_BOOL_NONE': 224, - 'TO_BOOL_STR': 225, - 'UNPACK_SEQUENCE_LIST': 226, - 'UNPACK_SEQUENCE_TUPLE': 227, - 'UNPACK_SEQUENCE_TWO_TUPLE': 228, + 'BINARY_OP_MULTIPLY_FLOAT': 154, + 'BINARY_OP_MULTIPLY_INT': 155, + 'BINARY_OP_SUBTRACT_FLOAT': 156, + 'BINARY_OP_SUBTRACT_INT': 157, + 'BINARY_SUBSCR_DICT': 158, + 'BINARY_SUBSCR_GETITEM': 159, + 'BINARY_SUBSCR_LIST_INT': 160, + 'BINARY_SUBSCR_STR_INT': 161, + 'BINARY_SUBSCR_TUPLE_INT': 162, + 'CALL_ALLOC_AND_ENTER_INIT': 163, + 'CALL_BOUND_METHOD_EXACT_ARGS': 164, + 'CALL_BOUND_METHOD_GENERAL': 165, + 'CALL_BUILTIN_CLASS': 166, + 'CALL_BUILTIN_FAST': 167, + 'CALL_BUILTIN_FAST_WITH_KEYWORDS': 168, + 'CALL_BUILTIN_O': 169, + 'CALL_ISINSTANCE': 170, + 'CALL_KW_BOUND_METHOD': 171, + 'CALL_KW_NON_PY': 172, + 'CALL_KW_PY': 173, + 'CALL_LEN': 174, + 'CALL_LIST_APPEND': 175, + 'CALL_METHOD_DESCRIPTOR_FAST': 176, + 'CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS': 177, + 'CALL_METHOD_DESCRIPTOR_NOARGS': 178, + 'CALL_METHOD_DESCRIPTOR_O': 179, + 'CALL_NON_PY_GENERAL': 180, + 'CALL_PY_EXACT_ARGS': 181, + 'CALL_PY_GENERAL': 182, + 'CALL_STR_1': 183, + 'CALL_TUPLE_1': 184, + 'CALL_TYPE_1': 185, + 'COMPARE_OP_FLOAT': 186, + 'COMPARE_OP_INT': 187, + 'COMPARE_OP_STR': 188, + 'CONTAINS_OP_DICT': 189, + 'CONTAINS_OP_SET': 190, + 'FOR_ITER_GEN': 191, + 'FOR_ITER_LIST': 192, + 'FOR_ITER_RANGE': 193, + 'FOR_ITER_TUPLE': 194, + 'LOAD_ATTR_CLASS': 195, + 'LOAD_ATTR_CLASS_WITH_METACLASS_CHECK': 196, + 'LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN': 197, + 'LOAD_ATTR_INSTANCE_VALUE': 198, + 'LOAD_ATTR_METHOD_LAZY_DICT': 199, + 'LOAD_ATTR_METHOD_NO_DICT': 200, + 'LOAD_ATTR_METHOD_WITH_VALUES': 201, + 'LOAD_ATTR_MODULE': 202, + 'LOAD_ATTR_NONDESCRIPTOR_NO_DICT': 203, + 'LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES': 204, + 'LOAD_ATTR_PROPERTY': 205, + 'LOAD_ATTR_SLOT': 206, + 'LOAD_ATTR_WITH_HINT': 207, + 'LOAD_CONST_IMMORTAL': 208, + 'LOAD_CONST_MORTAL': 209, + 'LOAD_GLOBAL_BUILTIN': 210, + 'LOAD_GLOBAL_MODULE': 211, + 'LOAD_SUPER_ATTR_ATTR': 212, + 'LOAD_SUPER_ATTR_METHOD': 213, + 'RESUME_CHECK': 214, + 'SEND_GEN': 215, + 'STORE_ATTR_INSTANCE_VALUE': 216, + 'STORE_ATTR_SLOT': 217, + 'STORE_ATTR_WITH_HINT': 218, + 'STORE_SUBSCR_DICT': 219, + 'STORE_SUBSCR_LIST_INT': 220, + 'TO_BOOL_ALWAYS_TRUE': 221, + 'TO_BOOL_BOOL': 222, + 'TO_BOOL_INT': 223, + 'TO_BOOL_LIST': 224, + 'TO_BOOL_NONE': 225, + 'TO_BOOL_STR': 226, + 'UNPACK_SEQUENCE_LIST': 227, + 'UNPACK_SEQUENCE_TUPLE': 228, + 'UNPACK_SEQUENCE_TWO_TUPLE': 229, } opmap = { diff --git a/Lib/opcode.py b/Lib/opcode.py index aba66153af1b2e..4ee0d64026bd0a 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -52,6 +52,7 @@ }, "BINARY_OP": { "counter": 1, + "descr": 4, }, "UNPACK_SEQUENCE": { "counter": 1, diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 7ffa4eb8639add..69c1ee0690d269 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -429,14 +429,14 @@ def test_invalid_bytecode(self): def foo(): pass - # assert that opcode 229 is invalid - self.assertEqual(opname[229], '<229>') + # assert that opcode 135 is invalid + self.assertEqual(opname[135], '<135>') - # change first opcode to 0xeb (=229) + # change first opcode to 0x87 (=135) foo.__code__ = foo.__code__.replace( - co_code=b'\xe5' + foo.__code__.co_code[1:]) + co_code=b'\x87' + foo.__code__.co_code[1:]) - msg = "unknown opcode 229" + msg = "unknown opcode 135" with self.assertRaisesRegex(SystemError, msg): foo() diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 8afe9653f19f6e..da57aad2f84fbd 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -438,7 +438,7 @@ def foo(a: int, b: str) -> str: LOAD_SMALL_INT 1 BINARY_OP 13 (+=) STORE_NAME 0 (x) - JUMP_BACKWARD 8 (to L1) + JUMP_BACKWARD 12 (to L1) """ dis_traceback = """\ @@ -843,7 +843,7 @@ def foo(x): L1: RESUME 0 LOAD_FAST 0 (.0) GET_ITER - L2: FOR_ITER 10 (to L3) + L2: FOR_ITER 14 (to L3) STORE_FAST 1 (z) LOAD_DEREF 2 (x) LOAD_FAST 1 (z) @@ -851,7 +851,7 @@ def foo(x): YIELD_VALUE 0 RESUME 5 POP_TOP - JUMP_BACKWARD 12 (to L2) + JUMP_BACKWARD 16 (to L2) L3: END_FOR POP_ITER LOAD_CONST 0 (None) @@ -1807,7 +1807,7 @@ def _prepare_test_cases(): Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=116, start_offset=116, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), Instruction(opname='LOAD_FAST_CHECK', opcode=85, arg=0, argval='i', argrepr='i', offset=118, start_offset=118, starts_line=True, line_number=11, label=5, positions=None, cache_info=None), Instruction(opname='TO_BOOL', opcode=39, arg=None, argval=None, argrepr='', offset=120, start_offset=120, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=36, argval=204, argrepr='to L8', offset=128, start_offset=128, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=40, argval=212, argrepr='to L8', offset=128, start_offset=128, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=132, start_offset=132, starts_line=False, line_number=11, label=None, positions=None, cache_info=None), Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=134, start_offset=134, starts_line=True, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=144, start_offset=144, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), @@ -1815,93 +1815,93 @@ def _prepare_test_cases(): Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=154, start_offset=154, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=156, start_offset=156, starts_line=True, line_number=13, label=None, positions=None, cache_info=None), Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=1, argval=1, argrepr='', offset=158, start_offset=158, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), - Instruction(opname='BINARY_OP', opcode=44, arg=23, argval=23, argrepr='-=', offset=160, start_offset=160, starts_line=False, line_number=13, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='STORE_FAST', opcode=109, arg=0, argval='i', argrepr='i', offset=164, start_offset=164, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=166, start_offset=166, starts_line=True, line_number=14, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=6, argval=6, argrepr='', offset=168, start_offset=168, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=56, arg=148, argval='>', argrepr='bool(>)', offset=170, start_offset=170, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=3, argval=184, argrepr='to L6', offset=174, start_offset=174, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=178, start_offset=178, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD', opcode=74, arg=33, argval=118, argrepr='to L5', offset=180, start_offset=180, starts_line=True, line_number=15, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=184, start_offset=184, starts_line=True, line_number=16, label=6, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=4, argval=4, argrepr='', offset=186, start_offset=186, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=56, arg=18, argval='<', argrepr='bool(<)', offset=188, start_offset=188, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=3, argval=202, argrepr='to L7', offset=192, start_offset=192, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=196, start_offset=196, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD', opcode=74, arg=42, argval=118, argrepr='to L5', offset=198, start_offset=198, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='JUMP_FORWARD', opcode=76, arg=11, argval=226, argrepr='to L9', offset=202, start_offset=202, starts_line=True, line_number=17, label=7, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=204, start_offset=204, starts_line=True, line_number=19, label=8, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=1, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=214, start_offset=214, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=216, start_offset=216, starts_line=False, line_number=19, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=224, start_offset=224, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), - Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=226, start_offset=226, starts_line=True, line_number=20, label=9, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=1, argval=1, argrepr='', offset=228, start_offset=228, starts_line=True, line_number=21, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=0, argval=0, argrepr='', offset=230, start_offset=230, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), - Instruction(opname='BINARY_OP', opcode=44, arg=11, argval=11, argrepr='/', offset=232, start_offset=232, starts_line=False, line_number=21, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=236, start_offset=236, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=238, start_offset=238, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=59, arg=1, argval=1, argrepr='', offset=240, start_offset=240, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SPECIAL', opcode=92, arg=1, argval=1, argrepr='__exit__', offset=242, start_offset=242, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='SWAP', opcode=114, arg=2, argval=2, argrepr='', offset=244, start_offset=244, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='SWAP', opcode=114, arg=3, argval=3, argrepr='', offset=246, start_offset=246, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SPECIAL', opcode=92, arg=0, argval=0, argrepr='__enter__', offset=248, start_offset=248, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=0, argval=0, argrepr='', offset=250, start_offset=250, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='STORE_FAST', opcode=109, arg=1, argval='dodgy', argrepr='dodgy', offset=258, start_offset=258, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=260, start_offset=260, starts_line=True, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=2, argval='Never reach this', argrepr="'Never reach this'", offset=270, start_offset=270, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=272, start_offset=272, starts_line=False, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=280, start_offset=280, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=282, start_offset=282, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=284, start_offset=284, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=286, start_offset=286, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=3, argval=3, argrepr='', offset=288, start_offset=288, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=296, start_offset=296, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=298, start_offset=298, starts_line=True, line_number=28, label=10, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=308, start_offset=308, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=310, start_offset=310, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=318, start_offset=318, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=320, start_offset=320, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='RETURN_VALUE', opcode=35, arg=None, argval=None, argrepr='', offset=322, start_offset=322, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=324, start_offset=324, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='WITH_EXCEPT_START', opcode=43, arg=None, argval=None, argrepr='', offset=326, start_offset=326, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='TO_BOOL', opcode=39, arg=None, argval=None, argrepr='', offset=328, start_offset=328, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=2, argval=344, argrepr='to L11', offset=336, start_offset=336, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=340, start_offset=340, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=2, argval=2, argrepr='', offset=342, start_offset=342, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=344, start_offset=344, starts_line=False, line_number=25, label=11, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=346, start_offset=346, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=348, start_offset=348, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=350, start_offset=350, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=352, start_offset=352, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=75, arg=29, argval=298, argrepr='to L10', offset=354, start_offset=354, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=356, start_offset=356, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=358, start_offset=358, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=360, start_offset=360, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=364, start_offset=364, starts_line=True, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='CHECK_EXC_MATCH', opcode=5, arg=None, argval=None, argrepr='', offset=374, start_offset=374, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=15, argval=410, argrepr='to L12', offset=376, start_offset=376, starts_line=False, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=380, start_offset=380, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=382, start_offset=382, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=384, start_offset=384, starts_line=True, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=4, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=394, start_offset=394, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=396, start_offset=396, starts_line=False, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=404, start_offset=404, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=406, start_offset=406, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=75, arg=56, argval=298, argrepr='to L10', offset=408, start_offset=408, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=410, start_offset=410, starts_line=True, line_number=22, label=12, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=412, start_offset=412, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=414, start_offset=414, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=416, start_offset=416, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=418, start_offset=418, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=420, start_offset=420, starts_line=True, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=430, start_offset=430, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=432, start_offset=432, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=440, start_offset=440, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=442, start_offset=442, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=444, start_offset=444, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=446, start_offset=446, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=448, start_offset=448, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='BINARY_OP', opcode=44, arg=23, argval=23, argrepr='-=', offset=160, start_offset=160, starts_line=False, line_number=13, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('descr', 4, b'\x00\x00\x00\x00\x00\x00\x00\x00')]), + Instruction(opname='STORE_FAST', opcode=109, arg=0, argval='i', argrepr='i', offset=172, start_offset=172, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=174, start_offset=174, starts_line=True, line_number=14, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=6, argval=6, argrepr='', offset=176, start_offset=176, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=56, arg=148, argval='>', argrepr='bool(>)', offset=178, start_offset=178, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=3, argval=192, argrepr='to L6', offset=182, start_offset=182, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=186, start_offset=186, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=74, arg=37, argval=118, argrepr='to L5', offset=188, start_offset=188, starts_line=True, line_number=15, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=192, start_offset=192, starts_line=True, line_number=16, label=6, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=4, argval=4, argrepr='', offset=194, start_offset=194, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=56, arg=18, argval='<', argrepr='bool(<)', offset=196, start_offset=196, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=3, argval=210, argrepr='to L7', offset=200, start_offset=200, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=204, start_offset=204, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=74, arg=46, argval=118, argrepr='to L5', offset=206, start_offset=206, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='JUMP_FORWARD', opcode=76, arg=11, argval=234, argrepr='to L9', offset=210, start_offset=210, starts_line=True, line_number=17, label=7, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=212, start_offset=212, starts_line=True, line_number=19, label=8, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=1, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=222, start_offset=222, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=224, start_offset=224, starts_line=False, line_number=19, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=232, start_offset=232, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), + Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=234, start_offset=234, starts_line=True, line_number=20, label=9, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=1, argval=1, argrepr='', offset=236, start_offset=236, starts_line=True, line_number=21, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=0, argval=0, argrepr='', offset=238, start_offset=238, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), + Instruction(opname='BINARY_OP', opcode=44, arg=11, argval=11, argrepr='/', offset=240, start_offset=240, starts_line=False, line_number=21, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('descr', 4, b'\x00\x00\x00\x00\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=252, start_offset=252, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=254, start_offset=254, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=59, arg=1, argval=1, argrepr='', offset=256, start_offset=256, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SPECIAL', opcode=92, arg=1, argval=1, argrepr='__exit__', offset=258, start_offset=258, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='SWAP', opcode=114, arg=2, argval=2, argrepr='', offset=260, start_offset=260, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='SWAP', opcode=114, arg=3, argval=3, argrepr='', offset=262, start_offset=262, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SPECIAL', opcode=92, arg=0, argval=0, argrepr='__enter__', offset=264, start_offset=264, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=0, argval=0, argrepr='', offset=266, start_offset=266, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='STORE_FAST', opcode=109, arg=1, argval='dodgy', argrepr='dodgy', offset=274, start_offset=274, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=276, start_offset=276, starts_line=True, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=2, argval='Never reach this', argrepr="'Never reach this'", offset=286, start_offset=286, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=288, start_offset=288, starts_line=False, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=296, start_offset=296, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=298, start_offset=298, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=300, start_offset=300, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=302, start_offset=302, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=3, argval=3, argrepr='', offset=304, start_offset=304, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=312, start_offset=312, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=314, start_offset=314, starts_line=True, line_number=28, label=10, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=324, start_offset=324, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=326, start_offset=326, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=334, start_offset=334, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=336, start_offset=336, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='RETURN_VALUE', opcode=35, arg=None, argval=None, argrepr='', offset=338, start_offset=338, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=340, start_offset=340, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='WITH_EXCEPT_START', opcode=43, arg=None, argval=None, argrepr='', offset=342, start_offset=342, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='TO_BOOL', opcode=39, arg=None, argval=None, argrepr='', offset=344, start_offset=344, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=2, argval=360, argrepr='to L11', offset=352, start_offset=352, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=356, start_offset=356, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=2, argval=2, argrepr='', offset=358, start_offset=358, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=360, start_offset=360, starts_line=False, line_number=25, label=11, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=364, start_offset=364, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=366, start_offset=366, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=368, start_offset=368, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=75, arg=29, argval=314, argrepr='to L10', offset=370, start_offset=370, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=372, start_offset=372, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=374, start_offset=374, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=376, start_offset=376, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=378, start_offset=378, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=380, start_offset=380, starts_line=True, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='CHECK_EXC_MATCH', opcode=5, arg=None, argval=None, argrepr='', offset=390, start_offset=390, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=15, argval=426, argrepr='to L12', offset=392, start_offset=392, starts_line=False, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=396, start_offset=396, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=398, start_offset=398, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=400, start_offset=400, starts_line=True, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=4, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=410, start_offset=410, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=412, start_offset=412, starts_line=False, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=420, start_offset=420, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=422, start_offset=422, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=75, arg=56, argval=314, argrepr='to L10', offset=424, start_offset=424, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=426, start_offset=426, starts_line=True, line_number=22, label=12, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=428, start_offset=428, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=430, start_offset=430, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=432, start_offset=432, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=434, start_offset=434, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=436, start_offset=436, starts_line=True, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=81, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=446, start_offset=446, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=448, start_offset=448, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=456, start_offset=456, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=458, start_offset=458, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=460, start_offset=460, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=462, start_offset=462, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=464, start_offset=464, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), ] # One last piece of inspect fodder to check the default line number handling diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py index 43e3e56639db62..bea0e48883bf9a 100644 --- a/Lib/test/test_monitoring.py +++ b/Lib/test/test_monitoring.py @@ -1649,7 +1649,7 @@ def foo(n=0): return None in_loop = ('branch left', 'foo', 10, 16) - exit_loop = ('branch right', 'foo', 10, 32) + exit_loop = ('branch right', 'foo', 10, 40) self.check_events(foo, recorders = BRANCH_OFFSET_RECORDERS, expected = [ in_loop, in_loop, diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index b80ccbf17f1ee6..72b845fcc8fdbf 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -1338,6 +1338,31 @@ def binary_op_add_unicode(): self.assert_specialized(binary_op_add_unicode, "BINARY_OP_ADD_UNICODE") self.assert_no_opcode(binary_op_add_unicode, "BINARY_OP") + def binary_op_add_extend(): + for _ in range(100): + a, b = 6, 3.0 + c = a + b + self.assertEqual(c, 9.0) + c = b + a + self.assertEqual(c, 9.0) + c = a - b + self.assertEqual(c, 3.0) + c = b - a + self.assertEqual(c, -3.0) + c = a * b + self.assertEqual(c, 18.0) + c = b * a + self.assertEqual(c, 18.0) + c = a / b + self.assertEqual(c, 2.0) + c = b / a + self.assertEqual(c, 0.5) + + binary_op_add_extend() + self.assert_specialized(binary_op_add_extend, "BINARY_OP_EXTEND") + self.assert_no_opcode(binary_op_add_extend, "BINARY_OP") + + @cpython_only @requires_specialization_ft def test_load_super_attr(self): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-10-23-54-16.gh-issue-100239.ijOOUs.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-10-23-54-16.gh-issue-100239.ijOOUs.rst new file mode 100644 index 00000000000000..f58c1fc767515e --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-10-23-54-16.gh-issue-100239.ijOOUs.rst @@ -0,0 +1,2 @@ +Add opcode ``BINARY_OP_EXTEND`` which executes a pair of functions (guard and +specialization functions) accessed from the inline cache. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index a906ded365650c..b1d61a8707bd4c 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -522,6 +522,7 @@ dummy_func( BINARY_OP_SUBTRACT_FLOAT, BINARY_OP_ADD_UNICODE, // BINARY_OP_INPLACE_ADD_UNICODE, // See comments at that opcode. + BINARY_OP_EXTEND, }; op(_GUARD_BOTH_INT, (left, right -- left, right)) { @@ -587,11 +588,11 @@ dummy_func( } macro(BINARY_OP_MULTIPLY_INT) = - _GUARD_BOTH_INT + unused/1 + _BINARY_OP_MULTIPLY_INT; + _GUARD_BOTH_INT + unused/5 + _BINARY_OP_MULTIPLY_INT; macro(BINARY_OP_ADD_INT) = - _GUARD_BOTH_INT + unused/1 + _BINARY_OP_ADD_INT; + _GUARD_BOTH_INT + unused/5 + _BINARY_OP_ADD_INT; macro(BINARY_OP_SUBTRACT_INT) = - _GUARD_BOTH_INT + unused/1 + _BINARY_OP_SUBTRACT_INT; + _GUARD_BOTH_INT + unused/5 + _BINARY_OP_SUBTRACT_INT; op(_GUARD_BOTH_FLOAT, (left, right -- left, right)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); @@ -659,11 +660,11 @@ dummy_func( } macro(BINARY_OP_MULTIPLY_FLOAT) = - _GUARD_BOTH_FLOAT + unused/1 + _BINARY_OP_MULTIPLY_FLOAT; + _GUARD_BOTH_FLOAT + unused/5 + _BINARY_OP_MULTIPLY_FLOAT; macro(BINARY_OP_ADD_FLOAT) = - _GUARD_BOTH_FLOAT + unused/1 + _BINARY_OP_ADD_FLOAT; + _GUARD_BOTH_FLOAT + unused/5 + _BINARY_OP_ADD_FLOAT; macro(BINARY_OP_SUBTRACT_FLOAT) = - _GUARD_BOTH_FLOAT + unused/1 + _BINARY_OP_SUBTRACT_FLOAT; + _GUARD_BOTH_FLOAT + unused/5 + _BINARY_OP_SUBTRACT_FLOAT; op(_GUARD_BOTH_UNICODE, (left, right -- left, right)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); @@ -689,7 +690,7 @@ dummy_func( } macro(BINARY_OP_ADD_UNICODE) = - _GUARD_BOTH_UNICODE + unused/1 + _BINARY_OP_ADD_UNICODE; + _GUARD_BOTH_UNICODE + unused/5 + _BINARY_OP_ADD_UNICODE; // This is a subtle one. It's a super-instruction for // BINARY_OP_ADD_UNICODE followed by STORE_FAST @@ -741,8 +742,34 @@ dummy_func( #endif } + op(_GUARD_BINARY_OP_EXTEND, (descr/4, left, right -- left, right)) { + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + assert(d && d->guard); + int res = d->guard(left_o, right_o); + EXIT_IF(!res); + } + + pure op(_BINARY_OP_EXTEND, (descr/4, left, right -- res)) { + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + + STAT_INC(BINARY_OP, hit); + + PyObject *res_o = d->action(left_o, right_o); + DECREF_INPUTS(); + res = PyStackRef_FromPyObjectSteal(res_o); + } + + macro(BINARY_OP_EXTEND) = + unused/1 + _GUARD_BINARY_OP_EXTEND + rewind/-4 + _BINARY_OP_EXTEND; + macro(BINARY_OP_INPLACE_ADD_UNICODE) = - _GUARD_BOTH_UNICODE + unused/1 + _BINARY_OP_INPLACE_ADD_UNICODE; + _GUARD_BOTH_UNICODE + unused/5 + _BINARY_OP_INPLACE_ADD_UNICODE; family(BINARY_SUBSCR, INLINE_CACHE_ENTRIES_BINARY_SUBSCR) = { BINARY_SUBSCR_DICT, @@ -4742,7 +4769,7 @@ dummy_func( res = PyStackRef_FromPyObjectSteal(res_o); } - macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + _BINARY_OP; + macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + unused/4 + _BINARY_OP; pure inst(SWAP, (bottom_in, unused[oparg-2], top_in -- top_out, unused[oparg-2], bottom_out)) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index cda01bb768c269..ad825881c8228c 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -899,6 +899,51 @@ break; } + case _GUARD_BINARY_OP_EXTEND: { + _PyStackRef right; + _PyStackRef left; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *descr = (PyObject *)CURRENT_OPERAND0(); + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + assert(d && d->guard); + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = d->guard(left_o, right_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (!res) { + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); + } + break; + } + + case _BINARY_OP_EXTEND: { + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *descr = (PyObject *)CURRENT_OPERAND0(); + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + STAT_INC(BINARY_OP, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = d->action(left_o, right_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + PyStackRef_CLOSE(left); + PyStackRef_CLOSE(right); + res = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2] = res; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + break; + } + case _BINARY_SUBSCR: { _PyStackRef sub; _PyStackRef container; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 81408380d6b2b8..dc90f75f2645e1 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -11,10 +11,10 @@ TARGET(BINARY_OP) { frame->instr_ptr = next_instr; - next_instr += 2; + next_instr += 6; INSTRUCTION_STATS(BINARY_OP); PREDICTED(BINARY_OP); - _Py_CODEUNIT* const this_instr = next_instr - 2; + _Py_CODEUNIT* const this_instr = next_instr - 6; (void)this_instr; _PyStackRef lhs; _PyStackRef rhs; @@ -39,6 +39,7 @@ assert(NB_ADD <= oparg); assert(oparg <= NB_INPLACE_XOR); } + /* Skip 4 cache entries */ // _BINARY_OP { PyObject *lhs_o = PyStackRef_AsPyObjectBorrow(lhs); @@ -60,9 +61,9 @@ TARGET(BINARY_OP_ADD_FLOAT) { frame->instr_ptr = next_instr; - next_instr += 2; + next_instr += 6; INSTRUCTION_STATS(BINARY_OP_ADD_FLOAT); - static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); _PyStackRef left; _PyStackRef right; _PyStackRef res; @@ -75,7 +76,7 @@ DEOPT_IF(!PyFloat_CheckExact(left_o), BINARY_OP); DEOPT_IF(!PyFloat_CheckExact(right_o), BINARY_OP); } - /* Skip 1 cache entry */ + /* Skip 5 cache entries */ // _BINARY_OP_ADD_FLOAT { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); @@ -98,9 +99,9 @@ TARGET(BINARY_OP_ADD_INT) { frame->instr_ptr = next_instr; - next_instr += 2; + next_instr += 6; INSTRUCTION_STATS(BINARY_OP_ADD_INT); - static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); _PyStackRef left; _PyStackRef right; _PyStackRef res; @@ -113,7 +114,7 @@ DEOPT_IF(!PyLong_CheckExact(left_o), BINARY_OP); DEOPT_IF(!PyLong_CheckExact(right_o), BINARY_OP); } - /* Skip 1 cache entry */ + /* Skip 5 cache entries */ // _BINARY_OP_ADD_INT { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); @@ -135,9 +136,9 @@ TARGET(BINARY_OP_ADD_UNICODE) { frame->instr_ptr = next_instr; - next_instr += 2; + next_instr += 6; INSTRUCTION_STATS(BINARY_OP_ADD_UNICODE); - static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); _PyStackRef left; _PyStackRef right; _PyStackRef res; @@ -150,7 +151,7 @@ DEOPT_IF(!PyUnicode_CheckExact(left_o), BINARY_OP); DEOPT_IF(!PyUnicode_CheckExact(right_o), BINARY_OP); } - /* Skip 1 cache entry */ + /* Skip 5 cache entries */ // _BINARY_OP_ADD_UNICODE { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); @@ -170,11 +171,57 @@ DISPATCH(); } + TARGET(BINARY_OP_EXTEND) { + _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_EXTEND); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + /* Skip 1 cache entry */ + // _GUARD_BINARY_OP_EXTEND + { + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *descr = read_obj(&this_instr[2].cache); + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + assert(d && d->guard); + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = d->guard(left_o, right_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + DEOPT_IF(!res, BINARY_OP); + } + /* Skip -4 cache entry */ + // _BINARY_OP_EXTEND + { + PyObject *descr = read_obj(&this_instr[2].cache); + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + STAT_INC(BINARY_OP, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = d->action(left_o, right_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + PyStackRef_CLOSE(left); + PyStackRef_CLOSE(right); + res = PyStackRef_FromPyObjectSteal(res_o); + } + stack_pointer[-2] = res; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + DISPATCH(); + } + TARGET(BINARY_OP_INPLACE_ADD_UNICODE) { frame->instr_ptr = next_instr; - next_instr += 2; + next_instr += 6; INSTRUCTION_STATS(BINARY_OP_INPLACE_ADD_UNICODE); - static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); _PyStackRef left; _PyStackRef right; // _GUARD_BOTH_UNICODE @@ -186,7 +233,7 @@ DEOPT_IF(!PyUnicode_CheckExact(left_o), BINARY_OP); DEOPT_IF(!PyUnicode_CheckExact(right_o), BINARY_OP); } - /* Skip 1 cache entry */ + /* Skip 5 cache entries */ // _BINARY_OP_INPLACE_ADD_UNICODE { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); @@ -235,9 +282,9 @@ TARGET(BINARY_OP_MULTIPLY_FLOAT) { frame->instr_ptr = next_instr; - next_instr += 2; + next_instr += 6; INSTRUCTION_STATS(BINARY_OP_MULTIPLY_FLOAT); - static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); _PyStackRef left; _PyStackRef right; _PyStackRef res; @@ -250,7 +297,7 @@ DEOPT_IF(!PyFloat_CheckExact(left_o), BINARY_OP); DEOPT_IF(!PyFloat_CheckExact(right_o), BINARY_OP); } - /* Skip 1 cache entry */ + /* Skip 5 cache entries */ // _BINARY_OP_MULTIPLY_FLOAT { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); @@ -273,9 +320,9 @@ TARGET(BINARY_OP_MULTIPLY_INT) { frame->instr_ptr = next_instr; - next_instr += 2; + next_instr += 6; INSTRUCTION_STATS(BINARY_OP_MULTIPLY_INT); - static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); _PyStackRef left; _PyStackRef right; _PyStackRef res; @@ -288,7 +335,7 @@ DEOPT_IF(!PyLong_CheckExact(left_o), BINARY_OP); DEOPT_IF(!PyLong_CheckExact(right_o), BINARY_OP); } - /* Skip 1 cache entry */ + /* Skip 5 cache entries */ // _BINARY_OP_MULTIPLY_INT { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); @@ -310,9 +357,9 @@ TARGET(BINARY_OP_SUBTRACT_FLOAT) { frame->instr_ptr = next_instr; - next_instr += 2; + next_instr += 6; INSTRUCTION_STATS(BINARY_OP_SUBTRACT_FLOAT); - static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); _PyStackRef left; _PyStackRef right; _PyStackRef res; @@ -325,7 +372,7 @@ DEOPT_IF(!PyFloat_CheckExact(left_o), BINARY_OP); DEOPT_IF(!PyFloat_CheckExact(right_o), BINARY_OP); } - /* Skip 1 cache entry */ + /* Skip 5 cache entries */ // _BINARY_OP_SUBTRACT_FLOAT { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); @@ -348,9 +395,9 @@ TARGET(BINARY_OP_SUBTRACT_INT) { frame->instr_ptr = next_instr; - next_instr += 2; + next_instr += 6; INSTRUCTION_STATS(BINARY_OP_SUBTRACT_INT); - static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); _PyStackRef left; _PyStackRef right; _PyStackRef res; @@ -363,7 +410,7 @@ DEOPT_IF(!PyLong_CheckExact(left_o), BINARY_OP); DEOPT_IF(!PyLong_CheckExact(right_o), BINARY_OP); } - /* Skip 1 cache entry */ + /* Skip 5 cache entries */ // _BINARY_OP_SUBTRACT_INT { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index c5c008fcbe574e..cb6c33f01d3598 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -152,6 +152,7 @@ static void *opcode_targets[256] = { &&TARGET_BINARY_OP_ADD_FLOAT, &&TARGET_BINARY_OP_ADD_INT, &&TARGET_BINARY_OP_ADD_UNICODE, + &&TARGET_BINARY_OP_EXTEND, &&TARGET_BINARY_OP_MULTIPLY_FLOAT, &&TARGET_BINARY_OP_MULTIPLY_INT, &&TARGET_BINARY_OP_SUBTRACT_FLOAT, @@ -233,7 +234,6 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, - &&_unknown_opcode, &&TARGET_INSTRUMENTED_END_FOR, &&TARGET_INSTRUMENTED_POP_ITER, &&TARGET_INSTRUMENTED_END_SEND, diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index aff4493fdc4dd7..90838d274a5e87 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -562,6 +562,19 @@ break; } + case _GUARD_BINARY_OP_EXTEND: { + break; + } + + case _BINARY_OP_EXTEND: { + _Py_UopsSymbol *res; + res = sym_new_not_null(ctx); + stack_pointer[-2] = res; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + break; + } + case _BINARY_SUBSCR: { _Py_UopsSymbol *res; res = sym_new_not_null(ctx); diff --git a/Python/specialize.c b/Python/specialize.c index 8d9f19c8895187..09bfcd34b5a543 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1174,7 +1174,7 @@ do_specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* assert(tp_version != 0); write_u32(lm_cache->type_version, tp_version); /* borrowed */ - write_obj(lm_cache->descr, fget); + write_ptr(lm_cache->descr, fget); specialize(instr, LOAD_ATTR_PROPERTY); return 0; } @@ -1254,7 +1254,7 @@ do_specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* #endif write_u32(lm_cache->keys_version, version); /* borrowed */ - write_obj(lm_cache->descr, descr); + write_ptr(lm_cache->descr, descr); write_u32(lm_cache->type_version, tp_version); specialize(instr, LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN); return 0; @@ -1534,7 +1534,7 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, } #endif write_u32(cache->type_version, tp_version); - write_obj(cache->descr, descr); + write_ptr(cache->descr, descr); if (metaclass_check) { write_u32(cache->keys_version, meta_version); specialize(instr, LOAD_ATTR_CLASS_WITH_METACLASS_CHECK); @@ -1642,7 +1642,7 @@ specialize_attr_loadclassattr(PyObject *owner, _Py_CODEUNIT *instr, * working since Python 2.6 and it's battle-tested. */ write_u32(cache->type_version, tp_version); - write_obj(cache->descr, descr); + write_ptr(cache->descr, descr); return 1; } @@ -2412,6 +2412,92 @@ binary_op_fail_kind(int oparg, PyObject *lhs, PyObject *rhs) } #endif +/** Binary Op Specialization Extensions */ + +/* float-long */ + +static int +float_compactlong_guard(PyObject *lhs, PyObject *rhs) +{ + return ( + PyFloat_CheckExact(lhs) && + PyLong_CheckExact(rhs) && + _PyLong_IsCompact((PyLongObject *)rhs) + ); +} + +#define FLOAT_LONG_ACTION(NAME, OP) \ + static PyObject * \ + (NAME)(PyObject *lhs, PyObject *rhs) \ + { \ + double lhs_val = PyFloat_AsDouble(lhs); \ + Py_ssize_t rhs_val = _PyLong_CompactValue((PyLongObject *)rhs); \ + return PyFloat_FromDouble(lhs_val OP rhs_val); \ + } +FLOAT_LONG_ACTION(float_compactlong_add, +) +FLOAT_LONG_ACTION(float_compactlong_subtract, -) +FLOAT_LONG_ACTION(float_compactlong_multiply, *) +FLOAT_LONG_ACTION(float_compactlong_true_div, /) +#undef FLOAT_LONG_ACTION + +/* long-float */ + +static int +compactlong_float_guard(PyObject *lhs, PyObject *rhs) +{ + return ( + PyFloat_CheckExact(rhs) && + PyLong_CheckExact(lhs) && + _PyLong_IsCompact((PyLongObject *)lhs) + ); +} + +#define LONG_FLOAT_ACTION(NAME, OP) \ + static PyObject * \ + (NAME)(PyObject *lhs, PyObject *rhs) \ + { \ + double rhs_val = PyFloat_AsDouble(rhs); \ + Py_ssize_t lhs_val = _PyLong_CompactValue((PyLongObject *)lhs); \ + return PyFloat_FromDouble(lhs_val OP rhs_val); \ + } +LONG_FLOAT_ACTION(compactlong_float_add, +) +LONG_FLOAT_ACTION(compactlong_float_subtract, -) +LONG_FLOAT_ACTION(compactlong_float_multiply, *) +LONG_FLOAT_ACTION(compactlong_float_true_div, /) +#undef LONG_FLOAT_ACTION + +static _PyBinaryOpSpecializationDescr float_compactlong_specs[NB_OPARG_LAST+1] = { + [NB_ADD] = {float_compactlong_guard, float_compactlong_add}, + [NB_SUBTRACT] = {float_compactlong_guard, float_compactlong_subtract}, + [NB_TRUE_DIVIDE] = {float_compactlong_guard, float_compactlong_true_div}, + [NB_MULTIPLY] = {float_compactlong_guard, float_compactlong_multiply}, +}; + +static _PyBinaryOpSpecializationDescr compactlong_float_specs[NB_OPARG_LAST+1] = { + [NB_ADD] = {compactlong_float_guard, compactlong_float_add}, + [NB_SUBTRACT] = {compactlong_float_guard, compactlong_float_subtract}, + [NB_TRUE_DIVIDE] = {compactlong_float_guard, compactlong_float_true_div}, + [NB_MULTIPLY] = {compactlong_float_guard, compactlong_float_multiply}, +}; + +static int +binary_op_extended_specialization(PyObject *lhs, PyObject *rhs, int oparg, + _PyBinaryOpSpecializationDescr **descr) +{ +#define LOOKUP_SPEC(TABLE, OPARG) \ + if ((TABLE)[(OPARG)].action) { \ + if ((TABLE)[(OPARG)].guard(lhs, rhs)) { \ + *descr = &((TABLE)[OPARG]); \ + return 1; \ + } \ + } + + LOOKUP_SPEC(compactlong_float_specs, oparg); + LOOKUP_SPEC(float_compactlong_specs, oparg); +#undef LOOKUP_SPEC + return 0; +} + void _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *instr, int oparg, _PyStackRef *locals) @@ -2420,6 +2506,12 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in PyObject *rhs = PyStackRef_AsPyObjectBorrow(rhs_st); assert(ENABLE_SPECIALIZATION_FT); assert(_PyOpcode_Caches[BINARY_OP] == INLINE_CACHE_ENTRIES_BINARY_OP); + + _PyBinaryOpCache *cache = (_PyBinaryOpCache *)(instr + 1); + if (instr->op.code == BINARY_OP_EXTEND) { + write_ptr(cache->external_cache, NULL); + } + switch (oparg) { case NB_ADD: case NB_INPLACE_ADD: @@ -2474,8 +2566,17 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in } break; } + + _PyBinaryOpSpecializationDescr *descr; + if (binary_op_extended_specialization(lhs, rhs, oparg, &descr)) { + specialize(instr, BINARY_OP_EXTEND); + write_ptr(cache->external_cache, (void*)descr); + return; + } + SPECIALIZATION_FAIL(BINARY_OP, binary_op_fail_kind(oparg, lhs, rhs)); unspecialize(instr); + return; } diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index c8c30a7985aa2e..213165b06866b5 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -378,6 +378,8 @@ Python/pylifecycle.c - INTERPRETER_TRAMPOLINE_CODEDEF - Python/pystate.c - initial - Python/specialize.c - adaptive_opcodes - Python/specialize.c - cache_requirements - +Python/specialize.c - float_compactlong_specs - +Python/specialize.c - compactlong_float_specs - Python/stdlib_module_names.h - _Py_stdlib_module_names - Python/sysmodule.c - perf_map_state - Python/sysmodule.c - _PySys_ImplCacheTag - diff --git a/Tools/cases_generator/parsing.py b/Tools/cases_generator/parsing.py index de31d9b232f9df..41b36b6a546360 100644 --- a/Tools/cases_generator/parsing.py +++ b/Tools/cases_generator/parsing.py @@ -357,9 +357,12 @@ def uops(self) -> list[UOp] | None: def uop(self) -> UOp | None: if tkn := self.expect(lx.IDENTIFIER): if self.expect(lx.DIVIDE): + sign = 1 + if negate := self.expect(lx.MINUS): + sign = -1 if num := self.expect(lx.NUMBER): try: - size = int(num.text) + size = sign * int(num.text) except ValueError: raise self.make_syntax_error( f"Expected integer, got {num.text!r}" From f48702dade921beed3e227d2a5ac82a9ae2533d0 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 16 Jan 2025 18:41:40 +0300 Subject: [PATCH 332/376] gh-128910: Remove `_PyTrash_begin` and `_PyTrash_end` C-API functions (#128919) --- Include/cpython/object.h | 3 --- .../2025-01-16-18-16-18.gh-issue-128910.9pqfab.rst | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-16-18-16-18.gh-issue-128910.9pqfab.rst diff --git a/Include/cpython/object.h b/Include/cpython/object.h index c8c6bc97fa32ee..ba31e2464abf84 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -475,9 +475,6 @@ partially-deallocated object. To check this, the tp_dealloc function must be passed as second argument to Py_TRASHCAN_BEGIN(). */ -/* Python 3.9 private API, invoked by the macros below. */ -PyAPI_FUNC(int) _PyTrash_begin(PyThreadState *tstate, PyObject *op); -PyAPI_FUNC(void) _PyTrash_end(PyThreadState *tstate); PyAPI_FUNC(void) _PyTrash_thread_deposit_object(PyThreadState *tstate, PyObject *op); PyAPI_FUNC(void) _PyTrash_thread_destroy_chain(PyThreadState *tstate); diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-16-18-16-18.gh-issue-128910.9pqfab.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-16-18-16-18.gh-issue-128910.9pqfab.rst new file mode 100644 index 00000000000000..e095ba9ebf6be4 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-16-18-16-18.gh-issue-128910.9pqfab.rst @@ -0,0 +1,2 @@ +Undocumented and unused private C-API functions ``_PyTrash_begin`` and +``_PyTrash_end`` are removed. From 211f41316b7f205d18eb65c1ececd7f7fb30b02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Srinivas=20Reddy=20Thatiparthy=20=28=E0=B0=A4=E0=B0=BE?= =?UTF-8?q?=E0=B0=9F=E0=B0=BF=E0=B0=AA=E0=B0=B0=E0=B1=8D=E0=B0=A4=E0=B0=BF?= =?UTF-8?q?=20=E0=B0=B6=E0=B1=8D=E0=B0=B0=E0=B1=80=E0=B0=A8=E0=B0=BF?= =?UTF-8?q?=E0=B0=B5=E0=B0=BE=E0=B0=B8=E0=B1=8D=20=20=E0=B0=B0=E0=B1=86?= =?UTF-8?q?=E0=B0=A1=E0=B1=8D=E0=B0=A1=E0=B0=BF=29?= Date: Thu, 16 Jan 2025 22:02:17 +0530 Subject: [PATCH 333/376] Fix typo in doc (#128917) --- InternalDocs/generators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InternalDocs/generators.md b/InternalDocs/generators.md index 608bd215aae65a..87fbb91236844b 100644 --- a/InternalDocs/generators.md +++ b/InternalDocs/generators.md @@ -11,7 +11,7 @@ execution state. A generator object resumes execution in its frame when its `send()` method is called. This is analogous to a function executing in its own -fram when it is called, but a function returns to the calling frame only once, +frame when it is called, but a function returns to the calling frame only once, while a generator "returns" execution to the caller's frame every time it emits a new item with a [`yield` expression](https://docs.python.org/dev/reference/expressions.html#yield-expressions). From 27494dd9ad6032c29e273cd7f45c204c00d6512c Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 16 Jan 2025 21:29:16 +0000 Subject: [PATCH 334/376] gh-121604: fix warnings in test_importlib.test_abc and test_importlib.test_windows (GH-128904) --- Lib/test/test_importlib/test_abc.py | 26 +++++++++++++++++---- Lib/test/test_importlib/test_windows.py | 31 +++++++++++++++++++++---- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_importlib/test_abc.py b/Lib/test/test_importlib/test_abc.py index 00af2dd712425a..92a77e079e57e7 100644 --- a/Lib/test/test_importlib/test_abc.py +++ b/Lib/test/test_importlib/test_abc.py @@ -226,7 +226,15 @@ class ResourceLoaderDefaultsTests(ABCTestHarness): SPLIT = make_abc_subclasses(ResourceLoader) def test_get_data(self): - with self.assertRaises(IOError): + with ( + self.assertRaises(IOError), + self.assertWarnsRegex( + DeprecationWarning, + r"importlib\.abc\.ResourceLoader is deprecated in favour of " + r"supporting resource loading through importlib\.resources" + r"\.abc\.TraversableResources.", + ), + ): self.ins.get_data('/some/path') @@ -927,9 +935,19 @@ def get_filename(self, fullname): def path_stats(self, path): return {'mtime': 1} - - loader = DummySourceLoader() - with self.assertWarns(DeprecationWarning): + with self.assertWarnsRegex( + DeprecationWarning, + r"importlib\.abc\.ResourceLoader is deprecated in favour of " + r"supporting resource loading through importlib\.resources" + r"\.abc\.TraversableResources.", + ): + loader = DummySourceLoader() + + with self.assertWarnsRegex( + DeprecationWarning, + r"SourceLoader\.path_mtime is deprecated in favour of " + r"SourceLoader\.path_stats\(\)\." + ): loader.path_mtime('foo.py') diff --git a/Lib/test/test_importlib/test_windows.py b/Lib/test/test_importlib/test_windows.py index f32680bdbeb9e3..bef4fb46f859a4 100644 --- a/Lib/test/test_importlib/test_windows.py +++ b/Lib/test/test_importlib/test_windows.py @@ -91,23 +91,46 @@ class WindowsRegistryFinderTests: test_module = "spamham{}".format(os.getpid()) def test_find_spec_missing(self): - spec = self.machinery.WindowsRegistryFinder.find_spec('spam') + with self.assertWarnsRegex( + DeprecationWarning, + r"importlib\.machinery\.WindowsRegistryFinder is deprecated; " + r"use site configuration instead\. Future versions of Python may " + r"not enable this finder by default\." + ): + spec = self.machinery.WindowsRegistryFinder.find_spec('spam') self.assertIsNone(spec) def test_module_found(self): with setup_module(self.machinery, self.test_module): - spec = self.machinery.WindowsRegistryFinder.find_spec(self.test_module) + with self.assertWarnsRegex( + DeprecationWarning, + r"importlib\.machinery\.WindowsRegistryFinder is deprecated; " + r"use site configuration instead\. Future versions of Python may " + r"not enable this finder by default\." + ): + spec = self.machinery.WindowsRegistryFinder.find_spec(self.test_module) self.assertIsNotNone(spec) def test_module_not_found(self): with setup_module(self.machinery, self.test_module, path="."): - spec = self.machinery.WindowsRegistryFinder.find_spec(self.test_module) + with self.assertWarnsRegex( + DeprecationWarning, + r"importlib\.machinery\.WindowsRegistryFinder is deprecated; " + r"use site configuration instead\. Future versions of Python may " + r"not enable this finder by default\." + ): + spec = self.machinery.WindowsRegistryFinder.find_spec(self.test_module) self.assertIsNone(spec) def test_raises_deprecation_warning(self): # WindowsRegistryFinder is not meant to be instantiated, so the # deprecation warning is raised in the 'find_spec' method instead. - with self.assertWarns(DeprecationWarning): + with self.assertWarnsRegex( + DeprecationWarning, + r"importlib\.machinery\.WindowsRegistryFinder is deprecated; " + r"use site configuration instead\. Future versions of Python may " + r"not enable this finder by default\." + ): self.machinery.WindowsRegistryFinder.find_spec('spam') (Frozen_WindowsRegistryFinderTests, From b44ff6d0df9ec2d60be63e5a106ca079464ece52 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang <44627253+xuantengh@users.noreply.github.com> Date: Fri, 17 Jan 2025 07:57:04 +0800 Subject: [PATCH 335/376] GH-126599: Remove the "counter" optimizer/executor (GH-126853) --- Include/internal/pycore_optimizer.h | 8 - Include/internal/pycore_uop_ids.h | 185 +++++++++--------- Include/internal/pycore_uop_metadata.h | 4 - Lib/test/test_capi/test_opt.py | 98 +--------- Lib/test/test_monitoring.py | 15 -- ...-01-01-03-25-38.gh-issue-126599.MRCYlH.rst | 1 + Modules/_testinternalcapi.c | 13 -- Objects/object.c | 2 - Python/bytecodes.c | 7 - Python/executor_cases.c.h | 10 - Python/optimizer.c | 99 ---------- Python/optimizer_cases.c.h | 6 - Tools/c-analyzer/cpython/ignored.tsv | 2 - 13 files changed, 97 insertions(+), 353 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-01-01-03-25-38.gh-issue-126599.MRCYlH.rst diff --git a/Include/internal/pycore_optimizer.h b/Include/internal/pycore_optimizer.h index bc7cfcde613d65..a02b9ab4291bfc 100644 --- a/Include/internal/pycore_optimizer.h +++ b/Include/internal/pycore_optimizer.h @@ -98,11 +98,6 @@ struct _PyOptimizerObject { }; /** Test support **/ -typedef struct { - _PyOptimizerObject base; - int64_t count; -} _PyCounterOptimizerObject; - _PyOptimizerObject *_Py_SetOptimizer(PyInterpreterState *interp, _PyOptimizerObject* optimizer); @@ -119,7 +114,6 @@ PyAPI_FUNC(void) _Py_Executor_DependsOn(_PyExecutorObject *executor, void *obj); // Export for '_testinternalcapi' shared extension. PyAPI_FUNC(_PyOptimizerObject *) _Py_GetOptimizer(void); PyAPI_FUNC(int) _Py_SetTier2Optimizer(_PyOptimizerObject* optimizer); -PyAPI_FUNC(PyObject *) _PyOptimizer_NewCounter(void); PyAPI_FUNC(PyObject *) _PyOptimizer_NewUOpOptimizer(void); #define _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS 3 @@ -150,8 +144,6 @@ int _Py_uop_analyze_and_optimize(struct _PyInterpreterFrame *frame, _PyUOpInstruction *trace, int trace_len, int curr_stackentries, _PyBloomFilter *dependencies); -extern PyTypeObject _PyCounterExecutor_Type; -extern PyTypeObject _PyCounterOptimizer_Type; extern PyTypeObject _PyDefaultOptimizer_Type; extern PyTypeObject _PyUOpExecutor_Type; extern PyTypeObject _PyUOpOptimizer_Type; diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index f95defbc364aed..066165a2c810d5 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -164,144 +164,143 @@ extern "C" { #define _INSTRUMENTED_POP_JUMP_IF_NONE INSTRUMENTED_POP_JUMP_IF_NONE #define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE INSTRUMENTED_POP_JUMP_IF_NOT_NONE #define _INSTRUMENTED_POP_JUMP_IF_TRUE INSTRUMENTED_POP_JUMP_IF_TRUE -#define _INTERNAL_INCREMENT_OPT_COUNTER 399 -#define _IS_NONE 400 +#define _IS_NONE 399 #define _IS_OP IS_OP -#define _ITER_CHECK_LIST 401 -#define _ITER_CHECK_RANGE 402 -#define _ITER_CHECK_TUPLE 403 -#define _ITER_JUMP_LIST 404 -#define _ITER_JUMP_RANGE 405 -#define _ITER_JUMP_TUPLE 406 -#define _ITER_NEXT_LIST 407 -#define _ITER_NEXT_RANGE 408 -#define _ITER_NEXT_TUPLE 409 -#define _JUMP_TO_TOP 410 +#define _ITER_CHECK_LIST 400 +#define _ITER_CHECK_RANGE 401 +#define _ITER_CHECK_TUPLE 402 +#define _ITER_JUMP_LIST 403 +#define _ITER_JUMP_RANGE 404 +#define _ITER_JUMP_TUPLE 405 +#define _ITER_NEXT_LIST 406 +#define _ITER_NEXT_RANGE 407 +#define _ITER_NEXT_TUPLE 408 +#define _JUMP_TO_TOP 409 #define _LIST_APPEND LIST_APPEND #define _LIST_EXTEND LIST_EXTEND -#define _LOAD_ATTR 411 -#define _LOAD_ATTR_CLASS 412 -#define _LOAD_ATTR_CLASS_0 413 -#define _LOAD_ATTR_CLASS_1 414 +#define _LOAD_ATTR 410 +#define _LOAD_ATTR_CLASS 411 +#define _LOAD_ATTR_CLASS_0 412 +#define _LOAD_ATTR_CLASS_1 413 #define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN -#define _LOAD_ATTR_INSTANCE_VALUE 415 -#define _LOAD_ATTR_INSTANCE_VALUE_0 416 -#define _LOAD_ATTR_INSTANCE_VALUE_1 417 -#define _LOAD_ATTR_METHOD_LAZY_DICT 418 -#define _LOAD_ATTR_METHOD_NO_DICT 419 -#define _LOAD_ATTR_METHOD_WITH_VALUES 420 -#define _LOAD_ATTR_MODULE 421 -#define _LOAD_ATTR_MODULE_FROM_KEYS 422 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 423 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 424 -#define _LOAD_ATTR_PROPERTY_FRAME 425 -#define _LOAD_ATTR_SLOT 426 -#define _LOAD_ATTR_SLOT_0 427 -#define _LOAD_ATTR_SLOT_1 428 -#define _LOAD_ATTR_WITH_HINT 429 +#define _LOAD_ATTR_INSTANCE_VALUE 414 +#define _LOAD_ATTR_INSTANCE_VALUE_0 415 +#define _LOAD_ATTR_INSTANCE_VALUE_1 416 +#define _LOAD_ATTR_METHOD_LAZY_DICT 417 +#define _LOAD_ATTR_METHOD_NO_DICT 418 +#define _LOAD_ATTR_METHOD_WITH_VALUES 419 +#define _LOAD_ATTR_MODULE 420 +#define _LOAD_ATTR_MODULE_FROM_KEYS 421 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 422 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 423 +#define _LOAD_ATTR_PROPERTY_FRAME 424 +#define _LOAD_ATTR_SLOT 425 +#define _LOAD_ATTR_SLOT_0 426 +#define _LOAD_ATTR_SLOT_1 427 +#define _LOAD_ATTR_WITH_HINT 428 #define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS -#define _LOAD_BYTECODE 430 +#define _LOAD_BYTECODE 429 #define _LOAD_COMMON_CONSTANT LOAD_COMMON_CONSTANT #define _LOAD_CONST LOAD_CONST #define _LOAD_CONST_IMMORTAL LOAD_CONST_IMMORTAL -#define _LOAD_CONST_INLINE 431 -#define _LOAD_CONST_INLINE_BORROW 432 -#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 433 -#define _LOAD_CONST_INLINE_WITH_NULL 434 +#define _LOAD_CONST_INLINE 430 +#define _LOAD_CONST_INLINE_BORROW 431 +#define _LOAD_CONST_INLINE_BORROW_WITH_NULL 432 +#define _LOAD_CONST_INLINE_WITH_NULL 433 #define _LOAD_CONST_MORTAL LOAD_CONST_MORTAL #define _LOAD_DEREF LOAD_DEREF -#define _LOAD_FAST 435 -#define _LOAD_FAST_0 436 -#define _LOAD_FAST_1 437 -#define _LOAD_FAST_2 438 -#define _LOAD_FAST_3 439 -#define _LOAD_FAST_4 440 -#define _LOAD_FAST_5 441 -#define _LOAD_FAST_6 442 -#define _LOAD_FAST_7 443 +#define _LOAD_FAST 434 +#define _LOAD_FAST_0 435 +#define _LOAD_FAST_1 436 +#define _LOAD_FAST_2 437 +#define _LOAD_FAST_3 438 +#define _LOAD_FAST_4 439 +#define _LOAD_FAST_5 440 +#define _LOAD_FAST_6 441 +#define _LOAD_FAST_7 442 #define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR #define _LOAD_FAST_CHECK LOAD_FAST_CHECK #define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST #define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF #define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS -#define _LOAD_GLOBAL 444 -#define _LOAD_GLOBAL_BUILTINS 445 -#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 446 -#define _LOAD_GLOBAL_MODULE 447 -#define _LOAD_GLOBAL_MODULE_FROM_KEYS 448 +#define _LOAD_GLOBAL 443 +#define _LOAD_GLOBAL_BUILTINS 444 +#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 445 +#define _LOAD_GLOBAL_MODULE 446 +#define _LOAD_GLOBAL_MODULE_FROM_KEYS 447 #define _LOAD_LOCALS LOAD_LOCALS #define _LOAD_NAME LOAD_NAME -#define _LOAD_SMALL_INT 449 -#define _LOAD_SMALL_INT_0 450 -#define _LOAD_SMALL_INT_1 451 -#define _LOAD_SMALL_INT_2 452 -#define _LOAD_SMALL_INT_3 453 +#define _LOAD_SMALL_INT 448 +#define _LOAD_SMALL_INT_0 449 +#define _LOAD_SMALL_INT_1 450 +#define _LOAD_SMALL_INT_2 451 +#define _LOAD_SMALL_INT_3 452 #define _LOAD_SPECIAL LOAD_SPECIAL #define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR #define _LOAD_SUPER_ATTR_METHOD LOAD_SUPER_ATTR_METHOD -#define _MAKE_CALLARGS_A_TUPLE 454 +#define _MAKE_CALLARGS_A_TUPLE 453 #define _MAKE_CELL MAKE_CELL #define _MAKE_FUNCTION MAKE_FUNCTION -#define _MAKE_WARM 455 +#define _MAKE_WARM 454 #define _MAP_ADD MAP_ADD #define _MATCH_CLASS MATCH_CLASS #define _MATCH_KEYS MATCH_KEYS #define _MATCH_MAPPING MATCH_MAPPING #define _MATCH_SEQUENCE MATCH_SEQUENCE -#define _MAYBE_EXPAND_METHOD 456 -#define _MAYBE_EXPAND_METHOD_KW 457 -#define _MONITOR_CALL 458 -#define _MONITOR_JUMP_BACKWARD 459 -#define _MONITOR_RESUME 460 +#define _MAYBE_EXPAND_METHOD 455 +#define _MAYBE_EXPAND_METHOD_KW 456 +#define _MONITOR_CALL 457 +#define _MONITOR_JUMP_BACKWARD 458 +#define _MONITOR_RESUME 459 #define _NOP NOP #define _POP_EXCEPT POP_EXCEPT -#define _POP_JUMP_IF_FALSE 461 -#define _POP_JUMP_IF_TRUE 462 +#define _POP_JUMP_IF_FALSE 460 +#define _POP_JUMP_IF_TRUE 461 #define _POP_TOP POP_TOP -#define _POP_TOP_LOAD_CONST_INLINE_BORROW 463 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW 462 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 464 +#define _PUSH_FRAME 463 #define _PUSH_NULL PUSH_NULL -#define _PY_FRAME_GENERAL 465 -#define _PY_FRAME_KW 466 -#define _QUICKEN_RESUME 467 -#define _REPLACE_WITH_TRUE 468 +#define _PY_FRAME_GENERAL 464 +#define _PY_FRAME_KW 465 +#define _QUICKEN_RESUME 466 +#define _REPLACE_WITH_TRUE 467 #define _RESUME_CHECK RESUME_CHECK #define _RETURN_GENERATOR RETURN_GENERATOR #define _RETURN_VALUE RETURN_VALUE -#define _SAVE_RETURN_OFFSET 469 -#define _SEND 470 -#define _SEND_GEN_FRAME 471 +#define _SAVE_RETURN_OFFSET 468 +#define _SEND 469 +#define _SEND_GEN_FRAME 470 #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _START_EXECUTOR 472 -#define _STORE_ATTR 473 -#define _STORE_ATTR_INSTANCE_VALUE 474 -#define _STORE_ATTR_SLOT 475 -#define _STORE_ATTR_WITH_HINT 476 +#define _START_EXECUTOR 471 +#define _STORE_ATTR 472 +#define _STORE_ATTR_INSTANCE_VALUE 473 +#define _STORE_ATTR_SLOT 474 +#define _STORE_ATTR_WITH_HINT 475 #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 477 -#define _STORE_FAST_0 478 -#define _STORE_FAST_1 479 -#define _STORE_FAST_2 480 -#define _STORE_FAST_3 481 -#define _STORE_FAST_4 482 -#define _STORE_FAST_5 483 -#define _STORE_FAST_6 484 -#define _STORE_FAST_7 485 +#define _STORE_FAST 476 +#define _STORE_FAST_0 477 +#define _STORE_FAST_1 478 +#define _STORE_FAST_2 479 +#define _STORE_FAST_3 480 +#define _STORE_FAST_4 481 +#define _STORE_FAST_5 482 +#define _STORE_FAST_6 483 +#define _STORE_FAST_7 484 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME -#define _STORE_SLICE 486 -#define _STORE_SUBSCR 487 +#define _STORE_SLICE 485 +#define _STORE_SUBSCR 486 #define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT #define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT #define _SWAP SWAP -#define _TIER2_RESUME_CHECK 488 -#define _TO_BOOL 489 +#define _TIER2_RESUME_CHECK 487 +#define _TO_BOOL 488 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT #define _TO_BOOL_LIST TO_BOOL_LIST @@ -311,13 +310,13 @@ extern "C" { #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 490 +#define _UNPACK_SEQUENCE 489 #define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START #define _YIELD_VALUE YIELD_VALUE -#define MAX_UOP_ID 490 +#define MAX_UOP_ID 489 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 298e918b872c62..7b28667a26e94d 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -288,7 +288,6 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_GLOBAL_MODULE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_LOAD_GLOBAL_BUILTINS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_LOAD_ATTR_MODULE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, - [_INTERNAL_INCREMENT_OPT_COUNTER] = 0, [_DYNAMIC_EXIT] = HAS_ESCAPES_FLAG, [_START_EXECUTOR] = HAS_ESCAPES_FLAG, [_MAKE_WARM] = 0, @@ -445,7 +444,6 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_INIT_CALL_PY_EXACT_ARGS_2] = "_INIT_CALL_PY_EXACT_ARGS_2", [_INIT_CALL_PY_EXACT_ARGS_3] = "_INIT_CALL_PY_EXACT_ARGS_3", [_INIT_CALL_PY_EXACT_ARGS_4] = "_INIT_CALL_PY_EXACT_ARGS_4", - [_INTERNAL_INCREMENT_OPT_COUNTER] = "_INTERNAL_INCREMENT_OPT_COUNTER", [_IS_NONE] = "_IS_NONE", [_IS_OP] = "_IS_OP", [_ITER_CHECK_LIST] = "_ITER_CHECK_LIST", @@ -1127,8 +1125,6 @@ int _PyUop_num_popped(int opcode, int oparg) return 0; case _LOAD_ATTR_MODULE: return 1; - case _INTERNAL_INCREMENT_OPT_COUNTER: - return 1; case _DYNAMIC_EXIT: return 0; case _START_EXECUTOR: diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 12542d8b7fa62e..a74b8fdd3923b7 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -35,90 +35,6 @@ def clear_executors(func): func.__code__ = func.__code__.replace() -@requires_specialization -@unittest.skipIf(Py_GIL_DISABLED, "optimizer not yet supported in free-threaded builds") -@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"), - "Requires optimizer infrastructure") -class TestOptimizerAPI(unittest.TestCase): - - def test_new_counter_optimizer_dealloc(self): - # See gh-108727 - def f(): - _testinternalcapi.new_counter_optimizer() - - f() - - def test_get_set_optimizer(self): - old = _testinternalcapi.get_optimizer() - opt = _testinternalcapi.new_counter_optimizer() - try: - _testinternalcapi.set_optimizer(opt) - self.assertEqual(_testinternalcapi.get_optimizer(), opt) - _testinternalcapi.set_optimizer(None) - self.assertEqual(_testinternalcapi.get_optimizer(), None) - finally: - _testinternalcapi.set_optimizer(old) - - - def test_counter_optimizer(self): - # Generate a new function at each call - ns = {} - exec(textwrap.dedent(f""" - def loop(): - for _ in range({TIER2_THRESHOLD + 1000}): - pass - """), ns, ns) - loop = ns['loop'] - - for repeat in range(5): - opt = _testinternalcapi.new_counter_optimizer() - with temporary_optimizer(opt): - self.assertEqual(opt.get_count(), 0) - with clear_executors(loop): - loop() - self.assertEqual(opt.get_count(), 1001) - - def test_long_loop(self): - "Check that we aren't confused by EXTENDED_ARG" - - # Generate a new function at each call - ns = {} - exec(textwrap.dedent(f""" - def nop(): - pass - - def long_loop(): - for _ in range({TIER2_THRESHOLD + 20}): - nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); - nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); - nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); - nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); - nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); - nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); - nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); - """), ns, ns) - long_loop = ns['long_loop'] - - opt = _testinternalcapi.new_counter_optimizer() - with temporary_optimizer(opt): - self.assertEqual(opt.get_count(), 0) - long_loop() - self.assertEqual(opt.get_count(), 21) # Need iterations to warm up - - def test_code_restore_for_ENTER_EXECUTOR(self): - def testfunc(x): - i = 0 - while i < x: - i += 1 - - opt = _testinternalcapi.new_counter_optimizer() - with temporary_optimizer(opt): - testfunc(1000) - code, replace_code = testfunc.__code__, testfunc.__code__.replace() - self.assertEqual(code, replace_code) - self.assertEqual(hash(code), hash(replace_code)) - - def get_first_executor(func): code = func.__code__ co_code = code.co_code @@ -145,14 +61,6 @@ def get_opnames(ex): "Requires optimizer infrastructure") class TestExecutorInvalidation(unittest.TestCase): - def setUp(self): - self.old = _testinternalcapi.get_optimizer() - self.opt = _testinternalcapi.new_counter_optimizer() - _testinternalcapi.set_optimizer(self.opt) - - def tearDown(self): - _testinternalcapi.set_optimizer(self.old) - def test_invalidate_object(self): # Generate a new set of functions at each call ns = {} @@ -167,8 +75,10 @@ def f{n}(): funcs = [ ns[f'f{n}'] for n in range(5)] objects = [object() for _ in range(5)] - for f in funcs: - f() + opt = _testinternalcapi.new_uop_optimizer() + with temporary_optimizer(opt): + for f in funcs: + f() executors = [get_first_executor(f) for f in funcs] # Set things up so each executor depends on the objects # with an equal or lower index. diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py index bea0e48883bf9a..364381e7dce00a 100644 --- a/Lib/test/test_monitoring.py +++ b/Lib/test/test_monitoring.py @@ -12,7 +12,6 @@ import test.support from test.support import requires_specialization_ft, script_helper -from test.support.import_helper import import_module _testcapi = test.support.import_helper.import_module("_testcapi") @@ -2087,20 +2086,6 @@ def callback(code, instruction_offset): class TestOptimizer(MonitoringTestBase, unittest.TestCase): - def setUp(self): - _testinternalcapi = import_module("_testinternalcapi") - if hasattr(_testinternalcapi, "get_optimizer"): - self.old_opt = _testinternalcapi.get_optimizer() - opt = _testinternalcapi.new_counter_optimizer() - _testinternalcapi.set_optimizer(opt) - super(TestOptimizer, self).setUp() - - def tearDown(self): - super(TestOptimizer, self).tearDown() - import _testinternalcapi - if hasattr(_testinternalcapi, "get_optimizer"): - _testinternalcapi.set_optimizer(self.old_opt) - def test_for_loop(self): def test_func(x): i = 0 diff --git a/Misc/NEWS.d/next/C_API/2025-01-01-03-25-38.gh-issue-126599.MRCYlH.rst b/Misc/NEWS.d/next/C_API/2025-01-01-03-25-38.gh-issue-126599.MRCYlH.rst new file mode 100644 index 00000000000000..8362ee3a2b1760 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-01-01-03-25-38.gh-issue-126599.MRCYlH.rst @@ -0,0 +1 @@ +Remove some internal test APIs for the experimental JIT compiler. diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 150d34d168f5e4..98aea5b596e920 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -989,12 +989,6 @@ get_co_framesize(PyObject *self, PyObject *arg) #ifdef _Py_TIER2 -static PyObject * -new_counter_optimizer(PyObject *self, PyObject *arg) -{ - return _PyOptimizer_NewCounter(); -} - static PyObject * new_uop_optimizer(PyObject *self, PyObject *arg) { @@ -1034,12 +1028,6 @@ add_executor_dependency(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "OO", &exec, &obj)) { return NULL; } - /* No way to tell in general if exec is an executor, so we only accept - * counting_executor */ - if (strcmp(Py_TYPE(exec)->tp_name, "counting_executor")) { - PyErr_SetString(PyExc_TypeError, "argument must be a counting_executor"); - return NULL; - } _Py_Executor_DependsOn((_PyExecutorObject *)exec, obj); Py_RETURN_NONE; } @@ -2101,7 +2089,6 @@ static PyMethodDef module_functions[] = { #ifdef _Py_TIER2 {"get_optimizer", get_optimizer, METH_NOARGS, NULL}, {"set_optimizer", set_optimizer, METH_O, NULL}, - {"new_counter_optimizer", new_counter_optimizer, METH_NOARGS, NULL}, {"new_uop_optimizer", new_uop_optimizer, METH_NOARGS, NULL}, {"add_executor_dependency", add_executor_dependency, METH_VARARGS, NULL}, {"invalidate_executors", invalidate_executors, METH_O, NULL}, diff --git a/Objects/object.c b/Objects/object.c index 9befd92e3231c8..4e900d8e79d91a 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2380,8 +2380,6 @@ static PyTypeObject* static_types[] = { &_PyContextTokenMissing_Type, &_PyCoroWrapper_Type, #ifdef _Py_TIER2 - &_PyCounterExecutor_Type, - &_PyCounterOptimizer_Type, &_PyDefaultOptimizer_Type, #endif &_Py_GenericAliasIterType, diff --git a/Python/bytecodes.c b/Python/bytecodes.c index b1d61a8707bd4c..c0ef767a9dd68b 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -5076,13 +5076,6 @@ dummy_func( DECREF_INPUTS(); } - /* Internal -- for testing executors */ - op(_INTERNAL_INCREMENT_OPT_COUNTER, (opt --)) { - _PyCounterOptimizerObject *exe = (_PyCounterOptimizerObject *)PyStackRef_AsPyObjectBorrow(opt); - exe->count++; - DEAD(opt); - } - tier2 op(_DYNAMIC_EXIT, (exit_p/4 --)) { tstate->previous_executor = (PyObject *)current_executor; _PyExitData *exit = (_PyExitData *)exit_p; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index ad825881c8228c..e2eaca2c90fa76 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -6209,16 +6209,6 @@ break; } - case _INTERNAL_INCREMENT_OPT_COUNTER: { - _PyStackRef opt; - opt = stack_pointer[-1]; - _PyCounterOptimizerObject *exe = (_PyCounterOptimizerObject *)PyStackRef_AsPyObjectBorrow(opt); - exe->count++; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - break; - } - case _DYNAMIC_EXIT: { PyObject *exit_p = (PyObject *)CURRENT_OPERAND0(); tstate->previous_executor = (PyObject *)current_executor; diff --git a/Python/optimizer.c b/Python/optimizer.c index 52b3f0a84afedf..9beb47246eb3d6 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -132,8 +132,6 @@ _Py_GetOptimizer(void) static _PyExecutorObject * make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies); -static const _PyBloomFilter EMPTY_FILTER = { 0 }; - _PyOptimizerObject * _Py_SetOptimizer(PyInterpreterState *interp, _PyOptimizerObject *optimizer) { @@ -251,13 +249,6 @@ get_oparg(PyObject *self, PyObject *Py_UNUSED(ignored)) return PyLong_FromUnsignedLong(((_PyExecutorObject *)self)->vm_data.oparg); } -static PyMethodDef executor_methods[] = { - { "is_valid", is_valid, METH_NOARGS, NULL }, - { "get_opcode", get_opcode, METH_NOARGS, NULL }, - { "get_oparg", get_oparg, METH_NOARGS, NULL }, - { NULL, NULL }, -}; - ///////////////////// Experimental UOp Optimizer ///////////////////// static int executor_clear(_PyExecutorObject *executor); @@ -1332,96 +1323,6 @@ _PyOptimizer_NewUOpOptimizer(void) return (PyObject *)opt; } -static void -counter_dealloc(_PyExecutorObject *self) { - /* The optimizer is the operand of the second uop. */ - PyObject *opt = (PyObject *)self->trace[1].operand0; - Py_DECREF(opt); - uop_dealloc(self); -} - -PyTypeObject _PyCounterExecutor_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - .tp_name = "counting_executor", - .tp_basicsize = offsetof(_PyExecutorObject, exits), - .tp_itemsize = 1, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_HAVE_GC, - .tp_dealloc = (destructor)counter_dealloc, - .tp_methods = executor_methods, - .tp_traverse = executor_traverse, - .tp_clear = (inquiry)executor_clear, -}; - -static int -counter_optimize( - _PyOptimizerObject* self, - _PyInterpreterFrame *frame, - _Py_CODEUNIT *instr, - _PyExecutorObject **exec_ptr, - int Py_UNUSED(curr_stackentries), - bool Py_UNUSED(progress_needed) -) -{ - PyCodeObject *code = _PyFrame_GetCode(frame); - int oparg = instr->op.arg; - while (instr->op.code == EXTENDED_ARG) { - instr++; - oparg = (oparg << 8) | instr->op.arg; - } - if (instr->op.code != JUMP_BACKWARD) { - /* Counter optimizer can only handle backward edges */ - return 0; - } - _Py_CODEUNIT *target = instr + 1 + _PyOpcode_Caches[JUMP_BACKWARD] - oparg; - _PyUOpInstruction buffer[4] = { - { .opcode = _START_EXECUTOR, .jump_target = 3, .format=UOP_FORMAT_JUMP }, - { .opcode = _LOAD_CONST_INLINE, .operand0 = (uintptr_t)self }, - { .opcode = _INTERNAL_INCREMENT_OPT_COUNTER }, - { .opcode = _EXIT_TRACE, .target = (uint32_t)(target - _PyCode_CODE(code)), .format=UOP_FORMAT_TARGET } - }; - _PyExecutorObject *executor = make_executor_from_uops(buffer, 4, &EMPTY_FILTER); - if (executor == NULL) { - return -1; - } - Py_INCREF(self); - Py_SET_TYPE(executor, &_PyCounterExecutor_Type); - *exec_ptr = executor; - return 1; -} - -static PyObject * -counter_get_counter(PyObject *self, PyObject *args) -{ - return PyLong_FromLongLong(((_PyCounterOptimizerObject *)self)->count); -} - -static PyMethodDef counter_optimizer_methods[] = { - { "get_count", counter_get_counter, METH_NOARGS, NULL }, - { NULL, NULL }, -}; - -PyTypeObject _PyCounterOptimizer_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - .tp_name = "Counter optimizer", - .tp_basicsize = sizeof(_PyCounterOptimizerObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, - .tp_methods = counter_optimizer_methods, - .tp_dealloc = (destructor)PyObject_Free, -}; - -PyObject * -_PyOptimizer_NewCounter(void) -{ - _PyCounterOptimizerObject *opt = (_PyCounterOptimizerObject *)_PyObject_New(&_PyCounterOptimizer_Type); - if (opt == NULL) { - return NULL; - } - opt->base.optimize = counter_optimize; - opt->count = 0; - return (PyObject *)opt; -} - /***************************************** * Executor management diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 90838d274a5e87..1f2b29c947434f 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -2667,12 +2667,6 @@ break; } - case _INTERNAL_INCREMENT_OPT_COUNTER: { - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - break; - } - case _DYNAMIC_EXIT: { break; } diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index 213165b06866b5..da2cfedfd802c8 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -386,8 +386,6 @@ Python/sysmodule.c - _PySys_ImplCacheTag - Python/sysmodule.c - _PySys_ImplName - Python/sysmodule.c - whatstrings - Python/optimizer.c - _PyDefaultOptimizer_Type - -Python/optimizer.c - _PyCounterExecutor_Type - -Python/optimizer.c - _PyCounterOptimizer_Type - Python/optimizer.c - _PyUOpExecutor_Type - Python/optimizer.c - _PyUOpOptimizer_Type - Python/optimizer.c - _PyOptimizer_Default - From c879de71eb4d5e88c289b654a08fc4e4dafa3b05 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Fri, 17 Jan 2025 02:45:46 +0000 Subject: [PATCH 336/376] ACKS: Add myself (#128924) --- Misc/ACKS | 1 + 1 file changed, 1 insertion(+) diff --git a/Misc/ACKS b/Misc/ACKS index deda334bee7417..7759bd0b95ed8b 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1922,6 +1922,7 @@ Bill Tutt Fraser Tweedale Doobee R. Tzeck Eren Türkay +Stan Ulbrych Lionel Ulmer Adnan Umer Utkarsh Upadhyay From ea6cc26e759704ac69cda07740cd484bf96596ca Mon Sep 17 00:00:00 2001 From: Taneli Hukkinen <3275109+hukkin@users.noreply.github.com> Date: Fri, 17 Jan 2025 04:48:42 +0200 Subject: [PATCH 337/376] gh-118761: Improve import time of `tomllib` (#128907) Improve import time of `tomllib` (in sync with upstream) --- Lib/test/test_tomllib/test_misc.py | 9 ++++++ Lib/tomllib/_parser.py | 31 ++++++++++++------- Lib/tomllib/_re.py | 7 +++-- ...-01-16-10-06-40.gh-issue-118761.z100LC.rst | 2 ++ 4 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-16-10-06-40.gh-issue-118761.z100LC.rst diff --git a/Lib/test/test_tomllib/test_misc.py b/Lib/test/test_tomllib/test_misc.py index 9e677a337a2835..59116afa1f36ad 100644 --- a/Lib/test/test_tomllib/test_misc.py +++ b/Lib/test/test_tomllib/test_misc.py @@ -5,6 +5,7 @@ import copy import datetime from decimal import Decimal as D +import importlib from pathlib import Path import sys import tempfile @@ -113,3 +114,11 @@ def test_inline_table_recursion_limit(self): nest_count=nest_count): recursive_table_toml = nest_count * "key = {" + nest_count * "}" tomllib.loads(recursive_table_toml) + + def test_types_import(self): + """Test that `_types` module runs. + + The module is for type annotations only, so it is otherwise + never imported by tests. + """ + importlib.import_module(f"{tomllib.__name__}._types") diff --git a/Lib/tomllib/_parser.py b/Lib/tomllib/_parser.py index 4d208bcfb4a9a6..0e522c3a69e6fe 100644 --- a/Lib/tomllib/_parser.py +++ b/Lib/tomllib/_parser.py @@ -4,11 +4,7 @@ from __future__ import annotations -from collections.abc import Iterable -import string from types import MappingProxyType -from typing import Any, BinaryIO, NamedTuple -import warnings from ._re import ( RE_DATETIME, @@ -18,7 +14,13 @@ match_to_localtime, match_to_number, ) -from ._types import Key, ParseFloat, Pos + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Iterable + from typing import IO, Any + + from ._types import Key, ParseFloat, Pos ASCII_CTRL = frozenset(chr(i) for i in range(32)) | frozenset(chr(127)) @@ -34,9 +36,11 @@ TOML_WS = frozenset(" \t") TOML_WS_AND_NEWLINE = TOML_WS | frozenset("\n") -BARE_KEY_CHARS = frozenset(string.ascii_letters + string.digits + "-_") +BARE_KEY_CHARS = frozenset( + "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "-_" +) KEY_INITIAL_CHARS = BARE_KEY_CHARS | frozenset("\"'") -HEXDIGIT_CHARS = frozenset(string.hexdigits) +HEXDIGIT_CHARS = frozenset("abcdef" "ABCDEF" "0123456789") BASIC_STR_ESCAPE_REPLACEMENTS = MappingProxyType( { @@ -80,6 +84,8 @@ def __init__( or not isinstance(doc, str) or not isinstance(pos, int) ): + import warnings + warnings.warn( "Free-form arguments for TOMLDecodeError are deprecated. " "Please set 'msg' (str), 'doc' (str) and 'pos' (int) arguments only.", @@ -115,7 +121,7 @@ def __init__( self.colno = colno -def load(fp: BinaryIO, /, *, parse_float: ParseFloat = float) -> dict[str, Any]: +def load(fp: IO[bytes], /, *, parse_float: ParseFloat = float) -> dict[str, Any]: """Parse TOML from a binary file object.""" b = fp.read() try: @@ -139,7 +145,7 @@ def loads(s: str, /, *, parse_float: ParseFloat = float) -> dict[str, Any]: # n f"Expected str object, not '{type(s).__qualname__}'" ) from None pos = 0 - out = Output(NestedDict(), Flags()) + out = Output() header: Key = () parse_float = make_safe_parse_float(parse_float) @@ -290,9 +296,10 @@ def append_nest_to_list(self, key: Key) -> None: cont[last_key] = [{}] -class Output(NamedTuple): - data: NestedDict - flags: Flags +class Output: + def __init__(self) -> None: + self.data = NestedDict() + self.flags = Flags() def skip_chars(src: str, pos: Pos, chars: Iterable[str]) -> Pos: diff --git a/Lib/tomllib/_re.py b/Lib/tomllib/_re.py index 9eacefc729544e..1ca6bef77a0b03 100644 --- a/Lib/tomllib/_re.py +++ b/Lib/tomllib/_re.py @@ -7,9 +7,12 @@ from datetime import date, datetime, time, timedelta, timezone, tzinfo from functools import lru_cache import re -from typing import Any -from ._types import ParseFloat +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Any + + from ._types import ParseFloat # E.g. # - 00:32:00.999999 diff --git a/Misc/NEWS.d/next/Library/2025-01-16-10-06-40.gh-issue-118761.z100LC.rst b/Misc/NEWS.d/next/Library/2025-01-16-10-06-40.gh-issue-118761.z100LC.rst new file mode 100644 index 00000000000000..ea71ecaaeb2936 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-16-10-06-40.gh-issue-118761.z100LC.rst @@ -0,0 +1,2 @@ +Improve import time of :mod:`tomllib` by removing ``typing``, ``string``, +and ``tomllib._types`` imports. Patch by Taneli Hukkinen. From 7807b407306d3c311bcc601cc279623180f8da05 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Fri, 17 Jan 2025 11:39:16 +0000 Subject: [PATCH 338/376] gh-128770: raise warnings as errors in test suite - except for test_socket which still logs warnings (#128726) Co-authored-by: sobolevn Co-authored-by: Brett Cannon --- Lib/test/libregrtest/main.py | 4 +-- Lib/test/test_pyrepl/test_pyrepl.py | 31 ++++++++++++----------- Lib/test/test_socket.py | 38 +++++++++++++++++++++++------ 3 files changed, 47 insertions(+), 26 deletions(-) diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index dcbcc6790c68d8..cd4d512771e05f 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -642,9 +642,9 @@ def _add_ci_python_opts(self, python_opts, keep_environ): if not sys.stdout.write_through: python_opts.append('-u') - # Add warnings filter 'default' + # Add warnings filter 'error' if 'default' not in sys.warnoptions: - python_opts.extend(('-W', 'default')) + python_opts.extend(('-W', 'error')) # Error on bytes/str comparison if sys.flags.bytes_warning < 2: diff --git a/Lib/test/test_pyrepl/test_pyrepl.py b/Lib/test/test_pyrepl/test_pyrepl.py index f29a7ffbd7cafd..113ea1d2fe709f 100644 --- a/Lib/test/test_pyrepl/test_pyrepl.py +++ b/Lib/test/test_pyrepl/test_pyrepl.py @@ -1324,22 +1324,21 @@ def test_readline_history_file(self): if readline.backend != "editline": self.skipTest("GNU readline is not affected by this issue") - hfile = tempfile.NamedTemporaryFile() - self.addCleanup(unlink, hfile.name) - env = os.environ.copy() - env["PYTHON_HISTORY"] = hfile.name - - env["PYTHON_BASIC_REPL"] = "1" - output, exit_code = self.run_repl("spam \nexit()\n", env=env) - self.assertEqual(exit_code, 0) - self.assertIn("spam ", output) - self.assertNotEqual(pathlib.Path(hfile.name).stat().st_size, 0) - self.assertIn("spam\\040", pathlib.Path(hfile.name).read_text()) - - env.pop("PYTHON_BASIC_REPL", None) - output, exit_code = self.run_repl("exit\n", env=env) - self.assertEqual(exit_code, 0) - self.assertNotIn("\\040", pathlib.Path(hfile.name).read_text()) + with tempfile.NamedTemporaryFile() as hfile: + env = os.environ.copy() + env["PYTHON_HISTORY"] = hfile.name + + env["PYTHON_BASIC_REPL"] = "1" + output, exit_code = self.run_repl("spam \nexit()\n", env=env) + self.assertEqual(exit_code, 0) + self.assertIn("spam ", output) + self.assertNotEqual(pathlib.Path(hfile.name).stat().st_size, 0) + self.assertIn("spam\\040", pathlib.Path(hfile.name).read_text()) + + env.pop("PYTHON_BASIC_REPL", None) + output, exit_code = self.run_repl("exit\n", env=env) + self.assertEqual(exit_code, 0) + self.assertNotIn("\\040", pathlib.Path(hfile.name).read_text()) def test_keyboard_interrupt_after_isearch(self): output, exit_code = self.run_repl(["\x12", "\x03", "exit"]) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index faf326d9164e1b..7233847f37bf39 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1,7 +1,8 @@ import unittest +import warnings from test import support from test.support import ( - is_apple, os_helper, refleak_helper, socket_helper, threading_helper + is_apple, os_helper, refleak_helper, socket_helper, threading_helper, ) import _thread as thread import array @@ -198,6 +199,24 @@ def socket_setdefaulttimeout(timeout): socket.setdefaulttimeout(old_timeout) +@contextlib.contextmanager +def downgrade_malformed_data_warning(): + # This warning happens on macos and win, but does not always happen on linux. + if sys.platform not in {"win32", "darwin"}: + yield + return + + with warnings.catch_warnings(): + # TODO: gh-110012, we should investigate why this warning is happening + # and fix it properly. + warnings.filterwarnings( + action="always", + message=r"received malformed or improperly-truncated ancillary data", + category=RuntimeWarning, + ) + yield + + HAVE_SOCKET_CAN = _have_socket_can() HAVE_SOCKET_CAN_ISOTP = _have_socket_can_isotp() @@ -3946,8 +3965,9 @@ def checkTruncatedArray(self, ancbuf, maxdata, mindata=0): # mindata and maxdata bytes when received with buffer size # ancbuf, and that any complete file descriptor numbers are # valid. - msg, ancdata, flags, addr = self.doRecvmsg(self.serv_sock, - len(MSG), ancbuf) + with downgrade_malformed_data_warning(): # TODO: gh-110012 + msg, ancdata, flags, addr = self.doRecvmsg(self.serv_sock, + len(MSG), ancbuf) self.assertEqual(msg, MSG) self.checkRecvmsgAddress(addr, self.cli_addr) self.checkFlags(flags, eor=True, checkset=socket.MSG_CTRUNC) @@ -4298,8 +4318,9 @@ def testSingleCmsgTruncInData(self): self.serv_sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_RECVHOPLIMIT, 1) self.misc_event.set() - msg, ancdata, flags, addr = self.doRecvmsg( - self.serv_sock, len(MSG), socket.CMSG_LEN(SIZEOF_INT) - 1) + with downgrade_malformed_data_warning(): # TODO: gh-110012 + msg, ancdata, flags, addr = self.doRecvmsg( + self.serv_sock, len(MSG), socket.CMSG_LEN(SIZEOF_INT) - 1) self.assertEqual(msg, MSG) self.checkRecvmsgAddress(addr, self.cli_addr) @@ -4402,9 +4423,10 @@ def testSecondCmsgTruncInData(self): self.serv_sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_RECVTCLASS, 1) self.misc_event.set() - msg, ancdata, flags, addr = self.doRecvmsg( - self.serv_sock, len(MSG), - socket.CMSG_SPACE(SIZEOF_INT) + socket.CMSG_LEN(SIZEOF_INT) - 1) + with downgrade_malformed_data_warning(): # TODO: gh-110012 + msg, ancdata, flags, addr = self.doRecvmsg( + self.serv_sock, len(MSG), + socket.CMSG_SPACE(SIZEOF_INT) + socket.CMSG_LEN(SIZEOF_INT) - 1) self.assertEqual(msg, MSG) self.checkRecvmsgAddress(addr, self.cli_addr) From 939df0f9f6a76e0916b3ba53841e1413ab90952e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 17 Jan 2025 13:30:40 +0100 Subject: [PATCH 339/376] gh-58689: Fix os.kill() error handling on Windows (#128932) --- Modules/posixmodule.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index bb8d698bfed375..fb9e55a57703fc 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9582,42 +9582,33 @@ os_kill_impl(PyObject *module, pid_t pid, Py_ssize_t signal) Py_RETURN_NONE; #else /* !MS_WINDOWS */ - PyObject *result; DWORD sig = (DWORD)signal; - DWORD err; - HANDLE handle; #ifdef HAVE_WINDOWS_CONSOLE_IO /* Console processes which share a common console can be sent CTRL+C or CTRL+BREAK events, provided they handle said events. */ if (sig == CTRL_C_EVENT || sig == CTRL_BREAK_EVENT) { if (GenerateConsoleCtrlEvent(sig, (DWORD)pid) == 0) { - err = GetLastError(); - PyErr_SetFromWindowsErr(err); - } - else { - Py_RETURN_NONE; + return PyErr_SetFromWindowsErr(0); } + Py_RETURN_NONE; } #endif /* HAVE_WINDOWS_CONSOLE_IO */ /* If the signal is outside of what GenerateConsoleCtrlEvent can use, attempt to open and terminate the process. */ - handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid); + HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid); if (handle == NULL) { - err = GetLastError(); - return PyErr_SetFromWindowsErr(err); + return PyErr_SetFromWindowsErr(0); } - if (TerminateProcess(handle, sig) == 0) { - err = GetLastError(); - result = PyErr_SetFromWindowsErr(err); - } else { - result = Py_NewRef(Py_None); + BOOL res = TerminateProcess(handle, sig); + CloseHandle(handle); + if (res == 0) { + return PyErr_SetFromWindowsErr(0); } - CloseHandle(handle); - return result; + Py_RETURN_NONE; #endif /* !MS_WINDOWS */ } #endif /* HAVE_KILL */ From 76856ae1659dbba066e51f353b31cb7cf0a8a5fe Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Fri, 17 Jan 2025 15:16:10 +0200 Subject: [PATCH 340/376] Revert "gh-128770: raise warnings as errors in test suite - except for test_socket which still logs warnings (#128726)" (#128936) --- Lib/test/libregrtest/main.py | 4 +-- Lib/test/test_pyrepl/test_pyrepl.py | 31 +++++++++++------------ Lib/test/test_socket.py | 38 ++++++----------------------- 3 files changed, 26 insertions(+), 47 deletions(-) diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index cd4d512771e05f..dcbcc6790c68d8 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -642,9 +642,9 @@ def _add_ci_python_opts(self, python_opts, keep_environ): if not sys.stdout.write_through: python_opts.append('-u') - # Add warnings filter 'error' + # Add warnings filter 'default' if 'default' not in sys.warnoptions: - python_opts.extend(('-W', 'error')) + python_opts.extend(('-W', 'default')) # Error on bytes/str comparison if sys.flags.bytes_warning < 2: diff --git a/Lib/test/test_pyrepl/test_pyrepl.py b/Lib/test/test_pyrepl/test_pyrepl.py index 113ea1d2fe709f..f29a7ffbd7cafd 100644 --- a/Lib/test/test_pyrepl/test_pyrepl.py +++ b/Lib/test/test_pyrepl/test_pyrepl.py @@ -1324,21 +1324,22 @@ def test_readline_history_file(self): if readline.backend != "editline": self.skipTest("GNU readline is not affected by this issue") - with tempfile.NamedTemporaryFile() as hfile: - env = os.environ.copy() - env["PYTHON_HISTORY"] = hfile.name - - env["PYTHON_BASIC_REPL"] = "1" - output, exit_code = self.run_repl("spam \nexit()\n", env=env) - self.assertEqual(exit_code, 0) - self.assertIn("spam ", output) - self.assertNotEqual(pathlib.Path(hfile.name).stat().st_size, 0) - self.assertIn("spam\\040", pathlib.Path(hfile.name).read_text()) - - env.pop("PYTHON_BASIC_REPL", None) - output, exit_code = self.run_repl("exit\n", env=env) - self.assertEqual(exit_code, 0) - self.assertNotIn("\\040", pathlib.Path(hfile.name).read_text()) + hfile = tempfile.NamedTemporaryFile() + self.addCleanup(unlink, hfile.name) + env = os.environ.copy() + env["PYTHON_HISTORY"] = hfile.name + + env["PYTHON_BASIC_REPL"] = "1" + output, exit_code = self.run_repl("spam \nexit()\n", env=env) + self.assertEqual(exit_code, 0) + self.assertIn("spam ", output) + self.assertNotEqual(pathlib.Path(hfile.name).stat().st_size, 0) + self.assertIn("spam\\040", pathlib.Path(hfile.name).read_text()) + + env.pop("PYTHON_BASIC_REPL", None) + output, exit_code = self.run_repl("exit\n", env=env) + self.assertEqual(exit_code, 0) + self.assertNotIn("\\040", pathlib.Path(hfile.name).read_text()) def test_keyboard_interrupt_after_isearch(self): output, exit_code = self.run_repl(["\x12", "\x03", "exit"]) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 7233847f37bf39..faf326d9164e1b 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1,8 +1,7 @@ import unittest -import warnings from test import support from test.support import ( - is_apple, os_helper, refleak_helper, socket_helper, threading_helper, + is_apple, os_helper, refleak_helper, socket_helper, threading_helper ) import _thread as thread import array @@ -199,24 +198,6 @@ def socket_setdefaulttimeout(timeout): socket.setdefaulttimeout(old_timeout) -@contextlib.contextmanager -def downgrade_malformed_data_warning(): - # This warning happens on macos and win, but does not always happen on linux. - if sys.platform not in {"win32", "darwin"}: - yield - return - - with warnings.catch_warnings(): - # TODO: gh-110012, we should investigate why this warning is happening - # and fix it properly. - warnings.filterwarnings( - action="always", - message=r"received malformed or improperly-truncated ancillary data", - category=RuntimeWarning, - ) - yield - - HAVE_SOCKET_CAN = _have_socket_can() HAVE_SOCKET_CAN_ISOTP = _have_socket_can_isotp() @@ -3965,9 +3946,8 @@ def checkTruncatedArray(self, ancbuf, maxdata, mindata=0): # mindata and maxdata bytes when received with buffer size # ancbuf, and that any complete file descriptor numbers are # valid. - with downgrade_malformed_data_warning(): # TODO: gh-110012 - msg, ancdata, flags, addr = self.doRecvmsg(self.serv_sock, - len(MSG), ancbuf) + msg, ancdata, flags, addr = self.doRecvmsg(self.serv_sock, + len(MSG), ancbuf) self.assertEqual(msg, MSG) self.checkRecvmsgAddress(addr, self.cli_addr) self.checkFlags(flags, eor=True, checkset=socket.MSG_CTRUNC) @@ -4318,9 +4298,8 @@ def testSingleCmsgTruncInData(self): self.serv_sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_RECVHOPLIMIT, 1) self.misc_event.set() - with downgrade_malformed_data_warning(): # TODO: gh-110012 - msg, ancdata, flags, addr = self.doRecvmsg( - self.serv_sock, len(MSG), socket.CMSG_LEN(SIZEOF_INT) - 1) + msg, ancdata, flags, addr = self.doRecvmsg( + self.serv_sock, len(MSG), socket.CMSG_LEN(SIZEOF_INT) - 1) self.assertEqual(msg, MSG) self.checkRecvmsgAddress(addr, self.cli_addr) @@ -4423,10 +4402,9 @@ def testSecondCmsgTruncInData(self): self.serv_sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_RECVTCLASS, 1) self.misc_event.set() - with downgrade_malformed_data_warning(): # TODO: gh-110012 - msg, ancdata, flags, addr = self.doRecvmsg( - self.serv_sock, len(MSG), - socket.CMSG_SPACE(SIZEOF_INT) + socket.CMSG_LEN(SIZEOF_INT) - 1) + msg, ancdata, flags, addr = self.doRecvmsg( + self.serv_sock, len(MSG), + socket.CMSG_SPACE(SIZEOF_INT) + socket.CMSG_LEN(SIZEOF_INT) - 1) self.assertEqual(msg, MSG) self.checkRecvmsgAddress(addr, self.cli_addr) From d7f703d54d64c5a380f43e2363c439f7dab0d09d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 17 Jan 2025 14:55:43 +0100 Subject: [PATCH 341/376] gh-59705: Implement _thread.set_name() on Windows (#128675) Implement set_name() with SetThreadDescription() and _get_name() with GetThreadDescription(). If SetThreadDescription() or GetThreadDescription() is not available in kernelbase.dll, delete the method when the _thread module is imported. Truncate the thread name to 32766 characters. Co-authored-by: Eryk Sun --- Lib/test/test_threading.py | 41 +++++++++++++--- Modules/_threadmodule.c | 83 +++++++++++++++++++++++++++++++- Modules/clinic/_threadmodule.c.h | 10 ++-- PC/pyconfig.h.in | 4 ++ 4 files changed, 123 insertions(+), 15 deletions(-) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 3e164a12581dd1..214e1ba0b53dd2 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -2130,6 +2130,15 @@ def test_set_name(self): # Test long non-ASCII name (truncated) "x" * (limit - 1) + "é€", + + # Test long non-BMP names (truncated) creating surrogate pairs + # on Windows + "x" * (limit - 1) + "\U0010FFFF", + "x" * (limit - 2) + "\U0010FFFF" * 2, + "x" + "\U0001f40d" * limit, + "xx" + "\U0001f40d" * limit, + "xxx" + "\U0001f40d" * limit, + "xxxx" + "\U0001f40d" * limit, ] if os_helper.FS_NONASCII: tests.append(f"nonascii:{os_helper.FS_NONASCII}") @@ -2146,15 +2155,31 @@ def work(): work_name = _thread._get_name() for name in tests: - encoded = name.encode(encoding, "replace") - if b'\0' in encoded: - encoded = encoded.split(b'\0', 1)[0] - if truncate is not None: - encoded = encoded[:truncate] - if sys.platform.startswith("solaris"): - expected = encoded.decode("utf-8", "surrogateescape") + if not support.MS_WINDOWS: + encoded = name.encode(encoding, "replace") + if b'\0' in encoded: + encoded = encoded.split(b'\0', 1)[0] + if truncate is not None: + encoded = encoded[:truncate] + if sys.platform.startswith("solaris"): + expected = encoded.decode("utf-8", "surrogateescape") + else: + expected = os.fsdecode(encoded) else: - expected = os.fsdecode(encoded) + size = 0 + chars = [] + for ch in name: + if ord(ch) > 0xFFFF: + size += 2 + else: + size += 1 + if size > truncate: + break + chars.append(ch) + expected = ''.join(chars) + + if '\0' in expected: + expected = expected.split('\0', 1)[0] with self.subTest(name=name, expected=expected): work_name = None diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index d19ae326bd6b48..586ed368024fb1 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -47,6 +47,14 @@ get_thread_state(PyObject *module) } +#ifdef MS_WINDOWS +typedef HRESULT (WINAPI *PF_GET_THREAD_DESCRIPTION)(HANDLE, PCWSTR*); +typedef HRESULT (WINAPI *PF_SET_THREAD_DESCRIPTION)(HANDLE, PCWSTR); +static PF_GET_THREAD_DESCRIPTION pGetThreadDescription = NULL; +static PF_SET_THREAD_DESCRIPTION pSetThreadDescription = NULL; +#endif + + /*[clinic input] module _thread [clinic start generated code]*/ @@ -2368,7 +2376,7 @@ Internal only. Return a non-zero integer that uniquely identifies the main threa of the main interpreter."); -#ifdef HAVE_PTHREAD_GETNAME_NP +#if defined(HAVE_PTHREAD_GETNAME_NP) || defined(MS_WINDOWS) /*[clinic input] _thread._get_name @@ -2379,6 +2387,7 @@ static PyObject * _thread__get_name_impl(PyObject *module) /*[clinic end generated code: output=20026e7ee3da3dd7 input=35cec676833d04c8]*/ { +#ifndef MS_WINDOWS // Linux and macOS are limited to respectively 16 and 64 bytes char name[100]; pthread_t thread = pthread_self(); @@ -2393,11 +2402,26 @@ _thread__get_name_impl(PyObject *module) #else return PyUnicode_DecodeFSDefault(name); #endif +#else + // Windows implementation + assert(pGetThreadDescription != NULL); + + wchar_t *name; + HRESULT hr = pGetThreadDescription(GetCurrentThread(), &name); + if (FAILED(hr)) { + PyErr_SetFromWindowsErr(0); + return NULL; + } + + PyObject *name_obj = PyUnicode_FromWideChar(name, -1); + LocalFree(name); + return name_obj; +#endif } #endif // HAVE_PTHREAD_GETNAME_NP -#ifdef HAVE_PTHREAD_SETNAME_NP +#if defined(HAVE_PTHREAD_SETNAME_NP) || defined(MS_WINDOWS) /*[clinic input] _thread.set_name @@ -2410,6 +2434,7 @@ static PyObject * _thread_set_name_impl(PyObject *module, PyObject *name_obj) /*[clinic end generated code: output=402b0c68e0c0daed input=7e7acd98261be82f]*/ { +#ifndef MS_WINDOWS #ifdef __sun // Solaris always uses UTF-8 const char *encoding = "utf-8"; @@ -2455,6 +2480,35 @@ _thread_set_name_impl(PyObject *module, PyObject *name_obj) return PyErr_SetFromErrno(PyExc_OSError); } Py_RETURN_NONE; +#else + // Windows implementation + assert(pSetThreadDescription != NULL); + + Py_ssize_t len; + wchar_t *name = PyUnicode_AsWideCharString(name_obj, &len); + if (name == NULL) { + return NULL; + } + + if (len > PYTHREAD_NAME_MAXLEN) { + // Truncate the name + Py_UCS4 ch = name[PYTHREAD_NAME_MAXLEN-1]; + if (Py_UNICODE_IS_HIGH_SURROGATE(ch)) { + name[PYTHREAD_NAME_MAXLEN-1] = 0; + } + else { + name[PYTHREAD_NAME_MAXLEN] = 0; + } + } + + HRESULT hr = pSetThreadDescription(GetCurrentThread(), name); + PyMem_Free(name); + if (FAILED(hr)) { + PyErr_SetFromWindowsErr((int)hr); + return NULL; + } + Py_RETURN_NONE; +#endif } #endif // HAVE_PTHREAD_SETNAME_NP @@ -2598,6 +2652,31 @@ thread_module_exec(PyObject *module) } #endif +#ifdef MS_WINDOWS + HMODULE kernelbase = GetModuleHandleW(L"kernelbase.dll"); + if (kernelbase != NULL) { + if (pGetThreadDescription == NULL) { + pGetThreadDescription = (PF_GET_THREAD_DESCRIPTION)GetProcAddress( + kernelbase, "GetThreadDescription"); + } + if (pSetThreadDescription == NULL) { + pSetThreadDescription = (PF_SET_THREAD_DESCRIPTION)GetProcAddress( + kernelbase, "SetThreadDescription"); + } + } + + if (pGetThreadDescription == NULL) { + if (PyObject_DelAttrString(module, "_get_name") < 0) { + return -1; + } + } + if (pSetThreadDescription == NULL) { + if (PyObject_DelAttrString(module, "set_name") < 0) { + return -1; + } + } +#endif + return 0; } diff --git a/Modules/clinic/_threadmodule.c.h b/Modules/clinic/_threadmodule.c.h index 8f0507d40285b3..09b7afebd6d8d9 100644 --- a/Modules/clinic/_threadmodule.c.h +++ b/Modules/clinic/_threadmodule.c.h @@ -8,7 +8,7 @@ preserve #endif #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() -#if defined(HAVE_PTHREAD_GETNAME_NP) +#if (defined(HAVE_PTHREAD_GETNAME_NP) || defined(MS_WINDOWS)) PyDoc_STRVAR(_thread__get_name__doc__, "_get_name($module, /)\n" @@ -28,9 +28,9 @@ _thread__get_name(PyObject *module, PyObject *Py_UNUSED(ignored)) return _thread__get_name_impl(module); } -#endif /* defined(HAVE_PTHREAD_GETNAME_NP) */ +#endif /* (defined(HAVE_PTHREAD_GETNAME_NP) || defined(MS_WINDOWS)) */ -#if defined(HAVE_PTHREAD_SETNAME_NP) +#if (defined(HAVE_PTHREAD_SETNAME_NP) || defined(MS_WINDOWS)) PyDoc_STRVAR(_thread_set_name__doc__, "set_name($module, /, name)\n" @@ -92,7 +92,7 @@ _thread_set_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb return return_value; } -#endif /* defined(HAVE_PTHREAD_SETNAME_NP) */ +#endif /* (defined(HAVE_PTHREAD_SETNAME_NP) || defined(MS_WINDOWS)) */ #ifndef _THREAD__GET_NAME_METHODDEF #define _THREAD__GET_NAME_METHODDEF @@ -101,4 +101,4 @@ _thread_set_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb #ifndef _THREAD_SET_NAME_METHODDEF #define _THREAD_SET_NAME_METHODDEF #endif /* !defined(_THREAD_SET_NAME_METHODDEF) */ -/*[clinic end generated code: output=b5cb85aaccc45bf6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6e88ef6b126cece8 input=a9049054013a1b77]*/ diff --git a/PC/pyconfig.h.in b/PC/pyconfig.h.in index 010f5fe5646630..837461d0e884bc 100644 --- a/PC/pyconfig.h.in +++ b/PC/pyconfig.h.in @@ -753,4 +753,8 @@ Py_NO_ENABLE_SHARED to find out. Also support MS_NO_COREDLL for b/w compat */ /* Define if libssl has X509_VERIFY_PARAM_set1_host and related function */ #define HAVE_X509_VERIFY_PARAM_SET1_HOST 1 +// Truncate the thread name to 64 characters. The OS limit is 32766 wide +// characters, but long names aren't of practical use. +#define PYTHREAD_NAME_MAXLEN 32766 + #endif /* !Py_CONFIG_H */ From 767c89ba7c5a70626df6e75eb56b546bf911b997 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Fri, 17 Jan 2025 10:33:04 -0500 Subject: [PATCH 342/376] gh-58956: Fix a frame refleak in bdb (#128190) --- Lib/bdb.py | 2 + Lib/test/test_pdb.py | 51 +++++++++++++++++++ ...4-12-23-02-09-44.gh-issue-58956.4OdMdT.rst | 1 + 3 files changed, 54 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-12-23-02-09-44.gh-issue-58956.4OdMdT.rst diff --git a/Lib/bdb.py b/Lib/bdb.py index 81bba8a130f97c..73e249621a053b 100644 --- a/Lib/bdb.py +++ b/Lib/bdb.py @@ -404,6 +404,7 @@ def set_trace(self, frame=None): frame.f_trace_lines = True frame = frame.f_back self.set_stepinstr() + self.enterframe = None sys.settrace(self.trace_dispatch) def set_continue(self): @@ -423,6 +424,7 @@ def set_continue(self): for frame, (trace_lines, trace_opcodes) in self.frame_trace_lines_opcodes.items(): frame.f_trace_lines, frame.f_trace_opcodes = trace_lines, trace_opcodes self.frame_trace_lines_opcodes = {} + self.enterframe = None def set_quit(self): """Set quitting attribute to True. diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index ace5544f4106d0..09601623b29ac1 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -3009,6 +3009,57 @@ def test_pdb_f_trace_lines(): (Pdb) continue """ +def test_pdb_frame_refleak(): + """ + pdb should not leak reference to frames + + >>> def frame_leaker(container): + ... import sys + ... container.append(sys._getframe()) + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + ... pass + + >>> def test_function(): + ... import gc + ... container = [] + ... frame_leaker(container) # c + ... print(len(gc.get_referrers(container[0]))) + ... container = [] + ... frame_leaker(container) # n c + ... print(len(gc.get_referrers(container[0]))) + ... container = [] + ... frame_leaker(container) # r c + ... print(len(gc.get_referrers(container[0]))) + + >>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE + ... 'continue', + ... 'next', + ... 'continue', + ... 'return', + ... 'continue', + ... ]): + ... test_function() + > (4)frame_leaker() + -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + (Pdb) continue + 1 + > (4)frame_leaker() + -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + (Pdb) next + > (5)frame_leaker() + -> pass + (Pdb) continue + 1 + > (4)frame_leaker() + -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + (Pdb) return + --Return-- + > (5)frame_leaker()->None + -> pass + (Pdb) continue + 1 + """ + def test_pdb_function_break(): """Testing the line number of break on function diff --git a/Misc/NEWS.d/next/Library/2024-12-23-02-09-44.gh-issue-58956.4OdMdT.rst b/Misc/NEWS.d/next/Library/2024-12-23-02-09-44.gh-issue-58956.4OdMdT.rst new file mode 100644 index 00000000000000..b78bc5aaf44217 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-23-02-09-44.gh-issue-58956.4OdMdT.rst @@ -0,0 +1 @@ +Fixed a frame reference leak in :mod:`bdb`. From d66c08aa757f221c0e8893300edac105dfcde7e8 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Fri, 17 Jan 2025 10:42:27 -0500 Subject: [PATCH 343/376] gh-128923: Use zero to indicate unassigned unique id (#128925) In the free threading build, the per thread reference counting uses a unique id for some objects to index into the local reference count table. Use 0 instead of -1 to indicate that the id is not assigned. This avoids bugs where zero-initialized heap type objects look like they have a unique id assigned. --- Include/internal/pycore_dict.h | 3 +- Include/internal/pycore_object.h | 24 +++++------ Include/internal/pycore_uniqueid.h | 6 ++- Lib/test/test_capi/test_type.py | 7 ++++ Modules/_testcapimodule.c | 64 ++++++++++++++++++++++++++++++ Objects/codeobject.c | 2 +- Objects/dictobject.c | 6 ++- Objects/typeobject.c | 2 +- Python/uniqueid.c | 29 ++++++++------ 9 files changed, 110 insertions(+), 33 deletions(-) diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index 74ac8f2148174c..f4c55ca6cf64d2 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -347,8 +347,7 @@ PyDictObject *_PyObject_MaterializeManagedDict_LockHeld(PyObject *); static inline Py_ssize_t _PyDict_UniqueId(PyDictObject *mp) { - // Offset by one so that _ma_watcher_tag=0 represents an unassigned id - return (Py_ssize_t)(mp->_ma_watcher_tag >> DICT_UNIQUE_ID_SHIFT) - 1; + return (Py_ssize_t)(mp->_ma_watcher_tag >> DICT_UNIQUE_ID_SHIFT); } static inline void diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 322305bc8c664a..19d657070ff221 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -336,20 +336,20 @@ _Py_THREAD_INCREF_OBJECT(PyObject *obj, Py_ssize_t unique_id) { _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET(); - // Unsigned comparison so that `unique_id=-1`, which indicates that - // per-thread refcounting has been disabled on this object, is handled by - // the "else". - if ((size_t)unique_id < (size_t)tstate->refcounts.size) { + // The table index is `unique_id - 1` because 0 is not a valid unique id. + // Unsigned comparison so that `idx=-1` is handled by the "else". + size_t idx = (size_t)(unique_id - 1); + if (idx < (size_t)tstate->refcounts.size) { # ifdef Py_REF_DEBUG _Py_INCREF_IncRefTotal(); # endif _Py_INCREF_STAT_INC(); - tstate->refcounts.values[unique_id]++; + tstate->refcounts.values[idx]++; } else { // The slow path resizes the per-thread refcount array if necessary. - // It handles the unique_id=-1 case to keep the inlinable function smaller. - _PyObject_ThreadIncrefSlow(obj, unique_id); + // It handles the unique_id=0 case to keep the inlinable function smaller. + _PyObject_ThreadIncrefSlow(obj, idx); } } @@ -386,15 +386,15 @@ _Py_THREAD_DECREF_OBJECT(PyObject *obj, Py_ssize_t unique_id) { _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET(); - // Unsigned comparison so that `unique_id=-1`, which indicates that - // per-thread refcounting has been disabled on this object, is handled by - // the "else". - if ((size_t)unique_id < (size_t)tstate->refcounts.size) { + // The table index is `unique_id - 1` because 0 is not a valid unique id. + // Unsigned comparison so that `idx=-1` is handled by the "else". + size_t idx = (size_t)(unique_id - 1); + if (idx < (size_t)tstate->refcounts.size) { # ifdef Py_REF_DEBUG _Py_DECREF_DecRefTotal(); # endif _Py_DECREF_STAT_INC(); - tstate->refcounts.values[unique_id]--; + tstate->refcounts.values[idx]--; } else { // Directly decref the object if the id is not assigned or if diff --git a/Include/internal/pycore_uniqueid.h b/Include/internal/pycore_uniqueid.h index d3db49ddb78103..9d3c866a704894 100644 --- a/Include/internal/pycore_uniqueid.h +++ b/Include/internal/pycore_uniqueid.h @@ -16,7 +16,7 @@ extern "C" { // Per-thread reference counting is used along with deferred reference // counting to avoid scaling bottlenecks due to reference count contention. // -// An id of -1 is used to indicate that an object doesn't use per-thread +// An id of 0 is used to indicate that an object doesn't use per-thread // refcounting. This value is used when the object is finalized by the GC // and during interpreter shutdown to allow the object to be // deallocated promptly when the object's refcount reaches zero. @@ -45,6 +45,8 @@ struct _Py_unique_id_pool { Py_ssize_t size; }; +#define _Py_INVALID_UNIQUE_ID 0 + // Assigns the next id from the pool of ids. extern Py_ssize_t _PyObject_AssignUniqueId(PyObject *obj); @@ -65,7 +67,7 @@ extern void _PyObject_FinalizePerThreadRefcounts(_PyThreadStateImpl *tstate); extern void _PyObject_FinalizeUniqueIdPool(PyInterpreterState *interp); // Increfs the object, resizing the thread-local refcount array if necessary. -PyAPI_FUNC(void) _PyObject_ThreadIncrefSlow(PyObject *obj, Py_ssize_t unique_id); +PyAPI_FUNC(void) _PyObject_ThreadIncrefSlow(PyObject *obj, size_t idx); #endif /* Py_GIL_DISABLED */ diff --git a/Lib/test/test_capi/test_type.py b/Lib/test/test_capi/test_type.py index 92d056e802eeed..ffcaae73bca236 100644 --- a/Lib/test/test_capi/test_type.py +++ b/Lib/test/test_capi/test_type.py @@ -67,3 +67,10 @@ class FreezeThis(metaclass=Meta): Base.value = 3 type_freeze(FreezeThis) self.assertEqual(FreezeThis.value, 2) + + def test_manual_heap_type(self): + # gh-128923: test that a manually allocated and initailized heap type + # works correctly + ManualHeapType = _testcapi.ManualHeapType + for i in range(100): + self.assertIsInstance(ManualHeapType(), ManualHeapType) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 7d304add5999d1..b657bb665bd5c5 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -4149,6 +4149,61 @@ static PyTypeObject ContainerNoGC_type = { .tp_new = ContainerNoGC_new, }; +/* Manually allocated heap type */ + +typedef struct { + PyObject_HEAD + PyObject *dict; +} ManualHeapType; + +static int +ManualHeapType_traverse(PyObject *self, visitproc visit, void *arg) +{ + ManualHeapType *mht = (ManualHeapType *)self; + Py_VISIT(mht->dict); + return 0; +} + +static void +ManualHeapType_dealloc(PyObject *self) +{ + ManualHeapType *mht = (ManualHeapType *)self; + PyObject_GC_UnTrack(self); + Py_XDECREF(mht->dict); + PyTypeObject *type = Py_TYPE(self); + Py_TYPE(self)->tp_free(self); + Py_DECREF(type); +} + +static PyObject * +create_manual_heap_type(void) +{ + // gh-128923: Ensure that a heap type allocated through PyType_Type.tp_alloc + // with minimal initialization works correctly. + PyHeapTypeObject *heap_type = (PyHeapTypeObject *)PyType_Type.tp_alloc(&PyType_Type, 0); + if (heap_type == NULL) { + return NULL; + } + PyTypeObject* type = &heap_type->ht_type; + type->tp_basicsize = sizeof(ManualHeapType); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_HAVE_GC; + type->tp_new = PyType_GenericNew; + type->tp_name = "ManualHeapType"; + type->tp_dictoffset = offsetof(ManualHeapType, dict); + type->tp_traverse = ManualHeapType_traverse; + type->tp_dealloc = ManualHeapType_dealloc; + heap_type->ht_name = PyUnicode_FromString(type->tp_name); + if (!heap_type->ht_name) { + Py_DECREF(type); + return NULL; + } + heap_type->ht_qualname = Py_NewRef(heap_type->ht_name); + if (PyType_Ready(type) < 0) { + Py_DECREF(type); + return NULL; + } + return (PyObject *)type; +} static struct PyModuleDef _testcapimodule = { PyModuleDef_HEAD_INIT, @@ -4283,6 +4338,15 @@ PyInit__testcapi(void) (PyObject *) &ContainerNoGC_type) < 0) return NULL; + PyObject *manual_heap_type = create_manual_heap_type(); + if (manual_heap_type == NULL) { + return NULL; + } + if (PyModule_Add(m, "ManualHeapType", manual_heap_type) < 0) { + return NULL; + } + + /* Include tests from the _testcapi/ directory */ if (_PyTestCapi_Init_Vectorcall(m) < 0) { return NULL; diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 15b36a868a47df..539200c97c1206 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1911,7 +1911,7 @@ code_dealloc(PyObject *self) Py_XDECREF(co->co_linetable); Py_XDECREF(co->co_exceptiontable); #ifdef Py_GIL_DISABLED - assert(co->_co_unique_id == -1); + assert(co->_co_unique_id == _Py_INVALID_UNIQUE_ID); #endif if (co->_co_cached != NULL) { Py_XDECREF(co->_co_cached->_co_code); diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 82789d5e56f523..504e65b01ca959 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1659,6 +1659,9 @@ _PyDict_EnablePerThreadRefcounting(PyObject *op) assert(PyDict_Check(op)); #ifdef Py_GIL_DISABLED Py_ssize_t id = _PyObject_AssignUniqueId(op); + if (id == _Py_INVALID_UNIQUE_ID) { + return; + } if ((uint64_t)id >= (uint64_t)DICT_UNIQUE_ID_MAX) { _PyObject_ReleaseUniqueId(id); return; @@ -1666,8 +1669,7 @@ _PyDict_EnablePerThreadRefcounting(PyObject *op) PyDictObject *mp = (PyDictObject *)op; assert((mp->_ma_watcher_tag >> DICT_UNIQUE_ID_SHIFT) == 0); - // Plus 1 so that _ma_watcher_tag=0 represents an unassigned id - mp->_ma_watcher_tag += ((uint64_t)id + 1) << DICT_UNIQUE_ID_SHIFT; + mp->_ma_watcher_tag += (uint64_t)id << DICT_UNIQUE_ID_SHIFT; #endif } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index d8f5f6d9cb2366..93920341a179e8 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -6160,7 +6160,7 @@ type_dealloc(PyObject *self) Py_XDECREF(et->ht_module); PyMem_Free(et->_ht_tpname); #ifdef Py_GIL_DISABLED - assert(et->unique_id == -1); + assert(et->unique_id == _Py_INVALID_UNIQUE_ID); #endif et->ht_token = NULL; Py_TYPE(type)->tp_free((PyObject *)type); diff --git a/Python/uniqueid.c b/Python/uniqueid.c index b9f30713feeb57..64c3e6cfbbe825 100644 --- a/Python/uniqueid.c +++ b/Python/uniqueid.c @@ -86,7 +86,7 @@ _PyObject_AssignUniqueId(PyObject *obj) if (pool->freelist == NULL) { if (resize_interp_type_id_pool(pool) < 0) { UNLOCK_POOL(pool); - return -1; + return _Py_INVALID_UNIQUE_ID; } } @@ -94,7 +94,9 @@ _PyObject_AssignUniqueId(PyObject *obj) pool->freelist = entry->next; entry->obj = obj; _PyObject_SetDeferredRefcount(obj); - Py_ssize_t unique_id = (entry - pool->table); + // The unique id is one plus the index of the entry in the table. + Py_ssize_t unique_id = (entry - pool->table) + 1; + assert(unique_id > 0); UNLOCK_POOL(pool); return unique_id; } @@ -106,8 +108,9 @@ _PyObject_ReleaseUniqueId(Py_ssize_t unique_id) struct _Py_unique_id_pool *pool = &interp->unique_ids; LOCK_POOL(pool); - assert(unique_id >= 0 && unique_id < pool->size); - _Py_unique_id_entry *entry = &pool->table[unique_id]; + assert(unique_id > 0 && unique_id <= pool->size); + Py_ssize_t idx = unique_id - 1; + _Py_unique_id_entry *entry = &pool->table[idx]; entry->next = pool->freelist; pool->freelist = entry; UNLOCK_POOL(pool); @@ -116,18 +119,18 @@ _PyObject_ReleaseUniqueId(Py_ssize_t unique_id) static Py_ssize_t clear_unique_id(PyObject *obj) { - Py_ssize_t id = -1; + Py_ssize_t id = _Py_INVALID_UNIQUE_ID; if (PyType_Check(obj)) { if (PyType_HasFeature((PyTypeObject *)obj, Py_TPFLAGS_HEAPTYPE)) { PyHeapTypeObject *ht = (PyHeapTypeObject *)obj; id = ht->unique_id; - ht->unique_id = -1; + ht->unique_id = _Py_INVALID_UNIQUE_ID; } } else if (PyCode_Check(obj)) { PyCodeObject *co = (PyCodeObject *)obj; id = co->_co_unique_id; - co->_co_unique_id = -1; + co->_co_unique_id = _Py_INVALID_UNIQUE_ID; } else if (PyDict_Check(obj)) { PyDictObject *mp = (PyDictObject *)obj; @@ -141,23 +144,23 @@ void _PyObject_DisablePerThreadRefcounting(PyObject *obj) { Py_ssize_t id = clear_unique_id(obj); - if (id >= 0) { + if (id != _Py_INVALID_UNIQUE_ID) { _PyObject_ReleaseUniqueId(id); } } void -_PyObject_ThreadIncrefSlow(PyObject *obj, Py_ssize_t unique_id) +_PyObject_ThreadIncrefSlow(PyObject *obj, size_t idx) { _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET(); - if (unique_id < 0 || resize_local_refcounts(tstate) < 0) { + if (((Py_ssize_t)idx) < 0 || resize_local_refcounts(tstate) < 0) { // just incref the object directly. Py_INCREF(obj); return; } - assert(unique_id < tstate->refcounts.size); - tstate->refcounts.values[unique_id]++; + assert(idx < (size_t)tstate->refcounts.size); + tstate->refcounts.values[idx]++; #ifdef Py_REF_DEBUG _Py_IncRefTotal((PyThreadState *)tstate); #endif @@ -217,7 +220,7 @@ _PyObject_FinalizeUniqueIdPool(PyInterpreterState *interp) if (obj != NULL) { Py_ssize_t id = clear_unique_id(obj); (void)id; - assert(id == i); + assert(id == i + 1); } } PyMem_Free(pool->table); From b5558cd63c62855ed43d66a55907f9d4398134e3 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 17 Jan 2025 16:59:30 +0000 Subject: [PATCH 344/376] Refactor code generators a bit (GH-128920) Refactor code generators a bit to avoid passing stack property around all over the place --- Tools/cases_generator/optimizer_generator.py | 8 ++++---- Tools/cases_generator/stack.py | 21 ++++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Tools/cases_generator/optimizer_generator.py b/Tools/cases_generator/optimizer_generator.py index d08b621aed552b..2928440fecca0c 100644 --- a/Tools/cases_generator/optimizer_generator.py +++ b/Tools/cases_generator/optimizer_generator.py @@ -126,7 +126,7 @@ def write_uop( try: out.start_line() if override: - code_list, storage = Storage.for_uop(stack, prototype, extract_bits=False) + code_list, storage = Storage.for_uop(stack, prototype) for code in code_list: out.emit(code) if debug: @@ -151,11 +151,11 @@ def write_uop( var.defined = False storage = emitter.emit_tokens(override, storage, None) out.start_line() - storage.flush(out, cast_type="_Py_UopsSymbol *", extract_bits=False) + storage.flush(out, cast_type="_Py_UopsSymbol *") else: emit_default(out, uop, stack) out.start_line() - stack.flush(out, cast_type="_Py_UopsSymbol *", extract_bits=False) + stack.flush(out, cast_type="_Py_UopsSymbol *") except StackError as ex: raise analysis_error(ex.args[0], prototype.body[0]) # from None @@ -198,7 +198,7 @@ def generate_abstract_interpreter( declare_variables(override, out, skip_inputs=False) else: declare_variables(uop, out, skip_inputs=True) - stack = Stack() + stack = Stack(False) write_uop(override, uop, out, stack, debug, skip_inputs=(override is None)) out.start_line() out.emit("break;\n") diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 286f47d0cfb11b..5121837ed8334b 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -224,13 +224,14 @@ def array_or_scalar(var: StackItem | Local) -> str: return "array" if var.is_array() else "scalar" class Stack: - def __init__(self) -> None: + def __init__(self, extract_bits: bool=True) -> None: self.top_offset = StackOffset.empty() self.base_offset = StackOffset.empty() self.variables: list[Local] = [] self.defined: set[str] = set() + self.extract_bits = extract_bits - def pop(self, var: StackItem, extract_bits: bool = True) -> tuple[str, Local]: + def pop(self, var: StackItem) -> tuple[str, Local]: self.top_offset.pop(var) indirect = "&" if var.is_array() else "" if self.variables: @@ -272,7 +273,7 @@ def pop(self, var: StackItem, extract_bits: bool = True) -> tuple[str, Local]: return "", Local.unused(var) self.defined.add(var.name) cast = f"({var.type})" if (not indirect and var.type) else "" - bits = ".bits" if cast and extract_bits else "" + bits = ".bits" if cast and self.extract_bits else "" assign = f"{var.name} = {cast}{indirect}stack_pointer[{self.base_offset.to_c()}]{bits};" if var.condition: if var.condition == "1": @@ -315,7 +316,7 @@ def _adjust_stack_pointer(self, out: CWriter, number: str) -> None: out.emit("assert(WITHIN_STACK_BOUNDS());\n") def flush( - self, out: CWriter, cast_type: str = "uintptr_t", extract_bits: bool = True + self, out: CWriter, cast_type: str = "uintptr_t" ) -> None: out.start_line() var_offset = self.base_offset.copy() @@ -324,7 +325,7 @@ def flush( var.defined and not var.in_memory ): - Stack._do_emit(out, var.item, var_offset, cast_type, extract_bits) + Stack._do_emit(out, var.item, var_offset, cast_type, self.extract_bits) var.in_memory = True var_offset.push(var.item) number = self.top_offset.to_c() @@ -346,7 +347,7 @@ def as_comment(self) -> str: ) def copy(self) -> "Stack": - other = Stack() + other = Stack(self.extract_bits) other.top_offset = self.top_offset.copy() other.base_offset = self.base_offset.copy() other.variables = [var.copy() for var in self.variables] @@ -507,10 +508,10 @@ def locals_cached(self) -> bool: return True return False - def flush(self, out: CWriter, cast_type: str = "uintptr_t", extract_bits: bool = True) -> None: + def flush(self, out: CWriter, cast_type: str = "uintptr_t") -> None: self.clear_dead_inputs() self._push_defined_outputs() - self.stack.flush(out, cast_type, extract_bits) + self.stack.flush(out, cast_type) def save(self, out: CWriter) -> None: assert self.spilled >= 0 @@ -530,12 +531,12 @@ def reload(self, out: CWriter) -> None: out.emit("stack_pointer = _PyFrame_GetStackPointer(frame);\n") @staticmethod - def for_uop(stack: Stack, uop: Uop, extract_bits: bool = True) -> tuple[list[str], "Storage"]: + def for_uop(stack: Stack, uop: Uop) -> tuple[list[str], "Storage"]: code_list: list[str] = [] inputs: list[Local] = [] peeks: list[Local] = [] for input in reversed(uop.stack.inputs): - code, local = stack.pop(input, extract_bits) + code, local = stack.pop(input) code_list.append(code) if input.peek: peeks.append(local) From d95ba9fa1110534b7247fa2ff12b90e930c93256 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 17 Jan 2025 18:52:18 +0100 Subject: [PATCH 345/376] gh-128911: Add tests on the PyImport C API (#128915) * Add Modules/_testlimitedcapi/import.c * Add Lib/test/test_capi/test_import.py * Remove _testcapi.check_pyimport_addmodule(): tests already covered by newly added tests. Co-authored-by: Serhiy Storchaka --- Lib/test/test_capi/test_import.py | 327 +++++++++++++++++++++++ Lib/test/test_import/__init__.py | 24 -- Modules/Setup.stdlib.in | 2 +- Modules/_testcapimodule.c | 47 ---- Modules/_testlimitedcapi.c | 3 + Modules/_testlimitedcapi/import.c | 306 +++++++++++++++++++++ Modules/_testlimitedcapi/parts.h | 1 + PCbuild/_testlimitedcapi.vcxproj | 1 + PCbuild/_testlimitedcapi.vcxproj.filters | 1 + 9 files changed, 640 insertions(+), 72 deletions(-) create mode 100644 Lib/test/test_capi/test_import.py create mode 100644 Modules/_testlimitedcapi/import.c diff --git a/Lib/test/test_capi/test_import.py b/Lib/test/test_capi/test_import.py new file mode 100644 index 00000000000000..94f96728d9174b --- /dev/null +++ b/Lib/test/test_capi/test_import.py @@ -0,0 +1,327 @@ +import importlib.util +import os.path +import sys +import types +import unittest +from test.support import os_helper +from test.support import import_helper +from test.support.warnings_helper import check_warnings + +_testlimitedcapi = import_helper.import_module('_testlimitedcapi') +NULL = None + + +class ImportTests(unittest.TestCase): + def test_getmagicnumber(self): + # Test PyImport_GetMagicNumber() + magic = _testlimitedcapi.PyImport_GetMagicNumber() + self.assertEqual(magic, + int.from_bytes(importlib.util.MAGIC_NUMBER, 'little')) + + def test_getmagictag(self): + # Test PyImport_GetMagicTag() + tag = _testlimitedcapi.PyImport_GetMagicTag() + self.assertEqual(tag, sys.implementation.cache_tag) + + def test_getmoduledict(self): + # Test PyImport_GetModuleDict() + modules = _testlimitedcapi.PyImport_GetModuleDict() + self.assertIs(modules, sys.modules) + + def check_import_loaded_module(self, import_module): + for name in ('os', 'sys', 'test', 'unittest'): + with self.subTest(name=name): + self.assertIn(name, sys.modules) + old_module = sys.modules[name] + module = import_module(name) + self.assertIsInstance(module, types.ModuleType) + self.assertIs(module, old_module) + + def check_import_fresh_module(self, import_module): + old_modules = dict(sys.modules) + try: + for name in ('colorsys', 'math'): + with self.subTest(name=name): + sys.modules.pop(name, None) + module = import_module(name) + self.assertIsInstance(module, types.ModuleType) + self.assertIs(module, sys.modules[name]) + self.assertEqual(module.__name__, name) + finally: + sys.modules.clear() + sys.modules.update(old_modules) + + def test_getmodule(self): + # Test PyImport_GetModule() + getmodule = _testlimitedcapi.PyImport_GetModule + self.check_import_loaded_module(getmodule) + + nonexistent = 'nonexistent' + self.assertNotIn(nonexistent, sys.modules) + self.assertIs(getmodule(nonexistent), KeyError) + self.assertIs(getmodule(''), KeyError) + self.assertIs(getmodule(object()), KeyError) + + self.assertRaises(TypeError, getmodule, []) # unhashable + # CRASHES getmodule(NULL) + + def check_addmodule(self, add_module, accept_nonstr=False): + # create a new module + names = ['nonexistent'] + if accept_nonstr: + names.append(b'\xff') # non-UTF-8 + # PyImport_AddModuleObject() accepts non-string names + names.append(tuple(['hashable non-string'])) + for name in names: + with self.subTest(name=name): + self.assertNotIn(name, sys.modules) + try: + module = add_module(name) + self.assertIsInstance(module, types.ModuleType) + self.assertEqual(module.__name__, name) + self.assertIs(module, sys.modules[name]) + finally: + sys.modules.pop(name, None) + + # get an existing module + self.check_import_loaded_module(add_module) + + def test_addmoduleobject(self): + # Test PyImport_AddModuleObject() + addmoduleobject = _testlimitedcapi.PyImport_AddModuleObject + self.check_addmodule(addmoduleobject, accept_nonstr=True) + + self.assertRaises(TypeError, addmoduleobject, []) # unhashable + # CRASHES addmoduleobject(NULL) + + def test_addmodule(self): + # Test PyImport_AddModule() + addmodule = _testlimitedcapi.PyImport_AddModule + self.check_addmodule(addmodule) + + self.assertRaises(UnicodeDecodeError, addmodule, b'\xff') + # CRASHES addmodule(NULL) + + def test_addmoduleref(self): + # Test PyImport_AddModuleRef() + addmoduleref = _testlimitedcapi.PyImport_AddModuleRef + self.check_addmodule(addmoduleref) + + self.assertRaises(UnicodeDecodeError, addmoduleref, b'\xff') + # CRASHES addmoduleref(NULL) + + def check_import_func(self, import_module): + self.check_import_loaded_module(import_module) + self.check_import_fresh_module(import_module) + self.assertRaises(ModuleNotFoundError, import_module, 'nonexistent') + self.assertRaises(ValueError, import_module, '') + + def test_import(self): + # Test PyImport_Import() + import_ = _testlimitedcapi.PyImport_Import + self.check_import_func(import_) + + self.assertRaises(TypeError, import_, b'os') + self.assertRaises(SystemError, import_, NULL) + + def test_importmodule(self): + # Test PyImport_ImportModule() + importmodule = _testlimitedcapi.PyImport_ImportModule + self.check_import_func(importmodule) + + self.assertRaises(UnicodeDecodeError, importmodule, b'\xff') + # CRASHES importmodule(NULL) + + def test_importmodulenoblock(self): + # Test deprecated PyImport_ImportModuleNoBlock() + importmodulenoblock = _testlimitedcapi.PyImport_ImportModuleNoBlock + with check_warnings(('', DeprecationWarning)): + self.check_import_func(importmodulenoblock) + self.assertRaises(UnicodeDecodeError, importmodulenoblock, b'\xff') + + # CRASHES importmodulenoblock(NULL) + + def check_frozen_import(self, import_frozen_module): + # Importing a frozen module executes its code, so start by unloading + # the module to execute the code in a new (temporary) module. + old_zipimport = sys.modules.pop('zipimport') + try: + self.assertEqual(import_frozen_module('zipimport'), 1) + + # import zipimport again + self.assertEqual(import_frozen_module('zipimport'), 1) + finally: + sys.modules['zipimport'] = old_zipimport + + # not a frozen module + self.assertEqual(import_frozen_module('sys'), 0) + self.assertEqual(import_frozen_module('nonexistent'), 0) + self.assertEqual(import_frozen_module(''), 0) + + def test_importfrozenmodule(self): + # Test PyImport_ImportFrozenModule() + importfrozenmodule = _testlimitedcapi.PyImport_ImportFrozenModule + self.check_frozen_import(importfrozenmodule) + + self.assertRaises(UnicodeDecodeError, importfrozenmodule, b'\xff') + # CRASHES importfrozenmodule(NULL) + + def test_importfrozenmoduleobject(self): + # Test PyImport_ImportFrozenModuleObject() + importfrozenmoduleobject = _testlimitedcapi.PyImport_ImportFrozenModuleObject + self.check_frozen_import(importfrozenmoduleobject) + self.assertEqual(importfrozenmoduleobject(b'zipimport'), 0) + self.assertEqual(importfrozenmoduleobject(NULL), 0) + + def test_importmoduleex(self): + # Test PyImport_ImportModuleEx() + importmoduleex = _testlimitedcapi.PyImport_ImportModuleEx + self.check_import_func(lambda name: importmoduleex(name, NULL, NULL, NULL)) + + self.assertRaises(ModuleNotFoundError, importmoduleex, 'nonexistent', NULL, NULL, NULL) + self.assertRaises(ValueError, importmoduleex, '', NULL, NULL, NULL) + self.assertRaises(UnicodeDecodeError, importmoduleex, b'\xff', NULL, NULL, NULL) + # CRASHES importmoduleex(NULL, NULL, NULL, NULL) + + def check_importmodulelevel(self, importmodulelevel): + self.check_import_func(lambda name: importmodulelevel(name, NULL, NULL, NULL, 0)) + + self.assertRaises(ModuleNotFoundError, importmodulelevel, 'nonexistent', NULL, NULL, NULL, 0) + self.assertRaises(ValueError, importmodulelevel, '', NULL, NULL, NULL, 0) + + if __package__: + self.assertIs(importmodulelevel('test_import', globals(), NULL, NULL, 1), + sys.modules['test.test_capi.test_import']) + self.assertIs(importmodulelevel('test_capi', globals(), NULL, NULL, 2), + sys.modules['test.test_capi']) + self.assertRaises(ValueError, importmodulelevel, 'os', NULL, NULL, NULL, -1) + with self.assertWarns(ImportWarning): + self.assertRaises(KeyError, importmodulelevel, 'test_import', {}, NULL, NULL, 1) + self.assertRaises(TypeError, importmodulelevel, 'test_import', [], NULL, NULL, 1) + + def test_importmodulelevel(self): + # Test PyImport_ImportModuleLevel() + importmodulelevel = _testlimitedcapi.PyImport_ImportModuleLevel + self.check_importmodulelevel(importmodulelevel) + + self.assertRaises(UnicodeDecodeError, importmodulelevel, b'\xff', NULL, NULL, NULL, 0) + # CRASHES importmodulelevel(NULL, NULL, NULL, NULL, 0) + + def test_importmodulelevelobject(self): + # Test PyImport_ImportModuleLevelObject() + importmodulelevel = _testlimitedcapi.PyImport_ImportModuleLevelObject + self.check_importmodulelevel(importmodulelevel) + + self.assertRaises(TypeError, importmodulelevel, b'os', NULL, NULL, NULL, 0) + self.assertRaises(ValueError, importmodulelevel, NULL, NULL, NULL, NULL, 0) + + def check_executecodemodule(self, execute_code, *args): + name = 'test_import_executecode' + try: + # Create a temporary module where the code will be executed + self.assertNotIn(name, sys.modules) + module = _testlimitedcapi.PyImport_AddModuleRef(name) + self.assertNotHasAttr(module, 'attr') + + # Execute the code + code = compile('attr = 1', '', 'exec') + module2 = execute_code(name, code, *args) + self.assertIs(module2, module) + + # Check the function side effects + self.assertEqual(module.attr, 1) + finally: + sys.modules.pop(name, None) + return module.__spec__.origin + + def test_executecodemodule(self): + # Test PyImport_ExecCodeModule() + execcodemodule = _testlimitedcapi.PyImport_ExecCodeModule + self.check_executecodemodule(execcodemodule) + + code = compile('attr = 1', '', 'exec') + self.assertRaises(UnicodeDecodeError, execcodemodule, b'\xff', code) + # CRASHES execcodemodule(NULL, code) + # CRASHES execcodemodule(name, NULL) + + def test_executecodemoduleex(self): + # Test PyImport_ExecCodeModuleEx() + execcodemoduleex = _testlimitedcapi.PyImport_ExecCodeModuleEx + + # Test NULL path (it should not crash) + self.check_executecodemodule(execcodemoduleex, NULL) + + # Test non-NULL path + pathname = b'pathname' + origin = self.check_executecodemodule(execcodemoduleex, pathname) + self.assertEqual(origin, os.path.abspath(os.fsdecode(pathname))) + + pathname = os_helper.TESTFN_UNDECODABLE + if pathname: + origin = self.check_executecodemodule(execcodemoduleex, pathname) + self.assertEqual(origin, os.path.abspath(os.fsdecode(pathname))) + + code = compile('attr = 1', '', 'exec') + self.assertRaises(UnicodeDecodeError, execcodemoduleex, b'\xff', code, NULL) + # CRASHES execcodemoduleex(NULL, code, NULL) + # CRASHES execcodemoduleex(name, NULL, NULL) + + def check_executecode_pathnames(self, execute_code_func, object=False): + # Test non-NULL pathname and NULL cpathname + + # Test NULL paths (it should not crash) + self.check_executecodemodule(execute_code_func, NULL, NULL) + + pathname = 'pathname' + origin = self.check_executecodemodule(execute_code_func, pathname, NULL) + self.assertEqual(origin, os.path.abspath(os.fsdecode(pathname))) + origin = self.check_executecodemodule(execute_code_func, NULL, pathname) + if not object: + self.assertEqual(origin, os.path.abspath(os.fsdecode(pathname))) + + pathname = os_helper.TESTFN_UNDECODABLE + if pathname: + if object: + pathname = os.fsdecode(pathname) + origin = self.check_executecodemodule(execute_code_func, pathname, NULL) + self.assertEqual(origin, os.path.abspath(os.fsdecode(pathname))) + self.check_executecodemodule(execute_code_func, NULL, pathname) + + # Test NULL pathname and non-NULL cpathname + pyc_filename = importlib.util.cache_from_source(__file__) + py_filename = importlib.util.source_from_cache(pyc_filename) + origin = self.check_executecodemodule(execute_code_func, NULL, pyc_filename) + if not object: + self.assertEqual(origin, py_filename) + + def test_executecodemodulewithpathnames(self): + # Test PyImport_ExecCodeModuleWithPathnames() + execute_code_func = _testlimitedcapi.PyImport_ExecCodeModuleWithPathnames + self.check_executecode_pathnames(execute_code_func) + + code = compile('attr = 1', '', 'exec') + self.assertRaises(UnicodeDecodeError, execute_code_func, b'\xff', code, NULL, NULL) + # CRASHES execute_code_func(NULL, code, NULL, NULL) + # CRASHES execute_code_func(name, NULL, NULL, NULL) + + def test_executecodemoduleobject(self): + # Test PyImport_ExecCodeModuleObject() + execute_code_func = _testlimitedcapi.PyImport_ExecCodeModuleObject + self.check_executecode_pathnames(execute_code_func, object=True) + + code = compile('attr = 1', '', 'exec') + self.assertRaises(TypeError, execute_code_func, [], code, NULL, NULL) + nonstring = tuple(['hashable non-string']) + self.assertRaises(AttributeError, execute_code_func, nonstring, code, NULL, NULL) + sys.modules.pop(nonstring, None) + # CRASHES execute_code_func(NULL, code, NULL, NULL) + # CRASHES execute_code_func(name, NULL, NULL, NULL) + + # TODO: test PyImport_GetImporter() + # TODO: test PyImport_ReloadModule() + # TODO: test PyImport_ExtendInittab() + # PyImport_AppendInittab() is tested by test_embed + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index c2cec6444cb43a..1e706023c795b6 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -3311,30 +3311,6 @@ def test_basic_multiple_interpreters_reset_each(self): # * module's global state was initialized, not reset -@cpython_only -class CAPITests(unittest.TestCase): - def test_pyimport_addmodule(self): - # gh-105922: Test PyImport_AddModuleRef(), PyImport_AddModule() - # and PyImport_AddModuleObject() - _testcapi = import_module("_testcapi") - for name in ( - 'sys', # frozen module - 'test', # package - __name__, # package.module - ): - _testcapi.check_pyimport_addmodule(name) - - def test_pyimport_addmodule_create(self): - # gh-105922: Test PyImport_AddModuleRef(), create a new module - _testcapi = import_module("_testcapi") - name = 'dontexist' - self.assertNotIn(name, sys.modules) - self.addCleanup(unload, name) - - mod = _testcapi.check_pyimport_addmodule(name) - self.assertIs(mod, sys.modules[name]) - - @cpython_only class TestMagicNumber(unittest.TestCase): def test_magic_number_endianness(self): diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index b7357f41768a2f..6b6a8ae57a5119 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -163,7 +163,7 @@ @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c @MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c -@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c +@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index b657bb665bd5c5..996b96bc000450 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3059,52 +3059,6 @@ function_set_closure(PyObject *self, PyObject *args) Py_RETURN_NONE; } -static PyObject * -check_pyimport_addmodule(PyObject *self, PyObject *args) -{ - const char *name; - if (!PyArg_ParseTuple(args, "s", &name)) { - return NULL; - } - - // test PyImport_AddModuleRef() - PyObject *module = PyImport_AddModuleRef(name); - if (module == NULL) { - return NULL; - } - assert(PyModule_Check(module)); - // module is a strong reference - - // test PyImport_AddModule() - PyObject *module2 = PyImport_AddModule(name); - if (module2 == NULL) { - goto error; - } - assert(PyModule_Check(module2)); - assert(module2 == module); - // module2 is a borrowed ref - - // test PyImport_AddModuleObject() - PyObject *name_obj = PyUnicode_FromString(name); - if (name_obj == NULL) { - goto error; - } - PyObject *module3 = PyImport_AddModuleObject(name_obj); - Py_DECREF(name_obj); - if (module3 == NULL) { - goto error; - } - assert(PyModule_Check(module3)); - assert(module3 == module); - // module3 is a borrowed ref - - return module; - -error: - Py_DECREF(module); - return NULL; -} - static PyObject * test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) @@ -3668,7 +3622,6 @@ static PyMethodDef TestMethods[] = { {"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL}, {"function_get_closure", function_get_closure, METH_O, NULL}, {"function_set_closure", function_set_closure, METH_VARARGS, NULL}, - {"check_pyimport_addmodule", check_pyimport_addmodule, METH_VARARGS}, {"test_weakref_capi", test_weakref_capi, METH_NOARGS}, {"function_set_warning", function_set_warning, METH_NOARGS}, {"test_critical_sections", test_critical_sections, METH_NOARGS}, diff --git a/Modules/_testlimitedcapi.c b/Modules/_testlimitedcapi.c index bcc69a339ec5c4..82dac1c999470f 100644 --- a/Modules/_testlimitedcapi.c +++ b/Modules/_testlimitedcapi.c @@ -56,6 +56,9 @@ PyInit__testlimitedcapi(void) if (_PyTestLimitedCAPI_Init_HeaptypeRelative(mod) < 0) { return NULL; } + if (_PyTestLimitedCAPI_Init_Import(mod) < 0) { + return NULL; + } if (_PyTestLimitedCAPI_Init_List(mod) < 0) { return NULL; } diff --git a/Modules/_testlimitedcapi/import.c b/Modules/_testlimitedcapi/import.c new file mode 100644 index 00000000000000..3707dbedeea0d9 --- /dev/null +++ b/Modules/_testlimitedcapi/import.c @@ -0,0 +1,306 @@ +// Need limited C API version 3.13 for PyImport_AddModuleRef() +#include "pyconfig.h" // Py_GIL_DISABLED +#if !defined(Py_GIL_DISABLED) && !defined(Py_LIMITED_API) +# define Py_LIMITED_API 0x030d0000 +#endif + +#include "parts.h" +#include "util.h" + + +/* Test PyImport_GetMagicNumber() */ +static PyObject * +pyimport_getmagicnumber(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) +{ + long magic = PyImport_GetMagicNumber(); + return PyLong_FromLong(magic); +} + + +/* Test PyImport_GetMagicTag() */ +static PyObject * +pyimport_getmagictag(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) +{ + const char *tag = PyImport_GetMagicTag(); + return PyUnicode_FromString(tag); +} + + +/* Test PyImport_GetModuleDict() */ +static PyObject * +pyimport_getmoduledict(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) +{ + return Py_XNewRef(PyImport_GetModuleDict()); +} + + +/* Test PyImport_GetModule() */ +static PyObject * +pyimport_getmodule(PyObject *Py_UNUSED(module), PyObject *name) +{ + assert(!PyErr_Occurred()); + NULLABLE(name); + PyObject *module = PyImport_GetModule(name); + if (module == NULL && !PyErr_Occurred()) { + return Py_NewRef(PyExc_KeyError); + } + return module; +} + + +/* Test PyImport_AddModuleObject() */ +static PyObject * +pyimport_addmoduleobject(PyObject *Py_UNUSED(module), PyObject *name) +{ + NULLABLE(name); + return Py_XNewRef(PyImport_AddModuleObject(name)); +} + + +/* Test PyImport_AddModule() */ +static PyObject * +pyimport_addmodule(PyObject *Py_UNUSED(module), PyObject *args) +{ + const char *name; + Py_ssize_t size; + if (!PyArg_ParseTuple(args, "z#", &name, &size)) { + return NULL; + } + + return Py_XNewRef(PyImport_AddModule(name)); +} + + +/* Test PyImport_AddModuleRef() */ +static PyObject * +pyimport_addmoduleref(PyObject *Py_UNUSED(module), PyObject *args) +{ + const char *name; + Py_ssize_t size; + if (!PyArg_ParseTuple(args, "z#", &name, &size)) { + return NULL; + } + + return PyImport_AddModuleRef(name); +} + + +/* Test PyImport_Import() */ +static PyObject * +pyimport_import(PyObject *Py_UNUSED(module), PyObject *name) +{ + NULLABLE(name); + return PyImport_Import(name); +} + + +/* Test PyImport_ImportModule() */ +static PyObject * +pyimport_importmodule(PyObject *Py_UNUSED(module), PyObject *args) +{ + const char *name; + Py_ssize_t size; + if (!PyArg_ParseTuple(args, "z#", &name, &size)) { + return NULL; + } + + return PyImport_ImportModule(name); +} + + +/* Test PyImport_ImportModuleNoBlock() */ +static PyObject * +pyimport_importmodulenoblock(PyObject *Py_UNUSED(module), PyObject *args) +{ + const char *name; + Py_ssize_t size; + if (!PyArg_ParseTuple(args, "z#", &name, &size)) { + return NULL; + } + + _Py_COMP_DIAG_PUSH + _Py_COMP_DIAG_IGNORE_DEPR_DECLS + return PyImport_ImportModuleNoBlock(name); + _Py_COMP_DIAG_POP +} + + +/* Test PyImport_ImportModuleEx() */ +static PyObject * +pyimport_importmoduleex(PyObject *Py_UNUSED(module), PyObject *args) +{ + const char *name; + Py_ssize_t size; + PyObject *globals, *locals, *fromlist; + if (!PyArg_ParseTuple(args, "z#OOO", + &name, &size, &globals, &locals, &fromlist)) { + return NULL; + } + NULLABLE(globals); + NULLABLE(locals); + NULLABLE(fromlist); + + return PyImport_ImportModuleEx(name, globals, locals, fromlist); +} + + +/* Test PyImport_ImportModuleLevel() */ +static PyObject * +pyimport_importmodulelevel(PyObject *Py_UNUSED(module), PyObject *args) +{ + const char *name; + Py_ssize_t size; + PyObject *globals, *locals, *fromlist; + int level; + if (!PyArg_ParseTuple(args, "z#OOOi", + &name, &size, &globals, &locals, &fromlist, &level)) { + return NULL; + } + NULLABLE(globals); + NULLABLE(locals); + NULLABLE(fromlist); + + return PyImport_ImportModuleLevel(name, globals, locals, fromlist, level); +} + + +/* Test PyImport_ImportModuleLevelObject() */ +static PyObject * +pyimport_importmodulelevelobject(PyObject *Py_UNUSED(module), PyObject *args) +{ + PyObject *name, *globals, *locals, *fromlist; + int level; + if (!PyArg_ParseTuple(args, "OOOOi", + &name, &globals, &locals, &fromlist, &level)) { + return NULL; + } + NULLABLE(name); + NULLABLE(globals); + NULLABLE(locals); + NULLABLE(fromlist); + + return PyImport_ImportModuleLevelObject(name, globals, locals, fromlist, level); +} + + +/* Test PyImport_ImportFrozenModule() */ +static PyObject * +pyimport_importfrozenmodule(PyObject *Py_UNUSED(module), PyObject *args) +{ + const char *name; + Py_ssize_t size; + if (!PyArg_ParseTuple(args, "z#", &name, &size)) { + return NULL; + } + + RETURN_INT(PyImport_ImportFrozenModule(name)); +} + + +/* Test PyImport_ImportFrozenModuleObject() */ +static PyObject * +pyimport_importfrozenmoduleobject(PyObject *Py_UNUSED(module), PyObject *name) +{ + NULLABLE(name); + RETURN_INT(PyImport_ImportFrozenModuleObject(name)); +} + + +/* Test PyImport_ExecCodeModule() */ +static PyObject * +pyimport_executecodemodule(PyObject *Py_UNUSED(module), PyObject *args) +{ + const char *name; + Py_ssize_t size; + PyObject *code; + if (!PyArg_ParseTuple(args, "z#O", &name, &size, &code)) { + return NULL; + } + NULLABLE(code); + + return PyImport_ExecCodeModule(name, code); +} + + +/* Test PyImport_ExecCodeModuleEx() */ +static PyObject * +pyimport_executecodemoduleex(PyObject *Py_UNUSED(module), PyObject *args) +{ + const char *name; + Py_ssize_t size; + PyObject *code; + const char *pathname; + if (!PyArg_ParseTuple(args, "z#Oz#", &name, &size, &code, &pathname, &size)) { + return NULL; + } + NULLABLE(code); + + return PyImport_ExecCodeModuleEx(name, code, pathname); +} + + +/* Test PyImport_ExecCodeModuleWithPathnames() */ +static PyObject * +pyimport_executecodemodulewithpathnames(PyObject *Py_UNUSED(module), PyObject *args) +{ + const char *name; + Py_ssize_t size; + PyObject *code; + const char *pathname; + const char *cpathname; + if (!PyArg_ParseTuple(args, "z#Oz#z#", &name, &size, &code, &pathname, &size, &cpathname, &size)) { + return NULL; + } + NULLABLE(code); + + return PyImport_ExecCodeModuleWithPathnames(name, code, + pathname, cpathname); +} + + +/* Test PyImport_ExecCodeModuleObject() */ +static PyObject * +pyimport_executecodemoduleobject(PyObject *Py_UNUSED(module), PyObject *args) +{ + PyObject *name, *code, *pathname, *cpathname; + if (!PyArg_ParseTuple(args, "OOOO", &name, &code, &pathname, &cpathname)) { + return NULL; + } + NULLABLE(name); + NULLABLE(code); + NULLABLE(pathname); + NULLABLE(cpathname); + + return PyImport_ExecCodeModuleObject(name, code, pathname, cpathname); +} + + +static PyMethodDef test_methods[] = { + {"PyImport_GetMagicNumber", pyimport_getmagicnumber, METH_NOARGS}, + {"PyImport_GetMagicTag", pyimport_getmagictag, METH_NOARGS}, + {"PyImport_GetModuleDict", pyimport_getmoduledict, METH_NOARGS}, + {"PyImport_GetModule", pyimport_getmodule, METH_O}, + {"PyImport_AddModuleObject", pyimport_addmoduleobject, METH_O}, + {"PyImport_AddModule", pyimport_addmodule, METH_VARARGS}, + {"PyImport_AddModuleRef", pyimport_addmoduleref, METH_VARARGS}, + {"PyImport_Import", pyimport_import, METH_O}, + {"PyImport_ImportModule", pyimport_importmodule, METH_VARARGS}, + {"PyImport_ImportModuleNoBlock", pyimport_importmodulenoblock, METH_VARARGS}, + {"PyImport_ImportModuleEx", pyimport_importmoduleex, METH_VARARGS}, + {"PyImport_ImportModuleLevel", pyimport_importmodulelevel, METH_VARARGS}, + {"PyImport_ImportModuleLevelObject", pyimport_importmodulelevelobject, METH_VARARGS}, + {"PyImport_ImportFrozenModule", pyimport_importfrozenmodule, METH_VARARGS}, + {"PyImport_ImportFrozenModuleObject", pyimport_importfrozenmoduleobject, METH_O}, + {"PyImport_ExecCodeModule", pyimport_executecodemodule, METH_VARARGS}, + {"PyImport_ExecCodeModuleEx", pyimport_executecodemoduleex, METH_VARARGS}, + {"PyImport_ExecCodeModuleWithPathnames", pyimport_executecodemodulewithpathnames, METH_VARARGS}, + {"PyImport_ExecCodeModuleObject", pyimport_executecodemoduleobject, METH_VARARGS}, + {NULL}, +}; + + +int +_PyTestLimitedCAPI_Init_Import(PyObject *module) +{ + return PyModule_AddFunctions(module, test_methods); +} diff --git a/Modules/_testlimitedcapi/parts.h b/Modules/_testlimitedcapi/parts.h index 56d566b66565a3..9efcd8dcb71e5b 100644 --- a/Modules/_testlimitedcapi/parts.h +++ b/Modules/_testlimitedcapi/parts.h @@ -31,6 +31,7 @@ int _PyTestLimitedCAPI_Init_Dict(PyObject *module); int _PyTestLimitedCAPI_Init_Eval(PyObject *module); int _PyTestLimitedCAPI_Init_Float(PyObject *module); int _PyTestLimitedCAPI_Init_HeaptypeRelative(PyObject *module); +int _PyTestLimitedCAPI_Init_Import(PyObject *module); int _PyTestLimitedCAPI_Init_Object(PyObject *module); int _PyTestLimitedCAPI_Init_List(PyObject *module); int _PyTestLimitedCAPI_Init_Long(PyObject *module); diff --git a/PCbuild/_testlimitedcapi.vcxproj b/PCbuild/_testlimitedcapi.vcxproj index 0ea5edba3aa9a7..87abff52493098 100644 --- a/PCbuild/_testlimitedcapi.vcxproj +++ b/PCbuild/_testlimitedcapi.vcxproj @@ -103,6 +103,7 @@ + diff --git a/PCbuild/_testlimitedcapi.vcxproj.filters b/PCbuild/_testlimitedcapi.vcxproj.filters index b379090eb599f5..a975a508506905 100644 --- a/PCbuild/_testlimitedcapi.vcxproj.filters +++ b/PCbuild/_testlimitedcapi.vcxproj.filters @@ -18,6 +18,7 @@ + From 13c4def692228f09df0b30c5f93bc515e89fc77f Mon Sep 17 00:00:00 2001 From: mpage Date: Fri, 17 Jan 2025 12:53:29 -0800 Subject: [PATCH 346/376] gh-128955: Fix goto if tlbc creation fails when throwing into a generator (#128957) We don't have the correct copy of the bytecode and can't update next_instr appropriately, so just unwind. --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/ceval.c b/Python/ceval.c index e0362c3c89fe6a..28b0b4c6de39a7 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -846,7 +846,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _Py_CODEUNIT *bytecode = _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame)); if (bytecode == NULL) { - goto error; + goto exit_unwind; } ptrdiff_t off = frame->instr_ptr - _PyFrame_GetBytecode(frame); frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; From d4544cb232dee5f836a64b9126fbbefcbb6099de Mon Sep 17 00:00:00 2001 From: "T. Wouters" Date: Fri, 17 Jan 2025 23:43:17 +0100 Subject: [PATCH 347/376] gh-128923: fix test_pydoc for object subclasses without `__module__` (#128951) Fix pydoc's docclass() for classes inheriting from object without the `__module__` attribute, like `_testcapi.HeapType`. --- Lib/pydoc.py | 3 ++- Lib/test/test_pydoc/test_pydoc.py | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 9e84292aaf825f..922946e5fa7ddb 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -1435,7 +1435,8 @@ def makename(c, m=object.__module__): # List the built-in subclasses, if any: subclasses = sorted( (str(cls.__name__) for cls in type.__subclasses__(object) - if not cls.__name__.startswith("_") and cls.__module__ == "builtins"), + if (not cls.__name__.startswith("_") and + getattr(cls, '__module__', '') == "builtins")), key=str.lower ) no_of_subclasses = len(subclasses) diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index cec18aa9440c9e..b02ba3aafd4d20 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -556,6 +556,14 @@ class object | ... and 82 other subclasses """ doc = pydoc.TextDoc() + try: + # Make sure HeapType, which has no __module__ attribute, is one + # of the known subclasses of object. (doc.docclass() used to + # fail if HeapType was imported before running this test, like + # when running tests sequentially.) + from _testcapi import HeapType + except ImportError: + pass text = doc.docclass(object) snip = (" | Built-in subclasses:\n" " | async_generator\n" From 8174770d311ba09c07a47cc3ae90a1db2e7d7708 Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Sat, 18 Jan 2025 00:46:19 +0000 Subject: [PATCH 348/376] Fix definition of a `generator iterator` in `glossary.rst` (GH-128952) Fix possible typo/grammar in glossary.rst As discussed in https://discuss.python.org/t/typo-in-glossary-entry-for-generator-iterator/77163 --- Doc/glossary.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/glossary.rst b/Doc/glossary.rst index f67f3ecad0bc40..33e77c9de211eb 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -115,7 +115,7 @@ Glossary :keyword:`yield` expression. Each :keyword:`yield` temporarily suspends processing, remembering the - location execution state (including local variables and pending + execution state (including local variables and pending try-statements). When the *asynchronous generator iterator* effectively resumes with another awaitable returned by :meth:`~object.__anext__`, it picks up where it left off. See :pep:`492` and :pep:`525`. @@ -564,7 +564,7 @@ Glossary An object created by a :term:`generator` function. Each :keyword:`yield` temporarily suspends processing, remembering the - location execution state (including local variables and pending + execution state (including local variables and pending try-statements). When the *generator iterator* resumes, it picks up where it left off (in contrast to functions which start fresh on every invocation). From 3829104ab412a47bf3f36b8c133c886d2cc9a6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Sat, 18 Jan 2025 01:49:16 +0100 Subject: [PATCH 349/376] gh-128916: Do not set `SO_REUSEPORT` on non-`AF_INET*` sockets (GH-128933) * gh-128916: Do not set `SO_REUSEPORT` on non-`AF_INET*` sockets Do not attempt to set ``SO_REUSEPORT`` on sockets of address familifies other than ``AF_INET`` and ``AF_INET6``, as it is meaningless with these address families, and the call with fail with Linux kernel 6.12.9 and newer. * Apply suggestions from code review Co-authored-by: Vinay Sajip --------- Co-authored-by: Vinay Sajip --- Lib/asyncio/base_events.py | 4 +++- Lib/socket.py | 4 +++- Lib/socketserver.py | 7 ++++++- .../Library/2025-01-17-11-46-16.gh-issue-128916.GEePbO.rst | 3 +++ 4 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-17-11-46-16.gh-issue-128916.GEePbO.rst diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 6e6e5aaac15caf..85018797db33bb 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -1593,7 +1593,9 @@ async def create_server( if reuse_address: sock.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, True) - if reuse_port: + # Since Linux 6.12.9, SO_REUSEPORT is not allowed + # on other address families than AF_INET/AF_INET6. + if reuse_port and af in (socket.AF_INET, socket.AF_INET6): _set_reuseport(sock) if keep_alive: sock.setsockopt( diff --git a/Lib/socket.py b/Lib/socket.py index be37c24d6174a2..727b0e75f03595 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -937,7 +937,9 @@ def create_server(address, *, family=AF_INET, backlog=None, reuse_port=False, # Fail later on bind(), for platforms which may not # support this option. pass - if reuse_port: + # Since Linux 6.12.9, SO_REUSEPORT is not allowed + # on other address families than AF_INET/AF_INET6. + if reuse_port and family in (AF_INET, AF_INET6): sock.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1) if has_ipv6 and family == AF_INET6: if dualstack_ipv6: diff --git a/Lib/socketserver.py b/Lib/socketserver.py index cd028ef1c63b85..35b2723de3babe 100644 --- a/Lib/socketserver.py +++ b/Lib/socketserver.py @@ -468,7 +468,12 @@ def server_bind(self): """ if self.allow_reuse_address and hasattr(socket, "SO_REUSEADDR"): self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - if self.allow_reuse_port and hasattr(socket, "SO_REUSEPORT"): + # Since Linux 6.12.9, SO_REUSEPORT is not allowed + # on other address families than AF_INET/AF_INET6. + if ( + self.allow_reuse_port and hasattr(socket, "SO_REUSEPORT") + and self.address_family in (socket.AF_INET, socket.AF_INET6) + ): self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) self.socket.bind(self.server_address) self.server_address = self.socket.getsockname() diff --git a/Misc/NEWS.d/next/Library/2025-01-17-11-46-16.gh-issue-128916.GEePbO.rst b/Misc/NEWS.d/next/Library/2025-01-17-11-46-16.gh-issue-128916.GEePbO.rst new file mode 100644 index 00000000000000..f2db341ef81621 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-17-11-46-16.gh-issue-128916.GEePbO.rst @@ -0,0 +1,3 @@ +Do not attempt to set ``SO_REUSEPORT`` on sockets of address families +other than ``AF_INET`` and ``AF_INET6``, as it is meaningless with these +address families, and the call with fail with Linux kernel 6.12.9 and newer. From 9ed7bf2cd783b78b28f18abc090f43467a28f4aa Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Sat, 18 Jan 2025 01:32:23 -0600 Subject: [PATCH 350/376] gh-128515: Add BOLT build to CI (gh-128845) --- .github/workflows/build.yml | 11 +++++++++++ .github/workflows/reusable-ubuntu.yml | 15 +++++++++++++++ Lib/test/test_perf_profiler.py | 3 +++ 3 files changed, 29 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9adf860632e8a2..8a70cde3d8bc13 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -231,10 +231,14 @@ jobs: name: >- Ubuntu ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }} + ${{ fromJSON(matrix.bolt) && '(bolt)' || '' }} needs: check_source if: needs.check_source.outputs.run_tests == 'true' strategy: matrix: + bolt: + - false + - true free-threading: - false - true @@ -246,9 +250,16 @@ jobs: exclude: - os: ubuntu-24.04-aarch64 is-fork: true + # Do not test BOLT with free-threading, to conserve resources + - bolt: true + free-threading: true + # BOLT currently crashes during instrumentation on aarch64 + - os: ubuntu-24.04-aarch64 + bolt: true uses: ./.github/workflows/reusable-ubuntu.yml with: config_hash: ${{ needs.check_source.outputs.config_hash }} + bolt-optimizations: ${{ matrix.bolt }} free-threading: ${{ matrix.free-threading }} os: ${{ matrix.os }} diff --git a/.github/workflows/reusable-ubuntu.yml b/.github/workflows/reusable-ubuntu.yml index 46c542940c8483..686e8fe1abc980 100644 --- a/.github/workflows/reusable-ubuntu.yml +++ b/.github/workflows/reusable-ubuntu.yml @@ -6,6 +6,11 @@ on: config_hash: required: true type: string + bolt-optimizations: + description: Whether to enable BOLT optimizations + required: false + type: boolean + default: false free-threading: description: Whether to use free-threaded mode required: false @@ -34,6 +39,12 @@ jobs: run: echo "::add-matcher::.github/problem-matchers/gcc.json" - name: Install dependencies run: sudo ./.github/workflows/posix-deps-apt.sh + - name: Install Clang and BOLT + if: ${{ fromJSON(inputs.bolt-optimizations) }} + run: | + sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh 19 + sudo apt-get install bolt-19 + echo PATH="$(llvm-config-19 --bindir):$PATH" >> $GITHUB_ENV - name: Configure OpenSSL env vars run: | echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> "$GITHUB_ENV" @@ -73,7 +84,10 @@ jobs: key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ inputs.config_hash }} - name: Configure CPython out-of-tree working-directory: ${{ env.CPYTHON_BUILDDIR }} + # `test_unpickle_module_race` writes to the source directory, which is + # read-only during builds — so we exclude it from profiling with BOLT. run: >- + PROFILE_TASK='-m test --pgo --ignore test_unpickle_module_race' ../cpython-ro-srcdir/configure --config-cache --with-pydebug @@ -81,6 +95,7 @@ jobs: --enable-safety --with-openssl="$OPENSSL_DIR" ${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }} + ${{ fromJSON(inputs.bolt-optimizations) && '--enable-bolt' || '' }} - name: Build CPython out-of-tree if: ${{ inputs.free-threading }} working-directory: ${{ env.CPYTHON_BUILDDIR }} diff --git a/Lib/test/test_perf_profiler.py b/Lib/test/test_perf_profiler.py index 1e74990878007a..6f1fd8d38e4ea0 100644 --- a/Lib/test/test_perf_profiler.py +++ b/Lib/test/test_perf_profiler.py @@ -47,6 +47,7 @@ def tearDown(self) -> None: for file in files_to_delete: file.unlink() + @unittest.skipIf(support.check_bolt_optimized, "fails on BOLT instrumented binaries") def test_trampoline_works(self): code = """if 1: def foo(): @@ -100,6 +101,7 @@ def baz(): "Address should contain only hex characters", ) + @unittest.skipIf(support.check_bolt_optimized, "fails on BOLT instrumented binaries") def test_trampoline_works_with_forks(self): code = """if 1: import os, sys @@ -160,6 +162,7 @@ def baz(): self.assertIn(f"py::bar_fork:{script}", child_perf_file_contents) self.assertIn(f"py::baz_fork:{script}", child_perf_file_contents) + @unittest.skipIf(support.check_bolt_optimized, "fails on BOLT instrumented binaries") def test_sys_api(self): code = """if 1: import sys From 4dade055f4e18a7e91bc70293abb4db745ad16ca Mon Sep 17 00:00:00 2001 From: Tomasz Pytel Date: Sat, 18 Jan 2025 04:55:29 -0500 Subject: [PATCH 351/376] gh-128961: Fix exhausted array iterator crash in __setstate__() (#128962) --- Lib/test/test_array.py | 8 ++++++++ ...2025-01-17-21-33-11.gh-issue-128961.XwvyIZ.rst | 1 + Modules/arraymodule.c | 15 ++++++++++----- 3 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-17-21-33-11.gh-issue-128961.XwvyIZ.rst diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index f621f343eb062a..58ea89c4fac833 100755 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -1665,5 +1665,13 @@ def test_tolist(self, size): self.assertEqual(ls[:8], list(example[:8])) self.assertEqual(ls[-8:], list(example[-8:])) + def test_gh_128961(self): + a = array.array('i') + it = iter(a) + list(it) + it.__setstate__(0) + self.assertRaises(StopIteration, next, it) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2025-01-17-21-33-11.gh-issue-128961.XwvyIZ.rst b/Misc/NEWS.d/next/Library/2025-01-17-21-33-11.gh-issue-128961.XwvyIZ.rst new file mode 100644 index 00000000000000..9c985df77743da --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-17-21-33-11.gh-issue-128961.XwvyIZ.rst @@ -0,0 +1 @@ +Fix a crash when setting state on an exhausted :class:`array.array` iterator. diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index b80c964f20d65e..fdbb0f0e8d6691 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -3090,11 +3090,16 @@ array_arrayiterator___setstate__(arrayiterobject *self, PyObject *state) Py_ssize_t index = PyLong_AsSsize_t(state); if (index == -1 && PyErr_Occurred()) return NULL; - if (index < 0) - index = 0; - else if (index > Py_SIZE(self->ao)) - index = Py_SIZE(self->ao); /* iterator exhausted */ - self->index = index; + arrayobject *ao = self->ao; + if (ao != NULL) { + if (index < 0) { + index = 0; + } + else if (index > Py_SIZE(ao)) { + index = Py_SIZE(ao); /* iterator exhausted */ + } + self->index = index; + } Py_RETURN_NONE; } From d3adf02c90a570c5ee5d9b8cefe9b10305cff9e6 Mon Sep 17 00:00:00 2001 From: Marie Roald Date: Sat, 18 Jan 2025 11:27:22 +0100 Subject: [PATCH 352/376] gh-126349: Add 'fill', 'poly', and 'no_animation' context managers to turtle (#126350) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Marie Roald Co-authored-by: Yngve Mardal Moe Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Daniel Hollas Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Co-authored-by: Erlend E. Aasland --- Doc/library/turtle.rst | 85 +++++++++++++ Doc/whatsnew/3.14.rst | 8 ++ Lib/test/test_turtle.py | 112 +++++++++++++++++- Lib/turtle.py | 86 ++++++++++++-- ...-11-03-06-05-16.gh-issue-126349.7YwWsI.rst | 2 + 5 files changed, 277 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-03-06-05-16.gh-issue-126349.7YwWsI.rst diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst index 548b9d3543fb94..98dfa0f53bc6a5 100644 --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -213,6 +213,31 @@ useful when working with learners for whom typing is not a skill. use turtle graphics with a learner. +Automatically begin and end filling +----------------------------------- + +Starting with Python 3.14, you can use the :func:`fill` :term:`context manager` +instead of :func:`begin_fill` and :func:`end_fill` to automatically begin and +end fill. Here is an example:: + + with fill(): + for i in range(4): + forward(100) + right(90) + + forward(200) + +The code above is equivalent to:: + + begin_fill() + for i in range(4): + forward(100) + right(90) + end_fill() + + forward(200) + + Use the ``turtle`` module namespace ----------------------------------- @@ -351,6 +376,7 @@ Pen control Filling | :func:`filling` + | :func:`fill` | :func:`begin_fill` | :func:`end_fill` @@ -381,6 +407,7 @@ Using events | :func:`ondrag` Special Turtle methods + | :func:`poly` | :func:`begin_poly` | :func:`end_poly` | :func:`get_poly` @@ -403,6 +430,7 @@ Window control | :func:`setworldcoordinates` Animation control + | :func:`no_animation` | :func:`delay` | :func:`tracer` | :func:`update` @@ -1275,6 +1303,29 @@ Filling ... else: ... turtle.pensize(3) +.. function:: fill() + + Fill the shape drawn in the ``with turtle.fill():`` block. + + .. doctest:: + :skipif: _tkinter is None + + >>> turtle.color("black", "red") + >>> with turtle.fill(): + ... turtle.circle(80) + + Using :func:`!fill` is equivalent to adding the :func:`begin_fill` before the + fill-block and :func:`end_fill` after the fill-block: + + .. doctest:: + :skipif: _tkinter is None + + >>> turtle.color("black", "red") + >>> turtle.begin_fill() + >>> turtle.circle(80) + >>> turtle.end_fill() + + .. versionadded:: next .. function:: begin_fill() @@ -1648,6 +1699,23 @@ Using events Special Turtle methods ---------------------- + +.. function:: poly() + + Record the vertices of a polygon drawn in the ``with turtle.poly():`` block. + The first and last vertices will be connected. + + .. doctest:: + :skipif: _tkinter is None + + >>> with turtle.poly(): + ... turtle.forward(100) + ... turtle.right(60) + ... turtle.forward(100) + + .. versionadded:: next + + .. function:: begin_poly() Start recording the vertices of a polygon. Current turtle position is first @@ -1926,6 +1994,23 @@ Window control Animation control ----------------- +.. function:: no_animation() + + Temporarily disable turtle animation. The code written inside the + ``no_animation`` block will not be animated; + once the code block is exited, the drawing will appear. + + .. doctest:: + :skipif: _tkinter is None + + >>> with screen.no_animation(): + ... for dist in range(2, 400, 2): + ... fd(dist) + ... rt(90) + + .. versionadded:: next + + .. function:: delay(delay=None) :param delay: positive integer diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 9f7ef101e56478..d6aa6b346417e5 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -660,6 +660,14 @@ tkinter (Contributed by Zhikang Yan in :gh:`126899`.) +turtle +------ + +* Add context managers for :func:`turtle.fill`, :func:`turtle.poly` + and :func:`turtle.no_animation`. + (Contributed by Marie Roald and Yngve Mardal Moe in :gh:`126350`.) + + unicodedata ----------- diff --git a/Lib/test/test_turtle.py b/Lib/test/test_turtle.py index c75a002a89b4c4..de6508ff8c791d 100644 --- a/Lib/test/test_turtle.py +++ b/Lib/test/test_turtle.py @@ -1,9 +1,9 @@ import os import pickle import re +import tempfile import unittest import unittest.mock -import tempfile from test import support from test.support import import_helper from test.support import os_helper @@ -54,6 +54,21 @@ """ +def patch_screen(): + """Patch turtle._Screen for testing without a display. + + We must patch the _Screen class itself instead of the _Screen + instance because instantiating it requires a display. + """ + return unittest.mock.patch( + "turtle._Screen.__new__", + **{ + "return_value.__class__": turtle._Screen, + "return_value.mode.return_value": "standard", + }, + ) + + class TurtleConfigTest(unittest.TestCase): def get_cfg_file(self, cfg_str): @@ -513,7 +528,7 @@ def test_save_overwrites_if_specified(self) -> None: turtle.TurtleScreen.save(screen, file_path, overwrite=True) with open(file_path) as f: - assert f.read() == "postscript" + self.assertEqual(f.read(), "postscript") def test_save(self) -> None: screen = unittest.mock.Mock() @@ -524,7 +539,98 @@ def test_save(self) -> None: turtle.TurtleScreen.save(screen, file_path) with open(file_path) as f: - assert f.read() == "postscript" + self.assertEqual(f.read(), "postscript") + + def test_no_animation_sets_tracer_0(self): + s = turtle.TurtleScreen(cv=unittest.mock.MagicMock()) + + with s.no_animation(): + self.assertEqual(s.tracer(), 0) + + def test_no_animation_resets_tracer_to_old_value(self): + s = turtle.TurtleScreen(cv=unittest.mock.MagicMock()) + + for tracer in [0, 1, 5]: + s.tracer(tracer) + with s.no_animation(): + pass + self.assertEqual(s.tracer(), tracer) + + def test_no_animation_calls_update_at_exit(self): + s = turtle.TurtleScreen(cv=unittest.mock.MagicMock()) + s.update = unittest.mock.MagicMock() + + with s.no_animation(): + s.update.assert_not_called() + s.update.assert_called_once() + + +class TestTurtle(unittest.TestCase): + def setUp(self): + with patch_screen(): + self.turtle = turtle.Turtle() + + def test_begin_end_fill(self): + self.assertFalse(self.turtle.filling()) + self.turtle.begin_fill() + self.assertTrue(self.turtle.filling()) + self.turtle.end_fill() + self.assertFalse(self.turtle.filling()) + + def test_fill(self): + # The context manager behaves like begin_fill and end_fill. + self.assertFalse(self.turtle.filling()) + with self.turtle.fill(): + self.assertTrue(self.turtle.filling()) + self.assertFalse(self.turtle.filling()) + + def test_fill_resets_after_exception(self): + # The context manager cleans up correctly after exceptions. + try: + with self.turtle.fill(): + self.assertTrue(self.turtle.filling()) + raise ValueError + except ValueError: + self.assertFalse(self.turtle.filling()) + + def test_fill_context_when_filling(self): + # The context manager works even when the turtle is already filling. + self.turtle.begin_fill() + self.assertTrue(self.turtle.filling()) + with self.turtle.fill(): + self.assertTrue(self.turtle.filling()) + self.assertFalse(self.turtle.filling()) + + def test_begin_end_poly(self): + self.assertFalse(self.turtle._creatingPoly) + self.turtle.begin_poly() + self.assertTrue(self.turtle._creatingPoly) + self.turtle.end_poly() + self.assertFalse(self.turtle._creatingPoly) + + def test_poly(self): + # The context manager behaves like begin_poly and end_poly. + self.assertFalse(self.turtle._creatingPoly) + with self.turtle.poly(): + self.assertTrue(self.turtle._creatingPoly) + self.assertFalse(self.turtle._creatingPoly) + + def test_poly_resets_after_exception(self): + # The context manager cleans up correctly after exceptions. + try: + with self.turtle.poly(): + self.assertTrue(self.turtle._creatingPoly) + raise ValueError + except ValueError: + self.assertFalse(self.turtle._creatingPoly) + + def test_poly_context_when_creating_poly(self): + # The context manager works when the turtle is already creating poly. + self.turtle.begin_poly() + self.assertTrue(self.turtle._creatingPoly) + with self.turtle.poly(): + self.assertTrue(self.turtle._creatingPoly) + self.assertFalse(self.turtle._creatingPoly) class TestModuleLevel(unittest.TestCase): diff --git a/Lib/turtle.py b/Lib/turtle.py index 1320cfd93fd6db..e88981d298ad52 100644 --- a/Lib/turtle.py +++ b/Lib/turtle.py @@ -107,6 +107,7 @@ from os.path import isfile, split, join from pathlib import Path +from contextlib import contextmanager from copy import deepcopy from tkinter import simpledialog @@ -114,23 +115,24 @@ 'RawTurtle', 'Turtle', 'RawPen', 'Pen', 'Shape', 'Vec2D'] _tg_screen_functions = ['addshape', 'bgcolor', 'bgpic', 'bye', 'clearscreen', 'colormode', 'delay', 'exitonclick', 'getcanvas', - 'getshapes', 'listen', 'mainloop', 'mode', 'numinput', + 'getshapes', 'listen', 'mainloop', 'mode', 'no_animation', 'numinput', 'onkey', 'onkeypress', 'onkeyrelease', 'onscreenclick', 'ontimer', 'register_shape', 'resetscreen', 'screensize', 'save', 'setup', - 'setworldcoordinates', 'textinput', 'title', 'tracer', 'turtles', 'update', - 'window_height', 'window_width'] + 'setworldcoordinates', 'textinput', 'title', 'tracer', 'turtles', + 'update', 'window_height', 'window_width'] _tg_turtle_functions = ['back', 'backward', 'begin_fill', 'begin_poly', 'bk', 'circle', 'clear', 'clearstamp', 'clearstamps', 'clone', 'color', 'degrees', 'distance', 'dot', 'down', 'end_fill', 'end_poly', 'fd', - 'fillcolor', 'filling', 'forward', 'get_poly', 'getpen', 'getscreen', 'get_shapepoly', - 'getturtle', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'isdown', - 'isvisible', 'left', 'lt', 'onclick', 'ondrag', 'onrelease', 'pd', - 'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position', - 'pu', 'radians', 'right', 'reset', 'resizemode', 'rt', - 'seth', 'setheading', 'setpos', 'setposition', - 'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'shapetransform', 'shearfactor', 'showturtle', - 'speed', 'st', 'stamp', 'teleport', 'tilt', 'tiltangle', 'towards', - 'turtlesize', 'undo', 'undobufferentries', 'up', 'width', + 'fillcolor', 'fill', 'filling', 'forward', 'get_poly', 'getpen', + 'getscreen', 'get_shapepoly', 'getturtle', 'goto', 'heading', + 'hideturtle', 'home', 'ht', 'isdown', 'isvisible', 'left', 'lt', + 'onclick', 'ondrag', 'onrelease', 'pd', 'pen', 'pencolor', 'pendown', + 'pensize', 'penup', 'poly', 'pos', 'position', 'pu', 'radians', 'right', + 'reset', 'resizemode', 'rt', 'seth', 'setheading', 'setpos', + 'setposition', 'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', + 'shapetransform', 'shearfactor', 'showturtle', 'speed', 'st', 'stamp', + 'teleport', 'tilt', 'tiltangle', 'towards', 'turtlesize', 'undo', + 'undobufferentries', 'up', 'width', 'write', 'xcor', 'ycor'] _tg_utilities = ['write_docstringdict', 'done'] @@ -1275,6 +1277,26 @@ def delay(self, delay=None): return self._delayvalue self._delayvalue = int(delay) + @contextmanager + def no_animation(self): + """Temporarily turn off auto-updating the screen. + + This is useful for drawing complex shapes where even the fastest setting + is too slow. Once this context manager is exited, the drawing will + be displayed. + + Example (for a TurtleScreen instance named screen + and a Turtle instance named turtle): + >>> with screen.no_animation(): + ... turtle.circle(50) + """ + tracer = self.tracer() + try: + self.tracer(0) + yield + finally: + self.tracer(tracer) + def _incrementudc(self): """Increment update counter.""" if not TurtleScreen._RUNNING: @@ -3380,6 +3402,24 @@ def filling(self): """ return isinstance(self._fillpath, list) + @contextmanager + def fill(self): + """A context manager for filling a shape. + + Implicitly ensures the code block is wrapped with + begin_fill() and end_fill(). + + Example (for a Turtle instance named turtle): + >>> turtle.color("black", "red") + >>> with turtle.fill(): + ... turtle.circle(60) + """ + self.begin_fill() + try: + yield + finally: + self.end_fill() + def begin_fill(self): """Called just before drawing a shape to be filled. @@ -3400,7 +3440,6 @@ def begin_fill(self): self.undobuffer.push(("beginfill", self._fillitem)) self._update() - def end_fill(self): """Fill the shape drawn after the call begin_fill(). @@ -3504,6 +3543,27 @@ def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")): if self.undobuffer: self.undobuffer.cumulate = False + @contextmanager + def poly(self): + """A context manager for recording the vertices of a polygon. + + Implicitly ensures that the code block is wrapped with + begin_poly() and end_poly() + + Example (for a Turtle instance named turtle) where we create a + triangle as the polygon and move the turtle 100 steps forward: + >>> with turtle.poly(): + ... for side in range(3) + ... turtle.forward(50) + ... turtle.right(60) + >>> turtle.forward(100) + """ + self.begin_poly() + try: + yield + finally: + self.end_poly() + def begin_poly(self): """Start recording the vertices of a polygon. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-03-06-05-16.gh-issue-126349.7YwWsI.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-03-06-05-16.gh-issue-126349.7YwWsI.rst new file mode 100644 index 00000000000000..aecc8c9abf3ce9 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-03-06-05-16.gh-issue-126349.7YwWsI.rst @@ -0,0 +1,2 @@ +Add :func:`turtle.fill`, :func:`turtle.poly` and :func:`turtle.no_animation` context managers. +Patch by Marie Roald and Yngve Mardal Moe. From d5e9aa690a190bcfa9b43cd3858e26b7b2f72d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 18 Jan 2025 11:45:18 +0100 Subject: [PATCH 353/376] gh-118761: Improve import time for `csv` (#128858) This reduces the import time of the `csv` module by up to five times, by importing `re` on demand. In particular, the `re` module is no more implicitly exposed as `csv.re`. --- Lib/csv.py | 2 +- .../Library/2025-01-15-09-45-43.gh-issue-118761.TvAC8E.rst | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-15-09-45-43.gh-issue-118761.TvAC8E.rst diff --git a/Lib/csv.py b/Lib/csv.py index cd202659873811..0a627ba7a512fa 100644 --- a/Lib/csv.py +++ b/Lib/csv.py @@ -63,7 +63,6 @@ class excel: written as two quotes """ -import re import types from _csv import Error, writer, reader, register_dialect, \ unregister_dialect, get_dialect, list_dialects, \ @@ -281,6 +280,7 @@ def _guess_quote_and_delimiter(self, data, delimiters): If there is no quotechar the delimiter can't be determined this way. """ + import re matches = [] for restr in (r'(?P[^\w\n"\'])(?P ?)(?P["\']).*?(?P=quote)(?P=delim)', # ,".*?", diff --git a/Misc/NEWS.d/next/Library/2025-01-15-09-45-43.gh-issue-118761.TvAC8E.rst b/Misc/NEWS.d/next/Library/2025-01-15-09-45-43.gh-issue-118761.TvAC8E.rst new file mode 100644 index 00000000000000..38d18b7f4ca05e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-15-09-45-43.gh-issue-118761.TvAC8E.rst @@ -0,0 +1,3 @@ +Reduce the import time of :mod:`csv` by up to five times, by importing +:mod:`re` on demand. In particular, ``re`` is no more implicitly exposed +as ``csv.re``. Patch by Bénédikt Tran. From f4afaa6f1190fbbea3e27c590096951d8ffdfb71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 18 Jan 2025 12:02:43 +0100 Subject: [PATCH 354/376] gh-125997: suggest efficient alternatives for `time.sleep(0)` (#128752) --- Doc/library/os.rst | 4 +++- Doc/library/time.rst | 11 ++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 6fec27bc6f68a2..2445b008eb5a75 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -5411,6 +5411,8 @@ information, consult your Unix manpages. The following scheduling policies are exposed if they are supported by the operating system. +.. _os-scheduling-policy: + .. data:: SCHED_OTHER The default scheduling policy. @@ -5514,7 +5516,7 @@ operating system. .. function:: sched_yield() - Voluntarily relinquish the CPU. + Voluntarily relinquish the CPU. See :manpage:`sched_yield(2)` for details. .. function:: sched_setaffinity(pid, mask, /) diff --git a/Doc/library/time.rst b/Doc/library/time.rst index 6265c2214eaa0d..804e2679027bd4 100644 --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -385,6 +385,8 @@ Functions The suspension time may be longer than requested by an arbitrary amount, because of the scheduling of other activity in the system. + .. rubric:: Windows implementation + On Windows, if *secs* is zero, the thread relinquishes the remainder of its time slice to any other thread that is ready to run. If there are no other threads ready to run, the function returns immediately, and the thread @@ -393,12 +395,19 @@ Functions `_ which provides resolution of 100 nanoseconds. If *secs* is zero, ``Sleep(0)`` is used. - Unix implementation: + .. rubric:: Unix implementation * Use ``clock_nanosleep()`` if available (resolution: 1 nanosecond); * Or use ``nanosleep()`` if available (resolution: 1 nanosecond); * Or use ``select()`` (resolution: 1 microsecond). + .. note:: + + To emulate a "no-op", use :keyword:`pass` instead of ``time.sleep(0)``. + + To voluntarily relinquish the CPU, specify a real-time :ref:`scheduling + policy ` and use :func:`os.sched_yield` instead. + .. audit-event:: time.sleep secs .. versionchanged:: 3.5 From 81159fce3643d2b5b265c441f3079166203e873e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 18 Jan 2025 16:10:29 +0100 Subject: [PATCH 355/376] gh-59705: Make PYTHREAD_NAME_MAXLEN macro private (#128945) Rename PYTHREAD_NAME_MAXLEN to _PYTHREAD_NAME_MAXLEN. --- Modules/_threadmodule.c | 20 ++++++++++---------- PC/pyconfig.h.in | 5 ++--- configure | 20 ++++++++++---------- configure.ac | 20 ++++++++++---------- pyconfig.h.in | 6 +++--- 5 files changed, 35 insertions(+), 36 deletions(-) diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 586ed368024fb1..dbc574f7816b85 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -2450,12 +2450,12 @@ _thread_set_name_impl(PyObject *module, PyObject *name_obj) return NULL; } -#ifdef PYTHREAD_NAME_MAXLEN - // Truncate to PYTHREAD_NAME_MAXLEN bytes + the NUL byte if needed - if (PyBytes_GET_SIZE(name_encoded) > PYTHREAD_NAME_MAXLEN) { +#ifdef _PYTHREAD_NAME_MAXLEN + // Truncate to _PYTHREAD_NAME_MAXLEN bytes + the NUL byte if needed + if (PyBytes_GET_SIZE(name_encoded) > _PYTHREAD_NAME_MAXLEN) { PyObject *truncated; truncated = PyBytes_FromStringAndSize(PyBytes_AS_STRING(name_encoded), - PYTHREAD_NAME_MAXLEN); + _PYTHREAD_NAME_MAXLEN); if (truncated == NULL) { Py_DECREF(name_encoded); return NULL; @@ -2490,14 +2490,14 @@ _thread_set_name_impl(PyObject *module, PyObject *name_obj) return NULL; } - if (len > PYTHREAD_NAME_MAXLEN) { + if (len > _PYTHREAD_NAME_MAXLEN) { // Truncate the name - Py_UCS4 ch = name[PYTHREAD_NAME_MAXLEN-1]; + Py_UCS4 ch = name[_PYTHREAD_NAME_MAXLEN-1]; if (Py_UNICODE_IS_HIGH_SURROGATE(ch)) { - name[PYTHREAD_NAME_MAXLEN-1] = 0; + name[_PYTHREAD_NAME_MAXLEN-1] = 0; } else { - name[PYTHREAD_NAME_MAXLEN] = 0; + name[_PYTHREAD_NAME_MAXLEN] = 0; } } @@ -2645,9 +2645,9 @@ thread_module_exec(PyObject *module) llist_init(&state->shutdown_handles); -#ifdef PYTHREAD_NAME_MAXLEN +#ifdef _PYTHREAD_NAME_MAXLEN if (PyModule_AddIntConstant(module, "_NAME_MAXLEN", - PYTHREAD_NAME_MAXLEN) < 0) { + _PYTHREAD_NAME_MAXLEN) < 0) { return -1; } #endif diff --git a/PC/pyconfig.h.in b/PC/pyconfig.h.in index 837461d0e884bc..f4f57c5d270028 100644 --- a/PC/pyconfig.h.in +++ b/PC/pyconfig.h.in @@ -753,8 +753,7 @@ Py_NO_ENABLE_SHARED to find out. Also support MS_NO_COREDLL for b/w compat */ /* Define if libssl has X509_VERIFY_PARAM_set1_host and related function */ #define HAVE_X509_VERIFY_PARAM_SET1_HOST 1 -// Truncate the thread name to 64 characters. The OS limit is 32766 wide -// characters, but long names aren't of practical use. -#define PYTHREAD_NAME_MAXLEN 32766 +// Truncate the thread name to 32766 characters. +#define _PYTHREAD_NAME_MAXLEN 32766 #endif /* !Py_CONFIG_H */ diff --git a/configure b/configure index 70581e11b60682..ae1b649630171f 100755 --- a/configure +++ b/configure @@ -814,7 +814,7 @@ MODULE_TIME_TRUE MODULE__IO_FALSE MODULE__IO_TRUE MODULE_BUILDTYPE -PYTHREAD_NAME_MAXLEN +_PYTHREAD_NAME_MAXLEN TEST_MODULES OPENSSL_LDFLAGS OPENSSL_LIBS @@ -30403,17 +30403,17 @@ CPPFLAGS=$save_CPPFLAGS # gh-59705: Maximum length in bytes of a thread name case "$ac_sys_system" in - Linux*) PYTHREAD_NAME_MAXLEN=15;; # Linux and Android - SunOS*) PYTHREAD_NAME_MAXLEN=31;; - NetBSD*) PYTHREAD_NAME_MAXLEN=31;; - Darwin) PYTHREAD_NAME_MAXLEN=63;; - iOS) PYTHREAD_NAME_MAXLEN=63;; - FreeBSD*) PYTHREAD_NAME_MAXLEN=98;; - *) PYTHREAD_NAME_MAXLEN=;; + Linux*) _PYTHREAD_NAME_MAXLEN=15;; # Linux and Android + SunOS*) _PYTHREAD_NAME_MAXLEN=31;; + NetBSD*) _PYTHREAD_NAME_MAXLEN=31;; + Darwin) _PYTHREAD_NAME_MAXLEN=63;; + iOS) _PYTHREAD_NAME_MAXLEN=63;; + FreeBSD*) _PYTHREAD_NAME_MAXLEN=98;; + *) _PYTHREAD_NAME_MAXLEN=;; esac -if test -n "$PYTHREAD_NAME_MAXLEN"; then +if test -n "$_PYTHREAD_NAME_MAXLEN"; then -printf "%s\n" "#define PYTHREAD_NAME_MAXLEN $PYTHREAD_NAME_MAXLEN" >>confdefs.h +printf "%s\n" "#define _PYTHREAD_NAME_MAXLEN $_PYTHREAD_NAME_MAXLEN" >>confdefs.h fi diff --git a/configure.ac b/configure.ac index d7c3920d049d67..cc499dfbc502f1 100644 --- a/configure.ac +++ b/configure.ac @@ -7539,19 +7539,19 @@ _RESTORE_VAR([CPPFLAGS]) # gh-59705: Maximum length in bytes of a thread name case "$ac_sys_system" in - Linux*) PYTHREAD_NAME_MAXLEN=15;; # Linux and Android - SunOS*) PYTHREAD_NAME_MAXLEN=31;; - NetBSD*) PYTHREAD_NAME_MAXLEN=31;; - Darwin) PYTHREAD_NAME_MAXLEN=63;; - iOS) PYTHREAD_NAME_MAXLEN=63;; - FreeBSD*) PYTHREAD_NAME_MAXLEN=98;; - *) PYTHREAD_NAME_MAXLEN=;; + Linux*) _PYTHREAD_NAME_MAXLEN=15;; # Linux and Android + SunOS*) _PYTHREAD_NAME_MAXLEN=31;; + NetBSD*) _PYTHREAD_NAME_MAXLEN=31;; + Darwin) _PYTHREAD_NAME_MAXLEN=63;; + iOS) _PYTHREAD_NAME_MAXLEN=63;; + FreeBSD*) _PYTHREAD_NAME_MAXLEN=98;; + *) _PYTHREAD_NAME_MAXLEN=;; esac -if test -n "$PYTHREAD_NAME_MAXLEN"; then - AC_DEFINE_UNQUOTED([PYTHREAD_NAME_MAXLEN], [$PYTHREAD_NAME_MAXLEN], +if test -n "$_PYTHREAD_NAME_MAXLEN"; then + AC_DEFINE_UNQUOTED([_PYTHREAD_NAME_MAXLEN], [$_PYTHREAD_NAME_MAXLEN], [Maximum length in bytes of a thread name]) fi -AC_SUBST([PYTHREAD_NAME_MAXLEN]) +AC_SUBST([_PYTHREAD_NAME_MAXLEN]) # stdlib diff --git a/pyconfig.h.in b/pyconfig.h.in index aaf52168c3d39d..30e55158bad4d6 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1662,9 +1662,6 @@ /* Define as the preferred size in bits of long digits */ #undef PYLONG_BITS_IN_DIGIT -/* Maximum length in bytes of a thread name */ -#undef PYTHREAD_NAME_MAXLEN - /* enabled builtin hash modules */ #undef PY_BUILTIN_HASHLIB_HASHES @@ -1980,6 +1977,9 @@ /* framework name */ #undef _PYTHONFRAMEWORK +/* Maximum length in bytes of a thread name */ +#undef _PYTHREAD_NAME_MAXLEN + /* Define to force use of thread-safe errno, h_errno, and other functions */ #undef _REENTRANT From 9bc19643ed54b2aa8558e87fd3a04e8bd1ea7fb2 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 18 Jan 2025 16:13:54 +0100 Subject: [PATCH 356/376] gh-128679: Skip test_tracemalloc_track_race() on debug build (#128988) There is a race condition between PyMem_SetAllocator() and PyMem_RawMalloc()/PyMem_RawFree(). While PyMem_SetAllocator() write is protected by a lock, PyMem_RawMalloc()/PyMem_RawFree() reads are not protected by a lock. PyMem_RawMalloc()/PyMem_RawFree() can be called with an old context and the new function pointer. On a release build, it's not an issue since the context is not used. On a debug build, the debug hooks use the context and so can crash. --- Lib/test/test_tracemalloc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_tracemalloc.py b/Lib/test/test_tracemalloc.py index da2db28775578a..a848363fcd1de9 100644 --- a/Lib/test/test_tracemalloc.py +++ b/Lib/test/test_tracemalloc.py @@ -1103,6 +1103,8 @@ def test_stop_untrack(self): @unittest.skipIf(_testcapi is None, 'need _testcapi') @threading_helper.requires_working_threading() + # gh-128679: Test crash on a debug build (especially on FreeBSD). + @unittest.skipIf(support.Py_DEBUG, 'need release build') def test_tracemalloc_track_race(self): # gh-128679: Test fix for tracemalloc.stop() race condition _testcapi.tracemalloc_track_race() From 3de7cc15c2f3186b84c0fb105bc84c27e9589902 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Sat, 18 Jan 2025 12:53:17 -0500 Subject: [PATCH 357/376] Pre-commit: Drop specific language version and bump hooks (#128801) --- .pre-commit-config.yaml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index af6accd89b5bd4..74b56f060342bf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.2 + rev: v0.9.1 hooks: - id: ruff name: Run Ruff (lint) on Doc/ @@ -29,12 +29,10 @@ repos: - id: black name: Run Black on Tools/build/check_warnings.py files: ^Tools/build/check_warnings.py - language_version: python3.12 args: [--line-length=79] - id: black name: Run Black on Tools/jit/ files: ^Tools/jit/ - language_version: python3.12 - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 @@ -51,19 +49,19 @@ repos: types_or: [c, inc, python, rst] - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.30.0 + rev: 0.31.0 hooks: - id: check-dependabot - id: check-github-workflows - id: check-readthedocs - repo: https://github.com/rhysd/actionlint - rev: v1.7.4 + rev: v1.7.6 hooks: - id: actionlint - repo: https://github.com/woodruffw/zizmor-pre-commit - rev: v0.8.0 + rev: v1.1.1 hooks: - id: zizmor From fba475ae6f932d0aaee6832b4102b2d4c50df70f Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Sat, 18 Jan 2025 13:34:40 -0500 Subject: [PATCH 358/376] gh-121542: Document trailing newline behavior in `set_content()` (#121543) Co-authored-by: Yizheng Meng --- Doc/library/email.contentmanager.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Doc/library/email.contentmanager.rst b/Doc/library/email.contentmanager.rst index a86e227429b06d..b33fe82a6e4c9f 100644 --- a/Doc/library/email.contentmanager.rst +++ b/Doc/library/email.contentmanager.rst @@ -157,7 +157,13 @@ Currently the email package provides only one concrete content manager, :exc:`ValueError`. * For ``str`` objects, if *cte* is not set use heuristics to - determine the most compact encoding. + determine the most compact encoding. Prior to encoding, + :meth:`str.splitlines` is used to normalize all line boundaries, + ensuring that each line of the payload is terminated by the + current policy's :data:`~email.policy.Policy.linesep` property + (even if the original string did not end with one). + * For ``bytes`` objects, *cte* is taken to be base64 if not set, + and the aforementioned newline translation is not performed. * For :class:`~email.message.EmailMessage`, per :rfc:`2046`, raise an error if a *cte* of ``quoted-printable`` or ``base64`` is requested for *subtype* ``rfc822``, and for any *cte* other than From e8092e5cdcd6707ac0b16d8fb37fa080a88bcc97 Mon Sep 17 00:00:00 2001 From: Rafael Fontenelle Date: Sat, 18 Jan 2025 17:52:30 -0300 Subject: [PATCH 359/376] gh-128998: Fix indentation of numbered list and literal block (#128999) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/faq/programming.rst | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index fa7b22bde1dc6f..776bab1ed5b779 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1906,28 +1906,30 @@ In the standard library code, you will see several common patterns for correctly using identity tests: 1) As recommended by :pep:`8`, an identity test is the preferred way to check -for ``None``. This reads like plain English in code and avoids confusion with -other objects that may have boolean values that evaluate to false. + for ``None``. This reads like plain English in code and avoids confusion + with other objects that may have boolean values that evaluate to false. 2) Detecting optional arguments can be tricky when ``None`` is a valid input -value. In those situations, you can create a singleton sentinel object -guaranteed to be distinct from other objects. For example, here is how -to implement a method that behaves like :meth:`dict.pop`:: + value. In those situations, you can create a singleton sentinel object + guaranteed to be distinct from other objects. For example, here is how + to implement a method that behaves like :meth:`dict.pop`: - _sentinel = object() + .. code-block:: python - def pop(self, key, default=_sentinel): - if key in self: - value = self[key] - del self[key] - return value - if default is _sentinel: - raise KeyError(key) - return default + _sentinel = object() + + def pop(self, key, default=_sentinel): + if key in self: + value = self[key] + del self[key] + return value + if default is _sentinel: + raise KeyError(key) + return default 3) Container implementations sometimes need to augment equality tests with -identity tests. This prevents the code from being confused by objects such as -``float('NaN')`` that are not equal to themselves. + identity tests. This prevents the code from being confused by objects + such as ``float('NaN')`` that are not equal to themselves. For example, here is the implementation of :meth:`!collections.abc.Sequence.__contains__`:: From 61b35f74aa4a6ac606635e245147ff3658628d99 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Sat, 18 Jan 2025 16:53:06 -0500 Subject: [PATCH 360/376] gh-128991: Release the enter frame reference within bdb callback (#128992) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Release the enter frame reference within bdb callback * 📜🤖 Added by blurb_it. --------- Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> --- Lib/bdb.py | 68 ++++++++++--------- ...-01-18-16-58-10.gh-issue-128991.EzJit9.rst | 1 + 2 files changed, 37 insertions(+), 32 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-18-16-58-10.gh-issue-128991.EzJit9.rst diff --git a/Lib/bdb.py b/Lib/bdb.py index 73e249621a053b..a741628e32a981 100644 --- a/Lib/bdb.py +++ b/Lib/bdb.py @@ -4,6 +4,7 @@ import sys import os import weakref +from contextlib import contextmanager from inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR __all__ = ["BdbQuit", "Bdb", "Breakpoint"] @@ -65,6 +66,12 @@ def reset(self): self.botframe = None self._set_stopinfo(None, None) + @contextmanager + def set_enterframe(self, frame): + self.enterframe = frame + yield + self.enterframe = None + def trace_dispatch(self, frame, event, arg): """Dispatch a trace function for debugged frames based on the event. @@ -90,28 +97,27 @@ def trace_dispatch(self, frame, event, arg): The arg parameter depends on the previous event. """ - self.enterframe = frame - - if self.quitting: - return # None - if event == 'line': - return self.dispatch_line(frame) - if event == 'call': - return self.dispatch_call(frame, arg) - if event == 'return': - return self.dispatch_return(frame, arg) - if event == 'exception': - return self.dispatch_exception(frame, arg) - if event == 'c_call': - return self.trace_dispatch - if event == 'c_exception': - return self.trace_dispatch - if event == 'c_return': + with self.set_enterframe(frame): + if self.quitting: + return # None + if event == 'line': + return self.dispatch_line(frame) + if event == 'call': + return self.dispatch_call(frame, arg) + if event == 'return': + return self.dispatch_return(frame, arg) + if event == 'exception': + return self.dispatch_exception(frame, arg) + if event == 'c_call': + return self.trace_dispatch + if event == 'c_exception': + return self.trace_dispatch + if event == 'c_return': + return self.trace_dispatch + if event == 'opcode': + return self.dispatch_opcode(frame, arg) + print('bdb.Bdb.dispatch: unknown debugging event:', repr(event)) return self.trace_dispatch - if event == 'opcode': - return self.dispatch_opcode(frame, arg) - print('bdb.Bdb.dispatch: unknown debugging event:', repr(event)) - return self.trace_dispatch def dispatch_line(self, frame): """Invoke user function and return trace function for line event. @@ -395,16 +401,15 @@ def set_trace(self, frame=None): if frame is None: frame = sys._getframe().f_back self.reset() - self.enterframe = frame - while frame: - frame.f_trace = self.trace_dispatch - self.botframe = frame - self.frame_trace_lines_opcodes[frame] = (frame.f_trace_lines, frame.f_trace_opcodes) - # We need f_trace_lines == True for the debugger to work - frame.f_trace_lines = True - frame = frame.f_back - self.set_stepinstr() - self.enterframe = None + with self.set_enterframe(frame): + while frame: + frame.f_trace = self.trace_dispatch + self.botframe = frame + self.frame_trace_lines_opcodes[frame] = (frame.f_trace_lines, frame.f_trace_opcodes) + # We need f_trace_lines == True for the debugger to work + frame.f_trace_lines = True + frame = frame.f_back + self.set_stepinstr() sys.settrace(self.trace_dispatch) def set_continue(self): @@ -424,7 +429,6 @@ def set_continue(self): for frame, (trace_lines, trace_opcodes) in self.frame_trace_lines_opcodes.items(): frame.f_trace_lines, frame.f_trace_opcodes = trace_lines, trace_opcodes self.frame_trace_lines_opcodes = {} - self.enterframe = None def set_quit(self): """Set quitting attribute to True. diff --git a/Misc/NEWS.d/next/Library/2025-01-18-16-58-10.gh-issue-128991.EzJit9.rst b/Misc/NEWS.d/next/Library/2025-01-18-16-58-10.gh-issue-128991.EzJit9.rst new file mode 100644 index 00000000000000..64fa04fb53e89c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-18-16-58-10.gh-issue-128991.EzJit9.rst @@ -0,0 +1 @@ +Release the enter frame reference within :mod:`bdb` callback From 5aaf41685834901e4ed0a40f4c055b92991a0bb5 Mon Sep 17 00:00:00 2001 From: Mike Edmunds Date: Sat, 18 Jan 2025 16:50:52 -0800 Subject: [PATCH 361/376] gh-80222: Fix email address header folding with long quoted-string (#122753) Email generators using email.policy.default could incorrectly omit the quote ('"') characters from a quoted-string during header refolding, leading to invalid address headers and enabling header spoofing. This change restores the quote characters on a bare-quoted-string as the header is refolded, and escapes backslash and quote chars in the string. --- Lib/email/_header_value_parser.py | 19 +++++++++++- .../test_email/test__header_value_parser.py | 31 +++++++++++++++++-- ...4-08-06-11-43-08.gh-issue-80222.wfR4BU.rst | 6 ++++ 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2024-08-06-11-43-08.gh-issue-80222.wfR4BU.rst diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index ec2215a5e5f33c..3d845c09d415f6 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -95,8 +95,16 @@ NLSET = {'\n', '\r'} SPECIALSNL = SPECIALS | NLSET + +def make_quoted_pairs(value): + """Escape dquote and backslash for use within a quoted-string.""" + return str(value).replace('\\', '\\\\').replace('"', '\\"') + + def quote_string(value): - return '"'+str(value).replace('\\', '\\\\').replace('"', r'\"')+'"' + escaped = make_quoted_pairs(value) + return f'"{escaped}"' + # Match a RFC 2047 word, looks like =?utf-8?q?someword?= rfc2047_matcher = re.compile(r''' @@ -2905,6 +2913,15 @@ def _refold_parse_tree(parse_tree, *, policy): if not hasattr(part, 'encode'): # It's not a terminal, try folding the subparts. newparts = list(part) + if part.token_type == 'bare-quoted-string': + # To fold a quoted string we need to create a list of terminal + # tokens that will render the leading and trailing quotes + # and use quoted pairs in the value as appropriate. + newparts = ( + [ValueTerminal('"', 'ptext')] + + [ValueTerminal(make_quoted_pairs(p), 'ptext') + for p in newparts] + + [ValueTerminal('"', 'ptext')]) if not part.as_ew_allowed: wrap_as_ew_blocked += 1 newparts.append(end_ew_not_allowed) diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py index 95224e19f67ce5..d60a7039f9d4c6 100644 --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -3082,13 +3082,40 @@ def test_address_list_with_list_separator_after_fold(self): self._test(parser.get_address_list(to)[0], f'{a},\n =?utf-8?q?H=C3=BCbsch?= Kaktus \n') - a = '.' * 79 + a = '.' * 79 # ('.' is a special, so must be in quoted-string.) to = f'"{a}" , "Hübsch Kaktus" ' self._test(parser.get_address_list(to)[0], - f'{a}\n' + f'"{a}"\n' ' , =?utf-8?q?H=C3=BCbsch?= Kaktus ' '\n') + def test_address_list_with_specials_in_long_quoted_string(self): + # Regression for gh-80222. + policy = self.policy.clone(max_line_length=40) + cases = [ + # (to, folded) + ('"Exfiltrator (unclosed comment?" ', + '"Exfiltrator (unclosed\n' + ' comment?" \n'), + ('"Escaped \\" chars \\\\ in quoted-string stay escaped" ', + '"Escaped \\" chars \\\\ in quoted-string\n' + ' stay escaped" \n'), + ('This long display name does not need quotes ', + 'This long display name does not need\n' + ' quotes \n'), + ('"Quotes are not required but are retained here" ', + '"Quotes are not required but are\n' + ' retained here" \n'), + ('"A quoted-string, it can be a valid local-part"@example.com', + '"A quoted-string, it can be a valid\n' + ' local-part"@example.com\n'), + ('"local-part-with-specials@but-no-fws.cannot-fold"@example.com', + '"local-part-with-specials@but-no-fws.cannot-fold"@example.com\n'), + ] + for (to, folded) in cases: + with self.subTest(to=to): + self._test(parser.get_address_list(to)[0], folded, policy=policy) + # XXX Need tests with comments on various sides of a unicode token, # and with unicode tokens in the comments. Spaces inside the quotes # currently don't do the right thing. diff --git a/Misc/NEWS.d/next/Security/2024-08-06-11-43-08.gh-issue-80222.wfR4BU.rst b/Misc/NEWS.d/next/Security/2024-08-06-11-43-08.gh-issue-80222.wfR4BU.rst new file mode 100644 index 00000000000000..0f0661d0b1cf4a --- /dev/null +++ b/Misc/NEWS.d/next/Security/2024-08-06-11-43-08.gh-issue-80222.wfR4BU.rst @@ -0,0 +1,6 @@ +Fix bug in the folding of quoted strings when flattening an email message using +a modern email policy. Previously when a quoted string was folded so that +it spanned more than one line, the surrounding quotes and internal escapes +would be omitted. This could theoretically be used to spoof header lines +using a carefully constructed quoted string if the resulting rendered email +was transmitted or re-parsed. From 6c52ada5512ccd1a0891c00cc84c7d3170d3328c Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Sun, 19 Jan 2025 13:02:49 +0200 Subject: [PATCH 362/376] gh-100239: Handle NaN and zero division in guards for `BINARY_OP_EXTEND` (#128963) Co-authored-by: Tomas R. Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> --- Lib/test/test_opcache.py | 47 ++++++++++++++++++++++++++++++++++++++++ Python/specialize.c | 30 ++++++++++++++++++++----- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index 72b845fcc8fdbf..4d7304b1c9abb6 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -1362,6 +1362,53 @@ def binary_op_add_extend(): self.assert_specialized(binary_op_add_extend, "BINARY_OP_EXTEND") self.assert_no_opcode(binary_op_add_extend, "BINARY_OP") + def binary_op_zero_division(): + def compactlong_lhs(arg): + 42 / arg + def float_lhs(arg): + 42.0 / arg + + with self.assertRaises(ZeroDivisionError): + compactlong_lhs(0) + with self.assertRaises(ZeroDivisionError): + compactlong_lhs(0.0) + with self.assertRaises(ZeroDivisionError): + float_lhs(0.0) + with self.assertRaises(ZeroDivisionError): + float_lhs(0) + + self.assert_no_opcode(compactlong_lhs, "BINARY_OP_EXTEND") + self.assert_no_opcode(float_lhs, "BINARY_OP_EXTEND") + + binary_op_zero_division() + + def binary_op_nan(): + def compactlong_lhs(arg): + return ( + 42 + arg, + 42 - arg, + 42 * arg, + 42 / arg, + ) + def compactlong_rhs(arg): + return ( + arg + 42, + arg - 42, + arg * 2, + arg / 42, + ) + nan = float('nan') + self.assertEqual(compactlong_lhs(1.0), (43.0, 41.0, 42.0, 42.0)) + for _ in range(100): + self.assertTrue(all(filter(lambda x: x is nan, compactlong_lhs(nan)))) + self.assertEqual(compactlong_rhs(42.0), (84.0, 0.0, 84.0, 1.0)) + for _ in range(100): + self.assertTrue(all(filter(lambda x: x is nan, compactlong_rhs(nan)))) + + self.assert_no_opcode(compactlong_lhs, "BINARY_OP_EXTEND") + self.assert_no_opcode(compactlong_rhs, "BINARY_OP_EXTEND") + + binary_op_nan() @cpython_only @requires_specialization_ft diff --git a/Python/specialize.c b/Python/specialize.c index 09bfcd34b5a543..fa022346bdea6a 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -2416,16 +2416,25 @@ binary_op_fail_kind(int oparg, PyObject *lhs, PyObject *rhs) /* float-long */ -static int +static inline int float_compactlong_guard(PyObject *lhs, PyObject *rhs) { return ( PyFloat_CheckExact(lhs) && + !isnan(PyFloat_AsDouble(lhs)) && PyLong_CheckExact(rhs) && _PyLong_IsCompact((PyLongObject *)rhs) ); } +static inline int +nonzero_float_compactlong_guard(PyObject *lhs, PyObject *rhs) +{ + return ( + float_compactlong_guard(lhs, rhs) && !PyLong_IsZero(rhs) + ); +} + #define FLOAT_LONG_ACTION(NAME, OP) \ static PyObject * \ (NAME)(PyObject *lhs, PyObject *rhs) \ @@ -2442,13 +2451,22 @@ FLOAT_LONG_ACTION(float_compactlong_true_div, /) /* long-float */ -static int +static inline int compactlong_float_guard(PyObject *lhs, PyObject *rhs) { return ( - PyFloat_CheckExact(rhs) && PyLong_CheckExact(lhs) && - _PyLong_IsCompact((PyLongObject *)lhs) + _PyLong_IsCompact((PyLongObject *)lhs) && + PyFloat_CheckExact(rhs) && + !isnan(PyFloat_AsDouble(rhs)) + ); +} + +static inline int +nonzero_compactlong_float_guard(PyObject *lhs, PyObject *rhs) +{ + return ( + compactlong_float_guard(lhs, rhs) && PyFloat_AsDouble(rhs) != 0.0 ); } @@ -2469,14 +2487,14 @@ LONG_FLOAT_ACTION(compactlong_float_true_div, /) static _PyBinaryOpSpecializationDescr float_compactlong_specs[NB_OPARG_LAST+1] = { [NB_ADD] = {float_compactlong_guard, float_compactlong_add}, [NB_SUBTRACT] = {float_compactlong_guard, float_compactlong_subtract}, - [NB_TRUE_DIVIDE] = {float_compactlong_guard, float_compactlong_true_div}, + [NB_TRUE_DIVIDE] = {nonzero_float_compactlong_guard, float_compactlong_true_div}, [NB_MULTIPLY] = {float_compactlong_guard, float_compactlong_multiply}, }; static _PyBinaryOpSpecializationDescr compactlong_float_specs[NB_OPARG_LAST+1] = { [NB_ADD] = {compactlong_float_guard, compactlong_float_add}, [NB_SUBTRACT] = {compactlong_float_guard, compactlong_float_subtract}, - [NB_TRUE_DIVIDE] = {compactlong_float_guard, compactlong_float_true_div}, + [NB_TRUE_DIVIDE] = {nonzero_compactlong_float_guard, compactlong_float_true_div}, [NB_MULTIPLY] = {compactlong_float_guard, compactlong_float_multiply}, }; From d57b2d83dd470c5d3a391f4388e4bb41c9843fad Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 19 Jan 2025 20:51:17 +0100 Subject: [PATCH 363/376] gh-77782: Postpone Py_VerboseFlag removal to Python 3.15 (#129024) Postpone the global configuration variables removal to Python 3.15. Do the same for PySys_SetArgvEx(), PySys_SetArgv(), Py_SetProgramName() and Py_SetPythonHome(). --- .../c-api-pending-removal-in-3.14.rst | 64 -------------- .../c-api-pending-removal-in-3.15.rst | 83 +++++++++++++++++++ 2 files changed, 83 insertions(+), 64 deletions(-) diff --git a/Doc/deprecations/c-api-pending-removal-in-3.14.rst b/Doc/deprecations/c-api-pending-removal-in-3.14.rst index 9e10bf2691e5c8..c805074669811a 100644 --- a/Doc/deprecations/c-api-pending-removal-in-3.14.rst +++ b/Doc/deprecations/c-api-pending-removal-in-3.14.rst @@ -6,67 +6,3 @@ Pending removal in Python 3.14 * Creating :c:data:`immutable types ` with mutable bases (:gh:`95388`). - -* Functions to configure Python's initialization, deprecated in Python 3.11: - - * :c:func:`!PySys_SetArgvEx()`: - Set :c:member:`PyConfig.argv` instead. - * :c:func:`!PySys_SetArgv()`: - Set :c:member:`PyConfig.argv` instead. - * :c:func:`!Py_SetProgramName()`: - Set :c:member:`PyConfig.program_name` instead. - * :c:func:`!Py_SetPythonHome()`: - Set :c:member:`PyConfig.home` instead. - - The :c:func:`Py_InitializeFromConfig` API should be used with - :c:type:`PyConfig` instead. - -* Global configuration variables: - - * :c:var:`Py_DebugFlag`: - Use :c:member:`PyConfig.parser_debug` instead. - * :c:var:`Py_VerboseFlag`: - Use :c:member:`PyConfig.verbose` instead. - * :c:var:`Py_QuietFlag`: - Use :c:member:`PyConfig.quiet` instead. - * :c:var:`Py_InteractiveFlag`: - Use :c:member:`PyConfig.interactive` instead. - * :c:var:`Py_InspectFlag`: - Use :c:member:`PyConfig.inspect` instead. - * :c:var:`Py_OptimizeFlag`: - Use :c:member:`PyConfig.optimization_level` instead. - * :c:var:`Py_NoSiteFlag`: - Use :c:member:`PyConfig.site_import` instead. - * :c:var:`Py_BytesWarningFlag`: - Use :c:member:`PyConfig.bytes_warning` instead. - * :c:var:`Py_FrozenFlag`: - Use :c:member:`PyConfig.pathconfig_warnings` instead. - * :c:var:`Py_IgnoreEnvironmentFlag`: - Use :c:member:`PyConfig.use_environment` instead. - * :c:var:`Py_DontWriteBytecodeFlag`: - Use :c:member:`PyConfig.write_bytecode` instead. - * :c:var:`Py_NoUserSiteDirectory`: - Use :c:member:`PyConfig.user_site_directory` instead. - * :c:var:`Py_UnbufferedStdioFlag`: - Use :c:member:`PyConfig.buffered_stdio` instead. - * :c:var:`Py_HashRandomizationFlag`: - Use :c:member:`PyConfig.use_hash_seed` - and :c:member:`PyConfig.hash_seed` instead. - * :c:var:`Py_IsolatedFlag`: - Use :c:member:`PyConfig.isolated` instead. - * :c:var:`Py_LegacyWindowsFSEncodingFlag`: - Use :c:member:`PyPreConfig.legacy_windows_fs_encoding` instead. - * :c:var:`Py_LegacyWindowsStdioFlag`: - Use :c:member:`PyConfig.legacy_windows_stdio` instead. - * :c:var:`!Py_FileSystemDefaultEncoding`: - Use :c:member:`PyConfig.filesystem_encoding` instead. - * :c:var:`!Py_HasFileSystemDefaultEncoding`: - Use :c:member:`PyConfig.filesystem_encoding` instead. - * :c:var:`!Py_FileSystemDefaultEncodeErrors`: - Use :c:member:`PyConfig.filesystem_errors` instead. - * :c:var:`!Py_UTF8Mode`: - Use :c:member:`PyPreConfig.utf8_mode` instead. - (see :c:func:`Py_PreInitialize`) - - The :c:func:`Py_InitializeFromConfig` API should be used with - :c:type:`PyConfig` instead. diff --git a/Doc/deprecations/c-api-pending-removal-in-3.15.rst b/Doc/deprecations/c-api-pending-removal-in-3.15.rst index 0ce0f9c118c094..ba908f81b7f800 100644 --- a/Doc/deprecations/c-api-pending-removal-in-3.15.rst +++ b/Doc/deprecations/c-api-pending-removal-in-3.15.rst @@ -25,3 +25,86 @@ Pending removal in Python 3.15 * :c:func:`Py_GetPythonHome`: Get :c:member:`PyConfig.home` or the :envvar:`PYTHONHOME` environment variable instead. + +* Functions to configure Python's initialization, deprecated in Python 3.11: + + * :c:func:`!PySys_SetArgvEx()`: + Set :c:member:`PyConfig.argv` instead. + * :c:func:`!PySys_SetArgv()`: + Set :c:member:`PyConfig.argv` instead. + * :c:func:`!Py_SetProgramName()`: + Set :c:member:`PyConfig.program_name` instead. + * :c:func:`!Py_SetPythonHome()`: + Set :c:member:`PyConfig.home` instead. + + The :c:func:`Py_InitializeFromConfig` API should be used with + :c:type:`PyConfig` instead. + +* Global configuration variables: + + * :c:var:`Py_DebugFlag`: + Use :c:member:`PyConfig.parser_debug` or + :c:func:`PyConfig_Get("parser_debug") ` instead. + * :c:var:`Py_VerboseFlag`: + Use :c:member:`PyConfig.verbose` or + :c:func:`PyConfig_Get("verbose") ` instead. + * :c:var:`Py_QuietFlag`: + Use :c:member:`PyConfig.quiet` or + :c:func:`PyConfig_Get("quiet") ` instead. + * :c:var:`Py_InteractiveFlag`: + Use :c:member:`PyConfig.interactive` or + :c:func:`PyConfig_Get("interactive") ` instead. + * :c:var:`Py_InspectFlag`: + Use :c:member:`PyConfig.inspect` or + :c:func:`PyConfig_Get("inspect") ` instead. + * :c:var:`Py_OptimizeFlag`: + Use :c:member:`PyConfig.optimization_level` or + :c:func:`PyConfig_Get("optimization_level") ` instead. + * :c:var:`Py_NoSiteFlag`: + Use :c:member:`PyConfig.site_import` or + :c:func:`PyConfig_Get("site_import") ` instead. + * :c:var:`Py_BytesWarningFlag`: + Use :c:member:`PyConfig.bytes_warning` or + :c:func:`PyConfig_Get("bytes_warning") ` instead. + * :c:var:`Py_FrozenFlag`: + Use :c:member:`PyConfig.pathconfig_warnings` or + :c:func:`PyConfig_Get("pathconfig_warnings") ` instead. + * :c:var:`Py_IgnoreEnvironmentFlag`: + Use :c:member:`PyConfig.use_environment` or + :c:func:`PyConfig_Get("use_environment") ` instead. + * :c:var:`Py_DontWriteBytecodeFlag`: + Use :c:member:`PyConfig.write_bytecode` or + :c:func:`PyConfig_Get("write_bytecode") ` instead. + * :c:var:`Py_NoUserSiteDirectory`: + Use :c:member:`PyConfig.user_site_directory` or + :c:func:`PyConfig_Get("user_site_directory") ` instead. + * :c:var:`Py_UnbufferedStdioFlag`: + Use :c:member:`PyConfig.buffered_stdio` or + :c:func:`PyConfig_Get("buffered_stdio") ` instead. + * :c:var:`Py_HashRandomizationFlag`: + Use :c:member:`PyConfig.use_hash_seed` + and :c:member:`PyConfig.hash_seed` or + :c:func:`PyConfig_Get("hash_seed") ` instead. + * :c:var:`Py_IsolatedFlag`: + Use :c:member:`PyConfig.isolated` or + :c:func:`PyConfig_Get("isolated") ` instead. + * :c:var:`Py_LegacyWindowsFSEncodingFlag`: + Use :c:member:`PyPreConfig.legacy_windows_fs_encoding` or + :c:func:`PyConfig_Get("legacy_windows_fs_encoding") ` instead. + * :c:var:`Py_LegacyWindowsStdioFlag`: + Use :c:member:`PyConfig.legacy_windows_stdio` or + :c:func:`PyConfig_Get("legacy_windows_stdio") ` instead. + * :c:var:`!Py_FileSystemDefaultEncoding`, :c:var:`!Py_HasFileSystemDefaultEncoding`: + Use :c:member:`PyConfig.filesystem_encoding` or + :c:func:`PyConfig_Get("filesystem_encoding") ` instead. + * :c:var:`!Py_FileSystemDefaultEncodeErrors`: + Use :c:member:`PyConfig.filesystem_errors` or + :c:func:`PyConfig_Get("filesystem_errors") ` instead. + * :c:var:`!Py_UTF8Mode`: + Use :c:member:`PyPreConfig.utf8_mode` or + :c:func:`PyConfig_Get("utf8_mode") ` instead. + (see :c:func:`Py_PreInitialize`) + + The :c:func:`Py_InitializeFromConfig` API should be used with + :c:type:`PyConfig` to set these options. Or :c:func:`PyConfig_Get` can be + used to get these options at runtime. From bbeb219354764aef85e660be6570f0f329e7227e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 19 Jan 2025 22:47:05 +0100 Subject: [PATCH 364/376] Complete doc of pending C API removals in Python 3.15 (#129032) --- Doc/c-api/init.rst | 40 +++++++++---------- .../c-api-pending-removal-in-3.15.rst | 8 +++- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 97996a6f69dd22..dc44f3eaf87765 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -109,7 +109,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-b` option. - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 .. c:var:: int Py_DebugFlag @@ -123,7 +123,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-d` option and the :envvar:`PYTHONDEBUG` environment variable. - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 .. c:var:: int Py_DontWriteBytecodeFlag @@ -137,7 +137,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-B` option and the :envvar:`PYTHONDONTWRITEBYTECODE` environment variable. - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 .. c:var:: int Py_FrozenFlag @@ -150,7 +150,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Private flag used by ``_freeze_module`` and ``frozenmain`` programs. - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 .. c:var:: int Py_HashRandomizationFlag @@ -165,7 +165,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. If the flag is non-zero, read the :envvar:`PYTHONHASHSEED` environment variable to initialize the secret hash seed. - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 .. c:var:: int Py_IgnoreEnvironmentFlag @@ -178,7 +178,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-E` and :option:`-I` options. - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 .. c:var:: int Py_InspectFlag @@ -193,7 +193,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-i` option and the :envvar:`PYTHONINSPECT` environment variable. - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 .. c:var:: int Py_InteractiveFlag @@ -218,7 +218,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. .. versionadded:: 3.4 - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 .. c:var:: int Py_LegacyWindowsFSEncodingFlag @@ -237,7 +237,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. .. availability:: Windows. - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 .. c:var:: int Py_LegacyWindowsStdioFlag @@ -255,7 +255,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. .. availability:: Windows. - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 .. c:var:: int Py_NoSiteFlag @@ -270,7 +270,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-S` option. - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 .. c:var:: int Py_NoUserSiteDirectory @@ -284,7 +284,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-s` and :option:`-I` options, and the :envvar:`PYTHONNOUSERSITE` environment variable. - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 .. c:var:: int Py_OptimizeFlag @@ -295,7 +295,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-O` option and the :envvar:`PYTHONOPTIMIZE` environment variable. - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 .. c:var:: int Py_QuietFlag @@ -309,7 +309,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. .. versionadded:: 3.2 - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 .. c:var:: int Py_UnbufferedStdioFlag @@ -322,7 +322,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-u` option and the :envvar:`PYTHONUNBUFFERED` environment variable. - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 .. c:var:: int Py_VerboseFlag @@ -338,7 +338,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. Set by the :option:`-v` option and the :envvar:`PYTHONVERBOSE` environment variable. - .. deprecated-removed:: 3.12 3.14 + .. deprecated-removed:: 3.12 3.15 Initializing and finalizing the interpreter @@ -606,7 +606,7 @@ Process-wide parameters Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a :c:expr:`wchar_*` string. - .. deprecated:: 3.11 + .. deprecated-removed:: 3.11 3.15 .. c:function:: wchar_t* Py_GetProgramName() @@ -868,7 +868,7 @@ Process-wide parameters .. XXX impl. doesn't seem consistent in allowing ``0``/``NULL`` for the params; check w/ Guido. - .. deprecated:: 3.11 + .. deprecated-removed:: 3.11 3.15 .. c:function:: void PySys_SetArgv(int argc, wchar_t **argv) @@ -889,7 +889,7 @@ Process-wide parameters .. versionchanged:: 3.4 The *updatepath* value depends on :option:`-I`. - .. deprecated:: 3.11 + .. deprecated-removed:: 3.11 3.15 .. c:function:: void Py_SetPythonHome(const wchar_t *home) @@ -910,7 +910,7 @@ Process-wide parameters Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a :c:expr:`wchar_*` string. - .. deprecated:: 3.11 + .. deprecated-removed:: 3.11 3.15 .. c:function:: wchar_t* Py_GetPythonHome() diff --git a/Doc/deprecations/c-api-pending-removal-in-3.15.rst b/Doc/deprecations/c-api-pending-removal-in-3.15.rst index ba908f81b7f800..ac31b3cc8cd451 100644 --- a/Doc/deprecations/c-api-pending-removal-in-3.15.rst +++ b/Doc/deprecations/c-api-pending-removal-in-3.15.rst @@ -5,7 +5,9 @@ Pending removal in Python 3.15 * The :c:func:`PyImport_ImportModuleNoBlock`: Use :c:func:`PyImport_ImportModule` instead. * :c:func:`PyWeakref_GetObject` and :c:func:`PyWeakref_GET_OBJECT`: - Use :c:func:`PyWeakref_GetRef` instead. + Use :c:func:`PyWeakref_GetRef` instead. The `pythoncapi-compat project + `__ can be used to get + :c:func:`PyWeakref_GetRef` on Python 3.12 and older. * :c:type:`Py_UNICODE` type and the :c:macro:`!Py_UNICODE_WIDE` macro: Use :c:type:`wchar_t` instead. * Python initialization functions: @@ -23,9 +25,11 @@ Pending removal in Python 3.15 * :c:func:`Py_GetProgramName`: Get :data:`sys.executable` instead. * :c:func:`Py_GetPythonHome`: - Get :c:member:`PyConfig.home` + Get :c:func:`PyConfig_Get("home") ` or the :envvar:`PYTHONHOME` environment variable instead. + See also the :c:func:`PyConfig_Get` function. + * Functions to configure Python's initialization, deprecated in Python 3.11: * :c:func:`!PySys_SetArgvEx()`: From c9c9fcb8fcc3ef43e1d8bd71ae0ed3d4231a6013 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Sun, 19 Jan 2025 19:01:20 -0500 Subject: [PATCH 365/376] gh-118761: Improve the import time of ``gettext`` (#128898) ``gettext`` is often imported in programs that may not end up translating anything. In fact, the ``struct`` module already has a delayed import when parsing ``GNUTranslations`` to speed up the no ``.mo`` files case. The re module is also used in the same situation, but behind a function chain only called by ``GNUTranslations``. Cache the compiled regex globally the first time it is used. The finditer function is converted to a method call on the compiled object which is slightly more efficient, and necessary for the delayed re import. --- Lib/gettext.py | 33 ++++++++++--------- ...-01-15-19-16-50.gh-issue-118761.cbW2ZL.rst | 3 ++ 2 files changed, 21 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-15-19-16-50.gh-issue-118761.cbW2ZL.rst diff --git a/Lib/gettext.py b/Lib/gettext.py index a0d81cf846a05c..4c1f9427459b14 100644 --- a/Lib/gettext.py +++ b/Lib/gettext.py @@ -48,7 +48,6 @@ import operator import os -import re import sys @@ -70,22 +69,26 @@ # https://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms # http://git.savannah.gnu.org/cgit/gettext.git/tree/gettext-runtime/intl/plural.y -_token_pattern = re.compile(r""" - (?P[ \t]+) | # spaces and horizontal tabs - (?P[0-9]+\b) | # decimal integer - (?Pn\b) | # only n is allowed - (?P[()]) | - (?P[-*/%+?:]|[>, - # <=, >=, ==, !=, &&, ||, - # ? : - # unary and bitwise ops - # not allowed - (?P\w+|.) # invalid token - """, re.VERBOSE|re.DOTALL) - +_token_pattern = None def _tokenize(plural): - for mo in re.finditer(_token_pattern, plural): + global _token_pattern + if _token_pattern is None: + import re + _token_pattern = re.compile(r""" + (?P[ \t]+) | # spaces and horizontal tabs + (?P[0-9]+\b) | # decimal integer + (?Pn\b) | # only n is allowed + (?P[()]) | + (?P[-*/%+?:]|[>, + # <=, >=, ==, !=, &&, ||, + # ? : + # unary and bitwise ops + # not allowed + (?P\w+|.) # invalid token + """, re.VERBOSE|re.DOTALL) + + for mo in _token_pattern.finditer(plural): kind = mo.lastgroup if kind == 'WHITESPACES': continue diff --git a/Misc/NEWS.d/next/Library/2025-01-15-19-16-50.gh-issue-118761.cbW2ZL.rst b/Misc/NEWS.d/next/Library/2025-01-15-19-16-50.gh-issue-118761.cbW2ZL.rst new file mode 100644 index 00000000000000..0eef8777512dd8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-15-19-16-50.gh-issue-118761.cbW2ZL.rst @@ -0,0 +1,3 @@ +Reduce import time of :mod:`gettext` by up to ten times, by importing +:mod:`re` on demand. In particular, ``re`` is no longer implicitly +exposed as ``gettext.re``. Patch by Eli Schwartz. From 3b6a27c5607d2b199f127c2a5ef5316bbc30ae42 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Sun, 19 Jan 2025 19:03:19 -0500 Subject: [PATCH 366/376] gh-118761: Redudce the import time of ``optparse`` (#128899) The same change was made, and for the same reason, by ``argparse`` back in 2017. The ``textwrap`` module is only used when printing help text, so most invocations will never need it imported. Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Lib/optparse.py | 3 ++- .../Library/2025-01-15-18-54-48.gh-issue-118761.G1dv6E.rst | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-15-18-54-48.gh-issue-118761.G1dv6E.rst diff --git a/Lib/optparse.py b/Lib/optparse.py index cbe3451ced8bc3..38cf16d21efffa 100644 --- a/Lib/optparse.py +++ b/Lib/optparse.py @@ -74,7 +74,6 @@ """ import sys, os -import textwrap from gettext import gettext as _, ngettext @@ -252,6 +251,7 @@ def _format_text(self, text): Format a paragraph of free-form text for inclusion in the help output at the current indentation level. """ + import textwrap text_width = max(self.width - self.current_indent, 11) indent = " "*self.current_indent return textwrap.fill(text, @@ -308,6 +308,7 @@ def format_option(self, option): indent_first = 0 result.append(opts) if option.help: + import textwrap help_text = self.expand_default(option) help_lines = textwrap.wrap(help_text, self.help_width) result.append("%*s%s\n" % (indent_first, "", help_lines[0])) diff --git a/Misc/NEWS.d/next/Library/2025-01-15-18-54-48.gh-issue-118761.G1dv6E.rst b/Misc/NEWS.d/next/Library/2025-01-15-18-54-48.gh-issue-118761.G1dv6E.rst new file mode 100644 index 00000000000000..4144ef8f40e6dd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-15-18-54-48.gh-issue-118761.G1dv6E.rst @@ -0,0 +1,2 @@ +Reduce the import time of :mod:`optparse` when no help text is printed. +Patch by Eli Schwartz. From bca35f0e782848ae2acdcfbfb000cd4a2af49fbd Mon Sep 17 00:00:00 2001 From: "Tomas R." Date: Mon, 20 Jan 2025 01:05:22 +0100 Subject: [PATCH 367/376] gh-129020: Remove ambiguous sentence from `tokenize.untokenize` docs (#129021) --- Doc/library/tokenize.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Doc/library/tokenize.rst b/Doc/library/tokenize.rst index f719319a302a23..b80917eae66f8b 100644 --- a/Doc/library/tokenize.rst +++ b/Doc/library/tokenize.rst @@ -91,11 +91,10 @@ write back the modified script. sequences with at least two elements, the token type and the token string. Any additional sequence elements are ignored. - The reconstructed script is returned as a single string. The result is - guaranteed to tokenize back to match the input so that the conversion is - lossless and round-trips are assured. The guarantee applies only to the - token type and token string as the spacing between tokens (column - positions) may change. + The result is guaranteed to tokenize back to match the input so that the + conversion is lossless and round-trips are assured. The guarantee applies + only to the token type and token string as the spacing between tokens + (column positions) may change. It returns bytes, encoded using the :data:`~token.ENCODING` token, which is the first token sequence output by :func:`.tokenize`. If there is no From d46b577ec026c2e700a9f920f81cfbf698e53eb6 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Mon, 20 Jan 2025 00:26:24 +0000 Subject: [PATCH 368/376] GH-125722: Increase minimum supported Sphinx to 8.1.3 (#128922) --- .github/workflows/reusable-docs.yml | 20 ----------- Doc/conf.py | 24 ++----------- Doc/constraints.txt | 14 ++++---- Doc/requirements-oldest-sphinx.txt | 35 ------------------- Doc/requirements.txt | 3 +- Doc/tools/extensions/c_annotations.py | 11 ------ ...-01-16-18-59-11.gh-issue-125722.eHHRga.rst | 2 ++ 7 files changed, 13 insertions(+), 96 deletions(-) delete mode 100644 Doc/requirements-oldest-sphinx.txt create mode 100644 Misc/NEWS.d/next/Documentation/2025-01-16-18-59-11.gh-issue-125722.eHHRga.rst diff --git a/.github/workflows/reusable-docs.yml b/.github/workflows/reusable-docs.yml index 88da55bf08b8fe..84675fa62e1516 100644 --- a/.github/workflows/reusable-docs.yml +++ b/.github/workflows/reusable-docs.yml @@ -76,26 +76,6 @@ jobs: --fail-if-improved \ --fail-if-new-news-nit - # This build doesn't use problem matchers or check annotations - build_doc_oldest_supported_sphinx: - name: 'Docs (Oldest Sphinx)' - runs-on: ubuntu-latest - timeout-minutes: 60 - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - name: 'Set up Python' - uses: actions/setup-python@v5 - with: - python-version: '3.13' # known to work with Sphinx 7.2.6 - cache: 'pip' - cache-dependency-path: 'Doc/requirements-oldest-sphinx.txt' - - name: 'Install build dependencies' - run: make -C Doc/ venv REQUIREMENTS="requirements-oldest-sphinx.txt" - - name: 'Build HTML documentation' - run: make -C Doc/ SPHINXOPTS="-q" SPHINXERRORHANDLING="-W --keep-going" html - # Run "doctest" on HEAD as new syntax doesn't exist in the latest stable release doctest: name: 'Doctest' diff --git a/Doc/conf.py b/Doc/conf.py index ae08c7fa288080..b4a924b7bf0857 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -9,9 +9,6 @@ import importlib import os import sys -import time - -import sphinx # Make our custom extensions available to Sphinx sys.path.append(os.path.abspath('tools/extensions')) @@ -97,7 +94,8 @@ highlight_language = 'python3' # Minimum version of sphinx required -needs_sphinx = '7.2.6' +# Keep this version in sync with ``Doc/requirements.txt``. +needs_sphinx = '8.1.3' # Create table of contents entries for domain objects (e.g. functions, classes, # attributes, etc.). Default is True. @@ -376,13 +374,7 @@ # This 'Last updated on:' timestamp is inserted at the bottom of every page. html_last_updated_fmt = '%b %d, %Y (%H:%M UTC)' -if sphinx.version_info[:2] >= (8, 1): - html_last_updated_use_utc = True -else: - html_time = int(os.environ.get('SOURCE_DATE_EPOCH', time.time())) - html_last_updated_fmt = time.strftime( - html_last_updated_fmt, time.gmtime(html_time) - ) +html_last_updated_use_utc = True # Path to find HTML templates to override theme templates_path = ['tools/templates'] @@ -619,16 +611,6 @@ } extlinks_detect_hardcoded_links = True -if sphinx.version_info[:2] < (8, 1): - # Sphinx 8.1 has in-built CVE and CWE roles. - extlinks |= { - "cve": ( - "https://www.cve.org/CVERecord?id=CVE-%s", - "CVE-%s", - ), - "cwe": ("https://cwe.mitre.org/data/definitions/%s.html", "CWE-%s"), - } - # Options for c_annotations extension # ----------------------------------- diff --git a/Doc/constraints.txt b/Doc/constraints.txt index 26ac1862dbac0b..29cd4be1d3c8db 100644 --- a/Doc/constraints.txt +++ b/Doc/constraints.txt @@ -13,14 +13,12 @@ packaging<25 Pygments<3 requests<3 snowballstemmer<3 -# keep lower-bounds until Sphinx 8.1 is released -# https://github.com/sphinx-doc/sphinx/pull/12756 -sphinxcontrib-applehelp>=1.0.7,<3 -sphinxcontrib-devhelp>=1.0.6,<3 -sphinxcontrib-htmlhelp>=2.0.6,<3 -sphinxcontrib-jsmath>=1.0.1,<2 -sphinxcontrib-qthelp>=1.0.6,<3 -sphinxcontrib-serializinghtml>=1.1.9,<3 +sphinxcontrib-applehelp<3 +sphinxcontrib-devhelp<3 +sphinxcontrib-htmlhelp<3 +sphinxcontrib-jsmath<2 +sphinxcontrib-qthelp<3 +sphinxcontrib-serializinghtml<3 # Direct dependencies of Jinja2 (Jinja is a dependency of Sphinx, see above) MarkupSafe<3 diff --git a/Doc/requirements-oldest-sphinx.txt b/Doc/requirements-oldest-sphinx.txt deleted file mode 100644 index c8027a05706c21..00000000000000 --- a/Doc/requirements-oldest-sphinx.txt +++ /dev/null @@ -1,35 +0,0 @@ -# Requirements to build the Python documentation, for the oldest supported -# Sphinx version. -# -# We pin Sphinx and all of its dependencies to ensure a consistent environment. - -blurb -python-docs-theme>=2022.1 - -# Generated from: -# pip install "Sphinx~=7.2.6" -# pip freeze -# -# Sphinx 7.2.6 comes from ``needs_sphinx = '7.2.6'`` in ``Doc/conf.py``. - -alabaster==0.7.16 -babel==2.16.0 -certifi==2024.12.14 -charset-normalizer==3.4.0 -docutils==0.20.1 -idna==3.10 -imagesize==1.4.1 -Jinja2==3.1.5 -MarkupSafe==3.0.2 -packaging==24.2 -Pygments==2.18.0 -requests==2.32.3 -snowballstemmer==2.2.0 -Sphinx==7.2.6 -sphinxcontrib-applehelp==2.0.0 -sphinxcontrib-devhelp==2.0.0 -sphinxcontrib-htmlhelp==2.1.0 -sphinxcontrib-jsmath==1.0.1 -sphinxcontrib-qthelp==2.0.0 -sphinxcontrib-serializinghtml==2.0.0 -urllib3==2.3.0 diff --git a/Doc/requirements.txt b/Doc/requirements.txt index 5105786ccf283c..32ff8f74d05bb6 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -3,9 +3,10 @@ # Note that when updating this file, you will likely also have to update # the Doc/constraints.txt file. -# Sphinx version is pinned so that new versions that introduce new warnings +# The Sphinx version is pinned so that new versions that introduce new warnings # won't suddenly cause build failures. Updating the version is fine as long # as no warnings are raised by doing so. +# Keep this version in sync with ``Doc/conf.py``. sphinx~=8.1.0 blurb diff --git a/Doc/tools/extensions/c_annotations.py b/Doc/tools/extensions/c_annotations.py index 50065d34a2c27a..089614a1f6c421 100644 --- a/Doc/tools/extensions/c_annotations.py +++ b/Doc/tools/extensions/c_annotations.py @@ -16,7 +16,6 @@ from pathlib import Path from typing import TYPE_CHECKING -import sphinx from docutils import nodes from docutils.statemachine import StringList from sphinx import addnodes @@ -285,16 +284,6 @@ def setup(app: Sphinx) -> ExtensionMetadata: app.connect("builder-inited", init_annotations) app.connect("doctree-read", add_annotations) - if sphinx.version_info[:2] < (7, 2): - from docutils.parsers.rst import directives - from sphinx.domains.c import CObject - - # monkey-patch C object... - CObject.option_spec |= { - "no-index-entry": directives.flag, - "no-contents-entry": directives.flag, - } - return { "version": "1.0", "parallel_read_safe": True, diff --git a/Misc/NEWS.d/next/Documentation/2025-01-16-18-59-11.gh-issue-125722.eHHRga.rst b/Misc/NEWS.d/next/Documentation/2025-01-16-18-59-11.gh-issue-125722.eHHRga.rst new file mode 100644 index 00000000000000..bf6253eed2eb90 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2025-01-16-18-59-11.gh-issue-125722.eHHRga.rst @@ -0,0 +1,2 @@ +Require Sphinx 8.1.3 or later to build the Python documentation. Patch by +Adam Turner. From 4967fa6a9c0db7ea3ade905d724a04688cfc7a5e Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Mon, 20 Jan 2025 06:11:22 +0000 Subject: [PATCH 369/376] GH-125722: Use long options for Sphinx (#129039) --- .github/workflows/reusable-docs.yml | 6 +++--- Doc/Makefile | 22 +++++++++++----------- Doc/make.bat | 6 +++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/reusable-docs.yml b/.github/workflows/reusable-docs.yml index 84675fa62e1516..6738acc98c6565 100644 --- a/.github/workflows/reusable-docs.yml +++ b/.github/workflows/reusable-docs.yml @@ -65,8 +65,8 @@ jobs: continue-on-error: true run: | set -Eeuo pipefail - # Build docs with the '-n' (nit-picky) option; write warnings to file - make -C Doc/ PYTHON=../python SPHINXOPTS="-q -n -W --keep-going -w sphinx-warnings.txt" html + # Build docs with the nit-picky option; write warnings to file + make -C Doc/ PYTHON=../python SPHINXOPTS="--quiet --nitpicky --fail-on-warning --keep-going --warning-file sphinx-warnings.txt" html - name: 'Check warnings' if: github.event_name == 'pull_request' run: | @@ -101,4 +101,4 @@ jobs: run: make -C Doc/ PYTHON=../python venv # Use "xvfb-run" since some doctest tests open GUI windows - name: 'Run documentation doctest' - run: xvfb-run make -C Doc/ PYTHON=../python SPHINXERRORHANDLING="-W --keep-going" doctest + run: xvfb-run make -C Doc/ PYTHON=../python SPHINXERRORHANDLING="--fail-on-warning --keep-going" doctest diff --git a/Doc/Makefile b/Doc/Makefile index 4a704ad58b33d3..1a66642a4a03ed 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -14,15 +14,15 @@ PAPER = SOURCES = DISTVERSION = $(shell $(PYTHON) tools/extensions/patchlevel.py) REQUIREMENTS = requirements.txt -SPHINXERRORHANDLING = -W +SPHINXERRORHANDLING = --fail-on-warning # Internal variables. -PAPEROPT_a4 = -D latex_elements.papersize=a4paper -PAPEROPT_letter = -D latex_elements.papersize=letterpaper +PAPEROPT_a4 = --define latex_elements.papersize=a4paper +PAPEROPT_letter = --define latex_elements.papersize=letterpaper -ALLSPHINXOPTS = -b $(BUILDER) \ - -d build/doctrees \ - -j $(JOBS) \ +ALLSPHINXOPTS = --builder $(BUILDER) \ + --doctree-dir build/doctrees \ + --jobs $(JOBS) \ $(PAPEROPT_$(PAPER)) \ $(SPHINXOPTS) $(SPHINXERRORHANDLING) \ . build/$(BUILDER) $(SOURCES) @@ -144,7 +144,7 @@ pydoc-topics: build .PHONY: gettext gettext: BUILDER = gettext -gettext: override SPHINXOPTS := -d build/doctrees-gettext $(SPHINXOPTS) +gettext: override SPHINXOPTS := --doctree-dir build/doctrees-gettext $(SPHINXOPTS) gettext: build .PHONY: htmlview @@ -300,20 +300,20 @@ serve: # By default, Sphinx only rebuilds pages where the page content has changed. # This means it doesn't always pick up changes to preferred link targets, etc # To ensure such changes are picked up, we build the published docs with -# `-E` (to ignore the cached environment) and `-a` (to ignore already existing -# output files) +# ``--fresh-env`` (to ignore the cached environment) and ``--write-all`` +# (to ignore already existing output files) # for development releases: always build .PHONY: autobuild-dev autobuild-dev: DISTVERSION = $(shell $(PYTHON) tools/extensions/patchlevel.py --short) autobuild-dev: - $(MAKE) dist-no-html SPHINXOPTS='$(SPHINXOPTS) -Ea -A daily=1' DISTVERSION=$(DISTVERSION) + $(MAKE) dist-no-html SPHINXOPTS='$(SPHINXOPTS) --fresh-env --write-all --html-define daily=1' DISTVERSION=$(DISTVERSION) # for HTML-only rebuilds .PHONY: autobuild-dev-html autobuild-dev-html: DISTVERSION = $(shell $(PYTHON) tools/extensions/patchlevel.py --short) autobuild-dev-html: - $(MAKE) dist-html SPHINXOPTS='$(SPHINXOPTS) -Ea -A daily=1' DISTVERSION=$(DISTVERSION) + $(MAKE) dist-html SPHINXOPTS='$(SPHINXOPTS) --fresh-env --write-all --html-define daily=1' DISTVERSION=$(DISTVERSION) # for stable releases: only build if not in pre-release stage (alpha, beta) # release candidate downloads are okay, since the stable tree can be in that stage diff --git a/Doc/make.bat b/Doc/make.bat index 87d8359ef112bb..ede793ed3c6d70 100644 --- a/Doc/make.bat +++ b/Doc/make.bat @@ -144,12 +144,12 @@ if exist ..\Misc\NEWS ( ) if defined PAPER ( - set SPHINXOPTS=-D latex_elements.papersize=%PAPER% %SPHINXOPTS% + set SPHINXOPTS=--define latex_elements.papersize=%PAPER% %SPHINXOPTS% ) if "%1" EQU "htmlhelp" ( - set SPHINXOPTS=-D html_theme_options.body_max_width=none %SPHINXOPTS% + set SPHINXOPTS=--define html_theme_options.body_max_width=none %SPHINXOPTS% ) -cmd /S /C "%SPHINXBUILD% %SPHINXOPTS% -b%1 -dbuild\doctrees . "%BUILDDIR%\%1" %2 %3 %4 %5 %6 %7 %8 %9" +cmd /S /C "%SPHINXBUILD% %SPHINXOPTS% --builder %1 --doctree-dir build\doctrees . "%BUILDDIR%\%1" %2 %3 %4 %5 %6 %7 %8 %9" if "%1" EQU "htmlhelp" ( "%HTMLHELP%" "%BUILDDIR%\htmlhelp\python%DISTVERSION:.=%.hhp" From 887f2bcf48eaa13363f9276fdaa9e6b22deaeb65 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 20 Jan 2025 11:16:32 +0200 Subject: [PATCH 370/376] gh-71339: Use assertIsSubclass() and assertNotIsSubclass() in test_collections (GH-128824) --- Lib/test/test_collections.py | 211 +++++++++++++++++------------------ 1 file changed, 105 insertions(+), 106 deletions(-) diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index a24d3e3ea142b7..1e93530398be79 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -742,11 +742,11 @@ def validate_isinstance(self, abc, name): C = type('C', (object,), {'__hash__': None}) setattr(C, name, stub) self.assertIsInstance(C(), abc) - self.assertTrue(issubclass(C, abc)) + self.assertIsSubclass(C, abc) C = type('C', (object,), {'__hash__': None}) self.assertNotIsInstance(C(), abc) - self.assertFalse(issubclass(C, abc)) + self.assertNotIsSubclass(C, abc) def validate_comparison(self, instance): ops = ['lt', 'gt', 'le', 'ge', 'ne', 'or', 'and', 'xor', 'sub'] @@ -812,12 +812,12 @@ def __await__(self): non_samples = [None, int(), gen(), object()] for x in non_samples: self.assertNotIsInstance(x, Awaitable) - self.assertFalse(issubclass(type(x), Awaitable), repr(type(x))) + self.assertNotIsSubclass(type(x), Awaitable) samples = [Bar(), MinimalCoro()] for x in samples: self.assertIsInstance(x, Awaitable) - self.assertTrue(issubclass(type(x), Awaitable)) + self.assertIsSubclass(type(x), Awaitable) c = coro() # Iterable coroutines (generators with CO_ITERABLE_COROUTINE @@ -831,8 +831,8 @@ def __await__(self): class CoroLike: pass Coroutine.register(CoroLike) - self.assertTrue(isinstance(CoroLike(), Awaitable)) - self.assertTrue(issubclass(CoroLike, Awaitable)) + self.assertIsInstance(CoroLike(), Awaitable) + self.assertIsSubclass(CoroLike, Awaitable) CoroLike = None support.gc_collect() # Kill CoroLike to clean-up ABCMeta cache @@ -864,12 +864,12 @@ def __await__(self): non_samples = [None, int(), gen(), object(), Bar()] for x in non_samples: self.assertNotIsInstance(x, Coroutine) - self.assertFalse(issubclass(type(x), Coroutine), repr(type(x))) + self.assertNotIsSubclass(type(x), Coroutine) samples = [MinimalCoro()] for x in samples: self.assertIsInstance(x, Awaitable) - self.assertTrue(issubclass(type(x), Awaitable)) + self.assertIsSubclass(type(x), Awaitable) c = coro() # Iterable coroutines (generators with CO_ITERABLE_COROUTINE @@ -890,8 +890,8 @@ def close(self): pass def __await__(self): pass - self.assertTrue(isinstance(CoroLike(), Coroutine)) - self.assertTrue(issubclass(CoroLike, Coroutine)) + self.assertIsInstance(CoroLike(), Coroutine) + self.assertIsSubclass(CoroLike, Coroutine) class CoroLike: def send(self, value): @@ -900,15 +900,15 @@ def close(self): pass def __await__(self): pass - self.assertFalse(isinstance(CoroLike(), Coroutine)) - self.assertFalse(issubclass(CoroLike, Coroutine)) + self.assertNotIsInstance(CoroLike(), Coroutine) + self.assertNotIsSubclass(CoroLike, Coroutine) def test_Hashable(self): # Check some non-hashables non_samples = [bytearray(), list(), set(), dict()] for x in non_samples: self.assertNotIsInstance(x, Hashable) - self.assertFalse(issubclass(type(x), Hashable), repr(type(x))) + self.assertNotIsSubclass(type(x), Hashable) # Check some hashables samples = [None, int(), float(), complex(), @@ -918,14 +918,14 @@ def test_Hashable(self): ] for x in samples: self.assertIsInstance(x, Hashable) - self.assertTrue(issubclass(type(x), Hashable), repr(type(x))) + self.assertIsSubclass(type(x), Hashable) self.assertRaises(TypeError, Hashable) # Check direct subclassing class H(Hashable): def __hash__(self): return super().__hash__() self.assertEqual(hash(H()), 0) - self.assertFalse(issubclass(int, H)) + self.assertNotIsSubclass(int, H) self.validate_abstract_methods(Hashable, '__hash__') self.validate_isinstance(Hashable, '__hash__') @@ -933,13 +933,13 @@ def test_AsyncIterable(self): class AI: def __aiter__(self): return self - self.assertTrue(isinstance(AI(), AsyncIterable)) - self.assertTrue(issubclass(AI, AsyncIterable)) + self.assertIsInstance(AI(), AsyncIterable) + self.assertIsSubclass(AI, AsyncIterable) # Check some non-iterables non_samples = [None, object, []] for x in non_samples: self.assertNotIsInstance(x, AsyncIterable) - self.assertFalse(issubclass(type(x), AsyncIterable), repr(type(x))) + self.assertNotIsSubclass(type(x), AsyncIterable) self.validate_abstract_methods(AsyncIterable, '__aiter__') self.validate_isinstance(AsyncIterable, '__aiter__') @@ -949,13 +949,13 @@ def __aiter__(self): return self async def __anext__(self): raise StopAsyncIteration - self.assertTrue(isinstance(AI(), AsyncIterator)) - self.assertTrue(issubclass(AI, AsyncIterator)) + self.assertIsInstance(AI(), AsyncIterator) + self.assertIsSubclass(AI, AsyncIterator) non_samples = [None, object, []] # Check some non-iterables for x in non_samples: self.assertNotIsInstance(x, AsyncIterator) - self.assertFalse(issubclass(type(x), AsyncIterator), repr(type(x))) + self.assertNotIsSubclass(type(x), AsyncIterator) # Similarly to regular iterators (see issue 10565) class AnextOnly: async def __anext__(self): @@ -968,7 +968,7 @@ def test_Iterable(self): non_samples = [None, 42, 3.14, 1j] for x in non_samples: self.assertNotIsInstance(x, Iterable) - self.assertFalse(issubclass(type(x), Iterable), repr(type(x))) + self.assertNotIsSubclass(type(x), Iterable) # Check some iterables samples = [bytes(), str(), tuple(), list(), set(), frozenset(), dict(), @@ -978,13 +978,13 @@ def test_Iterable(self): ] for x in samples: self.assertIsInstance(x, Iterable) - self.assertTrue(issubclass(type(x), Iterable), repr(type(x))) + self.assertIsSubclass(type(x), Iterable) # Check direct subclassing class I(Iterable): def __iter__(self): return super().__iter__() self.assertEqual(list(I()), []) - self.assertFalse(issubclass(str, I)) + self.assertNotIsSubclass(str, I) self.validate_abstract_methods(Iterable, '__iter__') self.validate_isinstance(Iterable, '__iter__') # Check None blocking @@ -992,22 +992,22 @@ class It: def __iter__(self): return iter([]) class ItBlocked(It): __iter__ = None - self.assertTrue(issubclass(It, Iterable)) - self.assertTrue(isinstance(It(), Iterable)) - self.assertFalse(issubclass(ItBlocked, Iterable)) - self.assertFalse(isinstance(ItBlocked(), Iterable)) + self.assertIsSubclass(It, Iterable) + self.assertIsInstance(It(), Iterable) + self.assertNotIsSubclass(ItBlocked, Iterable) + self.assertNotIsInstance(ItBlocked(), Iterable) def test_Reversible(self): # Check some non-reversibles non_samples = [None, 42, 3.14, 1j, set(), frozenset()] for x in non_samples: self.assertNotIsInstance(x, Reversible) - self.assertFalse(issubclass(type(x), Reversible), repr(type(x))) + self.assertNotIsSubclass(type(x), Reversible) # Check some non-reversible iterables non_reversibles = [_test_gen(), (x for x in []), iter([]), reversed([])] for x in non_reversibles: self.assertNotIsInstance(x, Reversible) - self.assertFalse(issubclass(type(x), Reversible), repr(type(x))) + self.assertNotIsSubclass(type(x), Reversible) # Check some reversible iterables samples = [bytes(), str(), tuple(), list(), OrderedDict(), OrderedDict().keys(), OrderedDict().items(), @@ -1016,11 +1016,11 @@ def test_Reversible(self): dict().keys(), dict().items(), dict().values()] for x in samples: self.assertIsInstance(x, Reversible) - self.assertTrue(issubclass(type(x), Reversible), repr(type(x))) + self.assertIsSubclass(type(x), Reversible) # Check also Mapping, MutableMapping, and Sequence - self.assertTrue(issubclass(Sequence, Reversible), repr(Sequence)) - self.assertFalse(issubclass(Mapping, Reversible), repr(Mapping)) - self.assertFalse(issubclass(MutableMapping, Reversible), repr(MutableMapping)) + self.assertIsSubclass(Sequence, Reversible) + self.assertNotIsSubclass(Mapping, Reversible) + self.assertNotIsSubclass(MutableMapping, Reversible) # Check direct subclassing class R(Reversible): def __iter__(self): @@ -1028,17 +1028,17 @@ def __iter__(self): def __reversed__(self): return iter(list()) self.assertEqual(list(reversed(R())), []) - self.assertFalse(issubclass(float, R)) + self.assertNotIsSubclass(float, R) self.validate_abstract_methods(Reversible, '__reversed__', '__iter__') # Check reversible non-iterable (which is not Reversible) class RevNoIter: def __reversed__(self): return reversed([]) class RevPlusIter(RevNoIter): def __iter__(self): return iter([]) - self.assertFalse(issubclass(RevNoIter, Reversible)) - self.assertFalse(isinstance(RevNoIter(), Reversible)) - self.assertTrue(issubclass(RevPlusIter, Reversible)) - self.assertTrue(isinstance(RevPlusIter(), Reversible)) + self.assertNotIsSubclass(RevNoIter, Reversible) + self.assertNotIsInstance(RevNoIter(), Reversible) + self.assertIsSubclass(RevPlusIter, Reversible) + self.assertIsInstance(RevPlusIter(), Reversible) # Check None blocking class Rev: def __iter__(self): return iter([]) @@ -1047,39 +1047,38 @@ class RevItBlocked(Rev): __iter__ = None class RevRevBlocked(Rev): __reversed__ = None - self.assertTrue(issubclass(Rev, Reversible)) - self.assertTrue(isinstance(Rev(), Reversible)) - self.assertFalse(issubclass(RevItBlocked, Reversible)) - self.assertFalse(isinstance(RevItBlocked(), Reversible)) - self.assertFalse(issubclass(RevRevBlocked, Reversible)) - self.assertFalse(isinstance(RevRevBlocked(), Reversible)) + self.assertIsSubclass(Rev, Reversible) + self.assertIsInstance(Rev(), Reversible) + self.assertNotIsSubclass(RevItBlocked, Reversible) + self.assertNotIsInstance(RevItBlocked(), Reversible) + self.assertNotIsSubclass(RevRevBlocked, Reversible) + self.assertNotIsInstance(RevRevBlocked(), Reversible) def test_Collection(self): # Check some non-collections non_collections = [None, 42, 3.14, 1j, lambda x: 2*x] for x in non_collections: self.assertNotIsInstance(x, Collection) - self.assertFalse(issubclass(type(x), Collection), repr(type(x))) + self.assertNotIsSubclass(type(x), Collection) # Check some non-collection iterables non_col_iterables = [_test_gen(), iter(b''), iter(bytearray()), (x for x in [])] for x in non_col_iterables: self.assertNotIsInstance(x, Collection) - self.assertFalse(issubclass(type(x), Collection), repr(type(x))) + self.assertNotIsSubclass(type(x), Collection) # Check some collections samples = [set(), frozenset(), dict(), bytes(), str(), tuple(), list(), dict().keys(), dict().items(), dict().values()] for x in samples: self.assertIsInstance(x, Collection) - self.assertTrue(issubclass(type(x), Collection), repr(type(x))) + self.assertIsSubclass(type(x), Collection) # Check also Mapping, MutableMapping, etc. - self.assertTrue(issubclass(Sequence, Collection), repr(Sequence)) - self.assertTrue(issubclass(Mapping, Collection), repr(Mapping)) - self.assertTrue(issubclass(MutableMapping, Collection), - repr(MutableMapping)) - self.assertTrue(issubclass(Set, Collection), repr(Set)) - self.assertTrue(issubclass(MutableSet, Collection), repr(MutableSet)) - self.assertTrue(issubclass(Sequence, Collection), repr(MutableSet)) + self.assertIsSubclass(Sequence, Collection) + self.assertIsSubclass(Mapping, Collection) + self.assertIsSubclass(MutableMapping, Collection) + self.assertIsSubclass(Set, Collection) + self.assertIsSubclass(MutableSet, Collection) + self.assertIsSubclass(Sequence, Collection) # Check direct subclassing class Col(Collection): def __iter__(self): @@ -1090,13 +1089,13 @@ def __contains__(self, item): return False class DerCol(Col): pass self.assertEqual(list(iter(Col())), []) - self.assertFalse(issubclass(list, Col)) - self.assertFalse(issubclass(set, Col)) - self.assertFalse(issubclass(float, Col)) + self.assertNotIsSubclass(list, Col) + self.assertNotIsSubclass(set, Col) + self.assertNotIsSubclass(float, Col) self.assertEqual(list(iter(DerCol())), []) - self.assertFalse(issubclass(list, DerCol)) - self.assertFalse(issubclass(set, DerCol)) - self.assertFalse(issubclass(float, DerCol)) + self.assertNotIsSubclass(list, DerCol) + self.assertNotIsSubclass(set, DerCol) + self.assertNotIsSubclass(float, DerCol) self.validate_abstract_methods(Collection, '__len__', '__iter__', '__contains__') # Check sized container non-iterable (which is not Collection) etc. @@ -1109,12 +1108,12 @@ def __contains__(self, item): return False class ColNoCont: def __iter__(self): return iter([]) def __len__(self): return 0 - self.assertFalse(issubclass(ColNoIter, Collection)) - self.assertFalse(isinstance(ColNoIter(), Collection)) - self.assertFalse(issubclass(ColNoSize, Collection)) - self.assertFalse(isinstance(ColNoSize(), Collection)) - self.assertFalse(issubclass(ColNoCont, Collection)) - self.assertFalse(isinstance(ColNoCont(), Collection)) + self.assertNotIsSubclass(ColNoIter, Collection) + self.assertNotIsInstance(ColNoIter(), Collection) + self.assertNotIsSubclass(ColNoSize, Collection) + self.assertNotIsInstance(ColNoSize(), Collection) + self.assertNotIsSubclass(ColNoCont, Collection) + self.assertNotIsInstance(ColNoCont(), Collection) # Check None blocking class SizeBlock: def __iter__(self): return iter([]) @@ -1124,10 +1123,10 @@ class IterBlock: def __len__(self): return 0 def __contains__(self): return True __iter__ = None - self.assertFalse(issubclass(SizeBlock, Collection)) - self.assertFalse(isinstance(SizeBlock(), Collection)) - self.assertFalse(issubclass(IterBlock, Collection)) - self.assertFalse(isinstance(IterBlock(), Collection)) + self.assertNotIsSubclass(SizeBlock, Collection) + self.assertNotIsInstance(SizeBlock(), Collection) + self.assertNotIsSubclass(IterBlock, Collection) + self.assertNotIsInstance(IterBlock(), Collection) # Check None blocking in subclass class ColImpl: def __iter__(self): @@ -1138,15 +1137,15 @@ def __contains__(self, item): return False class NonCol(ColImpl): __contains__ = None - self.assertFalse(issubclass(NonCol, Collection)) - self.assertFalse(isinstance(NonCol(), Collection)) + self.assertNotIsSubclass(NonCol, Collection) + self.assertNotIsInstance(NonCol(), Collection) def test_Iterator(self): non_samples = [None, 42, 3.14, 1j, b"", "", (), [], {}, set()] for x in non_samples: self.assertNotIsInstance(x, Iterator) - self.assertFalse(issubclass(type(x), Iterator), repr(type(x))) + self.assertNotIsSubclass(type(x), Iterator) samples = [iter(bytes()), iter(str()), iter(tuple()), iter(list()), iter(dict()), iter(set()), iter(frozenset()), @@ -1157,7 +1156,7 @@ def test_Iterator(self): ] for x in samples: self.assertIsInstance(x, Iterator) - self.assertTrue(issubclass(type(x), Iterator), repr(type(x))) + self.assertIsSubclass(type(x), Iterator) self.validate_abstract_methods(Iterator, '__next__', '__iter__') # Issue 10565 @@ -1190,7 +1189,7 @@ def throw(self, typ, val=None, tb=None): pass iter(()), iter([]), NonGen1(), NonGen2(), NonGen3()] for x in non_samples: self.assertNotIsInstance(x, Generator) - self.assertFalse(issubclass(type(x), Generator), repr(type(x))) + self.assertNotIsSubclass(type(x), Generator) class Gen: def __iter__(self): return self @@ -1212,7 +1211,7 @@ def gen(): for x in samples: self.assertIsInstance(x, Iterator) self.assertIsInstance(x, Generator) - self.assertTrue(issubclass(type(x), Generator), repr(type(x))) + self.assertIsSubclass(type(x), Generator) self.validate_abstract_methods(Generator, 'send', 'throw') # mixin tests @@ -1261,7 +1260,7 @@ def athrow(self, typ, val=None, tb=None): pass iter(()), iter([]), NonAGen1(), NonAGen2(), NonAGen3()] for x in non_samples: self.assertNotIsInstance(x, AsyncGenerator) - self.assertFalse(issubclass(type(x), AsyncGenerator), repr(type(x))) + self.assertNotIsSubclass(type(x), AsyncGenerator) class Gen: def __aiter__(self): return self @@ -1283,7 +1282,7 @@ async def gen(): for x in samples: self.assertIsInstance(x, AsyncIterator) self.assertIsInstance(x, AsyncGenerator) - self.assertTrue(issubclass(type(x), AsyncGenerator), repr(type(x))) + self.assertIsSubclass(type(x), AsyncGenerator) self.validate_abstract_methods(AsyncGenerator, 'asend', 'athrow') def run_async(coro): @@ -1326,14 +1325,14 @@ def test_Sized(self): ] for x in non_samples: self.assertNotIsInstance(x, Sized) - self.assertFalse(issubclass(type(x), Sized), repr(type(x))) + self.assertNotIsSubclass(type(x), Sized) samples = [bytes(), str(), tuple(), list(), set(), frozenset(), dict(), dict().keys(), dict().items(), dict().values(), ] for x in samples: self.assertIsInstance(x, Sized) - self.assertTrue(issubclass(type(x), Sized), repr(type(x))) + self.assertIsSubclass(type(x), Sized) self.validate_abstract_methods(Sized, '__len__') self.validate_isinstance(Sized, '__len__') @@ -1344,14 +1343,14 @@ def test_Container(self): ] for x in non_samples: self.assertNotIsInstance(x, Container) - self.assertFalse(issubclass(type(x), Container), repr(type(x))) + self.assertNotIsSubclass(type(x), Container) samples = [bytes(), str(), tuple(), list(), set(), frozenset(), dict(), dict().keys(), dict().items(), ] for x in samples: self.assertIsInstance(x, Container) - self.assertTrue(issubclass(type(x), Container), repr(type(x))) + self.assertIsSubclass(type(x), Container) self.validate_abstract_methods(Container, '__contains__') self.validate_isinstance(Container, '__contains__') @@ -1363,7 +1362,7 @@ def test_Callable(self): ] for x in non_samples: self.assertNotIsInstance(x, Callable) - self.assertFalse(issubclass(type(x), Callable), repr(type(x))) + self.assertNotIsSubclass(type(x), Callable) samples = [lambda: None, type, int, object, len, @@ -1371,7 +1370,7 @@ def test_Callable(self): ] for x in samples: self.assertIsInstance(x, Callable) - self.assertTrue(issubclass(type(x), Callable), repr(type(x))) + self.assertIsSubclass(type(x), Callable) self.validate_abstract_methods(Callable, '__call__') self.validate_isinstance(Callable, '__call__') @@ -1379,16 +1378,16 @@ def test_direct_subclassing(self): for B in Hashable, Iterable, Iterator, Reversible, Sized, Container, Callable: class C(B): pass - self.assertTrue(issubclass(C, B)) - self.assertFalse(issubclass(int, C)) + self.assertIsSubclass(C, B) + self.assertNotIsSubclass(int, C) def test_registration(self): for B in Hashable, Iterable, Iterator, Reversible, Sized, Container, Callable: class C: __hash__ = None # Make sure it isn't hashable by default - self.assertFalse(issubclass(C, B), B.__name__) + self.assertNotIsSubclass(C, B) B.register(C) - self.assertTrue(issubclass(C, B)) + self.assertIsSubclass(C, B) class WithSet(MutableSet): @@ -1419,7 +1418,7 @@ class TestCollectionABCs(ABCTestCase): def test_Set(self): for sample in [set, frozenset]: self.assertIsInstance(sample(), Set) - self.assertTrue(issubclass(sample, Set)) + self.assertIsSubclass(sample, Set) self.validate_abstract_methods(Set, '__contains__', '__iter__', '__len__') class MySet(Set): def __contains__(self, x): @@ -1500,9 +1499,9 @@ def __len__(self): def test_MutableSet(self): self.assertIsInstance(set(), MutableSet) - self.assertTrue(issubclass(set, MutableSet)) + self.assertIsSubclass(set, MutableSet) self.assertNotIsInstance(frozenset(), MutableSet) - self.assertFalse(issubclass(frozenset, MutableSet)) + self.assertNotIsSubclass(frozenset, MutableSet) self.validate_abstract_methods(MutableSet, '__contains__', '__iter__', '__len__', 'add', 'discard') @@ -1841,7 +1840,7 @@ def test_Set_hash_matches_frozenset(self): def test_Mapping(self): for sample in [dict]: self.assertIsInstance(sample(), Mapping) - self.assertTrue(issubclass(sample, Mapping)) + self.assertIsSubclass(sample, Mapping) self.validate_abstract_methods(Mapping, '__contains__', '__iter__', '__len__', '__getitem__') class MyMapping(Mapping): @@ -1857,7 +1856,7 @@ def __iter__(self): def test_MutableMapping(self): for sample in [dict]: self.assertIsInstance(sample(), MutableMapping) - self.assertTrue(issubclass(sample, MutableMapping)) + self.assertIsSubclass(sample, MutableMapping) self.validate_abstract_methods(MutableMapping, '__contains__', '__iter__', '__len__', '__getitem__', '__setitem__', '__delitem__') @@ -1891,12 +1890,12 @@ def test_MutableMapping_subclass(self): def test_Sequence(self): for sample in [tuple, list, bytes, str]: self.assertIsInstance(sample(), Sequence) - self.assertTrue(issubclass(sample, Sequence)) + self.assertIsSubclass(sample, Sequence) self.assertIsInstance(range(10), Sequence) - self.assertTrue(issubclass(range, Sequence)) + self.assertIsSubclass(range, Sequence) self.assertIsInstance(memoryview(b""), Sequence) - self.assertTrue(issubclass(memoryview, Sequence)) - self.assertTrue(issubclass(str, Sequence)) + self.assertIsSubclass(memoryview, Sequence) + self.assertIsSubclass(str, Sequence) self.validate_abstract_methods(Sequence, '__contains__', '__iter__', '__len__', '__getitem__') @@ -1938,21 +1937,21 @@ def assert_index_same(seq1, seq2, index_args): def test_Buffer(self): for sample in [bytes, bytearray, memoryview]: self.assertIsInstance(sample(b"x"), Buffer) - self.assertTrue(issubclass(sample, Buffer)) + self.assertIsSubclass(sample, Buffer) for sample in [str, list, tuple]: self.assertNotIsInstance(sample(), Buffer) - self.assertFalse(issubclass(sample, Buffer)) + self.assertNotIsSubclass(sample, Buffer) self.validate_abstract_methods(Buffer, '__buffer__') def test_MutableSequence(self): for sample in [tuple, str, bytes]: self.assertNotIsInstance(sample(), MutableSequence) - self.assertFalse(issubclass(sample, MutableSequence)) + self.assertNotIsSubclass(sample, MutableSequence) for sample in [list, bytearray, deque]: self.assertIsInstance(sample(), MutableSequence) - self.assertTrue(issubclass(sample, MutableSequence)) - self.assertTrue(issubclass(array.array, MutableSequence)) - self.assertFalse(issubclass(str, MutableSequence)) + self.assertIsSubclass(sample, MutableSequence) + self.assertIsSubclass(array.array, MutableSequence) + self.assertNotIsSubclass(str, MutableSequence) self.validate_abstract_methods(MutableSequence, '__contains__', '__iter__', '__len__', '__getitem__', '__setitem__', '__delitem__', 'insert') @@ -2043,8 +2042,8 @@ def test_basics(self): self.assertEqual(c, Counter(a=3, b=2, c=1)) self.assertIsInstance(c, dict) self.assertIsInstance(c, Mapping) - self.assertTrue(issubclass(Counter, dict)) - self.assertTrue(issubclass(Counter, Mapping)) + self.assertIsSubclass(Counter, dict) + self.assertIsSubclass(Counter, Mapping) self.assertEqual(len(c), 3) self.assertEqual(sum(c.values()), 6) self.assertEqual(list(c.values()), [3, 2, 1]) From 89c401fb9a612b9618bd513508e67dededb0f031 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 20 Jan 2025 11:17:04 +0200 Subject: [PATCH 371/376] gh-71339: Use new assertion methods in test_functools (GH-128829) --- Lib/test/test_functools.py | 48 +++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 32224866082824..4beb4380c3ad6b 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -645,11 +645,11 @@ def test_bound_method_introspection(self): def test_unbound_method_retrieval(self): obj = self.A - self.assertFalse(hasattr(obj.both, "__self__")) - self.assertFalse(hasattr(obj.nested, "__self__")) - self.assertFalse(hasattr(obj.over_partial, "__self__")) - self.assertFalse(hasattr(obj.static, "__self__")) - self.assertFalse(hasattr(self.a.static, "__self__")) + self.assertNotHasAttr(obj.both, "__self__") + self.assertNotHasAttr(obj.nested, "__self__") + self.assertNotHasAttr(obj.over_partial, "__self__") + self.assertNotHasAttr(obj.static, "__self__") + self.assertNotHasAttr(self.a.static, "__self__") def test_descriptors(self): for obj in [self.A, self.a]: @@ -791,7 +791,7 @@ def wrapper(): self.assertNotEqual(wrapper.__qualname__, f.__qualname__) self.assertEqual(wrapper.__doc__, None) self.assertEqual(wrapper.__annotations__, {}) - self.assertFalse(hasattr(wrapper, 'attr')) + self.assertNotHasAttr(wrapper, 'attr') def test_selective_update(self): def f(): @@ -840,7 +840,7 @@ def wrapper(): pass functools.update_wrapper(wrapper, max) self.assertEqual(wrapper.__name__, 'max') - self.assertTrue(wrapper.__doc__.startswith('max(')) + self.assertStartsWith(wrapper.__doc__, 'max(') self.assertEqual(wrapper.__annotations__, {}) def test_update_type_wrapper(self): @@ -910,7 +910,7 @@ def wrapper(): self.assertEqual(wrapper.__name__, 'wrapper') self.assertNotEqual(wrapper.__qualname__, f.__qualname__) self.assertEqual(wrapper.__doc__, None) - self.assertFalse(hasattr(wrapper, 'attr')) + self.assertNotHasAttr(wrapper, 'attr') def test_selective_update(self): def f(): @@ -2666,15 +2666,15 @@ def _(self, arg): a.t(0) self.assertEqual(a.arg, "int") aa = A() - self.assertFalse(hasattr(aa, 'arg')) + self.assertNotHasAttr(aa, 'arg') a.t('') self.assertEqual(a.arg, "str") aa = A() - self.assertFalse(hasattr(aa, 'arg')) + self.assertNotHasAttr(aa, 'arg') a.t(0.0) self.assertEqual(a.arg, "base") aa = A() - self.assertFalse(hasattr(aa, 'arg')) + self.assertNotHasAttr(aa, 'arg') def test_staticmethod_register(self): class A: @@ -3036,16 +3036,16 @@ def i(arg): @i.register(42) def _(arg): return "I annotated with a non-type" - self.assertTrue(str(exc.exception).startswith(msg_prefix + "42")) - self.assertTrue(str(exc.exception).endswith(msg_suffix)) + self.assertStartsWith(str(exc.exception), msg_prefix + "42") + self.assertEndsWith(str(exc.exception), msg_suffix) with self.assertRaises(TypeError) as exc: @i.register def _(arg): return "I forgot to annotate" - self.assertTrue(str(exc.exception).startswith(msg_prefix + + self.assertStartsWith(str(exc.exception), msg_prefix + "._" - )) - self.assertTrue(str(exc.exception).endswith(msg_suffix)) + ) + self.assertEndsWith(str(exc.exception), msg_suffix) with self.assertRaises(TypeError) as exc: @i.register @@ -3055,23 +3055,23 @@ def _(arg: typing.Iterable[str]): # types from `typing`. Instead, annotate with regular types # or ABCs. return "I annotated with a generic collection" - self.assertTrue(str(exc.exception).startswith( + self.assertStartsWith(str(exc.exception), "Invalid annotation for 'arg'." - )) - self.assertTrue(str(exc.exception).endswith( + ) + self.assertEndsWith(str(exc.exception), 'typing.Iterable[str] is not a class.' - )) + ) with self.assertRaises(TypeError) as exc: @i.register def _(arg: typing.Union[int, typing.Iterable[str]]): return "Invalid Union" - self.assertTrue(str(exc.exception).startswith( + self.assertStartsWith(str(exc.exception), "Invalid annotation for 'arg'." - )) - self.assertTrue(str(exc.exception).endswith( + ) + self.assertEndsWith(str(exc.exception), 'typing.Union[int, typing.Iterable[str]] not all arguments are classes.' - )) + ) def test_invalid_positional_argument(self): @functools.singledispatch From da122b5facbbae9197a108e0a3c4b3f0594c5e92 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 20 Jan 2025 11:17:49 +0200 Subject: [PATCH 372/376] gh-71339: Improve error report for types in assertHasAttr() and assertNotHasAttr() (GH-128818) --- Lib/test/test_unittest/test_case.py | 20 ++++++++++++++++++-- Lib/unittest/case.py | 8 ++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_unittest/test_case.py b/Lib/test/test_unittest/test_case.py index cd366496eedca3..df1381451b7ebc 100644 --- a/Lib/test/test_unittest/test_case.py +++ b/Lib/test/test_unittest/test_case.py @@ -795,7 +795,15 @@ def testAssertHasAttr(self): with self.assertRaises(self.failureException) as cm: self.assertHasAttr(a, 'y') self.assertEqual(str(cm.exception), - "List instance has no attribute 'y'") + "'List' object has no attribute 'y'") + with self.assertRaises(self.failureException) as cm: + self.assertHasAttr(List, 'spam') + self.assertEqual(str(cm.exception), + "type object 'List' has no attribute 'spam'") + with self.assertRaises(self.failureException) as cm: + self.assertHasAttr(sys, 'spam') + self.assertEqual(str(cm.exception), + "module 'sys' has no attribute 'spam'") with self.assertRaises(self.failureException) as cm: self.assertHasAttr(a, 'y', 'ababahalamaha') @@ -811,7 +819,15 @@ def testAssertNotHasAttr(self): with self.assertRaises(self.failureException) as cm: self.assertNotHasAttr(a, 'x') self.assertEqual(str(cm.exception), - "List instance has unexpected attribute 'x'") + "'List' object has unexpected attribute 'x'") + with self.assertRaises(self.failureException) as cm: + self.assertNotHasAttr(List, 'append') + self.assertEqual(str(cm.exception), + "type object 'List' has unexpected attribute 'append'") + with self.assertRaises(self.failureException) as cm: + self.assertNotHasAttr(sys, 'modules') + self.assertEqual(str(cm.exception), + "module 'sys' has unexpected attribute 'modules'") with self.assertRaises(self.failureException) as cm: self.assertNotHasAttr(a, 'x', 'ababahalamaha') diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index e9ef551d0b3ded..10c3b7e122371e 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -1372,16 +1372,20 @@ def assertHasAttr(self, obj, name, msg=None): if not hasattr(obj, name): if isinstance(obj, types.ModuleType): standardMsg = f'module {obj.__name__!r} has no attribute {name!r}' + elif isinstance(obj, type): + standardMsg = f'type object {obj.__name__!r} has no attribute {name!r}' else: - standardMsg = f'{type(obj).__name__} instance has no attribute {name!r}' + standardMsg = f'{type(obj).__name__!r} object has no attribute {name!r}' self.fail(self._formatMessage(msg, standardMsg)) def assertNotHasAttr(self, obj, name, msg=None): if hasattr(obj, name): if isinstance(obj, types.ModuleType): standardMsg = f'module {obj.__name__!r} has unexpected attribute {name!r}' + elif isinstance(obj, type): + standardMsg = f'type object {obj.__name__!r} has unexpected attribute {name!r}' else: - standardMsg = f'{type(obj).__name__} instance has unexpected attribute {name!r}' + standardMsg = f'{type(obj).__name__!r} object has unexpected attribute {name!r}' self.fail(self._formatMessage(msg, standardMsg)) def assertRaisesRegex(self, expected_exception, expected_regex, From 36aaf4137d55df3e324d98ec06b983b0287d8949 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 20 Jan 2025 11:19:43 +0200 Subject: [PATCH 373/376] gh-71339: Use new assertion methods in test_abc (GH-128826) --- Lib/test/test_abc.py | 102 +++++++++++++++++++++---------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py index 5ce57cc209ea85..e90a8dc617c094 100644 --- a/Lib/test/test_abc.py +++ b/Lib/test/test_abc.py @@ -20,7 +20,7 @@ def test_abstractproperty_basics(self): def foo(self): pass self.assertTrue(foo.__isabstractmethod__) def bar(self): pass - self.assertFalse(hasattr(bar, "__isabstractmethod__")) + self.assertNotHasAttr(bar, "__isabstractmethod__") class C(metaclass=abc_ABCMeta): @abc.abstractproperty @@ -89,7 +89,7 @@ def test_abstractmethod_basics(self): def foo(self): pass self.assertTrue(foo.__isabstractmethod__) def bar(self): pass - self.assertFalse(hasattr(bar, "__isabstractmethod__")) + self.assertNotHasAttr(bar, "__isabstractmethod__") def test_abstractproperty_basics(self): @property @@ -276,21 +276,21 @@ class A(metaclass=abc_ABCMeta): class B(object): pass b = B() - self.assertFalse(issubclass(B, A)) - self.assertFalse(issubclass(B, (A,))) + self.assertNotIsSubclass(B, A) + self.assertNotIsSubclass(B, (A,)) self.assertNotIsInstance(b, A) self.assertNotIsInstance(b, (A,)) B1 = A.register(B) - self.assertTrue(issubclass(B, A)) - self.assertTrue(issubclass(B, (A,))) + self.assertIsSubclass(B, A) + self.assertIsSubclass(B, (A,)) self.assertIsInstance(b, A) self.assertIsInstance(b, (A,)) self.assertIs(B1, B) class C(B): pass c = C() - self.assertTrue(issubclass(C, A)) - self.assertTrue(issubclass(C, (A,))) + self.assertIsSubclass(C, A) + self.assertIsSubclass(C, (A,)) self.assertIsInstance(c, A) self.assertIsInstance(c, (A,)) @@ -301,16 +301,16 @@ class A(metaclass=abc_ABCMeta): class B(object): pass b = B() - self.assertTrue(issubclass(B, A)) - self.assertTrue(issubclass(B, (A,))) + self.assertIsSubclass(B, A) + self.assertIsSubclass(B, (A,)) self.assertIsInstance(b, A) self.assertIsInstance(b, (A,)) @A.register class C(B): pass c = C() - self.assertTrue(issubclass(C, A)) - self.assertTrue(issubclass(C, (A,))) + self.assertIsSubclass(C, A) + self.assertIsSubclass(C, (A,)) self.assertIsInstance(c, A) self.assertIsInstance(c, (A,)) self.assertIs(C, A.register(C)) @@ -321,14 +321,14 @@ class A(metaclass=abc_ABCMeta): class B: pass b = B() - self.assertFalse(isinstance(b, A)) - self.assertFalse(isinstance(b, (A,))) + self.assertNotIsInstance(b, A) + self.assertNotIsInstance(b, (A,)) token_old = abc_get_cache_token() A.register(B) token_new = abc_get_cache_token() self.assertGreater(token_new, token_old) - self.assertTrue(isinstance(b, A)) - self.assertTrue(isinstance(b, (A,))) + self.assertIsInstance(b, A) + self.assertIsInstance(b, (A,)) def test_registration_builtins(self): class A(metaclass=abc_ABCMeta): @@ -336,18 +336,18 @@ class A(metaclass=abc_ABCMeta): A.register(int) self.assertIsInstance(42, A) self.assertIsInstance(42, (A,)) - self.assertTrue(issubclass(int, A)) - self.assertTrue(issubclass(int, (A,))) + self.assertIsSubclass(int, A) + self.assertIsSubclass(int, (A,)) class B(A): pass B.register(str) class C(str): pass self.assertIsInstance("", A) self.assertIsInstance("", (A,)) - self.assertTrue(issubclass(str, A)) - self.assertTrue(issubclass(str, (A,))) - self.assertTrue(issubclass(C, A)) - self.assertTrue(issubclass(C, (A,))) + self.assertIsSubclass(str, A) + self.assertIsSubclass(str, (A,)) + self.assertIsSubclass(C, A) + self.assertIsSubclass(C, (A,)) def test_registration_edge_cases(self): class A(metaclass=abc_ABCMeta): @@ -375,39 +375,39 @@ class A(metaclass=abc_ABCMeta): def test_registration_transitiveness(self): class A(metaclass=abc_ABCMeta): pass - self.assertTrue(issubclass(A, A)) - self.assertTrue(issubclass(A, (A,))) + self.assertIsSubclass(A, A) + self.assertIsSubclass(A, (A,)) class B(metaclass=abc_ABCMeta): pass - self.assertFalse(issubclass(A, B)) - self.assertFalse(issubclass(A, (B,))) - self.assertFalse(issubclass(B, A)) - self.assertFalse(issubclass(B, (A,))) + self.assertNotIsSubclass(A, B) + self.assertNotIsSubclass(A, (B,)) + self.assertNotIsSubclass(B, A) + self.assertNotIsSubclass(B, (A,)) class C(metaclass=abc_ABCMeta): pass A.register(B) class B1(B): pass - self.assertTrue(issubclass(B1, A)) - self.assertTrue(issubclass(B1, (A,))) + self.assertIsSubclass(B1, A) + self.assertIsSubclass(B1, (A,)) class C1(C): pass B1.register(C1) - self.assertFalse(issubclass(C, B)) - self.assertFalse(issubclass(C, (B,))) - self.assertFalse(issubclass(C, B1)) - self.assertFalse(issubclass(C, (B1,))) - self.assertTrue(issubclass(C1, A)) - self.assertTrue(issubclass(C1, (A,))) - self.assertTrue(issubclass(C1, B)) - self.assertTrue(issubclass(C1, (B,))) - self.assertTrue(issubclass(C1, B1)) - self.assertTrue(issubclass(C1, (B1,))) + self.assertNotIsSubclass(C, B) + self.assertNotIsSubclass(C, (B,)) + self.assertNotIsSubclass(C, B1) + self.assertNotIsSubclass(C, (B1,)) + self.assertIsSubclass(C1, A) + self.assertIsSubclass(C1, (A,)) + self.assertIsSubclass(C1, B) + self.assertIsSubclass(C1, (B,)) + self.assertIsSubclass(C1, B1) + self.assertIsSubclass(C1, (B1,)) C1.register(int) class MyInt(int): pass - self.assertTrue(issubclass(MyInt, A)) - self.assertTrue(issubclass(MyInt, (A,))) + self.assertIsSubclass(MyInt, A) + self.assertIsSubclass(MyInt, (A,)) self.assertIsInstance(42, A) self.assertIsInstance(42, (A,)) @@ -467,16 +467,16 @@ def __subclasshook__(cls, C): if cls is A: return 'foo' in C.__dict__ return NotImplemented - self.assertFalse(issubclass(A, A)) - self.assertFalse(issubclass(A, (A,))) + self.assertNotIsSubclass(A, A) + self.assertNotIsSubclass(A, (A,)) class B: foo = 42 - self.assertTrue(issubclass(B, A)) - self.assertTrue(issubclass(B, (A,))) + self.assertIsSubclass(B, A) + self.assertIsSubclass(B, (A,)) class C: spam = 42 - self.assertFalse(issubclass(C, A)) - self.assertFalse(issubclass(C, (A,))) + self.assertNotIsSubclass(C, A) + self.assertNotIsSubclass(C, (A,)) def test_all_new_methods_are_called(self): class A(metaclass=abc_ABCMeta): @@ -493,7 +493,7 @@ class C(A, B): self.assertEqual(B.counter, 1) def test_ABC_has___slots__(self): - self.assertTrue(hasattr(abc.ABC, '__slots__')) + self.assertHasAttr(abc.ABC, '__slots__') def test_tricky_new_works(self): def with_metaclass(meta, *bases): @@ -515,7 +515,7 @@ def foo(self): del A.foo self.assertEqual(A.__abstractmethods__, {'foo'}) - self.assertFalse(hasattr(A, 'foo')) + self.assertNotHasAttr(A, 'foo') abc.update_abstractmethods(A) @@ -588,7 +588,7 @@ def updated_foo(self): A.foo = updated_foo abc.update_abstractmethods(A) A() - self.assertFalse(hasattr(A, '__abstractmethods__')) + self.assertNotHasAttr(A, '__abstractmethods__') def test_update_del_implementation(self): class A(metaclass=abc_ABCMeta): From 485d2e831ebf71a34634319adeeabdd1f9158bbe Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 20 Jan 2025 11:20:49 +0200 Subject: [PATCH 374/376] gh-71339: Use assertIsSubclass() and assertNotIsSubclass() in test_decimal (GH-128827) --- Lib/test/test_decimal.py | 58 ++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index bc6c6427740949..02d3fa985e75b9 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -752,7 +752,7 @@ def test_explicit_context_create_decimal(self): for v in [-2**63-1, -2**63, -2**31-1, -2**31, 0, 2**31-1, 2**31, 2**63-1, 2**63]: d = nc.create_decimal(v) - self.assertTrue(isinstance(d, Decimal)) + self.assertIsInstance(d, Decimal) self.assertEqual(int(d), v) nc.prec = 3 @@ -2590,8 +2590,8 @@ class PythonAPItests: def test_abc(self): Decimal = self.decimal.Decimal - self.assertTrue(issubclass(Decimal, numbers.Number)) - self.assertFalse(issubclass(Decimal, numbers.Real)) + self.assertIsSubclass(Decimal, numbers.Number) + self.assertNotIsSubclass(Decimal, numbers.Real) self.assertIsInstance(Decimal(0), numbers.Number) self.assertNotIsInstance(Decimal(0), numbers.Real) @@ -2690,7 +2690,7 @@ class MyDecimal(Decimal): def __init__(self, _): self.x = 'y' - self.assertTrue(issubclass(MyDecimal, Decimal)) + self.assertIsSubclass(MyDecimal, Decimal) r = MyDecimal.from_float(0.1) self.assertEqual(type(r), MyDecimal) @@ -2908,31 +2908,31 @@ def test_exception_hierarchy(self): Rounded = decimal.Rounded Clamped = decimal.Clamped - self.assertTrue(issubclass(DecimalException, ArithmeticError)) - - self.assertTrue(issubclass(InvalidOperation, DecimalException)) - self.assertTrue(issubclass(FloatOperation, DecimalException)) - self.assertTrue(issubclass(FloatOperation, TypeError)) - self.assertTrue(issubclass(DivisionByZero, DecimalException)) - self.assertTrue(issubclass(DivisionByZero, ZeroDivisionError)) - self.assertTrue(issubclass(Overflow, Rounded)) - self.assertTrue(issubclass(Overflow, Inexact)) - self.assertTrue(issubclass(Overflow, DecimalException)) - self.assertTrue(issubclass(Underflow, Inexact)) - self.assertTrue(issubclass(Underflow, Rounded)) - self.assertTrue(issubclass(Underflow, Subnormal)) - self.assertTrue(issubclass(Underflow, DecimalException)) - - self.assertTrue(issubclass(Subnormal, DecimalException)) - self.assertTrue(issubclass(Inexact, DecimalException)) - self.assertTrue(issubclass(Rounded, DecimalException)) - self.assertTrue(issubclass(Clamped, DecimalException)) - - self.assertTrue(issubclass(decimal.ConversionSyntax, InvalidOperation)) - self.assertTrue(issubclass(decimal.DivisionImpossible, InvalidOperation)) - self.assertTrue(issubclass(decimal.DivisionUndefined, InvalidOperation)) - self.assertTrue(issubclass(decimal.DivisionUndefined, ZeroDivisionError)) - self.assertTrue(issubclass(decimal.InvalidContext, InvalidOperation)) + self.assertIsSubclass(DecimalException, ArithmeticError) + + self.assertIsSubclass(InvalidOperation, DecimalException) + self.assertIsSubclass(FloatOperation, DecimalException) + self.assertIsSubclass(FloatOperation, TypeError) + self.assertIsSubclass(DivisionByZero, DecimalException) + self.assertIsSubclass(DivisionByZero, ZeroDivisionError) + self.assertIsSubclass(Overflow, Rounded) + self.assertIsSubclass(Overflow, Inexact) + self.assertIsSubclass(Overflow, DecimalException) + self.assertIsSubclass(Underflow, Inexact) + self.assertIsSubclass(Underflow, Rounded) + self.assertIsSubclass(Underflow, Subnormal) + self.assertIsSubclass(Underflow, DecimalException) + + self.assertIsSubclass(Subnormal, DecimalException) + self.assertIsSubclass(Inexact, DecimalException) + self.assertIsSubclass(Rounded, DecimalException) + self.assertIsSubclass(Clamped, DecimalException) + + self.assertIsSubclass(decimal.ConversionSyntax, InvalidOperation) + self.assertIsSubclass(decimal.DivisionImpossible, InvalidOperation) + self.assertIsSubclass(decimal.DivisionUndefined, InvalidOperation) + self.assertIsSubclass(decimal.DivisionUndefined, ZeroDivisionError) + self.assertIsSubclass(decimal.InvalidContext, InvalidOperation) @requires_cdecimal class CPythonAPItests(PythonAPItests, unittest.TestCase): From c463270c73a61ef8106ee7bd0571c7c6143e2c20 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 20 Jan 2025 11:22:33 +0200 Subject: [PATCH 375/376] gh-71339: Use new assertion methods in test_traceback (GH-128848) --- Lib/test/test_traceback.py | 40 ++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index abdfc4638f2e9c..a27a71c32ea521 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -86,7 +86,7 @@ def test_caret(self): err = self.get_exception_format(self.syntax_error_with_caret, SyntaxError) self.assertEqual(len(err), 4) - self.assertTrue(err[1].strip() == "return x!") + self.assertEqual(err[1].strip(), "return x!") self.assertIn("^", err[2]) # third line has caret self.assertEqual(err[1].find("!"), err[2].find("^")) # in the right place self.assertEqual(err[2].count("^"), 1) @@ -419,16 +419,10 @@ def do_test(firstlines, message, charset, lineno): err_line = "raise RuntimeError('{0}')".format(message_ascii) err_msg = "RuntimeError: {0}".format(message_ascii) - self.assertIn(("line %s" % lineno), stdout[1], - "Invalid line number: {0!r} instead of {1}".format( - stdout[1], lineno)) - self.assertTrue(stdout[2].endswith(err_line), - "Invalid traceback line: {0!r} instead of {1!r}".format( - stdout[2], err_line)) + self.assertIn("line %s" % lineno, stdout[1]) + self.assertEndsWith(stdout[2], err_line) actual_err_msg = stdout[3] - self.assertTrue(actual_err_msg == err_msg, - "Invalid error message: {0!r} instead of {1!r}".format( - actual_err_msg, err_msg)) + self.assertEqual(actual_err_msg, err_msg) do_test("", "foo", "ascii", 3) for charset in ("ascii", "iso-8859-1", "utf-8", "GBK"): @@ -1809,9 +1803,9 @@ def check_traceback_format(self, cleanup_func=None): banner = tb_lines[0] self.assertEqual(len(tb_lines), 5) location, source_line = tb_lines[-2], tb_lines[-1] - self.assertTrue(banner.startswith('Traceback')) - self.assertTrue(location.startswith(' File')) - self.assertTrue(source_line.startswith(' raise')) + self.assertStartsWith(banner, 'Traceback') + self.assertStartsWith(location, ' File') + self.assertStartsWith(source_line, ' raise') def test_traceback_format(self): self.check_traceback_format() @@ -2190,12 +2184,12 @@ def zero_div(self): def check_zero_div(self, msg): lines = msg.splitlines() if has_no_debug_ranges(): - self.assertTrue(lines[-3].startswith(' File')) + self.assertStartsWith(lines[-3], ' File') self.assertIn('1/0 # In zero_div', lines[-2]) else: - self.assertTrue(lines[-4].startswith(' File')) + self.assertStartsWith(lines[-4], ' File') self.assertIn('1/0 # In zero_div', lines[-3]) - self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1]) + self.assertStartsWith(lines[-1], 'ZeroDivisionError') def test_simple(self): try: @@ -2205,12 +2199,12 @@ def test_simple(self): lines = self.get_report(e).splitlines() if has_no_debug_ranges(): self.assertEqual(len(lines), 4) - self.assertTrue(lines[3].startswith('ZeroDivisionError')) + self.assertStartsWith(lines[3], 'ZeroDivisionError') else: self.assertEqual(len(lines), 5) - self.assertTrue(lines[4].startswith('ZeroDivisionError')) - self.assertTrue(lines[0].startswith('Traceback')) - self.assertTrue(lines[1].startswith(' File')) + self.assertStartsWith(lines[4], 'ZeroDivisionError') + self.assertStartsWith(lines[0], 'Traceback') + self.assertStartsWith(lines[1], ' File') self.assertIn('1/0 # Marker', lines[2]) def test_cause(self): @@ -2251,9 +2245,9 @@ def test_context_suppression(self): e = _ lines = self.get_report(e).splitlines() self.assertEqual(len(lines), 4) - self.assertTrue(lines[3].startswith('ZeroDivisionError')) - self.assertTrue(lines[0].startswith('Traceback')) - self.assertTrue(lines[1].startswith(' File')) + self.assertStartsWith(lines[3], 'ZeroDivisionError') + self.assertStartsWith(lines[0], 'Traceback') + self.assertStartsWith(lines[1], ' File') self.assertIn('ZeroDivisionError from None', lines[2]) def test_cause_and_context(self): From 07c3518ffb27875b14a0f1637aa85f773ff2f9ff Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 20 Jan 2025 11:03:22 +0100 Subject: [PATCH 376/376] gh-129033: Remove _Py_InitializeMain() function (#129034) Co-authored-by: Alyssa Coghlan --- Doc/c-api/init_config.rst | 92 ++----------------- Doc/whatsnew/3.14.rst | 4 + Include/cpython/pylifecycle.h | 3 - Lib/test/test_embed.py | 19 ---- ...-01-19-23-17-58.gh-issue-129033.cpRivP.rst | 3 + Programs/_testembed.c | 35 ------- Python/pylifecycle.c | 12 --- 7 files changed, 15 insertions(+), 153 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-01-19-23-17-58.gh-issue-129033.cpRivP.rst diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index 6b33d93a9f2af9..85566631ca1676 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -1946,89 +1946,13 @@ Py_GetArgcArgv() See also :c:member:`PyConfig.orig_argv` member. +Delaying main module execution +============================== -Multi-Phase Initialization Private Provisional API -================================================== +In some embedding use cases, it may be desirable to separate interpreter initialization +from the execution of the main module. -This section is a private provisional API introducing multi-phase -initialization, the core feature of :pep:`432`: - -* "Core" initialization phase, "bare minimum Python": - - * Builtin types; - * Builtin exceptions; - * Builtin and frozen modules; - * The :mod:`sys` module is only partially initialized - (ex: :data:`sys.path` doesn't exist yet). - -* "Main" initialization phase, Python is fully initialized: - - * Install and configure :mod:`importlib`; - * Apply the :ref:`Path Configuration `; - * Install signal handlers; - * Finish :mod:`sys` module initialization (ex: create :data:`sys.stdout` - and :data:`sys.path`); - * Enable optional features like :mod:`faulthandler` and :mod:`tracemalloc`; - * Import the :mod:`site` module; - * etc. - -Private provisional API: - -* :c:member:`PyConfig._init_main`: if set to ``0``, - :c:func:`Py_InitializeFromConfig` stops at the "Core" initialization phase. - -.. c:function:: PyStatus _Py_InitializeMain(void) - - Move to the "Main" initialization phase, finish the Python initialization. - -No module is imported during the "Core" phase and the ``importlib`` module is -not configured: the :ref:`Path Configuration ` is only -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 calculate the :ref:`Path Configuration -` in Python, after the Core phase and before the Main phase, -which is one of the :pep:`432` motivation. - -The "Core" phase is not properly defined: what should be and what should -not be available at this phase is not specified yet. The API is marked -as private and provisional: the API can be modified or even be removed -anytime until a proper public API is designed. - -Example running Python code between "Core" and "Main" initialization -phases:: - - void init_python(void) - { - PyStatus status; - - PyConfig config; - PyConfig_InitPythonConfig(&config); - config._init_main = 0; - - /* ... customize 'config' configuration ... */ - - status = Py_InitializeFromConfig(&config); - PyConfig_Clear(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } - - /* Use sys.stderr because sys.stdout is only created - by _Py_InitializeMain() */ - int res = PyRun_SimpleString( - "import sys; " - "print('Run Python code before _Py_InitializeMain', " - "file=sys.stderr)"); - if (res < 0) { - exit(1); - } - - /* ... put more configuration code here ... */ - - status = _Py_InitializeMain(); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } - } +This separation can be achieved by setting ``PyConfig.run_command`` to the empty +string during initialization (to prevent the interpreter from dropping into the +interactive prompt), and then subsequently executing the desired main module +code using ``__main__.__dict__`` as the global namespace. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index d6aa6b346417e5..7f149d5c03dfbb 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1375,3 +1375,7 @@ Removed * Creating :c:data:`immutable types ` with mutable bases was deprecated since 3.12 and now raises a :exc:`TypeError`. + +* Remove the private ``_Py_InitializeMain()`` function. It was a + :term:`provisional API` added to Python 3.8 by :pep:`587`. + (Contributed by Victor Stinner in :gh:`129033`.) diff --git a/Include/cpython/pylifecycle.h b/Include/cpython/pylifecycle.h index e46dfe59ec4630..86ce6e6f79824a 100644 --- a/Include/cpython/pylifecycle.h +++ b/Include/cpython/pylifecycle.h @@ -25,9 +25,6 @@ PyAPI_FUNC(PyStatus) Py_PreInitializeFromArgs( PyAPI_FUNC(PyStatus) Py_InitializeFromConfig( const PyConfig *config); -// Python 3.8 provisional API (PEP 587) -PyAPI_FUNC(PyStatus) _Py_InitializeMain(void); - PyAPI_FUNC(int) Py_RunMain(void); diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 1b55cd156d759d..a2400aa96c3ddd 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1274,24 +1274,6 @@ def test_init_run_main(self): } self.check_all_configs("test_init_run_main", config, api=API_PYTHON) - def test_init_main(self): - code = ('import _testinternalcapi, json; ' - 'print(json.dumps(_testinternalcapi.get_configs()))') - config = { - 'argv': ['-c', 'arg2'], - 'orig_argv': ['python3', - '-c', code, - 'arg2'], - 'program_name': './python3', - 'run_command': code + '\n', - 'parse_argv': True, - '_init_main': False, - 'sys_path_0': '', - } - self.check_all_configs("test_init_main", config, - api=API_PYTHON, - stderr="Run Python code before _Py_InitializeMain") - def test_init_parse_argv(self): config = { 'parse_argv': True, @@ -1768,7 +1750,6 @@ def test_init_warnoptions(self): def test_init_set_config(self): config = { - '_init_main': 0, 'bytes_warning': 2, 'warnoptions': ['error::BytesWarning'], } diff --git a/Misc/NEWS.d/next/C_API/2025-01-19-23-17-58.gh-issue-129033.cpRivP.rst b/Misc/NEWS.d/next/C_API/2025-01-19-23-17-58.gh-issue-129033.cpRivP.rst new file mode 100644 index 00000000000000..3cd19cc48e3416 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-01-19-23-17-58.gh-issue-129033.cpRivP.rst @@ -0,0 +1,3 @@ +Remove the private ``_Py_InitializeMain()`` function. It was a +:term:`provisional API` added to Python 3.8 by :pep:`587`. Patch by Victor +Stinner. diff --git a/Programs/_testembed.c b/Programs/_testembed.c index d15dd519dbf6af..3681a89376638a 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -1818,7 +1818,6 @@ static int test_init_set_config(void) PyConfig config; PyConfig_InitIsolatedConfig(&config); config_set_string(&config, &config.program_name, PROGRAM_NAME); - config._init_main = 0; config.bytes_warning = 0; init_from_config_clear(&config); @@ -1828,12 +1827,6 @@ static int test_init_set_config(void) return 1; } - // Finish initialization: main part - PyStatus status = _Py_InitializeMain(); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } - dump_config(); Py_Finalize(); return 0; @@ -2089,33 +2082,6 @@ static int test_init_run_main(void) } -static int test_init_main(void) -{ - PyConfig config; - PyConfig_InitPythonConfig(&config); - - configure_init_main(&config); - config._init_main = 0; - init_from_config_clear(&config); - - /* sys.stdout don't exist yet: it is created by _Py_InitializeMain() */ - int res = PyRun_SimpleString( - "import sys; " - "print('Run Python code before _Py_InitializeMain', " - "file=sys.stderr)"); - if (res < 0) { - exit(1); - } - - PyStatus status = _Py_InitializeMain(); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } - - return Py_RunMain(); -} - - static int test_run_main(void) { PyConfig config; @@ -2473,7 +2439,6 @@ static struct TestCase TestCases[] = { {"test_preinit_dont_parse_argv", test_preinit_dont_parse_argv}, {"test_init_read_set", test_init_read_set}, {"test_init_run_main", test_init_run_main}, - {"test_init_main", test_init_main}, {"test_init_sys_add", test_init_sys_add}, {"test_init_setpath", test_init_setpath}, {"test_init_setpath_config", test_init_setpath_config}, diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index f1ecee6a92e5a1..8ec12b437f8298 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1505,18 +1505,6 @@ Py_Initialize(void) } -PyStatus -_Py_InitializeMain(void) -{ - PyStatus status = _PyRuntime_Initialize(); - if (_PyStatus_EXCEPTION(status)) { - return status; - } - PyThreadState *tstate = _PyThreadState_GET(); - return pyinit_main(tstate); -} - - static void finalize_modules_delete_special(PyThreadState *tstate, int verbose) {