Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor exceptions #1093

Merged
merged 3 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions src/reuse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,11 +182,3 @@ def __bool__(self) -> bool:

def __or__(self, value: "ReuseInfo") -> "ReuseInfo":
return self.union(value)


class ReuseException(Exception):
"""Base exception."""


class IdentifierNotFound(ReuseException):
"""Could not find SPDX identifier for license file."""
6 changes: 3 additions & 3 deletions src/reuse/_annotate.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@
)
from .comment import (
NAME_STYLE_MAP,
CommentCreateError,
CommentStyle,
EmptyCommentStyle,
get_comment_style,
)
from .header import MissingReuseInfo, add_new_header, find_and_replace_header
from .exceptions import CommentCreateError, MissingReuseInfoError
from .header import add_new_header, find_and_replace_header
from .i18n import _
from .project import Project
from .types import StrPath
Expand Down Expand Up @@ -152,7 +152,7 @@ def add_header_to_file(
)
out.write("\n")
result = 1
except MissingReuseInfo:
except MissingReuseInfoError:
out.write(
_(
"Error: Generated comment header for '{path}' is missing"
Expand Down
6 changes: 3 additions & 3 deletions src/reuse/cli/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
from license_expression import ExpressionError

from .._util import _LICENSING
from ..global_licensing import GlobalLicensingParseError
from ..exceptions import GlobalLicensingConflictError, GlobalLicensingParseError
from ..i18n import _
from ..project import GlobalLicensingConflict, Project
from ..project import Project
from ..vcs import find_root


Expand Down Expand Up @@ -60,7 +60,7 @@ def project(self) -> Project:
).format(path=error.source, message=str(error))
) from error

except (GlobalLicensingConflict, OSError) as error:
except (GlobalLicensingConflictError, OSError) as error:
raise click.UsageError(str(error)) from error

self._project = project
Expand Down
9 changes: 1 addition & 8 deletions src/reuse/comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,12 @@
from textwrap import dedent
from typing import NamedTuple, Optional, Type, cast

from .exceptions import CommentCreateError, CommentParseError
from .types import StrPath

_LOGGER = logging.getLogger(__name__)


class CommentParseError(Exception):
"""An error occurred during the parsing of a comment."""


class CommentCreateError(Exception):
"""An error occurred during the creation of a comment."""


