示例#1
0
    def handle_cli(self, args, func):
        parser = argparse.ArgumentParser()
        parser.add_argument("--input", required=True)
        parser.add_argument("-o",
                            "--output",
                            default="str",
                            choices=["str", "json"])
        parsed_args = parser.parse_args(args)
        file_path = parsed_args.input

        verify_image_format_or_raise(file_path, self.accept_image_formats)
        if not os.path.isabs(file_path):
            file_path = os.path.abspath(file_path)

        image_array = self.fastai_vision.open_image(
            fn=file_path,
            convert_mode=self.convert_mode,
            div=self.div,
            after_open=self.after_open,
            cls=self.cls or self.fastai_vision.Image,
        )

        result = func(image_array)
        if parsed_args.output == "json":
            result = api_func_result_to_json(result)
        else:
            result = str(result)
        print(result)
示例#2
0
    def handle_aws_lambda_event(self, event, func):
        if event["headers"].get("Content-Type", "").startswith("images/"):
            image_data = self.imread(
                base64.decodebytes(event["body"]), pilmode=self.pilmode
            )
        else:
            raise BadInput(
                "BentoML currently doesn't support Content-Type: {content_type} for "
                "AWS Lambda".format(content_type=event["headers"]["Content-Type"])
            )

        if self.after_open:
            image_data = self.after_open(image_data)

        image_data = self.fastai_vision.pil2tensor(image_data, np.float32)
        if self.div:
            image_data = image_data.div_(255)
        if self.cls:
            image_data = self.cls(image_data)
        else:
            image_data = self.fastai_vision.Image(image_data)

        result = func(image_data)
        json_output = api_func_result_to_json(result)
        return {"statusCode": 200, "body": json_output}
    def handle_aws_lambda_event(self, event, func):
        if event["headers"].get("Content-Type", None) == "text/csv":
            df = pd.read_csv(event["body"])
        else:
            # Optimistically assuming Content-Type to be "application/json"
            try:
                df = pd.read_json(event["body"],
                                  orient=self.orient,
                                  typ=self.typ,
                                  dtype=False)
            except ValueError:
                raise BadInput(
                    "Failed parsing request data, only Content-Type application/json "
                    "and text/csv are supported in BentoML DataframeHandler")

        if self.typ == "frame" and self.input_dtypes is not None:
            _check_dataframe_column_contains(self.input_dtypes, df)

        result = func(df)
        result = api_func_result_to_json(
            result, pandas_dataframe_orient=self.output_orient)

        # Allow disabling CORS by setting it to None
        if self.cors:
            return {
                "statusCode": 200,
                "body": result,
                "headers": {
                    "Access-Control-Allow-Origin": self.cors
                },
            }

        return {"statusCode": 200, "body": result}
示例#4
0
    def handle_batch_request(self, requests: Iterable[SimpleRequest],
                             func) -> Iterable[SimpleResponse]:

        datas = [r.data for r in requests]
        headers = [{hk.lower(): hv
                    for hk, hv in r.headers or tuple()} for r in requests]
        content_types = [
            h.get('content-type', 'application/json') for h in headers
        ]
        # TODO: check content_type

        df_conc, slices = read_dataframes_from_json_n_csv(datas, content_types)

        result_conc = func(df_conc)
        # TODO: check length

        results = [s and result_conc[s] or BadResult for s in slices]

        responses = [SimpleResponse("bad request", None, 400)] * len(requests)
        for i, result in enumerate(results):
            if result is BadResult:
                continue
            json_output = api_func_result_to_json(
                result, pandas_dataframe_orient=self.output_orient)
            responses[i] = SimpleResponse(
                json_output, (("Content-Type", "application/json"), ), 200)
        return responses
