meta_package_manager packageΒΆ

Expose package-wide elements.

SubpackagesΒΆ

SubmodulesΒΆ

meta_package_manager.bar_plugin moduleΒΆ

Xbar and SwiftBar plugin for Meta Package Manager (i.e. 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.

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.

See: https://github.com/swiftbar/SwiftBar/issues/445

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.

class meta_package_manager.bar_plugin.MPMPlugin[source]ΒΆ

Bases: object

Implements the minimal code necessary to locate and call the mpm CLI on the system.

Once mpm is 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.

Return type:

str | None

static getenv_bool(var, default=False)[source]ΒΆ

Utility to normalize boolean environment variables.

Relies on configparser.RawConfigParser.BOOLEAN_STATES to translate strings into boolean. See: https://github.com/python/cpython/blob/89192c4/Lib/configparser.py#L597-L599

Return type:

bool

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_ids set and ignores the rest. By default, only color, font and size are 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:

str

static str_to_version(version_string)[source]ΒΆ

Transforms a string into a tuple of integers representing a version.

Return type:

tuple[int, ...]

static version_to_str(version_tuple)[source]ΒΆ

Transforms a tuple of integers representing a version into a string.

Return type:

str

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.

property default_font: strΒΆ

Make it easier to change font, sizes and colors of the output.

property monospace_font: strΒΆ

Make it easier to change font, sizes and colors of the output.

property error_font: strΒΆ

Error font is based on monospace font.

property is_swiftbar: boolΒΆ

SwiftBar is kind enough to tell us about its presence.

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 mpm from the virtualenv context, or None if the folder is not a venv.

Inspired by autoswitch_virtualenv.plugin.zsh and `uv's get_interpreter_info.py https://github.com/astral-sh/uv/blob/f770b25/crates/uv-python/python/get_interpreter_info.py>`_.

Return type:

tuple[str, ...] | None

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 mpm over 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.

Return type:

Generator[tuple[str, ...], None, None]

check_mpm(mpm_cli_args)[source]ΒΆ

Test-run mpm execution and extract its version.

Return type:

tuple[bool, bool, tuple[int, ...] | None, str | Exception | None]

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_mpm is 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.

Return type:

None

static print_error_header()[source]ΒΆ

Generic header for blocking error.

Return type:

None

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:

None

print_menu()[source]ΒΆ

Print the main menu.

Return type:

None

meta_package_manager.base moduleΒΆ

class meta_package_manager.base.Operations(*values)ΒΆ

Bases: Enum

Recognized operation IDs that are implemented by package manager with their specific CLI invocation.

Each operation has its own CLI subcommand.

installed = 1ΒΆ
outdated = 2ΒΆ
search = 3ΒΆ
install = 4ΒΆ
upgrade = 5ΒΆ
upgrade_all = 6ΒΆ
remove = 7ΒΆ
sync = 8ΒΆ
cleanup = 9ΒΆ
exception meta_package_manager.base.CLIError(code, output, error)[source]ΒΆ

Bases: Exception

An error occurred when running package manager CLI.

The exception internally keeps the result of CLI execution.

class meta_package_manager.base.Package(id, manager_id, name=None, description=None, installed_version=None, latest_version=None, arch=None)[source]ΒΆ

Bases: object

Lightweight representation of a package and its metadata.

id: strΒΆ

ID is required and is the primary key used by the manager.

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ΒΆ
description: str | None = NoneΒΆ
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_version and latest_version are allowed to temporarily be strings between __init__ and __post_init__. Once they reach the later, they’re parsed and normalized into either TokenizedString or None. 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.

arch: str | None = NoneΒΆ
property purl: PackageURLΒΆ

Returns the package’s pURL object.

meta_package_manager.base.packages_asdict(packages, keep_fields)[source]ΒΆ

Returns a list of packages casted to a dict with only a subset of its fields.

class meta_package_manager.base.MetaPackageManager(name, bases, dct)[source]ΒΆ

Bases: type

Custom 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.

meta_package_manager.base.highlight_cli_name(path, match_names)[source]ΒΆ

Highlight the binary name in the provided path.

If match_names is provided, only highlight the start of the binary name that is in the list.

Matching is insensitive to case on Windows and case-sensitive on other platforms, thanks to os.path.normcase.

Return type:

str | None

class meta_package_manager.base.PackageManager[source]ΒΆ

Bases: object

Base class from which all package manager definitions inherits.

Initialize cli_errors list.

deprecated: bool = FalseΒΆ

A manager marked as deprecated will be hidden from all package selection by default.

You can still use it but need to explicitly call for it on the command line.

Implementation of a deprecated manager will be kept within mpm source code, but some of its features or total implementation are allowed to be scraped in the face of maintenance pain and adversity.

Integration tests and unittests for deprecated managers can be removed. We do not care if a deprecated manager is not 100% reliable. A flakky deprecated manager should not block a release due to flakky tests.

deprecation_url: str | None = NoneΒΆ

Announcement from the official project or evidence of abandonment of maintenance.

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.

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 frozenset of Platform instances 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.

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 python or python3.

By default, this property’s value is derived from the manager’s ID (see the MetaPackageManager.__init__ method above).

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.

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.base.PackageManager.cli_path() works well on all platforms.

extra_env: Mapping[str, str | None] | None = NoneΒΆ

Additional environment variables to add to the current context.

Automatically applied on each meta_package_manager.base.PackageManager.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.base.PackageManager.run_cli() call.

Used to prepend sudo or other system utilities.

pre_args: tuple[str, ...] = ()ΒΆ
post_args: tuple[str, ...] = ()ΒΆ

Global list of options used before and after the invoked package manager CLI.

Automatically added to each meta_package_manager.base.PackageManager.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_regexes below to extract the version number.

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.base.PackageManager.version().

By default match the first part that is space-separated.

stop_on_error: bool = FalseΒΆ

Tell the manager to either raise or continue on errors.

ignore_auto_updates: bool = TrueΒΆ

Some managers can report or ignore packages which have their own auto-update mechanism.

dry_run: bool = FalseΒΆ

Do not actually perform any action, just simulate CLI calls.

timeout: int | None = NoneΒΆ

Maximum number of seconds to wait for a CLI call to complete.

package(**kwargs)[source]ΒΆ

Instantiate a Package object from the manager.

Sets its manage_id to the manager it belongs to.

Return type:

Package

cli_errors: list[CLIError]ΒΆ

Accumulate all CLI errors encountered by the package manager.

classmethod implements(op)[source]ΒΆ

Inspect manager’s implementation to check for proper support of an operation.

Return type:

bool

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.

Return type:

Generator[Path, None, None]

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.

which(cli_name)[source]ΒΆ

Emulates the which command.

Based on the search_all_cli() method.

Return type:

Path | None

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 by cli_search_path. In that order. Then returns the first match.

Executability of the CLI will be separately assessed later by the meta_package_manager.base.PackageManager.executable() method 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.TokenizedString instance.

property supported: boolΒΆ

Is the package manager supported on that platform?

property executable: boolΒΆ

Is the package manager CLI can be executed by the current user?

property fresh: boolΒΆ

Does the package manager match the version requirement?

property available: boolΒΆ

Is the package manager available and ready-to-use on the system?

Returns True only if the main CLI:

  1. is supported on the current platform,

  2. was found on the system,

  3. is executable, and

  4. match the version requirement.

run(*args, extra_env=None, must_succeed=False)[source]ΒΆ

Run a shell command, return the output and accumulate error messages.

args is allowed to be a nested structure of iterables, in which case it will be recursively flatten, then None will be discarded, and finally each item casted to strings.

Running commands with that method takes care of:
Parameters:

must_succeed (bool) – if True, raise meta_package_manager.base.CLIError on non-zero exit code regardless of stop_on_error. Use for calls whose output is parsed (JSON, XML, regex) and where a silent failure would be indistinguishable from empty results. Unlike stop_on_error (a user-facing preference for cross-manager resilience), must_succeed is a developer assertion that the invocation itself is correct.

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 *args with 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 *args and the full CLI string will be composed out of the globals, following this schema:

$ [<pre_cmds>|sudo] <cli_path> <pre_args> <*args> <post_args>

Each additional set of elements can be disabled with their respective flag:

  • auto_pre_cmds=False to skip the automatic addition of self.pre_cmds

  • auto_pre_args=False to skip the automatic addition of self.pre_args

  • auto_post_args=False to skip the automatic addition of self.post_args

Each global set of elements can be locally overridden with:

  • override_pre_cmds=tuple()

  • override_cli_path=str

  • override_pre_args=tuple()

  • override_post_args=tuple()

On linux, the command can be run with sudo if the parameter of the same name is set to True. In which case the override_pre_cmds parameter is not allowed to be set and the auto_pre_cmds parameter is forced to False.

Return type:

tuple[str, ...]

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 *args with the package manager’s global parameters.

After the CLI is built with the meta_package_manager.base.PackageManager.build_cli() method, it is executed with the meta_package_manager.base.PackageManager.run() method, augmented with environment variables from self.extra_env.

All parameters are the same as meta_package_manager.base.PackageManager.build_cli(), plus:

Return type:

str

property installed: Iterator[Package]ΒΆ

List packages currently installed on the system.

Optional. Will be simply skipped by mpm if not implemented.

property installed_ids: frozenset[str]ΒΆ

Installed package IDs, materialized once from installed().

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.0000 vs 2.000000). This filter drops those false positives.

