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.
- c2cwsgiutils/__init__.py +13 -13
- c2cwsgiutils/acceptance/connection.py +5 -2
- c2cwsgiutils/acceptance/image.py +98 -4
- c2cwsgiutils/acceptance/package-lock.json +1933 -0
- c2cwsgiutils/acceptance/package.json +7 -0
- c2cwsgiutils/acceptance/print.py +4 -4
- c2cwsgiutils/acceptance/screenshot.js +62 -0
- c2cwsgiutils/acceptance/utils.py +14 -22
- c2cwsgiutils/auth.py +4 -4
- c2cwsgiutils/broadcast/__init__.py +15 -7
- c2cwsgiutils/broadcast/interface.py +3 -2
- c2cwsgiutils/broadcast/local.py +3 -2
- c2cwsgiutils/broadcast/redis.py +8 -7
- c2cwsgiutils/client_info.py +5 -5
- c2cwsgiutils/config_utils.py +2 -1
- c2cwsgiutils/coverage_setup.py +2 -2
- c2cwsgiutils/db.py +58 -37
- c2cwsgiutils/db_maintenance_view.py +2 -1
- c2cwsgiutils/debug/_listeners.py +10 -9
- c2cwsgiutils/debug/_views.py +12 -11
- c2cwsgiutils/debug/utils.py +5 -5
- c2cwsgiutils/errors.py +7 -6
- c2cwsgiutils/health_check.py +96 -85
- c2cwsgiutils/index.py +90 -105
- c2cwsgiutils/loader.py +3 -3
- c2cwsgiutils/logging_view.py +3 -2
- c2cwsgiutils/models_graph.py +8 -6
- c2cwsgiutils/prometheus.py +175 -57
- c2cwsgiutils/pyramid.py +4 -2
- c2cwsgiutils/pyramid_logging.py +2 -1
- c2cwsgiutils/redis_stats.py +13 -11
- c2cwsgiutils/redis_utils.py +15 -14
- c2cwsgiutils/request_tracking/__init__.py +36 -30
- c2cwsgiutils/request_tracking/_sql.py +3 -1
- c2cwsgiutils/scripts/genversion.py +4 -4
- c2cwsgiutils/scripts/stats_db.py +130 -68
- c2cwsgiutils/scripts/test_print.py +1 -1
- c2cwsgiutils/sentry.py +2 -1
- c2cwsgiutils/setup_process.py +13 -17
- c2cwsgiutils/sql_profiler/_impl.py +12 -5
- c2cwsgiutils/sqlalchemylogger/README.md +48 -0
- c2cwsgiutils/sqlalchemylogger/_models.py +7 -4
- c2cwsgiutils/sqlalchemylogger/examples/example.py +15 -0
- c2cwsgiutils/sqlalchemylogger/handlers.py +11 -8
- c2cwsgiutils/static/favicon-16x16.png +0 -0
- c2cwsgiutils/static/favicon-32x32.png +0 -0
- c2cwsgiutils/stats_pyramid/__init__.py +7 -11
- c2cwsgiutils/stats_pyramid/_db_spy.py +14 -11
- c2cwsgiutils/stats_pyramid/_pyramid_spy.py +29 -20
- c2cwsgiutils/templates/index.html.mako +50 -0
- c2cwsgiutils/version.py +49 -16
- c2cwsgiutils-5.2.1.dev197.dist-info/LICENSE +22 -0
- {c2cwsgiutils-5.1.7.dev20230901073305.dist-info → c2cwsgiutils-5.2.1.dev197.dist-info}/METADATA +187 -135
- c2cwsgiutils-5.2.1.dev197.dist-info/RECORD +67 -0
- {c2cwsgiutils-5.1.7.dev20230901073305.dist-info → c2cwsgiutils-5.2.1.dev197.dist-info}/WHEEL +1 -2
- c2cwsgiutils-5.2.1.dev197.dist-info/entry_points.txt +21 -0
- c2cwsgiutils/acceptance/composition.py +0 -129
- c2cwsgiutils/metrics.py +0 -110
- c2cwsgiutils/scripts/check_es.py +0 -130
- c2cwsgiutils/scripts/coverage_report.py +0 -36
- c2cwsgiutils/stats.py +0 -355
- c2cwsgiutils/stats_pyramid/_views.py +0 -16
- c2cwsgiutils-5.1.7.dev20230901073305.data/scripts/c2cwsgiutils-run +0 -32
- c2cwsgiutils-5.1.7.dev20230901073305.dist-info/LICENSE.txt +0 -28
- c2cwsgiutils-5.1.7.dev20230901073305.dist-info/RECORD +0 -69
- c2cwsgiutils-5.1.7.dev20230901073305.dist-info/entry_points.txt +0 -25
- c2cwsgiutils-5.1.7.dev20230901073305.dist-info/top_level.txt +0 -2
- tests/acceptance/__init__.py +0 -0
- tests/acceptance/test_utils.py +0 -13
c2cwsgiutils/index.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import logging
|
2
2
|
import urllib.parse
|
3
3
|
import warnings
|
4
|
-
from typing import Any,
|
4
|
+
from typing import Any, Optional, Union, cast
|
5
5
|
|
6
6
|
import jwt
|
7
7
|
import pyramid.config
|
@@ -45,13 +45,13 @@ from c2cwsgiutils.config_utils import env_or_settings
|
|
45
45
|
LOG = logging.getLogger(__name__)
|
46
46
|
|
47
47
|
additional_title: Optional[str] = None
|
48
|
-
additional_noauth:
|
49
|
-
additional_auth:
|
48
|
+
additional_noauth: list[str] = []
|
49
|
+
additional_auth: list[str] = []
|
50
50
|
ELEM_ID = 0
|
51
51
|
|
52
52
|
|
53
53
|
def _url(
|
54
|
-
request: pyramid.request.Request, route: str, params: Optional[
|
54
|
+
request: pyramid.request.Request, route: str, params: Optional[dict[str, str]] = None
|
55
55
|
) -> Optional[str]:
|
56
56
|
try:
|
57
57
|
return request.route_url(route, _query=params) # type: ignore
|
@@ -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
|
-
<
|
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
|
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="
|
132
|
-
result +=
|
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) ->
|
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
|
-
|
189
|
-
|
190
|
-
|
153
|
+
body = ""
|
154
|
+
body += _health_check(request)
|
155
|
+
body += _stats(request)
|
156
|
+
body += _versions(request)
|
191
157
|
if has_access:
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
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
|
-
|
199
|
-
|
164
|
+
body += additional_title
|
165
|
+
body += "\n"
|
200
166
|
|
201
167
|
if has_access:
|
202
|
-
|
203
|
-
|
168
|
+
body += "\n".join(additional_auth)
|
169
|
+
body += "\n"
|
204
170
|
|
205
|
-
|
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
|
-
|
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
|
-
|
186
|
+
body += section(
|
221
187
|
"Authentication",
|
222
|
-
link(_url(request, "c2c_github_login"), "Login with GitHub", target=""),
|
188
|
+
paragraph(link(_url(request, "c2c_github_login"), "Login with GitHub", target="")),
|
223
189
|
sep=False,
|
224
190
|
)
|
225
191
|
elif auth_type_ == AuthenticationType.GITHUB:
|
226
|
-
|
192
|
+
body += section(
|
227
193
|
"Authentication",
|
228
|
-
f"Logged as: {link(user['url'], user['name'], cssclass='')}<br />"
|
229
|
-
f"{link(_url(request, 'c2c_github_logout'), 'Logout', target='')}",
|
194
|
+
f"<p>Logged as: {link(user['url'], user['name'], cssclass='')}<br />"
|
195
|
+
f"{link(_url(request, 'c2c_github_logout'), 'Logout', target='')}</p>",
|
230
196
|
sep=False,
|
231
197
|
)
|
232
198
|
|
233
|
-
|
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
|
|
@@ -257,17 +218,17 @@ def _stats(request: pyramid.request.Request) -> str:
|
|
257
218
|
def _profiler(request: pyramid.request.Request) -> str:
|
258
219
|
sql_profiler_url = _url(request, "c2c_sql_profiler")
|
259
220
|
if sql_profiler_url:
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
)
|
269
|
-
|
270
|
-
|
221
|
+
return section(
|
222
|
+
" ".join(
|
223
|
+
[
|
224
|
+
"SQL profiler",
|
225
|
+
link(sql_profiler_url, "Status"),
|
226
|
+
link(sql_profiler_url + "?enable=1", "Enable"),
|
227
|
+
link(sql_profiler_url + "?enable=0", "Disable"),
|
228
|
+
]
|
229
|
+
),
|
230
|
+
sep=False,
|
231
|
+
)
|
271
232
|
else:
|
272
233
|
return ""
|
273
234
|
|
@@ -288,6 +249,7 @@ def _db_maintenance(request: pyramid.request.Request) -> str:
|
|
288
249
|
button("Set readonly=false"),
|
289
250
|
input_("readonly", value="false", type_="hidden"),
|
290
251
|
),
|
252
|
+
sep=False,
|
291
253
|
)
|
292
254
|
else:
|
293
255
|
return ""
|
@@ -298,14 +260,19 @@ def _logging(request: pyramid.request.Request) -> str:
|
|
298
260
|
if logging_url:
|
299
261
|
return section(
|
300
262
|
"Logging",
|
301
|
-
form(logging_url, button("Get"), input_("name", value="c2cwsgiutils")),
|
302
263
|
form(
|
303
264
|
logging_url,
|
304
|
-
|
265
|
+
input_("name", value="c2cwsgiutils"),
|
266
|
+
button("Get"),
|
267
|
+
),
|
268
|
+
form(
|
269
|
+
logging_url,
|
305
270
|
input_("name", value="c2cwsgiutils"),
|
306
271
|
input_("level", value="INFO"),
|
272
|
+
button("Set"),
|
307
273
|
),
|
308
274
|
paragraph(link(logging_url, "List overrides")),
|
275
|
+
sep=False,
|
309
276
|
)
|
310
277
|
else:
|
311
278
|
return ""
|
@@ -315,39 +282,46 @@ def _debug(request: pyramid.request.Request) -> str:
|
|
315
282
|
dump_memory_url = _url(request, "c2c_debug_memory")
|
316
283
|
if dump_memory_url:
|
317
284
|
return section(
|
318
|
-
"
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
285
|
+
" ".join(
|
286
|
+
[
|
287
|
+
"Debug",
|
288
|
+
link(_url(request, "c2c_debug_stacks"), "Stack traces"),
|
289
|
+
link(_url(request, "c2c_debug_headers"), "HTTP headers"),
|
290
|
+
link(_url(request, "c2c_debug_memory_maps"), "Mapped memory"),
|
291
|
+
]
|
323
292
|
),
|
324
293
|
form(
|
325
294
|
dump_memory_url,
|
326
|
-
button("Dump memory usage"),
|
327
295
|
input_("limit", value=30),
|
328
296
|
input_("analyze_type"),
|
297
|
+
button("Dump memory usage"),
|
329
298
|
),
|
330
299
|
form(
|
331
300
|
_url(request, "c2c_debug_show_refs"),
|
332
|
-
button("Object refs"),
|
333
301
|
input_("analyze_type", value="gunicorn.app.wsgiapp.WSGIApplication"),
|
334
302
|
input_("max_depth", value=3),
|
335
303
|
input_("too_many", value=10),
|
336
304
|
input_("min_size_kb", type_="number"),
|
305
|
+
button("Object refs"),
|
337
306
|
),
|
338
307
|
form(
|
339
308
|
_url(request, "c2c_debug_memory_diff"),
|
340
|
-
button("Memory diff"),
|
341
309
|
input_("path"),
|
342
310
|
input_("limit", value=30),
|
311
|
+
button("Memory diff"),
|
312
|
+
),
|
313
|
+
form(
|
314
|
+
_url(request, "c2c_debug_sleep"),
|
315
|
+
input_("time", value=1),
|
316
|
+
button("Sleep"),
|
343
317
|
),
|
344
|
-
form(_url(request, "c2c_debug_sleep"), button("Sleep"), input_("time", value=1)),
|
345
318
|
form(_url(request, "c2c_debug_time"), button("Time")),
|
346
319
|
form(
|
347
320
|
_url(request, "c2c_debug_error"),
|
348
|
-
button("Generate an HTTP error"),
|
349
321
|
input_("status", value=500),
|
322
|
+
button("Generate an HTTP error"),
|
350
323
|
),
|
324
|
+
sep=False,
|
351
325
|
)
|
352
326
|
else:
|
353
327
|
return ""
|
@@ -358,14 +332,21 @@ def _health_check(request: pyramid.request.Request) -> str:
|
|
358
332
|
if health_check_url:
|
359
333
|
return section(
|
360
334
|
"Health checks",
|
361
|
-
form(
|
335
|
+
form(
|
336
|
+
health_check_url,
|
337
|
+
input_("max_level", value=1),
|
338
|
+
input_("checks"),
|
339
|
+
button("Run"),
|
340
|
+
),
|
341
|
+
sep=False,
|
362
342
|
)
|
363
343
|
else:
|
364
344
|
return ""
|
365
345
|
|
366
346
|
|
367
|
-
def _github_login(request: pyramid.request.Request) ->
|
347
|
+
def _github_login(request: pyramid.request.Request) -> dict[str, Any]:
|
368
348
|
"""Get the view that start the authentication on GitHub."""
|
349
|
+
|
369
350
|
settings = request.registry.settings
|
370
351
|
params = dict(request.params)
|
371
352
|
callback_url = _url(
|
@@ -399,7 +380,7 @@ def _github_login(request: pyramid.request.Request) -> Dict[str, Any]:
|
|
399
380
|
raise HTTPFound(location=authorization_url, headers=request.response.headers)
|
400
381
|
|
401
382
|
|
402
|
-
def _github_login_callback(request: pyramid.request.Request) ->
|
383
|
+
def _github_login_callback(request: pyramid.request.Request) -> dict[str, Any]:
|
403
384
|
"""
|
404
385
|
Do the post login operation authentication on GitHub.
|
405
386
|
|
@@ -466,7 +447,7 @@ def _github_login_callback(request: pyramid.request.Request) -> Dict[str, Any]:
|
|
466
447
|
"c2c-auth-jwt",
|
467
448
|
),
|
468
449
|
jwt.encode(
|
469
|
-
cast(
|
450
|
+
cast(dict[str, Any], user_information),
|
470
451
|
env_or_settings(
|
471
452
|
settings,
|
472
453
|
GITHUB_AUTH_SECRET_ENV,
|
@@ -481,7 +462,7 @@ def _github_login_callback(request: pyramid.request.Request) -> Dict[str, Any]:
|
|
481
462
|
)
|
482
463
|
|
483
464
|
|
484
|
-
def _github_logout(request: pyramid.request.Request) ->
|
465
|
+
def _github_logout(request: pyramid.request.Request) -> dict[str, Any]:
|
485
466
|
"""Logout the user."""
|
486
467
|
request.response.delete_cookie(SECRET_ENV)
|
487
468
|
request.response.delete_cookie(
|
@@ -507,10 +488,14 @@ def includeme(config: pyramid.config.Configurator) -> None:
|
|
507
488
|
"""Initialize the index page."""
|
508
489
|
base_path = config_utils.get_base_path(config)
|
509
490
|
if base_path != "":
|
491
|
+
config.add_static_view(name="static", path="c2cwsgiutils:static")
|
492
|
+
config.include("pyramid_mako")
|
510
493
|
config.add_route("c2c_index", base_path, request_method=("GET", "POST"))
|
511
|
-
config.add_view(_index, route_name="c2c_index", http_cache=0)
|
494
|
+
config.add_view(_index, route_name="c2c_index", http_cache=0, renderer="./templates/index.html.mako")
|
512
495
|
config.add_route("c2c_index_slash", base_path + "/", request_method=("GET", "POST"))
|
513
|
-
config.add_view(
|
496
|
+
config.add_view(
|
497
|
+
_index, route_name="c2c_index_slash", http_cache=0, renderer="./templates/index.html.mako"
|
498
|
+
)
|
514
499
|
|
515
500
|
settings = config.get_settings()
|
516
501
|
auth_type_ = auth_type(settings)
|
c2cwsgiutils/loader.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import logging
|
2
|
-
from typing import
|
2
|
+
from typing import Optional, cast
|
3
3
|
|
4
4
|
from plaster_pastedeploy import Loader as BaseLoader
|
5
5
|
|
@@ -11,10 +11,10 @@ LOG = logging.getLogger(__name__)
|
|
11
11
|
class Loader(BaseLoader): # type: ignore
|
12
12
|
"""The application loader."""
|
13
13
|
|
14
|
-
def _get_defaults(self, defaults: Optional[
|
14
|
+
def _get_defaults(self, defaults: Optional[dict[str, str]] = None) -> dict[str, str]:
|
15
15
|
d = get_config_defaults()
|
16
16
|
d.update(defaults or {})
|
17
|
-
return cast(
|
17
|
+
return cast(dict[str, str], super()._get_defaults(d))
|
18
18
|
|
19
19
|
def __repr__(self) -> str:
|
20
20
|
"""Get the object representation."""
|
c2cwsgiutils/logging_view.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import logging
|
2
2
|
import warnings
|
3
|
-
from
|
3
|
+
from collections.abc import Generator, Mapping
|
4
|
+
from typing import Any
|
4
5
|
|
5
6
|
import pyramid.request
|
6
7
|
|
@@ -80,7 +81,7 @@ def _store_override(settings: Mapping[str, Any], name: str, level: str) -> None:
|
|
80
81
|
pass
|
81
82
|
|
82
83
|
|
83
|
-
def _list_overrides(settings: Mapping[str, Any]) -> Generator[
|
84
|
+
def _list_overrides(settings: Mapping[str, Any]) -> Generator[tuple[str, str], None, None]:
|
84
85
|
_, slave, _ = redis_utils.get(settings)
|
85
86
|
if slave is not None:
|
86
87
|
for key in slave.scan_iter(REDIS_PREFIX + "*"):
|
c2cwsgiutils/models_graph.py
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
import inspect
|
2
2
|
import sys
|
3
|
-
from typing import Any
|
3
|
+
from typing import Any
|
4
4
|
|
5
5
|
import sqlalchemy as sa
|
6
6
|
|
7
7
|
|
8
8
|
def generate_model_graph(module: Any) -> None:
|
9
|
-
"""Generate a
|
9
|
+
"""Generate a graphical model of the database classes."""
|
10
|
+
|
11
|
+
base_name = None
|
10
12
|
if len(sys.argv) == 1:
|
11
13
|
base_name = "Base"
|
12
14
|
elif len(sys.argv) == 2:
|
@@ -40,7 +42,7 @@ def _generate_model_graph(module: Any, base: Any) -> None:
|
|
40
42
|
print("}")
|
41
43
|
|
42
44
|
|
43
|
-
def _print_node(symbol: Any, interesting:
|
45
|
+
def _print_node(symbol: Any, interesting: set[Any]) -> None:
|
44
46
|
print(f'{symbol.__name__} [label="{_get_table_desc(symbol)}", shape=box];')
|
45
47
|
for parent in symbol.__bases__:
|
46
48
|
if parent != object:
|
@@ -60,7 +62,7 @@ def _get_table_desc(symbol: Any) -> str:
|
|
60
62
|
return "\\n".join(cols)
|
61
63
|
|
62
64
|
|
63
|
-
def _get_all_cols(symbol: Any) ->
|
65
|
+
def _get_all_cols(symbol: Any) -> list[str]:
|
64
66
|
cols = []
|
65
67
|
|
66
68
|
for member_name in symbol.__dict__:
|
@@ -69,7 +71,7 @@ def _get_all_cols(symbol: Any) -> List[str]:
|
|
69
71
|
# Those are not fields
|
70
72
|
pass
|
71
73
|
elif isinstance(member, sa.sql.schema.SchemaItem):
|
72
|
-
cols.append(member_name + ("[null]" if member.nullable else ""))
|
74
|
+
cols.append(member_name + ("[null]" if member.nullable else "")) # type: ignore
|
73
75
|
elif isinstance(member, sa.orm.attributes.InstrumentedAttribute):
|
74
76
|
nullable = (
|
75
77
|
member.property.columns[0].nullable
|
@@ -82,7 +84,7 @@ def _get_all_cols(symbol: Any) -> List[str]:
|
|
82
84
|
return cols
|
83
85
|
|
84
86
|
|
85
|
-
def _get_local_cols(symbol: Any) ->
|
87
|
+
def _get_local_cols(symbol: Any) -> list[str]:
|
86
88
|
result = set(_get_all_cols(symbol))
|
87
89
|
for parent in symbol.__bases__:
|
88
90
|
result -= set(_get_all_cols(parent))
|