c2cwsgiutils 6.1.7.dev6__tar.gz → 6.2.0.dev4__tar.gz

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 (67) hide show
  1. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/LICENSE +1 -1
  2. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/PKG-INFO +2 -2
  3. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/README.md +1 -1
  4. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/acceptance/image.py +3 -10
  5. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/acceptance/package-lock.json +8 -8
  6. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/acceptance/package.json +1 -1
  7. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/client_info.py +4 -4
  8. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/debug/_listeners.py +6 -1
  9. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/debug/_views.py +6 -13
  10. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/index.py +4 -27
  11. c2cwsgiutils-6.2.0.dev4/c2cwsgiutils/loader.py +21 -0
  12. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/prometheus.py +3 -9
  13. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/sqlalchemylogger/handlers.py +1 -1
  14. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/stats_pyramid/_pyramid_spy.py +0 -12
  15. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/templates/index.html.mako +1 -4
  16. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/pyproject.toml +13 -13
  17. c2cwsgiutils-6.1.7.dev6/c2cwsgiutils/loader.py +0 -40
  18. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/__init__.py +0 -0
  19. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/acceptance/__init__.py +0 -0
  20. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/acceptance/connection.py +0 -0
  21. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/acceptance/print.py +0 -0
  22. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/acceptance/screenshot.js +0 -0
  23. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/acceptance/utils.py +0 -0
  24. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/auth.py +0 -0
  25. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/broadcast/__init__.py +0 -0
  26. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/broadcast/interface.py +0 -0
  27. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/broadcast/local.py +0 -0
  28. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/broadcast/redis.py +0 -0
  29. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/broadcast/utils.py +0 -0
  30. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/config_utils.py +0 -0
  31. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/coverage_setup.py +0 -0
  32. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/db.py +0 -0
  33. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/db_maintenance_view.py +0 -0
  34. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/debug/__init__.py +0 -0
  35. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/debug/utils.py +0 -0
  36. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/errors.py +0 -0
  37. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/health_check.py +0 -0
  38. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/logging_view.py +0 -0
  39. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/models_graph.py +0 -0
  40. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/pretty_json.py +0 -0
  41. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/profiler.py +0 -0
  42. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/py.typed +0 -0
  43. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/pyramid.py +0 -0
  44. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/pyramid_logging.py +0 -0
  45. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/redis_stats.py +0 -0
  46. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/redis_utils.py +0 -0
  47. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/request_tracking/__init__.py +0 -0
  48. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/request_tracking/_sql.py +0 -0
  49. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/scripts/__init__.py +0 -0
  50. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/scripts/genversion.py +0 -0
  51. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/scripts/stats_db.py +0 -0
  52. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/scripts/test_print.py +0 -0
  53. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/sentry.py +0 -0
  54. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/services.py +0 -0
  55. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/setup_process.py +0 -0
  56. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/sql_profiler/__init__.py +0 -0
  57. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/sql_profiler/_impl.py +0 -0
  58. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/sqlalchemylogger/README.md +0 -0
  59. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/sqlalchemylogger/__init__.py +0 -0
  60. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/sqlalchemylogger/_filters.py +0 -0
  61. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/sqlalchemylogger/_models.py +0 -0
  62. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/sqlalchemylogger/examples/example.py +0 -0
  63. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/static/favicon-16x16.png +0 -0
  64. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/static/favicon-32x32.png +0 -0
  65. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/stats_pyramid/__init__.py +0 -0
  66. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/stats_pyramid/_db_spy.py +0 -0
  67. {c2cwsgiutils-6.1.7.dev6 → c2cwsgiutils-6.2.0.dev4}/c2cwsgiutils/version.py +0 -0
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015-2025, Camptocamp SA
1
+ Copyright (c) 2015-2024, Camptocamp SA
2
2
  All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: c2cwsgiutils
3
- Version: 6.1.7.dev6
3
+ Version: 6.2.0.dev4
4
4
  Summary: Common utilities for Camptocamp WSGI applications
5
5
  Home-page: https://github.com/camptocamp/c2cwsgiutils
6
6
  License: BSD-2-Clause
