plesty-server 0.2.1__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 (43) hide show
  1. plesty/server/__init__.py +22 -0
  2. plesty/server/__main__.py +9 -0
  3. plesty/server/assets/console.qss +321 -0
  4. plesty/server/assets/env.example +12 -0
  5. plesty/server/assets/fleet.example.yaml +44 -0
  6. plesty/server/assets/plesty-mark.svg +54 -0
  7. plesty/server/cli/__init__.py +14 -0
  8. plesty/server/cli/main.py +761 -0
  9. plesty/server/config.yaml +21 -0
  10. plesty/server/host.py +262 -0
  11. plesty/server/model/__init__.py +144 -0
  12. plesty/server/model/catalogue.py +130 -0
  13. plesty/server/model/device.py +177 -0
  14. plesty/server/model/device_manager.py +364 -0
  15. plesty/server/model/envfile.py +65 -0
  16. plesty/server/model/errors.py +8 -0
  17. plesty/server/model/field_test.py +356 -0
  18. plesty/server/model/fleet.py +205 -0
  19. plesty/server/model/home.py +159 -0
  20. plesty/server/model/job.py +268 -0
  21. plesty/server/model/ports.py +136 -0
  22. plesty/server/model/process.py +124 -0
  23. plesty/server/presenter/__init__.py +15 -0
  24. plesty/server/presenter/bench.py +990 -0
  25. plesty/server/reexec.py +69 -0
  26. plesty/server/services/__init__.py +58 -0
  27. plesty/server/services/agent.py +243 -0
  28. plesty/server/services/catalogue.py +106 -0
  29. plesty/server/services/installer.py +324 -0
  30. plesty/server/services/jobs.py +118 -0
  31. plesty/server/services/launch.py +178 -0
  32. plesty/server/services/supervisor.py +437 -0
  33. plesty/server/view/__init__.py +21 -0
  34. plesty/server/view/app.py +97 -0
  35. plesty/server/view/dialogs.py +808 -0
  36. plesty/server/view/window.py +786 -0
  37. plesty/server/view/workers.py +92 -0
  38. plesty_server-0.2.1.dist-info/METADATA +45 -0
  39. plesty_server-0.2.1.dist-info/RECORD +43 -0
  40. plesty_server-0.2.1.dist-info/WHEEL +4 -0
  41. plesty_server-0.2.1.dist-info/entry_points.txt +5 -0
  42. plesty_server-0.2.1.dist-info/licenses/LICENSE +184 -0
  43. plesty_server-0.2.1.dist-info/licenses/LICENSES/LGPL-3.0-or-later.txt +165 -0
