c2cwsgiutils 5.1.7.dev20230901073305__py3-none-any.whl → 5.2.1.dev197__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 (69) hide show
  1. c2cwsgiutils/__init__.py +13 -13
  2. c2cwsgiutils/acceptance/connection.py +5 -2
  3. c2cwsgiutils/acceptance/image.py +98 -4
  4. c2cwsgiutils/acceptance/package-lock.json +1933 -0
  5. c2cwsgiutils/acceptance/package.json +7 -0
  6. c2cwsgiutils/acceptance/print.py +4 -4
  7. c2cwsgiutils/acceptance/screenshot.js +62 -0
  8. c2cwsgiutils/acceptance/utils.py +14 -22
  9. c2cwsgiutils/auth.py +4 -4
  10. c2cwsgiutils/broadcast/__init__.py +15 -7
  11. c2cwsgiutils/broadcast/interface.py +3 -2
  12. c2cwsgiutils/broadcast/local.py +3 -2
  13. c2cwsgiutils/broadcast/redis.py +8 -7
  14. c2cwsgiutils/client_info.py +5 -5
  15. c2cwsgiutils/config_utils.py +2 -1
  16. c2cwsgiutils/coverage_setup.py +2 -2
  17. c2cwsgiutils/db.py +58 -37
  18. c2cwsgiutils/db_maintenance_view.py +2 -1
  19. c2cwsgiutils/debug/_listeners.py +10 -9
  20. c2cwsgiutils/debug/_views.py +12 -11
  21. c2cwsgiutils/debug/utils.py +5 -5
  22. c2cwsgiutils/errors.py +7 -6
  23. c2cwsgiutils/health_check.py +96 -85
  24. c2cwsgiutils/index.py +90 -105
  25. c2cwsgiutils/loader.py +3 -3
  26. c2cwsgiutils/logging_view.py +3 -2
  27. c2cwsgiutils/models_graph.py +8 -6
  28. c2cwsgiutils/prometheus.py +175 -57
  29. c2cwsgiutils/pyramid.py +4 -2
  30. c2cwsgiutils/pyramid_logging.py +2 -1
  31. c2cwsgiutils/redis_stats.py +13 -11
  32. c2cwsgiutils/redis_utils.py +15 -14
  33. c2cwsgiutils/request_tracking/__init__.py +36 -30
  34. c2cwsgiutils/request_tracking/_sql.py +3 -1
  35. c2cwsgiutils/scripts/genversion.py +4 -4
  36. c2cwsgiutils/scripts/stats_db.py +130 -68
  37. c2cwsgiutils/scripts/test_print.py +1 -1
  38. c2cwsgiutils/sentry.py +2 -1
  39. c2cwsgiutils/setup_process.py +13 -17
  40. c2cwsgiutils/sql_profiler/_impl.py +12 -5
  41. c2cwsgiutils/sqlalchemylogger/README.md +48 -0
  42. c2cwsgiutils/sqlalchemylogger/_models.py +7 -4
  43. c2cwsgiutils/sqlalchemylogger/examples/example.py +15 -0
  44. c2cwsgiutils/sqlalchemylogger/handlers.py +11 -8
  45. c2cwsgiutils/static/favicon-16x16.png +0 -0
  46. c2cwsgiutils/static/favicon-32x32.png +0 -0
  47. c2cwsgiutils/stats_pyramid/__init__.py +7 -11
  48. c2cwsgiutils/stats_pyramid/_db_spy.py +14 -11
  49. c2cwsgiutils/stats_pyramid/_pyramid_spy.py +29 -20
  50. c2cwsgiutils/templates/index.html.mako +50 -0
  51. c2cwsgiutils/version.py +49 -16
  52. c2cwsgiutils-5.2.1.dev197.dist-info/LICENSE +22 -0
  53. {c2cwsgiutils-5.1.7.dev20230901073305.dist-info → c2cwsgiutils-5.2.1.dev197.dist-info}/METADATA +187 -135
  54. c2cwsgiutils-5.2.1.dev197.dist-info/RECORD +67 -0
  55. {c2cwsgiutils-5.1.7.dev20230901073305.dist-info → c2cwsgiutils-5.2.1.dev197.dist-info}/WHEEL +1 -2
  56. c2cwsgiutils-5.2.1.dev197.dist-info/entry_points.txt +21 -0
  57. c2cwsgiutils/acceptance/composition.py +0 -129
  58. c2cwsgiutils/metrics.py +0 -110
  59. c2cwsgiutils/scripts/check_es.py +0 -130
  60. c2cwsgiutils/scripts/coverage_report.py +0 -36
  61. c2cwsgiutils/stats.py +0 -355
  62. c2cwsgiutils/stats_pyramid/_views.py +0 -16
  63. c2cwsgiutils-5.1.7.dev20230901073305.data/scripts/c2cwsgiutils-run +0 -32
  64. c2cwsgiutils-5.1.7.dev20230901073305.dist-info/LICENSE.txt +0 -28
  65. c2cwsgiutils-5.1.7.dev20230901073305.dist-info/RECORD +0 -69
  66. c2cwsgiutils-5.1.7.dev20230901073305.dist-info/entry_points.txt +0 -25
  67. c2cwsgiutils-5.1.7.dev20230901073305.dist-info/top_level.txt +0 -2
  68. tests/acceptance/__init__.py +0 -0
  69. tests/acceptance/test_utils.py +0 -13
