hardpy 0.6.1__py3-none-any.whl → 0.7.0__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 (46) hide show
  1. hardpy/__init__.py +28 -26
  2. hardpy/cli/cli.py +8 -8
  3. hardpy/cli/template.py +6 -6
  4. hardpy/common/config.py +15 -14
  5. hardpy/hardpy_panel/api.py +9 -9
  6. hardpy/hardpy_panel/frontend/dist/asset-manifest.json +3 -3
  7. hardpy/hardpy_panel/frontend/dist/index.html +1 -1
  8. hardpy/hardpy_panel/frontend/dist/static/js/main.942e57d4.js +3 -0
  9. hardpy/hardpy_panel/frontend/dist/static/js/main.942e57d4.js.map +1 -0
  10. hardpy/pytest_hardpy/db/__init__.py +3 -4
  11. hardpy/pytest_hardpy/db/base_connector.py +6 -5
  12. hardpy/pytest_hardpy/db/base_server.py +1 -1
  13. hardpy/pytest_hardpy/db/base_store.py +14 -9
  14. hardpy/pytest_hardpy/db/const.py +3 -1
  15. hardpy/pytest_hardpy/db/runstore.py +13 -15
  16. hardpy/pytest_hardpy/db/schema/__init__.py +9 -0
  17. hardpy/pytest_hardpy/db/{schema.py → schema/v1.py} +120 -79
  18. hardpy/pytest_hardpy/db/statestore.py +8 -10
  19. hardpy/pytest_hardpy/plugin.py +103 -48
  20. hardpy/pytest_hardpy/pytest_call.py +75 -30
  21. hardpy/pytest_hardpy/pytest_wrapper.py +8 -7
  22. hardpy/pytest_hardpy/reporter/__init__.py +1 -1
  23. hardpy/pytest_hardpy/reporter/base.py +32 -7
  24. hardpy/pytest_hardpy/reporter/hook_reporter.py +65 -37
  25. hardpy/pytest_hardpy/reporter/runner_reporter.py +6 -8
  26. hardpy/pytest_hardpy/result/__init__.py +1 -1
  27. hardpy/pytest_hardpy/result/couchdb_config.py +20 -16
  28. hardpy/pytest_hardpy/result/report_loader/couchdb_loader.py +2 -2
  29. hardpy/pytest_hardpy/result/report_reader/couchdb_reader.py +36 -20
  30. hardpy/pytest_hardpy/utils/__init__.py +20 -19
  31. hardpy/pytest_hardpy/utils/connection_data.py +6 -8
  32. hardpy/pytest_hardpy/utils/const.py +1 -1
  33. hardpy/pytest_hardpy/utils/dialog_box.py +34 -22
  34. hardpy/pytest_hardpy/utils/exception.py +8 -8
  35. hardpy/pytest_hardpy/utils/machineid.py +15 -0
  36. hardpy/pytest_hardpy/utils/node_info.py +45 -16
  37. hardpy/pytest_hardpy/utils/progress_calculator.py +4 -3
  38. hardpy/pytest_hardpy/utils/singleton.py +23 -16
  39. {hardpy-0.6.1.dist-info → hardpy-0.7.0.dist-info}/METADATA +19 -28
  40. {hardpy-0.6.1.dist-info → hardpy-0.7.0.dist-info}/RECORD +44 -42
  41. hardpy/hardpy_panel/frontend/dist/static/js/main.7c954faf.js +0 -3
  42. hardpy/hardpy_panel/frontend/dist/static/js/main.7c954faf.js.map +0 -1
  43. /hardpy/hardpy_panel/frontend/dist/static/js/{main.7c954faf.js.LICENSE.txt → main.942e57d4.js.LICENSE.txt} +0 -0
  44. {hardpy-0.6.1.dist-info → hardpy-0.7.0.dist-info}/WHEEL +0 -0
  45. {hardpy-0.6.1.dist-info → hardpy-0.7.0.dist-info}/entry_points.txt +0 -0
  46. {hardpy-0.6.1.dist-info → hardpy-0.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -2,21 +2,30 @@
2
2
  # GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
3
3
 
4
4
  from logging import getLogger
5
+ from typing import Any
5
6
 
6
- from hardpy.pytest_hardpy.db import StateStore, RunStore
7
+ from hardpy.pytest_hardpy.db import (
8
+ DatabaseField as DF, # noqa: N817
9
+ RunStore,
10
+ StateStore,
11
+ )
7
12
 
8
13
 
9
14
  class BaseReporter:
10
15
  """Base class for test reporter."""
11
16
 
12
- def __init__(self):
17
+ def __init__(self) -> None:
13
18
  self._statestore = StateStore()
14
19
  self._runstore = RunStore()
15
20
  self._log = getLogger(__name__)
16
21
 
17
22
  def set_doc_value(
18
- self, key: str, value, runstore_only=False, statestore_only=False
19
- ):
23
+ self,
24
+ key: str,
25
+ value: Any, # noqa: ANN401
26
+ runstore_only: bool = False,
27
+ statestore_only: bool = False,
28
+ ) -> None:
20
29
  """Set value to the document.
21
30
 
22
31
  Update a document without writing to the database.
@@ -33,7 +42,8 @@ class BaseReporter:
33
42
  ValueError: if both runstore_only and statestore_only are True
34
43
  """
