Updated docs

This commit is contained in:
2023-02-20 15:55:20 +01:00
parent 48d0daabf5
commit 9e28dce5ce
632 changed files with 10917 additions and 6775 deletions

View File

@@ -1,21 +1,21 @@
# -*- coding: utf-8 -*-
"""
cpl-cli sh-edraft Common Python library CLI
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
sh-edraft Common Python library Command Line Interface
CPL Command Line Interface
:copyright: (c) 2020 - 2023 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = 'cpl_cli.command'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2023 sh-edraft.de'
__version__ = '2022.12.1'
__title__ = "cpl_cli.command"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2023 sh-edraft.de"
__version__ = "2023.2.0"
from collections import namedtuple
@@ -28,5 +28,5 @@ from .new_service import NewService
from .publish_service import PublishService
from .version_service import VersionService
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2022', minor='12', micro='1')
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2023", minor="2", micro="0")

View File

@@ -14,7 +14,6 @@ from cpl_cli.configuration.workspace_settings import WorkspaceSettings
class AddService(CommandABC):
def __init__(self, config: ConfigurationABC, workspace: WorkspaceSettings):
"""
Service for CLI command add
@@ -27,23 +26,30 @@ class AddService(CommandABC):
@property
def help_message(self) -> str:
return textwrap.dedent("""\
return textwrap.dedent(
"""\
Adds a project reference to given project.
Usage: cpl add <source-project> <target-project>
Arguments:
source-project: Name of the project to which the reference has to be
target-project: Name of the project to be referenced
""")
"""
)
def _edit_project_file(self, source: str, project_settings: ProjectSettings, build_settings: BuildSettings):
if self._is_simulation:
return
with open(source, 'w') as file:
file.write(json.dumps({
ProjectSettings.__name__: SettingsHelper.get_project_settings_dict(project_settings),
BuildSettings.__name__: SettingsHelper.get_build_settings_dict(build_settings)
}, indent=2))
with open(source, "w") as file:
file.write(
json.dumps(
{
ProjectSettings.__name__: SettingsHelper.get_project_settings_dict(project_settings),
BuildSettings.__name__: SettingsHelper.get_build_settings_dict(build_settings),
},
indent=2,
)
)
file.close()
def execute(self, args: list[str]):
@@ -52,17 +58,17 @@ class AddService(CommandABC):
:param args:
:return:
"""
if 'simulate' in args:
args.remove('simulate')
Console.write_line('Running in simulation mode:')
if "simulate" in args:
args.remove("simulate")
Console.write_line("Running in simulation mode:")
self._is_simulation = True
if len(args) == 0:
Console.error('Expected source and target project')
Console.error("Expected source and target project")
return
elif len(args) == 1:
Console.error('Expected target project')
Console.error("Expected target project")
return
elif len(args) > 2:
@@ -103,30 +109,30 @@ class AddService(CommandABC):
is_invalid_source = True
if is_invalid_source:
Console.error(f'Invalid source: {source}')
Console.error(f"Invalid source: {source}")
return
if is_invalid_target or source == target or not os.path.isfile(target):
Console.error(f'Invalid target: {target}')
Console.error(f"Invalid target: {target}")
return
if self._workspace is None:
target = f'../{target}'
target = f"../{target}"
else:
target = target.replace('src', '..')
target = target.replace("src", "..")
if target in build_settings.project_references:
Console.error(f'Project reference already exists.')
Console.error(f"Project reference already exists.")
return
build_settings.project_references.append(target)
Console.spinner(
f'Editing {source}',
f"Editing {source}",
self._edit_project_file,
source,
project_settings,
build_settings,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
spinner_foreground_color=ForegroundColorEnum.cyan,
)

View File

@@ -5,7 +5,6 @@ from cpl_cli.publish.publisher_abc import PublisherABC
class BuildService(CommandABC):
def __init__(self, publisher: PublisherABC):
"""
Service for the CLI command build
@@ -17,10 +16,12 @@ class BuildService(CommandABC):
@property
def help_message(self) -> str:
return textwrap.dedent("""\
return textwrap.dedent(
"""\
Copies an python app into an output directory named build/ at the given output path. Must be executed within a CPL workspace or project directory
Usage: cpl build
""")
"""
)
def execute(self, args: list[str]):
"""

View File

@@ -10,7 +10,6 @@ from cpl_cli.configuration.workspace_settings import WorkspaceSettings
class CustomScriptService(CommandABC):
def __init__(self, config: ConfigurationABC, env: ApplicationEnvironmentABC, ws: WorkspaceSettings):
"""
Service for CLI scripts
@@ -23,11 +22,11 @@ class CustomScriptService(CommandABC):
@property
def help_message(self) -> str:
return ''
return ""
def execute(self, args: list[str]):
cmd = self._config.get_configuration('ACTIVE_EXECUTABLE')
wd = self._config.get_configuration('PATH_WORKSPACE')
cmd = self._config.get_configuration("ACTIVE_EXECUTABLE")
wd = self._config.get_configuration("PATH_WORKSPACE")
if wd is not None:
self._env.set_working_directory(wd)
@@ -35,16 +34,16 @@ class CustomScriptService(CommandABC):
if script != cmd:
continue
command = ''
external_args = self._config.get_configuration('ARGS')
command = ""
external_args = self._config.get_configuration("ARGS")
if external_args is not None:
command += f'ARGS="{external_args}";'
command += self._workspace.scripts[script]
env_vars = os.environ
env_vars['CPL_ARGS'] = " ".join(args)
env_vars["CPL_ARGS"] = " ".join(args)
try:
subprocess.run(command, shell=True if os.name == 'posix' else None)
subprocess.run(command, shell=True if os.name == "posix" else None)
except Exception as e:
Console.error(str(e))

View File

