diff --git a/src/_bentoml_impl/loader.py b/src/_bentoml_impl/loader.py index 5e95fb4db30..6fb28eebc1c 100644 --- a/src/_bentoml_impl/loader.py +++ b/src/_bentoml_impl/loader.py @@ -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}") @@ -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) @@ -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_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)) @@ -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) diff --git a/src/bentoml/_internal/cloud/deployment.py b/src/bentoml/_internal/cloud/deployment.py index d4587106d8f..3167cfbd825 100644 --- a/src/bentoml/_internal/cloud/deployment.py +++ b/src/bentoml/_internal/cloud/deployment.py @@ -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 [ @@ -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): diff --git a/src/bentoml/_internal/service/loader.py b/src/bentoml/_internal/service/loader.py index f05f533140e..755dc78bcfa 100644 --- a/src/bentoml/_internal/service/loader.py +++ b/src/bentoml/_internal/service/loader.py @@ -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] diff --git a/src/bentoml/bentos.py b/src/bentoml/bentos.py index bddf73462b4..68aade97eba 100644 --- a/src/bentoml/bentos.py +++ b/src/bentoml/bentos.py @@ -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: