strictcli 0.41.1__tar.gz → 0.41.2__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 (109) hide show
  1. {strictcli-0.41.1 → strictcli-0.41.2}/PKG-INFO +10 -5
  2. {strictcli-0.41.1 → strictcli-0.41.2}/README.md +1 -1
  3. {strictcli-0.41.1 → strictcli-0.41.2}/pyproject.toml +18 -4
  4. {strictcli-0.41.1 → strictcli-0.41.2}/strictcli/__init__.py +7 -2
  5. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_coverage.py +36 -0
  6. {strictcli-0.41.1 → strictcli-0.41.2}/uv.lock +1 -1
  7. {strictcli-0.41.1 → strictcli-0.41.2}/.claude/settings.json +0 -0
  8. {strictcli-0.41.1 → strictcli-0.41.2}/.github/workflows/ci.yml +0 -0
  9. {strictcli-0.41.1 → strictcli-0.41.2}/.github/workflows/publish.yml +0 -0
  10. {strictcli-0.41.1 → strictcli-0.41.2}/.gitignore +0 -0
  11. {strictcli-0.41.1 → strictcli-0.41.2}/.rlsbl/config.json +0 -0
  12. {strictcli-0.41.1 → strictcli-0.41.2}/.rlsbl/lint/python.toml +0 -0
  13. {strictcli-0.41.1 → strictcli-0.41.2}/.rlsbl/managed-files.json +0 -0
  14. {strictcli-0.41.1 → strictcli-0.41.2}/CLAUDE.md +0 -0
  15. {strictcli-0.41.1 → strictcli-0.41.2}/LICENSE +0 -0
  16. {strictcli-0.41.1 → strictcli-0.41.2}/scripts/add_effect_classification.py +0 -0
  17. {strictcli-0.41.1 → strictcli-0.41.2}/scripts/add_forwarding_declaration.py +0 -0
  18. {strictcli-0.41.1 → strictcli-0.41.2}/scripts/migrate_presence.py +0 -0
  19. {strictcli-0.41.1 → strictcli-0.41.2}/strictcli/py.typed +0 -0
  20. {strictcli-0.41.1 → strictcli-0.41.2}/tests/conftest.py +0 -0
  21. {strictcli-0.41.1 → strictcli-0.41.2}/tests/flagship_app.py +0 -0
  22. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_arg_decorator_order.py +0 -0
  23. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_arg_default.py +0 -0
  24. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_arg_default_validation.py +0 -0
  25. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_argv_value_order.py +0 -0
  26. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_at_prefix.py +0 -0
  27. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_auto_version.py +0 -0
  28. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_call.py +0 -0
  29. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_check_command.py +0 -0
  30. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_check_discovery.py +0 -0
  31. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_check_provider.py +0 -0
  32. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_check_public_api.py +0 -0
  33. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_check_runner.py +0 -0
  34. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_check_schema.py +0 -0
  35. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_check_types.py +0 -0
  36. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_choice_records.py +0 -0
  37. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_choices.py +0 -0
  38. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_choices_none.py +0 -0
  39. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_claimed_rendering.py +0 -0
  40. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_classification.py +0 -0
  41. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_command_help_suggestion.py +0 -0
  42. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_command_tags.py +0 -0
  43. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_compound_types.py +0 -0
  44. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_config.py +0 -0
  45. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_config_fields.py +0 -0
  46. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_config_file_path.py +0 -0
  47. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_config_set_bugs.py +0 -0
  48. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_confirm.py +0 -0
  49. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_connection_env.py +0 -0
  50. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_constraints.py +0 -0
  51. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_context.py +0 -0
  52. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_deep_nesting.py +0 -0
  53. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_deprecated.py +0 -0
  54. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_dry_run_unsupported.py +0 -0
  55. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_dump_schema.py +0 -0
  56. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_e2e.py +0 -0
  57. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_effects.py +0 -0
  58. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_effects_bypass_check.py +0 -0
  59. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_env.py +0 -0
  60. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_exit_codes.py +0 -0
  61. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_flag_sets.py +0 -0
  62. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_flagship_preview.py +0 -0
  63. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_flat_pre_typed.py +0 -0
  64. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_float_format.py +0 -0
  65. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_float_type.py +0 -0
  66. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_float_vectors.py +0 -0
  67. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_global_flag_conflict_position.py +0 -0
  68. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_global_flags.py +0 -0
  69. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_guard_v2.py +0 -0
  70. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_help.py +0 -0
  71. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_hermetic.py +0 -0
  72. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_infra_env.py +0 -0
  73. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_int_type.py +0 -0
  74. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_invoke.py +0 -0
  75. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_keyword_flags.py +0 -0
  76. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_machine_mode.py +0 -0
  77. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_mcp.py +0 -0
  78. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_member_spelling.py +0 -0
  79. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_nesting.py +0 -0
  80. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_owns_stdout.py +0 -0
  81. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_parser.py +0 -0
  82. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_passthrough.py +0 -0
  83. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_payload_schema.py +0 -0
  84. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_presence.py +0 -0
  85. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_provenance.py +0 -0
  86. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_provenance_phase2.py +0 -0
  87. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_record_pre_typed.py +0 -0
  88. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_registration.py +0 -0
  89. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_repeatable.py +0 -0
  90. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_reserved_global_flags.py +0 -0
  91. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_reserved_quartet.py +0 -0
  92. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_selectors.py +0 -0
  93. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_tagdsl.py +0 -0
  94. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_toml_loading.py +0 -0
  95. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_tool_export.py +0 -0
  96. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_trace_store.py +0 -0
  97. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_typed_args.py +0 -0
  98. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_ulid_vectors.py +0 -0
  99. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_unique.py +0 -0
  100. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_update.py +0 -0
  101. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_utilities.py +0 -0
  102. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_validate.py +0 -0
  103. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_value_sweep_order.py +0 -0
  104. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_variadic.py +0 -0
  105. {strictcli-0.41.1 → strictcli-0.41.2}/tests/test_visibility.py +0 -0
  106. {strictcli-0.41.1 → strictcli-0.41.2}/todo/.defer/deferred.md +0 -0
  107. {strictcli-0.41.1 → strictcli-0.41.2}/todo/.done/keyword-collision-in-flag-param-name.md +0 -0
  108. {strictcli-0.41.1 → strictcli-0.41.2}/todo/.done/original-idea.md +0 -0
  109. {strictcli-0.41.1 → strictcli-0.41.2}/todo/.done/public-check-runner-api.md +0 -0
