meta_package_manager.cli module

The mpm command-line interface: the group and its shared plumbing.

Defines the Click command group (global options, manager selection, the GlobalOptions state every subcommand reads) and the helpers several subcommand modules share: the inventory snapshot, the per-package action engine, the failure gates and the file-output guards.

The subcommands themselves live in one module per help section — meta_package_manager.cli_explore (the read-only queries), meta_package_manager.cli_maintenance (the state changers and diagnostics), meta_package_manager.cli_snapshots (manifest export and replay) and meta_package_manager.cli_sbom — imported at the bottom of this module so their @mpm.command registrations run. Each subcommand selects the managers from meta_package_manager.pool that implement the matching meta_package_manager.capabilities.Operations action, runs it across all of them, and renders the aggregated, multi-manager result.

meta_package_manager.cli.SECTIONS = (Section(Explore subcommands, is_sorted=False), Section(Maintenance subcommands, is_sorted=False), Section(Package snapshots subcommands, is_sorted=False), Section(SBOM subcommands, is_sorted=False))

Help-screen order of the subcommand sections, from the broadest to the narrowest.

Cloup otherwise ranks a section by the first subcommand registered into it, which here is whichever module the import block at the bottom of this file reaches first. That block is sorted by module name, so the order was an accident of the alphabet.

meta_package_manager.cli.UNMAINTAINED_MARKER = '(unmaintained)'

Marker closing the help of a selector whose manager is unmaintained.

Click writes (DEPRECATED), or (DEPRECATED: {reason}) when the flag carries one. Neither is what these selectors mean: the option is not being retired, the tool behind it was abandoned while mpm keeps driving it, which is why the manager index marks it ⚠️ rather than dropping it. One word carries that, and unlike Click’s it fits on the line it annotates.

The wording is uniform across the unmaintained managers rather than drawn from each unmaintained_message: those are markdown paragraphs with links, sized for the manager’s own page, where a help label has one line to spend.

Note

click-extra paints Click’s own spelling and no other (DEPRECATED_RE matches the literal word), so this marker renders unstyled where Click’s took the deprecated theme slot.

Todo

Declare the marker so it takes that slot back, once a click-extra release carries a deprecated field on HelpKeywords:

mpm.extra_keywords = HelpKeywords(…, deprecated={UNMAINTAINED_MARKER})

Naming it on a release without the field raises TypeError at import, so this waits on the floor rather than being guarded.

meta_package_manager.cli.UNMAINTAINED_REASON = 'upstream is unmaintained'

Why UNMAINTAINED_MARKER’s manager is flagged, for Click’s own notice.

Read only at runtime, appended to the DeprecationWarning: The option ‘volta’ is deprecated. line Click prints on stderr when someone actually selects one. The help screen never shows it: ManagerSelector keeps Click from labelling the option and writes UNMAINTAINED_MARKER instead.

meta_package_manager.cli.XKCD_MANAGER_ORDER = ('pip', 'brew', 'npm', 'dnf', 'apt', 'steamcmd')

Sequence of package managers as defined by XKCD #1654: Universal Install Script.

See the corresponding implementation rationale in issue #10.

class meta_package_manager.cli.GlobalOptions(all_managers, user_selection, user_drops, selected_managers, description, summary, network, progress, timeout)[source]

Bases: object

Global options and selection state every subcommand reads from ctx.obj.

Built once by the mpm group body, after the eager option callbacks have accumulated the manager selectors into the transient ctx.obj dict this instance replaces (see update_manager_selection()).

all_managers: bool

Include unsupported and unmaintained managers in the selection.

user_selection: list[str] | None

Managers explicitly selected by the user, in priority order, or None.

user_drops: set[str] | None

Managers explicitly excluded by the user, or None.

selected_managers: Callable[[...], Iterator[PackageManager]]

Resolve the target managers, applying selection and manager-level options.

description: bool

Show package description in results.

summary: bool

Print the end-of-run summary on stderr.

network: bool