@@ -16,11 +16,10 @@ from cpl_core.utils.string import String
class GenerateService(CommandABC):
def __init__(
self,
configuration: ConfigurationABC,
workspace: WorkspaceSettings,
self,
configuration: ConfigurationABC,
workspace: WorkspaceSettings,
):
"""
Service for the CLI command generate
@@ -37,7 +36,7 @@ class GenerateService(CommandABC):
self._schematic_classes = set()
for package_name, version in Dependencies.get_cpl_packages():
if package_name == 'cpl-cli':
if package_name == "cpl-cli":
continue
package = importlib.import_module(String.convert_to_snake_case(package_name))
self._read_custom_schematics_from_path(os.path.dirname(package.__file__))
@@ -46,7 +45,7 @@ class GenerateService(CommandABC):
self._read_custom_schematics_from_path(self._env.runtime_directory)
if len(self._schematic_classes) == 0:
Console.error(f'No schematics found in template directory: .cpl')
Console.error(f"No schematics found in template directory: .cpl")
sys.exit()
known_schematics = []
@@ -60,13 +59,14 @@ class GenerateService(CommandABC):
def help_message(self) -> str:
schematics = []
for schematic in self._schematics:
aliases = '|'.join(self._schematics[schematic]['Aliases'])
aliases = "|".join(self._schematics[schematic]["Aliases"])
schematic_str = schematic
if len(aliases) > 0:
schematic_str = f'{schematic} ({aliases})'
schematic_str = f"{schematic} ({aliases})"
schematics.append(schematic_str)
help_msg = textwrap.dedent("""\
help_msg = textwrap.dedent(
"""\
Generate a file based on schematic.
Usage: cpl generate <schematic> <name>
@@ -74,24 +74,25 @@ class GenerateService(CommandABC):
schematic: The schematic to generate.
name: The name of the generated file
Schematics:""")
Schematics:"""
)
for schematic in schematics:
help_msg += f'\n {schematic}'
help_msg += f"\n {schematic}"
return help_msg
def _read_custom_schematics_from_path(self, path: str):
if not os.path.exists(os.path.join(path, '.cpl')):
if not os.path.exists(os.path.join(path, ".cpl")):
return
sys.path.insert(0, os.path.join(path, '.cpl'))
for r, d, f in os.walk(os.path.join(path, '.cpl')):
sys.path.insert(0, os.path.join(path, ".cpl"))
for r, d, f in os.walk(os.path.join(path, ".cpl")):
for file in f:
if not file.startswith('schematic_') or not file.endswith('.py'):
if not file.startswith("schematic_") or not file.endswith(".py"):
continue
try:
exec(open(os.path.join(r, file), 'r').read())
exec(open(os.path.join(r, file), "r").read())
self._schematic_classes.update(GenerateSchematicABC.__subclasses__())
except Exception as e:
Console.error(str(e), traceback.format_exc())
@@ -105,30 +106,32 @@ class GenerateService(CommandABC):
:param value:
:return:
"""
with open(file_path, 'w') as template:
with open(file_path, "w") as template:
template.write(value)
template.close()
def _create_init_files(self, file_path: str, template: GenerateSchematicABC, class_name: str, schematic: str, rel_path: str):
def _create_init_files(
self, file_path: str, template: GenerateSchematicABC, class_name: str, schematic: str, rel_path: str
):
if not os.path.isdir(os.path.dirname(file_path)):
os.makedirs(os.path.dirname(file_path))
directory = ''
for subdir in template.path.split('/'):
directory = ""
for subdir in template.path.split("/"):
directory = os.path.join(directory, subdir)
if subdir == 'src':
if subdir == "src":
continue
file = self._schematics['init']['Template'](class_name, 'init', rel_path)
file = self._schematics["init"]["Template"](class_name, "init", rel_path)
if os.path.exists(os.path.join(os.path.abspath(directory), file.name)):
continue
Console.spinner(
f'Creating {os.path.abspath(directory)}/{file.name}',
f"Creating {os.path.abspath(directory)}/{file.name}",
self._create_file,
os.path.join(os.path.abspath(directory), file.name),
file.get_code(),
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
spinner_foreground_color=ForegroundColorEnum.cyan,
)
def _generate(self, schematic: str, name: str, template: type):
@@ -140,10 +143,10 @@ class GenerateService(CommandABC):
:return:
"""
class_name = name
rel_path = ''
if '/' in name:
parts = name.split('/')
rel_path = '/'.join(parts[:-1])
rel_path = ""
if "/" in name:
parts = name.split("/")
rel_path = "/".join(parts[:-1])
class_name = parts[len(parts) - 1]
if self._workspace is not None and parts[0] in self._workspace.projects:
@@ -155,12 +158,12 @@ class GenerateService(CommandABC):
self._create_init_files(file_path, template, class_name, schematic, rel_path)
if os.path.isfile(file_path):
Console.error(f'{String.first_to_upper(schematic)} already exists!\n')
Console.error(f"{String.first_to_upper(schematic)} already exists!\n")
sys.exit()
message = f'Creating {self._env.working_directory}/{template.path}/{template.name}'
if template.path == '':
message = f'Creating {self._env.working_directory}/{template.name}'
message = f"Creating {self._env.working_directory}/{template.path}/{template.name}"
if template.path == "":
message = f"Creating {self._env.working_directory}/{template.name}"
Console.spinner(
message,
@@ -168,12 +171,12 @@ class GenerateService(CommandABC):
file_path,
template.get_code(),
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
spinner_foreground_color=ForegroundColorEnum.cyan,
)
def _get_schematic_by_alias(self, schematic: str) -> str:
for key in self._schematics:
if schematic in self._schematics[key]['Aliases']:
if schematic in self._schematics[key]["Aliases"]:
return key
return schematic
@@ -192,25 +195,29 @@ class GenerateService(CommandABC):
schematic = s
break
if schematic is None and len(args) >= 1 and (args[0] in self._schematics or self._get_schematic_by_alias(args[0]) != args[0]):
if (
schematic is None
and len(args) >= 1
and (args[0] in self._schematics or self._get_schematic_by_alias(args[0]) != args[0])
):
schematic = self._get_schematic_by_alias(args[0])
self._config.add_configuration(schematic, args[1])
value = args[1]
if schematic is None:
Console.error(f'Schematic not found')
Console.error(f"Schematic not found")
Console.write_line(self.help_message)
sys.exit()
name = value
if name is None:
name = Console.read(f'Name for the {args[0]}: ')
name = Console.read(f"Name for the {args[0]}: ")
if schematic in self._schematics:
s = self._schematics[schematic]
self._generate(schematic, name, s["Template"])
else:
self._help('Usage: cpl generate <schematic> [options]')
self._help("Usage: cpl generate <schematic> [options]")
Console.write_line()
sys.exit()

View File

@@ -8,7 +8,6 @@ from cpl_cli.command_abc import CommandABC
class HelpService(CommandABC):
def __init__(self, services: ServiceProviderABC):
"""
Service for CLI command help
@@ -19,10 +18,12 @@ class HelpService(CommandABC):
@property
def help_message(self) -> str:
return textwrap.dedent("""\
return textwrap.dedent(
"""\
Lists available command and their short descriptions.
Usage: cpl help
""")
"""
)
def execute(self, args: list[str]):
"""
@@ -34,24 +35,33 @@ class HelpService(CommandABC):
Console.error(f'Unexpected argument(s): {", ".join(args)}')
sys.exit()
Console.write_line('Available Commands:')
Console.write_line("Available Commands:")
commands = [
['add (a|a)', 'Adds a project reference to given project.'],
['build (b|B)', 'Prepares files for publish into an output directory named dist/ at the given output path. Must be executed from within a workspace directory.'],
['generate (g|G)', 'Generate a new file.'],
['help (h|H)', 'Lists available command and their short descriptions.'],
['install (i|I)', 'With argument installs packages to project, without argument installs project dependencies.'],
['new (n|N)', 'Creates new CPL project.'],
['publish (p|P)', 'Prepares files for publish into an output directory named dist/ at the given output path and executes setup.py. Must be executed from within a library workspace directory.'],
['remove (r|R)', 'Removes a project from workspace.'],
['start (s|S)', 'Starts CPL project, restarting on file changes.'],
['uninstall (ui|UI)', 'Uninstalls packages from project.'],
['update (u|u)', 'Update CPL and project dependencies.'],
['version (v|V)', 'Outputs CPL CLI version.']
["add (a|a)", "Adds a project reference to given project."],
[
"build (b|B)",
"Prepares files for publish into an output directory named dist/ at the given output path. Must be executed from within a workspace directory.",
],
["generate (g|G)", "Generate a new file."],
["help (h|H)", "Lists available command and their short descriptions."],
[
"install (i|I)",
"With argument installs packages to project, without argument installs project dependencies.",
],
["new (n|N)", "Creates new CPL project."],
[
"publish (p|P)",
"Prepares files for publish into an output directory named dist/ at the given output path and executes setup.py. Must be executed from within a library workspace directory.",
],
["remove (r|R)", "Removes a project from workspace."],
["start (s|S)", "Starts CPL project, restarting on file changes."],
["uninstall (ui|UI)", "Uninstalls packages from project."],
["update (u|u)", "Update CPL and project dependencies."],
["version (v|V)", "Outputs CPL CLI version."],
]
for name, description in commands:
Console.set_foreground_color(ForegroundColorEnum.blue)
Console.write(f'\n\t{name} ')
Console.write(f"\n\t{name} ")
Console.set_foreground_color(ForegroundColorEnum.default)
Console.write(f'{description}')
Console.write_line('\nRun \'cpl <command> --help\' for command specific information\'s\n')
Console.write(f"{description}")
Console.write_line("\nRun 'cpl <command> --help' for command specific information's\n")

View File

@@ -21,9 +21,14 @@ from cpl_core.utils.pip import Pip
class InstallService(CommandABC):
def __init__(self, config: ConfigurationABC, env: ApplicationEnvironmentABC, build_settings: BuildSettings,
project_settings: ProjectSettings, cli_settings: CLISettings):
def __init__(
self,
config: ConfigurationABC,
env: ApplicationEnvironmentABC,
build_settings: BuildSettings,
project_settings: ProjectSettings,
cli_settings: CLISettings,
):
"""
Service for the CLI command install
:param config:
@@ -44,17 +49,19 @@ class InstallService(CommandABC):
self._is_virtual = False
self._is_dev = False
self._project_file = f'{self._project_settings.name}.json'
self._project_file = f"{self._project_settings.name}.json"
@property
def help_message(self) -> str:
return textwrap.dedent("""\
return textwrap.dedent(
"""\
Installs given package via pip
Usage: cpl install <package>
Arguments:
package The package to install
""")
"""
)
def _wait(self, t: int, *args, source: str = None, stdout=None, stderr=None):
time.sleep(t)
@@ -65,43 +72,45 @@ class InstallService(CommandABC):
:return:
"""
if self._project_settings is None or self._build_settings is None:
Error.error('The command requires to be run in an CPL project, but a project could not be found.')
Error.error("The command requires to be run in an CPL project, but a project could not be found.")
return
if self._project_settings.dependencies is None:
Error.error(f'Found invalid dependencies in {self._project_file}.')
Error.error(f"Found invalid dependencies in {self._project_file}.")
return
for dependency in self._project_settings.dependencies:
Console.spinner(
f'Installing: {dependency}',
Pip.install if not self._is_virtual else self._wait, dependency if not self._is_virtual else 2,
'--upgrade',
f"Installing: {dependency}",
Pip.install if not self._is_virtual else self._wait,
dependency if not self._is_virtual else 2,
"--upgrade",
source=self._cli_settings.pip_path,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
spinner_foreground_color=ForegroundColorEnum.cyan,
)
local_package = Pip.get_package(dependency)
if local_package is None:
Error.warn(f'Installation of package {dependency} failed!')
Error.warn(f"Installation of package {dependency} failed!")
return
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,
'--upgrade',
f"Installing dev: {dependency}",
Pip.install if not self._is_virtual else self._wait,
dependency if not self._is_virtual else 2,
"--upgrade",
source=self._cli_settings.pip_path,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
spinner_foreground_color=ForegroundColorEnum.cyan,
)
local_package = Pip.get_package(dependency)
if local_package is None:
Error.warn(f'Installation of package {dependency} failed!')
Error.warn(f"Installation of package {dependency} failed!")
return
if not self._is_virtual:
@@ -115,18 +124,18 @@ class InstallService(CommandABC):
"""
is_already_in_project = False
if self._project_settings is None or self._build_settings is None:
Error.error('The command requires to be run in an CPL project, but a project could not be found.')
Error.error("The command requires to be run in an CPL project, but a project could not be found.")
return
if self._project_settings.dependencies is None:
Error.error(f'Found invalid dependencies in {self._project_file}.')
Error.error(f"Found invalid dependencies in {self._project_file}.")
return
package_version = ''
package_version = ""
name = package
if '==' in package:
name = package.split('==')[0]
package_version = package.split('==')[1]
if "==" in package:
name = package.split("==")[0]
package_version = package.split("==")[1]
to_remove_list = []
deps = self._project_settings.dependencies
@@ -134,13 +143,13 @@ class InstallService(CommandABC):
deps = self._project_settings.dev_dependencies
for dependency in deps:
dependency_version = ''
dependency_version = ""
if '==' in dependency:
dependency_version = dependency.split('==')[1]
if "==" in dependency:
dependency_version = dependency.split("==")[1]
if name in dependency:
if package_version != '' and version.parse(package_version) != version.parse(dependency_version):
if package_version != "" and version.parse(package_version) != version.parse(dependency_version):
to_remove_list.append(dependency)
break
else:
@@ -154,45 +163,48 @@ class InstallService(CommandABC):
local_package = Pip.get_package(package)
if local_package is not None and local_package in self._project_settings.dependencies:
Error.warn(f'Package {local_package} is already installed.')
Error.warn(f"Package {local_package} is already installed.")
return
elif is_already_in_project:
Error.warn(f'Package {package} is already installed.')
Error.warn(f"Package {package} is already installed.")
return
Console.spinner(
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,
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,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
spinner_foreground_color=ForegroundColorEnum.cyan,
)
if self._is_virtual:
new_package = name
else:
new_package = Pip.get_package(name)
if new_package is None \
or '==' in package and \
version.parse(package.split('==')[1]) != version.parse(new_package.split('==')[1]):
Console.error(f'Installation of package {package} failed')
if (
new_package is None
or "==" in package
and version.parse(package.split("==")[1]) != version.parse(new_package.split("==")[1])
):
Console.error(f"Installation of package {package} failed")
return
if not is_already_in_project:
new_name = package
if '==' in new_package:
if "==" in new_package:
new_name = new_package
elif '==' in name:
elif "==" in name:
new_name = name
if '/' in new_name:
new_name = new_name.split('/')[0]
if "/" in new_name:
new_name = new_name.split("/")[0]
if '\r' in new_name:
new_name = new_name.replace('\r', '')
if "\r" in new_name:
new_name = new_name.replace("\r", "")
if self._is_dev:
self._project_settings.dev_dependencies.append(new_name)
@@ -202,10 +214,10 @@ class InstallService(CommandABC):
if not self._is_simulation:
config = {
ProjectSettings.__name__: SettingsHelper.get_project_settings_dict(self._project_settings),
BuildSettings.__name__: SettingsHelper.get_build_settings_dict(self._build_settings)
BuildSettings.__name__: SettingsHelper.get_build_settings_dict(self._build_settings),
}
with open(os.path.join(self._env.working_directory, self._project_file), 'w') as project_file:
with open(os.path.join(self._env.working_directory, self._project_file), "w") as project_file:
project_file.write(json.dumps(config, indent=2))
project_file.close()
@@ -217,31 +229,31 @@ class InstallService(CommandABC):
:param args:
:return:
"""
if 'dev' in args:
if "dev" in args:
self._is_dev = True
args.remove('dev')
args.remove("dev")
if 'virtual' in args:
if "virtual" in args:
self._is_virtual = True
args.remove('virtual')
Console.write_line('Running in virtual mode:')
args.remove("virtual")
Console.write_line("Running in virtual mode:")
if 'simulate' in args:
if "simulate" in args:
self._is_simulation = True
args.remove('simulate')
Console.write_line('Running in simulation mode:')
args.remove("simulate")
Console.write_line("Running in simulation mode:")
if 'cpl-prod' in args:
args.remove('cpl-prod')
self._cli_settings.from_dict({'PipPath': 'https://pip.sh-edraft.de'})
if "cpl-prod" in args:
args.remove("cpl-prod")
self._cli_settings.from_dict({"PipPath": "https://pip.sh-edraft.de"})
if 'cpl-exp' in args:
args.remove('cpl-exp')
self._cli_settings.from_dict({'PipPath': 'https://pip-exp.sh-edraft.de'})
if "cpl-exp" in args:
args.remove("cpl-exp")
self._cli_settings.from_dict({"PipPath": "https://pip-exp.sh-edraft.de"})
if 'cpl-dev' in args:
args.remove('cpl-dev')
self._cli_settings.from_dict({'PipPath': 'https://pip-dev.sh-edraft.de'})
if "cpl-dev" in args:
args.remove("cpl-dev")
self._cli_settings.from_dict({"PipPath": "https://pip-dev.sh-edraft.de"})
VenvHelper.init_venv(self._is_virtual, self._env, self._project_settings)

View File

@@ -28,7 +28,6 @@ from cpl_core.utils.string import String
class NewService(CommandABC):
def __init__(self, configuration: ConfigurationABC):
"""
Service for the CLI command new
@@ -47,8 +46,8 @@ class NewService(CommandABC):
self._project_type_classes = set()
self._name: str = ''
self._rel_path: str = ''
self._name: str = ""
self._rel_path: str = ""
self._project_type: ProjectTypeEnum = ProjectTypeEnum.console
self._use_nothing: bool = False
self._use_application_api: bool = False
@@ -60,7 +59,8 @@ class NewService(CommandABC):
@property
def help_message(self) -> str:
return textwrap.dedent("""\
return textwrap.dedent(
"""\
Generates a workspace and initial project or add a project to workspace.
Usage: cpl new <type> <name>
@@ -72,37 +72,32 @@ class NewService(CommandABC):
console (c|C)
library (l|L)
unittest (ut|UT)
""")
"""
)
def _create_project_settings(self):
self._rel_path = os.path.dirname(self._name)
self._project_dict = {
ProjectSettingsNameEnum.name.value: os.path.basename(self._name),
ProjectSettingsNameEnum.version.value: {
VersionSettingsNameEnum.major.value: '0',
VersionSettingsNameEnum.minor.value: '0',
VersionSettingsNameEnum.micro.value: '0'
VersionSettingsNameEnum.major.value: "0",
VersionSettingsNameEnum.minor.value: "0",
VersionSettingsNameEnum.micro.value: "0",
},
ProjectSettingsNameEnum.author.value: '',
ProjectSettingsNameEnum.author_email.value: '',
ProjectSettingsNameEnum.description.value: '',
ProjectSettingsNameEnum.long_description.value: '',
ProjectSettingsNameEnum.url.value: '',
ProjectSettingsNameEnum.copyright_date.value: '',
ProjectSettingsNameEnum.copyright_name.value: '',
ProjectSettingsNameEnum.license_name.value: '',
ProjectSettingsNameEnum.license_description.value: '',
ProjectSettingsNameEnum.dependencies.value: [
f'cpl-core>={version.parse(cpl_core.__version__)}'
],
ProjectSettingsNameEnum.dev_dependencies.value: [
f'cpl-cli>={version.parse(cpl_cli.__version__)}'
],
ProjectSettingsNameEnum.author.value: "",
ProjectSettingsNameEnum.author_email.value: "",
ProjectSettingsNameEnum.description.value: "",
ProjectSettingsNameEnum.long_description.value: "",
ProjectSettingsNameEnum.url.value: "",
ProjectSettingsNameEnum.copyright_date.value: "",
ProjectSettingsNameEnum.copyright_name.value: "",
ProjectSettingsNameEnum.license_name.value: "",
ProjectSettingsNameEnum.license_description.value: "",
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/' if self._use_venv else ''
},
ProjectSettingsNameEnum.classifiers.value: []
ProjectSettingsNameEnum.python_path.value: {sys.platform: "../../venv/" if self._use_venv else ""},
ProjectSettingsNameEnum.classifiers.value: [],
}
self._project.from_dict(self._project_dict)
@@ -110,19 +105,15 @@ class NewService(CommandABC):
def _create_build_settings(self, project_type: str):
self._build_dict = {
BuildSettingsNameEnum.project_type.value: project_type,
BuildSettingsNameEnum.source_path.value: '',
BuildSettingsNameEnum.output_path.value: '../../dist',
BuildSettingsNameEnum.main.value: f'{String.convert_to_snake_case(self._project.name)}.main',
BuildSettingsNameEnum.source_path.value: "",
BuildSettingsNameEnum.output_path.value: "../../dist",
BuildSettingsNameEnum.main.value: f"{String.convert_to_snake_case(self._project.name)}.main",
BuildSettingsNameEnum.entry_point.value: self._project.name,
BuildSettingsNameEnum.include_package_data.value: False,
BuildSettingsNameEnum.included.value: [],
BuildSettingsNameEnum.excluded.value: [
'*/__pycache__',
'*/logs',
'*/tests'
],
BuildSettingsNameEnum.excluded.value: ["*/__pycache__", "*/logs", "*/tests"],
BuildSettingsNameEnum.package_data.value: {},
BuildSettingsNameEnum.project_references.value: []
BuildSettingsNameEnum.project_references.value: [],
}
self._build.from_dict(self._build_dict)
@@ -131,10 +122,7 @@ class NewService(CommandABC):
Creates cpl.json content
:return:
"""
self._project_json = {
ProjectSettings.__name__: self._project_dict,
BuildSettings.__name__: self._build_dict
}
self._project_json = {ProjectSettings.__name__: self._project_dict, BuildSettings.__name__: self._build_dict}
def _get_project_path(self) -> Optional[str]:
"""
@@ -144,12 +132,14 @@ class NewService(CommandABC):
if self._workspace is None:
project_path = os.path.join(self._env.working_directory, self._rel_path, self._project.name)
else:
base = '' if self._use_base else 'src'
project_path = os.path.join(self._env.working_directory, base, self._rel_path, String.convert_to_snake_case(self._project.name))
base = "" if self._use_base else "src"
project_path = os.path.join(
self._env.working_directory, base, self._rel_path, String.convert_to_snake_case(self._project.name)
)
if os.path.isdir(project_path) and len(os.listdir(project_path)) > 0:
Console.write_line(project_path)
Console.error('Project path is not empty\n')
Console.error("Project path is not empty\n")
return None
return project_path
@@ -159,27 +149,33 @@ class NewService(CommandABC):
Gets project information's from user
:return:
"""
is_unittest = project_type == 'unittest'
is_library = project_type == 'library'
is_unittest = project_type == "unittest"
is_library = project_type == "library"
if is_library:
return
if self._use_application_api or self._use_startup or self._use_service_providing or self._use_async or self._use_nothing:
if (
self._use_application_api
or self._use_startup
or self._use_service_providing
or self._use_async
or self._use_nothing
):
Console.set_foreground_color(ForegroundColorEnum.default)
Console.write_line('Skipping question due to given flags')
Console.write_line("Skipping question due to given flags")
return
if not is_unittest and not is_library:
self._use_application_api = Console.read('Do you want to use application base? (y/n) ').lower() == 'y'
self._use_application_api = Console.read("Do you want to use application base? (y/n) ").lower() == "y"
if not is_unittest and self._use_application_api:
self._use_startup = Console.read('Do you want to use startup? (y/n) ').lower() == 'y'
self._use_startup = Console.read("Do you want to use startup? (y/n) ").lower() == "y"
if not is_unittest and not self._use_application_api:
self._use_service_providing = Console.read('Do you want to use service providing? (y/n) ').lower() == 'y'
self._use_service_providing = Console.read("Do you want to use service providing? (y/n) ").lower() == "y"
if not self._use_async:
self._use_async = Console.read('Do you want to use async? (y/n) ').lower() == 'y'
self._use_async = Console.read("Do you want to use async? (y/n) ").lower() == "y"
Console.set_foreground_color(ForegroundColorEnum.default)
@@ -189,30 +185,32 @@ class NewService(CommandABC):
project = self._workspace.default_project
if self._env.working_directory.endswith(project):
project = ''
project = ""
if self._workspace is None and self._use_base:
project = f'{self._rel_path}/{project}'
project = f"{self._rel_path}/{project}"
VenvHelper.init_venv(
False,
self._env,
self._project,
explicit_path=os.path.join(self._env.working_directory, project, self._project.python_executable.replace('../', ''))
explicit_path=os.path.join(
self._env.working_directory, project, self._project.python_executable.replace("../", "")
),
)
def _read_custom_project_types_from_path(self, path: str):
if not os.path.exists(os.path.join(path, '.cpl')):
if not os.path.exists(os.path.join(path, ".cpl")):
return
sys.path.insert(0, os.path.join(path, '.cpl'))
for r, d, f in os.walk(os.path.join(path, '.cpl')):
sys.path.insert(0, os.path.join(path, ".cpl"))
for r, d, f in os.walk(os.path.join(path, ".cpl")):
for file in f:
if file.startswith('project_file_') or not file.startswith('project_') or not file.endswith('.py'):
if file.startswith("project_file_") or not file.startswith("project_") or not file.endswith(".py"):
continue
try:
exec(open(os.path.join(r, file), 'r').read())
exec(open(os.path.join(r, file), "r").read())
self._project_type_classes.update(ProjectTypeABC.__subclasses__())
except Exception as e:
Console.error(str(e), traceback.format_exc())
@@ -220,7 +218,7 @@ class NewService(CommandABC):
def _create_project(self, project_type: str):
for package_name in Dependencies.get_cpl_packages():
if package_name == 'cpl-cli':
if package_name == "cpl-cli":
continue
package = importlib.import_module(String.convert_to_snake_case(package_name[0]))
self._read_custom_project_types_from_path(os.path.dirname(package.__file__))
@@ -229,7 +227,7 @@ class NewService(CommandABC):
self._read_custom_project_types_from_path(self._env.runtime_directory)
if len(self._project_type_classes) == 0:
Console.error(f'No project types found in template directory: .cpl')
Console.error(f"No project types found in template directory: .cpl")
sys.exit()
project_class = None
@@ -242,7 +240,7 @@ class NewService(CommandABC):
project_class = p
if project_class is None:
Console.error(f'Project type {project_type} not found in template directory: .cpl/')
Console.error(f"Project type {project_type} not found in template directory: .cpl/")
sys.exit()
project_type = String.convert_to_snake_case(project_class.__name__)
@@ -255,60 +253,60 @@ class NewService(CommandABC):
self._get_project_information(project_type)
project_name = self._project.name
if self._rel_path != '':
project_name = f'{self._rel_path}/{project_name}'
if self._rel_path != "":
project_name = f"{self._rel_path}/{project_name}"
base = 'src/'
split_project_name = project_name.split('/')
base = "src/"
split_project_name = project_name.split("/")
if self._use_base and len(split_project_name) > 0:
base = f'{split_project_name[0]}/'
base = f"{split_project_name[0]}/"
project = project_class(
base if self._workspace is not None else 'src/',
base if self._workspace is not None else "src/",
project_name,
self._workspace,
self._use_application_api,
self._use_startup,
self._use_service_providing,
self._use_async,
self._project_json
self._project_json,
)
if self._workspace is None:
TemplateBuilder.create_workspace(
f'{project_name}/cpl-workspace.json',
project_name.split('/')[-1],
f"{project_name}/cpl-workspace.json",
project_name.split("/")[-1],
{
project_name: f'{base if self._workspace is not None else "src/"}{String.convert_to_snake_case(project_name)}/{project_name}.json'
},
{}
{},
)
else:
self._workspace.projects[project_name] = f'{base if self._workspace is not None else "src/"}{String.convert_to_snake_case(project_name)}/{project_name}.json'
TemplateBuilder.create_workspace('cpl-workspace.json', self._workspace.default_project, self._workspace.projects, self._workspace.scripts)
self._workspace.projects[
project_name
] = f'{base if self._workspace is not None else "src/"}{String.convert_to_snake_case(project_name)}/{project_name}.json'
TemplateBuilder.create_workspace(
"cpl-workspace.json", self._workspace.default_project, self._workspace.projects, self._workspace.scripts
)
for template in project.templates:
rel_base = '/'.join(project_name.split('/')[:-1])
template_path_base = template.path.split('/')[0]
if not self._use_base and rel_base != '' and template_path_base != '' and template_path_base != rel_base:
template.path = template.path.replace(f'{template_path_base}/', f'{template_path_base}/{rel_base}/')
rel_base = "/".join(project_name.split("/")[:-1])
template_path_base = template.path.split("/")[0]
if not self._use_base and rel_base != "" and template_path_base != "" and template_path_base != rel_base:
template.path = template.path.replace(f"{template_path_base}/", f"{template_path_base}/{rel_base}/")
if template.name.endswith(f'{project_name.split("/")[-1]}.json'):
pass
file_path = os.path.join(
project_name if self._workspace is None else '',
template.path,
template.name
)
file_path = os.path.join(project_name if self._workspace is None else "", template.path, template.name)
Console.spinner(
f'Creating {file_path}',
f"Creating {file_path}",
TemplateBuilder.build,
file_path,
template,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
spinner_foreground_color=ForegroundColorEnum.cyan,
)
if self._use_venv:
@@ -320,42 +318,42 @@ class NewService(CommandABC):
:param args:
:return:
"""
if 'nothing' in args:
if "nothing" in args:
self._use_nothing = True
self._use_async = False
self._use_application_api = False
self._use_startup = False
self._use_service_providing = False
if 'async' in args:
args.remove('async')
if 'application-base' in args:
args.remove('application-base')
if 'startup' in args:
args.remove('startup')
if 'service-providing' in args:
args.remove('service-providing')
if "async" in args:
args.remove("async")
if "application-base" in args:
args.remove("application-base")
if "startup" in args:
args.remove("startup")
if "service-providing" in args:
args.remove("service-providing")
if 'async' in args:
if "async" in args:
self._use_async = True
args.remove('async')
if 'application-base' in args:
args.remove("async")
if "application-base" in args:
self._use_application_api = True
args.remove('application-base')
if 'startup' in args:
args.remove("application-base")
if "startup" in args:
self._use_startup = True
args.remove('startup')
if 'service-providing' in args:
args.remove("startup")
if "service-providing" in args:
self._use_service_providing = True
args.remove('service-providing')
if 'venv' in args:
args.remove("service-providing")
if "venv" in args:
self._use_venv = True
args.remove('venv')
if 'base' in args:
args.remove("venv")
if "base" in args:
self._use_base = True
args.remove('base')
args.remove("base")
if len(args) <= 1:
Console.error(f'Project type not found')
Console.error(f"Project type not found")
Console.write_line(self.help_message)
return

