From be178b10c79cb926db254d1cfafe27382c63d0af Mon Sep 17 00:00:00 2001 From: kolo Date: Fri, 28 Nov 2025 14:31:51 +0300 Subject: [PATCH] refactor and optimize argspace --- docs/code_snippets/argparser/snippet.py | 9 ++-- docs/root/api/orchestrator/argparser.rst | 12 ++--- docs/root/api/orchestrator/arguments.rst | 2 +- mock/local_test.py | 12 ++++- mock/mock_app/main.py | 9 ++-- pyproject.toml | 2 +- src/argenta/orchestrator/argparser/entity.py | 53 ++++++++++++++------ 7 files changed, 68 insertions(+), 31 deletions(-) diff --git a/docs/code_snippets/argparser/snippet.py b/docs/code_snippets/argparser/snippet.py index b86b95f..4b4fab5 100644 --- a/docs/code_snippets/argparser/snippet.py +++ b/docs/code_snippets/argparser/snippet.py @@ -1,10 +1,13 @@ from argenta import App, Orchestrator -from argenta.orchestrator.argparser import ArgParser, BooleanArgument +from argenta.orchestrator.argparser import ArgParser, BooleanArgument, ValueArgument -arg_parser = ArgParser(processed_args=[BooleanArgument("config")]) +arg_parser = ArgParser(processed_args=[BooleanArgument("dev"), ValueArgument('some', possible_values=['fuck', 'cruck'])]) orchestrator = Orchestrator( arg_parser=arg_parser, ) if __name__ == "__main__": - orchestrator.start_polling(App()) + if arg_parser.parsed_argspace.get_by_name('dev'): + orchestrator.start_polling(App(initial_message='ArgentaDev')) + else: + orchestrator.start_polling(App()) diff --git a/docs/root/api/orchestrator/argparser.rst b/docs/root/api/orchestrator/argparser.rst index 6d62249..b8cef0c 100644 --- a/docs/root/api/orchestrator/argparser.rst +++ b/docs/root/api/orchestrator/argparser.rst @@ -25,7 +25,7 @@ ArgParser * ``description``: Описание приложения для отображения в справке. * ``epilog``: Дополнительная информация для отображения в конце справки. -Основные методы и атрибуты +Атрибуты --------------------------- .. py:attribute:: parsed_argspace: ArgSpace @@ -64,16 +64,16 @@ ArgParser .. code-block:: bash $ python app.py - usage: MyApp [-h] --config CONFIG - MyApp: error: the following arguments are required: --config + usage: Argenta [-h] --config CONFIG + Argenta: error: the following arguments are required: --config -**Недопустимое значение из списка choices:** +**Недопустимое значение из списка possible_values:** .. code-block:: bash $ python app.py --config app.yaml --log-level TRACE - usage: MyApp [-h] --log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL} - MyApp: error: argument --log-level: invalid choice: 'TRACE' + usage: Argenta [-h] --log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL} + Argenta: error: argument --log-level: invalid choice: 'TRACE' **Использование устаревшего аргумента:** diff --git a/docs/root/api/orchestrator/arguments.rst b/docs/root/api/orchestrator/arguments.rst index da13ff3..e75b313 100644 --- a/docs/root/api/orchestrator/arguments.rst +++ b/docs/root/api/orchestrator/arguments.rst @@ -5,7 +5,7 @@ Arguments Модуль ``Arguments`` предоставляет классы для работы с аргументами командной строки. Они позволяют настраивать поведение приложения в момент его запуска, передавая различные параметры конфигурации. -Аргументы регистрируются в `ArgParser` и после обработки становятся доступными в объекте `ArgSpace`. +Аргументы регистрируются в ``ArgParser`` и после обработки становятся доступными в объекте ``ArgSpace``. ----- diff --git a/mock/local_test.py b/mock/local_test.py index a9c4c13..032e20c 100644 --- a/mock/local_test.py +++ b/mock/local_test.py @@ -1,3 +1,11 @@ -arg = '-repeat' +from argenta.orchestrator.argparser import ArgSpace, BooleanArgument, ValueArgument +from argenta.orchestrator.argparser.arguments import InputArgument -print(arg[:arg.rfind('-')+1]) \ No newline at end of file +argspace = ArgSpace([ + InputArgument(name="arg1", value="val1", founder_class=ValueArgument), + InputArgument(name="arg2", value=True, founder_class=BooleanArgument), + InputArgument(name="arg3", value="val3", founder_class=ValueArgument), +]) + +print(argspace._name_object_paired_args) +print(argspace.get_by_type(ValueArgument)) \ No newline at end of file diff --git a/mock/mock_app/main.py b/mock/mock_app/main.py index 0ea40b8..13e115c 100644 --- a/mock/mock_app/main.py +++ b/mock/mock_app/main.py @@ -1,13 +1,16 @@ from argenta import App, Orchestrator from argenta.app import PredefinedMessages +from argenta.orchestrator.argparser import ArgParser, BooleanArgument from argenta.app.dividing_line.models import StaticDividingLine, DynamicDividingLine from mock.mock_app.routers import work_router app: App = App( dividing_line=DynamicDividingLine('^'), ) -orchestrator: Orchestrator = Orchestrator() +argparser = ArgParser([BooleanArgument('some')]) +orchestrator: Orchestrator = Orchestrator(argparser) +print(argparser.parsed_argspace.get_by_type(BooleanArgument)) def main(): app.include_router(work_router) @@ -19,7 +22,5 @@ def main(): orchestrator.start_polling(app) -if __name__ == "__main__": - main() - + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 3e3642a..ec70feb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,7 +45,7 @@ exclude = [ ".__pycache__", "tests" ] -line-length=110 +line-length=100 [tool.pyright] typeCheckingMode = "strict" diff --git a/src/argenta/orchestrator/argparser/entity.py b/src/argenta/orchestrator/argparser/entity.py index f05b841..d67861a 100644 --- a/src/argenta/orchestrator/argparser/entity.py +++ b/src/argenta/orchestrator/argparser/entity.py @@ -18,26 +18,48 @@ class ArgSpace: def __init__(self, all_arguments: list[InputArgument]) -> None: self.all_arguments = all_arguments + self._name_object_paired_args: dict[str, InputArgument] = {} + self._type_object_paired_args: dict[type[BaseArgument], list[InputArgument]] = { + BooleanArgument: [], + ValueArgument: [] + } + + self._setup_getters() + @classmethod def from_namespace( - cls, namespace: Namespace, processed_args: list[ValueArgument | BooleanArgument] + cls, + namespace: Namespace, + processed_args: list[ValueArgument | BooleanArgument] ) -> Self: - name_type_paired_args: dict[str, type[BaseArgument]] = {arg.name: type(arg) for arg in processed_args} - return cls( - [ - InputArgument(name=name, value=value, founder_class=name_type_paired_args[name]) - for name, value in vars(namespace).items() - ] - ) + name_type_paired_processed_args: dict[str, type[BaseArgument]] = { + arg.name: type(arg) for arg in processed_args + } + parsed_arguments: list[InputArgument] = [] + + for name, value in vars(namespace).items(): + parsed_arguments.append( + InputArgument( + name=name, + value=value, + founder_class=name_type_paired_processed_args[name] + ) + ) + + return cls(parsed_arguments) + + def _setup_getters(self): + if not self.all_arguments: + return + for input_arg in self.all_arguments: + self._name_object_paired_args[input_arg.name] = input_arg + self._type_object_paired_args[input_arg.founder_class].append(input_arg) def get_by_name(self, name: str) -> InputArgument | None: - for arg in self.all_arguments: - if arg.name == name: - return arg - return None + return self._name_object_paired_args.get(name) def get_by_type(self, arg_type: type[BaseArgument]) -> list[InputArgument] | list[Never]: - return [arg for arg in self.all_arguments if arg.founder_class is arg_type] + return self._type_object_paired_args.get(arg_type, []) class ArgParser: @@ -75,7 +97,10 @@ class ArgParser: for arg in processed_args: if isinstance(arg, BooleanArgument): _ = self._core.add_argument( - arg.string_entity, action=arg.action, help=arg.help, deprecated=arg.is_deprecated + arg.string_entity, + action=arg.action, + help=arg.help, + deprecated=arg.is_deprecated ) else: _ = self._core.add_argument(