From 00de79a3b3d8fd8be7ae3b2a9acbad6f48423c90 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 11:24:09 -0700 Subject: [PATCH 01/17] Add Python 3.15 profiling stubs --- stdlib/@tests/stubtest_allowlists/py315.txt | 3 -- stdlib/VERSIONS | 3 ++ stdlib/profiling/__init__.pyi | 3 ++ stdlib/profiling/sampling.pyi | 53 +++++++++++++++++++++ stdlib/profiling/tracing.pyi | 9 ++++ 5 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 stdlib/profiling/__init__.pyi create mode 100644 stdlib/profiling/sampling.pyi create mode 100644 stdlib/profiling/tracing.pyi diff --git a/stdlib/@tests/stubtest_allowlists/py315.txt b/stdlib/@tests/stubtest_allowlists/py315.txt index 25032c0a2219..f24289f5a683 100644 --- a/stdlib/@tests/stubtest_allowlists/py315.txt +++ b/stdlib/@tests/stubtest_allowlists/py315.txt @@ -178,8 +178,6 @@ posixpath.splitroot pprint.PrettyPrinter.__init__ pprint.pformat pprint.pprint -profiling -profiling.sampling profiling.sampling.binary_collector profiling.sampling.binary_reader profiling.sampling.cli @@ -196,7 +194,6 @@ profiling.sampling.pstats_collector profiling.sampling.sample profiling.sampling.stack_collector profiling.sampling.string_table -profiling.tracing pydoc.Doc.STDLIB_DIR pydoc.Doc.getdocloc site.addsitedir diff --git a/stdlib/VERSIONS b/stdlib/VERSIONS index 5487195b7165..768a919dd39b 100644 --- a/stdlib/VERSIONS +++ b/stdlib/VERSIONS @@ -242,6 +242,9 @@ posix: 3.0- posixpath: 3.0- pprint: 3.0- profile: 3.0- +profiling: 3.15- +profiling.sampling: 3.15- +profiling.tracing: 3.15- pstats: 3.0- pty: 3.0- pwd: 3.0- diff --git a/stdlib/profiling/__init__.pyi b/stdlib/profiling/__init__.pyi new file mode 100644 index 000000000000..ff869e903662 --- /dev/null +++ b/stdlib/profiling/__init__.pyi @@ -0,0 +1,3 @@ +from . import sampling as sampling, tracing as tracing + +__all__ = ["sampling", "tracing"] diff --git a/stdlib/profiling/sampling.pyi b/stdlib/profiling/sampling.pyi new file mode 100644 index 000000000000..ab1b9e8008ae --- /dev/null +++ b/stdlib/profiling/sampling.pyi @@ -0,0 +1,53 @@ +from _typeshed import StrOrBytesPath +from abc import ABC + +__all__ = [ + "Collector", + "PstatsCollector", + "CollapsedStackCollector", + "HeatmapCollector", + "GeckoCollector", + "JsonlCollector", + "StringTable", +] + +class Collector(ABC): + def collect(self, stack_frames, timestamps_us=None) -> None: ... + def collect_failed_sample(self) -> None: ... + def export(self, filename: StrOrBytesPath) -> None: ... + +class PstatsCollector(Collector): + def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... + def create_stats(self) -> None: ... + def print_stats(self, sort=-1, limit: int | None = None, show_summary: bool = True, mode=None) -> None: ... + +class CollapsedStackCollector(Collector): + def __init__(self, *args, **kwargs) -> None: ... + def process_frames(self, frames, thread_id: int, weight: int = 1) -> None: ... + +class HeatmapCollector(Collector): + FILE_INDEX_FORMAT: str + def __init__(self, *args, **kwargs) -> None: ... + def process_frames(self, frames, thread_id: int, weight: int = 1) -> None: ... + def set_stats( + self, + sample_interval_usec: int, + duration_sec: float, + sample_rate: float, + error_rate: float | None = None, + missed_samples: int | None = None, + **kwargs, + ) -> None: ... + +class GeckoCollector(Collector): + def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False, opcodes: bool = False) -> None: ... + +class JsonlCollector(Collector): + def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False, mode=None) -> None: ... + def process_frames(self, frames, _thread_id: int, weight: int = 1) -> None: ... + +class StringTable: + def intern(self, string: object) -> int: ... + def get_string(self, index: int) -> str: ... + def get_strings(self) -> list[str]: ... + def __len__(self) -> int: ... diff --git a/stdlib/profiling/tracing.pyi b/stdlib/profiling/tracing.pyi new file mode 100644 index 000000000000..2da9826998e5 --- /dev/null +++ b/stdlib/profiling/tracing.pyi @@ -0,0 +1,9 @@ +from cProfile import Profile as Profile, run as run, runctx as runctx +from types import CodeType +from typing import TypeAlias + +__all__ = ["run", "runctx", "Profile"] + +_Label: TypeAlias = tuple[str, int, str] + +def label(code: str | CodeType) -> _Label: ... # undocumented From acec6ffa52c59f7710ecea6088daa4657fa27c0c Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 11:25:55 -0700 Subject: [PATCH 02/17] Keep shared 3.15 allowlist entries --- stdlib/@tests/stubtest_allowlists/py315.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stdlib/@tests/stubtest_allowlists/py315.txt b/stdlib/@tests/stubtest_allowlists/py315.txt index f24289f5a683..25032c0a2219 100644 --- a/stdlib/@tests/stubtest_allowlists/py315.txt +++ b/stdlib/@tests/stubtest_allowlists/py315.txt @@ -178,6 +178,8 @@ posixpath.splitroot pprint.PrettyPrinter.__init__ pprint.pformat pprint.pprint +profiling +profiling.sampling profiling.sampling.binary_collector profiling.sampling.binary_reader profiling.sampling.cli @@ -194,6 +196,7 @@ profiling.sampling.pstats_collector profiling.sampling.sample profiling.sampling.stack_collector profiling.sampling.string_table +profiling.tracing pydoc.Doc.STDLIB_DIR pydoc.Doc.getdocloc site.addsitedir From 5be2eda046d8ae25bfa39df304a314b03ef01c2b Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 11:30:37 -0700 Subject: [PATCH 03/17] Match profiling runtime details --- stdlib/@tests/stubtest_allowlists/py315.txt | 22 +++------------------ stdlib/profiling/__init__.pyi | 2 +- stdlib/profiling/sampling.pyi | 12 +++++++---- stdlib/profiling/tracing.pyi | 2 +- 4 files changed, 13 insertions(+), 25 deletions(-) diff --git a/stdlib/@tests/stubtest_allowlists/py315.txt b/stdlib/@tests/stubtest_allowlists/py315.txt index 25032c0a2219..3d7369ab4d41 100644 --- a/stdlib/@tests/stubtest_allowlists/py315.txt +++ b/stdlib/@tests/stubtest_allowlists/py315.txt @@ -178,25 +178,9 @@ posixpath.splitroot pprint.PrettyPrinter.__init__ pprint.pformat pprint.pprint -profiling -profiling.sampling -profiling.sampling.binary_collector -profiling.sampling.binary_reader -profiling.sampling.cli -profiling.sampling.collector -profiling.sampling.constants -profiling.sampling.dump -profiling.sampling.errors -profiling.sampling.gecko_collector -profiling.sampling.heatmap_collector -profiling.sampling.jsonl_collector -profiling.sampling.module_utils -profiling.sampling.opcode_utils -profiling.sampling.pstats_collector -profiling.sampling.sample -profiling.sampling.stack_collector -profiling.sampling.string_table -profiling.tracing +profiling.__all__ +profiling\.sampling\..+ +profiling.tracing.__all__ pydoc.Doc.STDLIB_DIR pydoc.Doc.getdocloc site.addsitedir diff --git a/stdlib/profiling/__init__.pyi b/stdlib/profiling/__init__.pyi index ff869e903662..435f5a6cc66c 100644 --- a/stdlib/profiling/__init__.pyi +++ b/stdlib/profiling/__init__.pyi @@ -1,3 +1,3 @@ from . import sampling as sampling, tracing as tracing -__all__ = ["sampling", "tracing"] +__all__ = ("tracing", "sampling") diff --git a/stdlib/profiling/sampling.pyi b/stdlib/profiling/sampling.pyi index ab1b9e8008ae..634fbbc34fd2 100644 --- a/stdlib/profiling/sampling.pyi +++ b/stdlib/profiling/sampling.pyi @@ -1,7 +1,9 @@ +# pyright: reportMissingParameterType=false, reportUnknownParameterType=false + from _typeshed import StrOrBytesPath -from abc import ABC +from abc import ABC, abstractmethod -__all__ = [ +__all__ = ( "Collector", "PstatsCollector", "CollapsedStackCollector", @@ -9,12 +11,14 @@ __all__ = [ "GeckoCollector", "JsonlCollector", "StringTable", -] +) class Collector(ABC): + @abstractmethod def collect(self, stack_frames, timestamps_us=None) -> None: ... def collect_failed_sample(self) -> None: ... - def export(self, filename: StrOrBytesPath) -> None: ... + @abstractmethod + def export(self, output_path: StrOrBytesPath) -> None: ... class PstatsCollector(Collector): def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... diff --git a/stdlib/profiling/tracing.pyi b/stdlib/profiling/tracing.pyi index 2da9826998e5..af4ab508406a 100644 --- a/stdlib/profiling/tracing.pyi +++ b/stdlib/profiling/tracing.pyi @@ -2,7 +2,7 @@ from cProfile import Profile as Profile, run as run, runctx as runctx from types import CodeType from typing import TypeAlias -__all__ = ["run", "runctx", "Profile"] +__all__ = ("run", "runctx", "Profile") _Label: TypeAlias = tuple[str, int, str] From 84d876f9b28086b23fcc7b13fc10526add6183e5 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 11:32:58 -0700 Subject: [PATCH 04/17] Complete profiling collector methods --- stdlib/profiling/sampling.pyi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/stdlib/profiling/sampling.pyi b/stdlib/profiling/sampling.pyi index 634fbbc34fd2..1043916e6a70 100644 --- a/stdlib/profiling/sampling.pyi +++ b/stdlib/profiling/sampling.pyi @@ -22,16 +22,22 @@ class Collector(ABC): class PstatsCollector(Collector): def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... + def collect(self, stack_frames, timestamps_us=None) -> None: ... + def export(self, filename: StrOrBytesPath) -> None: ... def create_stats(self) -> None: ... def print_stats(self, sort=-1, limit: int | None = None, show_summary: bool = True, mode=None) -> None: ... class CollapsedStackCollector(Collector): def __init__(self, *args, **kwargs) -> None: ... + def collect(self, stack_frames, timestamps_us=None) -> None: ... + def export(self, filename: StrOrBytesPath) -> None: ... def process_frames(self, frames, thread_id: int, weight: int = 1) -> None: ... class HeatmapCollector(Collector): FILE_INDEX_FORMAT: str def __init__(self, *args, **kwargs) -> None: ... + def collect(self, stack_frames, timestamps_us=None) -> None: ... + def export(self, output_path: StrOrBytesPath) -> None: ... def process_frames(self, frames, thread_id: int, weight: int = 1) -> None: ... def set_stats( self, @@ -45,9 +51,13 @@ class HeatmapCollector(Collector): class GeckoCollector(Collector): def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False, opcodes: bool = False) -> None: ... + def collect(self, stack_frames, timestamps_us=None) -> None: ... + def export(self, filename: StrOrBytesPath) -> None: ... class JsonlCollector(Collector): def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False, mode=None) -> None: ... + def collect(self, stack_frames, timestamps_us=None) -> None: ... + def export(self, filename: StrOrBytesPath) -> None: ... def process_frames(self, frames, _thread_id: int, weight: int = 1) -> None: ... class StringTable: From d0b3f2b60f9e4658811898918a01d9391681564c Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 11:34:54 -0700 Subject: [PATCH 05/17] Remove covered profiling allowlist entries --- stdlib/@tests/stubtest_allowlists/py315.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/stdlib/@tests/stubtest_allowlists/py315.txt b/stdlib/@tests/stubtest_allowlists/py315.txt index 3d7369ab4d41..b1f1b8fbf0de 100644 --- a/stdlib/@tests/stubtest_allowlists/py315.txt +++ b/stdlib/@tests/stubtest_allowlists/py315.txt @@ -178,9 +178,6 @@ posixpath.splitroot pprint.PrettyPrinter.__init__ pprint.pformat pprint.pprint -profiling.__all__ -profiling\.sampling\..+ -profiling.tracing.__all__ pydoc.Doc.STDLIB_DIR pydoc.Doc.getdocloc site.addsitedir From bc4f386f97fce996dec05dcba73e3cb35171c059 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 11:37:03 -0700 Subject: [PATCH 06/17] Fix base profiling export parameter --- stdlib/profiling/sampling.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/profiling/sampling.pyi b/stdlib/profiling/sampling.pyi index 1043916e6a70..456fdb9d437a 100644 --- a/stdlib/profiling/sampling.pyi +++ b/stdlib/profiling/sampling.pyi @@ -18,7 +18,7 @@ class Collector(ABC): def collect(self, stack_frames, timestamps_us=None) -> None: ... def collect_failed_sample(self) -> None: ... @abstractmethod - def export(self, output_path: StrOrBytesPath) -> None: ... + def export(self, filename: StrOrBytesPath) -> None: ... class PstatsCollector(Collector): def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... From 0b05261c89b583d195bb83b823ea1100f6dd322f Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 11:41:42 -0700 Subject: [PATCH 07/17] Allowlist profiling sampling internals --- stdlib/@tests/stubtest_allowlists/py315.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/@tests/stubtest_allowlists/py315.txt b/stdlib/@tests/stubtest_allowlists/py315.txt index b1f1b8fbf0de..9580355729f3 100644 --- a/stdlib/@tests/stubtest_allowlists/py315.txt +++ b/stdlib/@tests/stubtest_allowlists/py315.txt @@ -178,6 +178,7 @@ posixpath.splitroot pprint.PrettyPrinter.__init__ pprint.pformat pprint.pprint +profiling\.sampling\..+ pydoc.Doc.STDLIB_DIR pydoc.Doc.getdocloc site.addsitedir From dc817c485591334a882d8e7f2dfa9732ff3cfb01 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 12:04:23 -0700 Subject: [PATCH 08/17] Annotate profiling sampling parameters --- stdlib/profiling/sampling.pyi | 68 ++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/stdlib/profiling/sampling.pyi b/stdlib/profiling/sampling.pyi index 456fdb9d437a..6944af1d294b 100644 --- a/stdlib/profiling/sampling.pyi +++ b/stdlib/profiling/sampling.pyi @@ -1,7 +1,7 @@ -# pyright: reportMissingParameterType=false, reportUnknownParameterType=false - from _typeshed import StrOrBytesPath from abc import ABC, abstractmethod +from collections.abc import Sequence +from typing import Protocol, TypeAlias __all__ = ( "Collector", @@ -13,52 +13,86 @@ __all__ = ( "StringTable", ) +_Location: TypeAlias = int | tuple[int, int, int, int] | None +_Timestamps: TypeAlias = Sequence[int] | None + +class _FrameInfo(Protocol): + filename: str + location: _Location + funcname: str + def __getitem__(self, index: int, /) -> object: ... + +class _ThreadInfo(Protocol): + status: int + thread_id: int + frame_info: Sequence[_FrameInfo] + +class _InterpreterInfo(Protocol): + threads: Sequence[_ThreadInfo] + +class _CoroutineInfo(Protocol): + call_stack: Sequence[_FrameInfo] + +class _TaskInfo(Protocol): + task_id: int + task_name: str + awaited_by: Sequence[_TaskInfo] + coroutine_stack: Sequence[_CoroutineInfo] + +class _AwaitedInfo(Protocol): + thread_id: int + awaited_by: Sequence[_TaskInfo] + +_StackFrames: TypeAlias = Sequence[_InterpreterInfo] | Sequence[_AwaitedInfo] + class Collector(ABC): @abstractmethod - def collect(self, stack_frames, timestamps_us=None) -> None: ... + def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... def collect_failed_sample(self) -> None: ... @abstractmethod def export(self, filename: StrOrBytesPath) -> None: ... class PstatsCollector(Collector): def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... - def collect(self, stack_frames, timestamps_us=None) -> None: ... + def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... def export(self, filename: StrOrBytesPath) -> None: ... def create_stats(self) -> None: ... - def print_stats(self, sort=-1, limit: int | None = None, show_summary: bool = True, mode=None) -> None: ... + def print_stats( + self, sort: int = -1, limit: int | None = None, show_summary: bool = True, mode: int | None = None + ) -> None: ... class CollapsedStackCollector(Collector): - def __init__(self, *args, **kwargs) -> None: ... - def collect(self, stack_frames, timestamps_us=None) -> None: ... + def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... + def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... def export(self, filename: StrOrBytesPath) -> None: ... - def process_frames(self, frames, thread_id: int, weight: int = 1) -> None: ... + def process_frames(self, frames: Sequence[_FrameInfo], thread_id: int, weight: int = 1) -> None: ... class HeatmapCollector(Collector): FILE_INDEX_FORMAT: str - def __init__(self, *args, **kwargs) -> None: ... - def collect(self, stack_frames, timestamps_us=None) -> None: ... + def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... + def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... def export(self, output_path: StrOrBytesPath) -> None: ... - def process_frames(self, frames, thread_id: int, weight: int = 1) -> None: ... + def process_frames(self, frames: Sequence[_FrameInfo], thread_id: int, weight: int = 1) -> None: ... def set_stats( self, sample_interval_usec: int, duration_sec: float, sample_rate: float, error_rate: float | None = None, - missed_samples: int | None = None, - **kwargs, + missed_samples: float | None = None, + **kwargs: object, ) -> None: ... class GeckoCollector(Collector): def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False, opcodes: bool = False) -> None: ... - def collect(self, stack_frames, timestamps_us=None) -> None: ... + def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... def export(self, filename: StrOrBytesPath) -> None: ... class JsonlCollector(Collector): - def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False, mode=None) -> None: ... - def collect(self, stack_frames, timestamps_us=None) -> None: ... + def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False, mode: int | None = None) -> None: ... + def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... def export(self, filename: StrOrBytesPath) -> None: ... - def process_frames(self, frames, _thread_id: int, weight: int = 1) -> None: ... + def process_frames(self, frames: Sequence[_FrameInfo], _thread_id: int, weight: int = 1) -> None: ... class StringTable: def intern(self, string: object) -> int: ... From e31d00cc82ebdb31a252cfd4a4d1c007d7030d77 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 13:36:10 -0700 Subject: [PATCH 09/17] Make profiling sampling a stub package --- stdlib/profiling/sampling.pyi | 101 ------------------ stdlib/profiling/sampling/__init__.pyi | 17 +++ stdlib/profiling/sampling/collector.pyi | 58 ++++++++++ stdlib/profiling/sampling/gecko_collector.pyi | 8 ++ .../profiling/sampling/heatmap_collector.pyi | 20 ++++ stdlib/profiling/sampling/jsonl_collector.pyi | 11 ++ .../profiling/sampling/pstats_collector.pyi | 12 +++ stdlib/profiling/sampling/stack_collector.pyi | 33 ++++++ stdlib/profiling/sampling/string_table.pyi | 5 + 9 files changed, 164 insertions(+), 101 deletions(-) delete mode 100644 stdlib/profiling/sampling.pyi create mode 100644 stdlib/profiling/sampling/__init__.pyi create mode 100644 stdlib/profiling/sampling/collector.pyi create mode 100644 stdlib/profiling/sampling/gecko_collector.pyi create mode 100644 stdlib/profiling/sampling/heatmap_collector.pyi create mode 100644 stdlib/profiling/sampling/jsonl_collector.pyi create mode 100644 stdlib/profiling/sampling/pstats_collector.pyi create mode 100644 stdlib/profiling/sampling/stack_collector.pyi create mode 100644 stdlib/profiling/sampling/string_table.pyi diff --git a/stdlib/profiling/sampling.pyi b/stdlib/profiling/sampling.pyi deleted file mode 100644 index 6944af1d294b..000000000000 --- a/stdlib/profiling/sampling.pyi +++ /dev/null @@ -1,101 +0,0 @@ -from _typeshed import StrOrBytesPath -from abc import ABC, abstractmethod -from collections.abc import Sequence -from typing import Protocol, TypeAlias - -__all__ = ( - "Collector", - "PstatsCollector", - "CollapsedStackCollector", - "HeatmapCollector", - "GeckoCollector", - "JsonlCollector", - "StringTable", -) - -_Location: TypeAlias = int | tuple[int, int, int, int] | None -_Timestamps: TypeAlias = Sequence[int] | None - -class _FrameInfo(Protocol): - filename: str - location: _Location - funcname: str - def __getitem__(self, index: int, /) -> object: ... - -class _ThreadInfo(Protocol): - status: int - thread_id: int - frame_info: Sequence[_FrameInfo] - -class _InterpreterInfo(Protocol): - threads: Sequence[_ThreadInfo] - -class _CoroutineInfo(Protocol): - call_stack: Sequence[_FrameInfo] - -class _TaskInfo(Protocol): - task_id: int - task_name: str - awaited_by: Sequence[_TaskInfo] - coroutine_stack: Sequence[_CoroutineInfo] - -class _AwaitedInfo(Protocol): - thread_id: int - awaited_by: Sequence[_TaskInfo] - -_StackFrames: TypeAlias = Sequence[_InterpreterInfo] | Sequence[_AwaitedInfo] - -class Collector(ABC): - @abstractmethod - def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... - def collect_failed_sample(self) -> None: ... - @abstractmethod - def export(self, filename: StrOrBytesPath) -> None: ... - -class PstatsCollector(Collector): - def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... - def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... - def export(self, filename: StrOrBytesPath) -> None: ... - def create_stats(self) -> None: ... - def print_stats( - self, sort: int = -1, limit: int | None = None, show_summary: bool = True, mode: int | None = None - ) -> None: ... - -class CollapsedStackCollector(Collector): - def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... - def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... - def export(self, filename: StrOrBytesPath) -> None: ... - def process_frames(self, frames: Sequence[_FrameInfo], thread_id: int, weight: int = 1) -> None: ... - -class HeatmapCollector(Collector): - FILE_INDEX_FORMAT: str - def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... - def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... - def export(self, output_path: StrOrBytesPath) -> None: ... - def process_frames(self, frames: Sequence[_FrameInfo], thread_id: int, weight: int = 1) -> None: ... - def set_stats( - self, - sample_interval_usec: int, - duration_sec: float, - sample_rate: float, - error_rate: float | None = None, - missed_samples: float | None = None, - **kwargs: object, - ) -> None: ... - -class GeckoCollector(Collector): - def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False, opcodes: bool = False) -> None: ... - def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... - def export(self, filename: StrOrBytesPath) -> None: ... - -class JsonlCollector(Collector): - def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False, mode: int | None = None) -> None: ... - def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... - def export(self, filename: StrOrBytesPath) -> None: ... - def process_frames(self, frames: Sequence[_FrameInfo], _thread_id: int, weight: int = 1) -> None: ... - -class StringTable: - def intern(self, string: object) -> int: ... - def get_string(self, index: int) -> str: ... - def get_strings(self) -> list[str]: ... - def __len__(self) -> int: ... diff --git a/stdlib/profiling/sampling/__init__.pyi b/stdlib/profiling/sampling/__init__.pyi new file mode 100644 index 000000000000..1f8b3f7d98fe --- /dev/null +++ b/stdlib/profiling/sampling/__init__.pyi @@ -0,0 +1,17 @@ +from .collector import Collector as Collector +from .gecko_collector import GeckoCollector as GeckoCollector +from .heatmap_collector import HeatmapCollector as HeatmapCollector +from .jsonl_collector import JsonlCollector as JsonlCollector +from .pstats_collector import PstatsCollector as PstatsCollector +from .stack_collector import CollapsedStackCollector as CollapsedStackCollector +from .string_table import StringTable as StringTable + +__all__ = ( + "Collector", + "PstatsCollector", + "CollapsedStackCollector", + "HeatmapCollector", + "GeckoCollector", + "JsonlCollector", + "StringTable", +) diff --git a/stdlib/profiling/sampling/collector.pyi b/stdlib/profiling/sampling/collector.pyi new file mode 100644 index 000000000000..7497249283e3 --- /dev/null +++ b/stdlib/profiling/sampling/collector.pyi @@ -0,0 +1,58 @@ +from _typeshed import StrOrBytesPath +from abc import ABC, abstractmethod +from collections.abc import Sequence +from typing import Protocol, TypeAlias + +_Location: TypeAlias = int | tuple[int, int, int, int] | _LocationInfo | None +_Frame: TypeAlias = _FrameInfo | tuple[str, _Location, str, int | None] +_Timestamps: TypeAlias = Sequence[int] | None +_StackFrames: TypeAlias = Sequence[_InterpreterInfo] | Sequence[_AwaitedInfo] + +class _LocationInfo(Protocol): + lineno: int + end_lineno: int + col_offset: int + end_col_offset: int + def __getitem__(self, index: int, /) -> int: ... + +class _FrameInfo(Protocol): + filename: str + location: _Location + funcname: str + opcode: int | None + def __getitem__(self, index: int, /) -> object: ... + +class _ThreadInfo(Protocol): + thread_id: int + status: int + frame_info: Sequence[_Frame] + +class _InterpreterInfo(Protocol): + interpreter_id: int + threads: Sequence[_ThreadInfo] + +class _CoroInfo(Protocol): + call_stack: Sequence[_Frame] + task_name: int | str + +class _TaskInfo(Protocol): + task_id: int + task_name: str + coroutine_stack: Sequence[_CoroInfo] + awaited_by: Sequence[_CoroInfo] + +class _AwaitedInfo(Protocol): + thread_id: int + awaited_by: Sequence[_TaskInfo] + +def normalize_location(location: _Location) -> tuple[int, int, int, int]: ... +def extract_lineno(location: _Location) -> int: ... +def filter_internal_frames(frames: Sequence[_Frame]) -> list[_Frame]: ... +def iter_async_frames(awaited_info_list: Sequence[_AwaitedInfo]) -> object: ... + +class Collector(ABC): + @abstractmethod + def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... + def collect_failed_sample(self) -> None: ... + @abstractmethod + def export(self, filename: StrOrBytesPath) -> None: ... diff --git a/stdlib/profiling/sampling/gecko_collector.pyi b/stdlib/profiling/sampling/gecko_collector.pyi new file mode 100644 index 000000000000..c918ddf9e661 --- /dev/null +++ b/stdlib/profiling/sampling/gecko_collector.pyi @@ -0,0 +1,8 @@ +from _typeshed import StrOrBytesPath + +from .collector import Collector, _StackFrames, _Timestamps + +class GeckoCollector(Collector): + def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False, opcodes: bool = False) -> None: ... + def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... + def export(self, filename: StrOrBytesPath) -> None: ... diff --git a/stdlib/profiling/sampling/heatmap_collector.pyi b/stdlib/profiling/sampling/heatmap_collector.pyi new file mode 100644 index 000000000000..966ca113dff6 --- /dev/null +++ b/stdlib/profiling/sampling/heatmap_collector.pyi @@ -0,0 +1,20 @@ +from _typeshed import StrOrBytesPath +from collections.abc import Sequence + +from .collector import Collector, _Frame, _StackFrames, _Timestamps + +class HeatmapCollector(Collector): + FILE_INDEX_FORMAT: str + def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... + def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... + def export(self, output_path: StrOrBytesPath) -> None: ... + def process_frames(self, frames: Sequence[_Frame], thread_id: int, weight: int = 1) -> None: ... + def set_stats( + self, + sample_interval_usec: int, + duration_sec: float, + sample_rate: float, + error_rate: float | None = None, + missed_samples: float | None = None, + **kwargs: object, + ) -> None: ... diff --git a/stdlib/profiling/sampling/jsonl_collector.pyi b/stdlib/profiling/sampling/jsonl_collector.pyi new file mode 100644 index 000000000000..baa24ec5b2ea --- /dev/null +++ b/stdlib/profiling/sampling/jsonl_collector.pyi @@ -0,0 +1,11 @@ +from _typeshed import StrOrBytesPath +from collections.abc import Sequence + +from .collector import _Frame, _StackFrames, _Timestamps +from .stack_collector import StackTraceCollector + +class JsonlCollector(StackTraceCollector): + def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False, mode: int | None = None) -> None: ... + def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... + def export(self, filename: StrOrBytesPath) -> None: ... + def process_frames(self, frames: Sequence[_Frame], _thread_id: int, weight: int = 1) -> None: ... diff --git a/stdlib/profiling/sampling/pstats_collector.pyi b/stdlib/profiling/sampling/pstats_collector.pyi new file mode 100644 index 000000000000..77dddfe1684a --- /dev/null +++ b/stdlib/profiling/sampling/pstats_collector.pyi @@ -0,0 +1,12 @@ +from _typeshed import StrOrBytesPath + +from .collector import Collector, _StackFrames, _Timestamps + +class PstatsCollector(Collector): + def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... + def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... + def export(self, filename: StrOrBytesPath) -> None: ... + def create_stats(self) -> None: ... + def print_stats( + self, sort: int = -1, limit: int | None = None, show_summary: bool = True, mode: int | None = None + ) -> None: ... diff --git a/stdlib/profiling/sampling/stack_collector.pyi b/stdlib/profiling/sampling/stack_collector.pyi new file mode 100644 index 000000000000..13c503672145 --- /dev/null +++ b/stdlib/profiling/sampling/stack_collector.pyi @@ -0,0 +1,33 @@ +from _typeshed import StrOrBytesPath +from abc import ABCMeta +from collections.abc import Sequence + +from .collector import Collector, _Frame, _StackFrames, _Timestamps + +class StackTraceCollector(Collector, metaclass=ABCMeta): + def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... + def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... + def process_frames(self, frames: Sequence[_Frame], thread_id: int, weight: int = 1) -> None: ... + +class CollapsedStackCollector(StackTraceCollector): + def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... + def process_frames(self, frames: Sequence[_Frame], thread_id: int, weight: int = 1) -> None: ... + def export(self, filename: StrOrBytesPath) -> None: ... + +class FlamegraphCollector(StackTraceCollector): + def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... + def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... + def set_stats( + self, + sample_interval_usec: int, + duration_sec: float, + sample_rate: float, + error_rate: float | None = None, + missed_samples: float | None = None, + mode: int | None = None, + ) -> None: ... + def export(self, filename: StrOrBytesPath) -> None: ... + def process_frames(self, frames: Sequence[_Frame], thread_id: int, weight: int = 1) -> None: ... + +class DiffFlamegraphCollector(FlamegraphCollector): + def __init__(self, sample_interval_usec: int, *, baseline_binary_path: StrOrBytesPath, skip_idle: bool = False) -> None: ... diff --git a/stdlib/profiling/sampling/string_table.pyi b/stdlib/profiling/sampling/string_table.pyi new file mode 100644 index 000000000000..cb71e82ec036 --- /dev/null +++ b/stdlib/profiling/sampling/string_table.pyi @@ -0,0 +1,5 @@ +class StringTable: + def intern(self, string: object) -> int: ... + def get_string(self, index: int) -> str: ... + def get_strings(self) -> list[str]: ... + def __len__(self) -> int: ... From 427d71264323ce05307ab53ccca80cf046b2f3f2 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 13:44:10 -0700 Subject: [PATCH 10/17] Use remote debugging structseqs in profiling --- stdlib/VERSIONS | 1 + stdlib/_remote_debugging.pyi | 154 ++++++++++++++++++++++++ stdlib/asyncio/tools.pyi | 25 +--- stdlib/profiling/sampling/collector.pyi | 49 ++------ 4 files changed, 164 insertions(+), 65 deletions(-) create mode 100644 stdlib/_remote_debugging.pyi diff --git a/stdlib/VERSIONS b/stdlib/VERSIONS index 768a919dd39b..158eb013fa4c 100644 --- a/stdlib/VERSIONS +++ b/stdlib/VERSIONS @@ -60,6 +60,7 @@ _py_abc: 3.7- _pydecimal: 3.5- _queue: 3.7- _random: 3.0- +_remote_debugging: 3.15- _sitebuiltins: 3.4- _socket: 3.0- # present in 3.0 at runtime, but not in typeshed _sqlite3: 3.0- diff --git a/stdlib/_remote_debugging.pyi b/stdlib/_remote_debugging.pyi new file mode 100644 index 000000000000..96ecd3346249 --- /dev/null +++ b/stdlib/_remote_debugging.pyi @@ -0,0 +1,154 @@ +from _typeshed import StrOrBytesPath, structseq +from collections.abc import Callable, Sequence +from typing import Final, Self, TypeAlias, final + +_Location: TypeAlias = int | LocationInfo | None +_Frame: TypeAlias = tuple[str, _Location, str, int | None] | FrameInfo +_StackFrames: TypeAlias = Sequence[InterpreterInfo] +_Stats: TypeAlias = dict[str, int | float] + +PROCESS_VM_READV_SUPPORTED: Final[int] +THREAD_STATUS_GIL_REQUESTED: Final[int] +THREAD_STATUS_HAS_EXCEPTION: Final[int] +THREAD_STATUS_HAS_GIL: Final[int] +THREAD_STATUS_MAIN_THREAD: Final[int] +THREAD_STATUS_ON_CPU: Final[int] +THREAD_STATUS_UNKNOWN: Final[int] + +@final +class LocationInfo(structseq[int], tuple[int, int, int, int]): + @property + def lineno(self) -> int: ... + @property + def end_lineno(self) -> int: ... + @property + def col_offset(self) -> int: ... + @property + def end_col_offset(self) -> int: ... + +@final +class FrameInfo(structseq[object], tuple[str, _Location, str, int | None]): + @property + def filename(self) -> str: ... + @property + def location(self) -> _Location: ... + @property + def funcname(self) -> str: ... + @property + def opcode(self) -> int | None: ... + +@final +class CoroInfo(structseq[object], tuple[list[_Frame], int | str]): + @property + def call_stack(self) -> list[_Frame]: ... + @property + def task_name(self) -> int | str: ... + +@final +class TaskInfo(structseq[object], tuple[int, str, list[CoroInfo], list[CoroInfo]]): + @property + def task_id(self) -> int: ... + @property + def task_name(self) -> str: ... + @property + def coroutine_stack(self) -> list[CoroInfo]: ... + @property + def awaited_by(self) -> list[CoroInfo]: ... + +@final +class ThreadInfo(structseq[object], tuple[int, int, list[_Frame]]): + @property + def thread_id(self) -> int: ... + @property + def status(self) -> int: ... + @property + def frame_info(self) -> list[_Frame]: ... + +@final +class InterpreterInfo(structseq[object], tuple[int, list[ThreadInfo]]): + @property + def interpreter_id(self) -> int: ... + @property + def threads(self) -> list[ThreadInfo]: ... + +@final +class AwaitedInfo(structseq[object], tuple[int, list[TaskInfo]]): + @property + def thread_id(self) -> int: ... + @property + def awaited_by(self) -> list[TaskInfo]: ... + +@final +class GCStatsInfo(structseq[object], tuple[int, int, int, int, int, int, int, int, int, float]): + @property + def gen(self) -> int: ... + @property + def iid(self) -> int: ... + @property + def ts_start(self) -> int: ... + @property + def ts_stop(self) -> int: ... + @property + def collections(self) -> int: ... + @property + def collected(self) -> int: ... + @property + def uncollectable(self) -> int: ... + @property + def candidates(self) -> int: ... + @property + def heap_size(self) -> int: ... + @property + def duration(self) -> float: ... + +class RemoteUnwinder: + def __init__( + self, + pid: int, + *, + all_threads: bool = False, + only_active_thread: bool = False, + mode: int = 0, + debug: bool = False, + skip_non_matching_threads: bool = True, + native: bool = False, + gc: bool = False, + ) -> None: ... + def get_stack_trace(self) -> list[InterpreterInfo]: ... + def get_all_awaited_by(self) -> list[AwaitedInfo]: ... + def get_async_stack_trace(self) -> list[AwaitedInfo]: ... + def get_stats(self) -> _Stats: ... + def pause_threads(self) -> bool: ... + def resume_threads(self) -> bool: ... + +class GCMonitor: + def __init__(self, pid: int, *, debug: bool = False) -> None: ... + def get_gc_stats(self, all_interpreters: bool = False) -> list[GCStatsInfo]: ... + +class BinaryWriter: + total_samples: int + def __init__( + self, filename: StrOrBytesPath, sample_interval_us: int, start_time_us: int, *, compression: int = 0 + ) -> None: ... + def write_sample(self, stack_frames: _StackFrames, timestamp_us: int) -> None: ... + def finalize(self) -> None: ... + def close(self) -> None: ... + def __enter__(self) -> Self: ... + def __exit__(self, exc_type: object = None, exc_val: object = None, exc_tb: object = None) -> bool: ... + def get_stats(self) -> _Stats: ... + +class BinaryReader: + sample_count: int + sample_interval_us: int + def __init__(self, filename: StrOrBytesPath) -> None: ... + def replay(self, collector: object, progress_callback: Callable[[int, int], object] | None = None) -> int: ... + def get_info(self) -> dict[str, object]: ... + def get_stats(self) -> _Stats: ... + def close(self) -> None: ... + def __enter__(self) -> Self: ... + def __exit__(self, exc_type: object = None, exc_val: object = None, exc_tb: object = None) -> bool: ... + +def zstd_available() -> bool: ... +def get_child_pids(pid: int, *, recursive: bool = True) -> list[int]: ... +def is_python_process(pid: int) -> bool: ... +def get_gc_stats(pid: int, *, all_interpreters: bool = False) -> list[GCStatsInfo]: ... diff --git a/stdlib/asyncio/tools.pyi b/stdlib/asyncio/tools.pyi index 36c65541c9cd..452876d7521b 100644 --- a/stdlib/asyncio/tools.pyi +++ b/stdlib/asyncio/tools.pyi @@ -1,30 +1,9 @@ import sys from collections.abc import Iterable from enum import Enum -from typing import NamedTuple, SupportsIndex, type_check_only +from typing import SupportsIndex -@type_check_only -class _AwaitedInfo(NamedTuple): # AwaitedInfo_Type from _remote_debugging - thread_id: int - awaited_by: list[_TaskInfo] - -@type_check_only -class _TaskInfo(NamedTuple): # TaskInfo_Type from _remote_debugging - task_id: int - task_name: str - coroutine_stack: list[_CoroInfo] - awaited_by: list[_CoroInfo] - -@type_check_only -class _CoroInfo(NamedTuple): # CoroInfo_Type from _remote_debugging - call_stack: list[_FrameInfo] - task_name: int | str - -@type_check_only -class _FrameInfo(NamedTuple): # FrameInfo_Type from _remote_debugging - filename: str - lineno: int - funcname: str +from _remote_debugging import AwaitedInfo as _AwaitedInfo class NodeType(Enum): COROUTINE = 1 diff --git a/stdlib/profiling/sampling/collector.pyi b/stdlib/profiling/sampling/collector.pyi index 7497249283e3..fe18907f4521 100644 --- a/stdlib/profiling/sampling/collector.pyi +++ b/stdlib/profiling/sampling/collector.pyi @@ -1,54 +1,19 @@ from _typeshed import StrOrBytesPath from abc import ABC, abstractmethod from collections.abc import Sequence -from typing import Protocol, TypeAlias +from typing import TypeAlias -_Location: TypeAlias = int | tuple[int, int, int, int] | _LocationInfo | None -_Frame: TypeAlias = _FrameInfo | tuple[str, _Location, str, int | None] -_Timestamps: TypeAlias = Sequence[int] | None -_StackFrames: TypeAlias = Sequence[_InterpreterInfo] | Sequence[_AwaitedInfo] - -class _LocationInfo(Protocol): - lineno: int - end_lineno: int - col_offset: int - end_col_offset: int - def __getitem__(self, index: int, /) -> int: ... - -class _FrameInfo(Protocol): - filename: str - location: _Location - funcname: str - opcode: int | None - def __getitem__(self, index: int, /) -> object: ... - -class _ThreadInfo(Protocol): - thread_id: int - status: int - frame_info: Sequence[_Frame] +from _remote_debugging import AwaitedInfo, FrameInfo, InterpreterInfo, LocationInfo -class _InterpreterInfo(Protocol): - interpreter_id: int - threads: Sequence[_ThreadInfo] - -class _CoroInfo(Protocol): - call_stack: Sequence[_Frame] - task_name: int | str - -class _TaskInfo(Protocol): - task_id: int - task_name: str - coroutine_stack: Sequence[_CoroInfo] - awaited_by: Sequence[_CoroInfo] - -class _AwaitedInfo(Protocol): - thread_id: int - awaited_by: Sequence[_TaskInfo] +_Location: TypeAlias = int | tuple[int, int, int, int] | LocationInfo | None +_Frame: TypeAlias = FrameInfo | tuple[str, _Location, str, int | None] +_Timestamps: TypeAlias = Sequence[int] | None +_StackFrames: TypeAlias = Sequence[InterpreterInfo] | Sequence[AwaitedInfo] def normalize_location(location: _Location) -> tuple[int, int, int, int]: ... def extract_lineno(location: _Location) -> int: ... def filter_internal_frames(frames: Sequence[_Frame]) -> list[_Frame]: ... -def iter_async_frames(awaited_info_list: Sequence[_AwaitedInfo]) -> object: ... +def iter_async_frames(awaited_info_list: Sequence[AwaitedInfo]) -> object: ... class Collector(ABC): @abstractmethod From 94306283129d925eaeebe43688ba572252a09fbc Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 13:48:40 -0700 Subject: [PATCH 11/17] Fix remote debugging stubtest issues --- stdlib/_remote_debugging.pyi | 30 +++++++++++++++++++++++++++++- stdlib/asyncio/tools.pyi | 25 +++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/stdlib/_remote_debugging.pyi b/stdlib/_remote_debugging.pyi index 96ecd3346249..669ace75aa5b 100644 --- a/stdlib/_remote_debugging.pyi +++ b/stdlib/_remote_debugging.pyi @@ -1,6 +1,7 @@ from _typeshed import StrOrBytesPath, structseq from collections.abc import Callable, Sequence -from typing import Final, Self, TypeAlias, final +from typing import Final, TypeAlias, final +from typing_extensions import Self, disjoint_base _Location: TypeAlias = int | LocationInfo | None _Frame: TypeAlias = tuple[str, _Location, str, int | None] | FrameInfo @@ -17,6 +18,7 @@ THREAD_STATUS_UNKNOWN: Final[int] @final class LocationInfo(structseq[int], tuple[int, int, int, int]): + __match_args__: Final = ("lineno", "end_lineno", "col_offset", "end_col_offset") @property def lineno(self) -> int: ... @property @@ -28,6 +30,7 @@ class LocationInfo(structseq[int], tuple[int, int, int, int]): @final class FrameInfo(structseq[object], tuple[str, _Location, str, int | None]): + __match_args__: Final = ("filename", "location", "funcname", "opcode") @property def filename(self) -> str: ... @property @@ -39,6 +42,7 @@ class FrameInfo(structseq[object], tuple[str, _Location, str, int | None]): @final class CoroInfo(structseq[object], tuple[list[_Frame], int | str]): + __match_args__: Final = ("call_stack", "task_name") @property def call_stack(self) -> list[_Frame]: ... @property @@ -46,6 +50,7 @@ class CoroInfo(structseq[object], tuple[list[_Frame], int | str]): @final class TaskInfo(structseq[object], tuple[int, str, list[CoroInfo], list[CoroInfo]]): + __match_args__: Final = ("task_id", "task_name", "coroutine_stack", "awaited_by") @property def task_id(self) -> int: ... @property @@ -57,6 +62,7 @@ class TaskInfo(structseq[object], tuple[int, str, list[CoroInfo], list[CoroInfo] @final class ThreadInfo(structseq[object], tuple[int, int, list[_Frame]]): + __match_args__: Final = ("thread_id", "status", "frame_info") @property def thread_id(self) -> int: ... @property @@ -66,6 +72,7 @@ class ThreadInfo(structseq[object], tuple[int, int, list[_Frame]]): @final class InterpreterInfo(structseq[object], tuple[int, list[ThreadInfo]]): + __match_args__: Final = ("interpreter_id", "threads") @property def interpreter_id(self) -> int: ... @property @@ -73,6 +80,7 @@ class InterpreterInfo(structseq[object], tuple[int, list[ThreadInfo]]): @final class AwaitedInfo(structseq[object], tuple[int, list[TaskInfo]]): + __match_args__: Final = ("thread_id", "awaited_by") @property def thread_id(self) -> int: ... @property @@ -80,6 +88,18 @@ class AwaitedInfo(structseq[object], tuple[int, list[TaskInfo]]): @final class GCStatsInfo(structseq[object], tuple[int, int, int, int, int, int, int, int, int, float]): + __match_args__: Final = ( + "gen", + "iid", + "ts_start", + "ts_stop", + "collections", + "collected", + "uncollectable", + "candidates", + "heap_size", + "duration", + ) @property def gen(self) -> int: ... @property @@ -101,6 +121,8 @@ class GCStatsInfo(structseq[object], tuple[int, int, int, int, int, int, int, in @property def duration(self) -> float: ... +@final +@disjoint_base class RemoteUnwinder: def __init__( self, @@ -121,10 +143,14 @@ class RemoteUnwinder: def pause_threads(self) -> bool: ... def resume_threads(self) -> bool: ... +@final +@disjoint_base class GCMonitor: def __init__(self, pid: int, *, debug: bool = False) -> None: ... def get_gc_stats(self, all_interpreters: bool = False) -> list[GCStatsInfo]: ... +@final +@disjoint_base class BinaryWriter: total_samples: int def __init__( @@ -137,6 +163,8 @@ class BinaryWriter: def __exit__(self, exc_type: object = None, exc_val: object = None, exc_tb: object = None) -> bool: ... def get_stats(self) -> _Stats: ... +@final +@disjoint_base class BinaryReader: sample_count: int sample_interval_us: int diff --git a/stdlib/asyncio/tools.pyi b/stdlib/asyncio/tools.pyi index 452876d7521b..36c65541c9cd 100644 --- a/stdlib/asyncio/tools.pyi +++ b/stdlib/asyncio/tools.pyi @@ -1,9 +1,30 @@ import sys from collections.abc import Iterable from enum import Enum -from typing import SupportsIndex +from typing import NamedTuple, SupportsIndex, type_check_only -from _remote_debugging import AwaitedInfo as _AwaitedInfo +@type_check_only +class _AwaitedInfo(NamedTuple): # AwaitedInfo_Type from _remote_debugging + thread_id: int + awaited_by: list[_TaskInfo] + +@type_check_only +class _TaskInfo(NamedTuple): # TaskInfo_Type from _remote_debugging + task_id: int + task_name: str + coroutine_stack: list[_CoroInfo] + awaited_by: list[_CoroInfo] + +@type_check_only +class _CoroInfo(NamedTuple): # CoroInfo_Type from _remote_debugging + call_stack: list[_FrameInfo] + task_name: int | str + +@type_check_only +class _FrameInfo(NamedTuple): # FrameInfo_Type from _remote_debugging + filename: str + lineno: int + funcname: str class NodeType(Enum): COROUTINE = 1 From 9f66b3f39856780445520a04f49fe4af667cde6f Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 13:51:41 -0700 Subject: [PATCH 12/17] Remove redundant disjoint markers --- stdlib/_remote_debugging.pyi | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/stdlib/_remote_debugging.pyi b/stdlib/_remote_debugging.pyi index 669ace75aa5b..252953763ae4 100644 --- a/stdlib/_remote_debugging.pyi +++ b/stdlib/_remote_debugging.pyi @@ -1,7 +1,7 @@ from _typeshed import StrOrBytesPath, structseq from collections.abc import Callable, Sequence from typing import Final, TypeAlias, final -from typing_extensions import Self, disjoint_base +from typing_extensions import Self _Location: TypeAlias = int | LocationInfo | None _Frame: TypeAlias = tuple[str, _Location, str, int | None] | FrameInfo @@ -122,7 +122,6 @@ class GCStatsInfo(structseq[object], tuple[int, int, int, int, int, int, int, in def duration(self) -> float: ... @final -@disjoint_base class RemoteUnwinder: def __init__( self, @@ -144,13 +143,11 @@ class RemoteUnwinder: def resume_threads(self) -> bool: ... @final -@disjoint_base class GCMonitor: def __init__(self, pid: int, *, debug: bool = False) -> None: ... def get_gc_stats(self, all_interpreters: bool = False) -> list[GCStatsInfo]: ... @final -@disjoint_base class BinaryWriter: total_samples: int def __init__( @@ -164,7 +161,6 @@ class BinaryWriter: def get_stats(self) -> _Stats: ... @final -@disjoint_base class BinaryReader: sample_count: int sample_interval_us: int From 8a6a2e39372b77525f930b9d0e86fcc2b1cd68ce Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 13:57:46 -0700 Subject: [PATCH 13/17] Refine remote debugging profiling stubs --- stdlib/_remote_debugging.pyi | 14 ++++++++------ stdlib/profiling/sampling/collector.pyi | 7 ++++--- stdlib/profiling/sampling/gecko_collector.pyi | 9 +++++++-- stdlib/profiling/sampling/heatmap_collector.pyi | 8 ++++++-- stdlib/profiling/sampling/jsonl_collector.pyi | 8 ++++++-- stdlib/profiling/sampling/pstats_collector.pyi | 9 +++++++-- stdlib/profiling/sampling/stack_collector.pyi | 12 +++++++++--- 7 files changed, 47 insertions(+), 20 deletions(-) diff --git a/stdlib/_remote_debugging.pyi b/stdlib/_remote_debugging.pyi index 252953763ae4..fd8a73e0b506 100644 --- a/stdlib/_remote_debugging.pyi +++ b/stdlib/_remote_debugging.pyi @@ -3,9 +3,8 @@ from collections.abc import Callable, Sequence from typing import Final, TypeAlias, final from typing_extensions import Self -_Location: TypeAlias = int | LocationInfo | None +_Location: TypeAlias = LocationInfo | None _Frame: TypeAlias = tuple[str, _Location, str, int | None] | FrameInfo -_StackFrames: TypeAlias = Sequence[InterpreterInfo] _Stats: TypeAlias = dict[str, int | float] PROCESS_VM_READV_SUPPORTED: Final[int] @@ -149,11 +148,12 @@ class GCMonitor: @final class BinaryWriter: - total_samples: int def __init__( self, filename: StrOrBytesPath, sample_interval_us: int, start_time_us: int, *, compression: int = 0 ) -> None: ... - def write_sample(self, stack_frames: _StackFrames, timestamp_us: int) -> None: ... + @property + def total_samples(self) -> int: ... + def write_sample(self, stack_frames: Sequence[InterpreterInfo], timestamp_us: int) -> None: ... def finalize(self) -> None: ... def close(self) -> None: ... def __enter__(self) -> Self: ... @@ -162,9 +162,11 @@ class BinaryWriter: @final class BinaryReader: - sample_count: int - sample_interval_us: int def __init__(self, filename: StrOrBytesPath) -> None: ... + @property + def sample_count(self) -> int: ... + @property + def sample_interval_us(self) -> int: ... def replay(self, collector: object, progress_callback: Callable[[int, int], object] | None = None) -> int: ... def get_info(self) -> dict[str, object]: ... def get_stats(self) -> _Stats: ... diff --git a/stdlib/profiling/sampling/collector.pyi b/stdlib/profiling/sampling/collector.pyi index fe18907f4521..db02e2d51e23 100644 --- a/stdlib/profiling/sampling/collector.pyi +++ b/stdlib/profiling/sampling/collector.pyi @@ -5,10 +5,9 @@ from typing import TypeAlias from _remote_debugging import AwaitedInfo, FrameInfo, InterpreterInfo, LocationInfo -_Location: TypeAlias = int | tuple[int, int, int, int] | LocationInfo | None +_Location: TypeAlias = tuple[int, int, int, int] | LocationInfo | None _Frame: TypeAlias = FrameInfo | tuple[str, _Location, str, int | None] _Timestamps: TypeAlias = Sequence[int] | None -_StackFrames: TypeAlias = Sequence[InterpreterInfo] | Sequence[AwaitedInfo] def normalize_location(location: _Location) -> tuple[int, int, int, int]: ... def extract_lineno(location: _Location) -> int: ... @@ -17,7 +16,9 @@ def iter_async_frames(awaited_info_list: Sequence[AwaitedInfo]) -> object: ... class Collector(ABC): @abstractmethod - def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... + def collect( + self, stack_frames: Sequence[InterpreterInfo] | Sequence[AwaitedInfo], timestamps_us: _Timestamps = None + ) -> None: ... def collect_failed_sample(self) -> None: ... @abstractmethod def export(self, filename: StrOrBytesPath) -> None: ... diff --git a/stdlib/profiling/sampling/gecko_collector.pyi b/stdlib/profiling/sampling/gecko_collector.pyi index c918ddf9e661..6072d4f2359a 100644 --- a/stdlib/profiling/sampling/gecko_collector.pyi +++ b/stdlib/profiling/sampling/gecko_collector.pyi @@ -1,8 +1,13 @@ from _typeshed import StrOrBytesPath +from collections.abc import Sequence -from .collector import Collector, _StackFrames, _Timestamps +from _remote_debugging import AwaitedInfo, InterpreterInfo + +from .collector import Collector, _Timestamps class GeckoCollector(Collector): def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False, opcodes: bool = False) -> None: ... - def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... + def collect( + self, stack_frames: Sequence[InterpreterInfo] | Sequence[AwaitedInfo], timestamps_us: _Timestamps = None + ) -> None: ... def export(self, filename: StrOrBytesPath) -> None: ... diff --git a/stdlib/profiling/sampling/heatmap_collector.pyi b/stdlib/profiling/sampling/heatmap_collector.pyi index 966ca113dff6..bd523bc38245 100644 --- a/stdlib/profiling/sampling/heatmap_collector.pyi +++ b/stdlib/profiling/sampling/heatmap_collector.pyi @@ -1,12 +1,16 @@ from _typeshed import StrOrBytesPath from collections.abc import Sequence -from .collector import Collector, _Frame, _StackFrames, _Timestamps +from _remote_debugging import AwaitedInfo, InterpreterInfo + +from .collector import Collector, _Frame, _Timestamps class HeatmapCollector(Collector): FILE_INDEX_FORMAT: str def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... - def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... + def collect( + self, stack_frames: Sequence[InterpreterInfo] | Sequence[AwaitedInfo], timestamps_us: _Timestamps = None + ) -> None: ... def export(self, output_path: StrOrBytesPath) -> None: ... def process_frames(self, frames: Sequence[_Frame], thread_id: int, weight: int = 1) -> None: ... def set_stats( diff --git a/stdlib/profiling/sampling/jsonl_collector.pyi b/stdlib/profiling/sampling/jsonl_collector.pyi index baa24ec5b2ea..3bdc4b81c01d 100644 --- a/stdlib/profiling/sampling/jsonl_collector.pyi +++ b/stdlib/profiling/sampling/jsonl_collector.pyi @@ -1,11 +1,15 @@ from _typeshed import StrOrBytesPath from collections.abc import Sequence -from .collector import _Frame, _StackFrames, _Timestamps +from _remote_debugging import AwaitedInfo, InterpreterInfo + +from .collector import _Frame, _Timestamps from .stack_collector import StackTraceCollector class JsonlCollector(StackTraceCollector): def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False, mode: int | None = None) -> None: ... - def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... + def collect( + self, stack_frames: Sequence[InterpreterInfo] | Sequence[AwaitedInfo], timestamps_us: _Timestamps = None + ) -> None: ... def export(self, filename: StrOrBytesPath) -> None: ... def process_frames(self, frames: Sequence[_Frame], _thread_id: int, weight: int = 1) -> None: ... diff --git a/stdlib/profiling/sampling/pstats_collector.pyi b/stdlib/profiling/sampling/pstats_collector.pyi index 77dddfe1684a..178d55a7af8e 100644 --- a/stdlib/profiling/sampling/pstats_collector.pyi +++ b/stdlib/profiling/sampling/pstats_collector.pyi @@ -1,10 +1,15 @@ from _typeshed import StrOrBytesPath +from collections.abc import Sequence -from .collector import Collector, _StackFrames, _Timestamps +from _remote_debugging import AwaitedInfo, InterpreterInfo + +from .collector import Collector, _Timestamps class PstatsCollector(Collector): def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... - def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... + def collect( + self, stack_frames: Sequence[InterpreterInfo] | Sequence[AwaitedInfo], timestamps_us: _Timestamps = None + ) -> None: ... def export(self, filename: StrOrBytesPath) -> None: ... def create_stats(self) -> None: ... def print_stats( diff --git a/stdlib/profiling/sampling/stack_collector.pyi b/stdlib/profiling/sampling/stack_collector.pyi index 13c503672145..0788e08295ad 100644 --- a/stdlib/profiling/sampling/stack_collector.pyi +++ b/stdlib/profiling/sampling/stack_collector.pyi @@ -2,11 +2,15 @@ from _typeshed import StrOrBytesPath from abc import ABCMeta from collections.abc import Sequence -from .collector import Collector, _Frame, _StackFrames, _Timestamps +from _remote_debugging import AwaitedInfo, InterpreterInfo + +from .collector import Collector, _Frame, _Timestamps class StackTraceCollector(Collector, metaclass=ABCMeta): def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... - def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... + def collect( + self, stack_frames: Sequence[InterpreterInfo] | Sequence[AwaitedInfo], timestamps_us: _Timestamps = None + ) -> None: ... def process_frames(self, frames: Sequence[_Frame], thread_id: int, weight: int = 1) -> None: ... class CollapsedStackCollector(StackTraceCollector): @@ -16,7 +20,9 @@ class CollapsedStackCollector(StackTraceCollector): class FlamegraphCollector(StackTraceCollector): def __init__(self, sample_interval_usec: int, *, skip_idle: bool = False) -> None: ... - def collect(self, stack_frames: _StackFrames, timestamps_us: _Timestamps = None) -> None: ... + def collect( + self, stack_frames: Sequence[InterpreterInfo] | Sequence[AwaitedInfo], timestamps_us: _Timestamps = None + ) -> None: ... def set_stats( self, sample_interval_usec: int, From f31b293a3a6dc72487e00850cde1ee0d0b39ea8d Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 14:19:10 -0700 Subject: [PATCH 14/17] Align remote debugging stubs with CPython --- stdlib/_remote_debugging.pyi | 9 ++++++--- stdlib/profiling/sampling/collector.pyi | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/stdlib/_remote_debugging.pyi b/stdlib/_remote_debugging.pyi index fd8a73e0b506..b001962aad7b 100644 --- a/stdlib/_remote_debugging.pyi +++ b/stdlib/_remote_debugging.pyi @@ -1,9 +1,9 @@ from _typeshed import StrOrBytesPath, structseq -from collections.abc import Callable, Sequence +from collections.abc import Callable from typing import Final, TypeAlias, final from typing_extensions import Self -_Location: TypeAlias = LocationInfo | None +_Location: TypeAlias = tuple[int, int, int, int] | LocationInfo | None _Frame: TypeAlias = tuple[str, _Location, str, int | None] | FrameInfo _Stats: TypeAlias = dict[str, int | float] @@ -133,6 +133,9 @@ class RemoteUnwinder: skip_non_matching_threads: bool = True, native: bool = False, gc: bool = False, + opcodes: bool = False, + cache_frames: bool = False, + stats: bool = False, ) -> None: ... def get_stack_trace(self) -> list[InterpreterInfo]: ... def get_all_awaited_by(self) -> list[AwaitedInfo]: ... @@ -153,7 +156,7 @@ class BinaryWriter: ) -> None: ... @property def total_samples(self) -> int: ... - def write_sample(self, stack_frames: Sequence[InterpreterInfo], timestamp_us: int) -> None: ... + def write_sample(self, stack_frames: list[InterpreterInfo], timestamp_us: int) -> None: ... def finalize(self) -> None: ... def close(self) -> None: ... def __enter__(self) -> Self: ... diff --git a/stdlib/profiling/sampling/collector.pyi b/stdlib/profiling/sampling/collector.pyi index db02e2d51e23..f191dbb66039 100644 --- a/stdlib/profiling/sampling/collector.pyi +++ b/stdlib/profiling/sampling/collector.pyi @@ -5,7 +5,7 @@ from typing import TypeAlias from _remote_debugging import AwaitedInfo, FrameInfo, InterpreterInfo, LocationInfo -_Location: TypeAlias = tuple[int, int, int, int] | LocationInfo | None +_Location: TypeAlias = int | tuple[int, int, int, int] | LocationInfo | None _Frame: TypeAlias = FrameInfo | tuple[str, _Location, str, int | None] _Timestamps: TypeAlias = Sequence[int] | None From 8f228756e0f3bd2dc4a7f52bbc0c3f8eb46387f1 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 14:30:37 -0700 Subject: [PATCH 15/17] Narrow profiling sampling allowlist --- .../stubtest_allowlists/darwin-py315.txt | 6 ---- .../stubtest_allowlists/linux-py315.txt | 6 ---- stdlib/@tests/stubtest_allowlists/py315.txt | 28 ++++++++++++++++++- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/stdlib/@tests/stubtest_allowlists/darwin-py315.txt b/stdlib/@tests/stubtest_allowlists/darwin-py315.txt index c7a5f5c82d3e..da5585d040b9 100644 --- a/stdlib/@tests/stubtest_allowlists/darwin-py315.txt +++ b/stdlib/@tests/stubtest_allowlists/darwin-py315.txt @@ -11,10 +11,4 @@ ctypes.c_longdouble_complex._type_ os.NODEV os.__all__ posix.NODEV -profiling.sampling.live_collector -profiling.sampling.live_collector.collector -profiling.sampling.live_collector.constants -profiling.sampling.live_collector.display -profiling.sampling.live_collector.trend_tracker -profiling.sampling.live_collector.widgets readline.get_pre_input_hook diff --git a/stdlib/@tests/stubtest_allowlists/linux-py315.txt b/stdlib/@tests/stubtest_allowlists/linux-py315.txt index 01f7975aaa97..58f7830d60fe 100644 --- a/stdlib/@tests/stubtest_allowlists/linux-py315.txt +++ b/stdlib/@tests/stubtest_allowlists/linux-py315.txt @@ -59,10 +59,4 @@ posix.STATX_SIZE posix.STATX_TYPE posix.STATX_UID posix.statx -profiling.sampling.live_collector -profiling.sampling.live_collector.collector -profiling.sampling.live_collector.constants -profiling.sampling.live_collector.display -profiling.sampling.live_collector.trend_tracker -profiling.sampling.live_collector.widgets readline.get_pre_input_hook diff --git a/stdlib/@tests/stubtest_allowlists/py315.txt b/stdlib/@tests/stubtest_allowlists/py315.txt index 9580355729f3..3a29c6e2d26d 100644 --- a/stdlib/@tests/stubtest_allowlists/py315.txt +++ b/stdlib/@tests/stubtest_allowlists/py315.txt @@ -178,7 +178,6 @@ posixpath.splitroot pprint.PrettyPrinter.__init__ pprint.pformat pprint.pprint -profiling\.sampling\..+ pydoc.Doc.STDLIB_DIR pydoc.Doc.getdocloc site.addsitedir @@ -259,3 +258,30 @@ xml.etree.ElementTree.__all__ xml.is_valid_name xml.utils zipimport.zipimporter.load_module + + +# ============================================================= +# Allowlist entries that cannot or should not be fixed; >= 3.15 +# ============================================================= + +# Internal implementation details of the sampling profiler. +profiling.sampling.__main__ +profiling.sampling._child_monitor +profiling.sampling._css_utils +profiling.sampling._format_utils +profiling.sampling._sync_coordinator +profiling.sampling.binary_collector +profiling.sampling.binary_reader +profiling.sampling.cli +profiling.sampling.constants +profiling.sampling.dump +profiling.sampling.errors +profiling.sampling.live_collector +profiling.sampling.live_collector.collector +profiling.sampling.live_collector.constants +profiling.sampling.live_collector.display +profiling.sampling.live_collector.trend_tracker +profiling.sampling.live_collector.widgets +profiling.sampling.module_utils +profiling.sampling.opcode_utils +profiling.sampling.sample From 887e2edda582f1c530e4f000ba4e428d5205365f Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 14:34:45 -0700 Subject: [PATCH 16/17] Fix profiling sampling allowlist entries --- stdlib/@tests/stubtest_allowlists/py315.txt | 25 ++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/stdlib/@tests/stubtest_allowlists/py315.txt b/stdlib/@tests/stubtest_allowlists/py315.txt index 3a29c6e2d26d..c1c5d6b73646 100644 --- a/stdlib/@tests/stubtest_allowlists/py315.txt +++ b/stdlib/@tests/stubtest_allowlists/py315.txt @@ -265,17 +265,32 @@ zipimport.zipimporter.load_module # ============================================================= # Internal implementation details of the sampling profiler. -profiling.sampling.__main__ -profiling.sampling._child_monitor -profiling.sampling._css_utils -profiling.sampling._format_utils -profiling.sampling._sync_coordinator profiling.sampling.binary_collector profiling.sampling.binary_reader profiling.sampling.cli profiling.sampling.constants profiling.sampling.dump profiling.sampling.errors +profiling.sampling.gecko_collector.CATEGORY_CODE_TYPE +profiling.sampling.gecko_collector.CATEGORY_CPU +profiling.sampling.gecko_collector.CATEGORY_EXCEPTION +profiling.sampling.gecko_collector.CATEGORY_GC +profiling.sampling.gecko_collector.CATEGORY_GIL +profiling.sampling.gecko_collector.CATEGORY_NATIVE +profiling.sampling.gecko_collector.CATEGORY_OPCODES +profiling.sampling.gecko_collector.CATEGORY_OTHER +profiling.sampling.gecko_collector.CATEGORY_PYTHON +profiling.sampling.gecko_collector.DEFAULT_SUBCATEGORY +profiling.sampling.gecko_collector.FRAME_ADDRESS_NONE +profiling.sampling.gecko_collector.FRAME_INLINE_DEPTH_ROOT +profiling.sampling.gecko_collector.GECKO_CATEGORIES +profiling.sampling.gecko_collector.GECKO_FORMAT_VERSION +profiling.sampling.gecko_collector.GECKO_PREPROCESSED_VERSION +profiling.sampling.gecko_collector.PROCESS_TYPE_MAIN +profiling.sampling.gecko_collector.RESOURCE_TYPE_LIBRARY +profiling.sampling.gecko_collector.STACKWALK_DISABLED +profiling.sampling.heatmap_collector.FileStats +profiling.sampling.heatmap_collector.TreeNode profiling.sampling.live_collector profiling.sampling.live_collector.collector profiling.sampling.live_collector.constants From cc76a369e6b26887503976b4e8e0a25e5deca8bd Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 May 2026 14:39:02 -0700 Subject: [PATCH 17/17] Split platform-specific profiling allowlist entries --- stdlib/@tests/stubtest_allowlists/darwin-py315.txt | 13 +++++++++++++ stdlib/@tests/stubtest_allowlists/linux-py315.txt | 13 +++++++++++++ stdlib/@tests/stubtest_allowlists/py315.txt | 6 ------ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/stdlib/@tests/stubtest_allowlists/darwin-py315.txt b/stdlib/@tests/stubtest_allowlists/darwin-py315.txt index da5585d040b9..0f5b248ad72b 100644 --- a/stdlib/@tests/stubtest_allowlists/darwin-py315.txt +++ b/stdlib/@tests/stubtest_allowlists/darwin-py315.txt @@ -12,3 +12,16 @@ os.NODEV os.__all__ posix.NODEV readline.get_pre_input_hook + + +# ============================================================= +# Allowlist entries that cannot or should not be fixed; >= 3.15 +# ============================================================= + +# Internal implementation details of the sampling profiler. +profiling.sampling.live_collector +profiling.sampling.live_collector.collector +profiling.sampling.live_collector.constants +profiling.sampling.live_collector.display +profiling.sampling.live_collector.trend_tracker +profiling.sampling.live_collector.widgets diff --git a/stdlib/@tests/stubtest_allowlists/linux-py315.txt b/stdlib/@tests/stubtest_allowlists/linux-py315.txt index 58f7830d60fe..fe1f0d179656 100644 --- a/stdlib/@tests/stubtest_allowlists/linux-py315.txt +++ b/stdlib/@tests/stubtest_allowlists/linux-py315.txt @@ -60,3 +60,16 @@ posix.STATX_TYPE posix.STATX_UID posix.statx readline.get_pre_input_hook + + +# ============================================================= +# Allowlist entries that cannot or should not be fixed; >= 3.15 +# ============================================================= + +# Internal implementation details of the sampling profiler. +profiling.sampling.live_collector +profiling.sampling.live_collector.collector +profiling.sampling.live_collector.constants +profiling.sampling.live_collector.display +profiling.sampling.live_collector.trend_tracker +profiling.sampling.live_collector.widgets diff --git a/stdlib/@tests/stubtest_allowlists/py315.txt b/stdlib/@tests/stubtest_allowlists/py315.txt index c1c5d6b73646..466b7ca98149 100644 --- a/stdlib/@tests/stubtest_allowlists/py315.txt +++ b/stdlib/@tests/stubtest_allowlists/py315.txt @@ -291,12 +291,6 @@ profiling.sampling.gecko_collector.RESOURCE_TYPE_LIBRARY profiling.sampling.gecko_collector.STACKWALK_DISABLED profiling.sampling.heatmap_collector.FileStats profiling.sampling.heatmap_collector.TreeNode -profiling.sampling.live_collector -profiling.sampling.live_collector.collector -profiling.sampling.live_collector.constants -profiling.sampling.live_collector.display -profiling.sampling.live_collector.trend_tracker -profiling.sampling.live_collector.widgets profiling.sampling.module_utils profiling.sampling.opcode_utils profiling.sampling.sample