diff --git a/python/seldon_core/seldon_methods.py b/python/seldon_core/seldon_methods.py index c815b99424..2b2040bbbd 100644 --- a/python/seldon_core/seldon_methods.py +++ b/python/seldon_core/seldon_methods.py @@ -34,10 +34,11 @@ def predict( logger.warning("predict_grpc is deprecated. Please use predict_raw") return user_model.predict_grpc(request) else: - try: - return user_model.predict_raw(request) - except NotImplementedError: - pass + if hasattr(user_model, "predict_raw"): + try: + return user_model.predict_raw(request) + except SeldonNotImplementedError: + pass if is_proto: (features, meta, datadef, data_type) = extract_request_parts(request) @@ -76,10 +77,11 @@ def send_feedback(user_model: Any, request: prediction_pb2.Feedback, response_json = user_model.send_feedback_grpc(request) return json_to_seldon_message(response_json) else: - try: - return user_model.send_feedback_raw(request) - except NotImplementedError: - pass + if hasattr(user_model, "send_feedback_raw"): + try: + return user_model.send_feedback_raw(request) + except SeldonNotImplementedError: + pass (datadef_request, features, truth, reward) = extract_feedback_request_parts(request) routing = request.response.meta.routing.get(predictive_unit_id) @@ -116,10 +118,11 @@ def transform_input(user_model: Any, request: prediction_pb2.SeldonMessage) -> p logger.warning("transform_input_grpc is deprecated. Please use transform_input_raw") return user_model.transform_input_grpc(request) else: - try: - return user_model.transform_input_raw(request) - except NotImplementedError: - pass + if hasattr(user_model, "transform_input_raw"): + try: + return user_model.transform_input_raw(request) + except SeldonNotImplementedError: + pass (features, meta, datadef, data_type) = extract_request_parts(request) client_response = client_transform_input(user_model, features, datadef.names, meta=meta) @@ -152,10 +155,11 @@ def transform_output(user_model: Any, logger.warning("transform_input_grpc is deprecated. Please use transform_input_raw") return user_model.transform_output_grpc(request) else: - try: - return user_model.transform_output_raw(request) - except NotImplementedError: - pass + if hasattr(user_model, "transform_output_raw"): + try: + return user_model.transform_output_raw(request) + except SeldonNotImplementedError: + pass (features, meta, datadef, data_type) = extract_request_parts(request) client_response = client_transform_output(user_model, features, datadef.names, meta=meta) @@ -184,10 +188,11 @@ def route(user_model: Any, request: prediction_pb2.SeldonMessage) -> prediction_ logger.warning("route_grpc is deprecated. Please use route_raw") return user_model.route_grpc(request) else: - try: - return user_model.route_raw(request) - except NotImplementedError: - pass + if hasattr(user_model, "route_raw"): + try: + return user_model.route_raw(request) + except SeldonNotImplementedError: + pass (features, meta, datadef, _) = extract_request_parts(request) client_response = client_route(user_model, features, datadef.names) @@ -222,10 +227,11 @@ def aggregate(user_model: Any, request: prediction_pb2.SeldonMessageList) -> pre logger.warning("aggregate_grpc is deprecated. Please use aggregate_raw") return user_model.aggregate_grpc(request) else: - try: - return user_model.aggregate_raw(request) - except NotImplementedError: - pass + if hasattr(user_model, "aggregate_raw"): + try: + return user_model.aggregate_raw(request) + except SeldonNotImplementedError: + pass features_list = [] names_list = [] diff --git a/python/seldon_core/user_model.py b/python/seldon_core/user_model.py index 04b74b0d7a..1bd014fd20 100644 --- a/python/seldon_core/user_model.py +++ b/python/seldon_core/user_model.py @@ -9,67 +9,70 @@ logger = logging.getLogger(__name__) +class SeldonNotImplementedError(SeldonMicroserviceException): + status_code = 403 + class SeldonComponent(object): def __init__(self, **kwargs): pass def tags(self) -> Dict: - raise NotImplementedError + raise SeldonNotImplementedError("tags is not implemented") def class_names(self) -> Iterable[str]: - raise NotImplementedError + raise SeldonNotImplementedError("class_names is not implemented") def load(self): pass def predict(self, X: np.ndarray, names: Iterable[str], meta: Dict = None) -> Union[ np.ndarray, List, str, bytes]: - raise NotImplementedError + raise SeldonNotImplementedError("predict is not implemented") def predict_raw(self, msg: prediction_pb2.SeldonMessage) -> prediction_pb2.SeldonMessage: - raise NotImplementedError + raise SeldonNotImplementedError("predict_raw is not implemented") def send_feedback_raw(self, feedback: prediction_pb2.Feedback) -> prediction_pb2.SeldonMessage: - raise NotImplementedError + raise SeldonNotImplementedError("send_feedback_raw is not implemented") def transform_input(self, X: np.ndarray, names: Iterable[str], meta: Dict = None) -> Union[ np.ndarray, List, str, bytes]: - raise NotImplementedError + raise SeldonNotImplementedError("transform is not implemented") def transform_input_raw(self, msg: prediction_pb2.SeldonMessage) -> prediction_pb2.SeldonMessage: - raise NotImplementedError + raise SeldonNotImplementedError("transform_input_raw is not implemented") def transform_output(self, X: np.ndarray, names: Iterable[str], meta: Dict = None) -> Union[ np.ndarray, List, str, bytes]: - raise NotImplementedError + raise SeldonNotImplementedError("transform_output is not implemented") def transform_output_raw(self, msg: prediction_pb2.SeldonMessage) -> prediction_pb2.SeldonMessage: - raise NotImplementedError + raise SeldonNotImplementedError("transform_output_raw is not implemented") def metrics(self) -> List[Dict]: - raise NotImplementedError + raise SeldonNotImplementedError("metrics is not implemented") def feature_names(self) -> Iterable[str]: - raise NotImplementedError + raise SeldonNotImplementedError("feature_names is not implemented") def send_feedback(self, features: Union[np.ndarray, str, bytes], feature_names: Iterable[str], reward: float, truth: Union[np.ndarray, str, bytes], routing: Union[int, None]) -> Union[ np.ndarray, List, str, bytes, None]: - raise NotImplementedError + raise SeldonNotImplementedError("send_feedback is not implemented") def route(self, features: Union[np.ndarray, str, bytes], feature_names: Iterable[str]) -> int: - raise NotImplementedError + raise SeldonNotImplementedError("route is not implemented") def route_raw(self, msg: prediction_pb2.SeldonMessage) -> prediction_pb2.SeldonMessage: - raise NotImplementedError + raise SeldonNotImplementedError("route_raw is not implemented") def aggregate(self, features_list: List[Union[np.ndarray, str, bytes]], feature_names_list: List) -> Union[ np.ndarray, List, str, bytes]: - raise NotImplementedError + raise SeldonNotImplementedError("aggregate is not implemented") def aggregate_raw(self, msgs: prediction_pb2.SeldonMessageList) -> prediction_pb2.SeldonMessage: - raise NotImplementedError + raise SeldonNotImplementedError("aggregate_raw is not implemented") def client_custom_tags(user_model: SeldonComponent) -> Dict: @@ -85,11 +88,13 @@ def client_custom_tags(user_model: SeldonComponent) -> Dict: Dictionary of key value pairs """ - try: - return user_model.tags() - except NotImplementedError: - logger.info("custom_tags is not implemented") - return {} + if hasattr(user_model, "tags"): + try: + return user_model.tags() + except SeldonNotImplementedError: + pass + logger.info("custom_tags is not implemented") + return {} def client_class_names(user_model: SeldonComponent, predictions: np.ndarray) -> Iterable[str]: @@ -107,16 +112,18 @@ def client_class_names(user_model: SeldonComponent, predictions: np.ndarray) -> Class names """ if len(predictions.shape) > 1: - try: + if hasattr(user_model, "class_names"): if inspect.ismethod(getattr(user_model, 'class_names')): - return user_model.class_names() + try: + return user_model.class_names() + except SeldonNotImplementedError: + pass else: logger.info("class_names attribute is deprecated. Please define a class_names method") return user_model.class_names - except NotImplementedError: - logger.info("class_names is not implemented") - n_targets = predictions.shape[1] - return ["t:{}".format(i) for i in range(n_targets)] + logger.info("class_names is not implemented") + n_targets = predictions.shape[1] + return ["t:{}".format(i) for i in range(n_targets)] else: return [] @@ -140,14 +147,16 @@ def client_predict(user_model: SeldonComponent, features: Union[np.ndarray, str, ------- A prediction from the user model """ - try: + if hasattr(user_model, "predict"): try: - return user_model.predict(features, feature_names, **kwargs) - except TypeError: - return user_model.predict(features, feature_names) - except NotImplementedError: - logger.info("predict is not implemented") - return [] + try: + return user_model.predict(features, feature_names, **kwargs) + except TypeError: + return user_model.predict(features, feature_names) + except SeldonNotImplementedError: + pass + logger.info("predict is not implemented") + return [] def client_transform_input(user_model: SeldonComponent, features: Union[np.ndarray, str, bytes], @@ -171,14 +180,16 @@ def client_transform_input(user_model: SeldonComponent, features: Union[np.ndarr Transformed data """ - try: + if hasattr(user_model, "transform_input"): try: - return user_model.transform_input(features, feature_names, **kwargs) - except TypeError: - return user_model.transform_input(features, feature_names) - except NotImplementedError: - logger.info("transform_input is not implemented") - return features + try: + return user_model.transform_input(features, feature_names, **kwargs) + except TypeError: + return user_model.transform_input(features, feature_names) + except SeldonNotImplementedError: + pass + logger.info("transform_input is not implemented") + return features def client_transform_output(user_model: SeldonComponent, features: Union[np.ndarray, str, bytes], @@ -201,14 +212,16 @@ def client_transform_output(user_model: SeldonComponent, features: Union[np.ndar Transformed data """ - try: + if hasattr(user_model, "transform_output"): try: - return user_model.transform_output(features, feature_names, **kwargs) - except TypeError: - return user_model.transform_output(features, feature_names) - except NotImplementedError: - logger.info("transform_output is not implemented") - return features + try: + return user_model.transform_output(features, feature_names, **kwargs) + except TypeError: + return user_model.transform_output(features, feature_names) + except SeldonNotImplementedError: + pass + logger.info("transform_output is not implemented") + return features def client_custom_metrics(user_model: SeldonComponent) -> List[Dict]: @@ -225,16 +238,18 @@ def client_custom_metrics(user_model: SeldonComponent) -> List[Dict]: A list of custom metrics """ - try: - metrics = user_model.metrics() - if not validate_metrics(metrics): - j_str = json.dumps(metrics) - raise SeldonMicroserviceException( - "Bad metric created during request: " + j_str, reason="MICROSERVICE_BAD_METRIC") - return metrics - except NotImplementedError: - logger.info("custom_metrics is not implemented") - return [] + if hasattr(user_model, "metrics"): + try: + metrics = user_model.metrics() + if not validate_metrics(metrics): + j_str = json.dumps(metrics) + raise SeldonMicroserviceException( + "Bad metric created during request: " + j_str, reason="MICROSERVICE_BAD_METRIC") + return metrics + except SeldonNotImplementedError: + pass + logger.info("custom_metrics is not implemented") + return [] def client_feature_names(user_model: SeldonComponent, original: Iterable[str]) -> Iterable[str]: @@ -251,11 +266,13 @@ def client_feature_names(user_model: SeldonComponent, original: Iterable[str]) - ------- A list if feature names """ - try: - return user_model.feature_names() - except NotImplementedError: - logger.info("feature_names is not implemented") - return original + if hasattr(user_model, "feature_names"): + try: + return user_model.feature_names() + except SeldonNotImplementedError: + pass + logger.info("feature_names is not implemented") + return original def client_send_feedback(user_model: SeldonComponent, features: Union[np.ndarray, str, bytes], @@ -285,11 +302,13 @@ def client_send_feedback(user_model: SeldonComponent, features: Union[np.ndarray Optional payload """ - try: - return user_model.send_feedback(features, feature_names, reward, truth, routing=routing) - except NotImplementedError: - logger.info("send_feedback is not implemented") - return None + if hasattr(user_model, "send_feedback"): + try: + return user_model.send_feedback(features, feature_names, reward, truth, routing=routing) + except SeldonNotImplementedError: + pass + logger.info("send_feedback is not implemented") + return None def client_route(user_model: SeldonComponent, features: Union[np.ndarray, str, bytes], @@ -310,10 +329,10 @@ def client_route(user_model: SeldonComponent, features: Union[np.ndarray, str, b ------- Routing index for one of children """ - try: + if hasattr(user_model, "route"): return user_model.route(features, feature_names) - except NotImplementedError: - raise SeldonMicroserviceException("Route not defined") + else: + raise SeldonNotImplementedError("Route not defined") def client_aggregate(user_model: SeldonComponent, features_list: List[Union[np.ndarray, str, bytes]], @@ -333,7 +352,7 @@ def client_aggregate(user_model: SeldonComponent, features_list: List[Union[np.n ------- An aggregated payload """ - try: + if hasattr(user_model, "aggregate"): return user_model.aggregate(features_list, feature_names_list) - except NotImplementedError: - raise SeldonMicroserviceException("Aggregate not defined") + else: + raise SeldonNotImplementedError("Aggregate not defined")