예제 #1
0
    def from_inference_job(  # pylint: disable=arguments-differ
            self,
            input_=None,
            input_file=None,
            **extra_args) -> Iterator[InferenceTask[str]]:
        '''
        Generate InferenceTask from calling bentom_svc.run(input_=None, input_file=None)

        Parameters
        ----------
        input_ : str
            The input value

        input_file : str
            The URI/path of the input file

        extra_args : dict
            Additional parameters

        '''
        if input_file is not None:
            for d in input_file:
                uri = pathlib.Path(d).absolute().as_uri()
                yield InferenceTask(inference_job_args=extra_args,
                                    data=FileLike(uri=uri))
        else:
            for d in input_:
                yield InferenceTask(
                    inference_job_args=extra_args,
                    data=FileLike(bytes_=d.encode()),
                )
예제 #2
0
def parse_cli_input(cli_args: Iterable[str]) -> Iterator[FileLike]:
    '''
    Parse CLI args and iter each input in bytes.

    >>> parse_cli_input('--input {"input":1} {"input":2}'.split(' '))
    OR
    >>> parse_cli_inputs("--input-file 1.jpg 2.jpg 3.jpg".split(' '))
    '''
    parser = argparse.ArgumentParser()
    input_g = parser.add_mutually_exclusive_group(required=True)
    input_g.add_argument('--input', nargs="+", type=str)
    input_g.add_argument('--input-file', nargs="+")

    parsed_args, _ = parser.parse_known_args(list(cli_args))

    inputs = tuple(parsed_args.input if parsed_args.input_file is None else
                   parsed_args.input_file)
    is_file = parsed_args.input_file is not None
    if is_file:
        for input_ in inputs:
            uri = pathlib.Path(input_).absolute().as_uri()
            yield FileLike(uri=uri)

    else:
        for input_ in inputs:
            rv = FileLike(bytes_=input_.encode())
            yield rv

    return _
예제 #3
0
 def from_aws_lambda_event(self, event: AwsLambdaEvent) -> InferenceTask[FileLike]:
     headers = HTTPHeaders.from_dict(event.get('headers', {}))
     if headers.content_type == "text/csv":
         f = FileLike(bytes_=event["body"].encode(), name="input.csv")
     else:
         # Optimistically assuming Content-Type to be "application/json"
         f = FileLike(bytes_=event["body"].encode(), name="input.json")
     return InferenceTask(aws_lambda_event=event, data=f,)
예제 #4
0
 def from_inference_job(  # pylint: disable=arguments-differ
         self,
         input_=None,
         input_file=None,
         **extra_args) -> Iterator[InferenceTask[FileLike]]:
     if input_file is not None:
         for ds in zip(*input_file):
             uris = (pathlib.Path(d).absolute().as_uri() for d in ds)
             fs = tuple(FileLike(uri=uri) for uri in uris)
             yield InferenceTask(data=fs, inference_job_args=extra_args)
     else:
         for ds in zip(*input_):
             fs = tuple(FileLike(bytes_=d.encode()) for d in ds)
             yield InferenceTask(data=fs, inference_job_args=extra_args)
예제 #5
0
 def from_http_request(self, req: HTTPRequest) -> InferenceTask[FileLike]:
     if req.headers.content_type == 'multipart/form-data':
         _, _, files = HTTPRequest.parse_form_data(req)
         if len(files) != 1:
             task = InferenceTask(data=None)
             task.discard(
                 http_status=400,
                 err_msg=
                 f"BentoML#{self.__class__.__name__} requires one and at"
                 " least one file at a time, if you just upgraded from"
                 " bentoml 0.7, you may need to use MultiFileAdapter instead",
             )
         else:
             input_file = next(iter(files.values()))
             task = InferenceTask(http_headers=req.headers, data=input_file)
     elif req.body:
         task = InferenceTask(
             http_headers=req.headers,
             data=FileLike(bytes_=req.body),
         )
     else:
         task = InferenceTask(data=None)
         task.discard(
             http_status=400,
             err_msg=
             f'BentoML#{self.__class__.__name__} unexpected HTTP request'
             ' format',
         )
     return task
예제 #6
0
    def parse(self, args: Sequence[str]) -> Iterator[Tuple[FileLike]]:
        try:
            parsed, _ = self.parser.parse_known_args(args)
        except SystemExit:
            parsed = None

        inputs = tuple(getattr(parsed, name, None) for name in self.arg_names)
        file_inputs = tuple(
            getattr(parsed, name, None) for name in self.file_arg_names)

        if any(inputs) and any(file_inputs):
            exit_cli('''
                Conflict arguments:
                --input* and --input-file* should not be provided at same time
                ''')
        if not all(inputs) and not all(file_inputs):
            exit_cli(f'''
                Insufficient arguments:
                ({' '.join(self.arg_strs)}) or
                ({' '.join(self.file_arg_strs)})
                are required
                ''')

        if all(inputs):
            if functools.reduce(lambda i, j: len(i) == len(j), inputs):
                for input_ in zip(*inputs):
                    yield tuple(FileLike(bytes_=i.encode()) for i in input_)
            else:
                exit_cli(f'''
                    Arguments length mismatch:
                    Each ({' '.join(self.arg_strs)})
                    should have same amount of inputs
                    ''')

        if all(file_inputs):
            if functools.reduce(lambda i, j: len(i) == len(j), file_inputs):
                for input_ in zip(*file_inputs):
                    uris = (pathlib.Path(fpath).absolute().as_uri()
                            for fpath in input_)
                    yield tuple(FileLike(uri=uri) for uri in uris)
            else:
                exit_cli(f'''
                    Arguments length mismatch:
                    Each ({' '.join(self.file_arg_strs)})
                    should have same amount of inputs
                    ''')