@@ -1,16 +1,21 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: strictcli
3
- Version: 0.41.1
4
- Summary: A strict CLI framework for Python
5
- Project-URL: Homepage, https://github.com/smm-h/strictcli
3
+ Version: 0.41.2
4
+ Summary: A strict CLI framework for Python, Go and TypeScript that makes you declare every command, flag, argument and help string, and errors at registration time on anything left unstated (Python implementation)
5
+ Project-URL: Homepage, https://smmh.dev/strictcli/
6
+ Project-URL: Documentation, https://smmh.dev/strictcli/
6
7
  Project-URL: Repository, https://github.com/smm-h/strictcli
8
+ Project-URL: Issues, https://github.com/smm-h/strictcli/issues
9
+ Project-URL: Changelog, https://github.com/smm-h/strictcli/blob/main/CHANGELOG.md
7
10
  Author-email: "S. M. Hosseini" <m.hosseini@veliu.com>
8
11
  License-Expression: MIT
9
12
  License-File: LICENSE
10
- Keywords: argparse,cli,command-line,framework,rlsbl,strictcli
13
+ Keywords: argparse,argument-parser,cli,cli-framework,command-line,framework,rlsbl,strictcli
11
14
  Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Environment :: Console
12
16
  Classifier: Intended Audience :: Developers
13
17
  Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
14
19
  Classifier: Programming Language :: Python :: 3
15
20
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
21
  Requires-Python: >=3.11
@@ -19,7 +24,7 @@ Description-Content-Type: text/markdown
19
24
 
20
25
  # strictcli
21
26
 
