cpython/Lib/profiling/sampling/live_collector/trend_tracker.py

158 lines
4.9 KiB
Python
Raw Normal View History

"""TrendTracker - Encapsulated trend tracking for live profiling metrics.
This module provides trend tracking functionality for profiling metrics,
calculating direction indicators (up/down/stable) and managing associated
visual attributes like colors.
"""
import curses
from typing import Dict, Literal, Any
TrendDirection = Literal["up", "down", "stable"]
class TrendTracker:
"""
Tracks metric trends over time and provides visual indicators.
This class encapsulates all logic for:
- Tracking previous values of metrics
- Calculating trend directions (up/down/stable)
- Determining visual attributes (colors) for trends
- Managing enable/disable state
Example:
tracker = TrendTracker(colors_dict)
tracker.update("func1", "nsamples", 10)
trend = tracker.get_trend("func1", "nsamples")
color = tracker.get_color("func1", "nsamples")
"""
# Threshold for determining if a value has changed significantly
CHANGE_THRESHOLD = 0.001
def __init__(self, colors: Dict[str, int], enabled: bool = True):
"""
Initialize the trend tracker.
Args:
colors: Dictionary containing color attributes including
'trend_up', 'trend_down', 'trend_stable'
enabled: Whether trend tracking is initially enabled
"""
self._previous_values: Dict[Any, Dict[str, float]] = {}
self._enabled = enabled
self._colors = colors
@property
def enabled(self) -> bool:
"""Whether trend tracking is enabled."""
return self._enabled
def toggle(self) -> bool:
"""
Toggle trend tracking on/off.
Returns:
New enabled state
"""
self._enabled = not self._enabled
return self._enabled
def set_enabled(self, enabled: bool) -> None:
"""Set trend tracking enabled state."""
self._enabled = enabled
def update(self, key: Any, metric: str, value: float) -> TrendDirection:
"""
Update a metric value and calculate its trend.
Args:
key: Identifier for the entity (e.g., function)
metric: Name of the metric (e.g., 'nsamples', 'tottime')
value: Current value of the metric
Returns:
Trend direction: 'up', 'down', or 'stable'
"""
# Initialize storage for this key if needed
if key not in self._previous_values:
self._previous_values[key] = {}
# Get previous value, defaulting to current if not tracked yet
prev_value = self._previous_values[key].get(metric, value)
# Calculate trend
if value > prev_value + self.CHANGE_THRESHOLD:
trend = "up"
elif value < prev_value - self.CHANGE_THRESHOLD:
trend = "down"
else:
trend = "stable"
# Update previous value for next iteration
self._previous_values[key][metric] = value
return trend
def get_trend(self, key: Any, metric: str) -> TrendDirection:
"""
Get the current trend for a metric without updating.
Args:
key: Identifier for the entity
metric: Name of the metric
Returns:
Trend direction, or 'stable' if not tracked
"""
# This would require storing trends separately, which we don't do
# For now, return stable if not found
return "stable"
def get_color(self, trend: TrendDirection) -> int:
"""
Get the color attribute for a trend direction.
Args:
trend: The trend direction
Returns:
Curses color attribute (or A_NORMAL if disabled)
"""
if not self._enabled:
return curses.A_NORMAL
if trend == "up":
return self._colors.get("trend_up", curses.A_BOLD)
elif trend == "down":
return self._colors.get("trend_down", curses.A_BOLD)
else: # stable
return self._colors.get("trend_stable", curses.A_NORMAL)
def update_metrics(self, key: Any, metrics: Dict[str, float]) -> Dict[str, TrendDirection]:
"""
Update multiple metrics at once and get their trends.
Args:
key: Identifier for the entity
metrics: Dictionary of metric_name -> value
Returns:
Dictionary of metric_name -> trend_direction
"""
trends = {}
for metric, value in metrics.items():
trends[metric] = self.update(key, metric, value)
return trends
def clear(self) -> None:
"""Clear all tracked values (useful on stats reset)."""
self._previous_values.clear()
def __repr__(self) -> str:
"""String representation for debugging."""
status = "enabled" if self._enabled else "disabled"
tracked = len(self._previous_values)
return f"TrendTracker({status}, tracking {tracked} entities)"