c2cwsgiutils/db.py CHANGED
@@ -2,8 +2,9 @@
2
2
  import logging
3
3
  import re
4
4
  import warnings
5
- from typing import Pattern # noqa # pylint: disable=unused-import
6
- from typing import Any, Callable, Iterable, Optional, Tuple, Union, cast
5
+ from collections.abc import Iterable
6
+ from re import Pattern
7
+ from typing import TYPE_CHECKING, Any, Callable, Optional, Union, cast
7
8
 
8
9
  import pyramid.config
9
10
  import pyramid.config.settings
@@ -13,9 +14,16 @@ import sqlalchemy.orm
13
14
  import transaction
14
15
  import zope.sqlalchemy
15
16
  from sqlalchemy import engine_from_config
16
- from sqlalchemy.orm import sessionmaker
17
17
  from zope.sqlalchemy import register
18
18
 
19
+ if TYPE_CHECKING:
20
+ scoped_session = sqlalchemy.orm.scoped_session[sqlalchemy.orm.Session]
21
+ sessionmaker = sqlalchemy.orm.sessionmaker[sqlalchemy.orm.Session]
22
+ else:
23
+ scoped_session = sqlalchemy.orm.scoped_session
24
+ sessionmaker = sqlalchemy.orm.sessionmaker
25
+
26
+
19
27
  LOG = logging.getLogger(__name__)
20
28
  RE_COMPILE: Callable[[str], Pattern[str]] = re.compile
21
29
 
@@ -35,8 +43,8 @@ def setup_session(
35
43
  slave_prefix: Optional[str] = None,
36
44
  force_master: Optional[Iterable[str]] = None,
37
45
  force_slave: Optional[Iterable[str]] = None,
38
- ) -> Tuple[
39
- Union[sqlalchemy.orm.Session, sqlalchemy.orm.scoped_session],
46
+ ) -> tuple[
47
+ Union[sqlalchemy.orm.Session, scoped_session],
40
48
  sqlalchemy.engine.Engine,
41
49
  sqlalchemy.engine.Engine,
42
50
  ]:
