|
27 | 27 | import threading |
28 | 28 | from importlib import metadata |
29 | 29 | from typing import ( |
| 30 | + TYPE_CHECKING, |
| 31 | + Any, |
30 | 32 | Callable, |
31 | 33 | Generic, |
32 | 34 | Iterable, |
@@ -262,8 +264,12 @@ def __attrs_post_init__(self) -> None: |
262 | 264 | MetricsEntry = TypeVar("MetricsEntry") |
263 | 265 |
|
264 | 266 |
|
265 | | -class InFlightGauge(Generic[MetricsEntry], Collector): |
266 | | - """Tracks number of things (e.g. requests, Measure blocks, etc) in flight |
| 267 | +class _InFlightGaugeRuntime(Collector): |
| 268 | + """ |
| 269 | + Runtime class for InFlightGauge. Contains all actual logic. |
| 270 | + Does not inherit from Generic to avoid method resolution order (MRO) conflicts. |
| 271 | +
|
| 272 | + Tracks number of things (e.g. requests, Measure blocks, etc) in flight |
267 | 273 | at any given time. |
268 | 274 |
|
269 | 275 | Each InFlightGauge will create a metric called `<name>_total` that counts |
@@ -292,16 +298,20 @@ def __init__( |
292 | 298 |
|
293 | 299 | # Create a class which have the sub_metrics values as attributes, which |
294 | 300 | # default to 0 on initialization. Used to pass to registered callbacks. |
295 | | - self._metrics_class: type[MetricsEntry] = attr.make_class( |
| 301 | + self._metrics_class = attr.make_class( |
296 | 302 | "_MetricsEntry", |
297 | 303 | attrs={x: attr.ib(default=0) for x in sub_metrics}, |
298 | 304 | slots=True, |
299 | 305 | ) |
300 | 306 |
|
301 | 307 | # Counts number of in flight blocks for a given set of label values |
302 | | - self._registrations: dict[ |
303 | | - tuple[str, ...], set[Callable[[MetricsEntry], None]] |
304 | | - ] = {} |
| 308 | + # `Callable` should be of type `Callable[[MetricsEntry], None]`, but |
| 309 | + # `MetricsEntry` has no meaning in this context without the higher level |
| 310 | + # `InFlightGauge` typing information. |
| 311 | + # Instead, the typing is enforced by having `_registrations` be private and all |
| 312 | + # accessor functions have proper `Callable[[MetricsEntry], None]` type |
| 313 | + # annotations. |
| 314 | + self._registrations: dict[tuple[str, ...], set[Callable[[Any], None]]] = {} |
305 | 315 |
|
306 | 316 | # Protects access to _registrations |
307 | 317 | self._lock = threading.Lock() |
@@ -398,6 +408,17 @@ def collect(self) -> Iterable[Metric]: |
398 | 408 | yield gauge |
399 | 409 |
|
400 | 410 |
|
| 411 | +if TYPE_CHECKING: |
| 412 | + |
| 413 | + class InFlightGauge(_InFlightGaugeRuntime, Generic[MetricsEntry]): |
| 414 | + """ |
| 415 | + Typing-only generic wrapper. |
| 416 | + Provides InFlightGauge[T] support to type checkers. |
| 417 | + """ |
| 418 | +else: |
| 419 | + InFlightGauge = _InFlightGaugeRuntime |
| 420 | + |
| 421 | + |
401 | 422 | class GaugeHistogramMetricFamilyWithLabels(GaugeHistogramMetricFamily): |
402 | 423 | """ |
403 | 424 | Custom version of `GaugeHistogramMetricFamily` from `prometheus_client` that allows |
|
0 commit comments