mirror of
https://github.com/kiviktnm/decman.git
synced 2026-09-19 12:08:28 +00:00
Improve orphan package discovery
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
import decman.plugins.pacman as pacman
|
import decman.plugins.pacman as pacman
|
||||||
import pyalpm
|
|
||||||
|
|
||||||
import decman.config as config
|
import decman.config as config
|
||||||
import decman.core.command as command
|
import decman.core.command as command
|
||||||
@@ -142,15 +141,7 @@ class AurPacmanInterface(pacman.PacmanInterface):
|
|||||||
"""
|
"""
|
||||||
Returns a set of orphaned foreign packages.
|
Returns a set of orphaned foreign packages.
|
||||||
"""
|
"""
|
||||||
out: set[str] = set()
|
return self._get_orphans(pacman.PacmanInterface._is_foreign)
|
||||||
for pkg in self._handle.get_localdb().pkgcache:
|
|
||||||
if pkg.reason != pyalpm.PKG_REASON_DEPEND:
|
|
||||||
continue
|
|
||||||
if pkg.compute_requiredby():
|
|
||||||
continue
|
|
||||||
if not self._is_native(pkg.name):
|
|
||||||
out.add(pkg.name)
|
|
||||||
return out
|
|
||||||
|
|
||||||
def is_installable(self, pkg: str) -> bool:
|
def is_installable(self, pkg: str) -> bool:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
import pyalpm
|
import pyalpm
|
||||||
|
|
||||||
@@ -208,6 +209,7 @@ class PacmanInterface:
|
|||||||
self._handle = self._create_pyalpm_handle()
|
self._handle = self._create_pyalpm_handle()
|
||||||
self._name_index = self._create_name_index()
|
self._name_index = self._create_name_index()
|
||||||
self._provides_index = self._create_provides_index()
|
self._provides_index = self._create_provides_index()
|
||||||
|
self._requiredby_index = self._create_requiredby_index()
|
||||||
|
|
||||||
def _create_pyalpm_handle(self):
|
def _create_pyalpm_handle(self):
|
||||||
root = "/"
|
root = "/"
|
||||||
@@ -226,8 +228,8 @@ class PacmanInterface:
|
|||||||
|
|
||||||
return h
|
return h
|
||||||
|
|
||||||
def _create_name_index(self) -> set[str]:
|
def _create_name_index(self) -> dict[str, pyalpm.Package]:
|
||||||
return {pkg.name for db in self._handle.get_syncdbs() for pkg in db.pkgcache}
|
return {pkg.name: pkg for db in self._handle.get_syncdbs() for pkg in db.pkgcache}
|
||||||
|
|
||||||
def _create_provides_index(self) -> dict[str, set[str]]:
|
def _create_provides_index(self) -> dict[str, set[str]]:
|
||||||
out: dict[str, set[str]] = {}
|
out: dict[str, set[str]] = {}
|
||||||
@@ -235,11 +237,18 @@ class PacmanInterface:
|
|||||||
for pkg in db.pkgcache:
|
for pkg in db.pkgcache:
|
||||||
for p in pkg.provides:
|
for p in pkg.provides:
|
||||||
out.setdefault(strip_dependency(p), set()).add(pkg.name)
|
out.setdefault(strip_dependency(p), set()).add(pkg.name)
|
||||||
|
out.setdefault(p, set()).add(pkg.name)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
def _create_requiredby_index(self) -> dict[str, set[str]]:
|
||||||
|
return {p.name: set(p.compute_requiredby()) for p in self._handle.get_localdb().pkgcache}
|
||||||
|
|
||||||
def _is_native(self, package: str) -> bool:
|
def _is_native(self, package: str) -> bool:
|
||||||
return package in self._name_index
|
return package in self._name_index
|
||||||
|
|
||||||
|
def _is_foreign(self, package: str) -> bool:
|
||||||
|
return not self._is_native(package)
|
||||||
|
|
||||||
def get_native_explicit(self) -> set[str]:
|
def get_native_explicit(self) -> set[str]:
|
||||||
"""
|
"""
|
||||||
Returns a set of explicitly installed native packages.
|
Returns a set of explicitly installed native packages.
|
||||||
@@ -252,19 +261,28 @@ class PacmanInterface:
|
|||||||
|
|
||||||
return packages
|
return packages
|
||||||
|
|
||||||
|
def _get_orphans(self, filter_fn: Callable[["PacmanInterface", str], bool]) -> set[str]:
|
||||||
|
orphans: set[str] = {
|
||||||
|
p.name
|
||||||
|
for p in self._handle.get_localdb().pkgcache
|
||||||
|
if p.reason == pyalpm.PKG_REASON_DEPEND and filter_fn(self, p.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prune orphans until there are only packages that are requiredby other orphans
|
||||||
|
changed = True
|
||||||
|
while changed:
|
||||||
|
changed = False
|
||||||
|
for name in tuple(orphans):
|
||||||
|
if self._requiredby_index.get(name, set()) - orphans:
|
||||||
|
orphans.remove(name)
|
||||||
|
changed = True
|
||||||
|
return orphans
|
||||||
|
|
||||||
def get_native_orphans(self) -> set[str]:
|
def get_native_orphans(self) -> set[str]:
|
||||||
"""
|
"""
|
||||||
Returns a set of orphaned native packages.
|
Returns a set of orphaned native packages.
|
||||||
"""
|
"""
|
||||||
out: set[str] = set()
|
return self._get_orphans(PacmanInterface._is_native)
|
||||||
for pkg in self._handle.get_localdb().pkgcache:
|
|
||||||
if pkg.reason != pyalpm.PKG_REASON_DEPEND:
|
|
||||||
continue
|
|
||||||
if pkg.compute_requiredby():
|
|
||||||
continue
|
|
||||||
if self._is_native(pkg.name):
|
|
||||||
out.add(pkg.name)
|
|
||||||
return out
|
|
||||||
|
|
||||||
def get_foreign_explicit(self) -> set[str]:
|
def get_foreign_explicit(self) -> set[str]:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import pyalpm
|
||||||
|
import pytest
|
||||||
|
from decman.plugins.aur import AurPacmanInterface
|
||||||
|
from decman.plugins.pacman import PacmanInterface
|
||||||
|
|
||||||
|
|
||||||
|
class FakePackage:
|
||||||
|
def __init__(self, name: str, is_explicit: bool, required_by: list[str]):
|
||||||
|
self.name = name
|
||||||
|
self.reason = pyalpm.PKG_REASON_EXPLICIT if is_explicit else pyalpm.PKG_REASON_DEPEND
|
||||||
|
self.required_by = required_by
|
||||||
|
self.provides = [name]
|
||||||
|
|
||||||
|
def compute_requiredby(self):
|
||||||
|
return self.required_by
|
||||||
|
|
||||||
|
|
||||||
|
class FakeDB:
|
||||||
|
def __init__(self, pkgcache: list[FakePackage]):
|
||||||
|
self.pkgcache = pkgcache
|
||||||
|
|
||||||
|
|
||||||
|
class FakePyalpmHandle:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_syncdbs(self):
|
||||||
|
return [
|
||||||
|
FakeDB(
|
||||||
|
[
|
||||||
|
FakePackage("a", True, []),
|
||||||
|
FakePackage("b", False, ["a"]),
|
||||||
|
FakePackage("c", False, ["b"]),
|
||||||
|
FakePackage("d", False, []),
|
||||||
|
FakePackage("e", False, ["f"]),
|
||||||
|
FakePackage("f", False, ["g"]),
|
||||||
|
FakePackage("g", False, []),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_localdb(self):
|
||||||
|
return FakeDB(
|
||||||
|
self.get_syncdbs()[0].pkgcache
|
||||||
|
+ [
|
||||||
|
FakePackage("h", True, []),
|
||||||
|
FakePackage("i", False, ["h"]),
|
||||||
|
FakePackage("j", False, []),
|
||||||
|
FakePackage("k", False, ["l"]),
|
||||||
|
FakePackage("l", False, []),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def fake_create_pyalpm_handle(self):
|
||||||
|
return FakePyalpmHandle()
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_native_orphans_pacman(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
monkeypatch.setattr(PacmanInterface, "_create_pyalpm_handle", fake_create_pyalpm_handle)
|
||||||
|
|
||||||
|
interface = PacmanInterface(
|
||||||
|
None, # type: ignore
|
||||||
|
False,
|
||||||
|
set(),
|
||||||
|
2048,
|
||||||
|
"/var/lib/pacman/",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert interface.get_native_orphans() == {"d", "e", "f", "g"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_foreign_orphans_aur(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
monkeypatch.setattr(AurPacmanInterface, "_create_pyalpm_handle", fake_create_pyalpm_handle)
|
||||||
|
|
||||||
|
interface = AurPacmanInterface(
|
||||||
|
None, # type: ignore
|
||||||
|
False,
|
||||||
|
set(),
|
||||||
|
2048,
|
||||||
|
"/var/lib/pacman/",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert interface.get_foreign_orphans() == {"j", "k", "l"}
|
||||||
Reference in New Issue
Block a user