Improved argument handling
This commit is contained in:
parent
4fe073580a
commit
0f5b1b7586
@ -22,8 +22,6 @@ from collections import namedtuple
|
|||||||
# imports:
|
# imports:
|
||||||
from .cli import CLI
|
from .cli import CLI
|
||||||
from .command_abc import CommandABC
|
from .command_abc import CommandABC
|
||||||
from .command_handler_service import CommandHandler
|
|
||||||
from .command_model import CommandModel
|
|
||||||
from .error import Error
|
from .error import Error
|
||||||
from .main import main
|
from .main import main
|
||||||
from .startup import Startup
|
from .startup import Startup
|
||||||
|
@ -26,7 +26,7 @@ class CLI(ApplicationABC):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self._configuration.parse_console_arguments()
|
self._configuration.parse_console_arguments(self._services)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
Console.write_line()
|
Console.write_line()
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
@ -38,6 +38,7 @@ class UpdateService(CommandABC):
|
|||||||
self._build_settings = build_settings
|
self._build_settings = build_settings
|
||||||
self._project_settings = project_settings
|
self._project_settings = project_settings
|
||||||
self._cli_settings = cli_settings
|
self._cli_settings = cli_settings
|
||||||
|
self._is_simulation = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def help_message(self) -> str:
|
def help_message(self) -> str:
|
||||||
@ -132,6 +133,9 @@ class UpdateService(CommandABC):
|
|||||||
:param new_package:
|
:param new_package:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
if self._is_simulation:
|
||||||
|
return
|
||||||
|
|
||||||
if old_package in self._project_settings.dependencies:
|
if old_package in self._project_settings.dependencies:
|
||||||
index = self._project_settings.dependencies.index(old_package)
|
index = self._project_settings.dependencies.index(old_package)
|
||||||
if '/' in new_package:
|
if '/' in new_package:
|
||||||
@ -158,6 +162,11 @@ class UpdateService(CommandABC):
|
|||||||
:param args:
|
:param args:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
if 'simulate' in args:
|
||||||
|
args.remove('simulate')
|
||||||
|
Console.write_line('Running in simulation mode:')
|
||||||
|
self._is_simulation = True
|
||||||
|
|
||||||
Pip.set_executable(self._project_settings.python_executable)
|
Pip.set_executable(self._project_settings.python_executable)
|
||||||
self._check_project_dependencies()
|
self._check_project_dependencies()
|
||||||
self._check_outdated()
|
self._check_outdated()
|
||||||
|
@ -1,137 +0,0 @@
|
|||||||
import os
|
|
||||||
from abc import ABC
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from cpl_core.configuration.configuration_abc import ConfigurationABC
|
|
||||||
from cpl_core.console.console import Console
|
|
||||||
from cpl_core.dependency_injection.service_provider_abc import ServiceProviderABC
|
|
||||||
from cpl_core.utils.string import String
|
|
||||||
from cpl_cli.command.custom_script_service import CustomScriptService
|
|
||||||
from cpl_cli.configuration.workspace_settings import WorkspaceSettings
|
|
||||||
from cpl_cli.error import Error
|
|
||||||
from cpl_cli.command_model import CommandModel
|
|
||||||
|
|
||||||
|
|
||||||
class CommandHandler(ABC):
|
|
||||||
|
|
||||||
def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
|
|
||||||
"""
|
|
||||||
Service to handle incoming commands and args
|
|
||||||
:param config:
|
|
||||||
:param services:
|
|
||||||
"""
|
|
||||||
ABC.__init__(self)
|
|
||||||
|
|
||||||
self._config = config
|
|
||||||
self._env = self._config.environment
|
|
||||||
self._services = services
|
|
||||||
|
|
||||||
self._commands: list[CommandModel] = []
|
|
||||||
|
|
||||||
@property
|
|
||||||
def commands(self) -> list[CommandModel]:
|
|
||||||
return self._commands
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _project_not_found():
|
|
||||||
Error.error(
|
|
||||||
'The command requires to be run in an CPL project, but a project could not be found.'
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_command(self, cmd: CommandModel):
|
|
||||||
self._commands.append(cmd)
|
|
||||||
|
|
||||||
def remove_command(self, cmd: CommandModel):
|
|
||||||
self._commands.remove(cmd)
|
|
||||||
|
|
||||||
def handle(self, cmd: str, args: list[str]):
|
|
||||||
"""
|
|
||||||
Handles incoming commands and args
|
|
||||||
:param cmd:
|
|
||||||
:param args:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
for command in self._commands:
|
|
||||||
if cmd == command.name or cmd in command.aliases:
|
|
||||||
error = None
|
|
||||||
project_name: Optional[str] = None
|
|
||||||
workspace: Optional[WorkspaceSettings] = None
|
|
||||||
|
|
||||||
if os.path.isfile(os.path.join(self._env.working_directory, 'cpl-workspace.json')):
|
|
||||||
workspace = self._config.get_configuration(WorkspaceSettings)
|
|
||||||
|
|
||||||
if command.is_project_needed:
|
|
||||||
name = os.path.basename(self._env.working_directory)
|
|
||||||
for r, d, f in os.walk(self._env.working_directory):
|
|
||||||
for file in f:
|
|
||||||
if file.endswith('.json'):
|
|
||||||
f_name = file.split('.json')[0]
|
|
||||||
if f_name == name or \
|
|
||||||
String.convert_to_camel_case(f_name).lower() == String.convert_to_camel_case(
|
|
||||||
name).lower():
|
|
||||||
project_name = f_name
|
|
||||||
break
|
|
||||||
|
|
||||||
if not command.is_workspace_needed and project_name is None and workspace is None:
|
|
||||||
self._project_not_found()
|
|
||||||
return
|
|
||||||
|
|
||||||
elif command.is_workspace_needed or project_name is None:
|
|
||||||
if workspace is None:
|
|
||||||
Error.error(
|
|
||||||
'The command requires to be run in an CPL workspace or project, '
|
|
||||||
'but a workspace or project could not be found.'
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
if project_name is None:
|
|
||||||
project_name = workspace.default_project
|
|
||||||
|
|
||||||
self._config.add_configuration('ProjectName', project_name)
|
|
||||||
project_json = f'{project_name}.json'
|
|
||||||
|
|
||||||
if workspace is not None:
|
|
||||||
if project_name not in workspace.projects:
|
|
||||||
Error.error(
|
|
||||||
f'Project {project_name} not found.'
|
|
||||||
)
|
|
||||||
return
|
|
||||||
project_json = workspace.projects[project_name]
|
|
||||||
|
|
||||||
if not os.path.isfile(os.path.join(self._env.working_directory, project_json)):
|
|
||||||
self._project_not_found()
|
|
||||||
return
|
|
||||||
|
|
||||||
project_json = os.path.join(self._env.working_directory, project_json)
|
|
||||||
|
|
||||||
if command.change_cwd:
|
|
||||||
self._env.set_working_directory(
|
|
||||||
os.path.join(self._env.working_directory, os.path.dirname(project_json))
|
|
||||||
)
|
|
||||||
|
|
||||||
self._config.add_json_file(project_json, optional=True, output=False)
|
|
||||||
|
|
||||||
# pre scripts
|
|
||||||
Console.write('\n')
|
|
||||||
self._handle_pre_or_post_scripts(True, workspace, command)
|
|
||||||
self._services.get_service(command.command).run(args)
|
|
||||||
# post scripts
|
|
||||||
Console.write('\n\n')
|
|
||||||
self._handle_pre_or_post_scripts(False, workspace, command)
|
|
||||||
Console.write('\n')
|
|
||||||
|
|
||||||
def _handle_pre_or_post_scripts(self, pre: bool, workspace: WorkspaceSettings, command: CommandModel):
|
|
||||||
script_type = 'pre-' if pre else 'post-'
|
|
||||||
if workspace is not None and len(workspace.scripts) > 0:
|
|
||||||
for script in workspace.scripts:
|
|
||||||
if script_type in script and script.split(script_type)[1] == command.name:
|
|
||||||
script_name = script
|
|
||||||
script_cmd = workspace.scripts[script]
|
|
||||||
if script_cmd in workspace.scripts:
|
|
||||||
script_name = workspace.scripts[script]
|
|
||||||
|
|
||||||
css: CustomScriptService = self._services.get_service(CustomScriptService)
|
|
||||||
if css is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
css.run([script_name])
|
|
@ -1,37 +0,0 @@
|
|||||||
from cpl_cli.command_abc import CommandABC
|
|
||||||
|
|
||||||
|
|
||||||
class CommandModel:
|
|
||||||
|
|
||||||
def __init__(self, name: str, aliases: list[str], command: CommandABC, is_workspace_needed: bool,
|
|
||||||
is_project_needed: bool, change_cwd: bool):
|
|
||||||
self._name = name
|
|
||||||
self._aliases = aliases
|
|
||||||
self._command = command
|
|
||||||
self._is_workspace_needed = is_workspace_needed
|
|
||||||
self._is_project_needed = is_project_needed
|
|
||||||
self._change_cwd = change_cwd
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
@property
|
|
||||||
def aliases(self) -> list[str]:
|
|
||||||
return self._aliases
|
|
||||||
|
|
||||||
@property
|
|
||||||
def command(self) -> CommandABC:
|
|
||||||
return self._command
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_workspace_needed(self) -> bool:
|
|
||||||
return self._is_workspace_needed
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_project_needed(self) -> bool:
|
|
||||||
return self._is_project_needed
|
|
||||||
|
|
||||||
@property
|
|
||||||
def change_cwd(self) -> bool:
|
|
||||||
return self._change_cwd
|
|
@ -18,7 +18,6 @@ from cpl_cli.configuration.workspace_settings import WorkspaceSettings
|
|||||||
from cpl_core.application import StartupExtensionABC
|
from cpl_core.application import StartupExtensionABC
|
||||||
from cpl_core.configuration.argument_type_enum import ArgumentTypeEnum
|
from cpl_core.configuration.argument_type_enum import ArgumentTypeEnum
|
||||||
from cpl_core.configuration.configuration_abc import ConfigurationABC
|
from cpl_core.configuration.configuration_abc import ConfigurationABC
|
||||||
from cpl_core.console import Console
|
|
||||||
from cpl_core.dependency_injection.service_collection_abc import ServiceCollectionABC
|
from cpl_core.dependency_injection.service_collection_abc import ServiceCollectionABC
|
||||||
from cpl_core.environment import ApplicationEnvironmentABC
|
from cpl_core.environment import ApplicationEnvironmentABC
|
||||||
from cpl_core.utils import String
|
from cpl_core.utils import String
|
||||||
@ -83,7 +82,8 @@ class StartupArgumentExtension(StartupExtensionABC):
|
|||||||
config.create_console_argument(ArgumentTypeEnum.Executable, '', 'uninstall', ['ui', 'UI'], UninstallService) \
|
config.create_console_argument(ArgumentTypeEnum.Executable, '', 'uninstall', ['ui', 'UI'], UninstallService) \
|
||||||
.add_console_argument(ArgumentTypeEnum.Flag, '--', 'virtual', ['v', 'V']) \
|
.add_console_argument(ArgumentTypeEnum.Flag, '--', 'virtual', ['v', 'V']) \
|
||||||
.add_console_argument(ArgumentTypeEnum.Flag, '--', 'simulate', ['s', 'S'])
|
.add_console_argument(ArgumentTypeEnum.Flag, '--', 'simulate', ['s', 'S'])
|
||||||
config.create_console_argument(ArgumentTypeEnum.Executable, '', 'update', ['u', 'U'], UpdateService)
|
config.create_console_argument(ArgumentTypeEnum.Executable, '', 'update', ['u', 'U'], UpdateService) \
|
||||||
|
.add_console_argument(ArgumentTypeEnum.Flag, '--', 'simulate', ['s', 'S'])
|
||||||
config.create_console_argument(ArgumentTypeEnum.Executable, '', 'version', ['v', 'V'], VersionService)
|
config.create_console_argument(ArgumentTypeEnum.Executable, '', 'version', ['v', 'V'], VersionService)
|
||||||
|
|
||||||
config.for_each_argument(
|
config.for_each_argument(
|
||||||
|
@ -10,7 +10,7 @@ from cpl_core.dependency_injection.service_collection import ServiceCollection
|
|||||||
|
|
||||||
|
|
||||||
class ApplicationBuilder(ApplicationBuilderABC):
|
class ApplicationBuilder(ApplicationBuilderABC):
|
||||||
r"""This is class is used to build a object of :class:`cpl_core.application.application_abc.ApplicationABC`
|
r"""This is class is used to build an object of :class:`cpl_core.application.application_abc.ApplicationABC`
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
---------
|
---------
|
||||||
@ -54,7 +54,6 @@ class ApplicationBuilder(ApplicationBuilderABC):
|
|||||||
|
|
||||||
config = self._configuration
|
config = self._configuration
|
||||||
services = self._services.build_service_provider()
|
services = self._services.build_service_provider()
|
||||||
config.resolve_runnable_argument_types(services)
|
|
||||||
|
|
||||||
for ex in self._app_extensions:
|
for ex in self._app_extensions:
|
||||||
extension = ex()
|
extension = ex()
|
||||||
|
@ -15,7 +15,7 @@ from cpl_core.configuration.flag_argument import FlagArgument
|
|||||||
from cpl_core.configuration.variable_argument import VariableArgument
|
from cpl_core.configuration.variable_argument import VariableArgument
|
||||||
from cpl_core.console.console import Console
|
from cpl_core.console.console import Console
|
||||||
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
|
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
|
||||||
from cpl_core.dependency_injection import ServiceProviderABC
|
from cpl_core.dependency_injection.service_provider_abc import ServiceProviderABC
|
||||||
from cpl_core.environment.application_environment import ApplicationEnvironment
|
from cpl_core.environment.application_environment import ApplicationEnvironment
|
||||||
from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC
|
from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC
|
||||||
from cpl_core.environment.environment_name_enum import EnvironmentNameEnum
|
from cpl_core.environment.environment_name_enum import EnvironmentNameEnum
|
||||||
@ -53,6 +53,10 @@ class Configuration(ConfigurationABC):
|
|||||||
def argument_error_function(self, argument_error_function: Callable):
|
def argument_error_function(self, argument_error_function: Callable):
|
||||||
self._argument_error_function = argument_error_function
|
self._argument_error_function = argument_error_function
|
||||||
|
|
||||||
|
@property
|
||||||
|
def arguments(self) -> list[ArgumentABC]:
|
||||||
|
return self._argument_types
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _print_info(name: str, message: str):
|
def _print_info(name: str, message: str):
|
||||||
r"""Prints an info message
|
r"""Prints an info message
|
||||||
@ -147,7 +151,7 @@ class Configuration(ConfigurationABC):
|
|||||||
self._print_error(__name__, f'Cannot load config file: {file}! -> {e}')
|
self._print_error(__name__, f'Cannot load config file: {file}! -> {e}')
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def _parse_arguments(self, call_stack: list[Callable], arg_list: list[str], args_types: list[ArgumentABC]):
|
def _parse_arguments(self, executables: list[ArgumentABC], arg_list: list[str], args_types: list[ArgumentABC]):
|
||||||
for i in range(0, len(arg_list)):
|
for i in range(0, len(arg_list)):
|
||||||
arg_str = arg_list[i]
|
arg_str = arg_list[i]
|
||||||
for n in range(0, len(args_types)):
|
for n in range(0, len(args_types)):
|
||||||
@ -160,9 +164,9 @@ class Configuration(ConfigurationABC):
|
|||||||
if isinstance(arg, ExecutableArgument):
|
if isinstance(arg, ExecutableArgument):
|
||||||
if arg_str.startswith(arg.token) \
|
if arg_str.startswith(arg.token) \
|
||||||
and arg_str_without_token == arg.name or arg_str_without_token in arg.aliases:
|
and arg_str_without_token == arg.name or arg_str_without_token in arg.aliases:
|
||||||
call_stack.append(arg.run)
|
executables.append(arg)
|
||||||
self._handled_args.append(arg_str)
|
self._handled_args.append(arg_str)
|
||||||
self._parse_arguments(call_stack, arg_list[i + 1:], arg.console_arguments)
|
self._parse_arguments(executables, arg_list[i + 1:], arg.console_arguments)
|
||||||
|
|
||||||
# variables
|
# variables
|
||||||
elif isinstance(arg, VariableArgument):
|
elif isinstance(arg, VariableArgument):
|
||||||
@ -178,7 +182,7 @@ class Configuration(ConfigurationABC):
|
|||||||
value = arg_list[i + 1]
|
value = arg_list[i + 1]
|
||||||
self._set_variable(arg.name, value)
|
self._set_variable(arg.name, value)
|
||||||
self._handled_args.append(arg_str)
|
self._handled_args.append(arg_str)
|
||||||
self._parse_arguments(call_stack, arg_list[i + 1:], arg.console_arguments)
|
self._parse_arguments(executables, arg_list[i + 1:], arg.console_arguments)
|
||||||
|
|
||||||
# flags
|
# flags
|
||||||
elif isinstance(arg, FlagArgument):
|
elif isinstance(arg, FlagArgument):
|
||||||
@ -186,7 +190,7 @@ class Configuration(ConfigurationABC):
|
|||||||
and arg_str_without_token == arg.name or arg_str_without_token in arg.aliases:
|
and arg_str_without_token == arg.name or arg_str_without_token in arg.aliases:
|
||||||
self._additional_arguments.append(arg.name)
|
self._additional_arguments.append(arg.name)
|
||||||
self._handled_args.append(arg_str)
|
self._handled_args.append(arg_str)
|
||||||
self._parse_arguments(call_stack, arg_list[i + 1:], arg.console_arguments)
|
self._parse_arguments(executables, arg_list[i + 1:], arg.console_arguments)
|
||||||
|
|
||||||
# add left over values to args
|
# add left over values to args
|
||||||
if arg_str not in self._additional_arguments and arg_str not in self._handled_args:
|
if arg_str not in self._additional_arguments and arg_str not in self._handled_args:
|
||||||
@ -266,19 +270,16 @@ class Configuration(ConfigurationABC):
|
|||||||
if config_model == search_type:
|
if config_model == search_type:
|
||||||
return self._config[config_model]
|
return self._config[config_model]
|
||||||
|
|
||||||
def parse_console_arguments(self, error: bool = None):
|
def parse_console_arguments(self, services: ServiceProviderABC, error: bool = None):
|
||||||
# sets environment variables as possible arguments as: --VAR=VALUE
|
# sets environment variables as possible arguments as: --VAR=VALUE
|
||||||
for arg_name in ConfigurationVariableNameEnum.to_list():
|
for arg_name in ConfigurationVariableNameEnum.to_list():
|
||||||
self.add_console_argument(VariableArgument('--', str(arg_name).upper(), [str(arg_name).lower()], '='))
|
self.add_console_argument(VariableArgument('--', str(arg_name).upper(), [str(arg_name).lower()], '='))
|
||||||
|
|
||||||
arg_list = sys.argv[1:]
|
arg_list = sys.argv[1:]
|
||||||
call_stack = []
|
executables: list[ExecutableArgument] = []
|
||||||
self._parse_arguments(call_stack, arg_list, self._argument_types)
|
self._parse_arguments(executables, arg_list, self._argument_types)
|
||||||
|
|
||||||
for call in call_stack:
|
for exe in executables:
|
||||||
call(self._additional_arguments)
|
service: ExecutableArgument = services.get_service(exe.executable_type)
|
||||||
|
service.run(self._additional_arguments)
|
||||||
|
|
||||||
def resolve_runnable_argument_types(self, services: ServiceProviderABC):
|
|
||||||
for arg in self._argument_types:
|
|
||||||
if isinstance(arg, ExecutableArgument):
|
|
||||||
arg.set_executable(services.get_service(arg.executable_type))
|
|
||||||
|
@ -31,6 +31,10 @@ class ConfigurationABC(ABC):
|
|||||||
@abstractmethod
|
@abstractmethod
|
||||||
def argument_error_function(self, argument_error_function: Callable): pass
|
def argument_error_function(self, argument_error_function: Callable): pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def arguments(self) -> list[ArgumentABC]: pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def add_environment_variables(self, prefix: str):
|
def add_environment_variables(self, prefix: str):
|
||||||
r"""Reads the environment variables
|
r"""Reads the environment variables
|
||||||
@ -137,7 +141,7 @@ class ConfigurationABC(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def parse_console_arguments(self, error: bool = None):
|
def parse_console_arguments(self, services: 'ServiceProviderABC', error: bool = None):
|
||||||
r"""Reads the console arguments
|
r"""Reads the console arguments
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
@ -146,14 +150,3 @@ class ConfigurationABC(ABC):
|
|||||||
Defines is invalid argument error will be shown or not
|
Defines is invalid argument error will be shown or not
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def resolve_runnable_argument_types(self, services: 'ServiceProviderABC'):
|
|
||||||
r"""Gets all objects for given types of ConsoleArguments
|
|
||||||
|
|
||||||
Parameter
|
|
||||||
---------
|
|
||||||
services: :class:`cpl_core.dependency_injection.service_provider_abc.ServiceProviderABC`
|
|
||||||
Provides services
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
Loading…
Reference in New Issue
Block a user