35
44
  if runstore_only and statestore_only:
36
- raise ValueError("Both runstore_only and statestore_only cannot be True")
45
+ msg = "Both runstore_only and statestore_only cannot be True"
46
+ raise ValueError(msg)
37
47
  if runstore_only:
38
48
  self._runstore.update_doc(key, value)
39
49
  return
@@ -43,12 +53,12 @@ class BaseReporter:
43
53
  self._runstore.update_doc(key, value)
44
54
  self._statestore.update_doc(key, value)
45
55
 
46
- def update_db_by_doc(self):
56
+ def update_db_by_doc(self) -> None:
47
57
  """Update database by current document."""
48
58
  self._statestore.update_db()
49
59
  self._runstore.update_db()
50
60
 
51
- def generate_key(self, *args) -> str:
61
+ def generate_key(self, *args: Any) -> str: # noqa: ANN401
52
62
  """Generate key for database.
53
63
 
54
64
  Args:
@@ -58,3 +68,18 @@ class BaseReporter:
58
68
  str: database key
59
69
  """
60
70
  return ".".join(args)
71
+
72
+ def get_current_attempt(self, module_id: str, case_id: str) -> int:
73
+ """Get current attempt.
74
+
75
+ Returns:
76
+ int: current attempt
77
+ """
78
+ key = self.generate_key(
79
+ DF.MODULES,
80
+ module_id,
81
+ DF.CASES,
82
+ case_id,
83
+ DF.ATTEMPT,
84
+ )
85
+ return self._statestore.get_field(key)
@@ -1,27 +1,29 @@
1
1
  # Copyright (c) 2024 Everypin
2
2
  # GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
3
+ from __future__ import annotations
3
4
 
4
- from time import time, tzname
5
- from logging import getLogger
6
5
  from copy import deepcopy
6
+ from datetime import datetime
7
+ from logging import getLogger
8
+ from time import time
7
9
 
8
10
  from natsort import natsorted
9
11
 
10
- from hardpy.pytest_hardpy.db import DatabaseField as DF
12
+ from hardpy.pytest_hardpy.db import DatabaseField as DF # noqa: N817
11
13
  from hardpy.pytest_hardpy.reporter.base import BaseReporter
12
- from hardpy.pytest_hardpy.utils import TestStatus, NodeInfo
14
+ from hardpy.pytest_hardpy.utils import NodeInfo, TestStatus, machine_id
13
15
 
14
16
 
15
17
  class HookReporter(BaseReporter):
16
18
  """Reporter for using in the hook HardPy plugin's hooks."""
17
19
 
18
- def __init__(self, is_clear_database: bool = False): # noqa: WPS612
20
+ def __init__(self, is_clear_database: bool = False) -> None:
19
21
  super().__init__()
20
22
  if is_clear_database:
21
23
  self._statestore.clear()
22
24
  self._log = getLogger(__name__)
23
25
 
