Skip to content
This repository has been archived by the owner on Sep 13, 2023. It is now read-only.

Release 0.3.0 #397

Merged
merged 21 commits into from
Oct 27, 2022
Merged
Show file tree
Hide file tree
Changes from 9 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
6 changes: 5 additions & 1 deletion .github/workflows/check-test-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:

env:
MLEM_TESTS: "true"
MLEM_DEBUG: "true"
aguschin marked this conversation as resolved.
Show resolved Hide resolved

jobs:
authorize:
Expand Down Expand Up @@ -58,7 +59,7 @@ jobs:
# no HDF5 support installed for tables
- os: windows-latest
python: "3.9"
fail-fast: true
fail-fast: false
steps:
- uses: actions/checkout@v3
with:
Expand Down Expand Up @@ -92,6 +93,9 @@ jobs:
pip install pre-commit .[tests]
- run: pre-commit run pylint -a -v --show-diff-on-failure
if: matrix.python != '3.7'
- name: Start minikube
if: matrix.os == 'ubuntu-latest' && matrix.python == '3.9'
uses: medyagh/setup-minikube@master
- name: Run tests
timeout-minutes: 40
run: pytest
Expand Down
7 changes: 4 additions & 3 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ disable=print-statement,
redefined-builtin, # TODO: https://github.com/iterative/mlem/issues/60
no-self-use, # TODO: https://github.com/iterative/mlem/issues/60 maybe leave it
import-outside-toplevel,
wrong-import-order # handeled by isort
wrong-import-order, # handeled by isort
cannot-enumerate-pytest-fixtures # TODO: https://github.com/iterative/mlem/issues/60
aguschin marked this conversation as resolved.
Show resolved Hide resolved

# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
Expand Down Expand Up @@ -369,7 +370,7 @@ indent-string=' '
max-line-length=100

# Maximum number of lines in a module.
max-module-lines=1000
max-module-lines=2000

# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
Expand All @@ -389,7 +390,7 @@ ignore-comments=yes
ignore-docstrings=yes

# Ignore imports when computing similarities.
ignore-imports=no
ignore-imports=yes

