From 2245a034e3f94b907c700ac10b9f0444e242ff2e Mon Sep 17 00:00:00 2001 From: Jitka Halova Date: Tue, 19 May 2026 17:30:14 +0200 Subject: [PATCH] Fix blocklist lookup for name-only entries follow-up to #1391 Assisted By: Claude Opus 4.6 --- CHANGES/pulp-glue/1391.feature | 2 +- pulp-glue/src/pulp_glue/python/context.py | 2 +- src/pulpcore/cli/python/repository.py | 111 +++++++++----------- tests/scripts/pulp_python/test_blocklist.sh | 35 +++--- 4 files changed, 68 insertions(+), 82 deletions(-) diff --git a/CHANGES/pulp-glue/1391.feature b/CHANGES/pulp-glue/1391.feature index fcdcbf20a..10c0fe394 100644 --- a/CHANGES/pulp-glue/1391.feature +++ b/CHANGES/pulp-glue/1391.feature @@ -1 +1 @@ -Added `PulpPythonBlocklistEntryContext` for `pulp_python>=3.30.0`. +Added `PulpPythonBlocklistEntryContext` for `pulp_python>=3.30.2`. diff --git a/pulp-glue/src/pulp_glue/python/context.py b/pulp-glue/src/pulp_glue/python/context.py index 8a708a5d3..5b309d368 100644 --- a/pulp-glue/src/pulp_glue/python/context.py +++ b/pulp-glue/src/pulp_glue/python/context.py @@ -174,7 +174,7 @@ class PulpPythonBlocklistEntryContext(PulpEntityContext): ENTITIES = _("blocklist entries") HREF = "python_python_python_blocklist_entry_href" ID_PREFIX = "repositories_python_python_blocklist_entries" - NEEDS_PLUGINS = [PluginRequirement("python", specifier=">=3.30.0")] + NEEDS_PLUGINS = [PluginRequirement("python", specifier=">=3.30.2")] repository_ctx: PulpPythonRepositoryContext def __init__( diff --git a/src/pulpcore/cli/python/repository.py b/src/pulpcore/cli/python/repository.py index e457abf15..ce9a6d9e6 100644 --- a/src/pulpcore/cli/python/repository.py +++ b/src/pulpcore/cli/python/repository.py @@ -30,7 +30,7 @@ list_command, lookup_callback, name_option, - pass_entity_context, + option_group, pass_pulp_context, pass_repository_context, pulp_group, @@ -155,7 +155,7 @@ def repository() -> None: ) -@repository.group(needs_plugins=[PluginRequirement("python", specifier=">=3.30.0")]) +@repository.group(needs_plugins=[PluginRequirement("python", specifier=">=3.30.2")]) @pass_repository_context @pass_pulp_context @click.pass_context @@ -172,33 +172,54 @@ def blocklist( ctx.obj = PulpPythonBlocklistEntryContext(pulp_ctx, repository_ctx) -_HELP_BLOCKLIST_NAME = _("Package name to block. Required when 'filename' is not provided.") -_HELP_BLOCKLIST_VERSION = _("Exact version to block. Only used when 'name' is set.") -_HELP_BLOCKLIST_FILENAME = _("Exact filename to block. Required when 'name' is not provided.") +def _blocklist_callback(ctx: click.Context, value: dict[str, t.Any]) -> t.Any: + name = value.get("name") + version = value.get("version") + filename = value.get("filename") + + if version and filename: + raise click.ClickException(_("'version' cannot be used with 'filename'.")) + if version and not name: + raise click.ClickException(_("'version' requires 'name' to be provided.")) + if name and filename: + raise click.ClickException(_("Exactly one of 'name' or 'filename' must be provided.")) + + lookup: dict[str, t.Any] = {} + if name and version: + lookup = {"name": name, "version": version} + elif name: + lookup = {"name": name, "version__isnull": True} + elif filename: + lookup = {"filename": filename} + + if lookup: + entity_ctx = ctx.find_object(PulpEntityContext) + assert entity_ctx is not None + entity_ctx.entity = lookup + blocklist_options = [ - click.option("--name", help=_HELP_BLOCKLIST_NAME), - click.option("--version", help=_HELP_BLOCKLIST_VERSION), - click.option("--filename", help=_HELP_BLOCKLIST_FILENAME), -] -blocklist_lookup_options = [ - pulp_option( + click.option( "--name", - help=_HELP_BLOCKLIST_NAME, - callback=lookup_callback("name"), - expose_value=False, + help=_("Package name to block (all versions). Required when 'filename' is not provided."), ), - pulp_option( - "--version", - help=_HELP_BLOCKLIST_VERSION, - callback=lookup_callback("version"), - expose_value=False, + click.option("--version", help=_("Package version to block. Only used when 'name' is set.")), + click.option( + "--filename", help=_("Package filename to block. Required when 'name' is not provided.") ), - pulp_option( - "--filename", - help=_HELP_BLOCKLIST_FILENAME, - callback=lookup_callback("filename"), +] +blocklist_list_options = [ + click.option("--name", help="Package name to block."), + click.option("--version", help="Package version to block."), + click.option("--filename", help="Package filename to block."), +] +blocklist_lookup_options = blocklist_options + [ + option_group( + "blocklist_lookup", + ["name", "version", "filename"], + require_all=False, expose_value=False, + callback=_blocklist_callback, ), href_option, ] @@ -206,43 +227,15 @@ def blocklist( blocklist.add_command( create_command(name="add", decorators=nested_lookup_options + blocklist_options) ) -blocklist.add_command(list_command(decorators=nested_lookup_options + blocklist_options)) +blocklist.add_command(list_command(decorators=nested_lookup_options + blocklist_list_options)) blocklist.add_command(show_command(decorators=nested_lookup_options + blocklist_lookup_options)) - - -@blocklist.command(name="remove") -@repository_href_option -@repository_lookup_option -@click.option("--name", help=_HELP_BLOCKLIST_NAME) -@click.option("--version", help=_HELP_BLOCKLIST_VERSION) -@click.option("--filename", help=_HELP_BLOCKLIST_FILENAME) -@href_option -@pass_entity_context -def blocklist_remove( - entity_ctx: PulpEntityContext, - /, - name: str | None, - version: str | None, - filename: str | None, -) -> None: - """ - Remove a blocklist entry. - """ - assert isinstance(entity_ctx, PulpPythonBlocklistEntryContext) - if version and filename: - raise click.ClickException(_("'version' cannot be used with 'filename'.")) - if version and not name: - raise click.ClickException(_("'version' requires 'name' to be provided.")) - if name and filename: - raise click.ClickException(_("Exactly one of 'name' or 'filename' must be provided.")) - - if name: - entity_ctx.entity = {"name": name} - if version: - entity_ctx.entity = {"version": version} - if filename: - entity_ctx.entity = {"filename": filename} - entity_ctx.delete() +blocklist.add_command( + destroy_command( + name="remove", + help=_("Remove a {entity}."), + decorators=nested_lookup_options + blocklist_lookup_options, + ) +) @repository.command() diff --git a/tests/scripts/pulp_python/test_blocklist.sh b/tests/scripts/pulp_python/test_blocklist.sh index 171d04c0e..72708ee92 100755 --- a/tests/scripts/pulp_python/test_blocklist.sh +++ b/tests/scripts/pulp_python/test_blocklist.sh @@ -4,7 +4,7 @@ set -eu # shellcheck source=tests/scripts/config.source . "$(dirname "$(dirname "$(realpath "$0")")")"/config.source -pulp debug has-plugin --name "python" --specifier ">=3.30.0" || exit 23 +pulp debug has-plugin --name "python" --specifier ">=3.30.2" || exit 23 cleanup() { pulp python repository destroy --name "cli_test_python_blocklist" || true @@ -13,45 +13,38 @@ trap cleanup EXIT expect_succ pulp python repository create --name "cli_test_python_blocklist" -# Test adding a blocklist entry by package name +# Test adding blocklist entries expect_succ pulp python repository blocklist add --repository "cli_test_python_blocklist" --name "pkg" test "$(echo "$OUTPUT" | jq -r '.name')" = "pkg" test "$(echo "$OUTPUT" | jq -r '.version')" = "null" test "$(echo "$OUTPUT" | jq -r '.filename')" = "null" ENTRY_HREF="$(echo "$OUTPUT" | jq -r '.pulp_href')" +expect_succ pulp python repository blocklist add --repository "cli_test_python_blocklist" --name "pkg" --version "2.0" +expect_succ pulp python repository blocklist add --repository "cli_test_python_blocklist" --filename "pkg-3.0.tar.gz" # Test listing blocklist entries expect_succ pulp python repository blocklist list --repository "cli_test_python_blocklist" -expect_succ test "$(echo "$OUTPUT" | jq -r '.|length')" = "1" +expect_succ test "$(echo "$OUTPUT" | jq -r '.|length')" = "3" expect_succ pulp python repository blocklist list --repository "cli_test_python_blocklist" --name "pkg" -expect_succ test "$(echo "$OUTPUT" | jq -r '.|length')" = "1" +expect_succ test "$(echo "$OUTPUT" | jq -r '.|length')" = "2" expect_succ pulp python repository blocklist list --repository "cli_test_python_blocklist" --name "nonexistent" expect_succ test "$(echo "$OUTPUT" | jq -r '.|length')" = "0" # Test showing a specific blocklist entry expect_succ pulp python repository blocklist show --repository "cli_test_python_blocklist" --href "$ENTRY_HREF" -expect_succ test "$(echo "$OUTPUT" | jq -r '.name')" = "pkg" +test "$(echo "$OUTPUT" | jq -r '.name')" = "pkg" +test "$(echo "$OUTPUT" | jq -r '.version')" = "null" expect_succ pulp python repository blocklist show --repository "cli_test_python_blocklist" --name "pkg" -expect_succ test "$(echo "$OUTPUT" | jq -r '.name')" = "pkg" +test "$(echo "$OUTPUT" | jq -r '.pulp_href')" = "$ENTRY_HREF" -# Test remove validation -expect_fail pulp python repository blocklist remove --repository "cli_test_python_blocklist" --version "1.0" --filename "pkg-1.0.tar.gz" -expect_fail pulp python repository blocklist remove --repository "cli_test_python_blocklist" --version "1.0" -expect_fail pulp python repository blocklist remove --repository "cli_test_python_blocklist" --name "pkg" --filename "pkg-1.0.tar.gz" +# Test input validation +expect_fail pulp python repository blocklist show --repository "cli_test_python_blocklist" --version "1.0" --filename "pkg-1.0.tar.gz" +expect_fail pulp python repository blocklist show --repository "cli_test_python_blocklist" --version "1.0" +expect_fail pulp python repository blocklist show --repository "cli_test_python_blocklist" --name "pkg" --filename "pkg-1.0.tar.gz" -# Test removing a blocklist entry by href +# Test removing blocklist entries expect_succ pulp python repository blocklist remove --repository "cli_test_python_blocklist" --href "$ENTRY_HREF" -expect_succ pulp python repository blocklist list --repository "cli_test_python_blocklist" -expect_succ test "$(echo "$OUTPUT" | jq -r '.|length')" = "0" - -# Test removing a blocklist entry by package name + version -expect_succ pulp python repository blocklist add --repository "cli_test_python_blocklist" --name "pkg" --version "2.0" expect_succ pulp python repository blocklist remove --repository "cli_test_python_blocklist" --name "pkg" --version "2.0" -expect_succ pulp python repository blocklist list --repository "cli_test_python_blocklist" -expect_succ test "$(echo "$OUTPUT" | jq -r '.|length')" = "0" - -# Test removing a blocklist entry by package filename -expect_succ pulp python repository blocklist add --repository "cli_test_python_blocklist" --filename "pkg-3.0.tar.gz" expect_succ pulp python repository blocklist remove --repository "cli_test_python_blocklist" --filename "pkg-3.0.tar.gz" expect_succ pulp python repository blocklist list --repository "cli_test_python_blocklist" expect_succ test "$(echo "$OUTPUT" | jq -r '.|length')" = "0"