strictcli 0.32.0__tar.gz → 0.32.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.
Files changed (87) hide show
  1. {strictcli-0.32.0 → strictcli-0.32.1}/PKG-INFO +1 -1
  2. {strictcli-0.32.0 → strictcli-0.32.1}/pyproject.toml +1 -1
  3. {strictcli-0.32.0 → strictcli-0.32.1}/strictcli/__init__.py +22 -1
  4. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_coverage.py +26 -6
  5. {strictcli-0.32.0 → strictcli-0.32.1}/uv.lock +1 -1
  6. {strictcli-0.32.0 → strictcli-0.32.1}/.claude/settings.json +0 -0
  7. {strictcli-0.32.0 → strictcli-0.32.1}/.github/workflows/ci.yml +0 -0
  8. {strictcli-0.32.0 → strictcli-0.32.1}/.github/workflows/publish.yml +0 -0
  9. {strictcli-0.32.0 → strictcli-0.32.1}/.gitignore +0 -0
  10. {strictcli-0.32.0 → strictcli-0.32.1}/.rlsbl/bases/.github/workflows/ci.yml +0 -0
  11. {strictcli-0.32.0 → strictcli-0.32.1}/.rlsbl/bases/.github/workflows/publish.yml +0 -0
  12. {strictcli-0.32.0 → strictcli-0.32.1}/.rlsbl/bases/.gitignore +0 -0
  13. {strictcli-0.32.0 → strictcli-0.32.1}/.rlsbl/bases/.rlsbl/lint/python.toml +0 -0
  14. {strictcli-0.32.0 → strictcli-0.32.1}/.rlsbl/config.json +0 -0
  15. {strictcli-0.32.0 → strictcli-0.32.1}/.rlsbl/lint/python.toml +0 -0
  16. {strictcli-0.32.0 → strictcli-0.32.1}/.rlsbl/managed-files.json +0 -0
  17. {strictcli-0.32.0 → strictcli-0.32.1}/.rlsbl/version +0 -0
  18. {strictcli-0.32.0 → strictcli-0.32.1}/.strictcli/schema.json +0 -0
  19. {strictcli-0.32.0 → strictcli-0.32.1}/CLAUDE.md +0 -0
  20. {strictcli-0.32.0 → strictcli-0.32.1}/LICENSE +0 -0
  21. {strictcli-0.32.0 → strictcli-0.32.1}/README.md +0 -0
  22. {strictcli-0.32.0 → strictcli-0.32.1}/strictcli/py.typed +0 -0
  23. {strictcli-0.32.0 → strictcli-0.32.1}/tests/conftest.py +0 -0
  24. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_arg_default.py +0 -0
  25. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_arg_default_validation.py +0 -0
  26. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_at_prefix.py +0 -0
  27. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_auto_version.py +0 -0
  28. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_call.py +0 -0
  29. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_check_command.py +0 -0
  30. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_check_discovery.py +0 -0
  31. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_check_provider.py +0 -0
  32. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_check_public_api.py +0 -0
  33. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_check_runner.py +0 -0
  34. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_check_schema.py +0 -0
  35. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_check_types.py +0 -0
  36. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_choices.py +0 -0
  37. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_choices_none.py +0 -0
  38. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_command_help_suggestion.py +0 -0
  39. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_command_tags.py +0 -0
  40. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_compound_types.py +0 -0
  41. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_config.py +0 -0
  42. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_config_fields.py +0 -0
  43. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_config_file_path.py +0 -0
  44. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_config_set_bugs.py +0 -0
  45. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_context.py +0 -0
  46. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_deep_nesting.py +0 -0
  47. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_dependencies.py +0 -0
  48. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_deprecated.py +0 -0
  49. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_dump_schema.py +0 -0
  50. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_e2e.py +0 -0
  51. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_env.py +0 -0
  52. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_exit_codes.py +0 -0
  53. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_flag_sets.py +0 -0
  54. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_float_format.py +0 -0
  55. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_float_type.py +0 -0
  56. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_float_vectors.py +0 -0
  57. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_global_flag_conflict_position.py +0 -0
  58. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_global_flags.py +0 -0
  59. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_help.py +0 -0
  60. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_hermetic.py +0 -0
  61. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_infra_env.py +0 -0
  62. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_int_type.py +0 -0
  63. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_invoke.py +0 -0
  64. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_keyword_flags.py +0 -0
  65. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_mcp.py +0 -0
  66. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_mutex.py +0 -0
  67. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_nesting.py +0 -0
  68. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_parser.py +0 -0
  69. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_passthrough.py +0 -0
  70. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_provenance.py +0 -0
  71. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_provenance_phase2.py +0 -0
  72. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_registration.py +0 -0
  73. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_repeatable.py +0 -0
  74. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_reserved_global_flags.py +0 -0
  75. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_tagdsl.py +0 -0
  76. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_toml_loading.py +0 -0
  77. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_tool_export.py +0 -0
  78. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_typed_args.py +0 -0
  79. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_unique.py +0 -0
  80. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_utilities.py +0 -0
  81. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_validate.py +0 -0
  82. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_variadic.py +0 -0
  83. {strictcli-0.32.0 → strictcli-0.32.1}/tests/test_visibility.py +0 -0
  84. {strictcli-0.32.0 → strictcli-0.32.1}/todo/.defer/deferred.md +0 -0
  85. {strictcli-0.32.0 → strictcli-0.32.1}/todo/.done/keyword-collision-in-flag-param-name.md +0 -0
  86. {strictcli-0.32.0 → strictcli-0.32.1}/todo/.done/original-idea.md +0 -0
  87. {strictcli-0.32.0 → strictcli-0.32.1}/todo/.done/public-check-runner-api.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: strictcli