22
- A strict CLI framework for Python.
27
+ A strict CLI framework for Python, Go and TypeScript that makes you declare every command, flag, argument and help string, and errors at registration time on anything left unstated (Python implementation).
23
28
 
24
29
  strictcli makes you declare everything -- every command, flag, argument, and environment variable must have help text or the framework errors at registration time. Four types only: `str`, `bool`, `int`, `float`. No magic type inference, no implicit defaults.
25
30
 
@@ -1,6 +1,6 @@
1
1
  # strictcli
2
2
 
3
- A strict CLI framework for Python.
3
+ A strict CLI framework for Python, Go and TypeScript that makes you declare every command, flag, argument and help string, and errors at registration time on anything left unstated (Python implementation).
4
4
 
5
5
  strictcli makes you declare everything -- every command, flag, argument, and environment variable must have help text or the framework errors at registration time. Four types only: `str`, `bool`, `int`, `float`. No magic type inference, no implicit defaults.
6
6
 
@@ -4,17 +4,28 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "strictcli"
7
- version = "0.41.1"
8
- description = "A strict CLI framework for Python"
7
+ version = "0.41.2"
8
+ description = "A strict CLI framework for Python, Go and TypeScript that makes you declare every command, flag, argument and help string, and errors at registration time on anything left unstated (Python implementation)"
9
9
  readme = "README.md"
10
10
  license = "MIT"
11
11
  requires-python = ">=3.11"
12
12
  authors = [{ name = "S. M. Hosseini", email = "m.hosseini@veliu.com" }]
13
- keywords = ["strictcli", "cli", "argparse", "command-line", "framework", "rlsbl"]
13
+ keywords = [
14
+ "strictcli",
15
+ "cli",
16
+ "cli-framework",
17
+ "command-line",
18
+ "argument-parser",
19
+ "argparse",
20
+ "framework",
21
+ "rlsbl",
22
+ ]
14
23
  classifiers = [
15
24
  "Development Status :: 3 - Alpha",
25
+ "Environment :: Console",
16
26
  "Intended Audience :: Developers",
17
27
  "License :: OSI Approved :: MIT License",
28
+ "Operating System :: OS Independent",
18
29
  "Programming Language :: Python :: 3",
19
30
  "Topic :: Software Development :: Libraries :: Python Modules",
20
31
  ]
@@ -29,5 +40,8 @@ dev = ["pytest", "allpairspy", "jsonschema"]
29
40
  testpaths = ["tests"]
30
41
 
31
42
  [project.urls]
32
- Homepage = "https://github.com/smm-h/strictcli"
43
+ Homepage = "https://smmh.dev/strictcli/"
44
+ Documentation = "https://smmh.dev/strictcli/"
33
45
  Repository = "https://github.com/smm-h/strictcli"
46
+ Issues = "https://github.com/smm-h/strictcli/issues"
47
+ Changelog = "https://github.com/smm-h/strictcli/blob/main/CHANGELOG.md"
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "0.41.1"
5
+ __version__ = "0.41.2"
6
6
 
7
7
  __all__ = [
8
8
  "App", "Flag", "Arg", "FlagSet",
@@ -8978,6 +8978,12 @@ class App:
8978
8978
  # recorder and the check provider use these absolute paths so that
8979
8979
  # tests which chdir still record into the repo, and a check evaluated
8980
8980
  # from a foreign cwd reads the app's own repo state.
8981
+ #
8982
+ # Only the PATHS are computed here. The directory itself is created
8983
+ # lazily by _record_coverage, immediately before the first shard
8984
+ # write: shards are written only on the test-harness paths (test()
8985
+ # and call()), so a plain CLI invocation must leave no .strictcli/
8986
+ # behind in whatever directory it was run from.
8981
8987
  self._coverage_dir = os.path.abspath(
8982
8988
  os.path.join(".strictcli", "coverage")
8983
8989
  )
@@ -8988,7 +8994,6 @@ class App:
8988
8994
  self._coverage_dir,
8989
8995
  f"{os.getpid()}.jsonl",
8990
8996
  )
8991
- os.makedirs(self._coverage_dir, exist_ok=True)
8992
8997
  self.register_check_provider(self._test_coverage_provider)
