c2cwsgiutils 6.0.10.dev11__py3-none-any.whl → 6.1.0__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 (56) hide show
  1. c2cwsgiutils/__init__.py +14 -11
  2. c2cwsgiutils/acceptance/__init__.py +2 -3
  3. c2cwsgiutils/acceptance/connection.py +1 -2
  4. c2cwsgiutils/acceptance/image.py +9 -17
  5. c2cwsgiutils/acceptance/package-lock.json +270 -1062
  6. c2cwsgiutils/acceptance/package.json +2 -2
  7. c2cwsgiutils/acceptance/print.py +7 -3
  8. c2cwsgiutils/acceptance/utils.py +1 -3
  9. c2cwsgiutils/auth.py +43 -37
  10. c2cwsgiutils/broadcast/__init__.py +16 -16
  11. c2cwsgiutils/broadcast/interface.py +3 -3
  12. c2cwsgiutils/broadcast/local.py +1 -0
  13. c2cwsgiutils/broadcast/redis.py +13 -12
  14. c2cwsgiutils/client_info.py +13 -5
  15. c2cwsgiutils/config_utils.py +1 -0
  16. c2cwsgiutils/coverage_setup.py +4 -3
  17. c2cwsgiutils/db.py +36 -41
  18. c2cwsgiutils/db_maintenance_view.py +13 -13
  19. c2cwsgiutils/debug/__init__.py +2 -2
  20. c2cwsgiutils/debug/_listeners.py +1 -1
  21. c2cwsgiutils/debug/_views.py +7 -6
  22. c2cwsgiutils/debug/utils.py +9 -9
  23. c2cwsgiutils/errors.py +13 -14
  24. c2cwsgiutils/health_check.py +25 -30
  25. c2cwsgiutils/index.py +14 -16
  26. c2cwsgiutils/loader.py +1 -1
  27. c2cwsgiutils/logging_view.py +12 -12
  28. c2cwsgiutils/models_graph.py +0 -1
  29. c2cwsgiutils/pretty_json.py +0 -1
  30. c2cwsgiutils/prometheus.py +1 -7
  31. c2cwsgiutils/pyramid.py +0 -1
  32. c2cwsgiutils/pyramid_logging.py +2 -1
  33. c2cwsgiutils/redis_stats.py +9 -9
  34. c2cwsgiutils/redis_utils.py +19 -18
  35. c2cwsgiutils/request_tracking/__init__.py +14 -13
  36. c2cwsgiutils/request_tracking/_sql.py +0 -1
  37. c2cwsgiutils/scripts/genversion.py +5 -5
  38. c2cwsgiutils/scripts/stats_db.py +19 -17
  39. c2cwsgiutils/scripts/test_print.py +5 -5
  40. c2cwsgiutils/sentry.py +55 -20
  41. c2cwsgiutils/services.py +2 -2
  42. c2cwsgiutils/setup_process.py +0 -2
  43. c2cwsgiutils/sql_profiler/__init__.py +6 -6
  44. c2cwsgiutils/sql_profiler/_impl.py +19 -17
  45. c2cwsgiutils/sqlalchemylogger/README.md +30 -13
  46. c2cwsgiutils/sqlalchemylogger/handlers.py +12 -11
  47. c2cwsgiutils/stats_pyramid/__init__.py +1 -5
  48. c2cwsgiutils/stats_pyramid/_db_spy.py +2 -2
  49. c2cwsgiutils/stats_pyramid/_pyramid_spy.py +0 -1
  50. c2cwsgiutils/version.py +11 -5
  51. {c2cwsgiutils-6.0.10.dev11.dist-info → c2cwsgiutils-6.1.0.dist-info}/LICENSE +1 -1
  52. {c2cwsgiutils-6.0.10.dev11.dist-info → c2cwsgiutils-6.1.0.dist-info}/METADATA +12 -12
  53. c2cwsgiutils-6.1.0.dist-info/RECORD +67 -0
  54. {c2cwsgiutils-6.0.10.dev11.dist-info → c2cwsgiutils-6.1.0.dist-info}/WHEEL +1 -1
  55. c2cwsgiutils-6.0.10.dev11.dist-info/RECORD +0 -67
  56. {c2cwsgiutils-6.0.10.dev11.dist-info → c2cwsgiutils-6.1.0.dist-info}/entry_points.txt +0 -0
@@ -4,6 +4,7 @@ Setup an health_check API.
4
4
  To use it, create an instance of this class in your application initialization and do a few calls to its
5
5
  methods add_db_check()
