Skip to content

Commit d90b29d

Browse files
authored
Merge branch 'main' into 3.14-zstd-c-code
2 parents 10e2e80 + 2bc8365 commit d90b29d

22 files changed

+1443
-117
lines changed

Doc/library/curses.rst

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,21 @@ The module :mod:`curses` defines the following exception:
6868
The module :mod:`curses` defines the following functions:
6969

7070

71+
.. function:: assume_default_colors(fg, bg)
72+
73+
Allow use of default values for colors on terminals supporting this feature.
74+
Use this to support transparency in your application.
75+
76+
* Assign terminal default foreground/background colors to color number ``-1``.
77+
So ``init_pair(x, COLOR_RED, -1)`` will initialize pair *x* as red
78+
on default background and ``init_pair(x, -1, COLOR_BLUE)`` will
79+
initialize pair *x* as default foreground on blue.
80+
81+
* Change the definition of the color-pair ``0`` to ``(fg, bg)``.
82+
83+
.. versionadded:: next
84+
85+
7186
.. function:: baudrate()
7287

7388
Return the output speed of the terminal in bits per second. On software
@@ -290,9 +305,11 @@ The module :mod:`curses` defines the following functions:
290305
Change the definition of a color-pair. It takes three arguments: the number of
291306
the color-pair to be changed, the foreground color number, and the background
292307
color number. The value of *pair_number* must be between ``1`` and
293-
``COLOR_PAIRS - 1`` (the ``0`` color pair is wired to white on black and cannot
294-
be changed). The value of *fg* and *bg* arguments must be between ``0`` and
295-
``COLORS - 1``, or, after calling :func:`use_default_colors`, ``-1``.
308+
``COLOR_PAIRS - 1`` (the ``0`` color pair can only be changed by
309+
:func:`use_default_colors` and :func:`assume_default_colors`).
310+
The value of *fg* and *bg* arguments must be between ``0`` and
311+
``COLORS - 1``, or, after calling :func:`!use_default_colors` or
312+
:func:`!assume_default_colors`, ``-1``.
296313
If the color-pair was previously initialized, the screen is
297314
refreshed and all occurrences of that color-pair are changed to the new
298315
definition.
@@ -678,11 +695,7 @@ The module :mod:`curses` defines the following functions:
678695

679696
.. function:: use_default_colors()
680697

681-
Allow use of default values for colors on terminals supporting this feature. Use
682-
this to support transparency in your application. The default color is assigned
683-
to the color number ``-1``. After calling this function, ``init_pair(x,
684-
curses.COLOR_RED, -1)`` initializes, for instance, color pair *x* to a red
685-
foreground color on the default background.
698+
Equivalent to ``assume_default_colors(-1, -1)``.
686699

687700

688701
.. function:: wrapper(func, /, *args, **kwargs)

Doc/whatsnew/3.14.rst

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,105 @@ configuration mechanisms).
543543
.. seealso::
544544
:pep:`741`.
545545

