# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright 2015-2021 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Exceptions raised by the packages handling subsystem."""
from collections.abc import Sequence
from craft_parts.errors import PartsError
from craft_parts.utils import formatting_utils
[docs]
class PackagesError(PartsError):
"""Base class for package handler errors."""
[docs]
class PackageBackendNotSupported(PartsError):
"""Requested package resolved not supported on this host."""
def __init__(self, backend: str) -> None:
self.backend = backend
super().__init__(
brief=f"Package backend {backend!r} is not supported on this environment.",
)
[docs]
class PackageNotFound(PackagesError):
"""Requested package doesn't exist in the remote repository.
:param package_name: The name of the missing package.
"""
def __init__(self, package_name: str) -> None:
self.package_name = package_name
brief = f"Package not found: {package_name}."
super().__init__(brief=brief)
[docs]
class PackagesNotFound(PackagesError):
"""Requested package doesn't exist in the remote repository.
:param package_name: The names of the missing packages.
"""
def __init__(self, packages: Sequence[str]) -> None:
self.packages = packages
missing_pkgs = formatting_utils.humanize_list(packages, "and")
brief = f"Failed to find installation candidate for packages: {missing_pkgs}."
resolution = (
"Make sure the repository configuration and package names are correct."
)
super().__init__(brief=brief, resolution=resolution)
[docs]
class PackageFetchError(PackagesError):
"""Failed to fetch package from remote repository.
:param message: The error message.
"""
def __init__(self, message: str) -> None:
self.message = message
brief = f"Failed to fetch package: {message}."
super().__init__(brief=brief)
[docs]
class PackageListRefreshError(PackagesError):
"""Failed to refresh the list of available packages.
:param message: The error message.
"""
def __init__(self, message: str) -> None:
self.message = message
brief = f"Failed to refresh package list: {message}."
super().__init__(brief=brief)
[docs]
class PackageBroken(PackagesError):
"""Package has unmet dependencies.
:param package_name: The name of the package with unmet dependencies.
:param deps: The list of unmet dependencies.
"""
def __init__(self, package_name: str, *, deps: Sequence[str]) -> None:
self.package_name = package_name
self.deps = deps
brief = f"Package {package_name!r} has unmet dependencies: {', '.join(deps)}."
super().__init__(brief=brief)
[docs]
class FileProviderNotFound(PackagesError):
"""A file is not provided by any package.
:param file_path: The file path.
"""
def __init__(self, *, file_path: str) -> None:
self.file_path = file_path
brief = f"{file_path} is not provided by any package."
super().__init__(brief=brief)
[docs]
class BuildPackageNotFound(PackagesError):
"""A package listed in 'build-packages' was not found.
:param package: The name of the missing package.
"""
def __init__(self, package: str) -> None:
self.package = package
brief = f"Cannot find package listed in 'build-packages': {package}"
super().__init__(brief=brief)
[docs]
class BuildPackagesNotInstalled(PackagesError):
"""Could not install all requested build packages.
:param packages: The packages to install.
"""
def __init__(self, *, packages: Sequence[str]) -> None:
self.packages = packages
brief = f"Cannot install all requested build packages: {', '.join(packages)}"
super().__init__(brief=brief)
[docs]
class PackagesDownloadError(PackagesError):
"""Failed to download packages from remote repository.
:param packages: The packages to download.
"""
def __init__(self, *, packages: Sequence[str]) -> None:
self.packages = packages
brief = f"Failed to download all requested packages: {', '.join(packages)}"
resolution = (
"Make sure the network configuration and package names are correct."
)
super().__init__(brief=brief, resolution=resolution)
[docs]
class UnpackError(PackagesError):
"""Error unpacking stage package.
:param package: The package that failed to unpack.
"""
def __init__(self, package: str) -> None:
self.package = package
brief = f"Error unpacking {package!r}"
super().__init__(brief=brief)
[docs]
class SnapUnavailable(PackagesError):
"""Failed to install or refresh a snap.
:param snap_name: The snap name.
:param snap_channel: The snap channel.
"""
def __init__(self, *, snap_name: str, snap_channel: str) -> None:
self.snap_name = snap_name
self.snap_channel = snap_channel
brief = f"Failed to install or refresh snap {snap_name!r}."
details = (
f"{snap_name!r} does not exist or is not available on channel "
f"{snap_channel!r}."
)
resolution = (
f"Use `snap info {snap_name}` to get a list of channels the snap "
"is available on."
)
super().__init__(brief=brief, details=details, resolution=resolution)
[docs]
class SnapInstallError(PackagesError):
"""Failed to install a snap.
:param snap_name: The snap name.
:param snap_channel: The snap channel.
"""
def __init__(self, *, snap_name: str, snap_channel: str) -> None:
self.snap_name = snap_name
self.snap_channel = snap_channel
brief = f"Error installing snap {snap_name!r} from channel {snap_channel!r}."
super().__init__(brief=brief)
[docs]
class SnapDownloadError(PackagesError):
"""Failed to download a snap.
:param snap_name: The snap name.
:param snap_channel: The snap channel.
"""
def __init__(self, *, snap_name: str, snap_channel: str) -> None:
self.snap_name = snap_name
self.snap_channel = snap_channel
brief = f"Error downloading snap {snap_name!r} from channel {snap_channel!r}."
super().__init__(brief=brief)
[docs]
class SnapRefreshError(PackagesError):
"""Failed to refresh a snap.
:param snap_name: The snap name.
:param snap_channel: The snap channel.
"""
def __init__(self, *, snap_name: str, snap_channel: str) -> None:
self.snap_name = snap_name
self.snap_channel = snap_channel
brief = f"Error refreshing snap {snap_name!r} to channel {snap_channel!r}."
super().__init__(brief=brief)
[docs]
class SnapGetAssertionError(PackagesError):
"""Failed to retrieve snap assertion.
:param assertion_params: The snap assertion parameters.
"""
def __init__(self, *, assertion_params: Sequence[str]) -> None:
self.assertion_params = assertion_params
brief = f"Error retrieving assertion with parameters {assertion_params!r}"
resolution = "Verify the assertion exists and try again."
super().__init__(brief=brief, resolution=resolution)
[docs]
class SnapdConnectionError(PackagesError):
"""Failed to connect to snapd.
:param snap_name: The snap name.
:param url: The failed connection URL.
"""
def __init__(self, *, snap_name: str, url: str) -> None:
self.snap_name = snap_name
self.url = url
brief = (
f"Failed to get information for snap {snap_name!r}: could not connect "
f"to {url!r}."
)
super().__init__(brief=brief)
[docs]
class ChiselError(PackagesError):
"""A "chisel"-related command failed."""
def __init__(self, *, slices: list[str], output: str) -> None:
"""Create a ChiselError from a list of slices and the command output.
:param slices: The Chisel slices that were requested.
:param output: The output of the Chisel command.
"""
brief = f"Failed to cut requested chisel slices: {', '.join(slices)}"
details = None
for line in output.splitlines():
if line.startswith(":: error:"):
details = line
break
super().__init__(brief=brief, details=details)