classmethod query_parts(query)[source]ΒΆ

Returns a set of all contiguous alphanumeric string segments.

Contrary to meta_package_manager.version.TokenizedString, do no splits on colated number/alphabetic junctions.

Return type:

set[str]

search(query, extended, exact)[source]ΒΆ

Search packages available for install.

There is no need for this method to be perfect and sensitive to extended and exact parameters. 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 the meta_package_manager.base.PackageManager.refiltered_search() method below.

Optional. Will be simply skipped by mpm if not implemented.

Return type:

Iterator[Package]

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.base.PackageManager.search() results to either exclude non-extended or non-exact matches.

Returns a generator producing the same data as the meta_package_manager.base.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.base.PackageManager.search() method above and try to produce results as precise as possible using the native filtering capabilities of the package manager CLI.

Return type:

Iterator[Package]

install(package_id, version=None)[source]ΒΆ

Install one package and one only.

Allows a specific version to be provided.

Return type:

str

upgrade_all_cli()[source]ΒΆ

Returns the complete CLI to upgrade all outdated packages on the system.

Return type:

tuple[str, ...]

upgrade_one_cli(package_id, version=None)[source]ΒΆ

Returns the complete CLI to upgrade one package and one only.

Allows a specific version to be provided.

Return type:

tuple[str, ...]

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.base.PackageManager.upgrade_all_cli() or meta_package_manager.base.PackageManager.upgrade_one_cli().

