diff --git a/src/cpl_cli/command/install_service.py b/src/cpl_cli/command/install_service.py index 43db92b9..b68c20dc 100644 --- a/src/cpl_cli/command/install_service.py +++ b/src/cpl_cli/command/install_service.py @@ -1,10 +1,11 @@ import json import os -import shutil import subprocess import textwrap import time +from packaging import version + from cpl_cli.cli_settings import CLISettings from cpl_cli.command_abc import CommandABC from cpl_cli.configuration.build_settings import BuildSettings @@ -17,7 +18,6 @@ from cpl_core.console.console import Console from cpl_core.console.foreground_color_enum import ForegroundColorEnum from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC from cpl_core.utils.pip import Pip -from packaging import version class InstallService(CommandABC): @@ -42,6 +42,7 @@ class InstallService(CommandABC): self._is_simulation = False self._is_virtual = False + self._is_dev = False self._project_file = f'{self._project_settings.name}.json' @@ -82,6 +83,17 @@ class InstallService(CommandABC): spinner_foreground_color=ForegroundColorEnum.cyan ) + for dependency in self._project_settings.dev_dependencies: + Console.spinner( + f'Installing dev: {dependency}', + Pip.install if not self._is_virtual else self._wait, dependency if not self._is_virtual else 2, + source=self._cli_settings.pip_path if 'cpl-' in dependency else None, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + text_foreground_color=ForegroundColorEnum.green, + spinner_foreground_color=ForegroundColorEnum.cyan + ) + if not self._is_virtual: Pip.reset_executable() @@ -107,7 +119,11 @@ class InstallService(CommandABC): package_version = package.split('==')[1] to_remove_list = [] - for dependency in self._project_settings.dependencies: + deps = self._project_settings.dependencies + if self._is_dev: + deps = self._project_settings.dev_dependencies + + for dependency in deps: dependency_version = '' if '==' in dependency: @@ -121,7 +137,10 @@ class InstallService(CommandABC): is_already_in_project = True for to_remove in to_remove_list: - self._project_settings.dependencies.remove(to_remove) + if self._is_dev: + self._project_settings.dev_dependencies.remove(to_remove) + else: + self._project_settings.dependencies.remove(to_remove) local_package = Pip.get_package(package) if local_package is not None and local_package in self._project_settings.dependencies: @@ -133,9 +152,9 @@ class InstallService(CommandABC): return Console.spinner( - f'Installing: {package}', + f'Installing: {package}' if not self._is_dev else f'Installing dev: {package}', Pip.install if not self._is_virtual else self._wait, package if not self._is_virtual else 2, - source=self._cli_settings.pip_path if 'cpl-' in package else None, + source=self._cli_settings.pip_path if 'cpl-' in package or 'cpl_' in package else None, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, text_foreground_color=ForegroundColorEnum.green, @@ -165,7 +184,10 @@ class InstallService(CommandABC): if '\r' in new_name: new_name = new_name.replace('\r', '') - self._project_settings.dependencies.append(new_name) + if self._is_dev: + self._project_settings.dev_dependencies.append(new_name) + else: + self._project_settings.dependencies.append(new_name) if not self._is_simulation: config = { @@ -185,6 +207,10 @@ class InstallService(CommandABC): :param args: :return: """ + if 'dev' in args: + self._is_dev = True + args.remove('dev') + if 'virtual' in args: self._is_virtual = True args.remove('virtual') diff --git a/src/cpl_cli/command/new_service.py b/src/cpl_cli/command/new_service.py index e01bbf92..387cf8ce 100644 --- a/src/cpl_cli/command/new_service.py +++ b/src/cpl_cli/command/new_service.py @@ -5,6 +5,7 @@ from typing import Optional from packaging import version +import cpl_cli import cpl_core from cpl_cli.configuration.venv_helper_service import VenvHelper from cpl_cli.source_creator.unittest_builder import UnittestBuilder @@ -107,6 +108,9 @@ class NewService(CommandABC): ProjectSettingsNameEnum.dependencies.value: [ f'cpl-core>={version.parse(cpl_core.__version__)}' ], + ProjectSettingsNameEnum.dev_dependencies.value: [ + f'cpl-cli>={version.parse(cpl_cli.__version__)}' + ], ProjectSettingsNameEnum.python_version.value: f'>={sys.version.split(" ")[0]}', ProjectSettingsNameEnum.python_path.value: { sys.platform: '../../venv/bin/python' if self._use_venv else '' diff --git a/src/cpl_cli/command/uninstall_service.py b/src/cpl_cli/command/uninstall_service.py index ab83dcbd..585f728a 100644 --- a/src/cpl_cli/command/uninstall_service.py +++ b/src/cpl_cli/command/uninstall_service.py @@ -36,6 +36,8 @@ class UninstallService(CommandABC): self._is_simulating = False self._is_virtual = False + self._is_dev = False + self._project_file = f'{self._project_settings.name}.json' @property @@ -62,6 +64,10 @@ class UninstallService(CommandABC): Console.error(f'Usage: cpl uninstall ') return + if 'dev' in args: + self._is_dev = True + args.remove('dev') + if '--virtual' in args: self._is_virtual = True args.remove('--virtual') @@ -82,7 +88,11 @@ class UninstallService(CommandABC): else: pip_package = package - for dependency in self._project_settings.dependencies: + deps = self._project_settings.dependencies + if self._is_dev: + deps = self._project_settings.dev_dependencies + + for dependency in deps: if package in dependency: is_in_dependencies = True package = dependency @@ -95,7 +105,7 @@ class UninstallService(CommandABC): package = pip_package Console.spinner( - f'Uninstalling: {package}', + f'Uninstalling: {package}' if not self._is_dev else f'Uninstalling dev: {package}', Pip.uninstall if not self._is_virtual else self._wait, package if not self._is_virtual else 2, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, @@ -103,8 +113,12 @@ class UninstallService(CommandABC): spinner_foreground_color=ForegroundColorEnum.cyan ) - if package in self._project_settings.dependencies: - self._project_settings.dependencies.remove(package) + deps = self._project_settings.dependencies + if self._is_dev: + deps = self._project_settings.dev_dependencies + + if package in deps: + deps.remove(package) if not self._is_simulating: config = { ProjectSettings.__name__: SettingsHelper.get_project_settings_dict(self._project_settings), diff --git a/src/cpl_cli/configuration/project_settings.py b/src/cpl_cli/configuration/project_settings.py index 94d490c6..98d0fa97 100644 --- a/src/cpl_cli/configuration/project_settings.py +++ b/src/cpl_cli/configuration/project_settings.py @@ -28,6 +28,7 @@ class ProjectSettings(ConfigurationModelABC): self._license_name: Optional[str] = None self._license_description: Optional[str] = None self._dependencies: Optional[list[str]] = None + self._dev_dependencies: Optional[list[str]] = None self._python_version: Optional[str] = None self._python_path: Optional[str] = None self._python_executable: Optional[str] = None @@ -81,6 +82,10 @@ class ProjectSettings(ConfigurationModelABC): def dependencies(self) -> list[str]: return self._dependencies + @property + def dev_dependencies(self) -> list[str]: + return self._dev_dependencies + @property def python_version(self) -> str: return self._python_version @@ -111,6 +116,9 @@ class ProjectSettings(ConfigurationModelABC): self._license_name = settings[ProjectSettingsNameEnum.license_name.value] self._license_description = settings[ProjectSettingsNameEnum.license_description.value] self._dependencies = settings[ProjectSettingsNameEnum.dependencies.value] + if ProjectSettingsNameEnum.dev_dependencies.value not in settings: + settings[ProjectSettingsNameEnum.dev_dependencies.value] = [] + self._dev_dependencies = settings[ProjectSettingsNameEnum.dev_dependencies.value] self._python_version = settings[ProjectSettingsNameEnum.python_version.value] self._python_path = settings[ProjectSettingsNameEnum.python_path.value] diff --git a/src/cpl_cli/configuration/project_settings_name_enum.py b/src/cpl_cli/configuration/project_settings_name_enum.py index 7b4b6557..17d2319b 100644 --- a/src/cpl_cli/configuration/project_settings_name_enum.py +++ b/src/cpl_cli/configuration/project_settings_name_enum.py @@ -14,6 +14,7 @@ class ProjectSettingsNameEnum(Enum): license_name = 'LicenseName' license_description = 'LicenseDescription' dependencies = 'Dependencies' + dev_dependencies = 'DevDependencies' python_version = 'PythonVersion' python_path = 'PythonPath' classifiers = 'Classifiers' diff --git a/src/cpl_cli/configuration/settings_helper.py b/src/cpl_cli/configuration/settings_helper.py index 8a7c47ad..660c1285 100644 --- a/src/cpl_cli/configuration/settings_helper.py +++ b/src/cpl_cli/configuration/settings_helper.py @@ -26,6 +26,7 @@ class SettingsHelper: ProjectSettingsNameEnum.license_name.value: project.license_name, ProjectSettingsNameEnum.license_description.value: project.license_description, ProjectSettingsNameEnum.dependencies.value: project.dependencies, + ProjectSettingsNameEnum.dev_dependencies.value: project.dev_dependencies, ProjectSettingsNameEnum.python_version.value: project.python_version, ProjectSettingsNameEnum.python_path.value: project.python_path, ProjectSettingsNameEnum.classifiers.value: project.classifiers diff --git a/src/cpl_cli/cpl-cli.json b/src/cpl_cli/cpl-cli.json index f0b0ebb9..95803358 100644 --- a/src/cpl_cli/cpl-cli.json +++ b/src/cpl_cli/cpl-cli.json @@ -18,6 +18,7 @@ "Dependencies": [ "cpl-core>=2022.6.17.dev8" ], + "DevDependencies": [], "PythonVersion": ">=3.10", "PythonPath": { "linux": "../../venv" diff --git a/src/cpl_cli/startup_argument_extension.py b/src/cpl_cli/startup_argument_extension.py index e3f1fef4..de0047fd 100644 --- a/src/cpl_cli/startup_argument_extension.py +++ b/src/cpl_cli/startup_argument_extension.py @@ -40,6 +40,7 @@ class StartupArgumentExtension(StartupExtensionABC): .add_console_argument(ArgumentTypeEnum.Variable, '', 'thread', ['t', 'T'], ' ') \ .add_console_argument(ArgumentTypeEnum.Variable, '', 'validator', ['v', 'V'], ' ') config.create_console_argument(ArgumentTypeEnum.Executable, '', 'install', ['i', 'I'], InstallService, True, validators=[ProjectValidator]) \ + .add_console_argument(ArgumentTypeEnum.Flag, '--', 'dev', ['d', 'D']) \ .add_console_argument(ArgumentTypeEnum.Flag, '--', 'virtual', ['v', 'V']) \ .add_console_argument(ArgumentTypeEnum.Flag, '--', 'simulate', ['s', 'S']) config.create_console_argument(ArgumentTypeEnum.Executable, '', 'new', ['n', 'N'], NewService, True) \ @@ -58,6 +59,7 @@ class StartupArgumentExtension(StartupExtensionABC): config.create_console_argument(ArgumentTypeEnum.Executable, '', 'run', [], RunService, True, validators=[ProjectValidator]) config.create_console_argument(ArgumentTypeEnum.Executable, '', 'start', ['s', 'S'], StartService, True, validators=[ProjectValidator]) config.create_console_argument(ArgumentTypeEnum.Executable, '', 'uninstall', ['ui', 'UI'], UninstallService, True, validators=[ProjectValidator]) \ + .add_console_argument(ArgumentTypeEnum.Flag, '--', 'dev', ['d', 'D']) \ .add_console_argument(ArgumentTypeEnum.Flag, '--', 'virtual', ['v', 'V']) \ .add_console_argument(ArgumentTypeEnum.Flag, '--', 'simulate', ['s', 'S']) config.create_console_argument(ArgumentTypeEnum.Executable, '', 'update', ['u', 'U'], UpdateService, True, validators=[ProjectValidator]) \ diff --git a/src/cpl_core/cpl-core.json b/src/cpl_core/cpl-core.json index 37028396..fb2e6f38 100644 --- a/src/cpl_core/cpl-core.json +++ b/src/cpl_core/cpl-core.json @@ -28,6 +28,7 @@ "watchdog==2.1.7", "wheel==0.37.1" ], + "DevDependencies": [], "PythonVersion": ">=3.10", "PythonPath": {}, "Classifiers": [] diff --git a/src/cpl_core/utils/pip.py b/src/cpl_core/utils/pip.py index c0ebf062..7d97e496 100644 --- a/src/cpl_core/utils/pip.py +++ b/src/cpl_core/utils/pip.py @@ -126,7 +126,6 @@ class Pip: pip_args.append(source) pip_args.append(package) - print(pip_args) subprocess.run(pip_args, stdout=stdout, stderr=stderr, env=cls._env) @classmethod diff --git a/src/cpl_query/cpl-query.json b/src/cpl_query/cpl-query.json index 355d295b..3fde0ce7 100644 --- a/src/cpl_query/cpl-query.json +++ b/src/cpl_query/cpl-query.json @@ -16,6 +16,7 @@ "LicenseName": "MIT", "LicenseDescription": "MIT, see LICENSE for more details.", "Dependencies": [], + "DevDependencies": [], "PythonVersion": ">=3.10", "PythonPath": {}, "Classifiers": [] diff --git a/src/tests/custom/general/src/general/general.json b/src/tests/custom/general/src/general/general.json index fc68548e..3c53ccda 100644 --- a/src/tests/custom/general/src/general/general.json +++ b/src/tests/custom/general/src/general/general.json @@ -18,6 +18,7 @@ "Dependencies": [ "cpl_core==2022.6.1" ], + "DevDependencies": [], "PythonVersion": ">=3.10", "PythonPath": { "linux": "../../venv/bin/python", diff --git a/unittests/unittests_cli/install_test_case.py b/unittests/unittests_cli/install_test_case.py index ceefbf4f..527256d1 100644 --- a/unittests/unittests_cli/install_test_case.py +++ b/unittests/unittests_cli/install_test_case.py @@ -64,6 +64,28 @@ class InstallTestCase(unittest.TestCase): self.assertIn(package_name, packages) self.assertEqual(version, packages[package_name]) + def test_dev_install_package(self): + version = '1.7.3' + package_name = 'discord.py' + package = f'{package_name}=={version}' + CLICommands.install(package, is_dev=True) + settings = self._get_project_settings() + self.assertNotEqual(settings, {}) + self.assertIn('ProjectSettings', settings) + self.assertIn('Dependencies', settings['ProjectSettings']) + self.assertIn('DevDependencies', settings['ProjectSettings']) + self.assertNotIn( + package, + settings['ProjectSettings']['Dependencies'] + ) + self.assertIn( + package, + settings['ProjectSettings']['DevDependencies'] + ) + packages = self._get_installed_packages() + self.assertIn(package_name, packages) + self.assertEqual(version, packages[package_name]) + def _test_install_all(self): version = '1.7.3' package_name = 'discord.py' @@ -71,21 +93,33 @@ class InstallTestCase(unittest.TestCase): settings = self._get_project_settings() self.assertIn('ProjectSettings', settings) self.assertIn('Dependencies', settings['ProjectSettings']) + self.assertIn('DevDependencies', settings['ProjectSettings']) + self.assertNotIn( + package, + settings['ProjectSettings']['Dependencies'] + ) + self.assertIn('DevDependencies', settings['ProjectSettings']) self.assertNotIn( package, settings['ProjectSettings']['Dependencies'] ) settings['ProjectSettings']['Dependencies'].append(package) + settings['ProjectSettings']['DevDependencies'].append(package) self._save_project_settings(settings) CLICommands.install() new_settings = self._get_project_settings() self.assertEqual(settings, new_settings) self.assertIn('ProjectSettings', new_settings) self.assertIn('Dependencies', new_settings['ProjectSettings']) + self.assertIn('DevDependencies', new_settings['ProjectSettings']) self.assertIn( package, new_settings['ProjectSettings']['Dependencies'] ) + self.assertIn( + package, + new_settings['ProjectSettings']['DevDependencies'] + ) packages = self._get_installed_packages() self.assertIn(package_name, packages) self.assertEqual(version, packages[package_name]) diff --git a/unittests/unittests_cli/uninstall_test_case.py b/unittests/unittests_cli/uninstall_test_case.py index ac29aaf3..d81bddf9 100644 --- a/unittests/unittests_cli/uninstall_test_case.py +++ b/unittests/unittests_cli/uninstall_test_case.py @@ -33,7 +33,6 @@ class UninstallTestCase(unittest.TestCase): # create projects CLICommands.new('console', self._source, '--ab', '--s') os.chdir(os.path.join(os.getcwd(), self._source)) - CLICommands.install(self._package) def cleanUp(self): # remove projects @@ -47,6 +46,7 @@ class UninstallTestCase(unittest.TestCase): return dict([tuple(r.decode().split('==')) for r in reqs.split()]) def test_uninstall(self): + CLICommands.install(self._package) CLICommands.uninstall(self._package) settings = self._get_project_settings() self.assertNotEqual(settings, {}) @@ -56,5 +56,28 @@ class UninstallTestCase(unittest.TestCase): self._package, settings['ProjectSettings']['Dependencies'] ) + self.assertNotIn( + self._package, + settings['ProjectSettings']['DevDependencies'] + ) + packages = self._get_installed_packages() + self.assertNotIn(self._package_name, packages) + + def test_dev_uninstall(self): + CLICommands.install(self._package, is_dev=True) + CLICommands.uninstall(self._package, is_dev=True) + settings = self._get_project_settings() + self.assertNotEqual(settings, {}) + self.assertIn('ProjectSettings', settings) + self.assertIn('Dependencies', settings['ProjectSettings']) + self.assertIn('DevDependencies', settings['ProjectSettings']) + self.assertNotIn( + self._package, + settings['ProjectSettings']['Dependencies'] + ) + self.assertNotIn( + self._package, + settings['ProjectSettings']['DevDependencies'] + ) packages = self._get_installed_packages() self.assertNotIn(self._package_name, packages) diff --git a/unittests/unittests_shared/cli_commands.py b/unittests/unittests_shared/cli_commands.py index a458390c..70570bc7 100644 --- a/unittests/unittests_shared/cli_commands.py +++ b/unittests/unittests_shared/cli_commands.py @@ -44,12 +44,12 @@ class CLICommands: cls._run('generate', schematic, name, output=output) @classmethod - def install(cls, package: str = None, output=False): + def install(cls, package: str = None, is_dev=False, output=False): if package is None: cls._run('install', output=output) return - cls._run('install', package, output=output) + cls._run('install', package, '--dev' if is_dev else '', output=output) @classmethod def new(cls, project_type: str, name: str, *args, output=False): @@ -75,8 +75,8 @@ class CLICommands: cls._run('start', output=output) @classmethod - def uninstall(cls, package: str, output=False): - cls._run('uninstall', package, output=output) + def uninstall(cls, package: str, is_dev=False, output=False): + cls._run('uninstall', package, '--dev' if is_dev else '', output=output) @classmethod def update(cls, output=False):