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 global mpm --sort-by selector. The selector itself is click-extra’s field-vocabulary SortByOption, and the per-table resolution (sort by the selected fields the table carries, keep the original row order when it carries none) happens inside click_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 the SortableField the column carries (None for a column that cannot drive the sort). A registry is the single source of truth for its command: the same tuple feeds the @columns_option declaration (which validates the user selection) and print_projected_table() (which projects headers and rows before rendering).

  • print_projected_table() and print_serialized(), the human-friendly and machine-friendly rendering paths every table-producing subcommand goes through. The read commands serialize through print_serialized_and_exit(), which stops the command there.

class meta_package_manager.tables.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'¶
class meta_package_manager.tables.PackageOutcome(*values)[source]¶

Bases: StrEnum

What a command did to one package, as the status column of CHANGE_REPORT_COLUMNS spells it.

Classified by meta_package_manager.cli.package_outcomes() from the inventory a manager reports before and after the command changes it. The values are the words the serialized payload carries and the trail line counts (1 upgraded, 2 cooldown), so a member reads as a word rather than as an identifier. The table shows each one as its label.

UPGRADED = 'upgraded'¶

The installed version moved to a newer release, or in a direction that mpm cannot tell.

DOWNGRADED = 'downgraded'¶

The installed version moved back to an older release.

INSTALLED = 'installed'¶

Absent before the command: a package it installed, or a dependency it pulled in.

REMOVED = 'removed'¶

Present before the command, gone after it.

COOLDOWN = 'cooldown'¶

Still outdated because the release-age cooldown holds it back.

The word the trail already tags such a package with, so one word names the hold on the trail, in the table and in the serialized payload.

STILL_OUTDATED = 'still outdated'¶

Still outdated for any other reason: a failed build, a pinned package, or one the native command leaves alone.

property label: str¶

The outcome as the report table shows it: its glyph from PACKAGE_OUTCOME_GLYPHS, then its word.

meta_package_manager.tables.PACKAGE_OUTCOME_GLYPHS: dict[PackageOutcome, str] = {PackageOutcome.COOLDOWN: '⏸️', PackageOutcome.DOWNGRADED: '⏪', PackageOutcome.INSTALLED: '🆕', PackageOutcome.REMOVED: '🗑️', PackageOutcome.STILL_OUTDATED: '⏳', PackageOutcome.UPGRADED: '🆙'}¶

The glyph leading each PackageOutcome in the report table.

All but one come from the legend repomatic’s dependency reports use for the same moves (🆙 updated, ⏪ stepped back, 🆕 new, 🗑️ removed, ⏸️ held back by cooldown), so a reader of both reads one legend. still outdated has no counterpart there: ⏳ marks a package whose upgrade is still pending, whatever the cause.

Only the table carries them. The serialized report and the trail line keep the bare word, which is what a script matches.

meta_package_manager.tables.TColumn¶

One column of a registry: its spec, and the field it sorts on, if any.

alias of tuple[ColumnSpec, SortableField | None]

meta_package_manager.tables.PACKAGE_ID_COLUMN: tuple[ColumnSpec, SortableField | None] = (ColumnSpec(id='package_id', label='Package ID', description="Package's identifier.", max_width=None, optional=False, sortable=True), SortableField.PACKAGE_ID)¶

The package_id column every package table opens on.

Deliberately the one column of every package table left uncapped, and the rule holds wherever it is reused. It is the value the user copies back into an mpm install, mpm remove or mpm upgrade invocation, and a cell wrapped over two lines cannot be selected in one go. So it never wraps, and the shrinking falls on its neighbors instead: the name is free prose, and a version is read rather than retyped.

The trade is explicit. A package ID wider than the terminal on its own still pushes the table past the edge, because the alternative is handing the user a broken identifier. Both halves of the trade are real here: vim-pack names its plugins by GitHub URL (50 characters), and Homebrew Cask reports versions like 1.26832.0,056ee2be623b207f6a4d24dfb1b2fb5a82db0ecf.

meta_package_manager.tables.PACKAGE_NAME_COLUMN: tuple[ColumnSpec, SortableField | None] = (ColumnSpec(id='package_name', label='Name', description="Package's common name.", max_width='auto', optional=False, sortable=True), SortableField.PACKAGE_NAME)¶

The package_name column following PACKAGE_ID_COLUMN in every package table, wrapping inside its own cell.

meta_package_manager.tables.MANAGERS_COLUMNS: tuple[tuple[ColumnSpec, SortableField | None], ...] = ((ColumnSpec(id='manager_id', label='Manager ID', description="Manager's identifier.", max_width=None, optional=False, sortable=True), SortableField.MANAGER_ID), (ColumnSpec(id='manager_name', label='Name', description="Manager's common name.", max_width=None, optional=False, sortable=True), SortableField.MANAGER_NAME), (ColumnSpec(id='supported', label='Supported', description='Support status on the current platform.', max_width='auto', optional=False, sortable=True), None), (ColumnSpec(id='cli', label='CLI', description="Location of the manager's binary on the system.", max_width='auto', optional=False, sortable=True), None), (ColumnSpec(id='executable', label='Executable', description='Whether the binary is executable.', max_width=None, optional=False, sortable=True), None), (ColumnSpec(id='version', label='Version', description="Manager's self-reported version, and the unsatisfied requirement when stale.", max_width=None, optional=False, sortable=True), SortableField.VERSION))¶

