Coverage for aiopromql/models/core.py: 91%
44 statements
« prev ^ index » next coverage.py v7.8.2, created at 2025-05-29 17:49 +0300
« prev ^ index » next coverage.py v7.8.2, created at 2025-05-29 17:49 +0300
1"""
2Generic data structures for modeling time series and labeled metrics.
3"""
5from datetime import datetime
6from typing import Dict, List, NamedTuple
9class MetricLabelSet:
10 """
11 Hashable wrapper around a Prometheus metric label dictionary.
13 Prometheus metrics are identified by a set of key-value labels
14 (e.g., {"job": "api", "instance": "localhost:9090"}). This class allows such
15 a label set to be used as a key in Python dictionaries by making it hashable
16 and comparable.
18 Instances of this class are used as keys in the dictionary returned by
19 `PrometheusResponseModel.to_metric_map()`, where each MetricLabelSet maps to
20 a TimeSeries object.
21 """
23 def __init__(self, metric: Dict[str, str]):
24 self.dict = metric
25 self._key = frozenset(metric.items())
27 def __hash__(self) -> int:
28 return hash(self._key)
30 def __eq__(self, other) -> bool:
31 if not isinstance(other, MetricLabelSet):
32 return False
33 return self._key == other._key
35 def __repr__(self) -> str:
36 return f"MetricLabelSet({self.dict})"
38 def get(self, label: str, default=None):
39 """
40 Return the value for the given label key, or default if not present.
42 :param label: The label key to retrieve from the metric dictionary.
43 :type label: str
44 :param default: The value to return if the label is not found. Defaults to None.
45 :return: The value corresponding to the label, or the default if label is missing.
46 :rtype: str or Any
47 """
48 return self.dict.get(label, default)
51class TimeSeriesPoint(NamedTuple):
52 """
53 A single timestamped data point from a Prometheus time series.
55 Represents one (timestamp, value) pair, where the timestamp is a `datetime`
56 object and the value is a float. Useful for building time series from Prometheus
57 query results.
58 """
60 timestamp: datetime
61 value: float
63 @classmethod
64 def from_prometheus_value(cls, ts: float, value: str) -> "TimeSeriesPoint":
65 """
66 Converts a Prometheus response (timestamp, value) pair to TimeSeriesPoint.
68 Args:
69 ts: Epoch timestamp.
70 value: String representation of float value.
72 Returns:
73 A TimeSeriesPoint instance.
74 """
75 return cls(datetime.fromtimestamp(ts), float(value))
77 def __str__(self):
78 return f"{self.timestamp.isoformat()} → {self.value:.2f}"
81class TimeSeries:
82 """
83 A sequence of timestamped float values (TimeSeriesPoint) with utility methods.
85 This class abstracts a Prometheus time series and provides methods for inspection,
86 aggregation, and manipulation. Used in `PrometheusResponseModel.to_metric_map()`
87 where each MetricLabelSet maps to a TimeSeries.
88 """
90 def __init__(self, values: List[TimeSeriesPoint]):
91 """
92 Args:
93 values: List of initial TimeSeriesPoint objects.
94 """
95 self.values: List[TimeSeriesPoint] = values
97 def __iter__(self):
98 return iter(self.values)
100 def __len__(self):
101 return len(self.values)
103 def __getitem__(self, idx) -> TimeSeriesPoint:
104 return self.values[idx]
106 def __repr__(self):
107 return f"Values({self.values})"
109 def add_point(self, point: TimeSeriesPoint):
110 """Adds a new data point."""
111 self.values.append(point)
113 def extend(self, other: "TimeSeries"):
114 """Appends another TimeSeries' points to this one."""
115 self.values.extend(other.values)
117 def latest(self) -> TimeSeriesPoint | None:
118 """Returns the latest (most recent) data point."""
119 return max(self.values, key=lambda x: x.timestamp, default=None)
121 def average(self) -> float | None:
122 """Computes the average of all values."""
123 nums = [v.value for v in self.values if isinstance(v.value, (int, float))]
124 return sum(nums) / len(nums) if nums else None