plesty-sdk 0.5.0.dev1__tar.gz → 0.5.1__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.
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/.gitlab-ci.yml +2 -1
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/CHANGELOG.md +39 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/PKG-INFO +1 -1
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/docs/architecture.md +4 -11
- plesty_sdk-0.5.1/docs/commands/init-field-test.md +128 -0
- plesty_sdk-0.5.1/docs/commands/init-mock-test.md +86 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/docs/commands/init.md +7 -190
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/docs/getting-started.md +43 -14
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/docs/index.md +15 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/docs/toc.yaml +5 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/gl-secret-detection-report.json +6 -6
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/hub-theme/plesty-logo.svg +4 -2
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/hub-theme/plesty-mark.svg +4 -2
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/extras/device/preflight.py +2 -2
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/commands/init.py +9 -10
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/pyproject.toml +1 -1
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/uv.lock +18 -18
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/.coverage +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/.gitignore +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/.mailmap +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/LICENSE +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/LICENSES/LGPL-3.0-or-later.txt +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/README.md +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/REUSE.toml +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/docs/commands/check.md +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/docs/commands/docs.md +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/docs/commands/license.md +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/docs/commands/manual.md +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/docs/commands/update.md +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/packaging/versioning.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/__init__.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/check/guidance.yaml +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/docs-build-gitlab-ci.yml +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/hub-theme/plesty-hub.css +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/hub-theme/plesty-versions.css +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/hub-theme/plesty-versions.js +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/init/client-field-test.py.template +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/init/field-test-env.example +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/init/field-test-questions.yaml +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/init/field-test.py.template +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/init/mock-test.py.template +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/init/next-steps.yaml +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/manual/pdf-common.css +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/manual/pdf-page-landscape.css +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/manual/pdf-page-portrait.css +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/ruff.toml +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/copier.yml +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/extras/analyzer/analyzer.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/extras/device/base_device.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/extras/device/device.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/extras/experiment/experiment.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/.gitignore +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/CHANGELOG.md.jinja +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/LICENSE +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/LICENSES/LGPL-3.0-or-later.txt +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/README.md.jinja +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/REUSE.toml.jinja +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/docs/index.md.jinja +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/docs/toc.yaml +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/packaging/versioning.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/plesty/{{module_name}}/__init__.py.jinja +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/plesty/{{module_name}}/config.yaml.jinja +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/plesty/{{module_name}}/{% if project_type in ['device', 'experiment'] %}__main__.py{% endif %}.jinja +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/pyproject.toml.jinja +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/tests/__init__.py.jinja +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/tests/test_{{module_name}}.py.jinja +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/tests/{% if project_type == 'device' %}test_preflight.py{% endif %}.jinja +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/{% if project_type == 'experiment' %}config{% endif %}/default.yaml +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/{% if project_type == 'experiment' %}setup.yaml{% endif %} +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/assets/templates/template/{% if project_type in ['device', 'experiment'] %}.env.example{% endif %}.jinja +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/authorship.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/cache.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/cli.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/commands/__init__.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/commands/check.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/commands/docs/__init__.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/commands/docs/deploy.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/commands/docs/generate.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/commands/docs/picker.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/commands/docs/serve.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/commands/docs/sphinx_builder.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/commands/field_test.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/commands/license.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/commands/manual.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/commands/mock_test.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/commands/update.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/context.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/report.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/semver.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/plesty/sdk/tui.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/__init__.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_authorship.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_cache.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_check.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_check_experiment_manifest.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_cli.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_context.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_device_preflight_template.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_docs_deploy.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_docs_generate.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_docs_picker.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_docs_serve.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_field_test_scaffold.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_init.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_license.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_manual.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_mock_test_scaffold.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_report.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_semver.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_sphinx_builder.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_tui.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_update.py +0 -0
- {plesty_sdk-0.5.0.dev1 → plesty_sdk-0.5.1}/tests/test_versioning_rule.py +0 -0
|
@@ -2,9 +2,10 @@ workflow:
|
|
|
2
2
|
rules:
|
|
3
3
|
# Create pipelines for merge requests
|
|
4
4
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
5
|
-
# Create branch pipelines for main and
|
|
5
|
+
# Create branch pipelines for main, exp and every supported maintain/* line
|
|
6
6
|
- if: $CI_COMMIT_BRANCH == "main"
|
|
7
7
|
- if: $CI_COMMIT_BRANCH == "exp"
|
|
8
|
+
- if: $CI_COMMIT_BRANCH =~ /^maintain\//
|
|
8
9
|
# Create pipelines for tags (dev and stable releases)
|
|
9
10
|
- if: $CI_COMMIT_TAG
|
|
10
11
|
# Otherwise do not create a pipeline
|
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
The format is based on [Keep a Changelog](https://keepachangelog.com).
|
|
4
4
|
|
|
5
|
+
## [0.5.1] — 2026-10-15
|
|
6
|
+
|
|
7
|
+
Fixes on the 0.5 line, the same as in 0.4.1. Supported lines: 0.5 and 0.4
|
|
8
|
+
(`maintain/0.4`).
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- The logo in the generated docs pages no longer animates forever. The photon
|
|
12
|
+
in the PLESTY mark looped indefinitely through a blur filter, so an idle docs
|
|
13
|
+
page kept the browser redrawing every frame (~25% CPU in Safari). It now
|
|
14
|
+
travels three times and stops.
|
|
15
|
+
|
|
16
|
+
### Security
|
|
17
|
+
- urllib3 2.8.0 and tornado 6.5.10 in the lockfile: 2.7.0 and 6.5.8 carry six
|
|
18
|
+
published advisories (PYSEC-2026-4175/4176/4177, GHSA-chx6-46f5-w4vp,
|
|
19
|
+
GHSA-c2m8-h5v5-343r, GHSA-3hv7-mjh2-fv65), which failed the audit gate.
|
|
20
|
+
|
|
21
|
+
## [0.5.0] — 2026-09-29
|
|
22
|
+
|
|
23
|
+
Opens the 0.5 line, scaffolding against the released plesty-lib 0.5.0: every
|
|
24
|
+
change listed under 0.5.0.dev1 below, and the pin that follows. Supported
|
|
25
|
+
lines: 0.5 and 0.4 (`maintain/0.4`); 0.3 is retired.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- **Scaffolds pin the released plesty-lib 0.5.0, not a snapshot.** The floor
|
|
29
|
+
`plesty init` writes when PyPI cannot be reached (`PLESTY_LIB_VERSION`), the
|
|
30
|
+
version a scaffolded device's preflight names when its library is too old,
|
|
31
|
+
and the dev-group pin are `0.5.0`, where they named `0.5.0.dev7`.
|
|
32
|
+
|
|
5
33
|
## [0.5.0.dev1] — 2026-09-25
|
|
6
34
|
|
|
7
35
|
Development pre-release, the first of the 0.5 line: scaffolds generated against
|
|
@@ -98,6 +126,17 @@ writes them before 0.5.0 is ready to be called finished.
|
|
|
98
126
|
`major.minor`, taken from the line the checkout can reach by tag. Pre-release
|
|
99
127
|
and yanked files are skipped. Nothing changes on the newest line.
|
|
100
128
|
|
|
129
|
+
### Added
|
|
130
|
+
- **Gate 8 baselines against the line being built.** It compared the working
|
|
131
|
+
tree against the newest release of the whole project, which is right only
|
|
132
|
+
while there is one line. A `maintain/x.y` branch takes fixes to its own
|
|
133
|
+
features and merges upward only, so it never gains what a later line added:
|
|
134
|
+
once that line publishes, every function it introduced would read as a
|
|
135
|
+
removal from the older tree, on every run, for as long as the line is
|
|
136
|
+
supported. The baseline is now the newest release at or below the tree's own
|
|
137
|
+
`major.minor`, taken from the line the checkout can reach by tag. Pre-release
|
|
138
|
+
and yanked files are skipped. Nothing changes on the newest line.
|
|
139
|
+
|
|
101
140
|
### Fixed
|
|
102
141
|
- **A scaffolded experiment ships the `setup.yaml` plesty-lib requires**
|
|
103
142
|
(plesty-sdk#43). plesty-lib 0.5.0.dev6 made every experiment module declare
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: plesty-sdk
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: Command-line application for developing Python modules compliant with the Plesty standard.
|
|
5
5
|
Author: Plesty Development Team, Yunshuang Yuan, Christopher Borchers, fvrehlinger, Maximilian Heller, Michael Zopf
|
|
6
6
|
License-Expression: LGPL-3.0-or-later
|
|
@@ -124,17 +124,10 @@ needs network access (OSV queries), so pushing requires connectivity under `quan
|
|
|
124
124
|
### Version-derived default standard
|
|
125
125
|
|
|
126
126
|
When `[tool.plesty] standard` is unset, `derive_standard()` maps the release version to a
|
|
127
|
-
level, so shared prototypes ramp up automatically
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
| `< 0.1.0`, or a dynamic version with no release tag yet | `pixel` |
|
|
132
|
-
| `>= 0.1.0, < 1.0.0` | `nebula` |
|
|
133
|
-
| `>= 1.0.0` | `quantum` |
|
|
134
|
-
|
|
135
|
-
An explicit `standard` always wins; a project with no version information at all falls back
|
|
136
|
-
to `quantum`, the conservative default. `plesty check` echoes which rule applied, so the
|
|
137
|
-
resolution is never silent.
|
|
127
|
+
level, so shared prototypes ramp up automatically — pixel before `0.1.0`, nebula before
|
|
128
|
+
`1.0.0`, quantum from then on. The mapping and its fallbacks are specified once, under
|
|
129
|
+
[Standard resolution](commands/check.md#standard-resolution) on the `plesty check` page.
|
|
130
|
+
`plesty check` echoes which rule applied, so the resolution is never silent.
|
|
138
131
|
|
|
139
132
|
## Design Decisions
|
|
140
133
|
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# plesty init field-test
|
|
2
|
+
|
|
3
|
+
Adds a field test — the gate sequence run against the real instrument — to an
|
|
4
|
+
**existing** device module. Unlike [`plesty init mock-test`](init-mock-test.md),
|
|
5
|
+
it interviews you.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd plesty-pm100d
|
|
9
|
+
plesty init field-test # interactive
|
|
10
|
+
plesty init field-test --force # replace an existing tests/field_test.py
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`plesty.lib.test.field_test.FieldTestPipeline` gives every module the same gate
|
|
14
|
+
sequence. The gates are the easy part — they are generic and already shared.
|
|
15
|
+
What takes the time, and what three modules each got wrong at least once, is
|
|
16
|
+
the **configuration**, because every line of it is a judgement about the
|
|
17
|
+
instrument that no schema inspection can make:
|
|
18
|
+
|
|
19
|
+
- which operations are safe to call unattended — the wrong answer moved a
|
|
20
|
+
stage, fired an exposure, and rewrote a sensor's zero calibration
|
|
21
|
+
- which parameters survive being written back their own value — one crashed
|
|
22
|
+
the controlling application outright, killing every call after it
|
|
23
|
+
- what is slow enough to be timed out on demand, since a call returning in two
|
|
24
|
+
milliseconds cannot be abandoned and its gate silently skips
|
|
25
|
+
- which two parameters are distinguishable enough that a stale reply is
|
|
26
|
+
unambiguous
|
|
27
|
+
- whether connecting is cheap, or starts an application and costs half a minute
|
|
28
|
+
|
|
29
|
+
## Candidates from the device, judgement from you
|
|
30
|
+
|
|
31
|
+
So the command asks the **device** for the *candidates*, and you for the
|
|
32
|
+
*judgement*. It constructs the module hardware-free — exactly as `plesty docs`
|
|
33
|
+
does, which gate d3 already requires — and reads `doc_model()`.
|
|
34
|
+
|
|
35
|
+
Not the JSON schema files, and the difference is not cosmetic. A module
|
|
36
|
+
registers parameters and operations in Python too (`register_config` in
|
|
37
|
+
`__init__`, an `@expose_to_api` method), and none of that is in a file:
|
|
38
|
+
`plesty-pm100d` alone has two such operations, which a file-reading generator
|
|
39
|
+
would never offer. And for a grouped schema the keys differ —
|
|
40
|
+
`get_config_list()` answers `group_prefix.param_key` while the file is keyed by
|
|
41
|
+
the bare name, so an `only_keys` list built from the file would name parameters
|
|
42
|
+
the pipeline cannot resolve. That is the group-name against command-prefix
|
|
43
|
+
mismatch three modules hit independently.
|
|
44
|
+
|
|
45
|
+
There is no fallback to the files. A device module that cannot build its doc
|
|
46
|
+
model already fails gate d3, and generating from a partial picture would
|
|
47
|
+
produce a file that looks complete and tests less than it claims.
|
|
48
|
+
|
|
49
|
+
## The interview
|
|
50
|
+
|
|
51
|
+
Each question states what the answer is used for before offering the choices,
|
|
52
|
+
and each answer is followed by "why?" — the reason is written into the
|
|
53
|
+
generated file as a comment, so the next reader sees why and not only what.
|
|
54
|
+
|
|
55
|
+
Choices are a checkable list: arrow keys move, space selects, `a` selects all,
|
|
56
|
+
`n` clears, Enter confirms, Escape backs out. Each entry shows what it is —
|
|
57
|
+
a parameter's type and range, an operation's description — because choosing
|
|
58
|
+
which operations may run unsupervised from a list of bare names is how the
|
|
59
|
+
wrong one gets picked.
|
|
60
|
+
|
|
61
|
+
Nothing is ticked to begin with, and Enter confirms whatever *is* ticked, so
|
|
62
|
+
the cheapest keypress gives the most conservative answer. `a` exists but has
|
|
63
|
+
to be typed. While a list is empty it says what empty means **for that
|
|
64
|
+
question**, which is not the same everywhere: no operation selected means none
|
|
65
|
+
is called, while no parameter selected means every one is written back.
|
|
66
|
+
|
|
67
|
+
A run with no keyboard — piped input, a script, CI — falls back to typing
|
|
68
|
+
comma-separated numbers rather than waiting for a keypress that never comes.
|
|
69
|
+
|
|
70
|
+
**Nothing unclassified is treated as safe.** An operation you do not select is
|
|
71
|
+
in neither list: it is never called, and the gate that would have called it
|
|
72
|
+
skips saying so. A generated allow-list that quietly included a motion command
|
|
73
|
+
would be worse than no generator at all.
|
|
74
|
+
|
|
75
|
+
**What the module declares is not asked again.** An operation exposed with a
|
|
76
|
+
kind — `@expose_to_api(kind="motion", position_key="MO.Position", step=1.0)`,
|
|
77
|
+
`kind="lifecycle"`, `kind="control"`, or a `"kind"` block in
|
|
78
|
+
`schema_func.json` (plesty-lib#39) — is shown as `[motion]`, `[lifecycle]`,
|
|
79
|
+
`[control]` in the chooser, and the "acts on the world" question is asked only
|
|
80
|
+
about operations that declared nothing. A `configure` operation is not offered
|
|
81
|
+
at all: it is recorded as excluded, because a setting that outlives the call is
|
|
82
|
+
verified by the round-trip gate. For an allow-listed motion the interview
|
|
83
|
+
confirms how far the test may move it (`step`; the declared value is the
|
|
84
|
+
default) and, if the module left it open, which parameter reports the position
|
|
85
|
+
— a motion with neither is dropped, since the pipeline drives a motion one step
|
|
86
|
+
out from that parameter and back and will not guess how far. A motion chosen as
|
|
87
|
+
the slow operation is asked only how far the long move goes: the pipeline
|
|
88
|
+
starts it from wherever the part is and moves it back afterwards. Both
|
|
89
|
+
generated files carry the result as `OP_KINDS`.
|
|
90
|
+
|
|
91
|
+
## The generated files
|
|
92
|
+
|
|
93
|
+
The result is two files from one interview. `tests/field_test.py` runs on the
|
|
94
|
+
bench against the instrument: hardware-gated (it defines no pytest test
|
|
95
|
+
functions, so CI collects nothing), wired to publish
|
|
96
|
+
`reports/<instrument>/field-test.json`, and runnable against the simulator
|
|
97
|
+
first:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
uv run python tests/field_test.py --address mock
|
|
101
|
+
uv run python tests/field_test.py --address <address>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`tests/field_test_client.py` runs the same checks over the wire, against the
|
|
105
|
+
served module. It inherits the answers rather than asking again: an operation
|
|
106
|
+
unsafe on the bench is unsafe over the network too, and a second allow-list
|
|
107
|
+
would be a second chance to disagree.
|
|
108
|
+
|
|
109
|
+
Neither file is replaced without `--force`: they hold answers somebody thought
|
|
110
|
+
about, and a hardware run may since have corrected them.
|
|
111
|
+
|
|
112
|
+
## The answers are kept
|
|
113
|
+
|
|
114
|
+
Each answer is written to `.plesty/field-test.json` as it is given, and every
|
|
115
|
+
run of the command starts from what that file holds — so Ctrl-C loses nothing,
|
|
116
|
+
and running `plesty init field-test` again carries on from where the interview
|
|
117
|
+
stopped. A question that was displayed but not answered is asked again; it is
|
|
118
|
+
marked done only once an answer is in hand. The file is the source of truth:
|
|
119
|
+
edit it to change an answer without repeating the interview, because an answer
|
|
120
|
+
hand-edited in the generated `.py` is overwritten by the next run. Answers left
|
|
121
|
+
by an older SDK in `.plesty-field-test.partial.json` are moved into `.plesty/`
|
|
122
|
+
on the first run.
|
|
123
|
+
|
|
124
|
+
The run ends by saying which constant holds which answer.
|
|
125
|
+
|
|
126
|
+
The interview text ships in the wheel
|
|
127
|
+
(`plesty/sdk/assets/init/field-test-questions.yaml`) and the generated file's
|
|
128
|
+
skeleton alongside it (`field-test.py.template`).
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# plesty init mock-test
|
|
2
|
+
|
|
3
|
+
Adds the hardware-free test gate d1 requires to an **existing** device module.
|
|
4
|
+
Unlike [`plesty init field-test`](init-field-test.md), it asks nothing.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
cd plesty-pm100d
|
|
8
|
+
plesty init mock-test # writes tests/test_mock_pipeline.py
|
|
9
|
+
plesty init mock-test --force # regenerate over its own previous output
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The field-test interview exists because every one of its questions is a safety
|
|
13
|
+
judgement about a real instrument. Nothing in the mock pipeline can move a
|
|
14
|
+
stage or fire an exposure, and everything it needs is either declared by the
|
|
15
|
+
device or settled by trying it:
|
|
16
|
+
|
|
17
|
+
| Source | What it gives |
|
|
18
|
+
|---|---|
|
|
19
|
+
| the device's `doc_model()` | the parameters and operations, read through the same reader the field test uses |
|
|
20
|
+
| the package name | the module and the import |
|
|
21
|
+
| a construction probe | how to reach the device without hardware |
|
|
22
|
+
|
|
23
|
+
## The probe
|
|
24
|
+
|
|
25
|
+
**The probe is the design.** A device module is hardware-free in one of
|
|
26
|
+
several ways — `address="mock"` selects an in-memory simulator in one
|
|
27
|
+
reference module, `use_mock_solver=True` does it in the other — and which one
|
|
28
|
+
applies is knowable by constructing the device and looking. Each candidate is
|
|
29
|
+
verified *the way the gates will verify it*: connect, read `is_operatable`,
|
|
30
|
+
call `identity()`, and call `check_errors()`, which must return a list — empty
|
|
31
|
+
on a fresh device. A strategy is written into the file only once it has passed
|
|
32
|
+
the checks the file will make, which is why a generated test that gets written
|
|
33
|
+
is a generated test that passes those checks.
|
|
34
|
+
|
|
35
|
+
When neither applies, the last resort is the module's own `DocDevice` — the
|
|
36
|
+
hardware-free construction gate d3 already requires. A module scaffolded by
|
|
37
|
+
`plesty init device` exports it as `partial(Device, id=...)`, and because
|
|
38
|
+
`DevicePipeline` constructs from a class, the file passes `Device` and
|
|
39
|
+
forwards the partial's own arguments:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
PIPELINE = DevicePipeline(Device, *DocDevice.args, **DocDevice.keywords)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
A `DocDevice` that is neither a class nor a partial of `Device` is refused
|
|
46
|
+
with that reason.
|
|
47
|
+
|
|
48
|
+
Where no strategy works the command refuses and lists what it tried. It does
|
|
49
|
+
not fall back to asking: a developer handed a question the probe could not
|
|
50
|
+
answer would be guessing at the same thing, and a file built on a guess looks
|
|
51
|
+
complete and tests less than it claims. Give the device a hardware-free path —
|
|
52
|
+
`address="mock"` is the convention, and
|
|
53
|
+
`plesty.lib.sim.demo_device.DemoDevice` is the reference implementation of it —
|
|
54
|
+
then run the command again.
|
|
55
|
+
|
|
56
|
+
The probe covers three of the eight gates. On a fresh scaffold the file is
|
|
57
|
+
written and gate 2 (parameter key resolution) still fails until the device
|
|
58
|
+
declares its schema — the first of the next steps `plesty init device` prints.
|
|
59
|
+
|
|
60
|
+
## The generated file
|
|
61
|
+
|
|
62
|
+
The generated file is short, because `run_mock_pipeline()` runs all eight
|
|
63
|
+
gates and raises with everything that failed:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from plesty.lib.test.device_pipeline import DevicePipeline
|
|
67
|
+
|
|
68
|
+
from plesty.pm100d import Device
|
|
69
|
+
|
|
70
|
+
PIPELINE = DevicePipeline(Device, address="mock")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_mock_pipeline() -> None:
|
|
74
|
+
"""Run the eight mock gates and fail with everything that broke."""
|
|
75
|
+
PIPELINE.run_mock_pipeline()
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
No schema path appears in it. The gates read what the device declares at run
|
|
79
|
+
time, so nothing here restates a schema and nothing goes stale when the device
|
|
80
|
+
gains a parameter.
|
|
81
|
+
|
|
82
|
+
The file is always `tests/test_mock_pipeline.py`, never `tests/test_<module>.py`
|
|
83
|
+
— that is where a module's hand-written unit tests already live, and one
|
|
84
|
+
reference module keeps 487 lines of them. `--force` regenerates over this
|
|
85
|
+
command's own output and refuses anything else, checked by a marker in the
|
|
86
|
+
file, so a regeneration cannot destroy work.
|
|
@@ -19,8 +19,9 @@ plesty init [PROJECT_TYPE] [OPTIONS]
|
|
|
19
19
|
and `experiment` the plan/journal experiment skeleton with the E1 gate tests.
|
|
20
20
|
|
|
21
21
|
Two further types, `mock-test` and `field-test`, add a test to an **existing** device
|
|
22
|
-
module instead of creating a project
|
|
23
|
-
|
|
22
|
+
module instead of creating a project; each has its own page:
|
|
23
|
+
[`plesty init mock-test`](init-mock-test.md) and
|
|
24
|
+
[`plesty init field-test`](init-field-test.md).
|
|
24
25
|
|
|
25
26
|
## Naming conventions
|
|
26
27
|
|
|
@@ -63,9 +64,10 @@ After copying the template, `plesty init` automatically:
|
|
|
63
64
|
2. **Creates the initial git commit** — required by `versioningit` to resolve a package
|
|
64
65
|
version from git history.
|
|
65
66
|
3. **Installs the pre-push hook** — writes `.git/hooks/pre-push` to run
|
|
66
|
-
`plesty check` automatically before every `git push
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
`plesty check` automatically before every `git push`, at the project's
|
|
68
|
+
resolved standard (see [Standard resolution](check.md#standard-resolution)).
|
|
69
|
+
Hooks are not cloned: a contributor who clones the project installs it once
|
|
70
|
+
with `plesty update --install-hook`.
|
|
69
71
|
|
|
70
72
|
## Examples
|
|
71
73
|
|
|
@@ -316,188 +318,3 @@ the global `--quiet` flag to print only the result line:
|
|
|
316
318
|
```bash
|
|
317
319
|
plesty --quiet --project-dir plesty-foo-bar init device
|
|
318
320
|
```
|
|
319
|
-
|
|
320
|
-
## `plesty init mock-test`
|
|
321
|
-
|
|
322
|
-
Adds to an **existing** device module, like `field-test` — and unlike it, asks
|
|
323
|
-
nothing.
|
|
324
|
-
|
|
325
|
-
```bash
|
|
326
|
-
cd plesty-pm100d
|
|
327
|
-
plesty init mock-test # writes tests/test_mock_pipeline.py
|
|
328
|
-
plesty init mock-test --force # regenerate over its own previous output
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
The field-test interview exists because every one of its questions is a safety
|
|
332
|
-
judgement about a real instrument. Nothing in the mock pipeline can move a
|
|
333
|
-
stage or fire an exposure, and everything it needs is either declared by the
|
|
334
|
-
device or settled by trying it:
|
|
335
|
-
|
|
336
|
-
| Source | What it gives |
|
|
337
|
-
|---|---|
|
|
338
|
-
| the device's `doc_model()` | the parameters and operations, read through the same reader the field test uses |
|
|
339
|
-
| the package name | the module and the import |
|
|
340
|
-
| a construction probe | how to reach the device without hardware |
|
|
341
|
-
|
|
342
|
-
**The probe is the design.** A device module is hardware-free in one of
|
|
343
|
-
several ways — `address="mock"` selects an in-memory simulator in one
|
|
344
|
-
reference module, `use_mock_solver=True` does it in the other — and which one
|
|
345
|
-
applies is knowable by constructing the device and looking. Each candidate is
|
|
346
|
-
verified *the way the gates will verify it*: connect, read `is_operatable`,
|
|
347
|
-
call `identity()` and `check_errors()`. A strategy is written into the file
|
|
348
|
-
only once it has passed the checks the file will make, which is why a
|
|
349
|
-
generated test that gets written is a generated test that passes.
|
|
350
|
-
|
|
351
|
-
Where no strategy works the command refuses and lists what it tried. It does
|
|
352
|
-
not fall back to asking: a developer handed a question the probe could not
|
|
353
|
-
answer would be guessing at the same thing, and a file built on a guess looks
|
|
354
|
-
complete and tests less than it claims. Give the device a hardware-free path —
|
|
355
|
-
`address="mock"` is the convention, and
|
|
356
|
-
`plesty.lib.sim.demo_device.DemoDevice` is the reference implementation of it —
|
|
357
|
-
then run the command again.
|
|
358
|
-
|
|
359
|
-
The generated file is short, because `run_mock_pipeline()` runs all eight
|
|
360
|
-
gates and raises with everything that failed:
|
|
361
|
-
|
|
362
|
-
```python
|
|
363
|
-
from plesty.lib.test.device_pipeline import DevicePipeline
|
|
364
|
-
|
|
365
|
-
from plesty.pm100d import Device
|
|
366
|
-
|
|
367
|
-
PIPELINE = DevicePipeline(Device, address="mock")
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
def test_mock_pipeline() -> None:
|
|
371
|
-
"""Run the eight mock gates and fail with everything that broke."""
|
|
372
|
-
PIPELINE.run_mock_pipeline()
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
No schema path appears in it. The gates read what the device declares at run
|
|
376
|
-
time, so nothing here restates a schema and nothing goes stale when the device
|
|
377
|
-
gains a parameter.
|
|
378
|
-
|
|
379
|
-
The file is always `tests/test_mock_pipeline.py`, never `tests/test_<module>.py`
|
|
380
|
-
— that is where a module's hand-written unit tests already live, and one
|
|
381
|
-
reference module keeps 487 lines of them. `--force` regenerates over this
|
|
382
|
-
command's own output and refuses anything else, checked by a marker in the
|
|
383
|
-
file, so a regeneration cannot destroy work.
|
|
384
|
-
|
|
385
|
-
## `plesty init field-test`
|
|
386
|
-
|
|
387
|
-
Different from the other project types: it adds to an **existing** device
|
|
388
|
-
module rather than creating a project.
|
|
389
|
-
|
|
390
|
-
```bash
|
|
391
|
-
cd plesty-pm100d
|
|
392
|
-
plesty init field-test # interactive
|
|
393
|
-
plesty init field-test --force # replace an existing tests/field_test.py
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
`plesty.lib.test.field_test.FieldTestPipeline` gives every module the same gate
|
|
397
|
-
sequence. The gates are the easy part — they are generic and already shared.
|
|
398
|
-
What takes the time, and what three modules each got wrong at least once, is
|
|
399
|
-
the **configuration**, because every line of it is a judgement about the
|
|
400
|
-
instrument that no schema inspection can make:
|
|
401
|
-
|
|
402
|
-
- which operations are safe to call unattended — the wrong answer moved a
|
|
403
|
-
stage, fired an exposure, and rewrote a sensor's zero calibration
|
|
404
|
-
- which parameters survive being written back their own value — one crashed
|
|
405
|
-
the controlling application outright, killing every call after it
|
|
406
|
-
- what is slow enough to be timed out on demand, since a call returning in two
|
|
407
|
-
milliseconds cannot be abandoned and its gate silently skips
|
|
408
|
-
- which two parameters are distinguishable enough that a stale reply is
|
|
409
|
-
unambiguous
|
|
410
|
-
- whether connecting is cheap, or starts an application and costs half a minute
|
|
411
|
-
|
|
412
|
-
So the command asks the **device** for the *candidates*, and you for the
|
|
413
|
-
*judgement*. It constructs the module hardware-free — exactly as `plesty docs`
|
|
414
|
-
does, which gate d3 already requires — and reads `doc_model()`.
|
|
415
|
-
|
|
416
|
-
Not the JSON schema files, and the difference is not cosmetic. A module
|
|
417
|
-
registers parameters and operations in Python too (`register_config` in
|
|
418
|
-
`__init__`, an `@expose_to_api` method), and none of that is in a file:
|
|
419
|
-
`plesty-pm100d` alone has two such operations, which a file-reading generator
|
|
420
|
-
would never offer. And for a grouped schema the keys differ —
|
|
421
|
-
`get_config_list()` answers `group_prefix.param_key` while the file is keyed by
|
|
422
|
-
the bare name, so an `only_keys` list built from the file would name parameters
|
|
423
|
-
the pipeline cannot resolve. That is the group-name against command-prefix
|
|
424
|
-
mismatch three modules hit independently.
|
|
425
|
-
|
|
426
|
-
There is no fallback to the files. A device module that cannot build its doc
|
|
427
|
-
model already fails gate d3, and generating from a partial picture would
|
|
428
|
-
produce a file that looks complete and tests less than it claims.
|
|
429
|
-
|
|
430
|
-
Each question states what the answer is used for before offering the choices,
|
|
431
|
-
and each answer is followed by "why?" — the reason is written into the
|
|
432
|
-
generated file as a comment, so the next reader sees why and not only what.
|
|
433
|
-
|
|
434
|
-
Choices are a checkable list: arrow keys move, space selects, `a` selects all,
|
|
435
|
-
`n` clears, Enter confirms, Escape backs out. Each entry shows what it is —
|
|
436
|
-
a parameter's type and range, an operation's description — because choosing
|
|
437
|
-
which operations may run unsupervised from a list of bare names is how the
|
|
438
|
-
wrong one gets picked.
|
|
439
|
-
|
|
440
|
-
Nothing is ticked to begin with, and Enter confirms whatever *is* ticked, so
|
|
441
|
-
the cheapest keypress gives the most conservative answer. `a` exists but has
|
|
442
|
-
to be typed. While a list is empty it says what empty means **for that
|
|
443
|
-
question**, which is not the same everywhere: no operation selected means none
|
|
444
|
-
is called, while no parameter selected means every one is written back.
|
|
445
|
-
|
|
446
|
-
A run with no keyboard — piped input, a script, CI — falls back to typing
|
|
447
|
-
comma-separated numbers rather than waiting for a keypress that never comes.
|
|
448
|
-
|
|
449
|
-
**Nothing unclassified is treated as safe.** An operation you do not select is
|
|
450
|
-
in neither list: it is never called, and the gate that would have called it
|
|
451
|
-
skips saying so. A generated allow-list that quietly included a motion command
|
|
452
|
-
would be worse than no generator at all.
|
|
453
|
-
|
|
454
|
-
**What the module declares is not asked again.** An operation exposed with a
|
|
455
|
-
kind — `@expose_to_api(kind="motion", position_key="MO.Position", step=1.0)`,
|
|
456
|
-
`kind="lifecycle"`, `kind="control"`, or a `"kind"` block in
|
|
457
|
-
`schema_func.json` (plesty-lib#39) — is shown as `[motion]`, `[lifecycle]`,
|
|
458
|
-
`[control]` in the chooser, and the "acts on the world" question is asked only
|
|
459
|
-
about operations that declared nothing. A `configure` operation is not offered
|
|
460
|
-
at all: it is recorded as excluded, because a setting that outlives the call is
|
|
461
|
-
verified by the round-trip gate. For an allow-listed motion the interview
|
|
462
|
-
confirms how far the test may move it (`step`; the declared value is the
|
|
463
|
-
default) and, if the module left it open, which parameter reports the position
|
|
464
|
-
— a motion with neither is dropped, since the pipeline drives a motion one step
|
|
465
|
-
out from that parameter and back and will not guess how far. A motion chosen as
|
|
466
|
-
the slow operation is asked only how far the long move goes: the pipeline
|
|
467
|
-
starts it from wherever the part is and moves it back afterwards. Both
|
|
468
|
-
generated files carry the result as `OP_KINDS`.
|
|
469
|
-
|
|
470
|
-
The result is two files from one interview. `tests/field_test.py` runs on the
|
|
471
|
-
bench against the instrument: hardware-gated (it defines no pytest test
|
|
472
|
-
functions, so CI collects nothing), wired to publish
|
|
473
|
-
`reports/<instrument>/field-test.json`, and runnable against the simulator
|
|
474
|
-
first:
|
|
475
|
-
|
|
476
|
-
```bash
|
|
477
|
-
uv run python tests/field_test.py --address mock
|
|
478
|
-
uv run python tests/field_test.py --address <address>
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
`tests/field_test_client.py` runs the same checks over the wire, against the
|
|
482
|
-
served module. It inherits the answers rather than asking again: an operation
|
|
483
|
-
unsafe on the bench is unsafe over the network too, and a second allow-list
|
|
484
|
-
would be a second chance to disagree.
|
|
485
|
-
|
|
486
|
-
Neither file is replaced without `--force`: they hold answers somebody thought
|
|
487
|
-
about, and a hardware run may since have corrected them.
|
|
488
|
-
|
|
489
|
-
**The answers are kept.** Each one is written to `.plesty/field-test.json` as
|
|
490
|
-
it is given, and every run of the command starts from what that file holds —
|
|
491
|
-
so Ctrl-C loses nothing, and running `plesty init field-test` again carries on
|
|
492
|
-
from where the interview stopped. A question that was displayed but not
|
|
493
|
-
answered is asked again; it is marked done only once an answer is in hand. The
|
|
494
|
-
file is the source of truth: edit it to change an answer without repeating the
|
|
495
|
-
interview, because an answer hand-edited in the generated `.py` is overwritten
|
|
496
|
-
by the next run. Answers left by an older SDK in
|
|
497
|
-
`.plesty-field-test.partial.json` are moved into `.plesty/` on the first run.
|
|
498
|
-
|
|
499
|
-
The run ends by saying which constant holds which answer.
|
|
500
|
-
|
|
501
|
-
The interview text ships in the wheel
|
|
502
|
-
(`plesty/sdk/assets/init/field-test-questions.yaml`) and the generated file's
|
|
503
|
-
skeleton alongside it (`field-test.py.template`).
|
|
@@ -27,9 +27,16 @@ plesty --project-dir ~/projects/plesty-power-meter init device \
|
|
|
27
27
|
This generates a project pre-configured with the expected repository structure,
|
|
28
28
|
`pyproject.toml`, example source files, CI pipeline (`.gitlab-ci.yml`), documentation
|
|
29
29
|
stubs, and a `CHANGELOG.md`. A pre-push git hook is also installed automatically —
|
|
30
|
-
it runs `plesty check` before every `git push`,
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
it runs `plesty check` before every `git push`, at the project's resolved standard
|
|
31
|
+
(see [Standard resolution](commands/check.md#standard-resolution)).
|
|
32
|
+
|
|
33
|
+
```{note}
|
|
34
|
+
Git hooks live in `.git/` and are never cloned. Anyone who clones an existing
|
|
35
|
+
project — every contributor after the one who scaffolded it — installs the hook
|
|
36
|
+
once:
|
|
37
|
+
|
|
38
|
+
plesty update --install-hook
|
|
39
|
+
```
|
|
33
40
|
|
|
34
41
|
Use `--standard` to choose the compliance standard the generated CI pipeline enforces
|
|
35
42
|
(default `pixel` — a fresh scaffold is a prototype; bump it alongside your releases), or
|
|
@@ -61,15 +68,37 @@ cd plesty-power-meter
|
|
|
61
68
|
plesty check
|
|
62
69
|
```
|
|
63
70
|
|
|
64
|
-
By default this checks against the standard
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
By default this checks against the project's resolved standard — a fresh scaffold
|
|
72
|
+
without a release tag starts at **pixel** and ramps up to **quantum** with `v1.0.0`
|
|
73
|
+
(see [Standard resolution](commands/check.md#standard-resolution)). Add
|
|
74
|
+
`--standard pixel` for a fast metadata + code hygiene check, or `--standard nebula` for
|
|
75
|
+
all static gates (1–6) without the slower quantum-only ones (7–11 and the module-type
|
|
76
|
+
gates). Fix any reported issues and re-run until the check passes.
|
|
77
|
+
|
|
78
|
+
## 3. Test the device, without and then with hardware
|
|
79
|
+
|
|
80
|
+
Under `quantum`, a device module must pass gate d1: a test that runs plesty-lib's eight
|
|
81
|
+
mock gates without an instrument attached. Once the device declares its schema, generate
|
|
82
|
+
that test — the command works out by itself how to reach the device without hardware:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
plesty init mock-test # writes tests/test_mock_pipeline.py
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
When the instrument is on the bench, add the field test. It interviews you about what is
|
|
89
|
+
safe to run unattended, then writes a test that runs against the real device and
|
|
90
|
+
publishes a report:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
plesty init field-test
|
|
94
|
+
uv run python tests/field_test.py --address mock # rehearse against the simulator
|
|
95
|
+
uv run python tests/field_test.py --address <address> # then the instrument
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
See [`plesty init mock-test`](commands/init-mock-test.md) and
|
|
99
|
+
[`plesty init field-test`](commands/init-field-test.md) for what each asks and writes.
|
|
71
100
|
|
|
72
|
-
##
|
|
101
|
+
## 4. Preview the documentation
|
|
73
102
|
|
|
74
103
|
Start a local documentation server with live reload:
|
|
75
104
|
|
|
@@ -80,7 +109,7 @@ plesty docs serve
|
|
|
80
109
|
Open [http://localhost:8000](http://localhost:8000) in your browser. Any changes to `.md`
|
|
81
110
|
or `.rst` files in the `docs/` directory are reflected immediately.
|
|
82
111
|
|
|
83
|
-
##
|
|
112
|
+
## 5. Apply license headers
|
|
84
113
|
|
|
85
114
|
Keep all Python files covered by the SPDX license header:
|
|
86
115
|
|
|
@@ -91,7 +120,7 @@ plesty license update
|
|
|
91
120
|
This reads author and maintainer information from `pyproject.toml` and writes the correct
|
|
92
121
|
header to every `.py` file in the project.
|
|
93
122
|
|
|
94
|
-
##
|
|
123
|
+
## 6. Update project metadata
|
|
95
124
|
|
|
96
125
|
Use `plesty update` to change author info, the PyPI distribution name, or the CI pipeline
|
|
97
126
|
at any time without re-scaffolding the project:
|
|
@@ -107,7 +136,7 @@ plesty update --name plesty-pm100d
|
|
|
107
136
|
plesty update --standard quantum
|
|
108
137
|
```
|
|
109
138
|
|
|
110
|
-
##
|
|
139
|
+
## 7. Deploy documentation
|
|
111
140
|
|
|
112
141
|
Documentation deployment is handled automatically by the `hub-deploy-docs` job of the
|
|
113
142
|
`plesty-standard-ci` component when commits land on the default branch or a release tag is
|
|
@@ -26,6 +26,21 @@ plesty -d ../my-device docs serve
|
|
|
26
26
|
Every command reads `pyproject.toml`, the `.plesty` cache, and all source paths relative to
|
|
27
27
|
this directory.
|
|
28
28
|
|
|
29
|
+
## The `--quiet` option
|
|
30
|
+
|
|
31
|
+
The second global option, `--quiet` (`-q`), suppresses progress and guidance output —
|
|
32
|
+
`plesty check`'s per-gate progress lines, the numbered **Next steps** `plesty init` ends
|
|
33
|
+
with. Failures, their guidance, and the result line always print, so a quiet run still
|
|
34
|
+
says what broke and whether it passed:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
plesty --quiet check
|
|
38
|
+
plesty -q --project-dir plesty-foo-bar init device
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Global options go before the command name: `plesty --quiet check`, not
|
|
42
|
+
`plesty check --quiet`.
|
|
43
|
+
|
|
29
44
|
## Commands at a glance
|
|
30
45
|
|
|
31
46
|
| Command | Description |
|
|
@@ -8,6 +8,11 @@ entries:
|
|
|
8
8
|
title: check
|
|
9
9
|
- file: commands/init.md
|
|
10
10
|
title: init
|
|
11
|
+
entries:
|
|
12
|
+
- file: commands/init-mock-test.md
|
|
13
|
+
title: init mock-test
|
|
14
|
+
- file: commands/init-field-test.md
|
|
15
|
+
title: init field-test
|
|
11
16
|
- file: commands/docs.md
|
|
12
17
|
title: docs
|
|
13
18
|
- file: commands/license.md
|