Skip to content

Commit

Permalink
Merge pull request #651 from klalit/master
Browse files Browse the repository at this point in the history
Allow User Defined Exceptions
  • Loading branch information
ukclivecox committed Jun 26, 2019
2 parents 8a49973 + 968242f commit 2dbf3be
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 0 deletions.
58 changes: 58 additions & 0 deletions doc/source/python/python_component.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,64 @@ If you want more control you can provide a low-level methods that will provide a
def aggregate_raw(self, msgs: prediction_pb2.SeldonMessageList) -> prediction_pb2.SeldonMessage:
```

## User Defined Exceptions
If you want to handle custom exceptions define a field `model_error_handler` as shown below:
```
model_error_handler = flask.Blueprint('error_handlers', __name__)
```
An example is as follow:

```python
"""
Model Template
"""
class MyModel(Object):

"""
The field is used to register custom exceptions
"""
model_error_handler = flask.Blueprint('error_handlers', __name__)

"""
Register the handler for an exception
"""
@model_error_handler.app_errorhandler(UserCustomException)
def handleCustomError(error):
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response

def __init__(self, metrics_ok=True, ret_nparray=False, ret_meta=False):
pass

def predict(self, X, features_names, **kwargs):
raise UserCustomException('Test-Error-Msg',1402,402)
return X
```

```python
"""
User Defined Exception
"""
class UserCustomException(Exception):

status_code = 404

def __init__(self, message, application_error_code,http_status_code):
Exception.__init__(self)
self.message = message
if http_status_code is not None:
self.status_code = http_status_code
self.application_error_code = application_error_code

def to_dict(self):
rv = {"status": {"status": self.status_code, "message": self.message,
"app_code": self.application_error_code}}
return rv

```


## Next Steps

After you have created the Component you need to create a Docker image that can be managed by Seldon Core. Follow the documentation to do this with [s2i](./python_wrapping_s2i.md) or [Docker](./python_wrapping_docker.md).
Expand Down
4 changes: 4 additions & 0 deletions python/seldon_core/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ def get_rest_microservice(user_model):
app = Flask(__name__, static_url_path='')
CORS(app)

if hasattr(user_model, 'model_error_handler'):
logger.info('Registering the custom error handler...')
app.register_blueprint(user_model.model_error_handler)

@app.errorhandler(SeldonMicroserviceException)
def handle_invalid_usage(error):
response = jsonify(error.to_dict())
Expand Down
93 changes: 93 additions & 0 deletions python/tests/test_application_exception_microservice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import json
import numpy as np
from google.protobuf import json_format
import base64
import tensorflow as tf
from tensorflow.core.framework.tensor_pb2 import TensorProto

from seldon_core.wrapper import get_rest_microservice, SeldonModelGRPC, get_grpc_server
from seldon_core.proto import prediction_pb2
from seldon_core.user_model import SeldonComponent

import flask
from flask import jsonify

class UserCustomException(Exception):
status_code = 404

def __init__(self, message, application_error_code,http_status_code):
Exception.__init__(self)
self.message = message
if http_status_code is not None:
self.status_code = http_status_code
self.application_error_code = application_error_code

def to_dict(self):
rv = {"status": {"status": self.status_code, "message": self.message,
"app_code": self.application_error_code}}
return rv

class UserObject(SeldonComponent):

model_error_handler = flask.Blueprint('error_handlers', __name__)

@model_error_handler.app_errorhandler(UserCustomException)
def handleCustomError(error):
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response


def __init__(self, metrics_ok=True, ret_nparray=False, ret_meta=False):
self.metrics_ok = metrics_ok
self.ret_nparray = ret_nparray
self.nparray = np.array([1, 2, 3])
self.ret_meta = ret_meta

def predict(self, X, features_names, **kwargs):
raise UserCustomException('Test-Error-Msg',1402,402)
return X


class UserObjectLowLevel(SeldonComponent):

model_error_handler = flask.Blueprint('error_handlers', __name__)

@model_error_handler.app_errorhandler(UserCustomException)
def handleCustomError(error):
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response


def __init__(self, metrics_ok=True, ret_nparray=False):
self.metrics_ok = metrics_ok
self.ret_nparray = ret_nparray
self.nparray = np.array([1, 2, 3])

def predict_rest(self, request):
raise UserCustomException('Test-Error-Msg',1402,402)
return {"data": {"ndarray": [9, 9]}}


def test_raise_exception():
user_object = UserObject()
app = get_rest_microservice(user_object)
client = app.test_client()
rv = client.get('/predict?json={"data":{"names":["a","b"],"ndarray":[[1,2]]}}')
j = json.loads(rv.data)
print(j)
assert rv.status_code == 402
assert j["status"]["app_code"] == 1402


def test_raise_eception_lowlevel():
user_object = UserObjectLowLevel()
app = get_rest_microservice(user_object)
client = app.test_client()
rv = client.get('/predict?json={"data":{"ndarray":[1,2]}}')
j = json.loads(rv.data)
print(j)
assert rv.status_code == 402
assert j["status"]["app_code"] == 1402

0 comments on commit 2dbf3be

Please sign in to comment.