c2cwsgiutils 6.2.0.dev74__py3-none-any.whl → 6.2.0.dev76__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. c2cwsgiutils/__init__.py +4 -4
  2. c2cwsgiutils/acceptance/__init__.py +4 -1
  3. c2cwsgiutils/acceptance/connection.py +45 -32
  4. c2cwsgiutils/acceptance/image.py +45 -39
  5. c2cwsgiutils/acceptance/print.py +2 -2
  6. c2cwsgiutils/acceptance/utils.py +4 -4
  7. c2cwsgiutils/auth.py +20 -13
  8. c2cwsgiutils/broadcast/__init__.py +25 -16
  9. c2cwsgiutils/broadcast/interface.py +8 -4
  10. c2cwsgiutils/broadcast/local.py +9 -4
  11. c2cwsgiutils/broadcast/redis.py +19 -12
  12. c2cwsgiutils/client_info.py +3 -2
  13. c2cwsgiutils/config_utils.py +14 -10
  14. c2cwsgiutils/coverage_setup.py +5 -5
  15. c2cwsgiutils/db.py +54 -47
  16. c2cwsgiutils/db_maintenance_view.py +7 -8
  17. c2cwsgiutils/debug/__init__.py +1 -2
  18. c2cwsgiutils/debug/_listeners.py +6 -4
  19. c2cwsgiutils/debug/_views.py +15 -10
  20. c2cwsgiutils/debug/utils.py +5 -5
  21. c2cwsgiutils/errors.py +15 -10
  22. c2cwsgiutils/health_check.py +79 -60
  23. c2cwsgiutils/index.py +35 -37
  24. c2cwsgiutils/loader.py +5 -4
  25. c2cwsgiutils/logging_view.py +13 -6
  26. c2cwsgiutils/models_graph.py +5 -5
  27. c2cwsgiutils/pretty_json.py +3 -2
  28. c2cwsgiutils/profiler.py +1 -2
  29. c2cwsgiutils/prometheus.py +6 -6
  30. c2cwsgiutils/pyramid.py +1 -1
  31. c2cwsgiutils/pyramid_logging.py +9 -4
  32. c2cwsgiutils/redis_stats.py +12 -7
  33. c2cwsgiutils/redis_utils.py +18 -10
  34. c2cwsgiutils/request_tracking/__init__.py +20 -12
  35. c2cwsgiutils/request_tracking/_sql.py +2 -1
  36. c2cwsgiutils/scripts/genversion.py +10 -9
  37. c2cwsgiutils/scripts/stats_db.py +29 -13
  38. c2cwsgiutils/sentry.py +44 -20
  39. c2cwsgiutils/setup_process.py +7 -4
  40. c2cwsgiutils/sql_profiler/_impl.py +12 -11
  41. c2cwsgiutils/sqlalchemylogger/_models.py +3 -3
  42. c2cwsgiutils/sqlalchemylogger/examples/__init__.py +0 -0
  43. c2cwsgiutils/sqlalchemylogger/handlers.py +8 -9
  44. c2cwsgiutils/stats_pyramid/_db_spy.py +12 -3
  45. c2cwsgiutils/stats_pyramid/_pyramid_spy.py +10 -9
  46. c2cwsgiutils/version.py +10 -7
  47. {c2cwsgiutils-6.2.0.dev74.dist-info → c2cwsgiutils-6.2.0.dev76.dist-info}/METADATA +1 -1
  48. c2cwsgiutils-6.2.0.dev76.dist-info/RECORD +68 -0
  49. c2cwsgiutils-6.2.0.dev74.dist-info/RECORD +0 -67
  50. {c2cwsgiutils-6.2.0.dev74.dist-info → c2cwsgiutils-6.2.0.dev76.dist-info}/LICENSE +0 -0
  51. {c2cwsgiutils-6.2.0.dev74.dist-info → c2cwsgiutils-6.2.0.dev76.dist-info}/WHEEL +0 -0
  52. {c2cwsgiutils-6.2.0.dev74.dist-info → c2cwsgiutils-6.2.0.dev76.dist-info}/entry_points.txt +0 -0
@@ -1,12 +1,12 @@
1
+ import datetime
1
2
  import gc
