hardpy 0.15.2__py3-none-any.whl → 0.16.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 (62) hide show
  1. hardpy/__init__.py +7 -5
  2. hardpy/cli/cli.py +16 -10
  3. hardpy/common/config.py +70 -50
  4. hardpy/{pytest_hardpy/utils → common}/singleton.py +1 -1
  5. hardpy/hardpy_panel/api.py +13 -2
  6. hardpy/hardpy_panel/frontend/dist/assets/{allPaths-CV5wjLMB.js → allPaths-Cy69sdSD.js} +1 -1
  7. hardpy/hardpy_panel/frontend/dist/assets/{allPathsLoader-JIzW_pSb.js → allPathsLoader-D993NqQ9.js} +2 -2
  8. hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-16-Bfs1BwbR.ttf → blueprint-icons-16-B2twAPZE.ttf} +0 -0
  9. hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-16-RCDSkC4W.eot → blueprint-icons-16-C0Unyq1d.eot} +0 -0
  10. hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-16-CzsyEoPG.svg → blueprint-icons-16-CVy9qFng.svg} +249 -3
  11. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-Ck1ifK4A.woff +0 -0
  12. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-DwWyHYRo.woff2 +0 -0
  13. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-9zitLjlL.woff2 +0 -0
  14. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-CjKGIKxE.woff +0 -0
  15. hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-20-DyVnGNfQ.svg → blueprint-icons-20-DQ09GSQq.svg} +249 -3
  16. hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-20-BGGGsqDJ.ttf → blueprint-icons-20-DmR755bS.ttf} +0 -0
  17. hardpy/hardpy_panel/frontend/dist/assets/{blueprint-icons-20-Doom1bSH.eot → blueprint-icons-20-p9MhBXD8.eot} +0 -0
  18. hardpy/hardpy_panel/frontend/dist/assets/browser-ponyfill-DD76sq2d.js +2 -0
  19. hardpy/hardpy_panel/frontend/dist/assets/index-B-fsa5Ru.js +1 -0
  20. hardpy/hardpy_panel/frontend/dist/assets/index-B7T9xvaW.css +1 -0
  21. hardpy/hardpy_panel/frontend/dist/assets/index-C93zcGIi.js +4672 -0
  22. hardpy/hardpy_panel/frontend/dist/assets/index-DLOviMB1.js +1 -0
  23. hardpy/hardpy_panel/frontend/dist/assets/{splitPathsBySizeLoader-DkZadBcn.js → splitPathsBySizeLoader-D4hRORV6.js} +1 -1
  24. hardpy/hardpy_panel/frontend/dist/index.html +2 -2
  25. hardpy/hardpy_panel/frontend/dist/locales/de/translation.json +9 -0
  26. hardpy/hardpy_panel/frontend/dist/locales/en/translation.json +9 -0
  27. hardpy/hardpy_panel/frontend/dist/locales/es/translation.json +9 -0
  28. hardpy/hardpy_panel/frontend/dist/locales/fr/translation.json +9 -0
  29. hardpy/hardpy_panel/frontend/dist/locales/ja/translation.json +9 -0
  30. hardpy/hardpy_panel/frontend/dist/locales/ru/translation.json +9 -0
  31. hardpy/hardpy_panel/frontend/dist/locales/zh/translation.json +9 -0
  32. hardpy/pytest_hardpy/db/__init__.py +12 -0
  33. hardpy/pytest_hardpy/db/base_store.py +25 -4
  34. hardpy/pytest_hardpy/db/runstore.py +1 -1
  35. hardpy/pytest_hardpy/db/schema/v1.py +8 -6
  36. hardpy/pytest_hardpy/db/statestore.py +1 -1
  37. hardpy/pytest_hardpy/plugin.py +30 -22
  38. hardpy/pytest_hardpy/pytest_call.py +12 -23
  39. hardpy/pytest_hardpy/pytest_wrapper.py +9 -7
  40. hardpy/pytest_hardpy/reporter/base.py +21 -1
  41. hardpy/pytest_hardpy/reporter/runner_reporter.py +1 -1
  42. hardpy/pytest_hardpy/result/report_loader/stand_cloud_loader.py +35 -4
  43. hardpy/pytest_hardpy/utils/__init__.py +0 -16
  44. {hardpy-0.15.2.dist-info → hardpy-0.16.0.dist-info}/METADATA +2 -2
  45. hardpy-0.16.0.dist-info/RECORD +83 -0
  46. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-Btb8d-Hu.woff +0 -0
  47. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-DrH54W_x.woff2 +0 -0
  48. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-D9WO2FSG.woff2 +0 -0
  49. hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-ZW-9JnPf.woff +0 -0
  50. hardpy/hardpy_panel/frontend/dist/assets/browser-ponyfill-CccdstaD.js +0 -2
  51. hardpy/hardpy_panel/frontend/dist/assets/index-6RIgWzcZ.js +0 -790
  52. hardpy/hardpy_panel/frontend/dist/assets/index-BMEat_ws.js +0 -1
  53. hardpy/hardpy_panel/frontend/dist/assets/index-BwCQzehg.css +0 -1
  54. hardpy/hardpy_panel/frontend/dist/assets/index-xb4M2ucX.js +0 -1
  55. hardpy/pytest_hardpy/db/base_connector.py +0 -31
  56. hardpy/pytest_hardpy/db/base_server.py +0 -14
  57. hardpy/pytest_hardpy/utils/connection_data.py +0 -13
  58. hardpy-0.15.2.dist-info/RECORD +0 -86
  59. /hardpy/pytest_hardpy/{utils → db}/stand_type.py +0 -0
  60. {hardpy-0.15.2.dist-info → hardpy-0.16.0.dist-info}/WHEEL +0 -0
  61. {hardpy-0.15.2.dist-info → hardpy-0.16.0.dist-info}/entry_points.txt +0 -0
  62. {hardpy-0.15.2.dist-info → hardpy-0.16.0.dist-info}/licenses/LICENSE +0 -0
@@ -1 +1 @@
1
- import{_ as o,a as _,b as i,p as c,I as u}from"./index-6RIgWzcZ.js";var p=function(n,s){return o(void 0,void 0,void 0,function(){var a,r;return _(this,function(e){switch(e.label){case 0:return a=c(n),s!==u.STANDARD?[3,2]:[4,i(()=>import("./index-xb4M2ucX.js").then(t=>t.I),[])];case 1:return r=e.sent(),[3,4];case 2:return[4,i(()=>import("./index-BMEat_ws.js").then(t=>t.I),[])];case 3:r=e.sent(),e.label=4;case 4:return[2,r[a]]}})})};export{p as splitPathsBySizeLoader};
1
+ import{_ as o,a as _,b as i,p as c,I as u}from"./index-C93zcGIi.js";var p=function(n,s){return o(void 0,void 0,void 0,function(){var a,r;return _(this,function(e){switch(e.label){case 0:return a=c(n),s!==u.STANDARD?[3,2]:[4,i(()=>import("./index-DLOviMB1.js").then(t=>t.I),[])];case 1:return r=e.sent(),[3,4];case 2:return[4,i(()=>import("./index-B-fsa5Ru.js").then(t=>t.I),[])];case 3:r=e.sent(),e.label=4;case 4:return[2,r[a]]}})})};export{p as splitPathsBySizeLoader};
@@ -25,8 +25,8 @@
25
25
  Learn how to configure a non-root public URL by running `npm run build`.
26
26
  -->
27
27
  <title>HardPy Operator Panel</title>
28
- <script type="module" crossorigin src="/assets/index-6RIgWzcZ.js"></script>
29
- <link rel="stylesheet" crossorigin href="/assets/index-BwCQzehg.css">
28
+ <script type="module" crossorigin src="/assets/index-C93zcGIi.js"></script>
29
+ <link rel="stylesheet" crossorigin href="/assets/index-B7T9xvaW.css">
30
30
  </head>
31
31
  <body>
32
32
  <noscript>You need to enable JavaScript to run this app.</noscript>
@@ -29,6 +29,15 @@
29
29
  "dbConnectionTitle": "Datenbankverbindungsfehler",
30
30
  "dbConnectionMessage": "Verbindung zur Datenbank konnte nicht hergestellt werden"
31
31
  },