3
- Version: 0.32.0
3
+ Version: 0.32.1
4
4
  Summary: A strict CLI framework for Python
5
5
  Project-URL: Homepage, https://github.com/smm-h/strictcli
6
6
  Project-URL: Repository, https://github.com/smm-h/strictcli
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "strictcli"
7
- version = "0.32.0"
7
+ version = "0.32.1"
8
8
  description = "A strict CLI framework for Python"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "0.32.0"
5
+ __version__ = "0.32.1"
6
6
 
7
7
  __all__ = [
8
8
  "App", "Flag", "Arg", "FlagSet", "MutexGroup", "CoRequired", "Requires",
@@ -2905,6 +2905,27 @@ class App:
2905
2905
  coverage_dir = self._coverage_dir
2906
2906
  manifest_path = self._coverage_manifest_path
2907
2907
 
2908
+ # Subject-matter gating (the sanctioned skip class, mirroring
2909
+ # project-type gating): when the anchored coverage root holds NEITHER
2910
+ # a committed manifest NOR any shard files, this is not the app's own
2911
+ # development tree -- e.g. an installed app running its checks from a
2912
+ # foreign project's cwd, where the construction-anchored root points
2913
+ # at a directory with no coverage state. Report a visible SKIP instead
2914
+ # of failing with the app's entire command surface listed as
2915
+ # uncovered. When EITHER exists, behavior is unchanged: a partial
2916
+ # manifest still fails honestly, and an empty-manifest file present
2917
+ # still means "coverage configured but empty" = fail listing all.
2918
+ manifest_exists = bool(manifest_path) and os.path.isfile(manifest_path)
2919
+ shards_exist = bool(coverage_dir) and os.path.isdir(coverage_dir) and any(
2920
+ fname.endswith(".jsonl") for fname in os.listdir(coverage_dir)
2921
+ )
2922
+ if not manifest_exists and not shards_exist:
2923
+ anchor = os.path.dirname(manifest_path) if manifest_path else coverage_dir
2924
+ return reporter.skipped(
2925
+ f"no coverage state at {anchor} -- cli-test-coverage applies "
2926
+ "to the app's own development tree"
2927
+ )
2928
+
2908
2929
  covered: set[str] = set()
2909
2930
 
2910
2931
  # Seed from the committed manifest -- this is what makes the verdict
@@ -175,16 +175,36 @@ class TestCoverageCheck:
175
175
  cov_result = next(r for r in results if r.name == "cli-test-coverage")
176
176
  assert cov_result.status == "pass"
177
177
 
178
- def test_empty_coverage_fails_naming_all(self, tmp_path):
179
- """No shards and no manifest -> deterministic FAIL naming every command.
180
-
181
- The verdict is derived from committed state (manifest union shards), so an
182
- empty everything state fails by listing every uncovered command rather than
183
- the old machine-dependent "no coverage data" error.
178
+ def test_zero_coverage_state_skips(self, tmp_path):
179
+ """No shards and no manifest -> SKIP with a subject-matter reason.
180
+
181
+ Subject-matter gating: when the anchored coverage root holds NEITHER a
182
+ committed manifest NOR any shard files, this is not the app's own
183
+ development tree (e.g. an installed app running its checks from a foreign
184
+ project's cwd). The check reports a visible SKIP naming the anchored path
185
+ rather than failing with the whole command surface listed as uncovered.
184
186
  """
185
187
  app = _make_app(tmp_path)
186
188
  # Don't run any test() or call() -- no shards, no manifest
187
189
 
190
+ results, _, code = app.run_checks(
191
+ SimpleCtx(project_root=tmp_path),
192
+ run_all=True,
193
+ )
194
+ cov_result = next(r for r in results if r.name == "cli-test-coverage")
195
+ assert cov_result.status == "skip"
196
+ assert "development tree" in cov_result.message
197
+ # Reason names the anchored .strictcli path, not the foreign cwd.
198
+ assert str(tmp_path / ".strictcli") in cov_result.message
199
+
200
+ def test_empty_manifest_file_present_fails_listing_all(self, tmp_path):
201
+ """An empty-manifest file present means "coverage configured but empty"
202
+ -> FAIL listing every command, NOT a skip. The skip class only triggers
203
+ when NEITHER a manifest NOR any shards exist."""
204
+ app = _make_app(tmp_path)
205
+ manifest_path = tmp_path / ".strictcli" / "test-coverage.json"
206
+ manifest_path.write_text("[]\n")
207
+
188
208
  results, _, code = app.run_checks(
189
209
  SimpleCtx(project_root=tmp_path),
190
210
  run_all=True,
@@ -232,7 +232,7 @@ wheels = [
232
232
 
233
233
  [[package]]
234
234
  name = "strictcli"
235
- version = "0.32.0"
235
+ version = "0.32.1"
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
File without changes
File without changes