Improve error reporting

This commit is contained in:
Kivi Kaitaniemi
2024-04-26 14:43:52 +03:00
parent c97587b3f5
commit fa6aee8d66
3 changed files with 86 additions and 50 deletions
+24 -17
View File
@@ -23,33 +23,35 @@ def sh(sh_cmd: str,
env = os.environ.copy() env = os.environ.copy()
for var, val in env_overrides.items(): for var, val in env_overrides.items():
env[var] = val env[var] = val
try:
if user is None: if user is None:
try:
subprocess.run(sh_cmd, shell=True, check=True, env=env) subprocess.run(sh_cmd, shell=True, check=True, env=env)
except subprocess.CalledProcessError as e:
raise decman.error.UserFacingError(
f"Running user defined shell command '{sh_cmd}' failed."
) from e
else: else:
try:
uid = pwd.getpwnam(user).pw_uid uid = pwd.getpwnam(user).pw_uid
gid = pwd.getpwnam(user).pw_gid 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, with subprocess.Popen(sh_cmd, shell=True, group=gid, user=uid,
shell=True,
group=gid,
user=uid,
env=env) as process: env=env) as process:
if process.wait() != 0: if process.wait() != 0:
raise decman.error.UserFacingError( raise decman.error.UserFacingError(
f"Running user shell command '{sh_cmd}' as {user} failed." 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
def cmd(command: list[str], def prg(command: list[str],
user: typing.Optional[str] = None, user: typing.Optional[str] = None,
env_overrides: typing.Optional[dict[str, 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: if env_overrides is None:
env_overrides = {} env_overrides = {}
@@ -58,21 +60,26 @@ def cmd(command: list[str],
for var, val in env_overrides.items(): for var, val in env_overrides.items():
env[var] = val env[var] = val
try:
if user is None: if user is None:
try:
subprocess.run(command, check=True, env=env) subprocess.run(command, check=True, env=env)
except subprocess.CalledProcessError as e:
raise decman.error.UserFacingError(
f"Running user defined program '{command}' failed.") from e
else: else:
try:
uid = pwd.getpwnam(user).pw_uid uid = pwd.getpwnam(user).pw_uid
gid = pwd.getpwnam(user).pw_gid 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, with subprocess.Popen(command, group=gid, user=uid,
env=env) as process: env=env) as process:
if process.wait() != 0: if process.wait() != 0:
raise decman.error.UserFacingError( raise decman.error.UserFacingError(
f"Running user command '{command}' as {user} failed.") f"Running user program '{command}' as {user} failed.")
except (subprocess.CalledProcessError, KeyError) as e:
raise decman.error.UserFacingError(
f"Running user command '{command}' failed.") from e
class File: class File:
+30 -15
View File
@@ -182,7 +182,8 @@ class Store:
with open(path, "wt", encoding="utf-8") as file: with open(path, "wt", encoding="utf-8") as file:
json.dump(d, file) json.dump(d, file)
except OSError as e: 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 @staticmethod
def restore() -> "Store": def restore() -> "Store":
@@ -220,9 +221,13 @@ class Store:
return store return store
except json.JSONDecodeError as e: 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: 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: class Source:
@@ -302,6 +307,7 @@ class Source:
file.copy_to(target, variables) file.copy_to(target, variables)
print_debug(f"Installing file to {target}.") print_debug(f"Installing file to {target}.")
except OSError as e: except OSError as e:
print_error(f"{e}")
raise err.UserFacingError( raise err.UserFacingError(
f"Failed to install file to {target}.") from e f"Failed to install file to {target}.") from e
@@ -312,6 +318,7 @@ class Source:
print_debug(f"Installing directory to {target}.") print_debug(f"Installing directory to {target}.")
directory.copy_to(target, variables) directory.copy_to(target, variables)
except OSError as e: except OSError as e:
print_error(f"{e}")
raise err.UserFacingError( raise err.UserFacingError(
f"Failed to install directory to {target}.") from e f"Failed to install directory to {target}.") from e
@@ -543,7 +550,7 @@ class Pacman:
for line in output] for line in output]
except IndexError as error: except IndexError as error:
raise err.UserFacingError( 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 ) from error
def install(self, packages: list[str]): def install(self, packages: list[str]):
@@ -553,7 +560,8 @@ class Pacman:
try: try:
subprocess.run(conf.commands.install_pkgs(packages), check=True) subprocess.run(conf.commands.install_pkgs(packages), check=True)
except subprocess.CalledProcessError as error: 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]): def install_dependencies(self, deps: list[str]):
""" """
@@ -563,7 +571,8 @@ class Pacman:
subprocess.run(conf.commands.install_deps(deps), check=True) subprocess.run(conf.commands.install_deps(deps), check=True)
except subprocess.CalledProcessError as error: except subprocess.CalledProcessError as error:
raise err.UserFacingError( 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]): def install_files(self, files: list[str], as_explicit: list[str]):
""" """
@@ -577,8 +586,11 @@ class Pacman:
check=True, check=True,
capture_output=conf.suppress_command_output) capture_output=conf.suppress_command_output)
except subprocess.CalledProcessError as error: except subprocess.CalledProcessError as error:
if conf.suppress_command_output:
print_error("Output:")
print_continuation(error.output)
raise err.UserFacingError( raise err.UserFacingError(
"Failed to install foreign packages.") from error "Failed to install package files using pacman.") from error
def upgrade(self): def upgrade(self):
""" """
@@ -587,7 +599,8 @@ class Pacman:
try: try:
subprocess.run(conf.commands.upgrade(), check=True) subprocess.run(conf.commands.upgrade(), check=True)
except subprocess.CalledProcessError as error: 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]): def remove(self, packages: list[str]):
""" """
@@ -596,7 +609,8 @@ class Pacman:
try: try:
subprocess.run(conf.commands.remove(packages), check=True) subprocess.run(conf.commands.remove(packages), check=True)
except subprocess.CalledProcessError as error: 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: class Systemd:
@@ -615,7 +629,7 @@ class Systemd:
subprocess.run(conf.commands.enable_units(units), check=True) subprocess.run(conf.commands.enable_units(units), check=True)
except subprocess.CalledProcessError as error: except subprocess.CalledProcessError as error:
raise err.UserFacingError( 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 self.state.enabled_systemd_units += units
def disable_units(self, units: list[str]): def disable_units(self, units: list[str]):
@@ -626,7 +640,7 @@ class Systemd:
subprocess.run(conf.commands.disable_units(units), check=True) subprocess.run(conf.commands.disable_units(units), check=True)
except subprocess.CalledProcessError as error: except subprocess.CalledProcessError as error:
raise err.UserFacingError( raise err.UserFacingError(
"Failed to disable systemd units.") from error f"Failed to disable systemd units: {units}") from error
for unit in units: for unit in units:
try: try:
self.state.enabled_systemd_units.remove(unit) self.state.enabled_systemd_units.remove(unit)
@@ -646,10 +660,10 @@ class Systemd:
user=uid) as process: user=uid) as process:
if process.wait() != 0: if process.wait() != 0:
raise err.UserFacingError( raise err.UserFacingError(
f"Failed to enable systemd units for {user}.") f"Failed to enable systemd units: {units} for {user}.")
except KeyError as error: except KeyError as error:
raise err.UserFacingError( 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 ) from error
for unit in units: for unit in units:
self.state.enabled_user_systemd_units.append((user, unit)) self.state.enabled_user_systemd_units.append((user, unit))
@@ -667,10 +681,11 @@ class Systemd:
user=uid) as process: user=uid) as process:
if process.wait() != 0: if process.wait() != 0:
raise err.UserFacingError( raise err.UserFacingError(
f"Failed to disable systemd units for {user}.") f"Failed to disable systemd units: {units} for {user}."
)
except KeyError as error: except KeyError as error:
raise err.UserFacingError( 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 ) from error
for unit in units: for unit in units:
+20 -6
View File
@@ -324,8 +324,10 @@ class ExtendedPackageSearch:
l.print_debug("Request completed.") l.print_debug("Request completed.")
except (requests.RequestException, KeyError) as e: except (requests.RequestException, KeyError) as e:
l.print_error(f"{e}")
raise err.UserFacingError( 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]: def get_package_info(self, package: str) -> typing.Optional[PackageInfo]:
""" """
@@ -379,8 +381,10 @@ class ExtendedPackageSearch:
return info return info
except (requests.RequestException, KeyError) as e: except (requests.RequestException, KeyError) as e:
l.print_error(f"{e}")
raise err.UserFacingError( 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( def find_provider(
self, stripped_dependency: str) -> typing.Optional[PackageInfo]: self, stripped_dependency: str) -> typing.Optional[PackageInfo]:
@@ -452,8 +456,10 @@ class ExtendedPackageSearch:
return self._choose_provider(stripped_dependency, results, "AUR") return self._choose_provider(stripped_dependency, results, "AUR")
except (requests.RequestException, KeyError) as e: except (requests.RequestException, KeyError) as e:
l.print_error(f"{e}")
raise err.UserFacingError( 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], def _choose_provider(self, dep: str, possible_providers: list[str],
where: str) -> typing.Optional[PackageInfo]: where: str) -> typing.Optional[PackageInfo]:
@@ -569,7 +575,9 @@ class ForeignPackageManager:
info = self._search.get_package_info(pkg) info = self._search.get_package_info(pkg)
if info is None: 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, if self.should_upgrade_package(pkg, ver, info.version,
upgrade_devel): upgrade_devel):
@@ -650,6 +658,7 @@ class ForeignPackageManager:
builder.build_packages(pkgbase, packages, force) builder.build_packages(pkgbase, packages, force)
except (subprocess.CalledProcessError, OSError) as e: except (subprocess.CalledProcessError, OSError) as e:
l.print_error(f"{e}")
raise err.UserFacingError("Failed to build packages.") from e raise err.UserFacingError("Failed to build packages.") from e
packages_to_install = list(resolved_dependencies.foreign_pkgs) packages_to_install = list(resolved_dependencies.foreign_pkgs)
@@ -792,7 +801,9 @@ class ForeignPackageManager:
) )
return should_upgrade return should_upgrade
except (ValueError, subprocess.CalledProcessError) as error: 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: class PackageBuilder:
@@ -1058,7 +1069,7 @@ class PackageBuilder:
if len(matches) != 1: if len(matches) != 1:
raise err.UserFacingError( 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] return matches[0]
@@ -1100,6 +1111,9 @@ class PackageBuilder:
raise err.UserFacingError("Building aborted.") raise err.UserFacingError("Building aborted.")
except subprocess.CalledProcessError as error: except subprocess.CalledProcessError as error:
if conf.suppress_command_output:
l.print_error("Output:")
l.print_continuation(error.output)
raise err.UserFacingError( raise err.UserFacingError(
f"Failed to clone and review PKGBUILD from {git_url}" f"Failed to clone and review PKGBUILD from {git_url}"
) from error ) from error