示例#5
0
    def handle_cli(self, args, func):
        parser = argparse.ArgumentParser()
        parser.add_argument("--input", required=True, nargs='+')
        parser.add_argument("-o",
                            "--output",
                            default="str",
                            choices=["str", "json"])
        parser.add_argument("--batch-size", default=None, type=int)
        parsed_args = parser.parse_args(args)
        file_paths = parsed_args.input

        batch_size = (parsed_args.batch_size
                      if parsed_args.batch_size else len(file_paths))

        for i in range(0, len(file_paths), batch_size):
            step_file_paths = file_paths[i:i + batch_size]
            image_arrays = []
            for file_path in step_file_paths:
                verify_image_format_or_raise(file_path,
                                             self.accept_image_formats)
                if not os.path.isabs(file_path):
                    file_path = os.path.abspath(file_path)

                image_arrays.append(
                    self.imread(file_path, pilmode=self.pilmode))

            results = func(image_arrays)

            for result in results:
                if parsed_args.output == "json":
                    result = api_func_result_to_json(result)
                else:
                    result = str(result)
                print(result)
示例#6
0
    def handle_batch_request(self, requests: Iterable[SimpleRequest],
                             func: callable) -> Iterable[SimpleResponse]:
        """
        Batch version of handle_request
        """
        bad_resp = SimpleResponse(400, None, "Bad Input")
        responses = [bad_resp] * len(requests)

        input_datas = []
        ids = []
        for i, req in enumerate(requests):
            if not req.data:
                continue
            request = Request.from_values(
                input_stream=BytesIO(req.data),
                content_length=len(req.data),
                headers=req.headers,
            )
            try:
                input_data = self._load_image_data(request)
            except BadInput as e:
                responses[i] = SimpleResponse(400, None, str(e))
                continue

            input_datas.append(input_data)
            ids.append(i)

        results = func(input_datas) if input_datas else []
        for i, result in zip(ids, results):
            responses[i] = SimpleResponse(200, None,
                                          api_func_result_to_json(result))

        return responses
    def handle_batch_request(self, requests: Iterable[SimpleRequest],
                             func) -> Iterable[SimpleResponse]:

        datas = [r.data for r in requests]
        content_types = [
            r.formated_headers.get('content-type', 'application/json')
            for r in requests
        ]
        # TODO: check content_type

        df_conc, slices = read_dataframes_from_json_n_csv(datas, content_types)

        result_conc = func(df_conc)
        # TODO: check length

        results = [result_conc[s] if s else BadResult for s in slices]

        responses = [SimpleResponse(400, None, "bad request")] * len(requests)
        for i, result in enumerate(results):
            if result is BadResult:
                continue
            json_output = api_func_result_to_json(
                result, pandas_dataframe_orient=self.output_orient)
            responses[i] = SimpleResponse(
                200, (("Content-Type", "application/json"), ), json_output)
        return responses
示例#8
0
    def handle_request(self, request, func):
        if request.content_type == "text/csv":
            csv_string = StringIO(request.data.decode('utf-8'))
            df = pd.read_csv(csv_string)
        else:
            # Optimistically assuming Content-Type to be "application/json"
            try:
                df = pd.read_json(
                    request.data.decode("utf-8"),
                    orient=self.orient,
                    typ=self.typ,
                    dtype=False,
                )
            except ValueError:
                raise BadInput(
                    "Failed parsing request data, only Content-Type application/json "
                    "and text/csv are supported in BentoML DataframeHandler")

        if self.typ == "frame" and self.input_dtypes is not None:
            _check_dataframe_column_contains(self.input_dtypes, df)

        result = func(df)
        json_output = api_func_result_to_json(
            result, pandas_dataframe_orient=self.output_orient)
        return Response(response=json_output,
                        status=200,
                        mimetype="application/json")
示例#9
0
    def handle_request(self, request, func):
        if request.content_type == "application/json":
            df = pd.read_json(
                request.data.decode("utf-8"),
                orient=self.orient,
                typ=self.typ,
                dtype=False,
            )
        elif request.content_type == "text/csv":
            csv_string = StringIO(request.data.decode('utf-8'))
            df = pd.read_csv(csv_string)
        else:
            raise BadInput(
                "Request content-type not supported, only application/json and "
                "text/csv are supported")

        if self.typ == "frame" and self.input_dtypes is not None:
            _check_dataframe_column_contains(self.input_dtypes, df)

        result = func(df)
        json_output = api_func_result_to_json(
            result, pandas_dataframe_orient=self.output_orient)
        return Response(response=json_output,
                        status=200,
                        mimetype="application/json")
