def save(bento_service, dst, version=None): """ Save given BentoService along with all artifacts to target path """ if version is None: version = _generate_new_version_str() _validate_version_str(version) if bento_service._version_major is not None and bento_service._version_minor is not None: # BentoML uses semantic versioning for BentoService distribution # when user specified the MAJOR and MINOR version number along with # the BentoService class definition with '@ver' decorator. # The parameter version(or auto generated version) here will be used as # PATCH field in the final version: version = '.'.join( [str(bento_service._version_major), str(bento_service._version_minor), version]) # Full path containing saved BentoArchive, it the dst path with service name # and service version as prefix. e.g.: # - s3://my-bucket/base_path => s3://my-bucket/base_path/service_name/version/ # - /tmp/my_bento_archive/ => /tmp/my_bento_archive/service_name/version/ full_saved_path = os.path.join(dst, bento_service.name, version) if is_s3_url(dst): with TempDirectory() as tempdir: _save(bento_service, tempdir, version) upload_to_s3(full_saved_path, tempdir) else: _save(bento_service, dst, version) return full_saved_path
def save(bento_service, dst, version=None): """Save given BentoService along with all its artifacts, source code and dependencies to target path Args: bento_service (bentoml.service.BentoService): a Bento Service instance dst (str): Destination of where the bento service will be saved. It could be a local file path or a s3 path version (:obj:`str`, optional): version text to use for saved archive Returns: string: The complete path of saved Bento service. """ if version is None: version = _generate_new_version_str() _validate_version_str(version) if (bento_service._version_major is not None and bento_service._version_minor is not None): # BentoML uses semantic versioning for BentoService distribution # when user specified the MAJOR and MINOR version number along with # the BentoService class definition with '@ver' decorator. # The parameter version(or auto generated version) here will be used as # PATCH field in the final version: version = ".".join([ str(bento_service._version_major), str(bento_service._version_minor), version, ]) # Full path containing saved BentoArchive, it the dst path with service name # and service version as prefix. e.g.: # - s3://my-bucket/base_path => s3://my-bucket/base_path/service_name/version/ # - /tmp/my_bento_archive/ => /tmp/my_bento_archive/service_name/version/ full_saved_path = os.path.join(dst, bento_service.name, version) if is_s3_url(dst): with TempDirectory() as tempdir: _save(bento_service, tempdir, version) upload_to_s3(full_saved_path, tempdir) else: _save(bento_service, dst, version) LOG.info("BentoService %s:%s saved to %s", bento_service.name, version, full_saved_path) return full_saved_path
def save(bento_service, dst, version=None, pypi_package_version="1.0.0"): """ Save given BentoService along with all artifacts to target path """ if version is None: version = _generate_new_version_str() _validate_version_str(version) # Full path containing saved BentoArchive, it the dst path with service name # and service version as prefix. e.g.: # - s3://my-bucket/base_path => s3://my-bucket/base_path/service_name/version/ # - /tmp/my_bento_archive/ => /tmp/my_bento_archive/service_name/version/ full_saved_path = os.path.join(dst, bento_service.name, version) if is_s3_url(dst): with TempDirectory() as tempdir: _save(bento_service, tempdir, version, pypi_package_version) upload_to_s3(full_saved_path, tempdir) else: _save(bento_service, dst, version, pypi_package_version) return full_saved_path
def save(bento_service, dst, version=None, pypi_package_version="1.0.0"): """ Save given BentoService along with all artifacts to target path """ if version is None: version = _generate_new_version_str() _validate_version_str(version) s3_url = None if is_s3_url(dst): s3_url = os.path.join(dst, bento_service.name, version) # TODO: check s3_url not exist, otherwise raise exception temp_dir = tempfile.mkdtemp() Path(temp_dir, bento_service.name).mkdir(parents=True, exist_ok=True) # Update path to subfolder in the form of 'base/service_name/version/' path = os.path.join(temp_dir, bento_service.name, version) else: Path(os.path.join(dst), bento_service.name).mkdir(parents=True, exist_ok=True) # Update path to subfolder in the form of 'base/service_name/version/' path = os.path.join(dst, bento_service.name, version) if os.path.exists(path): raise ValueError("Version {version} in Path: {dst} already " "exist.".format(version=version, dst=dst)) os.mkdir(path) module_base_path = os.path.join(path, bento_service.name) os.mkdir(module_base_path) # write README.md with user model's docstring if bento_service.__class__.__doc__: model_description = bento_service.__class__.__doc__.strip() else: model_description = DEFAULT_BENTO_ARCHIVE_DESCRIPTION with open(os.path.join(path, 'README.md'), 'w') as f: f.write(model_description) # save all model artifacts to 'base_path/name/artifacts/' directory bento_service.artifacts.save(module_base_path) # write conda environment, requirement.txt bento_service.env.save(path) # TODO: add bentoml.find_packages helper for more fine grained control over # this process, e.g. packages=find_packages(base, [], exclude=[], used_module_only=True) # copy over all custom model code module_name, module_file = copy_used_py_modules(bento_service.__class__.__module__, os.path.join(path, bento_service.name)) if os.path.isabs(module_file): module_file = module_name.replace('.', os.sep) + '.py' # create __init__.py with open(os.path.join(path, bento_service.name, '__init__.py'), "w") as f: f.write( INIT_PY_TEMPLATE.format(service_name=bento_service.name, module_name=module_name, pypi_package_version=pypi_package_version)) # write setup.py, make exported model pip installable setup_py_content = BENTO_MODEL_SETUP_PY_TEMPLATE.format( name=bento_service.name, pypi_package_version=pypi_package_version, long_description=model_description) with open(os.path.join(path, 'setup.py'), 'w') as f: f.write(setup_py_content) with open(os.path.join(path, 'MANIFEST.in'), 'w') as f: f.write(MANIFEST_IN_TEMPLATE.format(service_name=bento_service.name)) # write Dockerfile with open(os.path.join(path, 'Dockerfile'), 'w') as f: f.write( BENTO_SERVER_SINGLE_MODEL_DOCKERFILE_TEMPLATE.format( conda_env_name=bento_service.env.get_conda_env_name())) # write bentoml.yml bentoml_yml_content = BENTOML_CONFIG_YAML_TEMPLATE.format( service_name=bento_service.name, bentoml_version=BENTOML_VERSION, service_version=version, module_name=module_name, module_file=module_file, created_at=str(datetime.now())) with open(os.path.join(path, 'bentoml.yml'), 'w') as f: f.write(bentoml_yml_content) # Also write bentoml.yml to module base path to make it accessible # as package data after pip installed as a python package with open(os.path.join(module_base_path, 'bentoml.yml'), 'w') as f: f.write(bentoml_yml_content) if s3_url: upload_to_s3(s3_url, path) return s3_url else: return path