32
+ "chart": {
33
+ "dataChart": "Diagrammdaten",
34
+ "xAxis": "X-Achse",
35
+ "yAxis": "Y-Achse",
36
+ "chart": "Diagramm",
37
+ "showChart": "Diagramm anzeigen {{title}}",
38
+ "fullscreenButton": "Diagramm im Vollbildmodus öffnen",
39
+ "series": "Reihe {{number}}"
40
+ },
32
41
  "operatorDialog": {
33
42
  "defaultTitle": "Nachricht",
34
43
  "imageAlt": "Operator-Nachrichtenbild",
@@ -29,6 +29,15 @@
29
29
  "dbConnectionTitle": "Database Connection Error",
30
30
  "dbConnectionMessage": "Failed to establish connection with the database"
31
31
  },
32
+ "chart": {
33
+ "dataChart": "Chart Data",
34
+ "xAxis": "X Axis",
35
+ "yAxis": "Y Axis",
36
+ "chart": "Chart",
37
+ "showChart": "Show chart {{title}}",
38
+ "fullscreenButton": "Open chart in full screen",
39
+ "series": "Series {{number}}"
40
+ },
32
41
  "operatorDialog": {
33
42
  "defaultTitle": "Message",
34
43
  "imageAlt": "Operator message image",
@@ -29,6 +29,15 @@
29
29
  "dbConnectionTitle": "Error de conexión a la base de datos",
30
30
  "dbConnectionMessage": "No se pudo establecer la conexión con la base de datos"
31
31
  },
32
+ "chart": {
33
+ "dataChart": "Datos del gráfico",
34
+ "xAxis": "Eje X",
35
+ "yAxis": "Eje Y",
36
+ "chart": "Gráfico",
37
+ "showChart": "Mostrar gráfico {{title}}",
38
+ "fullscreenButton": "Abrir gráfico en pantalla completa",
39
+ "series": "Serie {{number}}"
40
+ },
32
41
  "operatorDialog": {
33
42
  "defaultTitle": "Mensaje",
34
43
  "imageAlt": "Imagen del mensaje del operador",
@@ -29,6 +29,15 @@
29
29
  "dbConnectionTitle": "Erreur de connexion à la base de données",
30
30
  "dbConnectionMessage": "Échec de la connexion à la base de données"
31
31
  },
32
+ "chart": {
33
+ "dataChart": "Données du graphique",
34
+ "xAxis": "Axe X",
35
+ "yAxis": "Axe Y",
36
+ "chart": "Graphique",
37
+ "showChart": "Afficher le graphique {{title}}",
38
+ "fullscreenButton": "Ouvrir le graphique en plein écran",
39
+ "series": "Série {{number}}"
40
+ },
32
41
  "operatorDialog": {
33
42
  "defaultTitle": "Message",
34
43
  "imageAlt": "Image du message de l'opérateur",
@@ -29,6 +29,15 @@
29
29
  "dbConnectionTitle": "データベース接続エラー",
30
30
  "dbConnectionMessage": "データベースへの接続に失敗しました"
31
31
  },
32
+ "chart": {
33
+ "dataChart": "チャートデータ",
34
+ "xAxis": "X軸",
35
+ "yAxis": "Y軸",
36
+ "chart": "グラフ",
37
+ "showChart": "グラフを表示 {{title}}",
38
+ "fullscreenButton": "グラフを全画面表示で開く",
39
+ "series": "シリーズ{{number}}"
40
+ },
32
41
  "operatorDialog": {
33
42
  "defaultTitle": "メッセージ",
34
43
  "imageAlt": "オペレーターメッセージ画像",
@@ -29,6 +29,15 @@
29
29
  "dbConnectionTitle": "Ошибка подключения к базе данных",
30
30
  "dbConnectionMessage": "Не удалось установить соединение с базой данных"
31
31
  },
32
+ "chart": {
33
+ "dataChart": "Данные графика",
34
+ "xAxis": "Ось X",
35
+ "yAxis": "Ось Y",
36
+ "chart": "График",
37
+ "showChart": "Показать график {{title}}",
38
+ "fullscreenButton": "Открыть график в полноэкранном режиме",
39
+ "series": "Серия {{number}}"
40
+ },
32
41
  "operatorDialog": {
33
42
  "defaultTitle": "Сообщение",
34
43
  "imageAlt": "Изображение в сообщении оператора",
@@ -29,6 +29,15 @@
29
29
  "dbConnectionTitle": "数据库连接错误",
30
30
  "dbConnectionMessage": "无法建立数据库连接"
31
31
  },