2
3
  import logging
3
4
  import os
4
5
  import re
5
6
  import time
6
- from collections.abc import Mapping
7
- from datetime import datetime
7
+ from collections.abc import Callable, Mapping
8
8
  from io import StringIO
9
- from typing import Any, Callable, cast
9
+ from typing import Any, cast
10
10
 
11
11
  import objgraph
12
12
  import psutil
@@ -79,7 +79,7 @@ def _dump_memory_diff(request: pyramid.request.Request) -> list[Any]:
79
79
  try:
80
80
  if request.params.get("no_warmup", "0").lower() in ("1", "true", "on"):
81
81
  request.invoke_subrequest(sub_request)
82
- except Exception: # pylint: disable=broad-except
82
+ except Exception: # pylint: disable=broad-exception-caught
83
83
  pass
84
84
 
85
85
  _LOG.debug("checking memory growth for %s", path)
@@ -119,7 +119,7 @@ def _dump_memory_diff(request: pyramid.request.Request) -> list[Any]:
119
119
  "dirty_kb": (mem_after.dirty - mem_before.dirty) / 1024,
120
120
  },
121
121
  "elapsed_time": elapsed_time,
122
- "objgraph.growth": objgraph.growth(limit=limit, peak_stats=peak_stats, shortnames=False), # type: ignore
122
+ "objgraph.growth": objgraph.growth(limit=limit, peak_stats=peak_stats, shortnames=False), # type: ignore[return-value]
123
123
  }
124
124
 
125
125
 
@@ -162,18 +162,23 @@ def _error(request: pyramid.request.Request) -> Any:
162
162
  def _time(request: pyramid.request.Request) -> Any:
163
163
  del request # unused
164
164
  return {
165
- "local_time": str(datetime.now()),
166
- "gmt_time": str(datetime.utcnow()),
165
+ "local_time": str(datetime.datetime.now()), # noqa: DTZ005
166
+ "gmt_time": str(datetime.datetime.now(datetime.timezone.utc)),
167
167
  "epoch": time.time(),
168
- "timezone": datetime.now().astimezone().tzname(),
168
+ "timezone": datetime.datetime.now().astimezone().tzname(),
169
169
  }
170
170
 
171
171
 
172
172
  def _add_view(
173
- config: pyramid.config.Configurator, name: str, path: str, view: Callable[[pyramid.request.Request], Any]
173
+ config: pyramid.config.Configurator,
174
+ name: str,
175
+ path: str,
176
+ view: Callable[[pyramid.request.Request], Any],
174
177
  ) -> None:
175
178
  config.add_route(
176
- "c2c_debug_" + name, config_utils.get_base_path(config) + r"/debug/" + path, request_method="GET"
179
+ "c2c_debug_" + name,
180
+ config_utils.get_base_path(config) + r"/debug/" + path,
181
+ request_method="GET",
177
182
  )
178
183
  config.add_view(view, route_name="c2c_debug_" + name, renderer="fast_json", http_cache=0)
179
184
 
@@ -1,9 +1,9 @@
1
1
  import gc
2
2
  import logging
3
- import os
4
3
  import re
5
4
  import sys
6
5
  from collections import defaultdict
6
+ from pathlib import Path
7
7
  from types import FunctionType, ModuleType
8
8
  from typing import Any
9
9
 
@@ -37,14 +37,14 @@ def get_size(obj: Any) -> int:
37
37
 
38
38
  def dump_memory_maps(pid: str = "self") -> list[dict[str, Any]]:
39
39
  """Get the Linux memory maps."""
40
- filename = os.path.join("/proc", pid, "smaps")
41
- if not os.path.exists(filename):
40
+ filename = Path("/proc") / pid / "smaps"
41
+ if not filename.exists():
42
42
  return []
43
- with open(filename, encoding="utf-8") as input_:
43
+ with filename.open(encoding="utf-8") as input_:
44
44
  cur_dict: dict[str, int] = defaultdict(int)
45
45
  sizes: dict[str, Any] = {}
46
46
  for line in input_:
47
- line = line.rstrip("\n")
47
+ line = line.rstrip("\n") # noqa: PLW2901
48
48
  matcher = _SMAPS_LOCATION_RE.match(line)