View File

@@ -5,7 +5,6 @@ from cpl_cli.publish.publisher_abc import PublisherABC
class PublishService(CommandABC):
def __init__(self, publisher: PublisherABC):
"""
Service for the CLI command publish
@@ -17,10 +16,12 @@ class PublishService(CommandABC):
@property
def help_message(self) -> str:
return textwrap.dedent("""\
return textwrap.dedent(
"""\
Prepares files for publish into an output directory named dist/ at the given output path and executes setup.py.
Usage: cpl publish
""")
"""
)
def execute(self, args: list[str]):
"""

View File

@@ -10,11 +10,16 @@ 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_cli.command_abc import CommandABC
from cpl_cli.configuration import WorkspaceSettings, WorkspaceSettingsNameEnum, BuildSettingsNameEnum, ProjectSettings, BuildSettings
from cpl_cli.configuration import (
WorkspaceSettings,
WorkspaceSettingsNameEnum,
BuildSettingsNameEnum,
ProjectSettings,
BuildSettings,
)
class RemoveService(CommandABC):
def __init__(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
"""
Service for CLI command remove
@@ -31,13 +36,15 @@ class RemoveService(CommandABC):
@property
def help_message(self) -> str:
return textwrap.dedent("""\
return textwrap.dedent(
"""\
Removes a project from workspace.
Usage: cpl remove <project>
Arguments:
project The name of the project to delete
""")
"""
)
def _create_file(self, file_name: str, content: dict):
if self._is_simulation:
@@ -50,7 +57,7 @@ class RemoveService(CommandABC):
if not os.path.isdir(path):
os.makedirs(path)
with open(file_name, 'w') as project_json:
with open(file_name, "w") as project_json:
project_json.write(json.dumps(content, indent=2))
project_json.close()
@@ -64,14 +71,14 @@ class RemoveService(CommandABC):
WorkspaceSettings.__name__: {
WorkspaceSettingsNameEnum.default_project.value: self._workspace.default_project,
WorkspaceSettingsNameEnum.projects.value: self._workspace.projects,
WorkspaceSettingsNameEnum.scripts.value: self._workspace.scripts
WorkspaceSettingsNameEnum.scripts.value: self._workspace.scripts,
}
}
self._create_file(path, ws_dict)
def _get_project_settings(self, project: str) -> dict:
with open(os.path.join(os.getcwd(), self._workspace.projects[project]), 'r', encoding='utf-8') as cfg:
with open(os.path.join(os.getcwd(), self._workspace.projects[project]), "r", encoding="utf-8") as cfg:
# load json
project_json = json.load(cfg)
cfg.close()
@@ -79,11 +86,12 @@ class RemoveService(CommandABC):
return project_json
def _write_project_settings(self, project: str, project_settings: dict, build_settings: dict):
with open(os.path.join(os.getcwd(), self._workspace.projects[project]), 'w', encoding='utf-8') as file:
file.write(json.dumps({
ProjectSettings.__name__: project_settings,
BuildSettings.__name__: build_settings
}, indent=2))
with open(os.path.join(os.getcwd(), self._workspace.projects[project]), "w", encoding="utf-8") as file:
file.write(
json.dumps(
{ProjectSettings.__name__: project_settings, BuildSettings.__name__: build_settings}, indent=2
)
)
file.close()
def _find_deps_in_projects(self, project_name: str, rel_path: str):
@@ -92,26 +100,34 @@ class RemoveService(CommandABC):
continue
project_settings = self._get_project_settings(project)
if BuildSettings.__name__ not in project_settings or BuildSettingsNameEnum.project_references.value not in project_settings[BuildSettings.__name__]:
if (
BuildSettings.__name__ not in project_settings
or BuildSettingsNameEnum.project_references.value not in project_settings[BuildSettings.__name__]
):
continue
ref_to_delete = ''
ref_to_delete = ""
for ref in project_settings[BuildSettings.__name__][BuildSettingsNameEnum.project_references.value]:
if os.path.basename(ref) == f'{project_name}.json':
if os.path.basename(ref) == f"{project_name}.json":
ref_to_delete = ref
if ref_to_delete not in project_settings[BuildSettings.__name__][BuildSettingsNameEnum.project_references.value]:
if (
ref_to_delete
not in project_settings[BuildSettings.__name__][BuildSettingsNameEnum.project_references.value]
):
continue
project_settings[BuildSettings.__name__][BuildSettingsNameEnum.project_references.value].remove(ref_to_delete)
project_settings[BuildSettings.__name__][BuildSettingsNameEnum.project_references.value].remove(
ref_to_delete
)
Console.spinner(
f'Removing {project_name} from {project}',
f"Removing {project_name} from {project}",
self._write_project_settings,
project,
project_settings[ProjectSettings.__name__],
project_settings[BuildSettings.__name__],
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
spinner_foreground_color=ForegroundColorEnum.cyan,
)
def execute(self, args: list[str]):
@@ -120,37 +136,37 @@ class RemoveService(CommandABC):
:param args:
:return:
"""
if 'simulate' in args:
args.remove('simulate')
Console.write_line('Running in simulation mode:')
if "simulate" in args:
args.remove("simulate")
Console.write_line("Running in simulation mode:")
self._is_simulation = True
project_name = args[0]
if project_name not in self._workspace.projects:
Console.error(f'Project {project_name} not found in workspace.')
Console.error(f"Project {project_name} not found in workspace.")
return
if project_name == self._workspace.default_project:
Console.error(f'Project {project_name} is the default project.')
Console.error(f"Project {project_name} is the default project.")
return
src_path = os.path.dirname(self._workspace.projects[project_name])
Console.spinner(
f'Removing {src_path}',
f"Removing {src_path}",
self._remove_sources,
os.path.abspath(src_path),
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
spinner_foreground_color=ForegroundColorEnum.cyan,
)
self._find_deps_in_projects(project_name, src_path)
del self._workspace.projects[project_name]
path = 'cpl-workspace.json'
path = "cpl-workspace.json"
Console.spinner(
f'Changing {path}',
f"Changing {path}",
self._create_workspace,
path,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
spinner_foreground_color=ForegroundColorEnum.cyan,
)

View File

@@ -17,16 +17,16 @@ from cpl_core.utils.string import String
class RunService(CommandABC):
def __init__(self,
config: ConfigurationABC,
env: ApplicationEnvironmentABC,
services: ServiceProviderABC,
project_settings: ProjectSettings,
build_settings: BuildSettings,
workspace: WorkspaceSettings,
publisher: PublisherService,
):
def __init__(
self,
config: ConfigurationABC,
env: ApplicationEnvironmentABC,
services: ServiceProviderABC,
project_settings: ProjectSettings,
build_settings: BuildSettings,
workspace: WorkspaceSettings,
publisher: PublisherService,
):
"""
Service for the CLI command start
:param config:
@@ -51,18 +51,20 @@ class RunService(CommandABC):
@property
def help_message(self) -> str:
return textwrap.dedent("""\
return textwrap.dedent(
"""\
Starts your application.
Usage: cpl run
""")
"""
)
def _set_project_by_args(self, name: str):
if self._workspace is None:
Error.error('The command requires to be run in an CPL workspace, but a workspace could not be found.')
Error.error("The command requires to be run in an CPL workspace, but a workspace could not be found.")
sys.exit()
if name not in self._workspace.projects:
Error.error(f'Project {name} not found in workspace')
Error.error(f"Project {name} not found in workspace")
sys.exit()
project_path = self._workspace.projects[name]
@@ -70,7 +72,7 @@ class RunService(CommandABC):
self._config.add_configuration(ProjectSettings, None)
self._config.add_configuration(BuildSettings, None)
working_directory = self._config.get_configuration('PATH_WORKSPACE')
working_directory = self._config.get_configuration("PATH_WORKSPACE")
if working_directory is not None:
self._env.set_working_directory(working_directory)
@@ -80,7 +82,7 @@ class RunService(CommandABC):
self._build_settings: BuildSettings = self._config.get_configuration(BuildSettings)
if self._project_settings is None or self._build_settings is None:
Error.error(f'Project {name} not found')
Error.error(f"Project {name} not found")
sys.exit()
self._src_dir = os.path.dirname(json_file)
@@ -92,13 +94,15 @@ class RunService(CommandABC):
self._env.set_working_directory(self._src_dir)
self._publisher.build()
self._env.set_working_directory(self._src_dir)
self._src_dir = os.path.abspath(os.path.join(
self._src_dir,
self._build_settings.output_path,
self._project_settings.name,
'build',
String.convert_to_snake_case(self._project_settings.name)
))
self._src_dir = os.path.abspath(
os.path.join(
self._src_dir,
self._build_settings.output_path,
self._project_settings.name,
"build",
String.convert_to_snake_case(self._project_settings.name),
)
)
def execute(self, args: list[str]):
"""
@@ -106,9 +110,9 @@ class RunService(CommandABC):
:param args:
:return:
"""
if 'dev' in args:
if "dev" in args:
self._is_dev = True
args.remove('dev')
args.remove("dev")
if len(args) >= 1:
self._set_project_by_args(args[0])

View File

@@ -5,7 +5,6 @@ from cpl_cli.live_server.live_server_service import LiveServerService
class StartService(CommandABC):
def __init__(self, live_server: LiveServerService):
"""
Service for the CLI command start
@@ -17,10 +16,12 @@ class StartService(CommandABC):
@property
def help_message(self) -> str:
return textwrap.dedent("""\
return textwrap.dedent(
"""\
Starts your application, restarting on file changes.
Usage: cpl start
""")
"""
)
def execute(self, args: list[str]):
"""

View File

@@ -17,9 +17,13 @@ from cpl_cli.configuration.settings_helper import SettingsHelper
class UninstallService(CommandABC):
def __init__(self, config: ConfigurationABC, env: ApplicationEnvironmentABC, build_settings: BuildSettings,
project_settings: ProjectSettings):
def __init__(
self,
config: ConfigurationABC,
env: ApplicationEnvironmentABC,
build_settings: BuildSettings,
project_settings: ProjectSettings,
):
"""
Service for the CLI command uninstall
:param config:
@@ -33,23 +37,25 @@ class UninstallService(CommandABC):
self._env = env
self._build_settings = build_settings
self._project_settings = project_settings
self._is_simulating = False
self._is_virtual = False
self._is_dev = False
self._project_file = f'{self._project_settings.name}.json'
self._project_file = f"{self._project_settings.name}.json"
@property
def help_message(self) -> str:
return textwrap.dedent("""\
return textwrap.dedent(
"""\
Uninstalls given package via pip
Usage: cpl uninstall <package>
Arguments:
package The package to uninstall
""")
"""
)
def _wait(self, t: int, *args, source: str = None, stdout=None, stderr=None):
time.sleep(t)
@@ -60,23 +66,23 @@ class UninstallService(CommandABC):
:return:
"""
if len(args) == 0:
Console.error(f'Expected package')
Console.error(f'Usage: cpl uninstall <package>')
Console.error(f"Expected package")
Console.error(f"Usage: cpl uninstall <package>")
return
if 'dev' in args:
if "dev" in args:
self._is_dev = True
args.remove('dev')
args.remove("dev")
if '--virtual' in args:
if "--virtual" in args:
self._is_virtual = True
args.remove('--virtual')
Console.write_line('Running in virtual mode:')
if '--simulate' in args:
args.remove("--virtual")
Console.write_line("Running in virtual mode:")
if "--simulate" in args:
self._is_virtual = True
args.remove('--simulate')
Console.write_line('Running in simulation mode:')
args.remove("--simulate")
Console.write_line("Running in simulation mode:")
VenvHelper.init_venv(self._is_virtual, self._env, self._project_settings)
@@ -98,19 +104,20 @@ class UninstallService(CommandABC):
package = dependency
if not is_in_dependencies and pip_package is None:
Console.error(f'Package {package} not found')
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}' 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,
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,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
spinner_foreground_color=ForegroundColorEnum.cyan,
)
deps = self._project_settings.dependencies
@@ -122,12 +129,12 @@ class UninstallService(CommandABC):
if not self._is_simulating:
config = {
ProjectSettings.__name__: SettingsHelper.get_project_settings_dict(self._project_settings),
BuildSettings.__name__: SettingsHelper.get_build_settings_dict(self._build_settings)
BuildSettings.__name__: SettingsHelper.get_build_settings_dict(self._build_settings),
}
with open(os.path.join(self._env.working_directory, self._project_file), 'w') as project_file:
with open(os.path.join(self._env.working_directory, self._project_file), "w") as project_file:
project_file.write(json.dumps(config, indent=2))
project_file.close()
Console.write_line(f'Removed {package}')
Console.write_line(f"Removed {package}")
if not self._is_virtual:
Pip.reset_executable()

View File

@@ -18,14 +18,15 @@ from cpl_cli.configuration.settings_helper import SettingsHelper
class UpdateService(CommandABC):
def __init__(self,
config: ConfigurationABC,
env: ApplicationEnvironmentABC,
build_settings: BuildSettings,
project_settings: ProjectSettings,
cli_settings: CLISettings,
migrations: MigrationServiceABC):
def __init__(
self,
config: ConfigurationABC,
env: ApplicationEnvironmentABC,
build_settings: BuildSettings,
project_settings: ProjectSettings,
cli_settings: CLISettings,
migrations: MigrationServiceABC,
):
"""
Service for the CLI command update
:param config:
@@ -44,14 +45,16 @@ class UpdateService(CommandABC):
self._migrations = migrations
self._is_simulation = False
self._project_file = f'{self._project_settings.name}.json'
self._project_file = f"{self._project_settings.name}.json"
@property
def help_message(self) -> str:
return textwrap.dedent("""\
return textwrap.dedent(
"""\
Updates the CPL and project dependencies.
Usage: cpl update
""")
"""
)
def _collect_project_dependencies(self) -> list[tuple]:
"""
@@ -61,8 +64,8 @@ class UpdateService(CommandABC):
dependencies = []
for package in [*self._project_settings.dependencies, *self._project_settings.dev_dependencies]:
name = package
if '==' in package:
name = package.split('==')[0]
if "==" in package:
name = package.split("==")[0]
dependencies.append((package, name))
@@ -76,17 +79,17 @@ class UpdateService(CommandABC):
for package, name in dependencies:
Pip.install(
name,
'--upgrade',
'--upgrade-strategy',
'eager',
"--upgrade",
"--upgrade-strategy",
"eager",
source=self._cli_settings.pip_path,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
stderr=subprocess.DEVNULL,
)
new_package = Pip.get_package(name)
if new_package is None:
Console.error(f'Update for package {package} failed')
Console.error(f"Update for package {package} failed")
continue
self._project_json_update_dependency(package, new_package)
@@ -97,27 +100,32 @@ class UpdateService(CommandABC):
:return:
"""
dependencies = Console.spinner(
'Collecting installed dependencies', self._collect_project_dependencies,
"Collecting installed dependencies",
self._collect_project_dependencies,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
spinner_foreground_color=ForegroundColorEnum.cyan,
)
Console.spinner(
'Updating installed dependencies', self._update_project_dependencies, dependencies,
"Updating installed dependencies",
self._update_project_dependencies,
dependencies,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
spinner_foreground_color=ForegroundColorEnum.cyan,
)
if 'cpl-cli' in [y for x, y in dependencies]:
if "cpl-cli" in [y for x, y in dependencies]:
import cpl_cli
Console.spinner(
'Running migrations', self._migrations.migrate_from, cpl_cli.__version__,
"Running migrations",
self._migrations.migrate_from,
cpl_cli.__version__,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
spinner_foreground_color=ForegroundColorEnum.cyan,
)
Console.write_line(f'Found {len(self._project_settings.dependencies)} dependencies.')
Console.write_line(f"Found {len(self._project_settings.dependencies)} dependencies.")
@staticmethod
def _check_outdated():
@@ -126,19 +134,20 @@ class UpdateService(CommandABC):
:return:
"""
table_str: bytes = Console.spinner(
'Analyzing for available package updates', Pip.get_outdated,
"Analyzing for available package updates",
Pip.get_outdated,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
spinner_foreground_color=ForegroundColorEnum.cyan,
)
table = str(table_str, 'utf-8').split('\n')
if len(table) > 1 and table[0] != '':
Console.write_line('\tAvailable updates for packages:')
table = str(table_str, "utf-8").split("\n")
if len(table) > 1 and table[0] != "":
Console.write_line("\tAvailable updates for packages:")
for row in table:
Console.write_line(f'\t{row}')
Console.write_line(f"\t{row}")
Console.set_foreground_color(ForegroundColorEnum.yellow)
Console.write_line(f'\tUpdate with {Pip.get_executable()} -m pip install --upgrade <package>')
Console.write_line(f"\tUpdate with {Pip.get_executable()} -m pip install --upgrade <package>")
Console.set_foreground_color(ForegroundColorEnum.default)
def _project_json_update_dependency(self, old_package: str, new_package: str):
@@ -153,20 +162,20 @@ class UpdateService(CommandABC):
if old_package in self._project_settings.dependencies:
index = self._project_settings.dependencies.index(old_package)
if '/' in new_package:
new_package = new_package.split('/')[0]
if "/" in new_package:
new_package = new_package.split("/")[0]
if '\r' in new_package:
new_package = new_package.replace('\r', '')
if "\r" in new_package:
new_package = new_package.replace("\r", "")
self._project_settings.dependencies[index] = new_package
config = {
ProjectSettings.__name__: SettingsHelper.get_project_settings_dict(self._project_settings),
BuildSettings.__name__: SettingsHelper.get_build_settings_dict(self._build_settings)
BuildSettings.__name__: SettingsHelper.get_build_settings_dict(self._build_settings),
}
with open(os.path.join(self._env.working_directory, self._project_file), 'w') as project:
with open(os.path.join(self._env.working_directory, self._project_file), "w") as project:
project.write(json.dumps(config, indent=2))
project.close()
@@ -176,22 +185,22 @@ class UpdateService(CommandABC):
:param args:
:return:
"""
if 'simulate' in args:
args.remove('simulate')
Console.write_line('Running in simulation mode:')
if "simulate" in args:
args.remove("simulate")
Console.write_line("Running in simulation mode:")
self._is_simulation = True
if 'cpl-prod' in args:
args.remove('cpl-prod')
self._cli_settings.from_dict({'PipPath': 'https://pip.sh-edraft.de'})
if "cpl-prod" in args:
args.remove("cpl-prod")
self._cli_settings.from_dict({"PipPath": "https://pip.sh-edraft.de"})
if 'cpl-exp' in args:
args.remove('cpl-exp')
self._cli_settings.from_dict({'PipPath': 'https://pip-exp.sh-edraft.de'})
if "cpl-exp" in args:
args.remove("cpl-exp")
self._cli_settings.from_dict({"PipPath": "https://pip-exp.sh-edraft.de"})
if 'cpl-dev' in args:
args.remove('cpl-dev')
self._cli_settings.from_dict({'PipPath': 'https://pip-dev.sh-edraft.de'})
if "cpl-dev" in args:
args.remove("cpl-dev")
self._cli_settings.from_dict({"PipPath": "https://pip-dev.sh-edraft.de"})
VenvHelper.init_venv(False, self._env, self._project_settings)

View File

@@ -12,7 +12,6 @@ from cpl_cli.command_abc import CommandABC
class VersionService(CommandABC):
def __init__(self):
"""
Service for the CLI command version
@@ -21,10 +20,12 @@ class VersionService(CommandABC):
@property
def help_message(self) -> str:
return textwrap.dedent("""\
return textwrap.dedent(
"""\
Lists the version of CPL, CPL CLI and all installed packages from pip.
Usage: cpl version
""")
"""
)
def execute(self, args: list[str]):
"""
@@ -33,16 +34,16 @@ class VersionService(CommandABC):
:return:
"""
Console.set_foreground_color(ForegroundColorEnum.yellow)
Console.banner('CPL CLI')
Console.banner("CPL CLI")
Console.set_foreground_color(ForegroundColorEnum.default)
if '__version__' in dir(cpl_cli):
Console.write_line(f'Common Python library CLI: ')
if "__version__" in dir(cpl_cli):
Console.write_line(f"Common Python library CLI: ")
Console.write(cpl_cli.__version__)
Console.write_line(f'Python: ')
Console.write(f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}')
Console.write_line(f'OS: {platform.system()} {platform.processor()}')
Console.write_line('\nCPL packages:')
Console.table(['Name', 'Version'], Dependencies.get_cpl_packages())
Console.write_line('\nPython packages:')
Console.table(['Name', 'Version'], Dependencies.get_packages())
Console.write_line(f"Python: ")
Console.write(f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")
Console.write_line(f"OS: {platform.system()} {platform.processor()}")
Console.write_line("\nCPL packages:")
Console.table(["Name", "Version"], Dependencies.get_cpl_packages())
Console.write_line("\nPython packages:")
Console.table(["Name", "Version"], Dependencies.get_packages())