Skip to content

Commit

Permalink
fix: allow legacy runnable methods when calling API (#4573)
Browse files Browse the repository at this point in the history
* fix: allow legacy runnable methods when calling API

Signed-off-by: Frost Ming <me@frostming.com>

* fix: output content type

Signed-off-by: Frost Ming <me@frostming.com>

* fix compatibility with python 3.9

Signed-off-by: Frost Ming <me@frostming.com>
  • Loading branch information
frostming committed Mar 14, 2024
1 parent 4be4030 commit 9b04536
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 15 deletions.
3 changes: 1 addition & 2 deletions src/_bentoml_impl/client/proxy.py
Expand Up @@ -7,7 +7,6 @@
import typing as t

from _bentoml_sdk import Service
from _bentoml_sdk.method import APIMethod
from bentoml.exceptions import BentoMLException

from .http import AbstractClient
Expand Down Expand Up @@ -68,7 +67,7 @@ def as_service(self) -> T:

def call(self, __name: str, /, *args: t.Any, **kwargs: t.Any) -> t.Any:
original_func = getattr(self._inner, __name)
if not isinstance(original_func, APIMethod):
if not hasattr(original_func, "func"):
raise BentoMLException(f"calling non-api method {__name} is not allowed")
original_func = original_func.func
while isinstance(original_func, functools.partial):
Expand Down
24 changes: 12 additions & 12 deletions src/_bentoml_sdk/io_models.py
Expand Up @@ -141,8 +141,6 @@ async def to_http_response(cls, obj: t.Any, serde: Serde) -> Response:

from _bentoml_impl.serde import JSONSerde

structured_media_type = cls.media_type or serde.media_type

if inspect.isasyncgen(obj):

async def async_stream() -> t.AsyncGenerator[str | bytes, None]:
Expand Down Expand Up @@ -173,7 +171,7 @@ def content_stream() -> t.Generator[str | bytes, None, None]:
)
return Response(
content=serde.serialize_model(t.cast(IODescriptor, obj)),
media_type=structured_media_type,
media_type=cls.mime_type(),
)
else:
if is_file_type(type(obj)) and isinstance(serde, JSONSerde):
Expand Down Expand Up @@ -208,7 +206,7 @@ def content_stream() -> t.Generator[str | bytes, None, None]:
return Response(content=rendered, media_type=cls.mime_type())
else:
return Response(
content=serde.serialize_model(ins), media_type=structured_media_type
content=serde.serialize_model(ins), media_type=cls.mime_type()
)


Expand Down Expand Up @@ -307,23 +305,25 @@ def from_output(cls, func: t.Callable[..., t.Any]) -> type[IODescriptor]:
) from e


def ensure_io_descriptor(output_type: type) -> type[IODescriptor]:
def ensure_io_descriptor(typ_: type) -> type[IODescriptor]:
from pydantic._internal._utils import lenient_issubclass

if inspect.isclass(output_type) and lenient_issubclass(output_type, BaseModel):
if not issubclass(output_type, IODescriptor):
type_name = getattr(typ_, "__name__", "")

if inspect.isclass(typ_) and lenient_issubclass(typ_, BaseModel):
if not issubclass(typ_, IODescriptor):
return t.cast(
t.Type[IODescriptor],
create_model(
f"{output_type.__name__}IODescriptor",
__base__=(IOMixin, output_type),
f"{type_name}IODescriptor",
__base__=(IOMixin, typ_),
),
)
return output_type
return typ_
return t.cast(
t.Type[IODescriptor],
create_model(
f"{output_type.__name__}IODescriptor",
__base__=(IOMixin, RootModel[output_type]),
f"{type_name}IODescriptor",
__base__=(IOMixin, RootModel[typ_]),
),
)
5 changes: 4 additions & 1 deletion src/bentoml/_internal/runner/runnable.py
Expand Up @@ -140,9 +140,12 @@ class RunnableMethod(t.Generic[T, P, R]):
config: RunnableMethodConfig
_bentoml_runnable_method: None = None

def __get__(self, obj: T, _: t.Type[T] | None = None) -> t.Callable[P, R]:
def __get__(self, obj: T | None, _: t.Type[T] | None = None) -> t.Callable[P, R]:
from ..utils import is_async_callable

if obj is None:
return self

if is_async_callable(self.func):

async def method(*args: P.args, **kwargs: P.kwargs) -> R:
Expand Down

0 comments on commit 9b04536

Please sign in to comment.