c2cwsgiutils 6.2.0.dev74__py3-none-any.whl → 6.2.0.dev75__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 (52) hide show
  1. c2cwsgiutils/__init__.py +4 -4
  2. c2cwsgiutils/acceptance/__init__.py +4 -1
  3. c2cwsgiutils/acceptance/connection.py +45 -32
  4. c2cwsgiutils/acceptance/image.py +45 -39
  5. c2cwsgiutils/acceptance/print.py +2 -2
  6. c2cwsgiutils/acceptance/utils.py +4 -4
  7. c2cwsgiutils/auth.py +20 -13
  8. c2cwsgiutils/broadcast/__init__.py +25 -16
  9. c2cwsgiutils/broadcast/interface.py +8 -4
  10. c2cwsgiutils/broadcast/local.py +9 -4
  11. c2cwsgiutils/broadcast/redis.py +19 -12
  12. c2cwsgiutils/client_info.py +3 -2
  13. c2cwsgiutils/config_utils.py +14 -10
  14. c2cwsgiutils/coverage_setup.py +5 -5
  15. c2cwsgiutils/db.py +54 -47
  16. c2cwsgiutils/db_maintenance_view.py +7 -8
  17. c2cwsgiutils/debug/__init__.py +1 -2
  18. c2cwsgiutils/debug/_listeners.py +6 -4
  19. c2cwsgiutils/debug/_views.py +15 -10
  20. c2cwsgiutils/debug/utils.py +5 -5
  21. c2cwsgiutils/errors.py +15 -10
  22. c2cwsgiutils/health_check.py +79 -60
  23. c2cwsgiutils/index.py +35 -37
  24. c2cwsgiutils/loader.py +5 -4
  25. c2cwsgiutils/logging_view.py +13 -6
  26. c2cwsgiutils/models_graph.py +5 -5
  27. c2cwsgiutils/pretty_json.py +3 -2
  28. c2cwsgiutils/profiler.py +1 -2
  29. c2cwsgiutils/prometheus.py +6 -6
  30. c2cwsgiutils/pyramid.py +1 -1
  31. c2cwsgiutils/pyramid_logging.py +9 -4
  32. c2cwsgiutils/redis_stats.py +12 -7
  33. c2cwsgiutils/redis_utils.py +18 -10
  34. c2cwsgiutils/request_tracking/__init__.py +20 -12
  35. c2cwsgiutils/request_tracking/_sql.py +2 -1
  36. c2cwsgiutils/scripts/genversion.py +10 -9
  37. c2cwsgiutils/scripts/stats_db.py +29 -13
  38. c2cwsgiutils/sentry.py +44 -20
  39. c2cwsgiutils/setup_process.py +7 -4
  40. c2cwsgiutils/sql_profiler/_impl.py +12 -11
  41. c2cwsgiutils/sqlalchemylogger/_models.py +3 -3
  42. c2cwsgiutils/sqlalchemylogger/examples/__init__.py +0 -0
  43. c2cwsgiutils/sqlalchemylogger/handlers.py +8 -9
  44. c2cwsgiutils/stats_pyramid/_db_spy.py +12 -3
  45. c2cwsgiutils/stats_pyramid/_pyramid_spy.py +10 -9
  46. c2cwsgiutils/version.py +10 -7
  47. {c2cwsgiutils-6.2.0.dev74.dist-info → c2cwsgiutils-6.2.0.dev75.dist-info}/METADATA +1 -1
  48. c2cwsgiutils-6.2.0.dev75.dist-info/RECORD +68 -0
  49. c2cwsgiutils-6.2.0.dev74.dist-info/RECORD +0 -67
  50. {c2cwsgiutils-6.2.0.dev74.dist-info → c2cwsgiutils-6.2.0.dev75.dist-info}/LICENSE +0 -0
  51. {c2cwsgiutils-6.2.0.dev74.dist-info → c2cwsgiutils-6.2.0.dev75.dist-info}/WHEEL +0 -0
  52. {c2cwsgiutils-6.2.0.dev74.dist-info → c2cwsgiutils-6.2.0.dev75.dist-info}/entry_points.txt +0 -0