# Ignore function signatures when computing similarities.
ignore-signatures=no
Expand Down
67 changes: 36 additions & 31 deletions mlem/api/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
WrongMethodError,
)
from mlem.core.import_objects import ImportAnalyzer, ImportHook
from mlem.core.meta_io import MLEM_DIR, Location, UriResolver, get_fs
from mlem.core.meta_io import MLEM_DIR, Location, get_fs
from mlem.core.metadata import load_meta, save
from mlem.core.objects import (
MlemBuilder,
Expand Down Expand Up @@ -369,7 +369,7 @@ def ls( # pylint: disable=too-many-locals
include_links: bool = True,
ignore_errors: bool = False,
) -> Dict[Type[MlemObject], List[MlemObject]]:
loc = UriResolver.resolve(
loc = Location.resolve(
"", project=project, rev=rev, fs=fs, find_project=True
)
_validate_ls_project(loc, project)
Expand All @@ -394,7 +394,7 @@ def import_object(
"""Try to load an object as MLEM model (or data) and return it,
optionally saving to the specified target location
"""
loc = UriResolver.resolve(path, project, rev, fs)
loc = Location.resolve(path, project, rev, fs)
echo(EMOJI_LOAD + f"Importing object from {loc.uri_repr}")
if type_ is not None:
type_, modifier = parse_import_type_modifier(type_)
Expand All @@ -416,52 +416,57 @@ def import_object(

def deploy(
deploy_meta_or_path: Union[MlemDeployment, str],
model: Union[MlemModel, str] = None,
model: Union[MlemModel, str],
env: Union[MlemEnv, str] = None,
project: Optional[str] = None,
rev: Optional[str] = None,
fs: Optional[AbstractFileSystem] = None,
external: bool = None,
index: bool = None,
env_kwargs: Dict[str, Any] = None,
**deploy_kwargs,
) -> MlemDeployment:
deploy_path = None
deploy_meta: MlemDeployment
update = False
if isinstance(deploy_meta_or_path, str):
deploy_path = deploy_meta_or_path
try:
deploy_meta = load_meta(
path=deploy_path,
path=deploy_meta_or_path,
project=project,
rev=rev,
fs=fs,
force_type=MlemDeployment,
)
except MlemObjectNotFound:
deploy_meta = None

update = True
except MlemObjectNotFound as e:
if env is None:
raise MlemError(
"Please provide model and env args for new deployment"
) from e
if not deploy_meta_or_path:
raise MlemError("deploy_path cannot be empty") from e

env_meta = ensure_meta(MlemEnv, env, allow_typename=True)
if isinstance(env_meta, type):
env = None
if env_kwargs:
env = env_meta(**env_kwargs)
deploy_type = env_meta.deploy_type
deploy_meta = deploy_type(
env=env,
**deploy_kwargs,
)
deploy_meta.dump(deploy_meta_or_path, fs, project, index, external)
else:
deploy_meta = deploy_meta_or_path
if model is not None:
deploy_meta.replace_model(get_model_meta(model))
update = True

if deploy_meta is None:
if model is None or env is None:
raise MlemError(
"Please provide model and env args for new deployment"
)
if not deploy_path:
raise MlemError("deploy_path cannot be empty")
model_meta = get_model_meta(model)
env_meta = ensure_meta(MlemEnv, env)
deploy_meta = env_meta.deploy_type(
model=model_meta,
env=env_meta,
env_link=env_meta.make_link(),
model_link=model_meta.make_link(),
**deploy_kwargs,
)
deploy_meta.dump(deploy_path, fs, project, index, external)
if update:
pass # todo update from deploy_args and env_args
# ensuring links are working
deploy_meta.get_env()
deploy_meta.get_model()
model_meta = get_model_meta(model)

deploy_meta.run()
deploy_meta.check_unchanged()
deploy_meta.deploy(model_meta)
return deploy_meta
44 changes: 39 additions & 5 deletions mlem/api/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import re
from typing import Any, Optional, Tuple, Type, TypeVar, Union
from typing import Any, Optional, Tuple, Type, TypeVar, Union, overload

from mlem.core.base import MlemABC, build_mlem_object
from mlem.core.errors import InvalidArgumentError
from typing_extensions import Literal

from mlem.core.base import MlemABC, build_mlem_object, load_impl_ext
from mlem.core.errors import InvalidArgumentError, MlemObjectNotFound
from mlem.core.metadata import load, load_meta
from mlem.core.objects import MlemData, MlemModel, MlemObject

Expand Down Expand Up @@ -45,9 +47,41 @@ def get_model_meta(
MM = TypeVar("MM", bound=MlemObject)


def ensure_meta(as_class: Type[MM], obj_or_path: Union[str, MM]) -> MM:
@overload
def ensure_meta(
as_class: Type[MM],
obj_or_path: Union[str, MM],
allow_typename: bool = False,
) -> Union[MM, Type[MM]]:
pass


@overload
def ensure_meta(
as_class: Type[MM],
obj_or_path: Union[str, MM],
allow_typename: Literal[False] = False,
) -> MM:
pass


def ensure_meta(
as_class: Type[MM],
obj_or_path: Union[str, MM],
allow_typename: bool = False,
) -> Union[MM, Type[MM]]:
if isinstance(obj_or_path, str):
return load_meta(obj_or_path, force_type=as_class)
try:
return load_meta(obj_or_path, force_type=as_class)
except MlemObjectNotFound:
if allow_typename:
impl = load_impl_ext(
as_class.abs_name, obj_or_path, raise_on_missing=False
)
if impl is None or not issubclass(impl, as_class):
raise
return impl
raise
if isinstance(obj_or_path, as_class):
return obj_or_path
raise ValueError(f"Cannot get {as_class} from '{obj_or_path}'")
Expand Down
Loading