hardpy 0.13.0__tar.gz → 0.15.0__tar.gz

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 (90) hide show
  1. {hardpy-0.13.0 → hardpy-0.15.0}/PKG-INFO +2 -1
  2. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/__init__.py +45 -10
  3. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/cli/cli.py +32 -28
  4. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/common/config.py +5 -4
  5. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/common/stand_cloud/connector.py +2 -2
  6. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/api.py +13 -3
  7. hardpy-0.13.0/hardpy/hardpy_panel/frontend/dist/assets/allPaths-Cg7WZDXy.js → hardpy-0.15.0/hardpy/hardpy_panel/frontend/dist/assets/allPaths-CV5wjLMB.js +1 -1
  8. hardpy-0.13.0/hardpy/hardpy_panel/frontend/dist/assets/allPathsLoader-C79wUwqR.js → hardpy-0.15.0/hardpy/hardpy_panel/frontend/dist/assets/allPathsLoader-JIzW_pSb.js +2 -2
  9. hardpy-0.15.0/hardpy/hardpy_panel/frontend/dist/assets/browser-ponyfill-CccdstaD.js +2 -0
  10. hardpy-0.15.0/hardpy/hardpy_panel/frontend/dist/assets/index-6RIgWzcZ.js +790 -0
  11. hardpy-0.13.0/hardpy/hardpy_panel/frontend/dist/assets/splitPathsBySizeLoader-hWuLTMwD.js → hardpy-0.15.0/hardpy/hardpy_panel/frontend/dist/assets/splitPathsBySizeLoader-DkZadBcn.js +1 -1
  12. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/index.html +1 -1
  13. hardpy-0.15.0/hardpy/hardpy_panel/frontend/dist/locales/de/translation.json +60 -0
  14. hardpy-0.15.0/hardpy/hardpy_panel/frontend/dist/locales/en/translation.json +60 -0
  15. hardpy-0.15.0/hardpy/hardpy_panel/frontend/dist/locales/es/translation.json +60 -0
  16. hardpy-0.15.0/hardpy/hardpy_panel/frontend/dist/locales/fr/translation.json +60 -0
  17. hardpy-0.15.0/hardpy/hardpy_panel/frontend/dist/locales/ja/translation.json +60 -0
  18. hardpy-0.15.0/hardpy/hardpy_panel/frontend/dist/locales/ru/translation.json +60 -0
  19. hardpy-0.15.0/hardpy/hardpy_panel/frontend/dist/locales/zh/translation.json +60 -0
  20. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/db/base_store.py +23 -0
  21. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/db/const.py +40 -19
  22. hardpy-0.15.0/hardpy/pytest_hardpy/db/schema/v1.py +239 -0
  23. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/plugin.py +32 -1
  24. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/pytest_call.py +331 -22
  25. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/pytest_wrapper.py +25 -34
  26. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/reporter/hook_reporter.py +53 -2
  27. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/result/report_loader/stand_cloud_loader.py +8 -2
  28. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/result/report_reader/couchdb_reader.py +1 -5
  29. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/utils/__init__.py +25 -11
  30. hardpy-0.15.0/hardpy/pytest_hardpy/utils/const.py +91 -0
  31. hardpy-0.15.0/hardpy/pytest_hardpy/utils/exception.py +40 -0
  32. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/utils/node_info.py +55 -1
  33. hardpy-0.15.0/hardpy/pytest_hardpy/utils/stand_type.py +198 -0
  34. {hardpy-0.13.0 → hardpy-0.15.0}/pyproject.toml +6 -3
  35. hardpy-0.13.0/hardpy/hardpy_panel/frontend/dist/assets/index-De5CJ3kt.js +0 -790
  36. hardpy-0.13.0/hardpy/pytest_hardpy/db/schema/v1.py +0 -425
  37. hardpy-0.13.0/hardpy/pytest_hardpy/utils/const.py +0 -19
  38. hardpy-0.13.0/hardpy/pytest_hardpy/utils/exception.py +0 -65
  39. {hardpy-0.13.0 → hardpy-0.15.0}/.gitignore +0 -0
  40. {hardpy-0.13.0 → hardpy-0.15.0}/LICENSE +0 -0
  41. {hardpy-0.13.0 → hardpy-0.15.0}/README.md +0 -0
  42. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/cli/__init__.py +0 -0
  43. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/cli/template.py +0 -0
  44. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/common/__init__.py +0 -0
  45. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/common/stand_cloud/__init__.py +0 -0
  46. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/common/stand_cloud/exception.py +0 -0
  47. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/common/stand_cloud/oauth2.py +0 -0
  48. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/common/stand_cloud/registration.py +0 -0
  49. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/common/stand_cloud/token_manager.py +0 -0
  50. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/common/stand_cloud/utils.py +0 -0
  51. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/__init__.py +0 -0
  52. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-Bfs1BwbR.ttf +0 -0
  53. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-Btb8d-Hu.woff +0 -0
  54. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-CzsyEoPG.svg +0 -0
  55. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-DrH54W_x.woff2 +0 -0
  56. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-RCDSkC4W.eot +0 -0
  57. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-BGGGsqDJ.ttf +0 -0
  58. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-D9WO2FSG.woff2 +0 -0
  59. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-Doom1bSH.eot +0 -0
  60. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-DyVnGNfQ.svg +0 -0
  61. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-ZW-9JnPf.woff +0 -0
  62. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/assets/index-BMEat_ws.js +0 -0
  63. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/assets/index-BwCQzehg.css +0 -0
  64. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/assets/index-xb4M2ucX.js +0 -0
  65. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/assets/logo_smol-CK3jE85c.png +0 -0
  66. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/favicon.ico +0 -0
  67. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/logo192.png +0 -0
  68. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/logo512.png +0 -0
  69. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/hardpy_panel/frontend/dist/manifest.json +0 -0
  70. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/__init__.py +0 -0
  71. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/db/__init__.py +0 -0
  72. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/db/base_connector.py +0 -0
  73. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/db/base_server.py +0 -0
  74. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/db/runstore.py +0 -0
  75. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/db/schema/__init__.py +0 -0
  76. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/db/statestore.py +0 -0
  77. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/reporter/__init__.py +0 -0
  78. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/reporter/base.py +0 -0
  79. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/reporter/runner_reporter.py +0 -0
  80. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/result/__init__.py +0 -0
  81. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/result/couchdb_config.py +0 -0
  82. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/result/report_loader/__init__.py +0 -0
  83. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/result/report_loader/couchdb_loader.py +0 -0
  84. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/result/report_reader/__init__.py +0 -0
  85. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/result/report_reader/stand_cloud_reader.py +0 -0
  86. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/utils/connection_data.py +0 -0
  87. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/utils/dialog_box.py +0 -0
  88. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/utils/machineid.py +0 -0
  89. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/utils/progress_calculator.py +0 -0
  90. {hardpy-0.13.0 → hardpy-0.15.0}/hardpy/pytest_hardpy/utils/singleton.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hardpy