Allow network calls during the run.

progress: bool

Whether progress indicators may render, after mpm’s output-mode gating.

The same value the group folds into each manager’s progress flag, exposed for the subcommands resolving their own managers instead of going through selected_managers (managers, whose only wait is the detection round the pool runs for it). The widget still auto-gates on an interactive stderr.

timeout: int | None

User-set maximum duration in seconds for each CLI call, or None.

meta_package_manager.cli.COOLDOWN_SUPPORTED_MANAGERS = ('flatpak', 'mas', 'npm', 'paru', 'pip', 'pipx', 'pnpm', 'uv', 'uvx', 'yay')

IDs of the managers that can enforce a release-age mpm --cooldown.

Derived from the pool so the --cooldown help text never drifts from the set of managers that actually enforce the window, whether through a native cooldown_env_var or through the per-package release_date probe: adding cooldown support to a manager surfaces it here automatically.

meta_package_manager.cli.RELEASE_INTRODUCING_OPERATIONS = frozenset({'install', 'upgrade'})

Operation names that bring new package versions onto the system.

The per-package cooldown hold of cooldown_hold_reason only applies to these: remove introduces nothing, and read-only queries are never blocked. restore rides the install operation name, so it is covered.

meta_package_manager.cli.guard_existing_output(ctx, output_path, *, overwrite)[source]

Block clobbering an existing output file unless overwrite is set.

Warns and exits with code 2 when output_path already exists and the user did not pass --overwrite/--force/--replace. No-op when the file is absent. Callers handle the stdout case separately.

Return type:

None

meta_package_manager.cli.update_manager_selection(ctx, param, value)[source]

Update global selection list of managers in the context.

Accumulate and merge all manager selectors to form the initial population enforced by the user.

Return type:

None

class meta_package_manager.cli.ManagerSelector(*args, unmaintained=False, **kwargs)[source]

Bases: Option

The --<id>/--no-<id> boolean flag pair of one manager.

Collects every occurrence instead of keeping the last one, as a plain boolean flag would: exclusion takes precedence over inclusion whatever the order the two halves are given in, so --no-brew --brew must still show update_manager_selection() its --no-brew half.

Note

A configuration file reaches the option as a lone value through default_map, which Click cannot cast against a parameter collecting several. Wrapping it here is what lets brew = false land as one occurrence.

Mark the flag pair of an unmaintained manager, in mpm’s own words.

deprecated is set after Click has built the option rather than passed into it: Click appends its (DEPRECATED: …) label to the help string from the constructor, and reads the attribute again only at parse time. Assigning it afterwards therefore keeps the stderr notice a user gets for actually selecting one, while leaving the help line to UNMAINTAINED_MARKER.

type_cast_value(ctx, value)[source]

Read a lone configuration value as a single occurrence of the flag.

Return type:

Any

meta_package_manager.cli.single_manager_selectors()[source]

Dynamically creates a dedicated flag selector pair for each manager.

meta_package_manager.cli.bar_plugin_path(ctx, param, value)[source]

Print the location of the SwiftBar/Xbar plugin.

Returns the normalized path of the standalone bar_plugin.py script that is distributed with this Python module. This is made available under the mpm --bar-plugin-path option.

Notice that the fully-qualified home directory get replaced by its shorthand (~) if applicable:

  • the full /home/user/.python/site-packages/mpm/bar_plugin.py path is simplified to ~/.python/site-packages/mpm/bar_plugin.py,

  • but /usr/bin/python3.10/mpm/bar_plugin.py is returned as-is.

meta_package_manager.cli.install_teardown_interrupt_guard(ctx)[source]

Make a Ctrl+C pressed during teardown exit at once, quietly.

install_interrupt_handler() covers the run itself: the first Ctrl+C terminates every live subprocess, then aborts. It cannot cover what follows, because its own handler is restored when the context closes: the interpreter is still joining any worker thread whose child is draining, and a Ctrl+C landing inside that join surfaces as Exception ignored on threading shutdown, a dozen lines of threading internals ending on a bare KeyboardInterrupt. It reads as a crash, where the user only asked twice for the run to stop.