24
- def init_doc(self, doc_name: str):
26
+ def init_doc(self, doc_name: str) -> None:
25
27
  """Initialize document.
26
28
 
27
29
  Args:
@@ -30,23 +32,26 @@ class HookReporter(BaseReporter):
30
32
  self.set_doc_value(DF.NAME, doc_name)
31
33
  self.set_doc_value(DF.STATUS, TestStatus.READY)
32
34
  self.set_doc_value(DF.START_TIME, None)
33
- self.set_doc_value(DF.TIMEZONE, None)
34
35
  self.set_doc_value(DF.STOP_TIME, None)
35
- self.set_doc_value(DF.PROGRESS, 0)
36
- self.set_doc_value(DF.DRIVERS, {})
36
+ self.set_doc_value(DF.PROGRESS, 0, statestore_only=True)
37
37
  self.set_doc_value(DF.ARTIFACT, {}, runstore_only=True)
38
38
  self.set_doc_value(DF.OPERATOR_MSG, {}, statestore_only=True)
39
39
 
40
- def start(self):
40
+ test_stand_tz = self.generate_key(DF.TEST_STAND, DF.TIMEZONE)
41
+ self.set_doc_value(test_stand_tz, datetime.now().astimezone().tzname())
42
+
43
+ test_stand_id_key = self.generate_key(DF.TEST_STAND, DF.HW_ID)
44
+ self.set_doc_value(test_stand_id_key, machine_id())
45
+
46
+ def start(self) -> None:
41
47
  """Start test."""
42
48
  self._log.debug("Starting test run.")
43
49
  start_time = int(time())
44
50
  self.set_doc_value(DF.START_TIME, start_time)
45
51
  self.set_doc_value(DF.STATUS, TestStatus.RUN)
46
- self.set_doc_value(DF.TIMEZONE, tzname) # noqa: WPS432
47
- self.set_doc_value(DF.PROGRESS, 0)
52
+ self.set_doc_value(DF.PROGRESS, 0, statestore_only=True)
48
53
 
49
- def finish(self, status: TestStatus):
54
+ def finish(self, status: TestStatus) -> None:
50
55
  """Finish test.
51
56
 
52
57
  This method must be called at the end of test run.
@@ -56,20 +61,20 @@ class HookReporter(BaseReporter):
56
61
  self.set_doc_value(DF.STOP_TIME, stop_time)
57
62
  self.set_doc_value(DF.STATUS, status)
58
63
 
59
- def compact_all(self):
60
- """Compact all databases"""
64
+ def compact_all(self) -> None:
65
+ """Compact all databases."""
61
66
  self._statestore.compact()
62
67
  self._runstore.compact()
63
68
 
64
- def set_progress(self, progress: int):
69
+ def set_progress(self, progress: int) -> None:
65
70
  """Set test progress.
66
71
 
67
72
  Args:
68
73
  progress (int): test progress
69
74
  """
70
- self.set_doc_value(DF.PROGRESS, progress)
75
+ self.set_doc_value(DF.PROGRESS, progress, statestore_only=True)
71
76
 
72
- def set_assertion_msg(self, module_id: str, case_id: str, msg: str | None):
77
+ def set_assertion_msg(self, module_id: str, case_id: str, msg: str | None) -> None:
73
78
  """Set case assertion message.
74
79
 
75
80
  Args:
@@ -78,11 +83,15 @@ class HookReporter(BaseReporter):
78
83
  msg (str): assertion message
79
84
  """
80
85
  key = self.generate_key(
81
- DF.MODULES, module_id, DF.CASES, case_id, DF.ASSERTION_MSG
86
+ DF.MODULES,
87
+ module_id,
88
+ DF.CASES,
89
+ case_id,
90
+ DF.ASSERTION_MSG,
82
91
  )
83
92
  self.set_doc_value(key, msg)
84
93
 