3
- Version: 0.13.0
3
+ Version: 0.15.0
4
4
  Summary: HardPy library for device testing
5
5
  Project-URL: Homepage, https://github.com/everypinio/hardpy/
6
6
  Project-URL: Documentation, https://everypinio.github.io/hardpy/
@@ -51,6 +51,7 @@ Requires-Dist: ruff==0.8.0; extra == 'dev'
51
51
  Requires-Dist: wemake-python-styleguide>=0.19.2; extra == 'dev'
52
52
  Provides-Extra: tests
53
53
  Requires-Dist: psutil~=7.0.0; extra == 'tests'
54
+ Requires-Dist: pytest-timeout==2.4.0; extra == 'tests'
54
55
  Description-Content-Type: text/markdown
55
56
 
56
57
  <h1 align="center">
@@ -3,23 +3,37 @@
3
3
 
4
4
  from hardpy.common.stand_cloud import StandCloudConnector, StandCloudError
5
5
  from hardpy.pytest_hardpy.pytest_call import (
6
+ ErrorCode,
6
7
  clear_operator_message,
7
8
  get_current_attempt,
8
9
  get_current_report,
9
10
  run_dialog_box,
11
+ set_batch_serial_number,
10
12
  set_case_artifact,
13
+ set_case_chart,
14
+ set_case_measurement,
11
15
  set_driver_info,
12
16
  set_dut_info,
17
+ set_dut_name,
13
18
  set_dut_part_number,
19
+ set_dut_revision,
14
20
  set_dut_serial_number,
21
+ set_dut_sub_unit,
22
+ set_dut_type,
23
+ set_instrument,
15
24
  set_message,
16
25
  set_module_artifact,
17
26
  set_operator_message,
27
+ set_process_info,
28
+ set_process_name,
29
+ set_process_number,
18
30
  set_run_artifact,
19
31
  set_stand_info,
20
32
  set_stand_location,
21
33
  set_stand_name,
22
34
  set_stand_number,
35
+ set_stand_revision,
36
+ set_user_name,
23
37
  )