If the manager doesn’t provides a full upgrade one-liner (i.e. if meta_package_manager.base.PackageManager.upgrade_all_cli() raises NotImplementedError), then the list of all outdated packages will be fetched (via meta_package_manager.base.PackageManager.outdated()) and each package will be updated one by one by calling meta_package_manager.base.PackageManager.upgrade_one_cli().

See for example the case of meta_package_manager.managers.pip.Pip.upgrade_one_cli().

Return type:

str

remove(package_id)[source]ΒΆ

Remove one package and one only.

Optional. Will be simply skipped by mpm if not implemented.

Return type:

str

sync()[source]ΒΆ

Refresh package metadata from remote repositories.

Optional. Will be simply skipped by mpm if not implemented.

Return type:

None

cleanup()[source]ΒΆ

Prune left-overs, remove orphaned dependencies and clear caches.

Optional. Will be simply skipped by mpm if not implemented.

Return type:

None

meta_package_manager.capabilities moduleΒΆ

Utilities and helper to organize, inspect and audit the capabilities of mpm and package managers.

meta_package_manager.capabilities.search_capabilities(extended_support=True, exact_support=True)[source]ΒΆ

Decorator factory to be used on search() operations to signal mpm framework manager’s capabilities.

meta_package_manager.capabilities.version_not_implemented(func)[source]ΒΆ

Decorator to be used on install() or upgrade_one_cli() operations to signal that a particular operation does not implement (yet) the version specifier parameter.

Return type:

Callable[[ParamSpec(P, bound= None)], TypeVar(T)]

meta_package_manager.cli moduleΒΆ

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.MpmConfig(all_managers=False, ignore_auto_updates=True, stop_on_error=False, dry_run=False, timeout=500, description=False, sort_by='manager_id', stats=True)[source]ΒΆ

Bases: object

Schema for mpm configuration files.