示例#10
0
    def handle_cli(self, args, func):
        parser = argparse.ArgumentParser()
        parser.add_argument("--input", required=True)
        parser.add_argument("-o",
                            "--output",
                            default="str",
                            choices=["str", "json"])
        parser.add_argument(
            "--orient",
            default=self.orient,
            choices=PANDAS_DATAFRAME_TO_DICT_ORIENT_OPTIONS,
        )
        parser.add_argument(
            "--output_orient",
            default=self.output_orient,
            choices=PANDAS_DATAFRAME_TO_DICT_ORIENT_OPTIONS,
        )
        parsed_args = parser.parse_args(args)

        orient = parsed_args.orient
        output_orient = parsed_args.output_orient
        cli_input = parsed_args.input

        if os.path.isfile(cli_input) or is_s3_url(cli_input) or is_url(
                cli_input):
            if cli_input.endswith(".csv"):
                df = pd.read_csv(cli_input)
            elif cli_input.endswith(".json"):
                df = pd.read_json(cli_input,
                                  orient=orient,
                                  typ=self.typ,
                                  dtype=False)
            else:
                raise BadInput(
                    "Input file format not supported, BentoML cli only accepts .json "
                    "and .csv file")
        else:
            # Assuming input string is JSON format
            try:
                df = pd.read_json(cli_input,
                                  orient=orient,
                                  typ=self.typ,
                                  dtype=False)
            except ValueError as e:
                raise BadInput(
                    "Unexpected input format, BentoML DataframeHandler expects json "
                    "string as input: {}".format(e))

        if self.typ == "frame" and self.input_dtypes is not None:
            _check_dataframe_column_contains(self.input_dtypes, df)

        result = func(df)
        if parsed_args.output == 'json':
            result = api_func_result_to_json(
                result, pandas_dataframe_orient=output_orient)
        else:
            result = str(result)
        print(result)
示例#11
0
    def handle_aws_lambda_event(self, event, func):
        if event["headers"]["Content-Type"] == "application/json":
            parsed_json = json.loads(event["body"])
        else:
            raise BadInput(
                "Request content-type must be 'application/json' for this "
                "BentoService API lambda endpoint")

        result = func(parsed_json)
        json_output = api_func_result_to_json(result)
        return {"statusCode": 200, "body": json_output}
示例#12
0
    def handle_request(self, request, func):
        if request.content_type == "application/json":
            parsed_json = json.loads(request.data.decode("utf-8"))
        else:
            raise BadInput(
                "Request content-type must be 'application/json' for this "
                "BentoService API"
            )

        result = func(parsed_json)
        json_output = api_func_result_to_json(result)
        return Response(response=json_output, status=200, mimetype="application/json")
示例#13
0
    def handle_batch_request(
        self, requests: Iterable[SimpleRequest], func
    ) -> Iterable[SimpleResponse]:
        """
        TODO(hrmthw):
        1. check content type
        1. specify batch dim
        1. output str fromat
        """
        import tensorflow as tf

        bad_resp = SimpleResponse(b"Bad Input", None, 400)
        instances_list = [None] * len(requests)
        responses = [bad_resp] * len(requests)

        for i, request in enumerate(requests):
            try:
                raw_str = request[0]  # .decode("utf-8")
                parsed_json = json.loads(raw_str)
                if parsed_json.get("instances") is not None:
                    instances = parsed_json.get("instances")
                    if instances is None:
                        continue
                    instances = decode_b64_if_needed(instances)
                    if not isinstance(instances, (list, tuple)):
                        instances = [instances]
                    instances_list[i] = instances

                elif parsed_json.get("inputs"):
                    responses[i] = SimpleResponse(
                        "Column format 'inputs' not implemented", None, 501,
                    )

            except (json.exceptions.JSONDecodeError, UnicodeDecodeError):
                import traceback

                traceback.print_exc()

        merged_instances, slices = concat_list(instances_list)

        parsed_tensor = tf.constant(merged_instances)
        merged_result = func(parsed_tensor)
        merged_result = decode_tf_if_needed(merged_result)
        assert isinstance(merged_result, (list, tuple))

        results = [merged_result[s] for s in slices]

        for i, result in enumerate(results):
            result_str = api_func_result_to_json(result)
            responses[i] = SimpleResponse(result_str, dict(), 200)

        return responses
