c2cwsgiutils 6.2.0.dev35__py3-none-any.whl → 6.2.0.dev36__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 (47) hide show
  1. c2cwsgiutils/acceptance/__init__.py +1 -0
  2. c2cwsgiutils/acceptance/connection.py +3 -2
  3. c2cwsgiutils/acceptance/image.py +5 -3
  4. c2cwsgiutils/acceptance/package-lock.json +13 -13
  5. c2cwsgiutils/acceptance/package.json +1 -1
  6. c2cwsgiutils/acceptance/print.py +1 -0
  7. c2cwsgiutils/acceptance/utils.py +2 -1
  8. c2cwsgiutils/auth.py +9 -8
  9. c2cwsgiutils/broadcast/__init__.py +1 -1
  10. c2cwsgiutils/broadcast/local.py +4 -0
  11. c2cwsgiutils/broadcast/redis.py +8 -2
  12. c2cwsgiutils/client_info.py +2 -0
  13. c2cwsgiutils/coverage_setup.py +2 -2
  14. c2cwsgiutils/db.py +11 -2
  15. c2cwsgiutils/db_maintenance_view.py +1 -1
  16. c2cwsgiutils/debug/__init__.py +4 -2
  17. c2cwsgiutils/debug/_views.py +2 -3
  18. c2cwsgiutils/errors.py +7 -2
  19. c2cwsgiutils/health_check.py +39 -29
  20. c2cwsgiutils/index.py +1 -1
  21. c2cwsgiutils/logging_view.py +1 -1
  22. c2cwsgiutils/models_graph.py +1 -1
  23. c2cwsgiutils/pretty_json.py +1 -1
  24. c2cwsgiutils/profiler.py +1 -0
  25. c2cwsgiutils/prometheus.py +2 -0
  26. c2cwsgiutils/pyramid.py +1 -0
  27. c2cwsgiutils/pyramid_logging.py +4 -0
  28. c2cwsgiutils/redis_stats.py +1 -1
  29. c2cwsgiutils/redis_utils.py +2 -0
  30. c2cwsgiutils/request_tracking/__init__.py +1 -1
  31. c2cwsgiutils/scripts/genversion.py +4 -2
  32. c2cwsgiutils/scripts/stats_db.py +1 -0
  33. c2cwsgiutils/scripts/test_print.py +4 -1
  34. c2cwsgiutils/sentry.py +1 -1
  35. c2cwsgiutils/setup_process.py +3 -1
  36. c2cwsgiutils/sql_profiler/__init__.py +1 -1
  37. c2cwsgiutils/sql_profiler/_impl.py +1 -1
  38. c2cwsgiutils/sqlalchemylogger/handlers.py +18 -12
  39. c2cwsgiutils/stats_pyramid/__init__.py +2 -1
  40. c2cwsgiutils/stats_pyramid/_pyramid_spy.py +1 -0
  41. c2cwsgiutils/version.py +1 -1
  42. {c2cwsgiutils-6.2.0.dev35.dist-info → c2cwsgiutils-6.2.0.dev36.dist-info}/METADATA +1 -1
  43. c2cwsgiutils-6.2.0.dev36.dist-info/RECORD +67 -0
  44. c2cwsgiutils-6.2.0.dev35.dist-info/RECORD +0 -67
  45. {c2cwsgiutils-6.2.0.dev35.dist-info → c2cwsgiutils-6.2.0.dev36.dist-info}/LICENSE +0 -0
  46. {c2cwsgiutils-6.2.0.dev35.dist-info → c2cwsgiutils-6.2.0.dev36.dist-info}/WHEEL +0 -0
  47. {c2cwsgiutils-6.2.0.dev35.dist-info → c2cwsgiutils-6.2.0.dev36.dist-info}/entry_points.txt +0 -0