Defines the recognized options for the [mpm] (or [tool.mpm]) configuration section. Each field corresponds to a CLI option on the root mpm group.

Note

Dynamic manager selectors (brew = true, pip = false, etc.) and click-extra built-in options (verbosity, table_format) are handled by the default_map pipeline and do not appear here.

all_managers: bool = FalseΒΆ

Force evaluation of all managers, including unsupported and deprecated.

ignore_auto_updates: bool = TrueΒΆ

Exclude auto-updating packages from outdated/upgrade results.

stop_on_error: bool = FalseΒΆ

Stop on first manager CLI error instead of continuing.

dry_run: bool = FalseΒΆ

Simulate CLI calls without performing any action.

timeout: int = 500ΒΆ

Maximum duration in seconds for each manager CLI call.

description: bool = FalseΒΆ

Show package description in results.

sort_by: str = 'manager_id'ΒΆ

Default field to sort results by.

stats: bool = TrueΒΆ

Print per-manager package statistics.

meta_package_manager.cli.is_stdout(filepath)[source]ΒΆ

Check if a file path is set to stdout.

Prevents the creation of a - file in the current directory.

Return type:

bool

meta_package_manager.cli.prep_path(filepath)[source]ΒΆ

Prepare the output file parameter for Click’s echo function.

Return type:

IO | None

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

Update global selection list of managers in the context.

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

Return type:

None

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-path option.

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

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

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

meta_package_manager.inventory moduleΒΆ

Introspection utilities to produce feature inventory of all managers.

meta_package_manager.inventory.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.labels moduleΒΆ

Utilities to generate extra labels to use for GitHub issues and PRs.

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_labels parameter, unless it is part of a group. In which case a dedicated label for that group will be created.

Return type:

dict[str, str]

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', 'yarn', 'yarn-berry'}), 'pacman-based': frozenset({'pacaur', 'pacman', 'paru', 'yay'}), 'pip-based': frozenset({'pip', 'pipx'}), 'rpm-based': frozenset({'dnf', 'dnf5', 'yum', 'zypper'}), '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({'apm', 'apt', 'apt-mint', 'brew', 'cargo', 'cask', 'choco', 'composer', 'cpan', 'deb-get', 'dnf', 'dnf5', 'emerge', 'eopkg', 'flatpak', 'fwupd', 'gem', 'mas', 'mpm', 'nix', 'npm', 'opkg', 'pacaur', 'pacman', 'pacstall', 'paru', 'pip', 'pipx', 'pkg', 'scoop', 'sdkman', 'snap', 'steamcmd', 'stew', 'uv', 'uvx', 'vscode', 'vscodium', 'winget', 'yarn', 'yarn-berry', 'yay', 'yum', 'zerobrew', 'zypper'})ΒΆ

Adds mpm as its own manager alongside all those implemented.

