Skip to content

Commit

Permalink
chore: new error handling strategy (DEV-1770) (#324)
Browse files Browse the repository at this point in the history
  • Loading branch information
jnussbaum committed Mar 10, 2023
1 parent e5cddf5 commit f2aa4b0
Show file tree
Hide file tree
Showing 41 changed files with 199 additions and 133 deletions.
162 changes: 105 additions & 57 deletions src/dsp_tools/dsp_tools.py
Expand Up @@ -7,6 +7,7 @@
from importlib.metadata import version

from dsp_tools.excel2xml import excel2xml
from dsp_tools.models.exceptions import UserError
from dsp_tools.utils.excel_to_json_lists import excel2lists, validate_lists_section_with_schema
from dsp_tools.utils.excel_to_json_project import excel2json
from dsp_tools.utils.excel_to_json_properties import excel2properties
Expand All @@ -21,15 +22,12 @@
from dsp_tools.utils.xml_upload import xml_upload


def program(user_args: list[str]) -> None:
def make_parser() -> argparse.ArgumentParser:
"""
The program parses the command line arguments and calls the requested action
Args:
user_args: list of arguments passed by the user from the command line
Create a parser for the command line arguments
Returns:
None
parser
"""
# help texts
username_text = "username (e-mail) used for authentication with the DSP-API "
Expand All @@ -42,7 +40,7 @@ def program(user_args: list[str]) -> None:
default_user = "root@example.com"
default_pw = "test"

# parse the arguments of the command line
# make a parser
parser = argparse.ArgumentParser(description=f"DSP-TOOLS (version {version('dsp-tools')}, © {datetime.datetime.now().year} by DaSCH)")
subparsers = parser.add_subparsers(title="Subcommands", description="Valid subcommands are", help="sub-command help")

Expand Down Expand Up @@ -161,8 +159,28 @@ def program(user_args: list[str]) -> None:
)
parser_stackdown.set_defaults(action="stop-stack")

return parser


def call_requested_action(
user_args: list[str],
parser: argparse.ArgumentParser
) -> bool:
"""
With the help of the parser, parse the user arguments and call the appropriate method of DSP-TOOLS.
Args:
user_args: list of arguments passed by the user from the command line
parser: parser to parse the user arguments
# call the requested action
Raises:
BaseError from the called methods
UserError from the called methods
unexpected errors from the called methods and underlying libraries
Returns:
success status
"""
args = parser.parse_args(user_args)
if not hasattr(args, "action"):
parser.print_help(sys.stderr)
Expand All @@ -173,81 +191,111 @@ def program(user_args: list[str]) -> None:
success = validate_lists_section_with_schema(path_to_json_project_file=args.project_definition)
print("'Lists' section of the JSON project file is syntactically correct and passed validation.")
else:
_, success = create_lists(project_file_as_path_or_parsed=args.project_definition,
server=args.server,
user=args.user,
password=args.password,
dump=args.dump)
_, success = create_lists(
project_file_as_path_or_parsed=args.project_definition,
server=args.server,
user=args.user,
password=args.password,
dump=args.dump
)
else:
if args.validate_only:
success = validate_project(args.project_definition)
print("JSON project file is syntactically correct and passed validation.")
else:
success = create_project(project_file_as_path_or_parsed=args.project_definition,
server=args.server,
user_mail=args.user,
password=args.password,
verbose=args.verbose,
dump=args.dump if args.dump else False)
success = create_project(
project_file_as_path_or_parsed=args.project_definition,
server=args.server,
user_mail=args.user,
password=args.password,
verbose=args.verbose,
dump=args.dump
)
elif args.action == "get":
success = get_project(project_identifier=args.project,
outfile_path=args.project_definition,
server=args.server,
user=args.user,
password=args.password,
verbose=args.verbose)
success = get_project(
project_identifier=args.project,
outfile_path=args.project_definition,
server=args.server,
user=args.user,
password=args.password,
verbose=args.verbose
)
elif args.action == "xmlupload":
if args.validate:
success = validate_xml_against_schema(input_file=args.xmlfile)
else:
success = xml_upload(input_file=args.xmlfile,
server=args.server,
user=args.user,
password=args.password,
imgdir=args.imgdir,
sipi=args.sipi,
verbose=args.verbose,
incremental=args.incremental,
save_metrics=args.metrics)
success = xml_upload(
input_file=args.xmlfile,
server=args.server,
user=args.user,
password=args.password,
imgdir=args.imgdir,
sipi=args.sipi,
verbose=args.verbose,
incremental=args.incremental,
save_metrics=args.metrics
)
elif args.action == "excel2json":
success = excel2json(data_model_files=args.excelfolder,
path_to_output_file=args.project_definition)
success = excel2json(
data_model_files=args.excelfolder,
path_to_output_file=args.project_definition
)
elif args.action == "excel2lists":
_, success = excel2lists(excelfolder=args.excelfolder,
path_to_output_file=args.lists_section,
verbose=args.verbose)
_, success = excel2lists(
excelfolder=args.excelfolder,
path_to_output_file=args.lists_section,
verbose=args.verbose
)
elif args.action == "excel2resources":
_, success = excel2resources(excelfile=args.excelfile,
path_to_output_file=args.resources_section)
_, success = excel2resources(
excelfile=args.excelfile,
path_to_output_file=args.resources_section
)
elif args.action == "excel2properties":
_, success = excel2properties(excelfile=args.excelfile,
path_to_output_file=args.properties_section)
_, success = excel2properties(
excelfile=args.excelfile,
path_to_output_file=args.properties_section
)
elif args.action == "id2iri":
success = id_to_iri(xml_file=args.xmlfile,
json_file=args.mapping,
out_file=args.outfile,
verbose=args.verbose)
success = id_to_iri(
xml_file=args.xmlfile,
json_file=args.mapping,
out_file=args.outfile,
verbose=args.verbose
)
elif args.action == "excel2xml":
success = excel2xml(datafile=args.data_source,
shortcode=args.project_shortcode,
default_ontology=args.ontology_name)
success = excel2xml(
datafile=args.data_source,
shortcode=args.project_shortcode,
default_ontology=args.ontology_name
)
elif args.action == "start-stack":
success = start_stack(max_file_size=args.max_file_size,
enforce_docker_system_prune=args.prune,
suppress_docker_system_prune=args.no_prune)
success = start_stack(
max_file_size=args.max_file_size,
enforce_docker_system_prune=args.prune,
suppress_docker_system_prune=args.no_prune
)
elif args.action == "stop-stack":
success = stop_stack()
else:
success = False

if not success:
exit(1)
return success



def main() -> None:
"""Main entry point of the program as referenced in pyproject.toml"""
program(sys.argv[1:])
parser = make_parser()
try:
success = call_requested_action(user_args=sys.argv[1:], parser=parser)
except UserError as err:
print(err.message)
exit(1)
# let BaseError and all unexpected errors escalate, so that a stack trace is printed

if not success:
exit(1)

if __name__ == "__main__":
program(sys.argv[1:])
main()
3 changes: 2 additions & 1 deletion src/dsp_tools/excel2xml.py
Expand Up @@ -15,7 +15,8 @@
from lxml import etree
from lxml.builder import E

from dsp_tools.models.helpers import BaseError, DateTimeStamp
from dsp_tools.models.exceptions import BaseError
from dsp_tools.models.helpers import DateTimeStamp
from dsp_tools.models.propertyelement import PropertyElement
from dsp_tools.models.value import UriValue
from dsp_tools.utils.shared import simplify_name, check_notna, validate_xml_against_schema
Expand Down
2 changes: 1 addition & 1 deletion src/dsp_tools/models/connection.py
Expand Up @@ -5,7 +5,7 @@
import requests
from pystrict import strict

from dsp_tools.models.helpers import BaseError
from dsp_tools.models.exceptions import BaseError


@strict
Expand Down
29 changes: 29 additions & 0 deletions src/dsp_tools/models/exceptions.py
@@ -0,0 +1,29 @@
class BaseError(Exception):
"""
A basic error class for DSP-TOOLS
"""
message: str

def __init__(self, message: str) -> None:
super().__init__()
self.message = message

def __str__(self) -> str:
return self.message


class InternalError(BaseError):
"""
Class for errors that will be handled by a higher level function
"""
pass


class UserError(BaseError):
"""
Class for errors that are intended for user feedback.
Typically, a UserError is raised when the execution of a program must be interrupted
due to a bad condition in the input data that prevents further processing.
The message should be as user-friendly as possible.
"""
pass
3 changes: 2 additions & 1 deletion src/dsp_tools/models/group.py
Expand Up @@ -8,7 +8,8 @@

from dsp_tools.models.langstring import LangString
from dsp_tools.models.connection import Connection
from dsp_tools.models.helpers import Actions, BaseError
from dsp_tools.models.helpers import Actions
from dsp_tools.models.exceptions import BaseError
from dsp_tools.models.model import Model
from dsp_tools.models.project import Project

Expand Down
28 changes: 1 addition & 27 deletions src/dsp_tools/models/helpers.py
Expand Up @@ -6,6 +6,7 @@

from pystrict import strict

from dsp_tools.models.exceptions import BaseError

#
# here we do some data typing that should help
Expand Down Expand Up @@ -42,33 +43,6 @@ def test(cls, val: str) -> bool:
return m.span()[1] == len(val) if m else False


@strict
class BaseError(Exception):
"""
A basic error class
"""
_message: str

def __init__(self, message: str) -> None:
"""
Constructor for error message
:param message: error message string
"""
super().__init__()
self._message = message

def __str__(self) -> str:
"""
Convert to string
:return: stringyfied error message
"""
return self._message

@property
def message(self) -> str:
return self._message


@unique
class Actions(Enum):
Create = 1
Expand Down
2 changes: 1 addition & 1 deletion src/dsp_tools/models/langstring.py
@@ -1,7 +1,7 @@
from enum import Enum, unique
from typing import Tuple, Optional, Any, Union

from dsp_tools.models.helpers import BaseError
from dsp_tools.models.exceptions import BaseError


@unique
Expand Down
3 changes: 2 additions & 1 deletion src/dsp_tools/models/listnode.py
Expand Up @@ -7,7 +7,8 @@

from dsp_tools.models.set_encoder import SetEncoder
from dsp_tools.models.connection import Connection
from dsp_tools.models.helpers import Actions, BaseError
from dsp_tools.models.helpers import Actions
from dsp_tools.models.exceptions import BaseError
from dsp_tools.models.langstring import Languages, LangString
from dsp_tools.models.model import Model
from dsp_tools.models.project import Project
Expand Down
2 changes: 1 addition & 1 deletion src/dsp_tools/models/model.py
@@ -1,5 +1,5 @@
from dsp_tools.models.connection import Connection
from dsp_tools.models.helpers import BaseError
from dsp_tools.models.exceptions import BaseError


class Model:
Expand Down
3 changes: 2 additions & 1 deletion src/dsp_tools/models/ontology.py
Expand Up @@ -7,7 +7,8 @@
from pystrict import strict

from dsp_tools.models.connection import Connection
from dsp_tools.models.helpers import Actions, BaseError, Context, DateTimeStamp, WithId
from dsp_tools.models.helpers import Actions, Context, DateTimeStamp, WithId
from dsp_tools.models.exceptions import BaseError
from dsp_tools.models.model import Model
from dsp_tools.models.project import Project
from dsp_tools.models.propertyclass import PropertyClass
Expand Down
3 changes: 2 additions & 1 deletion src/dsp_tools/models/project.py
Expand Up @@ -9,7 +9,8 @@

from dsp_tools.models.set_encoder import SetEncoder
from dsp_tools.models.connection import Connection
from dsp_tools.models.helpers import Actions, BaseError
from dsp_tools.models.helpers import Actions
from dsp_tools.models.exceptions import BaseError
from dsp_tools.models.langstring import Languages, LangString
from dsp_tools.models.model import Model

Expand Down
2 changes: 1 addition & 1 deletion src/dsp_tools/models/projectContext.py
Expand Up @@ -2,7 +2,7 @@

from dsp_tools.models.connection import Connection
from dsp_tools.models.group import Group
from dsp_tools.models.helpers import BaseError
from dsp_tools.models.exceptions import BaseError
from dsp_tools.models.project import Project


Expand Down
3 changes: 2 additions & 1 deletion src/dsp_tools/models/propertyclass.py
Expand Up @@ -7,7 +7,8 @@

from dsp_tools.models.set_encoder import SetEncoder
from dsp_tools.models.connection import Connection
from dsp_tools.models.helpers import Actions, BaseError, Context, DateTimeStamp, WithId
from dsp_tools.models.helpers import Actions, Context, DateTimeStamp, WithId
from dsp_tools.models.exceptions import BaseError
from dsp_tools.models.langstring import Languages, LangString
from dsp_tools.models.listnode import ListNode
from dsp_tools.models.model import Model
Expand Down

0 comments on commit f2aa4b0

Please sign in to comment.