aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRuomei Yan <ruomei.yan@arm.com>2022-11-02 16:47:56 +0000
committerRuomei Yan <ruomei.yan@arm.com>2022-11-15 13:02:56 +0000
commit47fc50576e7040680c19e152592b2c5e5cc297f5 (patch)
treef10fc331e7bc7358c7da8cf3582d9428db4e7367 /src
parentef73bb773df214f3f33f8e4ca7d276041106cad2 (diff)
downloadmlia-47fc50576e7040680c19e152592b2c5e5cc297f5.tar.gz
MLIA-649 Strip mlia backend management into a new command
* add entry point for mlia-backend in setup.cfg and main.py * add --force option for install from path: uninstall existing backend in ML Inference Advisor and install from given path * add uninstall and list program parameters: uninstall has backend_name as input arg, install has backend_name as a mandatory argument * add unit tests in test_cli_commands.py, test_cli_main.py, test_tools_metadata_common.py, test_tools_metadata_corstone.py * updated README.md * remove --download option for installing backend * add new lines for the display section when we do mlia-backen list * add case insensitive support for backend names in command line argument Change-Id: Icb89d8957fa6be4b767710e24fa074f26472674b
Diffstat (limited to 'src')
-rw-r--r--src/mlia/cli/commands.py41
-rw-r--r--src/mlia/cli/common.py3
-rw-r--r--src/mlia/cli/main.py81
-rw-r--r--src/mlia/cli/options.py37
-rw-r--r--src/mlia/tools/metadata/common.py57
-rw-r--r--src/mlia/tools/metadata/corstone.py6
6 files changed, 151 insertions, 74 deletions
diff --git a/src/mlia/cli/commands.py b/src/mlia/cli/commands.py
index 72ae4bb..4be7f3e 100644
--- a/src/mlia/cli/commands.py
+++ b/src/mlia/cli/commands.py
@@ -29,7 +29,6 @@ from mlia.api import PathOrFileLike
from mlia.cli.config import get_installation_manager
from mlia.cli.options import parse_optimization_parameters
from mlia.utils.console import create_section_header
-from mlia.utils.types import only_one_selected
logger = logging.getLogger(__name__)
@@ -243,34 +242,36 @@ def optimization(
)
-def backend(
- backend_action: str,
+def backend_install(
+ name: str,
path: Path | None = None,
- download: bool = False,
- name: str | None = None,
i_agree_to_the_contained_eula: bool = False,
noninteractive: bool = False,
+ force: bool = False,
) -> None:
- """Backends configuration."""
+ """Install configuration."""
logger.info(CONFIG)
manager = get_installation_manager(noninteractive)
- if backend_action == "status":
- manager.show_env_details()
+ install_from_path = path is not None
- if backend_action == "install":
- install_from_path = path is not None
+ if install_from_path:
+ manager.install_from(cast(Path, path), name, force)
+ else:
+ eula_agreement = not i_agree_to_the_contained_eula
+ manager.download_and_install(name, eula_agreement)
- if not only_one_selected(install_from_path, download):
- raise Exception(
- "Please select only one action: download or "
- "provide path to the backend installation"
- )
- if install_from_path:
- manager.install_from(cast(Path, path), name)
+def backend_uninstall(
+ name: str,
+) -> None:
+ """Uninstall backend(s)."""
+ manager = get_installation_manager(noninteractive=True)
+ manager.uninstall(name)
+
- if download:
- eula_agreement = not i_agree_to_the_contained_eula
- manager.download_and_install(name, eula_agreement)
+def backend_list() -> None:
+ """List backend status."""
+ manager = get_installation_manager(noninteractive=True)
+ manager.show_env_details()
diff --git a/src/mlia/cli/common.py b/src/mlia/cli/common.py
index 3f60668..077f456 100644
--- a/src/mlia/cli/common.py
+++ b/src/mlia/cli/common.py
@@ -16,11 +16,12 @@ class CommandInfo:
aliases: list[str]
opt_groups: list[Callable[[argparse.ArgumentParser], None]]
is_default: bool = False
+ name: str | None = None
@property
def command_name(self) -> str:
"""Return command name."""
- return self.func.__name__
+ return self.name or self.func.__name__
@property
def command_name_and_aliases(self) -> list[str]:
diff --git a/src/mlia/cli/main.py b/src/mlia/cli/main.py
index d36d2d9..61b8f05 100644
--- a/src/mlia/cli/main.py
+++ b/src/mlia/cli/main.py
@@ -12,14 +12,17 @@ from pathlib import Path
from mlia import __version__
from mlia.cli.commands import all_tests
-from mlia.cli.commands import backend
+from mlia.cli.commands import backend_install
+from mlia.cli.commands import backend_list
+from mlia.cli.commands import backend_uninstall
from mlia.cli.commands import operators
from mlia.cli.commands import optimization
from mlia.cli.commands import performance
from mlia.cli.common import CommandInfo
from mlia.cli.helpers import CLIActionResolver
from mlia.cli.logging import setup_logging
-from mlia.cli.options import add_backend_options
+from mlia.cli.options import add_backend_install_options
+from mlia.cli.options import add_backend_uninstall_options
from mlia.cli.options import add_custom_supported_operators_options
from mlia.cli.options import add_debug_options
from mlia.cli.options import add_evaluation_options
@@ -99,42 +102,66 @@ def get_commands() -> list[CommandInfo]:
add_evaluation_options,
],
),
+ ]
+
+
+def backend_commands() -> list[CommandInfo]:
+ """Return commands configuration."""
+ return [
+ CommandInfo(
+ backend_install,
+ [],
+ [
+ add_backend_install_options,
+ add_debug_options,
+ ],
+ name="install",
+ ),
CommandInfo(
- backend,
+ backend_uninstall,
[],
[
- add_backend_options,
+ add_backend_uninstall_options,
add_debug_options,
],
+ name="uninstall",
+ ),
+ CommandInfo(
+ backend_list,
+ [],
+ [
+ add_debug_options,
+ ],
+ name="list",
),
]
-def get_default_command() -> str | None:
+def get_default_command(commands: list[CommandInfo]) -> str | None:
"""Get name of the default command."""
- commands = get_commands()
-
marked_as_default = [cmd.command_name for cmd in commands if cmd.is_default]
assert len(marked_as_default) <= 1, "Only one command could be marked as default"
return next(iter(marked_as_default), None)
-def get_possible_command_names() -> list[str]:
+def get_possible_command_names(commands: list[CommandInfo]) -> list[str]:
"""Get all possible command names including aliases."""
return [
name_or_alias
- for cmd in get_commands()
+ for cmd in commands
for name_or_alias in cmd.command_name_and_aliases
]
-def init_commands(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
+def init_commands(
+ parser: argparse.ArgumentParser, commands: list[CommandInfo]
+) -> argparse.ArgumentParser:
"""Init cli subcommands."""
subparsers = parser.add_subparsers(title="Commands", dest="command")
subparsers.required = True
- for command in get_commands():
+ for command in commands:
command_parser = subparsers.add_parser(
command.command_name,
aliases=command.aliases,
@@ -188,7 +215,6 @@ def run_command(args: argparse.Namespace) -> int:
try:
logger.info(INFO_MESSAGE)
-
args.func(**func_args)
return 0
except KeyboardInterrupt:
@@ -251,12 +277,14 @@ def init_subcommand_parser(parent: argparse.ArgumentParser) -> argparse.Argument
return parser
-def add_default_command_if_needed(args: list[str]) -> None:
+def add_default_command_if_needed(
+ args: list[str], input_commands: list[CommandInfo]
+) -> None:
"""Add default command to the list of the arguments if needed."""
- default_command = get_default_command()
+ default_command = get_default_command(input_commands)
if default_command and len(args) > 0:
- commands = get_possible_command_names()
+ commands = get_possible_command_names(input_commands)
help_or_version = ["-h", "--help", "-v", "--version"]
command_is_missing = args[0] not in [*commands, *help_or_version]
@@ -264,16 +292,31 @@ def add_default_command_if_needed(args: list[str]) -> None:
args.insert(0, default_command)
-def main(argv: list[str] | None = None) -> int:
- """Entry point of the application."""
+def generic_main(
+ commands: list[CommandInfo], argv: list[str] | None = None
+) -> argparse.Namespace:
+ """Enable multiple entry points."""
common_parser = init_common_parser()
subcommand_parser = init_subcommand_parser(common_parser)
- init_commands(subcommand_parser)
+ init_commands(subcommand_parser, commands)
common_args, subcommand_args = common_parser.parse_known_args(argv)
- add_default_command_if_needed(subcommand_args)
+
+ add_default_command_if_needed(subcommand_args, commands)
args = subcommand_parser.parse_args(subcommand_args, common_args)
+ return args
+
+
+def main(argv: list[str] | None = None) -> int:
+ """Entry point of the main application."""
+ args = generic_main(get_commands(), argv)
+ return run_command(args)
+
+
+def backend_main(argv: list[str] | None = None) -> int:
+ """Entry point of the backend application."""
+ args = generic_main(backend_commands(), argv)
return run_command(args)
diff --git a/src/mlia/cli/options.py b/src/mlia/cli/options.py
index f6dcf75..bf2f09b 100644
--- a/src/mlia/cli/options.py
+++ b/src/mlia/cli/options.py
@@ -131,7 +131,7 @@ def add_custom_supported_operators_options(parser: argparse.ArgumentParser) -> N
)
-def add_backend_options(parser: argparse.ArgumentParser) -> None:
+def add_backend_install_options(parser: argparse.ArgumentParser) -> None:
"""Add options for the backends configuration."""
def valid_directory(param: str) -> Path:
@@ -141,42 +141,39 @@ def add_backend_options(parser: argparse.ArgumentParser) -> None:
return dir_path
- subparsers = parser.add_subparsers(title="Backend actions", dest="backend_action")
- subparsers.required = True
-
- install_subparser = subparsers.add_parser(
- "install", help="Install backend", allow_abbrev=False
- )
- install_type_group = install_subparser.add_mutually_exclusive_group()
- install_type_group.required = True
- install_type_group.add_argument(
+ parser.add_argument(
"--path", type=valid_directory, help="Path to the installed backend"
)
- install_type_group.add_argument(
- "--download",
+ parser.add_argument(
+ "--i-agree-to-the-contained-eula",
default=False,
action="store_true",
- help="Download and install backend",
+ help=argparse.SUPPRESS,
)
- install_subparser.add_argument(
- "--i-agree-to-the-contained-eula",
+ parser.add_argument(
+ "--force",
default=False,
action="store_true",
- help=argparse.SUPPRESS,
+ help="Force reinstall backend in the specified path",
)
- install_subparser.add_argument(
+ parser.add_argument(
"--noninteractive",
default=False,
action="store_true",
help="Non interactive mode with automatic confirmation of every action",
)
- install_subparser.add_argument(
+ parser.add_argument(
"name",
- nargs="?",
help="Name of the backend to install",
)
- subparsers.add_parser("status", help="Show backends status")
+
+def add_backend_uninstall_options(parser: argparse.ArgumentParser) -> None:
+ """Add options for the backends configuration."""
+ parser.add_argument(
+ "name",
+ help="Name of the installed backend",
+ )
def add_evaluation_options(parser: argparse.ArgumentParser) -> None:
diff --git a/src/mlia/tools/metadata/common.py b/src/mlia/tools/metadata/common.py
index dd4571a..927be74 100644
--- a/src/mlia/tools/metadata/common.py
+++ b/src/mlia/tools/metadata/common.py
@@ -65,6 +65,10 @@ class Installation(ABC):
def install(self, install_type: InstallationType) -> None:
"""Install the backend."""
+ @abstractmethod
+ def uninstall(self) -> None:
+ """Uninstall the backend."""
+
InstallationFilter = Callable[[Installation], bool]
@@ -106,20 +110,21 @@ class SearchByNameFilter:
def __call__(self, installation: Installation) -> bool:
"""Installation filter."""
- return not self.backend_name or installation.name == self.backend_name
+ return (
+ not self.backend_name
+ or installation.name.casefold() == self.backend_name.casefold()
+ )
class InstallationManager(ABC):
"""Helper class for managing installations."""
@abstractmethod
- def install_from(self, backend_path: Path, backend_name: str | None) -> None:
+ def install_from(self, backend_path: Path, backend_name: str, force: bool) -> None:
"""Install backend from the local directory."""
@abstractmethod
- def download_and_install(
- self, backend_name: str | None, eula_agreement: bool
- ) -> None:
+ def download_and_install(self, backend_name: str, eula_agreement: bool) -> None:
"""Download and install backends."""
@abstractmethod
@@ -130,6 +135,10 @@ class InstallationManager(ABC):
def backend_installed(self, backend_name: str) -> bool:
"""Return true if requested backend installed."""
+ @abstractmethod
+ def uninstall(self, backend_name: str) -> None:
+ """Delete the existing installation."""
+
class InstallationFiltersMixin:
"""Mixin for filtering installation based on different conditions."""
@@ -145,7 +154,7 @@ class InstallationFiltersMixin:
]
def could_be_installed_from(
- self, backend_path: Path, backend_name: str | None
+ self, backend_path: Path, backend_name: str
) -> list[Installation]:
"""Return installations that could be installed from provided directory."""
return self.filter_by(
@@ -154,7 +163,7 @@ class InstallationFiltersMixin:
)
def could_be_downloaded_and_installed(
- self, backend_name: str | None = None
+ self, backend_name: str
) -> list[Installation]:
"""Return installations that could be downloaded and installed."""
return self.filter_by(
@@ -163,7 +172,7 @@ class InstallationFiltersMixin:
ReadyForInstallationFilter(),
)
- def already_installed(self, backend_name: str | None = None) -> list[Installation]:
+ def already_installed(self, backend_name: str = None) -> list[Installation]:
"""Return list of backends that are already installed."""
return self.filter_by(
AlreadyInstalledFilter(), SearchByNameFilter(backend_name)
@@ -185,7 +194,7 @@ class DefaultInstallationManager(InstallationManager, InstallationFiltersMixin):
self.noninteractive = noninteractive
def choose_installation_for_path(
- self, backend_path: Path, backend_name: str | None
+ self, backend_path: Path, backend_name: str, force: bool
) -> Installation | None:
"""Check available installation and select one if possible."""
installs = self.could_be_installed_from(backend_path, backend_name)
@@ -210,21 +219,33 @@ class DefaultInstallationManager(InstallationManager, InstallationFiltersMixin):
installation = installs[0]
if installation.already_installed:
logger.info(
- "%s was found in %s, but it has been already installed.",
+ "%s was found in %s, but it has been already installed "
+ "in the ML Inference Advisor.",
installation.name,
backend_path,
)
- return None
+ return installation if force else None
return installation
- def install_from(self, backend_path: Path, backend_name: str | None) -> None:
+ def install_from(
+ self, backend_path: Path, backend_name: str, force: bool = False
+ ) -> None:
"""Install from the provided directory."""
- installation = self.choose_installation_for_path(backend_path, backend_name)
+ installation = self.choose_installation_for_path(
+ backend_path, backend_name, force
+ )
if not installation:
return
+ if force:
+ self.uninstall(backend_name)
+ logger.info(
+ "Force installing %s, so delete the existing installed backend first.",
+ installation.name,
+ )
+
prompt = (
f"{installation.name} was found in {backend_path}. "
"Would you like to install it?"
@@ -232,7 +253,7 @@ class DefaultInstallationManager(InstallationManager, InstallationFiltersMixin):
self._install(installation, InstallFromPath(backend_path), prompt)
def download_and_install(
- self, backend_name: str | None = None, eula_agreement: bool = True
+ self, backend_name: str, eula_agreement: bool = True
) -> None:
"""Download and install available backends."""
installations = self.could_be_downloaded_and_installed(backend_name)
@@ -275,6 +296,14 @@ class DefaultInstallationManager(InstallationManager, InstallationFiltersMixin):
for installation in installations:
logger.info(" - %s", installation.name)
+ def uninstall(self, backend_name: str) -> None:
+ """Uninstall the backend with name backend_name."""
+ installations = self.already_installed(backend_name)
+ if not installations:
+ raise Exception("No backend available for uninstall")
+ for installation in installations:
+ installation.uninstall()
+
def _install(
self,
installation: Installation,
diff --git a/src/mlia/tools/metadata/corstone.py b/src/mlia/tools/metadata/corstone.py
index cea1ec9..04b13b5 100644
--- a/src/mlia/tools/metadata/corstone.py
+++ b/src/mlia/tools/metadata/corstone.py
@@ -19,6 +19,7 @@ from typing import Iterable
from typing import Optional
import mlia.backend.manager as backend_manager
+from mlia.backend.system import remove_system
from mlia.tools.metadata.common import DownloadAndInstall
from mlia.tools.metadata.common import Installation
from mlia.tools.metadata.common import InstallationType
@@ -205,6 +206,11 @@ class BackendInstallation(Installation):
self.install(InstallFromPath(backend_path))
+ def uninstall(self) -> None:
+ """Uninstall the backend."""
+ remove_system(self.metadata.fvp_dir_name)
+ logger.info("%s successfully uninstalled.", self.name)
+
class PackagePathChecker:
"""Package path checker."""