Columns of the mpm managers table.

supported and cli are the two whose content no manager bounds: the first enumerates every platform a manager runs on when they do not collapse to a group name, the second holds a filesystem path. Both take AUTO_WIDTH so they share whatever the fixed columns leave on the terminal and wrap inside their own cell, instead of stretching the table past the edge and mangling every border.

meta_package_manager.tables.MANAGERS_DETECTED_COLUMNS: tuple[str, ...] = ('manager_id', 'manager_name', 'cli', 'version')¶

Columns kept by the default, detected-only view of the mpm managers table.

A detected manager is by definition supported on this platform, found and executable, so supported and executable render the same âś“ on every row and carry no information. Both come back in the wider views, where an unsupported platform or a missing binary makes them vary again. Only the default selection narrows: --columns still addresses every column of MANAGERS_COLUMNS.

meta_package_manager.tables.INSTALLED_COLUMNS: tuple[tuple[ColumnSpec, SortableField | None], ...] = ((ColumnSpec(id='package_id', label='Package ID', description="Package's identifier.", max_width=None, optional=False, sortable=True), SortableField.PACKAGE_ID), (ColumnSpec(id='package_name', label='Name', description="Package's common name.", max_width='auto', optional=False, sortable=True), SortableField.PACKAGE_NAME), (ColumnSpec(id='manager_id', label='Manager', description='Manager reporting the package.', max_width=None, optional=False, sortable=True), SortableField.MANAGER_ID), (ColumnSpec(id='installed_version', label='Installed version', description='Version currently installed.', max_width='auto', optional=False, sortable=True), SortableField.VERSION))¶

Columns of the mpm installed table, and of the mpm orphans one.

The width policy is the one PACKAGE_ID_COLUMN documents: the ID never wraps, every other column does.

meta_package_manager.tables.OUTDATED_COLUMNS: tuple[tuple[ColumnSpec, SortableField | None], ...] = ((ColumnSpec(id='package_id', label='Package ID', description="Package's identifier.", max_width=None, optional=False, sortable=True), SortableField.PACKAGE_ID), (ColumnSpec(id='package_name', label='Name', description="Package's common name.", max_width='auto', optional=False, sortable=True), SortableField.PACKAGE_NAME), (ColumnSpec(id='manager_id', label='Manager', description='Manager reporting the package.', max_width=None, optional=False, sortable=True), SortableField.MANAGER_ID), (ColumnSpec(id='installed_version', label='Installed version', description='Version currently installed.', max_width='auto', optional=False, sortable=True), SortableField.VERSION), (ColumnSpec(id='latest_version', label='Latest version', description='Version available for upgrade.', max_width='auto', optional=False, sortable=True), None))¶

Columns of the mpm outdated table.

Inherits the width policy documented on PACKAGE_ID_COLUMN: the second version column wraps like the first, package_id still does not.