c2cwsgiutils/__init__.py CHANGED
@@ -35,7 +35,8 @@ def _create_handlers(config: configparser.ConfigParser) -> dict[str, Any]:
35
35
  for hh in handlers:
36
36
  block = config[f"handler_{hh}"]
37
37
  if "args" in block:
38
- raise ValueError(f"Can not parse args of handlers {hh}, use kwargs instead.")
38
+ message = f"Can not parse args of handlers {hh}, use kwargs instead."
39
+ raise ValueError(message)
39
40
  c = block["class"]
40
41
  if "." not in c:
41
42
  # classes like StreamHandler does not need the prefix in the ini so we add it here
@@ -116,10 +117,9 @@ def get_paste_config() -> str:
116
117
  for val in sys.argv:
117
118
  if next_one:
118
119
  return val
119
- if val.startswith("--paste=") or val.startswith("--paster="):
120
+ if val.startswith(("--paste=", "--paster=")):
120
121
  return val.split("=")[1]
121
122
  if val in ["--paste", "--paster"]:
122
123
  next_one = True
123
124
 
124
- fallback = os.environ.get("C2CWSGIUTILS_CONFIG", "production.ini")
125
- return fallback
125
+ return os.environ.get("C2CWSGIUTILS_CONFIG", "production.ini")
@@ -7,7 +7,10 @@ _LOG = logging.getLogger(__name__)
7
7
 
8
8
 
9
9
  def retry(
10
- exception_to_check: typing.Any, tries: float = 3, delay: float = 0.5, backoff: float = 2
10
+ exception_to_check: typing.Any,
11
+ tries: float = 3,
12
+ delay: float = 0.5,
13
+ backoff: float = 2,
11
14
  ) -> typing.Callable[..., typing.Any]:
