2020import platform
2121import threading
2222import time
23- from typing import Callable , Dict , Iterable , Mapping , Optional , Tuple , Union
23+ from typing import (
24+ Any ,
25+ Callable ,
26+ Dict ,
27+ Generic ,
28+ Iterable ,
29+ Mapping ,
30+ Optional ,
31+ Sequence ,
32+ Set ,
33+ Tuple ,
34+ Type ,
35+ TypeVar ,
36+ Union ,
37+ cast ,
38+ )
2439
2540import attr
26- from prometheus_client import Counter , Gauge , Histogram
41+ from prometheus_client import CollectorRegistry , Counter , Gauge , Histogram , Metric
2742from prometheus_client .core import (
2843 REGISTRY ,
2944 CounterMetricFamily ,
3247)
3348
3449from twisted .internet import reactor
50+ from twisted .internet .base import ReactorBase
3551from twisted .python .threadpool import ThreadPool
3652
3753import synapse
5470
5571class RegistryProxy :
5672 @staticmethod
57- def collect ():
73+ def collect () -> Iterable [ Metric ] :
5874 for metric in REGISTRY .collect ():
5975 if not metric .name .startswith ("__" ):
6076 yield metric
@@ -74,7 +90,7 @@ class LaterGauge:
7490 ]
7591 )
7692
77- def collect (self ):
93+ def collect (self ) -> Iterable [ Metric ] :
7894
7995 g = GaugeMetricFamily (self .name , self .desc , labels = self .labels )
8096
@@ -93,10 +109,10 @@ def collect(self):
93109
94110 yield g
95111
96- def __attrs_post_init__ (self ):
112+ def __attrs_post_init__ (self ) -> None :
97113 self ._register ()
98114
99- def _register (self ):
115+ def _register (self ) -> None :
100116 if self .name in all_gauges .keys ():
101117 logger .warning ("%s already registered, reregistering" % (self .name ,))
102118 REGISTRY .unregister (all_gauges .pop (self .name ))
@@ -105,7 +121,12 @@ def _register(self):
105121 all_gauges [self .name ] = self
106122
107123
108- class InFlightGauge :
124+ # `MetricsEntry` only makes sense when it is a `Protocol`,
125+ # but `Protocol` can't be used as a `TypeVar` bound.
126+ MetricsEntry = TypeVar ("MetricsEntry" )
127+
128+
129+ class InFlightGauge (Generic [MetricsEntry ]):
109130 """Tracks number of things (e.g. requests, Measure blocks, etc) in flight
110131 at any given time.
111132
@@ -115,34 +136,45 @@ class InFlightGauge:
115136 callbacks.
116137
117138 Args:
118- name (str)
119- desc (str)
120- labels (list[str])
121- sub_metrics (list[str]): A list of sub metrics that the callbacks
122- will update.
139+ name
140+ desc
141+ labels
142+ sub_metrics: A list of sub metrics that the callbacks will update.
123143 """
124144
125- def __init__ (self , name , desc , labels , sub_metrics ):
145+ def __init__ (
146+ self ,
147+ name : str ,
148+ desc : str ,
149+ labels : Sequence [str ],
150+ sub_metrics : Sequence [str ],
151+ ):
126152 self .name = name
127153 self .desc = desc
128154 self .labels = labels
129155 self .sub_metrics = sub_metrics
130156
131157 # Create a class which have the sub_metrics values as attributes, which
132158 # default to 0 on initialization. Used to pass to registered callbacks.
133- self ._metrics_class = attr .make_class (
159+ self ._metrics_class : Type [ MetricsEntry ] = attr .make_class (
134160 "_MetricsEntry" , attrs = {x : attr .ib (0 ) for x in sub_metrics }, slots = True
135161 )
136162
137163 # Counts number of in flight blocks for a given set of label values
138- self ._registrations : Dict = {}
164+ self ._registrations : Dict [
165+ Tuple [str , ...], Set [Callable [[MetricsEntry ], None ]]
166+ ] = {}
139167
140168 # Protects access to _registrations
141169 self ._lock = threading .Lock ()
142170
143171 self ._register_with_collector ()
144172
145- def register (self , key , callback ):
173+ def register (
174+ self ,
175+ key : Tuple [str , ...],
176+ callback : Callable [[MetricsEntry ], None ],
177+ ) -> None :
146178 """Registers that we've entered a new block with labels `key`.
147179
148180 `callback` gets called each time the metrics are collected. The same
@@ -158,13 +190,17 @@ def register(self, key, callback):
158190 with self ._lock :
159191 self ._registrations .setdefault (key , set ()).add (callback )
160192
161- def unregister (self , key , callback ):
193+ def unregister (
194+ self ,
195+ key : Tuple [str , ...],
196+ callback : Callable [[MetricsEntry ], None ],
197+ ) -> None :
162198 """Registers that we've exited a block with labels `key`."""
163199
164200 with self ._lock :
165201 self ._registrations .setdefault (key , set ()).discard (callback )
166202
167- def collect (self ):
203+ def collect (self ) -> Iterable [ Metric ] :
168204 """Called by prometheus client when it reads metrics.
169205
170206 Note: may be called by a separate thread.
@@ -200,7 +236,7 @@ def collect(self):
200236 gauge .add_metric (key , getattr (metrics , name ))
201237 yield gauge
202238
203- def _register_with_collector (self ):
239+ def _register_with_collector (self ) -> None :
204240 if self .name in all_gauges .keys ():
205241 logger .warning ("%s already registered, reregistering" % (self .name ,))
206242 REGISTRY .unregister (all_gauges .pop (self .name ))
@@ -230,7 +266,7 @@ def __init__(
230266 name : str ,
231267 documentation : str ,
232268 buckets : Iterable [float ],
233- registry = REGISTRY ,
269+ registry : CollectorRegistry = REGISTRY ,
234270 ):
235271 """
236272 Args:
@@ -257,12 +293,12 @@ def __init__(
257293
258294 registry .register (self )
259295
260- def collect (self ):
296+ def collect (self ) -> Iterable [ Metric ] :
261297 # Don't report metrics unless we've already collected some data
262298 if self ._metric is not None :
263299 yield self ._metric
264300
265- def update_data (self , values : Iterable [float ]):
301+ def update_data (self , values : Iterable [float ]) -> None :
266302 """Update the data to be reported by the metric
267303
268304 The existing data is cleared, and each measurement in the input is assigned
@@ -304,7 +340,7 @@ def _values_to_metric(self, values: Iterable[float]) -> GaugeHistogramMetricFami
304340
305341
306342class CPUMetrics :
307- def __init__ (self ):
343+ def __init__ (self ) -> None :
308344 ticks_per_sec = 100
309345 try :
310346 # Try and get the system config
@@ -314,7 +350,7 @@ def __init__(self):
314350
315351 self .ticks_per_sec = ticks_per_sec
316352
317- def collect (self ):
353+ def collect (self ) -> Iterable [ Metric ] :
318354 if not HAVE_PROC_SELF_STAT :
319355 return
320356
@@ -364,7 +400,7 @@ def collect(self):
364400
365401
366402class GCCounts :
367- def collect (self ):
403+ def collect (self ) -> Iterable [ Metric ] :
368404 cm = GaugeMetricFamily ("python_gc_counts" , "GC object counts" , labels = ["gen" ])
369405 for n , m in enumerate (gc .get_count ()):
370406 cm .add_metric ([str (n )], m )
@@ -382,7 +418,7 @@ def collect(self):
382418
383419
384420class PyPyGCStats :
385- def collect (self ):
421+ def collect (self ) -> Iterable [ Metric ] :
386422
387423 # @stats is a pretty-printer object with __str__() returning a nice table,
388424 # plus some fields that contain data from that table.
@@ -565,7 +601,7 @@ def register_threadpool(name: str, threadpool: ThreadPool) -> None:
565601
566602
567603class ReactorLastSeenMetric :
568- def collect (self ):
604+ def collect (self ) -> Iterable [ Metric ] :
569605 cm = GaugeMetricFamily (
570606 "python_twisted_reactor_last_seen" ,
571607 "Seconds since the Twisted reactor was last seen" ,
@@ -584,9 +620,12 @@ def collect(self):
584620_last_gc = [0.0 , 0.0 , 0.0 ]
585621
586622
587- def runUntilCurrentTimer (reactor , func ):
623+ F = TypeVar ("F" , bound = Callable [..., Any ])
624+
625+
626+ def runUntilCurrentTimer (reactor : ReactorBase , func : F ) -> F :
588627 @functools .wraps (func )
589- def f (* args , ** kwargs ) :
628+ def f (* args : Any , ** kwargs : Any ) -> Any :
590629 now = reactor .seconds ()
591630 num_pending = 0
592631
@@ -649,7 +688,7 @@ def f(*args, **kwargs):
649688
650689 return ret
651690
652- return f
691+ return cast ( F , f )
653692
654693
655694try :
@@ -677,5 +716,5 @@ def f(*args, **kwargs):
677716 "start_http_server" ,
678717 "LaterGauge" ,
679718 "InFlightGauge" ,
680- "BucketCollector " ,
719+ "GaugeBucketCollector " ,
681720]
0 commit comments