diff --git a/docs/todo.txt b/docs/todo.txt index fd524bee..e69de29b 100644 --- a/docs/todo.txt +++ b/docs/todo.txt @@ -1,3 +0,0 @@ -- add command install - - no args install deps from proj - - args install packages \ No newline at end of file diff --git a/src/cpl/utils/pip.py b/src/cpl/utils/pip.py index 98c1ebb3..bc8c0db3 100644 --- a/src/cpl/utils/pip.py +++ b/src/cpl/utils/pip.py @@ -26,7 +26,7 @@ class Pip: @staticmethod def install(package: str, *args, source: str = None, stdout=None, stderr=None): - pip_args = [sys.executable, "-m", "pip", "install"] + pip_args = [sys.executable, "-m", "pip", "install", "--yes"] for arg in args: pip_args.append(arg) @@ -37,3 +37,7 @@ class Pip: pip_args.append(package) subprocess.run(pip_args, stdout=stdout, stderr=stderr) + + @staticmethod + def uninstall(package: str, stdout=None, stderr=None): + subprocess.run([sys.executable, "-m", "pip", "uninstall", "--yes", package], stdout=stdout, stderr=stderr) diff --git a/src/cpl_cli/cli.py b/src/cpl_cli/cli.py index adfc94be..ef6c7c27 100644 --- a/src/cpl_cli/cli.py +++ b/src/cpl_cli/cli.py @@ -7,6 +7,7 @@ from cpl_cli.command.install_service import InstallService from cpl_cli.command.new_service import NewService from cpl_cli.command.publish_service import PublishService from cpl_cli.command.start_service import StartService +from cpl_cli.command.uninstall_service import UninstallService from cpl_cli.command.update_service import UpdateService from cpl_cli.command_handler_service import CommandHandler from cpl_cli.command_model import CommandModel @@ -32,6 +33,7 @@ class CLI(ApplicationABC): self._command_handler.add_command(CommandModel('new', ['n', 'N'], NewService, False)) self._command_handler.add_command(CommandModel('publish', ['p', 'P'], PublishService, True)) self._command_handler.add_command(CommandModel('start', ['s', 'S'], StartService, True)) + self._command_handler.add_command(CommandModel('uninstall', ['ui', 'UI'], UninstallService, True)) self._command_handler.add_command(CommandModel('update', ['u', 'U'], UpdateService, True)) self._command_handler.add_command(CommandModel('version', ['v', 'V'], VersionService, False)) diff --git a/src/cpl_cli/command/uninstall_service.py b/src/cpl_cli/command/uninstall_service.py new file mode 100644 index 00000000..b97c1c42 --- /dev/null +++ b/src/cpl_cli/command/uninstall_service.py @@ -0,0 +1,106 @@ +import json +import os +import subprocess + +from cpl.application import ApplicationRuntimeABC +from cpl.console.console import Console +from cpl.console.foreground_color_enum import ForegroundColorEnum +from cpl.utils.pip import Pip +from cpl_cli.command_abc import CommandABC +from cpl_cli.configuration import ProjectSettingsNameEnum, VersionSettingsNameEnum, BuildSettingsNameEnum +from cpl_cli.configuration.build_settings import BuildSettings +from cpl_cli.configuration.project_settings import ProjectSettings + + +class UninstallService(CommandABC): + + def __init__(self, runtime: ApplicationRuntimeABC, build_settings: BuildSettings, + project_settings: ProjectSettings): + CommandABC.__init__(self) + + self._runtime = runtime + + self._build_settings = build_settings + self._project_settings = project_settings + + @staticmethod + def _get_project_settings_dict(project: ProjectSettings) -> dict: + return { + ProjectSettingsNameEnum.name.value: project.name, + ProjectSettingsNameEnum.version.value: { + VersionSettingsNameEnum.major.value: project.version.major, + VersionSettingsNameEnum.minor.value: project.version.minor, + VersionSettingsNameEnum.micro.value: project.version.micro + }, + ProjectSettingsNameEnum.author.value: project.author, + ProjectSettingsNameEnum.author_email.value: project.author_email, + ProjectSettingsNameEnum.description.value: project.description, + ProjectSettingsNameEnum.long_description.value: project.long_description, + ProjectSettingsNameEnum.url.value: project.url, + ProjectSettingsNameEnum.copyright_date.value: project.copyright_date, + ProjectSettingsNameEnum.copyright_name.value: project.copyright_name, + ProjectSettingsNameEnum.license_name.value: project.license_name, + ProjectSettingsNameEnum.license_description.value: project.license_description, + ProjectSettingsNameEnum.dependencies.value: project.dependencies, + ProjectSettingsNameEnum.python_version.value: project.python_version + } + + @staticmethod + def _get_build_settings_dict(build: BuildSettings) -> dict: + return { + BuildSettingsNameEnum.source_path.value: build.source_path, + BuildSettingsNameEnum.output_path.value: build.output_path, + BuildSettingsNameEnum.main.value: build.main, + BuildSettingsNameEnum.entry_point.value: build.entry_point, + BuildSettingsNameEnum.include_package_data.value: build.include_package_data, + BuildSettingsNameEnum.included.value: build.included, + BuildSettingsNameEnum.excluded.value: build.excluded, + BuildSettingsNameEnum.package_data.value: build.package_data + } + + def run(self, args: list[str]): + if len(args) == 0: + Console.error(f'Expected package') + Console.error(f'Usage: cpl uninstall ') + return + + package = args[0] + is_in_dependencies = False + + pip_package = Pip.get_package(package) + + for dependency in self._project_settings.dependencies: + if package in dependency: + is_in_dependencies = True + package = dependency + + if not is_in_dependencies and pip_package is None: + Console.error(f'Package {package} not found') + return + + elif not is_in_dependencies and pip_package is not None: + package = pip_package + + Console.spinner( + f'Uninstalling: {package}', + Pip.uninstall, package, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + text_foreground_color=ForegroundColorEnum.green, + spinner_foreground_color=ForegroundColorEnum.cyan + ) + + if package in self._project_settings.dependencies: + self._project_settings.dependencies.remove(package) + config = { + ProjectSettings.__name__: self._get_project_settings_dict(self._project_settings), + BuildSettings.__name__: self._get_build_settings_dict(self._build_settings) + } + 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() + + Console.write_line(f'Removed {package}') + + + diff --git a/src/cpl_cli/startup.py b/src/cpl_cli/startup.py index f7c03e04..4acdbd95 100644 --- a/src/cpl_cli/startup.py +++ b/src/cpl_cli/startup.py @@ -12,6 +12,7 @@ from cpl_cli.command.install_service import InstallService from cpl_cli.command.new_service import NewService from cpl_cli.command.publish_service import PublishService from cpl_cli.command.start_service import StartService +from cpl_cli.command.uninstall_service import UninstallService from cpl_cli.command.update_service import UpdateService from cpl_cli.command_handler_service import CommandHandler from cpl_cli.command.help_service import HelpService @@ -64,6 +65,8 @@ class Startup(StartupABC): ])) self._configuration.add_console_argument(ConsoleArgument('', 'publish', ['p', 'P'], '')) self._configuration.add_console_argument(ConsoleArgument('', 'start', ['s', 'S'], '')) + self._configuration.add_console_argument( + ConsoleArgument('', 'uninstall', ['ui', 'UI'], ' ', is_value_token_optional=True)) self._configuration.add_console_argument(ConsoleArgument('', 'update', ['u', 'U'], '')) self._configuration.add_console_argument(ConsoleArgument('', 'version', ['v', 'V'], '')) self._configuration.add_console_arguments() @@ -83,6 +86,7 @@ class Startup(StartupABC): self._services.add_transient(NewService) self._services.add_transient(PublishService) self._services.add_transient(StartService) + self._services.add_transient(UninstallService) self._services.add_transient(UpdateService) self._services.add_transient(VersionService) diff --git a/src/tests/custom/general/cpl.json b/src/tests/custom/general/cpl.json index 8a7c355c..ad5855e2 100644 --- a/src/tests/custom/general/cpl.json +++ b/src/tests/custom/general/cpl.json @@ -15,9 +15,7 @@ "CopyrightName": "sh-edraft.de", "LicenseName": "MIT", "LicenseDescription": "MIT, see LICENSE for more details.", - "Dependencies": [ - "discord.py==1.6.0" - ], + "Dependencies": [], "PythonVersion": ">=3.8" }, "BuildSettings": {