@@ -51,7 +59,7 @@ def setup_session(
51
59
  Those parameters are lists of regex that are going to be matched against "{VERB} {PATH}". Warning, the
52
60
  path includes the route_prefix.
53
61
 
54
- Arguments:
62
+ Keyword Arguments:
55
63
 
56
64
  config: The pyramid Configuration object
57
65
  master_prefix: The prefix for the master connection configuration entries in the application \
@@ -68,7 +76,7 @@ def setup_session(
68
76
  slave_prefix = master_prefix
69
77
  settings = config.registry.settings
70
78
  rw_engine = sqlalchemy.engine_from_config(settings, master_prefix + ".")
71
- rw_engine.c2c_name = master_prefix
79
+ rw_engine.c2c_name = master_prefix # type: ignore
72
80
  factory = sqlalchemy.orm.sessionmaker(bind=rw_engine)
73
81
  register(factory)
74
82
  db_session = sqlalchemy.orm.scoped_session(factory)
@@ -77,14 +85,14 @@ def setup_session(
77
85
  if settings[master_prefix + ".url"] != settings.get(slave_prefix + ".url"):
78
86
  LOG.info("Using a slave DB for reading %s", master_prefix)
79
87
  ro_engine = sqlalchemy.engine_from_config(config.get_settings(), slave_prefix + ".")
80
- ro_engine.c2c_name = slave_prefix
88
+ ro_engine.c2c_name = slave_prefix # type: ignore
81
89
  tween_name = master_prefix.replace(".", "_")
82
90
  _add_tween(config, tween_name, db_session, force_master, force_slave)
83
91
  else:
84
92
  ro_engine = rw_engine
85
93
 
86
- db_session.c2c_rw_bind = rw_engine
87
- db_session.c2c_ro_bind = ro_engine
94
+ db_session.c2c_rw_bind = rw_engine # type: ignore
95
+ db_session.c2c_ro_bind = ro_engine # type: ignore
88
96
  return db_session, rw_engine, ro_engine
89
97
 
90
98
 
@@ -96,7 +104,7 @@ def create_session(
96
104
  force_master: Optional[Iterable[str]] = None,
97
105
  force_slave: Optional[Iterable[str]] = None,
98
106
  **engine_config: Any,
99
- ) -> Union[sqlalchemy.orm.Session, sqlalchemy.orm.scoped_session]:
107
+ ) -> Union[sqlalchemy.orm.Session, scoped_session]:
100
108
  """
101
109
  Create a SQLAlchemy session.
102
110
 
@@ -108,7 +116,7 @@ def create_session(
108
116
  Those parameters are lists of regex that are going to be matched against "{VERB} {PATH}". Warning, the
109
117
  path includes the route_prefix.
110
118
 
111
- Arguments:
119
+ Keyword Arguments:
112
120
 
113
121
  config: The pyramid Configuration object. If None, only master is used
114
122
  name: The name of the check
@@ -134,21 +142,21 @@ def create_session(
134
142
  LOG.info("Using a slave DB for reading %s", name)
135
143
  ro_engine = sqlalchemy.create_engine(slave_url, **engine_config)
136
144
  _add_tween(config, name, db_session, force_master, force_slave)
137
- rw_engine.c2c_name = name + "_master"
138
- ro_engine.c2c_name = name + "_slave"
145
+ rw_engine.c2c_name = name + "_master" # type: ignore
146
+ ro_engine.c2c_name = name + "_slave" # type: ignore
139
147
  else:
140
- rw_engine.c2c_name = name
148
+ rw_engine.c2c_name = name # type: ignore
141
149
  ro_engine = rw_engine
142
150
 
143
- db_session.c2c_rw_bind = rw_engine
144
- db_session.c2c_ro_bind = ro_engine
151
+ db_session.c2c_rw_bind = rw_engine # type: ignore
152
+ db_session.c2c_ro_bind = ro_engine # type: ignore
145
153
  return db_session
146
154
 
147
155
 
148
156
  def _add_tween(
149
157
  config: pyramid.config.Configurator,
150
158
  name: str,
151
- db_session: Union[sqlalchemy.orm.Session, sqlalchemy.orm.scoped_session],
159
+ db_session: scoped_session,
152
160
  force_master: Optional[Iterable[str]],
153
161
  force_slave: Optional[Iterable[str]],
154
162
  ) -> None:
@@ -177,11 +185,19 @@ def _add_tween(
177
185
  not has_force_master
178
186
  and (request.method in ("GET", "OPTIONS") or any(r.match(method_path) for r in slave_paths))
179
187
  ):
180
- LOG.debug("Using %s database for: %s", db_session.c2c_ro_bind.c2c_name, method_path)
181
- session.bind = db_session.c2c_ro_bind
188
+ LOG.debug(
189
+ "Using %s database for: %s",
190
+ db_session.c2c_ro_bind.c2c_name, # type: ignore
191
+ method_path,
192
+ )
193
+ session.bind = db_session.c2c_ro_bind # type: ignore
182
194
  else:
183
- LOG.debug("Using %s database for: %s", db_session.c2c_rw_bind.c2c_name, method_path)
184
- session.bind = db_session.c2c_rw_bind
195
+ LOG.debug(
196
+ "Using %s database for: %s",
197
+ db_session.c2c_rw_bind.c2c_name, # type: ignore
198
+ method_path,
199
+ )
200
+ session.bind = db_session.c2c_rw_bind # type: ignore
185
201
 
186
202
  try:
187
203
  return handler(request)
@@ -194,7 +210,7 @@ def _add_tween(
194
210
  config.add_tween("c2cwsgiutils.db.tweens." + name, over="pyramid_tm.tm_tween_factory")
195
211
 
196
212
 
197
- class SessionFactory(sessionmaker): # type: ignore
213
+ class SessionFactory(sessionmaker):
198
214
  """The custom session factory that manage the read only and read write sessions."""
199
215
 
200
216
  def __init__(
@@ -214,18 +230,18 @@ class SessionFactory(sessionmaker): # type: ignore
214
230
 
215
231
  def engine_name(self, readwrite: bool) -> str:
216
232
  if readwrite:
217
- return cast(str, self.rw_engine.c2c_name)
218
- return cast(str, self.ro_engine.c2c_name)
233
+ return cast(str, self.rw_engine.c2c_name) # type: ignore
234
+ return cast(str, self.ro_engine.c2c_name) # type: ignore
219
235
 
220
- def __call__(
236
+ def __call__( # type: ignore
221
237
  self, request: Optional[pyramid.request.Request], readwrite: Optional[bool] = None, **local_kw: Any
222
- ) -> sqlalchemy.orm.Session:
238
+ ) -> scoped_session:
223
239
  if readwrite is not None:
224
240
  if readwrite and not force_readonly:
225
- LOG.debug("Using %s database", self.rw_engine.c2c_name)
241
+ LOG.debug("Using %s database", self.rw_engine.c2c_name) # type: ignore
226
242
  self.configure(bind=self.rw_engine)
227
243
  else:
228
- LOG.debug("Using %s database", self.ro_engine.c2c_name)
244
+ LOG.debug("Using %s database", self.ro_engine.c2c_name) # type: ignore
229
245
  self.configure(bind=self.ro_engine)
230
246
  else:
231
247
  assert request is not None
@@ -238,12 +254,12 @@ class SessionFactory(sessionmaker): # type: ignore
238
254
  or any(r.match(method_path) for r in self.slave_paths)
239
255
  )
240
256
  ):
241
- LOG.debug("Using %s database for: %s", self.ro_engine.c2c_name, method_path)
257
+ LOG.debug("Using %s database for: %s", self.ro_engine.c2c_name, method_path) # type: ignore
242
258
  self.configure(bind=self.ro_engine)
243
259
  else:
244
- LOG.debug("Using %s database for: %s", self.rw_engine.c2c_name, method_path)
260
+ LOG.debug("Using %s database for: %s", self.rw_engine.c2c_name, method_path) # type: ignore
245
261
  self.configure(bind=self.rw_engine)
246
- return super().__call__(**local_kw)
262
+ return super().__call__(**local_kw) # type: ignore
247
263
 
248
264
 
249
265
  def get_engine(
@@ -253,7 +269,9 @@ def get_engine(
253
269
  return engine_from_config(settings, prefix)
254
270
 
255
271
 
256
- def get_session_factory(engine: sqlalchemy.engine.Engine) -> sessionmaker:
272
+ def get_session_factory(
273
+ engine: sqlalchemy.engine.Engine,
274
+ ) -> sessionmaker:
257
275
  """Get the session factory from the engine."""
258
276
  factory = sessionmaker()
259
277
  factory.configure(bind=engine)
@@ -320,6 +338,7 @@ def get_tm_session(
320
338
  request = dbsession.info["request"]
321
339
  """
322
340
  dbsession = session_factory()
341
+ assert isinstance(dbsession, sqlalchemy.orm.Session), type(dbsession)
323
342
  zope.sqlalchemy.register(dbsession, transaction_manager=transaction_manager)
324
343
  return dbsession
325
344
 
@@ -328,7 +347,7 @@ def get_tm_session_pyramid(
328
347
  session_factory: SessionFactory,
329
348
  transaction_manager: transaction.TransactionManager,
330
349
  request: pyramid.request.Request,
331
- ) -> sqlalchemy.orm.Session:
350
+ ) -> scoped_session:
332
351
  """
333
352
  Get a ``sqlalchemy.orm.Session`` instance backed by a transaction.
334
353
 
@@ -349,7 +368,7 @@ def init(
349
368
  """
350
369
  Initialize the database for a Pyramid app.
351
370
 
352
- Arguments:
371
+ Keyword Arguments:
353
372
 
354
373
  config: The pyramid Configuration object
355
374
  master_prefix: The prefix for the master connection configuration entries in the application \
@@ -368,13 +387,13 @@ def init(
368
387
  dbengine = settings.get("dbengine")
369
388
  if not dbengine:
370
389
  rw_engine = get_engine(settings, master_prefix + ".")
371
- rw_engine.c2c_name = master_prefix
390
+ rw_engine.c2c_name = master_prefix # type: ignore
372
391
 
373
392
  # Setup a slave DB connection and add a tween to use it.
374
393
  if slave_prefix and settings[master_prefix + ".url"] != settings.get(slave_prefix + ".url"):
375
394
  LOG.info("Using a slave DB for reading %s", master_prefix)
376
395
  ro_engine = get_engine(config.get_settings(), slave_prefix + ".")
377
- ro_engine.c2c_name = slave_prefix
396
+ ro_engine.c2c_name = slave_prefix # type: ignore
378
397
  else:
379
398
  ro_engine = rw_engine
380
399
  else:
@@ -390,6 +409,8 @@ def init(
390
409
  if dbsession is None:
391
410
  # request.tm is the transaction manager used by pyramid_tm
392
411
  dbsession = get_tm_session_pyramid(session_factory, request.tm, request=request)
412
+ assert dbsession is not None
413
+ assert isinstance(dbsession, sqlalchemy.orm.Session), type(dbsession)
393
414
  return dbsession
394
415
 
395
416
  config.add_request_method(dbsession, reify=True)
@@ -1,6 +1,7 @@
1
1
  import logging
2
2
  import warnings
3
- from typing import Any, Mapping, Optional, cast
3
+ from collections.abc import Mapping
4
+ from typing import Any, Optional, cast
4
5
 
5
6
  import pyramid.request
6
7
 
@@ -3,7 +3,8 @@ import sys
3
3
  import threading
4
4
  import time
5
5
  import traceback
6
- from typing import Any, Dict, List, Mapping, Optional, Tuple, cast
6
+ from collections.abc import Mapping
7
+ from typing import Any, Optional, cast
7
8
 
8
9
  import objgraph
9
10
 
@@ -13,7 +14,7 @@ from c2cwsgiutils.debug.utils import get_size
13
14
  FILES_FIELDS = {"__name__", "__doc__", "__package__", "__loader__", "__spec__", "__file__"}
14
15
 
15
16
 
16
- def _dump_stacks_impl() -> Dict[str, Any]:
17
+ def _dump_stacks_impl() -> dict[str, Any]:
17
18
  id2name = {th.ident: th.name for th in threading.enumerate()}
18
19
  threads = {}
19
20
  for thread_id, stack in sys._current_frames().items(): # pylint: disable=W0212
@@ -47,10 +48,10 @@ def _dump_memory_impl(
47
48
 
48
49
  if analyze_type:
49
50
  # timeout after one minute, must be set to a bit less that the timeout of the broadcast in _views.py
50
- timeout = time.monotonic() + 60
51
+ timeout = time.perf_counter() + 60
51
52
 
52
- mod_counts: Dict[str, int] = {}
53
- biggest_objects: List[Tuple[float, Any]] = []
53
+ mod_counts: dict[str, int] = {}
54
+ biggest_objects: list[tuple[float, Any]] = []
54
55
  result[analyze_type] = {}
55
56
  for obj in objgraph.by_type(analyze_type):
56
57
  if analyze_type == "builtins.function":
@@ -80,12 +81,12 @@ def _dump_memory_impl(
80
81
  biggest_objects.sort(key=lambda x: x[0])
81
82
  if len(biggest_objects) > limit:
82
83
  biggest_objects = biggest_objects[-limit:]
83
- if time.monotonic() > timeout:
84
+ if time.perf_counter() > timeout:
84
85
  result[analyze_type]["timeout"] = True
85
86
  break
86
87
  if analyze_type == "builtins.function":
87
88
  result[analyze_type]["modules"] = [
88
- dict(module=i[0], nb_func=i[1])
89
+ {"module": i[0], "nb_func": i[1]}
89
90
  for i in sorted(mod_counts.items(), key=lambda x: -x[1])[:limit]
90
91
  ]
91
92
  elif analyze_type == "linecache":
@@ -93,13 +94,13 @@ def _dump_memory_impl(
93
94
 
94
95
  cache = linecache.cache
95
96
  result[analyze_type]["biggest_objects"] = sorted(
96
- (dict(filename=k, size_kb=get_size(v)) for k, v in cache.items()),
97
+ ({"filename": k, "size_kb": get_size(v)} for k, v in cache.items()),
97
98
  key=lambda i: -(cast(int, i["size_kb"])),
98
99
  )
99
100
  else:
100
101
  biggest_objects.reverse()
101
102
  result[analyze_type]["biggest_objects"] = [
102
- dict(size_kb=i[0], repr=repr(i[1])) for i in biggest_objects
103
+ {"size_kb": i[0], "repr": repr(i[1])} for i in biggest_objects
103
104
  ]
104
105
  return result
105
106
 
@@ -2,9 +2,10 @@ import gc
2
2
  import logging
3
3
  import re
4
4
  import time
5
+ from collections.abc import Mapping
5
6
  from datetime import datetime
6
7
  from io import StringIO
7
- from typing import Any, Callable, Dict, List, Mapping, cast
8
+ from typing import Any, Callable, cast
8
9
 
9
10
  import objgraph
10
11
  import pyramid.config
@@ -19,9 +20,9 @@ LOG = logging.getLogger(__name__)
19
20
  SPACE_RE = re.compile(r" +")
20
21
 
21
22
 
22
- def _beautify_stacks(source: List[Mapping[str, Any]]) -> List[Mapping[str, Any]]:
23
+ def _beautify_stacks(source: list[Mapping[str, Any]]) -> list[Mapping[str, Any]]:
23
24
  """Group the identical stacks together along with a list of threads sporting them."""
24
- results: List[Mapping[str, Any]] = []
25
+ results: list[Mapping[str, Any]] = []
25
26
  for host_stacks in source:
26
27
  host_id = f"{host_stacks['hostname']}/{host_stacks['pid']:d}"
27
28
  for thread, frames in host_stacks["threads"].items():
@@ -35,14 +36,14 @@ def _beautify_stacks(source: List[Mapping[str, Any]]) -> List[Mapping[str, Any]]
35
36
  return results
36
37
 
37
38
 
38
- def _dump_stacks(request: pyramid.request.Request) -> List[Mapping[str, Any]]:
39
+ def _dump_stacks(request: pyramid.request.Request) -> list[Mapping[str, Any]]:
39
40
  auth.auth_view(request)
40
41
  result = broadcast.broadcast("c2c_dump_stacks", expect_answers=True)
41
42
  assert result is not None
42
43
  return _beautify_stacks(result)
43
44
 
44
45
 
45
- def _dump_memory(request: pyramid.request.Request) -> List[Mapping[str, Any]]:
46
+ def _dump_memory(request: pyramid.request.Request) -> list[Mapping[str, Any]]:
46
47
  auth.auth_view(request)
47
48
  limit = int(request.params.get("limit", "30"))
48
49
  analyze_type = request.params.get("analyze_type")
@@ -57,7 +58,7 @@ def _dump_memory(request: pyramid.request.Request) -> List[Mapping[str, Any]]:
57
58
  return result
58
59
 
59
60
 
60
- def _dump_memory_diff(request: pyramid.request.Request) -> List[Any]:
61
+ def _dump_memory_diff(request: pyramid.request.Request) -> list[Any]:
61
62
  auth.auth_view(request)
62
63
  limit = int(request.params.get("limit", "30"))
63
64
  if "path" in request.matchdict:
@@ -72,7 +73,7 @@ def _dump_memory_diff(request: pyramid.request.Request) -> List[Any]:
72
73
  if len(split_path) > 1:
73
74
  sub_request.query_string = split_path[1]
74
75
 
75
- # Warm-up run
76
+ # warm-up run
76
77
  try:
77
78
  if "no_warmup" not in request.params:
78
79
  request.invoke_subrequest(sub_request)
@@ -81,7 +82,7 @@ def _dump_memory_diff(request: pyramid.request.Request) -> List[Any]:
81
82
 
82
83
  LOG.debug("checking memory growth for %s", path)
83
84
 
84
- peak_stats: Dict[Any, Any] = {}
85
+ peak_stats: dict[Any, Any] = {}
85
86
  for i in range(3):
86
87
  gc.collect(i)
87
88
 
@@ -158,7 +159,7 @@ def _add_view(
158
159
  config.add_view(view, route_name="c2c_debug_" + name, renderer="fast_json", http_cache=0)
159
160
 
160
161
 
161
- def _dump_memory_maps(request: pyramid.request.Request) -> List[Dict[str, Any]]:
162
+ def _dump_memory_maps(request: pyramid.request.Request) -> list[dict[str, Any]]:
162
163
  auth.auth_view(request)
163
164
  return sorted(dump_memory_maps(), key=lambda i: cast(int, -i.get("pss_kb", 0)))
164
165
 
@@ -168,13 +169,13 @@ def _show_refs(request: pyramid.request.Request) -> pyramid.response.Response:
168
169
  for generation in range(3):
169
170
  gc.collect(generation)
170
171
 
171
- objs: List[Any] = []
172
+ objs: list[Any] = []
172
173
  if "analyze_type" in request.params:
173
174
  objs = objgraph.by_type(request.params["analyze_type"])
174
175
  elif "analyze_id" in request.params:
175
176
  objs = [objgraph.by(int(request.params["analyze_id"]))]
176
177
 
177
- args: Dict[str, Any] = {
178
+ args: dict[str, Any] = {
178
179
  "refcounts": True,
179
180
  }
180
181
  if request.params.get("max_depth", "") != "":
@@ -5,7 +5,7 @@ import re
5
5
  import sys
6
6
  from collections import defaultdict
7
7
  from types import FunctionType, ModuleType
8
- from typing import Any, Dict, List, Set
8
+ from typing import Any
9
9
 
10
10
  # 7ff7d33bd000-7ff7d33be000 r--p 00000000 00:65 49 /usr/lib/toto.so
11
11
  SMAPS_LOCATION_RE = re.compile(r"^[0-9a-f]+-[0-9a-f]+ +.... +[0-9a-f]+ +[^ ]+ +\d+ +(.*)$")
@@ -21,7 +21,7 @@ def get_size(obj: Any) -> int:
21
21
  """Get the sum size of object & members."""
22
22
  if isinstance(obj, BLACKLIST):
23
23
  return 0
24
- seen_ids: Set[int] = set()
24
+ seen_ids: set[int] = set()
25
25
  size = 0
26
26
  objects = [obj]
27
27
  while objects:
@@ -35,14 +35,14 @@ def get_size(obj: Any) -> int:
35
35
  return size
36
36
 
37
37
 
38
- def dump_memory_maps(pid: str = "self") -> List[Dict[str, Any]]:
38
+ def dump_memory_maps(pid: str = "self") -> list[dict[str, Any]]:
39
39
  """Get the Linux memory maps."""
40
40
  filename = os.path.join("/proc", pid, "smaps")
41
41
  if not os.path.exists(filename):
42
42
  return []
43
43
  with open(filename, encoding="utf-8") as input_:
44
- cur_dict: Dict[str, int] = defaultdict(int)
45
- sizes: Dict[str, Any] = {}
44
+ cur_dict: dict[str, int] = defaultdict(int)
45
+ sizes: dict[str, Any] = {}
46
46
  for line in input_:
47
47
  line = line.rstrip("\n")
48
48
  matcher = SMAPS_LOCATION_RE.match(line)
c2cwsgiutils/errors.py CHANGED
@@ -60,7 +60,7 @@ def _do_error(
60
60
  request.method,
61
61
  request.url,
62
62
  status,
63
- extra={"referer": request.referer},
63
+ extra={"referrer": request.referrer},
64
64
  exc_info=exception,
65
65
  )
66
66
 
@@ -88,7 +88,7 @@ def _http_error(exception: HTTPException, request: pyramid.request.Request) -> A
88
88
  request.url,
89
89
  exception.status_code,
90
90
  str(exception),
91
- extra={"referer": request.referer},
91
+ extra={"referrer": request.referrer},
92
92
  )
93
93
  request.response.headers.update(exception.headers) # forward headers
94
94
  _add_cors(request)
@@ -107,10 +107,11 @@ def _include_dev_details(request: pyramid.request.Request) -> bool:
107
107
  def _integrity_error(
108
108
  exception: sqlalchemy.exc.StatementError, request: pyramid.request.Request
109
109
  ) -> pyramid.response.Response:
110
- def reduce_info_sent(e: sqlalchemy.exc.StatementError) -> None:
111
- # remove details (SQL statement and links to SQLAlchemy) from the error
112
- e.statement = None
113
- e.code = None
110
+ def reduce_info_sent(e: Exception) -> None:
111
+ if isinstance(e, sqlalchemy.exc.StatementError):
112
+ # remove details (SQL statement and links to SQLAlchemy) from the error
113
+ e.statement = None
114
+ e.code = None
114
115
 
115
116
  return _do_error(request, 400, exception, reduce_info_sent=reduce_info_sent)
116
117