Skip to content

Commit

Permalink
Merge pull request ga4gh#233 from jeromekelleher/centralise-exceptions
Browse files Browse the repository at this point in the history
Centralised and simplified exceptions.
  • Loading branch information
dcolligan committed Mar 12, 2015
2 parents 2a5fe02 + 4eb7359 commit cfc2fe5
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 317 deletions.
6 changes: 3 additions & 3 deletions ga4gh/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import ga4gh.protocol as protocol
import ga4gh.datamodel.references as references
import ga4gh.datamodel.reads as reads
import ga4gh.backend_exceptions as backendExceptions
import ga4gh.exceptions as exceptions
import ga4gh.datamodel.variants as variants


Expand Down Expand Up @@ -268,7 +268,7 @@ def validateRequest(self, jsonDict, requestClass):
"""
if self._requestValidation:
if not requestClass.validate(jsonDict):
raise backendExceptions.RequestValidationFailureException()
raise exceptions.RequestValidationFailureException()

def validateResponse(self, jsonDict, responseClass):
"""
Expand All @@ -277,7 +277,7 @@ def validateResponse(self, jsonDict, responseClass):
"""
if self._responseValidation:
if not responseClass.validate(jsonDict):
raise backendExceptions.ResponseValidationFailureException()
raise exceptions.ResponseValidationFailureException()

def setRequestValidation(self, requestValidation):
"""
Expand Down
42 changes: 0 additions & 42 deletions ga4gh/backend_exceptions.py

This file was deleted.

15 changes: 8 additions & 7 deletions ga4gh/datamodel/variants.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import wormtable as wt

import ga4gh.protocol as protocol
import ga4gh.backend_exceptions as backendExceptions
import ga4gh.exceptions as exceptions


def convertVCFPhaseset(vcfPhaseset):
Expand Down Expand Up @@ -329,7 +329,7 @@ def getVariants(self, referenceName, startPosition, endPosition,
try:
cols = [col for name, col in self._sampleCols[callSetId]]
except KeyError:
raise backendExceptions.CallSetNotInVariantSetException(
raise exceptions.CallSetNotInVariantSetException(
callSetId, self._variantSetId)
readCols.extend(cols)
# Now we get the row positions for the sample columns
Expand Down Expand Up @@ -367,7 +367,8 @@ def getCallSets(self, name, startPosition):
"""
# TODO: implement name string search after semantics is clarified
if name is not None:
raise NotImplementedError()
raise exceptions.NotImplementedException(
"Searching by name not supported")
else:
callSetIds = self._sampleNames[startPosition:]
for i in range(len(callSetIds)):
Expand Down Expand Up @@ -461,10 +462,10 @@ def getVariants(self, referenceName, startPosition, endPosition,
correspond to the attributes of a GASearchVariantsRequest object.
"""
if variantName is not None:
raise NotImplementedError(
raise exceptions.NotImplementedException(
"Searching by variantName is not supported")
if callSetIds is not None:
raise NotImplementedError(
raise exceptions.NotImplementedException(
"Specifying call set ids is not supported")
if referenceName in self._chromTabixFileMap:
tabixFile = self._chromTabixFileMap[referenceName]
Expand Down Expand Up @@ -568,10 +569,10 @@ def getVariants(self, referenceName, startPosition, endPosition,
correspond to the attributes of a GASearchVariantsRequest object.
"""
if variantName is not None:
raise NotImplementedError(
raise exceptions.NotImplementedException(
"Searching by variantName is not supported")
if callSetIds is not None:
raise NotImplementedError(
raise exceptions.NotImplementedException(
"Specifying call set ids is not supported")
if referenceName in self._chromFileMap:
varFile = self._chromFileMap[referenceName]
Expand Down
180 changes: 180 additions & 0 deletions ga4gh/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
"""
Exceptions for the GA4GH server. Each exception that can occur in the server
is given a unique error code that is derived from its name.
"""
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import zlib

import ga4gh.protocol as protocol


def getServerError(exception):
"""
Converts the specified exception that is not a subclass of
BaseServerException into a ServerError so that it can be correctly
serialised and communicated back to the client.
"""
assert not isinstance(exception, BaseServerException)
serverException = ServerError()
serverException.message = str(exception)
return serverException


class BaseServerException(Exception):
"""
Superclass of all exceptions that can occur in the GA4GH reference
server.
"""
httpStatus = -1
message = "Error code not set in exception; this is a bug."

def __init__(self, *args, **kwargs):
# We have this constructor so that we can always create an
# instance of our base classes using inspect. This is useful
# for testing.
super(BaseServerException, self).__init__(*args, **kwargs)

def getMessage(self):
"""
Returns the message that we map into the GA4GH protocol message.
For many exceptions we can simply set the message statically
for a given class, and this message will be returned by
default. For more elaborate messages that must be contructed
at run time, we can override this method and use state held
in instance variables in the exception.
"""
return self.message

@classmethod
def getErrorCode(cls):
"""
Returns an integer that can be used to identify this class.
"""
# We use the CRC32 digest of the class name as a unique code.
# We follow the recommendation of the Python docs to ensure
# that this value is signed 32 bit integer.
code = zlib.crc32(cls.__name__) & 0xffffffff
return code

def toProtocolElement(self):
"""
Converts this exception into the GA4GH protocol type so that
it can be communicated back to the client.
"""
error = protocol.GAException()
error.errorCode = self.getErrorCode()
error.message = self.getMessage()
return error

#####################################################################
#
# Exceptions that occur in the normal operation of the server
#
#####################################################################


class BadRequestException(BaseServerException):
"""
A request that we don't like was sent to the server.
"""
httpStatus = 400
message = "Bad request"


class BadPageSizeException(BadRequestException):
message = "Request page size invalid"


class BadPageTokenException(BadRequestException):
message = "Request page token invalid"


class NotFoundException(BaseServerException):
"""
The superclass of all exceptions in which some resource was not
found.
"""
httpStatus = 404
message = "A resource was not found"


class PathNotFoundException(NotFoundException):
message = "The request path was not found"


class ObjectNotFoundException(NotFoundException):
message = "The requested object was not found"


class UnsupportedMediaTypeException(BaseServerException):
httpStatus = 415
message = "Unsupported media type"


class VersionNotSupportedException(NotFoundException):
message = "API version not supported"


class MethodNotAllowedException(BaseServerException):
httpStatus = 405
message = "Method not allowed"


class NotImplementedException(BaseServerException):
"""
Exception raised when a part of the API has not been implemented.
"""
httpStatus = 501

def __init__(self, message):
self.message = message


class RequestValidationFailureException(BaseServerException):
"""
A validation of the request data failed
"""
def getMessage(self):
message = (
"Malformed request: JSON does not conform to the GA4GH"
"protocol version {}".format(protocol.version))
return message


class CallSetNotInVariantSetException(NotFoundException):
"""
Indicates a request was made for a callSet not in the actual variantSet
"""
def __init__(self, callSetId, variantSetId):
self.callSetId = callSetId
self.variantSetId = variantSetId

def getMessage(self):
message = "callSet '{0}' not in variantSet '{1}'".format(
self.callSetId, self.variantSetId)
return message

###############################################################
#
# Internal errors. These are exceptions that we regard as bugs.
#
###############################################################


class ServerError(BaseServerException):
"""
Superclass of all exceptions that indicate a bug has occured.
"""
httpStatus = 500


class ResponseValidationFailureException(ServerError):
"""
A validation of the response data failed
"""
message = (
"Validation of the generated response failed. "
"Please file a bug report")
Loading

0 comments on commit cfc2fe5

Please sign in to comment.