32
+ "chart": {
33
+ "dataChart": "图表数据",
34
+ "xAxis": "X轴",
35
+ "yAxis": "Y轴",
36
+ "chart": "图表",
37
+ "showChart": "显示图表 {{title}}",
38
+ "fullscreenButton": "全屏显示图表",
39
+ "series": "系列{{number}}"
40
+ },
32
41
  "operatorDialog": {
33
42
  "defaultTitle": "消息",
34
43
  "imageAlt": "操作员消息图片",
@@ -5,13 +5,25 @@ from hardpy.pytest_hardpy.db.base_store import BaseStore
5
5
  from hardpy.pytest_hardpy.db.const import DatabaseField
6
6
  from hardpy.pytest_hardpy.db.runstore import RunStore
7
7
  from hardpy.pytest_hardpy.db.schema import ResultRunStore, ResultStateStore
8
+ from hardpy.pytest_hardpy.db.stand_type import (
9
+ Chart,
10
+ Instrument,
11
+ NumericMeasurement,
12
+ StringMeasurement,
13
+ SubUnit,
14
+ )
8
15
  from hardpy.pytest_hardpy.db.statestore import StateStore
9
16
 
10
17
  __all__ = [
11
18
  "BaseStore",
19
+ "Chart",
12
20
  "DatabaseField",
21
+ "Instrument",
22
+ "NumericMeasurement",
13
23
  "ResultRunStore",
14
24
  "ResultStateStore",
15
25
  "RunStore",
16
26
  "StateStore",
27
+ "StringMeasurement",
28
+ "SubUnit",
17
29
  ]
@@ -5,18 +5,26 @@ from logging import getLogger
5
5
  from typing import Any
6
6
 
7
7
  from glom import assign, glom
8
- from pycouchdb.exceptions import Conflict, NotFound
8
+ from pycouchdb import Server as DbServer
9
+ from pycouchdb.client import Database
10
+ from pycouchdb.exceptions import Conflict, GenericError, NotFound
9
11
  from pydantic._internal._model_construction import ModelMetaclass
12
+ from requests.exceptions import ConnectionError # noqa: A004
10
13
 
11
- from hardpy.pytest_hardpy.db.base_connector import BaseConnector
14
+ from hardpy.common.config import ConfigManager
12
15
  from hardpy.pytest_hardpy.db.const import DatabaseField as DF # noqa: N817
13
16
 
14
17
 
15
- class BaseStore(BaseConnector):
18
+ class BaseStore:
16
19
  """HardPy base storage interface for CouchDB."""
17
20
 
18
21
  def __init__(self, db_name: str) -> None:
19
- super().__init__(db_name)
22
+ config_manager = ConfigManager()
23
+ config = config_manager.config
24
+ self._db_srv = DbServer(config.database.url)
25
+ self._db_name = db_name
26
+ self._db = self._init_db()
27
+ self._doc_id = config.database.doc_id
20
28
  self._log = getLogger(__name__)
21
29
  self._doc: dict = self._init_doc()
22
30
  self._schema: ModelMetaclass
@@ -83,6 +91,19 @@ class BaseStore(BaseConnector):
83
91
  self._log.debug("Database will be created for the first time")
84
92
  self._doc: dict = self._init_doc()
85
93
 
94
+ def _init_db(self) -> Database:
95
+ try:
96
+ return self._db_srv.create(self._db_name) # type: ignore
97
+ except Conflict:
98
+ # database is already created
99
+ return self._db_srv.database(self._db_name)
100
+ except GenericError as exc:
101
+ msg = f"Error initializing database {exc}"
102
+ raise RuntimeError(msg) from exc
103
+ except ConnectionError as exc:
104
+ msg = f"Error initializing database: {exc}"
105
+ raise RuntimeError(msg) from exc
106
+
86
107
  def _init_doc(self) -> dict:
87
108
  try:
88
109
  doc = self._db.get(self._doc_id)
@@ -5,9 +5,9 @@ from logging import getLogger
5
5
 
6
6
  from pycouchdb.exceptions import Conflict, NotFound
7
7
 
8
+ from hardpy.common.singleton import SingletonMeta
8
9
  from hardpy.pytest_hardpy.db.base_store import BaseStore
9
10
  from hardpy.pytest_hardpy.db.schema import ResultRunStore
10
- from hardpy.pytest_hardpy.utils import SingletonMeta
11
11
 
12
12
 
13
13
  class RunStore(BaseStore, metaclass=SingletonMeta):
@@ -8,7 +8,7 @@ from typing import ClassVar
8
8
 
9
9
  from pydantic import BaseModel, ConfigDict, Field
10
10
 
11
- from hardpy.pytest_hardpy.utils import (
11
+ from hardpy.pytest_hardpy.utils.const import (
12
12
  ChartType,
13
13
  ComparisonOperation as CompOp,
14
14
  Group,
@@ -78,7 +78,7 @@ class Dut(BaseModel):
78
78
  part_number: str | None = None
79
79
  revision: str | None = None
80
80
  sub_units: list[SubUnit] = []
81
- info: Mapping[str, str | int | float] = {}
81
+ info: Mapping[str, str | int | float | None] = {}
82
82
 
83
83
 
84
84
  class SubUnit(BaseModel):
@@ -91,7 +91,7 @@ class SubUnit(BaseModel):
91
91
  serial_number: str | None = None
92
92
  part_number: str | None = None
93
93
  revision: str | None = None
94
- info: Mapping[str, str | int | float] = {}
94
+ info: Mapping[str, str | int | float | None] = {}
95
95
 
96
96
 
97
97
  class Instrument(BaseModel):
@@ -101,9 +101,11 @@ class Instrument(BaseModel):
101
101
 
102
102
  name: str | None = None
103
103
  revision: str | None = None
104
+ serial_number: str | None = None
105
+ part_number: str | None = None
104
106
  number: int | None = None
105
107
  comment: str | None = None
106
- info: Mapping[str, str | int | float] = {}
108
+ info: Mapping[str, str | int | float | None] = {}
107
109
 
108
110
 
109
111
  class TestStand(BaseModel):
@@ -119,7 +121,7 @@ class TestStand(BaseModel):
119
121
  number: int | None = None
120
122
  drivers: dict = {} # deprecated, remove in v2
121
123
  instruments: list[Instrument] = []
122
- info: Mapping[str, str | int | float] = {}
124
+ info: Mapping[str, str | int | float | None] = {}
123
125
 
124
126
 
125
127
  class Process(BaseModel):
@@ -129,7 +131,7 @@ class Process(BaseModel):
129
131
 
130
132
  name: str | None = None
131
133
  number: int | None = None
132
- info: Mapping[str, str | int | float] = {}
134
+ info: Mapping[str, str | int | float | None] = {}
133
135
 
134
136
 
135
137
  class IBaseMeasurement(BaseModel, ABC):
@@ -3,9 +3,9 @@
3
3
 
4
4
  from logging import getLogger
5
5
 
6
+ from hardpy.common.singleton import SingletonMeta
6
7
  from hardpy.pytest_hardpy.db.base_store import BaseStore
7
8
  from hardpy.pytest_hardpy.db.schema import ResultStateStore
8
- from hardpy.pytest_hardpy.utils import SingletonMeta
9
9
 
10
10
 
11
11
  class StateStore(BaseStore, metaclass=SingletonMeta):
@@ -32,14 +32,10 @@ from pytest import (
32
32
  skip,
33
33
  )
34
34
 
35
+ from hardpy.common.config import ConfigManager, HardpyConfig
35
36
  from hardpy.common.stand_cloud.connector import StandCloudConnector, StandCloudError
36
37
  from hardpy.pytest_hardpy.reporter import HookReporter
37
- from hardpy.pytest_hardpy.utils import (
38
- ConnectionData,
39
- NodeInfo,
40
- ProgressCalculator,
41
- TestStatus,
42
- )
38
+ from hardpy.pytest_hardpy.utils import NodeInfo, ProgressCalculator, TestStatus
43
39
  from hardpy.pytest_hardpy.utils.node_info import TestDependencyInfo
44
40
 
45
41
  if __debug__:
@@ -51,11 +47,11 @@ if __debug__:
51
47
 
52
48
  def pytest_addoption(parser: Parser) -> None:
53
49
  """Register argparse-style options."""
54
- con_data = ConnectionData()
50
+ default_config = HardpyConfig()
55
51
  parser.addoption(
56
52
  "--hardpy-db-url",
57
53
  action="store",
58
- default=con_data.database_url,
54
+ default=default_config.database.url,
59
55
  help="database url",
60
56
  )
61
57
  parser.addoption(
@@ -90,13 +86,13 @@ def pytest_addoption(parser: Parser) -> None:
90
86
  parser.addoption(
91
87
  "--sc-address",
92
88
  action="store",
93
- default=con_data.sc_address,
89
+ default=default_config.stand_cloud.address,
94
90
  help="StandCloud address",
95
91
  )
96
92
  parser.addoption(
97
93
  "--sc-connection-only",
98
94
  action="store_true",
99
- default=con_data.sc_connection_only,
95
+ default=default_config.stand_cloud.connection_only,
100
96
  help="check StandCloud availability",
101
97
  )
102
98
  parser.addoption(
@@ -144,11 +140,15 @@ class HardpyPlugin:
144
140
 
145
141
  def pytest_configure(self, config: Config) -> None:
146
142
  """Configure pytest."""
147
- con_data = ConnectionData()
143
+ config_manager = ConfigManager()
144
+ hardpy_config = config_manager.read_config(Path(config.rootpath))
145
+
146
+ if not hardpy_config:
147
+ hardpy_config = HardpyConfig()
148
148
 
149
149
  database_url = config.getoption("--hardpy-db-url")
150
150
  if database_url:
151
- con_data.database_url = str(database_url) # type: ignore
151
+ hardpy_config.database.url = str(database_url) # type: ignore
152
152
 
153
153
  tests_name = config.getoption("--hardpy-tests-name")
154
154
  if tests_name:
@@ -160,11 +160,11 @@ class HardpyPlugin:
160
160
 
161
161
  sc_address = config.getoption("--sc-address")
162
162
  if sc_address:
163
- con_data.sc_address = str(sc_address) # type: ignore
163
+ hardpy_config.stand_cloud.address = str(sc_address) # type: ignore
164
164
 
165
165
  sc_connection_only = config.getoption("--sc-connection-only")
166
166
  if sc_connection_only:
167
- con_data.sc_connection_only = bool(sc_connection_only) # type: ignore
167
+ hardpy_config.stand_cloud.connection_only = bool(sc_connection_only) # type: ignore
168
168
 
169
169
  _args = config.getoption("--hardpy-start-arg") or []
170
170
  if _args:
@@ -246,17 +246,24 @@ class HardpyPlugin:
246
246
 
247
247
  def pytest_runtestloop(self, session: Session) -> bool | None:
248
248
  """Call at the start of test run."""
249
- self._progress.set_test_amount(session.testscollected)
249
+ try:
250
+ self._progress.set_test_amount(session.testscollected)
251
+ except ValueError:
252
+ msg = "No tests collected"
253
+ self._reporter.set_alert(msg)
254
+ exit(msg, ExitCode.NO_TESTS_COLLECTED)
250
255
  if session.config.option.collectonly:
251
256
  # ignore collect only mode
252
257
  return True
253
258
 
254
- con_data = ConnectionData()
259
+ config_manager = ConfigManager()
255
260
 
256
261
  # running tests depends on a connection to StandCloud
257
- if con_data.sc_connection_only:
262
+ if config_manager.config.stand_cloud.connection_only:
258
263
  try:
259
- sc_connector = StandCloudConnector(addr=con_data.sc_address)
264
+ sc_connector = StandCloudConnector(
265
+ addr=config_manager.config.stand_cloud.address,
266
+ )
260
267
  except StandCloudError as exc:
261
268
  msg = str(exc)
262
269
  self._reporter.set_alert(msg)
@@ -264,7 +271,7 @@ class HardpyPlugin:
264
271
  try:
265
272
  sc_connector.healthcheck()
266
273
  except Exception: # noqa: BLE001
267
- addr = con_data.sc_address
274
+ addr = config_manager.config.stand_cloud.address
268
275
  msg = (
269
276
  f"StandCloud service at the address {addr} "
270
277
  "not available or HardPy user is not authorized"
@@ -337,15 +344,16 @@ class HardpyPlugin:
337
344
  self._reporter.clear_case_data(module_id, case_id)
338
345
  self._reporter.update_db_by_doc()
339
346
 
347
+ # clear the error code if there were no failed tests before
348
+ if caused_dut_failure_id is None:
349
+ self._reporter.clear_error_code()
350
+
340
351
  try:
341
352
  item.runtest()
342
353
  call.excinfo = None
343
354
  self._is_critical_not_passed = False
344
355
  is_dut_failure = False
345
356
  self._reporter.set_case_status(module_id, case_id, TestStatus.PASSED)
346
- # clear the error code if there were no failed tests before
347
- if caused_dut_failure_id is None:
348
- self._reporter.clear_error_code()
349
357
  break
350
358
  except AssertionError:
351
359
  self._reporter.set_case_status(module_id, case_id, TestStatus.FAILED)
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2024 Everypin
1
+ # Copyright (c) 2025 Everypin
2
2
  # GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
3
3
  from __future__ import annotations
4
4
 
@@ -9,25 +9,21 @@ from time import sleep
9
9
  from typing import TYPE_CHECKING, Any
10
10
  from uuid import uuid4
11
11
 
12
- from pycouchdb.exceptions import NotFound
13
- from pydantic import ValidationError
14
-
15
12
  from hardpy.pytest_hardpy.db import (
13
+ Chart,
16
14
  DatabaseField as DF, # noqa: N817
15
+ Instrument,
16
+ NumericMeasurement,
17
17
  ResultRunStore,
18
- RunStore,
18
+ StringMeasurement,
19
+ SubUnit,
19
20
  )
20
21
  from hardpy.pytest_hardpy.reporter import RunnerReporter
21
22
  from hardpy.pytest_hardpy.utils import (
22
- Chart,
23
23
  DialogBox,
24
24
  DuplicateParameterError,
25
25
  HTMLComponent,
26
26
  ImageComponent,
27
- Instrument,
28
- NumericMeasurement,
29
- StringMeasurement,
30
- SubUnit,
31
27
  TestStandNumberError,
32
28
  )
33
29
 
@@ -71,7 +67,7 @@ class ErrorCode:
71
67
  if reporter.get_field(key) is None:
72
68
  reporter.set_doc_value(key, code)
73
69
  reporter.update_db_by_doc()
74
- self._message = message
70
+ self._message = message if message else f"Error code = {code}"
75
71
 
76
72
  def __repr__(self) -> str:
77
73
  return self._message
@@ -86,15 +82,8 @@ def get_current_report() -> ResultRunStore | None:
86
82
  Returns:
87
83
  ResultRunStore | None: report, or None if not found or invalid
88
84
  """
89
- runstore = RunStore()
90
- try:
91
- return runstore.get_document() # type: ignore
92
- except NotFound:
93
- return None
94
- except ValidationError:
95
- return None
96
- except TypeError:
97
- return None
85
+ reporter = RunnerReporter()
86
+ return reporter.get_report()
98
87
 
99
88
 
100
89
  def set_user_name(name: str) -> None:
@@ -154,7 +143,7 @@ def set_dut_sub_unit(sub_unit: SubUnit) -> int:
154
143
  return len(sub_units) - 1
155
144
 
156
145
 
157
- def set_dut_info(info: Mapping[str, str | int | float]) -> None:
146
+ def set_dut_info(info: Mapping[str, str | int | float | None]) -> None:
158
147
  """Set DUT info to document.
159
148
 
160
149
  Args:
@@ -278,7 +267,7 @@ def set_stand_name(name: str) -> None:
278
267
  reporter.update_db_by_doc()
279
268
 
280
269
 
281
- def set_stand_info(info: Mapping[str, str | int | float]) -> None:
270
+ def set_stand_info(info: Mapping[str, str | int | float | None]) -> None:
282
271
  """Add test stand info to document.
283
272
 
284
273
  Args:
@@ -524,7 +513,7 @@ def set_process_number(number: int) -> None:
524
513
  reporter.update_db_by_doc()
525
514
 
526
515
 
527
- def set_process_info(info: Mapping[str, str | int | float]) -> None:
516
+ def set_process_info(info: Mapping[str, str | int | float | None]) -> None:
528
517
  """Set process info to document.
529
518
 
530
519
  Args:
@@ -22,7 +22,8 @@ class PyTestWrapper:
22
22
 
23
23
  # Make sure test structure is stored in DB
24
24
  # before clients come in
25
- self.config = ConfigManager().get_config()
25
+ self._config_manager = ConfigManager()
26
+ self.config = self._config_manager.config
26
27
  self.collect(is_clear_database=True)
27
28
 
28
29
  def start(self, start_args: dict | None = None) -> bool:
@@ -42,7 +43,7 @@ class PyTestWrapper:
42
43
  "-m",
43
44
  "pytest",
44
45
  "--hardpy-db-url",
45
- self.config.database.connection_url(),
46
+ self.config.database.url,
46
47
  "--hardpy-tests-name",
47
48
  self.config.tests_name,
48
49
  "--sc-address",
@@ -59,13 +60,13 @@ class PyTestWrapper:
59
60
  if system() == "Windows":
60
61
  self._proc = subprocess.Popen( # noqa: S603
61
62
  cmd,
62
- cwd=ConfigManager().get_tests_path(),
63
+ cwd=self._config_manager.tests_path,
63
64
  creationflags=subprocess.CREATE_NEW_PROCESS_GROUP,
64
65
  )
65
66
  if system() == "Linux":
66
67
  self._proc = subprocess.Popen( # noqa: S603
67
68
  cmd,
68
- cwd=ConfigManager().get_tests_path(),
69
+ cwd=self._config_manager.tests_path,
69
70
  )
70
71
 
71
72
  return True
@@ -105,7 +106,7 @@ class PyTestWrapper:
105
106
  "pytest",
106
107
  "--collect-only",
107
108
  "--hardpy-db-url",
108
- self.config.database.connection_url(),
109
+ self.config.database.url,
109
110
  "--hardpy-tests-name",
110
111
  self.config.tests_name,
111
112
  "--hardpy-pt",
@@ -116,7 +117,7 @@ class PyTestWrapper:
116
117
 
117
118
  subprocess.Popen( # noqa: S603
118
119
  [self.python_executable, *args],
119
- cwd=ConfigManager().get_tests_path(),
120
+ cwd=self._config_manager.tests_path,
120
121
  )
121
122
  return True
122
123
 
@@ -153,4 +154,5 @@ class PyTestWrapper:
153
154
  Returns:
154
155
  dict: HardPy configuration
155
156
  """
156
- return ConfigManager().get_config().model_dump()
157
+ config_manager = ConfigManager()
158
+ return config_manager.config.model_dump()
@@ -1,11 +1,16 @@
1
- # Copyright (c) 2024 Everypin
1
+ # Copyright (c) 2025 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
  from logging import getLogger
5
6
  from typing import Any
6
7
 
8
+ from pycouchdb.exceptions import NotFound
9
+ from pydantic import ValidationError
10
+
7
11
  from hardpy.pytest_hardpy.db import (
8
12
  DatabaseField as DF, # noqa: N817
13
+ ResultRunStore,
9
14
  RunStore,
10
15
  StateStore,
11
16
  )
@@ -82,6 +87,21 @@ class BaseReporter:
82
87
  """
83
88
  return ".".join(args)
84
89
 
90
+ def get_report(self) -> ResultRunStore | None:
91
+ """Get current report from runstore database.
92
+
93
+ Returns:
94
+ ResultRunStore | None: report, or None if not found or invalid
95
+ """
96
+ try:
97
+ return self._runstore.get_document() # type: ignore
98
+ except NotFound:
99
+ return None
100
+ except ValidationError:
101
+ return None
102
+ except TypeError:
103
+ return None
104
+
85
105
  def get_current_attempt(self, module_id: str, case_id: str) -> int:
86
106
  """Get current attempt.
87
107
 
@@ -4,8 +4,8 @@
4
4
  from logging import getLogger
5
5
  from typing import Any
6
6
 
7
+ from hardpy.common.singleton import SingletonMeta
7
8
  from hardpy.pytest_hardpy.reporter.base import BaseReporter
8
- from hardpy.pytest_hardpy.utils import SingletonMeta
9
9
 
10
10
 
11
11
  class RunnerReporter(BaseReporter, metaclass=SingletonMeta):