Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support bentoml serve without service name #5208

Merged
merged 2 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions src/_bentoml_impl/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ def normalize_identifier(
if path.joinpath(filename).is_file()
)
bento_path = pathlib.Path(working_dir) if working_dir is not None else path
elif path.joinpath("service.py").is_file():
return "service", pathlib.Path(
working_dir
) if working_dir is not None else path
else:
raise ValueError(f"found a path but not a bento: {service_identifier}")

Expand Down Expand Up @@ -133,7 +137,7 @@ def import_service(
bento_path = bento_path.absolute()

# patch python path if needed
if bento_path != pathlib.Path(".").absolute():
if str(bento_path) not in sys.path:
# a project
extra_python_path = str(bento_path)
sys.path.insert(0, extra_python_path)
Expand Down Expand Up @@ -170,14 +174,23 @@ def import_service(
model_aliases = build_config.model_aliases
break
BentoMLContainer.model_aliases.set(model_aliases)
from bentoml.exceptions import ImportServiceError

try:
module_name, _, attrs_str = service_identifier.partition(":")

assert module_name and attrs_str, (
f'Invalid import target "{service_identifier}", must format as "<module>:<attribute>"'
)
module_name, has_attr, attrs_str = service_identifier.partition(":")
module = importlib.import_module(module_name)
if not has_attr:
all_services = {o for o in vars(module).values() if isinstance(o, Service)}
for svc in list(all_services):
for dep in svc.dependencies.values():
if dep.on is not None:
all_services.discard(dep.on)
if not all_services:
raise ImportServiceError("No service found in the module")
if len(all_services) > 1:
raise ImportServiceError("Multiple root services found in the module")
return all_services.pop()

root_service_name, _, depend_path = attrs_str.partition(".")
root_service = t.cast("Service[t.Any]", getattr(module, root_service_name))

Expand Down Expand Up @@ -206,8 +219,6 @@ def import_service(
if original_path is not None:
os.chdir(original_path)

from bentoml.exceptions import ImportServiceError

message = f'Failed to import service "{service_identifier}": {e}, sys.path: {sys_path}, cwd: {pathlib.Path.cwd()}'
if (
isinstance(e, ImportError)
Expand Down
78 changes: 25 additions & 53 deletions src/bentoml/_internal/cloud/deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,33 +94,29 @@ def verify(self, create: bool = True):

from .secret import SecretAPI

deploy_by_param = (
self.name
or self.bento
or self.cluster
or self.access_authorization
or self.scaling_min
or self.scaling_max
or self.instance_type
or self.strategy
or self.envs
or self.secrets
or self.extras
)

if (
self.config_dict
and self.config_file
or self.config_dict
and deploy_by_param
or self.config_file
and deploy_by_param
):
raise BentoMLException(
"Configure a deployment can only use one of the following: config_dict, config_file, or the other parameters"
)

if deploy_by_param:
if self.config_dict:
self.cfg_dict = self.config_dict
elif isinstance(self.config_file, str):
real_path = resolve_user_filepath(self.config_file, self.path_context)
try:
with open(real_path, "r") as file:
self.cfg_dict = yaml.safe_load(file)
except FileNotFoundError:
raise ValueError(f"File not found: {real_path}")
except yaml.YAMLError as exc:
logger.error("Error while parsing YAML file: %s", exc)
raise
except Exception as e:
raise ValueError(
f"An error occurred while reading the file: {real_path}\n{e}"
)
elif self.config_file:
try:
self.cfg_dict = yaml.safe_load(self.config_file)
except yaml.YAMLError as exc:
logger.error("Error while parsing YAML config-file stream: %s", exc)
raise
else:
self.cfg_dict = {
k: v
for k, v in [
Expand All @@ -146,37 +142,13 @@ def verify(self, create: bool = True):
]
if v is not None
}
elif self.config_dict:
self.cfg_dict = self.config_dict
elif isinstance(self.config_file, str):
real_path = resolve_user_filepath(self.config_file, self.path_context)
try:
with open(real_path, "r") as file:
self.cfg_dict = yaml.safe_load(file)
except FileNotFoundError:
raise ValueError(f"File not found: {real_path}")
except yaml.YAMLError as exc:
logger.error("Error while parsing YAML file: %s", exc)
raise
except Exception as e:
raise ValueError(
f"An error occurred while reading the file: {real_path}\n{e}"
)
elif self.config_file:
try:
self.cfg_dict = yaml.safe_load(self.config_file)
except yaml.YAMLError as exc:
logger.error("Error while parsing YAML config-file stream: %s", exc)
raise
else:
raise BentoMLException(
"Must provide either config_dict, config_file, or the other parameters"
)

if self.cfg_dict is None:
self.cfg_dict = {}

bento_name = self.cfg_dict.get("bento")
if bento_name is None and create:
bento_name = os.getcwd()
# determine if bento is a path or a name
if bento_name:
if isinstance(bento_name, str) and os.path.exists(bento_name):
Expand Down
9 changes: 9 additions & 0 deletions src/bentoml/_internal/service/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ def import_service(
"""
from bentoml import Service

if not svc_import_path:
from _bentoml_impl.loader import import_service as import_1_2_service
from _bentoml_impl.loader import normalize_identifier

_bento_identifier, _working_dir = normalize_identifier(
svc_import_path, working_dir
)
return import_1_2_service(_bento_identifier, _working_dir)

from ..context import server_context

service_types: list[type] = [Service]
Expand Down
6 changes: 1 addition & 5 deletions src/bentoml/bentos.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,11 +418,7 @@ def build_bentofile(
build_config = BentoBuildConfig.from_file(bentofile)
break
else:
if service is None:
raise InvalidArgument(
"No build config file found and no service specified"
)
build_config = BentoBuildConfig(service=service)
build_config = BentoBuildConfig(service=service or "")

if labels:
if not build_config.labels:
Expand Down