strictcli 0.33.0__tar.gz → 0.34.0__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.
- {strictcli-0.33.0 → strictcli-0.34.0}/PKG-INFO +1 -1
- {strictcli-0.33.0 → strictcli-0.34.0}/pyproject.toml +1 -1
- {strictcli-0.33.0 → strictcli-0.34.0}/strictcli/__init__.py +24 -2
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_connection_env.py +36 -2
- {strictcli-0.33.0 → strictcli-0.34.0}/uv.lock +1 -1
- {strictcli-0.33.0 → strictcli-0.34.0}/.claude/settings.json +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/.github/workflows/ci.yml +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/.github/workflows/publish.yml +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/.gitignore +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/.rlsbl/bases/.github/workflows/ci.yml +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/.rlsbl/bases/.github/workflows/publish.yml +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/.rlsbl/bases/.gitignore +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/.rlsbl/bases/.rlsbl/lint/python.toml +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/.rlsbl/config.json +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/.rlsbl/lint/python.toml +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/.rlsbl/managed-files.json +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/.rlsbl/version +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/.strictcli/schema.json +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/CLAUDE.md +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/LICENSE +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/README.md +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/strictcli/py.typed +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/conftest.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_arg_default.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_arg_default_validation.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_at_prefix.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_auto_version.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_call.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_check_command.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_check_discovery.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_check_provider.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_check_public_api.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_check_runner.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_check_schema.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_check_types.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_choices.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_choices_none.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_command_help_suggestion.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_command_tags.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_compound_types.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_config.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_config_fields.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_config_file_path.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_config_set_bugs.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_context.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_coverage.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_deep_nesting.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_dependencies.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_deprecated.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_dump_schema.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_e2e.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_env.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_exit_codes.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_flag_sets.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_float_format.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_float_type.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_float_vectors.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_global_flag_conflict_position.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_global_flags.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_help.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_hermetic.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_infra_env.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_int_type.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_invoke.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_keyword_flags.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_mcp.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_mutex.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_nesting.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_parser.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_passthrough.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_provenance.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_provenance_phase2.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_registration.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_repeatable.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_reserved_global_flags.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_tagdsl.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_toml_loading.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_tool_export.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_typed_args.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_unique.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_utilities.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_validate.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_variadic.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/tests/test_visibility.py +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/todo/.defer/deferred.md +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/todo/.done/keyword-collision-in-flag-param-name.md +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/todo/.done/original-idea.md +0 -0
- {strictcli-0.33.0 → strictcli-0.34.0}/todo/.done/public-check-runner-api.md +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
__version__ = "0.
|
|
5
|
+
__version__ = "0.34.0"
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
8
|
"App", "Flag", "Arg", "FlagSet", "MutexGroup", "CoRequired", "Requires",
|
|
@@ -2651,10 +2651,27 @@ class ConnectionEnvReader(Protocol):
|
|
|
2651
2651
|
instead of connecting. The check command wraps the tool-supplied check
|
|
2652
2652
|
context in a value that satisfies this protocol, backed by the app's declared
|
|
2653
2653
|
connection envs and the invocation's hermetic state. Checks that need a
|
|
2654
|
-
connection URL call ``ctx.connection_env_value("DATABASE_URL")``.
|
|
2654
|
+
connection URL call ``ctx.connection_env_value("DATABASE_URL")``.
|
|
2655
|
+
|
|
2656
|
+
``is_hermetic()`` reports whether the invocation ran under --hermetic. It
|
|
2657
|
+
exists so a check can DISTINGUISH the two cases that
|
|
2658
|
+
``connection_env_value``'s ``present=False`` otherwise conflates:
|
|
2659
|
+
"--hermetic suppressed the connection env" vs "the env var is simply unset".
|
|
2660
|
+
A check that layers config fallbacks below the env must honor hermetic even
|
|
2661
|
+
when the env is unset -- otherwise it falls through to a config URL and
|
|
2662
|
+
connects, violating the hermetic guarantee::
|
|
2663
|
+
|
|
2664
|
+
dsn, present = ctx.connection_env_value("DATABASE_URL")
|
|
2665
|
+
if not present:
|
|
2666
|
+
if ctx.is_hermetic():
|
|
2667
|
+
return reporter.skipped("hermetic: connection suppressed")
|
|
2668
|
+
# env unset but not hermetic -- config fallback is allowed here
|
|
2669
|
+
"""
|
|
2655
2670
|
|
|
2656
2671
|
def connection_env_value(self, env_var: str) -> "tuple[str | None, bool]": ...
|
|
2657
2672
|
|
|
2673
|
+
def is_hermetic(self) -> bool: ...
|
|
2674
|
+
|
|
2658
2675
|
|
|
2659
2676
|
class _CheckContextWithConn:
|
|
2660
2677
|
"""Wraps a tool-supplied check context, delegating attribute access while
|
|
@@ -2679,6 +2696,11 @@ class _CheckContextWithConn:
|
|
|
2679
2696
|
return None, False
|
|
2680
2697
|
raise KeyError(f'"{env_var}" is not a declared connection env var')
|
|
2681
2698
|
|
|
2699
|
+
def is_hermetic(self) -> bool:
|
|
2700
|
+
"""Report whether the invocation ran under --hermetic. Mirrors the
|
|
2701
|
+
hermetic flag captured when the wrapper was built."""
|
|
2702
|
+
return self._hermetic
|
|
2703
|
+
|
|
2682
2704
|
|
|
2683
2705
|
@dataclass
|
|
2684
2706
|
class _CheckDef:
|
|
@@ -178,9 +178,15 @@ def _make_conn_check_app(tmp_path):
|
|
|
178
178
|
|
|
179
179
|
@app.error_check("db-reachable")
|
|
180
180
|
def db_reachable(ctx, reporter):
|
|
181
|
+
reporter.note(f"hermetic={ctx.is_hermetic()}")
|
|
181
182
|
dsn, present = ctx.connection_env_value("DATABASE_URL")
|
|
182
183
|
if not present:
|
|
183
|
-
|
|
184
|
+
# Distinguish hermetic suppression from a plainly-unset env: a
|
|
185
|
+
# consumer layering config fallbacks below the env must honor
|
|
186
|
+
# hermetic (skip) rather than fall through and connect.
|
|
187
|
+
if ctx.is_hermetic():
|
|
188
|
+
return reporter.skipped("DATABASE_URL suppressed by --hermetic")
|
|
189
|
+
return reporter.skipped("DATABASE_URL unset")
|
|
184
190
|
reporter.note(f"dsn={dsn}")
|
|
185
191
|
return reporter.passed("connection env visible")
|
|
186
192
|
|
|
@@ -194,14 +200,42 @@ def test_connection_env_check_side_access(tmp_path, monkeypatch):
|
|
|
194
200
|
r = app.test(["check", "--tag", "db", "--verbose"])
|
|
195
201
|
assert "dsn=postgres://check/db" in r.stdout
|
|
196
202
|
assert "PASS" in r.stdout
|
|
203
|
+
# A non-hermetic invocation reports is_hermetic()==False to the check.
|
|
204
|
+
assert "hermetic=False" in r.stdout
|
|
197
205
|
|
|
198
206
|
|
|
199
207
|
def test_connection_env_check_side_hermetic_skips(tmp_path, monkeypatch):
|
|
200
208
|
monkeypatch.setenv("DATABASE_URL", "postgres://check/db")
|
|
201
209
|
app = _make_conn_check_app(tmp_path)
|
|
202
|
-
r = app.test(["--hermetic", "check", "--tag", "db"])
|
|
210
|
+
r = app.test(["--hermetic", "check", "--tag", "db", "--verbose"])
|
|
203
211
|
assert "SKIP" in r.stdout
|
|
204
212
|
assert "dsn=" not in r.stdout
|
|
213
|
+
# The check SEES hermetic (is_hermetic()==True) and skips for that reason.
|
|
214
|
+
assert "hermetic=True" in r.stdout
|
|
215
|
+
assert "suppressed by --hermetic" in r.stdout
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def test_connection_env_check_side_hermetic_conflation(tmp_path, monkeypatch):
|
|
219
|
+
"""Documents the exact gap is_hermetic() closes: under --hermetic with the
|
|
220
|
+
env var UNSET, connection_env_value returns present=False (indistinguishable
|
|
221
|
+
from a plain unset), yet is_hermetic() returns True -- so a check layering
|
|
222
|
+
config fallbacks can honor hermetic instead of connecting via a config URL."""
|
|
223
|
+
monkeypatch.delenv("DATABASE_URL", raising=False) # env UNSET
|
|
224
|
+
app = _make_conn_check_app(tmp_path)
|
|
225
|
+
r = app.test(["--hermetic", "check", "--tag", "db", "--verbose"])
|
|
226
|
+
assert "hermetic=True" in r.stdout
|
|
227
|
+
assert "suppressed by --hermetic" in r.stdout
|
|
228
|
+
assert "DATABASE_URL unset" not in r.stdout
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def test_connection_env_check_side_unset_not_hermetic(tmp_path, monkeypatch):
|
|
232
|
+
"""Counterpart: env unset and NOT hermetic -> is_hermetic()==False, so a
|
|
233
|
+
consumer is free to consult a config fallback (here reported as plain unset)."""
|
|
234
|
+
monkeypatch.delenv("DATABASE_URL", raising=False) # env UNSET, no --hermetic
|
|
235
|
+
app = _make_conn_check_app(tmp_path)
|
|
236
|
+
r = app.test(["check", "--tag", "db", "--verbose"])
|
|
237
|
+
assert "hermetic=False" in r.stdout
|
|
238
|
+
assert "DATABASE_URL unset" in r.stdout
|
|
205
239
|
|
|
206
240
|
|
|
207
241
|
# --- Registration-time enforcement ---
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|