@@ -64,7 +64,7 @@ Description-Content-Type: text/markdown
64
64
 
65
65
  # Camptocamp WSGI utilities
66
66
 
67
- This is a Python 3 library providing common tools for Camptocamp WSGI
67
+ This is a Python 3 library (>=3.5) providing common tools for Camptocamp WSGI
68
68
  applications:
69
69
 
70
70
  - Provide prometheus metrics
@@ -1,6 +1,6 @@
1
1
  # Camptocamp WSGI utilities
2
2
 
3
- This is a Python 3 library providing common tools for Camptocamp WSGI
3
+ This is a Python 3 library (>=3.5) providing common tools for Camptocamp WSGI
4
4
  applications:
5
5
 
6
6
  - Provide prometheus metrics
@@ -58,7 +58,7 @@ def normalize_image(image: NpNdarrayInt) -> NpNdarrayInt:
58
58
  return image
59
59
 
60
60
 
61
- def check_image( # pylint: disable=too-many-locals,too-many-statements
61
+ def check_image(
62
62
  result_folder: str,
63
63
  image_to_check: NpNdarrayInt,
64
64
  expected_filename: str,
@@ -108,13 +108,6 @@ def check_image( # pylint: disable=too-many-locals,too-many-statements
108
108
 
109
109
  mask = None
110
110
  if mask_filename is not None:
111
- background_color = [255, 255, 255]
112
- for color in range(3):
113
- img_hist, _ = skimage.exposure.histogram(
114
- image_to_check[..., color], nbins=256, source_range="dtype"
115
- )
116
- background_color[color] = np.argmax(img_hist)
117
-
118
111
  mask = skimage.io.imread(mask_filename)
119
112
 
120
113
  assert mask is not None, "Wrong mask: " + mask_filename
@@ -137,7 +130,7 @@ def check_image( # pylint: disable=too-many-locals,too-many-statements
137
130
  assert (
138
131
  mask.shape[0] == image_to_check.shape[0] and mask.shape[1] == image_to_check.shape[1]
139
132
  ), f"Mask and image should have the same shape ({mask.shape} != {image_to_check.shape})"
140
- image_to_check[mask] = background_color
133
+ image_to_check[mask] = [255, 255, 255]
141
134
 
142
135
  if not os.path.exists(result_folder):
143
136
  os.makedirs(result_folder)
@@ -155,7 +148,7 @@ def check_image( # pylint: disable=too-many-locals,too-many-statements
155
148
  assert (
156
149
  expected.shape[0] == mask.shape[0] and expected.shape[1] == mask.shape[1]
157
150
  ), f"Mask and expected image should have the same shape ({mask.shape} != {expected.shape})"
158
- expected[mask] = background_color
151
+ expected[mask] = [255, 255, 255]
159
152
 
160
153
  assert (
161
154
  expected.shape == image_to_check.shape
@@ -6,7 +6,7 @@
6
6
  "": {
7
7
  "dependencies": {
8
8
  "commander": "12.1.0",
9
- "puppeteer": "23.5.3"
9
+ "puppeteer": "23.5.0"
10
10
  }
11
11
  },
12
12
  "node_modules/@babel/code-frame": {
@@ -909,9 +909,9 @@
909
909
  }
910
910
  },
911
911
  "node_modules/puppeteer": {
912
- "version": "23.5.3",
913
- "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.5.3.tgz",
914
- "integrity": "sha512-FghmfBsr/UUpe48OiCg1gV3W4vVfQJKjQehbF07SjnQvEpWcvPTah1nykfGWdOQQ1ydJPIXcajzWN7fliCU3zw==",
912
+ "version": "23.5.0",
913
+ "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.5.0.tgz",
914
+ "integrity": "sha512-jnUx5M0YtFva7vXr39qqsxgB46JiwXJavuM1Hgsqbd9WWiGTEUt9klGpTxyHi+ZQf3NUgleDhNsnI10IK8Ebsg==",
915
915
  "hasInstallScript": true,
916
916
  "license": "Apache-2.0",
917
917
  "dependencies": {
@@ -919,7 +919,7 @@
919
919
  "chromium-bidi": "0.8.0",
920
920
  "cosmiconfig": "^9.0.0",
921
921
  "devtools-protocol": "0.0.1342118",
922
- "puppeteer-core": "23.5.3",
922
+ "puppeteer-core": "23.5.0",
923
923
  "typed-query-selector": "^2.12.0"
924
924
  },
925
925
  "bin": {
@@ -930,9 +930,9 @@
930
930
  }
931
931
  },
932
932
  "node_modules/puppeteer-core": {
933
- "version": "23.5.3",
934
- "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.5.3.tgz",
935
- "integrity": "sha512-V58MZD/B3CwkYsqSEQlHKbavMJptF04fzhMdUpiCRCmUVhwZNwSGEPhaiZ1f8I3ABQUirg3VNhXVB6Z1ubHXtQ==",
933
+ "version": "23.5.0",
934
+ "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.5.0.tgz",
935
+ "integrity": "sha512-+5ed+625GuQ2emRHqYec8khT9LP14FWzv8hYl0HiM6hnnlNzdVU9uDJIPHeCPLIWxq15ost9MeF8kBk4R3eiFw==",
936
936
  "license": "Apache-2.0",
937
937
  "dependencies": {
938
938
  "@puppeteer/browsers": "2.4.0",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "dependencies": {
3
3
  "commander": "12.1.0",
4
- "puppeteer": "23.5.3"
4
+ "puppeteer": "23.5.0"
5
5
  },
6
6
  "type": "module"
7
7
  }
@@ -26,13 +26,13 @@ class Filter:
26
26
  _handle_others(environ)
27
27
 
28
28
  if "C2CWSGIUTILS_FORCE_PROTO" in os.environ:
29
- environ["wsgi.url_scheme"] = os.environ["C2CWSGIUTILS_FORCE_PROTO"]
29
+ os.environ["wsgi.url_scheme"] = os.environ.pop("C2CWSGIUTILS_FORCE_PROTO")
30
30
  if "C2CWSGIUTILS_FORCE_HOST" in os.environ:
31
- environ["HTTP_HOST"] = os.environ["C2CWSGIUTILS_FORCE_HOST"]
31
+ os.environ["HTTP_HOST"] = os.environ.pop("C2CWSGIUTILS_FORCE_HOST")
32
32
  if "C2CWSGIUTILS_FORCE_SERVER_NAME" in os.environ:
33
- environ["SERVER_NAME"] = os.environ["C2CWSGIUTILS_FORCE_SERVER_NAME"]
33
+ os.environ["SERVER_NAME"] = os.environ.pop("C2CWSGIUTILS_FORCE_SERVER_NAME")
34
34
  if "C2CWSGIUTILS_FORCE_REMOTE_ADDR" in os.environ:
35
- environ["REMOTE_ADDR"] = os.environ["C2CWSGIUTILS_FORCE_REMOTE_ADDR"]
35
+ os.environ["REMOTE_ADDR"] = os.environ.pop("C2CWSGIUTILS_FORCE_REMOTE_ADDR")
36
36
 
37
37
  return self._application(environ, start_response)
38
38
 
@@ -68,7 +68,12 @@ def _dump_memory_impl(
68
68
  and not (FILES_FIELDS - set(obj["globals"].keys()))
69
69
  ):
70
70
  python_internal = True
71
- if python_internal != python_internals_map:
71
+ if (
72
+ python_internal
73
+ and not python_internals_map
74
+ or not python_internal
75
+ and python_internals_map
76
+ ):
72
77
  continue
73
78
  size = get_size(obj) / 1024
74
79
  if len(biggest_objects) < limit or size > biggest_objects[0][0]:
@@ -47,7 +47,7 @@ def _dump_memory(request: pyramid.request.Request) -> list[Mapping[str, Any]]:
47
47
  auth.auth_view(request)
48
48
  limit = int(request.params.get("limit", "30"))
49
49
  analyze_type = request.params.get("analyze_type")
50
- python_internals_map = request.params.get("python_internals_map", "0").lower() in ("1", "true", "on")
50
+ python_internals_map = request.params.get("python_internals_map", "0").lower() in ("", "1", "true", "on")
51
51
  result = broadcast.broadcast(
52
52
  "c2c_dump_memory",
53
53
  params={"limit": limit, "analyze_type": analyze_type, "python_internals_map": python_internals_map},
@@ -75,7 +75,7 @@ def _dump_memory_diff(request: pyramid.request.Request) -> list[Any]:
75
75
 
76
76
  # warm-up run
77
77
  try:
78
- if request.params.get("no_warmup", "0").lower() in ("1", "true", "on"):
78
+ if "no_warmup" not in request.params:
79
79
  request.invoke_subrequest(sub_request)
80
80
  except Exception: # nosec # pylint: disable=broad-except
81
81
  pass
@@ -189,17 +189,10 @@ def _show_refs(request: pyramid.request.Request) -> pyramid.response.Response:
189
189
  args["extra_info"] = lambda obj: f"{get_size(obj) / 1024:.3f} kb\n{id(obj)}"
190
190
 
191
191
  result = StringIO()
192
- if request.params.get("backrefs", "") == "":
193
-
194
- def new_filter(x: Any) -> bool:
195
- return not objgraph.inspect.isclass(x)
196
-
197
- if "filter" in args:
198
- old_filter = args["filter"]
199
- args["filter"] = lambda x: old_filter(x) and new_filter(x)
200
- else:
201
- args["filter"] = new_filter
202
- objgraph.show_backrefs(objs, output=result, **args)
192
+ if request.params.get("backrefs", "") != "":
193
+ objgraph.show_backrefs(objs, output=result, **args)
194
+ else:
195
+ objgraph.show_refs(objs, output=result, filter=lambda x: not objgraph.inspect.isclass(x), **args)
203
196
 
204
197
  request.response.content_type = "text/vnd.graphviz"
205
198
  request.response.text = result.getvalue()
@@ -122,20 +122,8 @@ def input_(
122
122
  if type_ is None:
123
123
  if isinstance(value, int):
124
124
  type_ = "number"
125
- elif isinstance(value, bool):
126
- type_ = "checkbox"
127
125
  else:
128
126
  type_ = "text"
129
- if type_ == "checkbox":
130
- checked = " checked" if value else ""
131
- return f"""
132
- <div class="form-check">
133
- <input class="form-check-input" type="checkbox" name="{name}" value="true" id="{id_}"{checked}>
134
- <label class="form-check-label" for="{id_}">
135
- {label}
136
- </label>
137
- </div>"""
138
-
139
127
  result = ""
140
128
  if label is not None:
141
129
  result += f'<div class="row mb-3"><label class="col-sm-2 col-form-label" for="{id_}">{label}</label>'
@@ -213,7 +201,7 @@ def _index(request: pyramid.request.Request) -> dict[str, str]:
213
201
  def _versions(request: pyramid.request.Request) -> str:
214
202
  versions_url = _url(request, "c2c_versions")
215
203
  if versions_url:
216
- return section("Versions " + link(versions_url, "Get"), sep=False)
204
+ return section("Versions", paragraph(link(versions_url, "Get")), sep=False)
217
205
  else:
218
206
  return ""
219
207
 
@@ -292,7 +280,6 @@ def _logging(request: pyramid.request.Request) -> str:
292
280
  def _debug(request: pyramid.request.Request) -> str:
293
281
  dump_memory_url = _url(request, "c2c_debug_memory")
294
282
  if dump_memory_url:
295
- as_dot = 'as <a href="https://graphviz.org/">dot diagram</a>, can be open with <a href="https://pypi.org/project/xdot/">xdot</a>'
296
283
  return section(
297
284
  " ".join(
298
285
  [
@@ -302,42 +289,32 @@ def _debug(request: pyramid.request.Request) -> str:
302
289
  link(_url(request, "c2c_debug_memory_maps"), "Mapped memory"),
303
290
  ]
304
291
  ),
305
- '<h2>Memory usage<span style="font-size: 0.5em;">, with <a href="https://mg.pov.lt/objgraph/">objgraph</a></span></h2>',
306
- "<p>Runs the garbage collector and dumps the memory usage as JSON.</p>",
307
292
  form(
308
293
  dump_memory_url,
309
294
  input_("limit", value=30),
310
295
  input_("analyze_type"),
311
- input_("python_internals_map", type_="checkbox"),
312
296
  button("Dump memory usage"),
313
297
  ),
314
- f"<p>Runs the garbage collector and dumps the memory refs {as_dot}.</p>",
315
298
  form(
316
299
  _url(request, "c2c_debug_show_refs"),
317
300
  input_("analyze_type", value="gunicorn.app.wsgiapp.WSGIApplication"),
318
- input_("analyze_id", type_="number"),
319
- input_("max_depth", type_="number", value=3),
320
- input_("too_many", type_="number", value=10),
301
+ input_("max_depth", value=3),
302
+ input_("too_many", value=10),
321
303
  input_("min_size_kb", type_="number"),
322
304
  button("Object refs"),
323
305
  ),
324
- "<p>Runs the garbage collector, query the path, runs the garbage collector again, get the memory diff as JSON.</p>",
325
306
  form(
326
307
  _url(request, "c2c_debug_memory_diff"),
327
308
  input_("path"),
328
309
  input_("limit", value=30),
329
- input_("no_warmup", type_="checkbox"),
330
310
  button("Memory diff"),
331
311
  ),
332
- "<h2>Sleep</h2>",
333
312
  form(
334
313
  _url(request, "c2c_debug_sleep"),
335
314
  input_("time", value=1),
336
315
  button("Sleep"),
337
316
  ),
338
- "<h2>Server times</h2>",
339
- form(_url(request, "c2c_debug_time"), button("Get")),
340
- "<h2>HTTP error</h2>",
317
+ form(_url(request, "c2c_debug_time"), button("Time")),
341
318
  form(
342
319
  _url(request, "c2c_debug_error"),
343
320
  input_("status", value=500),
@@ -0,0 +1,21 @@
1
+ import logging
2
+ from typing import Optional, cast
3
+
4
+ from plaster_pastedeploy import Loader as BaseLoader
5
+
6
+ from c2cwsgiutils import get_config_defaults
7
+
8
+ _LOG = logging.getLogger(__name__)
9
+
10
+
11
+ class Loader(BaseLoader): # type: ignore
12
+ """The application loader."""
13
+
14
+ def _get_defaults(self, defaults: Optional[dict[str, str]] = None) -> dict[str, str]:
15
+ d = get_config_defaults()
16
+ d.update(defaults or {})
17
+ return cast(dict[str, str], super()._get_defaults(d))
18
+
19
+ def __repr__(self) -> str:
20
+ """Get the object representation."""
21
+ return f'c2cwsgiutils.loader.Loader(uri="{self.uri}")'
@@ -1,6 +1,5 @@
1
1
  """Every thing we needs to have the metrics in Prometheus."""
2
2
 
3
- import logging
4
3
  import os
5
4
  import re
6
5
  from collections.abc import Generator, Iterable
@@ -12,12 +11,10 @@ import prometheus_client.metrics_core
12
11
  import prometheus_client.multiprocess
13
12
  import prometheus_client.registry
14
13
  import pyramid.config
15
- import redis.exceptions
16
14
 
17
15
  from c2cwsgiutils import broadcast, redis_utils
18
16
  from c2cwsgiutils.debug.utils import dump_memory_maps
19
17
 
20
- _LOG = logging.getLogger(__name__)
21
18
  _NUMBER_RE = re.compile(r"^[0-9]+$")
22
19
  MULTI_PROCESS_COLLECTOR_BROADCAST_CHANNELS = [
23
20
  "c2cwsgiutils_prometheus_collector_gc",
@@ -120,12 +117,9 @@ class MultiProcessCustomCollector(prometheus_client.registry.Collector):
120
117
  def collect(self) -> Generator[prometheus_client.core.Metric, None, None]:
121
118
  results: list[list[SerializedMetric]] = []
122
119
  for channel in MULTI_PROCESS_COLLECTOR_BROADCAST_CHANNELS:
123
- try:
124
- result = broadcast.broadcast(channel, expect_answers=True)
125
- if result is not None:
126
- results.extend(cast(Iterable[list[SerializedMetric]], result))
127
- except redis.exceptions.ConnectionError:
128
- _LOG.error("Failed to get the metrics from the other processes")
120
+ result = broadcast.broadcast(channel, expect_answers=True)
121
+ if result is not None:
122
+ results.extend(cast(Iterable[list[SerializedMetric]], result))
129
123
  return _deserialize_collected_data(results)
130
124
 
131
125
 
@@ -102,7 +102,7 @@ class SQLAlchemyHandler(logging.Handler):
102
102
  with self.engine.begin() as connection:
103
103
  if not self.engine.dialect.has_schema(connection, self.Log.__table_args__["schema"]):
104
104
  connection.execute(
105
- sqlalchemy.schema.CreateSchema(self.Log.__table_args__["schema"]),
105
+ sqlalchemy.schema.CreateSchema(self.Log.__table_args__["schema"]), # type: ignore
106
106
  )
107
107
  Base.metadata.create_all(self.engine)
108
108
 
@@ -1,4 +1,3 @@
1
- import logging
2
1
  import time
3
2
  from typing import Callable, Optional
4
3
 
@@ -10,8 +9,6 @@ from pyramid.httpexceptions import HTTPException
10
9
 
11
10
  from c2cwsgiutils import prometheus
12
11
 
13
- _LOG = logging.getLogger(__name__)
14
-
15
12
  _PROMETHEUS_PYRAMID_ROUTES_SUMMARY = prometheus_client.Summary(
16
13
  prometheus.build_metric_name("pyramid_routes"),
17
14
  "Pyramid routes",
@@ -64,15 +61,6 @@ def _create_finished_cb(
64
61
  name = request.matched_route.name
65
62
  if kind == "route":
66
63
  _add_server_metric(request, "route", description=name)
67
- if status >= 500:
68
- _LOG.warning(
69
- "Request %s %s %s route %s return status %s",
70
- request.method,
71
- request.path,
72
- kind,
73
- name,
74
- status,
75
- )
76
64
  measure.labels(
77
65
  method=request.method, route=name, status=status, group=str(status // 100 * 100)
78
66
  ).observe(time.process_time() - start)
@@ -24,7 +24,7 @@
24
24
  crossorigin="anonymous"
25
25
  referrerpolicy="no-referrer"
26
26
  />
27
- <title>C2C WSGI Utils tools</title>
27
+ <title>c2cwsgiutils tools</title>
28
28
  <style>
29
29
  body {
30
30
  margin-top: 0.5rem;
@@ -32,9 +32,6 @@
32
32
  button, p {
33
33
  margin-bottom: 0.5rem;
34
34
  }
35
- .row > h2 {
36
- margin-top: 1rem;
37
- }
38
35
  </style>
39
36
  </head>
40
37
  <body>
@@ -16,7 +16,7 @@ strict = true
16
16
 
17
17
  [tool.poetry]
18
18
  name = "c2cwsgiutils"
19
- version = "6.1.7.dev6"
19
+ version = "6.2.0.dev4"
20
20
  description = "Common utilities for Camptocamp WSGI applications"
21
21
  readme = "README.md"
22
22
  authors = ["Camptocamp <info@camptocamp.com>"]
@@ -72,25 +72,25 @@ pyyaml = { version = "6.0.2" }
72
72
  alembic = { version = "1.13.3", optional = true }
73
73
  boltons = { version = "24.0.0", optional = true }
74
74
  cornice = { version = "6.1.0", optional = true }
75
- redis = { version = "5.1.1", optional = true }
75
+ redis = { version = "5.1.0", optional = true }
76
76
  gunicorn = { version = "23.0.0", optional = true }
77
77
  lxml = { version = "5.3.0", optional = true }
78
- objgraph = { version = "3.6.2", optional = true }
79
- psycopg2 = { version = "2.9.10", optional = true }
78
+ objgraph = { version = "3.6.1", optional = true }
79
+ psycopg2 = { version = "2.9.9", optional = true }
80
80
  pyramid = { version = "2.0.2", optional = true }
81
81
  pyramid-tm = { version = "2.5", optional = true }
82
82
  sentry-sdk = { version = "2.15.0", optional = true }
83
83
  ujson = { version = "5.10.0" }
84
84
  cee_syslog_handler = { version = "0.6.0" }
85
- SQLAlchemy = { version = "2.0.36", optional = true }
85
+ SQLAlchemy = { version = "2.0.35", optional = true }
86
86
  SQLAlchemy-Utils = { version = "0.41.2", optional = true }
87
87
  "zope.interface" = { version = "7.0.3", optional = true }
88
88
  "zope.sqlalchemy" = { version = "3.1", optional = true }
89
89
  pyjwt = { version = "2.9.0", optional = true }
90
90
  requests-oauthlib = { version = "2.0.0", optional = true }
91
- waitress = "3.0.2"
91
+ waitress = { version = "3.0.0", optional = true }
92
92
  scikit-image = { version = "0.24.0", optional = true }
93
- prometheus-client = { version = "0.21.1", optional = true}
93
+ prometheus-client = { version = "0.21.0", optional = true}
94
94
  pyramid_mako = { version = "1.1.0", optional = true}
95
95
 
96
96
  [tool.poetry.extras]
@@ -170,19 +170,19 @@ test_images = ["scikit-image"]
170
170
 
171
171
  [tool.poetry.group.dev.dependencies]
172
172
  # pylint = { version = "2.15.6" }
173
- prospector = { extras = ["with_bandit", "with_mypy", "with_pyroma"], version = "1.12.1" }
173
+ prospector = { extras = ["with_bandit", "with_mypy", "with_pyroma"], version = "1.12.0" }
174
174
  prospector-profile-duplicated = "1.6.0"
175
175
  prospector-profile-utils = "1.9.1"
176
- coverage = "7.6.9"
176
+ coverage = "7.6.1"
177
177
  junit2html = "31.0.2"
178
- pytest = "8.3.4"
178
+ pytest = "8.3.3"
179
179
  pytest-cov = "5.0.0"
180
180
  pytest-html = "4.1.1"
181
- types-pyyaml = "6.0.12.20241221"
182
- types-requests = "2.32.0.20241016"
181
+ types-pyyaml = "6.0.12.20240917"
182
+ types-requests = "2.32.0.20240914"
183
183
  types-redis = "4.6.0.20241004"
184
184
  types-ujson = "5.10.0.20240515"
185
- types-python-dateutil = "2.9.0.20241206"
185
+ types-python-dateutil = "2.9.0.20241003"
186
186
  typing_extensions = "4.12.2"
187
187
 
188
188
  [build-system]
@@ -1,40 +0,0 @@
1
- import logging
2
- from typing import Optional, cast
3
-
4
- from plaster_pastedeploy import Loader as BaseLoader
5
-
6
- from c2cwsgiutils import get_config_defaults, get_logconfig_dict
7
-
8
- _LOG = logging.getLogger(__name__)
9
-
10
-
11
- class Loader(BaseLoader): # type: ignore
12
- """The application loader."""
13
-
14
- def _get_defaults(self, defaults: Optional[dict[str, str]] = None) -> dict[str, str]:
15
- d = get_config_defaults()
16
- d.update(defaults or {})
17
- return cast(dict[str, str], super()._get_defaults(d))
18
-
19
- def __repr__(self) -> str:
20
- """Get the object representation."""
21
- return f'c2cwsgiutils.loader.Loader(uri="{self.uri}")'
22
-
23
- def setup_logging(self, defaults: Optional[dict[str, str]] = None) -> None:
24
- """
25
- Set up logging via :func:`logging.config.dictConfig` with value returned from c2cwsgiutils.get_logconfig_dict.
26
-
27
- Defaults are specified for the special ``__file__`` and ``here``
28
- variables, similar to PasteDeploy config loading. Extra defaults can
29
- optionally be specified as a dict in ``defaults``.
30
-
31
- Arguments:
32
- ---------
33
- defaults: The defaults that will be used when passed to
34
- :func:`logging.config.fileConfig`.
35
-
36
- """
37
- if "loggers" in self.get_sections():
38
- logging.config.dictConfig(get_logconfig_dict(self.uri.path))
39
- else:
40
- logging.basicConfig()