예제 #7
0
    def from_inference_job(  # pylint: disable=arguments-differ
        self,
        input_=None,
        input_file=None,
        **extra_args,
    ) -> Iterator[InferenceTask[str]]:
        '''
        Generate InferenceTask from calling bentom_svc.run(input_=None, input_file=None)

        Parameters
        ----------
        input_ : str
            The input value

        input_file : str
            The URI/path of the input file

        extra_args : dict
            Additional parameters
        '''
        if input_file is not None:
            for d in input_file:
                uri = pathlib.Path(d).absolute().as_uri()
                bytes_ = FileLike(uri=uri).read()
                try:
                    charset = chardet.detect(bytes_)['encoding'] or "utf-8"
                    yield InferenceTask(
                        inference_job_args=extra_args,
                        data=bytes_.decode(charset),
                    )
                except UnicodeDecodeError:
                    yield InferenceTask().discard(
                        http_status=400,
                        err_msg=f"{self.__class__.__name__}: "
                        f"Try decoding with {charset} but failed with DecodeError.",
                    )
                except LookupError:
                    yield InferenceTask().discard(
                        http_status=400,
                        err_msg=f"{self.__class__.__name__}: "
                        f"Unsupported charset {charset}",
                    )
        else:
            for d in input_:
                yield InferenceTask(inference_job_args=extra_args, data=d)
예제 #8
0
 def from_aws_lambda_event(self, event):
     if event["headers"].get("Content-Type", "").startswith("images/"):
         img_bytes = base64.b64decode(event["body"])
         _, ext = event["headers"]["Content-Type"].split('/')
         f = FileLike(bytes_=img_bytes, name=f"default.{ext}")
         task = InferenceTask(data=(f, ))
     else:
         task = InferenceTask(data=None)
         task.discard(
             http_status=400,
             err_msg="BentoML currently doesn't support Content-Type: "
             "{content_type} for AWS Lambda".format(
                 content_type=event["headers"]["Content-Type"]),
         )
     return task
예제 #9
0
 def from_http_request(self, req: HTTPRequest) -> MultiImgTask:
     if len(self.input_names) == 1:
         # broad parsing while single input
         if req.headers.content_type == 'multipart/form-data':
             _, _, files = HTTPRequest.parse_form_data(req)
             if not any(files):
                 task = InferenceTask(data=None)
                 task.discard(
                     http_status=400,
                     err_msg=
                     f"BentoML#{self.__class__.__name__} requires inputs"
                     f"fields {self.input_names}",
                 )
             else:
                 f = next(iter(files.values()))
                 task = InferenceTask(
                     http_headers=req.headers,
                     data=(f, ),
                 )
         elif req.headers.content_type.startswith('image/'):
             # for images/*
             _, ext = req.headers.content_type.split('/')
             task = InferenceTask(
                 http_headers=req.headers,
                 data=(FileLike(bytes_=req.body, name=f'default.{ext}'), ),
             )
         else:
             task = InferenceTask(
                 http_headers=req.headers,
                 data=(FileLike(bytes_=req.body), ),
             )
     elif req.headers.content_type == 'multipart/form-data':
         _, _, files = HTTPRequest.parse_form_data(req)
         files = tuple(files.get(k) for k in self.input_names)
         if not any(files):
             task = InferenceTask(data=None)
             task.discard(
                 http_status=400,
                 err_msg=f"BentoML#{self.__class__.__name__} requires inputs "
                 f"fields {self.input_names}",
             )
         elif not all(files) and not self.allow_none:
             task = InferenceTask(data=None)
             task.discard(
                 http_status=400,
                 err_msg=f"BentoML#{self.__class__.__name__} requires inputs "
                 f"fields {self.input_names}",
             )
         else:
             task = InferenceTask(
                 http_headers=req.headers,
                 data=files,
             )
     else:
         task = InferenceTask(data=None)
         task.discard(
             http_status=400,
             err_msg=
             f"BentoML#{self.__class__.__name__} with multiple inputs "
             "accepts requests with Content-Type: multipart/form-data only",
         )
     return task
예제 #10
0
 def from_aws_lambda_event(
         self, event: AwsLambdaEvent) -> InferenceTask[FileLike]:
     f = FileLike(bytes_=base64.decodebytes(event.get('body', "")))
     return InferenceTask(aws_lambda_event=event, data=f)