Skip to content
This repository has been archived by the owner on Nov 28, 2021. It is now read-only.

Commit

Permalink
first cut of #107, needs more testing before we can consider it stable
Browse files Browse the repository at this point in the history
  • Loading branch information
BradMclain committed Apr 15, 2016
1 parent 08c43be commit 9501461
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 33 deletions.
84 changes: 61 additions & 23 deletions prestans/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
'UnsupportedContentTypeError',
'ValidationError',
'DataValidationException',
'ParserException',
'HandlerException',
'RequestException',
'ResponseException',

#: Parser Exceptions
'NoEndpointError',
Expand Down Expand Up @@ -185,18 +185,56 @@ def __str__(self):
_loggable_message = "%s %s" % (self._attribute_name, self._message)
return unicode(_loggable_message).encode('utf-8')

class ParserException(Base):
class HandlerException(Base):
"""
The base class for request and response exceptions.
"""
def __init__(self, code, message):

self._request = None

super(HandlerException, self).__init__(code, message)

@property
def request(self):
return self._request

@request.setter
def request(self, value):
self._request = value

@property
def log_message(self):
if self.request is None:
return self._message
else:
#method:url:user agent message
return "%s %s %s \"%s\"" % (
self.request.method,
self.request.url,
self.request.user_agent,
self._message
)

def __unicode__(self):
return self.log_message

def __str__(self):
return self.log_message.encode('utf-8')


class RequestException(HandlerException):
"""
ParserException are Exceptions raised if prestans fails to parse
RequestException are Exceptions raised if prestans fails to parse
a request; these generally revolve around the Content-Types or
missing payload. Specific parsing messages are of type DataValidationException
"""
def __init__(self, code, message):
super(ParserException, self).__init__(code, message)
super(RequestException, self).__init__(code, message)

class HandlerException(Base):
class ResponseException(HandlerException):
"""
HandlerExceptions are Exceptions that are raised by handlers of the
ResponseExceptions are Exceptions that are raised by response handlers of the
REST application. REST encourages the use different error codes to
denote what the error is.
Expand All @@ -207,13 +245,13 @@ class HandlerException(Base):
in prestans.http
"""
def __init__(self, code, message):
super(HandlerException, self).__init__(code, message)
super(ResponseException, self).__init__(code, message)

#:
#: Parser Exception
#:

class UnimplementedVerbError(ParserException):
class UnimplementedVerbError(RequestException):

def __init__(self, verb_name):

Expand All @@ -225,15 +263,15 @@ def __init__(self, verb_name):
'verb': verb_name,
})

class NoEndpointError(ParserException):
class NoEndpointError(RequestException):

def __init__(self):

_code = prestans.http.STATUS.NOT_FOUND
_message = "API does not provide this end-point"
super(NoEndpointError, self).__init__(_code, _message)

class AuthenticationError(ParserException):
class AuthenticationError(RequestException):

def __init__(self, message=None):

Expand All @@ -245,7 +283,7 @@ def __init__(self, message=None):

super(AuthenticationError, self).__init__(_code, _message)

class AuthorizationError(ParserException):
class AuthorizationError(RequestException):

def __init__(self, role_name):

Expand All @@ -254,23 +292,23 @@ def __init__(self, role_name):
super(AuthorizationError, self).__init__(_code, _message)


class SerializationFailedError(ParserException):
class SerializationFailedError(RequestException):

def __init__(self, format):

_code = prestans.http.STATUS.NOT_FOUND
_message = "Serialization failed: %s" % format
super(SerializationFailedError, self).__init__(_code, _message)

class DeSerializationFailedError(ParserException):
class DeSerializationFailedError(RequestException):

def __init__(self, format):

_code = prestans.http.STATUS.NOT_FOUND
_message = "DeSerialization failed: %s" % format
super(DeSerializationFailedError, self).__init__(_code, _message)

