diff --git a/i3pystatus/updates/__init__.py b/i3pystatus/updates/__init__.py index 3e2beb2..d0b93e7 100644 --- a/i3pystatus/updates/__init__.py +++ b/i3pystatus/updates/__init__.py @@ -126,7 +126,10 @@ class Updates(Module): for backend in self.backends: name = backend.__class__.__name__ updates, notif_body = backend.updates - updates_count += updates + try: + updates_count += updates + except TypeError: + pass self.data[name] = updates self.notif_body[name] = notif_body or "" diff --git a/i3pystatus/updates/dnf.py b/i3pystatus/updates/dnf.py index c57ee4b..fcd4d80 100644 --- a/i3pystatus/updates/dnf.py +++ b/i3pystatus/updates/dnf.py @@ -1,33 +1,48 @@ -from i3pystatus.core.command import run_through_shell from i3pystatus.updates import Backend -from re import split, sub +import sys + +# Remove first dir from sys.path to avoid shadowing dnf module from +# site-packages dir when this module executed directly on the CLI. +__module_dir = sys.path.pop(0) +try: + import dnf + HAS_DNF_BINDINGS = True +except ImportError: + HAS_DNF_BINDINGS = False +finally: + # Replace the directory we popped earlier + sys.path.insert(0, __module_dir) class Dnf(Backend): """ - Gets updates for RPM-based distributions with `dnf check-update`. + Gets updates for RPM-based distributions using the `DNF API`_ - The notification body consists of the status line followed by the package - name and version for each update. + The notification body consists of the package name and version for each + available update. - https://dnf.readthedocs.org/en/latest/command_ref.html#check-update-command + .. _`DNF API`: http://dnf.readthedocs.io/en/latest/api.html """ @property def updates(self): - command = ["dnf", "check-update"] - dnf = run_through_shell(command) - if dnf.err: - return "?", dnf.err + if HAS_DNF_BINDINGS: + try: + with dnf.Base() as base: + base.read_all_repos() + base.fill_sack() + upgrades = base.sack.query().upgrades().run() - raw = dnf.out - update_count = 0 - if dnf.rc == 100: - lines = raw.splitlines()[2:] - lines = [l for l in lines if len(split("\s+", l.rstrip())) == 3] - update_count = len(lines) - notif_body = sub(r"(\S+)\s+(\S+)\s+\S+\s*\n", r"\1: \2\n", raw) - return update_count, notif_body + notif_body = ''.join([ + '%s: %s-%s\n' % (pkg.name, pkg.version, pkg.release) + for pkg in upgrades + ]) + return len(upgrades), notif_body + except Exception as exc: + self.logger.error('DNF update check failed', exc_info=True) + return '?', exc.__str__() + else: + return '?', 'Failed to import DNF Python bindings' Backend = Dnf