Skip to main content

pkgmanager

A single, reliable Python interface for package management on Arch Linux — official repos via pacman, and the AUR via whichever helper (yay, paru, trizen, pikaur) is installed. A lighter, AUR-helper-agnostic backend in the spirit of pamac.

Install

pip install -e .

Quick start

from pkgmanager import PackageManager

pm = PackageManager()
print(pm.helper_info(), pm.supports_aur())

for pkg in pm.search("firefox"):
    print(pkg.repository, pkg.name, pkg.version, "[installed]" if pkg.installed else "")

pm.install(["firefox"], callback=print)   # streams pacman/helper output line by line
detail = pm.info("firefox")
print(detail.depends, detail.license)

Every method is safe to call from a background thread — nothing touches UI state, and install/remove/update/refresh stream output via callback so a Tkinter/Qt window can update a log widget live instead of blocking.

pm.refresh(callback=print)          # sync package databases (-Sy)
for pkg in pm.list_upgradable():    # installed packages with a newer version
    print(pkg.name, pkg.version, "->", pkg.new_version)
for pkg in pm.list_repo_packages("extra"):  # every package in one official repo
    print(pkg.name, pkg.version)

GUI

pkgmanager_gui.pyw is a tabbed Tkinter front-end (Search/Gallery, Installed, Updates) with a Settings dialog:

  • Search — text search, an All/Official/AUR source filter, and a gallery (shown until the first search) of recent searches, popular packages, and repo-browse chips for core/extra/community/multilib/aur.
  • Installed — every installed package with a live name filter and a Remove button.
  • Updates — list_upgradable() results with an Update All button.
  • Settings — force a specific helper (or auto) and toggle --noconfirm, writing to ~/.config/pkgmanager/config.json. The chosen helper is validated by actually constructing a PackageManager before saving -- picking one that isn't installed shows an error and leaves your current, working backend untouched rather than silently breaking it.

Security model

  • Every command is built and run as an argument list (subprocess.Popen(..., shell=False)) — nothing is ever concatenated into a shell string.
  • search / info / list_installed run unprivileged.
  • install / remove / update are elevated via pkexec in a graphical session (with DISPLAY/XAUTHORITY/WAYLAND_DISPLAY forwarded, since pkexec starts a clean environment) or sudo otherwise.
  • If no AUR helper is installed, AUR operations are unavailable and supports_aur() reports False; official-repo operations keep working through plain pacman.

Structure

pkgmanager/
├── __init__.py     # public exports
├── models.py       # Package, PackageDetail
├── exceptions.py   # exception hierarchy
├── helpers.py      # HELPER_COMMANDS, detect_helper(), output parsers
├── permissions.py  # command execution + pkexec/sudo elevation
├── backend.py      # PackageManager -- the public API
└── config.py       # optional ~/.config/pkgmanager/config.json loader
tests/
├── test_helpers.py
├── test_backend.py
└── test_permissions.py

Where this deviates from the original spec

A few places where I made a judgment call or fixed something rather than transcribing literally:

  1. Forcing an unmapped helper no longer silently becomes pacman. The original fallback (HELPER_COMMANDS.get(self.helper, HELPER_COMMANDS['pacman'])) meant that forcing a helper with no command mapping (e.g. pacaur, which the spec explicitly allows forcing despite excluding it from auto-detection) would silently run pacman's commands while helper_info() kept reporting the forced name and supports_aur() returned False regardless of what that helper actually supports. detect_helper() now raises HelperNotFoundError immediately for any forced name with no entry in HELPER_COMMANDS.
  2. list_installed parsing accepts real pacman output, not just the spec's example. The spec's example (local/firefox 120.0.1-1) has a local/ prefix; real pacman -Q output has none — it's just firefox 120.0.1-1. The parser accepts both, defaulting to 'local' when no prefix is present.
  3. Search results now flag already-installed packages. Real pacman/AUR-helper -Ss output tags installed entries with [installed]; this is now parsed into Package.installed, so a GUI search view can badge them without a separate list_installed() call.
  4. AURHelperMissingError now has an actual trigger. The spec left this open ("we can't know without checking – we'll trust the user"). install() takes an opt-in known_aur=True flag for callers that already know a package is AUR-only (e.g. from a search() result's is_aur flag), raising immediately instead of leaving pacman to fail confusingly. Not added to remove()/update() — neither needs the AUR helper to operate on already-installed packages.
  5. The streaming generator distinguishes early-exit from failure. If a caller stops consuming install/remove/update output partway through (rather than the process actually failing), it cleans up without raising a spurious CommandFailedError.
  6. Repo-category browsing resolved with a real listing, not a stub. The follow-up spec explicitly punted on this ("pragmatic: skip category filtering for now, but keep the buttons for future extension"). Rather than ship dead buttons, list_repo_packages(repo) wraps pacman -Sl <repo> to actually list everything in an official repo. It always calls pacman directly regardless of the active AUR helper, since this is a pure sync-database read with no helper-specific behavior. There's no AUR equivalent (no bulk-listing endpoint exists), so the 'aur' category chip instead sets the source filter to AUR and re-renders the current result set client-side.
  7. Settings validates before it commits. The follow-up spec's _save_settings sketch replaced self.pm and wrote the config file unconditionally. If the chosen helper isn't actually installed, that would silently leave the app with a broken backend and a config file that fails on next launch too. The dialog now constructs the new PackageManager first; on failure it shows an error and leaves the previous, working backend and config untouched.
  8. Tab-switching no longer assumes the backend has finished initializing. Switching to Installed/Updates before PackageManager() detection completes would otherwise hand None to a background thread, crash it silently, and leave the UI stuck in a "busy" state (buttons disabled, spinner running) forever.

Tests

python -m unittest discover -s tests -t . -v

47 tests, all mocked (subprocess.Popen / shutil.which) — there's no live pacman or AUR helper in scope here, so nothing actually shells out during the test run. The GUI itself is checked with a headless smoke test under Xvfb during development (not shipped -- it needs tkinter and a display, neither guaranteed on a CI box) that builds the full tabbed UI, exercises tab-switching, gallery/filter logic, and the settings dialog.

Metadata

Release files for aur-gui 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aur-gui 0.1.0
File Size Uploaded Provenance Actions
aur_gui-0.1.0.tar.gz 16.7 kB Without provenance Details for aur_gui-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for aur-gui 0.1.0
File Interpreter ABI Platform Actions
aur_gui-0.1.0-py3-none-any.whl Python 3 none any Details for aur_gui-0.1.0-py3-none-any.whl

Total release size: 32.4 kB

Release files / aur_gui-0.1.0.tar.gz

Download URL aur_gui-0.1.0.tar.gz
Size 16.7 kB
Tags Source
SHA-256 checksum
How to use checksums
4c8f74fb3e450bf919b39cf6fb963839aed0d70f71196fa2881eda32d7e21b89
BLAKE2b-256 checksum
How to use checksums
c83fbe25b141cd6820c681ce24168cf9703570d3b918b84638449fa91e1c2057
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Release files / aur_gui-0.1.0-py3-none-any.whl

Download URL aur_gui-0.1.0-py3-none-any.whl
Size 15.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b854b219e14db3ee11f976b60271a5c1291887b395763181dbf335b6f2d2d400
BLAKE2b-256 checksum
How to use checksums
3f1453baacef7c3a2c6f54166e2456a91d45f0ed4a32c5ac491366324b6bc96e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Security for aur-gui 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page