Исходный код dublib.CLI.Terminalyzer.Helper

from .Command.Definition import _Argument, _BasePosition, Command, _Flag, _Key, _Position
from ..TextStyler import FastStyler

from dataclasses import dataclass
from typing import Callable

from prettytable import PLAIN_COLUMNS, PrettyTable

#==========================================================================================#
# >>>>> ДОПОЛНИТЕЛЬНЫЕ СТРУКТУРЫ ДАННЫХ <<<<< #
#==========================================================================================#

@dataclass
class _HelpLabels:
	"""
	Контейнер используемых в модуле помощи строк.

	Для `COMMAND_NOT_FOUND` можно определить место подстановки команды через `%c`.
	"""

	COMMAND_DESCRIPTION: str = "Print list of supported commands. For details, add name of command as argument."
	ARGUMENT_DESCRIPTION: str = "The name of command for which you want to see detailed help."
	COMMAND_NOT_FOUND: str = "Command \"%c\" not found."
	CATEGORY_OTHER: str = "Other"

#==========================================================================================#
# >>>>> ОСНОВНОЙ КЛАСС <<<<< #
#==========================================================================================#

[документация] class Helper: """Модуль помощи.""" #==========================================================================================# # >>>>> СВОЙСТВА <<<<< # #==========================================================================================# @property def callback(self) -> Callable: """Функция, в которую направляется вывод помощи.""" return self.__Callback @property def category(self) -> str | None: """Категория команд.""" return self.__Category @property def command(self) -> Command: """Описание команды помощи.""" return self.__HelpCommand @property def is_enabled(self) -> bool: """Состояние: активирован ли модуль помощи.""" return self.__IsEnabled @property def is_sorting_enabled(self) -> bool: """Состояние: выполняется ли сортировка команд по алфавиту.""" return self.__IsSortingEnabled @property def labels(self) -> _HelpLabels: """Оператор работы с используемыми в модуле помощи строками.""" return self.__Labels #==========================================================================================# # >>>>> ПРИВАТНЫЕ МЕТОДЫ ГЕНЕРАЦИИ ПОМОЩИ <<<<< # #==========================================================================================# def __BuildBasePositionDescription(self, base_position: "_BasePosition", typing: bool = True) -> list[str]: """ Строит описание базовой позиции. :param base_position: Данные базовой позиции. :type base_position: _BasePosition :param typing: Переключает отображение типов. :type typing: bool :return: Список строк, описывающих позицию. :rtype: list[str] """ Indicator = "• " Indent = " " Help = list() if not base_position.parameters: return Help Help.append(f"{Indicator}Other parameters:") for CurrentParameter in base_position.parameters: Description = Indent * 2 Description += self.__BuildParameterLabel(CurrentParameter, typing) if CurrentParameter.description: Description += f": {CurrentParameter.description}" Help.append(Description) for Index in range(len(Help)): Help[Index] = f"\n{Indent}" + Help[Index] return Help def __BuildParameterLabel(self, parameter: _Argument | _Flag | _Key, typing: bool = True) -> str: """ Строит надпись-индикатор для параметра. :param parameter: Параметр позиции. :type parameter: Argument | Flag | Key :param typing: Переключает отображение типов. :type typing: bool :raises ValueError: Передан неверный объект. :return: Надпиьс-индикатор. :rtype: str """ match parameter.__class__.__name__: case "_Argument": Typer = f"<{parameter.type.value}>" if typing else "" return f"[argument{Typer}]" case "_Flag": Name = self.__GetParameterName(parameter.name, parameter.aliases) return f"[flag {Name}]" case "_Key": Typer = f"<{parameter.type.value}>" if typing else "" Name = self.__GetParameterName(parameter.name, parameter.aliases) return f"[key{Typer} {Name}]" case _: raise ValueError(f"Incorrect parameter object: {parameter}.") def __BuildPositionDescription(self, position: _Position, typing: bool = True) -> list[str]: """ Строит описание позиции. :param position: Данные позиции. :type position: _Position :param typing: Переключает отображение типов. :type typing: bool :return: Список строк, описывающих позицию. :rtype: list[str] """ Indicator = "• " Indent = " " Help = list() Title = Indicator Name = position.name if position.is_important: Name = FastStyler(Name).colorize.blue Title += Name if len(position.parameters) == 1: Title += " " + self.__BuildParameterLabel(position.parameters[0], typing) if position.description: Title += f": {position.description}" Help.append(Title) else: if position.description: Title += f": {position.description}" Help.append(Title) for CurrentParameter in position.parameters: Description = Indent * 2 Description += self.__BuildParameterLabel(CurrentParameter, typing) if CurrentParameter.description: Description += f": {CurrentParameter.description}" Help.append(Description) for Index in range(len(Help)): Help[Index] = f"\n{Indent}" + Help[Index] return Help def __GenerateCommandMap(self, command: Command) -> str: """ Генерирует позиционную карту команды. :param command: Данные команды. :type command: Command :return: Позиционная карта. Обязательные позиции выделены синим. :rtype: str """ CommandMap = str() for Position in command.positions: Name = Position.name or "POSITION" if Position.is_important: Name = FastStyler(Name).colorize.blue CommandMap += " {" + Name + "}" return CommandMap def __GetParameterName(self, name: str, aliases: list[str]) -> str: """ Возвращает форматированное имя параметра вместе с псевдонимами. :param name: Имя параметра. :type name: str :param aliases: Список псевдонимов. :type aliases: list[str] :return: Форматированное имя параметра вместе с псевдонимами. :rtype: str """ name = FastStyler(name).decorate.bold for Index in range(len(aliases)): aliases[Index] = FastStyler(aliases[Index]).decorate.bold if aliases: return ", ".join([name] + aliases) return name #==========================================================================================# # >>>>> ПУБЛИЧНЫЕ МЕТОДЫ <<<<< # #==========================================================================================# def __init__(self): """Модуль помощи.""" self.__Labels = _HelpLabels() self.__Callback = print self.__Category = None self.__IsEnabled = False self.__IsSortingEnabled = False self.__HelpCommand = Command("help", self.labels.COMMAND_DESCRIPTION, self.__Category) ComPos = self.__HelpCommand.create_position("COMMAND", "Command name for help details.") ComPos.set_argument() self.__HelpCommand.base.add_flag("-t", aliases = ("--typed",), description = "Show arguments and keys expected types.")
[документация] def generate_help_command(self, commands: list[Command], command_name: str, typing: bool = True): """ Отправляет подробное описание команды в callback-функцию. :param commands: Описательные структуры комманд. :type commands: list[Command] :param command_name: Название команды, для которой требуется получить помощь. :type command_name: str :param typing: Переключает отображение типов. :type typing: bool """ CommandForHelp = None for CurrentCommand in commands: if CurrentCommand.name == command_name: CommandForHelp = CurrentCommand break if CommandForHelp: Help = FastStyler(CommandForHelp.name).decorate.bold Help += self.__GenerateCommandMap(CommandForHelp) if CommandForHelp.description: Help += "\n" + FastStyler(CommandForHelp.description).decorate.italic for Position in CommandForHelp.positions: Lines = self.__BuildPositionDescription(Position, typing) if Lines: Help += "".join(Lines) Lines = self.__BuildBasePositionDescription(CommandForHelp.base, typing) if Lines: Help += "".join(Lines) self.__Callback(Help) else: self.__Callback(self.__Labels.COMMAND_NOT_FOUND.replace(r"%c", command_name))
[документация] def generate_help_list(self, commands: list[Command]): """ Отправляет список команд с их описанием в callback-функцию. :param commands: Описательные структуры комманд. :type commands: list[Command] """ #---> Получение данных. #==========================================================================================# CommandsCategories: dict[None | str, list[Command]] = { None: [] } if self.__IsSortingEnabled: commands = sorted(commands, key = lambda CurrentCommand: CurrentCommand.name) for CurrentCommand in commands: if CurrentCommand.category in CommandsCategories.keys(): CommandsCategories[CurrentCommand.category].append(CurrentCommand) else: CommandsCategories[CurrentCommand.category] = [CurrentCommand] # Помещение команд без категории в конец. if len(CommandsCategories.keys()) > 1: NoneCategory = CommandsCategories[None] del CommandsCategories[None] CommandsCategories[None] = NoneCategory #---> Генерация таблицы. #==========================================================================================# Tables = list() for Category in CommandsCategories.keys(): HelpTable = { "Commands": [], "Descriptions": [] } Commands = sorted(CommandsCategories[Category], key = lambda Element: Element.name) for CurrentCommand in Commands: HelpTable["Commands"].append(" " + CurrentCommand.name) HelpTable["Descriptions"].append(CurrentCommand.description or "") TableObject = PrettyTable() CategoryTitle = Category or self.__Labels.CATEGORY_OTHER if len(CommandsCategories.keys()) > 1: TableObject.title = FastStyler(f">>> {CategoryTitle} <<<").decorate.bold TableObject.set_style(PLAIN_COLUMNS) for ColumnName in HelpTable.keys(): Buffer = FastStyler(ColumnName).decorate.bold TableObject.add_column(Buffer, HelpTable[ColumnName]) TableObject.align = "l" TableString = TableObject.get_string() if TableObject.header: TableString = " " * 8 + TableString.lstrip() Tables.append(TableString) self.__Callback("\n\n".join(Tables))
#==========================================================================================# # >>>>> ПУБЛИЧНЫЕ МЕТОДЫ НАСТРОЙКИ <<<<< # #==========================================================================================#
[документация] def enable(self, status: bool = True): """ Переключает использование модуля помощи. :param status: Статус использования модуля. :type status: bool """ self.__IsEnabled = status
[документация] def enable_sorting(self, status: bool = True): """ Переключает сортировку команд в алфавитном порядке. :param status: Состояние сортировки. :type status: bool """ self.__IsSortingEnabled = status
[документация] def set_callback(self, callback: Callable): """ Задаёт функцию, в которую будет передан вывод помощи. :param callback: Функция, в которую направляется вывод помощи. Принимает строку в качестве аргумента. :type callback: Callable """ self.__Callback = callback
[документация] def set_category(self, category: str | None): """ Задаёт категорию для команды помощи. :param category: Название категории. :type category: str | None """ self.__Category = category