546+
.. _whatsnew314-asyncio-introspection:
547+
548+
Asyncio introspection capabilities
549+
----------------------------------
550+
551+
Added a new command-line interface to inspect running Python processes using
552+
asynchronous tasks, available via:
553+
554+
.. code-block:: bash
555+
556+
python -m asyncio ps PID
557+
558+
This tool inspects the given process ID (PID) and displays information about
559+
currently running asyncio tasks. It outputs a task table: a flat
560+
listing of all tasks, their names, their coroutine stacks, and which tasks are
561+
awaiting them.
562+
563+
.. code-block:: bash
564+
565+
python -m asyncio pstree PID
566+
567+
This tool fetches the same information, but renders a visual async call tree,
568+
showing coroutine relationships in a hierarchical format. This command is
569+
particularly useful for debugging long-running or stuck asynchronous programs.
570+
It can help developers quickly identify where a program is blocked, what tasks
571+
are pending, and how coroutines are chained together.
572+
573+
For example given this code:
574+
575+
.. code-block:: python
576+
577+
import asyncio
578+
579+
async def play(track):
580+
await asyncio.sleep(5)
581+
print(f"🎵 Finished: {track}")
582+
583+
async def album(name, tracks):
584+
async with asyncio.TaskGroup() as tg:
585+
for track in tracks:
586+
tg.create_task(play(track), name=track)
587+
588+
async def main():
589+
async with asyncio.TaskGroup() as tg:
590+
tg.create_task(
591+
album("Sundowning", ["TNDNBTG", "Levitate"]), name="Sundowning")
592+
tg.create_task(
593+
album("TMBTE", ["DYWTYLM", "Aqua Regia"]), name="TMBTE")
594+
595+
if __name__ == "__main__":
596+
asyncio.run(main())
597+
598+
Executing the new tool on the running process will yield a table like this:
599+
600+
.. code-block:: bash
601+
602+
python -m asyncio ps 12345
603+
604+
tid task id task name coroutine chain awaiter name awaiter id
605+
---------------------------------------------------------------------------------------------------------------------------------------
606+
8138752 0x564bd3d0210 Task-1 0x0
607+
8138752 0x564bd3d0410 Sundowning _aexit -> __aexit__ -> main Task-1 0x564bd3d0210
608+
8138752 0x564bd3d0610 TMBTE _aexit -> __aexit__ -> main Task-1 0x564bd3d0210
609+
8138752 0x564bd3d0810 TNDNBTG _aexit -> __aexit__ -> album Sundowning 0x564bd3d0410
610+
8138752 0x564bd3d0a10 Levitate _aexit -> __aexit__ -> album Sundowning 0x564bd3d0410
611+
8138752 0x564bd3e0550 DYWTYLM _aexit -> __aexit__ -> album TMBTE 0x564bd3d0610
612+
8138752 0x564bd3e0710 Aqua Regia _aexit -> __aexit__ -> album TMBTE 0x564bd3d0610
613+
614+
615+
or:
616+
617+
.. code-block:: bash
618+
619+
python -m asyncio pstree 12345
620+
621+
└── (T) Task-1
622+
└── main
623+
└── __aexit__
624+
└── _aexit
625+
├── (T) Sundowning
626+
│ └── album
627+
│ └── __aexit__
628+
│ └── _aexit
629+
│ ├── (T) TNDNBTG
630+
│ └── (T) Levitate
631+
└── (T) TMBTE
632+
└── album
633+
└── __aexit__
634+
└── _aexit
635+
├── (T) DYWTYLM
636+
└── (T) Aqua Regia
637+
638+
If a cycle is detected in the async await graph (which could indicate a
639+
programming issue), the tool raises an error and lists the cycle paths that
640+
prevent tree construction.
641+
642+
(Contributed by Pablo Galindo, Łukasz Langa, Yury Selivanov, and Marta
643+
Gomez Macias in :gh:`91048`.)
644+
546645
.. _whatsnew314-tail-call:
547646

548647
A new type of interpreter
@@ -891,6 +990,14 @@ ctypes
891990
making it a :term:`generic type`.
892991
(Contributed by Brian Schubert in :gh:`132168`.)
893992

993+
curses
994+
------
995+
996+
* Add the :func:`~curses.assume_default_colors` function,
997+
a refinement of the :func:`~curses.use_default_colors` function which
998+
allows to change the color pair ``0``.
999+
(Contributed by Serhiy Storchaka in :gh:`133139`.)
1000+
8941001
datetime
8951002
--------
8961003

Lib/asyncio/__main__.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import argparse
12
import ast
23
import asyncio
4+
import asyncio.tools
35
import concurrent.futures
46
import contextvars
57
import inspect
@@ -140,6 +142,36 @@ def interrupt(self) -> None:
140142

141143

142144
if __name__ == '__main__':
145+
parser = argparse.ArgumentParser(
146+
prog="python3 -m asyncio",
147+
description="Interactive asyncio shell and CLI tools",
148+
)
149+
subparsers = parser.add_subparsers(help="sub-commands", dest="command")
150+
ps = subparsers.add_parser(
151+
"ps", help="Display a table of all pending tasks in a process"
152+
)
153+
ps.add_argument("pid", type=int, help="Process ID to inspect")
154+
pstree = subparsers.add_parser(
155+
"pstree", help="Display a tree of all pending tasks in a process"
156+
)
157+
pstree.add_argument("pid", type=int, help="Process ID to inspect")
158+
args = parser.parse_args()
159+
match args.command:
160+
case "ps":
161+
asyncio.tools.display_awaited_by_tasks_table(args.pid)
162+
sys.exit(0)
163+
case "pstree":
164+
asyncio.tools.display_awaited_by_tasks_tree(args.pid)
165+
sys.exit(0)
166+
case None:
167+
pass # continue to the interactive shell
168+
case _:
169+
# shouldn't happen as an invalid command-line wouldn't parse
170+
# but let's keep it for the next person adding a command
171+
print(f"error: unhandled command {args.command}", file=sys.stderr)
172+
parser.print_usage(file=sys.stderr)
173+
sys.exit(1)
174+
143175
sys.audit("cpython.run_stdin")
144176

145177
if os.getenv('PYTHON_BASIC_REPL'):

0 commit comments

Comments
 (0)