6
6
  """
7
+
7
8
  import configparser
8
9
  import copy
9
10
  import logging
@@ -15,7 +16,7 @@ import traceback
15
16
  from collections.abc import Mapping
16
17
  from enum import Enum
17
18
  from types import TracebackType
18
- from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Union, cast
19
+ from typing import Any, Callable, Literal, Optional, Union, cast
19
20
 
20
21
  import prometheus_client
21
22
  import pyramid.config
@@ -29,13 +30,10 @@ from pyramid.httpexceptions import HTTPNotFound
29
30
  import c2cwsgiutils.db
30
31
  from c2cwsgiutils import auth, broadcast, config_utils, prometheus, redis_utils, version
31
32
 
32
- if TYPE_CHECKING:
33
- scoped_session = sqlalchemy.orm.scoped_session[sqlalchemy.orm.Session]
34
- else:
35
- scoped_session = sqlalchemy.orm.scoped_session
33
+ _scoped_session = sqlalchemy.orm.scoped_session[sqlalchemy.orm.Session]
36
34
 
37
- LOG = logging.getLogger(__name__)
38
- ALEMBIC_HEAD_RE = re.compile(r"^([a-f0-9]+) \(head\)\n$")
35
+ _LOG = logging.getLogger(__name__)
36
+ _ALEMBIC_HEAD_RE = re.compile(r"^([a-f0-9]+) \(head\)\n$")
39
37
 
40
38
  _PROMETHEUS_DB_SUMMARY = prometheus_client.Summary(
41
39
  prometheus.build_metric_name("health_check_db"),
@@ -77,14 +75,16 @@ class JsonCheckException(Exception):
77
75
  return self.message
78
76
 
79
77
  def json_data(self) -> Any:
78
+ """Return the JSON data to be returned in the response."""
80
79
  return self.json
81
80
 
82
81
 
83
82
  class _Binding:
84
83
  def name(self) -> str:
84
+ """Return the name of the binding."""
85
85
  raise NotImplementedError()
86
86
 
87
- def __enter__(self) -> scoped_session:
87
+ def __enter__(self) -> _scoped_session:
88
88
  raise NotImplementedError()
89
89
 
90
90
  def __exit__(
@@ -104,12 +104,12 @@ class _NewBinding(_Binding):
104
104
  def name(self) -> str:
105
105
  return self.session.engine_name(self.readwrite)
106
106
 
107
- def __enter__(self) -> scoped_session:
107
+ def __enter__(self) -> _scoped_session:
108
108
  return self.session(None, self.readwrite)
109
109
 
110
110
 
111
111
  class _OldBinding(_Binding):
112
- def __init__(self, session: scoped_session, engine: sqlalchemy.engine.Engine):
112
+ def __init__(self, session: _scoped_session, engine: sqlalchemy.engine.Engine):
113
113
  self.session = session
114
114
  self.engine = engine
115
115
  self.prev_bind = None
@@ -117,7 +117,7 @@ class _OldBinding(_Binding):
117
117
  def name(self) -> str:
118
118
  return cast(str, self.engine.c2c_name) # type: ignore
119
119
 
120
- def __enter__(self) -> scoped_session:
120
+ def __enter__(self) -> _scoped_session:
121
121
  self.prev_bind = self.session.bind # type: ignore
122
122
  self.session.bind = self.engine
123
123
  return self.session
@@ -133,7 +133,7 @@ class _OldBinding(_Binding):
133
133
 
134
134
 
135
135
  def _get_binding_class(
136
- session: Union[scoped_session, c2cwsgiutils.db.SessionFactory],
136
+ session: Union[_scoped_session, c2cwsgiutils.db.SessionFactory],
137
137
  ro_engin: sqlalchemy.engine.Engine,
138
138
  rw_engin: sqlalchemy.engine.Engine,
139
139
  readwrite: bool,
@@ -145,7 +145,7 @@ def _get_binding_class(
145
145
 
146
146
 
147
147
  def _get_bindings(
148
- session: Union[scoped_session, c2cwsgiutils.db.SessionFactory],
148
+ session: Union[_scoped_session, c2cwsgiutils.db.SessionFactory],
149
149
  engine_type: EngineType,
150
150
  ) -> list[_Binding]:
151
151
  if isinstance(session, c2cwsgiutils.db.SessionFactory):
@@ -183,7 +183,7 @@ def _get_alembic_version(alembic_ini_path: str, name: str) -> str:
183
183
  out = subprocess.check_output( # nosec
184
184
  ["alembic", "--config", alembic_ini_path, "--name", name, "heads"], cwd=dirname, env=env
185
185
  ).decode("utf-8")
186
- out_match = ALEMBIC_HEAD_RE.match(out)
186
+ out_match = _ALEMBIC_HEAD_RE.match(out)
187
187
  if not out_match:
188
188
  raise Exception( # pylint: disable=broad-exception-raised
189
189
  "Cannot get the alembic HEAD version from: " + out
@@ -218,8 +218,8 @@ class HealthCheck:
218
218
 
219
219
  def add_db_session_check(
220
220
  self,
221
- session: Union[scoped_session, c2cwsgiutils.db.SessionFactory],
222
- query_cb: Optional[Callable[[scoped_session], Any]] = None,
221
+ session: Union[_scoped_session, c2cwsgiutils.db.SessionFactory],
222
+ query_cb: Optional[Callable[[_scoped_session], Any]] = None,
223
223
  at_least_one_model: Optional[object] = None,
224
224
  level: int = 1,
225
225
  engine_type: EngineType = EngineType.READ_AND_WRITE,
@@ -228,7 +228,6 @@ class HealthCheck:
228
228
  Check a DB session is working. You can specify either query_cb or at_least_one_model.
229
229
 
230
230
  Arguments:
231
-
232
231
  session: a DB session created by c2cwsgiutils.db.init()
233
232
  query_cb: a callable that take a session as parameter and check it works
234
233
  at_least_one_model: a model that must have at least one entry in the DB
@@ -246,7 +245,7 @@ class HealthCheck:
246
245
 
247
246
  def add_alembic_check(
248
247
  self,
249
- session: scoped_session,
248
+ session: _scoped_session,
250
249
  alembic_ini_path: str,
251
250
  level: int = 2,
252
251
  name: str = "alembic",
@@ -257,7 +256,6 @@ class HealthCheck:
257
256
  Check the DB version against the HEAD version of Alembic.
258
257
 
259
258
  Arguments:
260
-
261
259
  session: A DB session created by c2cwsgiutils.db.init() giving access to the DB \
262
260
  managed by Alembic
263
261
  alembic_ini_path: Path to the Alembic INI file
@@ -282,7 +280,7 @@ class HealthCheck:
282
280
  assert version_table
283
281
 
284
282
  class _Check:
285
- def __init__(self, session: scoped_session) -> None:
283
+ def __init__(self, session: _scoped_session) -> None:
286
284
  self.session = session
287
285
 
288
286
  def __call__(self, request: pyramid.request.Request) -> str:
@@ -333,7 +331,6 @@ class HealthCheck:
333
331
  Check that a GET on an URL returns 2xx.
334
332
 
335
333
  Arguments:
336
-
337
334
  url: the URL to query or a function taking the request and returning it
338
335
  params: the parameters or a function taking the request and returning them
339
336
  headers: the headers or a function taking the request and returning them
@@ -366,7 +363,6 @@ class HealthCheck:
366
363
  One such check is automatically added if the broadcaster is configured with redis.
367
364
 
368
365
  Arguments:
369
-
370
366
  name: the name of the check (defaults to url)
371
367
  level: the level of the health check
372
368
  """