meta_package_manager.labels.MANAGER_LABELS = {'apm': 'πŸ“¦ manager: apm', 'apt': 'πŸ“¦ manager: dpkg-based', 'apt-mint': 'πŸ“¦ manager: dpkg-based', 'brew': 'πŸ“¦ manager: homebrew', 'cargo': 'πŸ“¦ manager: cargo', 'cask': 'πŸ“¦ manager: homebrew', 'choco': 'πŸ“¦ manager: choco', 'composer': 'πŸ“¦ manager: composer', 'cpan': 'πŸ“¦ manager: cpan', 'deb-get': 'πŸ“¦ manager: dpkg-based', 'dnf': 'πŸ“¦ manager: rpm-based', 'dnf5': 'πŸ“¦ manager: rpm-based', 'emerge': 'πŸ“¦ manager: emerge', 'eopkg': 'πŸ“¦ manager: eopkg', 'flatpak': 'πŸ“¦ manager: flatpak', 'fwupd': 'πŸ“¦ manager: fwupd', 'gem': 'πŸ“¦ manager: gem', 'mas': 'πŸ“¦ manager: mas', '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', 'pkg': 'πŸ“¦ manager: pkg', 'scoop': 'πŸ“¦ manager: scoop', 'sdkman': 'πŸ“¦ manager: sdkman', 'snap': 'πŸ“¦ manager: snap', 'steamcmd': 'πŸ“¦ manager: steamcmd', 'stew': 'πŸ“¦ manager: stew', 'uv': 'πŸ“¦ manager: uv-based', 'uvx': 'πŸ“¦ manager: uv-based', 'vscode': 'πŸ“¦ manager: vscode-based', 'vscodium': 'πŸ“¦ manager: vscode-based', 'winget': 'πŸ“¦ manager: winget', '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', 'Alpine Linux': 'πŸ–₯ platform: Linux', 'Amazon Linux': 'πŸ–₯ platform: Linux', 'Android': 'πŸ–₯ platform: Linux', 'Arch Linux': 'πŸ–₯ platform: Linux', 'Buildroot': 'πŸ–₯ platform: Linux', 'CachyOS': 'πŸ–₯ platform: Linux', 'CentOS': 'πŸ–₯ platform: Linux', 'CloudLinux OS': 'πŸ–₯ platform: Linux', 'Cygwin': 'πŸ–₯ platform: Unix', 'Debian': 'πŸ–₯ platform: Linux', 'DragonFly BSD': 'πŸ–₯ platform: BSD', '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', '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', 'Nobara': 'πŸ–₯ platform: Linux', 'OpenBSD': 'πŸ–₯ platform: BSD', 'OpenWrt': 'πŸ–₯ platform: Linux', 'Oracle Linux': 'πŸ–₯ platform: Linux', 'Parallels': 'πŸ–₯ platform: Linux', 'Pidora': 'πŸ–₯ 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', 'Solaris': 'πŸ–₯ platform: Unix', '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', 'openSUSE Tumbleweed': 'πŸ–₯ platform: Linux'}ΒΆ

Maps all platform names to their labels.

meta_package_manager.labels.LABELS: list[tuple[str, str, str]] = [('πŸ“¦ manager: apm', '#bfdadc', 'apm'), ('πŸ“¦ manager: cargo', '#bfdadc', 'cargo'), ('πŸ“¦ manager: choco', '#bfdadc', 'choco'), ('πŸ“¦ manager: composer', '#bfdadc', 'composer'), ('πŸ“¦ manager: cpan', '#bfdadc', 'cpan'), ('πŸ“¦ manager: dpkg-based', '#bfdadc', 'apt, apt-mint, deb-get, opkg, pacstall'), ('πŸ“¦ manager: emerge', '#bfdadc', 'emerge'), ('πŸ“¦ manager: eopkg', '#bfdadc', 'eopkg'), ('πŸ“¦ manager: flatpak', '#bfdadc', 'flatpak'), ('πŸ“¦ manager: fwupd', '#bfdadc', 'fwupd'), ('πŸ“¦ manager: gem', '#bfdadc', 'gem'), ('πŸ“¦ manager: homebrew', '#bfdadc', 'brew, cask, zerobrew'), ('πŸ“¦ manager: mas', '#bfdadc', 'mas'), ('πŸ“¦ manager: mpm', '#bfdadc', 'mpm'), ('πŸ“¦ manager: nix', '#bfdadc', 'nix'), ('πŸ“¦ manager: npm-based', '#bfdadc', 'npm, yarn, yarn-berry'), ('πŸ“¦ manager: pacman-based', '#bfdadc', 'pacaur, pacman, paru, yay'), ('πŸ“¦ manager: pip-based', '#bfdadc', 'pip, pipx'), ('πŸ“¦ manager: pkg', '#bfdadc', 'pkg'), ('πŸ“¦ manager: rpm-based', '#bfdadc', 'dnf, dnf5, yum, zypper'), ('πŸ“¦ manager: scoop', '#bfdadc', 'scoop'), ('πŸ“¦ manager: sdkman', '#bfdadc', 'sdkman'), ('πŸ“¦ manager: snap', '#bfdadc', 'snap'), ('πŸ“¦ manager: steamcmd', '#bfdadc', 'steamcmd'), ('πŸ“¦ manager: stew', '#bfdadc', 'stew'), ('πŸ“¦ manager: uv-based', '#bfdadc', 'uv, uvx'), ('πŸ“¦ manager: vscode-based', '#bfdadc', 'vscode, vscodium'), ('πŸ“¦ manager: winget', '#bfdadc', 'winget'), ('πŸ”Œ bar-plugin', '#fef2c0', 'Xbar/SwiftBar plugin code, documentation and features'), ('πŸ–₯ platform: BSD', '#bfd4f2', 'DragonFly BSD, FreeBSD, MidnightBSD, NetBSD, OpenBSD, SunOS'), ('πŸ–₯ platform: Linux', '#bfd4f2', '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, illumos, Solaris'), ('πŸ–₯ platform: Windows', '#bfd4f2', 'Windows')]ΒΆ

