# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright 2017-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/>.
"""Source handler error definitions."""
from typing import List, Sequence
from craft_parts import errors
from craft_parts.utils import formatting_utils
[docs]class SourceError(errors.PartsError):
"""Base class for source handler errors."""
[docs]class InvalidSourceType(SourceError):
"""Failed to determine a source type.
:param source: The source defined for the part.
"""
def __init__(self, source: str):
self.source = source
brief = f"Failed to pull source: unable to determine source type of {source!r}."
super().__init__(brief=brief)
[docs]class InvalidSourceOption(SourceError):
"""A source option is not allowed for the given source type.
:param source_type: The part's source type.
:param option: The invalid source option.
"""
def __init__(self, *, source_type: str, option: str):
self.source_type = source_type
self.option = option
brief = (
f"Failed to pull source: {option!r} cannot be used "
f"with a {source_type} source."
)
resolution = "Make sure sources are correctly specified."
super().__init__(brief=brief, resolution=resolution)
# TODO: Merge this with InvalidSourceOption above
[docs]class InvalidSourceOptions(SourceError):
"""A source option is not allowed for the given source type.
:param source_type: The part's source type.
:param options: The invalid source options.
"""
def __init__(self, *, source_type: str, options: List[str]):
self.source_type = source_type
self.options = options
humanized_options = formatting_utils.humanize_list(options, "and")
brief = (
f"Failed to pull source: {humanized_options} cannot be used "
f"with a {source_type} source."
)
resolution = "Make sure sources are correctly specified."
super().__init__(brief=brief, resolution=resolution)
[docs]class IncompatibleSourceOptions(SourceError):
"""Source specified options that cannot be used at the same time.
:param source_type: The part's source type.
:param options: The list of incompatible source options.
"""
def __init__(self, source_type: str, options: List[str]):
self.source_type = source_type
self.options = options
humanized_options = formatting_utils.humanize_list(options, "and")
brief = (
f"Failed to pull source: cannot specify both {humanized_options} "
f"for a {source_type} source."
)
resolution = "Make sure sources are correctly specified."
super().__init__(brief=brief, resolution=resolution)
[docs]class ChecksumMismatch(SourceError):
"""A checksum doesn't match the expected value.
:param expected: The expected checksum.
:param obtained: The actual checksum.
"""
def __init__(self, *, expected: str, obtained: str):
self.expected = expected
self.obtained = obtained
brief = f"Expected digest {expected}, obtained {obtained}."
super().__init__(brief=brief)
[docs]class SourceUpdateUnsupported(SourceError):
"""The source handler doesn't support updating.
:param name: The source type.
"""
def __init__(self, name: str):
self.name = name
brief = f"Failed to update source: {name!r} sources don't support updating."
super().__init__(brief=brief)
[docs]class NetworkRequestError(SourceError):
"""A network request operation failed.
:param message: The error message.
"""
def __init__(self, message: str):
self.message = message
brief = f"Network request error: {message}."
resolution = "Check the network and try again."
super().__init__(brief=brief, resolution=resolution)
[docs]class SourceNotFound(SourceError):
"""Failed to retrieve a source.
:param source: The source defined for the part.
"""
def __init__(self, source: str):
self.source = source
brief = f"Failed to pull source: {source!r} not found."
resolution = "Make sure the source path is correct and accessible."
super().__init__(brief=brief, resolution=resolution)
[docs]class InvalidSnapPackage(SourceError):
"""A snap package is invalid.
:param snap_file: The snap file name.
"""
def __init__(self, snap_file: str):
self.snap_file = snap_file
brief = f"Snap {snap_file!r} does not contain valid data."
resolution = "Ensure the source lists a proper snap file."
super().__init__(brief=brief, resolution=resolution)
[docs]class InvalidRpmPackage(SourceError):
"""An rpm package is invalid.
:param rpm_file: The filename.
"""
def __init__(self, rpm_file: str):
self.rpm_file = rpm_file
brief = f"RPM file {rpm_file!r} could not be extracted."
resolution = "Ensure the source lists a valid rpm file."
super().__init__(brief=brief, resolution=resolution)
[docs]class PullError(SourceError):
"""Failed pulling source.
:param command: The command used to pull the source.
:param exit_code: The command exit code.
"""
def __init__(self, *, command: Sequence, exit_code: int):
self.command = command
self.exit_code = exit_code
brief = (
f"Failed to pull source: command {command!r} exited with code {exit_code}."
)
resolution = "Make sure sources are correctly specified."
super().__init__(brief=brief, resolution=resolution)
[docs]class VCSError(SourceError):
"""A version control system command failed."""
def __init__(self, message: str):
self.message = message
brief = message
super().__init__(brief=brief)