Commit e630752e authored by Jakub Ružička's avatar Jakub Ružička
Browse files

Merge branch 'exception' into 'master'

refactor exceptions

Closes #35

See merge request !32
parents ade04df5 e864be4c
Pipeline #78470 passed with stages
in 2 minutes and 51 seconds
......@@ -28,7 +28,7 @@ from docopt import docopt
from apkg import __version__
from apkg import commands # noqa: F401 (dynamic import)
from apkg import exception
from apkg import ex
from apkg.log import getLogger, T
import apkg.log as _log
......@@ -73,13 +73,13 @@ def apkg(*cargs):
if args['<command>']:
run_command(cargs)
code = 0
except exception.CommandFailed as ex:
except ex.CommandFailed as e:
# this was logged already
code = ex.exit_code
except exception.ApkgException as ex:
code = e.exit_code
except ex.ApkgException as e:
print()
print(T.bold_yellow(str(ex)))
code = ex.exit_code
print(T.bold_yellow(str(e)))
code = e.exit_code
return code
......@@ -94,7 +94,7 @@ def run_command(cargs):
try:
mod = __import__(modname, fromlist=[''])
except ModuleNotFoundError:
raise exception.InvalidApkgCommand(command=command)
raise ex.InvalidApkgCommand(command=command)
return mod.run_command(cargs)
......
"""
apkg exceptions
when apkg CLI run results in exception being raised
its exit_code is returned as process exit code
"""
class ApkgException(Exception):
msg_fmt = "An unknown error occurred"
exit_code = 1
......@@ -16,6 +24,7 @@ class ApkgException(Exception):
super().__init__(msg)
# 10-29: Invalid
class InvalidApkgCommand(ApkgException):
msg_fmt = (
"Invalid apkg command: %(command)s\n\n"
......@@ -26,74 +35,78 @@ class InvalidApkgCommand(ApkgException):
class InvalidInput(ApkgException):
msg_fmt = "Invalid input: %(fail)s"
exit_code = 12
exit_code = 11
class InvalidType(ApkgException):
msg_fmt = "Invalid type: $(var) must be %(desired)s but is %(type)s"
exit_code = 14
exit_code = 12
class InvalidChoice(ApkgException):
msg_fmt = "Invalid choice: %(var)s must be one of: %(opts)s (is: %(val)s)"
exit_code = 16
exit_code = 13
class InvalidVersion(ApkgException):
msg_fmt = "Invalid version: %(ver)s"
exit_code = 14
class InvalidFormat(ApkgException):
msg_fmt = "Invalid format: %(fmt)s"
exit_code = 17
exit_code = 16
class InvalidArchiveFormat(InvalidFormat):
msg_fmt = "Invalid archive format: %(fmt)s"
exit_code = 18
exit_code = 17
class InvalidSourcePackageFormat(InvalidFormat):
msg_fmt = "Invalid source package format: %(fmt)s"
exit_code = 19
exit_code = 18
# 30-39: Missing and NotFound
class MissingRequiredArgument(ApkgException):
msg_fmt = "Missing required argument: %(arg)s"
exit_code = 20
exit_code = 30
class MissingRequiredConfigOption(ApkgException):
msg_fmt = "Missing required config option: %(opt)s"
exit_code = 22
exit_code = 31
class MissingPackagingTemplate(ApkgException):
msg_fmt = "Missing package template: %(temp)s"
exit_code = 24
exit_code = 32
class ParsingFailed(ApkgException):
msg_fmt = "Unable to parse: %(fail)s"
exit_code = 30
class InvalidVersion(ApkgException):
msg_fmt = "Invalid version: %(ver)s"
exit_code = 40
class ArchiveNotFound(ApkgException):
msg_fmt = "%(type)s archive not found: %(ar)s"
exit_code = 36
class DistroNotSupported(ApkgException):
msg_fmt = "Distro not supported: %(distro)s"
exit_code = 44
class SourcePackageNotFound(ApkgException):
msg_fmt = "%(type)s source package not found: %(srcpkg)s"
exit_code = 37
class HTTPRequestFailed(ApkgException):
msg_fmt = "HTTP request failed with code %(code)s: %(request)s"
exit_code = 54
# 40-49: Parsing
class ParsingFailed(ApkgException):
msg_fmt = "Unable to parse: %(fail)s"
exit_code = 42
# 50-59: remote failures
class FileDownloadFailed(ApkgException):
msg_fmt = "Failed to download file with code %(code)s:\n\n%(url)s"
exit_code = 56
exit_code = 52
# 60-69: Command
class CommandNotFound(ApkgException):
msg_fmt = "Command not found: %(cmd)s"
exit_code = 60
......@@ -106,19 +119,16 @@ class CommandFailed(ApkgException):
class UnexpectedCommandOutput(ApkgException):
msg_fmt = "Unexpected command output: %(out)s"
exit_code = 66
class ArchiveNotFound(ApkgException):
msg_fmt = "%(type)s archive not found: %(ar)s"
exit_code = 80
exit_code = 64
class SourcePackageNotFound(ApkgException):
msg_fmt = "%(type)s source package not found: %(srcpkg)s"
exit_code = 82
# 70-79: Distro
class DistroNotSupported(ApkgException):
msg_fmt = "Distro not supported: %(distro)s"
exit_code = 70
# 80-89: auto-magic
class UnableToDetectUpstreamVersion(ApkgException):
msg_fmt = (
"Unable to detect upstream version.\n\n"
......@@ -127,4 +137,4 @@ class UnableToDetectUpstreamVersion(ApkgException):
"(requires htmllistparse module)\n"
"2) set upstream.version_script to custom script\n"
"3) manually supply version using -v/--version option")
exit_code = 90
exit_code = 84
......@@ -4,7 +4,7 @@ apkg lib for handling source archives
from pathlib import Path
import requests
from apkg import exception
from apkg import ex
from apkg.lib import common
from apkg.log import getLogger
from apkg.parse import split_archive_fn, parse_version
......@@ -44,7 +44,7 @@ def make_archive(
"archive and prints its path to stdout.\n\n"
"Please update project config with required information:\n\n"
"%s" % proj.config_path)
raise exception.MissingRequiredConfigOption(msg=msg)
raise ex.MissingRequiredConfigOption(msg=msg)
log.info("running make_archive_script: %s", script)
out = run(script)
......@@ -55,7 +55,7 @@ def make_archive(
msg = ("make_archive_script finished successfully but the archive\n"
"(indicated by last script stdout line) doesn't exist:\n\n"
"%s" % in_archive_path)
raise exception.UnexpectedCommandOutput(msg=msg)
raise ex.UnexpectedCommandOutput(msg=msg)
log.info("archive created: %s", in_archive_path)
if result_dir:
......@@ -94,7 +94,7 @@ def get_archive(
if not version:
version = proj.upstream_version
if not version:
raise exception.UnableToDetectUpstreamVersion()
raise ex.UnableToDetectUpstreamVersion()
archive_url = proj.upstream_archive_url(version)
use_cache = proj.cache.enabled(use_cache)
......@@ -110,11 +110,11 @@ def get_archive(
log.info('downloading archive: %s', archive_url)
r = requests.get(archive_url, allow_redirects=True)
if r.status_code != 200:
raise exception.FileDownloadFailed(code=r.status_code, url=archive_url)
raise ex.FileDownloadFailed(code=r.status_code, url=archive_url)
content_type = r.headers['content-type']
if not content_type.startswith('application/'):
msg = 'Failed to download archive - invalid content-type "%s":\n\n%s'
raise exception.FileDownloadFailed(
raise ex.FileDownloadFailed(
msg=msg % (content_type, archive_url))
if result_dir:
......@@ -135,7 +135,7 @@ def get_archive(
log.info('downloading signature: %s', signature_url)
r = requests.get(signature_url, allow_redirects=True)
if not r.ok:
raise exception.FileDownloadFailed(
raise ex.FileDownloadFailed(
code=r.status_code, url=signature_url)
_, _, signature_name = signature_url.rpartition('/')
signature_path = ar_base_path / signature_name
......@@ -165,13 +165,13 @@ def find_archive(archive, upstream=False, project=None):
project = Project()
ars = project.find_archives_by_name(archive, upstream=upstream)
if not ars:
raise exception.ArchiveNotFound(ar=archive, type=ar_type)
raise ex.ArchiveNotFound(ar=archive, type=ar_type)
if len(ars) > 1:
msg = ("multiple matching %s archives found - "
"not sure which one to use:\n" % ar_type)
for ar in ars:
msg += "\n%s" % ar
raise exception.ArchiveNotFound(msg=msg)
raise ex.ArchiveNotFound(msg=msg)
ar_path = Path(ars[0])
log.verbose("found %s archive: %s", ar_type, ar_path)
......@@ -196,7 +196,7 @@ def get_archive_version(archive_path, version=None):
msg = ("archive name doesn't match desired version: %s\n\n"
"desired version: %s\n"
"archive version: %s" % (archive_path.name, version, ver))
raise exception.InvalidVersion(msg=msg)
raise ex.InvalidVersion(msg=msg)
else:
# no version was requested - use archive version
version = ar_ver
......
......@@ -5,7 +5,7 @@ from pathlib import Path
from apkg import adistro
from apkg.cache import file_checksum
from apkg import exception
from apkg import ex
from apkg.lib import srcpkg as _srcpkg
from apkg.lib import common
from apkg.log import getLogger
......@@ -75,8 +75,8 @@ def build_package(
install_build_deps(
srcpkg=srcpkg_path,
distro=distro)
except exception.DistroNotSupported as ex:
log.warning("%s - SKIPPING", ex)
except ex.DistroNotSupported as e:
log.warning("%s - SKIPPING", e)
# fetch pkgstyle (deb, rpm, arch, ...)
template = proj.get_template_for_distro(distro)
......@@ -111,7 +111,7 @@ def build_package(
if not pkgs:
msg = ("package build reported success but there are "
"no packages:\n\n%s" % result_path)
raise exception.UnexpectedCommandOutput(msg=msg)
raise ex.UnexpectedCommandOutput(msg=msg)
log.success("built %s packages in: %s", len(pkgs), result_path)
if use_cache and not upstream:
......@@ -142,13 +142,13 @@ def install_build_deps(
if not hasattr(pkgstyle, 'install_build_deps'):
msg = "build deps installation isn't supported on distro: %s"
raise exception.DistroNotSupported(msg % distro)
raise ex.DistroNotSupported(msg % distro)
if srcpkg:
# use existing source package
srcpkg_path = Path(srcpkg)
if not srcpkg_path.exists():
raise exception.SourcePackageNotFound(
raise ex.SourcePackageNotFound(
srcpkg=srcpkg, type=distro)
log.info("using existing source package: %s", srcpkg_path)
else:
......
from pathlib import Path
import sys
from apkg import ex
from apkg.log import getLogger
from apkg import exception
import apkg.util.shutil35 as shutil
......@@ -61,7 +61,7 @@ def parse_input_files(files, file_lists):
if len([fl for fl in file_lists if fl == '-']) > 1:
fail = "requested to read stdin multiple times"
raise exception.InvalidInput(fail=fail)
raise ex.InvalidInput(fail=fail)
for fl in file_lists:
if fl == '-':
......@@ -76,9 +76,9 @@ def parse_input_files(files, file_lists):
def ensure_input_files(infiles):
if not infiles:
raise exception.InvalidInput(
raise ex.InvalidInput(
fail="no input file(s) specified")
for f in infiles:
if not f.exists():
raise exception.InvalidInput(
raise ex.InvalidInput(
fail="input file not found: %s" % f)
......@@ -4,7 +4,7 @@ apkg lib for handling package installation using native package manager
import sys
from apkg import adistro
from apkg import exception
from apkg import ex
from apkg.log import getLogger
from apkg import pkgstyle
......@@ -34,7 +34,7 @@ def install_packages(
ps = pkgstyle.get_pkgstyle_for_distro(distro)
if not ps:
raise exception.DistroNotSupported(distro=distro)
raise ex.DistroNotSupported(distro=distro)
log.info("target pkgstyle: %s", ps.name)
if distro_pkgs:
......
......@@ -5,7 +5,7 @@ from pathlib import Path
from apkg import adistro
from apkg.cache import file_checksum
from apkg import exception
from apkg import ex
from apkg.lib import ar
from apkg.lib import common
from apkg.log import getLogger
......@@ -90,7 +90,7 @@ def make_srcpkg(
tdir = proj.templates_path
msg = ("missing package template for distro: %s\n\n"
"you can add it into: %s" % (distro, tdir))
raise exception.MissingPackagingTemplate(msg=msg)
raise ex.MissingPackagingTemplate(msg=msg)
ps = template.pkgstyle
log.info("package style: %s", ps.name)
log.info("package template: %s", template.path)
......@@ -148,7 +148,7 @@ def make_srcpkg(
if not Path(p).exists():
msg = ("source package build reported success but result is "
"missing:\n\n%s" % p)
raise exception.UnexpectedCommandOutput(msg=msg)
raise ex.UnexpectedCommandOutput(msg=msg)
log.success("made source package: %s", results[0])
if use_cache:
......
import re
from apkg import exception
from apkg import ex
# subgroups:
......@@ -27,7 +27,7 @@ def split_archive_fn(archive_fn):
return r.groups() + (ext,)
msg = "unable to parse version from archive file name: %s" % archive_fn
raise exception.ParsingFailed(msg=msg)
raise ex.ParsingFailed(msg=msg)
def parse_version(version_str):
......
......@@ -10,7 +10,7 @@ apkg package style for **Arch** linux.
import glob
from pathlib import Path
from apkg import exception
from apkg import ex
from apkg.log import getLogger
from apkg.util.run import cd, run, sudo
import apkg.util.shutil35 as shutil
......@@ -71,7 +71,7 @@ def build_packages(
**kwargs):
srcpkg_path = srcpkg_paths[0]
if srcpkg_path.name != 'PKGBUILD':
raise exception.InvalidSourcePackageFormat(
raise ex.InvalidSourcePackageFormat(
fmt="arch source package format is PKGBUILD but got: %s"
% srcpkg_path.name)
isolated = kwargs.get('isolated')
......
......@@ -13,7 +13,7 @@ import glob
from pathlib import Path
import re
from apkg import exception
from apkg import ex
from apkg.log import getLogger
from apkg import parse
from apkg.util.run import cd, run, sudo
......@@ -47,7 +47,7 @@ def get_template_name(path):
if m:
return m.group(1)
raise exception.ParsingFailed(
raise ex.ParsingFailed(
msg="unable to determine Source from: %s" % control)
......@@ -81,7 +81,7 @@ def build_srcpkg(
# NOTE: if this happens oftern (it shouldn't), consider using
# atool's --save-outdir option above
msg = "archive unpack didn't result in expected dir: %s" % source_path
raise exception.UnexpectedCommandOutput(msg=msg)
raise ex.UnexpectedCommandOutput(msg=msg)
# render template
debian_path = source_path / 'debian'
template.render(debian_path, env)
......@@ -113,7 +113,7 @@ def build_srcpkg(
fns = [fns.pop(i)] + fns
break
else:
raise exception.UnexpectedCommandOutput(
raise ex.UnexpectedCommandOutput(
msg="no *.dsc found after moving built source package")
return list(map(Path, fns))
......@@ -148,7 +148,7 @@ def build_packages(
source_path = Path(glob.glob(source_glob)[0])
except IndexError:
msg = "failed to find unpacked source dir: %s"
raise exception.UnexpectedCommandOutput(msg % source_glob)
raise ex.UnexpectedCommandOutput(msg % source_glob)
log.info("starting direct host build using dpkg-buildpackage")
with cd(source_path):
......
......@@ -14,7 +14,7 @@ from pathlib import Path
import re
import subprocess
from apkg import exception
from apkg import ex
from apkg.log import getLogger
from apkg.util.run import run, sudo
import apkg.util.shutil35 as shutil
......@@ -58,7 +58,7 @@ def get_template_name(path):
if m:
return m.group(1)
raise exception.ParsingFailed(
raise ex.ParsingFailed(
msg="unable to determine Name from: %s" % spec)
......@@ -122,7 +122,7 @@ def build_srcpkg(
shutil.copyfile(src_srpm, dst_srpm)
srcpkgs.append(dst_srpm)
if not srcpkgs:
raise exception.ParsingFailed(
raise ex.ParsingFailed(
msg="unable to parse rpmbuild results")
return srcpkgs
......@@ -168,7 +168,7 @@ def build_packages(
shutil.copyfile(src_pkg, dst_pkg)
pkgs.append(dst_pkg)
if not pkgs:
raise exception.ParsingFailed(
raise ex.ParsingFailed(
msg="unable to parse rpmbuild results")
return pkgs
......
......@@ -12,7 +12,7 @@ except ImportError:
from cached_property import cached_property
from apkg import cache as _cache
from apkg import exception
from apkg import ex
from apkg.log import getLogger
from apkg import pkgtemplate
from apkg.util.git import git
......@@ -228,7 +228,7 @@ class Project:
tdir = self.templates_path
msg = ("missing package template for distro: %s\n\n"
"you can add it into: %s" % (distro, tdir))
raise exception.MissingPackagingTemplate(msg=msg)
raise ex.MissingPackagingTemplate(msg=msg)
return template
def find_archives_by_name(self, name, upstream=False):
......
......@@ -3,7 +3,7 @@ apkg archive (tarball) utils
"""
from pathlib import Path
from apkg import exception
from apkg import ex
from apkg.util.run import run
......@@ -25,5 +25,5 @@ def unpack_archive(archive_path, out_path):
n_root_files = len(root_dirs)
if n_root_files != 1:
fmt = "Expected a single root dir but insteat got %d files in root"
raise exception.InvalidArchiveFormat(fmt=fmt % n_root_files)
raise ex.InvalidArchiveFormat(fmt=fmt % n_root_files)
return out_path / root_dirs.pop()
......@@ -2,7 +2,7 @@
import contextlib
import os
from apkg import exception
from apkg import ex
from apkg.util.run import run
from apkg.util.run import ShellCommand
......@@ -134,7 +134,7 @@ class Git(ShellCommand):
try:
o = self('for-each-ref', '--format=%(upstream:short)',
'refs/heads/%s' % branch, log_cmd=False, log_fail=False)
except exception.CommandFailed:
except ex.CommandFailed:
return None
if not o:
return None
......@@ -147,7 +147,7 @@ class Git(ShellCommand):
def create_branch(self, new_branch, branch):
try:
self('branch', '-f', new_branch, branch)
except exception.CommandFailed:
except ex.CommandFailed:
# this could only fail if we're on the branch
self('reset', '--hard', branch)
......@@ -247,7 +247,7 @@ class Git(ShellCommand):
try:
return self("config", "--get", param,
log_fail=False, log_cmd=False)
except exception.CommandFailed:
except ex.CommandFailed:
return default
def config_set(self, param, value, is_global=False):
......
......@@ -4,7 +4,7 @@ import os
import subprocess
import sys
from apkg import exception
from apkg import ex
from apkg.log import getLogger, T, LOG_LEVEL, INFO
......@@ -79,7 +79,7 @@ def run(*cmd, **kwargs):
prc = subprocess.Popen(cmd, stdin=stdin, stdout=stdout,
stderr=stderr, env=env)
except OSError:
raise exception.CommandNotFound(cmd=cmd[0])
raise ex.CommandNotFound(cmd=cmd[0])
out, err = prc.communicate(input=_input)
if isinstance(out, bytes):
......@@ -109,7 +109,7 @@ def run(*cmd, **kwargs):
if log_fail:
log_cmd_fail(cmd_str, cout)
if fatal:
raise exception.CommandFailed(cmd=cmd, out=cout)
raise ex.CommandFailed(cmd=cmd, out=cout)
return cout
......
"""
generate docs from apkg code/docstrings using mkdocs-macros-plugin
"""
import inspect
from apkg import ex
from apkg import pkgstyle
from apkg.cli import cmd2mod
from pathlib import Path
......@@ -17,6 +20,7 @@ def define_env(env):
"""
env.variables.pkgstyles = pkgstyle.PKGSTYLES
env.variables.new_issue_url = APKG_NEW_ISSUE_URL
env.variables.exceptions = get_exceptions()
@env.filter
def relpath(path):
......@@ -43,3 +47,12 @@ def define_env(env):
modname = 'apkg.commands.%s' % cmd2mod(cmd)
return "``` text\n$> apkg %s --help\n\n%s\n```" % (
cmd, mod_doc(modname))
def get_exceptions():
"""
return all apkg exceptions sorted by exit_code
"""
exs = [e for _, e in inspect.getmembers(ex, inspect.isclass)]
exs.sort(key=lambda x: x.exit_code)
return exs
# apkg errors