Global registry of all labels used in the project.

Structure:

("label_name", "color", "optional_description")

meta_package_manager.output moduleΒΆ

Helpers and utilities to render and print content.

class meta_package_manager.output.SortableField(*values)[source]ΒΆ

Bases: StrEnum

Fields 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.output.print_stats(manager_stats)[source]ΒΆ

Prints statistics to <stderr>: total packages and a break down by package manager.

Prints something like:

10 packages total (brew: 2, pip: 2, gem: 2, vscode: 2, npm: 2, composer: 0).
Return type:

None

class meta_package_manager.output.BarPluginRenderer[source]ΒΆ

Bases: MPMPlugin

All 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 at meta_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.

property submenu_layout: boolΒΆ

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_LAYOUT environment variable.

property dark_mode: boolΒΆ

Detect dark mode by inspecting environment variables.

Value is sourced from two environment variables depending on the plugin:

  • OS_APPEARANCE for SwiftBar

  • XBARDarkMode for XBar

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:

str

print_cli_item(*args)[source]ΒΆ

Print two CLI entries:

  • one that is silent

  • a second one that is the exact copy of the above but forces the execution by the way of a visible terminal

Return type:

None

print_upgrade_all_item(manager, submenu='')[source]ΒΆ

Print the menu entry to upgrade all outdated package of a manager.

Return type:

None

render(outdated_data)[source]ΒΆ

Wraps the meta_package_manager.output.BarPluginRenderer._render() function above to capture all print statements.

Return type:

str

add_upgrade_cli(outdated_data)[source]ΒΆ

Augment the outdated data from mpm outdated subcommand with upgrade CLI fields for bar plugin consumption.

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().

Return type:

None

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.apm.APM'>, <class 'meta_package_manager.managers.apt.APT'>, <class 'meta_package_manager.managers.apt.APT_Mint'>, <class 'meta_package_manager.managers.homebrew.Brew'>, <class 'meta_package_manager.managers.cargo.Cargo'>, <class 'meta_package_manager.managers.homebrew.Cask'>, <class 'meta_package_manager.managers.chocolatey.Choco'>, <class 'meta_package_manager.managers.composer.Composer'>, <class 'meta_package_manager.managers.cpan.CPAN'>, <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.mas.MAS'>, <class 'meta_package_manager.managers.nix.Nix'>, <class 'meta_package_manager.managers.npm.NPM'>, <class 'meta_package_manager.managers.opkg.OPKG'>, <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.pkg.PKG'>, <class 'meta_package_manager.managers.scoop.Scoop'>, <class 'meta_package_manager.managers.sdkman.SDKMAN'>, <class 'meta_package_manager.managers.snap.Snap'>, <class 'meta_package_manager.managers.steamcmd.SteamCMD'>, <class 'meta_package_manager.managers.stew.Stew'>, <class 'meta_package_manager.managers.uv.UV'>, <class 'meta_package_manager.managers.uv.UVX'>, <class 'meta_package_manager.managers.vscode.VSCode'>, <class 'meta_package_manager.managers.vscode.VSCodium'>, <class 'meta_package_manager.managers.winget.WinGet'>, <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.zerobrew.ZeroBrew'>, <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:

  1. are located in the :py:prop:`meta_package_manager.pool.ManagerPool.manager_subfolder`

    subfolder, and

  2. are sub-classes of meta_package_manager.base.PackageManager, and

  3. are not :py:prop:`meta_package_manager.base.PackageManager.virtual` (i.e. have a

    non-null :py:prop:`meta_package_manager.base.PackageManager.cli_names` property).

These properties are checked and enforced in unittests.

class meta_package_manager.pool.ManagerPool[source]ΒΆ

Bases: object

A dict-like register, instantiating all supported package managers.

