https://github.com/python/cpython/commit/07410da204648efac126a42e7c9ae24031134a08
commit: 07410da204648efac126a42e7c9ae24031134a08
branch: main
author: Pablo Galindo Salgado <[email protected]>
committer: pablogsal <[email protected]>
date: 2025-11-21T00:35:37Z
summary:
gh-141645: Refactor tachyon's live TUI tests to not use private fields (#141806)
files:
A Lib/data.bin
M Lib/profiling/sampling/live_collector/collector.py
M Lib/profiling/sampling/live_collector/widgets.py
M Lib/test/test_profiling/test_sampling_profiler/test_live_collector_core.py
M
Lib/test/test_profiling/test_sampling_profiler/test_live_collector_interaction.py
M Lib/test/test_profiling/test_sampling_profiler/test_live_collector_ui.py
diff --git a/Lib/data.bin b/Lib/data.bin
new file mode 100644
index 00000000000000..1d1fab72c4aaa4
Binary files /dev/null and b/Lib/data.bin differ
diff --git a/Lib/profiling/sampling/live_collector/collector.py
b/Lib/profiling/sampling/live_collector/collector.py
index 137657f051ba35..4b69275a2f077f 100644
--- a/Lib/profiling/sampling/live_collector/collector.py
+++ b/Lib/profiling/sampling/live_collector/collector.py
@@ -137,20 +137,20 @@ def __init__(
self._saved_stderr = None
self._devnull = None
self._last_display_update = None
- self._max_sample_rate = 0 # Track maximum sample rate seen
- self._successful_samples = 0 # Track samples that captured frames
- self._failed_samples = 0 # Track samples that failed to capture frames
- self._display_update_interval = DISPLAY_UPDATE_INTERVAL # Instance
variable for display refresh rate
+ self.max_sample_rate = 0 # Track maximum sample rate seen
+ self.successful_samples = 0 # Track samples that captured frames
+ self.failed_samples = 0 # Track samples that failed to capture frames
+ self.display_update_interval = DISPLAY_UPDATE_INTERVAL # Instance
variable for display refresh rate
# Thread status statistics (bit flags)
- self._thread_status_counts = {
+ self.thread_status_counts = {
"has_gil": 0,
"on_cpu": 0,
"gil_requested": 0,
"unknown": 0,
"total": 0, # Total thread count across all samples
}
- self._gc_frame_samples = 0 # Track samples with GC frames
+ self.gc_frame_samples = 0 # Track samples with GC frames
# Interactive controls state
self.paused = False # Pause UI updates (profiling continues)
@@ -174,10 +174,10 @@ def __init__(
self._path_prefixes = self._get_common_path_prefixes()
# Widgets (initialized when display is available)
- self._header_widget = None
- self._table_widget = None
- self._footer_widget = None
- self._help_widget = None
+ self.header_widget = None
+ self.table_widget = None
+ self.footer_widget = None
+ self.help_widget = None
# Color mode
self._can_colorize = _colorize.can_colorize()
@@ -256,7 +256,7 @@ def _get_common_path_prefixes(self):
return prefixes
- def _simplify_path(self, filepath):
+ def simplify_path(self, filepath):
"""Simplify a file path by removing common prefixes."""
# Try to match against known prefixes
for prefix_path in self._path_prefixes:
@@ -268,7 +268,7 @@ def _simplify_path(self, filepath):
# If no match, return the original path
return filepath
- def _process_frames(self, frames, thread_id=None):
+ def process_frames(self, frames, thread_id=None):
"""Process a single thread's frame stack.
Args:
@@ -295,7 +295,7 @@ def _process_frames(self, frames, thread_id=None):
thread_data.result[top_location]["direct_calls"] += 1
def collect_failed_sample(self):
- self._failed_samples += 1
+ self.failed_samples += 1
self.total_samples += 1
def collect(self, stack_frames):
@@ -349,7 +349,7 @@ def collect(self, stack_frames):
frames = getattr(thread_info, "frame_info", None)
if frames:
- self._process_frames(frames, thread_id=thread_id)
+ self.process_frames(frames, thread_id=thread_id)
# Track thread IDs only for threads that actually have
samples
if (
@@ -375,12 +375,12 @@ def collect(self, stack_frames):
# Update cumulative thread status counts
for key, count in temp_status_counts.items():
- self._thread_status_counts[key] += count
+ self.thread_status_counts[key] += count
if has_gc_frame:
- self._gc_frame_samples += 1
+ self.gc_frame_samples += 1
- self._successful_samples += 1
+ self.successful_samples += 1
self.total_samples += 1
# Handle input on every sample for instant responsiveness
@@ -393,7 +393,7 @@ def collect(self, stack_frames):
if (
self._last_display_update is None
or (current_time - self._last_display_update)
- >= self._display_update_interval
+ >= self.display_update_interval
):
self._update_display()
self._last_display_update = current_time
@@ -401,7 +401,7 @@ def collect(self, stack_frames):
def _prepare_display_data(self, height):
"""Prepare data for display rendering."""
elapsed = self.elapsed_time
- stats_list = self._build_stats_list()
+ stats_list = self.build_stats_list()
# Calculate available space for stats
# Add extra lines for finished banner when in finished state
@@ -422,15 +422,15 @@ def _prepare_display_data(self, height):
def _initialize_widgets(self, colors):
"""Initialize widgets with display and colors."""
- if self._header_widget is None:
+ if self.header_widget is None:
# Initialize trend tracker with colors
if self._trend_tracker is None:
self._trend_tracker = TrendTracker(colors, enabled=True)
- self._header_widget = HeaderWidget(self.display, colors, self)
- self._table_widget = TableWidget(self.display, colors, self)
- self._footer_widget = FooterWidget(self.display, colors, self)
- self._help_widget = HelpWidget(self.display, colors)
+ self.header_widget = HeaderWidget(self.display, colors, self)
+ self.table_widget = TableWidget(self.display, colors, self)
+ self.footer_widget = FooterWidget(self.display, colors, self)
+ self.help_widget = HelpWidget(self.display, colors)
def _render_display_sections(
self, height, width, elapsed, stats_list, colors
@@ -442,12 +442,12 @@ def _render_display_sections(
self._initialize_widgets(colors)
# Render header
- line = self._header_widget.render(
+ line = self.header_widget.render(
line, width, elapsed=elapsed, stats_list=stats_list
)
# Render table
- line = self._table_widget.render(
+ line = self.table_widget.render(
line, width, height=height, stats_list=stats_list
)
@@ -473,7 +473,7 @@ def _update_display(self):
# Show help screen if requested
if self.show_help:
- self._help_widget.render(0, width, height=height)
+ self.help_widget.render(0, width, height=height)
self.display.refresh()
return
@@ -486,11 +486,11 @@ def _update_display(self):
)
# Footer
- self._footer_widget.render(height - 2, width)
+ self.footer_widget.render(height - 2, width)
# Show filter input prompt if in filter input mode
if self.filter_input_mode:
- self._footer_widget.render_filter_input_prompt(
+ self.footer_widget.render_filter_input_prompt(
height - 1, width
)
@@ -616,7 +616,7 @@ def _setup_colors(self):
"trend_stable": A_NORMAL,
}
- def _build_stats_list(self):
+ def build_stats_list(self):
"""Build and sort the statistics list."""
stats_list = []
result_source = self._get_current_result_source()
@@ -707,17 +707,17 @@ def reset_stats(self):
self.view_mode = "ALL"
self.current_thread_index = 0
self.total_samples = 0
- self._successful_samples = 0
- self._failed_samples = 0
- self._max_sample_rate = 0
- self._thread_status_counts = {
+ self.successful_samples = 0
+ self.failed_samples = 0
+ self.max_sample_rate = 0
+ self.thread_status_counts = {
"has_gil": 0,
"on_cpu": 0,
"gil_requested": 0,
"unknown": 0,
"total": 0,
}
- self._gc_frame_samples = 0
+ self.gc_frame_samples = 0
# Clear trend tracking
if self._trend_tracker is not None:
self._trend_tracker.clear()
@@ -886,14 +886,14 @@ def _handle_input(self):
elif ch == ord("+") or ch == ord("="):
# Decrease update interval (faster refresh)
- self._display_update_interval = max(
- 0.05, self._display_update_interval - 0.05
+ self.display_update_interval = max(
+ 0.05, self.display_update_interval - 0.05
) # Min 20Hz
elif ch == ord("-") or ch == ord("_"):
# Increase update interval (slower refresh)
- self._display_update_interval = min(
- 1.0, self._display_update_interval + 0.05
+ self.display_update_interval = min(
+ 1.0, self.display_update_interval + 0.05
) # Max 1Hz
elif ch == ord("c") or ch == ord("C"):
diff --git a/Lib/profiling/sampling/live_collector/widgets.py
b/Lib/profiling/sampling/live_collector/widgets.py
index ffc566a8a2a56e..2af8caa2c2f6d9 100644
--- a/Lib/profiling/sampling/live_collector/widgets.py
+++ b/Lib/profiling/sampling/live_collector/widgets.py
@@ -180,7 +180,7 @@ def draw_header_info(self, line, width, elapsed):
# Calculate display refresh rate
refresh_hz = (
- 1.0 / self.collector._display_update_interval if
self.collector._display_update_interval > 0 else 0
+ 1.0 / self.collector.display_update_interval if
self.collector.display_update_interval > 0 else 0
)
# Get current view mode and thread display
@@ -248,8 +248,8 @@ def draw_sample_stats(self, line, width, elapsed):
)
# Update max sample rate
- if sample_rate > self.collector._max_sample_rate:
- self.collector._max_sample_rate = sample_rate
+ if sample_rate > self.collector.max_sample_rate:
+ self.collector.max_sample_rate = sample_rate
col = 0
self.add_str(line, col, "Samples: ", curses.A_BOLD)
@@ -308,11 +308,11 @@ def draw_sample_stats(self, line, width, elapsed):
def draw_efficiency_bar(self, line, width):
"""Draw sample efficiency bar showing success/failure rates."""
success_pct = (
- self.collector._successful_samples
+ self.collector.successful_samples
/ max(1, self.collector.total_samples)
) * 100
failed_pct = (
- self.collector._failed_samples
+ self.collector.failed_samples
/ max(1, self.collector.total_samples)
) * 100
@@ -327,7 +327,7 @@ def draw_efficiency_bar(self, line, width):
bar_width = min(MAX_EFFICIENCY_BAR_WIDTH, available_width)
success_fill = int(
(
- self.collector._successful_samples
+ self.collector.successful_samples
/ max(1, self.collector.total_samples)
)
* bar_width
@@ -381,7 +381,7 @@ def draw_thread_status(self, line, width):
"""Draw thread status statistics and GC information."""
# Get status counts for current view mode
thread_data = self.collector._get_current_thread_data()
- status_counts = thread_data.as_status_dict() if thread_data else
self.collector._thread_status_counts
+ status_counts = thread_data.as_status_dict() if thread_data else
self.collector.thread_status_counts
# Calculate percentages
total_threads = max(1, status_counts["total"])
@@ -395,7 +395,7 @@ def draw_thread_status(self, line, width):
pct_gc = (thread_data.gc_frame_samples / total_samples) * 100
else:
total_samples = max(1, self.collector.total_samples)
- pct_gc = (self.collector._gc_frame_samples / total_samples) * 100
+ pct_gc = (self.collector.gc_frame_samples / total_samples) * 100
col = 0
self.add_str(line, col, "Threads: ", curses.A_BOLD)
@@ -809,7 +809,7 @@ def get_trend_color(column_name):
# File:line column
if col < width - 10:
- simplified_path = self.collector._simplify_path(filename)
+ simplified_path = self.collector.simplify_path(filename)
file_line = f"{simplified_path}:{lineno}"
remaining_width = width - col - 1
self.add_str(
diff --git
a/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_core.py
b/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_core.py
index bfdc9d830cf14c..04e6cd2f1fcb8b 100644
--- a/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_core.py
+++ b/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_core.py
@@ -36,7 +36,7 @@ def test_simplify_stdlib_path(self):
if os_file:
stdlib_dir = os.path.dirname(os.path.abspath(os_file))
test_path = os.path.join(stdlib_dir, "json", "decoder.py")
- simplified = collector._simplify_path(test_path)
+ simplified = collector.simplify_path(test_path)
# Should remove the stdlib prefix
self.assertNotIn(stdlib_dir, simplified)
self.assertIn("json", simplified)
@@ -45,7 +45,7 @@ def test_simplify_unknown_path(self):
"""Test that unknown paths are returned unchanged."""
collector = LiveStatsCollector(1000)
test_path = "/some/unknown/path/file.py"
- simplified = collector._simplify_path(test_path)
+ simplified = collector.simplify_path(test_path)
self.assertEqual(simplified, test_path)
@@ -56,7 +56,7 @@ def test_process_single_frame(self):
"""Test processing a single frame."""
collector = LiveStatsCollector(1000)
frames = [MockFrameInfo("test.py", 10, "test_func")]
- collector._process_frames(frames)
+ collector.process_frames(frames)
location = ("test.py", 10, "test_func")
self.assertEqual(collector.result[location]["direct_calls"], 1)
@@ -70,7 +70,7 @@ def test_process_multiple_frames(self):
MockFrameInfo("test.py", 20, "middle_func"),
MockFrameInfo("test.py", 30, "outer_func"),
]
- collector._process_frames(frames)
+ collector.process_frames(frames)
# Top frame (inner_func) should have both direct and cumulative
inner_loc = ("test.py", 10, "inner_func")
@@ -89,7 +89,7 @@ def test_process_multiple_frames(self):
def test_process_empty_frames(self):
"""Test processing empty frames list."""
collector = LiveStatsCollector(1000)
- collector._process_frames([])
+ collector.process_frames([])
# Should not raise an error and result should remain empty
self.assertEqual(len(collector.result), 0)
@@ -98,9 +98,9 @@ def test_process_frames_accumulation(self):
collector = LiveStatsCollector(1000)
frames = [MockFrameInfo("test.py", 10, "test_func")]
- collector._process_frames(frames)
- collector._process_frames(frames)
- collector._process_frames(frames)
+ collector.process_frames(frames)
+ collector.process_frames(frames)
+ collector.process_frames(frames)
location = ("test.py", 10, "test_func")
self.assertEqual(collector.result[location]["direct_calls"], 3)
@@ -112,7 +112,7 @@ def test_process_frames_with_thread_id(self):
frames = [MockFrameInfo("test.py", 10, "test_func")]
# Process frames with thread_id
- collector._process_frames(frames, thread_id=123)
+ collector.process_frames(frames, thread_id=123)
# Check aggregated result
location = ("test.py", 10, "test_func")
@@ -135,8 +135,8 @@ def test_process_frames_multiple_threads(self):
frames2 = [MockFrameInfo("test.py", 20, "other_func")]
# Process frames from different threads
- collector._process_frames(frames1, thread_id=123)
- collector._process_frames(frames2, thread_id=456)
+ collector.process_frames(frames1, thread_id=123)
+ collector.process_frames(frames2, thread_id=456)
# Check that both threads have their own data
self.assertIn(123, collector.per_thread_data)
@@ -199,8 +199,8 @@ def test_collect_with_frames(self):
location = ("test.py", 10, "test_func")
self.assertEqual(collector.result[location]["direct_calls"], 1)
- self.assertEqual(collector._successful_samples, 1)
- self.assertEqual(collector._failed_samples, 0)
+ self.assertEqual(collector.successful_samples, 1)
+ self.assertEqual(collector.failed_samples, 0)
def test_collect_with_empty_frames(self):
"""Test collect with empty frames."""
@@ -212,8 +212,8 @@ def test_collect_with_empty_frames(self):
collector.collect(stack_frames)
# Empty frames still count as successful since collect() was called
successfully
- self.assertEqual(collector._successful_samples, 1)
- self.assertEqual(collector._failed_samples, 0)
+ self.assertEqual(collector.successful_samples, 1)
+ self.assertEqual(collector.failed_samples, 0)
def test_collect_skip_idle_threads(self):
"""Test that idle threads are skipped when skip_idle=True."""
@@ -284,7 +284,7 @@ def setUp(self):
def test_build_stats_list(self):
"""Test that stats list is built correctly."""
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 3)
# Check that all expected keys are present
@@ -298,7 +298,7 @@ def test_build_stats_list(self):
def test_sort_by_nsamples(self):
"""Test sorting by number of samples."""
self.collector.sort_by = "nsamples"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should be sorted by direct_calls descending
self.assertEqual(stats_list[0]["func"][2], "func1") # 100 samples
@@ -308,7 +308,7 @@ def test_sort_by_nsamples(self):
def test_sort_by_tottime(self):
"""Test sorting by total time."""
self.collector.sort_by = "tottime"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should be sorted by total_time descending
# total_time = direct_calls * sample_interval_sec
@@ -319,7 +319,7 @@ def test_sort_by_tottime(self):
def test_sort_by_cumtime(self):
"""Test sorting by cumulative time."""
self.collector.sort_by = "cumtime"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should be sorted by cumulative_time descending
self.assertEqual(stats_list[0]["func"][2], "func2") # 200 cumulative
@@ -329,7 +329,7 @@ def test_sort_by_cumtime(self):
def test_sort_by_sample_pct(self):
"""Test sorting by sample percentage."""
self.collector.sort_by = "sample_pct"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should be sorted by percentage of direct_calls
self.assertEqual(stats_list[0]["func"][2], "func1") # 33.3%
@@ -339,7 +339,7 @@ def test_sort_by_sample_pct(self):
def test_sort_by_cumul_pct(self):
"""Test sorting by cumulative percentage."""
self.collector.sort_by = "cumul_pct"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should be sorted by percentage of cumulative_calls
self.assertEqual(stats_list[0]["func"][2], "func2") # 66.7%
@@ -438,14 +438,14 @@ def test_format_uptime_seconds(self):
collector = LiveStatsCollector(1000, display=MockDisplay())
colors = collector._setup_colors()
collector._initialize_widgets(colors)
- self.assertEqual(collector._header_widget.format_uptime(45), "0m45s")
+ self.assertEqual(collector.header_widget.format_uptime(45), "0m45s")
def test_format_uptime_minutes(self):
"""Test uptime formatting for minutes."""
collector = LiveStatsCollector(1000, display=MockDisplay())
colors = collector._setup_colors()
collector._initialize_widgets(colors)
- self.assertEqual(collector._header_widget.format_uptime(125), "2m05s")
+ self.assertEqual(collector.header_widget.format_uptime(125), "2m05s")
def test_format_uptime_hours(self):
"""Test uptime formatting for hours."""
@@ -453,7 +453,7 @@ def test_format_uptime_hours(self):
colors = collector._setup_colors()
collector._initialize_widgets(colors)
self.assertEqual(
- collector._header_widget.format_uptime(3661), "1h01m01s"
+ collector.header_widget.format_uptime(3661), "1h01m01s"
)
def test_format_uptime_large_values(self):
@@ -462,7 +462,7 @@ def test_format_uptime_large_values(self):
colors = collector._setup_colors()
collector._initialize_widgets(colors)
self.assertEqual(
- collector._header_widget.format_uptime(86400), "24h00m00s"
+ collector.header_widget.format_uptime(86400), "24h00m00s"
)
def test_format_uptime_zero(self):
@@ -470,7 +470,7 @@ def test_format_uptime_zero(self):
collector = LiveStatsCollector(1000, display=MockDisplay())
colors = collector._setup_colors()
collector._initialize_widgets(colors)
- self.assertEqual(collector._header_widget.format_uptime(0), "0m00s")
+ self.assertEqual(collector.header_widget.format_uptime(0), "0m00s")
if __name__ == "__main__":
diff --git
a/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_interaction.py
b/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_interaction.py
index 3c226987323cc9..388f462cf21b3d 100644
---
a/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_interaction.py
+++
b/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_interaction.py
@@ -35,7 +35,7 @@ def setUp(self):
)
self.collector.start_time = time.perf_counter()
# Set a consistent display update interval for tests
- self.collector._display_update_interval = 0.1
+ self.collector.display_update_interval = 0.1
def tearDown(self):
"""Clean up after test."""
@@ -92,8 +92,8 @@ def test_reset_stats(self):
"""Test reset statistics functionality."""
# Add some stats
self.collector.total_samples = 100
- self.collector._successful_samples = 90
- self.collector._failed_samples = 10
+ self.collector.successful_samples = 90
+ self.collector.failed_samples = 10
self.collector.result[("test.py", 1, "func")] = {
"direct_calls": 50,
"cumulative_calls": 75,
@@ -104,51 +104,51 @@ def test_reset_stats(self):
self.collector.reset_stats()
self.assertEqual(self.collector.total_samples, 0)
- self.assertEqual(self.collector._successful_samples, 0)
- self.assertEqual(self.collector._failed_samples, 0)
+ self.assertEqual(self.collector.successful_samples, 0)
+ self.assertEqual(self.collector.failed_samples, 0)
self.assertEqual(len(self.collector.result), 0)
def test_increase_refresh_rate(self):
"""Test increasing refresh rate (faster updates)."""
- initial_interval = self.collector._display_update_interval
+ initial_interval = self.collector.display_update_interval
# Simulate '+' key press (faster = smaller interval)
self.display.simulate_input(ord("+"))
self.collector._handle_input()
- self.assertLess(self.collector._display_update_interval,
initial_interval)
+ self.assertLess(self.collector.display_update_interval,
initial_interval)
def test_decrease_refresh_rate(self):
"""Test decreasing refresh rate (slower updates)."""
- initial_interval = self.collector._display_update_interval
+ initial_interval = self.collector.display_update_interval
# Simulate '-' key press (slower = larger interval)
self.display.simulate_input(ord("-"))
self.collector._handle_input()
- self.assertGreater(self.collector._display_update_interval,
initial_interval)
+ self.assertGreater(self.collector.display_update_interval,
initial_interval)
def test_refresh_rate_minimum(self):
"""Test that refresh rate has a minimum (max speed)."""
- self.collector._display_update_interval = 0.05 # Set to minimum
+ self.collector.display_update_interval = 0.05 # Set to minimum
# Try to go faster
self.display.simulate_input(ord("+"))
self.collector._handle_input()
# Should stay at minimum
- self.assertEqual(self.collector._display_update_interval, 0.05)
+ self.assertEqual(self.collector.display_update_interval, 0.05)
def test_refresh_rate_maximum(self):
"""Test that refresh rate has a maximum (min speed)."""
- self.collector._display_update_interval = 1.0 # Set to maximum
+ self.collector.display_update_interval = 1.0 # Set to maximum
# Try to go slower
self.display.simulate_input(ord("-"))
self.collector._handle_input()
# Should stay at maximum
- self.assertEqual(self.collector._display_update_interval, 1.0)
+ self.assertEqual(self.collector.display_update_interval, 1.0)
def test_help_toggle(self):
"""Test help screen toggle."""
@@ -276,23 +276,23 @@ def test_filter_clear_uppercase(self):
def test_increase_refresh_rate_with_equals(self):
"""Test increasing refresh rate with '=' key."""
- initial_interval = self.collector._display_update_interval
+ initial_interval = self.collector.display_update_interval
# Simulate '=' key press (alternative to '+')
self.display.simulate_input(ord("="))
self.collector._handle_input()
- self.assertLess(self.collector._display_update_interval,
initial_interval)
+ self.assertLess(self.collector.display_update_interval,
initial_interval)
def test_decrease_refresh_rate_with_underscore(self):
"""Test decreasing refresh rate with '_' key."""
- initial_interval = self.collector._display_update_interval
+ initial_interval = self.collector.display_update_interval
# Simulate '_' key press (alternative to '-')
self.display.simulate_input(ord("_"))
self.collector._handle_input()
- self.assertGreater(self.collector._display_update_interval,
initial_interval)
+ self.assertGreater(self.collector.display_update_interval,
initial_interval)
def test_finished_state_displays_banner(self):
"""Test that finished state shows prominent banner."""
@@ -431,7 +431,7 @@ def test_filter_by_filename(self):
"""Test filtering by filename pattern."""
self.collector.filter_pattern = "models"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Only models.py should be included
self.assertEqual(len(stats_list), 1)
@@ -441,7 +441,7 @@ def test_filter_by_function_name(self):
"""Test filtering by function name."""
self.collector.filter_pattern = "render"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 1)
self.assertEqual(stats_list[0]["func"][2], "render")
@@ -450,7 +450,7 @@ def test_filter_case_insensitive(self):
"""Test that filtering is case-insensitive."""
self.collector.filter_pattern = "MODELS"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should still match models.py
self.assertEqual(len(stats_list), 1)
@@ -459,7 +459,7 @@ def test_filter_substring_matching(self):
"""Test substring filtering."""
self.collector.filter_pattern = "app/"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should match both app files
self.assertEqual(len(stats_list), 2)
@@ -468,7 +468,7 @@ def test_no_filter(self):
"""Test with no filter applied."""
self.collector.filter_pattern = None
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# All items should be included
self.assertEqual(len(stats_list), 3)
@@ -477,7 +477,7 @@ def test_filter_partial_function_name(self):
"""Test filtering by partial function name."""
self.collector.filter_pattern = "save"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 1)
self.assertEqual(stats_list[0]["func"][2], "save")
@@ -486,7 +486,7 @@ def test_filter_combined_filename_funcname(self):
"""Test filtering matches filename:funcname pattern."""
self.collector.filter_pattern = "views.py:render"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should match the combined pattern
self.assertEqual(len(stats_list), 1)
@@ -496,7 +496,7 @@ def test_filter_no_matches(self):
"""Test filter that matches nothing."""
self.collector.filter_pattern = "nonexistent"
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 0)
@@ -822,7 +822,7 @@ def test_arrow_keys_switch_to_per_thread_mode(self):
def test_stats_list_in_all_mode(self):
"""Test that stats list uses aggregated data in ALL mode."""
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should have all 3 functions
self.assertEqual(len(stats_list), 3)
@@ -835,7 +835,7 @@ def test_stats_list_in_per_thread_mode(self):
self.collector.view_mode = "PER_THREAD"
self.collector.current_thread_index = 0 # First thread (111)
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
# Should only have func1 from thread 111
self.assertEqual(len(stats_list), 1)
@@ -847,19 +847,19 @@ def test_stats_list_switches_with_thread_navigation(self):
# Thread 0 (111) -> func1
self.collector.current_thread_index = 0
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 1)
self.assertEqual(stats_list[0]["func"][2], "func1")
# Thread 1 (222) -> func2
self.collector.current_thread_index = 1
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 1)
self.assertEqual(stats_list[0]["func"][2], "func2")
# Thread 2 (333) -> func3
self.collector.current_thread_index = 2
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 1)
self.assertEqual(stats_list[0]["func"][2], "func3")
@@ -1036,8 +1036,8 @@ def
test_display_uses_per_thread_stats_in_per_thread_mode(self):
# In ALL mode, should show mixed stats (50% on GIL, 50% off GIL)
self.assertEqual(collector.view_mode, "ALL")
- total_has_gil = collector._thread_status_counts["has_gil"]
- total_threads = collector._thread_status_counts["total"]
+ total_has_gil = collector.thread_status_counts["has_gil"]
+ total_threads = collector.thread_status_counts["total"]
self.assertEqual(total_has_gil, 10) # Only thread 111 has GIL
self.assertEqual(total_threads, 20) # 10 samples * 2 threads
@@ -1082,7 +1082,7 @@ def
test_display_uses_per_thread_gc_stats_in_per_thread_mode(self):
# Check aggregated GC stats (ALL mode)
# 2 GC samples out of 10 total = 20%
- self.assertEqual(collector._gc_frame_samples, 2)
+ self.assertEqual(collector.gc_frame_samples, 2)
self.assertEqual(collector.total_samples, 5) # 5 collect() calls
# Check per-thread GC stats
diff --git
a/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_ui.py
b/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_ui.py
index 5c54022356b79c..b5a387fa3a3a71 100644
--- a/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_ui.py
+++ b/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_ui.py
@@ -68,20 +68,20 @@ def test_handle_input_sort_cycle(self):
def test_draw_methods_with_mock_display(self):
"""Test that draw methods write to mock display."""
self.collector.total_samples = 500
- self.collector._successful_samples = 450
- self.collector._failed_samples = 50
+ self.collector.successful_samples = 450
+ self.collector.failed_samples = 50
colors = self.collector._setup_colors()
self.collector._initialize_widgets(colors)
# Test individual widget methods
- line = self.collector._header_widget.draw_header_info(0, 160, 100.5)
+ line = self.collector.header_widget.draw_header_info(0, 160, 100.5)
self.assertEqual(line, 2) # Title + header info line
self.assertGreater(len(self.mock_display.buffer), 0)
# Clear buffer and test next method
self.mock_display.buffer.clear()
- line = self.collector._header_widget.draw_sample_stats(0, 160, 10.0)
+ line = self.collector.header_widget.draw_sample_stats(0, 160, 10.0)
self.assertEqual(line, 1)
self.assertGreater(len(self.mock_display.buffer), 0)
@@ -100,8 +100,8 @@ def test_full_display_rendering_with_data(self):
"""Test complete display rendering with realistic data."""
# Add multiple functions with different call counts
self.collector.total_samples = 1000
- self.collector._successful_samples = 950
- self.collector._failed_samples = 50
+ self.collector.successful_samples = 950
+ self.collector.failed_samples = 50
self.collector.result[("app.py", 10, "main")] = {
"direct_calls": 100,
@@ -135,12 +135,12 @@ def test_full_display_rendering_with_data(self):
def test_efficiency_bar_visualization(self):
"""Test that efficiency bar shows correct proportions."""
self.collector.total_samples = 100
- self.collector._successful_samples = 75
- self.collector._failed_samples = 25
+ self.collector.successful_samples = 75
+ self.collector.failed_samples = 25
colors = self.collector._setup_colors()
self.collector._initialize_widgets(colors)
- self.collector._header_widget.draw_efficiency_bar(0, 160)
+ self.collector.header_widget.draw_efficiency_bar(0, 160)
# Check that something was drawn to the display
self.assertGreater(len(self.mock_display.buffer), 0)
@@ -170,7 +170,7 @@ def test_stats_display_with_different_sort_modes(self):
self.mock_display.buffer.clear()
self.collector.sort_by = sort_mode
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
self.assertEqual(len(stats_list), 2)
# Verify sorting worked (func_b should be first for most modes)
@@ -186,7 +186,7 @@ def test_narrow_terminal_column_hiding(self):
colors = collector._setup_colors()
collector._initialize_widgets(colors)
line, show_sample_pct, show_tottime, show_cumul_pct, show_cumtime = (
- collector._table_widget.draw_column_headers(0, 70)
+ collector.table_widget.draw_column_headers(0, 70)
)
# On narrow terminal, some columns should be hidden
@@ -204,7 +204,7 @@ def test_very_narrow_terminal_minimal_columns(self):
colors = collector._setup_colors()
collector._initialize_widgets(colors)
line, show_sample_pct, show_tottime, show_cumul_pct, show_cumtime = (
- collector._table_widget.draw_column_headers(0, 60)
+ collector.table_widget.draw_column_headers(0, 60)
)
# Very narrow should hide even more columns
@@ -252,9 +252,9 @@ def test_top_functions_display(self):
colors = self.collector._setup_colors()
self.collector._initialize_widgets(colors)
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
- self.collector._header_widget.draw_top_functions(0, 160, stats_list)
+ self.collector.header_widget.draw_top_functions(0, 160, stats_list)
# Top functions section should have written something
self.assertGreater(len(self.mock_display.buffer), 0)
@@ -334,7 +334,7 @@ def test_add_str_with_mock_display(self):
colors = collector._setup_colors()
collector._initialize_widgets(colors)
- collector._header_widget.add_str(5, 10, "Test", 0)
+ collector.header_widget.add_str(5, 10, "Test", 0)
# Verify it was added to the buffer
self.assertIn((5, 10), mock_display.buffer)
@@ -444,7 +444,7 @@ def test_draw_header_info(self):
}
self.collector._initialize_widgets(colors)
- line = self.collector._header_widget.draw_header_info(0, 160, 100.5)
+ line = self.collector.header_widget.draw_header_info(0, 160, 100.5)
self.assertEqual(line, 2) # Title + header info line
def test_draw_sample_stats(self):
@@ -453,9 +453,9 @@ def test_draw_sample_stats(self):
colors = {"cyan": curses.A_BOLD, "green": curses.A_BOLD}
self.collector._initialize_widgets(colors)
- line = self.collector._header_widget.draw_sample_stats(0, 160, 10.0)
+ line = self.collector.header_widget.draw_sample_stats(0, 160, 10.0)
self.assertEqual(line, 1)
- self.assertGreater(self.collector._max_sample_rate, 0)
+ self.assertGreater(self.collector.max_sample_rate, 0)
def test_progress_bar_uses_target_rate(self):
"""Test that progress bar uses target rate instead of max rate."""
@@ -465,7 +465,7 @@ def test_progress_bar_uses_target_rate(self):
) # 10ms = 100Hz target
collector.start_time = time.perf_counter()
collector.total_samples = 500
- collector._max_sample_rate = (
+ collector.max_sample_rate = (
150 # Higher than target to test we don't use this
)
@@ -477,7 +477,7 @@ def test_progress_bar_uses_target_rate(self):
# Draw sample stats with a known elapsed time that gives us a specific
sample rate
elapsed = 10.0 # 500 samples in 10 seconds = 50 samples/second
- line = collector._header_widget.draw_sample_stats(0, 160, elapsed)
+ line = collector.header_widget.draw_sample_stats(0, 160, elapsed)
# Verify display was updated
self.assertEqual(line, 1)
@@ -543,7 +543,7 @@ def test_progress_bar_different_intervals(self):
collector.display.buffer.clear()
# Draw with 1 second elapsed time (gives us current rate of
100Hz)
- collector._header_widget.draw_sample_stats(0, 160, 1.0)
+ collector.header_widget.draw_sample_stats(0, 160, 1.0)
# Check that the current/target format appears in the display
with proper units
found_current_target_format = False
@@ -564,13 +564,13 @@ def test_progress_bar_different_intervals(self):
def test_draw_efficiency_bar(self):
"""Test drawing efficiency bar."""
- self.collector._successful_samples = 900
- self.collector._failed_samples = 100
+ self.collector.successful_samples = 900
+ self.collector.failed_samples = 100
self.collector.total_samples = 1000
colors = {"green": curses.A_BOLD, "red": curses.A_BOLD}
self.collector._initialize_widgets(colors)
- line = self.collector._header_widget.draw_efficiency_bar(0, 160)
+ line = self.collector.header_widget.draw_efficiency_bar(0, 160)
self.assertEqual(line, 1)
def test_draw_function_stats(self):
@@ -586,7 +586,7 @@ def test_draw_function_stats(self):
"total_rec_calls": 0,
}
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
colors = {
"cyan": curses.A_BOLD,
"green": curses.A_BOLD,
@@ -595,7 +595,7 @@ def test_draw_function_stats(self):
}
self.collector._initialize_widgets(colors)
- line = self.collector._header_widget.draw_function_stats(
+ line = self.collector.header_widget.draw_function_stats(
0, 160, stats_list
)
self.assertEqual(line, 1)
@@ -609,7 +609,7 @@ def test_draw_top_functions(self):
"total_rec_calls": 0,
}
- stats_list = self.collector._build_stats_list()
+ stats_list = self.collector.build_stats_list()
colors = {
"red": curses.A_BOLD,
"yellow": curses.A_BOLD,
@@ -617,7 +617,7 @@ def test_draw_top_functions(self):
}
self.collector._initialize_widgets(colors)
- line = self.collector._header_widget.draw_top_functions(
+ line = self.collector.header_widget.draw_top_functions(
0, 160, stats_list
)
self.assertEqual(line, 1)
@@ -636,7 +636,7 @@ def test_draw_column_headers(self):
show_tottime,
show_cumul_pct,
show_cumtime,
- ) = self.collector._table_widget.draw_column_headers(0, 160)
+ ) = self.collector.table_widget.draw_column_headers(0, 160)
self.assertEqual(line, 1)
self.assertTrue(show_sample_pct)
self.assertTrue(show_tottime)
@@ -657,7 +657,7 @@ def test_draw_column_headers_narrow_terminal(self):
show_tottime,
show_cumul_pct,
show_cumtime,
- ) = self.collector._table_widget.draw_column_headers(0, 70)
+ ) = self.collector.table_widget.draw_column_headers(0, 70)
self.assertEqual(line, 1)
# Some columns should be hidden on narrow terminal
self.assertFalse(show_cumul_pct)
@@ -666,7 +666,7 @@ def test_draw_footer(self):
"""Test drawing footer."""
colors = self.collector._setup_colors()
self.collector._initialize_widgets(colors)
- self.collector._footer_widget.render(38, 160)
+ self.collector.footer_widget.render(38, 160)
# Should have written some content to the display buffer
self.assertGreater(len(self.mock_display.buffer), 0)
@@ -674,7 +674,7 @@ def test_draw_progress_bar(self):
"""Test progress bar drawing."""
colors = self.collector._setup_colors()
self.collector._initialize_widgets(colors)
- bar, length = self.collector._header_widget.progress_bar.render_bar(
+ bar, length = self.collector.header_widget.progress_bar.render_bar(
50, 100, 30
)
@@ -699,7 +699,7 @@ def test_very_long_function_name(self):
"total_rec_calls": 0,
}
- stats_list = collector._build_stats_list()
+ stats_list = collector.build_stats_list()
self.assertEqual(len(stats_list), 1)
self.assertEqual(stats_list[0]["func"][2], long_name)
@@ -729,8 +729,8 @@ def test_update_display_terminal_too_small(self):
def test_update_display_normal(self):
"""Test normal update_display operation."""
self.collector.total_samples = 100
- self.collector._successful_samples = 90
- self.collector._failed_samples = 10
+ self.collector.successful_samples = 90
+ self.collector.failed_samples = 10
self.collector.result[("test.py", 10, "func")] = {
"direct_calls": 50,
"cumulative_calls": 75,
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]