meta_package_manager.tables.SEARCH_COLUMNS: tuple[tuple[ColumnSpec, SortableField | None], ...] = ((ColumnSpec(id='package_id', label='Package ID', description="Package's identifier.", max_width=None, optional=False, sortable=True), SortableField.PACKAGE_ID), (ColumnSpec(id='package_name', label='Name', description="Package's common name.", max_width='auto', optional=False, sortable=True), SortableField.PACKAGE_NAME), (ColumnSpec(id='manager_id', label='Manager', description='Manager reporting the match.', max_width=None, optional=False, sortable=True), SortableField.MANAGER_ID), (ColumnSpec(id='latest_version', label='Latest version', description='Latest version available.', max_width='auto', optional=False, sortable=True), 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.', max_width='auto', optional=False, sortable=True), None))¶

Columns of the mpm search table.

The description column exists in the registry (so --columns can select it) but stays out of the default selection unless --description (or --extended, which searches descriptions) is passed.

It is the column holding the longest free prose, of a length no manager bounds: a single verbose match used to stretch the table far past the terminal and wrap every row at the edge, mangling the borders. AUTO_WIDTH caps it at whatever the other columns leave on the terminal, so the description wraps inside its own cell. The name and version columns share that treatment, and package_id is exempt from it, per the width policy documented on PACKAGE_ID_COLUMN.

meta_package_manager.tables.COOLDOWN_COLUMNS: tuple[tuple[ColumnSpec, SortableField | None], ...] = ((ColumnSpec(id='released', label='Released', description='Publication date of the release the cooldown holds back.', max_width=None, optional=False, sortable=True), None), (ColumnSpec(id='eligible', label='Eligible', description='Date that release clears the cooldown window.', max_width=None, optional=False, sortable=True), None))¶

The two columns dating a package the release-age cooldown holds back, closing CHANGE_REPORT_COLUMNS.

Named after the columns of repomatic’s Held back by cooldown table, for the reason PACKAGE_OUTCOME_GLYPHS borrows its legend. Only a cooldown row whose release could be dated fills them, so they join the default selection of a report when one of its rows does: see meta_package_manager.cli.print_change_report(). --columns selects them whatever the rows carry.

meta_package_manager.tables.CHANGE_REPORT_COLUMNS: tuple[tuple[ColumnSpec, SortableField | None], ...] = ((ColumnSpec(id='package_id', label='Package ID', description="Package's identifier.", max_width=None, optional=False, sortable=True), SortableField.PACKAGE_ID), (ColumnSpec(id='package_name', label='Name', description="Package's common name.", max_width='auto', optional=False, sortable=True), SortableField.PACKAGE_NAME), (ColumnSpec(id='manager_id', label='Manager', description='Manager whose inventory changed.', max_width=None, optional=False, sortable=True), SortableField.MANAGER_ID), (ColumnSpec(id='from_version', label='From', description='Version installed before the command.', max_width='auto', optional=False, sortable=True), None), (ColumnSpec(id='to_version', label='To', description='Version installed after the command, or the one still available for a package that did not move.', max_width='auto', optional=False, sortable=True), None), (ColumnSpec(id='status', label='Status', description='What the command did to the package: upgraded, downgraded, installed, removed, cooldown, still outdated.', max_width=None, optional=False, sortable=True), None), (ColumnSpec(id='released', label='Released', description='Publication date of the release the cooldown holds back.', max_width=None, optional=False, sortable=True), None), (ColumnSpec(id='eligible', label='Eligible', description='Date that release clears the cooldown window.', max_width=None, optional=False, sortable=True), None))¶

Columns of the change report closing every command that changes the installed inventory: install, remove, upgrade, restore and cleanup --orphans.

One row per package the command moved or should have, each carrying one PackageOutcome; see meta_package_manager.cli.package_outcomes() for how a row is classified. The version columns wrap like every other table’s, and package_id does not, per PACKAGE_ID_COLUMN. Neither version column drives --sort-by: a report sorts by what moved, not by how far. The two COOLDOWN_COLUMNS close the registry.

meta_package_manager.tables.WHICH_COLUMNS: tuple[tuple[ColumnSpec, SortableField | None], ...] = ((ColumnSpec(id='manager_id', label='Manager ID', description='Manager whose search path found the binary.', max_width=None, optional=False, sortable=True), SortableField.MANAGER_ID), (ColumnSpec(id='priority', label='Priority', description="Rank of the match in the manager's search path.", max_width=None, optional=False, sortable=True), None), (ColumnSpec(id='cli_path', label='CLI path', description='Location of the matched binary.', max_width=None, optional=False, sortable=True), None), (ColumnSpec(id='symlink', label='Symlink destination', description='Resolved target when the match is a symlink.', max_width=None, optional=False, sortable=True), None))¶

Columns of the mpm which table.

meta_package_manager.tables.column_specs(columns)[source]¶

Extract the bare ColumnSpec tuple from a column registry.

Return type:

tuple[ColumnSpec, ...]

meta_package_manager.tables.sort_header(spec, field)[source]¶

The header spec renders under, keyed by the field it sorts on.

click-extra reads a header’s ID as the field --sort-by addresses. For mpm that is the SortableField the column carries, not the column’s own ID: installed_version sorts on version. A column carrying no field keeps its own ID and declares sortable=False, which drops it from the selection.

The spec carries its own max_width, which click-extra reads off the header to size the column.

Return type:

ColumnSpec

meta_package_manager.tables.print_projected_table(ctx, columns, rows, default_ids=None)[source]¶

Render dict rows as a table projected through --columns.

The --columns selection restricts and reorders the rendering, SQL-SELECT-style; click-extra’s ColumnsOption already validated it against the same columns registry, so unknown IDs never reach this point. default_ids is the selection applied when the user passed none (search uses it to hide the description column unless --description); None keeps 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’s print_table() reads the selection (with the --table-format one) from the shared context meta and 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.

Note

A rendered header addresses its column by the field it sorts on, not by the ID --columns selects it with: the two can differ, and a column may sort on nothing at all. sort_header() builds each one.

Return type:

None

meta_package_manager.tables.print_serialized(ctx, data)[source]¶

Render data in the active serialization format, if one is active.

When the global --table-format resolves to one of the structured serialization formats (JSON, YAML, TOML, XML, …), serialize data under the shared mpm root element and return True. Otherwise print nothing and return False, so the caller falls through to its human-friendly rendering.

Return type:

bool

meta_package_manager.tables.print_serialized_and_exit(ctx, data)[source]¶

Render data in the active serialization format, then exit.

The read commands’ variant of print_serialized(): their serialized document is their whole output, so the command stops there. Otherwise return, so the caller falls through to its human-friendly table rendering.

Return type:

None