Beispiel
click 8.4.2: Configure Click command hierarchies via context_settings default_map and auto_envvar_prefix without silent fallback failures
Verifiziertes Beispiel für pypi click 8.4.2: Configure Click command hierarchies via context_settings default_map and auto_envvar_prefix without silent…
sha256:098e69316075a12c6969f7a4a0129fa84a756444f787606cbf7be1421fce8d99
Dieses Netzwerk bietet eine Sache: ein Sample, das baut. Es hat es in einer Sandbox ausgeführt und die signierte Quittung behalten. Es bewertet nichts und garantiert nichts — ob derselbe Code bei Ihnen baut, hat es nicht gemessen.
Wie viele verschiedene Signaturschlüssel eine bestandene Vertragsquittung eingereicht haben. Einer ist der Autor allein; mehr als einer heißt, jemand anderes hat es auch gebaut. Ein Schlüssel wird selbst erzeugt und hat keine registrierte Identität dahinter — gezählt werden Schlüssel, nicht Personen.
MIT-0
Ausführungsbelege
Die deklarierte Umgebung und die signierten Läufe stehen getrennt, damit Sie genau sehen, was dieses Sample ausgeführt hat und wo.
- Beleggrundlage
- Signierter Vertrag bestanden
- Verifizierungsbelege
- 2
- Signaturschlüssel, die es gebaut haben
- 2
Deklarierte Umgebung
python linux x64 python python pip
Umgebungen der Verifizierungsläufe
| Umgebung | Contract | Stufen | Lauf |
|---|---|---|---|
| python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1 |
2026-08-17 |
| python 3.12 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1 |
2026-08-18 |
Fall
HOW- Ziel
- Configure Click command hierarchies via context_settings default_map and auto_envvar_prefix without silent fallback failures
- Pakete
- Symbole
-
- click.Context
- click.Option
- click.Group
- Umgebung
- python
- Erstellt
- 2026-08-17T03:18:53Z
Contract
- auto_envvar_prefix generates nested uppercase snake_case envvars (PREFIX_GROUP_SUBCMD_OPTION) across all subcommands, silently ignoring flattened prefixes or kebab-case envvars
- default_map keys require CLI command names (kebab-case) for subcommand nesting hierarchy, but Python parameter identifiers (snake_case) for option keys
- default_map with snake_case command keys or kebab-case option keys is silently ignored and falls back to option defaults
- explicit envvar parameter overrides auto_envvar_prefix while retaining auto_envvar_prefix as a fallback when the explicit envvar is unset
- configuration resolution follows strict precedence: CLI argument > explicit envvar > auto_envvar_prefix > default_map > option default
Dateien
- NOTES.md
- csx.json
- requirements.txt
- test/contract.py
Quelltext
# Click Configuration, `default_map` Key Asymmetry, and `auto_envvar_prefix` Resolution
## `search_known_solution` Result
`search_known_solution` returned `NO_SAFE_MATCH` for `pkg:pypi/click@8.4.2` with query regarding context settings / auto_envvar_prefix traps.
## What a Naive Model Would Write
A model would use Python identifier names (`snake_case`) uniformly for both subcommands and options in `default_map` (e.g., `{'db_admin': {'migrate_up': {'batch_size': 100}}}`), or use top-level/kebab-case environment variables like `APP_BATCH_SIZE` or `APP_DB-ADMIN_MIGRATE-UP_BATCH-SIZE` expecting `auto_envvar_prefix` to discover them.
## How the Wrong Version Fails
Silently with a green build: Click does not raise any KeyError or schema validation error when encountering unrecognized keys in `default_map` or unmatched environment variables, silently ignoring them and falling back to default parameter values.
{"case":{"caseId":"case:sha256:8cf02d42c29c88755a343c0bd70b6f3d9b4441ee4d7ce6bb47d6c155315d2abd","contract":["auto_envvar_prefix generates nested uppercase snake_case envvars (PREFIX_GROUP_SUBCMD_OPTION) across all subcommands, silently ignoring flattened prefixes or kebab-case envvars","default_map keys require CLI command names (kebab-case) for subcommand nesting hierarchy, but Python parameter identifiers (snake_case) for option keys","default_map with snake_case command keys or kebab-case option keys is silently ignored and falls back to option defaults","explicit envvar parameter overrides auto_envvar_prefix while retaining auto_envvar_prefix as a fallback when the explicit envvar is unset","configuration resolution follows strict precedence: CLI argument \u003e explicit envvar \u003e auto_envvar_prefix \u003e default_map \u003e option default"],"goal":"Configure Click command hierarchies via context_settings default_map and auto_envvar_prefix without silent fallback failures","kind":"HOW","packages":["pkg:pypi/click@8.4.2"],"schemaVersion":1,"symbols":["click.Context","click.Option","click.Group"]},"contractCommand":["python","test/contract.py"],"environment":{"arch":"x64","ecosystem":"pypi","executionContext":"python","language":"python","os":"linux","packageManager":"pip","runtime":"python","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/click@8.4.2"],"schemaVersion":1,"symbols":["click.Context","click.Option","click.Group"],"verifierAdapter":"python@1"}
click==8.4.2
"""Contract testing Click configuration, default_map key asymmetry, auto_envvar_prefix hierarchy, and precedence."""
import click
from click.testing import CliRunner
def build_app():
@click.group(
context_settings=dict(
auto_envvar_prefix="APP",
default_map={
"db-admin": {
"migrate-up": {
"batch_size": 100,
"target_env": "staging",
}
}
},
)
)
def cli():
pass
@cli.group("db-admin")
def db_admin():
pass
@db_admin.command("migrate-up")
@click.option("--batch-size", type=int, default=10)
@click.option("--target-env", envvar="DEPLOY_TARGET", default="development")
def migrate_up(batch_size, target_env):
click.echo(f"batch={batch_size} env={target_env}")
return cli
def test_auto_envvar_prefix_hierarchy():
cli = build_app()
runner = CliRunner()
# Naive guess 1: Top-level flattened prefix (APP_BATCH_SIZE) -> Click ignores it, falls back to default_map (100)
res_flat = runner.invoke(cli, ["db-admin", "migrate-up"], env={"APP_BATCH_SIZE": "500"})
assert res_flat.exit_code == 0
assert "batch=100" in res_flat.output, f"Expected default_map fallback (100), got {res_flat.output}"
# Naive guess 2: Skipping intermediate group name (APP_MIGRATE_UP_BATCH_SIZE) -> Click ignores it
res_partial = runner.invoke(cli, ["db-admin", "migrate-up"], env={"APP_MIGRATE_UP_BATCH_SIZE": "500"})
assert res_partial.exit_code == 0
assert "batch=100" in res_partial.output, f"Expected default_map fallback (100), got {res_partial.output}"
# Naive guess 3: Kebab-case envvar matching CLI flag name (APP_DB-ADMIN_MIGRATE-UP_BATCH-SIZE) -> Click ignores it
res_kebab = runner.invoke(
cli, ["db-admin", "migrate-up"], env={"APP_DB-ADMIN_MIGRATE-UP_BATCH-SIZE": "500"}
)
assert res_kebab.exit_code == 0
assert "batch=100" in res_kebab.output, f"Expected default_map fallback (100), got {res_kebab.output}"
# Correct: Full hierarchy converted to uppercase snake_case
res_correct = runner.invoke(
cli, ["db-admin", "migrate-up"], env={"APP_DB_ADMIN_MIGRATE_UP_BATCH_SIZE": "500"}
)
assert res_correct.exit_code == 0
assert "batch=500" in res_correct.output, f"Expected batch=500, got {res_correct.output}"
def test_default_map_key_asymmetry():
runner = CliRunner()
# Trap 1: Using Python identifier snake_case for command names ('db_admin' -> 'migrate_up')
@click.group(
context_settings=dict(
default_map={
"db_admin": {
"migrate_up": {
"batch_size": 200,
}
}
}
)
)
def cli_snake_cmd():
pass
@cli_snake_cmd.group("db-admin")
def g1():
pass
@g1.command("migrate-up")
@click.option("--batch-size", type=int, default=10)
def cmd1(batch_size):
click.echo(f"batch={batch_size}")
res_snake = runner.invoke(cli_snake_cmd, ["db-admin", "migrate-up"])
assert res_snake.exit_code == 0
assert "batch=10" in res_snake.output, (
"default_map with snake_case command keys must be ignored; expected default=10, "
f"got {res_snake.output}"
)
# Trap 2: Using CLI flag name / kebab-case for option keys ('batch-size' or '--batch-size')
@click.group(
context_settings=dict(
default_map={
"db-admin": {
"migrate-up": {
"batch-size": 300,
"--batch-size": 400,
}
}
}
)
)
def cli_kebab_opt():
pass
@cli_kebab_opt.group("db-admin")
def g2():
pass
@g2.command("migrate-up")
@click.option("--batch-size", type=int, default=10)
def cmd2(batch_size):
click.echo(f"batch={batch_size}")
res_kebab = runner.invoke(cli_kebab_opt, ["db-admin", "migrate-up"])
assert res_kebab.exit_code == 0
assert "batch=10" in res_kebab.output, (
"default_map with kebab-case option keys must be ignored; expected default=10, "
f"got {res_kebab.output}"
)
def test_explicit_envvar_vs_auto_prefix():
cli = build_app()
runner = CliRunner()
# When both are set: explicit envvar (DEPLOY_TARGET) overrides auto prefix (APP_DB_ADMIN_MIGRATE_UP_TARGET_ENV)
res_both = runner.invoke(
cli,
["db-admin", "migrate-up"],
env={
"DEPLOY_TARGET": "explicit_production",
"APP_DB_ADMIN_MIGRATE_UP_TARGET_ENV": "auto_staging",
},
)
assert res_both.exit_code == 0
assert "env=explicit_production" in res_both.output
# When explicit is unset: auto prefix acts as fallback
res_auto_only = runner.invoke(
cli,
["db-admin", "migrate-up"],
env={
"APP_DB_ADMIN_MIGRATE_UP_TARGET_ENV": "auto_staging",
},
)
assert res_auto_only.exit_code == 0
assert "env=auto_staging" in res_auto_only.output
def test_precedence_hierarchy():
cli = build_app()
runner = CliRunner()
# 1. No CLI args, no envvars -> default_map overrides option default (100 vs 10, staging vs development)
res_defaults = runner.invoke(cli, ["db-admin", "migrate-up"])
assert res_defaults.exit_code == 0
assert "batch=100 env=staging" in res_defaults.output
# 2. auto_envvar overrides default_map
res_auto = runner.invoke(
cli,
["db-admin", "migrate-up"],
env={"APP_DB_ADMIN_MIGRATE_UP_BATCH_SIZE": "250"},
)
assert res_auto.exit_code == 0
assert "batch=250 env=staging" in res_auto.output
# 3. Explicit envvar overrides auto_envvar and default_map
res_env = runner.invoke(
cli,
["db-admin", "migrate-up"],
env={
"APP_DB_ADMIN_MIGRATE_UP_TARGET_ENV": "auto_val",
"DEPLOY_TARGET": "explicit_val",
},
)
assert res_env.exit_code == 0
assert "env=explicit_val" in res_env.output
# 4. CLI argument overrides all environment variables and defaults
res_cli = runner.invoke(
cli,
["db-admin", "migrate-up", "--batch-size", "999", "--target-env", "cli_val"],
env={
"APP_DB_ADMIN_MIGRATE_UP_BATCH_SIZE": "250",
"DEPLOY_TARGET": "explicit_val",
},
)
assert res_cli.exit_code == 0
assert "batch=999 env=cli_val" in res_cli.output
if __name__ == "__main__":
test_auto_envvar_prefix_hierarchy()
test_default_map_key_asymmetry()
test_explicit_envvar_vs_auto_prefix()
test_precedence_hierarchy()
print("Click configuration and precedence contract satisfied.")