meta_package_manager packageΒΆ
Meta Package Manager: a unified CLI wrapping many package managers.
Root package. Holds the canonical __version__; the mpm entry
point lives in meta_package_manager.cli.
SubpackagesΒΆ
- meta_package_manager.managers package
- Submodules
- meta_package_manager.managers.apk module
- meta_package_manager.managers.apm module
- meta_package_manager.managers.apt module
APTAPT.nameAPT.homepage_urlAPT.platformsAPT.default_sudoAPT.requirementAPT.pre_argsAPT.version_regexesAPT.installedAPT.outdatedAPT.orphansAPT.search()APT.install()APT.upgrade_all_cli()APT.upgrade_one_cli()APT.remove()APT.remove_orphan()APT.sync()APT.cleanup_orphan()APT.cleanup_cache()APT.doctor_cli()APT.cli_namesAPT.idAPT.virtual
APT_Mint
- meta_package_manager.managers.asdf module
- meta_package_manager.managers.composer module
ComposerComposer.nameComposer.homepage_urlComposer.platformsComposer.requirementComposer.pre_argsComposer.version_regexesComposer.installedComposer.outdatedComposer.search()Composer.install()Composer.upgrade_all_cli()Composer.upgrade_one_cli()Composer.remove()Composer.cleanup_cache()Composer.cli_namesComposer.doctor_cli()Composer.idComposer.virtual
- meta_package_manager.managers.conda module
- meta_package_manager.managers.deb_get module
Deb_GetDeb_Get.nameDeb_Get.homepage_urlDeb_Get.platformsDeb_Get.default_sudoDeb_Get.version_cli_optionsDeb_Get.version_regexesDeb_Get.installedDeb_Get.outdatedDeb_Get.search()Deb_Get.install()Deb_Get.upgrade_all_cli()Deb_Get.upgrade_one_cli()Deb_Get.remove()Deb_Get.sync()Deb_Get.cleanup_cache()Deb_Get.cli_namesDeb_Get.idDeb_Get.virtual
- meta_package_manager.managers.dnf module
DNFDNF.maintenance_noteDNF.nameDNF.homepage_urlDNF.platformsDNF.default_sudoDNF.requirementDNF.cli_namesDNF.pre_argsDNF.DELIMITERDNF.installedDNF.outdatedDNF.orphansDNF.search()DNF.install()DNF.upgrade_all_cli()DNF.upgrade_one_cli()DNF.remove()DNF.remove_orphan()DNF.sync()DNF.cleanup_orphan()DNF.cleanup_cache()DNF.doctor_cli()DNF.idDNF.virtual
DNF5YUM
- meta_package_manager.managers.emerge module
EmergeEmerge.nameEmerge.homepage_urlEmerge.platformsEmerge.default_sudoEmerge.requirementEmerge.pre_argsEmerge.version_regexesEmerge.installedEmerge.outdatedEmerge.orphansEmerge.search()Emerge.install()Emerge.upgrade_all_cli()Emerge.upgrade_one_cli()Emerge.remove()Emerge.sync()Emerge.cleanup_orphan()Emerge.cli_namesEmerge.idEmerge.virtualEmerge.cleanup_cache()
- meta_package_manager.managers.eopkg module
EOPKGEOPKG.nameEOPKG.homepage_urlEOPKG.platformsEOPKG.default_sudoEOPKG.requirementEOPKG.pre_argsEOPKG.version_regexesEOPKG.installedEOPKG.outdatedEOPKG.search()EOPKG.install()EOPKG.upgrade_all_cli()EOPKG.upgrade_one_cli()EOPKG.remove()EOPKG.sync()EOPKG.cleanup_cache()EOPKG.cli_namesEOPKG.idEOPKG.virtual
- meta_package_manager.managers.flatpak module
FlatpakFlatpak.homepage_urlFlatpak.brewfile_entry_typeFlatpak.platformsFlatpak.requirementFlatpak.version_regexesFlatpak.installedFlatpak.outdatedFlatpak.search()Flatpak.install()Flatpak.upgrade_all_cli()Flatpak.upgrade_one_cli()Flatpak.remove()Flatpak.cleanup_orphan()Flatpak.cleanup_repair()Flatpak.cli_namesFlatpak.idFlatpak.nameFlatpak.virtualFlatpak.doctor_cli()
- meta_package_manager.managers.fwupd module
- meta_package_manager.managers.gem module
- meta_package_manager.managers.guix module
- meta_package_manager.managers.homebrew module
HomebrewHomebrew.platformsHomebrew.requirementHomebrew.virtualHomebrew.extra_envHomebrew.version_regexesHomebrew.installedHomebrew.package_metadata_batch()Homebrew.outdatedHomebrew.search()Homebrew.trust_tap()Homebrew.install()Homebrew.upgrade_all_cli()Homebrew.upgrade_one_cli()Homebrew.remove()Homebrew.sync()Homebrew.cleanup_orphan()Homebrew.cleanup_cache()Homebrew.doctor_cli()Homebrew.cli_namesHomebrew.idHomebrew.name
BrewCask
- meta_package_manager.managers.mas module
- meta_package_manager.managers.mise module
- meta_package_manager.managers.nix module
- meta_package_manager.managers.npm module
NPMNPM.nameNPM.homepage_urlNPM.brewfile_entry_typeNPM.platformsNPM.requirementNPM.cooldown_env_varNPM.pre_argsNPM.cooldown_env_value()NPM.run_cli()NPM.installedNPM.outdatedNPM.search()NPM.install()NPM.upgrade_all_cli()NPM.upgrade_one_cli()NPM.cli_namesNPM.idNPM.remove()NPM.virtualNPM.cleanup_cache()NPM.doctor_cli()
- meta_package_manager.managers.pacman module
PacmanPacman.namePacman.homepage_urlPacman.platformsPacman.default_sudoPacman.requirementPacman.pre_argsPacman.version_regexesPacman.installedPacman.outdatedPacman.orphansPacman.search()Pacman.install()Pacman.upgrade_all_cli()Pacman.upgrade_one_cli()Pacman.remove()Pacman.remove_orphan()Pacman.sync()Pacman.cleanup_cache()Pacman.doctor_cli()Pacman.cli_namesPacman.idPacman.virtual
PacaurParuYay
- meta_package_manager.managers.pacstall module
PacstallPacstall.homepage_urlPacstall.platformsPacstall.internal_sudoPacstall.requirementPacstall.extra_envPacstall.version_regexesPacstall.installedPacstall.outdatedPacstall.search()Pacstall.install()Pacstall.upgrade_all_cli()Pacstall.upgrade_one_cli()Pacstall.cli_namesPacstall.idPacstall.namePacstall.remove()Pacstall.virtualPacstall.sync()
- meta_package_manager.managers.pip module
PipPip.namePip.homepage_urlPip.platformsPip.requirementPip.cooldown_env_varPip.cli_namesPip.pre_argsPip.version_cli_optionsPip.version_regexesPip.search_all_cli()Pip.versionPip.installedPip.package_metadata_batch()Pip.outdatedPip.install()Pip.idPip.upgrade_one_cli()Pip.virtualPip.remove()Pip.cleanup_cache()Pip.doctor_cli()
- meta_package_manager.managers.pipx module
- meta_package_manager.managers.pkcon module
- meta_package_manager.managers.pkg module
PORTS_TREEPKGPKG.namePKG.homepage_urlPKG.platformsPKG.default_sudoPKG.requirementPKG.pre_argsPKG.installedPKG.outdatedPKG.orphansPKG.search()PKG.install()PKG.upgrade_all_cli()PKG.upgrade_one_cli()PKG.remove()PKG.sync()PKG.cleanup_orphan()PKG.cleanup_cache()PKG.doctor_cli()PKG.cli_namesPKG.idPKG.virtual
PortsPorts.namePorts.homepage_urlPorts.platformsPorts.default_sudoPorts.cli_namesPorts.extra_envPorts.version_cli_optionsPorts.version_regexesPorts.availablePorts.idPorts.installedPorts.virtualPorts.outdatedPorts.install()Ports.upgrade_all_cli()Ports.upgrade_one_cli()Ports.removePorts.sync()Ports.cleanup_cache()
- meta_package_manager.managers.pnpm module
- meta_package_manager.managers.pwsh_gallery module
PWSH_GalleryPWSH_Gallery.namePWSH_Gallery.homepage_urlPWSH_Gallery.platformsPWSH_Gallery.requirementPWSH_Gallery.cli_namesPWSH_Gallery.pre_argsPWSH_Gallery.version_regexesPWSH_Gallery.installedPWSH_Gallery.outdatedPWSH_Gallery.search()PWSH_Gallery.install()PWSH_Gallery.idPWSH_Gallery.upgrade_all_cli()PWSH_Gallery.virtualPWSH_Gallery.upgrade_one_cli()PWSH_Gallery.remove()
- meta_package_manager.managers.scoop module
ScoopScoop.nameScoop.homepage_urlScoop.platformsScoop.requirementScoop.version_regexesScoop.remove_headers()Scoop.installedScoop.outdatedScoop.search()Scoop.install()Scoop.upgrade_all_cli()Scoop.upgrade_one_cli()Scoop.remove()Scoop.sync()Scoop.cleanup_cache()Scoop.cli_namesScoop.doctor_cli()Scoop.idScoop.virtual
- meta_package_manager.managers.sdkman module
SDKMANSDKMAN.homepage_urlSDKMAN.platformsSDKMAN.requirementSDKMAN.cli_namesSDKMAN.cli_search_pathSDKMAN.extra_envSDKMAN.version_cli_optionsSDKMAN.version_regexesSDKMAN.build_cli()SDKMAN.installedSDKMAN.outdatedSDKMAN.install()SDKMAN.upgrade_all_cli()SDKMAN.idSDKMAN.nameSDKMAN.upgrade_one_cli()SDKMAN.virtualSDKMAN.remove()SDKMAN.sync()SDKMAN.cleanup_cache()
- meta_package_manager.managers.sfsu module
- meta_package_manager.managers.snap module
- meta_package_manager.managers.sun_tools module
Sun_ToolsSun_Tools.maintenance_noteSun_Tools.nameSun_Tools.homepage_urlSun_Tools.platformsSun_Tools.default_sudoSun_Tools.cli_namesSun_Tools.cli_search_pathSun_Tools.version_cliSun_Tools.version_cli_optionsSun_Tools.version_regexesSun_Tools.idSun_Tools.installedSun_Tools.virtualSun_Tools.remove()
- meta_package_manager.managers.tazpkg module
TazpkgTazpkg.maintenance_noteTazpkg.nameTazpkg.homepage_urlTazpkg.platformsTazpkg.default_sudoTazpkg.extra_envTazpkg.post_argsTazpkg.version_cliTazpkg.version_cli_optionsTazpkg.version_regexesTazpkg.installedTazpkg.search()Tazpkg.install()Tazpkg.upgrade_all_cli()Tazpkg.upgrade_one_cli()Tazpkg.cli_namesTazpkg.idTazpkg.remove()Tazpkg.virtualTazpkg.sync()Tazpkg.cleanup_cache()
- meta_package_manager.managers.uv module
- meta_package_manager.managers.volta module
- meta_package_manager.managers.winget module
WinGetWinGet.homepage_urlWinGet.brewfile_entry_typeWinGet.platformsWinGet.requirementWinGet.post_argsWinGet.version_regexesWinGet.windows_creation_flagsWinGet.windows_processes_to_cleanupWinGet.installedWinGet.outdatedWinGet.search()WinGet.install()WinGet.cli_namesWinGet.idWinGet.nameWinGet.upgrade_all_cli()WinGet.virtualWinGet.upgrade_one_cli()WinGet.remove()WinGet.sync()
- meta_package_manager.managers.xbps module
XBPSXBPS.nameXBPS.homepage_urlXBPS.platformsXBPS.default_sudoXBPS.requirementXBPS.cli_namesXBPS.installedXBPS.outdatedXBPS.orphansXBPS.search()XBPS.install()XBPS.upgrade_all_cli()XBPS.upgrade_one_cli()XBPS.remove()XBPS.idXBPS.remove_orphan()XBPS.virtualXBPS.sync()XBPS.cleanup_orphan()XBPS.cleanup_cache()XBPS.doctor_cli()
- meta_package_manager.managers.yarn module
YarnYarnClassicYarnClassic.maintenance_noteYarnClassic.idYarnClassic.nameYarnClassic.requirementYarnClassic.cli_namesYarnClassic.pre_argsYarnClassic.installedYarnClassic.global_dirYarnClassic.outdatedYarnClassic.search()YarnClassic.install()YarnClassic.upgrade_all_cli()YarnClassic.upgrade_one_cli()YarnClassic.remove()YarnClassic.virtual
YarnBerry
- meta_package_manager.managers.zypper module
ZypperZypper.nameZypper.homepage_urlZypper.platformsZypper.default_sudoZypper.requirementZypper.pre_argsZypper.version_regexesZypper.installedZypper.outdatedZypper.orphansZypper.search()Zypper.install()Zypper.upgrade_all_cli()Zypper.upgrade_one_cli()Zypper.remove()Zypper.remove_orphan()Zypper.cli_namesZypper.idZypper.sync()Zypper.virtualZypper.cleanup_cache()
meta_package_manager.sbompackage
SubmodulesΒΆ
meta_package_manager.bar_plugin moduleΒΆ
Xbar and SwiftBar plugin for Meta Package Manager (the mpm CLI).
Default update cycle should be set to several hours so we have a chance to get userβs attention once a day. Higher frequency might ruin the system as all checks are quite resource intensive, and Homebrew might hit GitHubβs API calls quota.
Xbar automatically bridge plugin options between its UI and environment variable on script execution.
This is in progress for SwiftBar.
- meta_package_manager.bar_plugin.SWIFTBAR_MIN_VERSION = (2, 1, 2)ΒΆ
SwiftBar v2.1.2 fix an issue with multiple parameters in the font strings.
- meta_package_manager.bar_plugin.XBAR_MIN_VERSION = (2, 1, 7)ΒΆ
Xbar v2.1.7-beta is the latest version available on Homebrew.
- meta_package_manager.bar_plugin.MPM_MIN_VERSION = (5, 0, 0)ΒΆ
Mpm v5.0.0 was the first version taking care of the complete layout rendering.
- meta_package_manager.bar_plugin.MPM_TIMEOUT = 60ΒΆ
Maximum duration in seconds the plugin lets any single
mpmcall run.Passed as
--timeoutto everympminvocation so the plugin is never at the mercy of mpmβs own per-operation defaults, which are tuned for interactive CLI use and far too long for a background menubar refresh (120s for read-only queries, 500s for state-changing operations likesync). A wedged package manager then fails the whole refresh in a minute instead of freezing the menubar for several.
- class meta_package_manager.bar_plugin.MPMPlugin[source]ΒΆ
Bases:
objectImplements the minimal code necessary to locate and call the
mpmCLI on the system.Once
mpmis located, we can rely on it to produce the main output of the plugin.The output must supports both Xbar dialect and SwiftBar dialect.
- static getenv_str(var, default=None)[source]ΒΆ
Utility to get environment variables.
Note that all environment variables are strings. Always returns a lowered-case string.
- static getenv_bool(var, default=False)[source]ΒΆ
Utility to normalize boolean environment variables.
Relies on
configparser.RawConfigParser.BOOLEAN_STATESto translate strings into boolean. See: https://github.com/python/cpython/blob/3c298e2e385fc6f462abaada2fd680deb1a2b58e/Lib/configparser.py#L596-L597- Return type:
- static normalize_params(font_string, valid_ids=None)[source]ΒΆ
Parse a multi-parameters string and return a normalized string.
The string is expected to be a space-separated list of parameters, each parameter being a key/value pair separated by an equal sign.
Only keeps the parameters that are in the
valid_idsset and ignores the rest. By default, onlycolor,fontandsizeare kept.Multiple values for the same parameter will be deduplicated, and the last one will be kept.
Available parameters are: - https://github.com/swiftbar/SwiftBar?tab=readme-ov-file#parameters - https://github.com/matryer/xbar-plugins/blob/main/CONTRIBUTING.md#parameters
- Return type:
- static str_to_version(version_string)[source]ΒΆ
Transforms a string into a tuple of integers representing a version.
- static version_to_str(version_tuple)[source]ΒΆ
Transforms a tuple of integers representing a version into a string.
- Return type:
- property table_rendering: boolΒΆ
Aligns package names and versions, like a table, for easier visual parsing.
If
True, will aligns all items using a fixed-width font.
- static search_venv(folder)[source]ΒΆ
Search for signs of a virtual env in the provided folder.
Returns CLI arguments that can be used to run
mpmfrom the virtualenv context, orNoneif the folder is not a venv.Inspired by autoswitch_virtualenv.plugin.zsh and uvβs get_interpreter_info.py.
- search_mpm()[source]ΒΆ
Iterate over possible CLI commands to execute
mpm.Should be able to produce the full spectrum of alternative commands we can use to invoke
mpmover different context.The order in which the candidates are returned by this method is conserved by the
ranked_mpm()method below.We prioritize venv-based findings first, as theyβre more likely to have all dependencies installed and sorted out. Theyβre also our prime candidates in unittests.
Then we search for system-wide installation. And finally Python modules.
- property ranked_mpm: list[tuple[tuple[str, ...], bool, bool, tuple[int, ...] | None, str | Exception | None]]ΒΆ
Rank the mpm candidates we found on the system.
Sort them by: - runnability - up-to-date status - version number - error
On tie, the order from
search_mpmis respected.
- property best_mpm: tuple[tuple[str, ...], bool, bool, tuple[int, ...] | None, str | Exception | None]ΒΆ
- static pp(label, *args)[source]ΒΆ
Print one menu-line with the Xbar/SwiftBar dialect.
First argument is the menu-line label, separated by a pipe to all other non- empty parameters, themselves separated by a space.
Skip printing of the line if label is empty. A
Noneparameter renders nothing, so a package without an upgrade CLI still gets its label-only menu line.- Return type:
- print_error(message, submenu='')[source]ΒΆ
Print a formatted error message line by line.
A red, fixed-width font is used to preserve traceback and exception layout. For compactness, the block message is dedented and empty lines are skipped.
Message is always casted to a string as we allow passing of exception objects and have them rendered.
- Return type:
Print the main menu.
- Return type:
meta_package_manager.bar_plugin_renderer moduleΒΆ
mpm-side renderer that builds Xbar/SwiftBar plugin output.
Lives in its own module rather than in
meta_package_manager.bar_plugin because that module is
intentionally stdlib-only: the
meta_package_manager.bar_plugin.MPMPlugin class is the
script that gets installed as the userβs actual bar plugin and must
stay light on dependencies.
This module is the heavier mpm-side companion that augments the
shippable plugin code with click_extra, boltons, the manager pool, and
the theme system to produce the final rendered output from
mpm outdated --plugin-output.
- meta_package_manager.bar_plugin_renderer.VERSION_PREFIX_COLOR = 245ΒΆ
Xterm-256 palette index coloring the unchanged version prefix in menu lines.
The CLI table keeps
meta_package_manager.version.diff_versions()βs defaultbright_black(SGR90), which terminals remap to their own theme. SwiftBar instead hard-maps SGR90to a fixedNSColor.darkGray, near-invisible on a dark-mode menu, while its 256-color support renders palette index245as a theme-neutral mid-gray (#8a8a8a), legible on both appearances. Xbar strips the ANSI codes it does not render, so the choice is inert there.
- meta_package_manager.bar_plugin_renderer.LIGHT_MENU_OLD_COLOR = 124ΒΆ
Palette index for the old-version (red) suffix on a light-appearance menu.
#af0000, a6.5:1contrast ratio on the cream material. SeeBarPluginRenderer.menu_diff_colors()for why the override exists.
- meta_package_manager.bar_plugin_renderer.LIGHT_MENU_NEW_COLOR = 23ΒΆ
Palette index for the new-version (green) suffix on a light-appearance menu.
#006600, a6.3:1contrast ratio on the cream material. SeeBarPluginRenderer.menu_diff_colors().
- meta_package_manager.bar_plugin_renderer.DARK_MENU_NEW_COLOR = 46ΒΆ
Palette index for the new-version (green) suffix on a dark-appearance menu.
#00ff00, lifting the worst-case contrast from4.0:1(the adaptiveNSColor.systemGreen) to5.8:1over a bright wallpaper showing through the translucent menu. The old-version (red) suffix keepssystemRed: it is already the most readable recognizable red the xterm-256 palette can express (a pure#ff0000scores lower, and brighter options read as orange). SeeBarPluginRenderer.menu_diff_colors().
- class meta_package_manager.bar_plugin_renderer.BarPluginRenderer[source]ΒΆ
Bases:
MPMPluginAll utilities used to render output compatible with both Xbar and SwiftBar plugin dialect.
The minimal code to locate
mpm, then call it and print its output resides in the plugin itself atmeta_package_manager.bar_plugin.MPMPlugin.best_mpm().All other stuff, especially the rendering code, is managed here, to allow for more complex layouts relying on external Python dependencies. This also limits the number of required updates on the plugin itself.
Group packages into manager sub-menus.
If
True, will replace the default flat layout with an alternative structure where actions are grouped into submenus, one for each manager.Value is sourced from the
VAR_SUBMENU_LAYOUTenvironment variable.
Appearance-adaptive version-diff suffix colors for the menu.
SwiftBar maps
meta_package_manager.version.diff_versions()βs default SGR31/32suffixes to the adaptiveNSColor.systemRed/systemGreen, and exports the menu appearance in theOS_APPEARANCEenvironment variable (which propagates to the mpm outdated βplugin-output subprocess). On the translucent βLiquid Glassβ menus of recent macOS releases these system colors lose contrast against the material, so override them per appearance:A light menu washes out both suffixes (the green measured
1.9:1), so darken them toLIGHT_MENU_OLD_COLORandLIGHT_MENU_NEW_COLOR.A dark menu over a bright wallpaper dims the green to
4.0:1, so brighten it toDARK_MENU_NEW_COLOR; the red keepssystemRed, already the most readable red the palette allows.
The result is returned as
diff_versionskeyword arguments. When the variable is absent (a consumer like Xbar, which strips these codes anyway) return an empty mapping, keeping the system-color defaults.
- property mpm_cli: tuple[str, ...]ΒΆ
Absolute
mpminvocation the menu actions are routed through.Re-enters the very interpreter rendering the menu, so a click runs the
mpmthe plugin called and resolves the same configuration file. Derived fromsys.executablerather thansys.argv[0]: the former is always an absolute path to a runnable entry point, while the latter degrades to a console script, a__main__.pyor a bare-cdepending on howmpmwas started. A Nuitka-compiledmpmis its own interpreter, so it is invoked directly instead of through the module.Note
The candidates
meta_package_manager.bar_plugin.MPMPlugin.search_mpm()produces are deliberately not reused here. The venv ones lead with a bareuv/pipenv/poetrycommand name, while a bar app spawns a menu action with the barelaunchdPATH, where such a name does not resolve.
- static render_cli(cmd_args)[source]ΒΆ
Return a formatted CLI compatible with Xbar and SwiftBar plugin format.
I.e. a string with this schema:
shell=cmd_args[0] param1=cmd_args[1] param2=cmd_args[2] ...
- Return type:
- print_cli_item(*args)[source]ΒΆ
Print two CLI entries:
one that opens a visible terminal so the user can follow the execution
a second one, reachable by holding the
Optionkey, that runs silently
- Return type:
- print_upgrade_all_item(manager, submenu='')[source]ΒΆ
Print the menu entry to upgrade all outdated package of a manager.
- Return type:
- render(outdated_data)[source]ΒΆ
Wraps the
_render()method above to capture its<stdout>output.Every producer down the
_renderpath (the inheritedppandprint_errorincluded) writes through bareprintcalls, so redirecting<stdout>captures the whole rendering.- Return type:
- add_upgrade_cli(outdated_data)[source]ΒΆ
Augment the outdated data from
mpm outdatedsubcommand with upgrade CLI fields for bar plugin consumption.Every menu action is an
mpm_cliinvocation restricted to the manager owning the section (mpm --brew upgrade wget), never that managerβs own native command. Going back throughmpmis what subjects a click to the same policy as the run that rendered the menu: the configuration file found on the system, and with it the release-age cooldown, the manager selection, the sudo policy and the per-manager overrides. A native command escapes all of them, silently upgrading a packagempmitself would have held back.Only the manager selector and the operation are passed, so every other setting is resolved from the userβs configuration at click time.
A manager is offered the action only when it
implements()it, which is the same predicatempmuses to route the subcommand: a manager it would skip gets aNoneCLI and renders as a label-only menu line.
- print(outdated_data)[source]ΒΆ
Print the final plugin rendering to
<stdout>.Capturing the output of the plugin and re-printing it will introduce an extra line return, hence the extra call to
rstrip().Colors are forced on
echoβs auto-detection: the bar plugin capturesmpm outdated --plugin-outputthrough a pipe, whereechowould strip every ANSI code and the version-diff colors would never reach SwiftBar or Xbar. TTY detection is meaningless for this dialect, which flags ANSI rendering per line with theansi=true/ansi=falseparameters. An explicit opt-out (--color=never,NO_COLOR) is still honored: only the automatic (None) state is overridden.- Return type:
meta_package_manager.brewfile moduleΒΆ
Render the installed-package inventory as a Brewfile.
Defines build_brewfile() and the helpers used by mpm dump --brewfile
to emit a Brewfile that brew bundle install can consume.
Note
Brewfile is a Ruby DSL. The format reference is the Homebrew Bundle source at
Library/Homebrew/bundle/dsl.rb and the extensions under
Library/Homebrew/bundle/extensions/ (brew 6.0.0+).
- meta_package_manager.brewfile.BUNDLE_ENTRY_TYPES: tuple[str, ...] = ('tap', 'brew', 'cask', 'mas', 'vscode', 'npm', 'cargo', 'uv', 'winget', 'flatpak')ΒΆ
Canonical emission order of Brewfile sections.
Mirrors the registration order of
Homebrew::Bundle.dump_package_typesand the extensions underLibrary/Homebrew/bundle/extensions/.tapalways comes first so that any third-party tap a downstreambreworcaskentry references is registered before the install step runs.
- meta_package_manager.brewfile.DEFAULT_TAPS: frozenset[tuple[str, str]] = frozenset({('homebrew', 'cask'), ('homebrew', 'core')})ΒΆ
Taps that
brewenables by default. Never emitted as explicittaplines.
- meta_package_manager.brewfile.quote(value)[source]ΒΆ
Ruby-compatible double-quoted string literal.
brew bundle dumpuses RubyβsString#inspect: double quotes with backslash escapes for control characters and unicode.json.dumps(..., ensure_ascii=False)produces the same output for ASCII content and a Ruby-parseable double-quoted string for non-ASCII codepoints.- Return type:
- meta_package_manager.brewfile.format_entry(entry_type, name, options=None)[source]ΒΆ
Render a single Brewfile DSL line.
Supports the two shapes
Homebrew::Bundle::Extensions::Extension.dump_entryemits:bare:
brew "git"with options:
mas "Xcode", id: 497799835orflatpak "org.mozilla.firefox", with: ["flathub"]
- Return type:
- meta_package_manager.brewfile.format_header(coverage, skipped, platform)[source]ΒΆ
Render the comment block at the top of a Brewfile dump.
- Return type:
- meta_package_manager.brewfile.tap_from_package_id(package_id)[source]ΒΆ
Return
user/tapifpackage_idis tap-qualified, elseNone.Default taps in
DEFAULT_TAPSare filtered out: those are always enabled bybrewand emittingtaplines for them would be noise.
- meta_package_manager.brewfile.build_brewfile(managers, *, packages_by_manager=None, include_header=True, skipped_counts=None, platform='')[source]ΒΆ
Render a Brewfile from the given managersβ installed packages.
Only managers whose
brewfile_entry_typeis set contribute output; the caller is expected to have filtered the iterable accordingly, but managers without a configured entry type are silently skipped as a defensive measure.packages_by_manager(keyed by manager id) supplies each managerβs installed packages so the caller can fetch them concurrently up front. When omitted, each managerβsinstalledis queried inline instead (the path the unit tests exercise).skipped_countsis a per-manager-id tally of packages excluded because their manager has no Brewfile mapping; it is rendered in the header for visibility.- Return type:
meta_package_manager.capabilities moduleΒΆ
Declaration and inspection of the operations each package manager supports.
A concrete manager advertises what it can do by implementing operation methods and annotating them with the helpers defined here:
meta_package_manager.capabilities.search_capabilities()andmeta_package_manager.capabilities.version_not_implemented()flag the refinements an operation does not natively support, letting the framework compensate (refiltering search results, warning about ignored version pins).meta_package_manager.capabilities.Delegateandmeta_package_manager.capabilities.DelegatedMethodlet a manager reuse another managerβs CLI for an operation instead of reimplementing it.
Together they expose a uniform capability surface that
meta_package_manager.capabilities.implements() introspects and the CLI uses to
route each command only to the managers that support it. The
meta_package_manager.capabilities.Operations enum is the vocabulary of those
routable actions.
- class meta_package_manager.capabilities.Operations(*values)[source]ΒΆ
Bases:
EnumRecognized operation IDs that are implemented by package manager with their specific CLI invocation.
Each operation has its own CLI subcommand.
- installed = 'installed'ΒΆ
- outdated = 'outdated'ΒΆ
- orphans = 'orphans'ΒΆ
- search = 'search'ΒΆ
- install = 'install'ΒΆ
- upgrade = 'upgrade'ΒΆ
- upgrade_all = 'upgrade_all'ΒΆ
- remove = 'remove'ΒΆ
- sync = 'sync'ΒΆ
- cleanup = 'cleanup'ΒΆ
- doctor = 'doctor'ΒΆ
- meta_package_manager.capabilities.implements(manager, op)[source]ΒΆ
Inspect a managerβs implementation to check for proper support of an operation.
Accepts either a manager instance or its class; support is determined from the class hierarchy. The verdict is narrated as a single answered
DEBUGline (brew implements installed.), keyed on the manager ID rather than the raw class repr.- Return type:
- meta_package_manager.capabilities.upgrade_all_is_synthesized(manager)[source]ΒΆ
Whether
mpmbackfills the managerβsupgrade --all.Truewhen the manager supports the operation only through the one-by-one fallback ofmeta_package_manager.manager.PackageManager.upgrade(): it implementsoutdatedandupgrade_one_clibut no class in its hierarchy provides a nativeupgrade_all_cli.Falsewhen a native one-shot command exists, or when the operation is not supported at all.Feeds the per-manager table of
docs/augmentations.md, rendered live bymeta_package_manager._docs.- Return type:
- meta_package_manager.capabilities.implements_method(manager, method_name)[source]ΒΆ
Whether a non-base class in the managerβs MRO defines
method_name.The orphan refinements
remove_orphanandcleanup_orphanare optional variants of theremoveandcleanupcommands rather than standaloneOperations, soimplements()cannot route them. This reports whether a manager overrides the baseβs stub for one, delegating the MRO walk tometa_package_manager.manager.PackageManager._defines()(shared with the basecleanupcomposer), so it works for config-defined managers (whose methods live on the synthesized subclass) too.- Return type:
- meta_package_manager.capabilities.cleanup_orphan_is_synthesized(manager)[source]ΒΆ
Whether
mpmbackfills the managerβs system-wide orphan sweep.Truewhen no class in the managerβs hierarchy overridescleanup_orphanwith a native sweep, but the manager implements both theorphansquery andremove: the basemeta_package_manager.manager.PackageManager.cleanup_orphan()then synthesizes the sweep by listing the orphans and removing them one by one, the exact pattern of the synthesized fullupgrade --all.Falsewhen a native sweep exists, or when the manager lacks the building blocks.Feeds the per-manager table of
docs/augmentations.md, rendered live bymeta_package_manager._docs.- Return type:
- meta_package_manager.capabilities.supports_cleanup_cache(manager)[source]ΒΆ
Whether
mpm cleanup --cachecan drive the manager.- Return type:
- meta_package_manager.capabilities.supports_cleanup_repair(manager)[source]ΒΆ
Whether
mpm cleanup --repaircan drive the manager.- Return type:
- meta_package_manager.capabilities.exact_search_is_synthesized(manager)[source]ΒΆ
Whether
mpmbackfills the managerβssearch --exactrefinement.Truewhen the managerβs native search cannot filter exact matches, someta_package_manager.manager.PackageManager.refiltered_search()does the narrowing itself. Feeds the per-manager table ofdocs/augmentations.mdand the per-manager operation tables, rendered live bymeta_package_manager._docs.- Return type:
- meta_package_manager.capabilities.extended_search_is_synthesized(manager)[source]ΒΆ
Whether
mpmbackfills the managerβssearch --extendedrefinement.Truewhen the managerβs native search cannot reach descriptions, someta_package_manager.manager.PackageManager.refiltered_search()does the filtering itself. Feeds the per-manager table ofdocs/augmentations.mdand the per-manager operation tables, rendered live bymeta_package_manager._docs.- Return type:
- meta_package_manager.capabilities.search_capabilities(extended_support=True, exact_support=True)[source]ΒΆ
Decorator factory to be used on
search()operations to signalmpmframework managerβs capabilities.The flags are exposed as
extended_supportandexact_supportattributes on the wrapped method, so the documentation can derive which managers rely onmeta_package_manager.manager.PackageManager.refiltered_search()to honor the--exactand--extendedflags. An undecoratedsearchcarries no attribute and is read as natively supporting both refinements.
- meta_package_manager.capabilities.version_not_implemented(func)[source]ΒΆ
Decorator to be used on
install()orupgrade_one_cli()operations to signal that a particular operation does not implement (yet) the version specifier parameter.
- class meta_package_manager.capabilities.DelegatedMethod(method, cli_name)[source]ΒΆ
Bases:
objectDescriptor that delegates a method call to another managerβs CLI.
When accessed on an instance, returns a wrapper that sets
_delegate_cli_pathon the instance so thatbuild_cliuses the target managerβs binary instead of the host managerβs own CLI.
- class meta_package_manager.capabilities.Delegate(source_class)[source]ΒΆ
Bases:
objectFactory that creates
DelegatedMethoddescriptors for delegating operations to another package managerβs CLI.Typical usage in a manager class body:
from .scoop import Scoop _scoop = Delegate(Scoop) install = _scoop.install remove = _scoop.remove
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.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, timeout)[source]ΒΆ
Bases:
objectGlobal options and selection state every subcommand reads from
ctx.obj.Built once by the
mpmgroup body, after the eager option callbacks have accumulated the manager selectors into the transientctx.objdict this instance replaces (seeupdate_manager_selection()).- user_selection: list[str] | NoneΒΆ
Managers explicitly selected by the user, in priority order, or
None.
- selected_managers: Callable[[...], Iterator[PackageManager]]ΒΆ
Resolve the target managers, applying selection and manager-level options.
- meta_package_manager.cli.COOLDOWN_SUPPORTED_MANAGERS = ('npm', 'pip', 'pipx', 'pnpm', 'uv', 'uvx', 'yay')ΒΆ
IDs of the managers that natively enforce a release-age
mpm --cooldown.Derived from the pool so the
--cooldownhelp text never drifts from the set of managers that actually carry acooldown_env_var: adding cooldown support to a manager surfaces it here automatically.
- meta_package_manager.cli.guard_existing_output(ctx, output_path, *, overwrite)[source]ΒΆ
Block clobbering an existing output file unless
overwriteis set.Warns and exits with code 2 when
output_pathalready 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:
- 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:
- meta_package_manager.cli.single_manager_selectors()[source]ΒΆ
Dynamiccaly creates a dedicated flag selector alias for each manager.
- meta_package_manager.cli.bar_plugin_path(ctx, param, value)[source]ΒΆ
Print the location of the Xbar/SwiftBar 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-pathoption.Notice that the fully-qualified home directory get replaced by its shorthand (
~) if applicable:the full
/home/user/.python/site-packages/mpm/bar_plugin.pypath is simplified to~/.python/site-packages/mpm/bar_plugin.py,but
/usr/bin/python3.10/mpm/bar_plugin.pyis returned as-is.
- meta_package_manager.cli.query_option(f)ΒΆ
--queryfilter of the inventory exporters (dump,sbom).
- meta_package_manager.cli.query_exact_option(f)ΒΆ
--exactrefinement ofquery_option.
- meta_package_manager.cli.overwrite_option(f)ΒΆ
Opt-in clobbering of an existing output file (
dump,sbom); seeguard_existing_output().
- meta_package_manager.cli.package_label(spec)[source]ΒΆ
Render a spec as
package_idorpackage_id@versionfor trail output.- Return type:
- meta_package_manager.cli.fail_unless_zero_exit(ctx, message)[source]ΒΆ
Print the durable
critical: :message:` record, then exit `1unless-0/--zero-exitopted out of the gate.The shared failure gate of the action commands (
exit_on_failures()) anddoctor: the summary always prints, following the linter convention where findings gate automation, and-0keeps the exit code at0with the printed summary staying the durable record. Usage and configuration errors are unaffected: they exit2regardless, as genuine execution failures.- Return type:
- meta_package_manager.cli.exit_on_failures(ctx, verb, failures)[source]ΒΆ
Report the per-package
failurescollected this run and exit non-zero.A no-op when
failuresis empty. Otherwise routes the deduplicated, sortedCould not {verb}: ...summary throughfail_unless_zero_exit(). Shared by every action command (install,remove,upgrade <packages>,restore).- Return type:
meta_package_manager.cli_explore moduleΒΆ
The explore subcommands: the read-only queries and inspection tools.
managers, installed, outdated, orphans, search, which and
config-template, plus the query plumbing they share: the concurrent
collect prelude, the row builders and the query-match highlighter. Every
command here only reads system state and renders a table (or its serialized
counterpart).
The mpm group itself, and the plumbing shared with the other subcommand
modules, live in meta_package_manager.cli.
- meta_package_manager.cli_explore.exact_match_option(f)ΒΆ
--exactrefinement of the optional positionalQUERYofinstalledandoutdated.
meta_package_manager.cli_maintenance moduleΒΆ
The maintenance subcommands: the state changers and diagnostics.
install, upgrade, remove, sync, cleanup and doctor, plus
the machinery only they need: the cooldown gate, the sourced-operation
dispatch that resolves each package spec to its source managers, and the
cleanup category selection.
The mpm group itself, and the per-package action engine restore also
drives, live in meta_package_manager.cli.
- meta_package_manager.cli_maintenance.cooldown_permits(manager)[source]ΒΆ
Decide whether a release-introducing operation may run on
manager.Returns
Truewhen no cooldown is active, when the manager can enforce it natively, or when the user opted out of the requirement with--allow-unsupported-managersor itsrequire_cooldown_supportconfiguration key. ReturnsFalse(after logging the skip) when an active cooldown cannot be enforced and the requirement still holds, so the caller leaves the manager alone rather than letting a freshly-published version slip in.The skip message names both remedies, the one-shot flag and the persistent configuration key: opting out of a supply-chain safeguard is a standing policy decision, not something to re-type on every run.
- Return type:
- meta_package_manager.cli_maintenance.CLEANUP_CATEGORIES = ('orphans', 'cache', 'repair')ΒΆ
Cumulative categories the
cleanupsubcommand decomposes into.Each category has a two-sided
--<category>/--skip-<category>flag pair. Positive flags narrow the run to exactly the listed categories; skip flags subtract categories from the default selection.
- meta_package_manager.cli_maintenance.DEFAULT_CLEANUP_CATEGORIES = frozenset({'cache', 'repair'})ΒΆ
Categories a plain
cleanup(no category flag) runs.The orphan sweep is deliberately absent: it removes packages, where cache pruning and state repair only reclaim disk and fix metadata. Keeping it strictly behind an explicit
--orphansmakes the default non-destructive and identical on every manager, native sweep or not, mirroring howremovekeeps its cascade behind the same flag.
meta_package_manager.cli_sbom moduleΒΆ
The SBOM subcommand: export the package inventory as a standard document.
sbom renders the installed inventory as a SPDX or CycloneDX file, with
optional metadata enrichment and an opt-in OSV vulnerability scan.
The mpm group itself, and the plumbing shared with the other subcommand
modules, live in meta_package_manager.cli.
meta_package_manager.cli_snapshots moduleΒΆ
The package snapshots subcommands: manifest export and replay.
dump (TOML manifest or Brewfile) and restore (install back the
packages a TOML manifest references, through the shared per-package action
engine).
The mpm group itself, and the plumbing shared with the other subcommand
modules, live in meta_package_manager.cli.
meta_package_manager.config moduleΒΆ
Configuration utilities for mpm.
Hosts the schema of the [mpm] configuration section consumed by
click_extra and the runtime policy around the [mpm.managers.<id>]
sections of the same configuration file: applying attribute overrides to shipped
managers, gating manager definitions on the trust of their source, and registering
them into the pool.
The concerns stay separate across three modules:
meta_package_manager.pool.ManagerPool owns the live manager instances
and the per-manager overridden_fields tracking dict;
meta_package_manager.definitions owns the declarative schema (which
fields a section may set, how to coerce values) and the class factory; this module
owns the loading policy and mutates the pool through the
apply_manager_overrides() and register_config_managers() helpers.
- class meta_package_manager.config.MpmConfig(all_managers=False, ignore_auto_updates=True, stop_on_error=False, dry_run=False, plan=False, sudo=None, timeout=None, jobs='auto', cooldown='', require_cooldown_support=True, description=False, sort_by=<factory>, summary=True, network=False, suggest_contribs=True, managers=<factory>)[source]ΒΆ
Bases:
objectSchema for
mpmconfiguration files.Defines the recognized options for the
[mpm](or[tool.mpm]) configuration section. Each field corresponds to a CLI option on the rootmpmgroup.Note
Dynamic manager selectors (
brew = true,pip = false, etc.), click-extra built-in options (verbosity,table_format) and one-shot utility flags (--bar-plugin-path,--xkcd) are handled by thedefault_mappipeline and do not appear here.Note
Multi-word fields pin their config path through
CONFIG_PATH_METADATA_KEY: mpmβs configuration convention is underscored keys (matching Click parameter names, the--validate-configchecks and every documented example), while click-extra would otherwise kebab-case field names in the renderedclick:configreference.- all_managers: bool = FalseΒΆ
Force evaluation of all managers, including unsupported and unmaintained.
- sudo: bool | None = NoneΒΆ
Force privileged manager operations with (
True) or without (False)sudo. Unset by default: system managers escalate, user-level managers do not. Overridden per manager by asudoentry in[mpm.managers.<id>].
- timeout: int | None = NoneΒΆ
Maximum duration in seconds for each manager CLI call. When unset, a per-operation default applies:
120for read-only queries (installed,outdated,search) and500for state-changing operations. A set value overrides every operation.
- jobs: int | str = 'auto'ΒΆ
Maximum number of managers to run concurrently. Accepts an integer, or the keywords
auto(one fewer than the logical CPU count, the default) andmax(every logical CPU); set1to run sequentially.
- cooldown: str = ''ΒΆ
Minimum release age (like
7 daysor1 week) a package version must reach before it can be installed or upgraded. Empty disables the gate.
- require_cooldown_support: bool = TrueΒΆ
Require managers to natively support a requested cooldown to run install/upgrade: skip those that cannot (fail-closed). Set to
Falseto run them anyway, without the safeguard.
- summary: bool = TrueΒΆ
Print an end-of-run summary on stderr: a count line of per-manager totals plus any subcommand-specific follow-up notes.
- network: bool = FalseΒΆ
Opt into network calls during the run. Today this only affects
mpm sbom, which queries OSV.dev for vulnerability data.
- suggest_contribs: bool = TrueΒΆ
Print a contribution invitation when a user override targets a field that likely indicates an upstream detection bug.
- managers: dict[str, dict]ΒΆ
Per-manager attribute overrides keyed by manager ID.
Typed as
dict[str, dict]so click-extra treats the sub-tree as opaque: its keys are manager IDs (data, not flag names) and its leaf entries are validated byvalidate_manager_overrides_section()registered as aclick_extra.ConfigValidator. The field carries no CLI flag β it only exists in the schema to declare opacity and to enable--validate-configcoverage of the override block.
- meta_package_manager.config.INVALIDATED_CACHED_PROPS: Final[tuple[str, ...]] = ('available', 'cli_path', 'executable', 'fresh', 'supported', 'version')ΒΆ
Cached properties on
meta_package_manager.manager.PackageManagerthat may have been computed from attributes covered byOVERRIDABLE_FIELDS.Any pre-computed values are popped from the manager instanceβs
__dict__after an override is applied so the next access recomputes them against the new attribute values. Safe to pop even if nothing was cached.
- meta_package_manager.config.CONTRIBUTION_HINT_FIELDS: Final[frozenset[str]] = frozenset({'cli_names', 'cli_search_path', 'requirement', 'version_cli_options', 'version_regexes'})ΒΆ
Subset of
OVERRIDABLE_FIELDSwhose override probably reflects a real upstream detection bug rather than a personal preference.When the user overrides one of these, mpm did not find the binary, used the wrong binary name, rejected a valid version, or failed to parse one. The other overridable fields (
timeout,ignore_auto_updates,pre_args, etc.) are user preferences and do not warrant a contribution invitation.
- meta_package_manager.config.ISSUE_TRACKER_NEW_URL: Final[str] = 'https://github.com/kdeldycke/meta-package-manager/issues/new'ΒΆ
Base URL of the upstream GitHub issue trackerβs new-issue endpoint.
- meta_package_manager.config.MAX_ISSUE_URL_LENGTH: Final[int] = 8192ΒΆ
Practical upper bound on the length of a pre-filled GitHub new-issue URL.
GitHub silently truncates very long URLs, which yields a broken issue form when the user clicks the invitation. Anything past 8 KiB is treated as a bug in the URL builder rather than a configuration we should tolerate.
- class meta_package_manager.config.ContributionHint(manager_id, field, user_value, detected_cli_path)[source]ΒΆ
Bases:
objectA user override of a detection-related field, candidate for upstream contribution.
Captured at override time by
apply_manager_overrides()so the user can later be invited to file an upstream issue with a pre-filled bug-report URL.- field: strΒΆ
Name of the overridden
PackageManagerattribute.
- meta_package_manager.config.format_contribution_hints(hints)[source]ΒΆ
Render a multi-line, human-readable batch message inviting the user to contribute their overrides back upstream.
Returns an empty string for an empty list so the caller can branch on truthiness without a length check.
- Return type:
- meta_package_manager.config.validate_manager_overrides_section(section, *, pool)[source]ΒΆ
Strict validator for the
[mpm.managers.<id>]configuration sub-tree.Pure function: inspects
sectionagainst the poolβs registered managers andOVERRIDABLE_FIELDS, raises the firstclick_extra.ValidationErrorit encounters, never mutates the pool. Suitable for registration as aclick_extra.ConfigValidatorand for direct invocation byapply_manager_overrides()so both the--validate-configpath and the runtime application path enforce the same rules.A section keyed by a built-in manager ID is validated as an override (its fields must be a subset of
OVERRIDABLE_FIELDS). A section keyed by any other ID is validated as a brand-new manager definition viaparse_manager_definition().- Raises:
click_extra.ValidationError β when
sectionis not a mapping, an override sets an unknown field or a wrong-typed value, or a definition is malformed. Thepathof the raised error is relative to the[mpm.managers]section root (e.g."winget.cli_searchpath"); click-extra prepends the app prefix when surfacing the error.- Return type:
- meta_package_manager.config.apply_manager_overrides(pool, overrides)[source]ΒΆ
Apply per-manager attribute overrides parsed from the userβs config file.
Expects
overridesto be a mapping of manager ID to a mapping of attribute name to its new value, as returned byconf["mpm"]["managers"].Noneand empty mappings are accepted as no-op shortcuts so callers can unconditionally forward whatever was parsed from the config file.Validation is delegated to
validate_manager_overrides_section(), which raisesclick_extra.ValidationErroron the first issue. Both the runtime config-loading path and the explicit--validate-configpath enforce the same rules through that single validator, so a config that survives one survives the other.After validation succeeds, every override is applied as an instance attribute (shadowing the class default for the lifetime of the process), recorded in
ManagerPool.overridden_fieldssoManagerPool._select_managers()skips the matching global--<flag>defaults for that manager, and the cached properties derived from the affected attributes are evicted so the next access recomputes them. List-valued fields use replace semantics: the override fully supersedes the built-in default.Returns a list of
ContributionHintentries, one per accepted override that targets aCONTRIBUTION_HINT_FIELDSfield. Each hint captures the pre-overridecli_pathso the contribution invitation can show what mpm would have detected without the userβs intervention.- Return type:
- meta_package_manager.config.build_manager_overrides_validator(pool)[source]ΒΆ
Construct a
click_extra.ConfigValidatorfor the[mpm.managers]sub-tree, bound to a specificManagerPool.Used by the CLI bootstrap (
@groupdecorator) to register a validator against the live pool. Wrappingvalidate_manager_overrides_section()in a closure satisfies theclick_extra.ConfigValidator.validatorsignature (Callable[[dict], None]) while keeping the underlying validator pool-agnostic and testable in isolation.- Return type:
- meta_package_manager.config.dump_manager_overrides(manager)[source]ΒΆ
Return the current overridable attributes of
manageras a TOML-ready dict.Walks
OVERRIDABLE_FIELDSin alphabetical order, reads each attribute from the manager instance, and converts tuples to lists sotomli_wcan serialize the result without translation. Attributes whose value isNoneare skipped: TOML cannot expressNoneand the user cannot override a field toNoneeither, so emitting the key would be misleading.Every other overridable field is emitted, including ones still at the class default. The output is meant to be a canonical override template: paste, prune the rows that donβt apply, and customize the rest.
- meta_package_manager.config.CTX_HINTS_KEY: Final[str] = 'mpm.contribution_hints'ΒΆ
ctx.metakey under which collectedContributionHintentries are accumulated betweenapply_manager_overrides_from_context()andprint_contribution_hints().
- meta_package_manager.config.apply_manager_overrides_from_context(ctx, pool)[source]ΒΆ
Read the
[mpm.managers.<id>]sections from the loaded config and apply them topool.Reads the full parsed config that
click_extraexposes underCONF_FULLafter configuration discovery and forwards the["mpm"]["managers"]subtree toapply_manager_overrides(). Returns silently when no configuration file was loaded or when the section is absent.Any
ContributionHintreturned byapply_manager_overrides()is stashed underCTX_HINTS_KEYforprint_contribution_hints()to surface at the end of the run.- Return type:
- meta_package_manager.config.print_contribution_hints(ctx)[source]ΒΆ
Print the collected contribution hints to
<stderr>.Reads from
CTX_HINTS_KEYand writes viaclick_extra.echo()rather than the logging module, so the message survives--verbosity CRITICAL``and the``logging.disable()block that suppresses log output for serialization formats. Caller is expected to gate this on the userβssuggest_contribspreference.- Return type:
- meta_package_manager.config.RISKY_OVERRIDE_FIELDS: Final[frozenset[str]] = frozenset({'cli_names', 'cli_search_path', 'pre_cmds', 'sudo'})ΒΆ
Override fields that can redirect mpm to run an arbitrary binary (or
sudo).When such an override is read from an untrusted config source,
apply_manager_overrides_from_context()logs a warning. Seedocs/security.md.
- meta_package_manager.config.config_file_is_trusted(path)[source]ΒΆ
Whether a config file is safe to load executable manager definitions from.
Trusted on POSIX when both the file and its parent directory are owned by the current user or root and are not group- or world-writable, mirroring how
ssh,gitandsudoreason about config-file trust: a writable file (or a writable directory that lets an attacker swap the file) could inject arbitrary commands.On platforms without
os.getuid(Windows), the POSIX ownership model does not apply and the check is skipped (returnsTrue); seedocs/security.mdfor the rationale and the residual risk.- Return type:
- meta_package_manager.config.register_config_managers(pool, definitions, *, source=None, source_is_url=False)[source]ΒΆ
Build and register config-defined managers into
pool, applying the trust gate.A definition is skipped (with a warning) when its ID collides with a built-in, when it comes from a remote URL config, or when its local config file fails
config_file_is_trusted(). Returns the IDs actually registered. Definitions whose ID is already in the pool (e.g. registered by the eager pre-load) are silently skipped so the eager and callback passes are idempotent.
- meta_package_manager.config.register_config_managers_from_context(ctx, pool)[source]ΒΆ
Register config-defined managers from the loaded config (authoritative pass).
Reads the parsed config under
CONF_FULL, parses the non-built-in[mpm.managers.<id>]sections, and registers them throughregister_config_managers(). This is the source of truth for availability: a manager defined in a config the eager pre-load could not reach (a URL, a custom path) still works from here, it just does not get a dedicated CLI flag.- Return type:
- meta_package_manager.config.discover_config_definitions(pool)[source]ΒΆ
Eagerly read new-manager definitions before the CLI group is built.
Best-effort and local-only: any error (no config, parse failure, missing reader) yields no definitions so CLI startup never breaks. URL configs are deferred to the authoritative
register_config_managers_from_context()pass. Supports both the standalone[mpm.managers]layout and[tool.mpm.managers]inpyproject.toml.
- meta_package_manager.config.register_eager_config_managers(pool)[source]ΒΆ
Register config-defined managers before the CLI group is constructed.
Called from
__main__.main()ahead of importing the Click group, so the dynamic--<id>/--no-<id>selectors enumerate the augmented pool and config-defined managers become first-class flags alongside the built-ins.- Return type:
meta_package_manager.definitions moduleΒΆ
Declarative package managers: the TOML schema and its class factory.
A [mpm.managers.<id>] configuration section describes a manager as data. This
module owns everything that turns such a description into a live
PackageManager subclass:
the schema vocabulary: which manager attributes a section may set, both on a shipped manager (
OVERRIDABLE_FIELDS) and on a brand-new definition (DEFINITION_CLI_FIELDS, the operations DSL constants);the validation and parsing layer (
parse_manager_definition()), shared by--validate-configand the runtime registration path so a config that survives one survives the other;the class factory (
build_manager_class()), which synthesizes aConfigDrivenManagersubclass implementing exactly the operations the definition declares;the bundled-definition loader (
load_bundled_definitions(),build_bundled_managers()): mpm ships some managers as*.tomlpackage data undermeta_package_manager/managers/, each a single[mpm.managers.<id>]section in the exact schema a user would write.
The runtime policy around definitions stays in
meta_package_manager.config: where sections may be loaded from, the
trust gate on local files, the override-application pass and the registration
passes wired into the CLI. The split keeps this module dependent on
meta_package_manager.manager only, so the configuration layer can build
on it without a circular import.
- meta_package_manager.definitions.OVERRIDABLE_FIELDS: Final[Mapping[str, Callable[[Any], Any]]] = {'cli_names': <function _to_str_tuple>, 'cli_search_path': <function _to_str_tuple>, 'dry_run': <function _to_bool>, 'extra_env': <function _to_str_dict>, 'ignore_auto_updates': <function _to_bool>, 'plan': <function _to_bool>, 'post_args': <function _to_str_tuple>, 'pre_args': <function _to_str_tuple>, 'pre_cmds': <function _to_str_tuple>, 'requirement': <function _to_str>, 'stop_on_error': <function _to_bool>, 'sudo': <function _to_bool>, 'timeout': <function _to_int>, 'unmaintained': <function _to_bool>, 'version_cli_options': <function _to_str_tuple>, 'version_regexes': <function _to_str_tuple>}ΒΆ
Per-manager attributes a user is allowed to override from the
[mpm.managers.<id>]configuration section.Each entry maps a
meta_package_manager.manager.PackageManagerattribute name to a converter that validates the raw TOML value and returns the value as the attributeβs expected runtime type. Lists are coerced into tuples to match the attributesβ tuple types.Note
id,name,platforms,homepage_urlandvirtualare intentionally excluded: they are identity, lookup or platform-classification attributes that the poolβs registration relies on. Phase 1 of TOML-driven configuration only exposes attributes whose runtime override is safe.
- meta_package_manager.definitions.VALID_PLATFORM_TOKENS: Final[frozenset[str]] = frozenset({'aix', 'all_agents', 'all_architectures', 'all_arm', 'all_ci', 'all_mips', 'all_platforms', 'all_shells', 'all_sparc', 'all_terminals', 'all_traits', 'all_windows', 'almalinux', 'alpine', 'altlinux', 'amzn', 'android', 'arch', 'arch_32_bit', 'arch_64_bit', 'big_endian', 'bourne_shells', 'bsd', 'bsd_without_macos', 'buildroot', 'c_shells', 'cachyos', 'centos', 'chromeos', 'clearlinux', 'cloudlinux', 'cygwin', 'debian', 'dragonfly_bsd', 'endeavouros', 'exherbo', 'fedora', 'freebsd', 'generic_linux', 'gentoo', 'gpu_terminals', 'guix', 'haiku', 'hurd', 'ibm_mainframe', 'ibm_powerkvm', 'illumos', 'kali', 'kvmibm', 'linux', 'linux_layers', 'linux_like', 'linuxmint', 'little_endian', 'loongarch', 'macos', 'mageia', 'mandriva', 'manjaro', 'midnightbsd', 'multiplexers', 'native_terminals', 'netbsd', 'nixos', 'nobara', 'openbsd', 'opensuse', 'openwrt', 'oracle', 'os400', 'other_posix', 'other_shells', 'parallels', 'pidora', 'pikaos', 'powerpc', 'raspbian', 'rhel', 'riscv', 'rocky', 'scientific', 'slackware', 'sles', 'slitaz', 'solaris', 'sourcemage', 'sunos', 'system_v', 'tuxedo', 'ubuntu', 'ultramarine', 'unix', 'unix_layers', 'unix_without_macos', 'void', 'web_terminals', 'webassembly', 'windows', 'windows_shells', 'wsl1', 'wsl2', 'x86', 'xenserver'})ΒΆ
Platform and group IDs accepted in a definitionβs
platformslist.Union of every
extra_platforms.PlatformID and every group ID, so both a specific platform (ubuntu) and a group (linux,all_platforms) resolve.
- meta_package_manager.definitions.DEFINITION_CLI_FIELDS: Final[Mapping[str, Callable[[Any], Any]]] = {'brewfile_entry_type': <function _to_str>, 'brewfile_skip_warning': <function _to_str>, 'cli_names': <function _to_str_tuple>, 'cli_search_path': <function _to_str_tuple>, 'default_sudo': <function _to_bool>, 'extra_env': <function _to_str_dict>, 'internal_sudo': <function _to_bool>, 'maintenance_note': <function _to_str>, 'post_args': <function _to_str_tuple>, 'pre_args': <function _to_str_tuple>, 'pre_cmds': <function _to_str_tuple>, 'requirement': <function _to_str>, 'timeout': <function _to_int>, 'unmaintained': <function _to_bool>, 'unmaintained_message': <function _to_str>, 'version_cli': <function _to_str>, 'version_cli_options': <function _to_str_tuple>, 'version_regexes': <function _to_str_tuple>}ΒΆ
CLI-execution attributes a definition may set, mostly reusing the override converters.
The runtime-preference fields (
dry_run,ignore_auto_updates,plan,stop_on_error) are excluded: they are command-line/global concerns, not part of a managerβs identity, and resolve through the usual option precedence.unmaintainedis reused from the override converters so a TOML-defined manager can flag its own upstream as abandoned (seedocs/cooldown.mdfor the affected managers).Seven fields are definition-only:
brewfile_entry_typemaps the manager onto a Homebrew Bundle DSL entry so its installed packages joinmpm dump --brewfileexports (seebrewfile_entry_type).brewfile_skip_warningis the message emitted when the managerβs packages are deliberately left out of such an export (seebrewfile_skip_warning).default_sudois the managerβs built-in escalation policy (seedefault_sudo). Operations markedsudo = trueescalate by default, while the userβs global--no-sudoflag or asudooverride still win.internal_sudomarks a manager whose CLI invokessudoitself mid-run (seeinternal_sudo). mpm never wraps its commands insudo; priming instead reuses a warm credential cache for these internal escalations. Seedocs/sudo.md.maintenance_noterenders a{note}admonition atop the managerβs page for a still-maintained upstream under watch (seemaintenance_note).unmaintained_messagedocuments an abandoned upstream, rendering a{warning}admonition and theβ οΈtable markers (seeunmaintained_message).version_clinames an alternate binary for the version probe (seeversion_cli), for suites whose own binaries expose no version flag (OpenBSDβspkg_add).
- meta_package_manager.definitions.DEFINITION_IDENTITY_FIELDS: Final[frozenset[str]] = frozenset({'homepage_url', 'name', 'operations', 'platforms'})ΒΆ
Top-level keys of a definition section that are not CLI-execution fields.
- meta_package_manager.definitions.QUERY_OPERATIONS: Final[frozenset[str]] = frozenset({'installed', 'orphans', 'outdated', 'search'})ΒΆ
Operations that parse the commandβs stdout into packages.
- meta_package_manager.definitions.COMMAND_OPERATIONS: Final[frozenset[str]] = frozenset({'cleanup_cache', 'cleanup_orphan', 'cleanup_repair', 'doctor', 'install', 'remove', 'remove_orphan', 'sync', 'upgrade_all', 'upgrade_one'})ΒΆ
Operations that only run a command and produce no inventory to parse.
cleanupitself is deliberately absent: it is not an operation a manager defines anymore, but the fixed composition of the declared cleanup categories (seemeta_package_manager.manager.PackageManager.cleanup()). A definition declaring it is rejected with a targeted error.
- meta_package_manager.definitions.ALL_DEFINITION_OPERATIONS: Final[frozenset[str]] = frozenset({'cleanup_cache', 'cleanup_orphan', 'cleanup_repair', 'doctor', 'install', 'installed', 'orphans', 'outdated', 'remove', 'remove_orphan', 'search', 'sync', 'upgrade_all', 'upgrade_one'})ΒΆ
Every operation name a definition may declare.
- meta_package_manager.definitions.RECOGNIZED_PARSE_FIELDS: Final[frozenset[str]] = frozenset({'installed_version', 'latest_version', 'package_id'})ΒΆ
Named regex groups / JSON field keys a query parser may map to a package.
- meta_package_manager.definitions.REQUIRED_PARSE_FIELDS: Final[Mapping[str, frozenset[str]]] = {'installed': frozenset({'package_id'}), 'orphans': frozenset({'package_id'}), 'outdated': frozenset({'latest_version', 'package_id'}), 'search': frozenset({'package_id'})}ΒΆ
Parse fields each query operation must extract to be useful.
installedneeds only the package ID: some tools genuinely track no per-package version (Clear Linux bundles underswupd, Cygwin listings underapt-cyg), and mpmβs package model treats the installed version as optional everywhere.outdatedwithout alatest_versionwould report nothing actionable, so there the version capture stays mandatory.
- meta_package_manager.definitions.OPERATION_ARG_PLACEHOLDER: Final[Mapping[str, str]] = {'install': 'package_id', 'remove': 'package_id', 'remove_orphan': 'package_id', 'upgrade_one': 'package_id'}ΒΆ
Placeholder each operationβs
argsmust reference, so a value is actually passed to the CLI (aremovewith no{package_id}would target nothing).searchis deliberately absent: its{query}placeholder is optional. A tool with no real search command can still declare the operation by listing its whole catalog (opkg list,swupd bundle-list --all) and lettingmeta_package_manager.manager.PackageManager.refiltered_search()narrow the results, mirroring the search-from-scratch augmentation some built-in managers use.
- meta_package_manager.definitions.SEARCH_REFINEMENT_KEYS: Final[frozenset[str]] = frozenset({'exact_args', 'extended_args', 'id_name_only_args'})ΒΆ
Optional per-refinement argument templates of the
searchoperation.Each key holds the CLI arguments spliced into the
argstemplate β at the position of the matching{exact_args}-style marker β when the refinement is active:exact_argsfor an--exactsearch,extended_argsfor an--extendedone, andid_name_only_argsfor the default ID/name-restricted mode (mpmβs--id-name-only, for tools like Chocolatey whose unrestricted search is the default and take a flag to narrow it). An inactive refinement expands its marker to nothing.Declaring a key advertises native support for the matching mpm flag (
exact_argssets thesearchmethodβsexact_supportintrospection attribute, either of the other two setsextended_support), which feeds the augmentations documentation.meta_package_manager.manager.PackageManager.refiltered_search()still refines the results client-side either way, exactly as for the built-in managers.
- meta_package_manager.definitions.ALLOWED_ARG_PLACEHOLDERS: Final[Mapping[str, frozenset[str]]] = {'install': frozenset({'package_id'}), 'remove': frozenset({'package_id'}), 'remove_orphan': frozenset({'package_id'}), 'search': frozenset({'exact_args', 'extended_args', 'id_name_only_args', 'query'}), 'upgrade_one': frozenset({'package_id'})}ΒΆ
Placeholders each operationβs
argsmay reference.Operations absent from this mapping take no placeholder at all. Any
{token}outside the operationβs set is rejected at parse time: a typoed{qeury}would otherwise reach the CLI as a literal argument and fail in silent, tool-specific ways.
- meta_package_manager.definitions.ARG_PLACEHOLDER_REGEX: Final = re.compile('\\{([a-z_]+)\\}')ΒΆ
Match
{placeholder}tokens in an operationβs args, for validation.
- meta_package_manager.definitions.QUERY_OPERATION_KEYS: Final[frozenset[str]] = frozenset({'args', 'cli', 'fields', 'format', 'list_path', 'regex', 'sudo'})ΒΆ
Keys allowed in a query operationβs table.
cliis the same alternate-binary hook as on command operations.sudo = truemarks the query as privileged, for the rare tool that gates even its read-only listings behind root (deb-getβs upgradable check); escalation then follows the usual per-manager policy.
- meta_package_manager.definitions.SEARCH_OPERATION_KEYS: Final[frozenset[str]] = frozenset({'args', 'cli', 'exact_args', 'extended_args', 'fields', 'format', 'id_name_only_args', 'list_path', 'regex', 'sudo'})ΒΆ
Keys allowed in the
searchoperationβs table: a query operation plus the per-refinement argument templates ofSEARCH_REFINEMENT_KEYS.
- meta_package_manager.definitions.COMMAND_OPERATION_KEYS: Final[frozenset[str]] = frozenset({'args', 'cli', 'sudo'})ΒΆ
Keys allowed in a command operationβs table.
clinames an alternate binary for this operation, resolved on the search path at call time: it lets one definition span sibling binaries (urpmqquerying whileurpmiinstalls).sudo = truemarks the operation as privileged, mirroring thesudo=Trueflag built-in managers pass torun_cli: escalation then follows the per-manager policy (the definitionβsdefault_sudo, overridden by the userβs--sudo/--no-sudo).
- class meta_package_manager.definitions.OperationSpec(args, cli=None, sudo=False, exact_args=None, extended_args=None, id_name_only_args=None, parse_mode='none', regex=None, list_path=None, fields=None)[source]ΒΆ
Bases:
objectDeclarative specification of one operation of a config-defined manager.
- args: tuple[str, ...]ΒΆ
CLI arguments appended after the resolved binary, before
post_args.May embed the
{package_id}and{query}placeholders, substituted at call time.{version}is intentionally unsupported: config-defined managers do not pin versions (see_make_install()).
- cli: str | None = NoneΒΆ
Alternate binary name for this operation, or
Nonefor the managerβs maincli_path.Resolved with
which()at call time, so one definition can span sibling binaries (urpmi/urpme/urpmq,cast/dispel/gaze). The operation fails withFileNotFoundErrorwhen the binary is missing rather than silently falling back to the main CLI.
- sudo: bool = FalseΒΆ
Mark the operation as privileged, mirroring the
sudo=Trueflag built-in managers pass torun_cli().Escalation still follows the per-manager policy: the definitionβs
default_sudo, overridden by the userβs--sudo/--no-sudo. Command operations are the usual bearers; a query may also set it, for the rare tool that gates its read-only listings behind root (deb-get).
- exact_args: tuple[str, ...] | None = NoneΒΆ
Arguments spliced at the
{exact_args}marker of asearchβsargswhen an exact match is requested, orNonewhen the tool has no native exact mode. SeeSEARCH_REFINEMENT_KEYS.
- extended_args: tuple[str, ...] | None = NoneΒΆ
Arguments spliced at the
{extended_args}marker of asearchβsargswhen the extended (description-reaching) mode is requested, orNonewhen the tool has no native switch for it. SeeSEARCH_REFINEMENT_KEYS.
- id_name_only_args: tuple[str, ...] | None = NoneΒΆ
Arguments spliced at the
{id_name_only_args}marker of asearchβsargswhen the default ID/name-restricted mode is requested, for tools whose unrestricted search is the default (Chocolateyβs--by-id-only), orNone. SeeSEARCH_REFINEMENT_KEYS.
- parse_mode: str = 'none'ΒΆ
How to turn the commandβs stdout into packages:
"regex"(per-line named groups),"json"(structured extraction), or"none"for command-only operations that produce no inventory (install, remove, sync, β¦).
- regex: str | None = NoneΒΆ
Regular expression matched against each stdout line in
"regex"mode.Recognized named groups:
package_id(required),installed_versionandlatest_version(optional). Compiled withre.MULTILINE.
- class meta_package_manager.definitions.ManagerDefinition(manager_id, name, platforms, homepage_url, cli_fields, operations)[source]ΒΆ
Bases:
objectA brand-new package manager declared from a
[mpm.managers.<id>]section.Produced by
parse_manager_definition()after validation, consumed bybuild_manager_class().- platforms: tuple[str, ...]ΒΆ
Platform and group ID strings, resolved to
extra_platforms.Platformmembers at build time.
- cli_fields: dict[str, object]ΒΆ
Overridable CLI-execution attributes (
cli_names,requirement,version_regexes, β¦), pre-coerced to their runtime types.
- operations: dict[str, OperationSpec]ΒΆ
Declared operations keyed by name (
installed,install, β¦).
- class meta_package_manager.definitions.ConfigDrivenManager[source]ΒΆ
Bases:
PackageManagerBase class for managers synthesized from configuration.
Carries no operation methods on purpose: only the dynamically-created subclass returned by
build_manager_class()defines the operations the user actually declared, someta_package_manager.capabilities.implements()reports an accurate capability set. Defining an operation here would make every config-defined manager falsely advertise it.Exists mainly as a marker (
isinstance(manager, ConfigDrivenManager)distinguishes user-defined managers from built-ins) and as a shared home for any future config-driven behavior.Initialize
cli_errorslist.- cli_names: tuple[str, ...] = ('configdrivenmanager',)ΒΆ
List of CLI names the package manager is known as.
This list of recognized CLI names is ordered by priority. That way we can influence the search of the right binary.
- ..hint::
This was helpful in the case of the Python transition from 2.x to 3.x, where multiple versions of the same executable were named
pythonorpython3.
By default, this propertyβs value is derived from the managerβs ID (see the
MetaPackageManager.__init__method above).
- id: str = 'configdrivenmanager'ΒΆ
Package managerβs ID.
Derived by defaults from the lower-cased class name in which underscores
_are replaced by dashes-.This ID must be unique among all package manager definitions and lower-case, as theyβre used as feature flags for the mpm CLI.
- name: str = 'ConfigDrivenManager'ΒΆ
Return package managerβs common name.
Default value is based on class name.
- virtual: bool = FalseΒΆ
Should we expose the package manager to the user?
Virtual package manager are just skeleton classes used to factorize code among managers of the same family.
- definition_source: str | None = NoneΒΆ
Repo-relative path to the bundled TOML file this manager was defined in.
Set by
build_bundled_managers()for the managers mpm ships as package data; staysNonefor a manager defined in a userβs own configuration file. The documentation generator links a bundled managerβs benchmark entry to this file, a config-defined manager having no Python source line to point at.
- meta_package_manager.definitions.parse_manager_definition(manager_id, section)[source]ΒΆ
Validate and parse one
[mpm.managers.<id>]definition section.Returns a
ManagerDefinitionready forbuild_manager_class(). Raisesclick_extra.ValidationError(path relative to the[mpm.managers]root) on any problem, so the same function backs both--validate-configand the runtime registration path.- Return type:
- meta_package_manager.definitions.build_manager_class(definition)[source]ΒΆ
Synthesize a
PackageManagersubclass from a validated definition.Assembles a class namespace from the definitionβs identity and CLI fields, then adds one method (or property) per declared operation. Only the declared operations land in the namespace, so
meta_package_manager.capabilities.implements()reflects exactly what the user configured. Single- and all-package upgrades map toupgrade_one_cli()/upgrade_all_cli()so the inheritedupgrade()orchestrator drives them, just like the built-in managers.- Return type:
- meta_package_manager.definitions.BUNDLED_DEFINITIONS_PACKAGE: Final[str] = 'meta_package_manager.managers'ΒΆ
Import package whose
*.tomlresources hold mpmβs bundled manager definitions.
- meta_package_manager.definitions.load_bundled_definitions()[source]ΒΆ
Parse every bundled
[mpm.managers.<id>]definition shipped as package data.Reads each
*.tomlresource ofBUNDLED_DEFINITIONS_PACKAGEviaimportlib.resources(so it works the same from an unpacked install, a zip or a Nuitka onefile), and validates every section withparse_manager_definition(). Returns(definition, source)pairs, wheresourceis the repo-relative path used to link the managerβs documentation. Cached because the shipped files never change at runtime.A malformed bundled file is a packaging bug, but it is logged and skipped rather than raised so one bad resource cannot break
mpmstartup for everyone. The hermetictest_bundled_inventoryandtest_bundled_registeredkeep the shipped files valid.- Return type:
- meta_package_manager.definitions.bundled_manager_ids()[source]ΒΆ
IDs of the managers mpm ships as bundled configuration definitions.
- meta_package_manager.definitions.build_bundled_managers()[source]ΒΆ
Instantiate every bundled definition into a live, pool-ready manager.
Each
ConfigDrivenManagersubclass records the TOML file it came from inConfigDrivenManager.definition_source, so the documentation generator can link to it. Called once bymeta_package_manager.pool.ManagerPool.register.- Return type:
meta_package_manager.dispatch moduleΒΆ
Cross-manager dispatch: scheduling many package managers at once.
Where meta_package_manager.execution runs one managerβs CLI in one
subprocess, this module schedules many managers concurrently: the job-count
policy that decides sequential-vs-concurrent (effective_jobs()), the
up-front availability probe used during selection (warm_availability()),
the two progress-wrapped fan-out primitives the CLI subcommands drive
(collect_from_managers(), collect_per_package()) with their shared
dispatch() engine, the backend-lock catalog that serializes conflicting
managers (SHARED_LOCK_FAMILIES and merge_into_lock_lanes()), and
the manager-bound β/β ledger (OperationTrail) that the
concurrent and sequential paths both report through.
The generic layers live upstream in click-extra: the concurrency primitives in
click_extra.execution (run_jobs/run_lanes driven by
mpm --jobs) and the batch-reporting trail in click_extra.spinner
(OperationTrail with its
trail_glyph/trail_line atoms). This module keeps what is package-manager
policy: which managers must never overlap, how the trail binds to the poolβs
--progress state, and when a batch collapses to a sequential pass.
- meta_package_manager.dispatch.SHARED_LOCK_FAMILIES: Final[tuple[frozenset[str], ...]] = (frozenset({'apt', 'apt-mint', 'deb-get'}), frozenset({'brew', 'cask'}), frozenset({'dnf', 'dnf5', 'yum', 'zypper'}), frozenset({'pacman', 'pacstall'}))ΒΆ
Managers that contend for one shared backend lock, grouped by backend.
Different managers are otherwise independent processes over disjoint state, so running them in parallel is safe. The exception is a handful that drive a shared backend and serialize on its lock:
apt,apt-mintanddeb-getall reach dpkg (/var/lib/dpkg/lock).brewandcaskare the same brew binary and serialize on Homebrewβs own update lock: two concurrentbrew update(which mpm sync issues identically for both, as the formula/cask split does not apply to it) collide, one failing with βAnother active Homebrew update process is already runningβ.dnf,dnf5,yumandzypperall reach the RPM database.pacmanandpacstallall reach the pacman database (/var/lib/pacman/db.lck).
Concurrency is safe across families and unsafe within one, just as it is unsafe within a single manager (which is why a managerβs own packages stay serial). When two members run at once the shared lock makes them block or fail, never corrupt.
Enforced for the mutating fan-outs only:
merge_into_lock_lanes()collapses each familyβs members into a singledispatch()lane, so they run serially while distinct families still run in parallel. The read-only queries (installed/outdated/search) take no backend lock, so they keep one lane per manager and stay fully concurrent. Members of a lane also share a command cache (seeCLIExecutor.run_cache), so two that resolve to a byte-identical invocation (brewandcaskforsyncandcleanup) run the subprocess once.Adding a newly-conflicting set of managers is a one-line edit here: append a
frozensetof their ids and both the serialization and the cache pick it up.
- meta_package_manager.dispatch.effective_jobs(ctx, count)[source]ΒΆ
Resolve how many worker threads to use for a batch of
countitems.Thin wrapper over
click_extra.execution.resolve_jobs()pinning mpmβs policy: always collapse to a single (sequential) worker atDEBUGverbosity, where coherent per-manager log narration matters more than the speed-up (interleaved threads would scramble it). The base helper also collapses to sequential with no active CLI context, for a single item, or atmpm --jobs1; otherwise thempm --jobsvalue wins, capped atcount(no point spinning up more workers than there are items).- Return type:
- meta_package_manager.dispatch.warm_availability(managers)[source]ΒΆ
Probe several managersβ
availableconcurrently.Reading
availableforces a managerβs--versiondetection, whose result (and thecli_path/executable/versionit depends on) is cached on the instance. Warming the candidate set up front turns the sequential string of probes into a single round bounded by the slowest one, shaving startup latency off any command that touches many managers.Each manager is a distinct instance with its own cached attributes and subprocess, so the probes are independent and thread-safe; the GIL is released while each waits. The executor barrier publishes every cached value before the caller reads it back.
Sized by
effective_jobs(): a no-op (leaving the probes to lazy, sequential evaluation) without an active context, atDEBUGverbosity, for a single candidate, or atmpm --jobs1.- Return type:
- class meta_package_manager.dispatch.OperationTrail(managers, *, label='', unit='', total=0, jobs=1, coverage=False)[source]ΒΆ
Bases:
OperationTrailclick_extra.spinner.OperationTrailbound to the manager pool.The upstream class owns the two renderings (sequential echoed lines, or one aggregate indicator with buffered-then-streamed lines) and the interactive gating; this subclass supplies mpmβs policy around it:
Enablement follows ``βprogress``, folded into each managerβs
progressflag by the CLI (a TTY, no serialized output, not atDEBUGverbosity): any enabled manager turns the trail on, auto-gated on an interactive stderr.A concurrent batch mutes the managersβ own per-call spinners (which would collide on stderr) for the duration of the aggregate one.
A concurrent batchβs aggregate indicator is a determinate progress bar, not an indeterminate spinner: every
dispatch()batch counts its work up front (one task per manager, or per package-manager pair), so the bar always has a length to render against.``coverage`` keeps the read-command semantics: their result table is the real output and each manager keeps its per-call spinner, so the sequential rendering stays silent (upstreamβs
echo_sequential=False).
The ordering-bound sequential state changers (
installβs priority search) construct it bare; everydispatch()batch drives it as a context manager.- Parameters:
managers (
Iterable[PackageManager]) β the batchβs managers, read for the--progressgate and (when concurrent) to mute their per-call spinners.label (
str) β present-tense verb for the running indicator (βSearchingβ).unit (
str) β the noun counted in the indicator tally (βmanagersβ, βpackagesβ).total (
int) β how many outcomes are expected, for thedone/totalcount and the progress barβs length.jobs (
int) β the worker count fromeffective_jobs();> 1selects the concurrent rendering.coverage (
bool) β when set, a sequential run stays silent (the caller has another output, its result table). Unused when concurrent.
Configure (but do not start) the trail.
- Parameters:
label (
str) β present-tense verb for the running aggregate indicator ("Fetching"), composed into its{label} {done}/{total} {unit}tally.unit (
str) β the noun counted in the tally ("files","feeds").total (
int) β how many outcomes are expected, for thedone/totalcount.jobs (
int) β the batchβs worker count;> 1selects the concurrent rendering (one aggregate spinner),<= 1the sequential one (plain echoed lines).spinner β a
SpinnerPresetfrom theSPINNERScatalog (spinner=SPINNERS["moon"]) for the concurrent aggregate spinner. Ignored by the sequential and progress-bar renderings, and mutually exclusive withprogress_bar.progress_bar β render the aggregate indicator as a determinate
click.progressbar()instead of a spinner, for a sequential or concurrent batch alike. Requires a positivetotal(a bar needs a length) and is mutually exclusive withspinner.timer β append each operationβs and the batchβs elapsed time to the trail lines and the finisher.
None(the default) follows the CLIβs--time/--no-timeflag;Trueforces timing on withformat_duration()βs compact clock, a callable(seconds: float) -> strforces it on with a custom format, andFalseforces it off. Per-operation times come from asecondsargument tomark(), filled in automatically by anoperation()handle.clock β whether a running aggregate indicator shows elapsed time (
"elapsed", the default: a stopwatch counting up, visible from the start) or remaining time ("eta": an estimate from the batchβs rate, appearing only once an outcome lets it be computed). Both the progress bar and the concurrent spinner honor"eta"(the spinner reuses Clickβs progress-bar estimate, since the trail knows itstotal). Per-operation and finisher times are always elapsed.enabled β force the trail on or off.
None(the default) auto-detects: the sequential echo renders only on an interactive stream, and the aggregate indicator applies its own TTY gate.echo_sequential β whether a sequential batch echoes its outcome lines and finisher at all. Turn it off when the batch has another output that is the real product (a result table) and the trail would be noise; an aggregate indicator is unaffected.
delay β seconds before the aggregate indicator first draws: a fast batch then completes without ever flashing one.
stream β where to render; defaults to
sys.stderrso the trail never mixes intostdoutdata.
- Raises:
ValueError β if
progress_baris set without a positivetotal, or together withspinner, or ifclockis neither"elapsed"nor"eta".
- meta_package_manager.dispatch.dispatch(label, done_label, unit, lanes, *, coverage=False, ctx=None)[source]ΒΆ
Fan a set of work lanes out across managers, narrating a
β/βtrail.The single scheduling primitive behind both
collect_from_managers()andcollect_per_package(). A lane is one or more managers paired with a list of callables; lanes run concurrently (one worker each) while a laneβs own callables run serially, because a package manager cannot safely run two of its own invocations at once, nor can two managers sharing a backend lock (seeSHARED_LOCK_FAMILIES). A lane usually wraps a single manager;merge_into_lock_lanes()is what bundles a whole lock family into one, and such a lane also gets a shared command cache (seeCLIExecutor.run_cache) so its members collapse identical invocations.Each callable does its work, records its own outcome (output to
INFO, failures into a caller-owned list) and returns(ok, message)for the trail. The whole batch reports through oneOperationTrail: a per-outcomeβ/βline plus a finisher, behind a single aggregate progress bar when concurrent (a slow batch on a terminal) and silent otherwise.Concurrency is sized by
effective_jobs()(driven bympm --jobs): it collapses to a sequential pass β preserving each managerβs own per-call spinner β for a single lane, at--jobs 1, or atDEBUGverbosity.- Parameters:
coverage (
bool) β forwarded toOperationTrail. Read commands set it (their result table is the output, so the sequential pass stays silent and the finisher reports coverage,{done_label} N {unit}, alwaysβ). Maintenance and state-changing commands leave itFalse(the trail is their output, so the finisher reports the success count,{done_label} N/M {unit},βon any failure).ctx (
Context|None) β the active click context, read only to size concurrency (effective_jobs()). Defaults to the current context, so a command need not thread it; tests pass an explicit stand-in.
- Return type:
- meta_package_manager.dispatch.merge_into_lock_lanes(pairs)[source]ΒΆ
Group
(manager, task)pairs intodispatch()lanes, one per lock family.Managers sharing a
SHARED_LOCK_FAMILIESentry collapse into a single lane so their tasks run serially (the lane isdispatch()βs unit of mutual exclusion), while unrelated managers each keep their own lane and run concurrently. A manager not in any family keys on its own id, so its tasks still group together (a managerβs own invocations cannot overlap either). First-seen order is preserved, both across lanes and within a laneβs task list.Used by the mutating fan-outs only: the state changers through
collect_per_package(), andsync/cleanup/upgrade --allthroughcollect_from_managers(). The read commands take no backend lock and skip this, keeping one lane per manager.
- meta_package_manager.dispatch.collect_from_managers(label, done_label, managers, work, *, report_state=False, ctx=None)[source]ΒΆ
Run
work(manager)for every manager concurrently, results in input order.The fan-out primitive for the read-only commands (
installed/outdated/search) and the independent maintenance commands (sync/cleanup/upgrade --all). It adapts each manager into adispatch()unit that runsworkand stashes the(id, data)result in input position, so the returned list mirrorsmanagersregardless of completion order. The maintenance commands (report_state) then merge lock-family members into shared serial lanes (merge_into_lock_lanes()); the read commands keep one lane per manager.workreturns this managerβs(id, data); it must handle its ownmeta_package_manager.execution.CLIError(each manager owns its subprocess and error list, so the call is thread-safe per manager). A truthydata["errors"](ordata["failed"]) marks that managerβs trail lineβ; an optionaldata["label"]overrides its text (upgrade --alluses it for cooldown skips).- Parameters:
report_state (
bool) β maintenance commands set it (their only output is the trail). It flips the finisher to a success count, keeps the trail in the sequential fallback, and turns on lock-family serialization. Read commands leave itFalse: their table is the output, so the sequential fallback is silent and the finisher reports coverage. Passed todispatch()as the inverse ofcoverage.- Return type:
- meta_package_manager.dispatch.collect_per_package(label, done_label, tasks, *, ctx=None)[source]ΒΆ
Run per-package operations across managers concurrently, serial within each.
The fan-out primitive for the ordering-free state changers that act on many (package, manager) pairs:
remove,upgrade <packages>,restoreand the manager-tied specs ofinstall. Takes a flat list of(manager, task)pairs and groups them into lanes by lock family (merge_into_lock_lanes()) β so a managerβs own packages, and any lock-family peers, stay serial while unrelated managers run in parallel β then drivesdispatch(). Each task returns(ok, message)after doing its CLI call and recording its own outcome. The unmatched-package priority search ofinstallis not routed here: it has genuine cross-manager ordering (stop at the first manager that has the package) and stays sequential on its own.- Return type:
- meta_package_manager.dispatch.warn_jobs_ignored(ctx)[source]ΒΆ
Note that
--jobsdoes not parallelize this run.Only
installwith at least one untied package reaches this: those packages need a priority search (install with the first manager that has the package, skip the rest), which is cross-manager-sequential, so the whole command runs serially. The other state changers (remove,upgrade <packages>,restore, andinstallof fully manager-tied specs) now fan out throughcollect_per_package(). When the user explicitly raisedmpm --jobsabove1, say so once atINFO: the request simply has no effect on this run, which is narration, not a problem.- Return type:
meta_package_manager.docstring_corpus moduleΒΆ
Harvest the CLI-session samples documented in manager source docstrings.
Every query method (and the version_regexes attribute) documents a sample
invocation and its output in a MyST {code-block} shell-session fence
sitting right next to the regex (or JSON parser) that consumes it. This module
reads those blocks straight from the source and exposes them, with a shared
notion of which ones are literal, replayable fixtures.
It has two consumers:
tests.test_docstring_corpusreplays each literal block back through the parser it illustrates, asserting the documented example still yields well-formed packages.meta_package_manager._docsrenders the literal blocks as the reference traces of a class-based managerβs documentation page, the config-defined twin of the[samples]fixtures shipped alongside TOML-defined managers.
Blocks are harvested from raw source, not the escape-processed __doc__:
inspect.cleandoc() expands tabs and the compiler collapses \\, either
of which would rewrite a tab-delimited or escaped-JSON fixture into
something the parser rejects. Everything here reads static source through
ast/inspect, so it is host-independent: safe to call at
documentation build time on any machine.
- meta_package_manager.docstring_corpus.FENCE_OPENERS = ('```{code-block} shell-session', '```{code-block} pwsh-session')ΒΆ
MyST fence openers introducing a captured CLI session.
PowerShell sessions use
>as their prompt, which the dissector already recognizes, so both flavors share one extraction path.Important
These two openers are the fixture fences: every
installed/outdated/orphans/version_regexesblock written under one is a complete sample that must parse (the corpus round-trip enforces it) and is rendered as a reference trace. An illustration that is not a literal fixture (a human-readable variant, an interactive prompt, a narrative before/after transcript) uses a non-harvested{code-block} consolefence instead, so it stays out of the corpus and the traces while still rendering in the API docs.
- meta_package_manager.docstring_corpus.extract_blocks(docstring)[source]ΒΆ
Return the dedented body of every
shell-sessionfence in a docstring.A fence body runs from the opener to the first closing `
` ` line, and shares the fence's indentation. The blank line the MyST syntax puts between a ``{code-block}opener and its content is stripped along with the common indentation.
- meta_package_manager.docstring_corpus.dissect(block)[source]ΒΆ
Split a shell-session block into its command tokens and its output.
$starts a command and>continues it (the shellβs secondary prompt). A command may also continue onto unprefixed lines via a trailing backslash, so those are absorbed too. Every remaining line is output.
- meta_package_manager.docstring_corpus.split_session(block)[source]ΒΆ
Return just the command output of a shell-session block.
- Return type:
- meta_package_manager.docstring_corpus.block_commands(block)[source]ΒΆ
Return each documented command of a block as its own token list.
Unlike
dissect(), which pools every command of a block, this keeps commands separate so a block documenting several invocations (anaptcleanup runningautoremovethenautoclean) yields one list each. Prompt flavor is per-block:$-primary with>continuations for shell sessions,>-primary for PowerShell sessions.
- meta_package_manager.docstring_corpus.block_language(block)[source]ΒΆ
Return the fenced-code language matching a blockβs prompt flavor.
A shell session opens on a
$prompt, a PowerShell session on>. The documented reference traces are re-fenced with the flavor they were captured under so their prompts keep highlighting correctly.- Return type:
- meta_package_manager.docstring_corpus.class_blocks(cls)[source]ΒΆ
Map
{member: [blocks]}kept in raw source form for the corpus.Escapes and tabs survive verbatim so the round-trip feeds each block to the parser exactly as the CLI emits it. Rendered documentation wants the terminal-facing form instead: see
class_display_blocks().
- meta_package_manager.docstring_corpus.class_display_blocks(cls)[source]ΒΆ
Map
{member: [blocks]}in compiled form for rendered documentation.The reference-traces generator reads these so a transcript shows single backslashes and resolved escapes, matching what a reader would see running the command, rather than the doubled source escapes
class_blocks()preserves for the parser.
- meta_package_manager.docstring_corpus.is_fixture(output)[source]ΒΆ
A block is a fixture when it carries sample output to parse.
A
shell-sessionblock showing only a command (no output, an empty system) illustrates an invocation but has nothing for a parser to consume, so it is not a fixture.- Return type:
- meta_package_manager.docstring_corpus.literal_blocks(cls, members)[source]ΒΆ
Return
(member, index, block)for a classβs replayable fixture blocks.A block qualifies when it carries sample output (
is_fixture()). The index is its position within the memberβs full block list. Blocks come in compiled, terminal-facing form (class_display_blocks()): the escape/tab differences from the raw corpus form never touch a directive, so the same blocks are selected either way.
meta_package_manager.execution moduleΒΆ
CLI-execution engine shared by every package manager.
Runs one managerβs CLI in one subprocess: the
meta_package_manager.execution.CLIExecutor mixin (which
meta_package_manager.manager.PackageManager inherits) locates the binary
and runs it, the meta_package_manager.execution.CLIError exception carries
a failed callβs result, and meta_package_manager.execution.highlight_cli_name()
themes a binaryβs name.
Scheduling many managers at once is the next altitude up, and lives in
meta_package_manager.dispatch: the concurrent fan-out primitives, the
lock families and the shared β/β trail. The sudo machinery that cuts
across both altitudes (credential priming, the keepalive, the hidden-prompt stall
watchdog) lives in meta_package_manager.sudo: this module only consumes
it, to wrap escalated commands and diagnose their failures.
Note
The name and intent mirror click_extra.execution from the sibling
click-extra project, where the
generic layers now live: the concurrency primitives (run_jobs/run_lanes
driven by mpm --jobs), the single-subprocess engine
(click_extra.execution.run_cli(), which disclosed invocations and
streams output to the logs), and the Ctrl+C machinery
(click_extra.execution.install_interrupt_handler() terminating the
in-flight children registered by run_cli). This module keeps what is
package-manager policy: per-operation timeouts, sudo escalation, cooldown
enforcement and dry-run.
- meta_package_manager.execution.DIAGNOSIS_TAIL_LINES: Final = 10ΒΆ
Trailing lines of a failed commandβs report relayed at
WARNING.CLIs conclude with their actual error, so the tail is where the diagnosis lives; the cap keeps a verbose failure (a source buildβs compiler spew) from flooding the default view. The raw streams are always available in full, live, at
DEBUG.
- exception meta_package_manager.execution.CLIError(code, output, error)[source]ΒΆ
Bases:
ExceptionAn error occurred when running package manager CLI.
The exception internally keeps the result of CLI execution.
- property diagnosis: strΒΆ
The commandβs own account of its failure, capped for log relay.
Prefers
<stderr>, the conventional stream for error reporting, and falls back on<stdout>for the tools that report failures there (steamcmd); a command that died silently is reduced to its exit code. Only the lastDIAGNOSIS_TAIL_LINESlines are kept, behind a counter of the truncated ones: errors conclude streams.
- meta_package_manager.execution.VERSION_PROBE: Final = 'version'ΒΆ
Pseudo-operation stamped on
CLIExecutor._active_operationduring version detection.Not a member of
meta_package_manager.capabilities.Operations(no subcommand routes it), but it participates in the same per-operation machinery:OPERATION_TIMEOUTSbinds it to the short read-only cap, andCLIExecutor.run()demotes its command disclosure toDEBUGso the per-candidate probes cannot drown theINFOnarration.
- meta_package_manager.execution.format_plan_command(cmd_args, extra_env=None)[source]ΒΆ
Render a captured
mpm --plancommand as a copy-pasteable shell line.Unlike
click_extra.execution.format_cli_prompt()(styled, and prefixed with a$prompt sigil for logs and dry-runs), this returns a plain, unstyled, shell-quoted line: the forced environment assignments followed by the resolved binary and its arguments, ready to paste into a terminal or pipe into a shell. See the plan-mode branch ofCLIExecutor.run().- Return type:
- meta_package_manager.execution.PLAN_RECORDER: Final = <meta_package_manager.execution._PlanRecorder object>ΒΆ
Process-wide sink for
CLIExecutor.run()βs plan-mode captures.A module-level singleton because
runexecutes in the fan-outβs worker threads, where the click context is not reliably reachable. See_PlanRecorder.
- meta_package_manager.execution.highlight_cli_name(path, match_names)[source]ΒΆ
Highlight the binary name in the provided
path.The name is only highlighted when it matches one of the recognized
match_names, so an unrecognized binary stays plain. Matching is insensitive to case on Windows and case-sensitive on other platforms, thanks toos.path.normcase.The rendering is delegated to
click_extra.execution.highlight_bin_name(), the same helper behind the$-prompt and spawn-trace log lines, so thempm managerstable and the logs can never drift apart.
- meta_package_manager.execution.READ_ONLY_TIMEOUT: Final = 120ΒΆ
Default timeout (seconds) for read-only probes and queries.
These operations only inspect state, so a short cap lets a wedged binary fail fast instead of stalling the whole run. The value is generous enough for legitimately slow scans (a freshly-pulled
guix searchwalking every packageβs metadata) while still being far belowMUTATING_TIMEOUT.
- meta_package_manager.execution.MUTATING_TIMEOUT: Final = 500ΒΆ
Default timeout (seconds) for operations that change system state.
Installs, upgrades, removals, channel syncs and cleanups routinely build from source, download large archives or pull entire channels, so they need a long cap. Kept identical to the historical global default so these operations behave exactly as before when no explicit
--timeoutis given.
- meta_package_manager.execution.DEFAULT_TIMEOUT: Final = 500ΒΆ
Fallback timeout (seconds) for a CLI call whose operation is unknown.
Defaults to the conservative
MUTATING_TIMEOUT: when in doubt, wait rather than risk killing a legitimate long-running command.
- meta_package_manager.execution.OPERATION_TIMEOUTS: Final[dict[str, int]] = {'cleanup': 500, 'doctor': 500, 'install': 500, 'installed': 120, 'orphans': 120, 'outdated': 120, 'remove': 500, 'search': 120, 'sync': 500, 'upgrade': 500, 'upgrade_all': 500, 'version': 120}ΒΆ
Per-operation timeout defaults, applied only when the user has set no explicit
--timeout(or per-managertimeoutoverride).Keyed by the
meta_package_manager.capabilities.Operationsmember name, plus the special"version"detection probe. The keys are validated against theOperationsenum by the test suite so the two never drift apart. An operation absent from this map resolves toDEFAULT_TIMEOUT.
- meta_package_manager.execution.SPINNER_DELAY: Final = 0.1ΒΆ
Seconds a CLI call must run before its progress spinner appears.
Kept short so the spinner surfaces almost immediately on any call that is not instant: prompt feedback makes
mpmfeel responsive from the start rather than stalled during the first second. Only the quickest calls (cached version probes, trivial metadata queries) finish within this delay and stay silent; anything slower (aguix search, a source build) shows the spinner right away.
- class meta_package_manager.execution.CLIExecutor[source]ΒΆ
Bases:
objectLocate a managerβs CLI on the system and run it.
Mixin inherited by
meta_package_manager.manager.PackageManager. Owns the CLI-invocation configuration (names, search paths, environment, arguments, timeout) and the engine that searches for the binary, executes it, captures and normalizes its output, accumulates errors, and parses its self-reported version.Initialize
cli_errorslist.- cli_names: tuple[str, ...]ΒΆ
List of CLI names the package manager is known as.
This list of recognized CLI names is ordered by priority. That way we can influence the search of the right binary.
- ..hint::
This was helpful in the case of the Python transition from 2.x to 3.x, where multiple versions of the same executable were named
pythonorpython3.
By default, this propertyβs value is derived from the managerβs ID (see the
MetaPackageManager.__init__method above).
- cli_search_path: tuple[str, ...] = ()ΒΆ
List of additional path to help mpm hunt down the package manager CLI.
Must be a list of strings whose order dictates the search sequence.
Most of the time unnecessary:
meta_package_manager.execution.CLIExecutor.cli_pathworks well on all platforms.
- extra_env: ClassVar[Mapping[str, str | None] | None] = NoneΒΆ
Additional environment variables to add to the current context.
Automatically applied on each
meta_package_manager.execution.CLIExecutor.run_cli()calls.
- pre_cmds: tuple[str, ...] = ()ΒΆ
Global list of pre-commands to add before before invoked CLI.
Automatically added to each
meta_package_manager.execution.CLIExecutor.run_cli()call.Used to prepend sudo or other system utilities.
- post_args: tuple[str, ...] = ()ΒΆ
Global list of options used before and after the invoked package manager CLI.
Automatically added to each
meta_package_manager.execution.CLIExecutor.run_cli()call.Essentially used to force silencing, low verbosity or no-color output.
- version_cli_options: tuple[str, ...] = ('--version',)ΒΆ
CLI options used to produce the version of the package manager.
The raw output produced by the package manager CLI will be parsed with the
version_regexesbelow to extract the version number.
- version_cli: str | None = NoneΒΆ
Alternate binary probed for the managerβs version, instead of the main CLI.
Some manager suites expose no version flag on any of their own binaries (OpenBSDβs
pkg_add/pkg_info, Solarisβpkgadd/pkginfo): they ship with the base system and are versioned with the OS itself. Naming aversion_cli(likeuname) makes the version probe run that binary withversion_cli_optionsand parse its output withversion_regexes, while every operation keeps using the managerβs owncli_path. The binary is resolved withwhich(); the version resolves toNone(manager notfresh) when it is not found.
- version_regexes: tuple[str, ...] = ('(?P<version>\\S+)',)ΒΆ
Regular expressions used to extract the version number.
This property must be a tuple of strings, each of which is a valid regular expression that must contain a group named
<version>.The first of these regexes producing a match and returning non-empty
<version>group will be used as the version string of the package manager.That version string will then be sanitized and normalized by
meta_package_manager.execution.CLIExecutor.version.By default match the first part that is space-separated.
Caution
These regexes are compiled with
re.MULTILINEonly. They are not compiled withre.VERBOSE, so literal whitespace in the pattern is significant and matches whitespace in the CLI output.
- plan: bool = FalseΒΆ
Capture state-changing CLI calls for inspection instead of running them.
Set by
mpm --plan. Unlikedry_run(which simulates every call, read-only queries included), plan mode lets the read-only queries (installed,outdated,search) run for real so the resolved plan reflects actual system state, and records only the state-changing commands (see_MUTATING_OPERATIONS) intoPLAN_RECORDER.
- timeout: int | None = NoneΒΆ
Maximum number of seconds to wait for a CLI call to complete.
Nonemeans the user expressed no explicit preference: the effective cap is then resolved per-operation by_resolve_timeout()fromOPERATION_TIMEOUTS. A non-Nonevalue (the--timeoutflag or a per-manager override) wins for every operation.
- progress: bool = FalseΒΆ
Whether CLI calls may show a progress spinner while they block.
Set by the CLI to an interactive, human-facing run only (a TTY, no serialized output, not at DEBUG verbosity). Even when
Truethe spinner still self-suppresses off a TTY: see_make_spinner(). Defaults toFalseso programmatic use stays silent.
- cooldown: timedelta | None = NoneΒΆ
Minimum age a release must have before it can be installed or upgraded.
When set, the manager refuses to bring in any package version published more recently than
cooldownago. This is a mitigation against supply-chain attacks: a malicious release is typically detected and pulled within days of publication, so a waiting period keeps freshly-published (and potentially compromised) versions out of the system.Nonedisables the gate.Only managers able to natively enforce a release-age limit honor this; see
cooldown_env_varandsupports_cooldown.
- require_cooldown_support: bool = TrueΒΆ
Require native
cooldownsupport to run install/upgrade.By default (
True, fail-closed), when acooldownis requested, install and upgrade operations are skipped for managers lacking native release-age support, so nothing slips in unguarded. Setting this toFalseopts into running those operations anyway, without the safeguard.
- sudo: bool | None = NoneΒΆ
User escalation policy: run this managerβs privileged commands with
sudo.None(the default) means the user expressed no preference, so the built-indefault_sudodecides.True/Falseforce escalation on or off for every operation this manager marks privileged (abuild_cli(..., sudo=True)call). Set globally bympm --sudo/mpm --no-sudoand per manager by the[mpm.managers.<id>] sudoconfig key, the latter winning (seemeta_package_manager.pool.ManagerPool._select_managers()).Only privileged operations on UNIX are ever escalated. A manager that escalates internally (
internal_sudo) has no such markers and is never wrapped insudobympm: its ownsudoreuses the credential cache whenprime_sudo()finds it already warm, and is otherwise covered by the silent-call notice inrun().
- default_sudo: bool = FalseΒΆ
Built-in escalation default, used when
sudoisNone.Falseon the base: most managers install into user-writable trees and never need root. The system package managers whose privileged operations require root (apt,dnf,pacman,zypper, β¦) set this toTrueso theirbuild_cli(..., sudo=True)operations escalate out of the box, while staying switchable off throughsudo(--no-sudoor config) for rootless setups.
- internal_sudo: bool = FalseΒΆ
Marks a manager whose CLI invokes
sudoitself mid-run.Homebrew
caskruns it from installer artifacts,finkre-execs its root commands through it, and the AUR helpers callsudo pacmanfor their install steps. mpm never wraps such a managerβs commands: either none of its operations carry abuild_cli(..., sudo=True)marker (cask,fink), or itsdefault_sudo = Falsepolicy leaves the markers it inherits unescalated (the AUR helpers). Running the tool undersudois often forbidden outright (brewrefuses root,makepkgrefuses to build). Consumed byprime_sudo(), whose opportunistic probe keeps an already-warm credential cache alive for these internal escalations, and by the silent-call notice inrun(), which flags a possibly-hidden password prompt on a cold cache.Forcing
sudo = trueon such a manager (config key or--sudo) still never wraps its commands, but does promote it into the up-front prompt path ofprime_sudo().
- cooldown_env_var: ClassVar[str | None] = NoneΒΆ
Environment variable this manager reads to honor a
cooldown.None(the default) means the manager has no native release-age mechanism and cannot honor a cooldown. A subclass that sets this string advertises support (seesupports_cooldown); the value produced bycooldown_env_value()is then injected into the environment of every CLI call.
- windows_creation_flags: int = 0ΒΆ
Additional Windows process creation flags OR-ed with
CREATE_NO_WINDOW.Use this on individual managers to control how their subprocess is attached to the calling processβs console. For example, setting this to
subprocess.DETACHED_PROCESS(0x8) fully detaches the child from the parentβs console. Any grandchild process (like a COM server or installer EXE) that callsGenerateConsoleCtrlEvent(0)on exit will then fail silently because there is no console to broadcast to.No-op on non-Windows platforms (
getattrreturns0for Windows-only flags).
- windows_processes_to_cleanup: tuple[str, ...] = ()ΒΆ
Windows process image names to forcibly terminate after each CLI call.
When a package manager spawns grandchild processes that outlive the direct subprocess (like wingetβs
WindowsPackageManagerServer.exeCOM server), those orphans can linger and consume resources. List the image names here so they are killed aftercommunicate()returns.No-op on non-Windows platforms.
- run_cache: dict[tuple, tuple[int, str, str]] | None = NoneΒΆ
Optional cache that de-duplicates identical CLI runs within a lock family.
Noneby default, which disables caching: everyrun()call spawns its own subprocess.meta_package_manager.dispatch.dispatch()injects one shared dict into all the managers of a multi-manager lock-family lane (seemeta_package_manager.dispatch.SHARED_LOCK_FAMILIES) for the duration of that lane, so members resolving to a byte-identical command (brewandcaskboth runningbrew updatefor mpm sync) run the subprocess once and replay the cached(code, output, error)for the rest. The replay still walksrun()βs logging and failure gate, so a failed shared command is attributed to every member. Keyed on the resolved command line and its environment, so only genuinely identical invocations collapse.
- property supports_cooldown: boolΒΆ
Whether this manager can natively enforce a release-age
cooldown.
- cooldown_env_value()[source]ΒΆ
Render
cooldownas the value ofcooldown_env_var.Defaults to the RFC 3339 timestamp of the most recent release date still allowed, i.e. now minus the cooldown. Managers whose environment variable expects another format (a number of minutes, a bare day count, β¦) override this.
- Return type:
- cooldown_rounded_up(unit_seconds)[source]ΒΆ
Render
cooldownas an integer count ofunit_seconds-long units, rounded up.Helper for the
cooldown_env_value()overrides of managers whose native release-age knob expects a unit count rather than the default RFC 3339 timestamp (npmβs day-basedmin-release-age, pnpmβs minute-basedminimumReleaseAge). Sub-unit cooldowns round up so the gate over-protects rather than silently collapsing to0(the βno cooldownβ sentinel).- Return type:
- cooldown_env()[source]ΒΆ
Environment fragment enforcing the
cooldown, empty when inactive.Returns an empty mapping unless a
cooldownis set and the manager supports it. Merged into the environment of everyrun()call.
- search_all_cli(cli_names, env=None)[source]ΒΆ
Search for all binary files matching the CLI names, in all environment path.
This is like our own implementation of
shutil.which(), with the difference that it is capable of returning all the possible paths of the provided file names, in all environment path, not just the first one that match. And on Windows, prevents matching of CLI in the current directory, which takes precedence on other paths.Returns all files matching any
cli_names, by iterating over all folders in this order:folders provided by
cli_search_path,then in all the default places specified by the environment variable (i.e.
os.getenv("PATH")).
Only returns files that exists and are not empty.
Caution
Symlinks are not resolved, because some manager like Homebrew on Linux relies on some sort of symlink-based trickery to set environment variables.
- sibling_cli(name, *, same_dir=False)[source]ΒΆ
Resolve the path of a sibling binary of the managerβs main CLI.
Some managers ship as a suite of binaries (
xbps-install/xbps-query,pkg_add/pkg_info, emergeβsqlist): an operation then runs a sibling instead of the main CLI. By default the sibling is searched like the main CLI itself (which(), honoringcli_search_path), and a missing binary raisesFileNotFoundErrorrather than silently falling back to the wrong program.same_dir=Trueinstead takes the sibling from the directory ofcli_path, without an existence probe: suites installing all their binaries side by side (XBPS, Nix) guarantee the neighbor, and resolving it from the same directory can never mix two installations. A genuinely missing file then surfaces at spawn time.- Return type:
- property cli_path: Path | NoneΒΆ
Fully qualified path to the canonical package manager binary.
Try each CLI names provided by
cli_names, in each system path provided bycli_search_path. In that order. Then returns the first match.Executability of the CLI will be separately assessed later by the
meta_package_manager.execution.CLIExecutor.executableproperty below.
- property version: TokenizedString | NoneΒΆ
Invoke the manager and extract its own reported version string.
Returns a parsed and normalized version in the form of a
meta_package_manager.version.TokenizedStringinstance.Skipped on platforms where the manager is not supported, even if
cli_pathresolved to an executable: that binary almost certainly belongs to a different tool that happens to share the same name (e.g. GNUmakeon macOS getting matched by the FreeBSDportsmanager), so probing it would either misreport the version or surface confusing error output.
- acting_as(operation=None, *, stop_on_error=None)[source]ΒΆ
Temporarily adjust the managerβs execution state, restoring it on exit.
operationre-stamps_active_operation(the per-operation timeout and watchdog key) for the duration of the block;Noneleaves the current stamp untouched.stop_on_errorlikewise overrides the failure policy when set: the per-package state changers run their action understop_on_error=Trueso a botched operation raises and is recorded by the caller instead of being silently accumulated.The public seam for callers needing a scoped state override: the CLI layer must never poke
_active_operationorstop_on_errordirectly.
- run(*args, extra_env=None, must_succeed=False)[source]ΒΆ
Run a shell command, return the output and accumulate error messages.
argsis allowed to be a nested structure of iterables, in which case it will be recursively flatten, thenNonewill be discarded, and finally each item casted to strings.- Running commands with that method takes care of:
disclosing the invocation at
INFO(the reproducible$-prompt line with forced environment variables) and streaming the raw output live toDEBUG, prefixed with the manager ID, viaclick_extra.execution.run_cli()flagging, on a terminal, the mutating call of an internal escalator that goes silent on a cold credential cache and may be blocked on a hidden password prompt (see
_StallWatchdog)detaching every other call into its own POSIX session and process group, so a timeout or Ctrl+C reaps the whole process tree and a wedged grandchild cannot linger as an orphan; the flagged call above keeps the controlling terminal so its
sudoprompt stays answerableremoving ANSI escape codes from
subprocess.CompletedProcess.stdoutandsubprocess.CompletedProcess.stderrreturning ready-to-use normalized strings (dedented and stripped)
letting
mpm --dry-runandmpm --stop-on-errorhave expected effect on execution
- Parameters:
must_succeed (bool) β if
True, raisemeta_package_manager.manager.CLIErrorwhen the command fails, regardless of the user-facingstop_on_errorpreference, rather than accumulating the error for an end-of-run summary. Use for calls whose output is parsed (JSON, XML, regex), where a swallowed failure would be indistinguishable from empty results. A non-zero exit that leaves<stderr>empty is tolerated as a benign status code (npmandpnpm outdatedexit1when updates exist); only the per-package state changers, which run under a patchedstop_on_error, treat every non-zero exit as a failure. See the failure gate below for details.- Return type:
str
- build_cli(*args, auto_pre_cmds=True, auto_pre_args=True, auto_post_args=True, override_pre_cmds=None, override_cli_path=None, override_pre_args=None, override_post_args=None, sudo=False)[source]ΒΆ
Build the package manager CLI by combining the custom
*argswith the package managerβs global parameters.Returns a tuple of strings.
Helps the construction of CLIβs repeating patterns and makes the code easier to read. Just pass the specific
*argsand the full CLI string will be composed out of the globals, following this schema:$ [<pre_cmds>|sudo --non-interactive] <cli_path> <pre_args> <*args> <post_args>
- Return type:
tuple[str, β¦]
self.pre_cmdsis added before the CLI path.self.cli_pathis used as the main binary to execute.self.pre_argsandself.post_argsglobals are added before and after the provided*args.
Each additional set of elements can be disabled with their respective flag:
auto_pre_cmds=Falseto skip the automatic addition ofself.pre_cmdsauto_pre_args=Falseto skip the automatic addition ofself.pre_argsauto_post_args=Falseto skip the automatic addition ofself.post_args
Each global set of elements can be locally overridden with:
override_pre_cmds=tuple()override_cli_path=stroverride_pre_args=tuple()override_post_args=tuple()
On UNIX, an operation marked privileged (
sudo=True) is escalated only when the per-manager policy opts in (sudo, falling back todefault_sudo). It is then run through sudo with--non-interactive(it spends the credential cache warmed byprime_sudo()and fails fast rather than blocking on a password prompt). When escalation applies,override_pre_cmdsis not allowed to be set andauto_pre_cmdsis forced toFalse. A non-UNIX host never escalates.
- run_cli(*args, auto_extra_env=True, auto_pre_cmds=True, auto_pre_args=True, auto_post_args=True, override_extra_env=None, override_pre_cmds=None, override_cli_path=None, override_pre_args=None, override_post_args=None, force_exec=False, must_succeed=False, sudo=False)[source]ΒΆ
Build and run the package manager CLI by combining the custom
*argswith the package managerβs global parameters.After the CLI is built with the
meta_package_manager.execution.CLIExecutor.build_cli()method, it is executed with themeta_package_manager.execution.CLIExecutor.run()method, augmented with environment variables fromself.extra_env.All parameters are the same as
meta_package_manager.execution.CLIExecutor.build_cli(), plus:auto_extra_env=Falseto skip the automatic addition ofself.extra_envoverride_extra_env=dict()to locally overrides the laterforce_execignores thempm --dry-run,mpm --stop-on-errorandmpm --planoptions to force the execution and completion of the command. It is used for reads whose output is needed regardless (version detection,yarn global dir), which must run for real even when the user asked to simulate or to only plan mutations.must_succeedraises on non-zero exit regardless ofmpm --stop-on-error. Seerun()for details.
- Return type:
str
meta_package_manager.labels moduleΒΆ
Utilities to generate the extra labels and labeller rules for GitHub issues and PRs.
The content and file rules produced here are a convenience: they pre-label a
freshly filed issue or PR to save the maintainer a first pass. They never replace
the manual review and classification, and nothing downstream treats them as
authoritative. They are therefore tuned for precision over recall: a rule is
encoded only when its signal is unambiguous (see generate_content_rules()
and generate_file_rules()), and a manager with no unambiguous term simply
gets no content rule and is labelled by hand.
- meta_package_manager.labels.generate_labels(all_labels, groups, prefix, color)[source]ΒΆ
Generate labels.
A dedicated label is produced for each entry of the
all_labelsparameter, unless it is part of agroup. In which case a dedicated label for that group will be created.Returns the
{label_id: label_name}map and the list of(label_name, color, description)rows to register, leaving the caller to fold them into the globalLABELSregistry. Kept pure (no global mutation) so it can be called repeatedly without double-populating the registry.
- meta_package_manager.labels.MANAGER_LABEL_GROUPS: TLabelGroup = {'dpkg-based': frozenset({'apt', 'apt-mint', 'deb-get', 'opkg', 'pacstall'}), 'homebrew': frozenset({'brew', 'cask', 'zerobrew'}), 'npm-based': frozenset({'npm', 'pnpm', 'volta', 'yarn', 'yarn-berry'}), 'pacman-based': frozenset({'pacaur', 'pacman', 'paru', 'yay'}), 'pip-based': frozenset({'pip', 'pipx'}), 'pkg-based': frozenset({'pkg', 'ports'}), 'rpm-based': frozenset({'dnf', 'dnf5', 'urpmi', 'yum', 'zypper'}), 'scoop-based': frozenset({'scoop', 'sfsu'}), 'uv-based': frozenset({'uv', 'uvx'}), 'vscode-based': frozenset({'vscode', 'vscodium'})}ΒΆ
Managers sharing the same ecosystem are grouped together under the same label.
Grouping is by ecosystem (the underlying packaging system), not by installation paradigm. For example, source-based helpers like Pacstall and AUR helpers are grouped with their ecosystem (dpkg-based and pacman-based respectively), even though they build from source rather than fetching pre-built binaries.
- meta_package_manager.labels.all_manager_label_ids = frozenset({'apk', 'apm', 'apt', 'apt-cyg', 'apt-mint', 'asdf', 'brew', 'cargo', 'cask', 'cave', 'choco', 'chromebrew', 'composer', 'conda', 'cpan', 'deb-get', 'dnf', 'dnf5', 'emerge', 'eopkg', 'fink', 'flatpak', 'fwupd', 'gem', 'gh-ext', 'guix', 'macports', 'mas', 'mise', 'mpm', 'nix', 'npm', 'opkg', 'pacaur', 'pacman', 'pacstall', 'paru', 'pip', 'pipx', 'pkcon', 'pkg', 'pkg-tools', 'pkgin', 'pnpm', 'ports', 'pwsh-gallery', 'scoop', 'sdkman', 'sfsu', 'slapt-get', 'snap', 'soar', 'sorcery', 'steamcmd', 'stew', 'sun-tools', 'swupd', 'tazpkg', 'tlmgr', 'topgrade', 'urpmi', 'uv', 'uvx', 'volta', 'vscode', 'vscodium', 'winget', 'xbps', 'yarn', 'yarn-berry', 'yay', 'yum', 'zerobrew', 'zypper'})ΒΆ
Adds
mpmas its own manager alongside all those implemented.
- meta_package_manager.labels.MANAGER_LABELS = {'apk': 'π¦ manager: apk', 'apm': 'π¦ manager: apm', 'apt': 'π¦ manager: dpkg-based', 'apt-cyg': 'π¦ manager: apt-cyg', 'apt-mint': 'π¦ manager: dpkg-based', 'asdf': 'π¦ manager: asdf', 'brew': 'π¦ manager: homebrew', 'cargo': 'π¦ manager: cargo', 'cask': 'π¦ manager: homebrew', 'cave': 'π¦ manager: cave', 'choco': 'π¦ manager: choco', 'chromebrew': 'π¦ manager: chromebrew', 'composer': 'π¦ manager: composer', 'conda': 'π¦ manager: conda', 'cpan': 'π¦ manager: cpan', 'deb-get': 'π¦ manager: dpkg-based', 'dnf': 'π¦ manager: rpm-based', 'dnf5': 'π¦ manager: rpm-based', 'emerge': 'π¦ manager: emerge', 'eopkg': 'π¦ manager: eopkg', 'fink': 'π¦ manager: fink', 'flatpak': 'π¦ manager: flatpak', 'fwupd': 'π¦ manager: fwupd', 'gem': 'π¦ manager: gem', 'gh-ext': 'π¦ manager: gh-ext', 'guix': 'π¦ manager: guix', 'macports': 'π¦ manager: macports', 'mas': 'π¦ manager: mas', 'mise': 'π¦ manager: mise', 'mpm': 'π¦ manager: mpm', 'nix': 'π¦ manager: nix', 'npm': 'π¦ manager: npm-based', 'opkg': 'π¦ manager: dpkg-based', 'pacaur': 'π¦ manager: pacman-based', 'pacman': 'π¦ manager: pacman-based', 'pacstall': 'π¦ manager: dpkg-based', 'paru': 'π¦ manager: pacman-based', 'pip': 'π¦ manager: pip-based', 'pipx': 'π¦ manager: pip-based', 'pkcon': 'π¦ manager: pkcon', 'pkg': 'π¦ manager: pkg-based', 'pkg-tools': 'π¦ manager: pkg-tools', 'pkgin': 'π¦ manager: pkgin', 'pnpm': 'π¦ manager: npm-based', 'ports': 'π¦ manager: pkg-based', 'pwsh-gallery': 'π¦ manager: pwsh-gallery', 'scoop': 'π¦ manager: scoop-based', 'sdkman': 'π¦ manager: sdkman', 'sfsu': 'π¦ manager: scoop-based', 'slapt-get': 'π¦ manager: slapt-get', 'snap': 'π¦ manager: snap', 'soar': 'π¦ manager: soar', 'sorcery': 'π¦ manager: sorcery', 'steamcmd': 'π¦ manager: steamcmd', 'stew': 'π¦ manager: stew', 'sun-tools': 'π¦ manager: sun-tools', 'swupd': 'π¦ manager: swupd', 'tazpkg': 'π¦ manager: tazpkg', 'tlmgr': 'π¦ manager: tlmgr', 'topgrade': 'π¦ manager: topgrade', 'urpmi': 'π¦ manager: rpm-based', 'uv': 'π¦ manager: uv-based', 'uvx': 'π¦ manager: uv-based', 'volta': 'π¦ manager: npm-based', 'vscode': 'π¦ manager: vscode-based', 'vscodium': 'π¦ manager: vscode-based', 'winget': 'π¦ manager: winget', 'xbps': 'π¦ manager: xbps', 'yarn': 'π¦ manager: npm-based', 'yarn-berry': 'π¦ manager: npm-based', 'yay': 'π¦ manager: pacman-based', 'yum': 'π¦ manager: rpm-based', 'zerobrew': 'π¦ manager: homebrew', 'zypper': 'π¦ manager: rpm-based'}ΒΆ
Maps all manager IDs to their labels.
- meta_package_manager.labels.PLATFORM_LABELS = {'ALT Linux': 'π₯ platform: Linux', 'AlmaLinux': 'π₯ platform: Linux', 'Alpine Linux': 'π₯ platform: Linux', 'Amazon Linux': 'π₯ platform: Linux', 'Android': 'π₯ platform: Linux', 'Arch Linux': 'π₯ platform: Linux', 'Buildroot': 'π₯ platform: Linux', 'CachyOS': 'π₯ platform: Linux', 'CentOS': 'π₯ platform: Linux', 'ChromeOS': 'π₯ platform: Linux', 'Clear Linux OS': 'π₯ platform: Linux', 'CloudLinux OS': 'π₯ platform: Linux', 'Cygwin': 'π₯ platform: Unix', 'Debian': 'π₯ platform: Linux', 'DragonFly BSD': 'π₯ platform: BSD', 'EndeavourOS': 'π₯ platform: Linux', 'Exherbo Linux': 'π₯ platform: Linux', 'Fedora': 'π₯ platform: Linux', 'FreeBSD': 'π₯ platform: BSD', 'GNU/Hurd': 'π₯ platform: Unix', 'Generic Linux': 'π₯ platform: Linux', 'Gentoo Linux': 'π₯ platform: Linux', 'Guix System': 'π₯ platform: Linux', 'Haiku': 'π₯ platform: Unix', 'IBM AIX': 'π₯ platform: Unix', 'IBM PowerKVM': 'π₯ platform: Linux', 'IBM i': 'π₯ platform: Unix', 'KVM for IBM z Systems': 'π₯ platform: Linux', 'Kali Linux': 'π₯ platform: Linux', 'Linux Mint': 'π₯ platform: Linux', 'Mageia': 'π₯ platform: Linux', 'Mandriva Linux': 'π₯ platform: Linux', 'Manjaro Linux': 'π₯ platform: Linux', 'MidnightBSD': 'π₯ platform: BSD', 'NetBSD': 'π₯ platform: BSD', 'NixOS': 'π₯ platform: Linux', 'Nobara': 'π₯ platform: Linux', 'OpenBSD': 'π₯ platform: BSD', 'OpenWrt': 'π₯ platform: Linux', 'Oracle Linux': 'π₯ platform: Linux', 'Parallels': 'π₯ platform: Linux', 'Pidora': 'π₯ platform: Linux', 'PikaOS': 'π₯ platform: Linux', 'Raspbian': 'π₯ platform: Linux', 'RedHat Enterprise Linux': 'π₯ platform: Linux', 'Rocky Linux': 'π₯ platform: Linux', 'SUSE Linux Enterprise Server': 'π₯ platform: Linux', 'Scientific Linux': 'π₯ platform: Linux', 'Slackware': 'π₯ platform: Linux', 'SliTaz GNU/Linux': 'π₯ platform: Linux', 'Solaris': 'π₯ platform: Unix', 'Source Mage GNU/Linux': 'π₯ platform: Linux', 'SunOS': 'π₯ platform: BSD', 'Tuxedo OS': 'π₯ platform: Linux', 'Ubuntu': 'π₯ platform: Linux', 'Ultramarine': 'π₯ platform: Linux', 'Void Linux': 'π₯ platform: Linux', 'Windows': 'π₯ platform: Windows', 'Windows Subsystem for Linux v1': 'π₯ platform: Linux', 'Windows Subsystem for Linux v2': 'π₯ platform: Linux', 'XenServer': 'π₯ platform: Linux', 'illumos': 'π₯ platform: Unix', 'macOS': 'π₯ platform: macOS', 'openSUSE': 'π₯ platform: Linux'}ΒΆ
Maps all platform names to their labels.
- meta_package_manager.labels.LABELS: list[tuple[str, str, str]] = [('π¦ manager: apk', '#bfdadc', 'apk'), ('π¦ manager: apm', '#bfdadc', 'apm'), ('π¦ manager: apt-cyg', '#bfdadc', 'apt-cyg'), ('π¦ manager: asdf', '#bfdadc', 'asdf'), ('π¦ manager: cargo', '#bfdadc', 'cargo'), ('π¦ manager: cave', '#bfdadc', 'cave'), ('π¦ manager: choco', '#bfdadc', 'choco'), ('π¦ manager: chromebrew', '#bfdadc', 'chromebrew'), ('π¦ manager: composer', '#bfdadc', 'composer'), ('π¦ manager: conda', '#bfdadc', 'conda'), ('π¦ manager: cpan', '#bfdadc', 'cpan'), ('π¦ manager: dpkg-based', '#bfdadc', 'apt, apt-mint, deb-get, opkg, pacstall'), ('π¦ manager: emerge', '#bfdadc', 'emerge'), ('π¦ manager: eopkg', '#bfdadc', 'eopkg'), ('π¦ manager: fink', '#bfdadc', 'fink'), ('π¦ manager: flatpak', '#bfdadc', 'flatpak'), ('π¦ manager: fwupd', '#bfdadc', 'fwupd'), ('π¦ manager: gem', '#bfdadc', 'gem'), ('π¦ manager: gh-ext', '#bfdadc', 'gh-ext'), ('π¦ manager: guix', '#bfdadc', 'guix'), ('π¦ manager: homebrew', '#bfdadc', 'brew, cask, zerobrew'), ('π¦ manager: macports', '#bfdadc', 'macports'), ('π¦ manager: mas', '#bfdadc', 'mas'), ('π¦ manager: mise', '#bfdadc', 'mise'), ('π¦ manager: mpm', '#bfdadc', 'mpm'), ('π¦ manager: nix', '#bfdadc', 'nix'), ('π¦ manager: npm-based', '#bfdadc', 'npm, pnpm, volta, yarn, yarn-berry'), ('π¦ manager: pacman-based', '#bfdadc', 'pacaur, pacman, paru, yay'), ('π¦ manager: pip-based', '#bfdadc', 'pip, pipx'), ('π¦ manager: pkcon', '#bfdadc', 'pkcon'), ('π¦ manager: pkg-based', '#bfdadc', 'pkg, ports'), ('π¦ manager: pkg-tools', '#bfdadc', 'pkg-tools'), ('π¦ manager: pkgin', '#bfdadc', 'pkgin'), ('π¦ manager: pwsh-gallery', '#bfdadc', 'pwsh-gallery'), ('π¦ manager: rpm-based', '#bfdadc', 'dnf, dnf5, urpmi, yum, zypper'), ('π¦ manager: scoop-based', '#bfdadc', 'scoop, sfsu'), ('π¦ manager: sdkman', '#bfdadc', 'sdkman'), ('π¦ manager: slapt-get', '#bfdadc', 'slapt-get'), ('π¦ manager: snap', '#bfdadc', 'snap'), ('π¦ manager: soar', '#bfdadc', 'soar'), ('π¦ manager: sorcery', '#bfdadc', 'sorcery'), ('π¦ manager: steamcmd', '#bfdadc', 'steamcmd'), ('π¦ manager: stew', '#bfdadc', 'stew'), ('π¦ manager: sun-tools', '#bfdadc', 'sun-tools'), ('π¦ manager: swupd', '#bfdadc', 'swupd'), ('π¦ manager: tazpkg', '#bfdadc', 'tazpkg'), ('π¦ manager: tlmgr', '#bfdadc', 'tlmgr'), ('π¦ manager: topgrade', '#bfdadc', 'topgrade'), ('π¦ manager: uv-based', '#bfdadc', 'uv, uvx'), ('π¦ manager: vscode-based', '#bfdadc', 'vscode, vscodium'), ('π¦ manager: winget', '#bfdadc', 'winget'), ('π¦ manager: xbps', '#bfdadc', 'xbps'), ('π plugin', '#fef2c0', 'Xbar/SwiftBar/GNOME Shell plugin code, documentation and features'), ('π₯ platform: BSD', '#bfd4f2', 'DragonFly BSD, FreeBSD, MidnightBSD, NetBSD, OpenBSD, SunOS'), ('π₯ platform: Linux', '#bfd4f2', 'AlmaLinux, Alpine Linux, ALT Linux, Amazon Linux, Android, Arch Linux, Buildroot, CachyOS, CentOS, β¦'), ('π₯ platform: macOS', '#bfd4f2', 'macOS'), ('π₯ platform: Unix', '#bfd4f2', 'Cygwin, GNU/Hurd, Haiku, IBM AIX, IBM i, illumos, Solaris'), ('π₯ platform: Windows', '#bfd4f2', 'Windows')]ΒΆ
Global registry of all labels used in the project.
Structure:
("label_name", "color", "optional_description")
- meta_package_manager.labels.CONTENT_RULES_STATIC: TLabelRules = [('π plugin', ('gnome shell', 'gnome-shell', 'plugin', 'swiftbar', 'xbar'))]ΒΆ
Curated keywords feeding the content rules of labels not derived from the pool.
Holds keywords, not finished patterns:
generate_content_rules()runs them through_keyword_alternation()like every other content rule.
- meta_package_manager.labels.FILE_RULES_STATIC: TLabelRules = [('π plugin', ('gnome-shell/**', 'meta_package_manager/bar_plugin*', 'tests/*bar_plugin*', 'tests/*gnome*', 'tests/gnome/**')), ('π¦ manager: mpm', ('meta_package_manager/*',))]ΒΆ
File rules for labels that are not derived from the pool.
mpmgets no content rule: as the projectβs own name it would match nearly every issue and PR.
- meta_package_manager.labels.MANAGER_CONTENT_KEYWORDS: dict[str, tuple[str, ...]] = {'apk': ('alpine', 'alpine linux'), 'apm': ('atom',), 'apt-cyg': ('cygwin',), 'asdf': ('asdf-vm',), 'cargo': ('crate', 'rust'), 'cave': ('exherbo', 'paludis'), 'choco': ('chocolatey',), 'chromebrew': ('chrome os', 'chromeos'), 'composer': ('php',), 'conda': ('anaconda', 'conda-forge', 'miniconda'), 'cpan': ('perl',), 'dpkg-based': ('aptitude', 'debian', 'dpkg', 'ubuntu'), 'emerge': ('gentoo', 'portage'), 'eopkg': ('solus',), 'flatpak': ('flathub',), 'fwupd': ('lvfs',), 'gem': ('ruby',), 'gh-ext': ('gh extension', 'github cli'), 'guix': ('gnu guix',), 'homebrew': ('homebrew',), 'mas': ('app store', 'app-store'), 'nix': ('nixos', 'nixpkgs'), 'npm-based': ('node.js', 'nodejs'), 'pacman-based': ('arch',), 'pip-based': ('pypi',), 'pkcon': ('packagekit',), 'pkg-based': ('freebsd', 'freebsd ports'), 'pkg-tools': ('openbsd',), 'pkgin': ('netbsd', 'pkgsrc'), 'pwsh-gallery': ('powershell', 'powershell gallery', 'psgallery', 'psresourceget'), 'rpm-based': ('fedora', 'mageia', 'opensuse', 'redhat', 'rhel', 'rpm', 'suse'), 'sdkman': ('sdk man',), 'slapt-get': ('slackware',), 'snap': ('snapcraft',), 'sorcery': ('source mage',), 'steamcmd': ('valve',), 'sun-tools': ('solaris', 'svr4'), 'swupd': ('clear linux', 'clearlinux'), 'tazpkg': ('slitaz',), 'tlmgr': ('ctan', 'tex live', 'texlive'), 'vscode-based': ('visual studio', 'visual studio code'), 'xbps': ('void linux',)}ΒΆ
Curated ecosystem keywords feeding each manager labelβs content rule.
Keyed by the manager or group ID the label derives from. These are the only content patterns a manager label gets: the bare manager IDs are deliberately left out (see
generate_content_rules()). Add only terms that are both unambiguously about this manager and absent from anything mpm prints itself: theβ <id>trail, the<id>: <count>summary line, themanagerstable (which lists every managerβs ID and CLI binary) and the$-prompt command disclosure. That rules out manager IDs and CLI names (fwupdmgr,pwsh), leaving the distro, language and brand names a human types in an issue. A manager with no such term gets no content rule and is labelled by hand.Skip anything that doubles as a common word even once word-anchored (
port,flat,mint,void): dropping the ID removed the implicit AND-guard those leaned on, so on their own they match unrelated prose.
- meta_package_manager.labels.PLATFORM_CONTENT_KEYWORDS: dict[str, tuple[str, ...]] = {'BSD': ('bsd',), 'Linux': ('linux',), 'Unix': ('unix',), 'Windows': ('c:', 'microsoft', 'windows'), 'macOS': ('apple', 'mac os', 'macos', 'os x', 'osx')}ΒΆ
Curated keyword patterns feeding each platform labelβs content rule.
- meta_package_manager.labels.generate_content_rules()[source]ΒΆ
Build every content rule: the static ones plus one per manager or platform label that has curated keywords.
Manager labels are driven solely by
MANAGER_CONTENT_KEYWORDS, never by the bare manager IDs or CLI names. mpm enumerates every installed manager in its own output (theβ <id>trail, the<id>: <count>summary line, themanagerstable), so a pasted trace would otherwise make every manager on the userβs system match at once: acpan-only report came back taggedmise,pipanduvmerely because they sat in the trace. The keywords are the distro, language and brand names a human types, which mpm never prints.Every rule β static, manager and platform alike β emits exactly one pattern, built by
_keyword_alternation(). A rule listing its keywords raw would read as βall of theseβ under the labellerβs all-of semantics, which no issue ever satisfies: a multi-keyword label encoded that way is silently dead. A label with no keyword is skipped: that manager gets no content rule, only its file rule. Rules are sorted by label, both cases folded.
- meta_package_manager.labels.generate_file_rules()[source]ΒΆ
Build every file rule: static ones plus one per manager label.
A manager label matches its membersβ definition files (Python modules and bundled TOML files alike, anchored on the full stem so
pkg.*never swallowspkgin.tomlorpkcon.py) and any test file carrying a memberβs stem or ID. Platform labels have no file rule: no file is platform-specific.
meta_package_manager.manager moduleΒΆ
Abstract base class tying together every package manager definition.
Defines meta_package_manager.manager.PackageManager, the class each concrete
manager in meta_package_manager.managers inherits from, together with its
meta_package_manager.manager.MetaPackageManager metaclass and the
meta_package_manager.manager.ManagerScope classification.
A subclass declares its identity (supported platforms, version requirement, maintenance
status) and implements the operations it supports (installed, outdated,
install, upgrade, β¦). The CLI-execution engine it inherits lives in
meta_package_manager.execution, the operation vocabulary in
meta_package_manager.capabilities, and the package objects operations yield in
meta_package_manager.package. On top of the engine, this module adds the
availability policy: whether the manager is supported, fresh, and ready to use.
- meta_package_manager.manager.JSON_FIELD_SELECTOR_REGEX = re.compile('^(?P<key>[^\\[\\]]+?)(?:\\[(?P<index>\\d+)\\])?$')ΒΆ
Parse a JSON field selector: a key name with an optional
[N]list index.A bare
versionmaps the package field to the itemβsversionkey. Aversions[0]selector additionally picks one element out of a list-valued key (zerobrew reports each packageβs installed versions as an array). Anything more nested stays out on purpose: a query needing real JSON traversal is better served by a custom parser. Shared byPackageManager.parse_json_items()and the declarative-manager validation inmeta_package_manager.definitions.
- class meta_package_manager.manager.ManagerScope(*values)[source]ΒΆ
Bases:
EnumFilesystem scope a package manager operates within.
- SYSTEM = 'system'ΒΆ
Manages software installed globally, machine-wide.
All currently-maintained managers are system-scoped.
- PROJECT = 'project'ΒΆ
Manages dependencies confined to a projectβs working tree.
Not supported yet. See
meta_package_manager.manager.PackageManager.discover_projects().See also
Microsoftβs Python Environment Tools (PET) is a Rust tool that locates Python environments (venv, conda, pyenv, pipenv, Poetry, uv, β¦) across a machine. It only discovers environments and does not inventory their packages, but is a useful reference and benchmark for implementing Python project-scope discovery.
- class meta_package_manager.manager.MetaPackageManager(name, bases, dct)[source]ΒΆ
Bases:
typeCustom metaclass used as a class factory for package managers.
Sets some class defaults, but only if theyβre not redefined in the final manager class.
Also normalize list of platform, by ungrouping groups, deduplicate entries and freeze them into a set of unique platforms.
- class meta_package_manager.manager.PackageManager[source]ΒΆ
Bases:
CLIExecutorBase class from which all package manager definitions inherits.
Initialize
cli_errorslist.- scope: ClassVar[ManagerScope] = 'system'ΒΆ
Whether the manager operates on globally-installed software or project-local dependencies.
Defaults to
ManagerScope.SYSTEM, which covers every manager maintained today: they install and query software machine-wide. Project-scoped managers (Poetry, Bundler, Maven, β¦) resolve dependencies confined to a working tree and are not supported yet.
- unmaintained: bool = FalseΒΆ
A manager whose upstream project is no longer maintained.
Covers projects that are officially retired and those we infer are abandoned: archived on their forge, left without a release or commit for years, formally superseded by a successor, or part of a discontinued platform. See the stability policy in
CLAUDE.mdfor the full criteria.An unmaintained manager is hidden from package selection by default (you can still use it by explicitly calling for it on the command line), and is exempt from the project stability policy: it may be dropped, in part or in full, in any release and without notice, once keeping it working becomes too burdensome.
Unmaintained managers are kept out of the functional and integration test matrices, so an unreliable or flaky one never blocks a release and we save CI resources. The commitment is to keep the wrapper for as long as that stays cheap: the cheap static invariants (ID format, attribute ordering, β¦) still apply for as long as the managerβs code lives in the source tree, to keep that code valid.
Every unmaintained manager must document itself through
unmaintained_message.
- unmaintained_message: str | None = NoneΒΆ
Evidence and rationale for the
unmaintainedflag, as a MyST markdown block.Rendered into the documentation (the managerβs page, and a
β οΈmarker in the manager tables). May embed markdown links to the archival notice, the successor project, or the discontinuation announcement. Required for every manager whoseunmaintainedflag is set, and only meaningful on such managers. Enforced bytest_unmaintained.
- maintenance_note: str | None = NoneΒΆ
A watch note about a still-maintained upstream whose activity is slowing or whose status is ambiguous, as a MyST markdown block.
Unlike
unmaintained, this is purely informational: the manager stays in the default selection and in the test matrices. It renders as a{note}admonition atop the managerβs documentation page, flagging upstreams worth keeping an eye on (a slow release cadence, superseded-but-still-shipped tools, a discontinued platform still under vendor support). May embed markdown links. Mutually exclusive withunmaintained: a confirmed-dead manager carries anunmaintained_messageinstead. Enforced bytest_maintenance_note.
- id: str = 'packagemanager'ΒΆ
Package managerβs ID.
Derived by defaults from the lower-cased class name in which underscores
_are replaced by dashes-.This ID must be unique among all package manager definitions and lower-case, as theyβre used as feature flags for the mpm CLI.
- name: str = 'PackageManager'ΒΆ
Return package managerβs common name.
Default value is based on class name.
- homepage_url: str | None = NoneΒΆ
Home page of the project, only used in documentation for reference.
- brewfile_entry_type: ClassVar[str | None] = NoneΒΆ
Name of the Brewfile DSL entry type this manager maps to, or
Noneif the manager has no Brewfile equivalent.Set by the subset of managers covered by Homebrew Bundleβs DSL (
brew,cask,mas,vscode,npm,cargo,uv,winget,flatpak). Consumed bymeta_package_manager.brewfilewhen rendering the output ofmpm dump --brewfile.
- brewfile_skip_warning: ClassVar[str | None] = NoneΒΆ
Optional stderr warning emitted when this managerβs installed packages are excluded from a Brewfile dump.
Set on managers where silently dropping the entries would mislead the user. The string supports a single
{count}placeholder for the installed-package count.
- platforms: frozenset[Platform] | Group | Platform | Iterable[Platform | Group] = frozenset({})ΒΆ
List of platforms supported by the manager.
Allows for a mishmash of platforms and groups of platforms. Will be normalized into a
frozensetofPlatforminstances at instantiation.
- requirement: str | None = NoneΒΆ
Version requirement specifier.
Supports a comma-separated range of constraints (e.g.
">=1.20.0,<2.0.0"). A bare version string like"1.20.0"is treated as>=1.20.0.Parsed by
meta_package_manager.version.VersionRange.Defaults to
None, which deactivates version check entirely.
- virtual: bool = TrueΒΆ
Should we expose the package manager to the user?
Virtual package manager are just skeleton classes used to factorize code among managers of the same family.
- ignore_auto_updates: bool = TrueΒΆ
Some managers can report or ignore packages which have their own auto-update mechanism.
- split_name_version(token)[source]ΒΆ
Split a dash-joined
<package_id>-<version>token into its two parts.Matches
tokenagainst_NAME_VERSION_REGEXP(or the subclassβs override of it) and returns the(package_id, version)pair, orNonewhen the token carries no recognizable version. Shared by every manager whose listings glue the name and version together.
- parse_json(output)[source]ΒΆ
Parse a queryβs JSON
output, tolerating empty and malformed captures.The shared first step of every JSON-emitting query, for built-in managers and config-defined operations alike (see
meta_package_manager.definitions._parse_spec_output()). ReturnsNonewhen the command produced no output (a manager with nothing to report often prints nothing at all), and when the output is not valid JSON, which logs one warning tagged with the manager ID instead of raising: a query that cannot be parsed yields no packages, mirroring how the fan-out commands swallow a failed CLI call into an empty result.Queries whose failure semantics differ keep their own parsing: a per-line NDJSON stream (
pkg search), a hardCLIErroron malformed payloads (pwsh-gallery), a best-effort metadata enrichment logging atDEBUG(brew info).
- parse_regex_lines(pattern, output)[source]ΒΆ
Yield one package per line of
outputmatchingpattern.The shared engine of every line-oriented text listing, for built-in managers and config-defined operations alike (see
meta_package_manager.definitions._make_query_property()). The pattern is searched in each line, and its named groups map straight onto the package fields:package_id(required: a match without one is skipped),installed_version,latest_version,name,descriptionandarch, empty and absent groups being dropped.Managers whose listings need per-line post-processing (multi-version reduction, name/version splitting, cross-query joins) keep their own loop and this stays their reference semantics.
- parse_json_items(output, *, list_path=None, fields)[source]ΒΆ
Yield one package per item of a JSON listing.
The shared engine of every flat-JSON query, for built-in managers and config-defined operations alike (see
meta_package_manager.definitions._make_query_property()). The document is parsed throughparse_json()(so a malformed payload warns and yields nothing), the package array is reached by walking the dottedlist_path(Nonewhen the document is itself the array), andfieldsmaps each package field (package_id, required, plus any ofinstalled_version,latest_version,name,description,arch) to its JSON selector: a key name with an optional[N]list index, likeversionorversions[0](seeJSON_FIELD_SELECTOR_REGEX). Items missing theirpackage_idand fields resolving toNoneare dropped.
- package(**kwargs)[source]ΒΆ
Instantiate a
Packageobject from the manager.Sets its
manage_idto the manager it belongs to.- Return type:
- brewfile_entry(package)[source]ΒΆ
Return
(entry_name, entry_options)for a Brewfile line, orNoneto skip the package.Default: emit
meta_package_manager.package.Package.idas the entry name with no options. Override on managers whose Brewfile DSL counterpart expects a different shape:masuses the app name withid: ADAM_ID,flatpakaddswith: ["remote"]. Only called whenbrewfile_entry_typeis set.
- property available: boolΒΆ
Is the package manager available and ready-to-use on the system?
Returns
Trueonly if the main CLI:
Short, human-readable explanation of why
availableisFalse, orNoneif the manager is available.Returned in priority order so the most actionable cause is reported first: platform support, then CLI lookup, then executable bit, then version requirement.
- property installed: Iterator[Package]ΒΆ
List packages currently installed on the system.
Optional. Will be simply skipped by mpm if not implemented.
- installed_or_empty()[source]ΒΆ
Materialized
installed, or an empty tuple on CLI failure.Best-effort inventory snapshot for the
installed,dumpandsbomsubcommands: each wants βgive me whatβs installed, and just skip this manager if its CLI blew upβ rather than re-implementing the samemeta_package_manager.execution.CLIErrorswallow. Logs one canonical warning on error and returns()so the caller carries on with the other managers.
- property installed_ids: frozenset[str]ΒΆ
Installed package IDs, materialized once from
installed().
- property installed_version_map: dict[str, TokenizedString | str | None]ΒΆ
Installed versions keyed by package ID, materialized once from
installed().Convenience for
outdatedparsers that report each packageβs latest version but not its currently-installed one, and so must look the latter up by ID (snap,xbps). The value mirrorsmeta_package_manager.package.Package.installed_version, whose declared type still carries the transientstrit normalizes away in__post_init__.
- package_metadata_batch(packages)[source]ΒΆ
Yield
(package, metadata)pairs enriched with whatever rich per-package data this manager can surface.Called by
mpm sbomin--bundledmode to populate licenses, checksums, download URLs, supplier/originator, and the declared dependency graph. The base implementation yieldsmeta_package_manager.package.EMPTY_METADATAfor each package and stays compatible with managers that do not (yet) expose richer metadata: their SBOM entries stay at the minimalPackagelevel, matching the historical and--minimalmodes.Manager subclasses override this with their native query path:
bulk shell-outs when the CLI accepts a package list (
brew info --json=v2 --installed,dpkg-query -W,apt-cache show);on-disk parsing when the metadata already lives on the filesystem (pipβs
.dist-infodirectories, Homebrewβs per-formulasbom.spdx.json, dpkgβs.md5sums).
The yielded pairs do not need to preserve the input order; the SBOM renderer matches by
Packageidentity. Implementations are expected to swallow per-package extraction errors and yieldmeta_package_manager.package.EMPTY_METADATAfor the affected packages rather than failing the whole scan: a single misbehaving formula must not abort an enrichment pass spanning hundreds of packages.Todo
Today every extractor is local-only (shell-outs to the managerβs CLI, plus on-disk reads). When extractors start reaching for network resources (PyPIβs JSON API, npmβs registry, crates.io, GitHubβs security advisories) the
--bundledflag will no longer be a fine-grained enough knob: some users will want enrichment but not network traffic (offline scans, CI without egress). The natural split is a future--network/--no-networkflag layered under--bundledto gate the network-touching code paths specifically, leaving local enrichment always-on for--bundled.- Return type:
- property outdated: Iterator[Package]ΒΆ
List installed packages with available upgrades.
Optional. Will be simply skipped by mpm if not implemented.
- property refiltered_outdated: Iterator[Package]ΒΆ
Wraps
outdated()with a version-equality filter.Some package managers report packages as outdated when the version strings differ at the character level but are numerically equal after parsing (e.g., Perl floating-point versions
2.0000vs2.000000). This filter drops those false positives.
- property orphans: Iterator[Package]ΒΆ
List packages installed as dependencies that nothing requires anymore.
The read-only counterpart of the
--orphansaction flags: wherempm cleanup --orphansremoves the orphans, this query only reports them, through the managerβs native listing (pacman --query --deps --unrequired,brew autoremove --dry-run,dnf repoquery --unneeded, β¦). mpm builds no dependency graph: the manager decides what is orphaned.Optional. Will be simply skipped by mpm if not implemented.
- cli_names: tuple[str, ...] = ('packagemanager',)ΒΆ
List of CLI names the package manager is known as.
This list of recognized CLI names is ordered by priority. That way we can influence the search of the right binary.
- ..hint::
This was helpful in the case of the Python transition from 2.x to 3.x, where multiple versions of the same executable were named
pythonorpython3.
By default, this propertyβs value is derived from the managerβs ID (see the
MetaPackageManager.__init__method above).
- search(query, extended, exact)[source]ΒΆ
Search packages available for install.
There is no need for this method to be perfect and sensitive to
extendedandexactparameters. If the package manager is not supporting these kind of options out of the box, just returns the closest subset of matching package you can come up with. Finer refiltering will happens in themeta_package_manager.manager.PackageManager.refiltered_search()method below.Optional. Will be simply skipped by mpm if not implemented.
- refiltered_search(query, extended, exact)[source]ΒΆ
Returns search results with extra manual refiltering to refine gross matchings.
Some package managers returns unbounded results, and/or donβt support fine search criterions. In which case we use this method to manually refilters
meta_package_manager.manager.PackageManager.search()results to either exclude non-extended or non-exact matches.Returns a generator producing the same data as the
meta_package_manager.manager.PackageManager.search()method above.Tip
If you are implementing a package manager definition, do not waste time to filter CLI results. Let this method do this job.
Instead, just implement the core
meta_package_manager.manager.PackageManager.search()method above and try to produce results as precise as possible using the native filtering capabilities of the package manager CLI.
- install(package_id, version=None)[source]ΒΆ
Install one package and one only.
Allows a specific
versionto be provided.- Return type:
- upgrade_all_cli()[source]ΒΆ
Returns the complete CLI to upgrade all outdated packages on the system.
- upgrade_one_cli(package_id, version=None)[source]ΒΆ
Returns the complete CLI to upgrade one package and one only.
Allows a specific
versionto be provided.
- upgrade(package_id=None, version=None)[source]ΒΆ
Perform an upgrade of either all or one package.
Executes the CLI provided by either
meta_package_manager.manager.PackageManager.upgrade_all_cli()ormeta_package_manager.manager.PackageManager.upgrade_one_cli().If the manager doesnβt provides a full upgrade one-liner (i.e. if
meta_package_manager.manager.PackageManager.upgrade_all_cli()raisesNotImplementedError), then the list of all outdated packages will be fetched (viameta_package_manager.manager.PackageManager.outdated()) and each package will be updated one by one by callingmeta_package_manager.manager.PackageManager.upgrade_one_cli().See for example the case of
meta_package_manager.managers.pip.Pip.upgrade_one_cli().- Return type:
- remove(package_id)[source]ΒΆ
Remove one package and one only.
Optional. Will be simply skipped by mpm if not implemented.
- Return type:
- remove_orphan(package_id)[source]ΒΆ
Remove one package together with the dependencies it alone pulled in.
The opt-in counterpart to
meta_package_manager.manager.PackageManager.remove(), surfaced asmpm remove --orphans. It maps to the managerβs native βremove and drop now-unneeded dependenciesβ verb (apt remove --auto-remove,pacman --remove --recursive,dnf autoremove, β¦), so mpm builds no dependency graph of its own.Optional. A manager with no such native verb leaves this
NotImplementedError;mpm remove --orphansthen falls back tometa_package_manager.manager.PackageManager.remove()and logs oneINFOcapability-skip.- Return type:
- sync()[source]ΒΆ
Refresh package metadata from remote repositories.
Optional. Will be simply skipped by mpm if not implemented.
- Return type:
- cleanup()[source]ΒΆ
Run the managerβs non-destructive cleanup categories.
Not an operation managers define anymore:
cleanupis the fixed composition of the non-destructive category methods a manager overrides (cleanup_cache(), thencleanup_repair()). The orphan sweep never joins in, native or synthesized: it is the one category that removes packages, so it only runs on an explicitmpm cleanup --orphans(or a directcleanup_orphan()call), keeping a plaincleanuppackage-preserving on every manager.A manager overriding no category method does not advertise the
cleanupoperation at all (seemeta_package_manager.capabilities.implements()) and this composer is then a no-op.- Return type:
- cleanup_orphan()[source]ΒΆ
Remove every orphaned package on the system, sparing the caches.
The system-wide βremove all packages nothing depends on anymoreβ sweep (
apt autoremove,brew autoremove,flatpak uninstall --unused, β¦). The one cleanup category that removes packages, so it is deliberately kept out of the plaincleanup()composition and only runs on an explicitmpm cleanup --orphans.Distinct from
meta_package_manager.manager.PackageManager.remove_orphan(), which is scoped to one packageβs own orphaned dependencies. As withcleanup(), mpm builds no dependency graph: the manager decides what is orphaned.A manager with no native sweep verb is backfilled by this base implementation when it supports both the
orphansquery and package removal: list the orphans, remove each one (withremove_orphan()when available, so every listed root takes its own now-orphaned subtree along), then re-query and repeat until the listing settles, since removing an orphan can orphan its own dependencies. The exact pattern of the synthesized fullupgrade --all, and the in-process equivalent of Archβs classicpacman -Rns $(pacman -Qtdq)idiom. The re-query loop stops as soon as a round makes no progress, so removal failures cannot spin it forever.A manager implementing neither a native sweep nor the
orphansquery propagatesNotImplementedError, andmpm cleanup --orphanssimply skips it.- Return type:
- cleanup_cache()[source]ΒΆ
Prune the managerβs caches, downloads and other left-over artifacts.
The cache category of
cleanup(), surfaced asmpm cleanup --cacheand subtracted by--skip-cache(apt clean,dnf clean all,brew cleanup,npm cache clean, β¦). The broadest category: for most managers the whole cleanup amounts to it.Optional. Will be simply skipped by mpm if not implemented.
- Return type:
- cleanup_repair()[source]ΒΆ
Verify and repair the managerβs local installation state.
The repair category of
cleanup(), surfaced asmpm cleanup --repairand subtracted by--skip-repair(flatpak repair --user).Optional. Will be simply skipped by mpm if not implemented.
- Return type:
- doctor_cli()[source]ΒΆ
Returns the complete CLI running the managerβs native self-diagnosis.
The invocation must be read-only (
brew doctor,pip check,pacman --database --check, β¦):doctor()runs it, never mpmβs mutating machinery. The surveyed doctor verbs share one convention this contract leans on: a non-zero exit code means problems were found.Optional. Will be simply skipped by mpm if not implemented.
- doctor()[source]ΒΆ
Run the native self-diagnosis, returning
(healthy, report).Runs
doctor_cli()and interprets the outcome with a contract of its own, distinct from every other operation:Health is the exit code alone.
run()βs failure gate tolerates a non-zero exit with a silent<stderr>(a benign status for query parsers), but for a diagnosis that exit is the verdict:pip checkreports its conflicts on<stdout>only and would read as healthy under the gate.The report merges both streams. The tools split their findings across them (
brew doctorwarns on<stderr>), and the report is relayed verbatim to the user: there is nothing to parse.The diagnosis is not an error. The failure-gate entry an unhealthy exit may have accumulated is reclaimed from
cli_errors, so the end-of-run error summary is not inflated by a verdict mpm doctor``already reports on its own. The gateβs``WARNING diagnosis relay is skipped for the same reason (doctorsits in the gateβs_DIAGNOSIS_EXEMPT_OPERATIONS): the findings land in the report, verbatim. A run that never completed (timeout, interrupt, missing binary) keeps its entry: that is a genuine plumbing error, and the manager reports unhealthy.
- discover_projects()[source]ΒΆ
Locate project trees this manager governs by scanning the filesystem.
Extension point reserved for
ManagerScope.PROJECTmanagers: detecting virtual environments, lockfiles, or project manifests scattered across the filesystem.Caution
Not implemented for any manager yet. System-scoped managers (the default) own no project trees to discover.
Todo
Candidate ecosystems for project-scope discovery. Listed with the project files that signal each, grouped by whether
mpmalready ships a system-scoped manager that could grow a project mode.Already covered by a manager (
npm,yarn,pnpm,pip,uv,cargo,gem,composer,cpan):JavaScript:
package.json,package-lock.json,yarn.lock,pnpm-lock.yamlPython:
requirements.txt,pyproject.toml,poetry.lock,uv.lockRust:
Cargo.toml,Cargo.lockRuby:
Gemfile,Gemfile.lockPHP:
composer.json,composer.lockPerl:
cpanfile
No manager yet:
Java:
pom.xml(Maven),build.gradle(Gradle),ivy.xmlGo:
go.mod,go.sum.NET:
*.csproj,packages.config(NuGet)Swift:
Package.swift,Package.resolvedCocoaPods:
Podfile,Podfile.lockC/C++:
conanfile.txt(Conan),vcpkg.json(vcpkg)Conda:
conda-lock.yml
meta_package_manager.package moduleΒΆ
Manager-agnostic meta_package_manager.package.Package data model
and the meta_package_manager.package.PackageMetadata companion
that augments it with data pulled from sources outside the package manager
itself.
Defines the lightweight representation of a package (ID, name, installed and latest
versions, architecture) that every manager operation yields, plus
meta_package_manager.package.packages_asdict() to serialize a subset of its
fields for output.
Package is the inventory plane: what the package manager itself
reports through its native query commands. It backs every operation in
meta_package_manager.manager.
PackageMetadata is the enrichment plane: licenses, supplier,
checksums, declared dependency graph, on-disk per-package SBOMs, and other
facts gathered through extra queries (CLI sub-commands, on-disk parsers,
upstream registries). Populated by
meta_package_manager.manager.PackageManager.package_metadata_batch(),
consumed by meta_package_manager.sbom today and reserved for any
future caller that wants more than the bare inventory.
Kept deliberately free of manager logic, so it can be imported without pulling in the
manager engine (meta_package_manager.manager).
- class meta_package_manager.package.Package(id, manager_id, name=None, description=None, installed_version=None, latest_version=None, arch=None)[source]ΒΆ
Bases:
objectLightweight representation of a package and its metadata.
- manager_id: strΒΆ
Handy to backtrack whose manager this package belongs to.
The manager ID is good enough and allows for no coupling with the parent manager object.
- name: str | None = NoneΒΆ
Optional human-readable display name. Falls back to
idin output rendering, so only set this when the manager provides a name that differs from the package ID.
- installed_version: TokenizedString | str | None = NoneΒΆ
- latest_version: TokenizedString | str | None = NoneΒΆ
Installed and latest versions are optional: theyβre not always provided by the package manager.
installed_versionandlatest_versionare allowed to temporarily be strings between__init__and__post_init__. Once they reach the later, theyβre parsed and normalized into eitherTokenizedStringorNone. They canβt be strings beyond that point, i.e. after the Package instance has been fully instantiated. We donβt know how to declare this transient state with type hints, so weβre just going to allow string type.
- property purl: PackageURLΒΆ
Returns the packageβs pURL object.
- static query_parts(query)[source]ΒΆ
Split
queryinto its contiguous alphanumeric segments.Contrary to
meta_package_manager.version.TokenizedString, does not split on collated number/alphabetic junctions.Canonical tokenizer behind
matches()and thesearch/installed/outdatedquery matching.
- matches(query, extended=False, exact=False)[source]ΒΆ
Tell whether this package matches the free-form
query.Shared predicate behind the
search,installedandoutdatedsubcommands, so all three honor the same matching semantics:Fuzzy (default): a case-insensitive, tokenized substring match. Any alphanumeric segment of
query(seequery_parts()) found in the package ID or name counts as a match.Exact (
exact=True): the rawquerymust equal the package ID or name verbatim (case-sensitive, whole-string).Extended (
extended=True): also look into the packagedescription. Only meaningful when the description is populated, as it is forsearchresults.
A query with no alphanumeric segment (empty or punctuation-only) never matches.
- Return type:
- meta_package_manager.package.packages_asdict(packages, keep_fields)[source]ΒΆ
Returns a list of packages casted to a
dictwith only a subset of its fields.
- class meta_package_manager.package.DependencyScope(*values)[source]ΒΆ
-
Maps loosely onto SPDX
RelationshipTypevariants.SBOM renderers translate these into
RUNTIME_DEPENDENCY_OF,BUILD_DEPENDENCY_OF, etc.; CycloneDX collapses everything to its flatdependenciesgraph. Future non-SBOM consumers can apply their own mapping or just expose the raw scope label.- RUNTIME = 'runtime'ΒΆ
- BUILD = 'build'ΒΆ
- DEV = 'dev'ΒΆ
- OPTIONAL = 'optional'ΒΆ
- TEST = 'test'ΒΆ
- RECOMMENDED = 'recommended'ΒΆ
- class meta_package_manager.package.ChecksumAlgorithm(*values)[source]ΒΆ
-
Subset of algorithms shared by SPDX and CycloneDX schemas.
Used by
Checksumto identify a content hash without coupling the data model to any specific SBOM libraryβs enum.- MD5 = 'MD5'ΒΆ
- SHA1 = 'SHA1'ΒΆ
- SHA256 = 'SHA256'ΒΆ
- SHA512 = 'SHA512'ΒΆ
- SHA3_256 = 'SHA3-256'ΒΆ
- SHA3_512 = 'SHA3-512'ΒΆ
- BLAKE2B_256 = 'BLAKE2b-256'ΒΆ
- BLAKE2B_512 = 'BLAKE2b-512'ΒΆ
- class meta_package_manager.package.Checksum(algorithm, value)[source]ΒΆ
Bases:
objectA single
(algorithm, value)pair.- algorithm: ChecksumAlgorithmΒΆ
- class meta_package_manager.package.Supplier(name, url=None)[source]ΒΆ
Bases:
objectDistributor of the package.
Distinct from the originator: the supplier is whoever served the bits (Homebrew, Debian, PyPI), the originator is the upstream author.
- class meta_package_manager.package.Originator(name, email=None, is_organization=False)[source]ΒΆ
Bases:
objectUpstream author or organization that produced the package.
- class meta_package_manager.package.Dependency(target_id, scope=DependencyScope.RUNTIME, version_constraint=None)[source]ΒΆ
Bases:
objectA single edge in the packageβs declared dependency graph.
target_idis the dependencyβs manager-native identifier (e.g.openssl@3for Homebrew). Renderers match it against the inventoryβs installed packages to decide whether to emit a relationship.- scope: DependencyScope = 'runtime'ΒΆ
- class meta_package_manager.package.FileEntry(path, sha256=None, sha1=None, md5=None)[source]ΒΆ
Bases:
objectAn installed file shipped by the package.
Only populated for managers that can cheaply enumerate file contents and hashes (dpkg
.md5sums, pipRECORD). Omitted otherwise; the SBOM renderer leavesfilesAnalyzed=Falseon the SPDX Package.
- class meta_package_manager.package.PackageMetadata(download_url=None, homepage=None, vcs_url=None, issue_tracker_url=None, distribution_url=None, license_declared=None, license_concluded=None, copyright_text=None, supplier=None, originator=None, description=None, summary=None, cpe=None, dependencies=(), checksums=(), files=(), files_analyzed=False, install_date=None, build_date=None, release_date=None, external_sbom_path=None, extra_purls=(), extras=<factory>)[source]ΒΆ
Bases:
objectMaximalist metadata collected for a single installed package.
Distinct from
Packagein scope: wherePackagecarries only what the package manager itself surfaces through its inventory commands (id, name, version, arch),PackageMetadatacarries the augmentations gathered through extra queries (richer CLI sub-commands, on-disk parsing of dist-info or per-package SBOMs, upstream registry lookups). Today it powers the maximalistmpm sbom --bundledoutput; the structure is deliberately generic so a future search, audit, or info display can reuse it.All fields are optional.
extrasis the escape hatch for manager- native fields that donβt fit the portable model: a Homebrew tap, a pip classifier list, an aptSection. SBOM renderers consult known keys and surface the rest as CycloneDXproperties.- originator: Originator | None = NoneΒΆ
- dependencies: tuple[Dependency, ...] = ()ΒΆ
- external_sbom_path: Path | None = NoneΒΆ
Path to an on-disk upstream SBOM document for this package.
Brew formulae installed with
HOMEBREW_SBOM=1write a per-formula SPDX 2.3 file at<prefix>/sbom.spdx.json. The Homebrew extractor sets this so the SBOM renderer can merge the upstream document into the aggregate output (or attach it by reference).
- extra_purls: tuple[PackageURL, ...] = ()ΒΆ
Additional purls when the manager identifies the same package through multiple coordinate systems (multi-arch, multi-origin).
- meta_package_manager.package.EMPTY_METADATA = PackageMetadata(download_url=None, homepage=None, vcs_url=None, issue_tracker_url=None, distribution_url=None, license_declared=None, license_concluded=None, copyright_text=None, supplier=None, originator=None, description=None, summary=None, cpe=None, dependencies=(), checksums=(), files=(), files_analyzed=False, install_date=None, build_date=None, release_date=None, external_sbom_path=None, extra_purls=(), extras={})ΒΆ
Sentinel returned by the default no-op extractor on the base
meta_package_manager.manager.PackageManager. Consumers (the SBOM renderers today) treatEMPTY_METADATAexactly like--minimalmode for the package: no enrichment, no placeholders.
meta_package_manager.platforms moduleΒΆ
Top-level platform classification shared across mpm.
Defines MAIN_PLATFORMS, the curated platform groups (BSD, Linux, macOS,
Unix, Windows) used to label managers in the CLI managers matrix, in the GitHub
issue/PR platform labels, and in the documentation tables.
- meta_package_manager.platforms.MAIN_PLATFORMS: tuple[Group | Platform, ...] = (Group(id='bsd', name='BSD'), Group(id='linux', name='Linux'), Platform(id='macos', name='macOS'), Group(id='unix', name='Unix'), Group(id='windows', name='Windows'))ΒΆ
Top-level classification of platforms.
This is the local reference used to classify the execution targets of
mpm.Each entry of this list will have its own dedicated column in the matrix. This list is manually maintained with tweaked IDs and names to minimize the matrix verbosity and make it readable both in CLI and documentation.
The order of this list determine the order of the resulting columns.
meta_package_manager.pool moduleΒΆ
Registration, indexing and caching of package manager supported by mpm.
- meta_package_manager.pool.manager_classes = (<class 'meta_package_manager.managers.apk.APK'>, <class 'meta_package_manager.managers.apm.APM'>, <class 'meta_package_manager.managers.apt.APT'>, <class 'meta_package_manager.managers.apt.APT_Mint'>, <class 'meta_package_manager.managers.asdf.ASDF'>, <class 'meta_package_manager.managers.homebrew.Brew'>, <class 'meta_package_manager.managers.homebrew.Cask'>, <class 'meta_package_manager.managers.composer.Composer'>, <class 'meta_package_manager.managers.conda.Conda'>, <class 'meta_package_manager.managers.deb_get.Deb_Get'>, <class 'meta_package_manager.managers.dnf.DNF'>, <class 'meta_package_manager.managers.dnf.DNF5'>, <class 'meta_package_manager.managers.emerge.Emerge'>, <class 'meta_package_manager.managers.eopkg.EOPKG'>, <class 'meta_package_manager.managers.flatpak.Flatpak'>, <class 'meta_package_manager.managers.fwupd.FWUPD'>, <class 'meta_package_manager.managers.gem.Gem'>, <class 'meta_package_manager.managers.guix.Guix'>, <class 'meta_package_manager.managers.mas.MAS'>, <class 'meta_package_manager.managers.mise.Mise'>, <class 'meta_package_manager.managers.nix.Nix'>, <class 'meta_package_manager.managers.npm.NPM'>, <class 'meta_package_manager.managers.pacman.Pacaur'>, <class 'meta_package_manager.managers.pacman.Pacman'>, <class 'meta_package_manager.managers.pacstall.Pacstall'>, <class 'meta_package_manager.managers.pacman.Paru'>, <class 'meta_package_manager.managers.pip.Pip'>, <class 'meta_package_manager.managers.pipx.Pipx'>, <class 'meta_package_manager.managers.pkcon.Pkcon'>, <class 'meta_package_manager.managers.pkg.PKG'>, <class 'meta_package_manager.managers.pnpm.PNPM'>, <class 'meta_package_manager.managers.pkg.Ports'>, <class 'meta_package_manager.managers.pwsh_gallery.PWSH_Gallery'>, <class 'meta_package_manager.managers.scoop.Scoop'>, <class 'meta_package_manager.managers.sdkman.SDKMAN'>, <class 'meta_package_manager.managers.sfsu.SFSU'>, <class 'meta_package_manager.managers.snap.Snap'>, <class 'meta_package_manager.managers.sun_tools.Sun_Tools'>, <class 'meta_package_manager.managers.tazpkg.Tazpkg'>, <class 'meta_package_manager.managers.uv.UV'>, <class 'meta_package_manager.managers.uv.UVX'>, <class 'meta_package_manager.managers.volta.Volta'>, <class 'meta_package_manager.managers.winget.WinGet'>, <class 'meta_package_manager.managers.xbps.XBPS'>, <class 'meta_package_manager.managers.yarn.YarnBerry'>, <class 'meta_package_manager.managers.yarn.YarnClassic'>, <class 'meta_package_manager.managers.pacman.Yay'>, <class 'meta_package_manager.managers.dnf.YUM'>, <class 'meta_package_manager.managers.zypper.Zypper'>)ΒΆ
The list of all classes implementing the specific package managers.
Is considered valid package manager, definitions classes which:
- are located in the
meta_package_manager.pool.ManagerPool.manager_subfolder subfolder, and
- are located in the
are sub-classes of
meta_package_manager.manager.PackageManager, and- are not
meta_package_manager.manager.PackageManager.virtual(i.e. have a non-null
meta_package_manager.manager.PackageManager.cli_namesproperty).
- are not
These properties are checked and enforced in unittests.
- class meta_package_manager.pool.ManagerPool[source]ΒΆ
Bases:
objectA dict-like register, instantiating all supported package managers.
- ALLOWED_EXTRA_OPTION: Final = frozenset({'cooldown', 'dry_run', 'ignore_auto_updates', 'plan', 'progress', 'require_cooldown_support', 'stop_on_error', 'sudo', 'timeout'})ΒΆ
List of extra options that are allowed to be set on managers during the use of the
meta_package_manager.pool.ManagerPool.select_managers()helper below.
- property register: dict[str, PackageManager]ΒΆ
Instantiate all supported package managers.
Built-in classes first, then mpmβs bundled configuration-defined managers (built from shipped
*.tomlpackage data). Both land here at construction time, so the augmented pool is complete before the CLI enumerates it to build the dynamic--<id>flags, in every context including the test runner.
- property builtin_manager_ids: frozenset[str]ΒΆ
IDs of the managers shipped with mpm, taken from
manager_classes.Computed from the classes (their
idis set by the metaclass at class creation, no instantiation needed). Lets the configuration layer tell a built-in override apart from a brand-new manager definition: a[mpm.managers.<id>]section whose ID is in this set tunes a built-in, any other ID defines a new manager. Seemeta_package_manager.config.validate_manager_overrides_section().
- property config_defined_ids: set[str]ΒΆ
IDs of managers added at runtime from configuration definitions.
Populated by
add_manager(). Disjoint frombuiltin_manager_ids.
- property bundled_manager_ids: frozenset[str]ΒΆ
IDs of the managers mpm ships as bundled configuration definitions.
Config-defined (built from shipped
*.tomlpackage data, not a Python class) yet always present inregisterlike the built-ins. Disjoint frombuiltin_manager_idsandconfig_defined_ids.
- property known_manager_ids: frozenset[str]ΒΆ
Every manager ID mpm ships: built-in classes plus bundled definitions.
A
[mpm.managers.<id>]section keyed by one of these tunes a shipped manager (an override); any other ID defines a brand-new one. The configuration layer routes override-versus-definition on this set. Seemeta_package_manager.config.validate_manager_overrides_section().
- property overridden_fields: dict[str, set[str]]ΒΆ
Per-manager attribute names that the user explicitly overrode via
[mpm.managers.<id>].Populated by
meta_package_manager.config.apply_manager_overrides(). Read by_select_managersto skip the global--<flag>defaults for fields the user has explicitly set per manager. Tracked separately from instance__dict__membership so the global defaults can still refresh fields that were previously set by an earlier_select_managerscall but were never user-overridden.
- get(key)ΒΆ
- add_manager(manager)[source]ΒΆ
Register a runtime-built manager (from a config definition) into the pool.
Inserts the instance and evicts the cached ID lists so the new manager is picked up by selection, default-set computation and the dynamic CLI flags. Built into the pool (rather than mutating
registerfrom outside) so the cache invalidation stays in one place. Applied bymeta_package_manager.config.register_config_managers().- Return type:
- property all_manager_ids: tuple[str, ...]ΒΆ
All recognized manager IDs.
Returns a list of sorted items to provide consistency across all UI, and reproducibility in the order package managers are evaluated.
- property default_manager_ids: tuple[str, ...]ΒΆ
All manager IDs supported on the current platform and not unmaintained.
Must keep the same order defined by
meta_package_manager.pool.ManagerPool.all_manager_ids.
- property unsupported_manager_ids: tuple[str, ...]ΒΆ
All manager IDs unsupported on the current platform but still maintained.
Order is not important here as this list will be used to discard managers from selection sets.
meta_package_manager.specifier moduleΒΆ
Utilities to manage and resolve constraints from a set of package specifiers.
- meta_package_manager.specifier.VERSION_SEP: Final = '@'ΒΆ
Separator used by
mpmto split packageβs ID from its version:This has been chosen as a separator because it is shared by popular package managers (like
npm) and pURLs...code-block:
package_id@version
- meta_package_manager.specifier.PURL_MAP: dict[str, set[str] | None] = {'alpine': None, 'alpm': {'pacaur', 'pacman', 'paru', 'yay'}, 'android': None, 'apache': None, 'apk': {'apk'}, 'bitbucket': None, 'bitnami': None, 'bower': None, 'buildroot': None, 'cargo': {'cargo'}, 'carthage': None, 'chef': None, 'chocolatey': {'choco'}, 'clojars': None, 'cocoapods': None, 'composer': {'composer'}, 'conan': None, 'conda': None, 'coreos': None, 'cpan': {'cpan'}, 'cran': None, 'crystal': None, 'ctan': None, 'deb': {'apt', 'apt-mint'}, 'docker': None, 'drupal': None, 'dtype': None, 'dub': None, 'ebuild': {'emerge'}, 'eclipse': None, 'elm': None, 'gem': {'gem'}, 'generic': None, 'gitea': None, 'github': None, 'gitlab': None, 'golang': None, 'gradle': None, 'guix': {'guix'}, 'hackage': None, 'haxe': None, 'helm': None, 'hex': None, 'huggingface': None, 'julia': None, 'luarocks': None, 'maven': None, 'melpa': None, 'meteor': None, 'mlflow': None, 'nim': None, 'nix': {'nix'}, 'npm': {'npm', 'pnpm', 'volta', 'yarn', 'yarn-berry'}, 'nuget': None, 'oci': None, 'opam': None, 'openwrt': {'opkg'}, 'osgi': None, 'p2': None, 'pear': None, 'pecl': None, 'perl6': None, 'platformio': None, 'pub': None, 'puppet': None, 'pypi': {'pip', 'pipx', 'uv'}, 'qpkg': None, 'rpm': {'dnf', 'dnf5', 'yum', 'zypper'}, 'rubygems': {'gem'}, 'sourceforge': None, 'sublime': None, 'swid': None, 'terraform': None, 'vagrant': None, 'vim': None, 'wordpress': None, 'yocto': None}ΒΆ
Map pURLβs types to MPMβs manager IDs.
Keys are recognized pURLβs types, and values are the set of MPMβs manager IDs that can handle the package type.
Warning
There is no official list of
pkg:<type>/...prefixes defined in the pURL specification.The only source we found lying around in the pURL literature is this list of diverse aliases, examples and libraries. We use this document to compile the keys of this
PURL_MAPmapping.Todo
Reuse the mapping that is proposed upstream to the package-url Python project.
- class meta_package_manager.specifier.Specifier(raw_spec, package_id, manager_id=None, version=None)[source]ΒΆ
Bases:
objectLightweight representation of a package specification.
Contains all parsed metadata to be used as constraints.
- classmethod parse_purl(spec_str)[source]ΒΆ
Resolve a pURL into its corresponding manager candidates.
Yields
Specifierobjects or returnsNone.
- classmethod from_string(spec_str)[source]ΒΆ
Parse a string into a package specifier.
Supports various formats: - plain
package_id- simple package ID with version:package_id@version- package with multiple version separators:@eslint/json@0.9.0- pURL:pkg:npm/left-pad@3.7If a specifier resolves to multiple constraints (as it might be the case for pURL), we produce and returns all variations. That way the
Solverbelow has all necessary details to resolve the constraints.Returns a tuple of
Specifier.
- property parsed_version: TokenizedStringΒΆ
- exception meta_package_manager.specifier.EmptyReduction[source]ΒΆ
Bases:
ExceptionRaised by the solver if no constraint canβt be met.
- class meta_package_manager.specifier.Solver(spec_strings=None, manager_priority=None)[source]ΒΆ
Bases:
objectCombine a set of
Specifierand allow for the solving of the constraints they represent.- populate_from_strings(spec_strings)[source]ΒΆ
Populate the solver with package specifiers parsed from provided strings.
- top_priority_manager(keep_managers=None)[source]ΒΆ
Returns the top priority manager configured on the solver.
keep_managersallows for filtering by discarding managers not in that list.
- reduce_specs(specs)[source]ΒΆ
Reduce a collection of
Specifierto its essential, minimal and unique form.This method assumes that all provided
specsare of the same package (likeresolve_package_specs()does).The reduction process consist of several steps. At each step, as soon as we managed to reduce the constraints to one
Specifier, we returns it.Filtering steps:
We remove all constraints tied to all by the top priority manager if provided.
If no manager priority is provided, we discard constraints not tied to a manager.
We discard constraints not tied to a version.
We only keep constraints tied to the highest version.
If we ends up with more than one set of constraints after all this filtering, an error is raised to invite the developer to troubleshoot the situation and refine this process.
- Return type:
- resolve_package_specs()[source]ΒΆ
Regroup specs of the pool by package IDs, and solve their constraints.
Each package ID yields one reduced spec per distinct target manager. A package therefore produces several specs when the user explicitly names several managers for it (
pkg:uv/rich pkg:brew/richβ one spec each). In contrast, a single alias pURL that expands to several managers (pkg:rpm/pingβdnf/yum/zypper) is a set of alternatives, reduced to the top-priority one.
meta_package_manager.sudo moduleΒΆ
Privilege-escalation machinery for the mutating fan-outs.
This module owns sudo credential priming (prime_sudo()) and its
background keepalive (_start_sudo_keepalive()), escalation-policy
resolution (_resolved_sudo()), sudo-failure detection
(_is_sudo_auth_failure()), and the hidden-prompt stall watchdog
(_StallWatchdog). The execution engine
(meta_package_manager.execution) consumes the policy pieces to wrap and
diagnose escalated commands; the CLI calls prime_sudo() at the top of
each mutating subcommand.
Why priming exists: a concurrent state-changing command mutes per-manager output
and feeds each child stdin=/dev/null, so a sudo password prompt raised
mid-run (by mpmβs own sudo --non-interactive or by a manager that escalates
internally, like
Homebrew cask) lands invisibly on /dev/tty and can stall the run up to the
mutating timeout. Priming first probes the credential cache non-interactively:
found warm, it is silently kept alive for the whole run; found cold on a terminal,
the managers mpm itself escalates get a single up-front password prompt, naming
them and branded [mpm]. Internal escalators never prompt up front: their rare
cold-cache escalation is covered by the silent-call stall notice instead, raised
while the hidden prompt can still be answered.
Note
Everything in this module is UNIX-only: a Windows run returns early at
prime_sudo()βs guard and never arms the watchdog (the internal
escalators are macOS-only managers today).
- meta_package_manager.sudo.prime_sudo(ctx, managers)[source]ΒΆ
Warm the
sudocredential cache, up front, for a mutating fan-out.Probes the cache non-interactively (
sudo --non-interactive --validate) before considering any prompt. A warm cache (pre-authenticatedsudo --validate, aNOPASSWDrule, a recent run) is silently kept fresh for the whole invocation by_start_sudo_keepalive(), so every later escalation on the same terminal, mpmβs ownsudo --non-interactiveas well as a managerβs internalsudo(CLIExecutor.internal_sudo), spends the cache instead of blocking on an invisible prompt inside the concurrent fan-out. Only a cold cache, on an interactive terminal, with managers that mpm itself escalates (_resolved_sudo()), triggers the interactive path: a notice naming the managers and the subcommand, then a single brandedsudopassword prompt.Call at the top of each mutating subcommand, before the fan-out draws its spinner. Never prompts when:
Windows (no
sudo) or the process is already root,no selected manager escalates, through mpm or internally,
a dry run or a plan run (no state-changing CLI is executed),
already primed once this invocation (idempotent),
the
sudoexecutable is missing (one warning is logged),the probe finds the cache already warm (keepalive only, fully silent),
no interactive terminal is available: one warning names the managers mpm escalates and leaves them to fail fast rather than block on a prompt no one can answer, while an internal-only selection stays silent, or
only internal escalators are selected on a cold cache: most such runs never escalate, so the rare mid-run prompt is covered by the silent-call stall notice instead.
- Return type:
meta_package_manager.summary moduleΒΆ
End-of-run summary printing for mpm subcommands.
Every long-running subcommand (installed, outdated, search,
dump, sbom) closes with a one-line summary written to stderr:
223 packages total (brew: 223).
Plus optional follow-up lines specific to that subcommand (the SBOM
writer surfaces upstream-document merge counts and dependency-graph
edge counts here). The whole summary is gated by the global
--summary/--no-summary flag and respects the userβs choice across
every subcommand uniformly.
Vocabulary note: βsummaryβ describes the rendered text that lands on
stderr. βStatsβ describes the raw numbers fed into it
(meta_package_manager.sbom.base.SBOM.stats() returns a dict of
counts). The two terms stay distinct deliberately: the
flag/module/function name reflects what the user sees; the data-side
method keeps the unambiguous stats name.
This module is the single home of the summary contract:
print_summary()is the renderer.package_counts()collapses the boilerplateCounter({manager_id: len(payload[manager_id]["packages"]) for ...})pattern thatinstalled,outdated, andsearchall share.sbom_summary()adaptsmeta_package_manager.sbom.base.SBOM.stats()to the(counter, notes)shapeprint_summary()consumes, conditional on what the run actually did.
The renderer stays in this module rather than scattered across each subcommand so the visual format is unique and obvious to find. The adapters live here too because their job is to translate subcommand-native shapes into the print contract, which is also summary-domain logic.
- meta_package_manager.summary.print_summary(counts, notes=())[source]ΒΆ
Print a one-line per-category count to stderr, plus optional follow-up notes.
countsis acollections.Counterkeyed by an opaque category label. The label is usually a package manager id, but thedump --brewfilesubcommand uses Brewfile entry types and any future caller is free to use whatever bucket makes sense. The parameter is namedcountsrather thanmanager_statsto avoid lying about the keyβs meaning.Prints something like:
10 packages total (brew: 2, pip: 2, gem: 2, vscode: 2, npm: 2, composer: 0).
notesis an iterable of follow-up lines printed verbatim under the count line.mpm sbomuses it to surface facts that donβt fit the per-category-Counter shape: number of upstream SBOM documents merged into the aggregate, enrichment ratios, dependency-graph edge counts. Other subcommands today pass no notes; the count line is enough.Always writes to stderr so the call site is free to pipe stdout elsewhere (a generated SBOM document, a TOML manifest, a Brewfile) without the summary polluting the output. Gated upstream by the global
--summary/--no-summaryflag; this function itself is unconditional once called.- Return type:
- meta_package_manager.summary.package_counts(payload)[source]ΒΆ
Build a per-manager
Counterfrom a typical subcommand payload.installed,outdated, andsearchall stash their results in a{manager_id: {"packages": [...]}}dict. This helper turns that into the count-by-manager-idCounterthatprint_summary()accepts, eliminating theCounter({k: len(v["packages"]) for k, v in payload.items()})boilerplate that appeared verbatim at three CLI call sites.Mismatched payloads (an extractor that stashes packages under a different key, the
dump --brewfileline-counter pass) build theirCounterinline rather than wedging this helper into serving every shape.
- meta_package_manager.summary.sbom_summary(sbom, bundled)[source]ΒΆ
Adapt
meta_package_manager.sbom.base.SBOM.stats()to theprint_summary()shape.SBOM stats live on the renderer because the renderer knows what actually landed in the document (after dedup, after merge). This adapter flattens that structured dict into the count-line + follow-up-notes shape
print_summary()consumes, conditioning each note on what the run actually did so--minimalscans, casks-only runs, and formats without a merge concept all stay tidy.The function lives in this module (rather than next to the SBOM renderers) because its job is translating between two different data shapes: SBOM stats on one side, the print contract on the other. Summary-domain glue, not SBOM-domain logic.
meta_package_manager.tables moduleΒΆ
Table-output vocabulary and rendering plumbing shared by the subcommands.
The mpm subcommands render heterogeneous tables (different columns per command) but share the same output machinery. This module owns all of it:
SortableField, the vocabulary of the globalmpm --sort-byselector. The selector itself is click-extraβs field-vocabularySortByOption, and the per-table resolution (sort by the selected fields the table carries, keep the original row order when it carries none) happens insideclick_extra.table.print_table(), from the field each header pairs with its column in the registries below.The per-command column registries, each pairing a click-extra
ColumnSpec(whose ID addresses the column from--columns) with theSortableFieldthe column carries (Nonefor a column that cannot drive the sort). A registry is the single source of truth for its command: the same tuple feeds the@columns_optiondeclaration (which validates the user selection) andprint_projected_table()(which projects headers and rows before rendering).print_projected_table()andprint_serialized_and_exit(), the human-friendly and machine-friendly rendering paths every table-producing subcommand goes through.
Note
The registry pairsβ second element is annotated str | None rather than
SortableField | None: on Python 3.10, SortableField extends
backports.strenum.StrEnum, whose stubs type the members as plain
str, so the tighter annotation only checks under 3.11+.
StrEnum members being str subclasses, the wider annotation is
accurate on every supported version.
- class meta_package_manager.tables.SortableField(*values)[source]ΒΆ
Bases:
StrEnumFields IDs allowed to be sorted.
- MANAGER_ID = 'manager_id'ΒΆ
- MANAGER_NAME = 'manager_name'ΒΆ
- PACKAGE_ID = 'package_id'ΒΆ
- PACKAGE_NAME = 'package_name'ΒΆ
- VERSION = 'version'ΒΆ
- meta_package_manager.tables.MANAGERS_COLUMNS: tuple[tuple[ColumnSpec, str | None], ...] = ((ColumnSpec(id='manager_id', label='Manager ID', description="Manager's identifier."), SortableField.MANAGER_ID), (ColumnSpec(id='manager_name', label='Name', description="Manager's common name."), SortableField.MANAGER_NAME), (ColumnSpec(id='supported', label='Supported', description='Support status on the current platform.'), None), (ColumnSpec(id='cli', label='CLI', description="Location of the manager's binary on the system."), None), (ColumnSpec(id='executable', label='Executable', description='Whether the binary is executable.'), None), (ColumnSpec(id='version', label='Version', description="Manager's self-reported version, and the unsatisfied requirement when stale."), SortableField.VERSION))ΒΆ
Columns of the
mpm managerstable.
- meta_package_manager.tables.INSTALLED_COLUMNS: tuple[tuple[ColumnSpec, str | None], ...] = ((ColumnSpec(id='package_id', label='Package ID', description="Package's identifier."), SortableField.PACKAGE_ID), (ColumnSpec(id='package_name', label='Name', description="Package's common name."), SortableField.PACKAGE_NAME), (ColumnSpec(id='manager_id', label='Manager', description='Manager reporting the package.'), SortableField.MANAGER_ID), (ColumnSpec(id='installed_version', label='Installed version', description='Version currently installed.'), SortableField.VERSION))ΒΆ
Columns of the
mpm installedtable.
- meta_package_manager.tables.OUTDATED_COLUMNS: tuple[tuple[ColumnSpec, str | None], ...] = ((ColumnSpec(id='package_id', label='Package ID', description="Package's identifier."), SortableField.PACKAGE_ID), (ColumnSpec(id='package_name', label='Name', description="Package's common name."), SortableField.PACKAGE_NAME), (ColumnSpec(id='manager_id', label='Manager', description='Manager reporting the package.'), SortableField.MANAGER_ID), (ColumnSpec(id='installed_version', label='Installed version', description='Version currently installed.'), SortableField.VERSION), (ColumnSpec(id='latest_version', label='Latest version', description='Version available for upgrade.'), None))ΒΆ
Columns of the
mpm outdatedtable.
- meta_package_manager.tables.SEARCH_COLUMNS: tuple[tuple[ColumnSpec, str | None], ...] = ((ColumnSpec(id='package_id', label='Package ID', description="Package's identifier."), SortableField.PACKAGE_ID), (ColumnSpec(id='package_name', label='Name', description="Package's common name."), SortableField.PACKAGE_NAME), (ColumnSpec(id='manager_id', label='Manager', description='Manager reporting the match.'), SortableField.MANAGER_ID), (ColumnSpec(id='latest_version', label='Latest version', description='Latest version available.'), SortableField.VERSION), (ColumnSpec(id='description', label='Description', description='Package description, for managers that provide one. Out of the default selection: select it explicitly or pass --description.'), None))ΒΆ
Columns of the
mpm searchtable.The
descriptioncolumn exists in the registry (so--columnscan select it) but stays out of the default selection unless--description(or--extended, which searches descriptions) is passed.
- meta_package_manager.tables.WHICH_COLUMNS: tuple[tuple[ColumnSpec, str | None], ...] = ((ColumnSpec(id='manager_id', label='Manager ID', description='Manager whose search path found the binary.'), SortableField.MANAGER_ID), (ColumnSpec(id='priority', label='Priority', description="Rank of the match in the manager's search path."), None), (ColumnSpec(id='cli_path', label='CLI path', description='Location of the matched binary.'), None), (ColumnSpec(id='symlink', label='Symlink destination', description='Resolved target when the match is a symlink.'), None))ΒΆ
Columns of the
mpm whichtable.
- meta_package_manager.tables.column_specs(columns)[source]ΒΆ
Extract the bare
ColumnSpectuple from a column registry.- Return type:
- meta_package_manager.tables.print_projected_table(ctx, columns, rows, default_ids=None)[source]ΒΆ
Render dict
rowsas a table projected through--columns.The
--columnsselection restricts and reorders the rendering, SQL-SELECT-style; click-extraβsColumnsOptionalready validated it against the samecolumnsregistry, so unknown IDs never reach this point.default_idsis the selection applied when the user passed none (searchuses it to hide the description column unless--description);Nonekeeps every column in canonical order.Sorting stays on mpmβs global
--sort-by: each header pairs its label with the sortable field the column carries, and click-extraβsprint_table()reads the selection (with the--table-formatone) from the shared contextmetaand resolves it per table. A sort field whose column is projected out is simply skipped, and a table carrying none of the selected fields keeps its original row order.- Return type:
- meta_package_manager.tables.print_serialized_and_exit(ctx, data)[source]ΒΆ
Render
datain the active serialization format, then exit.When the global
--table-formatresolves to one of the structured serialization formats (JSON, YAML, TOML, XML, β¦), serializedataunder the sharedmpmroot element and stop the program. Otherwise return, so the caller falls through to its human-friendly table rendering.- Return type:
meta_package_manager.version moduleΒΆ
Helpers and utilities to parse and compare version numbers.
mpm wraps dozens of package managers, each with its own versioning
scheme: semver, PEP 440, calendar versioning, Debian epochs, Gentoo
suffixes, and others. Rather than implementing format-specific parsers,
this module provides a universal tokenizer that produces good-enough
ordering across all of them.
DesignΒΆ
The tokenizer splits version strings into alternating digit and
letter tokens at every digit/letter boundary and every
non-alphanumeric separator. Tokens that parse as integers are compared
numerically; the rest are compared as lowercase strings. This gives
natural sort order where (2019, 0, 1) > (9, 3) β something neither
pure-string nor pure-numeric comparison achieves.
Key rules:
Epochs dominate. A leading integer joined by
:(Debian, RPM, pacman) or!(PEP 440) is an epoch: a version-space reset that outranks the rest of the string.2:1.0 > 9.0and1!1.0 > 2.0because epoch2/1beats the implicit epoch0. Versions without an epoch default to0, so they compare unchanged.Integers outrank strings. A numeric token always sorts higher than a string token at the same position. This makes
3.12.0 > 3.12.0a4(release beats alpha) and0.1 > 0.beta2work without understanding PEP 440 or semver pre-release semantics.Trailing zeros are padding.
6.2and6.2.0compare equal. When one token tuple is a prefix of the other and all extra tokens are zero integers, the versions are equivalent.Pre-release suffixes lose. When a release version is a prefix of a longer version whose first significant extra token is a string (e.g.,
"alpha","git"), the shorter release is considered greater.Hex hashes stay whole. A contiguous run of 7+ hex characters with interleaved digits and letters (at least one letter-then-digit and one digit-then-letter adjacency) is kept as a single opaque token. Without this,
g6cd4c31would shatter into("g", 6, "cd", 4, "c", 31). The 7-character floor matchesgitβs default abbreviated hash length (core.abbrev, the de facto standard on GitHub/GitLab/Bitbucket). The interleaving requirement rejects coincidental hex strings like asciified Unicode (eeaccee231), that have only one transition direction.Digit/letter splitting is essential. Splitting
ubuntu1into("ubuntu", 1)enables natural numeric ordering of embedded version numbers:a4 < a10compares correctly because4and10become integer tokens. Without this split,"a4" > "a10"lexicographically.
LimitationsΒΆ
This is a heuristic comparator, not a format-specific parser.
PEP 440 ordering is richer than what we implement.
.devNordering relative to pre-releases is not handled. Usepackaging.versionfor strict PEP 440 compliance. Epochs (1!) are handled β see the epoch rule above.Perl floating-point versions (
1.1 == 1.10) are treated as(1, 1)vs(1, 10)β not equal. The Gentoo three-digit-group conversion scheme is not implemented.Format-specific separators like Java build metadata (
,) or Perl-style floats (.) are treated as plain delimiters, which can produce wrong comparison results when the separator carries structural meaning. The epoch separators:and!are recognized.
ReferencesΒΆ
PEP 440 β Pythonβs version identification spec. Defines
a/b/rcsuffix ordering that our integer-outranks-string rule approximates.Falsehoods about versions β 25 assumptions that break in practice. Validates our approach of not assuming any single format (falsehoods 4, 8, 13) and handling mixed numeric/string tokens (falsehoods 2, 3).
Gentoo Perl version scheme β illustrates how two incompatible formats (dotted-decimal and floating-point) require careful mapping. A reminder that version comparison cannot be reduced to βsplit on dots, compare integers.β
univers β scheme-aware version parsing and comparison (PEP 440, semver, Debian, RPM, Gentoo ebuild, and more) plus the
versrange spec, from the same AboutCode team maintaining purl. The reference implementation to evaluate if this heuristic comparator ever needs per-scheme accuracy.
- meta_package_manager.version.ALNUM_EXTRACTOR_CI = re.compile('(\n (?= [0-9a-f]* [a-f] [0-9] )\n (?= [0-9a-f]* [0-9] [a-f] )\n [0-9a-f]{7,}\n | \\d+\n | [a-z]+\n)', re.IGNORECASE|re.VERBOSE)ΒΆ
Case-insensitive variant used to split the original string and preserve case.
- meta_package_manager.version.TOKEN_ALIASES: dict[str, str] = {'alpha': 'a', 'beta': 'b', 'c': 'rc', 'preview': 'rc'}ΒΆ
Canonical short forms for pre-release tag spellings.
PEP 440 defines
alpha/a,beta/b, andc/rc/previewas equivalent aliases. These appear across ecosystems: Debian uses~alpha, npm uses-alpha, Homebrew usesalpha/beta. The long forms are always interchangeable with the short forms, so normalizing at tokenization time is safe. Normalization only affects comparison tokens, not the original string orpretty_print()output.
- meta_package_manager.version.POST_RELEASE_TAGS: frozenset[str] = frozenset({'patch', 'post'})ΒΆ
Suffixes that indicate a version newer than the base release.
PEP 440 defines
.postNas a post-release.patchcarries the same semantics in some ecosystems (e.g.,1.0-patch1). Without this set, the prefix-comparison rule treats all string suffixes as pre-release indicators, which wrongly makes1.0 > 1.0.post1.This set is deliberately small. Only tags with unambiguous βnewer than releaseβ semantics across multiple ecosystems belong here. Candidates like
revorpare excluded because they can also mean βrevisionβ (Gentoo-r0) or βpre-release patchlevelβ (FreeBSDp1), depending on context.
- class meta_package_manager.version.Token(value)[source]ΒΆ
Bases:
objectA normalized word, persisting its lossless integer variant.
Supports natural comparison with
strandinttypes. Used to compare versions and package IDs.Instantiates a
Tokenfrom an alphanumeric string or a non-negative integer.
- class meta_package_manager.version.TokenizedString(value)[source]ΒΆ
Bases:
objectTokenize a string for user-friendly sorting.
Essentially a wrapper around a list of
Tokeninstances.Parse and tokenize the provided raw
value.- pretty_print()[source]ΒΆ
Reconstruct the tokenized string using original-case segments and separators.
- Return type:
- static tokenize(string)[source]ΒΆ
Tokenize a string: ignore case and split at each non-alphanumeric characters.
Returns a tuple of
Tokeninstances, separator strings between consecutive tokens, and original-case segment strings for lossless display.re.split()with a capturing group alternates non-matching segments (even indices) and captured matches (odd indices):ALNUM_EXTRACTOR.split("4.2.1-5666.3") ['', '4', '.', '2', '.', '1', '-', '5666', '.', '3', ''] pre m sep m sep m sep m sep m suf
- meta_package_manager.version.parse_versionΒΆ
Alias for
TokenizedStringused in version-comparison contexts.
- meta_package_manager.version.OPERATOR_MAP: dict[str, Callable[[TokenizedString, TokenizedString], bool]] = {'!=': <built-in function ne>, '<': <built-in function lt>, '<=': <built-in function le>, '==': <built-in function eq>, '>': <built-in function gt>, '>=': <built-in function ge>}ΒΆ
Comparison operators recognized in a version range, mapped to their callable.
- meta_package_manager.version.RANGE_OPERATOR = re.compile('(?P<op>>=|<=|==|!=|>|<)\\s*(?P<version>.+)')ΒΆ
Matches a comparison operator prefix followed by a version string.
- class meta_package_manager.version.VersionRange(spec)[source]ΒΆ
Bases:
objectA set of version constraints parsed from a comma-separated specifier string.
Each constraint is an
(operator, version)pair. A version satisfies the range only if it satisfies every constraint.Bare version strings (no operator prefix) are treated as
>=.
- meta_package_manager.version.is_version(string)[source]ΒΆ
Returns
Trueif the string looks like a version.Heuristics: at least one token is an integer, or there is only one non-integer token.
- Return type:
- meta_package_manager.version.diff_versions(old, new, prefix_fg='bright_black', old_fg='red', new_fg='green')[source]ΒΆ
Color the common prefix gray, the old suffix red, the new suffix green.
The split point snaps to the nearest separator boundary so the full diverging token and its preceding separator are highlighted. For
2.1.1774638290vs2.1.1774896198, the common part is2.1and the diff includes.1774638290/.1774896198.prefix_fg,old_fgandnew_fgoverride the common-prefix, old-suffix and new-suffix colors, in any form accepted byclick_extra.style()(a named ANSI color or an xterm-256 palette index). Renderers whose consumer maps the named defaults poorly, like the bar plugin on a light translucent menu, pass their own.