CodeSampleX

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

  1. auto_envvar_prefix generates nested uppercase snake_case envvars (PREFIX_GROUP_SUBCMD_OPTION) across all subcommands, silently ignoring flattened prefixes or kebab-case envvars
  2. default_map keys require CLI command names (kebab-case) for subcommand nesting hierarchy, but Python parameter identifiers (snake_case) for option keys
  3. default_map with snake_case command keys or kebab-case option keys is silently ignored and falls back to option defaults
  4. explicit envvar parameter overrides auto_envvar_prefix while retaining auto_envvar_prefix as a fallback when the explicit envvar is unset
  5. 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

Quellartefakt herunterladen (tar.gz)

Quelltext

NOTES.md
# 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.
csx.json
{"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"}
requirements.txt
click==8.4.2
test/contract.py
"""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.")

Ursprungs-Seeder

csx-seed