85
- def add_case(self, node_info: NodeInfo):
94
+ def add_case(self, node_info: NodeInfo) -> None:
86
95
  """Add test case to document.
87
96
 
88
97
  Args:
@@ -99,7 +108,7 @@ class HookReporter(BaseReporter):
99
108
  self.set_doc_value(key, item_statestore, statestore_only=True)
100
109
  self.set_doc_value(key, item_runstore, runstore_only=True)
101
110
 
102
- def set_case_status(self, module_id: str, case_id: str, status: TestStatus):
111
+ def set_case_status(self, module_id: str, case_id: str, status: TestStatus) -> None:
103
112
  """Set test case status.
104
113
 
105
114
  Args:
@@ -110,7 +119,7 @@ class HookReporter(BaseReporter):
110
119
  key = self.generate_key(DF.MODULES, module_id, DF.CASES, case_id, DF.STATUS)
111
120
  self.set_doc_value(key, status)
112
121
 
113
- def set_case_start_time(self, module_id: str, case_id: str):
122
+ def set_case_start_time(self, module_id: str, case_id: str) -> None:
114
123
  """Set test case start_time.
115
124
 
116
125
  Args:
@@ -120,7 +129,7 @@ class HookReporter(BaseReporter):
120
129
  key = self.generate_key(DF.MODULES, module_id, DF.CASES, case_id, DF.START_TIME)
121
130
  self._set_time(key)
122
131
 
123
- def set_case_stop_time(self, module_id: str, case_id: str):
132
+ def set_case_stop_time(self, module_id: str, case_id: str) -> None:
124
133
  """Set test case start_time.
125
134
 
126
135
  Args:
@@ -130,7 +139,7 @@ class HookReporter(BaseReporter):
130
139
  key = self.generate_key(DF.MODULES, module_id, DF.CASES, case_id, DF.STOP_TIME)
131
140
  self._set_time(key)
132
141
 
133
- def set_module_status(self, module_id: str, status: TestStatus):
142
+ def set_module_status(self, module_id: str, status: TestStatus) -> None:
134
143
  """Set test module status.
135
144
 
136
145
  Args:
@@ -140,7 +149,7 @@ class HookReporter(BaseReporter):
140
149
  key = self.generate_key(DF.MODULES, module_id, DF.STATUS)
141
150
  self.set_doc_value(key, status)
142
151
 
143
- def set_module_start_time(self, module_id: str):
152
+ def set_module_start_time(self, module_id: str) -> None:
144
153
  """Set test module status.
145
154
 
146
155
  Args:
@@ -149,7 +158,7 @@ class HookReporter(BaseReporter):
149
158
  key = self.generate_key(DF.MODULES, module_id, DF.START_TIME)
150
159
  self._set_time(key)
151
160
 
152
- def set_module_stop_time(self, module_id: str):
161
+ def set_module_stop_time(self, module_id: str) -> None:
153
162
  """Set test module status.
154
163
 
155
164
  Args:
@@ -158,6 +167,23 @@ class HookReporter(BaseReporter):
158
167
  key = self.generate_key(DF.MODULES, module_id, DF.STOP_TIME)
159
168
  self._set_time(key)
160
169
 
170
+ def set_case_attempt(self, module_id: str, case_id: str, attempt: int) -> None:
171
+ """Set test case current attempt.
172
+
173
+ Args:
174
+ module_id (str): module id
175
+ case_id (str): case id
176
+ attempt (int): test case current attempt
177
+ """
178
+ key = self.generate_key(
179
+ DF.MODULES,
180
+ module_id,
181
+ DF.CASES,
182
+ case_id,
183
+ DF.ATTEMPT,
184
+ )
185
+ self.set_doc_value(key, attempt, statestore_only=True)
186
+
161
187
  def update_node_order(self, nodes: dict) -> None:
162
188
  """Update node order.
163
189
 
@@ -173,7 +199,7 @@ class HookReporter(BaseReporter):
173
199
  updated_module_order = self._update_module_order(updated_case_order)
174
200
  self.set_doc_value(key, updated_module_order, statestore_only=True)
175
201
 
176
- def _set_time(self, key: str):
202
+ def _set_time(self, key: str) -> None:
177
203
  current_time = self._statestore.get_field(key)
178
204
  if current_time is None:
179
205
  self.set_doc_value(key, int(time()))