@@ -414,13 +410,13 @@ class HealthCheck:
414
410
  """
415
411
  Check that the version matches across all instances.
416
412
 
417
- Keyword Arguments:
418
-
413
+ Arguments:
419
414
  name: the name of the check (defaults to "version")
420
415
  level: the level of the health check
421
416
  """
422
417
 
423
418
  def check(request: pyramid.request.Request) -> dict[str, Any]:
419
+ del request # unused
424
420
  ref = version.get_version()
425
421
  all_versions = _get_all_versions()
426
422
  assert all_versions
@@ -442,7 +438,6 @@ class HealthCheck:
442
438
  in the response. In case of failure it must raise an exception.
443
439
 
444
440
  Arguments:
445
-
446
441
  name: the name of the check
447
442
  check_cb: the callback to call (takes the request as parameter)
448
443
  level: the level of the health check
@@ -488,7 +483,7 @@ class HealthCheck:
488
483
  _set_success(check_name=name)
489
484
  except Exception as e: # pylint: disable=broad-except
490
485
  _PROMETHEUS_HEALTH_CHECKS_FAILURE.labels(name=name).set(1)
491
- LOG.warning("Health check %s failed", name, exc_info=True)
486
+ _LOG.warning("Health check %s failed", name, exc_info=True)
492
487
  failure = {"message": str(e), "timing": time.perf_counter() - start, "level": level}
493
488
  if isinstance(e, JsonCheckException) and e.json_data() is not None:
494
489
  failure["result"] = e.json_data()
@@ -499,9 +494,10 @@ class HealthCheck:
499
494
  @staticmethod