class AttributeFilterDiffers(ParserException):
class AttributeFilterDiffers(RequestException):
"""
AttributeFilter initialised from request input does not conform to
the configured template.
Expand Down Expand Up @@ -400,49 +438,49 @@ def __init__(self):
#: these and generates an appropriate error message for the end user.
#:

class ServiceUnavailable(HandlerException):
class ServiceUnavailable(ResponseException):

def __init__(self, message="This service is currently unavailable"):
_code = prestans.http.STATUS.SERVICE_UNAVAILABLE
super(ServiceUnavailable, self).__init__(_code, message)

class BadRequest(HandlerException):
class BadRequest(ResponseException):

def __init__(self, message):
_code = prestans.http.STATUS.BAD_REQUEST
super(BadRequest, self).__init__(_code, message)

class Conflict(HandlerException):
class Conflict(ResponseException):

def __init__(self, message):
_code = prestans.http.STATUS.CONFLICT
super(Conflict, self).__init__(_code, message)

class NotFound(HandlerException):
class NotFound(ResponseException):

def __init__(self, message):
_code = prestans.http.STATUS.NOT_FOUND
super(NotFound, self).__init__(_code, message)

class Unauthorized(HandlerException):
class Unauthorized(ResponseException):

def __init__(self, message):
_code = prestans.http.STATUS.UNAUTHORIZED
super(Unauthorized, self).__init__(_code, message)

class MovedPermanently(HandlerException):
class MovedPermanently(ResponseException):

def __init__(self, message):
_code = prestans.http.STATUS.MOVED_PERMANENTLY
super(MovedPermanently, self).__init__(_code, message)

class PaymentRequired(HandlerException):
class PaymentRequired(ResponseException):

def __init__(self, message):
_code = prestans.http.STATUS.PAYMENT_REQUIRED
super(PaymentRequired, self).__init__(_code, message)

class Forbidden(HandlerException):
class Forbidden(ResponseException):

def __init__(self, message):
_code = prestans.http.STATUS.FORBIDDEN
Expand Down
39 changes: 29 additions & 10 deletions prestans/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,8 @@ def __call__(self, environ, start_response):
if model_attribute_filter is not None:
try:
model_attribute_filter.conforms_to_template_filter(self.attribute_filter)
except prestans.exception.AttributeFilterDiffers, exp:
except prestans.exception.AttributeFilterDiffers as exception:
exception.request = self.request
self.logger.warn("%s" % exp)

#: Body should be of type DataCollection try; attempt calling
Expand Down Expand Up @@ -792,7 +793,6 @@ def __call__(self, environ, start_response):
self.logger.info("setting default response to %s" % self.request.accept)

try:

#: Register additional serializers and de-serializers
self.request.register_deserializers(self.register_deserializers())
self.response.register_serializers(self.register_serializers())
Expand All @@ -801,7 +801,9 @@ def __call__(self, environ, start_response):

#: Ensure we support the HTTP verb
if not prestans.http.VERB.is_supported_verb(self.request.method):
raise prestans.exception.UnimplementedVerbError(self.request.method)
unimplemented_verb_error = prestans.exception.UnimplementedVerbError(self.request.method)
unimplemented_verb_error.request = self.request
raise unimplemented_verb_error

#: Setup serializers
self._setup_serializers()
Expand Down Expand Up @@ -892,6 +894,9 @@ def __call__(self, environ, start_response):
self.delete(*self._args)
#: Re-raise all prestans exceptions
except prestans.exception.Base as exception:
if issubclass(exception.__class__, prestans.exception.HandlerException):
exception.request = self.request

raise exception
#: Handle any non-prestans exceptions
except Exception as exp:
Expand Down Expand Up @@ -940,22 +945,34 @@ def handler_did_run(self):
#:

def get(self, *args):
raise prestans.exception.UnimplementedVerbError(prestans.http.VERB.GET)
unimplemented_verb_error = prestans.exception.UnimplementedVerbError(prestans.http.VERB.GET)
unimplemented_verb_error.request = self.request
raise unimplemented_verb_error

def head(self, *args):
raise prestans.exception.UnimplementedVerbError(prestans.http.VERB.HEAD)
unimplemented_verb_error = prestans.exception.UnimplementedVerbError(prestans.http.VERB.HEAD)
unimplemented_verb_error.request = self.request
raise unimplemented_verb_error

def post(self, *args):
raise prestans.exception.UnimplementedVerbError(prestans.http.VERB.POST)
unimplemented_verb_error = prestans.exception.UnimplementedVerbError(prestans.http.VERB.POST)
unimplemented_verb_error.request = self.request
raise unimplemented_verb_error

def put(self, *args):
raise prestans.exception.UnimplementedVerbError(prestans.http.VERB.PUT)
unimplemented_verb_error = prestans.exception.UnimplementedVerbError(prestans.http.VERB.PUT)
unimplemented_verb_error.request = self.request
raise unimplemented_verb_error

def patch(self, *args):
raise prestans.exception.UnimplementedVerbError(prestans.http.VERB.PATCH)
unimplemented_verb_error = prestans.exception.UnimplementedVerbError(prestans.http.VERB.PATCH)
unimplemented_verb_error.request = self.request
raise unimplemented_verb_error

def delete(self, *args):
raise prestans.exception.UnimplementedVerbError(prestans.http.VERB.DELETE)
unimplemented_verb_error = prestans.exception.UnimplementedVerbError(prestans.http.VERB.DELETE)
unimplemented_verb_error.request = self.request
raise unimplemented_verb_error

def redirect(self, url, status=prestans.http.STATUS.TEMPORARY_REDIRECT):

Expand Down Expand Up @@ -1183,7 +1200,9 @@ def __call__(self, environ, start_response):
return request_handler(environ, start_response)

#: Request does not have a matched handler
raise prestans.exception.NoEndpointError()
no_endpoint = prestans.exception.NoEndpointError()
no_endpoint.request = request
raise no_endpoint

except prestans.exception.Base, exp:
self.logger.error(exp)
Expand Down

0 comments on commit 9501461

Please sign in to comment.