Source code for meta_package_manager.managers.pacstall

# Copyright Kevin Deldycke <kevin@deldycke.com> and contributors.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

from __future__ import annotations

import re
from typing import ClassVar

from extra_platforms import LINUX_LIKE

from ..capabilities import search_capabilities, version_not_implemented
from ..manager import PackageManager

TYPE_CHECKING = False
if TYPE_CHECKING:
    from collections.abc import Iterator

    from ..package import Package


[docs] class Pacstall(PackageManager): """AUR-inspired, source-based package manager for Ubuntu and other Linux distributions. Pacstall builds packages from "pacscripts" and installs the results as ``.deb`` archives through ``dpkg``. ``mpm`` forces ``NO_COLOR`` and ``DISABLE_PROMPTS`` so output is uncolored and every call stays non-interactive. .. note:: Listing installed packages is a two-step probe: piped ``--list`` prints bare names with no versions, so ``mpm`` follows each with a ``--cache-info <pkg> version`` call to recover its version. .. note:: ``--search`` matches names only and reports no versions. There is no per-package upgrade verb either, so upgrading one package reinstalls it through ``--install``. """ homepage_url = "https://pacstall.dev" platforms = LINUX_LIKE requirement = ">=6.0.0" extra_env: ClassVar = {"NO_COLOR": "1", "DISABLE_PROMPTS": "1"} """Suppress ANSI colors and disable interactive prompts.""" version_regexes = (r"(?P<version>\d+\.\d+\.\d+)",) """ .. code-block:: shell-session $ pacstall --version 6.3.7 Vanilla """ # Upgrade rows are indented (a tab in real pacstall output); the header line # sits at column 0. Match any indentation, not a literal tab: Python # formatters expand tabs inside the docstring sample the corpus test replays. _OUTDATED_REGEXP = re.compile( r"^[ \t]+(?P<package_id>\S+)\s+@\s+\S+" r"\s+\(\s*(?P<installed_version>\S+)" r"\s*->\s*(?P<latest_version>\S+)\s*\)", re.MULTILINE, ) _SEARCH_REGEXP = re.compile( r"^(?P<package_id>\S+)\s+@\s+\S+$", re.MULTILINE, ) @property def installed(self) -> Iterator[Package]: """Fetch installed packages. .. code-block:: shell-session $ pacstall --list neofetch neovim .. note:: When piped, ``pacstall --list`` outputs bare package names without versions. A follow-up ``pacstall --cache-info <pkg> version`` call retrieves the installed version per package. """ output = self.run_cli("--list") for line in output.splitlines(): package_id = line.strip() if not package_id: continue version_output = self.run_cli("--cache-info", package_id, "version") installed_version = version_output.strip() if version_output else None yield self.package( id=package_id, installed_version=installed_version, ) @property def outdated(self) -> Iterator[Package]: """Fetch outdated packages. .. code-block:: shell-session $ pacstall --list-upgrades Upgradable: 2 neofetch @ pacstall-programs#master ( 7.1.0-2 -> 7.2.0-1 ) neovim @ pacstall-programs#master ( 0.9.4-1 -> 0.10.0-1 ) """ output = self.run_cli("--list-upgrades") for match in self._OUTDATED_REGEXP.finditer(output): yield self.package( id=match.group("package_id"), installed_version=match.group("installed_version"), latest_version=match.group("latest_version"), )
[docs] @search_capabilities(extended_support=False, exact_support=False) def search(self, query: str, extended: bool, exact: bool) -> Iterator[Package]: """Fetch matching packages. .. caution:: Search does not support extended or exact matching, and does not provide version information. Returns the best subset of results and lets :py:meth:`meta_package_manager.manager.PackageManager.refiltered_search` refine them. .. code-block:: shell-session $ pacstall --search neovim neovim @ pacstall-programs neovim-git @ pacstall-programs """ output = self.run_cli("--search", query) for match in self._SEARCH_REGEXP.finditer(output): yield self.package(id=match.group("package_id"))
[docs] @version_not_implemented def install(self, package_id: str, version: str | None = None) -> str: """Install one package. .. code-block:: shell-session $ pacstall --install neofetch """ return self.run_cli("--install", package_id)
[docs] def upgrade_all_cli(self) -> tuple[str, ...]: """Generates the CLI to upgrade all packages. .. code-block:: shell-session $ pacstall --upgrade """ return self.build_cli("--upgrade")
[docs] @version_not_implemented def upgrade_one_cli( self, package_id: str, version: str | None = None, ) -> tuple[str, ...]: """Generates the CLI to upgrade one package by reinstalling it. .. code-block:: shell-session $ pacstall --install neofetch """ return self.build_cli("--install", package_id)
[docs] def remove(self, package_id: str) -> str: """Remove one package. .. code-block:: shell-session $ pacstall --remove neofetch """ return self.run_cli("--remove", package_id)
[docs] def sync(self) -> None: """Sync package metadata from remote repositories. .. code-block:: shell-session $ pacstall --update """ self.run_cli("--update")