500
495
  def _create_db_engine_check(
501
496
  binding: _Binding,
502
- query_cb: Callable[[scoped_session], None],
497
+ query_cb: Callable[[_scoped_session], None],
503
498
  ) -> tuple[str, Callable[[pyramid.request.Request], None]]:
504
499
  def check(request: pyramid.request.Request) -> None:
500
+ del request # unused
505
501
  with binding as session:
506
502
  with _PROMETHEUS_DB_SUMMARY.labels(
507
503
  connection=binding.name(), check="database", configuration="<default>"
@@ -511,8 +507,8 @@ class HealthCheck:
511
507
  return "db_engine_" + binding.name(), check
512
508
 
513
509
  @staticmethod
514
- def _at_least_one(model: Any) -> Callable[[scoped_session], Any]:
515
- def query(session: scoped_session) -> None:
510
+ def _at_least_one(model: Any) -> Callable[[_scoped_session], Any]:
511
+ def query(session: _scoped_session) -> None:
516
512
  result = session.query(model).first()
517
513
  if result is None:
518
514
  raise HTTPNotFound(model.__name__ + " record not found")
@@ -527,7 +523,6 @@ def _maybe_function(what: Any, request: pyramid.request.Request) -> Any:
527
523
  @broadcast.decorator(expect_answers=False)
528
524
  def _set_success(check_name: str) -> None:
529
525
  """Set check in success in all process."""
530
-
531
526
  _PROMETHEUS_HEALTH_CHECKS_FAILURE.labels(name=check_name).set(0)
532
527
 
533
528
 
c2cwsgiutils/index.py CHANGED
@@ -42,12 +42,12 @@ from c2cwsgiutils.auth import (
42
42
  )
43
43
  from c2cwsgiutils.config_utils import env_or_settings
44
44
 
45
- LOG = logging.getLogger(__name__)
45
+ _LOG = logging.getLogger(__name__)
46
46
 
47
- additional_title: Optional[str] = None
48
- additional_noauth: list[str] = []
49
- additional_auth: list[str] = []
50
- ELEM_ID = 0
47
+ _additional_title: Optional[str] = None
48
+ _additional_noauth: list[str] = []
49
+ _additional_auth: list[str] = []
50
+ _ELEM_ID = 0
51
51
 
52
52
 
53
53
  def _url(
@@ -113,9 +113,9 @@ def input_(
113
113
  name: str, label: Optional[str] = None, type_: Optional[str] = None, value: Union[str, int] = ""
114
114
  ) -> str:
115
115
  """Get an HTML input."""
116
- global ELEM_ID
117
- id_ = ELEM_ID
118
- ELEM_ID += 1
116
+ global _ELEM_ID # pylint: disable=global-statement
117
+ id_ = _ELEM_ID
118
+ _ELEM_ID += 1
119
119
 
120
120
  if label is None and type_ != "hidden":
121
121
  label = name.replace("_", " ").capitalize()
@@ -138,7 +138,6 @@ def input_(
138
138
 
139
139
  def button(label: str) -> str:
140
140
  """Get en HTML button."""
141
-
142
141
  return f'<button class="btn btn-primary" type="submit">{label}</button>'
143
142
 
144
143
 
@@ -153,22 +152,22 @@ def _index(request: pyramid.request.Request) -> dict[str, str]:
153
152
  body = ""
154
153
  body += _health_check(request)
155
154
  body += _stats(request)
156
- body += _versions(request)
157
155
  if has_access:
156
+ body += _versions(request)
158
157
  body += _debug(request)
159
158
  body += _db_maintenance(request)
160
159
  body += _logging(request)
161
160
  body += _profiler(request)
162
161
 
163
- if additional_title is not None and (has_access or additional_noauth):
164
- body += additional_title
162
+ if _additional_title is not None and (has_access or _additional_noauth):
163
+ body += _additional_title
165
164
  body += "\n"
166
165
 
167
166
  if has_access:
168
- body += "\n".join(additional_auth)
167
+ body += "\n".join(_additional_auth)
169
168
  body += "\n"
170
169
 
171
- body += "\n".join(additional_noauth)
170
+ body += "\n".join(_additional_noauth)
172
171
 
173
172
  settings = request.registry.settings
174
173
  auth_type_ = auth_type(settings)
@@ -346,7 +345,6 @@ def _health_check(request: pyramid.request.Request) -> str:
346
345
 
347
346
  def _github_login(request: pyramid.request.Request) -> dict[str, Any]:
348
347
  """Get the view that start the authentication on GitHub."""
349
-
350
348
  settings = request.registry.settings
351
349
  params = dict(request.params)
352
350
  callback_url = _url(
@@ -500,7 +498,7 @@ def includeme(config: pyramid.config.Configurator) -> None:
500
498
  settings = config.get_settings()
501
499
  auth_type_ = auth_type(settings)
502
500
  if auth_type_ == AuthenticationType.SECRET:
503
- LOG.warning(
501
+ _LOG.warning(
504
502
  "It is recommended to use OAuth2 with GitHub login instead of the `C2C_SECRET` because it "
505
503
  "protects from brute force attacks and the access grant is personal and can be revoked."
506
504
  )
c2cwsgiutils/loader.py CHANGED
@@ -5,7 +5,7 @@ from plaster_pastedeploy import Loader as BaseLoader
5
5
 
6
6
  from c2cwsgiutils import get_config_defaults
7
7
 
8
- LOG = logging.getLogger(__name__)
8
+ _LOG = logging.getLogger(__name__)
9
9
 
10
10
 
11
11
  class Loader(BaseLoader): # type: ignore
@@ -7,10 +7,10 @@ import pyramid.request
7
7
 
8
8
  from c2cwsgiutils import auth, broadcast, config_utils, redis_utils
9
9
 
10
- LOG = logging.getLogger(__name__)
11
- CONFIG_KEY = "c2c.log_view_enabled"
12
- ENV_KEY = "C2C_LOG_VIEW_ENABLED"
13
- REDIS_PREFIX = "c2c_logging_level_"
10
+ _LOG = logging.getLogger(__name__)
11
+ _CONFIG_KEY = "c2c.log_view_enabled"
12
+ _ENV_KEY = "C2C_LOG_VIEW_ENABLED"
13
+ _REDIS_PREFIX = "c2c_logging_level_"
14
14
 
15
15
 
16
16
  def install_subscriber(config: pyramid.config.Configurator) -> None:
@@ -21,7 +21,7 @@ def install_subscriber(config: pyramid.config.Configurator) -> None:
21
21
 
22
22
  def includeme(config: pyramid.config.Configurator) -> None:
23
23
  """Install the view to configure the loggers, if configured to do so."""
24
- if auth.is_enabled(config, ENV_KEY, CONFIG_KEY):
24
+ if auth.is_enabled(config, _ENV_KEY, _CONFIG_KEY):
25
25
  config.add_route(
26
26
  "c2c_logging_level", config_utils.get_base_path(config) + r"/logging/level", request_method="GET"
27
27
  )
@@ -29,7 +29,7 @@ def includeme(config: pyramid.config.Configurator) -> None:
29
29
  _logging_change_level, route_name="c2c_logging_level", renderer="fast_json", http_cache=0
30
30
  )
31
31
  _restore_overrides(config)
32
- LOG.info("Enabled the /logging/level API")
32
+ _LOG.info("Enabled the /logging/level API")
33
33
 
34
34
 
35
35
  def _logging_change_level(request: pyramid.request.Request) -> Mapping[str, Any]:
@@ -39,7 +39,7 @@ def _logging_change_level(request: pyramid.request.Request) -> Mapping[str, Any]
39
39
  level = request.params.get("level")
40
40
  logger = logging.getLogger(name)
41
41
  if level is not None:
42
- LOG.critical(
42
+ _LOG.critical(
43
43
  "Logging of %s changed from %s to %s", name, logging.getLevelName(logger.level), level
44
44
  )
45
45
  _set_level(name=name, level=level)
@@ -63,20 +63,20 @@ def _set_level(name: str, level: str) -> bool:
63
63
  def _restore_overrides(config: pyramid.config.Configurator) -> None:
64
64
  try:
65
65
  for name, level in _list_overrides(config.get_settings()):
66
- LOG.debug("Restoring logging level override for %s: %s", name, level)
66
+ _LOG.debug("Restoring logging level override for %s: %s", name, level)
67
67
  logging.getLogger(name).setLevel(level)
68
68
  except ImportError:
69
69
  pass # don't have redis
70
70
  except Exception: # pylint: disable=broad-except
71
71
  # survive an error there. Logging levels is not business critical...
72
- LOG.warning("Cannot restore logging levels", exc_info=True)
72
+ _LOG.warning("Cannot restore logging levels", exc_info=True)
73
73
 
74
74
 
75
75
  def _store_override(settings: Mapping[str, Any], name: str, level: str) -> None:
76
76
  try:
77
77
  master, _, _ = redis_utils.get(settings)
78
78
  if master:
79
- master.set(REDIS_PREFIX + name, level)
79
+ master.set(_REDIS_PREFIX + name, level)
80
80
  except ImportError:
81
81
  pass
82
82
 
@@ -84,8 +84,8 @@ def _store_override(settings: Mapping[str, Any], name: str, level: str) -> None:
84
84
  def _list_overrides(settings: Mapping[str, Any]) -> Generator[tuple[str, str], None, None]:
85
85
  _, slave, _ = redis_utils.get(settings)
86
86
  if slave is not None:
87
- for key in slave.scan_iter(REDIS_PREFIX + "*"):
87
+ for key in slave.scan_iter(_REDIS_PREFIX + "*"):
88
88
  level = slave.get(key)
89
- name = key[len(REDIS_PREFIX) :]
89
+ name = key[len(_REDIS_PREFIX) :]
90
90
  if level is not None:
91
91
  yield name, str(level)
@@ -7,7 +7,6 @@ import sqlalchemy as sa
7
7
 
8
8
  def generate_model_graph(module: Any) -> None:
9
9
  """Generate a graphical model of the database classes."""
10
-
11
10
  base_name = None
12
11
  if len(sys.argv) == 1:
13
12
  base_name = "Base"
@@ -34,7 +34,6 @@ def init(config: pyramid.config.Configurator) -> None:
34
34
 
35
35
  def includeme(config: pyramid.config.Configurator) -> None:
36
36
  """Initialize json and fast_json renderer."""
37
-
38
37
  pretty_print = config_bool(
39
38
  env_or_config(config, "C2C_JSON_PRETTY_PRINT", "c2c.json.pretty_print", "false")
40
39
  )
@@ -24,7 +24,6 @@ MULTI_PROCESS_COLLECTOR_BROADCAST_CHANNELS = [
24
24
 
25
25
  def start(registry: Optional[prometheus_client.CollectorRegistry] = None) -> None:
26
26
  """Start separate HTTP server to provide the Prometheus metrics."""
27
-
28
27
  if os.environ.get("C2C_PROMETHEUS_PORT") is not None:
29
28
  broadcast.includeme()
30
29
 
@@ -38,20 +37,18 @@ def start(registry: Optional[prometheus_client.CollectorRegistry] = None) -> Non
38
37
 
39
38
  def includeme(config: pyramid.config.Configurator) -> None:
40
39
  """Initialize prometheus_client in pyramid context."""
41
-
40
+ del config # unused
42
41
  broadcast.subscribe("c2cwsgiutils_prometheus_collector_gc", _broadcast_collector_gc)
43
42
  broadcast.subscribe("c2cwsgiutils_prometheus_collector_process", _broadcast_collector_process)
44
43
 
45
44
 
46
45
  def build_metric_name(postfix: str) -> str:
47
46
  """Build the metric name with the prefix from the environment variable."""
48
-
49
47
  return os.environ.get("C2C_PROMETHEUS_PREFIX", "c2cwsgiutils_") + postfix
50
48
 
51
49
 
52
50
  def cleanup() -> None:
53
51
  """Cleanup the prometheus_client registry."""
54
-
55
52
  redis_utils.cleanup()
56
53
  broadcast.cleanup()
57
54
 
@@ -74,7 +71,6 @@ class SerializedMetric(TypedDict):
74
71
 
75
72
  def _broadcast_collector_gc() -> list[SerializedMetric]:
76
73
  """Get the collected GC gauges."""
77
-
78
74
  return serialize_collected_data(prometheus_client.GC_COLLECTOR)
79
75
 
80
76
 
@@ -85,7 +81,6 @@ def _broadcast_collector_process() -> list[SerializedMetric]:
85
81
 
86
82
  def serialize_collected_data(collector: prometheus_client.registry.Collector) -> list[SerializedMetric]:
87
83
  """Serialize the data from the custom collector."""
88
-
89
84
  gauges: list[SerializedMetric] = []
90
85
  for process_gauge in collector.collect():
91
86
  gauge: SerializedMetric = {
@@ -162,7 +157,6 @@ class MemoryMapCollector(prometheus_client.registry.Collector):
162
157
  Initialize.
163
158
 
164
159
  Arguments:
165
-
166
160
  memory_type: can be rss, pss or size
167
161
  pids: the list of pids or none
168
162
  """
c2cwsgiutils/pyramid.py CHANGED
@@ -30,7 +30,6 @@ def includeme(config: pyramid.config.Configurator) -> None:
30
30
  Initialize all the pyramid services and event handlers provided by this library.
31
31
 
32
32
  Arguments:
33
-
34
33
  config: The pyramid Configuration
35
34
  """
36
35
  logging.captureWarnings(True)
@@ -10,6 +10,7 @@ To add some info about requests:
10
10
 
11
11
  A pyramid event handler is installed to setup this filter for the current request.
12
12
  """
13
+
13
14
  import json
14
15
  import logging
15
16
  import logging.config
@@ -20,7 +21,7 @@ from typing import TYPE_CHECKING, Any, Optional, TextIO
20
21
  import cee_syslog_handler
21
22
  from pyramid.threadlocal import get_current_request
22
23
 
23
- LOG = logging.getLogger(__name__)
24
+ _LOG = logging.getLogger(__name__)
24
25
 
25
26
 
26
27
  class _PyramidFilter(logging.Filter):
@@ -7,8 +7,8 @@ import pyramid.config
7
7
 
8
8
  from c2cwsgiutils import config_utils, prometheus
9
9
 
10
- LOG = logging.getLogger(__name__)
11
- ORIG: Optional[Callable[..., Any]] = None
10
+ _LOG = logging.getLogger(__name__)
11
+ _ORIG: Optional[Callable[..., Any]] = None
12
12
 
13
13
  _PROMETHEUS_REDIS_SUMMARY = prometheus_client.Summary(
14
14
  prometheus.build_metric_name("redis"),
@@ -19,9 +19,9 @@ _PROMETHEUS_REDIS_SUMMARY = prometheus_client.Summary(
19
19
 
20
20
 
21
21
  def _execute_command_patch(self: Any, command: str, *args: Any, **options: Any) -> Any:
22
- assert ORIG is not None
22
+ assert _ORIG is not None
23
23
  with _PROMETHEUS_REDIS_SUMMARY.labels(command=command).time():
24
- return ORIG(self, command, *args, **options)
24
+ return _ORIG(self, command, *args, **options)
25
25
 
26
26
 
27
27
  def init(config: Optional[pyramid.config.Configurator] = None) -> None:
@@ -32,15 +32,15 @@ def init(config: Optional[pyramid.config.Configurator] = None) -> None:
32
32
 
33
33
  def includeme(config: Optional[pyramid.config.Configurator] = None) -> None:
34
34
  """Initialize the Redis tracking."""
35
- global ORIG
35
+ global _ORIG # pylint: disable=global-statement
36
36
  if config_utils.env_or_config(
37
37
  config, "C2C_TRACK_REDIS", "c2c.track_redis", True, config_utils.config_bool
38
38
  ):
39
39
  try:
40
- import redis.client
40
+ import redis.client # pylint: disable=import-outside-toplevel
41
41
 
42
- ORIG = redis.client.Redis.execute_command
42
+ _ORIG = redis.client.Redis.execute_command
43
43
  redis.client.Redis.execute_command = _execute_command_patch # type: ignore
44
- LOG.info("Enabled the redis tracking")
44
+ _LOG.info("Enabled the redis tracking")
45
45
  except Exception: # pragma: nocover # pylint: disable=broad-except
46
- LOG.warning("Cannot enable redis tracking", exc_info=True)
46
+ _LOG.warning("Cannot enable redis tracking", exc_info=True)
@@ -11,19 +11,19 @@ import yaml
11
11
 
12
12
  import c2cwsgiutils.config_utils
13
13
 
14
- LOG = logging.getLogger(__name__)
14
+ _LOG = logging.getLogger(__name__)
15
15
 
16
16
  REDIS_URL_KEY = "C2C_REDIS_URL"
17
- REDIS_OPTIONS_KEY = "C2C_REDIS_OPTIONS"
17
+ _REDIS_OPTIONS_KEY = "C2C_REDIS_OPTIONS"
18
18
  REDIS_SENTINELS_KEY = "C2C_REDIS_SENTINELS"
19
19
  REDIS_SERVICENAME_KEY = "C2C_REDIS_SERVICENAME"
20
- REDIS_DB_KEY = "C2C_REDIS_DB"
20
+ _REDIS_DB_KEY = "C2C_REDIS_DB"
21
21
 
22
22
  REDIS_URL_KEY_PROP = "c2c.redis_url"
23
- REDIS_OPTIONS_KEY_PROP = "c2c.redis_options"
23
+ _REDIS_OPTIONS_KEY_PROP = "c2c.redis_options"
24
24
  REDIS_SENTINELS_KEY_PROP = "c2c.redis_sentinels"
25
25
  REDIS_SERVICENAME_KEY_PROP = "c2c.redis_servicename"
26
- REDIS_DB_KEY_PROP = "c2c.redis_db"
26
+ _REDIS_DB_KEY_PROP = "c2c.redis_db"
27
27
 
28
28
  _master: Optional["redis.client.Redis[str]"] = None
29
29
  _slave: Optional["redis.client.Redis[str]"] = None
@@ -32,7 +32,7 @@ _sentinel: Optional[redis.sentinel.Sentinel] = None
32
32
 
33
33
  def cleanup() -> None:
34
34
  """Cleanup the redis connections."""
35
- global _master, _slave, _sentinel
35
+ global _master, _slave, _sentinel # pylint: disable=global-statement
36
36
  _master = None
37
37
  _slave = None
38
38
  _sentinel = None
@@ -52,17 +52,17 @@ def get(
52
52
 
53
53
 
54
54
  def _init(settings: Optional[Mapping[str, Any]]) -> None:
55
- global _master, _slave, _sentinel
55
+ global _master, _slave, _sentinel # pylint: disable=global-statement
56
56
  sentinels = c2cwsgiutils.config_utils.env_or_settings(
57
57
  settings, REDIS_SENTINELS_KEY, REDIS_SENTINELS_KEY_PROP
58
58
  )
59
59
  service_name = c2cwsgiutils.config_utils.env_or_settings(
60
60
  settings, REDIS_SERVICENAME_KEY, REDIS_SERVICENAME_KEY_PROP
61
61
  )
62
- db = c2cwsgiutils.config_utils.env_or_settings(settings, REDIS_DB_KEY, REDIS_DB_KEY_PROP)
62
+ db = c2cwsgiutils.config_utils.env_or_settings(settings, _REDIS_DB_KEY, _REDIS_DB_KEY_PROP)
63
63
  url = c2cwsgiutils.config_utils.env_or_settings(settings, REDIS_URL_KEY, REDIS_URL_KEY_PROP)
64
64
  redis_options_ = c2cwsgiutils.config_utils.env_or_settings(
65
- settings, REDIS_OPTIONS_KEY, REDIS_OPTIONS_KEY_PROP
65
+ settings, _REDIS_OPTIONS_KEY, _REDIS_OPTIONS_KEY_PROP
66
66
  )
67
67
 
68
68
  redis_options = (
@@ -82,16 +82,16 @@ def _init(settings: Optional[Mapping[str, Any]]) -> None:
82
82
  db=db,
83
83
  **redis_options,
84
84
  )
85
- LOG.info("Redis setup using: %s, %s, %s", sentinels, service_name, redis_options_)
85
+ _LOG.info("Redis setup using: %s, %s, %s", sentinels, service_name, redis_options_)
86
86
  _master = _sentinel.master_for(service_name)
87
87
  _slave = _sentinel.slave_for(service_name)
88
88
  return
89
89
  if url:
90
- LOG.info("Redis setup using: %s, with options: %s", url, redis_options_)
90
+ _LOG.info("Redis setup using: %s, with options: %s", url, redis_options_)
91
91
  _master = redis.client.Redis.from_url(url, decode_responses=True, **redis_options)
92
92
  _slave = _master
93
93
  else:
94
- LOG.info(
94
+ _LOG.info(
95
95
  "No Redis configuration found, use %s or %s to configure it", REDIS_URL_KEY, REDIS_SENTINELS_KEY
96
96
  )
97
97
 
@@ -114,22 +114,23 @@ class PubSubWorkerThread(threading.Thread):
114
114
  try:
115
115
  pubsub.get_message(ignore_subscribe_messages=True, timeout=1)
116
116
  if not last_was_ok:
117
- LOG.info("Redis is back")
117
+ _LOG.info("Redis is back")
118
118
  last_was_ok = True
119
119
  except redis.exceptions.RedisError:
120
120
  if last_was_ok:
121
- LOG.warning("Redis connection problem")
121
+ _LOG.warning("Redis connection problem")
122
122
  last_was_ok = False
123
123
  time.sleep(0.5)
124
124
  except Exception: # pylint: disable=broad-except
125
- LOG.warning("Unexpected error", exc_info=True)
126
- LOG.info("Redis subscription worker stopped")
125
+ _LOG.warning("Unexpected error", exc_info=True)
126
+ _LOG.info("Redis subscription worker stopped")
127
127
  pubsub.close()
128
128
  self._running = False
129
129
 
130
130
  def stop(self) -> None:
131
- # stopping simply unsubscribes from all channels and patterns.
132
- # the unsubscribe responses that are generated will short circuit
131
+ """Stop the worker."""
132
+ # Stopping simply unsubscribes from all channels and patterns.
133
+ # The unsubscribe responses that are generated will short circuit
133
134
  # the loop in run(), calling pubsub.close() to clean up the connection
134
135
  self.pubsub.unsubscribe()
135
136
  self.pubsub.punsubscribe()