diff --git a/src/cpl/utils/pip.py b/src/cpl/utils/pip.py index 9dad4e9a..98c1ebb3 100644 --- a/src/cpl/utils/pip.py +++ b/src/cpl/utils/pip.py @@ -1,12 +1,19 @@ import subprocess import sys +from contextlib import suppress +from typing import Optional class Pip: @staticmethod - def get_package(package: str) -> str: - result = subprocess.check_output([sys.executable, "-m", "pip", "show", package]) + def get_package(package: str) -> Optional[str]: + result = None + with suppress(Exception): + result = subprocess.check_output([sys.executable, "-m", "pip", "show", package], stderr=subprocess.DEVNULL) + + if result is None: + return None new_package: list[str] = str(result, 'utf-8').lower().split('\n') new_version = '' diff --git a/src/cpl_cli/command/install_service.py b/src/cpl_cli/command/install_service.py index d0672272..dcb615c4 100644 --- a/src/cpl_cli/command/install_service.py +++ b/src/cpl_cli/command/install_service.py @@ -25,7 +25,26 @@ class InstallService(CommandABC): self._config = configuration def _install_project(self): - pass + project: ProjectSettings = self._config.get_configuration(ProjectSettings) + build: BuildSettings = self._config.get_configuration(BuildSettings) + + if project is None or build is None: + Error.error('The command requires to be run in an CPL project, but a project could not be found.') + return + + if project.dependencies is None: + Error.error('Found invalid dependencies in cpl.json.') + return + + for dependency in project.dependencies: + Console.spinner( + f'Installing: {dependency}', + Pip.install, dependency, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + text_foreground_color=ForegroundColorEnum.green, + spinner_foreground_color=ForegroundColorEnum.cyan + ) @staticmethod def _get_project_settings_dict(project: ProjectSettings) -> dict: @@ -63,6 +82,28 @@ class InstallService(CommandABC): } def _install_package(self, package: str): + is_already_in_project = False + project: ProjectSettings = self._config.get_configuration(ProjectSettings) + build: BuildSettings = self._config.get_configuration(BuildSettings) + + if project is None or build is None: + Error.error('The command requires to be run in an CPL project, but a project could not be found.') + return + + if project.dependencies is None: + Error.error('Found invalid dependencies in cpl.json.') + return + + old_package = Pip.get_package(package) + + for dependency in project.dependencies: + if package in dependency: + is_already_in_project = True + + if old_package is not None and old_package in project.dependencies or is_already_in_project: + Error.warn(f'Package {old_package} is already installed.') + return + Console.spinner( f'Installing: {package}', Pip.install, package, @@ -72,25 +113,22 @@ class InstallService(CommandABC): spinner_foreground_color=ForegroundColorEnum.cyan ) - new_package = Pip.get_package(package) - project: ProjectSettings = self._config.get_configuration(ProjectSettings) - build: BuildSettings = self._config.get_configuration(BuildSettings) - if project is None or build is None: - Error.error('The command requires to be run in an CPL project, but a project could not be found.') - return + if not is_already_in_project: + new_package = Pip.get_package(package) + if new_package is None: + new_package = package - project.dependencies.append(new_package) + project.dependencies.append(new_package) - config = { - ProjectSettings.__name__: self._get_project_settings_dict(project), - BuildSettings.__name__: self._get_build_settings_dict(build) - } - with open(os.path.join(self._runtime.working_directory, 'cpl.json'), 'w') as project_file: - project_file.write(json.dumps(config, indent=2)) - project_file.close() + config = { + ProjectSettings.__name__: self._get_project_settings_dict(project), + BuildSettings.__name__: self._get_build_settings_dict(build) + } + with open(os.path.join(self._runtime.working_directory, 'cpl.json'), 'w') as project_file: + project_file.write(json.dumps(config, indent=2)) + project_file.close() def run(self, args: list[str]): - if len(args) == 0: self._install_project() else: diff --git a/src/cpl_cli/error.py b/src/cpl_cli/error.py index eb2456ef..a6c74060 100644 --- a/src/cpl_cli/error.py +++ b/src/cpl_cli/error.py @@ -1,3 +1,4 @@ +from cpl.console import ForegroundColorEnum from cpl.console.console import Console @@ -7,3 +8,10 @@ class Error: def error(message: str): Console.error(message) Console.error('Run \'cpl help\'\n') + + @staticmethod + def warn(message: str): + Console.set_foreground_color(ForegroundColorEnum.yellow) + Console.write_line(message) + Console.set_foreground_color(ForegroundColorEnum.default) +