示例#14
0
    def handle_batch_request(
        self, requests: Iterable[SimpleRequest], func
    ) -> Iterable[SimpleResponse]:
        bad_resp = SimpleResponse(400, None, "Bad Input")
        instances_list = [None] * len(requests)
        responses = [bad_resp] * len(requests)
        batch_flags = [None] * len(requests)

        for i, request in enumerate(requests):
            batch_flags[i] = (
                request.formated_headers.get(
                    self._BATCH_REQUEST_HEADER.lower(),
                    "true" if self.config.get('is_batch_input') else "false",
                )
                == "true"
            )
            try:
                raw_str = request.data
                parsed_json = json.loads(raw_str)
                if not batch_flags[i]:
                    parsed_json = (parsed_json,)
                instances_list[i] = parsed_json
            except (json.JSONDecodeError, UnicodeDecodeError):
                responses[i] = SimpleResponse(400, None, "Not a valid json")
            except Exception:  # pylint: disable=broad-except
                import traceback

                err = traceback.format_exc()
                responses[i] = SimpleResponse(
                    500, None, f"Internal Server Error: {err}"
                )

        merged_instances, slices = concat_list(instances_list)
        merged_result = func(merged_instances)
        if not isinstance(merged_result, (list, tuple)) or len(merged_result) != len(
            merged_instances
        ):
            raise ValueError(
                "The return value with JsonHandler must be list of jsonable objects, "
                "and have same length as the inputs."
            )

        for i, s in enumerate(slices):
            if s is None:
                continue
            result = merged_result[s]
            if not batch_flags[i]:
                result = result[0]
            result_str = api_func_result_to_json(result)
            responses[i] = SimpleResponse(200, dict(), result_str)

        return responses
示例#15
0
    def handle_aws_lambda_event(self, event, func):
        if event["headers"].get("Content-Type", "").startswith("images/"):
            image = self.imread(base64.decodebytes(event["body"]),
                                pilmode=self.pilmode)
        else:
            raise BadInput(
                "BentoML currently doesn't support Content-Type: {content_type} for "
                "AWS Lambda".format(
                    content_type=event["headers"]["Content-Type"]))

        result = func(image)
        json_output = api_func_result_to_json(result)
        return {"statusCode": 200, "body": json_output}
示例#16
0
    def handle_request(self, request, func):
        """Handle http request that has image file/s. It will convert image into a
        ndarray for the function to consume.

        Args:
            request: incoming request object.
            func: function that will take ndarray as its arg.
            options: configuration for handling request object.
        Return:
            response object
        """
        if len(self.input_names) == 1 and len(request.files) == 1:
            # Ignore multipart form input name when LegacyImageHandler is intended
            # to accept only one image file at a time
            input_files = [file for _, file in request.files.items()]
        else:
            input_files = [
                request.files.get(form_input_name)
                for form_input_name in self.input_names
                if form_input_name in request.files
            ]

        if input_files:
            file_names = [
                secure_filename(file.filename) for file in input_files
            ]
            for file_name in file_names:
                verify_image_format_or_raise(file_name,
                                             self.accept_image_formats)
            input_streams = [
                BytesIO(input_file.read()) for input_file in input_files
            ]
        else:
            data = request.get_data()
            if data:
                input_streams = (data, )
            else:
                raise BadInput(
                    "BentoML#LegacyImageHandler unexpected HTTP request format"
                )

        input_data = tuple(
            self.imread(input_stream, pilmode=self.pilmode)
            for input_stream in input_streams)
        result = func(*input_data)
        json_output = api_func_result_to_json(result)
        return Response(response=json_output,
                        status=200,
                        mimetype="application/json")
