plesty-sdk 0.3.4__tar.gz → 0.3.5.dev1__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 (99) hide show
  1. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/CHANGELOG.md +16 -0
  2. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/PKG-INFO +1 -1
  3. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/docs/commands/init.md +12 -1
  4. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/gl-secret-detection-report.json +5 -5
  5. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/pyproject.toml.jinja +3 -3
  6. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/commands/init.py +64 -1
  7. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_init.py +55 -0
  8. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/.coverage +0 -0
  9. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/.gitignore +0 -0
  10. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/.gitlab-ci.yml +0 -0
  11. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/.mailmap +0 -0
  12. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/LICENSE +0 -0
  13. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/LICENSES/LGPL-3.0-or-later.txt +0 -0
  14. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/README.md +0 -0
  15. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/REUSE.toml +0 -0
  16. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/docs/architecture.md +0 -0
  17. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/docs/commands/check.md +0 -0
  18. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/docs/commands/docs.md +0 -0
  19. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/docs/commands/license.md +0 -0
  20. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/docs/commands/manual.md +0 -0
  21. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/docs/commands/update.md +0 -0
  22. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/docs/getting-started.md +0 -0
  23. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/docs/index.md +0 -0
  24. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/docs/toc.yaml +0 -0
  25. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/__init__.py +0 -0
  26. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/check/guidance.yaml +0 -0
  27. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/docs-build-gitlab-ci.yml +0 -0
  28. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/hub-theme/plesty-hub.css +0 -0
  29. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/hub-theme/plesty-logo.svg +0 -0
  30. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/hub-theme/plesty-mark.svg +0 -0
  31. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/init/client-field-test.py.template +0 -0
  32. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/init/field-test-env.example +0 -0
  33. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/init/field-test-questions.yaml +0 -0
  34. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/init/field-test.py.template +0 -0
  35. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/init/mock-test.py.template +0 -0
  36. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/init/next-steps.yaml +0 -0
  37. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/manual/pdf-common.css +0 -0
  38. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/manual/pdf-page-landscape.css +0 -0
  39. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/manual/pdf-page-portrait.css +0 -0
  40. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/ruff.toml +0 -0
  41. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/copier.yml +0 -0
  42. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/extras/analyzer/analyzer.py +0 -0
  43. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/extras/device/base_device.py +0 -0
  44. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/extras/device/device.py +0 -0
  45. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/extras/experiment/experiment.py +0 -0
  46. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/.gitignore +0 -0
  47. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/CHANGELOG.md.jinja +0 -0
  48. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/LICENSE +0 -0
  49. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/LICENSES/LGPL-3.0-or-later.txt +0 -0
  50. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/README.md.jinja +0 -0
  51. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/REUSE.toml.jinja +0 -0
  52. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/docs/index.md.jinja +0 -0
  53. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/docs/toc.yaml +0 -0
  54. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/plesty/{{module_name}}/__init__.py.jinja +0 -0
  55. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/plesty/{{module_name}}/config.yaml.jinja +0 -0
  56. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/plesty/{{module_name}}/{% if project_type in ['device', 'experiment'] %}__main__.py{% endif %}.jinja +0 -0
  57. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/tests/__init__.py.jinja +0 -0
  58. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/tests/test_{{module_name}}.py.jinja +0 -0
  59. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/{% if project_type == 'experiment' %}config{% endif %}/default.yaml +0 -0
  60. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/assets/templates/template/{% if project_type in ['device', 'experiment'] %}.env.example{% endif %}.jinja +0 -0
  61. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/authorship.py +0 -0
  62. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/cache.py +0 -0
  63. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/cli.py +0 -0
  64. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/commands/__init__.py +0 -0
  65. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/commands/check.py +0 -0
  66. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/commands/docs/__init__.py +0 -0
  67. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/commands/docs/deploy.py +0 -0
  68. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/commands/docs/generate.py +0 -0
  69. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/commands/docs/serve.py +0 -0
  70. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/commands/docs/sphinx_builder.py +0 -0
  71. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/commands/field_test.py +0 -0
  72. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/commands/license.py +0 -0
  73. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/commands/manual.py +0 -0
  74. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/commands/mock_test.py +0 -0
  75. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/commands/update.py +0 -0
  76. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/context.py +0 -0
  77. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/report.py +0 -0
  78. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/semver.py +0 -0
  79. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/plesty/sdk/tui.py +0 -0
  80. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/pyproject.toml +0 -0
  81. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/__init__.py +0 -0
  82. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_authorship.py +0 -0
  83. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_cache.py +0 -0
  84. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_check.py +0 -0
  85. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_cli.py +0 -0
  86. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_context.py +0 -0
  87. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_docs_deploy.py +0 -0
  88. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_docs_generate.py +0 -0
  89. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_docs_serve.py +0 -0
  90. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_field_test_scaffold.py +0 -0
  91. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_license.py +0 -0
  92. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_manual.py +0 -0
  93. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_mock_test_scaffold.py +0 -0
  94. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_report.py +0 -0
  95. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_semver.py +0 -0
  96. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_sphinx_builder.py +0 -0
  97. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_tui.py +0 -0
  98. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/tests/test_update.py +0 -0
  99. {plesty_sdk-0.3.4 → plesty_sdk-0.3.5.dev1}/uv.lock +0 -0
