c2cwsgiutils 6.0.0.dev99__py3-none-any.whl → 6.0.0.dev106__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.
@@ -80,6 +80,9 @@ def check_image(
80
80
  result_filename = os.path.join(result_folder, f"{image_file_basename}.result.png")
81
81
  diff_filename = os.path.join(result_folder, f"{image_file_basename}.diff.png")
82
82
 
83
+ if len(image_to_check.shape) == 3 and image_to_check.shape[2] == 4:
84
+ image_to_check = skimage.color.rgba2rgb(image_to_check)
85
+
83
86
  mask = None
84
87
  if mask_filename is not None:
85
88
  mask = skimage.io.imread(mask_filename)
@@ -87,12 +90,15 @@ def check_image(
87
90
  mask = skimage.color.rgb2gray(mask)
88
91
  if len(mask.shape) == 3 and mask.shape[2] == 4:
89
92
  mask = skimage.color.rgba2gray(mask)
90
- if len(image_to_check.shape) == 3 and image_to_check.shape[2] == 4:
91
- image_to_check = skimage.color.rgba2rgb(image_to_check)
92
93
 
93
94
  assert mask is not None, "Wrong mask: " + mask_filename
95
+ assert ((0 < mask) & (mask < 255)).sum() == 0, "Mask should be only black and white image"
96
+
94
97
  image_to_check[mask == 0] = [255, 255, 255]
95
98
 
99
+ if np.issubdtype(image_to_check.dtype, np.floating):
100
+ image_to_check = (image_to_check * 255).astype("uint8")
101
+
96
102
  if not os.path.exists(result_folder):
97
103
  os.makedirs(result_folder)
98
104
  if generate_expected_image:
@@ -105,6 +111,9 @@ def check_image(
105
111
  expected = skimage.io.imread(expected_filename)
106
112
  assert expected is not None, "Wrong image: " + expected_filename
107
113
 
114
+ if np.issubdtype(expected.dtype, np.floating):
115
+ expected = (expected * 255).astype("uint8")
116
+
108
117
  if mask is not None:
109
118
  expected[mask == 0] = [255, 255, 255]
110
119
 
c2cwsgiutils/index.py CHANGED
@@ -64,10 +64,8 @@ def section(title: str, *content: str, sep: Optional[bool] = True) -> str:
64
64
  printable_content = "\n".join(content)
65
65
  result = f"""
66
66
  <div class="row">
67
- <div class="col-sm-3"><h2>{title}</h2></div>
68
- <div class="col-lg">
67
+ <h2>{title}</h2>
69
68
  {printable_content}
70
- </div>
71
69
  </div>
72
70
  """
73
71
  if sep:
@@ -105,7 +103,7 @@ def form(url: Optional[str], *content: str, method: str = "get", target: str = "
105
103
  method_attrs = ' method="post" enctype="multipart/form-data"'
106
104
  printable_content = "\n".join(content)
107
105
  return f"""
108
- <form class="form-inline" action="{url}" target="{target}"{method_attrs}>
106
+ <form action="{url}" target="{target}"{method_attrs}>
109
107
  {printable_content}
110
108
  </form>
111
109
  """
@@ -120,7 +118,7 @@ def input_(
120
118
  ELEM_ID += 1
121
119
 
122
120
  if label is None and type_ != "hidden":
123
- label = name
121
+ label = name.replace("_", " ").capitalize()
124
122
  if type_ is None:
125
123
  if isinstance(value, int):
126
124
  type_ = "number"
@@ -128,8 +126,11 @@ def input_(
128
126
  type_ = "text"
129
127
  result = ""
130
128
  if label is not None:
131
- result += f'<div class="form-group form-inline"><label for="{id_}">{label}:</label>'
132
- result += f'<input class="form-control" type="{type_}" name="{name}" value="{value}" id="{id_}">'
129
+ result += f'<div class="row mb-3"><label class="col-sm-2 col-form-label" for="{id_}">{label}</label>'
130
+ result += (
131
+ '<div class="col-sm-10"><input class="form-control" '
132
+ f'type="{type_}" name="{name}" value="{value}" id="{id_}"></div>'
133
+ )
133
134
  if label is not None:
134
135
  result += "</div>"
135
136
  return result
@@ -137,72 +138,37 @@ def input_(
137
138
 
138
139
  def button(label: str) -> str:
139
140
  """Get en HTML button."""
141
+
140
142
  return f'<button class="btn btn-primary" type="submit">{label}</button>'
141
143
 
142
144
 
143
- def _index(request: pyramid.request.Request) -> pyramid.response.Response:
145
+ def _index(request: pyramid.request.Request) -> Dict[str, str]:
144
146
  response = request.response
145
147
 
146
148
  auth, user = is_auth_user(request)
147
149
  has_access = check_access(request)
148
150
 
149
151
  response.content_type = "text/html"
150
- response.text = """
151
- <!doctype html>
152
- <html lang="en">
153
- <head>
154
- <meta charset="utf-8">
155
- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
156
- <link rel="stylesheet"
157
- href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
158
- integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
159
- crossorigin="anonymous">
160
- <title>c2cwsgiutils tools</title>
161
- <style>
162
- form {
163
- margin-bottom: 1rem;
164
- }
165
- label {
166
- margin-right: 0.5rem;
167
- }
168
- input, button, a.btn {
169
- margin-right: 1rem;
170
- }
171
- div.col-lg {
172
- margin-top: 0.5rem;
173
- margin-bottom: 0.5rem;
174
- }
175
- hr {
176
- margin-top: 0.5rem;
177
- margin-bottom: 0.5rem;
178
- }
179
- body {
180
- margin-top: 0.5rem;
181
- }
182
- </style>
183
- </head>
184
- <body>
185
- <div class="container-fluid">
186
- """
187
152
 
188
- response.text += _health_check(request)
189
- response.text += _stats(request)
190
- response.text += _versions(request)
153
+ body = ""
154
+ body += _health_check(request)
155
+ body += _stats(request)
156
+ body += _versions(request)
191
157
  if has_access:
192
- response.text += _debug(request)
193
- response.text += _db_maintenance(request)
194
- response.text += _logging(request)
195
- response.text += _profiler(request)
158
+ body += _debug(request)
159
+ body += _db_maintenance(request)
160
+ body += _logging(request)
161
+ body += _profiler(request)
196
162
 
197
163
  if additional_title is not None and (has_access or additional_noauth):
198
- response.text += additional_title
199
- response.text += "\n"
164
+ body += additional_title
165
+ body += "\n"
200
166
 
201
167
  if has_access:
202
- response.text += "\n".join(additional_auth)
203
- response.text += "\n"
168
+ body += "\n".join(additional_auth)
169
+ body += "\n"
204
170
 
205
- response.text += "\n".join(additional_noauth)
171
+ body += "\n".join(additional_noauth)
206
172
 
207
173
  settings = request.registry.settings
208
174
  auth_type_ = auth_type(settings)
@@ -211,37 +177,32 @@ def _index(request: pyramid.request.Request) -> pyramid.response.Response:
211
177
  auth_fields = [input_("secret", type_="password"), button("Login")]
212
178
  else:
213
179
  auth_fields = [input_("secret", type_="hidden"), button("Logout")]
214
- response.text += section(
180
+ body += section(
215
181
  "Authentication",
216
182
  form(_url(request, "c2c_index"), *auth_fields, method="post", target="_self"),
217
183
  sep=False,
218
184
  )
219
185
  elif not auth and auth_type_ == AuthenticationType.GITHUB:
220
- response.text += section(
186
+ body += section(
221
187
  "Authentication",
222
188
  link(_url(request, "c2c_github_login"), "Login with GitHub", target=""),
223
189
  sep=False,
224
190
  )
225
191
  elif auth_type_ == AuthenticationType.GITHUB:
226
- response.text += section(
192
+ body += section(
227
193
  "Authentication",
228
194
  f"Logged as: {link(user['url'], user['name'], cssclass='')}<br />"
229
195
  f"{link(_url(request, 'c2c_github_logout'), 'Logout', target='')}",
230
196
  sep=False,
231
197
  )
232
198
 
233
- response.text += """
234
- </div>
235
- </body>
236
- </html>
237
- """
238
- return response
199
+ return {"body": body}
239
200
 
240
201
 
241
202
  def _versions(request: pyramid.request.Request) -> str:
242
203
  versions_url = _url(request, "c2c_versions")
243
204
  if versions_url:
244
- return section("Versions", paragraph(link(versions_url, "Get")))
205
+ return section("Versions", paragraph(link(versions_url, "Get")), sep=False)
245
206
  else:
246
207
  return ""
247
208
 
@@ -249,7 +210,7 @@ def _versions(request: pyramid.request.Request) -> str:
249
210
  def _stats(request: pyramid.request.Request) -> str:
250
211
  stats_url = _url(request, "c2c_read_stats_json")
251
212
  if stats_url:
252
- return section("Statistics", paragraph(link(stats_url, "Get")))
213
+ return section("Statistics", paragraph(link(stats_url, "Get")), sep=False)
253
214
  else:
254
215
  return ""
255
216
 
@@ -264,10 +225,9 @@ def _profiler(request: pyramid.request.Request) -> str:
264
225
  link(sql_profiler_url, "Status"),
265
226
  link(sql_profiler_url + "?enable=1", "Enable"),
266
227
  link(sql_profiler_url + "?enable=0", "Disable"),
267
- title="SQL",
268
228
  )
269
229
 
270
- return section("Profiler", result)
230
+ return section("SQL profiler", result, sep=False)
271
231
  else:
272
232
  return ""
273
233
 
@@ -288,6 +248,7 @@ def _db_maintenance(request: pyramid.request.Request) -> str:
288
248
  button("Set readonly=false"),
289
249
  input_("readonly", value="false", type_="hidden"),
290
250
  ),
251
+ sep=False,
291
252
  )
292
253
  else:
293
254
  return ""
@@ -298,14 +259,19 @@ def _logging(request: pyramid.request.Request) -> str:
298
259
  if logging_url:
299
260
  return section(
300
261
  "Logging",
301
- form(logging_url, button("Get"), input_("name", value="c2cwsgiutils")),
302
262
  form(
303
263
  logging_url,
304
- button("Set"),
264
+ input_("name", value="c2cwsgiutils"),
265
+ button("Get"),
266
+ ),
267
+ form(
268
+ logging_url,
305
269
  input_("name", value="c2cwsgiutils"),
306
270
  input_("level", value="INFO"),
271
+ button("Set"),
307
272
  ),
308
273
  paragraph(link(logging_url, "List overrides")),
274
+ sep=False,
309
275
  )
310
276
  else:
311
277
  return ""
@@ -323,31 +289,36 @@ def _debug(request: pyramid.request.Request) -> str:
323
289
  ),
324
290
  form(
325
291
  dump_memory_url,
326
- button("Dump memory usage"),
327
292
  input_("limit", value=30),
328
293
  input_("analyze_type"),
294
+ button("Dump memory usage"),
329
295
  ),
330
296
  form(
331
297
  _url(request, "c2c_debug_show_refs"),
332
- button("Object refs"),
333
298
  input_("analyze_type", value="gunicorn.app.wsgiapp.WSGIApplication"),
334
299
  input_("max_depth", value=3),
335
300
  input_("too_many", value=10),
336
301
  input_("min_size_kb", type_="number"),
302
+ button("Object refs"),
337
303
  ),
338
304
  form(
339
305
  _url(request, "c2c_debug_memory_diff"),
340
- button("Memory diff"),
341
306
  input_("path"),
342
307
  input_("limit", value=30),
308
+ button("Memory diff"),
309
+ ),
310
+ form(
311
+ _url(request, "c2c_debug_sleep"),
312
+ input_("time", value=1),
313
+ button("Sleep"),
343
314
  ),
344
- form(_url(request, "c2c_debug_sleep"), button("Sleep"), input_("time", value=1)),
345
315
  form(_url(request, "c2c_debug_time"), button("Time")),
346
316
  form(
347
317
  _url(request, "c2c_debug_error"),
348
- button("Generate an HTTP error"),
349
318
  input_("status", value=500),
319
+ button("Generate an HTTP error"),
350
320
  ),
321
+ sep=False,
351
322
  )
352
323
  else:
353
324
  return ""
@@ -358,7 +329,13 @@ def _health_check(request: pyramid.request.Request) -> str:
358
329
  if health_check_url:
359
330
  return section(
360
331
  "Health checks",
361
- form(health_check_url, button("Run"), input_("max_level", value=1), input_("checks")),
332
+ form(
333
+ health_check_url,
334
+ input_("max_level", value=1),
335
+ input_("checks"),
336
+ button("Run"),
337
+ ),
338
+ sep=False,
362
339
  )
363
340
  else:
364
341
  return ""
@@ -366,6 +343,7 @@ def _health_check(request: pyramid.request.Request) -> str:
366
343
 
367
344
  def _github_login(request: pyramid.request.Request) -> Dict[str, Any]:
368
345
  """Get the view that start the authentication on GitHub."""
346
+
369
347
  settings = request.registry.settings
370
348
  params = dict(request.params)
371
349
  callback_url = _url(
@@ -507,10 +485,14 @@ def includeme(config: pyramid.config.Configurator) -> None:
507
485
  """Initialize the index page."""
508
486
  base_path = config_utils.get_base_path(config)
509
487
  if base_path != "":
488
+ config.add_static_view(name="static", path="c2cwsgiutils:static")
489
+ config.include("pyramid_mako")
510
490
  config.add_route("c2c_index", base_path, request_method=("GET", "POST"))
511
- config.add_view(_index, route_name="c2c_index", http_cache=0)
491
+ config.add_view(_index, route_name="c2c_index", http_cache=0, renderer="./templates/index.html.mako")
512
492
  config.add_route("c2c_index_slash", base_path + "/", request_method=("GET", "POST"))
513
- config.add_view(_index, route_name="c2c_index_slash", http_cache=0)
493
+ config.add_view(
494
+ _index, route_name="c2c_index_slash", http_cache=0, renderer="./templates/index.html.mako"
495
+ )
514
496
 
515
497
  settings = config.get_settings()
516
498
  auth_type_ = auth_type(settings)
Binary file
Binary file
@@ -0,0 +1,42 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
6
+ <link
7
+ rel="icon"
8
+ type="image/png"
9
+ sizes="32x32"
10
+ href="${request.static_url('c2cwsgiutils:static/favicon-32x32.png')}"
11
+ referrerpolicy="no-referrer"
12
+ />
13
+ <link
14
+ rel="icon"
15
+ type="image/png"
16
+ sizes="16x16"
17
+ href="${request.static_url('c2cwsgiutils:static/favicon-16x16.png')}"
18
+ referrerpolicy="no-referrer"
19
+ />
20
+ <link
21
+ rel="stylesheet"
22
+ href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/css/bootstrap.min.css"
23
+ integrity="sha512-GQGU0fMMi238uA+a/bdWJfpUGKUkBdgfFdgBm72SUQ6BeyWjoY/ton0tEjH+OSH9iP4Dfh+7HM0I9f5eR0L/4w=="
24
+ crossorigin="anonymous"
25
+ referrerpolicy="no-referrer"
26
+ />
27
+ <title>c2cwsgiutils tools</title>
28
+ <style>
29
+ body {
30
+ margin-top: 0.5rem;
31
+ }
32
+ button, p {
33
+ margin-bottom: 0.5rem;
34
+ }
35
+ </style>
36
+ </head>
37
+ <body>
38
+ <div class="container-fluid">
39
+ ${ body | n }
40
+ </div>
41
+ </body>
42
+ </html>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: c2cwsgiutils
3
- Version: 6.0.0.dev99
3
+ Version: 6.0.0.dev106
4
4
  Summary: Common utilities for Camptocamp WSGI applications
5
5
  Home-page: https://github.com/camptocamp/c2cwsgiutils
6
6
  License: BSD-2-Clause
@@ -45,6 +45,7 @@ Requires-Dist: psycopg2 ; extra == "standard" or extra == "all"
45
45
  Requires-Dist: pyjwt ; extra == "oauth2" or extra == "all"
46
46
  Requires-Dist: pyramid ; extra == "standard" or extra == "all"
47
47
  Requires-Dist: pyramid-tm ; extra == "standard" or extra == "all"
48
+ Requires-Dist: pyramid_mako
48
49
  Requires-Dist: pyyaml
49
50
  Requires-Dist: redis ; extra == "standard" or extra == "broadcast" or extra == "all" or extra == "all"
50
51
  Requires-Dist: requests
@@ -2,7 +2,7 @@ c2cwsgiutils/__init__.py,sha256=sPZ6-3L_gJy-NIbYdr7EcoMK2TFZMUnn-qD2BglQP8w,3997
2
2
  c2cwsgiutils/acceptance/__init__.py,sha256=vjtpPfu0kbXUOYMx15Z8713IfPFZA9XnkUKkIFtVj_M,1500
3
3
  c2cwsgiutils/acceptance/composition.py,sha256=lqIyRNvJc_ig1tVGLthTFbZOxqcVW9N3vHnJ7CDS0Yw,5331
4
4
  c2cwsgiutils/acceptance/connection.py,sha256=PxqSPYi0Ogrj5FU4IPGK9bQPfBMqxz7L3vQQ4hRCL2U,9109
5
- c2cwsgiutils/acceptance/image.py,sha256=dN1Pg3ECySlzCqNSZ-TytDZV8VXZQuxmg8Q8YlXTdFc,4845
5
+ c2cwsgiutils/acceptance/image.py,sha256=fi6C-EBz-n8kEyXCLmqQ83UZ8iyPmT9IvJesDvIt_R4,5164
6
6
  c2cwsgiutils/acceptance/print.py,sha256=DDAugcYsDAiFPzMnAQ1k4Us_x37ANEbuk5hb4d6xyKc,2329
7
7
  c2cwsgiutils/acceptance/utils.py,sha256=dlNQ2X-rz6zEKEkbJR1gOKKmfgnf-HpLkOIdEZ4rUy4,2078
8
8
  c2cwsgiutils/auth.py,sha256=iTrmptbK0GnQgW0QvAdM5uX4rZ8OPczY5OCE0FzNvEA,9313
@@ -22,7 +22,7 @@ c2cwsgiutils/debug/_views.py,sha256=OxFHGglT6QVGAoZqsgREtK3sZ4aSAoxd4Je0CmP5eAc,
22
22
  c2cwsgiutils/debug/utils.py,sha256=1vtfXND_hzs6PH8ohnRGcPYaW2_qKRJ6VxDvEdG0hdU,2328
23
23
  c2cwsgiutils/errors.py,sha256=LUhZF3BW1i-v20x3EE-rZbT-TpdugpxiW7p5iCAu73Q,6723
24
24
  c2cwsgiutils/health_check.py,sha256=FXgV7mC0jjzsA97I1Jn328-6y-vB8sq5MElPKKCPj54,20028
25
- c2cwsgiutils/index.py,sha256=hXrkHNMuTQnNlG-pTe19teNWHX_gjBF4W1WmBAHiUBw,17356
25
+ c2cwsgiutils/index.py,sha256=XI4RdMyGv42kfWcQzg1bg--kvxiVt_xeV4j_kjtf8yI,16620
26
26
  c2cwsgiutils/loader.py,sha256=hFIB30saYkyj8brmL07AKE_PFAb2Bny_kWlSOg-xp1E,628
27
27
  c2cwsgiutils/logging_view.py,sha256=zVvZiNvJDdMwclm3KiqsfK8Ra2JSKPSSQt5VYGGpQ2k,3337
28
28
  c2cwsgiutils/models_graph.py,sha256=z-sfSssKR89W-X3JQvAEdfiAyl-vxkSLo-hs7PcfOUI,2691
@@ -51,12 +51,15 @@ c2cwsgiutils/sqlalchemylogger/_filters.py,sha256=OJQ9_WA-fd9fMZ7TUNFzHHTPI6msw2N
51
51
  c2cwsgiutils/sqlalchemylogger/_models.py,sha256=gCCP_AfGl8unVtV1mQW8l2vk8ltDMWl5UJj3-tJ94Pk,1477
52
52
  c2cwsgiutils/sqlalchemylogger/examples/example.py,sha256=n48dJdUi1FH1hfBMAbfHLGPSb1bOVD8pXMxXB57PnpQ,460
53
53
  c2cwsgiutils/sqlalchemylogger/handlers.py,sha256=NG9U5alNKbViYfde0-HlSQvrHEzLUgpo5bfZgBOJawY,4813
54
+ c2cwsgiutils/static/favicon-16x16.png,sha256=LKk6RFvb3NlPIZdDfAodE8H9IN8KM6CMGnMx4vOHlUQ,887
55
+ c2cwsgiutils/static/favicon-32x32.png,sha256=i4ucx08zAZARd8e7JTMGK-gb5WcOmyuDN6IN4brsEOo,1216
54
56
  c2cwsgiutils/stats_pyramid/__init__.py,sha256=7P10LjLv3c-ObEDGuYmRF_RFt7fRmO80ruqTGQAyC6w,747
55
57
  c2cwsgiutils/stats_pyramid/_db_spy.py,sha256=ZGRdrI17Bdl3mzaLjfPyAaEW3KK8Pikrgi-0WmH7zCs,2917
56
58
  c2cwsgiutils/stats_pyramid/_pyramid_spy.py,sha256=P212MGGl2VV_7UU4AXZA-rOuF7ouaONRklZwpas2wc8,3209
59
+ c2cwsgiutils/templates/index.html.mako,sha256=8WYmG9TZ-h1rwE78F6a_df_1RZ0tY0PQUJ1HZhIbcTw,1131
57
60
  c2cwsgiutils/version.py,sha256=kSva7UIAwgHqofb-HuqP_z0pU8B0owf_jRpJWGhl7qc,2876
58
- c2cwsgiutils-6.0.0.dev99.dist-info/LICENSE,sha256=rM6IWxociA3daRkXnNLYOxGndT5fbs3BfVZCA2Xgt-g,1304
59
- c2cwsgiutils-6.0.0.dev99.dist-info/METADATA,sha256=xzoEmuzny-7_CcEz0eZeLBDQUeGFJR1sh4W0FI-fPxs,31984
60
- c2cwsgiutils-6.0.0.dev99.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
61
- c2cwsgiutils-6.0.0.dev99.dist-info/entry_points.txt,sha256=ujgqMTL1awN9qDg8WXmrF7m0fgR-hslUM6zKH86pvy0,703
62
- c2cwsgiutils-6.0.0.dev99.dist-info/RECORD,,
61
+ c2cwsgiutils-6.0.0.dev106.dist-info/LICENSE,sha256=rM6IWxociA3daRkXnNLYOxGndT5fbs3BfVZCA2Xgt-g,1304
62
+ c2cwsgiutils-6.0.0.dev106.dist-info/METADATA,sha256=LYXQzcKvrPG9IlUB5N_xEVYPscfQDu8dltVetDO4awE,32013
63
+ c2cwsgiutils-6.0.0.dev106.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
64
+ c2cwsgiutils-6.0.0.dev106.dist-info/entry_points.txt,sha256=ujgqMTL1awN9qDg8WXmrF7m0fgR-hslUM6zKH86pvy0,703
65
+ c2cwsgiutils-6.0.0.dev106.dist-info/RECORD,,