diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8dbc8fb102cc60..57022de2b0e5ca 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,17 +4,23 @@ # It uses the same pattern rule for gitignore file # https://git-scm.com/docs/gitignore#_pattern_format -# GitHub +# Azure Pipelines +.azure-pipelines/ @AA-Turner + +# GitHub & related scripts .github/** @ezio-melotti @hugovk @AA-Turner +Tools/build/compute-changes.py @AA-Turner +Tools/build/verify_ensurepip_wheels.py @AA-Turner # pre-commit .pre-commit-config.yaml @hugovk .ruff.toml @hugovk @AlexWaygood @AA-Turner -# Build system -configure* @erlend-aasland @corona10 -Makefile.pre.in @erlend-aasland -Modules/Setup* @erlend-aasland +# Build system (autotools) +configure* @erlend-aasland @corona10 @AA-Turner +Makefile.pre.in @erlend-aasland @AA-Turner +Modules/Setup* @erlend-aasland @AA-Turner +Tools/build/regen-configure.sh @AA-Turner # argparse **/*argparse* @savannahostrowski @@ -67,6 +73,7 @@ Doc/make.bat @AA-Turner @hugovk Doc/requirements.txt @AA-Turner @hugovk Doc/_static/** @AA-Turner @hugovk Doc/tools/** @AA-Turner @hugovk +.readthedocs.yml @AA-Turner # runtime state/lifecycle **/*pylifecycle* @ericsnowcurrently @ZeroIntensity @@ -155,6 +162,10 @@ Doc/c-api/module.rst @ericsnowcurrently **/*importlib/resources/* @jaraco @warsaw @FFY00 **/*importlib/metadata/* @jaraco @warsaw +# Calendar +Lib/calendar.py @AA-Turner +Lib/test/test_calendar.py @AA-Turner + # Dates and times **/*datetime* @pganssle @abalkin **/*str*time* @pganssle @abalkin @@ -205,6 +216,11 @@ Lib/test/test_ast/ @eclips4 @tomasr8 # multiprocessing **/*multiprocessing* @gpshead +# pydoc +Lib/pydoc.py @AA-Turner +Lib/pydoc_data/ @AA-Turner +Lib/test/test_pydoc/ @AA-Turner + # SQLite 3 **/*sqlite* @berkerpeksag @erlend-aasland @@ -217,6 +233,11 @@ Lib/test/test_ast/ @eclips4 @tomasr8 **/*pdb* @gaogaotiantian **/*bdb* @gaogaotiantian +# types +Lib/test/test_types.py @AA-Turner +Lib/types.py @AA-Turner +Modules/_typesmodule.c @AA-Turner + # Limited C API & stable ABI Tools/build/stable_abi.py @encukou Misc/stable_abi.toml @encukou @@ -234,6 +255,11 @@ Doc/c-api/stable.rst @encukou /Tools/msi/ @python/windows-team /Tools/nuget/ @python/windows-team +# Zstandard +Lib/compression/zstd/ @AA-Turner +Lib/test/test_zstd.py @AA-Turner +Modules/_zstd/ @AA-Turner + # Misc **/*itertools* @rhettinger **/*collections* @rhettinger @@ -266,6 +292,9 @@ Doc/c-api/stable.rst @encukou **/*cjkcodecs* @corona10 +# Patchcheck +Tools/patchcheck/ @AA-Turner + # macOS /Mac/ @python/macos-team **/*osx_support* @python/macos-team @@ -277,9 +306,9 @@ Doc/c-api/stable.rst @encukou **/*zipfile/_path/* @jaraco # Argument Clinic -/Tools/clinic/** @erlend-aasland -/Lib/test/test_clinic.py @erlend-aasland -Doc/howto/clinic.rst @erlend-aasland +/Tools/clinic/** @erlend-aasland @AA-Turner +/Lib/test/test_clinic.py @erlend-aasland @AA-Turner +Doc/howto/clinic.rst @erlend-aasland @AA-Turner # Subinterpreters **/*interpreteridobject.* @ericsnowcurrently @@ -323,6 +352,7 @@ Lib/test/test_configparser.py @jaraco # Doc sections Doc/reference/ @willingc @AA-Turner +Doc/whatsnew/ @AA-Turner **/*weakref* @kumaraditya303 @@ -336,7 +366,7 @@ Modules/_xxtestfuzz/ @ammaraskar # t-strings **/*interpolationobject* @lysnikolaou **/*templateobject* @lysnikolaou -**/*templatelib* @lysnikolaou +**/*templatelib* @lysnikolaou @AA-Turner **/*tstring* @lysnikolaou # Remote debugging @@ -346,3 +376,6 @@ Modules/_remote_debugging_module.c @pablogsal @ambv @1st1 # gettext **/*gettext* @tomasr8 + +# Internal Docs +InternalDocs/ @AA-Turner diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 54964da473760d..89644a509a0bb4 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -418,6 +418,15 @@ ctypes (Contributed by Bénédikt Tran in :gh:`133866`.) +glob +---- + +* Removed the undocumented :func:`!glob.glob0` and :func:`!glob.glob1` + functions, which have been deprecated since Python 3.13. Use + :func:`glob.glob` and pass a directory to its *root_dir* argument instead. + (Contributed by Barney Gale in :gh:`137466`.) + + http.server ----------- diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py index ff3a69d1e17297..5ef2e1f9efc9ed 100644 --- a/Lib/asyncio/__main__.py +++ b/Lib/asyncio/__main__.py @@ -1,7 +1,6 @@ import argparse import ast import asyncio -import asyncio.tools import concurrent.futures import contextvars import inspect @@ -11,6 +10,9 @@ import threading import types import warnings +from asyncio.tools import (TaskTableOutputFormat, + display_awaited_by_tasks_table, + display_awaited_by_tasks_tree) from _colorize import get_theme from _pyrepl.console import InteractiveColoredConsole @@ -153,6 +155,11 @@ def interrupt(self) -> None: "ps", help="Display a table of all pending tasks in a process" ) ps.add_argument("pid", type=int, help="Process ID to inspect") + formats = [fmt.value for fmt in TaskTableOutputFormat] + formats_to_show = [fmt for fmt in formats + if fmt != TaskTableOutputFormat.bsv.value] + ps.add_argument("--format", choices=formats, default="table", + metavar=f"{{{','.join(formats_to_show)}}}") pstree = subparsers.add_parser( "pstree", help="Display a tree of all pending tasks in a process" ) @@ -160,10 +167,10 @@ def interrupt(self) -> None: args = parser.parse_args() match args.command: case "ps": - asyncio.tools.display_awaited_by_tasks_table(args.pid) + display_awaited_by_tasks_table(args.pid, format=args.format) sys.exit(0) case "pstree": - asyncio.tools.display_awaited_by_tasks_tree(args.pid) + display_awaited_by_tasks_tree(args.pid) sys.exit(0) case None: pass # continue to the interactive shell diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index 2683f34cc7113b..3887fb8ab5bf52 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -1,8 +1,9 @@ """Tools to analyze tasks running in asyncio programs.""" -from collections import defaultdict, namedtuple +from collections import defaultdict +import csv from itertools import count -from enum import Enum +from enum import Enum, StrEnum, auto import sys from _remote_debugging import RemoteUnwinder, FrameInfo @@ -232,18 +233,56 @@ def _get_awaited_by_tasks(pid: int) -> list: sys.exit(1) -def display_awaited_by_tasks_table(pid: int) -> None: +class TaskTableOutputFormat(StrEnum): + table = auto() + csv = auto() + bsv = auto() + # 🍌SV is not just a format. It's a lifestyle. A philosophy. + # https://www.youtube.com/watch?v=RrsVi1P6n0w + + +def display_awaited_by_tasks_table(pid, *, format=TaskTableOutputFormat.table): """Build and print a table of all pending tasks under `pid`.""" tasks = _get_awaited_by_tasks(pid) table = build_task_table(tasks) - # Print the table in a simple tabular format - print( - f"{'tid':<10} {'task id':<20} {'task name':<20} {'coroutine stack':<50} {'awaiter chain':<50} {'awaiter name':<15} {'awaiter id':<15}" - ) - print("-" * 180) + format = TaskTableOutputFormat(format) + if format == TaskTableOutputFormat.table: + _display_awaited_by_tasks_table(table) + else: + _display_awaited_by_tasks_csv(table, format=format) + + +_row_header = ('tid', 'task id', 'task name', 'coroutine stack', + 'awaiter chain', 'awaiter name', 'awaiter id') + + +def _display_awaited_by_tasks_table(table): + """Print the table in a simple tabular format.""" + print(_fmt_table_row(*_row_header)) + print('-' * 180) for row in table: - print(f"{row[0]:<10} {row[1]:<20} {row[2]:<20} {row[3]:<50} {row[4]:<50} {row[5]:<15} {row[6]:<15}") + print(_fmt_table_row(*row)) + + +def _fmt_table_row(tid, task_id, task_name, coro_stack, + awaiter_chain, awaiter_name, awaiter_id): + # Format a single row for the table format + return (f'{tid:<10} {task_id:<20} {task_name:<20} {coro_stack:<50} ' + f'{awaiter_chain:<50} {awaiter_name:<15} {awaiter_id:<15}') + + +def _display_awaited_by_tasks_csv(table, *, format): + """Print the table in CSV format""" + if format == TaskTableOutputFormat.csv: + delimiter = ',' + elif format == TaskTableOutputFormat.bsv: + delimiter = '\N{BANANA}' + else: + raise ValueError(f"Unknown output format: {format}") + csv_writer = csv.writer(sys.stdout, delimiter=delimiter) + csv_writer.writerow(_row_header) + csv_writer.writerows(table) def display_awaited_by_tasks_tree(pid: int) -> None: diff --git a/Lib/glob.py b/Lib/glob.py index 1e48fe43167200..5d42077003a240 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -122,21 +122,6 @@ def _glob0(dirname, basename, dir_fd, dironly, include_hidden=False): return [basename] return [] -_deprecated_function_message = ( - "{name} is deprecated and will be removed in Python {remove}. Use " - "glob.glob and pass a directory to its root_dir argument instead." -) - -def glob0(dirname, pattern): - import warnings - warnings._deprecated("glob.glob0", _deprecated_function_message, remove=(3, 15)) - return _glob0(dirname, pattern, None, False) - -def glob1(dirname, pattern): - import warnings - warnings._deprecated("glob.glob1", _deprecated_function_message, remove=(3, 15)) - return _glob1(dirname, pattern, None, False) - # This helper function recursively yields relative pathnames inside a literal # directory. diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py index d0ed5129253cdf..9e4e82b2542c15 100644 --- a/Lib/test/test_glob.py +++ b/Lib/test/test_glob.py @@ -4,7 +4,6 @@ import shutil import sys import unittest -import warnings from test.support import is_wasi, Py_DEBUG from test.support.os_helper import (TESTFN, skip_unless_symlink, @@ -393,36 +392,6 @@ def test_glob_many_open_files(self): for it in iters: self.assertEqual(next(it), p) - def test_glob0(self): - with self.assertWarns(DeprecationWarning): - glob.glob0(self.tempdir, 'a') - - with warnings.catch_warnings(): - warnings.simplefilter('ignore') - eq = self.assertSequencesEqual_noorder - eq(glob.glob0(self.tempdir, 'a'), ['a']) - eq(glob.glob0(self.tempdir, '.bb'), ['.bb']) - eq(glob.glob0(self.tempdir, '.b*'), []) - eq(glob.glob0(self.tempdir, 'b'), []) - eq(glob.glob0(self.tempdir, '?'), []) - eq(glob.glob0(self.tempdir, '*a'), []) - eq(glob.glob0(self.tempdir, 'a*'), []) - - def test_glob1(self): - with self.assertWarns(DeprecationWarning): - glob.glob1(self.tempdir, 'a') - - with warnings.catch_warnings(): - warnings.simplefilter('ignore') - eq = self.assertSequencesEqual_noorder - eq(glob.glob1(self.tempdir, 'a'), ['a']) - eq(glob.glob1(self.tempdir, '.bb'), ['.bb']) - eq(glob.glob1(self.tempdir, '.b*'), ['.bb']) - eq(glob.glob1(self.tempdir, 'b'), []) - eq(glob.glob1(self.tempdir, '?'), ['a']) - eq(glob.glob1(self.tempdir, '*a'), ['a', 'aaa']) - eq(glob.glob1(self.tempdir, 'a*'), ['a', 'aaa', 'aab']) - def test_translate_matching(self): match = re.compile(glob.translate('*')).match self.assertIsNotNone(match('foo')) diff --git a/Misc/NEWS.d/next/Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst b/Misc/NEWS.d/next/Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst new file mode 100644 index 00000000000000..07e4c61b404ba4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-29-19-00-37.gh-issue-134861.y2-fu-.rst @@ -0,0 +1 @@ +Add CSV as an output format for :program:`python -m asyncio ps`. diff --git a/Misc/NEWS.d/next/Library/2025-08-06-16-13-47.gh-issue-137466.Whv0-A.rst b/Misc/NEWS.d/next/Library/2025-08-06-16-13-47.gh-issue-137466.Whv0-A.rst new file mode 100644 index 00000000000000..918019aa8c03eb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-06-16-13-47.gh-issue-137466.Whv0-A.rst @@ -0,0 +1,3 @@ +Remove undocumented :func:`!glob.glob0` and :func:`!glob.glob1` functions, +which have been deprecated since Python 3.13. Use :func:`glob.glob` and pass +a directory to its *root_dir* argument instead. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-08-06-11-54-55.gh-issue-137484.8iFAQs.rst b/Misc/NEWS.d/next/Tools-Demos/2025-08-06-11-54-55.gh-issue-137484.8iFAQs.rst new file mode 100644 index 00000000000000..bd7bc0984ecfcc --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2025-08-06-11-54-55.gh-issue-137484.8iFAQs.rst @@ -0,0 +1,2 @@ +Have ``Tools/wasm/wasi`` put the build Python into a directory named after +the build triple instead of "build". diff --git a/Tools/wasm/wasi/__main__.py b/Tools/wasm/wasi/__main__.py index fdd93d5c0aa4af..973d78caa0849e 100644 --- a/Tools/wasm/wasi/__main__.py +++ b/Tools/wasm/wasi/__main__.py @@ -20,7 +20,8 @@ assert (CHECKOUT / "configure").is_file(), "Please update the location of the file" CROSS_BUILD_DIR = CHECKOUT / "cross-build" -BUILD_DIR = CROSS_BUILD_DIR / "build" +# Build platform can also be found via `config.guess`. +BUILD_DIR = CROSS_BUILD_DIR / sysconfig.get_config_var("BUILD_GNU_TYPE") LOCAL_SETUP = CHECKOUT / "Modules" / "Setup.local" LOCAL_SETUP_MARKER = ("# Generated by Tools/wasm/wasi .\n" @@ -80,7 +81,7 @@ def wrapper(context): print("📁", working_dir) if (clean_ok and getattr(context, "clean", False) and working_dir.exists()): - print(f"🚮 Deleting directory (--clean)...") + print("🚮 Deleting directory (--clean)...") shutil.rmtree(working_dir) working_dir.mkdir(parents=True, exist_ok=True) @@ -120,12 +121,6 @@ def call(command, *, context=None, quiet=False, logdir=None, **kwargs): subprocess.check_call(command, **kwargs, stdout=stdout, stderr=stderr) -def build_platform(): - """The name of the build/host platform.""" - # Can also be found via `config.guess`. - return sysconfig.get_config_var("BUILD_GNU_TYPE") - - def build_python_path(): """The path to the build Python binary.""" binary = BUILD_DIR / "python" @@ -274,7 +269,7 @@ def configure_wasi_python(context, working_dir): # executed from within a checkout. configure = [os.path.relpath(CHECKOUT / 'configure', working_dir), f"--host={context.host_triple}", - f"--build={build_platform()}", + f"--build={BUILD_DIR.name}", f"--with-build-python={build_python}"] if build_python_is_pydebug(): configure.append("--with-pydebug") @@ -357,8 +352,8 @@ def main(): "Python)") make_host = subcommands.add_parser("make-host", help="Run `make` for the host/WASI") - clean = subcommands.add_parser("clean", help="Delete files and directories " - "created by this script") + subcommands.add_parser("clean", help="Delete files and directories " + "created by this script") for subcommand in build, configure_build, make_build, configure_host, make_host: subcommand.add_argument("--quiet", action="store_true", default=False, dest="quiet",