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/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, Dict, List, Optional, Union, cast
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: List[str] = []
49
- additional_auth: List[str] = []
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[Dict[str, str]] = None
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
- <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
- 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
- response.text += section(
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
- 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
 
@@ -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
- result = ""
261
-
262
- if sql_profiler_url:
263
- result += paragraph(
264
- link(sql_profiler_url, "Status"),
265
- link(sql_profiler_url + "?enable=1", "Enable"),
266
- link(sql_profiler_url + "?enable=0", "Disable"),
267
- title="SQL",
268
- )
269
-
270
- return section("Profiler", result)
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
- button("Set"),
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
- "Debug",
319
- paragraph(
320
- link(_url(request, "c2c_debug_stacks"), "Stack traces"),
321
- link(_url(request, "c2c_debug_headers"), "HTTP headers"),
322
- link(_url(request, "c2c_debug_memory_maps"), "Mapped memory"),
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(health_check_url, button("Run"), input_("max_level", value=1), input_("checks")),
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) -> Dict[str, Any]:
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) -> Dict[str, Any]:
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(Dict[str, Any], user_information),
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) -> Dict[str, Any]:
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(_index, route_name="c2c_index_slash", http_cache=0)
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 Dict, Optional, cast
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[Dict[str, str]] = None) -> Dict[str, str]:
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(Dict[str, str], super()._get_defaults(d))
17
+ return cast(dict[str, str], super()._get_defaults(d))
18
18
 
19
19
  def __repr__(self) -> str:
20
20
  """Get the object representation."""
@@ -1,6 +1,7 @@
1
1
  import logging
2
2
  import warnings
3
- from typing import Any, Generator, Mapping, Tuple
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[Tuple[str, str], None, None]:
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 + "*"):
@@ -1,12 +1,14 @@
1
1
  import inspect
2
2
  import sys
3
- from typing import Any, List, Set
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 grophical model of the database classes."""
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: Set[Any]) -> None:
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) -> List[str]:
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) -> List[str]:
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))