class MultiLineSegments(NamedTuple):
"""Components that make up a multi-line comment style, e.g. '/*', '*', and
'*/'.
Expand Down
61 changes: 61 additions & 0 deletions src/reuse/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# SPDX-FileCopyrightText: 2017 Free Software Foundation Europe e.V. <https://fsfe.org>
#
# SPDX-License-Identifier: GPL-3.0-or-later

"""All exceptions owned by :mod:`reuse`. These exceptions all inherit
:class:`ReuseError`.
"""

from typing import Any, Optional


class ReuseError(Exception):
"""Base exception."""


class SpdxIdentifierNotFoundError(ReuseError):
"""Could not find SPDX identifier for license file."""


class GlobalLicensingParseError(ReuseError):
"""An exception representing any kind of error that occurs when trying to
parse a :class:`reuse.global_licensing.GlobalLicensing` file.
"""

def __init__(self, *args: Any, source: Optional[str] = None):
super().__init__(*args)
self.source = source


class GlobalLicensingParseTypeError(GlobalLicensingParseError, TypeError):
"""An exception representing a type error while trying to parse a
:class:`reuse.global_licensing.GlobalLicensing` file.
"""


class GlobalLicensingParseValueError(GlobalLicensingParseError, ValueError):
"""An exception representing a value error while trying to parse a
:class:`reuse.global_licensing.GlobalLicensing` file.
"""


class GlobalLicensingConflictError(ReuseError):
"""There are two global licensing files in the project that are not
compatible.
"""


class MissingReuseInfoError(ReuseError):
"""Some REUSE information is missing from the result."""


class CommentError(ReuseError):
"""An error occurred during an interaction with a comment."""


class CommentCreateError(Exception):
"""An error occurred during the creation of a comment."""


class CommentParseError(Exception):
"""An error occurred during the parsing of a comment."""
29 changes: 6 additions & 23 deletions src/reuse/global_licensing.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,14 @@
from debian.copyright import Error as DebianError
from license_expression import ExpressionError

from . import ReuseException, ReuseInfo, SourceType
from . import ReuseInfo, SourceType
from ._util import _LICENSING
from .covered_files import iter_files
from .exceptions import (
GlobalLicensingParseError,
GlobalLicensingParseTypeError,
GlobalLicensingParseValueError,
)
from .i18n import _
from .types import StrPath
from .vcs import VCSStrategy
Expand Down Expand Up @@ -72,28 +77,6 @@ class PrecedenceType(Enum):
OVERRIDE = "override"


class GlobalLicensingParseError(ReuseException):
"""An exception representing any kind of error that occurs when trying to
parse a :class:`GlobalLicensing` file.
"""

def __init__(self, *args: Any, source: Optional[str] = None):
super().__init__(*args)
self.source = source


class GlobalLicensingParseTypeError(GlobalLicensingParseError, TypeError):
"""An exception representing a type error while trying to parse a
:class:`GlobalLicensing` file.
"""


class GlobalLicensingParseValueError(GlobalLicensingParseError, ValueError):
"""An exception representing a value error while trying to parse a
:class:`GlobalLicensing` file.
"""


@attrs.define
class _CollectionOfValidator:
collection_type: Type[Collection] = attrs.field()
Expand Down
28 changes: 13 additions & 15 deletions src/reuse/header.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,11 @@
extract_reuse_info,
merge_copyright_lines,
)
from .comment import (
from .comment import CommentStyle, EmptyCommentStyle, PythonCommentStyle
from .exceptions import (
CommentCreateError,
CommentParseError,
CommentStyle,
EmptyCommentStyle,
PythonCommentStyle,
MissingReuseInfoError,
)
from .i18n import _

Expand All @@ -53,10 +52,6 @@ class _TextSections(NamedTuple):
after: str


class MissingReuseInfo(Exception):
"""Some REUSE information is missing from the result."""


def _create_new_header(
reuse_info: ReuseInfo,
template: Optional[Template] = None,
Expand All @@ -68,7 +63,8 @@ def _create_new_header(

Raises:
CommentCreateError: if a comment could not be created.
MissingReuseInfo: if the generated comment is missing SPDX information.
MissingReuseInfoError: if the generated comment is missing SPDX
information.
"""
if template is None:
template = DEFAULT_TEMPLATE
Expand Down Expand Up @@ -101,7 +97,7 @@ def _create_new_header(
)
)
_LOGGER.debug(result)
raise MissingReuseInfo()
raise MissingReuseInfoError()

return result

