From fa6aee8d66b36615a5b8de9152ca7ac4c79767ef Mon Sep 17 00:00:00 2001 From: Kivi Kaitaniemi Date: Fri, 26 Apr 2024 14:43:52 +0300 Subject: [PATCH] Improve error reporting --- src/decman/__init__.py | 63 +++++++++++++++++++++----------------- src/decman/lib/__init__.py | 45 ++++++++++++++++++--------- src/decman/lib/aur.py | 28 ++++++++++++----- 3 files changed, 86 insertions(+), 50 deletions(-) diff --git a/src/decman/__init__.py b/src/decman/__init__.py index 066b7f3..d6845e6 100644 --- a/src/decman/__init__.py +++ b/src/decman/__init__.py @@ -23,33 +23,35 @@ def sh(sh_cmd: str, env = os.environ.copy() for var, val in env_overrides.items(): env[var] = val - try: - if user is None: + if user is None: + try: subprocess.run(sh_cmd, shell=True, check=True, env=env) - else: + except subprocess.CalledProcessError as e: + raise decman.error.UserFacingError( + f"Running user defined shell command '{sh_cmd}' failed." + ) from e + else: + try: uid = pwd.getpwnam(user).pw_uid gid = pwd.getpwnam(user).pw_gid + except KeyError as e: + raise decman.error.UserFacingError( + f"Running user defined shell command failed because the user {user} doesn't exist." + ) from e - with subprocess.Popen(sh_cmd, - shell=True, - group=gid, - user=uid, - env=env) as process: - if process.wait() != 0: - raise decman.error.UserFacingError( - f"Running user shell command '{sh_cmd}' as {user} failed." - ) - except (subprocess.CalledProcessError, KeyError) as e: - raise decman.error.UserFacingError( - f"Running user shell command '{sh_cmd}' failed.") from e + with subprocess.Popen(sh_cmd, shell=True, group=gid, user=uid, + env=env) as process: + if process.wait() != 0: + raise decman.error.UserFacingError( + f"Running user shell command '{sh_cmd}' as {user} failed.") -def cmd(command: list[str], +def prg(command: list[str], user: typing.Optional[str] = None, env_overrides: typing.Optional[dict[str, str]] = None): """ - Shortcut for running a command. + Shortcut for running a program. """ if env_overrides is None: env_overrides = {} @@ -58,21 +60,26 @@ def cmd(command: list[str], for var, val in env_overrides.items(): env[var] = val - try: - if user is None: + if user is None: + try: subprocess.run(command, check=True, env=env) - else: + except subprocess.CalledProcessError as e: + raise decman.error.UserFacingError( + f"Running user defined program '{command}' failed.") from e + else: + try: uid = pwd.getpwnam(user).pw_uid gid = pwd.getpwnam(user).pw_gid + except KeyError as e: + raise decman.error.UserFacingError( + f"Running user defined program failed because the user {user} doesn't exist." + ) from e - with subprocess.Popen(command, group=gid, user=uid, - env=env) as process: - if process.wait() != 0: - raise decman.error.UserFacingError( - f"Running user command '{command}' as {user} failed.") - except (subprocess.CalledProcessError, KeyError) as e: - raise decman.error.UserFacingError( - f"Running user command '{command}' failed.") from e + with subprocess.Popen(command, group=gid, user=uid, + env=env) as process: + if process.wait() != 0: + raise decman.error.UserFacingError( + f"Running user program '{command}' as {user} failed.") class File: diff --git a/src/decman/lib/__init__.py b/src/decman/lib/__init__.py index cd9ba55..8920ec0 100644 --- a/src/decman/lib/__init__.py +++ b/src/decman/lib/__init__.py @@ -182,7 +182,8 @@ class Store: with open(path, "wt", encoding="utf-8") as file: json.dump(d, file) except OSError as e: - raise err.UserFacingError("Failed to save store.") from e + print_error(f"{e}") + raise err.UserFacingError("Failed to save decman store.") from e @staticmethod def restore() -> "Store": @@ -220,9 +221,13 @@ class Store: return store except json.JSONDecodeError as e: - raise err.UserFacingError("Failed to parse state json.") from e + print_error(f"{e}") + raise err.UserFacingError( + "Failed to parse decman store json.") from e except OSError as e: - raise err.UserFacingError("Failed to read saved store.") from e + print_error(f"{e}") + raise err.UserFacingError( + "Failed to read saved decman store.") from e class Source: @@ -302,6 +307,7 @@ class Source: file.copy_to(target, variables) print_debug(f"Installing file to {target}.") except OSError as e: + print_error(f"{e}") raise err.UserFacingError( f"Failed to install file to {target}.") from e @@ -312,6 +318,7 @@ class Source: print_debug(f"Installing directory to {target}.") directory.copy_to(target, variables) except OSError as e: + print_error(f"{e}") raise err.UserFacingError( f"Failed to install directory to {target}.") from e @@ -543,7 +550,7 @@ class Pacman: for line in output] except IndexError as error: raise err.UserFacingError( - f"Failed to get foreign packages from pacman output. Output: {output}" + f"Failed to parse foreign packages from pacman output. Output: {output}" ) from error def install(self, packages: list[str]): @@ -553,7 +560,8 @@ class Pacman: try: subprocess.run(conf.commands.install_pkgs(packages), check=True) except subprocess.CalledProcessError as error: - raise err.UserFacingError("Failed to install packages.") from error + raise err.UserFacingError( + "Failed to install packages using pacman.") from error def install_dependencies(self, deps: list[str]): """ @@ -563,7 +571,8 @@ class Pacman: subprocess.run(conf.commands.install_deps(deps), check=True) except subprocess.CalledProcessError as error: raise err.UserFacingError( - "Failed to install dependency packages.") from error + "Failed to install packages as dependencies using pacman." + ) from error def install_files(self, files: list[str], as_explicit: list[str]): """ @@ -577,8 +586,11 @@ class Pacman: check=True, capture_output=conf.suppress_command_output) except subprocess.CalledProcessError as error: + if conf.suppress_command_output: + print_error("Output:") + print_continuation(error.output) raise err.UserFacingError( - "Failed to install foreign packages.") from error + "Failed to install package files using pacman.") from error def upgrade(self): """ @@ -587,7 +599,8 @@ class Pacman: try: subprocess.run(conf.commands.upgrade(), check=True) except subprocess.CalledProcessError as error: - raise err.UserFacingError("Failed to update packages.") from error + raise err.UserFacingError( + "Failed to upgrade packages using pacman.") from error def remove(self, packages: list[str]): """ @@ -596,7 +609,8 @@ class Pacman: try: subprocess.run(conf.commands.remove(packages), check=True) except subprocess.CalledProcessError as error: - raise err.UserFacingError("Failed to remove packages.") from error + raise err.UserFacingError( + "Failed to remove packages using pacman.") from error class Systemd: @@ -615,7 +629,7 @@ class Systemd: subprocess.run(conf.commands.enable_units(units), check=True) except subprocess.CalledProcessError as error: raise err.UserFacingError( - "Failed to enable systemd units.") from error + f"Failed to enable systemd units: {units}") from error self.state.enabled_systemd_units += units def disable_units(self, units: list[str]): @@ -626,7 +640,7 @@ class Systemd: subprocess.run(conf.commands.disable_units(units), check=True) except subprocess.CalledProcessError as error: raise err.UserFacingError( - "Failed to disable systemd units.") from error + f"Failed to disable systemd units: {units}") from error for unit in units: try: self.state.enabled_systemd_units.remove(unit) @@ -646,10 +660,10 @@ class Systemd: user=uid) as process: if process.wait() != 0: raise err.UserFacingError( - f"Failed to enable systemd units for {user}.") + f"Failed to enable systemd units: {units} for {user}.") except KeyError as error: raise err.UserFacingError( - f"Failed to enable systemd units because user '{user}' doesn't exist." + f"Failed to enable systemd units because user {user} doesn't exist." ) from error for unit in units: self.state.enabled_user_systemd_units.append((user, unit)) @@ -667,10 +681,11 @@ class Systemd: user=uid) as process: if process.wait() != 0: raise err.UserFacingError( - f"Failed to disable systemd units for {user}.") + f"Failed to disable systemd units: {units} for {user}." + ) except KeyError as error: raise err.UserFacingError( - f"Failed to disable systemd units because user '{user}' doesn't exist." + f"Failed to disable systemd units because user {user} doesn't exist." ) from error for unit in units: diff --git a/src/decman/lib/aur.py b/src/decman/lib/aur.py index 4117ee1..3d23387 100644 --- a/src/decman/lib/aur.py +++ b/src/decman/lib/aur.py @@ -324,8 +324,10 @@ class ExtendedPackageSearch: l.print_debug("Request completed.") except (requests.RequestException, KeyError) as e: + l.print_error(f"{e}") raise err.UserFacingError( - "Failed to fetch package information from AUR RPC.") from e + f"Failed to fetch package information for {packages} from AUR RPC." + ) from e def get_package_info(self, package: str) -> typing.Optional[PackageInfo]: """ @@ -379,8 +381,10 @@ class ExtendedPackageSearch: return info except (requests.RequestException, KeyError) as e: + l.print_error(f"{e}") raise err.UserFacingError( - "Failed to fetch package information from AUR RPC.") from e + f"Failed to fetch package information for {package} from AUR RPC." + ) from e def find_provider( self, stripped_dependency: str) -> typing.Optional[PackageInfo]: @@ -452,8 +456,10 @@ class ExtendedPackageSearch: return self._choose_provider(stripped_dependency, results, "AUR") except (requests.RequestException, KeyError) as e: + l.print_error(f"{e}") raise err.UserFacingError( - "Failed to fetch package information from AUR RPC.") from e + f"Failed to search for {stripped_dependency} from AUR RPC." + ) from e def _choose_provider(self, dep: str, possible_providers: list[str], where: str) -> typing.Optional[PackageInfo]: @@ -569,7 +575,9 @@ class ForeignPackageManager: info = self._search.get_package_info(pkg) if info is None: - raise err.UserFacingError(f"Failed to find package: {pkg}.") + raise err.UserFacingError( + f"Failed to find '{pkg}' from AUR or user provided packages." + ) if self.should_upgrade_package(pkg, ver, info.version, upgrade_devel): @@ -650,6 +658,7 @@ class ForeignPackageManager: builder.build_packages(pkgbase, packages, force) except (subprocess.CalledProcessError, OSError) as e: + l.print_error(f"{e}") raise err.UserFacingError("Failed to build packages.") from e packages_to_install = list(resolved_dependencies.foreign_pkgs) @@ -792,7 +801,9 @@ class ForeignPackageManager: ) return should_upgrade except (ValueError, subprocess.CalledProcessError) as error: - raise err.UserFacingError("Failed to compare versions.") from error + l.print_error(f"{error}") + raise err.UserFacingError( + "Failed to compare versions using vercmp.") from error class PackageBuilder: @@ -1031,7 +1042,7 @@ class PackageBuilder: for foreign_pkg in chroot_foreign_pkgs: entry = self._store.get_package(foreign_pkg) assert entry is not None, "Build order determines that the dependencies are built \ - before and thus are found in the cache." +before and thus are found in the cache." _, file = entry @@ -1058,7 +1069,7 @@ class PackageBuilder: if len(matches) != 1: raise err.UserFacingError( - f"Failed to build package '{pkgname}', because the pkg file cannot be determined." + f"Failed to build package '{pkgname}', because the pkg file cannot be determined. Possible files are: {matches}" ) return matches[0] @@ -1100,6 +1111,9 @@ class PackageBuilder: raise err.UserFacingError("Building aborted.") except subprocess.CalledProcessError as error: + if conf.suppress_command_output: + l.print_error("Output:") + l.print_continuation(error.output) raise err.UserFacingError( f"Failed to clone and review PKGBUILD from {git_url}" ) from error