示例#17
0
    def handle_request(self, request, func):
        """Handle http request that has one image file. It will convert image into a
        ndarray for the function to consume.

        Args:
            request: incoming request object.
            func: function that will take ndarray as its arg.
            options: configuration for handling request object.
        Return:
            response object
        """
        input_data = self._load_image_data(request)
        result = func((input_data, ))[0]
        return Response(
            response=api_func_result_to_json(result),
            status=200,
            mimetype="application/json",
        )
示例#18
0
    def handle_cli(self, args, func):
        parser = argparse.ArgumentParser()
        parser.add_argument("--input", required=True)
        parser.add_argument("-o", "--output", default="str", choices=["str", "json"])
        parsed_args = parser.parse_args(args)

        if os.path.isfile(parsed_args.input):
            with open(parsed_args.input, "r") as content_file:
                content = content_file.read()
        else:
            content = parsed_args.input

        input_json = json.loads(content)
        result = func(input_json)
        if parsed_args.output == 'json':
            result = api_func_result_to_json(result)
        else:
            result = str(result)
        print(result)
示例#19
0
    def handle_request(self, request, func):
        input_streams = []
        for filename in self.input_names:
            file = request.files.get(filename)
            if file is not None:
                file_name = secure_filename(file.filename)
                verify_image_format_or_raise(file_name,
                                             self.accept_image_formats)
                input_streams.append(BytesIO(file.read()))

        if len(input_streams) == 0:
            data = request.get_data()
            if data:
                input_streams = (data, )
            else:
                raise BadInput(
                    "BentoML#ImageHandler unexpected HTTP request: %s" %
                    request)

        input_data = []
        for input_stream in input_streams:
            data = self.imread(input_stream, pilmode=self.convert_mode)

            if self.after_open:
                data = self.after_open(data)

            data = self.fastai_vision.pil2tensor(data, np.float32)

            if self.div:
                data = data.div_(255)

            if self.cls:
                data = self.cls(data)
            else:
                data = self.fastai_vision.Image(data)
            input_data.append(data)

        result = func(*input_data)
        json_output = api_func_result_to_json(result)
        return Response(response=json_output,
                        status=200,
                        mimetype="application/json")
示例#20
0
    def _handle_raw_str(self, raw_str, output_format, func):
        import tensorflow as tf

        parsed_json = json.loads(raw_str)
        if parsed_json.get("instances") is not None:
            instances = parsed_json.get("instances")
            instances = decode_b64_if_needed(instances)
            parsed_tensor = tf.constant(instances)
            result = func(parsed_tensor)
            result = decode_tf_if_needed(result)

        elif parsed_json.get("inputs"):
            raise NotImplementedError("column format 'inputs' is not implemented")

        if output_format == "json":
            result_str = api_func_result_to_json(result)
        elif output_format == "str":
            result_str = str(result)

        return result_str
示例#21
0
    def handle_aws_lambda_event(self, event, func):
        if event["headers"]["Content-Type"] == "application/json":
            df = pd.read_json(event["body"],
                              orient=self.orient,
                              typ=self.typ,
                              dtype=False)
        elif event["headers"]["Content-Type"] == "text/csv":
            df = pd.read_csv(event["body"])
        else:
            raise BadInput(
                "Request content-type not supported, only application/json and "
                "text/csv are supported")

        if self.typ == "frame" and self.input_dtypes is not None:
            _check_dataframe_column_contains(self.input_dtypes, df)

        result = func(df)
        result = api_func_result_to_json(
            result, pandas_dataframe_orient=self.output_orient)
        return {"statusCode": 200, "body": result}