So this guard is armed by a close callback rather than during the run, and is deliberately never restored: nothing follows it but the teardown it covers. The exit skips the join instead of unwinding through it, with the shell’s conventional 128 + SIGINT. Everything the run had to say is already flushed by then, the abort having printed it.

Registered before install_interrupt_handler(), so that its restore runs first and this one has the last word: a context unwinds its close callbacks last-in first-out.

Skipped off a terminal, where nobody is pressing Ctrl+C and a handler nothing restores would outlive the call: the test suite drives this CLI in-process, over and over, in a process whose own Ctrl+C belongs to pytest.

Return type:

None

class meta_package_manager.cli.SourcedParamsOption(param_decls=None, is_flag=True, expose_value=False, is_eager=True, help='Show all CLI parameters, their provenance, defaults and value, then exit.', **kwargs)[source]

Bases: ShowParamsOption

--params with the config_file column drawn without being asked for.

click-extra keeps that column opt-in, reachable through a --columns projection on the root command. mpm has no such option: its --columns are per-subcommand, each with its own vocabulary of package fields, and --params is a root option that prints and exits before any of them is parsed. The column would therefore be unreachable, which is why it is drawn by default here instead.

It earns the width: mpm layers a user-wide configuration under a project’s own (see group_params()), so Source saying CONFIGURATION leaves the reader asking which file, and this column is the answer.

Todo

Inert on click-extra 9.1 and below, which renders the table from the base class’s column set rather than the running option’s, so this override never reaches it. The one-line fix is proposed upstream; drop this paragraph once a release carrying it becomes the floor.

classmethod default_columns()[source]

Every non-opt-in column, plus config_file.

Return type:

tuple[ColumnSpec, ...]

meta_package_manager.cli.group_params()[source]

click-extra’s default parameters, with three of them swapped for ours.

--version is swapped by version_screen_params(), which this builds on. The other two are --config, which gains the cascade described below, and --params, which gains the column that makes the cascade readable.

Swapping instances in place keeps click-extra’s carefully ordered parameter sequence, and keeps the @group(config_schema=…, config_validators=…) arguments working: those are grafted onto whichever parameter is a ConfigOption, after the list is built.

Note

cascade=True layers every discovered configuration file, the most local one winning key by key. Without it exactly one file applies, so a machine-wide verbosity or manager exclusion was dropped in full the moment the working directory held a pyproject.toml carrying any [tool.mpm] key at all. An explicit --config never cascades: it pins one source, as it always did.

Return type:

list[Parameter]

meta_package_manager.cli.query_option(f)

--query filter of the inventory exporters (dump, sbom).

meta_package_manager.cli.query_exact_option(f)

--exact refinement of query_option.

meta_package_manager.cli.overwrite_option(f)

Opt-in clobbering of an existing output file (dump, sbom); see guard_existing_output().

meta_package_manager.cli.package_label(spec)[source]

Render a spec as package_id or package_id@version for trail output.

Return type:

str

meta_package_manager.cli.fail_unless_zero_exit(ctx, message)[source]

Print the durable critical: :message:` record, then exit `1 unless -0/--zero-exit opted out of the gate.

The shared failure gate of the action commands (exit_on_failures()) and doctor: the summary always prints, following the linter convention where findings gate automation, and -0 keeps the exit code at 0 with the printed summary staying the durable record. Usage and configuration errors are unaffected: they exit 2 regardless, as genuine execution failures.

Return type:

None

meta_package_manager.cli.exit_on_failures(ctx, verb, failures)[source]

Report the per-package failures collected this run and exit non-zero.

A no-op when failures is empty. Otherwise routes the deduplicated, sorted Could not {verb}: ... summary through fail_unless_zero_exit(). Shared by every action command (install, remove, upgrade <packages>, restore).

Return type:

None