49
49
  if matcher:
50
50
  cur_dict = sizes.setdefault(matcher.group(1), defaultdict(int))
c2cwsgiutils/errors.py CHANGED
@@ -4,7 +4,8 @@ import logging
4
4
  import os
5
5
  import traceback
6
6
  import warnings
7
- from typing import Any, Callable
7
+ from collections.abc import Callable
8
+ from typing import Any
8
9
 
9
10
  import pyramid.request
10
11
  import sqlalchemy.exc
@@ -34,7 +35,7 @@ def _crude_add_cors(request: pyramid.request.Request, response: pyramid.response
34
35
  response = request.response
35
36
  response.headers["Access-Control-Allow-Origin"] = "*"
36
37
  response.headers["Access-Control-Allow-Methods"] = ",".join(
37
- {request.headers.get("Access-Control-Request-Method", request.method)} | {"OPTIONS", "HEAD"}
38
+ {request.headers.get("Access-Control-Request-Method", request.method)} | {"OPTIONS", "HEAD"},
38
39
  )
39
40
  response.headers["Access-Control-Allow-Headers"] = "session-id"
40
41
  response.headers["Access-Control-Max-Age"] = "86400"
@@ -57,7 +58,7 @@ def _do_error(
57
58
  status: int,
58
59
  exception: Exception,
59
60
  logger: Callable[..., None] = _LOG.error,
60
- reduce_info_sent: Callable[[Exception], None] = lambda e: None,
61
+ reduce_info_sent: Callable[[Exception], None] = lambda e: None, # noqa: ARG005
61
62
  ) -> pyramid.response.Response:
62
63
  logger(
63
64
  "%s %s returned status code %s",
@@ -98,10 +99,9 @@ def _http_error(exception: HTTPException, request: pyramid.request.Request) -> A
98
99
  _add_cors(request)
99
100
  request.response.status_code = exception.status_code
100
101
  return {"message": str(exception), "status": exception.status_code}
101
- else:
102
- _crude_add_cors(request)
103
- request.response.status_code = 200
104
- return request.response
102
+ _crude_add_cors(request)
103
+ request.response.status_code = 200
104
+ return request.response
105
105
 
106
106
 
107
107
  def _include_dev_details(request: pyramid.request.Request) -> bool:
@@ -109,7 +109,8 @@ def _include_dev_details(request: pyramid.request.Request) -> bool:
109
109
 
110
110
 
111
111
  def _integrity_error(
112
- exception: sqlalchemy.exc.StatementError, request: pyramid.request.Request
112
+ exception: sqlalchemy.exc.StatementError,
113
+ request: pyramid.request.Request,
113
114
  ) -> pyramid.response.Response:
114
115
  def reduce_info_sent(e: Exception) -> None:
115
116
  if isinstance(e, sqlalchemy.exc.StatementError):
@@ -121,7 +122,8 @@ def _integrity_error(
121
122
 
122
123
 
123
124
  def _client_interrupted_error(
124
- exception: Exception, request: pyramid.request.Request
125
+ exception: Exception,
126
+ request: pyramid.request.Request,
125
127
  ) -> pyramid.response.Response:
126
128
  # No need to cry wolf if it's just the client that interrupted the connection
127
129
  return _do_error(request, 500, exception, logger=_LOG.info)
@@ -165,7 +167,10 @@ def includeme(config: pyramid.config.Configurator) -> None:
165
167
  """Initialize the error views."""
166
168
  if (
167
169
  config_utils.env_or_config(
168
- config, "C2C_ENABLE_EXCEPTION_HANDLING", "c2c.enable_exception_handling", "0"
170
+ config,
171
+ "C2C_ENABLE_EXCEPTION_HANDLING",
172
+ "c2c.enable_exception_handling",
173
+ "0",
169
174
  )
170
175
  != "0"
171
176
  ):
@@ -13,10 +13,11 @@ import re
13
13
  import subprocess # nosec
14
14
  import time
15
15
  import traceback
16
- from collections.abc import Mapping
16
+ from collections.abc import Callable, Mapping
17
17
  from enum import Enum
18
+ from pathlib import Path
18
19
  from types import TracebackType
19
- from typing import Any, Callable, Literal, Optional, Union, cast
20
+ from typing import Any, Literal, cast
20
21
 
21
22
  import prometheus_client
22
23
  import pyramid.config
@@ -63,10 +64,10 @@ class EngineType(Enum):
63
64
  READ_AND_WRITE = 3
64
65
 
65
66
 
66
- class JsonCheckException(Exception):
67
+ class JsonCheckException(Exception): # noqa: N818
67
68
  """Checker exception used to add some structured content to a failure."""
68
69
 
69
- def __init__(self, message: str, json: Any):
70
+ def __init__(self, message: str, json: Any) -> None:
70
71
  """Initialize the exception."""
71
72
  super().__init__()
72
73
  self.message = message
@@ -83,22 +84,22 @@ class JsonCheckException(Exception):
83
84
  class _Binding:
84
85
  def name(self) -> str:
85
86
  """Return the name of the binding."""
86
- raise NotImplementedError()
87
+ raise NotImplementedError
87
88
 
88
89
  def __enter__(self) -> _scoped_session:
89
- raise NotImplementedError()
90
+ raise NotImplementedError
90
91
 
91
92
  def __exit__(
92
93
  self,
93
- exc_type: Optional[type[BaseException]],
94
- exc_value: Optional[BaseException],
95
- exc_traceback: Optional[TracebackType],
94
+ exc_type: type[BaseException] | None,
95
+ exc_value: BaseException | None,
96
+ exc_traceback: TracebackType | None,
96
97
  ) -> Literal[False]:
97
98
  return False
98
99
 
99
100
 
100
101
  class _NewBinding(_Binding):
101
- def __init__(self, session: c2cwsgiutils.db.SessionFactory, readwrite: bool):
102
+ def __init__(self, session: c2cwsgiutils.db.SessionFactory, readwrite: bool) -> None:
102
103
  self.session = session
103
104
  self.readwrite = readwrite
104
105
 
@@ -110,84 +111,85 @@ class _NewBinding(_Binding):
110
111
 
111
112
 
112
113
  class _OldBinding(_Binding):
113
- def __init__(self, session: _scoped_session, engine: sqlalchemy.engine.Engine):
114
+ def __init__(self, session: _scoped_session, engine: sqlalchemy.engine.Engine) -> None:
114
115
  self.session = session
115
116
  self.engine = engine
116
117
  self.prev_bind = None
117
118
 
118
119
  def name(self) -> str:
119
- return cast(str, self.engine.c2c_name) # type: ignore
120
+ return cast(str, self.engine.c2c_name) # type: ignore[attr-defined]
120
121
 
121
122
  def __enter__(self) -> _scoped_session:
122
- self.prev_bind = self.session.bind # type: ignore
123
+ self.prev_bind = self.session.bind # type: ignore[assignment]
123
124
  self.session.bind = self.engine
124
125
  return self.session
125
126
 
126
127
  def __exit__(
127
128
  self,
128
- exc_type: Optional[type[BaseException]],
129
- exc_value: Optional[BaseException],
130
- exc_traceback: Optional[TracebackType],
129
+ exc_type: type[BaseException] | None,
130
+ exc_value: BaseException | None,
131
+ exc_traceback: TracebackType | None,
131
132
  ) -> Literal[False]:
132
133
  self.session.bind = self.prev_bind
133
134
  return False
134
135
 
135
136
 
136
137
  def _get_binding_class(
137
- session: Union[_scoped_session, c2cwsgiutils.db.SessionFactory],
138
+ session: _scoped_session | c2cwsgiutils.db.SessionFactory,
138
139
  ro_engin: sqlalchemy.engine.Engine,
139
140
  rw_engin: sqlalchemy.engine.Engine,
140
141
  readwrite: bool,
141
142
  ) -> _Binding:
142
143
  if isinstance(session, c2cwsgiutils.db.SessionFactory):
143
144
  return _NewBinding(session, readwrite)
144
- else:
145
- return _OldBinding(session, ro_engin if readwrite else rw_engin)
145
+ return _OldBinding(session, ro_engin if readwrite else rw_engin)
146
146
 
147
147
 
148
148
  def _get_bindings(
149
- session: Union[_scoped_session, c2cwsgiutils.db.SessionFactory],
149
+ session: _scoped_session | c2cwsgiutils.db.SessionFactory,
150
150
  engine_type: EngineType,
151
151
  ) -> list[_Binding]:
152
152
  if isinstance(session, c2cwsgiutils.db.SessionFactory):
153
153
  ro_engin = session.ro_engine
154
154
  rw_engin = session.rw_engine
155
155
  else:
156
- ro_engin = session.c2c_ro_bind # type: ignore
157
- rw_engin = session.c2c_rw_bind # type: ignore
156
+ ro_engin = session.c2c_ro_bind # type: ignore[attr-defined]
157
+ rw_engin = session.c2c_rw_bind # type: ignore[attr-defined]
158
158
 
159
159
  if rw_engin == ro_engin:
160
160
  engine_type = EngineType.WRITE_ONLY
161
161
 
162
162
  if engine_type == EngineType.READ_AND_WRITE:
163
163
  return [
164
- _get_binding_class(session, ro_engin, rw_engin, False),
165
- _get_binding_class(session, ro_engin, rw_engin, True),
164
+ _get_binding_class(session, ro_engin, rw_engin, readwrite=False),
165
+ _get_binding_class(session, ro_engin, rw_engin, readwrite=True),
166
166
  ]
167
167
  if engine_type == EngineType.READ_ONLY:
168
- return [_get_binding_class(session, ro_engin, ro_engin, False)]
168
+ return [_get_binding_class(session, ro_engin, ro_engin, readwrite=False)]
169
169
  if engine_type == EngineType.WRITE_ONLY:
170
- return [_get_binding_class(session, rw_engin, rw_engin, True)]
170
+ return [_get_binding_class(session, rw_engin, rw_engin, readwrite=True)]
171
171
 
172
172
  raise NotImplementedError(f"Unhandled engine type {engine_type}")
173
173
 
174
174
 
175
- def _get_alembic_version(alembic_ini_path: str, name: str) -> str:
175
+ def _get_alembic_version(alembic_ini_path: Path, name: str) -> str:
176
176
  # Go to the directory holding the config file and add '.' to the PYTHONPATH variable to support Alembic
177
177
  # migration scripts using common modules
178
178
  env = dict(os.environ)
179
179
  pythonpath = os.environ.get("PYTHONPATH", "")
180
180
  pythonpath = (pythonpath + ":" if pythonpath else "") + "."
181
181
  env["PYTHONPATH"] = pythonpath
182
- dirname = os.path.abspath(os.path.dirname(alembic_ini_path))
182
+ dirname = alembic_ini_path.parent.resolve()
183
183
 
184
184
  out = subprocess.check_output( # nosec
185
- ["alembic", "--config", alembic_ini_path, "--name", name, "heads"], cwd=dirname, env=env
185
+ ["alembic", "--config", alembic_ini_path, "--name", name, "heads"],
186
+ cwd=dirname,
187
+ env=env,
186
188
  ).decode("utf-8")
187
189
  out_match = _ALEMBIC_HEAD_RE.match(out)
188
190
  if not out_match:
189
191
  raise Exception( # pylint: disable=broad-exception-raised
190
- "Cannot get the alembic HEAD version from: " + out
192
+ "Cannot get the alembic HEAD version from: " + out,
191
193
  )
192
194
  return out_match.group(1)
193
195
 
@@ -202,7 +204,9 @@ class HealthCheck:
202
204
  def __init__(self, config: pyramid.config.Configurator) -> None:
203
205
  """Initialize the health check view."""
204
206
  config.add_route(
205
- "c2c_health_check", config_utils.get_base_path(config) + r"/health_check", request_method="GET"
207
+ "c2c_health_check",
208
+ config_utils.get_base_path(config) + r"/health_check",
209
+ request_method="GET",
206
210
  )
207
211
  config.add_view(self._view, route_name="c2c_health_check", renderer="fast_json", http_cache=0)
208
212
  self._checks: list[tuple[str, Callable[[pyramid.request.Request], Any], int]] = []
@@ -220,9 +224,9 @@ class HealthCheck:
220
224
 
221
225
  def add_db_session_check(
222
226
  self,
223
- session: Union[_scoped_session, c2cwsgiutils.db.SessionFactory],
224
- query_cb: Optional[Callable[[_scoped_session], Any]] = None,
225
- at_least_one_model: Optional[object] = None,
227
+ session: _scoped_session | c2cwsgiutils.db.SessionFactory,
228
+ query_cb: Callable[[_scoped_session], Any] | None = None,
229
+ at_least_one_model: object | None = None,
226
230
  level: int = 1,
227
231
  engine_type: EngineType = EngineType.READ_AND_WRITE,
228
232
  ) -> None:
@@ -249,11 +253,11 @@ class HealthCheck:
249
253
  def add_alembic_check(
250
254
  self,
251
255
  session: _scoped_session,
252
- alembic_ini_path: str,
256
+ alembic_ini_path: Path | str,
253
257
  level: int = 2,
254
258
  name: str = "alembic",
255
- version_schema: Optional[str] = None,
256
- version_table: Optional[str] = None,
259
+ version_schema: str | None = None,
260
+ version_table: str | None = None,
257
261
  ) -> None:
258
262
  """
259
263
  Check the DB version against the HEAD version of Alembic.
@@ -270,6 +274,8 @@ class HealthCheck:
270
274
  ro_engin: the RO engine to use (if None, use the session one)
271
275
 
272
276
  """
277
+ if isinstance(alembic_ini_path, str):
278
+ alembic_ini_path = Path(alembic_ini_path)
273
279
  version_ = _get_alembic_version(alembic_ini_path, name)
274
280
 
275
281
  config = configparser.ConfigParser()
@@ -288,47 +294,55 @@ class HealthCheck:
288
294
  self.session = session
289
295
 
290
296
  def __call__(self, request: pyramid.request.Request) -> str:
297
+ del request # Unused
291
298
  assert version_schema
292
299
  assert version_table
293
300
  for binding in _get_bindings(self.session, EngineType.READ_AND_WRITE):
294
301
  with (
295
302
  binding as binded_session,
296
303
  _PROMETHEUS_DB_SUMMARY.labels(
297
- configuration=alembic_ini_path, connection=binding.name(), check="alembic"
304
+ configuration=alembic_ini_path,
305
+ connection=binding.name(),
306
+ check="alembic",
298
307
  ).time(),
299
308
  ):
300
309
  result = binded_session.execute(
301
310
  sqlalchemy.text(
302
311
  "SELECT version_num FROM " # noqa: S608 # nosec
303
- f"{sqlalchemy.sql.quoted_name(version_schema, True)}."
304
- f"{sqlalchemy.sql.quoted_name(version_table, True)}"
305
- )
312
+ f"{sqlalchemy.sql.quoted_name(version_schema, quote=True)}."
313
+ f"{sqlalchemy.sql.quoted_name(version_table, quote=True)}",
314
+ ),
306
315
  ).fetchone()
307
316
  assert result is not None
308
317
  (actual_version,) = result
309
318
  _PROMETHEUS_ALEMBIC_VERSION.labels(
310
- version=actual_version, name=name, configuration=alembic_ini_path
319
+ version=actual_version,
320
+ name=name,
321
+ configuration=alembic_ini_path,
311
322
  ).set(1)
312
323
  if actual_version != version_:
313
- raise Exception( # pylint: disable=broad-exception-raised
324
+ error_message = (
314
325
  f"Invalid alembic version (db: {actual_version}, code: {version_})"
315
326
  )
327
+ raise Exception(error_message) # pylint: disable=broad-exception-raised
316
328
  return version_
317
329
 
318
330
  self._checks.append(
319
- ("alembic_" + alembic_ini_path.replace("/", "_").strip("_") + "_" + name, _Check(session), level)
331
+ (
332
+ "alembic_" + str(alembic_ini_path).replace("/", "_").strip("_") + "_" + name,
333
+ _Check(session),
334
+ level,
335
+ ),
320
336
  )
321
337
 
322
338
  def add_url_check(
323
339
  self,
324
- url: Union[str, Callable[[pyramid.request.Request], str]],
325
- params: Union[Mapping[str, str], Callable[[pyramid.request.Request], Mapping[str, str]], None] = None,
326
- headers: Union[
327
- Mapping[str, str], Callable[[pyramid.request.Request], Mapping[str, str]], None
328
- ] = None,
329
- name: Optional[str] = None,
330
- check_cb: Callable[[pyramid.request.Request, requests.Response], Any] = lambda request,
331
- response: None,
340
+ url: str | Callable[[pyramid.request.Request], str],
341
+ params: Mapping[str, str] | Callable[[pyramid.request.Request], Mapping[str, str]] | None = None,
342
+ headers: Mapping[str, str] | Callable[[pyramid.request.Request], Mapping[str, str]] | None = None,
343
+ name: str | None = None,
344
+ check_cb: Callable[[pyramid.request.Request, requests.Response], Any] = lambda request, # noqa: ARG005
345
+ response: None, # noqa: ARG005
332
346
  timeout: float = 3,
333
347
  level: int = 1,
334
348
  ) -> None:
@@ -344,7 +358,6 @@ class HealthCheck:
344
358
  response as parameters)
345
359
  timeout: the timeout
346
360
  level: the level of the health check
347
-
348
361
  """
349
362
 
350
363
  def check(request: pyramid.request.Request) -> Any:
@@ -362,7 +375,7 @@ class HealthCheck:
362
375
  assert name
363
376
  self._checks.append((name, check, level))
364
377
 
365
- def add_redis_check(self, name: Optional[str] = None, level: int = 1) -> None:
378
+ def add_redis_check(self, name: str | None = None, level: int = 1) -> None:
366
379
  """
367
380
  Check that the given redis server is reachable.
368
381
 
@@ -382,7 +395,7 @@ class HealthCheck:
382
395
  def add(name: str, func: Callable[..., Any], *args: Any) -> None:
383
396
  try:
384
397
  result[name] = func(*args)
385
- except Exception as e: # pylint: disable=broad-except
398
+ except Exception as e: # pylint: disable=broad-exception-caught
386
399
  result[name] = {"error": str(e)}
387
400
 
388
401
  if master is not None:
@@ -409,7 +422,8 @@ class HealthCheck:
409
422
  name = self.name
410
423
 
411
424
  if name is None:
412
- raise RuntimeError("Redis should be configured")
425
+ exception_message = "Redis should be configured"
426
+ raise RuntimeError(exception_message)
413
427
 
414
428
  self._checks.append((name, check, level))
415
429
 
@@ -437,7 +451,10 @@ class HealthCheck:
437
451
  self._checks.append((name, check, level))
438
452
 
439
453
  def add_custom_check(
440
- self, name: str, check_cb: Callable[[pyramid.request.Request], Any], level: int = 1
454
+ self,
455
+ name: str,
456
+ check_cb: Callable[[pyramid.request.Request], Any],
457
+ level: int = 1,
441
458
  ) -> None:
442
459
  """
443
460
  Add a custom check.
@@ -489,7 +506,7 @@ class HealthCheck:
489
506
  if result is not None:
490
507
  results["successes"][name]["result"] = result
491
508
  _set_success(check_name=name)
492
- except Exception as e: # pylint: disable=broad-except
509
+ except Exception as e: # pylint: disable=broad-exception-caught
493
510
  _PROMETHEUS_HEALTH_CHECKS_FAILURE.labels(name=name).set(1)
494
511
  _LOG.warning("Health check %s failed", name, exc_info=True)
495
512
  failure = {"message": str(e), "timing": time.perf_counter() - start, "level": level}
@@ -509,7 +526,9 @@ class HealthCheck:
509
526
  with (
510
527
  binding as session,
511
528
  _PROMETHEUS_DB_SUMMARY.labels(
512
- connection=binding.name(), check="database", configuration="<default>"
529
+ connection=binding.name(),
530
+ check="database",
531
+ configuration="<default>",
513
532
  ).time(),
514
533
  ):
515
534
  return query_cb(session)
@@ -537,5 +556,5 @@ def _set_success(check_name: str) -> None:
537
556
 
538
557
 
539
558
  @broadcast.decorator(expect_answers=True)
540
- def _get_all_versions() -> Optional[str]:
559
+ def _get_all_versions() -> str | None:
541
560
  return version.get_version()