示例#22
0
    def handle_cli(self, args, func):
        parser = argparse.ArgumentParser()
        parser.add_argument("--input", required=True)
        parser.add_argument("-o",
                            "--output",
                            default="str",
                            choices=["str", "json"])
        parsed_args = parser.parse_args(args)
        file_path = parsed_args.input

        verify_image_format_or_raise(file_path, self.accept_image_formats)
        if not os.path.isabs(file_path):
            file_path = os.path.abspath(file_path)

        image_array = self.imread(file_path, pilmode=self.pilmode)

        result = func(image_array)
        if parsed_args.output == "json":
            result = api_func_result_to_json(result)
        else:
            result = str(result)
        print(result)
示例#23
0
    def handle_batch_request(self, requests: Iterable[SimpleRequest],
                             func) -> Iterable[SimpleResponse]:
        bad_resp = SimpleResponse(400, None, "Bad Input")
        instances_list = [None] * len(requests)
        responses = [bad_resp] * len(requests)

        for i, request in enumerate(requests):
            try:
                raw_str = request.data
                parsed_json = json.loads(raw_str)
                instances_list[i] = parsed_json
            except (json.JSONDecodeError, UnicodeDecodeError):
                responses[i] = SimpleResponse(400, None,
                                              "not a valid json input")
            except Exception:  # pylint: disable=broad-except
                responses[i] = SimpleResponse(500, None,
                                              "internal server error")
                import traceback

                traceback.print_exc()

        merged_instances, slices = concat_list(instances_list)
        merged_result = func(merged_instances)
        if not isinstance(
                merged_result,
            (list, tuple)) or len(merged_result) != len(merged_instances):
            raise ValueError(
                "The return value with JsonHandler must be list of jsonable objects, "
                "and have same length as the inputs.")

        for i, s in enumerate(slices):
            if s is None:
                continue
            result_str = api_func_result_to_json(merged_result[s])
            responses[i] = SimpleResponse(200, dict(), result_str)

        return responses
示例#24
0
    def handle_batch_request(
        self, requests: Iterable[SimpleRequest], func
    ) -> Iterable[SimpleResponse]:
        """
        TODO(hrmthw):
        1. specify batch dim
        1. output str fromat
        """
        import tensorflow as tf

        bad_resp = SimpleResponse(400, None, "input format error")
        instances_list = [None] * len(requests)
        responses = [bad_resp] * len(requests)
        batch_flags = [None] * len(requests)

        for i, request in enumerate(requests):
            try:
                raw_str = request.data
                batch_flags[i] = (
                    request.formated_headers.get(
                        self._BATCH_REQUEST_HEADER.lower(),
                        "true" if self.config.get("is_batch_input") else "false",
                    )
                    == "true"
                )
                parsed_json = json.loads(raw_str)
                if parsed_json.get("instances") is not None:
                    instances = parsed_json.get("instances")
                    if instances is None:
                        continue
                    instances = decode_b64_if_needed(instances)
                    if not batch_flags[i]:
                        instances = (instances,)
                    instances_list[i] = instances

                elif parsed_json.get("inputs"):
                    responses[i] = SimpleResponse(
                        501, None, "Column format 'inputs' not implemented"
                    )

            except (json.JSONDecodeError, UnicodeDecodeError):
                pass
            except Exception:  # pylint: disable=broad-except
                import traceback

                err = traceback.format_exc()
                responses[i] = SimpleResponse(
                    500, None, f"Internal Server Error: {err}"
                )

        merged_instances, slices = concat_list(instances_list)

        parsed_tensor = tf.constant(merged_instances)
        merged_result = func(parsed_tensor)
        merged_result = decode_tf_if_needed(merged_result)
        assert isinstance(merged_result, (list, tuple))

        for i, s in enumerate(slices):
            if s is None:
                continue
            result = merged_result[s]
            if not batch_flags[i]:
                result = result[0]
            result_str = api_func_result_to_json(result)
            responses[i] = SimpleResponse(200, dict(), result_str)

        return responses