@@ -2,6 +2,22 @@
2
2
 
3
3
  The format is based on [Keep a Changelog](https://keepachangelog.com).
4
4
 
5
+ ## [0.3.5.dev1] — 2026-08-21
6
+
7
+ Development pre-release: the tutorial needs the pin option before 0.3.5 is
8
+ ready to be called finished.
9
+
10
+ ### Added
11
+ - `plesty init --lib-version VERSION` pins `plesty-lib` to what the caller
12
+ names instead of to the newest release on PyPI, and skips the lookup. A bare
13
+ version becomes a lower bound (`0.3.5.dev3` → `>=0.3.5.dev3`); a complete
14
+ specifier is written as given (`==0.4.0`, `>=0.3.5.dev3,<0.4`). One that PEP
15
+ 440 cannot read is refused at the command line, before a directory is
16
+ created. For the tutorial: a lesson quoting a `pyproject.toml` and the file
17
+ its reader ends up with have to keep saying the same thing however far the
18
+ library has moved since the page was written — and a scaffold on an
19
+ air-gapped machine now pins what was asked for rather than a fallback.
20
+
5
21
  ## [0.3.4] — 2026-08-21
6
22
 
7
23
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: plesty-sdk
3
- Version: 0.3.4
3
+ Version: 0.3.5.dev1
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
@@ -41,6 +41,7 @@ name, so `plesty-foo-bar` → `foo_bar` without needing `--name`.
41
41
  | `--author` | `John Doe` | Author name written into `pyproject.toml` and `REUSE.toml` |
42
42
  | `--email` | `john.doe@example.com` | Author email written into `pyproject.toml` |
43
43
  | `--standard` | `pixel` | Compliance standard for the generated CI pipeline: `pixel`, `nebula`, or `quantum`. A fresh scaffold is a prototype, so CI starts at `pixel`; bump it alongside your releases. |
44
+ | `--lib-version` | newest on PyPI | Pin `plesty-lib` to this version instead of looking it up. A bare version becomes a lower bound (`0.3.5.dev3` → `>=0.3.5.dev3`); a complete specifier is written as given (`==0.4.0`, `'>=0.3.5.dev3,<0.4'`). PyPI is not consulted, so the scaffold is reproducible. |
44
45
  | `--no-ci` | off | Skip generating `.gitlab-ci.yml` entirely. |
45
46
 
46
47
  ## Directory rules
@@ -126,7 +127,17 @@ The `pyproject.toml` is pre-configured with:
126
127
  no route to PyPI the pin falls back to `PLESTY_LIB_VERSION` in
127
128
  `plesty/sdk/commands/init.py`, the library the templates are written against.
128
129
  The pin is never lower than that floor: the scaffold uses that library's
129
- operation kinds and rig start-up, and an older one would not run it
130
+ operation kinds and rig start-up, and an older one would not run it.
131
+ `--lib-version` overrides all of that and skips the lookup — for a tutorial,
132
+ whose lesson text and reader's `pyproject.toml` have to keep agreeing however
133
+ far the library has moved since the page was written:
134
+
135
+ ```bash
136
+ plesty init experiment --name scan_exp --lib-version 0.3.5.dev3
137
+ # → "plesty-lib>=0.3.5.dev3"
138
+ plesty init experiment --name scan_exp --lib-version '>=0.3.5.dev3,<0.4'
139
+ # → "plesty-lib>=0.3.5.dev3,<0.4"
140
+ ```
130
141
  - `pytest`, `pytest-cov`, `mypy`, and `plesty-sdk` under `[dependency-groups] dev`
131
142
  (included automatically by `uv run` — no `--extra dev` needed)
132
143
  - Sphinx docs settings under `[tool.plesty.docs]`
@@ -19,19 +19,19 @@
19
19
  "version": "8.30.1"
20
20
  },
21
21
  "type": "secret_detection",
22
- "start_time": "2026-08-21T18:16:41",
23
- "end_time": "2026-08-21T18:16:41",
22
+ "start_time": "2026-08-21T18:31:49",
23
+ "end_time": "2026-08-21T18:31:49",
24
24
  "status": "success",
