Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Lib/profiling/sampling/pstats_collector.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import collections
import marshal
import pstats
lazy from _colorize import ANSIColors
lazy from _colorize import get_colors

from .collector import Collector, extract_lineno
from .constants import MICROSECONDS_PER_SECOND, PROFILING_MODE_CPU
Expand Down Expand Up @@ -178,6 +178,8 @@ def print_stats(self, sort=-1, limit=None, show_summary=True, mode=None):
}

# Print header with colors and proper alignment
ANSIColors = get_colors()

print(f"{ANSIColors.BOLD_BLUE}Profile Stats:{ANSIColors.RESET}")

header_nsamples = f"{ANSIColors.BOLD_BLUE}{'nsamples':>{col_widths['nsamples']}}{ANSIColors.RESET}"
Expand Down Expand Up @@ -269,6 +271,8 @@ def _determine_best_unit(max_value):

def _print_summary(self, stats_list, total_samples):
"""Print summary of interesting functions."""
ANSIColors = get_colors()

print(
f"\n{ANSIColors.BOLD_BLUE}Summary of Interesting Functions:{ANSIColors.RESET}"
)
Expand Down
8 changes: 7 additions & 1 deletion Lib/profiling/sampling/sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import sysconfig
import time
from collections import deque
lazy from _colorize import ANSIColors
lazy from _colorize import get_colors

from .binary_collector import BinaryCollector

Expand Down Expand Up @@ -272,6 +272,8 @@ def _print_realtime_stats(self):
) # Max time = Min Hz

# Build cache stats string if stats collection is enabled
ANSIColors = get_colors()

cache_stats_str = ""
if self.collect_stats:
try:
Expand Down Expand Up @@ -305,6 +307,8 @@ def _print_unwinder_stats(self):
except RuntimeError:
return # Stats not enabled

ANSIColors = get_colors()

print(f"\n{ANSIColors.BOLD_BLUE}{'='*50}{ANSIColors.RESET}")
print(f"{ANSIColors.BOLD_BLUE}Unwinder Statistics:{ANSIColors.RESET}")

Expand Down Expand Up @@ -399,6 +403,8 @@ def _print_binary_stats(self, collector):
except (ValueError, RuntimeError):
return # Collector closed or stats unavailable

ANSIColors = get_colors()

print(f" {ANSIColors.CYAN}Binary Encoding:{ANSIColors.RESET}")

repeat_records = stats.get('repeat_records', 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"Test only runs when _remote_debugging is available"
)

from test.support import force_not_colorized_test_class
from test.support import force_colorized, force_not_colorized_test_class


def print_sampled_stats(stats, sort=-1, limit=None, show_summary=True, sample_interval_usec=100):
Expand Down Expand Up @@ -484,6 +484,15 @@ def test_print_sampled_stats_basic(self):
self.assertIn("func1", result)
self.assertIn("func2", result)
self.assertIn("func3", result)
self.assertNotIn("\x1b[", result)

@force_colorized
def test_print_sampled_stats_colorized(self):
with io.StringIO() as output, mock.patch("sys.stdout", output):
print_sampled_stats(self.mock_stats)
result = output.getvalue()

self.assertIn("\x1b[1;34mProfile Stats:", result)

def test_print_sampled_stats_sorting(self):
"""Test different sorting options."""
Expand Down Expand Up @@ -751,6 +760,7 @@ def test_print_sampled_stats_sort_by_name(self):
and not "calls" in line # Skip summary lines
and not "total time" in line # Skip summary lines
and not "cumulative time" in line
and not "filename:lineno(function)" in line # Skip header line

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I'm mistaken, nothing in the tests fail if this regresses. TestPrintSampledStats has @force_not_colorized_test_class but do we assert if the output is free of escape codes?

I'm thinking about something like (sketch):

def test_print_sampled_stats_no_color(self):
    with io.StringIO() as output:
        with mock.patch("sys.stdout", output):
            print_sampled_stats(self.mock_stats, sample_interval_usec=100)
        self.assertNotIn("\x1b[", output.getvalue())

): # Skip summary lines
data_lines.append(line)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The :mod:`profiling.sampling` profiler now respects ``NO_COLOR``,
``FORCE_COLOR``, and ``PYTHON_COLORS`` and avoids colored output when
not writing to a terminal.
Loading