Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
140 lines (110 sloc) 4.22 KB
import logging
from pprint import pformat
from flask import jsonify
from pymacaron_core.exceptions import ValidationError, PyMacaronCoreException
from pymacaron_core.swagger.apipool import ApiPool
log = logging.getLogger(__name__)
class PyMacaronException(PyMacaronCoreException):
status = 500
error_id = None
user_message = None
error_caught = None
def tell_user(self, msg):
self.user_message = msg
return self
def caught(self, error):
self.error_caught = error
return self
def http_reply(self):
"""Return a Flask reply object describing this error"""
data = {
'status': self.status,
'error': self.code.upper(),
'error_description': str(self)
if self.error_caught:
data['error_caught'] = pformat(self.error_caught)
if self.error_id:
data['error_id'] = self.error_id
if self.user_message:
data['user_message'] = self.user_message
r = jsonify(data)
r.status_code = self.status
if str(self.status) != "200":
log.warn("ERROR: caught error %s %s [%s]" % (self.status, self.code, str(self)))
return r
def to_model(self):
"""Return a bravado-core Error instance"""
e = ApiPool().current_server_api.model.Error(
if self.error_id:
e.error_id = self.error_id
if self.user_message:
e.user_message = self.user_message
if self.error_caught:
e.error_caught = pformat(self.error_caught)
return e
# Interface to allow creating further Exception classes
code_to_class = {}
def add_error(name=None, code=None, status=None):
"""Create a new Exception class"""
if not name or not status or not code:
raise Exception("Can't create Exception class %s: you must set both name, status and code" % name)
myexception = type(name, (PyMacaronException, ), {"code": code, "status": status})
globals()[name] = myexception
if code in code_to_class:
raise Exception("ERROR! Exception %s is already defined." % code)
code_to_class[code] = myexception
return myexception
add_error('UnhandledServerError', 'UNHANDLED_SERVER_ERROR', 500)
add_error('InternalServerError', 'SERVER_ERROR', 500)
add_error('AuthMissingHeaderError', 'AUTHORIZATION_HEADER_MISSING', 401)
add_error('AuthTokenExpiredError', 'TOKEN_EXPIRED', 401)
add_error('AuthInvalidTokenError', 'TOKEN_INVALID', 401)
add_error('ValidationError', 'INVALID_PARAMETER', 400)
# Manipulate various error objects
def responsify(error):
"""Take an Error model and return it as a Flask response"""
assert str(type(error).__name__) == 'Error'
if error.error in code_to_class:
e = code_to_class[error.error](error.error_description)
if error.error_id:
e.error_id = error.error_id
if error.user_message:
e.user_message = error.user_message
return e.http_reply()
elif isinstance(error, PyMacaronException):
return error.http_reply()
return PyMacaronException("Caught un-mapped error: %s" % error).http_reply()
def is_error(o):
"""True if o is an instance of a swagger Error model or a flask Response of
an error model"""
if hasattr(o, 'error') and hasattr(o, 'error_description') and hasattr(o, 'status'):
return True
return False
def format_error(e):
"""Take an exception caught within pymacaron_core and turn it into a
bravado-core Error instance
if isinstance(e, PyMacaronException):
return e.to_model()
if isinstance(e, PyMacaronCoreException) and e.__class__.__name__ == 'ValidationError':
return ValidationError(str(e)).to_model()
# Turn this exception into a PyMacaron Error model
return UnhandledServerError(str(e)).to_model()
def raise_error(e):
"""Take a bravado-core Error model and raise it as an exception"""
code = e.error
if code in code_to_class:
raise code_to_class[code](e.error_description)
raise InternalServerError(e.error_description)