@@ -184,8 +210,8 @@ class HookReporter(BaseReporter):
184
210
  node_info: NodeInfo,
185
211
  is_only_runstore: bool = False,
186
212
  is_only_statestore: bool = False,
187
- ):
188
- module_default = { # noqa: WPS204
213
+ ) -> None:
214
+ module_default = {
189
215
  DF.STATUS: TestStatus.READY,
190
216
  DF.NAME: self._get_module_name(node_info),
191
217
  DF.START_TIME: None,
@@ -199,13 +225,12 @@ class HookReporter(BaseReporter):
199
225
  DF.STOP_TIME: None,
200
226
  DF.ASSERTION_MSG: None,
201
227
  DF.MSG: None,
202
- DF.ATTEMPT: 0,
203
228
  }
204
229
 
205
- if item.get(node_info.module_id) is None: # noqa: WPS204
230
+ if item.get(node_info.module_id) is None:
206
231
  if is_only_runstore:
207
232
  module_default[DF.ARTIFACT] = {}
208
- item[node_info.module_id] = module_default # noqa: WPS204
233
+ item[node_info.module_id] = module_default
209
234
  else:
210
235
  item[node_info.module_id][DF.STATUS] = TestStatus.READY
211
236
  item[node_info.module_id][DF.NAME] = self._get_module_name(node_info)
@@ -218,10 +243,14 @@ class HookReporter(BaseReporter):
218
243
 
219
244
  if is_only_statestore:
220
245
  case_default[DF.DIALOG_BOX] = {}
246
+ case_default[DF.ATTEMPT] = 0
221
247
  item[node_info.module_id][DF.CASES][node_info.case_id] = case_default
222
248
 
223
249
  def _remove_outdate_node(
224
- self, old_modules: dict, new_modules: dict, nodes: dict
250
+ self,
251
+ old_modules: dict,
252
+ new_modules: dict,
253
+ nodes: dict,
225
254
  ) -> dict:
226
255
  """Remove outdated nodes from StateStore database.
227
256
 
@@ -277,16 +306,15 @@ class HookReporter(BaseReporter):
277
306
  Returns:
278
307
  dict: list of modules and cases.
279
308
  """
280
-
281
309
  sorted_modules = natsorted(modules.items(), key=lambda item: item[0])
282
310
 
283
311
  new_modules = {}
284
312
  for module_id, module in sorted_modules:
285
- new_modules[module_id] = module
313
+ new_modules[module_id] = module # noqa: PERF403
286
314
 
287
315
  return new_modules
288
316
 
289
- def _get_module_name(self, node_info) -> str:
317
+ def _get_module_name(self, node_info: NodeInfo) -> str:
290
318
  """Get module name from markers or use default.
291
319
 
292
320
  Args:
@@ -297,7 +325,7 @@ class HookReporter(BaseReporter):
297
325
  """
298
326
  return node_info.module_name if node_info.module_name else node_info.module_id
299
327
 
300
- def _get_case_name(self, node_info) -> str:
328
+ def _get_case_name(self, node_info: NodeInfo) -> str:
301
329
  """Get case name from markers or use default.
302
330
 
303
331
  Args:
@@ -5,19 +5,17 @@ from logging import getLogger
5
5
  from typing import Any
6
6
 
7
7
  from hardpy.pytest_hardpy.reporter.base import BaseReporter
8
- from hardpy.pytest_hardpy.utils import Singleton
8
+ from hardpy.pytest_hardpy.utils import SingletonMeta
9
9
 
10
10
 
11
- class RunnerReporter(Singleton, BaseReporter):
11
+ class RunnerReporter(BaseReporter, metaclass=SingletonMeta):
12
12
  """Reporter for using in direct call from test runner with HardPy plugin."""
13
13
 
14
- def __init__(self):
15
- if not self._initialized:
16
- super().__init__()
17
- self._log = getLogger(__name__)
18
- self._initialized = True
14
+ def __init__(self) -> None:
15
+ super().__init__()
16
+ self._log = getLogger(__name__)
19
17
 
20
- def get_field(self, key: str) -> Any:
18
+ def get_field(self, key: str) -> Any: # noqa: ANN401
21
19
  """Get field from the statestore.