@@ -0,0 +1,22 @@
1
+ # SPDX-FileCopyrightText: 2026 Yunshuang Yuan
2
+ # SPDX-License-Identifier: LGPL-3.0-or-later
3
+
4
+ """Server plesty module."""
5
+
6
+ from typing import Any
7
+
8
+ from plesty.lib.utils.config import module_config
9
+
10
+ __all__ = [
11
+ "load_config",
12
+ ]
13
+
14
+
15
+ def load_config() -> dict[str, Any]:
16
+ """Load the packaged module configuration (constants and running defaults).
17
+
18
+ Returns:
19
+ The parsed ``config.yaml`` shipped with this module.
20
+ """
21
+ config: dict[str, Any] = module_config(__name__)
22
+ return config
@@ -0,0 +1,9 @@
1
+ # SPDX-FileCopyrightText: 2026 Yunshuang Yuan
2
+ # SPDX-License-Identifier: LGPL-3.0-or-later
3
+
4
+ """``python -m plesty.server`` — the ``plesty-server`` command line."""
5
+
6
+ from plesty.server.cli.main import main
7
+
8
+ if __name__ == "__main__": # pragma: no cover
9
+ main()
@@ -0,0 +1,321 @@
1
+ /* SPDX-FileCopyrightText: 2026 Yunshuang Yuan
2
+ * SPDX-License-Identifier: LGPL-3.0-or-later
3
+ *
4
+ * Qt stylesheet of the plesty-server console. The double-braced placeholders are
5
+ * substituted from plesty-lib's palette.json (plesty.net design tokens) at
6
+ * load time — colours are the platform's, the layout is ours; the two font
7
+ * tokens are filled with families the host actually has (see view.app).
8
+ */
9
+
10
+ QWidget {
11
+ background: {{night}};
12
+ color: {{text}};
13
+ font-family: {{ui_font}};
14
+ font-size: 13px;
15
+ }
16
+
17
+ QMainWindow, QDialog {
18
+ background: {{night}};
19
+ }
20
+
21
+ /* ── Menu bar and menus ─────────────────────────────────────────────────── */
22
+
23
+ QMenuBar {
24
+ background: {{night2}};
25
+ border-bottom: 1px solid {{line}};
26
+ padding: 2px 6px;
27
+ }
28
+
29
+ QMenuBar::item {
30
+ background: transparent;
31
+ padding: 4px 10px;
32
+ border-radius: 6px;
33
+ }
34
+
35
+ QMenuBar::item:selected {
36
+ background: {{surface3}};
37
+ color: {{text_bright}};
38
+ }
39
+
40
+ QMenu {
41
+ background: {{surface2}};
42
+ border: 1px solid {{line_strong}};
43
+ border-radius: 8px;
44
+ padding: 4px;
45
+ }
46
+
47
+ QMenu::item {
48
+ padding: 5px 24px 5px 14px;
49
+ border-radius: 5px;
50
+ }
51
+
52
+ QMenu::item:selected {
53
+ background: {{selection}};
54
+ color: {{text_bright}};
55
+ }
56
+
57
+ QMenu::item:disabled {
58
+ color: {{faint}};
59
+ }
60
+
61
+ QMenu::separator {
62
+ height: 1px;
63
+ background: {{line}};
64
+ margin: 4px 8px;
65
+ }
66
+
67
+ /* ── Cards: the device table and the detail pane ────────────────────────── */
68
+
69
+ #card {
70
+ background: {{surface}};
71
+ border: 1px solid {{line}};
72
+ border-radius: 10px;
73
+ }
74
+
75
+ #cardTitle {
76
+ background: transparent;
77
+ color: {{text}};
78
+ font-size: 12px;
79
+ font-weight: 600;
80
+ letter-spacing: 0.02em;
81
+ padding: 2px 4px;
82
+ }
83
+
84
+ #cardCaption {
85
+ background: transparent;
86
+ color: {{muted}};
87
+ font-family: {{mono_font}};
88
+ font-size: 11px;
89
+ }
90
+
91
+ QTableWidget, QTableView {
92
+ background: {{surface}};
93
+ alternate-background-color: {{surface2}};
94
+ border: none;
95
+ gridline-color: {{line}};
96
+ selection-background-color: {{selection}};
97
+ selection-color: {{text_bright}};
98
+ }
99
+
100
+ QHeaderView::section {
101
+ background: {{surface2}};
102
+ color: {{muted}};
103
+ border: none;
104
+ border-bottom: 1px solid {{line_strong}};
105
+ padding: 5px 8px;
106
+ font-size: 11px;
107
+ font-weight: 600;
108
+ letter-spacing: 0.04em;
109
+ }
110
+
111
+ QTableWidget::item {
112
+ padding: 4px 8px;
113
+ }
114
+
115
+ /* State chip text colours — the window-chrome language of plesty.net. */
116
+ #stateChip {
117
+ font-size: 12px;
118
+ font-weight: 600;
119
+ }
120
+
121
+ QLabel[tone="running"] { color: {{mint}}; }
122
+ QLabel[tone="starting"] { color: {{amber}}; }
123
+ QLabel[tone="exited"] { color: {{rose}}; }
124
+ QLabel[tone="stopped"] { color: {{slate}}; }
125
+ QLabel[tone="done"] { color: {{mint}}; }
126
+ QLabel[tone="failed"] { color: {{rose}}; }
127
+
128
+ /* ── Log panes ──────────────────────────────────────────────────────────── */
129
+
130
+ QPlainTextEdit, QTextEdit {
131
+ background: {{plot_bg}};
132
+ color: {{text}};
133
+ border: 1px solid {{line}};
134
+ border-radius: 8px;
135
+ font-family: {{mono_font}};
136
+ font-size: 11px;
137
+ padding: 6px;
138
+ selection-background-color: {{selection}};
139
+ }
140
+
141
+ QTabWidget::pane {
142
+ border: none;
143
+ background: transparent;
144
+ }
145
+
146
+ QTabBar::tab {
147
+ background: transparent;
148
+ color: {{muted}};
149
+ padding: 6px 14px;
150
+ border-bottom: 2px solid transparent;
151
+ }
152
+
153
+ QTabBar::tab:selected {
154
+ color: {{cyan}};
155
+ border-bottom: 2px solid {{cyan}};
156
+ }
157
+
158
+ QTabBar::tab:hover {
159
+ color: {{text_bright}};
160
+ }
161
+
162
+ /* ── Controls ───────────────────────────────────────────────────────────── */
163
+
164
+ QPushButton {
165
+ background: {{surface2}};
166
+ color: {{text}};
167
+ border: 1px solid {{line_strong}};
168
+ border-radius: 7px;
169
+ padding: 5px 14px;
170
+ }
171
+
172
+ QPushButton:hover {
173
+ background: {{surface3}};
174
+ color: {{text_bright}};
175
+ }
176
+
177
+ QPushButton:pressed {
178
+ background: {{selection}};
179
+ }
180
+
181
+ QPushButton:disabled {
182
+ color: {{faint}};
183
+ border-color: {{line}};
184
+ }
185
+
186
+ QPushButton#primary {
187
+ background: {{blue}};
188
+ border-color: {{blue}};
189
+ color: {{text_bright}};
190
+ }
191
+
192
+ QPushButton#primary:hover {
193
+ background: {{cyan}};
194
+ border-color: {{cyan}};
195
+ color: {{night}};
196
+ }
197
+
198
+ QPushButton#danger {
199
+ border-color: {{rose}};
200
+ color: {{rose}};
201
+ }
202
+
203
+ /* Card-header buttons: compact, quiet until hovered. */
204
+ QPushButton#toolButton, QPushButton#primary, QPushButton#danger {
205
+ padding: 3px 10px;
206
+ font-size: 12px;
207
+ }
208
+
209
+ QPushButton#toolButton {
210
+ background: transparent;
211
+ }
212
+
213
+ QPushButton#toolButton:hover {
214
+ background: {{surface3}};
215
+ }
216
+
217
+ QPushButton#primary:disabled, QPushButton#danger:disabled {
218
+ background: transparent;
219
+ border-color: {{line}};
220
+ color: {{faint}};
221
+ }
222
+
223
+ QLineEdit, QComboBox, QSpinBox, QPlainTextEdit#editor {
224
+ background: {{surface2}};
225
+ border: 1px solid {{line_strong}};
226
+ border-radius: 6px;
227
+ padding: 4px 8px;
228
+ selection-background-color: {{selection}};
229
+ }
230
+
231
+ QLineEdit:focus, QComboBox:focus, QSpinBox:focus {
232
+ border-color: {{cyan}};
233
+ }
234
+
235
+ QComboBox QAbstractItemView {
236
+ background: {{surface2}};
237
+ border: 1px solid {{line_strong}};
238
+ selection-background-color: {{selection}};
239
+ }
240
+
241
+ QCheckBox::indicator {
242
+ width: 14px;
243
+ height: 14px;
244
+ border: 1px solid {{line_strong}};
245
+ border-radius: 4px;
246
+ background: {{surface2}};
247
+ }
248
+
249
+ QCheckBox::indicator:checked {
250
+ background: {{cyan}};
251
+ border-color: {{cyan}};
252
+ }
253
+
254
+ QGroupBox {
255
+ border: 1px solid {{line}};
256
+ border-radius: 8px;
257
+ margin-top: 14px;
258
+ padding: 10px 8px 6px 8px;
259
+ }
260
+
261
+ QGroupBox::title {
262
+ subcontrol-origin: margin;
263
+ left: 10px;
264
+ padding: 0 4px;
265
+ color: {{muted}};
266
+ font-size: 11px;
267
+ font-weight: 600;
268
+ }
269
+
270
+ QStatusBar {
271
+ background: {{night2}};
272
+ border-top: 1px solid {{line}};
273
+ color: {{muted}};
274
+ font-family: {{mono_font}};
275
+ font-size: 11px;
276
+ }
277
+
278
+ QSplitter::handle {
279
+ background: {{night}};
280
+ }
281
+
282
+ QSplitter::handle:hover {
283
+ background: {{cyan}};
284
+ }
285
+
286
+ QScrollBar:vertical {
287
+ background: transparent;
288
+ width: 10px;
289
+ margin: 0;
290
+ }
291
+
292
+ QScrollBar::handle:vertical {
293
+ background: {{line_strong}};
294
+ border-radius: 5px;
295
+ min-height: 24px;
296
+ }
297
+
298
+ QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
299
+ height: 0;
300
+ }
301
+
302
+ QScrollBar:horizontal {
303
+ background: transparent;
304
+ height: 10px;
305
+ }
306
+
307
+ QScrollBar::handle:horizontal {
308
+ background: {{line_strong}};
309
+ border-radius: 5px;
310
+ }
311
+
312
+ QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {
313
+ width: 0;
314
+ }
315
+
316
+ QToolTip {
317
+ background: {{surface3}};
318
+ color: {{text}};
319
+ border: 1px solid {{line_strong}};
320
+ padding: 4px 6px;
321
+ }
@@ -0,0 +1,12 @@
1
+ # Credentials and addresses for this device instance. Read by the device
2
+ # server and the field test (plesty.lib.utils.EnvSettings); the fleet's
3
+ # `env:` and the process environment win over this file.
4
+ #
5
+ # This file stays on the bench: an address carries a serial number.
6
+
7
+ # The instrument this instance talks to — a VISA resource, a serial port, a
8
+ # vendor id; or `mock` for the module's simulator.
9
+ DEVICE_ADDRESS=
10
+
11
+ # Shared location every field-test run is also copied to (optional).
12
+ PLESTY_REPORT_ARCHIVE=
@@ -0,0 +1,44 @@
1
+ # fleet.yaml — the device servers this bench runs.
2
+ #
3
+ # plesty-server fleet up install what is missing, start every autostart device
4
+ # plesty-server fleet run same, then keep them up (headless bench mode)
5
+ # plesty-server fleet down stop them
6
+ # plesty-server fleet status who is running, on which port
7
+ #
8
+ # One entry per device instance. The key is the instance name: it names the
9
+ # venv, the log file and the process record, so it must be unique per bench.
10
+
11
+ version: 1
12
+
13
+ # Where venvs, logs and process state live. Optional; defaults to
14
+ # $PLESTY_SERVER_HOME, then ~/.plesty/server.
15
+ # home: ~/.plesty/server
16
+
17
+ # hub-registry catalogue used by `plesty-server search`. Optional.
18
+ # index_url: https://hub.plesty.net/api/modules/
19
+
20
+ devices:
21
+ # A Thorlabs PM100D power meter served on port 5555.
22
+ pm100d:
23
+ package: plesty-pm100d # distribution name of the device module
24
+ # Source. A released module is installed from PyPI at `version`; until a
25
+ # module has a release, `git:` installs it from its repository (the form
26
+ # the hub catalogue advertises), `ref:` picks a branch/tag/commit and
27
+ # defaults to the tag v<version> when a version is given.
28
+ git: https://gitlab.com/plesty/hub/devices/thorlab/plesty-pm100d.git
29
+ # version: 0.2.1 # pinned release; omit to take the newest at install time
30
+ args: [--tcp-port, "5555", --address, "USB0::0x1313::0x8078::P0000000::INSTR"]
31
+ env:
32
+ # Anything the module reads from .env can go here instead — plesty-lib
33
+ # takes the process environment first. Never put credentials in a
34
+ # fleet file that leaves the bench.
35
+ PLESTY_REPORT_ARCHIVE: /shares/plesty/reports
36
+ autostart: true
37
+
38
+ # A second instance of the same package, on another port, not started
39
+ # automatically — `plesty-server start pm100d-spare` when needed.
40
+ pm100d-spare:
41
+ package: plesty-pm100d
42
+ git: https://gitlab.com/plesty/hub/devices/thorlab/plesty-pm100d.git
43
+ args: [--tcp-port, "5556", --mock]
44
+ autostart: false
@@ -0,0 +1,54 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="177" height="177" viewBox="0 0 177 177" role="img" aria-labelledby="title desc">
2
+ <title id="title">Plesty mark</title>
3
+ <desc id="desc">A dark glass-style Plesty mark with coupled blue waveguide curves and a mint module.</desc>
4
+ <defs>
5
+ <linearGradient id="markGradientLeft" x1="94" y1="24" x2="94" y2="154" gradientUnits="userSpaceOnUse">
6
+ <stop offset="0" stop-color="#67e8f9"/>
7
+ <stop offset="0.52" stop-color="#3b82f6"/>
8
+ <stop offset="1" stop-color="#8b5cf6"/>
9
+ </linearGradient>
10
+ <linearGradient id="markGradientRight" x1="94" y1="24" x2="94" y2="154" gradientUnits="userSpaceOnUse">
11
+ <stop offset="0" stop-color="#67e8f9"/>
12
+ <stop offset="0.52" stop-color="#3b82f6"/>
13
+ <stop offset="1" stop-color="#8b5cf6"/>
14
+ </linearGradient>
15
+ <linearGradient id="mintGradient" x1="25" y1="89" x2="67" y2="126" gradientUnits="userSpaceOnUse">
16
+ <stop offset="0" stop-color="#34d399"/>
17
+ <stop offset="1" stop-color="#22d3ee"/>
18
+ </linearGradient>
19
+ <filter id="mintGlow" x="-80%" y="-80%" width="260%" height="260%" color-interpolation-filters="sRGB">
20
+ <feGaussianBlur in="SourceGraphic" stdDeviation="7" result="blur"/>
21
+ <feColorMatrix
22
+ in="blur"
23
+ type="matrix"
24
+ values="0 0 0 0 0.20
25
+ 0 0 0 0 0.83
26
+ 0 0 0 0 0.60
27
+ 0 0 0 0.95 0"
28
+ result="coloredBlur"
29
+ />
30
+ <feMerge>
31
+ <feMergeNode in="coloredBlur"/>
32
+ <feMergeNode in="SourceGraphic"/>
33
+ </feMerge>
34
+ </filter>
35
+ <filter id="whiteBorderGlow" x="-8%" y="-24%" width="116%" height="148%" color-interpolation-filters="sRGB">
36
+ <feGaussianBlur in="SourceGraphic" stdDeviation="3.5" result="blur"/>
37
+ <feMerge>
38
+ <feMergeNode in="blur"/>
39
+ <feMergeNode in="SourceGraphic"/>
40
+ </feMerge>
41
+ </filter>
42
+ </defs>
43
+
44
+ <rect width="177" height="177" fill="none"/>
45
+
46
+ <g>
47
+ <rect x="10" y="6" width="165" height="165" rx="0" fill="#0d1024" opacity="0.92"/>
48
+ <path d="M62 30 C94 40 108.5 66 108.5 89 C108.5 112 94 138 62 151" fill="none" stroke="url(#markGradientLeft)" stroke-width="34" stroke-linecap="butt" stroke-linejoin="round"/>
49
+ <path d="M155 30 C123 40 108.5 66 108.5 89 C108.5 112 123 138 155 151" fill="none" stroke="url(#markGradientRight)" stroke-width="34" stroke-linecap="butt" stroke-linejoin="round"/>
50
+ <rect x="26" y="91" width="36" height="36" transform="rotate(-22 44 109)" fill="#34d399" opacity="0.72" filter="url(#mintGlow)"/>
51
+ <rect x="26" y="91" width="36" height="36" transform="rotate(-22 44 109)" fill="url(#mintGradient)"/>
52
+ </g>
53
+ <rect x="9" y="5" width="166" height="166" rx="7" fill="none" stroke="#ffffff" stroke-opacity="0.72" stroke-width="2" filter="url(#whiteBorderGlow)"/>
54
+ </svg>
@@ -0,0 +1,14 @@
1
+ # SPDX-FileCopyrightText: 2026 Yunshuang Yuan
2
+ # SPDX-License-Identifier: LGPL-3.0-or-later
3
+
4
+ """Command-line entry point ``plesty-server``.
5
+
6
+ The headless face of the bench: ``install``/``start``/``stop``/``status``/
7
+ ``log``/``field-test`` for one device and ``fleet up|down|status`` for a
8
+ whole ``fleet.yaml``. Commands drive the :class:`~plesty.server.presenter.Bench`
9
+ presenter — the same code paths the GUI takes, minus the view.
10
+ """
11
+
12
+ from plesty.server.cli.main import app
13
+
14
+ __all__ = ["app"]