Source code for rmote.tools.facts.collectors.pacman
"""Read-only Arch package inventory."""
import os
import shutil
from dataclasses import dataclass
from rmote.process import async_process
from rmote.protocol import Tool
[docs]
@dataclass
class PacmanPackage:
version: str
[docs]
@dataclass
class PacmanInfo:
available: bool
version: str | None = None
packages: dict[str, PacmanPackage] | None = None
[docs]
class PacmanFacts(Tool):
"""Collect pacman version and installed packages under the ``pacman`` key.
Missing pacman produces ``available=False`` and None for version/packages.
``packages`` maps each installed name to its version, including locally
installed/foreign packages. ``version`` is the full pacman --version output.
Reads only the local database using -Q: no refresh, upgrades or network.
Each command has a 30-second timeout; command errors propagate.
"""
key = "pacman"
version = 1
result_type = PacmanInfo
[docs]
@staticmethod
def packages(output: str) -> dict[str, PacmanPackage]:
"""Parse pacman's name/version inventory without altering versions."""
result = {}
for line in output.splitlines():
name, version = line.split(maxsplit=1)
result[name] = PacmanPackage(version)
return result
[docs]
@staticmethod
async def collect() -> PacmanInfo:
"""Return the installed inventory without modifying pacman's database."""
executable = shutil.which("pacman")
result: PacmanInfo = PacmanInfo(available=executable is not None, version=None, packages=None)
if executable is None:
return result
options = {**os.environ, "LC_ALL": "C"}
version = await async_process(
executable, "--version", capture_output=True, text=True, check=True, timeout=30, env=options
)
inventory = await async_process(
executable, "-Q", capture_output=True, text=True, check=True, timeout=30, env=options
)
result.version = version.stdout.strip()
result.packages = PacmanFacts.packages(inventory.stdout)
return result
__tool_package__ = "rmote.tools.facts"