12
15
  """
13
16
  Retry calling the decorated function using an exponential backoff.
@@ -1,7 +1,8 @@
1
1
  import re
2
2
  from collections.abc import Mapping, MutableMapping
3
3
  from enum import Enum
4
- from typing import Any, Optional, Union
4
+ from pathlib import Path
5
+ from typing import Any
5
6
 
6
7
  import requests
7
8
 
@@ -32,10 +33,10 @@ class Connection:
32
33
  url: str,
33
34
  expected_status: int = 200,
34
35
  cors: bool = True,
35
- headers: Optional[Mapping[str, str]] = None,
36
+ headers: Mapping[str, str] | None = None,
36
37
  cache_expected: CacheExpected = CacheExpected.NO,
37
38
  **kwargs: Any,
38
- ) -> Optional[str]:
39
+ ) -> str | None:
39
40
  """Get the given URL (relative to the root of API)."""
40
41
  with self.session.get(self.base_url + url, headers=self._merge_headers(headers, cors), **kwargs) as r:
41
42
  check_response(r, expected_status, cache_expected=cache_expected)
@@ -46,7 +47,7 @@ class Connection:
46
47
  self,
47
48
  url: str,
48
49
  expected_status: int = 200,
49
- headers: Optional[Mapping[str, str]] = None,
50
+ headers: Mapping[str, str] | None = None,
50
51
  cors: bool = True,
51
52
  cache_expected: CacheExpected = CacheExpected.NO,
52
53
  **kwargs: Any,
@@ -61,7 +62,7 @@ class Connection:
61
62
  self,
62
63
  url: str,
63
64
  expected_status: int = 200,
64
- headers: Optional[Mapping[str, str]] = None,
65
+ headers: Mapping[str, str] | None = None,
65
66
  cors: bool = True,
66
67
  cache_expected: CacheExpected = CacheExpected.NO,
67
68
  **kwargs: Any,
@@ -75,9 +76,9 @@ class Connection:
75
76
  def get_xml(
76
77
  self,
77
78
  url: str,
78
- schema: Optional[str] = None,
79
+ schema: Path | None = None,
79
80
  expected_status: int = 200,
80
- headers: Optional[Mapping[str, str]] = None,
81
+ headers: Mapping[str, str] | None = None,
81
82
  cors: bool = True,
82
83
  cache_expected: CacheExpected = CacheExpected.NO,
83
84
  **kwargs: Any,
@@ -96,7 +97,7 @@ class Connection:
96
97
  r.raw.decode_content = True
97
98
  doc = etree.parse(r.raw) # noqa: S320
98
99
  if schema is not None:
99
- with open(schema, encoding="utf-8") as schema_file:
100
+ with schema.open(encoding="utf-8") as schema_file:
100
101
  xml_schema = etree.XMLSchema(etree.parse(schema_file)) # noqa: S320
101
102
  xml_schema.assertValid(doc)
102
103
  return doc
@@ -105,14 +106,16 @@ class Connection:
105
106
  self,
106
107
  url: str,
107
108
  expected_status: int = 200,
108
- headers: Optional[Mapping[str, str]] = None,
109
+ headers: Mapping[str, str] | None = None,
109
110
  cors: bool = True,
110
111
  cache_expected: CacheExpected = CacheExpected.NO,
111
112
  **kwargs: Any,
112
113
  ) -> Any:
113
114
  """POST the given URL (relative to the root of API)."""
114
115
  with self.session.post(
115
- self.base_url + url, headers=self._merge_headers(headers, cors), **kwargs
116
+ self.base_url + url,
117
+ headers=self._merge_headers(headers, cors),
118
+ **kwargs,
116
119
  ) as r:
117
120
  check_response(r, expected_status, cache_expected=cache_expected)
118
121
  self._check_cors(cors, r)
@@ -122,14 +125,16 @@ class Connection:
122
125
  self,
123
126
  url: str,
124
127
  expected_status: int = 200,
125
- headers: Optional[Mapping[str, str]] = None,
128
+ headers: Mapping[str, str] | None = None,
126
129
  cors: bool = True,
127
130
  cache_expected: CacheExpected = CacheExpected.NO,
128
131
  **kwargs: Any,
129
132
  ) -> Any:
130
133
  """POST files to the the given URL (relative to the root of API)."""
131
134
  with self.session.post(
132
- self.base_url + url, headers=self._merge_headers(headers, cors), **kwargs
135
+ self.base_url + url,
136
+ headers=self._merge_headers(headers, cors),
137
+ **kwargs,
133
138
  ) as r:
134
139
  check_response(r, expected_status, cache_expected)
135
140
  self._check_cors(cors, r)
@@ -139,14 +144,16 @@ class Connection:
139
144
  self,
140
145
  url: str,
141
146
  expected_status: int = 200,
142
- headers: Optional[Mapping[str, str]] = None,
147
+ headers: Mapping[str, str] | None = None,
143
148
  cors: bool = True,
144
149
  cache_expected: CacheExpected = CacheExpected.NO,
145
150
  **kwargs: Any,
146
- ) -> Optional[str]:
151
+ ) -> str | None:
147
152
  """POST the given URL (relative to the root of API)."""
148
153
  with self.session.post(
149
- self.base_url + url, headers=self._merge_headers(headers, cors), **kwargs
154
+ self.base_url + url,
155
+ headers=self._merge_headers(headers, cors),
156
+ **kwargs,
150
157
  ) as r:
151
158
  check_response(r, expected_status, cache_expected)
152
159
  self._check_cors(cors, r)
@@ -156,7 +163,7 @@ class Connection:
156
163
  self,
157
164
  url: str,
158
165
  expected_status: int = 200,
159
- headers: Optional[Mapping[str, str]] = None,
166
+ headers: Mapping[str, str] | None = None,
160
167
  cors: bool = True,
161
168
  cache_expected: CacheExpected = CacheExpected.NO,
162
169
  **kwargs: Any,
@@ -171,14 +178,16 @@ class Connection:
171
178
  self,
172
179
  url: str,
173
180
  expected_status: int = 200,
174
- headers: Optional[Mapping[str, str]] = None,
181
+ headers: Mapping[str, str] | None = None,
175
182
  cors: bool = True,
176
183
  cache_expected: CacheExpected = CacheExpected.NO,
177
184
  **kwargs: Any,
178
185
  ) -> Any:
179
186
  """PATCH the given URL (relative to the root of API)."""
180
187
  with self.session.patch(
181
- self.base_url + url, headers=self._merge_headers(headers, cors), **kwargs
188
+ self.base_url + url,
189
+ headers=self._merge_headers(headers, cors),
190
+ **kwargs,
182
191
  ) as r:
183
192
  check_response(r, expected_status, cache_expected)
184
193
  self._check_cors(cors, r)
@@ -188,14 +197,16 @@ class Connection:
188
197
  self,
189
198
  url: str,
190
199
  expected_status: int = 204,
191
- headers: Optional[Mapping[str, str]] = None,
200
+ headers: Mapping[str, str] | None = None,
192
201
  cors: bool = True,
193
202
  cache_expected: CacheExpected = CacheExpected.NO,
194
203
  **kwargs: Any,
195
204
  ) -> requests.Response:
196
205
  """DELETE the given URL (relative to the root of API)."""
197
206
  with self.session.delete(
198
- self.base_url + url, headers=self._merge_headers(headers, cors), **kwargs
207
+ self.base_url + url,
208
+ headers=self._merge_headers(headers, cors),
209
+ **kwargs,
199
210
  ) as r:
200
211
  check_response(r, expected_status, cache_expected)
201
212
  self._check_cors(cors, r)
@@ -205,13 +216,15 @@ class Connection:
205
216
  self,
206
217
  url: str,
207
218
  expected_status: int = 200,
208
- headers: Optional[Mapping[str, str]] = None,
219
+ headers: Mapping[str, str] | None = None,
209
220
  cache_expected: CacheExpected = CacheExpected.NO,
210
221
  **kwargs: Any,
211
222
  ) -> requests.Response:
212
223
  """Get the given URL (relative to the root of API)."""
213
224
  with self.session.options(
214
- self.base_url + url, headers=self._merge_headers(headers, False), **kwargs
225
+ self.base_url + url,
226
+ headers=self._merge_headers(headers, cors=False),
227
+ **kwargs,
215
228
  ) as r:
216
229
  check_response(r, expected_status, cache_expected=cache_expected)
217
230
  return r
@@ -219,8 +232,7 @@ class Connection:
219
232
  def _cors_headers(self, cors: bool) -> Mapping[str, str]:
220
233
  if cors:
221
234
  return {"Origin": self.origin}
222
- else:
223
- return {}
235
+ return {}
224
236
 
225
237
  def _check_cors(self, cors: bool, r: requests.Response) -> None:
226
238
  if cors:
@@ -230,8 +242,10 @@ class Connection:
230
242
  assert r.headers["Access-Control-Allow-Origin"] == "*"
231
243
 
232
244
  def _merge_headers(
233
- self, headers: Optional[Mapping[str, Union[str, bytes]]], cors: bool
234
- ) -> MutableMapping[str, Union[str, bytes]]:
245
+ self,
246
+ headers: Mapping[str, str | bytes] | None,
247
+ cors: bool,
248
+ ) -> MutableMapping[str, str | bytes]:
235
249
  merged = dict(headers) if headers is not None else {}
236
250
  if self.session.headers is not None:
237
251
  merged.update(self.session.headers)
@@ -265,9 +279,8 @@ def check_response(
265
279
  def _get_json(r: requests.Response) -> Any:
266
280
  if r.status_code == 204:
267
281
  return None
268
- else:
269
- content_type = r.headers["Content-Type"].split(";")[0]
270
- assert content_type == "application/json" or content_type.endswith("+json"), (
271
- f"{r.status_code}, {content_type}, {r.text}"
272
- )
273
- return r.json()
282
+ content_type = r.headers["Content-Type"].split(";")[0]
283
+ assert content_type == "application/json" or content_type.endswith("+json"), (
284
+ f"{r.status_code}, {content_type}, {r.text}"
285
+ )
286
+ return r.json()
@@ -1,7 +1,7 @@
1
1
  import json
2
- import os
3
2
  import subprocess # nosec
4
- from typing import TYPE_CHECKING, Any, Optional
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING, Any
5
5
 
6
6
  import numpy as np # pylint: disable=import-error
7
7
  import skimage.color # pylint: disable=import-error
@@ -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_extensions import TypeAlias
13
+ from typing import TypeAlias
14
14
 
15
15
  NpNdarrayInt: TypeAlias = np.ndarray[np.uint8, Any]
16
16
  else:
@@ -18,9 +18,9 @@ else:
18
18
 
19
19
 
20
20
  def check_image_file(
21
- result_folder: str,
22
- image_filename_to_check: str,
23
- expected_filename: str,
21
+ result_folder: str | Path,
22
+ image_filename_to_check: str | Path,
23
+ expected_filename: str | Path,
24
24
  level: float = 1.0,
25
25
  generate_expected_image: bool = False,
26
26
  use_mask: bool = True,
@@ -40,7 +40,7 @@ def check_image_file(
40
40
  we use it as a mask.
41
41
  """