Expand All @@ -125,7 +121,8 @@ def create_header(

Raises:
CommentCreateError: if a comment could not be created.
MissingReuseInfo: if the generated comment is missing SPDX information.
MissingReuseInfoError: if the generated comment is missing SPDX
information.
"""
if template is None:
template = DEFAULT_TEMPLATE
Expand Down Expand Up @@ -186,7 +183,7 @@ def _find_first_spdx_comment(
preceding the comment, the comment itself, and everything following it.

Raises:
MissingReuseInfo: if no REUSE info can be found in any comment
MissingReuseInfoError: if no REUSE info can be found in any comment.
"""
if style is None:
style = PythonCommentStyle
Expand All @@ -203,7 +200,7 @@ def _find_first_spdx_comment(
text[:index], comment + "\n", text[index + len(comment) + 1 :]
)

raise MissingReuseInfo()
raise MissingReuseInfoError()


def _extract_shebang(prefix: str, text: str) -> tuple[str, str]:
Expand Down Expand Up @@ -248,14 +245,15 @@ def find_and_replace_header(

Raises:
CommentCreateError: if a comment could not be created.
MissingReuseInfo: if the generated comment is missing SPDX information.
MissingReuseInfoError: if the generated comment is missing SPDX
information.
"""
if style is None:
style = PythonCommentStyle

try:
before, header, after = _find_first_spdx_comment(text, style=style)
except MissingReuseInfo:
except MissingReuseInfoError:
before, header, after = "", "", text

# Workaround. EmptyCommentStyle should always be completely replaced.
Expand Down
28 changes: 13 additions & 15 deletions src/reuse/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import attrs
from binaryornot.check import is_binary

from . import IdentifierNotFound, ReuseInfo
from . import ReuseInfo
from ._licenses import EXCEPTION_MAP, LICENSE_MAP
from ._util import (
_LICENSEREF_PATTERN,
Expand All @@ -30,6 +30,10 @@
reuse_info_of_file,
)
from .covered_files import iter_files
from .exceptions import (
GlobalLicensingConflictError,
SpdxIdentifierNotFoundError,
)
from .global_licensing import (
GlobalLicensing,
NestedReuseTOML,
Expand All @@ -44,12 +48,6 @@
_LOGGER = logging.getLogger(__name__)


class GlobalLicensingConflict(Exception):
"""There are two global licensing files in the project that are not
compatible.
"""


class GlobalLicensingFound(NamedTuple):
path: Path
cls: Type[GlobalLicensing]
Expand Down Expand Up @@ -111,8 +109,8 @@ def from_directory(
decoded.
GlobalLicensingParseError: if the global licensing config file could
not be parsed.
GlobalLicensingConflict: if more than one global licensing config
file is present.
GlobalLicensingConflictError: if more than one global licensing
config file is present.
"""
root = Path(root)
if not root.exists():
Expand Down Expand Up @@ -323,8 +321,8 @@ def find_global_licensing(
:class:`GlobalLicensing`.

Raises:
GlobalLicensingConflict: if more than one global licensing config
file is present.
GlobalLicensingConflictError: if more than one global licensing
config file is present.
"""
candidates: list[GlobalLicensingFound] = []
dep5_path = root / ".reuse/dep5"
Expand Down Expand Up @@ -352,7 +350,7 @@ def find_global_licensing(
]
if reuse_toml_candidates:
if candidates:
raise GlobalLicensingConflict(
raise GlobalLicensingConflictError(
_(
"Found both '{new_path}' and '{old_path}'. You"
" cannot keep both files simultaneously; they are"
Expand Down Expand Up @@ -384,13 +382,13 @@ def _identifier_of_license(self, path: Path) -> str:
License Identifier.
"""
if not path.suffix:
raise IdentifierNotFound(f"{path} has no file extension")
raise SpdxIdentifierNotFoundError(f"{path} has no file extension")
if path.stem in self.license_map:
return path.stem
if _LICENSEREF_PATTERN.match(path.stem):
return path.stem

raise IdentifierNotFound(
raise SpdxIdentifierNotFoundError(
f"Could not find SPDX License Identifier for {path}"
)

Expand Down Expand Up @@ -418,7 +416,7 @@ def _find_licenses(self) -> dict[str, Path]:

try:
identifier = self._identifier_of_license(path)
except IdentifierNotFound:
except SpdxIdentifierNotFoundError:
if path.name in self.license_map:
_LOGGER.info(
_("{path} does not have a file extension").format(
Expand Down
3 changes: 1 addition & 2 deletions tests/test_comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@
import pytest

from reuse.comment import (
CommentCreateError,
CommentParseError,
CommentStyle,
CppCommentStyle,
HtmlCommentStyle,
LispCommentStyle,
PythonCommentStyle,
_all_style_classes,
)
from reuse.exceptions import CommentCreateError, CommentParseError


@pytest.fixture(
Expand Down
6 changes: 4 additions & 2 deletions tests/test_global_licensing.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@

from reuse import ReuseInfo, SourceType
from reuse._util import _LICENSING
from reuse.global_licensing import (
AnnotationsItem,
from reuse.exceptions import (
GlobalLicensingParseError,
GlobalLicensingParseTypeError,
GlobalLicensingParseValueError,
)
from reuse.global_licensing import (
AnnotationsItem,
NestedReuseTOML,
PrecedenceType,
ReuseDep5,
Expand Down
Loading
Loading