diff --git a/smartsim/_core/_cli/__init__.py b/smartsim/_core/_cli/__init__.py index 066040cf4..11bf67cb4 100644 --- a/smartsim/_core/_cli/__init__.py +++ b/smartsim/_core/_cli/__init__.py @@ -24,8 +24,8 @@ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .build import Build -from .clean import Clean, Clobber -from .dbcli import DbCLI -from .site import Site -from .utils import get_install_path, MenuItem +# from .build import Build +# from .clean import Clean, Clobber +# from .dbcli import DbCLI +# from .site import Site +from .utils import get_install_path, MenuItemConfig diff --git a/smartsim/_core/_cli/build.py b/smartsim/_core/_cli/build.py index 27060bb30..c15973497 100644 --- a/smartsim/_core/_cli/build.py +++ b/smartsim/_core/_cli/build.py @@ -34,7 +34,7 @@ import typing as t from tabulate import tabulate -from smartsim._core._cli.utils import color_bool, pip_install, MenuItem +from smartsim._core._cli.utils import color_bool, pip_install, MenuItemOG from smartsim._core._install import builder from smartsim._core._install.buildenv import BuildEnv, SetupError, Version_, Versioner, DbEngine from smartsim._core._install.builder import BuildError @@ -77,374 +77,374 @@ def _install_torch_from_pip(versions: Versioner, device: str = "cpu", verbose: b pip_install(packages, end_point=end_point, verbose=verbose) -class Build(MenuItem): - def execute(self, args: argparse.Namespace) -> None: - self.verbose = args.v - self.keydb = args.keydb - - # torch and tf build by default - pt = not args.no_pt - tf = not args.no_tf - onnx = args.onnx - - logger.info("Running SmartSim build process...") - try: - logger.info("Checking requested versions...") - self.versions = Versioner() - - if args.only_python_packages: - logger.info("Only installing Python packages...skipping build") - self.build_env = BuildEnv(checks=False) - if not args.no_pt: - self.install_torch(device=args.device) - else: - logger.info("Checking for build tools...") - self.build_env = BuildEnv() +# class Build(MenuItemOG): +# def execute(self, args: argparse.Namespace) -> None: +# self.verbose = args.v +# self.keydb = args.keydb + +# # torch and tf build by default +# pt = not args.no_pt +# tf = not args.no_tf +# onnx = args.onnx + +# logger.info("Running SmartSim build process...") +# try: +# logger.info("Checking requested versions...") +# self.versions = Versioner() + +# if args.only_python_packages: +# logger.info("Only installing Python packages...skipping build") +# self.build_env = BuildEnv(checks=False) +# if not args.no_pt: +# self.install_torch(device=args.device) +# else: +# logger.info("Checking for build tools...") +# self.build_env = BuildEnv() - if self.verbose: - logger.info("Build Environment:") - env = self.build_env.as_dict() - env_vars = list(env.keys()) - print(tabulate(env, headers=env_vars, tablefmt="github"), "\n") - - if self.keydb: - self.versions.REDIS = Version_("6.2.0") - self.versions.REDIS_URL = "https://github.com/EQ-Alpha/KeyDB" - self.versions.REDIS_BRANCH = "v6.2.0" - CONFIG.conf_path = Path(CONFIG.core_path, "config", "keydb.conf") - if not CONFIG.conf_path.resolve().is_file(): - raise SSConfigError( - "Database configuration file at REDIS_CONF could not be found" - ) - - if self.verbose: - db_name: DbEngine = "KEYDB" if self.keydb else "REDIS" - logger.info("Version Information:") - vers = self.versions.as_dict(db_name=db_name) - version_names = list(vers.keys()) - print(tabulate(vers, headers=version_names, tablefmt="github"), "\n") - - # REDIS/KeyDB - self.build_database() - - # REDISAI - self.build_redis_ai( - str(args.device), - pt, - tf, - onnx, - args.torch_dir, - args.libtensorflow_dir, - ) - - backends = [ - backend.capitalize() for backend in installed_redisai_backends() - ] - logger.info( - (", ".join(backends) if backends else "No") + " backend(s) built" - ) - - except (SetupError, BuildError) as e: - logger.error(str(e)) - sys.exit(1) - - logger.info("SmartSim build complete!") - - def build_database(self) -> None: - # check database installation - database_name = "KeyDB" if self.keydb else "Redis" - database_builder = builder.DatabaseBuilder( - self.build_env(), self.build_env.MALLOC, self.build_env.JOBS, self.verbose - ) - if not database_builder.is_built: - logger.info( - f"Building {database_name} version {self.versions.REDIS} from {self.versions.REDIS_URL}" - ) - database_builder.build_from_git( - self.versions.REDIS_URL, self.versions.REDIS_BRANCH - ) - database_builder.cleanup() - logger.info(f"{database_name} build complete!") - - def build_redis_ai( - self, - device: str, - torch: bool = True, - tf: bool = True, - onnx: bool = False, - torch_dir: t.Union[str, Path, None] = None, - libtf_dir: t.Union[str, Path, None] = None - ) -> None: - - # make sure user isn't trying to do something silly on MacOS - if self.build_env.PLATFORM == "darwin" and device == "gpu": - raise BuildError("SmartSim does not support GPU on MacOS") - - # decide which runtimes to build - print("\nML Backends Requested") - backends_table = [ - ["PyTorch", self.versions.TORCH, color_bool(torch)], - ["TensorFlow", self.versions.TENSORFLOW, color_bool(tf)], - ["ONNX", self.versions.ONNX or "Unavailable", color_bool(onnx)], - ] - print(tabulate(backends_table, tablefmt="fancy_outline"), end="\n\n") - print(f"Building for GPU support: {color_bool(device == 'gpu')}\n") - - self.check_backends_install() - - # Check for onnx and tf in user python environemnt and prompt user - # to download them if they are not installed. this should not break - # the build however, as we use onnx and tf directly from RAI instead - # of pip like we do PyTorch. - if onnx: - self.check_onnx_install() - if tf: - self.check_tf_install() - - # TORCH - if torch: - if torch_dir: - torch_dir = Path(torch_dir).resolve() - if not torch_dir.is_dir(): - # we will always be able to find a torch version downloaded by - # pip so if we can't find it we know the user suggested a torch - # installation path that doesn't exist - logger.error("Could not find requested user Torch installation") - sys.exit(1) - else: - # install pytorch wheel, and get the path to the cmake dir - # we will use in the RAI build - self.install_torch(device=device) - torch_dir = self.build_env.torch_cmake_path - - if tf: - if libtf_dir: - libtf_dir = Path(libtf_dir).resolve() - - rai_builder = builder.RedisAIBuilder( - build_env=self.build_env(), - torch_dir=str(torch_dir) if torch_dir else "", - libtf_dir=str(libtf_dir) if libtf_dir else "", - build_torch=torch, - build_tf=tf, - build_onnx=onnx, - jobs=self.build_env.JOBS, - verbose=self.verbose, - ) - - if rai_builder.is_built: - logger.info("RedisAI installed. Run `smart clean` to remove.") - else: - # get the build environment, update with CUDNN env vars - # if present and building for GPU, otherwise warn the user - build_env = self.build_env() - if device == "gpu": - gpu_env = self.build_env.get_cudnn_env() - cudnn_env_vars = [ - "CUDNN_LIBRARY", - "CUDNN_INCLUDE_DIR", - "CUDNN_INCLUDE_PATH", - "CUDNN_LIBRARY_PATH", - ] - if not gpu_env: - logger.warning( - f"CUDNN environment variables not found.\n" - + f"Looked for {cudnn_env_vars}" - ) - else: - build_env.update(gpu_env) - # update RAI build env with cudnn env vars - rai_builder.env = build_env - - logger.info( - f"Building RedisAI version {self.versions.REDISAI}" - f" from {self.versions.REDISAI_URL}" - ) - - # NOTE: have the option to add other builds here in the future - # like "from_tarball" - rai_builder.build_from_git( - self.versions.REDISAI_URL, self.versions.REDISAI_BRANCH, device - ) - logger.info("ML Backends and RedisAI build complete!") - - def infer_torch_device(self) -> str: - backend_torch_path = f"{CONFIG.lib_path}/backends/redisai_torch" - device = "cpu" - if Path(f"{backend_torch_path}/lib/libtorch_cuda.so").is_file(): - device = "gpu" - return device - - def install_torch(self, device: str = "cpu") -> None: - """Torch shared libraries installed by pip are used in the build - for SmartSim backends so we download them here. - """ - - if not self.build_env.check_installed("torch", self.versions.TORCH): - inferred_device = self.infer_torch_device() - if (inferred_device == "gpu") and (device == "cpu"): - logger.warning("CPU requested, but GPU backend is available") - _install_torch_from_pip(self.versions, device, self.verbose) - # if torch already installed, check the versions to make sure correct - # torch version is downloaded for that particular device - else: - installed = Version_(pkg_resources.get_distribution("torch").version) - if device == "gpu": - # if torch version is x.x.x+cpu - if "cpu" in installed.patch: - msg = ( - "Torch CPU is currently installed but torch GPU requested. Uninstall all torch packages " - + "and run the `smart build` command again to obtain Torch GPU libraries" - ) - logger.warning(msg) - - if device == "cpu": - # if torch version if x.x.x then we need to install the cpu version - if "cpu" not in installed.patch and not self.build_env.is_macos(): - msg = ( - "Torch GPU installed in python environment but requested Torch CPU. " - + " Run `pip uninstall torch torchvision` and run `smart build` again" - ) - logger.error(msg) # error because this is usually fatal - logger.info(f"Torch {self.versions.TORCH} installed in Python environment") - - def check_onnx_install(self) -> None: - """Check Python environment for ONNX installation""" - if not self.versions.ONNX: - py_version = sys.version_info - msg = ( - "An onnx wheel is not available for " - f"Python {py_version.major}.{py_version.minor}. " - "Instead consider using Python 3.8 or 3.9 with Onnx " - ) - if sys.platform == "linux": - msg += "1.2.5 or " - msg += "1.2.7." - raise SetupError(msg) - try: - if not self.build_env.check_installed("onnx", self.versions.ONNX): - msg = ( - f"ONNX {self.versions.ONNX} not installed in python environment. " - + f"Consider installing onnx=={self.versions.ONNX} with pip" - ) - logger.warning(msg) - else: - logger.info( - f"ONNX {self.versions.ONNX} installed in Python environment" - ) - except SetupError as e: - logger.warning(str(e)) - - def check_tf_install(self) -> None: - """Check Python environment for TensorFlow installation""" - - try: - if not self.build_env.check_installed( - "tensorflow", self.versions.TENSORFLOW - ): - msg = ( - f"TensorFlow {self.versions.TENSORFLOW} not installed in Python environment. " - + f"Consider installing tensorflow=={self.versions.TENSORFLOW} with pip" - ) - logger.warning(msg) - else: - logger.info( - f"TensorFlow {self.versions.TENSORFLOW} installed in Python environment" - ) - except SetupError as e: - logger.warning(str(e)) - - def check_backends_install(self) -> None: - """Checks if backends have already been installed. - Logs details on how to proceed forward - if the RAI_PATH environment variable is set or if - backends have already been installed. - """ - if os.environ.get("RAI_PATH"): - if installed_redisai_backends(): - logger.error( - f"There is no need to build. Backends are already built and specified in the environment at 'RAI_PATH': {CONFIG.redisai}" - ) - else: - logger.error( - f"Before running 'smart build', unset your RAI_PATH environment variable with 'unset RAI_PATH'." - ) - sys.exit(1) - else: - if installed_redisai_backends(): - logger.error( - "If you wish to re-run `smart build`, you must first run `smart clean`." - ) - sys.exit(1) - - @staticmethod - def command() -> str: - return "build" +# if self.verbose: +# logger.info("Build Environment:") +# env = self.build_env.as_dict() +# env_vars = list(env.keys()) +# print(tabulate(env, headers=env_vars, tablefmt="github"), "\n") + +# if self.keydb: +# self.versions.REDIS = Version_("6.2.0") +# self.versions.REDIS_URL = "https://github.com/EQ-Alpha/KeyDB" +# self.versions.REDIS_BRANCH = "v6.2.0" +# CONFIG.conf_path = Path(CONFIG.core_path, "config", "keydb.conf") +# if not CONFIG.conf_path.resolve().is_file(): +# raise SSConfigError( +# "Database configuration file at REDIS_CONF could not be found" +# ) + +# if self.verbose: +# db_name: DbEngine = "KEYDB" if self.keydb else "REDIS" +# logger.info("Version Information:") +# vers = self.versions.as_dict(db_name=db_name) +# version_names = list(vers.keys()) +# print(tabulate(vers, headers=version_names, tablefmt="github"), "\n") + +# # REDIS/KeyDB +# self.build_database() + +# # REDISAI +# self.build_redis_ai( +# str(args.device), +# pt, +# tf, +# onnx, +# args.torch_dir, +# args.libtensorflow_dir, +# ) + +# backends = [ +# backend.capitalize() for backend in installed_redisai_backends() +# ] +# logger.info( +# (", ".join(backends) if backends else "No") + " backend(s) built" +# ) + +# except (SetupError, BuildError) as e: +# logger.error(str(e)) +# sys.exit(1) + +# logger.info("SmartSim build complete!") + +# def build_database(self) -> None: +# # check database installation +# database_name = "KeyDB" if self.keydb else "Redis" +# database_builder = builder.DatabaseBuilder( +# self.build_env(), self.build_env.MALLOC, self.build_env.JOBS, self.verbose +# ) +# if not database_builder.is_built: +# logger.info( +# f"Building {database_name} version {self.versions.REDIS} from {self.versions.REDIS_URL}" +# ) +# database_builder.build_from_git( +# self.versions.REDIS_URL, self.versions.REDIS_BRANCH +# ) +# database_builder.cleanup() +# logger.info(f"{database_name} build complete!") + +# def build_redis_ai( +# self, +# device: str, +# torch: bool = True, +# tf: bool = True, +# onnx: bool = False, +# torch_dir: t.Union[str, Path, None] = None, +# libtf_dir: t.Union[str, Path, None] = None +# ) -> None: + +# # make sure user isn't trying to do something silly on MacOS +# if self.build_env.PLATFORM == "darwin" and device == "gpu": +# raise BuildError("SmartSim does not support GPU on MacOS") + +# # decide which runtimes to build +# print("\nML Backends Requested") +# backends_table = [ +# ["PyTorch", self.versions.TORCH, color_bool(torch)], +# ["TensorFlow", self.versions.TENSORFLOW, color_bool(tf)], +# ["ONNX", self.versions.ONNX or "Unavailable", color_bool(onnx)], +# ] +# print(tabulate(backends_table, tablefmt="fancy_outline"), end="\n\n") +# print(f"Building for GPU support: {color_bool(device == 'gpu')}\n") + +# self.check_backends_install() + +# # Check for onnx and tf in user python environemnt and prompt user +# # to download them if they are not installed. this should not break +# # the build however, as we use onnx and tf directly from RAI instead +# # of pip like we do PyTorch. +# if onnx: +# self.check_onnx_install() +# if tf: +# self.check_tf_install() + +# # TORCH +# if torch: +# if torch_dir: +# torch_dir = Path(torch_dir).resolve() +# if not torch_dir.is_dir(): +# # we will always be able to find a torch version downloaded by +# # pip so if we can't find it we know the user suggested a torch +# # installation path that doesn't exist +# logger.error("Could not find requested user Torch installation") +# sys.exit(1) +# else: +# # install pytorch wheel, and get the path to the cmake dir +# # we will use in the RAI build +# self.install_torch(device=device) +# torch_dir = self.build_env.torch_cmake_path + +# if tf: +# if libtf_dir: +# libtf_dir = Path(libtf_dir).resolve() + +# rai_builder = builder.RedisAIBuilder( +# build_env=self.build_env(), +# torch_dir=str(torch_dir) if torch_dir else "", +# libtf_dir=str(libtf_dir) if libtf_dir else "", +# build_torch=torch, +# build_tf=tf, +# build_onnx=onnx, +# jobs=self.build_env.JOBS, +# verbose=self.verbose, +# ) + +# if rai_builder.is_built: +# logger.info("RedisAI installed. Run `smart clean` to remove.") +# else: +# # get the build environment, update with CUDNN env vars +# # if present and building for GPU, otherwise warn the user +# build_env = self.build_env() +# if device == "gpu": +# gpu_env = self.build_env.get_cudnn_env() +# cudnn_env_vars = [ +# "CUDNN_LIBRARY", +# "CUDNN_INCLUDE_DIR", +# "CUDNN_INCLUDE_PATH", +# "CUDNN_LIBRARY_PATH", +# ] +# if not gpu_env: +# logger.warning( +# f"CUDNN environment variables not found.\n" +# + f"Looked for {cudnn_env_vars}" +# ) +# else: +# build_env.update(gpu_env) +# # update RAI build env with cudnn env vars +# rai_builder.env = build_env + +# logger.info( +# f"Building RedisAI version {self.versions.REDISAI}" +# f" from {self.versions.REDISAI_URL}" +# ) + +# # NOTE: have the option to add other builds here in the future +# # like "from_tarball" +# rai_builder.build_from_git( +# self.versions.REDISAI_URL, self.versions.REDISAI_BRANCH, device +# ) +# logger.info("ML Backends and RedisAI build complete!") + +# def infer_torch_device(self) -> str: +# backend_torch_path = f"{CONFIG.lib_path}/backends/redisai_torch" +# device = "cpu" +# if Path(f"{backend_torch_path}/lib/libtorch_cuda.so").is_file(): +# device = "gpu" +# return device + +# def install_torch(self, device: str = "cpu") -> None: +# """Torch shared libraries installed by pip are used in the build +# for SmartSim backends so we download them here. +# """ + +# if not self.build_env.check_installed("torch", self.versions.TORCH): +# inferred_device = self.infer_torch_device() +# if (inferred_device == "gpu") and (device == "cpu"): +# logger.warning("CPU requested, but GPU backend is available") +# _install_torch_from_pip(self.versions, device, self.verbose) +# # if torch already installed, check the versions to make sure correct +# # torch version is downloaded for that particular device +# else: +# installed = Version_(pkg_resources.get_distribution("torch").version) +# if device == "gpu": +# # if torch version is x.x.x+cpu +# if "cpu" in installed.patch: +# msg = ( +# "Torch CPU is currently installed but torch GPU requested. Uninstall all torch packages " +# + "and run the `smart build` command again to obtain Torch GPU libraries" +# ) +# logger.warning(msg) + +# if device == "cpu": +# # if torch version if x.x.x then we need to install the cpu version +# if "cpu" not in installed.patch and not self.build_env.is_macos(): +# msg = ( +# "Torch GPU installed in python environment but requested Torch CPU. " +# + " Run `pip uninstall torch torchvision` and run `smart build` again" +# ) +# logger.error(msg) # error because this is usually fatal +# logger.info(f"Torch {self.versions.TORCH} installed in Python environment") + +# def check_onnx_install(self) -> None: +# """Check Python environment for ONNX installation""" +# if not self.versions.ONNX: +# py_version = sys.version_info +# msg = ( +# "An onnx wheel is not available for " +# f"Python {py_version.major}.{py_version.minor}. " +# "Instead consider using Python 3.8 or 3.9 with Onnx " +# ) +# if sys.platform == "linux": +# msg += "1.2.5 or " +# msg += "1.2.7." +# raise SetupError(msg) +# try: +# if not self.build_env.check_installed("onnx", self.versions.ONNX): +# msg = ( +# f"ONNX {self.versions.ONNX} not installed in python environment. " +# + f"Consider installing onnx=={self.versions.ONNX} with pip" +# ) +# logger.warning(msg) +# else: +# logger.info( +# f"ONNX {self.versions.ONNX} installed in Python environment" +# ) +# except SetupError as e: +# logger.warning(str(e)) + +# def check_tf_install(self) -> None: +# """Check Python environment for TensorFlow installation""" + +# try: +# if not self.build_env.check_installed( +# "tensorflow", self.versions.TENSORFLOW +# ): +# msg = ( +# f"TensorFlow {self.versions.TENSORFLOW} not installed in Python environment. " +# + f"Consider installing tensorflow=={self.versions.TENSORFLOW} with pip" +# ) +# logger.warning(msg) +# else: +# logger.info( +# f"TensorFlow {self.versions.TENSORFLOW} installed in Python environment" +# ) +# except SetupError as e: +# logger.warning(str(e)) + +# def check_backends_install(self) -> None: +# """Checks if backends have already been installed. +# Logs details on how to proceed forward +# if the RAI_PATH environment variable is set or if +# backends have already been installed. +# """ +# if os.environ.get("RAI_PATH"): +# if installed_redisai_backends(): +# logger.error( +# f"There is no need to build. Backends are already built and specified in the environment at 'RAI_PATH': {CONFIG.redisai}" +# ) +# else: +# logger.error( +# f"Before running 'smart build', unset your RAI_PATH environment variable with 'unset RAI_PATH'." +# ) +# sys.exit(1) +# else: +# if installed_redisai_backends(): +# logger.error( +# "If you wish to re-run `smart build`, you must first run `smart clean`." +# ) +# sys.exit(1) + +# @staticmethod +# def command() -> str: +# return "build" - @staticmethod - def help() -> str: - return Build.desc() +# @staticmethod +# def help() -> str: +# return Build.desc() - @staticmethod - def desc() -> str: - return "Build SmartSim dependencies (Redis, RedisAI, ML runtimes)" - - @staticmethod - def configure_parser(parser: argparse.ArgumentParser) -> argparse.ArgumentParser: - """Builds the parser for the command""" - parser.add_argument( - "-v", - action="store_true", - default=False, - help="Enable verbose build process", - ) - parser.add_argument( - "--device", - type=str.lower, - default="cpu", - choices=["cpu", "gpu"], - help="Device to build ML runtimes for", - ) - parser.add_argument( - "--no_pt", - action="store_true", - default=False, - help="Do not build PyTorch backend", - ) - parser.add_argument( - "--no_tf", - action="store_true", - default=False, - help="Do not build TensorFlow backend", - ) - parser.add_argument( - "--onnx", - action="store_true", - default=False, - help="Build ONNX backend (off by default)", - ) - parser.add_argument( - "--torch_dir", - default=None, - type=str, - help="Path to custom /torch/share/cmake/Torch/ directory (ONLY USE IF NEEDED)", - ) - parser.add_argument( - "--libtensorflow_dir", - default=None, - type=str, - help="Path to custom libtensorflow directory (ONLY USED IF NEEDED)", - ) - parser.add_argument( - "--only_python_packages", - action="store_true", - default=False, - help="If true, only install the python packages (i.e. skip backend builds)", - ) - parser.add_argument( - "--keydb", - action="store_true", - default=False, - help="Build KeyDB instead of Redis", - ) - return parser +# @staticmethod +# def desc() -> str: +# return "Build SmartSim dependencies (Redis, RedisAI, ML runtimes)" + +# @staticmethod +# def configure_parser(parser: argparse.ArgumentParser) -> argparse.ArgumentParser: +# """Builds the parser for the command""" +# parser.add_argument( +# "-v", +# action="store_true", +# default=False, +# help="Enable verbose build process", +# ) +# parser.add_argument( +# "--device", +# type=str.lower, +# default="cpu", +# choices=["cpu", "gpu"], +# help="Device to build ML runtimes for", +# ) +# parser.add_argument( +# "--no_pt", +# action="store_true", +# default=False, +# help="Do not build PyTorch backend", +# ) +# parser.add_argument( +# "--no_tf", +# action="store_true", +# default=False, +# help="Do not build TensorFlow backend", +# ) +# parser.add_argument( +# "--onnx", +# action="store_true", +# default=False, +# help="Build ONNX backend (off by default)", +# ) +# parser.add_argument( +# "--torch_dir", +# default=None, +# type=str, +# help="Path to custom /torch/share/cmake/Torch/ directory (ONLY USE IF NEEDED)", +# ) +# parser.add_argument( +# "--libtensorflow_dir", +# default=None, +# type=str, +# help="Path to custom libtensorflow directory (ONLY USED IF NEEDED)", +# ) +# parser.add_argument( +# "--only_python_packages", +# action="store_true", +# default=False, +# help="If true, only install the python packages (i.e. skip backend builds)", +# ) +# parser.add_argument( +# "--keydb", +# action="store_true", +# default=False, +# help="Build KeyDB instead of Redis", +# ) +# return parser diff --git a/smartsim/_core/_cli/clean.py b/smartsim/_core/_cli/clean.py index 7ee32f21d..f41eaf622 100644 --- a/smartsim/_core/_cli/clean.py +++ b/smartsim/_core/_cli/clean.py @@ -27,56 +27,17 @@ import argparse import typing as t -from smartsim._core._cli.utils import get_install_path, MenuItem, clean from smartsim.log import get_logger smart_logger_format = "[%(name)s] %(levelname)s %(message)s" logger = get_logger("Smart", fmt=smart_logger_format) -class Clean(MenuItem): - def execute(self, args: argparse.Namespace) -> None: - core_path = get_install_path() / "_core" - clobber = args.clobber - clean(core_path, _all=clobber) - - @staticmethod - def command() -> str: - return "clean" - - @staticmethod - def help() -> str: - return Clean.desc() - - @staticmethod - def desc() -> str: - return "Remove previous ML runtime installation" - - @staticmethod - def configure_parser(parser: argparse.ArgumentParser) -> argparse.ArgumentParser: - """Builds the parser for the command""" - parser.add_argument( - "--clobber", - action="store_true", - default=False, - help="Remove all SmartSim non-python dependencies as well", - ) - return parser - - -class Clobber(MenuItem): - def execute(self, args: argparse.Namespace) -> None: - core_path = get_install_path() / "_core" - clean(core_path, _all=True) - - @staticmethod - def command() -> str: - return "clobber" - - @staticmethod - def help() -> str: - return Clobber.desc() - - @staticmethod - def desc() -> str: - return "Remove all previous dependency installations" +def configure_parser(parser: argparse.ArgumentParser) -> None: + """Builds the parser for the command""" + parser.add_argument( + "--clobber", + action="store_true", + default=False, + help="Remove all SmartSim non-python dependencies as well", + ) diff --git a/smartsim/_core/_cli/cli.py b/smartsim/_core/_cli/cli.py index 6f06ccacf..535c855b9 100644 --- a/smartsim/_core/_cli/cli.py +++ b/smartsim/_core/_cli/cli.py @@ -30,15 +30,15 @@ import sys import typing as t -from pkg_resources import require - -import smartsim._core._cli as cli -from smartsim._core._cli.utils import MenuItem +# from pkg_resources import require +# import smartsim._core._cli as cli +from smartsim._core._cli.utils import MenuItemConfig, clean, get_install_path, get_db_path +from smartsim._core._cli.clean import configure_parser as clean_parser class SmartCli: - def __init__(self, menu: t.List[t.Type[MenuItem]]) -> None: - self.menu = {item.command(): item for item in menu} + def __init__(self, menu: t.List[MenuItemConfig]) -> None: + self.menu: t.Dict[str, MenuItemConfig] = {item.command: item for item in menu} parser = argparse.ArgumentParser( prog="smart", description="SmartSim command line interface", @@ -51,12 +51,8 @@ def __init__(self, menu: t.List[t.Type[MenuItem]]) -> None: help="Available commands") for cmd, item in self.menu.items(): - # usage = "smart []" - p = subparsers.add_parser(cmd, - description=item.desc(), - help=item.help(), - ) - item.configure_parser(p) + p = subparsers.add_parser(cmd, description=item.help, help=item.help) + item.configurator(p) def execute(self) -> None: if len(sys.argv) < 2: @@ -66,19 +62,40 @@ def execute(self) -> None: app_args = sys.argv[1:] args = self.parser.parse_args(app_args) - if not (handler := self.menu.get(app_args[0], None)): + if not (menu_item := self.menu.get(app_args[0], None)): self.parser.print_help() sys.exit(0) - handler().execute(args) + menu_item.handler(args) sys.exit(0) def main() -> None: - menu: t.Type[MenuItem] = [cli.Build, - cli.Clean, - cli.DbCLI, - cli.Site, - cli.Clobber] + build = MenuItemConfig("build", + "Build SmartSim dependencies (Redis, RedisAI, ML runtimes)", + lambda args: print('fake build'), + lambda p: p) + + cleanx = MenuItemConfig("clean", + "Remove previous ML runtime installation", + lambda args: clean(get_install_path() / "_core", _all=args.clobber), + clean_parser) + + dbcli = MenuItemConfig("dbcli", + "Print the path to the redis-cli binary", + lambda args: print(get_db_path()), + lambda p: p) + + site = MenuItemConfig("site", + "Print the installation site of SmartSim", + lambda args: print(get_install_path()), + lambda p: p) + + clobber = MenuItemConfig("clobber", + "Remove all previous dependency installations", + lambda args: clean(get_install_path() / "_core", _all=True), + lambda p: p) + + menu = [build, cleanx, dbcli, site, clobber] smart_cli = SmartCli(menu) smart_cli.execute() diff --git a/smartsim/_core/_cli/dbcli.py b/smartsim/_core/_cli/dbcli.py deleted file mode 100644 index 2c6c7cc64..000000000 --- a/smartsim/_core/_cli/dbcli.py +++ /dev/null @@ -1,57 +0,0 @@ -# BSD 2-Clause License -# -# Copyright (c) 2021-2023, Hewlett Packard Enterprise -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import argparse -import sys - -from smartsim._core._cli.utils import get_install_path, MenuItem -from smartsim.log import get_logger - -smart_logger_format = "[%(name)s] %(levelname)s %(message)s" -logger = get_logger("Smart", fmt=smart_logger_format) - - -class DbCLI(MenuItem): - def execute(self, args: argparse.Namespace) -> None: - bin_path = get_install_path() / "_core" / "bin" - for option in bin_path.iterdir(): - if option.name in ("redis-cli", "keydb-cli"): - print(option) - sys.exit(0) - print("Database (Redis or KeyDB) dependencies not found") - sys.exit(1) - - @staticmethod - def command() -> str: - return "dbcli" - - @staticmethod - def help() -> str: - return DbCLI.desc() - - @staticmethod - def desc() -> str: - return "Print the path to the redis-cli binary" diff --git a/smartsim/_core/_cli/site.py b/smartsim/_core/_cli/site.py deleted file mode 100644 index e17033d13..000000000 --- a/smartsim/_core/_cli/site.py +++ /dev/null @@ -1,50 +0,0 @@ -# BSD 2-Clause License -# -# Copyright (c) 2021-2023, Hewlett Packard Enterprise -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import argparse - -from smartsim._core._cli.utils import get_install_path, MenuItem -from smartsim.log import get_logger - -smart_logger_format = "[%(name)s] %(levelname)s %(message)s" -logger = get_logger("Smart", fmt=smart_logger_format) - - -class Site(MenuItem): - def execute(self, args: argparse.Namespace) -> None: - print(get_install_path()) - - @staticmethod - def command() -> str: - return "site" - - @staticmethod - def help() -> str: - return Site.desc() - - @staticmethod - def desc() -> str: - return "Print the installation site of SmartSim" diff --git a/smartsim/_core/_cli/utils.py b/smartsim/_core/_cli/utils.py index a6efb82c0..7f4cd1e7e 100644 --- a/smartsim/_core/_cli/utils.py +++ b/smartsim/_core/_cli/utils.py @@ -33,6 +33,7 @@ from smartsim._core._install.buildenv import SetupError from smartsim._core._install.builder import BuildError from smartsim._core.utils import colorize + from smartsim.log import get_logger smart_logger_format = "[%(name)s] %(levelname)s %(message)s" @@ -124,22 +125,50 @@ def clean(core_path: str, _all: bool = False) -> None: logger.info("Successfully removed SmartSim database installation") -class MenuItem(t.Protocol): - @staticmethod - def configure_parser(parser: ArgumentParser) -> ArgumentParser: - return parser +def get_db_path() -> Path: + bin_path = get_install_path() / "_core" / "bin" + for option in bin_path.iterdir(): + if option.name in ("redis-cli", "keydb-cli"): + return option + return "Database (Redis or KeyDB) dependencies not found" + + +class MenuItemConfig: + def __init__(self, cmd: str, help: str, handler: t.Callable[[Namespace], None], configurator: t.Callable[[ArgumentParser], ArgumentParser]): + self.command = cmd + self.help = help + self.handler = handler + self.configurator = configurator + + @property + def command(self) -> str: + return self._cmd + + @command.setter + def command(self, value: str) -> None: + self._cmd = value + + @property + def help(self) -> str: + return self._help + + @help.setter + def help(self, value: str) -> None: + self._help = value - @staticmethod - def command() -> str: - ... + @property + def handler(self) -> t.Callable[[Namespace], None]: + return self._handler - @staticmethod - def desc() -> str: - ... + @handler.setter + def handler(self, value: t.Callable[[Namespace], None]) -> None: + self._handler = value - def execute(self, args: Namespace) -> None: - ... + @property + def configurator(self) -> t.Callable[[ArgumentParser], None]: + return self._config - @staticmethod - def help() -> str: - ... + @configurator.setter + def configurator(self, value: t.Callable[[ArgumentParser], None]) -> None: + self._config = value + \ No newline at end of file