From dec4a45d9853a686b6434b57100b846026c078db Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 22 May 2022 18:32:34 +0200 Subject: [PATCH] Added logic to add CLI commands from external packages --- src/cpl_cli/cli.py | 12 ++- src/cpl_cli/command_abc.py | 14 ++-- src/cpl_cli/main.py | 41 ++++++++- src/cpl_cli/publish/publisher_service.py | 84 ++++++++++--------- .../configuration/argument_executable_abc.py | 2 +- src/cpl_core/configuration/configuration.py | 9 +- .../configuration/configuration_abc.py | 6 +- .../configuration/executable_argument.py | 3 +- 8 files changed, 114 insertions(+), 57 deletions(-) diff --git a/src/cpl_cli/cli.py b/src/cpl_cli/cli.py index 8a0f8f60..af128970 100644 --- a/src/cpl_cli/cli.py +++ b/src/cpl_cli/cli.py @@ -1,6 +1,7 @@ import sys import traceback +from cpl_cli.error import Error from cpl_core.application.application_abc import ApplicationABC from cpl_core.configuration.configuration_abc import ConfigurationABC from cpl_core.console.console import Console @@ -26,7 +27,16 @@ class CLI(ApplicationABC): :return: """ try: - self._configuration.parse_console_arguments(self._services) + result = self._configuration.parse_console_arguments(self._services) + if result: + return + + if len(self._configuration.additional_arguments) == 0: + Error.error('Expected command') + return + + unexpected_arguments = ', '.join(self._configuration.additional_arguments) + Error.error(f'Unexpected argument(s): {unexpected_arguments}') except KeyboardInterrupt: Console.write_line() sys.exit() diff --git a/src/cpl_cli/command_abc.py b/src/cpl_cli/command_abc.py index 2fe70f72..59ec51e4 100644 --- a/src/cpl_cli/command_abc.py +++ b/src/cpl_cli/command_abc.py @@ -1,10 +1,10 @@ from abc import abstractmethod, ABC -from cpl_core.configuration.executable_argument import ExecutableArgument +from cpl_core.configuration.argument_executable_abc import ArgumentExecutableABC from cpl_core.console import Console -class CommandABC(ExecutableArgument): +class CommandABC(ArgumentExecutableABC): @abstractmethod def __init__(self): @@ -14,12 +14,12 @@ class CommandABC(ExecutableArgument): @abstractmethod def help_message(self) -> str: pass - def execute(self, args: list[str]): + @abstractmethod + def execute(self, args: list[str]): pass + + def run(self, args: list[str]): if 'help' in args: Console.write_line(self.help_message) return - self.run(args) - - @abstractmethod - def run(self, args: list[str]): pass + self.execute(args) diff --git a/src/cpl_cli/main.py b/src/cpl_cli/main.py index 192a18ae..5ccac3ed 100644 --- a/src/cpl_cli/main.py +++ b/src/cpl_cli/main.py @@ -1,15 +1,52 @@ -from cpl_cli.startup_argument_extension import StartupArgumentExtension -from cpl_core.application.application_builder import ApplicationBuilder +from typing import Type + +import pkg_resources + from cpl_cli.cli import CLI from cpl_cli.startup import Startup +from cpl_cli.startup_argument_extension import StartupArgumentExtension +from cpl_core.application.application_builder import ApplicationBuilder +from cpl_core.application.startup_extension_abc import StartupExtensionABC + + +def get_startup_extensions() -> list[Type[StartupExtensionABC]]: + blacklisted_packages = ['cpl-cli'] + startup_extensions = [] + + installed_packages = pkg_resources.working_set + for p in installed_packages: + package = str(p).split(' ')[0] + if not package.startswith('cpl-') or package in blacklisted_packages: + continue + + package = package.replace('-', '_') + loaded_package = __import__(package) + if '__cli_startup_extension__' not in dir(loaded_package): + continue + startup_extensions.append(loaded_package.__cli_startup_extension__) + + return startup_extensions def main(): app_builder = ApplicationBuilder(CLI) app_builder.use_startup(Startup) app_builder.use_extension(StartupArgumentExtension) + for extension in get_startup_extensions(): + app_builder.use_extension(extension) + app_builder.build().run() if __name__ == '__main__': main() + +# (( +# ( `) +# ; / , +# / \/ +# / | +# / ~/ +# / ) ) ~ edraft +# ___// | / +# `--' \_~-, diff --git a/src/cpl_cli/publish/publisher_service.py b/src/cpl_cli/publish/publisher_service.py index ba1047e7..dc85215c 100644 --- a/src/cpl_cli/publish/publisher_service.py +++ b/src/cpl_cli/publish/publisher_service.py @@ -206,55 +206,57 @@ class PublisherService(PublisherABC): :return: """ for file in self._included_files: - if file.endswith('__init__.py'): - template_content = '' - module_file_lines: list[str] = [] + if not file.endswith('__init__.py'): + continue - title = self._get_module_name_from_dirs(file) - if title == '': - title = self._project_settings.name + template_content = '' + module_file_lines: list[str] = [] - module_py_lines: list[str] = [] - imports = '' + title = self._get_module_name_from_dirs(file) + if title == '': + title = self._project_settings.name - with open(file, 'r') as py_file: - module_file_lines = py_file.readlines() - py_file.close() + module_py_lines: list[str] = [] + imports = '' - if len(module_file_lines) == 0: - imports = '# imports:' - else: - is_started = False - for line in module_file_lines: - if line.__contains__('# imports'): - is_started = True + with open(file, 'r') as py_file: + module_file_lines = py_file.readlines() + py_file.close() - if (line.__contains__('from') or line.__contains__('import')) and is_started: - module_py_lines.append(line.replace('\n', '')) + if len(module_file_lines) == 0: + imports = '# imports:' + else: + is_started = False + for line in module_file_lines: + if line.__contains__('# imports'): + is_started = True - if len(module_py_lines) > 0: - imports = '\n'.join(module_py_lines) + if ((line.__contains__('from') or line.__contains__('import')) and is_started) or line.startswith('__cli_startup_extension__'): + module_py_lines.append(line.replace('\n', '')) - template_content = stringTemplate(InitTemplate.get_init_py()).substitute( - Name=self._project_settings.name, - Description=self._project_settings.description, - LongDescription=self._project_settings.long_description, - CopyrightDate=self._project_settings.copyright_date, - CopyrightName=self._project_settings.copyright_name, - LicenseName=self._project_settings.license_name, - LicenseDescription=self._project_settings.license_description, - Title=title if title is not None and title != '' else self._project_settings.name, - Author=self._project_settings.author, - Version=version.parse(self._project_settings.version.to_str()), - Major=self._project_settings.version.major, - Minor=self._project_settings.version.minor, - Micro=self._project_settings.version.micro, - Imports=imports - ) + if len(module_py_lines) > 0: + imports = '\n'.join(module_py_lines) - with open(file, 'w+') as py_file: - py_file.write(template_content) - py_file.close() + template_content = stringTemplate(InitTemplate.get_init_py()).substitute( + Name=self._project_settings.name, + Description=self._project_settings.description, + LongDescription=self._project_settings.long_description, + CopyrightDate=self._project_settings.copyright_date, + CopyrightName=self._project_settings.copyright_name, + LicenseName=self._project_settings.license_name, + LicenseDescription=self._project_settings.license_description, + Title=title if title is not None and title != '' else self._project_settings.name, + Author=self._project_settings.author, + Version=version.parse(self._project_settings.version.to_str()), + Major=self._project_settings.version.major, + Minor=self._project_settings.version.minor, + Micro=self._project_settings.version.micro, + Imports=imports + ) + + with open(file, 'w+') as py_file: + py_file.write(template_content) + py_file.close() def _dist_files(self): """ diff --git a/src/cpl_core/configuration/argument_executable_abc.py b/src/cpl_core/configuration/argument_executable_abc.py index d7f3319a..8263b338 100644 --- a/src/cpl_core/configuration/argument_executable_abc.py +++ b/src/cpl_core/configuration/argument_executable_abc.py @@ -7,4 +7,4 @@ class ArgumentExecutableABC(ABC): def __init__(self): pass @abstractmethod - def run(self, args: list[str]): pass + def execute(self, args: list[str]): pass diff --git a/src/cpl_core/configuration/configuration.py b/src/cpl_core/configuration/configuration.py index 93becd5f..c0dd8302 100644 --- a/src/cpl_core/configuration/configuration.py +++ b/src/cpl_core/configuration/configuration.py @@ -274,18 +274,19 @@ class Configuration(ConfigurationABC): if config_model == search_type: return self._config[config_model] - def parse_console_arguments(self, services: ServiceProviderABC, error: bool = None): + def parse_console_arguments(self, services: ServiceProviderABC, error: bool = None) -> bool: # sets environment variables as possible arguments as: --VAR=VALUE for arg_name in ConfigurationVariableNameEnum.to_list(): self.add_console_argument(VariableArgument('--', str(arg_name).upper(), [str(arg_name).lower()], '=')) + success = False try: arg_list = sys.argv[1:] executables: list[ExecutableArgument] = [] self._parse_arguments(executables, arg_list, self._argument_types) except Exception as e: Console.error('An error occurred while parsing arguments.') - exit() + sys.exit() try: prevent = False @@ -309,5 +310,9 @@ class Configuration(ConfigurationABC): self.add_configuration('ACTIVE_EXECUTABLE', exe.name) cmd.execute(self._additional_arguments) prevent = exe.prevent_next_executable + success = True except Exception as e: Console.error('An error occurred while executing arguments.') + sys.exit() + + return success diff --git a/src/cpl_core/configuration/configuration_abc.py b/src/cpl_core/configuration/configuration_abc.py index 733e86ce..caadc15f 100644 --- a/src/cpl_core/configuration/configuration_abc.py +++ b/src/cpl_core/configuration/configuration_abc.py @@ -141,12 +141,16 @@ class ConfigurationABC(ABC): pass @abstractmethod - def parse_console_arguments(self, services: 'ServiceProviderABC', error: bool = None): + def parse_console_arguments(self, services: 'ServiceProviderABC', error: bool = None) -> bool: r"""Reads the console arguments Parameter --------- error: :class:`bool` Defines is invalid argument error will be shown or not + + Returns + ------- + Bool to specify if executables were executed or not. """ pass diff --git a/src/cpl_core/configuration/executable_argument.py b/src/cpl_core/configuration/executable_argument.py index fd061a8e..6d2254dd 100644 --- a/src/cpl_core/configuration/executable_argument.py +++ b/src/cpl_core/configuration/executable_argument.py @@ -3,7 +3,6 @@ from typing import Type, Optional from cpl_core.configuration.argument_executable_abc import ArgumentExecutableABC from cpl_core.configuration.argument_abc import ArgumentABC from cpl_core.configuration.validator_abc import ValidatorABC -from cpl_core.console import Console class ExecutableArgument(ArgumentABC): @@ -40,4 +39,4 @@ class ExecutableArgument(ArgumentABC): """ if self._executable is None: return - self._executable.run(args) + self._executable.execute(args)