@@ -20,6 +20,7 @@ def retry(
20
20
  tries: number of times to try (not retry) before giving up
21
21
  delay: initial delay between retries in seconds
22
22
  backoff: backoff multiplier e.g. value of 2 will double the delay each retry
23
+
23
24
  """
24
25
 
25
26
  def deco_retry(f: typing.Callable[..., typing.Any]) -> typing.Callable[..., typing.Any]:
@@ -20,6 +20,7 @@ class Connection:
20
20
  """The connection."""
21
21
 
22
22
  def __init__(self, base_url: str, origin: str) -> None:
23
+ """Initialize the connection."""
23
24
  self.base_url = base_url
24
25
  if not self.base_url.endswith("/"):
25
26
  self.base_url += "/"
@@ -93,10 +94,10 @@ class Connection:
93
94
  check_response(r, expected_status, cache_expected=cache_expected)
94
95
  self._check_cors(cors, r)
95
96
  r.raw.decode_content = True
96
- doc = etree.parse(r.raw) # nosec
97
+ doc = etree.parse(r.raw) # noqa: S320
97
98
  if schema is not None:
98
99
  with open(schema, encoding="utf-8") as schema_file:
99
- xml_schema = etree.XMLSchema(etree.parse(schema_file)) # nosec
100
+ xml_schema = etree.XMLSchema(etree.parse(schema_file)) # noqa: S320
100
101
  xml_schema.assertValid(doc)
101
102
  return doc
102
103
 
@@ -10,7 +10,7 @@ import skimage.metrics # pylint: disable=import-error
10
10
  import skimage.transform # pylint: disable=import-error
11
11
 
12
12
  if TYPE_CHECKING:
13
- from typing import TypeAlias
13
+ from typing_extensions import TypeAlias
14
14
 
15
15
  NpNdarrayInt: TypeAlias = np.ndarray[np.uint8, Any]
16
16
  else:
@@ -89,6 +89,7 @@ def check_image(
89
89
  level: The minimum similarity level (between 0.0 and 1.0), default to 1.0
90
90
  generate_expected_image: If `True` generate the expected image instead of checking it
91
91
  use_mask: If `False` don't use the mask event if the file exists
92
+
92
93
  """
93
94
  assert image_to_check is not None, "Image required"
94
95
  image_file_basename = os.path.splitext(os.path.basename(expected_filename))[0]
@@ -122,7 +123,7 @@ def check_image(
122
123
  if np.issubdtype(mask.dtype, np.floating):
123
124
  mask = (mask * 255).astype("uint8")
124
125
 
125
- assert ((0 < mask) & (mask < 255)).sum() == 0, "Mask should be only black and white image"
126
+ assert ((mask > 0) & (mask < 255)).sum() == 0, "Mask should be only black and white image"
126
127
 
127
128
  # Convert to boolean
128
129
  mask = mask == 0
@@ -139,7 +140,7 @@ def check_image(
139
140
  return
140
141
  if not os.path.isfile(expected_filename):
141
142
  skimage.io.imsave(expected_filename, image_to_check)
142
- assert False, "Expected image not found: " + expected_filename
143
+ raise AssertionError("Expected image not found: " + expected_filename)
143
144
  expected = skimage.io.imread(expected_filename)
144
145
  assert expected is not None, "Wrong image: " + expected_filename
145
146
  expected = normalize_image(expected)
@@ -201,6 +202,7 @@ def check_screenshot(
201
202
  level: See `check_image`
202
203
  generate_expected_image: See `check_image`
203
204
  use_mask: See `check_image`
205
+
204
206
  """
205
207
  if headers is None:
206
208
  headers = {}
@@ -6,7 +6,7 @@
6
6
  "": {
7
7
  "dependencies": {
8
8
  "commander": "12.1.0",
9
- "puppeteer": "23.7.1"
9
+ "puppeteer": "23.9.0"
10
10
  }
11
11
  },
12
12
  "node_modules/@babel/code-frame": {
@@ -379,9 +379,9 @@
379
379
  }
380
380
  },
381
381
  "node_modules/devtools-protocol": {
382
- "version": "0.0.1354347",
383
- "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1354347.tgz",
384
- "integrity": "sha512-BlmkSqV0V84E2WnEnoPnwyix57rQxAM5SKJjf4TbYOCGLAWtz8CDH8RIaGOjPgPCXo2Mce3kxSY497OySidY3Q==",
382
+ "version": "0.0.1367902",
383
+ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz",
384
+ "integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==",
385
385
  "license": "BSD-3-Clause"
386
386
  },
387
387
  "node_modules/emoji-regex": {
@@ -868,17 +868,17 @@
868
868
  }
869
869
  },
870
870
  "node_modules/puppeteer": {
871
- "version": "23.7.1",
872
- "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.7.1.tgz",
873
- "integrity": "sha512-jS6XehagMvxQ12etwY/4EOYZ0Sm8GAsrtGhdQn4AqpJAyHc3RYl7tGd4QYh/MmShDw8sF9FWYQqGidhoXaqokQ==",
871
+ "version": "23.9.0",
872
+ "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.9.0.tgz",
873
+ "integrity": "sha512-WfB8jGwFV+qrD9dcJJVvWPFJBU6kxeu2wxJz9WooDGfM3vIiKLgzImEDBxUQnCBK/2cXB3d4dV6gs/LLpgfLDg==",
874
874
  "hasInstallScript": true,
875
875
  "license": "Apache-2.0",
876
876
  "dependencies": {
877
877
  "@puppeteer/browsers": "2.4.1",
878
878
  "chromium-bidi": "0.8.0",
879
879
  "cosmiconfig": "^9.0.0",
880
- "devtools-protocol": "0.0.1354347",
881
- "puppeteer-core": "23.7.1",
880
+ "devtools-protocol": "0.0.1367902",
881
+ "puppeteer-core": "23.9.0",
882
882
  "typed-query-selector": "^2.12.0"
883
883
  },
884
884
  "bin": {
@@ -889,15 +889,15 @@
889
889
  }
890
890
  },
891
891
  "node_modules/puppeteer-core": {
892
- "version": "23.7.1",
893
- "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.7.1.tgz",
894
- "integrity": "sha512-Om/qCZhd+HLoAr7GltrRAZpS3uOXwHu7tXAoDbNcJADHjG2zeAlDArgyIPXYGG4QB/EQUHk13Q6RklNxGM73Pg==",
892
+ "version": "23.9.0",
893
+ "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.9.0.tgz",
894
+ "integrity": "sha512-hLVrav2HYMVdK0YILtfJwtnkBAwNOztUdR4aJ5YKDvgsbtagNr6urUJk9HyjRA9e+PaLI3jzJ0wM7A4jSZ7Qxw==",
895
895
  "license": "Apache-2.0",
896
896
  "dependencies": {
897
897
  "@puppeteer/browsers": "2.4.1",
898
898
  "chromium-bidi": "0.8.0",
899
899
  "debug": "^4.3.7",
900
- "devtools-protocol": "0.0.1354347",
900
+ "devtools-protocol": "0.0.1367902",
901
901
  "typed-query-selector": "^2.12.0",
902
902
  "ws": "^8.18.0"
903
903
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "dependencies": {
3
3
  "commander": "12.1.0",
4
- "puppeteer": "23.7.1"
4
+ "puppeteer": "23.9.0"
5
5
  },
6
6
  "type": "module"
7
7
  }
@@ -21,6 +21,7 @@ class PrintConnection(connection.Connection):
21
21
  base_url: The base URL to the print server (including the /print)
22
22
  app: The name of the application to use
23
23
  origin: The origin and referrer to include in the requests
24
+
24
25
  """
25
26
  super().__init__(base_url=base_url, origin=origin)
26
27
  self.session.headers["Referrer"] = origin
@@ -32,6 +32,7 @@ def retry_timeout(what: Callable[[], Any], timeout: float = _DEFAULT_TIMEOUT, in
32
32
  what: the function to try
33
33
  timeout: the timeout to get a success
34
34
  interval: the interval between try
35
+
35
36
  """
36
37
  timeout = time.perf_counter() + timeout
37
38
  while True:
@@ -46,7 +47,7 @@ def retry_timeout(what: Callable[[], Any], timeout: float = _DEFAULT_TIMEOUT, in
46
47
  error = str(e)
47
48
  _LOG.info(" Failed: %s", e)
48
49
  if time.perf_counter() > timeout:
49
- assert False, "Timeout: " + error
50
+ raise AssertionError("Timeout: " + error)
50
51
  time.sleep(interval)
51
52
 
52
53
 
c2cwsgiutils/auth.py CHANGED
@@ -12,32 +12,32 @@ from requests_oauthlib import OAuth2Session
12
12
  from c2cwsgiutils.config_utils import config_bool, env_or_config, env_or_settings
13
13
 
14
14
  _COOKIE_AGE = 7 * 24 * 3600
15
- SECRET_PROP = "c2c.secret" # nosec # noqa
16
- SECRET_ENV = "C2C_SECRET" # nosec # noqa
15
+ SECRET_PROP = "c2c.secret" # noqa: S105
16
+ SECRET_ENV = "C2C_SECRET" # noqa: S105
17
17
  _GITHUB_REPOSITORY_PROP = "c2c.auth.github.repository"
18
18
  _GITHUB_REPOSITORY_ENV = "C2C_AUTH_GITHUB_REPOSITORY"
19
19
  _GITHUB_ACCESS_TYPE_PROP = "c2c.auth.github.access_type"
20
20
  _GITHUB_ACCESS_TYPE_ENV = "C2C_AUTH_GITHUB_ACCESS_TYPE"
21
21
  GITHUB_AUTH_URL_PROP = "c2c.auth.github.auth_url"
22
22
  GITHUB_AUTH_URL_ENV = "C2C_AUTH_GITHUB_AUTH_URL"
23
- GITHUB_TOKEN_URL_PROP = "c2c.auth.github.token_url" # nosec
24
- GITHUB_TOKEN_URL_ENV = "C2C_AUTH_GITHUB_TOKEN_URL" # nosec
23
+ GITHUB_TOKEN_URL_PROP = "c2c.auth.github.token_url" # noqa: S105
24
+ GITHUB_TOKEN_URL_ENV = "C2C_AUTH_GITHUB_TOKEN_URL" # noqa: S105
25
25
  GITHUB_USER_URL_PROP = "c2c.auth.github.user_url"
26
26
  GITHUB_USER_URL_ENV = "C2C_AUTH_GITHUB_USER_URL"
27
27
  _GITHUB_REPO_URL_PROP = "c2c.auth.github.repo_url"
28
28
  _GITHUB_REPO_URL_ENV = "C2C_AUTH_GITHUB_REPO_URL"
29
29
  GITHUB_CLIENT_ID_PROP = "c2c.auth.github.client_id"
30
30
  GITHUB_CLIENT_ID_ENV = "C2C_AUTH_GITHUB_CLIENT_ID"
31
- GITHUB_CLIENT_SECRET_PROP = "c2c.auth.github.client_secret" # nosec # noqa
32
- GITHUB_CLIENT_SECRET_ENV = "C2C_AUTH_GITHUB_CLIENT_SECRET" # nosec # noqa
31
+ GITHUB_CLIENT_SECRET_PROP = "c2c.auth.github.client_secret" # noqa: S105
32
+ GITHUB_CLIENT_SECRET_ENV = "C2C_AUTH_GITHUB_CLIENT_SECRET" # noqa: S105
33
33
  GITHUB_SCOPE_PROP = "c2c.auth.github.scope"
34
34
  GITHUB_SCOPE_ENV = "C2C_AUTH_GITHUB_SCOPE"
35
35
  # To be able to use private repository
36
36
  GITHUB_SCOPE_DEFAULT = "repo"
37
37
  GITHUB_AUTH_COOKIE_PROP = "c2c.auth.github.auth.cookie"
38
38
  GITHUB_AUTH_COOKIE_ENV = "C2C_AUTH_GITHUB_COOKIE"
39
- GITHUB_AUTH_SECRET_PROP = "c2c.auth.github.auth.secret" # nosec # noqa
40
- GITHUB_AUTH_SECRET_ENV = "C2C_AUTH_GITHUB_SECRET" # nosec # noqa
39
+ GITHUB_AUTH_SECRET_PROP = "c2c.auth.github.auth.secret" # noqa: S105
40
+ GITHUB_AUTH_SECRET_ENV = "C2C_AUTH_GITHUB_SECRET" # noqa: S105
41
41
  GITHUB_AUTH_PROXY_URL_PROP = "c2c.auth.github.auth.proxy_url"
42
42
  GITHUB_AUTH_PROXY_URL_ENV = "C2C_AUTH_GITHUB_PROXY_URL"
43
43
  USE_SESSION_PROP = "c2c.use_session"
@@ -209,6 +209,7 @@ def check_access(
209
209
  request: is the request object.
210
210
  repo: is the repository to check access to (<organization>/<repository>).
211
211
  access_type: is the type of access to check (admin|push|pull).
212
+
212
213
  """
213
214
  if not is_auth(request):
214
215
  return False
@@ -19,7 +19,7 @@ _broadcaster: Optional[interface.BaseBroadcaster] = None
19
19
 
20
20
  def init(config: Optional[pyramid.config.Configurator] = None) -> None:
21
21
  """Initialize the broadcaster with Redis, if configured, for backward compatibility."""
22
- warnings.warn("init function is deprecated; use includeme instead")
22
+ warnings.warn("init function is deprecated; use includeme instead", stacklevel=2)
23
23
  includeme(config)
24
24
 
25
25
 
@@ -9,17 +9,21 @@ class LocalBroadcaster(interface.BaseBroadcaster):
9
9
  """Fake implementation of broadcasting messages (will just answer locally)."""
10
10
 
11
11
  def __init__(self) -> None:
12
+ """Initialize the broadcaster."""
12
13
  self._subscribers: MutableMapping[str, Callable[..., Any]] = {}
13
14
 
14
15
  def subscribe(self, channel: str, callback: Callable[..., Any]) -> None:
16
+ """Subscribe to a channel."""
15
17
  self._subscribers[channel] = callback
16
18
 
17
19
  def unsubscribe(self, channel: str) -> None:
20
+ """Unsubscribe from a channel."""
18
21
  del self._subscribers[channel]
19
22
 
20
23
  def broadcast(
21
24
  self, channel: str, params: Mapping[str, Any], expect_answers: bool, timeout: float
22
25
  ) -> Optional[list[Any]]:
26
+ """Broadcast a message to all the listeners."""
23
27
  subscriber = self._subscribers.get(channel, None)
24
28
  answers = [utils.add_host_info(subscriber(**params))] if subscriber is not None else []
25
29
  return answers if expect_answers else None
@@ -23,6 +23,7 @@ class RedisBroadcaster(interface.BaseBroadcaster):
23
23
  master: "redis.client.Redis[str]",
24
24
  slave: "redis.client.Redis[str]",
25
25
  ) -> None:
26
+ """Initialize the broadcaster."""
26
27
  from c2cwsgiutils import redis_utils # pylint: disable=import-outside-toplevel
27
28
 
28
29
  self._master = master
@@ -40,6 +41,8 @@ class RedisBroadcaster(interface.BaseBroadcaster):
40
41
  return self._broadcast_prefix + channel
41
42
 
42
43
  def subscribe(self, channel: str, callback: Callable[..., Any]) -> None:
44
+ """Subscribe to a channel."""
45
+
43
46
  def wrapper(message: Mapping[str, Any]) -> None:
44
47
  _LOG.debug("Received a broadcast on %s: %s", message["channel"], repr(message["data"]))
45
48
  data = json.loads(message["data"])
@@ -58,6 +61,7 @@ class RedisBroadcaster(interface.BaseBroadcaster):
58
61
  self._pub_sub.subscribe(**{actual_channel: wrapper})
59
62
 
60
63
  def unsubscribe(self, channel: str) -> None:
64
+ """Unsubscribe from a channel."""
61
65
  _LOG.debug("Unsubscribing from %s")
62
66
  actual_channel = self._get_channel(channel)
63
67
  self._pub_sub.unsubscribe(actual_channel)
@@ -65,6 +69,7 @@ class RedisBroadcaster(interface.BaseBroadcaster):
65
69
  def broadcast(
66
70
  self, channel: str, params: Mapping[str, Any], expect_answers: bool, timeout: float
67
71
  ) -> Optional[list[Any]]:
72
+ """Broadcast a message to all the listeners."""
68
73
  if expect_answers:
69
74
  return self._broadcast_with_answer(channel, params, timeout)
70
75
  else:
@@ -85,7 +90,8 @@ class RedisBroadcaster(interface.BaseBroadcaster):
85
90
  cond.notify()
86
91
 
87
92
  answer_channel = self._get_channel(channel) + "".join(
88
- random.choice(string.ascii_uppercase + string.digits) for _ in range(10) # nosec
93
+ random.choice(string.ascii_uppercase + string.digits) # noqa: S311
94
+ for _ in range(10)
89
95
  )
90
96
  _LOG.debug("Subscribing for broadcast answers on %s", answer_channel)
91
97
  self._pub_sub.subscribe(**{answer_channel: callback})
@@ -98,7 +104,7 @@ class RedisBroadcaster(interface.BaseBroadcaster):
98
104
  with cond:
99
105
  while len(answers) < nb_received:
100
106
  to_wait = timeout_time - time.perf_counter()
101
- if to_wait <= 0.0: # pragma: no cover
107
+ if to_wait <= 0.0:
102
108
  _LOG.warning(
103
109
  "timeout waiting for %d/%d answers on %s",
104
110
  len(answers),
@@ -16,9 +16,11 @@ class Filter:
16
16
  """
17
17
 
18
18
  def __init__(self, application: Callable[[dict[str, str], Any], Any]):
19
+ """Initialize the filter."""
19
20
  self._application = application
20
21
 
21
22
  def __call__(self, environ: dict[str, str], start_response: Any) -> Any:
23
+ """Update the environ with the headers."""
22
24
  # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
23
25
  if "HTTP_FORWARDED" in environ:
24
26
  _handle_forwarded(environ)
@@ -10,7 +10,7 @@ _LOG = logging.getLogger(__name__)
10
10
 
11
11
  def init() -> None:
12
12
  """Initialize the code coverage, for backward compatibility."""
13
- warnings.warn("init function is deprecated; use includeme instead")
13
+ warnings.warn("init function is deprecated; use includeme instead", stacklevel=2)
14
14
  includeme()
15
15
 
16
16
 
@@ -22,7 +22,7 @@ def includeme(config: Optional[pyramid.config.Configurator] = None) -> None:
22
22
  import coverage # pylint: disable=import-outside-toplevel
23
23
 
24
24
  _LOG.warning("Setting up code coverage")
25
- report_dir = "/tmp/coverage/api" # nosec
25
+ report_dir = "/tmp/coverage/api" # noqa: S108
26
26
  os.makedirs(report_dir, exist_ok=True)
27
27
  cov = coverage.Coverage(
28
28
  data_file=os.path.join(report_dir, "coverage"),
c2cwsgiutils/db.py CHANGED
@@ -66,8 +66,11 @@ def setup_session(
66
66
  force_slave: The method/paths that needs to use the slave
67
67
 
68
68
  Returns: The SQLAlchemy session, the R/W engine and the R/O engine
69
+
69
70
  """
70
- warnings.warn("setup_session function is deprecated; use init and request.dbsession instead")
71
+ warnings.warn(
72
+ "setup_session function is deprecated; use init and request.dbsession instead", stacklevel=2
73
+ )
71
74
  if slave_prefix is None:
72
75
  slave_prefix = master_prefix
73
76
  settings = config.registry.settings
@@ -122,8 +125,11 @@ def create_session(
122
125
  engine_config: The rest of the parameters are passed as is to the sqlalchemy.create_engine function
123
126
 
124
127
  Returns: The SQLAlchemy session
128
+
125
129
  """
126
- warnings.warn("create_session function is deprecated; use init and request.dbsession instead")
130
+ warnings.warn(
131
+ "create_session function is deprecated; use init and request.dbsession instead", stacklevel=2
132
+ )
127
133
  if slave_url is None:
128
134
  slave_url = url
129
135
 
@@ -215,6 +221,7 @@ class SessionFactory(_sessionmaker):
215
221
  ro_engine: sqlalchemy.engine.Engine,
216
222
  rw_engine: sqlalchemy.engine.Engine,
217
223
  ):
224
+ """Initialize the session factory."""
218
225
  super().__init__()
219
226
  self.master_paths: Iterable[Pattern[str]] = (
220
227
  list(map(_RE_COMPILE, force_master)) if force_master else []
@@ -232,6 +239,7 @@ class SessionFactory(_sessionmaker):
232
239
  def __call__( # type: ignore
233
240
  self, request: Optional[pyramid.request.Request], readwrite: Optional[bool] = None, **local_kw: Any
234
241
  ) -> _scoped_session:
242
+ """Set the engine based on the request."""
235
243
  if readwrite is not None:
236
244
  if readwrite and not FORCE_READONLY:
237
245
  _LOG.debug("Using %s database", self.rw_engine.c2c_name) # type: ignore
@@ -374,6 +382,7 @@ def init(
374
382
  force_slave: The method/paths that needs to use the slave
375
383
 
376
384
  Returns: The SQLAlchemy session
385
+
377
386
  """
378
387
  settings = config.get_settings()
379
388
  settings["tm.manager_hook"] = "pyramid_tm.explicit_manager"
@@ -15,7 +15,7 @@ _REDIS_PREFIX = "c2c_db_maintenance_"
15
15
 
16
16
  def install_subscriber(config: pyramid.config.Configurator) -> None:
17
17
  """Install the view to configure the loggers, if configured to do so, for backward compatibility."""
18
- warnings.warn("install_subscriber function is deprecated; use includeme instead")
18
+ warnings.warn("install_subscriber function is deprecated; use includeme instead", stacklevel=2)
19
19
  includeme(config)
20
20
 
21
21
 
@@ -16,7 +16,7 @@ dump_memory_maps = utils.dump_memory_maps
16
16
 
17
17
  def init(config: pyramid.config.Configurator) -> None:
18
18
  """Initialize the debug tools, for backward compatibility."""
19
- warnings.warn("init function is deprecated; use includeme instead")
19
+ warnings.warn("init function is deprecated; use includeme instead", stacklevel=2)
20
20
  includeme(config)
21
21
 
22
22
 
@@ -37,6 +37,8 @@ def init_daemon(config: Optional[pyramid.config.Configurator] = None) -> None:
37
37
  those requests.
38
38
  """
39
39
  if config_utils.env_or_config(config, ENV_KEY, CONFIG_KEY, type_=config_utils.config_bool):
40
- from c2cwsgiutils.debug import _listeners # pylint: disable=import-outside-toplevel
40
+ from c2cwsgiutils.debug import ( # pylint: disable=import-outside-toplevel
41
+ _listeners,
42
+ )
41
43
 
42
44
  _listeners.init()
@@ -79,7 +79,7 @@ def _dump_memory_diff(request: pyramid.request.Request) -> list[Any]:
79
79
  try:
80
80
  if request.params.get("no_warmup", "0").lower() in ("1", "true", "on"):
81
81
  request.invoke_subrequest(sub_request)
82
- except Exception: # nosec # pylint: disable=broad-except
82
+ except Exception: # pylint: disable=broad-except
83
83
  pass
84
84
 
85
85
  _LOG.debug("checking memory growth for %s", path)
@@ -151,8 +151,7 @@ def _headers(request: pyramid.request.Request) -> Mapping[str, Any]:
151
151
  }
152
152
  if "status" in request.params:
153
153
  raise exception_response(int(request.params["status"]), detail=result)
154
- else:
155
- return result
154
+ return result
156
155
 
157
156
 
158
157
  def _error(request: pyramid.request.Request) -> Any:
c2cwsgiutils/errors.py CHANGED
@@ -9,7 +9,12 @@ from typing import Any, Callable
9
9
  import pyramid.request
10
10
  import sqlalchemy.exc
11
11
  from cornice import cors
12
- from pyramid.httpexceptions import HTTPError, HTTPException, HTTPRedirection, HTTPSuccessful
12
+ from pyramid.httpexceptions import (
13
+ HTTPError,
14
+ HTTPException,
15
+ HTTPRedirection,
16
+ HTTPSuccessful,
17
+ )
13
18
  from webob.request import DisconnectionError
14
19
 
15
20
  from c2cwsgiutils import auth, config_utils
@@ -152,7 +157,7 @@ def _passthrough(exception: HTTPException, request: pyramid.request.Request) ->
152
157
 
153
158
  def init(config: pyramid.config.Configurator) -> None:
154
159
  """Initialize the error views, for backward compatibility."""
155
- warnings.warn("init function is deprecated; use includeme instead")
160
+ warnings.warn("init function is deprecated; use includeme instead", stacklevel=2)
156
161
  includeme(config)
157
162
 
158
163
 
@@ -67,6 +67,7 @@ class JsonCheckException(Exception):
67
67
  """Checker exception used to add some structured content to a failure."""
68
68
 
69
69
  def __init__(self, message: str, json: Any):
70
+ """Initialize the exception."""
70
71
  super().__init__()
71
72
  self.message = message
72
73
  self.json = json
@@ -199,6 +200,7 @@ class HealthCheck:
199
200
  """
200
201
 
201
202
  def __init__(self, config: pyramid.config.Configurator) -> None:
203
+ """Initialize the health check view."""
202
204
  config.add_route(
203
205
  "c2c_health_check", config_utils.get_base_path(config) + r"/health_check", request_method="GET"
204
206
  )
@@ -235,6 +237,7 @@ class HealthCheck:
235
237
  engine_type: whether to check only the RW, RO or both engines
236
238
  rw_engin: the RW engine to use (if None, use the session one)
237
239
  ro_engin: the RO engine to use (if None, use the session one)
240
+
238
241
  """
239
242
  if query_cb is None:
240
243
  query_cb = self._at_least_one(at_least_one_model)
@@ -265,6 +268,7 @@ class HealthCheck:
265
268
  version_table: override the table name for the version
266
269
  rw_engin: the RW engine to use (if None, use the session one)
267
270
  ro_engin: the RO engine to use (if None, use the session one)
271
+
268
272
  """
269
273
  version_ = _get_alembic_version(alembic_ini_path, name)
270
274
 
@@ -287,26 +291,28 @@ class HealthCheck:
287
291
  assert version_schema
288
292
  assert version_table
289
293
  for binding in _get_bindings(self.session, EngineType.READ_AND_WRITE):
290
- with binding as binded_session:
291
- with _PROMETHEUS_DB_SUMMARY.labels(
294
+ with (
295
+ binding as binded_session,
296
+ _PROMETHEUS_DB_SUMMARY.labels(
292
297
  configuration=alembic_ini_path, connection=binding.name(), check="alembic"
293
- ).time():
294
- result = binded_session.execute(
295
- sqlalchemy.text(
296
- "SELECT version_num FROM " # nosec
297
- f"{sqlalchemy.sql.quoted_name(version_schema, True)}."
298
- f"{sqlalchemy.sql.quoted_name(version_table, True)}"
299
- )
300
- ).fetchone()
301
- assert result is not None
302
- (actual_version,) = result
303
- _PROMETHEUS_ALEMBIC_VERSION.labels(
304
- version=actual_version, name=name, configuration=alembic_ini_path
305
- ).set(1)
306
- if actual_version != version_:
307
- raise Exception( # pylint: disable=broad-exception-raised
308
- f"Invalid alembic version (db: {actual_version}, code: {version_})"
309
- )
298
+ ).time(),
299
+ ):
300
+ result = binded_session.execute(
301
+ sqlalchemy.text(
302
+ "SELECT version_num FROM " # noqa: S608
303
+ f"{sqlalchemy.sql.quoted_name(version_schema, True)}."
304
+ f"{sqlalchemy.sql.quoted_name(version_table, True)}"
305
+ )
306
+ ).fetchone()
307
+ assert result is not None
308
+ (actual_version,) = result
309
+ _PROMETHEUS_ALEMBIC_VERSION.labels(
310
+ version=actual_version, name=name, configuration=alembic_ini_path
311
+ ).set(1)
312
+ if actual_version != version_:
313
+ raise Exception( # pylint: disable=broad-exception-raised
314
+ f"Invalid alembic version (db: {actual_version}, code: {version_})"
315
+ )
310
316
  return version_
311
317
 
312
318
  self._checks.append(
@@ -321,9 +327,8 @@ class HealthCheck:
321
327
  Mapping[str, str], Callable[[pyramid.request.Request], Mapping[str, str]], None
322
328
  ] = None,
323
329
  name: Optional[str] = None,
324
- check_cb: Callable[
325
- [pyramid.request.Request, requests.Response], Any
326
- ] = lambda request, response: None,
330
+ check_cb: Callable[[pyramid.request.Request, requests.Response], Any] = lambda request,
331
+ response: None,
327
332
  timeout: float = 3,
328
333
  level: int = 1,
329
334
  ) -> None:
@@ -339,6 +344,7 @@ class HealthCheck:
339
344
  response as parameters)
340
345
  timeout: the timeout
341
346
  level: the level of the health check
347
+
342
348
  """
343
349
 
344
350
  def check(request: pyramid.request.Request) -> Any:
@@ -365,6 +371,7 @@ class HealthCheck:
365
371
  Arguments:
366
372
  name: the name of the check (defaults to url)
367
373
  level: the level of the health check
374
+
368
375
  """
369
376
 
370
377
  def check(request: pyramid.request.Request) -> Any:
@@ -413,6 +420,7 @@ class HealthCheck:
413
420
  Arguments:
414
421
  name: the name of the check (defaults to "version")
415
422
  level: the level of the health check
423
+
416
424
  """
417
425
 
418
426
  def check(request: pyramid.request.Request) -> dict[str, Any]:
@@ -441,6 +449,7 @@ class HealthCheck:
441
449
  name: the name of the check
442
450
  check_cb: the callback to call (takes the request as parameter)
443
451
  level: the level of the health check
452
+
444
453
  """
445
454
  assert name
446
455
  self._checks.append((name, check_cb, level))
@@ -453,9 +462,8 @@ class HealthCheck:
453
462
  "successes": {},
454
463
  }
455
464
  checks = None
456
- if "checks" in request.params:
457
- if request.params["checks"] != "":
458
- checks = request.params["checks"].split(",")
465
+ if "checks" in request.params and request.params["checks"] != "":
466
+ checks = request.params["checks"].split(",")
459
467
  for name, check, level in self._checks:
460
468
  if level <= max_level and (checks is None or name in checks):
461
469
  self._run_one(check, is_auth, level, name, request, results)
@@ -498,11 +506,13 @@ class HealthCheck:
498
506
  ) -> tuple[str, Callable[[pyramid.request.Request], None]]:
499
507
  def check(request: pyramid.request.Request) -> None:
500
508
  del request # unused
501
- with binding as session:
502
- with _PROMETHEUS_DB_SUMMARY.labels(
509
+ with (
510
+ binding as session,
511
+ _PROMETHEUS_DB_SUMMARY.labels(
503
512
  connection=binding.name(), check="database", configuration="<default>"
504
- ).time():
505
- return query_cb(session)
513
+ ).time(),
514
+ ):
515
+ return query_cb(session)
506
516
 
507
517
  return "db_engine_" + binding.name(), check
508
518
 
c2cwsgiutils/index.py CHANGED
@@ -501,7 +501,7 @@ def _github_logout(request: pyramid.request.Request) -> dict[str, Any]:
501
501
 
502
502
  def init(config: pyramid.config.Configurator) -> None:
503
503
  """Initialize the index page, for backward compatibility."""
504
- warnings.warn("init function is deprecated; use includeme instead")
504
+ warnings.warn("init function is deprecated; use includeme instead", stacklevel=2)
505
505
  includeme(config)
506
506
 
507
507
 
@@ -15,7 +15,7 @@ _REDIS_PREFIX = "c2c_logging_level_"
15
15
 
16
16
  def install_subscriber(config: pyramid.config.Configurator) -> None:
17
17
  """Install the view to configure the loggers, if configured to do so, for backward compatibility."""
18
- warnings.warn("install_subscriber function is deprecated; use includeme instead")
18
+ warnings.warn("install_subscriber function is deprecated; use includeme instead", stacklevel=2)
19
19
  includeme(config)
20
20
 
21
21
 
@@ -44,7 +44,7 @@ def _generate_model_graph(module: Any, base: Any) -> None:
44
44
  def _print_node(symbol: Any, interesting: set[Any]) -> None:
45
45
  print(f'{symbol.__name__} [label="{_get_table_desc(symbol)}", shape=box];')
46
46
  for parent in symbol.__bases__:
47
- if parent != object:
47
+ if parent is not object:
48
48
  if parent not in interesting:
49
49
  _print_node(parent, interesting)
50
50
  interesting.add(parent)
@@ -28,7 +28,7 @@ class _FastDumps:
28
28
 
29
29
  def init(config: pyramid.config.Configurator) -> None:
30
30
  """Initialize json and fast_json renderer, for backward compatibility."""
31
- warnings.warn("init function is deprecated; use includeme instead")
31
+ warnings.warn("init function is deprecated; use includeme instead", stacklevel=2)
32
32
  includeme(config)
33
33
 
34
34
 
c2cwsgiutils/profiler.py CHANGED
@@ -9,6 +9,7 @@ class Profile(contextlib.ContextDecorator):
9
9
  """Used to profile a function with a decorator or with a with statement."""
10
10
 
11
11
  def __init__(self, path: str, print_number: int = 0) -> None:
12
+ """Initialize the profiler."""
12
13
  self.path = path
13
14
  self.print_number = print_number
14
15
  self.pr = cProfile.Profile()
@@ -115,6 +115,7 @@ class MultiProcessCustomCollector(prometheus_client.registry.Collector):
115
115
  """Get the metrics from the custom collectors."""
116
116
 
117
117
  def collect(self) -> Generator[prometheus_client.core.Metric, None, None]:
118
+ """Get the metrics from the custom collectors."""
118
119
  results: list[list[SerializedMetric]] = []
119
120
  for channel in MULTI_PROCESS_COLLECTOR_BROADCAST_CHANNELS:
120
121
  result = broadcast.broadcast(channel, expect_answers=True)
@@ -159,6 +160,7 @@ class MemoryMapCollector(prometheus_client.registry.Collector):
159
160
  Arguments:
160
161
  memory_type: can be rss, pss or size
161
162
  pids: the list of pids or none
163
+
162
164
  """
163
165
  super().__init__()
164
166
  self.memory_type = memory_type
c2cwsgiutils/pyramid.py CHANGED
@@ -31,6 +31,7 @@ def includeme(config: pyramid.config.Configurator) -> None:
31
31
 
32
32
  Arguments:
33
33
  config: The pyramid Configuration
34
+
34
35
  """
35
36
  logging.captureWarnings(True)
36
37
  config.include(coverage_setup.includeme)
@@ -87,10 +87,12 @@ class PyramidCeeSysLogHandler(cee_syslog_handler.CeeSysLogHandler): # type: ign
87
87
  """A CEE (JSON format) log handler with additional information about the current request."""
88
88
 
89
89
  def __init__(self, *args: Any, **kargv: Any) -> None:
90
+ """Initialize the handler."""
90
91
  super().__init__(*args, **kargv)
91
92
  self.addFilter(_PYRAMID_FILTER)
92
93
 
93
94
  def format(self, record: Any) -> str:
95
+ """Format the record into a CEE string."""
94
96
  message = _make_message_dict(
95
97
  record,
96
98
  self._fqdn,
@@ -112,11 +114,13 @@ class JsonLogHandler(Base):
112
114
  """Log to stdout in JSON."""
113
115
 
114
116
  def __init__(self, stream: Optional[TextIO] = None):
117
+ """Initialize the handler."""
115
118
  super().__init__(stream)
116
119
  self.addFilter(_PYRAMID_FILTER)
117
120
  self._fqdn = socket.getfqdn()
118
121
 
119
122
  def format(self, record: Any) -> str:
123
+ """Format the record into a JSON string."""
120
124
  message = _make_message_dict(
121
125
  record, self._fqdn, debugging_fields=True, extra_fields=True, facility=None, static_fields={}
122
126
  )
@@ -26,7 +26,7 @@ def _execute_command_patch(self: Any, command: str, *args: Any, **options: Any)
26
26
 
27
27
  def init(config: Optional[pyramid.config.Configurator] = None) -> None:
28
28
  """Initialize the Redis tracking, for backward compatibility."""
29
- warnings.warn("init function is deprecated; use includeme instead")
29
+ warnings.warn("init function is deprecated; use includeme instead", stacklevel=2)
30
30
  includeme(config)
31
31
 
32
32
 
@@ -100,11 +100,13 @@ class PubSubWorkerThread(threading.Thread):
100
100
  """A clone of redis.client.PubSubWorkerThread that doesn't die when the connections are broken."""
101
101
 
102
102
  def __init__(self, pubsub: redis.client.PubSub, name: Optional[str] = None) -> None:
103
+ """Initialize the PubSubWorkerThread."""
103
104
  super().__init__(name=name, daemon=True)
104
105
  self.pubsub = pubsub
105
106
  self._running = False
106
107
 
107
108
  def run(self) -> None:
109
+ """Run the worker."""
108
110
  if self._running:
109
111
  return
110
112
  self._running = True
@@ -83,7 +83,7 @@ def _patch_requests() -> None:
83
83
 
84
84
  def init(config: Optional[pyramid.config.Configurator] = None) -> None:
85
85
  """Initialize the request tracking, for backward compatibility."""
86
- warnings.warn("init function is deprecated; use includeme instead")
86
+ warnings.warn("init function is deprecated; use includeme instead", stacklevel=2)
87
87
  includeme(config)
88
88
 
89
89
 
@@ -24,7 +24,7 @@ def _get_package_version(comp: str) -> tuple[Optional[str], Optional[str]]:
24
24
  if matcher:
25
25
  return cast(tuple[str, str], matcher.groups())
26
26
  else:
27
- if len(comp) > 0 and not comp[:3] == "-e ":
27
+ if len(comp) > 0 and comp[:3] != "-e ":
28
28
  print("Cannot parse package version: " + comp)
29
29
  return None, None
30
30
 
@@ -46,7 +46,9 @@ def _get_packages_version() -> dict[str, str]:
46
46
 
47
47
  def deprecated() -> None:
48
48
  """Run the command and print a deprecated notice."""
49
- warnings.warn("c2cwsgiutils_genversion.py is deprecated; use c2cwsgiutils-genversion instead")
49
+ warnings.warn(
50
+ "c2cwsgiutils_genversion.py is deprecated; use c2cwsgiutils-genversion instead", stacklevel=2
51
+ )
50
52
  return main()
51
53
 
52
54
 
@@ -62,6 +62,7 @@ class Reporter:
62
62
  """The stats reporter."""
63
63
 
64
64
  def __init__(self, args: argparse.Namespace) -> None:
65
+ """Initialize the reporter."""
65
66
  self._error: Optional[Exception] = None
66
67
  self.registry = CollectorRegistry()
67
68
  self.prometheus_push = args.prometheus_url is not None
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  """Test a MapfishPrint server."""
3
+
3
4
  import argparse
4
5
  import logging
5
6
  import pprint
@@ -23,7 +24,9 @@ def _parse_args() -> argparse.Namespace:
23
24
 
24
25
  def deprecated() -> None:
25
26
  """Run the command and print a deprecated notice."""
26
- warnings.warn("c2cwsgiutils_test_print.py is deprecated; use c2cwsgiutils-test-print instead")
27
+ warnings.warn(
28
+ "c2cwsgiutils_test_print.py is deprecated; use c2cwsgiutils-test-print instead", stacklevel=2
29
+ )
27
30
  return main()
28
31
 
29
32
 
c2cwsgiutils/sentry.py CHANGED
@@ -33,7 +33,7 @@ def _create_before_send_filter(tags: MutableMapping[str, str]) -> Callable[[Any,
33
33
 
34
34
  def init(config: Optional[pyramid.config.Configurator] = None) -> None:
35
35
  """Initialize the Sentry integration, for backward compatibility."""
36
- warnings.warn("init function is deprecated; use includeme instead")
36
+ warnings.warn("init function is deprecated; use includeme instead", stacklevel=2)
37
37
  includeme(config)
38
38
 
39
39
 
@@ -55,7 +55,9 @@ def init(config_file: str = "c2c:///app/production.ini") -> None:
55
55
 
56
56
  def init_logging(config_file: str = "c2c:///app/production.ini") -> None:
57
57
  """Initialize the non-WSGI application."""
58
- warnings.warn("init_logging function is deprecated; use init instead so that all features are enabled")
58
+ warnings.warn(
59
+ "init_logging function is deprecated; use init instead so that all features are enabled", stacklevel=2
60
+ )
59
61
  loader = get_config_loader(config_file)
60
62
  loader.setup_logging(None)
61
63
 
@@ -18,7 +18,7 @@ _LOG = logging.getLogger(__name__)
18
18
 
19
19
  def init(config: pyramid.config.Configurator) -> None:
20
20
  """Install a pyramid event handler that adds the request information, for backward compatibility."""
21
- warnings.warn("init function is deprecated; use includeme instead")
21
+ warnings.warn("init function is deprecated; use includeme instead", stacklevel=2)
22
22
  includeme(config)
23
23
 
24
24
 
@@ -56,7 +56,7 @@ class _Repository:
56
56
  ]
57
57
  )
58
58
  _LOG.info(output)
59
- except Exception: # nosec # pylint: disable=broad-except
59
+ except Exception: # pylint: disable=broad-except # noqa: S110
60
60
  pass
61
61
 
62
62
 
@@ -11,7 +11,10 @@ from sqlalchemy.exc import SQLAlchemyError
11
11
  from sqlalchemy.orm import sessionmaker
12
12
  from sqlalchemy_utils import create_database, database_exists
13
13
 
14
- from c2cwsgiutils.sqlalchemylogger._filters import ContainsExpression, DoesNotContainExpression
14
+ from c2cwsgiutils.sqlalchemylogger._filters import (
15
+ ContainsExpression,
16
+ DoesNotContainExpression,
17
+ )
15
18
  from c2cwsgiutils.sqlalchemylogger._models import Base, create_log_class
16
19
 
17
20
  _LOG = logging.getLogger(__name__)
@@ -29,12 +32,13 @@ class SQLAlchemyHandler(logging.Handler):
29
32
  does_not_contain_expression: str = "",
30
33
  contains_expression: str = "",
31
34
  ) -> None:
35
+ """Initialize the SQLAlchemyHandler."""
32
36
  super().__init__()
33
37
  # Initialize DB session
34
38
  self.engine = create_engine(sqlalchemy_url["url"])
35
39
  self.Log = create_log_class( # pylint: disable=invalid-name
36
40
  tablename=sqlalchemy_url.get("tablename", "logs"),
37
- tableargs=sqlalchemy_url.get("tableargs", None), # type: ignore
41
+ tableargs=sqlalchemy_url.get("tableargs"), # type: ignore
38
42
  )
39
43
  Base.metadata.bind = self.engine
40
44
  self.session = sessionmaker(bind=self.engine)() # noqa
@@ -61,15 +65,16 @@ class SQLAlchemyHandler(logging.Handler):
61
65
  if not self.log_queue.empty():
62
66
  logs.append(self.log_queue.get())
63
67
  self.log_queue.task_done()
64
- if logs:
65
- # try to reduce the number of INSERT requests to the DB
66
- # by writing chunks of self.MAX_NB_LOGS size,
67
- # but also do not wait forever before writing stuff (self.MAX_TIMOUT)
68
- if (len(logs) >= self.MAX_NB_LOGS) or (
69
- time.perf_counter() >= (time_since_last + self.MAX_TIMEOUT)
70
- ):
71
- self._write_logs(logs)
72
- break
68
+ # try to reduce the number of INSERT requests to the DB
69
+ # by writing chunks of self.MAX_NB_LOGS size,
70
+ # but also do not wait forever before writing stuff (self.MAX_TIMOUT)
71
+ if (
72
+ logs
73
+ and (len(logs) >= self.MAX_NB_LOGS)
74
+ or (time.perf_counter() >= (time_since_last + self.MAX_TIMEOUT))
75
+ ):
76
+ self._write_logs(logs)
77
+ break
73
78
  _LOG.debug("%s: stopping processor thread", __name__)
74
79
 
75
80
  def _write_logs(self, logs: list[Any]) -> None:
@@ -94,7 +99,7 @@ class SQLAlchemyHandler(logging.Handler):
94
99
  _LOG.info("%s: creating new database", __name__)
95
100
  if not database_exists(self.engine.url):
96
101
  create_database(self.engine.url)
97
- # FIXME: we should not access directly the private __table_args__
102
+ # FIXME: we should not access directly the private __table_args__ # pylint: disable=fixme
98
103
  # variable, but add an accessor method in models.Log class
99
104
  if not isinstance(self.Log.__table_args__, type(None)) and self.Log.__table_args__.get(
100
105
  "schema", None
@@ -107,6 +112,7 @@ class SQLAlchemyHandler(logging.Handler):
107
112
  Base.metadata.create_all(self.engine)
108
113
 
109
114
  def emit(self, record: Any) -> None:
115
+ """Emit the log."""
110
116
  trace = None
111
117
  exc = record.__dict__["exc_info"]
112
118
  if exc:
@@ -10,7 +10,7 @@ from c2cwsgiutils.stats_pyramid import _pyramid_spy
10
10
 
11
11
  def init(config: pyramid.config.Configurator) -> None:
12
12
  """Initialize the whole stats module, for backward compatibility."""
13
- warnings.warn("init function is deprecated; use includeme instead")
13
+ warnings.warn("init function is deprecated; use includeme instead", stacklevel=2)
14
14
  includeme(config)
15
15
 
16
16
 
@@ -20,6 +20,7 @@ def includeme(config: pyramid.config.Configurator) -> None:
20
20
 
21
21
  Arguments:
22
22
  config: The Pyramid config
23
+
23
24
  """
24
25
  _pyramid_spy.init(config)
25
26
  init_db_spy()
@@ -99,6 +99,7 @@ def init(config: pyramid.config.Configurator) -> None: # pragma: nocover
99
99
 
100
100
  Arguments:
101
101
  config: The Pyramid config
102
+
102
103
  """
103
104
  config.add_subscriber(_request_callback, pyramid.events.NewRequest)
104
105
  config.add_subscriber(_before_rendered_callback, pyramid.events.BeforeRender)
c2cwsgiutils/version.py CHANGED
@@ -37,7 +37,7 @@ _PROMETHEUS_VERSIONS_INFO = prometheus_client.Gauge(
37
37
 
38
38
  def init(config: pyramid.config.Configurator) -> None:
39
39
  """Initialize the versions view, for backward compatibility."""
40
- warnings.warn("init function is deprecated; use includeme instead")
40
+ warnings.warn("init function is deprecated; use includeme instead", stacklevel=2)
41
41
  includeme(config)
42
42
 
43
43
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: c2cwsgiutils
3
- Version: 6.2.0.dev35
3
+ Version: 6.2.0.dev36
4
4
  Summary: Common utilities for Camptocamp WSGI applications
5
5
  Home-page: https://github.com/camptocamp/c2cwsgiutils
6
6
  License: BSD-2-Clause
@@ -0,0 +1,67 @@
1
+ c2cwsgiutils/__init__.py,sha256=HVSc-4O8i2aB0ozEI4AI8Xsb-4S6fAwhl8uRhv-DsFg,4057
2
+ c2cwsgiutils/acceptance/__init__.py,sha256=5PeqPHxAPMRbZpKk1XlC9RCyotegSsKVfPxADYAgG7w,1502
3
+ c2cwsgiutils/acceptance/connection.py,sha256=A83v335e4s-_cePe-bqRcEHHTOdgguetzdAtVp19-Zk,9834
4
+ c2cwsgiutils/acceptance/image.py,sha256=Tr_cdtQL6WhRq0qIS0P6ulLDwOprbcE1jc1Ft1Qi4OY,8956
5
+ c2cwsgiutils/acceptance/package-lock.json,sha256=P9o-SpVVOwVPzXZy8Tglwkd3xQTQR6eO6XjVRdWw7C4,44497
6
+ c2cwsgiutils/acceptance/package.json,sha256=WEN1DpMNxNroBVG4wExr2Nmf9jyURyZhhGbckrVeoVU,101
7
+ c2cwsgiutils/acceptance/print.py,sha256=DOPDHUP9wfM75qiPYhFI1P3awQKIBH-edO_TmhCjT8k,2568
8
+ c2cwsgiutils/acceptance/screenshot.js,sha256=FAJYIWOLJFMm0MNggKzo3mIybtN-VtKLdMzPhQ9pO1g,2041
9
+ c2cwsgiutils/acceptance/utils.py,sha256=beq_pGJCMBRMmzLERw7z79vc9KtX5gY-g37WiUHa4q8,1901
10
+ c2cwsgiutils/auth.py,sha256=gNJ8VKXPJu_lSlOwxhNSrjjaJ9TzasWIbEpkmYnoauQ,9582
11
+ c2cwsgiutils/broadcast/__init__.py,sha256=9vhFSNUwMqNp73D3LXnQJeLOYB49VUU4KwIoOvDaS3o,4484
12
+ c2cwsgiutils/broadcast/interface.py,sha256=AkDT_m0eXTQp3mmMLf9KRtpXnWoVhJpb_UNjCbHM-3I,665
13
+ c2cwsgiutils/broadcast/local.py,sha256=BZ0iDmU81W8C3Xzs9Dz9jRdTO-WXXvA_7Z5xV6RWoNA,1317
14
+ c2cwsgiutils/broadcast/redis.py,sha256=9FsKxsddf9khYe7aTKou2lmLgXEVcRqVyeFuhxM8-ZM,5378
15
+ c2cwsgiutils/broadcast/utils.py,sha256=0fQZXPu3p_5LEJpGenJwiiMxECQjJhjZBjIkBk8h-ng,272
16
+ c2cwsgiutils/client_info.py,sha256=MIlD88JJ-IL3GwhJsMB73IbsWcglMkdnCyOgDEQG8ns,4004
17
+ c2cwsgiutils/config_utils.py,sha256=vkBu-3GQsE94NOBOvT5FE-Ij29EUrKnDsmdUdtu_yzo,1524
18
+ c2cwsgiutils/coverage_setup.py,sha256=OGsz3bDX8Vcy9K8TBkRb4aQVAhK6618OVV9Az2p3hGA,928
19
+ c2cwsgiutils/db.py,sha256=YvJKo1cXhKcjYwRpUkQbx70YOnOgsYJ5wID5rkLhF6g,16262
20
+ c2cwsgiutils/db_maintenance_view.py,sha256=1y_sfjnHsvl98yzTM24rqmchqGIXDp61KTwraXJZM0s,3102
21
+ c2cwsgiutils/debug/__init__.py,sha256=a2RytL9_DgqiJzc5CCdKd1yF2sSk6SCIVh1gUrWVq1o,1364
22
+ c2cwsgiutils/debug/_listeners.py,sha256=-fk3KFeB_E4m_YFXJ5MfxuqfA1sWCje9p3FH_93WfXM,4248
23
+ c2cwsgiutils/debug/_views.py,sha256=bge_jnylY6oBtWTZjSoDByLbx3MjapY4JiZu97xQoWU,8518
24
+ c2cwsgiutils/debug/utils.py,sha256=sIKZHQ8empzxE2OI3h7Uce8cvv4O7AD1y_VYeZfLVCA,2320
25
+ c2cwsgiutils/errors.py,sha256=miNVnEX9qBtwaRnny_k9IcBbbw-8vC3unFumi2w5Q9w,6679
26
+ c2cwsgiutils/health_check.py,sha256=Wi3JQCKa7tqACuSgBi13oE69Xc-Vz1nheqBaxfOGXQU,20141
27
+ c2cwsgiutils/index.py,sha256=ag7A8HJ9MgU72etDBuFMaci-xKjOD9R8OOm3UMEoQJY,18002
28
+ c2cwsgiutils/loader.py,sha256=vU7yEobl9TlSFZwdLI2eF1ceUJ6xgiu2re31HA5sw1g,623
29
+ c2cwsgiutils/logging_view.py,sha256=wa2hCjGKkpRwrCA4K6Zk1Z3hPGUMB6SJAnki-_lLY00,3384
30
+ c2cwsgiutils/models_graph.py,sha256=AROTzCHJCjjOiOmQj_Hk40WQdIPaHFLgN4Jech1aUzc,2683
31
+ c2cwsgiutils/pretty_json.py,sha256=houw_oeU63CgzQTPlBrrYzinpE-UtqPeYLfTXdLHyu0,1711
32
+ c2cwsgiutils/profiler.py,sha256=3902yp1rHpSNpsYKmqs_IrEpFg9P2QqNtjyolk53ZgQ,778
33
+ c2cwsgiutils/prometheus.py,sha256=XIVnsI9zcIZsg0-tqrDSiHClDwdr3l5KMkmpFKyfUXk,6635
34
+ c2cwsgiutils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
+ c2cwsgiutils/pyramid.py,sha256=kSCsBG5IPd0-tsXwdiJ3cj_yj-AxQYFusvTxPE6AaeQ,1388
36
+ c2cwsgiutils/pyramid_logging.py,sha256=U1MD7xEAGH7QdX7FA6O12eHhzMx76i1WCXtf_EiJgVE,3911
37
+ c2cwsgiutils/redis_stats.py,sha256=yUVVNGR_rIecH4uRfRWMySERCTYMWtJhSXAs0iK4TAs,1646
38
+ c2cwsgiutils/redis_utils.py,sha256=kY1QbZWVnUI-bxCeJOxn5VBfna2hS1oUQxx9HEws_ww,4813
39
+ c2cwsgiutils/request_tracking/__init__.py,sha256=ZeCxPJNrUxKLDmV5bOs0zmD9vx0FFIPs7T3xDNG-5BU,4146
40
+ c2cwsgiutils/request_tracking/_sql.py,sha256=ZOQ3hD5z-WasfBzNig75fjrCvw4tGr1rhWOv97iLYws,572
41
+ c2cwsgiutils/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
+ c2cwsgiutils/scripts/genversion.py,sha256=SZswHR6qbeErcxn5d5yAa4QPnw9z4LCeP9GOweTj16g,2014
43
+ c2cwsgiutils/scripts/stats_db.py,sha256=EaI1WAHGKj-sUWpznIOs8yMlgx2pnzwAksA8hA-nF28,10452
44
+ c2cwsgiutils/scripts/test_print.py,sha256=pLqqVZ9cVvjdD3MdJQ7Ul8i5OIBFvo1Gnr78rdH0Dh4,2130
45
+ c2cwsgiutils/sentry.py,sha256=ZnWo0-8xgwWxDG_wo9gI8rDtZlKgcZXorG3b0tU5Hh0,6508
46
+ c2cwsgiutils/services.py,sha256=AxdiH2S8gfEQpOY36R4WqKtJ-3P4OXhx0srTm5G5070,1569
47
+ c2cwsgiutils/setup_process.py,sha256=19p-0ZRZQ08S6-hkZvAq_VJjtrLeWxsdoX-afRLJapg,3454
48
+ c2cwsgiutils/sql_profiler/__init__.py,sha256=YR8003lCNNGC2eZdyIt5gt76-1frG5szw8IYE_EsQB4,921
49
+ c2cwsgiutils/sql_profiler/_impl.py,sha256=OdhAn-5x84Bz5RCldhgjX50b6yxSLnLh5nydNgHvgGc,3801
50
+ c2cwsgiutils/sqlalchemylogger/README.md,sha256=qXfyhGC4Kbt-Ye_geTPm_1InpclhthmKZpc66rqB4as,2018
51
+ c2cwsgiutils/sqlalchemylogger/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
+ c2cwsgiutils/sqlalchemylogger/_filters.py,sha256=OJQ9_WA-fd9fMZ7TUNFzHHTPI6msw2NVBl5RoeYFnGw,752
53
+ c2cwsgiutils/sqlalchemylogger/_models.py,sha256=A9SQ8AqUazCMemVjp5p_1x4bZG3LAYW9pOXT84FdNkE,1471
54
+ c2cwsgiutils/sqlalchemylogger/examples/example.py,sha256=n48dJdUi1FH1hfBMAbfHLGPSb1bOVD8pXMxXB57PnpQ,460
55
+ c2cwsgiutils/sqlalchemylogger/handlers.py,sha256=hDDWmiV6ksEcRCJlqIdQW3P9hq2BTC70wrLmY0vYkyA,4979
56
+ c2cwsgiutils/static/favicon-16x16.png,sha256=LKk6RFvb3NlPIZdDfAodE8H9IN8KM6CMGnMx4vOHlUQ,887
57
+ c2cwsgiutils/static/favicon-32x32.png,sha256=i4ucx08zAZARd8e7JTMGK-gb5WcOmyuDN6IN4brsEOo,1216
58
+ c2cwsgiutils/stats_pyramid/__init__.py,sha256=0NizIUbaPbNf6bPye7pGrF9AaFerIN30EhEMts1XgfY,801
59
+ c2cwsgiutils/stats_pyramid/_db_spy.py,sha256=A61t6VKIrRRIjbyZTldmAUl_Q3ZDVFYqyxjuntzmllc,2919
60
+ c2cwsgiutils/stats_pyramid/_pyramid_spy.py,sha256=GnJRlLSAPl4PwQH5jfmc2OX5Hz9MSRRVJT2VcqkniVE,3518
61
+ c2cwsgiutils/templates/index.html.mako,sha256=cK8qGBDeQG5SiJJCfvL0oKpgacr7dPKx634AAQivmjA,1416
62
+ c2cwsgiutils/version.py,sha256=7H3URblj26Ql0bL3eXtP0LSRBeW4HbEsQ8O_BfWNr90,3124
63
+ c2cwsgiutils-6.2.0.dev36.dist-info/LICENSE,sha256=r7ueGz9Fl2Bv3rmeQy0DEtohLmAiufRaCuv6Y5fyNhE,1304
64
+ c2cwsgiutils-6.2.0.dev36.dist-info/METADATA,sha256=s18TzIHXyoYwmI7W0eKRRh2r9Ed4DNMxAUAntveyU20,34461
65
+ c2cwsgiutils-6.2.0.dev36.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
66
+ c2cwsgiutils-6.2.0.dev36.dist-info/entry_points.txt,sha256=ujgqMTL1awN9qDg8WXmrF7m0fgR-hslUM6zKH86pvy0,703
67
+ c2cwsgiutils-6.2.0.dev36.dist-info/RECORD,,
@@ -1,67 +0,0 @@
1
- c2cwsgiutils/__init__.py,sha256=HVSc-4O8i2aB0ozEI4AI8Xsb-4S6fAwhl8uRhv-DsFg,4057
2
- c2cwsgiutils/acceptance/__init__.py,sha256=TJA1yzmyPujkg80oj-LBj2ueOQVYL8HLW87pejWTIDY,1501
3
- c2cwsgiutils/acceptance/connection.py,sha256=yqChhHBpYhQL0Cb7K8FqeP16jg1UtmxGAi6Tw1TXEbI,9783
4
- c2cwsgiutils/acceptance/image.py,sha256=Xtwvb1Y-Pczbw9kHn5OhJokogwbBvx5McLvcuGPzzog,8935
5
- c2cwsgiutils/acceptance/package-lock.json,sha256=LXmN7R3u55orpjALAmrLsDKC2ThlepP19-tjV5l7N9E,44497
6
- c2cwsgiutils/acceptance/package.json,sha256=tIRXzcx4H7EtS4b6cKqnirg9fQx0tks0pDE3F0I_TS0,101
7
- c2cwsgiutils/acceptance/print.py,sha256=qdh6pqlHgkIjUCJxS3rcgpOV4fDk9RxFlkfH5aAwDsQ,2567
8
- c2cwsgiutils/acceptance/screenshot.js,sha256=FAJYIWOLJFMm0MNggKzo3mIybtN-VtKLdMzPhQ9pO1g,2041
9
- c2cwsgiutils/acceptance/utils.py,sha256=zLvWqqPLBGCzGAtmIqidao66BKmER_Du1AfKCEhoc-I,1892
10
- c2cwsgiutils/auth.py,sha256=ljTUPYGpEkkDg6ifOegXS1Q1JlcRth0P5vbMCs7VK6k,9585
11
- c2cwsgiutils/broadcast/__init__.py,sha256=mYiTVL34QkJkEjxURlDIE7bmwZaEPT9Lw9zMwbiVtL0,4470
12
- c2cwsgiutils/broadcast/interface.py,sha256=AkDT_m0eXTQp3mmMLf9KRtpXnWoVhJpb_UNjCbHM-3I,665
13
- c2cwsgiutils/broadcast/local.py,sha256=kQp6OWcRU-nx7uEBAXwdMum_rOz3LSj6fyL_R12QrMY,1139
14
- c2cwsgiutils/broadcast/redis.py,sha256=xCcGSr7jr6QdFM-YZzFFaySQis1xKb0HqqbGR8qjSNM,5202
15
- c2cwsgiutils/broadcast/utils.py,sha256=0fQZXPu3p_5LEJpGenJwiiMxECQjJhjZBjIkBk8h-ng,272
16
- c2cwsgiutils/client_info.py,sha256=rvXQLyhma0kPoWQINwUzrxfG2gGIjQDNrxPUDRGJxpA,3916
17
- c2cwsgiutils/config_utils.py,sha256=vkBu-3GQsE94NOBOvT5FE-Ij29EUrKnDsmdUdtu_yzo,1524
18
- c2cwsgiutils/coverage_setup.py,sha256=BrdjYUmqYl1C-gHuKA7EI5gbQs8Dtdeb2eZKtzr-5L0,909
19
- c2cwsgiutils/db.py,sha256=JT5F9Dqm2r0Jsh3w3CX79ngAUtakMLpf1secfN1nQnk,16106
20
- c2cwsgiutils/db_maintenance_view.py,sha256=58F-p9drkhCI99GoLRPIqT5U-Pm8ckSSUEl-tNxMmjU,3088
21
- c2cwsgiutils/debug/__init__.py,sha256=GkYNt2fU5PFykw9HmqPEwZrF2mTJumjSu54pp38EhOM,1325
22
- c2cwsgiutils/debug/_listeners.py,sha256=-fk3KFeB_E4m_YFXJ5MfxuqfA1sWCje9p3FH_93WfXM,4248
23
- c2cwsgiutils/debug/_views.py,sha256=s4SmrfCRhbfyVtLd7CPFQROQJjtf4PrfSkBH-cEtEPQ,8541
24
- c2cwsgiutils/debug/utils.py,sha256=sIKZHQ8empzxE2OI3h7Uce8cvv4O7AD1y_VYeZfLVCA,2320
25
- c2cwsgiutils/errors.py,sha256=-hWLQ1qDlh9kn06-33U2c39BbOxuCvJIkBkdxriE5mo,6644
26
- c2cwsgiutils/health_check.py,sha256=OhfPcApBht1qtBOX8e8pdeq3QwF4FbgGkofjqpl8GvQ,20068
27
- c2cwsgiutils/index.py,sha256=zQTp2dlb50DZy0TZOPYDxJv84kivcYirswz7rFMbSDA,17988
28
- c2cwsgiutils/loader.py,sha256=vU7yEobl9TlSFZwdLI2eF1ceUJ6xgiu2re31HA5sw1g,623
29
- c2cwsgiutils/logging_view.py,sha256=d0UkvYRGkVUMY9_vbjEzXmm8-6CCec2B43a3mJAqWyw,3370
30
- c2cwsgiutils/models_graph.py,sha256=q5dW_gZ5iUZCzBya5Kpy57y9oqsG-rGi9AvrXCDgYqs,2679
31
- c2cwsgiutils/pretty_json.py,sha256=WQlgNVeWPD_QMEjkNq5rFVGdFwQ7xDyICf0uxj0Hu2U,1697
32
- c2cwsgiutils/profiler.py,sha256=3tIwoDSzOKQ06ug_U6j5VDR1BQ9auUOqdJRRLRhDoHw,739
33
- c2cwsgiutils/prometheus.py,sha256=XV_SSm0nT09MD8EEl2a4xtd2busO3r--JyboU9OvWaQ,6576
34
- c2cwsgiutils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- c2cwsgiutils/pyramid.py,sha256=MnVMskHMlJ4KQDjDR7CQdYaH70YXOtJ-1ThTPBqa03c,1387
36
- c2cwsgiutils/pyramid_logging.py,sha256=M4XtWQZStreEoyC5qlwxcDDKCp4PZOr9SN05GnaYvvA,3732
37
- c2cwsgiutils/redis_stats.py,sha256=8OTVRcElESgM6qjN944MOjoYRb0FGcQgo3JmDglfpPw,1632
38
- c2cwsgiutils/redis_utils.py,sha256=02QoBGSjfBwKsWk-4DrXT8119HuUJuV_ezW7bc-Rv8s,4734
39
- c2cwsgiutils/request_tracking/__init__.py,sha256=9Fp505q5zKjqfm9MuM7BDiYsx_pdg4vViNAr43OWYuk,4132
40
- c2cwsgiutils/request_tracking/_sql.py,sha256=ZOQ3hD5z-WasfBzNig75fjrCvw4tGr1rhWOv97iLYws,572
41
- c2cwsgiutils/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
- c2cwsgiutils/scripts/genversion.py,sha256=lpGqU5wNSKdoOMDwVjFvWJVPEzdk0P8Tpfk7NLcrTok,1990
43
- c2cwsgiutils/scripts/stats_db.py,sha256=kGCWBpG6gp_yDH3eZwEOujrvlyM0g97po6-EyjV8SXU,10413
44
- c2cwsgiutils/scripts/test_print.py,sha256=NSXc0a2DZvKXjZYc2yr6oF72PCpeyL3GJthNFxGErEA,2101
45
- c2cwsgiutils/sentry.py,sha256=su_t2SHxx_zK-gQfotmDbgbSdwTQdFvkgJnqcrQU7ps,6494
46
- c2cwsgiutils/services.py,sha256=AxdiH2S8gfEQpOY36R4WqKtJ-3P4OXhx0srTm5G5070,1569
47
- c2cwsgiutils/setup_process.py,sha256=VSiyVaQ65btIEBql1sBCZpOjCr0QQjRbcVDY2I7GbLM,3426
48
- c2cwsgiutils/sql_profiler/__init__.py,sha256=2bh5s4xqPLUl7EPnm75R1RDKtc098PgZLHkwHGfS-94,907
49
- c2cwsgiutils/sql_profiler/_impl.py,sha256=6qkXMhDGcheCDwOH09gFJDrtXRsngkvm29icZWZMtMI,3797
50
- c2cwsgiutils/sqlalchemylogger/README.md,sha256=qXfyhGC4Kbt-Ye_geTPm_1InpclhthmKZpc66rqB4as,2018
51
- c2cwsgiutils/sqlalchemylogger/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
- c2cwsgiutils/sqlalchemylogger/_filters.py,sha256=OJQ9_WA-fd9fMZ7TUNFzHHTPI6msw2NVBl5RoeYFnGw,752
53
- c2cwsgiutils/sqlalchemylogger/_models.py,sha256=A9SQ8AqUazCMemVjp5p_1x4bZG3LAYW9pOXT84FdNkE,1471
54
- c2cwsgiutils/sqlalchemylogger/examples/example.py,sha256=n48dJdUi1FH1hfBMAbfHLGPSb1bOVD8pXMxXB57PnpQ,460
55
- c2cwsgiutils/sqlalchemylogger/handlers.py,sha256=nr9-eQsZ5d0DHbtc4Ym0_faa7qg1dF44CsdoZtuuRZM,4878
56
- c2cwsgiutils/static/favicon-16x16.png,sha256=LKk6RFvb3NlPIZdDfAodE8H9IN8KM6CMGnMx4vOHlUQ,887
57
- c2cwsgiutils/static/favicon-32x32.png,sha256=i4ucx08zAZARd8e7JTMGK-gb5WcOmyuDN6IN4brsEOo,1216
58
- c2cwsgiutils/stats_pyramid/__init__.py,sha256=alSRhpCa5Kh9JnMnR5XqcMqr5wyL8ogROprrfsIl_qU,786
59
- c2cwsgiutils/stats_pyramid/_db_spy.py,sha256=A61t6VKIrRRIjbyZTldmAUl_Q3ZDVFYqyxjuntzmllc,2919
60
- c2cwsgiutils/stats_pyramid/_pyramid_spy.py,sha256=mRiOmQXV9x8JjkGV4MsaC7sD3qO6dWUTog0bharLLD0,3517
61
- c2cwsgiutils/templates/index.html.mako,sha256=cK8qGBDeQG5SiJJCfvL0oKpgacr7dPKx634AAQivmjA,1416
62
- c2cwsgiutils/version.py,sha256=1ghPu-aKMJdfCSUrxgBENNqNQ-7JMKJr6tS14dDmW4Q,3110
63
- c2cwsgiutils-6.2.0.dev35.dist-info/LICENSE,sha256=r7ueGz9Fl2Bv3rmeQy0DEtohLmAiufRaCuv6Y5fyNhE,1304
64
- c2cwsgiutils-6.2.0.dev35.dist-info/METADATA,sha256=A343_tmKTMJA3oVgG7VaaviJwWYggXUjJ9z0OUx74PA,34461
65
- c2cwsgiutils-6.2.0.dev35.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
66
- c2cwsgiutils-6.2.0.dev35.dist-info/entry_points.txt,sha256=ujgqMTL1awN9qDg8WXmrF7m0fgR-hslUM6zKH86pvy0,703
67
- c2cwsgiutils-6.2.0.dev35.dist-info/RECORD,,