24
38
  from hardpy.pytest_hardpy.result import (
25
39
  CouchdbLoader,
@@ -29,61 +43,82 @@ from hardpy.pytest_hardpy.result import (
29
43
  from hardpy.pytest_hardpy.result.couchdb_config import CouchdbConfig
30
44
  from hardpy.pytest_hardpy.utils import (
31
45
  BaseWidget,
46
+ Chart,
47
+ ChartType,
32
48
  CheckboxWidget,
49
+ ComparisonOperation,
33
50
  DialogBox,
34
- DuplicatePartNumberError,
35
- DuplicateSerialNumberError,
36
- DuplicateTestStandLocationError,
37
- DuplicateTestStandNameError,
38
- DuplicateTestStandNumberError,
51
+ DuplicateParameterError,
39
52
  HTMLComponent,
40
53
  ImageComponent,
54
+ Instrument,
41
55
  MultistepWidget,
42
56
  NumericInputWidget,
57
+ NumericMeasurement,
43
58
  RadiobuttonWidget,
44
59
  StepWidget,
60
+ StringMeasurement,
61
+ SubUnit,
45
62
  TestStandNumberError,
46
63
  TextInputWidget,
47
64
  )
48
65
 
49
66
  __all__ = [
50
67
  "BaseWidget",
68
+ "Chart",
69
+ "ChartType",
51
70
  "CheckboxWidget",
71
+ "ComparisonOperation",
52
72
  "CouchdbConfig",
53
73
  "CouchdbLoader",
54
74
  "DialogBox",
55
- "DuplicatePartNumberError",
56
- "DuplicateSerialNumberError",
57
- "DuplicateTestStandLocationError",
58
- "DuplicateTestStandNameError",
59
- "DuplicateTestStandNumberError",
75
+ "DuplicateParameterError",
76
+ "ErrorCode",
60
77
  "HTMLComponent",
61
78
  "ImageComponent",
79
+ "Instrument",
62
80
  "MultistepWidget",
63
81
  "NumericInputWidget",
82
+ "NumericMeasurement",
64
83
  "RadiobuttonWidget",
65
84
  "StandCloudConnector",
66
85
  "StandCloudError",
67
86
  "StandCloudLoader",
68
87
  "StandCloudReader",
69
88
  "StepWidget",
89
+ "StringMeasurement",
90
+ "SubUnit",
70
91
  "TestStandNumberError",
71
92
  "TextInputWidget",
72
93
  "clear_operator_message",
73
94
  "get_current_attempt",
74
95
  "get_current_report",
75
96
  "run_dialog_box",
97
+ "set_batch_serial_number",
76
98
  "set_case_artifact",
99
+ "set_case_chart",
100
+ "set_case_measurement",
77
101
  "set_driver_info",
102
+ "set_dut",
78
103
  "set_dut_info",
104
+ "set_dut_name",
79
105
  "set_dut_part_number",
106
+ "set_dut_revision",
80
107
  "set_dut_serial_number",
108
+ "set_dut_sub_unit",
109
+ "set_dut_type",
110
+ "set_instrument",
81
111
  "set_message",
82
112
  "set_module_artifact",
83
113
  "set_operator_message",
114
+ "set_process_info",
115
+ "set_process_name",
116
+ "set_process_number",
84
117
  "set_run_artifact",
85
118
  "set_stand_info",
86
119
  "set_stand_location",
87
120
  "set_stand_name",
88
121
  "set_stand_number",
122
+ "set_stand_revision",
123
+ "set_user_name",
89
124
  ]
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import socket
6
6
  import sys
7
+ import urllib
7
8
  from pathlib import Path
8
9
  from typing import Annotated, Optional
9
10
 
@@ -86,25 +87,24 @@ def init( # noqa: PLR0913
86
87
  database_port (int): Database port
87
88
  frontend_host (str): Panel operator host
88
89
  frontend_port (int): Panel operator port
90
+ frontend_language (str): Panel operator language
89
91
  sc_address (str): StandCloud address
90
92
  sc_connection_only (bool): Flag to check StandCloud service availability
91
93
  """
92
- _tests_dir = tests_dir if tests_dir else default_config.tests_dir
93
- _tests_name = tests_name if tests_name else default_config.tests_name
94
+ dir_path = Path(Path.cwd() / tests_dir if tests_dir else "tests")
94
95
  ConfigManager().init_config(
95
- tests_dir=str(_tests_dir),
96
- tests_name=_tests_name,
96
+ tests_name=tests_name if tests_name else dir_path.name,
97
97
  database_user=database_user,
98
98
  database_password=database_password,
99
99
  database_host=database_host,
100
100
  database_port=database_port,
101
101
  frontend_host=frontend_host,
102
102
  frontend_port=frontend_port,
103
+ frontend_language=default_config.frontend.language,
103
104
  sc_address=sc_address,
104
105
  sc_connection_only=sc_connection_only,
105
106
  )
106
107
  # create tests directory
107
- dir_path = Path(Path.cwd() / _tests_dir)
108
108
  Path.mkdir(dir_path, exist_ok=True, parents=True)
109
109
 
110
110
  if create_database:
@@ -113,11 +113,8 @@ def init( # noqa: PLR0913
113
113
 
114
114
  # create hardpy.toml
115
115
  ConfigManager().create_config(dir_path)
116
- config = ConfigManager().read_config(dir_path)
117
- if not config:
118
- print(f"hardpy.toml config by path {dir_path} not detected.")
119
- sys.exit()
120
116
 
117
+ config = _get_config(dir_path)
121
118
  template = TemplateGenerator(config)
122
119
 
123
120
  files = {}
@@ -143,12 +140,7 @@ def run(tests_dir: Annotated[Optional[str], typer.Argument()] = None) -> None:
143
140
  Args:
144
141
  tests_dir (Optional[str]): Test directory. Current directory by default
145
142
  """
146
- dir_path = Path.cwd() / tests_dir if tests_dir else Path.cwd()
147
- config = ConfigManager().read_config(dir_path)
148
-
149
- if not config:
150
- print(f"Config at path {dir_path} not found.")
151
- sys.exit()
143
+ config = _get_config(tests_dir)
152
144
 
153
145
  print("\nLaunch the HardPy operator panel...")
154
146
 
@@ -168,16 +160,29 @@ def run(tests_dir: Annotated[Optional[str], typer.Argument()] = None) -> None:
168
160
 
169
161
 
170
162
  @cli.command()
171
- def start(tests_dir: Annotated[Optional[str], typer.Argument()] = None) -> None:
163
+ def start(
164
+ ctx: typer.Context,
165
+ tests_dir: Annotated[Optional[str], typer.Argument()] = None,
166
+ arg: list[str] = typer.Option( # noqa: B008
167
+ [],
168
+ "--arg",
169
+ "-a",
170
+ help="Dynamic start arguments (format: key=value)",
171
+ ),
172
+ ) -> None:
172
173
  """Start HardPy tests.
173
174
 
174
175
  Args:
176
+ ctx: Typer context for accessing arguments from other sources
175
177
  tests_dir (Optional[str]): Test directory. Current directory by default
178
+ arg (list[str]): Dynamic arguments for test execution
176
179
  """
177
- config = _get_config(tests_dir)
178
- _check_config(config)
180
+ context_args = getattr(ctx, "hardpy_args", [])
181
+ all_args = arg + context_args
179
182
 
180
- url = f"http://{config.frontend.host}:{config.frontend.port}/api/start"
183
+ config = _get_config(tests_dir, validate=True)
184
+ query_args = "&".join([f"args={urllib.parse.quote(a)}" for a in all_args])
185
+ url = f"http://{config.frontend.host}:{config.frontend.port}/api/start?{query_args}"
181
186
  _request_hardpy(url)
182
187
 
183
188
 
@@ -188,9 +193,7 @@ def stop(tests_dir: Annotated[Optional[str], typer.Argument()] = None) -> None:
188
193
  Args:
189
194
  tests_dir (Optional[str]): Test directory. Current directory by default
190
195
  """
191
- config = _get_config(tests_dir)
192
- _check_config(config)
193
-
196
+ config = _get_config(tests_dir, validate=True)
194
197
  url = f"http://{config.frontend.host}:{config.frontend.port}/api/stop"
195
198
  _request_hardpy(url)
196
199
 
@@ -202,9 +205,7 @@ def status(tests_dir: Annotated[Optional[str], typer.Argument()] = None) -> None
202
205
  Args:
203
206
  tests_dir (Optional[str]): Test directory. Current directory by default
204
207
  """
205
- config = _get_config(tests_dir)
206
- _check_config(config)
207
-
208
+ config = _get_config(tests_dir, validate=True)
208
209
  url = f"http://{config.frontend.host}:{config.frontend.port}/api/status"
209
210
  _request_hardpy(url)
210
211
 
@@ -257,7 +258,7 @@ def sc_logout(address: Annotated[str, typer.Argument()]) -> None:
257
258
  print(f"HardPy logout failed from {address}")
258
259
 
259
260
 
260
- def _get_config(tests_dir: str | None = None) -> HardpyConfig:
261
+ def _get_config(tests_dir: str | None = None, validate: bool = False) -> HardpyConfig:
261
262
  dir_path = Path.cwd() / tests_dir if tests_dir else Path.cwd()
262
263
  config = ConfigManager().read_config(dir_path)
263
264
 
@@ -265,12 +266,15 @@ def _get_config(tests_dir: str | None = None) -> HardpyConfig:
265
266
  print(f"Config at path {dir_path} not found.")
266
267
  sys.exit()
267
268
 
269
+ if validate:
270
+ _validate_config(config, dir_path)
271
+
268
272
  return config
269
273
 
270
274
 
271
- def _check_config(config: HardpyConfig) -> None:
275
+ def _validate_config(config: HardpyConfig, tests_dir: str) -> None:
272
276
  url = f"http://{config.frontend.host}:{config.frontend.port}/api/hardpy_config"
273
- error_msg = f"HardPy in directory {config.tests_dir} does not run."
277
+ error_msg = f"HardPy in directory {tests_dir} does not run."
274
278
  try:
275
279
  response = requests.get(url, timeout=2)
276
280
  except Exception:
@@ -40,6 +40,7 @@ class FrontendConfig(BaseModel):
40
40
 
41
41
  host: str = "localhost"
42
42
  port: int = 8000
43
+ language: str = "en"
43
44
 
44
45
 
45
46
  class StandCloudConfig(BaseModel):
@@ -50,13 +51,13 @@ class StandCloudConfig(BaseModel):
50
51
  address: str = ""
51
52
  connection_only: bool = False
52
53
 
54
+
53
55
  class HardpyConfig(BaseModel, extra="allow"):
54
56
  """HardPy configuration."""
55
57
 
56
58
  model_config = ConfigDict(extra="forbid")
57
59
 
58
60
  title: str = "HardPy TOML config"
59
- tests_dir: str = "tests"
60
61
  tests_name: str = ""
61
62
  database: DatabaseConfig = DatabaseConfig()
62
63
  frontend: FrontendConfig = FrontendConfig()
@@ -72,7 +73,6 @@ class ConfigManager:
72
73
  @classmethod
73
74
  def init_config( # noqa: PLR0913
74
75
  cls,
75
- tests_dir: str,
76
76
  tests_name: str,
77
77
  database_user: str,
78
78
  database_password: str,
@@ -80,13 +80,13 @@ class ConfigManager:
80
80
  database_port: int,
81
81
  frontend_host: str,
82
82
  frontend_port: int,
83
+ frontend_language: str,
83
84
  sc_address: str = "",
84
85
  sc_connection_only: bool = False,
85
86
  ) -> None:
86
87
  """Initialize HardPy configuration.
87
88
 
88
89
  Args:
89
- tests_dir (str): Tests directory.
90
90
  tests_name (str): Tests suite name.
91
91
  database_user (str): Database user name.
92
92
  database_password (str): Database password.
@@ -94,10 +94,10 @@ class ConfigManager:
94
94
  database_port (int): Database port.
95
95
  frontend_host (str): Operator panel host.
96
96
  frontend_port (int): Operator panel port.
97
+ frontend_language (str): Operator panel language.
97
98
  sc_address (str): StandCloud address.
98
99
  sc_connection_only (bool): StandCloud check availability.
99
100
  """
100
- cls.obj.tests_dir = str(tests_dir)
101
101
  cls.obj.tests_name = tests_name
102
102
  cls.obj.database.user = database_user
103
103
  cls.obj.database.password = database_password
@@ -105,6 +105,7 @@ class ConfigManager:
105
105
  cls.obj.database.port = database_port
106
106
  cls.obj.frontend.host = frontend_host
107
107
  cls.obj.frontend.port = frontend_port
108
+ cls.obj.frontend.language = frontend_language
108
109
  cls.obj.stand_cloud.address = sc_address
109
110
  cls.obj.stand_cloud.connection_only = sc_connection_only
110
111
 
@@ -96,7 +96,6 @@ class StandCloudConnector:
96
96
  return False
97
97
  return True
98
98
 
99
-
100
99
  def get_access_token(self) -> BearerToken | None:
101
100
  """Read access token from token store.
102
101
 
@@ -125,7 +124,8 @@ class StandCloudConnector:
125
124
  timeout=10,
126
125
  )
127
126
  if req.status_code != HTTPStatus.OK:
128
- raise StandCloudError(req.text)
127
+ msg = f"status code {req.status_code}, {req.reason}, {req.text}"
128
+ raise StandCloudError(msg)
129
129
  return json.loads(req.content)
130
130
 
131
131
  def get_api(self, endpoint: str) -> ApiClient:
@@ -1,13 +1,15 @@
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 os
5
6
  import re
6
7
  from enum import Enum
7
8
  from pathlib import Path
9
+ from typing import Annotated
8
10
  from urllib.parse import unquote
9
11
 
10
- from fastapi import FastAPI
12
+ from fastapi import FastAPI, Query
11
13
  from fastapi.staticfiles import StaticFiles
12
14
 
13
15
  from hardpy.common.config import ConfigManager
@@ -42,13 +44,21 @@ def hardpy_config() -> dict:
42
44
 
43
45
 
44
46
  @app.get("/api/start")
45
- def start_pytest() -> dict:
47
+ def start_pytest(args: Annotated[list[str] | None, Query()] = None) -> dict:
46
48
  """Start pytest subprocess.
47
49
 
50
+ Args:
51
+ args: List of arguments in key=value format
52
+
48
53
  Returns:
49
54
  dict[str, RunStatus]: run status
50
55
  """
51
- if app.state.pytest_wrp.start():
56
+ if args is None:
57
+ args_dict = []
58
+ else:
59
+ args_dict = dict(arg.split("=", 1) for arg in args if "=" in arg)
60
+
61
+ if app.state.pytest_wrp.start(start_args=args_dict):
52
62
  return {"status": Status.STARTED}
53
63
  return {"status": Status.BUSY}
54
64
 
@@ -1 +1 @@
1
- import{I as n}from"./index-xb4M2ucX.js";import{I as e}from"./index-BMEat_ws.js";import{p as r,I as s}from"./index-De5CJ3kt.js";function I(o,t){var a=r(o);return t===s.STANDARD?n[a]:e[a]}function p(o){return r(o)}export{n as IconSvgPaths16,e as IconSvgPaths20,I as getIconPaths,p as iconNameToPathsRecordKey};
1
+ import{I as n}from"./index-xb4M2ucX.js";import{I as e}from"./index-BMEat_ws.js";import{p as r,I as s}from"./index-6RIgWzcZ.js";function I(o,t){var a=r(o);return t===s.STANDARD?n[a]:e[a]}function p(o){return r(o)}export{n as IconSvgPaths16,e as IconSvgPaths20,I as getIconPaths,p as iconNameToPathsRecordKey};
@@ -1,2 +1,2 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/allPaths-Cg7WZDXy.js","assets/index-xb4M2ucX.js","assets/index-BMEat_ws.js","assets/index-De5CJ3kt.js","assets/index-BwCQzehg.css"])))=>i.map(i=>d[i]);
2
- import{_ as o,a as n,b as i}from"./index-De5CJ3kt.js";var _=function(e,a){return o(void 0,void 0,void 0,function(){var t;return n(this,function(r){switch(r.label){case 0:return[4,i(()=>import("./allPaths-Cg7WZDXy.js"),__vite__mapDeps([0,1,2,3,4]))];case 1:return t=r.sent().getIconPaths,[2,t(e,a)]}})})};export{_ as allPathsLoader};
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/allPaths-CV5wjLMB.js","assets/index-xb4M2ucX.js","assets/index-BMEat_ws.js","assets/index-6RIgWzcZ.js","assets/index-BwCQzehg.css"])))=>i.map(i=>d[i]);
2
+ import{_ as o,a as n,b as i}from"./index-6RIgWzcZ.js";var _=function(e,a){return o(void 0,void 0,void 0,function(){var t;return n(this,function(r){switch(r.label){case 0:return[4,i(()=>import("./allPaths-CV5wjLMB.js"),__vite__mapDeps([0,1,2,3,4]))];case 1:return t=r.sent().getIconPaths,[2,t(e,a)]}})})};export{_ as allPathsLoader};
@@ -0,0 +1,2 @@
1
+ import{g as G}from"./index-6RIgWzcZ.js";function $(w,c){for(var m=0;m<c.length;m++){const d=c[m];if(typeof d!="string"&&!Array.isArray(d)){for(const y in d)if(y!=="default"&&!(y in w)){const p=Object.getOwnPropertyDescriptor(d,y);p&&Object.defineProperty(w,y,p.get?p:{enumerable:!0,get:()=>d[y]})}}}return Object.freeze(Object.defineProperty(w,Symbol.toStringTag,{value:"Module"}))}var E={exports:{}},U;function X(){return U||(U=1,function(w,c){var m={},d=typeof globalThis<"u"&&globalThis||typeof self<"u"&&self||typeof m<"u"&&m,y=function(){function v(){this.fetch=!1,this.DOMException=d.DOMException}return v.prototype=d,new v}();(function(v){(function(u){var a=typeof v<"u"&&v||typeof self<"u"&&self||typeof a<"u"&&a,f={searchParams:"URLSearchParams"in a,iterable:"Symbol"in a&&"iterator"in Symbol,blob:"FileReader"in a&&"Blob"in a&&function(){try{return new Blob,!0}catch{return!1}}(),formData:"FormData"in a,arrayBuffer:"ArrayBuffer"in a};function S(e){return e&&DataView.prototype.isPrototypeOf(e)}if(f.arrayBuffer)var F=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],I=ArrayBuffer.isView||function(e){return e&&F.indexOf(Object.prototype.toString.call(e))>-1};function _(e){if(typeof e!="string"&&(e=String(e)),/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(e)||e==="")throw new TypeError('Invalid character in header field name: "'+e+'"');return e.toLowerCase()}function T(e){return typeof e!="string"&&(e=String(e)),e}function B(e){var t={next:function(){var r=e.shift();return{done:r===void 0,value:r}}};return f.iterable&&(t[Symbol.iterator]=function(){return t}),t}function s(e){this.map={},e instanceof s?e.forEach(function(t,r){this.append(r,t)},this):Array.isArray(e)?e.forEach(function(t){this.append(t[0],t[1])},this):e&&Object.getOwnPropertyNames(e).forEach(function(t){this.append(t,e[t])},this)}s.prototype.append=function(e,t){e=_(e),t=T(t);var r=this.map[e];this.map[e]=r?r+", "+t:t},s.prototype.delete=function(e){delete this.map[_(e)]},s.prototype.get=function(e){return e=_(e),this.has(e)?this.map[e]:null},s.prototype.has=function(e){return this.map.hasOwnProperty(_(e))},s.prototype.set=function(e,t){this.map[_(e)]=T(t)},s.prototype.forEach=function(e,t){for(var r in this.map)this.map.hasOwnProperty(r)&&e.call(t,this.map[r],r,this)},s.prototype.keys=function(){var e=[];return this.forEach(function(t,r){e.push(r)}),B(e)},s.prototype.values=function(){var e=[];return this.forEach(function(t){e.push(t)}),B(e)},s.prototype.entries=function(){var e=[];return this.forEach(function(t,r){e.push([r,t])}),B(e)},f.iterable&&(s.prototype[Symbol.iterator]=s.prototype.entries);function O(e){if(e.bodyUsed)return Promise.reject(new TypeError("Already read"));e.bodyUsed=!0}function D(e){return new Promise(function(t,r){e.onload=function(){t(e.result)},e.onerror=function(){r(e.error)}})}function M(e){var t=new FileReader,r=D(t);return t.readAsArrayBuffer(e),r}function q(e){var t=new FileReader,r=D(t);return t.readAsText(e),r}function H(e){for(var t=new Uint8Array(e),r=new Array(t.length),n=0;n<t.length;n++)r[n]=String.fromCharCode(t[n]);return r.join("")}function x(e){if(e.slice)return e.slice(0);var t=new Uint8Array(e.byteLength);return t.set(new Uint8Array(e)),t.buffer}function R(){return this.bodyUsed=!1,this._initBody=function(e){this.bodyUsed=this.bodyUsed,this._bodyInit=e,e?typeof e=="string"?this._bodyText=e:f.blob&&Blob.prototype.isPrototypeOf(e)?this._bodyBlob=e:f.formData&&FormData.prototype.isPrototypeOf(e)?this._bodyFormData=e:f.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)?this._bodyText=e.toString():f.arrayBuffer&&f.blob&&S(e)?(this._bodyArrayBuffer=x(e.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):f.arrayBuffer&&(ArrayBuffer.prototype.isPrototypeOf(e)||I(e))?this._bodyArrayBuffer=x(e):this._bodyText=e=Object.prototype.toString.call(e):this._bodyText="",this.headers.get("content-type")||(typeof e=="string"?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):f.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},f.blob&&(this.blob=function(){var e=O(this);if(e)return e;if(this._bodyBlob)return Promise.resolve(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(new Blob([this._bodyArrayBuffer]));if(this._bodyFormData)throw new Error("could not read FormData body as blob");return Promise.resolve(new Blob([this._bodyText]))},this.arrayBuffer=function(){if(this._bodyArrayBuffer){var e=O(this);return e||(ArrayBuffer.isView(this._bodyArrayBuffer)?Promise.resolve(this._bodyArrayBuffer.buffer.slice(this._bodyArrayBuffer.byteOffset,this._bodyArrayBuffer.byteOffset+this._bodyArrayBuffer.byteLength)):Promise.resolve(this._bodyArrayBuffer))}else return this.blob().then(M)}),this.text=function(){var e=O(this);if(e)return e;if(this._bodyBlob)return q(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(H(this._bodyArrayBuffer));if(this._bodyFormData)throw new Error("could not read FormData body as text");return Promise.resolve(this._bodyText)},f.formData&&(this.formData=function(){return this.text().then(k)}),this.json=function(){return this.text().then(JSON.parse)},this}var L=["DELETE","GET","HEAD","OPTIONS","POST","PUT"];function C(e){var t=e.toUpperCase();return L.indexOf(t)>-1?t:e}function b(e,t){if(!(this instanceof b))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');t=t||{};var r=t.body;if(e instanceof b){if(e.bodyUsed)throw new TypeError("Already read");this.url=e.url,this.credentials=e.credentials,t.headers||(this.headers=new s(e.headers)),this.method=e.method,this.mode=e.mode,this.signal=e.signal,!r&&e._bodyInit!=null&&(r=e._bodyInit,e.bodyUsed=!0)}else this.url=String(e);if(this.credentials=t.credentials||this.credentials||"same-origin",(t.headers||!this.headers)&&(this.headers=new s(t.headers)),this.method=C(t.method||this.method||"GET"),this.mode=t.mode||this.mode||null,this.signal=t.signal||this.signal,this.referrer=null,(this.method==="GET"||this.method==="HEAD")&&r)throw new TypeError("Body not allowed for GET or HEAD requests");if(this._initBody(r),(this.method==="GET"||this.method==="HEAD")&&(t.cache==="no-store"||t.cache==="no-cache")){var n=/([?&])_=[^&]*/;if(n.test(this.url))this.url=this.url.replace(n,"$1_="+new Date().getTime());else{var i=/\?/;this.url+=(i.test(this.url)?"&":"?")+"_="+new Date().getTime()}}}b.prototype.clone=function(){return new b(this,{body:this._bodyInit})};function k(e){var t=new FormData;return e.trim().split("&").forEach(function(r){if(r){var n=r.split("="),i=n.shift().replace(/\+/g," "),o=n.join("=").replace(/\+/g," ");t.append(decodeURIComponent(i),decodeURIComponent(o))}}),t}function N(e){var t=new s,r=e.replace(/\r?\n[\t ]+/g," ");return r.split("\r").map(function(n){return n.indexOf(`
2
+ `)===0?n.substr(1,n.length):n}).forEach(function(n){var i=n.split(":"),o=i.shift().trim();if(o){var g=i.join(":").trim();t.append(o,g)}}),t}R.call(b.prototype);function l(e,t){if(!(this instanceof l))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');t||(t={}),this.type="default",this.status=t.status===void 0?200:t.status,this.ok=this.status>=200&&this.status<300,this.statusText=t.statusText===void 0?"":""+t.statusText,this.headers=new s(t.headers),this.url=t.url||"",this._initBody(e)}R.call(l.prototype),l.prototype.clone=function(){return new l(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new s(this.headers),url:this.url})},l.error=function(){var e=new l(null,{status:0,statusText:""});return e.type="error",e};var V=[301,302,303,307,308];l.redirect=function(e,t){if(V.indexOf(t)===-1)throw new RangeError("Invalid status code");return new l(null,{status:t,headers:{location:e}})},u.DOMException=a.DOMException;try{new u.DOMException}catch{u.DOMException=function(t,r){this.message=t,this.name=r;var n=Error(t);this.stack=n.stack},u.DOMException.prototype=Object.create(Error.prototype),u.DOMException.prototype.constructor=u.DOMException}function P(e,t){return new Promise(function(r,n){var i=new b(e,t);if(i.signal&&i.signal.aborted)return n(new u.DOMException("Aborted","AbortError"));var o=new XMLHttpRequest;function g(){o.abort()}o.onload=function(){var h={status:o.status,statusText:o.statusText,headers:N(o.getAllResponseHeaders()||"")};h.url="responseURL"in o?o.responseURL:h.headers.get("X-Request-URL");var A="response"in o?o.response:o.responseText;setTimeout(function(){r(new l(A,h))},0)},o.onerror=function(){setTimeout(function(){n(new TypeError("Network request failed"))},0)},o.ontimeout=function(){setTimeout(function(){n(new TypeError("Network request failed"))},0)},o.onabort=function(){setTimeout(function(){n(new u.DOMException("Aborted","AbortError"))},0)};function z(h){try{return h===""&&a.location.href?a.location.href:h}catch{return h}}o.open(i.method,z(i.url),!0),i.credentials==="include"?o.withCredentials=!0:i.credentials==="omit"&&(o.withCredentials=!1),"responseType"in o&&(f.blob?o.responseType="blob":f.arrayBuffer&&i.headers.get("Content-Type")&&i.headers.get("Content-Type").indexOf("application/octet-stream")!==-1&&(o.responseType="arraybuffer")),t&&typeof t.headers=="object"&&!(t.headers instanceof s)?Object.getOwnPropertyNames(t.headers).forEach(function(h){o.setRequestHeader(h,T(t.headers[h]))}):i.headers.forEach(function(h,A){o.setRequestHeader(A,h)}),i.signal&&(i.signal.addEventListener("abort",g),o.onreadystatechange=function(){o.readyState===4&&i.signal.removeEventListener("abort",g)}),o.send(typeof i._bodyInit>"u"?null:i._bodyInit)})}return P.polyfill=!0,a.fetch||(a.fetch=P,a.Headers=s,a.Request=b,a.Response=l),u.Headers=s,u.Request=b,u.Response=l,u.fetch=P,u})({})})(y),y.fetch.ponyfill=!0,delete y.fetch.polyfill;var p=d.fetch?d:y;c=p.fetch,c.default=p.fetch,c.fetch=p.fetch,c.Headers=p.Headers,c.Request=p.Request,c.Response=p.Response,w.exports=c}(E,E.exports)),E.exports}var j=X();const J=G(j),Q=$({__proto__:null,default:J},[j]);export{Q as b};