ALLOWED_EXTRA_OPTION: Final = frozenset({'dry_run', 'ignore_auto_updates', 'stop_on_error', '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.

get(key)ΒΆ
values()[source]ΒΆ
items()[source]ΒΆ
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 maintained_manager_ids: tuple[str, ...]ΒΆ

All manager IDs which are not deprecated.

property default_manager_ids: tuple[str, ...]ΒΆ

All manager IDs supported on the current platform and not deprecated.

Must keep the same order defined by :py:prop:`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.

select_managers(*args, **kwargs)[source]ΒΆ

Wraps _select_managers() to stop CLI execution if no manager are selected.

Return type:

Iterator[PackageManager]

meta_package_manager.sbom moduleΒΆ

class meta_package_manager.sbom.ExportFormat(*values)[source]ΒΆ

Bases: StrEnum

A user-friendly version of spdx_tools.spdx.formats.FileFormat.

Map format to user-friendly IDs.

JSON = 'json'ΒΆ
XML = 'xml'ΒΆ
YAML = 'yaml'ΒΆ
TAG_VALUE = 'tag'ΒΆ
RDF_XML = 'rdf'ΒΆ
class meta_package_manager.sbom.SBOM(export_format=ExportFormat.JSON)[source]ΒΆ

Bases: object

Utilities shared by all SBOM classes.

Defaults to JSON export format.

static autodetect_export_format(file_path)[source]ΒΆ

Better version of spdx_tools.spdx.formats.file_name_to_format which is based on Path objects and is case-insensitive.

Return type:

ExportFormat | None

class meta_package_manager.sbom.SPDX(export_format=ExportFormat.JSON)[source]ΒΆ

Bases: SBOM

Generates an SPDX document from a list of packages.

SPDX 2.3 specifications.

Defaults to JSON export format.

DOC_ID = 'SPDXRef-DOCUMENT'ΒΆ

Document root ID.

document: DocumentΒΆ
seen_ids: set[str]ΒΆ
classmethod normalize_spdx_id(str)[source]ΒΆ

SPDX IDs must only contain letters, numbers, . and -.

Return type:

str

init_doc()[source]ΒΆ

SPDX document metadata specifications.

Return type:

None

add_package(manager, package)[source]ΒΆ

SPDX package metadata specifications.

Return type:

None

export()[source]ΒΆ

Similar to spdx_tools.spdx.writer.write_anything.write_file but write directly to provided stream instead of file path.

Return type:

str

class meta_package_manager.sbom.CycloneDX(export_format=ExportFormat.JSON)[source]ΒΆ

Bases: SBOM

Generates a CycloneDX document from a list of packages.

CycloneDX 1.5 specifications.

Defaults to JSON export format.

document: BomΒΆ
init_doc()[source]ΒΆ

CycloneDX document metadata specifications.

Return type:

None

add_package(manager, package)[source]ΒΆ

CycloneDX package metadata specifications.

Return type:

None

export()[source]ΒΆ
Return type:

str

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 mpm to 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': None, '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': None, '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': None, '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': None, 'npm': {'npm', '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_MAP mapping.

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: object

Lightweight representation of a package specification.

Contains all parsed metadata to be used as constraints.

raw_spec: strΒΆ

Original, un-parsed specifier string provided by the user.

package_id: strΒΆ

ID is required and is the primary key used for specification.

manager_id: str | None = NoneΒΆ
version: str | None = NoneΒΆ

Version string, a 1:1 copy of the one provided by the user.

classmethod parse_purl(spec_str)[source]ΒΆ

Resolve a pURL into its corresponding manager candidates.

Yields Specifier objects or returns None.

Return type:

tuple[Specifier, ...] | None

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.7

If a specifier resolves to multiple constraints (as it might be the case for pURL), we produce and returns all variations. That way the Solver below has all necessary details to resolve the constraints.

Returns a tuple of Specifier.

Return type:

tuple[Specifier, ...]

property parsed_version: TokenizedStringΒΆ
property is_blank: boolΒΆ

Is considered blank a Specifier without any constraint on manager_id or version.

exception meta_package_manager.specifier.EmptyReduction[source]ΒΆ

Bases: Exception

Raised by the solver if no constraint can’t be met.

class meta_package_manager.specifier.Solver(spec_strings=None, manager_priority=None)[source]ΒΆ

Bases: object

Combine a set of Specifier and allow for the solving of the constraints they represent.

spec_pool: set[Specifier]ΒΆ
manager_priority: Sequence[str] = []ΒΆ
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_managers allows for filtering by discarding managers not in that list.

Return type:

str | None

reduce_specs(specs)[source]ΒΆ

Reduce a collection of Specifier to its essential, minimal and unique form.

This method assumes that all provided specs are of the same package (like resolve_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.

Return type:

Specifier

Filtering steps: 1. We remove all constraints tied to all by the top priority manager if

provided.

  1. If no manager priority is provided, we discard constraints not tied to a

    manager.

  2. We discard constraints not tied to a version.

  3. 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.

resolve_package_specs()[source]ΒΆ

Regroup specs of the pool by package IDs, and solve their constraints.

Return each package ID with its single, reduced spec, or None if it ha no constraints.

Return type:

Iterator[tuple[str, Specifier]]

resolve_specs_group_by_managers()[source]ΒΆ

Resolves package specs, and returns them grouped by managers.

Return type:

dict[str | None, set[Specifier]]

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:

  • 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) and 0.1 > 0.beta2 work without understanding PEP 440 or semver pre-release semantics.

  • Trailing zeros are padding. 6.2 and 6.2.0 compare 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, g6cd4c31 would shatter into ("g", 6, "cd", 4, "c", 31). The 7-character floor matches git’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 ubuntu1 into ("ubuntu", 1) enables natural numeric ordering of embedded version numbers: a4 < a10 compares correctly because 4 and 10 become 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. Epochs (1!), .devN ordering relative to pre-releases, and post-release semantics are not handled. Use packaging.version for strict PEP 440 compliance.

  • 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 Debian epochs (:), Java build metadata (,), or Perl-style floats (.) are treated as plain delimiters, which can produce wrong comparison results when the separator carries structural meaning.

ReferencesΒΆ

  • PEP 440 β€” Python’s version identification spec. Defines a/b/rc suffix 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.”

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, and c/rc/preview as equivalent aliases. These appear across ecosystems: Debian uses ~alpha, npm uses -alpha, Homebrew uses alpha/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 or pretty_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 .postN as a post-release. patch carries 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 makes 1.0 > 1.0.post1.

This set is deliberately small. Only tags with unambiguous β€œnewer than release” semantics across multiple ecosystems belong here. Candidates like rev or p are excluded because they can also mean β€œrevision” (Gentoo -r0) or β€œpre-release patchlevel” (FreeBSD p1), depending on context.

class meta_package_manager.version.Token(value)[source]ΒΆ

Bases: object

A normalized word, persisting its lossless integer variant.

Supports natural comparison with str and int types. Used to compare versions and package IDs.

Instantiates a Token from an alphanumeric string or a non-negative integer.

static str_to_int(value)[source]ΒΆ

Convert a str or an int to a (string, integer) couple.

Returns together the original string and its integer representation if conversion is successful and lossless. Else, returns the original value and None.

Return type:

tuple[str, int | None]

string: strΒΆ
integer: int | None = NoneΒΆ
property isint: boolΒΆ

Does the Token got an equivalent pure integer representation?

class meta_package_manager.version.TokenizedString(value)[source]ΒΆ

Bases: object

Tokenize a string for user-friendly sorting.

Essentially a wrapper around a list of Token instances.

Parse and tokenize the provided raw value.

string: strΒΆ
tokens: tuple[Token, ...] = ()ΒΆ
separators: tuple[str, ...] = ()ΒΆ
original_segments: tuple[str, ...] = ()ΒΆ

Original-case token strings for lossless pretty_print().

pretty_print()[source]ΒΆ

Reconstruct the tokenized string using original-case segments and separators.

Return type:

str

static tokenize(string)[source]ΒΆ

Tokenize a string: ignore case and split at each non-alphanumeric characters.

Returns a tuple of Token instances, 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
Return type:

tuple[tuple[Token, ...], tuple[str, ...], tuple[str, ...]]

meta_package_manager.version.parse_versionΒΆ

Alias for TokenizedString used in version-comparison contexts.

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: object

A 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 True if the string looks like a version.

Heuristics: at least one token is an integer, or there is only one non-integer token.

Return type:

bool

meta_package_manager.version.diff_versions(old, new)[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.1774638290 vs 2.1.1774896198, the common part is 2.1 and the diff includes .1774638290 / .1774896198.

Return type:

tuple[str, str]