8993
8998
 
8994
8999
  def _validate_flag_infra_marker(self, f: Flag) -> None:
@@ -206,6 +206,7 @@ class TestCoverageCheck:
206
206
  when NEITHER a manifest NOR any shards exist."""
207
207
  app = _make_app(tmp_path)
208
208
  manifest_path = tmp_path / ".strictcli" / "test-coverage.json"
209
+ manifest_path.parent.mkdir(parents=True, exist_ok=True)
209
210
  manifest_path.write_text("[]\n")
210
211
 
211
212
  results, _, code = app.run_checks(
@@ -296,6 +297,7 @@ class TestManifestUnionVerdict:
296
297
  with no shard files (the machine never ran the suite)."""
297
298
  app = _make_app(tmp_path)
298
299
  manifest_path = tmp_path / ".strictcli" / "test-coverage.json"
300
+ manifest_path.parent.mkdir(parents=True, exist_ok=True)
299
301
  manifest_path.write_text(
300
302
  json.dumps(["build", "deploy", "status"], indent=2) + "\n"
301
303
  )
@@ -312,6 +314,7 @@ class TestManifestUnionVerdict:
312
314
  (anchored manifest), not the foreign directory."""
313
315
  app = _make_app(tmp_path)
314
316
  manifest_path = tmp_path / ".strictcli" / "test-coverage.json"
317
+ manifest_path.parent.mkdir(parents=True, exist_ok=True)
315
318
  manifest_path.write_text(
316
319
  json.dumps(["build", "deploy", "status"], indent=2) + "\n"
317
320
  )
@@ -330,6 +333,7 @@ class TestManifestUnionVerdict:
330
333
  """A run recording only a subset keeps prior commands covered (union)."""
331
334
  app = _make_app(tmp_path)
332
335
  manifest_path = tmp_path / ".strictcli" / "test-coverage.json"
336
+ manifest_path.parent.mkdir(parents=True, exist_ok=True)
333
337
  manifest_path.write_text(
334
338
  json.dumps(["build", "deploy", "status"], indent=2) + "\n"
335
339
  )
@@ -374,3 +378,35 @@ class TestCoverageDisabled:
374
378
 
375
379
  coverage_dir = tmp_path / ".strictcli" / "coverage"
376
380
  assert not coverage_dir.exists()
381
+
382
+
383
+ class TestCoverageDirectoryIsLazy:
384
+ def test_construction_leaves_no_directory(self, tmp_path):
385
+ """Constructing an app with test_coverage=True must not create
386
+ .strictcli/ -- a plain CLI invocation never records coverage, so it must
387
+ not plant an empty directory in whatever cwd it was run from."""
388
+ app = _make_app(tmp_path)
389
+ assert app is not None
390
+ assert not (tmp_path / ".strictcli").exists()
391
+
392
+ def test_recording_creates_the_directory(self, tmp_path):
393
+ """The recorder creates the coverage directory immediately before the
394
+ first shard write."""
395
+ app = _make_app(tmp_path)
396
+ app.test(["deploy"])
397
+
398
+ shard = tmp_path / ".strictcli" / "coverage" / f"{os.getpid()}.jsonl"
399
+ assert shard.is_file()
400
+
401
+ def test_check_skips_when_directory_absent(self, tmp_path):
402
+ """The provider reads a coverage root that does not exist without
403
+ raising -- it reports the subject-matter SKIP."""
404
+ app = _make_app(tmp_path)
405
+ assert not (tmp_path / ".strictcli").exists()
406
+
407
+ results, _, _code = app.run_checks(
408
+ SimpleCtx(project_root=tmp_path),
409
+ run_all=True,
410
+ )
411
+ cov_result = next(r for r in results if r.name == "cli-test-coverage")
412
+ assert cov_result.status == "skip"
@@ -232,7 +232,7 @@ wheels = [
232
232
 
233
233
  [[package]]
234
234
  name = "strictcli"
235
- version = "0.41.1"
235
+ version = "0.41.2"
236
236
  source = { editable = "." }
237
237
  dependencies = [
238
238
  { name = "tomlkit" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes