diff --git a/src/caosdb/__init__.py b/src/caosdb/__init__.py index 75847bb15b8a64345119e0f0aefc00bd9f116081..ced43ede0e77f8428dcce2ec7bc6c67555e1887b 100644 --- a/src/caosdb/__init__.py +++ b/src/caosdb/__init__.py @@ -40,8 +40,8 @@ from caosdb.common.models import (ACL, ALL, FIX, NONE, OBLIGATORY, RECOMMENDED, get_known_permissions, raise_errors) from caosdb.configuration import configure, get_config from caosdb.connection.connection import configure_connection, get_connection -from caosdb.exceptions import * from caosdb.version import version as __version__ +from caosdb.exceptions import * # read configuration these files diff --git a/src/caosdb/connection/connection.py b/src/caosdb/connection/connection.py index 1330a49cedeaf8e29d87286088a6bee50cbe77c1..f9c4f2247ae1eccd652cb6ffedd6af81f77315a2 100644 --- a/src/caosdb/connection/connection.py +++ b/src/caosdb/connection/connection.py @@ -473,7 +473,7 @@ class _Connection(object): # pylint: disable=useless-object-inheritance "Bad CaosDBServerConnection implementation. The " "implementation must be a callable object which returns an " "instance of `CaosDBServerConnection` (e.g. a constructor " - "or a factory).", type_err) + "or a factory).\n{}".format(type_err.args[0])) self._delegate_connection.configure(**config) if "auth_token" in config: diff --git a/src/caosdb/exceptions.py b/src/caosdb/exceptions.py index 5279071587ceb7402502ee52868aa005c0465ef9..9406765e2ab2458088549a4a966da4dd58a0f199 100644 --- a/src/caosdb/exceptions.py +++ b/src/caosdb/exceptions.py @@ -23,6 +23,10 @@ # # ** end header # +"""The exceptions module defines exceptions for HTTP Errors (4xx and 5xx and +HTTP response codes) and for transaction errors (i.e. missing permissions, +dependencies, non-passing consistency checks etc.). +""" from lxml import etree @@ -30,8 +34,8 @@ from lxml import etree class CaosDBException(Exception): """Base class of all CaosDB exceptions.""" - def __init__(self, msg=None, *args): - Exception.__init__(self, msg, *args) + def __init__(self, msg): + Exception.__init__(self, msg) self.msg = msg @@ -45,7 +49,6 @@ class ConfigurationException(CaosDBException): msg : str A descriptin of the misconfiguration. The constructor adds a few lines with explainingg where to find the configuration. - *args Attributes ---------- @@ -53,10 +56,9 @@ class ConfigurationException(CaosDBException): A description of the misconfiguration. """ - def __init__(self, msg, *args): + def __init__(self, msg): super(ConfigurationException, self).__init__(msg + - ConfigurationException._INFO, - *args) + ConfigurationException._INFO) _INFO = ("\n\nPlease check your ~/.pycaosdb.ini and your $PWD/" ".pycaosdb.ini. Do at least one of them exist and are they correct?") @@ -66,6 +68,8 @@ class ConfigurationException(CaosDBException): class ClientErrorException(CaosDBException): + """ClientErrorException represents 4xx HTTP client errors.""" + def __init__(self, msg, status, body): self.status = status self.body = body @@ -73,6 +77,8 @@ class ClientErrorException(CaosDBException): class ServerErrorException(CaosDBException): + """ServerErrorException represents 5xx HTTP server errors.""" + def __init__(self, body): xml = etree.fromstring(body) error = xml.xpath('/Response/Error')[0] @@ -90,11 +96,11 @@ class ConnectionException(CaosDBException): CaosDBException.__init__(self, msg) -class URITooLongException(CaosDBException): +class URITooLongException(ClientErrorException): """The URI of the last request was too long.""" def __init__(self, msg=None): - CaosDBException.__init__(self, msg) + ClientErrorException.__init__(self, msg=msg, status=414, body=None) class AmbiguityException(CaosDBException): @@ -115,24 +121,24 @@ class LoginFailedException(CaosDBException): CaosDBException.__init__(self, msg=msg) -class HTTPAuthorizationException(CaosDBException): +class HTTPAuthorizationException(ClientErrorException): """You're lacking the required permissions. Corresponds to HTTP status 403. """ def __init__(self, msg=None): - CaosDBException.__init__(self, msg=msg) + ClientErrorException.__init__(self, msg=msg, status=403, body=None) -class ResourceNotFoundException(CaosDBException): +class ResourceNotFoundException(ClientErrorException): """The requested resource doesn't exist; corresponds to HTTP status 404. """ def __init__(self, msg=None): - CaosDBException.__init__(self, msg=msg) + ClientErrorException.__init__(self, msg=msg, status=404, body=None) # ######################### Transaction errors ######################### @@ -150,21 +156,15 @@ class TransactionError(CaosDBException): def __init__(self, error=None, msg="An error occured during the transaction.", container=None): + CaosDBException.__init__(self, msg=msg) self.errors = [] self.all_errors = set() self.entities = [] self.all_entities = set() - self.msg = msg self.container = container if error is not None: self.add_error(error) - def print_errs(self): - print(self) - - for err in self.errors: - err.print_errs() - def has_error(self, error_t, direct_children_only=False): """Check whether this transaction error contains an error of type error_t. If direct_children_only is True, only direct children @@ -203,8 +203,8 @@ class TransactionError(CaosDBException): """ if hasattr(error, "__iter__"): - for e in error: - self.add_error(e) + for err in error: + self.add_error(err) return self elif isinstance(error, EntityError): @@ -260,7 +260,7 @@ class TransactionError(CaosDBException): if self.container is not None: return self.container - from caosdb import Container + from caosdb.common.models import Container return Container().extend(self.entities) def get_error(self): @@ -301,14 +301,15 @@ class TransactionError(CaosDBException): for err in self.container.get_errors(): if err.code is not None: return err.code + return None def _repr_reasons(self, indent): if self.get_errors() is not None and len(self.get_errors()) > 0: ret = "\n" + indent + " +--| REASONS |--" - for c in self.get_errors(): + for err in self.get_errors(): ret += '\n' + indent + ' | -> ' + \ - c.__str__(indent=indent + ' |') + err.__str__(indent=indent + ' |') ret += "\n" + indent + " +----------------" return ret @@ -316,8 +317,11 @@ class TransactionError(CaosDBException): return '' def _repr_head(self, indent): - return str(type(self).__name__) + ((': ' + self.msg) - if hasattr(self, 'msg') and self.msg is not None else '') + return indent + str(type(self).__name__) + ( + (': ' + self.msg) + if hasattr(self, 'msg') and self.msg is not None + else '' + ) def __str__(self, indent=''): ret = self._repr_head(indent=indent) @@ -362,9 +366,11 @@ class EntityError(TransactionError): @property def description(self): + """The description of the error.""" return self.error.description if self.error is not None else None def get_code(self): + """The code of the error.""" return self.error.code if self.error is not None else None def _repr_head(self, indent): diff --git a/unittests/test_connection.py b/unittests/test_connection.py index 5c1518c65b51087ace514ca26fa77eff53130c73..df61d4c439ade9ae231f6a110eb02ad7f7f5bbd0 100644 --- a/unittests/test_connection.py +++ b/unittests/test_connection.py @@ -237,7 +237,7 @@ def test_bad_implementation_not_callable(): connection.configure(implementation=None) assert exc_info.value.args[0].startswith( "Bad CaosDBServerConnection implementation.") - assert exc_info.value.args[1].args[0] == "'NoneType' object is not callable" + assert "'NoneType' object is not callable" in exc_info.value.args[0] def test_bad_implementation_wrong_class(): @@ -246,9 +246,8 @@ def test_bad_implementation_wrong_class(): connection.configure(implementation=dict) assert exc_info.value.args[0].startswith( "Bad CaosDBServerConnection implementation.") - assert exc_info.value.args[1].args[0] == ( - "The `implementation` callable did not return an instance of " - "CaosDBServerConnection.") + assert ("The `implementation` callable did not return an instance of " + "CaosDBServerConnection.") in exc_info.value.args[0] def test_missing_auth_method():