Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 84fac0f

Browse files
authored
Add type annotations to synapse.metrics (#10847)
1 parent d993c3b commit 84fac0f

File tree

12 files changed

+173
-85
lines changed

12 files changed

+173
-85
lines changed

changelog.d/10847.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add type annotations to `synapse.metrics`.

mypy.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ disallow_untyped_defs = True
160160
[mypy-synapse.handlers.*]
161161
disallow_untyped_defs = True
162162

163+
[mypy-synapse.metrics.*]
164+
disallow_untyped_defs = True
165+
163166
[mypy-synapse.push.*]
164167
disallow_untyped_defs = True
165168

synapse/app/_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ async def start(hs: "HomeServer") -> None:
402402
if hasattr(signal, "SIGHUP"):
403403

404404
@wrap_as_background_process("sighup")
405-
def handle_sighup(*args: Any, **kwargs: Any) -> None:
405+
async def handle_sighup(*args: Any, **kwargs: Any) -> None:
406406
# Tell systemd our state, if we're using it. This will silently fail if
407407
# we're not using systemd.
408408
sdnotify(b"RELOADING=1")

synapse/groups/attestations.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040

4141
from signedjson.sign import sign_json
4242

43+
from twisted.internet.defer import Deferred
44+
4345
from synapse.api.errors import HttpResponseException, RequestSendFailed, SynapseError
4446
from synapse.metrics.background_process_metrics import run_as_background_process
4547
from synapse.types import JsonDict, get_domain_from_id
@@ -166,7 +168,7 @@ async def on_renew_attestation(
166168

167169
return {}
168170

169-
def _start_renew_attestations(self) -> None:
171+
def _start_renew_attestations(self) -> "Deferred[None]":
170172
return run_as_background_process("renew_attestations", self._renew_attestations)
171173

172174
async def _renew_attestations(self) -> None:

synapse/handlers/typing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def _reset(self) -> None:
9090
self.wheel_timer = WheelTimer(bucket_size=5000)
9191

9292
@wrap_as_background_process("typing._handle_timeouts")
93-
def _handle_timeouts(self) -> None:
93+
async def _handle_timeouts(self) -> None:
9494
logger.debug("Checking for typing timeouts")
9595

9696
now = self.clock.time_msec()

synapse/metrics/__init__.py

Lines changed: 70 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,25 @@
2020
import platform
2121
import threading
2222
import 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

2540
import attr
26-
from prometheus_client import Counter, Gauge, Histogram
41+
from prometheus_client import CollectorRegistry, Counter, Gauge, Histogram, Metric
2742
from prometheus_client.core import (
2843
REGISTRY,
2944
CounterMetricFamily,
@@ -32,6 +47,7 @@
3247
)
3348

3449
from twisted.internet import reactor
50+
from twisted.internet.base import ReactorBase
3551
from twisted.python.threadpool import ThreadPool
3652

3753
import synapse
@@ -54,7 +70,7 @@
5470

5571
class 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

306342
class 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

366402
class 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

384420
class 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

567603
class 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

655694
try:
@@ -677,5 +716,5 @@ def f(*args, **kwargs):
677716
"start_http_server",
678717
"LaterGauge",
679718
"InFlightGauge",
680-
"BucketCollector",
719+
"GaugeBucketCollector",
681720
]

0 commit comments

Comments
 (0)