From 9d9d058e702605e8f6dbeef0c75e05251f2f6449 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Tue, 15 Oct 2024 17:03:54 +0800 Subject: [PATCH] fix: emit deprecation warings for legacy APIs Signed-off-by: Frost Ming --- src/_bentoml_impl/frameworks/catboost.py | 4 +- src/_bentoml_impl/frameworks/importer.py | 5 ++ src/_bentoml_impl/frameworks/lightgbm.py | 4 +- src/_bentoml_impl/frameworks/mlflow.py | 4 +- src/_bentoml_impl/frameworks/sklearn.py | 5 +- src/_bentoml_impl/frameworks/xgboost.py | 4 +- src/_bentoml_sdk/service/factory.py | 2 + src/bentoml/__init__.py | 66 ++++++++++++++++++---- src/bentoml/_internal/runner/runner.py | 2 + src/bentoml/_internal/runner/strategy.py | 2 + src/bentoml/_internal/service/service.py | 2 + src/bentoml/_internal/utils/__init__.py | 44 +++++++++++++++ src/bentoml/_internal/utils/lazy_loader.py | 4 +- src/bentoml/io.py | 9 +++ 14 files changed, 133 insertions(+), 24 deletions(-) diff --git a/src/_bentoml_impl/frameworks/catboost.py b/src/_bentoml_impl/frameworks/catboost.py index 8a82022ade3..513d65cbc0b 100644 --- a/src/_bentoml_impl/frameworks/catboost.py +++ b/src/_bentoml_impl/frameworks/catboost.py @@ -8,11 +8,11 @@ import attr import numpy as np -from typing_extensions import deprecated import bentoml from bentoml import Tag from bentoml._internal.models.model import ModelContext +from bentoml._internal.utils import deprecated from bentoml._internal.utils.pkg import get_pkg_version from bentoml.exceptions import BentoMLException from bentoml.exceptions import InvalidArgument @@ -199,7 +199,7 @@ def save_model( return bento_model -@deprecated("`get_runnable` is a legacy API, use `get_service` instead.") +@deprecated(suggestion="Use `get_service` instead.") def get_runnable(bento_model: bentoml.Model) -> t.Type[bentoml.Runnable]: """ Private API: use :obj:`~bentoml.Model.to_runnable` instead. diff --git a/src/_bentoml_impl/frameworks/importer.py b/src/_bentoml_impl/frameworks/importer.py index bd405a96102..472917349d3 100644 --- a/src/_bentoml_impl/frameworks/importer.py +++ b/src/_bentoml_impl/frameworks/importer.py @@ -4,6 +4,8 @@ from importlib.abc import MetaPathFinder from typing import TYPE_CHECKING +from bentoml._internal.utils import warn_deprecated + if TYPE_CHECKING: from importlib.machinery import ModuleSpec from types import ModuleType @@ -21,6 +23,9 @@ def find_spec( return None spec = importlib.util.find_spec(f"_bentoml_impl.frameworks.{framework}") if spec is None: + warn_deprecated( + f"`bentoml.{framework}` is deprecated since BentoML v1.4 and will be removed in a future version.", + ) spec = importlib.util.find_spec(f"bentoml._internal.frameworks.{framework}") return spec diff --git a/src/_bentoml_impl/frameworks/lightgbm.py b/src/_bentoml_impl/frameworks/lightgbm.py index 6774cd8da28..bbee54ded69 100644 --- a/src/_bentoml_impl/frameworks/lightgbm.py +++ b/src/_bentoml_impl/frameworks/lightgbm.py @@ -6,11 +6,11 @@ from typing import TYPE_CHECKING import numpy as np -from typing_extensions import deprecated import bentoml from bentoml import Tag from bentoml._internal.models.model import ModelContext +from bentoml._internal.utils import deprecated from bentoml._internal.utils.pkg import get_pkg_version from bentoml.exceptions import InvalidArgument from bentoml.exceptions import MissingDependencyException @@ -203,7 +203,7 @@ def save_model( return bento_model -@deprecated("`get_runnable` is a legacy API, use `get_service` instead.") +@deprecated(suggestion="Use `get_service` instead.") def get_runnable(bento_model: bentoml.Model) -> t.Type[bentoml.Runnable]: """ Private API: use :obj:`~bentoml.Model.to_runnable` instead. diff --git a/src/_bentoml_impl/frameworks/mlflow.py b/src/_bentoml_impl/frameworks/mlflow.py index 6851b8661cb..dd4f0f6aa73 100644 --- a/src/_bentoml_impl/frameworks/mlflow.py +++ b/src/_bentoml_impl/frameworks/mlflow.py @@ -8,10 +8,10 @@ from typing import TYPE_CHECKING import numpy as np -from typing_extensions import deprecated import bentoml from bentoml import Tag +from bentoml._internal.utils import deprecated from bentoml.exceptions import BentoMLException from bentoml.exceptions import MissingDependencyException from bentoml.exceptions import NotFound @@ -212,7 +212,7 @@ def import_model( return bento_model -@deprecated("`get_runnable` is a legacy API, use `get_service` instead.") +@deprecated(suggestion="Use `get_service` instead.") def get_runnable(bento_model: bentoml.Model) -> t.Type[bentoml.Runnable]: """ Private API: use :obj:`~bentoml.Model.to_runnable` instead. diff --git a/src/_bentoml_impl/frameworks/sklearn.py b/src/_bentoml_impl/frameworks/sklearn.py index 92bb71f1ea7..810b983787a 100644 --- a/src/_bentoml_impl/frameworks/sklearn.py +++ b/src/_bentoml_impl/frameworks/sklearn.py @@ -5,11 +5,10 @@ from types import ModuleType from typing import TYPE_CHECKING -from typing_extensions import deprecated - import bentoml from bentoml import Tag from bentoml._internal.types import LazyType +from bentoml._internal.utils import deprecated from bentoml._internal.utils.pkg import get_pkg_version from bentoml.exceptions import MissingDependencyException from bentoml.exceptions import NotFound @@ -159,7 +158,7 @@ def save_model( return bento_model -@deprecated("`get_runnable` is a legacy API, use `get_service` instead.") +@deprecated(suggestion="Use `get_service` instead.") def get_runnable(bento_model: Model): """ Private API: use :obj:`~bentoml.Model.to_runnable` instead. diff --git a/src/_bentoml_impl/frameworks/xgboost.py b/src/_bentoml_impl/frameworks/xgboost.py index 2944f2d9bf3..391f5434003 100644 --- a/src/_bentoml_impl/frameworks/xgboost.py +++ b/src/_bentoml_impl/frameworks/xgboost.py @@ -8,11 +8,11 @@ import attr import numpy as np -from typing_extensions import deprecated import bentoml from bentoml import Tag from bentoml._internal.models.model import ModelContext +from bentoml._internal.utils import deprecated from bentoml._internal.utils.pkg import get_pkg_version from bentoml.exceptions import BentoMLException from bentoml.exceptions import InvalidArgument @@ -216,7 +216,7 @@ def save_model( return bento_model -@deprecated("`get_runnable` is a legacy API, use `get_service` instead.") +@deprecated(suggestion="Use `get_service` instead.") def get_runnable(bento_model: bentoml.Model) -> t.Type[bentoml.Runnable]: """ Private API: use :obj:`~bentoml.Model.to_runnable` instead. diff --git a/src/_bentoml_sdk/service/factory.py b/src/_bentoml_sdk/service/factory.py index 9e091eac3fb..f324fc062e2 100644 --- a/src/_bentoml_sdk/service/factory.py +++ b/src/_bentoml_sdk/service/factory.py @@ -22,6 +22,7 @@ from bentoml._internal.configuration.containers import BentoMLContainer from bentoml._internal.context import ServiceContext from bentoml._internal.models import Model as StoredModel +from bentoml._internal.utils import deprecated from bentoml._internal.utils import dict_filter_none from bentoml.exceptions import BentoMLConfigException from bentoml.exceptions import BentoMLException @@ -422,6 +423,7 @@ def decorator(inner: type[T]) -> Service[T]: return decorator(inner) if inner is not None else decorator +@deprecated() def runner_service(runner: Runner, **kwargs: Unpack[Config]) -> Service[t.Any]: """Make a service from a legacy Runner""" if not isinstance(runner, Runner): # type: ignore diff --git a/src/bentoml/__init__.py b/src/bentoml/__init__.py index f4ac94698c9..3613c6e9b8d 100644 --- a/src/bentoml/__init__.py +++ b/src/bentoml/__init__.py @@ -138,47 +138,89 @@ ) mlflow = _LazyLoader("bentoml.mlflow", globals(), "_bentoml_impl.frameworks.mlflow") detectron = _LazyLoader( - "bentoml.detectron", globals(), "bentoml._internal.frameworks.detectron" + "bentoml.detectron", + globals(), + "bentoml._internal.frameworks.detectron", + warning="`bentoml.detectron` is deprecated since v1.4 and will be removed in a future version.", ) diffusers = _LazyLoader( - "bentoml.diffusers", globals(), "bentoml._internal.frameworks.diffusers" + "bentoml.diffusers", + globals(), + "bentoml._internal.frameworks.diffusers", + warning="`bentoml.diffusers` is deprecated since v1.4 and will be removed in a future version.", ) diffusers_simple = _LazyLoader( - "bentoml.diffusers_simple", globals(), "bentoml.diffusers_simple" + "bentoml.diffusers_simple", + globals(), + "bentoml.diffusers_simple", + warning="`bentoml.diffusers_simple` is deprecated since v1.4 and will be removed in a future version.", ) easyocr = _LazyLoader( - "bentoml.easyocr", globals(), "bentoml._internal.frameworks.easyocr" + "bentoml.easyocr", + globals(), + "bentoml._internal.frameworks.easyocr", + warning="`bentoml.easyocr` is deprecated since v1.4 and will be removed in a future version.", + ) + flax = _LazyLoader( + "bentoml.flax", + globals(), + "bentoml._internal.frameworks.flax", + warning="`bentoml.flax` is deprecated since v1.4 and will be removed in a future version.", ) - flax = _LazyLoader("bentoml.flax", globals(), "bentoml._internal.frameworks.flax") fastai = _LazyLoader( - "bentoml.fastai", globals(), "bentoml._internal.frameworks.fastai" + "bentoml.fastai", + globals(), + "bentoml._internal.frameworks.fastai", + warning="`bentoml.fastai` is deprecated since v1.4 and will be removed in a future version.", ) - onnx = _LazyLoader("bentoml.onnx", globals(), "bentoml._internal.frameworks.onnx") + onnx = _LazyLoader( + "bentoml.onnx", + globals(), + "bentoml._internal.frameworks.onnx", + warning="`bentoml.onnx` is deprecated since v1.4 and will be removed in a future version.", + ) keras = _LazyLoader( - "bentoml.keras", globals(), "bentoml._internal.frameworks.keras" + "bentoml.keras", + globals(), + "bentoml._internal.frameworks.keras", + warning="`bentoml.keras` is deprecated since v1.4 and will be removed in a future version.", ) pytorch = _LazyLoader( - "bentoml.pytorch", globals(), "bentoml._internal.frameworks.pytorch" + "bentoml.pytorch", + globals(), + "bentoml._internal.frameworks.pytorch", + warning="`bentoml.pytorch` is deprecated since v1.4 and will be removed in a future version.", ) pytorch_lightning = _LazyLoader( "bentoml.pytorch_lightning", globals(), "bentoml._internal.frameworks.pytorch_lightning", + warning="`bentoml.pytorch_lightning` is deprecated since v1.4 and will be removed in a future version.", ) picklable_model = _LazyLoader( "bentoml.picklable_model", globals(), "bentoml._internal.frameworks.picklable_model", + warning="`bentoml.picklable_model` is deprecated since v1.4 and will be removed in a future version.", ) tensorflow = _LazyLoader( - "bentoml.tensorflow", globals(), "bentoml._internal.frameworks.tensorflow" + "bentoml.tensorflow", + globals(), + "bentoml._internal.frameworks.tensorflow", + warning="`bentoml.tensorflow` is deprecated since v1.4 and will be removed in a future version.", ) torchscript = _LazyLoader( - "bentoml.torchscript", globals(), "bentoml._internal.frameworks.torchscript" + "bentoml.torchscript", + globals(), + "bentoml._internal.frameworks.torchscript", + warning="`bentoml.torchscript` is deprecated since v1.4 and will be removed in a future version.", ) transformers = _LazyLoader( - "bentoml.transformers", globals(), "bentoml._internal.frameworks.transformers" + "bentoml.transformers", + globals(), + "bentoml._internal.frameworks.transformers", + warning="`bentoml.transformers` is deprecated since v1.4 and will be removed in a future version.", ) # Integrations diff --git a/src/bentoml/_internal/runner/runner.py b/src/bentoml/_internal/runner/runner.py index a9e64e74c97..bd4388e7893 100644 --- a/src/bentoml/_internal/runner/runner.py +++ b/src/bentoml/_internal/runner/runner.py @@ -13,6 +13,7 @@ from ..configuration.containers import BentoMLContainer from ..models.model import Model from ..tag import validate_tag_str +from ..utils import deprecated from ..utils import first_not_none from .runnable import Runnable from .runner_handle import DummyRunnerHandle @@ -114,6 +115,7 @@ def init_client( """ +@deprecated(suggestion="Please upgrade to new style services.") @attr.define(slots=False, frozen=True, eq=False, init=False) class Runner(AbstractRunner): if t.TYPE_CHECKING: diff --git a/src/bentoml/_internal/runner/strategy.py b/src/bentoml/_internal/runner/strategy.py index 7d44278af92..6b26e152a29 100644 --- a/src/bentoml/_internal/runner/strategy.py +++ b/src/bentoml/_internal/runner/strategy.py @@ -7,6 +7,7 @@ from ..resource import get_resource from ..resource import system_resources +from ..utils import deprecated if t.TYPE_CHECKING: from .runnable import Runnable @@ -14,6 +15,7 @@ logger = logging.getLogger(__name__) +@deprecated() class Strategy(abc.ABC): @classmethod @abc.abstractmethod diff --git a/src/bentoml/_internal/service/service.py b/src/bentoml/_internal/service/service.py index a92d2e6554f..4332a592acb 100644 --- a/src/bentoml/_internal/service/service.py +++ b/src/bentoml/_internal/service/service.py @@ -23,6 +23,7 @@ from ..runner.runner import AbstractRunner from ..runner.runner import Runner from ..tag import Tag +from ..utils import deprecated from ..utils import first_not_none from .inference_api import InferenceAPI @@ -78,6 +79,7 @@ def get_valid_service_name(user_provided_svc_name: str) -> str: return lower_name +@deprecated("bentoml.Service", suggestion="Please upgrade to @bentoml.service().") @attr.define(frozen=False, init=False) class Service: """The service definition is the manifestation of the Service Oriented Architecture diff --git a/src/bentoml/_internal/utils/__init__.py b/src/bentoml/_internal/utils/__init__.py index afe946507c5..a45c8a8a568 100644 --- a/src/bentoml/_internal/utils/__init__.py +++ b/src/bentoml/_internal/utils/__init__.py @@ -10,6 +10,7 @@ import re import socket import typing as t +import warnings from datetime import date from datetime import datetime from datetime import time @@ -588,3 +589,46 @@ def wrapper(app: Starlette) -> T: return func() return wrapper + + +class BentoMLDeprecationWarning(DeprecationWarning): + pass + + +warnings.simplefilter("default", BentoMLDeprecationWarning) + + +def warn_deprecated(message: str, stacklevel: int = 2) -> None: + warnings.warn( + message, category=BentoMLDeprecationWarning, stacklevel=stacklevel + 1 + ) + + +def deprecated( + name: str = "", deprecated_since: str = "1.4", suggestion: str = "" +) -> t.Callable[[t.Callable[P, T]], t.Callable[P, T]]: + def decorator(func: t.Callable[P, T]) -> t.Callable[P, T]: + obj_name = name or func.__name__ + + class _DeprecatedMixin: + def __init__(self, *args: P.args, **kwargs: P.kwargs) -> None: + warn_deprecated( + f"`{obj_name}` is deprecated since BentoML v{deprecated_since} and will be removed in a future version." + + (f" {suggestion}" if suggestion else "") + ) + super().__init__(*args, **kwargs) + + if inspect.isclass(func): + return type(func.__name__, (_DeprecatedMixin, func), {}) + + @functools.wraps(func) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: + warn_deprecated( + f"`{obj_name}` is deprecated since BentoML v{deprecated_since} and will be removed in a future version." + + (f" {suggestion}" if suggestion else "") + ) + return func(*args, **kwargs) + + return wrapper + + return decorator diff --git a/src/bentoml/_internal/utils/lazy_loader.py b/src/bentoml/_internal/utils/lazy_loader.py index 3ff100e5e57..a55301951c1 100644 --- a/src/bentoml/_internal/utils/lazy_loader.py +++ b/src/bentoml/_internal/utils/lazy_loader.py @@ -43,6 +43,8 @@ def __init__( def _load(self) -> types.ModuleType: """Load the module and insert it into the parent's globals.""" + from . import warn_deprecated + # Import the target module and insert it into the parent's namespace try: module = importlib.import_module(self.__name__) @@ -54,7 +56,7 @@ def _load(self) -> types.ModuleType: # Emit a warning if one was specified if self._warning: - logger.warning(self._warning) + warn_deprecated(self._warning, stacklevel=3) # Make sure to only warn once. self._warning = None diff --git a/src/bentoml/io.py b/src/bentoml/io.py index 1157c9f2428..cabe50cf4c2 100644 --- a/src/bentoml/io.py +++ b/src/bentoml/io.py @@ -1,5 +1,14 @@ +# ruff: noqa: E402 + from __future__ import annotations +from bentoml._internal.utils import warn_deprecated + +warn_deprecated( + "`bentoml.io` is deprecated since BentoML v1.4 and will be removed in a future version. Please upgrade to new style IO types instead.", + stacklevel=1, +) + from ._internal.io_descriptors import from_spec from ._internal.io_descriptors.base import IODescriptor from ._internal.io_descriptors.file import File