42
42
  result = skimage.io.imread(image_filename_to_check)
43
- assert result is not None, "Wrong image: " + image_filename_to_check
43
+ assert result is not None, f"Wrong image: {image_filename_to_check}"
44
44
  check_image(result_folder, result, expected_filename, level, generate_expected_image, use_mask)
45
45
 
46
46
 
@@ -59,9 +59,9 @@ def normalize_image(image: NpNdarrayInt) -> NpNdarrayInt:
59
59
 
60
60
 
61
61
  def check_image( # pylint: disable=too-many-locals,too-many-statements
62
- result_folder: str,
62
+ result_folder: str | Path,
63
63
  image_to_check: NpNdarrayInt,
64
- expected_filename: str,
64
+ expected_filename: str | Path,
65
65
  level: float = 1.0,
66
66
  generate_expected_image: bool = False,
67
67
  use_mask: bool = True,
@@ -92,18 +92,18 @@ def check_image( # pylint: disable=too-many-locals,too-many-statements
92
92
 
93
93
  """
94
94
  assert image_to_check is not None, "Image required"
95
- image_file_basename = os.path.splitext(os.path.basename(expected_filename))[0]
96
- mask_filename: Optional[str] = None
95
+ result_folder = Path(result_folder)
96
+ expected_filename = Path(expected_filename)
97
+ image_file_basename = expected_filename.stem
98
+ mask_filename: Path | None = None
97
99
  if image_file_basename.endswith(".expected"):
98
- image_file_basename = os.path.splitext(image_file_basename)[0]
100
+ image_file_basename = Path(image_file_basename).stem
99
101
  if use_mask:
100
- mask_filename = os.path.join(
101
- os.path.dirname(expected_filename), image_file_basename + ".mask.png"
102
- )
103
- if not os.path.isfile(mask_filename):
102
+ mask_filename = expected_filename.with_name(image_file_basename + ".mask.png")
103
+ if not mask_filename.is_file():
104
104
  mask_filename = None
105
- result_filename = os.path.join(result_folder, f"{image_file_basename}.actual-masked.png")
106
- diff_filename = os.path.join(result_folder, f"{image_file_basename}.diff.png")
105
+ result_filename = result_folder / f"{image_file_basename}.actual-masked.png"
106
+ diff_filename = result_folder / f"{image_file_basename}.diff.png"
107
107
 
108
108
  image_to_check = normalize_image(image_to_check)
109
109
 
@@ -112,13 +112,15 @@ def check_image( # pylint: disable=too-many-locals,too-many-statements
112
112
  background_color = [255, 255, 255]
113
113
  for color in range(3):
114
114
  img_hist, _ = skimage.exposure.histogram(
115
- image_to_check[..., color], nbins=256, source_range="dtype"
115
+ image_to_check[..., color],
116
+ nbins=256,
117
+ source_range="dtype",
116
118
  )
117
119
  background_color[color] = np.argmax(img_hist)
118
120
 
119
121
  mask = skimage.io.imread(mask_filename)
120
122
 
121
- assert mask is not None, "Wrong mask: " + mask_filename
123
+ assert mask is not None, "Wrong mask: " + str(mask_filename)
122
124
 
123
125
  # Normalize the mask
124
126
  if len(mask.shape) == 3 and mask.shape[2] == 3:
@@ -135,25 +137,25 @@ def check_image( # pylint: disable=too-many-locals,too-many-statements
135
137
  # Convert to boolean
136
138
  mask = mask == 0
137
139
 
138
- assert mask.shape[0] == image_to_check.shape[0] and mask.shape[1] == image_to_check.shape[1], (
140
+ assert mask.shape[0] == image_to_check.shape[0] and mask.shape[1] == image_to_check.shape[1], ( # noqa: PT018
139
141
  f"Mask and image should have the same shape ({mask.shape} != {image_to_check.shape})"
140
142
  )
141
143
  image_to_check[mask] = background_color
142
144
 
143
- if not os.path.exists(result_folder):
144
- os.makedirs(result_folder)
145
+ if not result_folder.exists():
146
+ result_folder.mkdir(parents=True)
145
147
  if generate_expected_image:
146
148
  skimage.io.imsave(expected_filename, image_to_check)
147
149
  return
148
- if not os.path.isfile(expected_filename):
150
+ if not expected_filename.is_file():
149
151
  skimage.io.imsave(expected_filename, image_to_check)
150
- raise AssertionError("Expected image not found: " + expected_filename)
152
+ raise AssertionError("Expected image not found: " + str(expected_filename))
151
153
  expected = skimage.io.imread(expected_filename)
152
- assert expected is not None, "Wrong image: " + expected_filename
154
+ assert expected is not None, "Wrong image: " + str(expected_filename)
153
155
  expected = normalize_image(expected)
154
156
 
155
157
  if mask is not None:
156
- assert expected.shape[0] == mask.shape[0] and expected.shape[1] == mask.shape[1], (
158
+ assert expected.shape[0] == mask.shape[0] and expected.shape[1] == mask.shape[1], ( # noqa: PT018
157
159
  f"Mask and expected image should have the same shape ({mask.shape} != {expected.shape})"
158
160
  )
159
161
  expected[mask] = background_color
@@ -162,7 +164,11 @@ def check_image( # pylint: disable=too-many-locals,too-many-statements
162
164
  f"Images have different shapes expected {expected.shape} != actual {image_to_check.shape}"
163
165
  )
164
166
  score, diff = skimage.metrics.structural_similarity(
165
- expected, image_to_check, multichannel=True, full=True, channel_axis=2
167
+ expected,
168
+ image_to_check,
169
+ multichannel=True,
170
+ full=True,
171
+ channel_axis=2,
166
172
  )
167
173
  diff = (255 - diff * 255).astype("uint8")
168
174
 
@@ -179,13 +185,13 @@ def check_image( # pylint: disable=too-many-locals,too-many-statements
179
185
 
180
186
  def check_screenshot(
181
187
  url: str,
182
- result_folder: str,
183
- expected_filename: str,
188
+ result_folder: str | Path,
189
+ expected_filename: str | Path,
184
190
  width: int = 800,
185
191
  height: int = 600,
186
192
  sleep: int = 100,
187
- headers: Optional[dict[str, str]] = None,
188
- media: Optional[list[dict[str, str]]] = None,
193
+ headers: dict[str, str] | None = None,
194
+ media: list[dict[str, str]] | None = None,
189
195
  level: float = 1.0,
190
196
  generate_expected_image: bool = False,
191
197
  use_mask: bool = True,
@@ -216,15 +222,15 @@ def check_screenshot(
216
222
  if media is None:
217
223
  media = []
218
224
 
219
- if not os.path.exists(os.path.join(os.path.dirname(__file__), "node_modules")):
220
- subprocess.run(["npm", "install"], cwd=os.path.dirname(__file__), check=True) # nosec
225
+ if not Path(__file__).parent.joinpath("node_modules").exists():
226
+ subprocess.run(["npm", "install"], cwd=Path(__file__).parent, check=True) # nosec
221
227
 
222
- image_file_basename = os.path.splitext(os.path.basename(expected_filename))[0]
228
+ image_file_basename = Path(expected_filename).stem
223
229
  if image_file_basename.endswith(".expected"):
224
- image_file_basename = os.path.splitext(image_file_basename)[0]
230
+ image_file_basename = Path(image_file_basename).stem
225
231
 
226
- result_folder = os.path.abspath(result_folder)
227
- actual_filename = os.path.join(result_folder, f"{image_file_basename}.actual.png")
232
+ result_folder = Path(result_folder).resolve()
233
+ actual_filename = result_folder / f"{image_file_basename}.actual.png"
228
234
  subprocess.run( # nosec
229
235
  [
230
236
  "node",
@@ -237,7 +243,7 @@ def check_screenshot(
237
243
  f"--media={json.dumps(media)}",
238
244
  f"--output={actual_filename}",
239
245
  ],
240
- cwd=os.path.dirname(__file__),
246
+ cwd=Path(__file__).parent,
241
247
  check=True,
242
248
  )
243
249
  check_image(
@@ -1,7 +1,7 @@
1
1
  import functools
2
2
  import json
3
3
  import logging
4
- from typing import Any, Optional
4
+ from typing import Any
5
5
 
6
6
  import requests
7
7
 
@@ -56,7 +56,7 @@ class PrintConnection(connection.Connection):
56
56
  assert report.headers["Content-Type"] == "application/pdf"
57
57
  return report
58
58
 
59
- def _check_completion(self, ref: str) -> Optional[Any]:
59
+ def _check_completion(self, ref: str) -> Any | None:
60
60
  status = self.get_json(f"status/{ref}.json")
61
61
  if status["done"]:
62
62
  return status
@@ -1,6 +1,7 @@
1
1
  import logging
2
2
  import time
3
- from typing import Any, Callable
3
+ from collections.abc import Callable
4
+ from typing import Any
4
5
 
5
6
  import pytest
6
7
  import requests
@@ -18,8 +19,7 @@ def wait_url(url: str, timeout: float = _DEFAULT_TIMEOUT) -> None:
18
19
  if r.status_code == 200:
19
20
  _LOG.info("%s service started", url)
20
21
  return True
21
- else:
22
- return False
22
+ return False
23
23
 
24
24
  retry_timeout(what, timeout=timeout)
25
25
 
@@ -43,7 +43,7 @@ def retry_timeout(what: Callable[[], Any], timeout: float = _DEFAULT_TIMEOUT, in
43
43
  return ret
44
44
  except NameError:
45
45
  raise
46
- except Exception as e: # pylint: disable=broad-except
46
+ except Exception as e: # pylint: disable=broad-exception-caught
47
47
  error = str(e)
48
48
  _LOG.info(" Failed: %s", e)
49
49
  if time.perf_counter() > timeout:
c2cwsgiutils/auth.py CHANGED
@@ -2,7 +2,7 @@ import hashlib
2
2
  import logging
3
3
  from collections.abc import Mapping
4
4
  from enum import Enum
5
- from typing import Any, Optional, TypedDict, cast
5
+ from typing import Any, TypedDict, cast
6
6
 
7
7
  import jwt
8
8
  import pyramid.request
@@ -51,15 +51,15 @@ class AuthConfig(TypedDict, total=False):
51
51
  """Configuration of the authentication."""
52
52
 
53
53
  # The repository to check access to (<organization>/<repository>).
54
- github_repository: Optional[str]
54
+ github_repository: str | None
55
55
  # The type of access to check (admin|push|pull).
56
- github_access_type: Optional[str]
56
+ github_access_type: str | None
57
57
 
58
58
 
59
59
  def get_expected_secret(request: pyramid.request.Request) -> str:
60
60
  """Return the secret expected from the client."""
61
61
  settings = request.registry.settings
62
- return cast(str, env_or_settings(settings, SECRET_ENV, SECRET_PROP, False))
62
+ return cast(str, env_or_settings(settings, SECRET_ENV, SECRET_PROP, default=False))
63
63
 
64
64
 
65
65
  def _hash_secret(secret: str) -> str:
@@ -80,10 +80,7 @@ def _is_auth_secret(request: pyramid.request.Request) -> bool:
80
80
  secret = request.params.get("secret")
81
81
  if secret is None:
82
82
  secret = request.headers.get("X-API-Key")
83
- if secret is None:
84
- secret_hash = request.cookies.get(SECRET_ENV)
85
- else:
86
- secret_hash = _hash_secret(secret)
83
+ secret_hash = request.cookies.get(SECRET_ENV) if secret is None else _hash_secret(secret)
87
84
 
88
85
  if secret_hash is not None:
89
86
  if secret_hash == "" or secret == "": # nosec
@@ -94,7 +91,12 @@ def _is_auth_secret(request: pyramid.request.Request) -> bool:
94
91
  return False
95
92
  # Login or refresh the cookie
96
93
  request.response.set_cookie(
97
- SECRET_ENV, secret_hash, max_age=_COOKIE_AGE, httponly=True, secure=True, samesite="Strict"
94
+ SECRET_ENV,
95
+ secret_hash,
96
+ max_age=_COOKIE_AGE,
97
+ httponly=True,
98
+ secure=True,
99
+ samesite="Strict",
98
100
  )
99
101
  # Since this could be used from outside c2cwsgiutils views, we cannot set the path to c2c
100
102
  return True
@@ -158,7 +160,8 @@ def is_auth(request: pyramid.request.Request) -> bool:
158
160
  def auth_view(request: pyramid.request.Request) -> None:
159
161
  """Get the authentication view."""
160
162
  if not is_auth(request):
161
- raise HTTPForbidden("Missing or invalid secret (parameter, X-API-Key header or cookie)")
163
+ message = "Missing or invalid secret (parameter, X-API-Key header or cookie)"
164
+ raise HTTPForbidden(message)
162
165
 
163
166
 
164
167
  class AuthenticationType(Enum):
@@ -172,7 +175,7 @@ class AuthenticationType(Enum):
172
175
  GITHUB = 2
173
176
 
174
177
 
175
- def auth_type(settings: Optional[Mapping[str, Any]]) -> Optional[AuthenticationType]:
178
+ def auth_type(settings: Mapping[str, Any] | None) -> AuthenticationType | None:
176
179
  """Get the authentication type."""
177
180
  if env_or_settings(settings, SECRET_ENV, SECRET_PROP, "") != "":
178
181
  return AuthenticationType.SECRET
@@ -198,7 +201,9 @@ def auth_type(settings: Optional[Mapping[str, Any]]) -> Optional[AuthenticationT
198
201
 
199
202
 
200
203
  def check_access(
201
- request: pyramid.request.Request, repo: Optional[str] = None, access_type: Optional[str] = None
204
+ request: pyramid.request.Request,
205
+ repo: str | None = None,
206
+ access_type: str | None = None,
202
207
  ) -> bool:
203
208
  """
204
209
  Check if the user has access to the resource.
@@ -274,7 +279,9 @@ def check_access_config(request: pyramid.request.Request, auth_config: AuthConfi
274
279
 
275
280
 
276
281
  def is_enabled(
277
- config: pyramid.config.Configurator, env_name: Optional[str] = None, config_name: Optional[str] = None
282
+ config: pyramid.config.Configurator,
283
+ env_name: str | None = None,
284
+ config_name: str | None = None,
278
285
  ) -> bool:
279
286
  """Is the authentication enable."""
280
287
  return (