22
20
 
23
21
  Args:
@@ -1,8 +1,8 @@
1
1
  # Copyright (c) 2024 Everypin
2
2
  # GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
3
3
 
4
- from hardpy.pytest_hardpy.result.report_reader.couchdb_reader import CouchdbReader
5
4
  from hardpy.pytest_hardpy.result.report_loader.couchdb_loader import CouchdbLoader
5
+ from hardpy.pytest_hardpy.result.report_reader.couchdb_reader import CouchdbReader
6
6
 
7
7
  __all__ = [
8
8
  "CouchdbReader",
@@ -1,16 +1,17 @@
1
1
  # Copyright (c) 2024 Everypin
2
2
  # GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
3
+ from __future__ import annotations
3
4
 
4
5
  import ast
5
- import requests
6
6
  import socket
7
-
8
7
  from dataclasses import dataclass
8
+
9
+ import requests
9
10
  from urllib3 import disable_warnings
10
11
 
11
12
 
12
13
  @dataclass
13
- class CouchdbConfig: # noqa: WPS306
14
+ class CouchdbConfig:
14
15
  """CouchDB loader config.
15
16
 
16
17
  If `connection_str` arg is not set, it will be created from other args.
@@ -23,7 +24,7 @@ class CouchdbConfig: # noqa: WPS306
23
24
  port: int = 5984
24
25
  connection_str: str | None = None
25
26
 
26
- def __post_init__(self):
27
+ def __post_init__(self) -> None:
27
28
  """Disable urllib3 warnings.
28
29
 
29
30
  More info: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
@@ -43,14 +44,14 @@ class CouchdbConfig: # noqa: WPS306
43
44
  if self.connection_str:
44
45
  return self.connection_str
45
46
 
46
- # TODO: Modify connection string creating based on protocol.
47
+ # TODO(xorialexandrov): Modify connection string creating based on protocol.
47
48
  # Some problems with http and https, different ports, local
48
49
  # and cloud databases.
49
50
  protocol = self._get_protocol()
50
51
 
51
52
  if protocol == "http":
52
53
  host_url = f"http://{self.host}:{self.port}"
53
- uri = f"{self.host}:{str(self.port)}" # noqa: WPS237
54
+ uri = f"{self.host}:{self.port!s}"
54
55
  elif protocol == "https":
55
56
  host_url = f"https://{self.host}"
56
57
  uri = f"{self.host}"
@@ -58,20 +59,22 @@ class CouchdbConfig: # noqa: WPS306
58
59
  try:
59
60
  response = requests.get(host_url, timeout=5)
60
61
  except requests.exceptions.RequestException:
61
- raise RuntimeError(f"Error CouchDB connecting to {host_url}.")
62
+ msg = f"Error CouchDB connecting to {host_url}."
63
+ raise RuntimeError(msg) # noqa: B904
62
64
 
63
65
  # fmt: off
64
66
  try:
65
- couchdb_dict = ast.literal_eval(response._content.decode("utf-8")) # noqa: WPS437,E501
67
+ couchdb_dict = ast.literal_eval(response._content.decode("utf-8")) # type: ignore # noqa: SLF001
66
68
  couchdb_dict.get("couchdb", False)
67
- except Exception:
68
- raise RuntimeError(f"Address {host_url} does not provide CouchDB attributes.")
69
+ except Exception: # noqa: BLE001
70
+ msg = f"Address {host_url} does not provide CouchDB attributes."
71
+ raise RuntimeError(msg) # noqa: B904
69
72
  # fmt: on
70
73
 
71
74
  credentials = f"{self.user}:{self.password}"
72
75
  return f"{protocol}://{credentials}@{uri}/"
73
76
 
74
- def _get_protocol(self) -> str: # noqa: WPS231
77
+ def _get_protocol(self) -> str:
75
78
  success = 200
76
79
  try:
77
80
  # HTTPS attempt
@@ -80,17 +83,18 @@ class CouchdbConfig: # noqa: WPS306
80
83
  request = f"https://{self.host}"
81
84
  if requests.get(request, timeout=5).status_code == success:
82
85
  return "https"
83
- raise OSError
86
+ raise OSError # noqa: TRY301
84
87
  except OSError:
85
- try: # noqa: WPS505
88
+ try:
86
89
  # HTTP attempt
87
90
  sock = socket.create_connection((self.host, self.port))
88
91
  sock.close()
89
92
  request = f"http://{self.host}:{self.port}"
90
93
  if requests.get(request, timeout=5).status_code == success:
91
94
  return "http"
92
- raise OSError
95
+ raise OSError # noqa: TRY301
93
96
  except OSError:
94
- raise RuntimeError(
95
- f"Error connecting to couchdb server {self.host}:{self.port}."
97
+ msg = f"Error connecting to couchdb server {self.host}:{self.port}."
98
+ raise RuntimeError( # noqa: B904
99
+ msg,
96
100
  )
@@ -5,8 +5,8 @@ from logging import getLogger
5
5
  from uuid import uuid4
6
6
 
7
7
  from pycouchdb import Server as DbServer
8
- from pycouchdb.exceptions import Conflict
9
8
  from pycouchdb.client import Database
9
+ from pycouchdb.exceptions import Conflict
10
10
 
11
11
  from hardpy.pytest_hardpy.db.schema import ResultRunStore
12
12
  from hardpy.pytest_hardpy.result.couchdb_config import CouchdbConfig
@@ -15,7 +15,7 @@ from hardpy.pytest_hardpy.result.couchdb_config import CouchdbConfig
15
15
  class CouchdbLoader:
16
16
  """CouchDB report generator."""
17
17
 
18
- def __init__(self, config: CouchdbConfig):
18
+ def __init__(self, config: CouchdbConfig) -> None:
19
19
  self._log = getLogger(__name__)
20
20
  self._config: CouchdbConfig = config
21
21
  self._db_srv = DbServer(config.connection_string)
@@ -1,17 +1,21 @@
1
1
  # Copyright (c) 2024 Everypin
2
2
  # GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
3
+ from __future__ import annotations
3
4
 
4
- from logging import getLogger
5
5
  from dataclasses import dataclass
6
- from typing import List, Optional
6
+ from logging import getLogger
7
+ from typing import TYPE_CHECKING
7
8
 
8
9
  from pycouchdb import Server as DbServer
9
- from pycouchdb.client import Database
10
10
  from pycouchdb.exceptions import NotFound
11
11
 
12
- from hardpy.pytest_hardpy.db import DatabaseField as DF
12
+ from hardpy.pytest_hardpy.db import DatabaseField as DF # noqa: N817
13
13
  from hardpy.pytest_hardpy.utils.const import TestStatus
14
- from hardpy.pytest_hardpy.result.couchdb_config import CouchdbConfig
14
+
15
+ if TYPE_CHECKING:
16
+ from pycouchdb.client import Database
17
+
18
+ from hardpy.pytest_hardpy.result.couchdb_config import CouchdbConfig
15
19
 
16
20
 
17
21
  @dataclass
@@ -22,14 +26,14 @@ class ReportInfo:
22
26
  status: str
23
27
  start_time: str
24
28
  end_time: str
25
- first_failed_test_name: Optional[str]
26
- first_failed_test_id: Optional[str]
29
+ first_failed_test_name: str | None
30
+ first_failed_test_id: str | None
27
31
 
28
32
 
29
33
  class CouchdbReader:
30
34
  """CouchDB report info reader."""
31
35
 
32
- def __init__(self, config: CouchdbConfig):
36
+ def __init__(self, config: CouchdbConfig) -> None:
33
37
  self._log = getLogger(__name__)
34
38
  self._config = config
35
39
  self._db_srv = DbServer(config.connection_string)
@@ -58,13 +62,14 @@ class CouchdbReader:
58
62
  int: number of reports
59
63
  """
60
64
  if start_time < 0 or end_time < 0:
61
- raise ValueError("Start time and end time must be positive values")
65
+ msg = "Start time and end time must be positive values"
66
+ raise ValueError(msg)
62
67
  return sum(
63
68
  1
64
69
  for report in self._db.all()
65
70
  if self._is_in_timeframe(
66
- self._get_start_time_from_db(report[self._doc_id]),
67
- self._get_stop_time_from_db(report[self._doc_id]),
71
+ self._get_start_time_from_db(report[self._doc_id]), # type: ignore
72
+ self._get_stop_time_from_db(report[self._doc_id]), # type: ignore
68
73
  start_time,
69
74
  end_time,
70
75
  )
@@ -85,10 +90,11 @@ class CouchdbReader:
85
90
  doc = self._db.get(report_name)
86
91
  status = doc[DF.STATUS]
87
92
  if status not in {TestStatus.PASSED, TestStatus.FAILED, TestStatus.SKIPPED}:
88
- raise ValueError("Invalid report status")
93
+ msg = "Invalid report status"
94
+ raise ValueError(msg)
89
95
  return status
90
96
 
91
- def get_report_infos(self) -> List[ReportInfo]:
97
+ def get_report_infos(self) -> list[ReportInfo]:
92
98
  """Get a list of information about all reports in the database.
93
99
 
94
100
  Returns:
@@ -102,8 +108,10 @@ class CouchdbReader:
102
108
  return reports_info
103
109
 
104
110
  def get_report_infos_in_timeframe(
105
- self, start_time: int, end_time: int
106
- ) -> List[ReportInfo]:
111
+ self,
112
+ start_time: int,
113
+ end_time: int,
114
+ ) -> list[ReportInfo]:
107
115
  """Get a list of information about reports in a timeframe in the database.
108
116
 
109
117
  Args:
@@ -117,14 +125,15 @@ class CouchdbReader:
117
125
  List[ReportInfo]: list of report information
118
126
  """
119
127
  if start_time < 0 or end_time < 0:
120
- raise ValueError("Start time and end time must be positive values")
128
+ msg = "Start time and end time must be positive values"
129
+ raise ValueError(msg)
121
130
 
122
131
  reports_info = []
123
132
  reports = self._db.all()
124
133
  for report in reports:
125
134
  start_t_db = self._get_start_time_from_db(report[self._doc_id])
126
135
  stop_t_db = self._get_stop_time_from_db(report[self._doc_id])
127
- if self._is_in_timeframe(start_t_db, stop_t_db, start_time, end_time):
136
+ if self._is_in_timeframe(start_t_db, stop_t_db, start_time, end_time): # type: ignore
128
137
  report_info = self._get_single_report_info(report)
129
138
  reports_info.append(report_info)
130
139
  return reports_info
@@ -134,7 +143,8 @@ class CouchdbReader:
134
143
  return self._db_srv.database(self._config.db_name)
135
144
  except NotFound as exc:
136
145
  self._log.error(f"Error initializing database: {exc}")
137
- raise RuntimeError("Error initializing database") from exc
146
+ msg = "Error initializing database"
147
+ raise RuntimeError(msg) from exc
138
148
 
139
149
  def _get_start_time_from_db(self, doc: dict) -> str:
140
150
  return doc[DF.START_TIME]
@@ -146,7 +156,7 @@ class CouchdbReader:
146
156
  first_failed_test_name = None
147
157
  first_failed_test_id = None
148
158
  report_doc = report[self._doc_id]
149
- for _module_name, module_info in report_doc[DF.MODULES].items():
159
+ for module_info in report_doc[DF.MODULES].values():
150
160
  for case_name, case_info in module_info[DF.CASES].items():
151
161
  if case_info[DF.STATUS] == TestStatus.FAILED:
152
162
  first_failed_test_name = case_info[DF.NAME]
@@ -160,5 +170,11 @@ class CouchdbReader:
160
170
  first_failed_test_id=first_failed_test_id,
161
171
  )
162
172
 
163
- def _is_in_timeframe(self, start, end, timeframe_start, timeframe_end):
173
+ def _is_in_timeframe(
174
+ self,
175
+ start: int,
176
+ end: int,
177
+ timeframe_start: int,
178
+ timeframe_end: int,
179
+ ) -> bool:
164
180
  return timeframe_start <= start and end <= timeframe_end