diff --git a/docs/devguide.rst b/docs/devguide.rst index d254cbe..ef27a0f 100644 --- a/docs/devguide.rst +++ b/docs/devguide.rst @@ -101,6 +101,12 @@ Class reference documentation .. automodule:: kas.includehandler :members: +``kas.kasusererror`` Module +............................. + +.. automodule:: kas.kasusererror + :members: + ``kas.plugins`` Module ...................... diff --git a/kas/includehandler.py b/kas/includehandler.py index 8d1d274..a0920b6 100644 --- a/kas/includehandler.py +++ b/kas/includehandler.py @@ -34,6 +34,7 @@ import logging from jsonschema.validators import Draft4Validator +from .kasusererror import KasUserError from . import __file_version__, __compatible_file_version__ from . import CONFIGSCHEMA @@ -41,7 +42,7 @@ __license__ = 'MIT' __copyright__ = 'Copyright (c) Siemens AG, 2017-2021' -class LoadConfigException(Exception): +class LoadConfigException(KasUserError): """ Class for exceptions that appear while loading a configuration file. """ @@ -101,7 +102,7 @@ def load_config(filename): return config -class IncludeException(Exception): +class IncludeException(KasUserError): """ Class for exceptions that appear in the include mechanism. """ diff --git a/kas/kas.py b/kas/kas.py index 4a07e5e..4d7edbf 100644 --- a/kas/kas.py +++ b/kas/kas.py @@ -21,7 +21,9 @@ # SOFTWARE. """ This module is the main entry point for kas, setup tool for bitbake based - projects + projects. In case of user errors (e.g. invalid configuration, repo fetch + failure) KAS exits with error code 2, while exiting with 1 for internal + errors. For details on error handling, see :mod:`kas.kasusererror`. """ import argparse @@ -32,6 +34,7 @@ import logging import signal import sys import os +from .kasusererror import KasUserError try: import colorlog @@ -173,6 +176,9 @@ def main(): try: kas(sys.argv[1:]) + except KasUserError as err: + logging.error('%s', err) + sys.exit(2) except Exception as err: logging.error('%s', err) traceback.print_exc() diff --git a/kas/kasusererror.py b/kas/kasusererror.py new file mode 100644 index 0000000..c9518d4 --- /dev/null +++ b/kas/kasusererror.py @@ -0,0 +1,45 @@ +# kas - setup tool for bitbake based projects +# +# Copyright (c) Siemens AG, 2017-2023 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" + This module provides a common base class for all exceptions + which are related to user or configuration errors. These exceptions + should be caught and reported to the user using a meaningful message + instead of a stacktrace. + + When handling errors in KAS, never return directly using `sys.exit`, + but instead throw an exception derived from :class:`KasUserError` (for user + errors), or one derived from `Exception` for internal errors. These + are then handled centrally, mapped to correct return codes and pretty + printed. +""" + + +__license__ = 'MIT' +__copyright__ = 'Copyright (c) Siemens AG, 2023' + + +class KasUserError(Exception): + """ + User or input error. Derive all user error exceptions from this class. + """ + pass diff --git a/kas/repos.py b/kas/repos.py index 410e290..7b4af5d 100644 --- a/kas/repos.py +++ b/kas/repos.py @@ -31,11 +31,26 @@ from urllib.parse import urlparse from tempfile import TemporaryDirectory from .context import get_context from .libkas import run_cmd_async, run_cmd +from .kasusererror import KasUserError __license__ = 'MIT' __copyright__ = 'Copyright (c) Siemens AG, 2017-2018' +class UnsupportedRepoTypeError(KasUserError, NotImplementedError): + """ + Exception for unsupported / not implemented repository types + """ + pass + + +class PatchFileNotFound(KasUserError, FileNotFoundError): + """ + The requested patch file was not found + """ + pass + + class Repo: """ Represents a repository in the kas configuration. @@ -151,7 +166,7 @@ class Repo: if typ == 'hg': return MercurialRepo(name, url, path, refspec, layers, patches, disable_operations) - raise NotImplementedError('Repo type "%s" not supported.' % typ) + raise UnsupportedRepoTypeError('Repo type "%s" not supported.' % typ) @staticmethod def get_root_path(path, fallback=True): @@ -324,7 +339,7 @@ class RepoImpl(Repo): if os.path.isfile(p): my_patches.append((p, patch['id'])) else: - raise FileNotFoundError(p) + raise PatchFileNotFound(p) else: logging.error('Could not find patch. ' '(patch path: %s, repo: %s, patch entry: %s)',