25
25
  "observability": {
26
26
  "events": [
27
27
  {
28
28
  "event": "collect_secrets_analyzer_scan_metrics_from_pipeline",
29
- "time_s": 0.267080436,
29
+ "time_s": 0.480570925,
30
30
  "exit_code": 0,
31
31
  "git_strategy": "FetchShallow",
32
- "repo_size_kb": 503,
32
+ "repo_size_kb": 468,
33
33
  "commit_count": 1,
34
- "bytes_scanned": 163,
34
+ "bytes_scanned": 136,
35
35
  "pipeline_type": "Tag"
36
36
  },
37
37
  {
@@ -16,9 +16,9 @@ dependencies = [
16
16
  # plesty.lib.utils.config (which load_config() below uses), the operation
17
17
  # kinds @expose_to_api declares and the field test drives, and the rig
18
18
  # start-up connect_rig()/preflight(). Pinned to the newest library published
19
- # when this project was scaffolded; a .devN bound is also what makes a
20
- # pre-release resolvable at all.
21
- "plesty-lib>={{ plesty_lib_version }}",
19
+ # when this project was scaffolded, or to whatever `plesty init --lib-version`
20
+ # asked for; a .devN bound is also what makes a pre-release resolvable at all.
21
+ "plesty-lib{{ plesty_lib_specifier }}",
22
22
  ]
23
23
 
24
24
  [tool.plesty]
@@ -21,6 +21,7 @@ from pathlib import Path
21
21
  import click
22
22
  import copier
23
23
  import yaml
24
+ from packaging.specifiers import InvalidSpecifier, SpecifierSet
24
25
  from packaging.version import InvalidVersion, Version
25
26
 
26
27
  from plesty.sdk.commands.field_test import scaffold_field_test
@@ -93,6 +94,50 @@ def _parse_version(version: str) -> Version | None:
93
94
  return None
94
95
 
95
96
 
97
+ def lib_specifier(requested: str | None = None) -> str:
98
+ """Return the version specifier to write after ``plesty-lib`` in pyproject.
99
+
100
+ Without *requested* the pin follows PyPI (:func:`resolve_lib_version`).
101
+ With it, the caller decides and no lookup happens — a tutorial pins the
102
+ library its lesson text was written against, so the page and the file the
103
+ reader ends up with keep saying the same thing however far the library has
104
+ moved since; an air-gapped machine gets the same determinism.
105
+
106
+ Args:
107
+ requested: A bare version (``"0.3.5.dev3"`` → ``">=0.3.5.dev3"``) or a
108
+ complete specifier, which is taken as written
109
+ (``"==0.4.0"``, ``">=0.3.5.dev3,<0.4"``).
110
+
111
+ Returns:
112
+ The specifier, leading operator included.
113
+
114
+ Raises:
115
+ click.ClickException: If *requested* is neither a version nor a
116
+ specifier PEP 440 can read.
117
+ """
118
+ if requested is None:
119
+ return f">={resolve_lib_version()}"
120
+ text = requested.strip()
121
+ if not text:
122
+ raise click.ClickException("--lib-version was given nothing to pin to.")
123
+ if text[0] in "<>=!~":
124
+ try:
125
+ SpecifierSet(text)
126
+ except InvalidSpecifier as exc:
127
+ raise click.ClickException(
128
+ f"--lib-version {requested!r} is not a valid version specifier: {exc}"
129
+ ) from exc
130
+ return text
131
+ try:
132
+ Version(text)
133
+ except InvalidVersion as exc:
134
+ raise click.ClickException(
135
+ f"--lib-version {requested!r} is not a valid version: {exc}. Give a version "
136
+ "(0.3.5.dev3) or a full specifier (>=0.3.5.dev3,<0.4)."
137
+ ) from exc
138
+ return f">={text}"
139
+
140
+
96
141
  @click.command(name="init", short_help="Initialize a new Plesty project from a template.")
97
142
  @click.argument(
98
143
  "project_type",
@@ -125,6 +170,19 @@ def _parse_version(version: str) -> Version | None:
125
170
  "quantum) — bump the CI input alongside your releases."
126
171
  ),
127
172
  )
173
+ @click.option(
174
+ "--lib-version",
175
+ default=None,
176
+ metavar="VERSION",
177
+ help=(
178
+ "Pin plesty-lib to this version instead of the newest published one. "
179
+ "A bare version becomes a lower bound (0.3.5.dev3 → >=0.3.5.dev3); a "
180
+ "complete specifier is written as given (==0.4.0, '>=0.3.5.dev3,<0.4'). "
181
+ "PyPI is not consulted, so the scaffold is reproducible — which is what "
182
+ "a tutorial needs: the lesson text and the reader's pyproject.toml keep "
183
+ "agreeing however far the library has moved since."
184
+ ),
185
+ )
128
186
  @click.option(
129
187
  "--no-ci",
130
188
  is_flag=True,
@@ -150,6 +208,7 @@ def init_command(
150
208
  author: str | None,
151
209
  email: str | None,
152
210
  standard: str,
211
+ lib_version: str | None,
153
212
  no_ci: bool,
154
213
  force: bool,
155
214
  ) -> None:
@@ -192,6 +251,10 @@ def init_command(
192
251
  scaffold_mock_test(target_dir, force=force)
193
252
  return
194
253
 
254
+ # Settle the pin before anything is created: an unreadable --lib-version
255
+ # should not leave a directory behind for a project that never happened.
256
+ lib_pin = lib_specifier(lib_version)
257
+
195
258
  if module_name is None:
196
259
  inferred = target_dir.name.removeprefix("plesty-").replace("-", "_")
197
260
  if not MODULE_NAME_PATTERN.match(inferred):
@@ -220,7 +283,7 @@ def init_command(
220
283
  "pypi_name": pypi_name,
221
284
  "project_type": project_type,
222
285
  "year": str(datetime.date.today().year),
223
- "plesty_lib_version": resolve_lib_version(),
286
+ "plesty_lib_specifier": lib_pin,
224
287
  "plesty_sdk_version": PLESTY_SDK_VERSION,
225
288
  }
226
289
  if author is not None:
@@ -9,6 +9,7 @@ import json
9
9
  from pathlib import Path
10
10
  from unittest.mock import patch
11
11
 
12
+ import click
12
13
  import pytest
13
14
  from click.testing import CliRunner
14
15
 
@@ -19,6 +20,7 @@ from plesty.sdk.commands.init import (
19
20
  PLESTY_SDK_VERSION,
20
21
  _STANDARD_CI_STAGES,
21
22
  generate_gitlab_ci,
23
+ lib_specifier,
22
24
  resolve_lib_version,
23
25
  )
24
26
 
@@ -590,3 +592,56 @@ class TestResolveLibVersion:
590
592
  def test_answer_without_releases_falls_back(self):
591
593
  """A body that is JSON but not the expected document is not a version list."""
592
594
  assert self._resolve({"info": {"version": "9.9.9"}}) == PLESTY_LIB_VERSION
595
+
596
+
597
+ class TestLibSpecifier:
598
+ """`--lib-version` decides the pin, and asking PyPI is then skipped."""
599
+
600
+ @pytest.fixture(autouse=True)
601
+ def no_network(self):
602
+ """Any lookup during these tests is a bug: a requested pin asks nobody."""
603
+ resolve_lib_version.cache_clear()
604
+ with patch(
605
+ "plesty.sdk.commands.init.urllib.request.urlopen",
606
+ side_effect=AssertionError("PyPI must not be consulted for a requested pin"),
607
+ ):
608
+ yield
609
+ resolve_lib_version.cache_clear()
610
+
611
+ @pytest.mark.parametrize(
612
+ ("requested", "expected"),
613
+ [
614
+ ("0.3.5.dev3", ">=0.3.5.dev3"),
615
+ (" 0.4.0 ", ">=0.4.0"),
616
+ ("==0.4.0", "==0.4.0"),
617
+ (">=0.3.5.dev3,<0.4", ">=0.3.5.dev3,<0.4"),
618
+ ("~=0.4.0", "~=0.4.0"),
619
+ ],
620
+ )
621
+ def test_requested_pin(self, requested, expected):
622
+ """A bare version becomes a lower bound; a specifier is taken as written."""
623
+ assert lib_specifier(requested) == expected
624
+
625
+ @pytest.mark.parametrize("requested", ["", " ", "not-a-version", ">=", "=>0.4.0"])
626
+ def test_unreadable_request_is_refused(self, requested):
627
+ """A pin nothing can resolve is named at the command line, not written out."""
628
+ with pytest.raises(click.ClickException):
629
+ lib_specifier(requested)
630
+
631
+ def test_scaffold_pins_what_was_asked_for(self, tmp_path):
632
+ """The requested pin is what lands in the generated pyproject.toml."""
633
+ result = _invoke_init(
634
+ tmp_path, "device", "--name", "my_sensor", "--lib-version", ">=0.3.5.dev3,<0.4"
635
+ )
636
+ assert result.exit_code == 0, result.output
637
+ assert '"plesty-lib>=0.3.5.dev3,<0.4"' in (tmp_path / "pyproject.toml").read_text()
638
+
639
+ def test_bad_pin_creates_nothing(self, tmp_path):
640
+ """A refused --lib-version leaves no half-scaffolded directory behind."""
641
+ target = tmp_path / "plesty-my-sensor"
642
+ result = _invoke_init(
643
+ target, "device", "--name", "my_sensor", "--lib-version", "not-a-version"
644
+ )
645
+ assert result.exit_code != 0
646
+ assert "not a valid version" in result.output
647
+ assert not target.exists()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes