diff --git a/.run/Тест ActionState.run.xml b/.run/Тест ActionState.run.xml new file mode 100644 index 0000000..161553e --- /dev/null +++ b/.run/Тест ActionState.run.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/.run/Тест VersionInfo.run.xml b/.run/Тест VersionInfo.run.xml new file mode 100644 index 0000000..52d0a0a --- /dev/null +++ b/.run/Тест VersionInfo.run.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/anb_python_components/classes/action_state.py b/anb_python_components/classes/action_state.py new file mode 100644 index 0000000..90188a3 --- /dev/null +++ b/anb_python_components/classes/action_state.py @@ -0,0 +1,210 @@ +# anb_python_components/classes/action_state.py +from typing import Callable + +from anb_python_components.exceptions.wrong_type_exception import WrongTypeException +from anb_python_components.models.action_state_message import ActionStateMessage, MessageType + +class ActionState[T]: + """ + Класс для хранения состояния действия. + """ + + def __init__ (self, default: T | None = None): + self.__messages: list[ActionStateMessage] = [] + self.value: T | None = default + + @staticmethod + def get_string_error_only () -> Callable[[ActionStateMessage], bool]: + """ + Возвращает лямбду для фильтрации только сообщений типа ERROR. + """ + return lambda message: message.message_type == MessageType.ERROR + + @staticmethod + def get_string_error_and_warning () -> Callable[[ActionStateMessage], bool]: + """ + Возвращает лямбду для фильтрации сообщений типа ERROR и WARNING. + """ + return lambda message: message.message_type in [MessageType.ERROR, MessageType.WARNING] + + @staticmethod + def get_string_all () -> Callable[[ActionStateMessage], bool]: + """ + Возвращает лямбду для фильтрации всех сообщений. + """ + return lambda message: True + + def add_message (self, message: ActionStateMessage): + """ + Добавляет сообщение в список. + :param message: Сообщение. + """ + if message: + self.__messages.append(message) + else: + raise ValueError("Сообщение не может быть пустым.") + + def add (self, message_type: MessageType, message: str, flags: dict[str, bool] | None = None): + """ + Добавляет сообщение в список. + :param message_type: Тип сообщения. + :param message: Сообщение. + :param flags: Флаги (необязательный). + """ + # Создаём сообщение + message = ActionStateMessage(message_type, message, flags) + + # Добавляем в список + self.add_message(message) + + def add_info (self, message: str, flags: dict[str, bool] | None = None): + """ + Добавляет информационное сообщение в список. + :param message: Сообщение. + :param flags: Флаги (необязательный). + """ + self.add(MessageType.INFO, message, flags) + + def add_warning (self, message: str, flags: dict[str, bool] | None = None): + """ + Добавляет предупреждающее сообщение в список. + :param message: Сообщение. + :param flags: Флаги (необязательный). + """ + self.add(MessageType.WARNING, message, flags) + + def add_error (self, message: str, flags: dict[str, bool] | None = None): + """ + Добавляет сообщение об ошибке в список. + :param message: Сообщение. + :param flags: Флаги (необязательный). + """ + self.add(MessageType.ERROR, message, flags) + + def add_state (self, state, clear_all_before: bool = False): + """ + Добавляет другое состояние действия в текущее. + :param state:ActionState - Состояние действия. + :param clear_all_before - Очистить список перед добавлением (по умолчанию - False). + + ВНИМАНИЕ! Метод не передаёт значение value состояния, а просто переносит его сообщения. + """ + # Проверяем тип + if not isinstance(state, ActionState): + # - и если не ActionState, то бросаем исключение + raise WrongTypeException("Неверный тип состояния действия.", "ActionState", str(type(state)), "state") + + # Если нужно очистить список перед добавлением + if clear_all_before: + # - то очищаем его + self.__messages.clear() + + # Получаем список сообщений из состояния + state_messages = state.get_messages() + + # Перебираем все сообщения переданного состояния + for state_message in state_messages: + # - и если это сообщение состояния + if isinstance(state_message, ActionStateMessage): + # -- то добавляем его в текущий список + self.__messages.append(state_message) + + def get_messages (self, predicate: Callable[[ActionStateMessage], bool] | None = None) -> list[ActionStateMessage]: + """ + Возвращает список сообщений с учетом условия (если указан). + :param predicate: Условие выборки. + :return: Список сообщений. + """ + # Если условие указано + if predicate: + # - то фильтруем список по нему + return list(filter(predicate, self.__messages)) + else: + # - если нет, то просто возвращаем весь список + return self.__messages.copy() + + def get_string_messages ( + self, predicate: Callable[[ActionStateMessage], bool] | None = None, separator: str = "\n" + ) -> str: + """ + Возвращает строку сообщений с учетом условия (если указано). + :param predicate: Условие выборки (необязательный). + :param separator: Разделитель строк (необязательный, по умолчанию - "\n"). + :return: Строка сообщений. + """ + # Получаем список сообщений с учетом условия + messages = self.get_messages(predicate) + + # Объединяем их в строку и возвращаем + return separator.join(map(lambda message: message.message, messages)) + + def has_infos (self) -> bool: + """ + Проверяет, есть ли в списке информационные сообщения. + :return: True, если есть, иначе False. + """ + return any(message.message_type == MessageType.INFO for message in self.__messages) + + def has_warnings (self) -> bool: + """ + Проверяет, есть ли в списке предупреждающие сообщения. + :return: True, если есть, иначе False. + """ + return any(message.message_type == MessageType.WARNING for message in self.__messages) + + def has_errors (self) -> bool: + """ + Проверяет, есть ли в списке сообщения об ошибках. + :return: True, если есть, иначе False. + """ + return any(message.message_type == MessageType.ERROR for message in self.__messages) + + def is_success (self, ignore_warnings: bool = False) -> bool: + """ + Проверяет, успешное ли состояние действия. + :param ignore_warnings: Игнорировать предупреждения (по умолчанию - False). + :return: Успешно ли состояние действия. + """ + # Задаем начальное значение результата + result = True + + # Если не нужно игнорировать предупреждения + if not ignore_warnings: + # - то проверяем наличие предупреждений + result = result and not self.has_warnings() + + # Проверяем наличие ошибок + result = result and not self.has_errors() + + # Выдаём результат + return result + + def clear (self, predicate: Callable[[ActionStateMessage], bool] | None = None): + """ + Очищает список сообщений с учетом условия (если указан). + :param predicate: Функция для фильтрации сообщений для очистки. + """ + # Если условие указано + if predicate: + # - то фильтруем список + self.__messages = list(filter(lambda message: not predicate(message), self.__messages)) + else: + # - если нет, то просто очищаем список + self.__messages.clear() + + def count (self, predicate: Callable[[ActionStateMessage], bool] | None = None) -> int: + """ + Возвращает количество сообщений в списке с учетом условия (если указано). + :param predicate: Условие выборки (необязательный). + :return: Количество сообщений. + """ + # Если условие указано + if predicate: + # - то фильтруем список по нему + messages = self.get_messages(predicate) + + # - и возвращаем его длину + return len(messages) + else: + # - если нет, то просто возвращаем длину списка + return len(self.__messages) \ No newline at end of file diff --git a/anb_python_components/enums/message_type.py b/anb_python_components/enums/message_type.py new file mode 100644 index 0000000..4cf00f3 --- /dev/null +++ b/anb_python_components/enums/message_type.py @@ -0,0 +1,44 @@ +# anb_python_components/enums/message_type.py + +from enum import Enum + +class MessageType(Enum): + """ + Перечисление типов сообщений: + - SUCCESS: Успешное выполнение. + - INFO: Информация. + - WARNING: Предупреждение. + - ERROR: Ошибка. + """ + # Успешное выполнение + SUCCESS = 0 + + # Информация + INFO = 1 + + # Предупреждение + WARNING = 2 + + # Ошибка + ERROR = 3 + + def __str__ (self): + """ + Переопределение метода __str__. + :return: Текстовое представление перечисления. + """ + # Получаем текстовое представление + match self: + case MessageType.SUCCESS: + result = "Успех" + case MessageType.INFO: + result = "Информация" + case MessageType.WARNING: + result = "Предупреждение" + case MessageType.ERROR: + result = "Ошибка" + case _: + result = "Неизвестный тип сообщения" + + # Возвращаем результат + return result \ No newline at end of file diff --git a/anb_python_components/models/__init__.py b/anb_python_components/models/__init__.py new file mode 100644 index 0000000..193583a --- /dev/null +++ b/anb_python_components/models/__init__.py @@ -0,0 +1 @@ +# anb_python_components/models/__init__.py \ No newline at end of file diff --git a/anb_python_components/models/action_state_message.py b/anb_python_components/models/action_state_message.py new file mode 100644 index 0000000..95caaa8 --- /dev/null +++ b/anb_python_components/models/action_state_message.py @@ -0,0 +1,27 @@ +# anb_python_components/models/action_state_message.py +from anb_python_components.enums.message_type import MessageType + +class ActionStateMessage: + """ + Модель сообщения о состояния действия. + """ + + def __init__ ( + self, message_type: MessageType = MessageType.INFO, message: str = "", flags: dict[str, bool] | None = None + ): + """ + Конструктор. + :param message_type: Тип сообщения (по умолчанию, INFO). + :param message: Текст сообщения (по умолчанию, пустая строка). + :param flags: Флаги сообщения (по умолчанию, пустой словарь). + """ + self.message_type: MessageType = message_type + self.message: str = message + self.flags: dict[str, bool] = flags if flags is not None else [] + + def __str__ (self): + """ + Переопределение метода __str__. + :return: Текстовое представление модели. + """ + return f"[{str(self.message_type).upper()}] {self.message}" \ No newline at end of file diff --git a/anb_python_components/types/two_dim_size.py b/anb_python_components/types/two_dim_size.py new file mode 100644 index 0000000..0da07b3 --- /dev/null +++ b/anb_python_components/types/two_dim_size.py @@ -0,0 +1,413 @@ +# anb_python_components/types/two_dim_size.py + +class TwoDimSize: + """ + Класс TwoDimSize для представления размеров двухмерного объекта. + """ + + # Разделитель частей по умолчанию. + DEFAULT_DELIMITER: str = ':' + + @staticmethod + def _get_valid_num (num: int, min_num: int | None = 0, max_num: int | None = None) -> int: + """ + Получает корректное значение представленного числа. + :param num: Проверяемое значение. + :param min_num: Минимальное значение (по умолчанию 0). Если None, то считается, что минимальное значение не ограничено. + :param max_num: Максимальное значение (по умолчанию None). Если None, то считается, что максимальное значение не ограничено. + :return: Корректное значение. + """ + # Флаг, разрешающий или запрещающий отрицательные значения + is_negative_allowed = min_num < 0 or min_num is None + + # Безлимитные значения + # - для минимального значения + is_unlimited_min = min_num is None + # - для максимального значения + is_unlimited_max = max_num is None + + # Если значение отрицательное, а отрицательные значения запрещены + if num < 0 and not is_negative_allowed: + # - то возвращаю 0 + return 0 + + # Если значение вышло за ограничения минимальное значения + if not is_unlimited_min and num < min_num: + # - то возвращаю минимальное значение + return min_num + + # Если значение вышло за ограничения максимальное значения + if not is_unlimited_max and num > max_num: + # - то возвращаю максимальное значение + return max_num + + # Если значение корректно, то возвращаю его + return num + + def __init__ ( + self, width: int = 0, height: int = 0, min_width: int | None = 0, max_width: int | None = None, + min_height: int | None = 0, max_height: int | None = None + ): + """ + Инициализирует объект TwoDimSize. + :param width: Ширина (по умолчанию 0). + :param height: Высота (по умолчанию 0). + :param min_width: Минимальная ширина (по умолчанию 0). Если None, то считается, что минимальная ширина не ограничена. + :param max_width: Максимальная ширина (по умолчанию None). Если None, то считается, что максимальная ширина не ограничена. + :param min_height: Минимальная высота (по умолчанию 0). Если None, то считается, что минимальная высота не ограничена. + :param max_height: Максимальная высота (по умолчанию None). Если None, то считается, что максимальная высота не ограничена. + """ + + # Если некорректно заданы минимальные и максимальные значения ширины + if min_width > max_width: + # - то выбрасываю исключение + raise ValueError("Минимальная ширина не может быть больше максимальной.") + + # Если некорректно заданы минимальные и максимальные значения высоты + elif min_height > max_height: + # - то выбрасываю исключение + raise ValueError("Минимальная высота не может быть больше максимальной.") + + # Задаю минимальные и максимальные + # - для ширины + # -- минимальное + self._min_width: int | None = min_width + # -- максимальное + self._max_width: int | None = max_width + # - для высоты + # -- минимальное + self._min_height: int | None = min_height + # -- максимальное + self._max_height: int | None = max_height + + # Устанавливаю ширину + self._width: int = self._get_valid_num(width, min_width, max_width) + + # Устанавливаю высоту + self._height: int = self._get_valid_num(height, min_height, max_height) + + @property + def min_width (self) -> int: + """ + Минимальная ширина. + :return: Значение минимальной ширины. + """ + return self._min_width + + @min_width.setter + def min_width (self, value: int): + """ + Устанавливает минимальную ширину и пересчитывает ширину, если она выходит за новые ограничения. + :param value: Новое значение минимальной ширины. + :return: None + """ + # Если максимальная ширина ограничена и значение минимальной ширины больше максимальной ширины, + # то устанавливаю минимальную ширину равной максимальной ширине, иначе возвращаю значение + self._min_width = self._max_width if self._max_width is not None and value > self._max_width else value + + # При изменении минимальной ширины пересчитываю ширину + self.width = self._width + + @min_width.deleter + def min_width (self): + """ + Удаляет минимальную ширину и устанавливает минимальную ширину в 0. + :return: None + """ + self._min_width = 0 + + @property + def max_width (self) -> int: + """ + Максимальная ширина. + :return: Значение максимальной ширины. + """ + return self._max_width + + @max_width.setter + def max_width (self, value: int): + """ + Устанавливает максимальную ширину и пересчитывает ширину, если она выходит за новые ограничения. + :param value: Новое значение максимальной ширины. + :return: None + """ + # Если минимальная ширина ограничена и значение максимальной ширины меньше минимальной ширины, + # то устанавливаю максимальную ширину равной минимальной ширине, иначе возвращаю значение + self._max_width = self._min_width if self._min_width is not None and value < self._min_width else value + + # При изменении максимальной ширины пересчитываю ширину + self.width = self._width + + @max_width.deleter + def max_width (self): + """ + Удаляет максимальную ширину. + :return: None + """ + self._max_width = None + + @property + def min_height (self) -> int: + """ + Минимальная высота. + :return: Значение минимальной высоты. + """ + return self._min_height + + @min_height.setter + def min_height (self, value: int): + """ + Устанавливает минимальную высоту и пересчитывает высоту, если она выходит за новые ограничения. + :param value: Новое значение минимальной высоты. + :return: None + """ + # Если максимальная высота ограничена и значение минимальной высоты больше максимальной высоты, + # то устанавливаю минимальную высоту равной максимальной высоте, иначе возвращаю значение + self._min_height = self._max_height if self._max_height is not None and value > self._max_height else value + + # При изменении минимальной высоты пересчитываю высоту + self.height = self._height + + @min_height.deleter + def min_height (self): + """ + Удаляет минимальную высоту и устанавливает минимальную высоту в 0. + :return: None + """ + self._min_height = 0 + + @property + def max_height (self) -> int: + """ + Максимальная высота. + :return: Значение максимальной высоты. + """ + return self._max_height + + @max_height.setter + def max_height (self, value: int): + """ + Устанавливает максимальную высоту и пересчитывает высоту, если она выходит за новые ограничения. + :param value: Новое значение максимальной высоты. + :return: None + """ + # Если минимальная высота ограничена и значение максимальной высоты меньше минимальной высоты, + # то устанавливаю максимальную высоту равной минимальной высоте, иначе возвращаю значение + self._max_height = self._min_height if self._min_height is not None and value < self._min_height else value + + # При изменении максимальной высоты пересчитываю высоту + self.height = self._height + + @max_height.deleter + def max_height (self): + """ + Удаляет максимальную высоту. + :return: None + """ + self._max_height = None + + @property + def width (self) -> int: + """ + Ширина. + :return: Установленная ширина. + """ + return self._width + + @width.setter + def width (self, value: int): + """ + Устанавливает ширину. + :param value: Новое значение ширины. + :return: None + """ + self._width = self._get_valid_num(value, self.min_width, self.max_width) + + @width.deleter + def width (self): + """ + Удаляет ширину. + :return: None + """ + self._width = 0 if self._min_width is None else self._min_width + + @property + def height (self) -> int: + """ + Высота. + :return: Установленная высота. + """ + return self._height + + @height.setter + def height (self, value: int): + """ + Устанавливает высоту. + :param value: Новое значение высоты. + :return: None + """ + self._height = self._get_valid_num(value, self.min_height, self.max_height) + + @height.deleter + def height (self): + """ + Удаляет высоту. + :return: None + """ + self._height = 0 if self._min_height is None else self._min_height + + def as_str (self, delimiter: str = DEFAULT_DELIMITER) -> str: + """ + Возвращает строковое представление объекта TwoDimSize в формате "ширина:высота". + :param delimiter: Разделитель частей ширины и высоты (по умолчанию ":"). + :return: Строковое представление объекта TwoDimSize. + """ + return f"{self.width}{delimiter}{self.height}" + + def __str__ (self) -> str: + """ + Строковое представление объекта TwoDimSize. + :return: Строковое представление объекта TwoDimSize. + """ + return self.as_str(TwoDimSize.DEFAULT_DELIMITER) + + def as_tuple (self) -> tuple[int, int]: + """ + Возвращает кортеж (ширина, высота). + :return: Кортеж (ширина, высота). + """ + return self.width, self.height + + def __eq__ (self, other: object) -> bool: + """ + Сравнивает объект TwoDimSize с другим объектом. + :param other: Объект для сравнения. + :return: True, если объекты равны, иначе False. + """ + # Если сравниваем с объектом TwoDimSize + if isinstance(other, TwoDimSize): + # - то сравниваю ширину и высоту + return self.width == other.width and self.height == other.height + else: + # - иначе возвращаю False + return False + + def __repr__ (self) -> str: + """ + Строковое представление объекта TwoDimSize для отладки. + :return: Строковое представление объекта TwoDimSize для отладки. + """ + return f"TwoDimSize({self.width}, {self.height}, min_width={self.min_width}, max_width={self.max_width}, min_height={self.min_height}, max_height={self.max_height})" + + def __hash__ (self) -> int: + """ + Хэш объекта TwoDimSize. + :return: Хэш объекта TwoDimSize. + """ + return hash((self.width, self.height)) + + def __copy__ (self) -> 'TwoDimSize': + """ + Копирует объект TwoDimSize. + :return: Копия объекта TwoDimSize. + """ + return TwoDimSize(self.width, self.height, self.min_width, self.max_width, self.min_height, self.max_height) + + def __deepcopy__ (self, memo: dict) -> 'TwoDimSize': + """ + Глубокое копирование объекта TwoDimSize. + :param memo: Словарь для хранения копий объектов. + :return: Глубокая копия объекта TwoDimSize. + """ + # Если объект уже был скопирован + if id(self) in memo: + # - то возвращаю его копию + return memo[id(self)] + else: + # - иначе создаю копию объекта + memo[id(self)] = copy = TwoDimSize( + self.width, self.height, self.min_width, self.max_width, self.min_height, self.max_height + ) + # - и возвращаю её + return copy + + def __add__ (self, other: 'TwoDimSize') -> 'TwoDimSize': + """ + Складывает объект TwoDimSize с другим объектом TwoDimSize. + :param other: Объект TwoDimSize для сложения. + :return: Объект TwoDimSize, полученный после сложения. + """ + # Если другой объект является объектом TwoDimSize + if isinstance(other, TwoDimSize): + # - то складываю ширину и высоту с объектом TwoDimSize и возвращаю результат в виде объекта TwoDimSize + return TwoDimSize( + self.width + other.width, self.height + other.height, self.min_width, self.max_width, + self.min_height, self.max_height + ) + else: + # - иначе выбрасываю исключение + raise TypeError("Невозможно сложить два объекта разных типов.") + + def __mul__ (self, other: int) -> 'TwoDimSize': + """ + Умножает объект TwoDimSize на целое число. + :param other: Целое число для умножения. + :return: Объект TwoDimSize, полученный после умножения. + """ + # Если другой объект является целым числом + if isinstance(other, int): + # - то перемножаю ширину и высоту на целое число и возвращаю результат в виде объекта TwoDimSize + return TwoDimSize( + self.width * other, self.height * other, self.min_width, self.max_width, self.min_height, + self.max_height + ) + else: + # - иначе выбрасываю исключение + raise TypeError("Невозможно перемножить объект TwoDimSize на другой объект.") + + @staticmethod + def parse ( + text: str, delimiter: str = DEFAULT_DELIMITER, min_width: int | None = 0, max_width: int | None = None, + min_height: int | None = 0, max_height: int | None = None + ) -> 'TwoDimSize': + """ + Создает объект TwoDimSize из строки. + :param max_height: Минимальная высота (по умолчанию 0). Если None, то считается, что минимальная высота не ограничена. + :param min_height: Максимальная высота (по умолчанию None). Если None, то считается, что максимальная высота не ограничена. + :param max_width: Максимальная ширина (по умолчанию None). Если None, то считается, что максимальная ширина не ограничена. + :param min_width: Минимальная ширина (по умолчанию 0). Если None, то считается, что минимальная ширина не ограничена. + :param text: Строка для создания объекта TwoDimSize. + :param delimiter: Разделитель частей ширины и высоты (по умолчанию ":"). + :return: Объект TwoDimSize. + :raises ValueError: Если строка имеет неверный формат. + :raises TypeError: При попытке преобразовать строку в объект int (ширину или высоту). + """ + # Разделяю значения + split_sizes = text.split(delimiter) + + # Проверяю, что массив имеет ровно два элемента + if len(split_sizes) != 2: + # - иначе выбрасываю исключение + raise ValueError("Неверный формат строки для TwoDimSize.") + + # Получаю ширину и высоту + width, height = split_sizes + + # Проверяю, что ширина получена + if width: + # - и перевожу ее в целое число + width = int(width) + else: + # - иначе выбрасываю исключение + raise ValueError("Неверный формат ширины в строке для TwoDimSize.") + + # Проверяю, что высота получена + if height: + # - и перевожу ее в целое число + height = int(height) + else: + # - иначе выбрасываю исключение + raise ValueError("Неверный формат высоты в строке для TwoDimSize.") + + # Если все проверки пройдены успешно, то создаю объект TwoDimSize с заданными шириной и высотой и возвращаю его + return TwoDimSize(width, height, min_width, max_width, min_height, max_height) \ No newline at end of file diff --git a/anb_python_components/types/version_info.py b/anb_python_components/types/version_info.py new file mode 100644 index 0000000..f5b6237 --- /dev/null +++ b/anb_python_components/types/version_info.py @@ -0,0 +1,461 @@ +# anb_python_components/types/version_info.py +import re + +from anb_python_components.extensions.string_extension import StringExtension + +class VersionInfo: + """ + Класс для работы с версиями. + """ + # Шаблон вывода по умолчанию. + DEFAULT_TEMPLATE: str = '#{Major}.#{Minor}.#{Release}.#{Build} #{Stage} #{StageNumber}' + + def __init__ (self, major: int, minor: int, release: int, build: int, stage: str = '', stage_number: int = 0): + """ + Создание экземпляра класса VersionInfo. + :param major: Мажорная версия. + :param minor: Минорная версия. + :param release: Номер релиза. + :param build: Номер сборки. + :param stage: Стадия. + :param stage_number: Номер стадии. + """ + self.__major: int = major + self.__minor: int = minor + self.__release: int = release + self.__build: int = build + self.__stage: str = stage + self.__stage_number: int = stage_number + + # Мажорная версия + @property + def major (self) -> int: + """ + Получение значения major. + :return: Значение major. + """ + return self.__major + + @major.setter + def major (self, value: int): + """ + Установка значения major. + :param value: Значение major. + :return: None + """ + self.__major = value if value >= 0 else 0 + + @major.deleter + def major (self): + """ + Удаление значения major. + :return: None + """ + self.__major = 0 + + # Минорная версия + @property + def minor (self) -> int: + """ + Получение значения minor. + :return: Значение minor. + """ + return self.__minor + + @minor.setter + def minor (self, value: int): + """ + Установка значения minor. + :param value: Значение minor. + :return: None + """ + self.__minor = value if value >= 0 else 0 + + @minor.deleter + def minor (self): + """ + Удаление значения minor. + :return: None + """ + self.__minor = 0 + + # Номер релиза + @property + def release (self) -> int: + """ + Получение значения release. + :return: Значение release. + """ + return self.__release + + @release.setter + def release (self, value: int): + """ + Установка значения release. + :param value: Значение release. + :return: None + """ + self.__release = value if value >= 0 else 0 + + @release.deleter + def release (self): + """ + Удаление значения release. + :return: None + """ + self.__release = 0 + + # Номер сборки + @property + def bild (self) -> int: + """ + Получение значения bild. + :return: Значение bild. + """ + return self.__build + + @bild.setter + def bild (self, value: int): + """ + Установка значения bild. + :param value: Значение bild. + :return: None + """ + self.__build = value if value >= 0 else 0 + + @bild.deleter + def bild (self): + """ + Удаление значения bild. + :return: None + """ + self.__build = 0 + + # Стадия + @property + def stage (self) -> str: + """ + Получение значения stage. + :return: Значение stage. + """ + return self.__stage + + @stage.setter + def stage (self, value: str | None): + """ + Установка значения stage. + :param value: Значение stage. + :return: None + """ + self.__stage = value if value is not None else '' + + @stage.deleter + def stage (self): + """ + Удаление значения stage. + :return: None + """ + self.__stage = '' + + # Номер стадии + @property + def stage_number (self) -> int: + """ + Получение значения stage_number. + :return: Значение stage_number. + """ + return self.__stage_number + + @stage_number.setter + def stage_number (self, value: int): + """ + Установка значения stage_number. + :param value: Значение stage_number. + :return: None + """ + self.__stage_number = value if value >= 0 else 0 + + @stage_number.deleter + def stage_number (self): + """ + Удаление значения stage_number. + :return: None + """ + self.__stage_number = 0 + + def to_string (self, template: str = DEFAULT_TEMPLATE) -> str: + """ + Преобразование экземпляра класса VersionInfo в строку. + :param template: Шаблон для преобразования. + :return: Строка с версией. + """ + # Создание словаря для замены + template_dict = { + '#{Major}': str(self.major), + '#{Minor}': str(self.minor), + '#{Release}': str(self.release), + '#{Build}': str(self.bild), + '#{Stage}': str(self.stage), + '#{StageNumber}': str(self.stage_number) if self.stage_number > 0 else '' + } + + # Замена значений в шаблоне + replaced = StringExtension.replace_all(template_dict, template) + + # Удаление лишних пробелов и символов в начале и конце строки и возврат результата + return replaced.strip() + + def __str__ (self) -> str: + """ + Переопределение метода __str__. + :return: Строка с версией. + """ + return self.to_string(VersionInfo.DEFAULT_TEMPLATE) + + def __repr__ (self) -> str: + """ + Переопределение метода __repr__. + :return: Строка с версией. + """ + return f'VersionInfo(major={self.major}, minor={self.minor}, release={self.release}, build={self.bild}, stage={self.stage}, stage_number={self.stage_number})' + + def __compare (self, other: 'VersionInfo') -> int: + """ + Сравнение версий. + :param other: Версия для сравнения. + :return: 1, если текущая версия больше, -1, если текущая версия меньше, 0, если версии равны. + """ + # Проверка типа + if not isinstance(other, VersionInfo): + # - если other не является экземпляром VersionInfo, то выбрасываем исключение + raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(other)}') + + # Сравнение мажорных версий. Если текущая мажорная версия больше + if self.major > other.major: + # - возвращаем 1 + return 1 + # Если текущая мажорная версия меньше + elif self.major < other.major: + # - возвращаем -1 + return -1 + + # Если мажорные версии равны, то сравниваем минорные версии. Если текущая минорная версия больше + if self.minor > other.minor: + # - возвращаем 1 + return 1 + # Если текущая минорная версия меньше + elif self.minor < other.minor: + # - возвращаем -1 + return -1 + + # Если мажорные и минорные версии равны, то сравниваем номер релиза. Если текущий номер релиза больше + if self.release > other.release: + # - возвращаем 1 + return 1 + # Если текущий номер релиза меньше + elif self.release < other.release: + # - возвращаем -1 + return -1 + + # Если мажорные, минорные и номер релиза равны, то сравниваем номер сборки. Если текущий номер сборки больше + if self.bild > other.bild: + # - возвращаем 1 + return 1 + # Если текущий номер сборки меньше + elif self.bild < other.bild: + # - возвращаем -1 + return -1 + + # Если мажорные, минорные, номер релиза и номер сборки равны, то равны и версии. Возвращаем 0. + return 0 + + def __eq__ (self, other: 'VersionInfo') -> bool: + """ + Сравнение версий на равенство (==). + :param other: Версия для сравнения. + :return: True, если версии равны, False, если версии не равны. + """ + # Проверка типа + if not isinstance(other, VersionInfo): + # - если other не является экземпляром VersionInfo, то выбрасываем исключение + raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(other)}') + + # Если версии равны, то возвращаем True + return self.__compare(other) == 0 + + def __ne__ (self, other: 'VersionInfo') -> bool: + """ + Сравнение версий на неравенство (!=). + :param other: Версия для сравнения. + :return: True, если версии не равны, False, если версии равны. + """ + # Проверка типа + if not isinstance(other, VersionInfo): + # - если other не является экземпляром VersionInfo, то выбрасываем исключение + raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(other)}') + + # Если версии не равны, то возвращаем True + return self.__compare(other) != 0 + + def __lt__ (self, other: 'VersionInfo') -> bool: + """ + Сравнение версий на меньше (<). + :param other: Версия для сравнения. + :return: True, если текущая версия меньше, False, если текущая версия больше или равна. + """ + # Проверка типа + if not isinstance(other, VersionInfo): + # - если other не является экземпляром VersionInfo, то выбрасываем исключение + raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(other)}') + + # Если текущая версия меньше, то возвращаем True + return self.__compare(other) == -1 + + def __gt__ (self, other: 'VersionInfo') -> bool: + """ + Сравнение версий на больше (>). + :param other: Версия для сравнения. + :return: True, если текущая версия больше, False, если текущая версия меньше или равна. + """ + # Проверка типа + if not isinstance(other, VersionInfo): + # - если other не является экземпляром VersionInfo, то выбрасываем исключение + raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(other)}') + + # Если текущая версия больше, то возвращаем True + return self.__compare(other) == 1 + + def __le__ (self, other: 'VersionInfo') -> bool: + """ + Сравнение версий на меньше или равно (<=). + :param other: Версия для сравнения. + :return: True, если текущая версия меньше или равна, False, если текущая версия больше. + """ + # Проверка типа + if not isinstance(other, VersionInfo): + # - если other не является экземпляром VersionInfo, то выбрасываем исключение + raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(other)}') + + # Если текущая версия меньше или равна, то возвращаем True + return self.__compare(other) in (0, -1) + + def __ge__ (self, other: 'VersionInfo') -> bool: + """ + Сравнение версий на больше или равно (>=). + :param other: Версия для сравнения. + :return: True, если текущая версия больше или равна, False, если текущая версия меньше. + """ + # Проверка типа + if not isinstance(other, VersionInfo): + # - если other не является экземпляром VersionInfo, то выбрасываем исключение + raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(other)}') + + # Если текущая версия больше или равна, то возвращаем True + return self.__compare(other) in (0, 1) + + def in_range ( + self, start: 'VersionInfo' or None = None, end: 'VersionInfo' or None = None, start_inclusive: bool = True, + end_inclusive: bool = True + ) -> bool: + """ + Проверка версии на принадлежность диапазону. + :param start: Начало диапазона (по умолчанию None). Если start равен None, то считается, что start не ограничен. + :param end: Конец диапазона (по умолчанию None). Если end равен None, то считается, что end не ограничен. + :param start_inclusive: Включать ли начало диапазона (по умолчанию True). + :param end_inclusive: Включать ли конец диапазона (по умолчанию True). + :return: True, если версия принадлежит диапазону, False, если версия не принадлежит диапазону. + """ + # Если start не указан + if start is None: + # - устанавливаем start_inclusive равным True + start_inclusive = True + # - устанавливаем start равным self + start = self + + # Если end не указан + if end is None: + # - устанавливаем end_inclusive равным True + end_inclusive = True + # - устанавливаем end равным self + end = self + + # Проверка типов + if not isinstance(start, VersionInfo) or not isinstance(end, VersionInfo): + # - если start или end не являются экземплярами VersionInfo, то выбрасываем исключение + raise TypeError(f'Невозможно сравнить тип VersionInfo с {type(start)} и {type(end)}') + + # Если start совпадает с версией + if self == start: + # - если включать начало диапазона (start_inclusive), то возвращаем True, иначе False + return True if start_inclusive else False + + # Если end совпадает с версией + if self == end: + # - если включать конец диапазона (end_inclusive), то возвращаем True, иначе False + return True if end_inclusive else False + + # Если текущая версия находится между start и end, то возвращаем True, иначе False + return True if start < self < end else False + + @staticmethod + def parse (version: str) -> 'VersionInfo': + version = version.strip() + + # Разбиваем строку на части по пробелам (1 часть - основная - мажор, минор, релиз, сборка, + # 2 часть - стадия и 3 - номер стадии): + # - находим позицию первого пробела + start = version.find(" ") + # - если позиция первого пробела не найдена + if start == -1: + # - устанавливаем конец строки + start = len(version) + # - находим позицию последнего пробела + end = version.rfind(" ") if start < len(version) else -1 + + # - получаем основную часть + main_part = version[:start].strip() + # - получаем стадию + stage = version[start:end].strip() if end > 0 else '' + # - получаем номер стадии в виде строки + stage_number_text = version[end:].strip() + # - получаем номер стадии из строки + try: + stage_number = int(stage_number_text) + except ValueError: + stage_number = 0 + + # Составляем регулярное выражение для парсинга базовой информации о версии + pattern = r'^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?$' + + # Парсим базовую информацию о версии + matches = re.match(pattern, main_part) + + # Если не удалось найти соответствие + if not matches: + # - возвращаем пустую версию + return VersionInfo(0, 0, 0, 0, stage, stage_number) + + # Получаем группы из совпадения + groups = matches.groups() + + # Проверяем, что найдены как минимум 2 части + if len(groups) < 2: + # - иначе возвращаем пустую версию + return VersionInfo(0, 0, 0, 0, stage, stage_number) + + # Проверяем, что групп 4 + if len(groups) != 4: + # - иначе возвращаем пустую версию + return VersionInfo(0, 0, 0, 0, stage, stage_number) + + # Распаковываем значения + major, minor, release, build = groups + + # Преобразуем строки в целые числа + major = int(major) if major else 0 + minor = int(minor) if minor else 0 + release = int(release) if release else 0 + build = int(build) if build else 0 + + # Возвращаем экземпляр класса VersionInfo с полученными значениями + return VersionInfo(major, minor, release, build, stage, stage_number) \ No newline at end of file diff --git a/help/class_desc/classes/action_state.md b/help/class_desc/classes/action_state.md new file mode 100644 index 0000000..3d48d04 --- /dev/null +++ b/help/class_desc/classes/action_state.md @@ -0,0 +1,379 @@ +# Класс `ActionState` + +Класс `ActionState` предназначен для отслеживания состояния выполняемых действий в программе, фиксируя изменения, +происходящие в процессе выполнения, а также накапливая сообщения о событиях, успехах и ошибках. Он служит контейнером +для сохранения промежуточных состояний и обеспечивает простой интерфейс взаимодействия с ними. + +## Основная информация + +- **Имя файла**: anb_python_components\classes\action_state.py +- **Автор**: Александр Бабаев +- **Версия**: 1.0.0 +- **Дата начала поддержки**: с версии 1.0 + +## Атрибуты и методы класса + +### Атрибуты + +- **`value: T`**: Хранит основное значение состояния (может быть любым объектом). +- **`__messages: list[ActionStateMessage]`**: Внутренний список сообщений, накопленных в процессе выполнения действия. + +### Конструктор (`__init__`) + +Принимает необязательное значение по умолчанию, которое устанавливается в качестве основного значения состояния. + +**Параметры**: + +- `default: T | None = None`: Значение по умолчанию. + +**Пример использования**: + +```python +from anb_python_components.classes.action_state import ActionState + +state = ActionState[bool](False) +``` + +### Основные методы + +#### Метод `add_message` + +Добавляет новое сообщение в список сообщений. + +**Параметры**: + +- `message: ActionStateMessage`: Модель сообщения. + +**Пример использования**: + +```python +from anb_python_components.classes.action_state import ActionState, ActionStateMessage, MessageType + +state = ActionState[bool](False) +state.add_message(ActionStateMessage(MessageType.INFO, "Процесс запущен.")) +``` + +--- + +#### Метод `add` + +Удобный способ добавления сообщений, принимающий непосредственно тип сообщения и его текст. + +**Параметры**: + +- `message_type: MessageType`: Тип сообщения. +- `message: str`: Сообщение. +- `flags: dict[str, bool] | None = None`: Флаги сообщения (необязательны). + +**Пример использования**: + +```python +from anb_python_components.classes.action_state import ActionState, MessageType + +state = ActionState[bool](False) +state.add(MessageType.WARNING, "Скорость низкая.") +``` + +--- + +### Метод `add_info` + +Добавляет информационное сообщение в список состояний. + +**Параметры**: + +- `message: str`: Текст самого информационного сообщения. +- `flags: dict[str, bool] | None = None`: Дополнительные мета-данные или признаки сообщения (например, отметка о + важности или срочности). + +**Пример использования**: + +```python +from anb_python_components.classes.action_state import ActionState + +state = ActionState[bool](False) +state.add_info("Начали загрузку данных.") +``` + +--- + +### Метод `add_warning` + +Добавляет предупреждение в список состояний. + +**Параметры**: + +- `message: str`: Текст предупреждения. +- `flags: dict[str, bool] | None = None`: Дополнительные флаги для конкретного предупреждения. + +**Пример использования**: + +```python +from anb_python_components.classes.action_state import ActionState + +state = ActionState[bool](False) +state.add_warning("Не хватает оперативной памяти.") +``` + +--- + +### Метод `add_error` + +Добавляет сообщение об ошибке в список состояний. + +**Параметры**: + +- `message: str`: Текст сообщения об ошибке. +- `flags: dict[str, bool] | None = None`: Дополнительные флаги для конкретной ошибки. + +**Пример использования**: + +```python +from anb_python_components.classes.action_state import ActionState + +state = ActionState[bool](False) +state.add_error("Не удалось установить соединение с базой данных.") +``` + +--- + +#### Метод `add_state` + +Объединяет текущее состояние с другим состоянием, перенося его сообщения. + +**Параметры**: + +- `state: ActionState`: Другое сообщение о состоянии. +- `clear_all_before: bool = False`: Очистить список перед добавлением (по умолчанию - False). + +**Пример использования**: + +```python +from anb_python_components.classes.action_state import ActionState + +state = ActionState[bool](False) +another_state = ActionState() +another_state.add_info("Дополнительная информация.") +state.add_state(another_state) +``` + +--- + +#### Метод `get_messages` + +Возвращает список сообщений, возможно применяя фильтр по условию. + +**Параметры**: + +- `predicate: Callable[[ActionStateMessage], bool] | None = None`: Условие выборки. + +**Метод возвращает** `list[ActionStateMessage]`: список сообщений. + +**Пример использования**: + +```python +from anb_python_components.classes.action_state import ActionState, MessageType + +state = ActionState[bool](False) +errors = state.get_messages(lambda m: m.message_type == MessageType.ERROR) +``` + +--- + +### Метод `get_string_messages` + +Возвращает объединённую строку сообщений, фильтруя их по определённому критерию (предикат). Полезен для визуализации +сообщений в логах или отчётах. + +**Параметры**: + +- `predicate: Callable[[ActionStateMessage], bool] | None = None`: Функциональный предикат для фильтрации сообщений + (например, показывать только ошибки или предупреждения). +- `separator: str = "\n"`: Разделитель между сообщениями (по умолчанию новая строка). + +**Метод возвращает** `str`: строка сообщений. + +**Пример использования**: + +```python +from anb_python_components.classes.action_state import ActionState, MessageType + +state = ActionState[bool](False) +state.add_error("Ошибка в сети.") +state.add_warning("Медленная скорость загрузки.") + +filtered_messages = state.get_string_messages(predicate = lambda msg: msg.message_type == MessageType.ERROR) +print(filtered_messages) # "Ошибка в сети." +``` + +--- + +### Метод `has_infos` + +Проверяет, имеются ли в списке сообщения информационного типа (`INFO`). + +**Метод возвращает** `bool`: True, если есть, иначе False. + +**Пример использования**: + +```python +from anb_python_components.classes.action_state import ActionState + +state = ActionState[bool](False) +state.add_info("Процесс завершён.") + +if state.has_infos(): + print("Имеются информационные сообщения.") # Будет выведено +``` + +--- + +### Метод `has_warnings` + +Проверяет, присутствуют ли в списке предупреждающие сообщения (`WARNING`). + +**Метод возвращает** `bool`: True, если есть, иначе False. + +**Пример использования**: + +```python +from anb_python_components.classes.action_state import ActionState + +state = ActionState[bool](False) +state.add_warning("Недостаточно памяти.") + +if state.has_warnings(): + print("Есть предупреждающие сообщения.") # Будет выведено +``` + +--- + +### Метод `has_errors` + +Проверяет, существуют ли в списке сообщения об ошибках (`ERROR`). + +**Метод возвращает** `bool`: True, если есть, иначе False. + +**Пример использования**: + +```python +from anb_python_components.classes.action_state import ActionState + +state = ActionState[bool](False) +state.add_error("Время ожидания истекло.") + +if state.has_errors(): + print("Присутствуют сообщения об ошибках.") # Будет выведено +``` + +--- + +### Метод `clear` + +Очищает список сообщений, возможно применяя условие фильтрации. + +**Параметры**: + +- `predicate: Callable[[ActionStateMessage], bool] | None = None`: Функциональный предикат для фильтрации сообщений. + +**Пример использования**: + +```python +from anb_python_components.classes.action_state import ActionState, MessageType + +state = ActionState[bool](False) +state.clear(lambda m: m.message_type == MessageType.INFO) +``` + +--- + +### Метод `is_success` + +Проверяет, было ли состояние успешным, основываясь на наличии ошибок и предупреждений. + +**Параметры**: + +- `ignore_warnings: bool = False`: Игнорировать предупреждения (по умолчанию - False). + +**Метод возвращает** `bool`: True, если успешно, иначе False. + +**Пример использования**: + +```python +from anb_python_components.classes.action_state import ActionState + +state = ActionState[bool](False) +if state.is_success(): + print("Операция прошла успешно.") +``` + +--- + +### Метод `count` + +Метод `count` позволяет получать количество сообщений в списке, потенциально применяя дополнительное условие +фильтрации (предикат). Таким образом, можно точно подсчитать число нужных сообщений (например, ошибок, предупреждений +или общих записей). + +**Параметры**: + +- **`predicate: Callable[[ActionStateMessage], bool] | None = None`**: Опциональный предикат-функция, которая получает + каждое сообщение и решает, включать ли его в итоговый счётчик. Если предикат не указан, учитываются все сообщения. + +**Метод возвращает** `int`: количество сообщений. + +**Пример использования**: + +Предположим, мы хотим посчитать количество сообщений каждого типа: + +```python +from anb_python_components.classes.action_state import ActionState, MessageType + +state = ActionState[bool](False) +state.add_info("Данные получены.") +state.add_warning("Память почти заполнена.") +state.add_error("Не удаётся подключиться к серверу.") + +# Посчитать общее количество сообщений +total_messages = state.count() +print(total_messages) # Вывод: 3 + +# Посчитать количество ошибок +error_count = state.count(predicate = lambda msg: msg.message_type == MessageType.ERROR) +print(error_count) # Вывод: 1 + +# Посчитать количество информационных сообщений +info_count = state.count(predicate = lambda msg: msg.message_type == MessageType.INFO) +print(info_count) # Вывод: 1 +``` + +### Вспомогательные статичные методы для фильтрации сообщений + +Эти методы предоставляют готовые предикаты для извлечения конкретных типов сообщений. + +- `get_string_error_only() -> Callable[[ActionStateMessage], bool]`: Возвращает предикат для фильтрации только сообщений + об ошибках. +- `get_string_error_and_warning() -> Callable[[ActionStateMessage], bool]`: Возвращает предикат для фильтрации сообщений + об ошибках и предупреждениях. +- `get_string_all() -> Callable[[ActionStateMessage], bool]`: Возвращает предикат, включающий все сообщения (без + фильтрации). + +**Примеры использования**: + +```python +from anb_python_components.classes.action_state import ActionState + +state = ActionState[bool](False) +# Получить только ошибки +only_errors = state.get_string_messages(predicate = ActionState.get_string_error_only(), separator = ", ") + +# Все сообщения +all_messages = state.get_string_messages(predicate = ActionState.get_string_all()) +``` + +## Заключение + +Класс `ActionState` играет ключевую роль в мониторинге и управлении состояниями в приложениях, позволяя контролировать +ход выполнения операций и оперативно реагировать на возникающие события и ошибки. + +[На главную](../../index.md) \ No newline at end of file diff --git a/help/class_desc/enums/message_type.md b/help/class_desc/enums/message_type.md new file mode 100644 index 0000000..388ac8a --- /dev/null +++ b/help/class_desc/enums/message_type.md @@ -0,0 +1,26 @@ +# Перечисление `MessageType` + +Перечисление `MessageType` предназначено для классификации сообщений в программном коде. Оно помогает упорядочить типы +уведомлений и отчетов, выводимых системой. Каждый элемент перечисления обозначает определенный статус или категорию +сообщения, облегчая чтение и интерпретацию результатов выполнения программ. + +## Основная информация + +- **Имя файла**: anb_python_components\enums\message_type.py +- **Автор**: Александр Бабаев +- **Версия**: 1.0.0 +- **Дата начала поддержки**: с версии 1.0 + +## Элементы перечисления + +- **SUCCESS**: Означает успешное завершение какого-то процесса или операции. +- **INFO**: Информационные уведомления общего характера. +- **WARNING**: Предупредительные сообщения, предупреждающие о потенциальных проблемах. +- **ERROR**: Сигнал о произошедшей ошибке или сбое в выполнении программы. + +## Заключение + +Перечисление `MessageType` способствует улучшению организации логики сообщений в приложениях, обеспечивая понятное +разделение на категории, что снижает риск путаницы и улучшает диагностику и тестирование. + +[На главную](../../index.md) \ No newline at end of file diff --git a/help/class_desc/extensions/guid_extension.md b/help/class_desc/extensions/guid_extension.md new file mode 100644 index 0000000..0609bec --- /dev/null +++ b/help/class_desc/extensions/guid_extension.md @@ -0,0 +1,92 @@ +# Класс `GUIDExtension` + +Класс `GUIDExtension` предназначен для расширения функциональности работы с глобальным уникальным идентификатором +(GUID). Он предоставляет дополнительные методы для генерации новых GUID, проверки их валидности, парсинга из строк и +сравнения между собой. + +## Основная информация + +- **Имя файла**: anb_python_components\extensions\guid_extension.py +- **Автор**: Александр Бабаев +- **Версия**: 1.0.0 +- **Дата начала поддержки**: с версии 1.0 + +## Атрибуты и методы класса + +### Метод `generate` + +Генерирует новый случайный GUID, следуя стандартам RFC 4122. Новый GUID создается с использованием криптографически +стойких алгоритмов генерации чисел. + +**Пример использования**: + +```python +from anb_python_components.extensions.guid_extension import GUIDExtension + +new_guid = GUIDExtension.generate() +print(new_guid) # Пример: 123e4567-e89b-12d3-a456-426655440000 +``` + +### Метод `is_equal` + +Проверяет равенство двух GUID, поддерживая передачу обоих как строк, так и объектов типа `GUID`. + +**Пример использования**: + +```python +from anb_python_components.extensions.guid_extension import GUIDExtension +from anb_python_components.types.guid import GUID + +guid1 = "123e4567-e89b-12d3-a456-426655440000" +guid2 = GUID("123e4567-e89b-12d3-a456-426655440000") + +equal = GUIDExtension.is_equal(guid1, guid2) +print(equal) # True +``` + +### Метод `validate` + +Проверяет, соответствует ли строка или объект правильному формату GUID. + +**Пример использования**: + +```python +from anb_python_components.extensions.guid_extension import GUIDExtension + +valid = GUIDExtension.validate("123e4567-e89b-12d3-a456-426655440000") +print(valid) # True +``` + +### Метод `is_invalid_or_empty` + +Проверяет, является ли GUID недействительным или пустым. + +**Пример использования**: + +```python +from anb_python_components.extensions.guid_extension import GUIDExtension + +invalid = GUIDExtension.is_invalid_or_empty("invalid-guid") +print(invalid) # True +``` + +### Метод `parse` + +Парсит строку в GUID, возвращая объект типа `GUID`. Может вернуть пустой GUID, если передано недопустимое значение. + +**Пример использования**: + +```python +from anb_python_components.extensions.guid_extension import GUIDExtension + +parsed_guid = GUIDExtension.parse("123e4567-e89b-12d3-a456-426655440000") +print(parsed_guid) # 123e4567-e89b-12d3-a456-426655440000 +``` + +## Заключение + +Класс `GUIDExtension` позволяет создать надежную систему работы с GUID в Python-проектах, облегчая генерацию, проверку и +сравнение идентификаторов. Благодаря этому обеспечивается дополнительная защита от возможных проблем с передачей +некорректных данных. + +[На главную](../../index.md) \ No newline at end of file diff --git a/help/class_desc/models/action_state_message.md b/help/class_desc/models/action_state_message.md new file mode 100644 index 0000000..a7be049 --- /dev/null +++ b/help/class_desc/models/action_state_message.md @@ -0,0 +1,55 @@ +# Класс модели `ActionStateMessage` + +Модель `ActionStateMessage` предназначена для формирования сообщений о состоянии выполненных действий в рамках проекта. +Она содержит основную информацию о результате выполненного действия, включая тип сообщения, текстовое описание и флаги ( +мета-данные). + +## Основная информация + +- **Имя файла**: anb_python_components\models\action_state_message.py +- **Автор**: Александр Бабаев +- **Версия**: 1.0.0 +- **Дата начала поддержки**: с версии 1.0 + +## Атрибуты и конструктор + +### Конструктор (`__init__`) + +Принимает три необязательных параметра: + +- **`message_type`**: Тип сообщения, использующий перечисление `MessageType` (по умолчанию `INFO`). +- **`message`**: Сам текст сообщения (по умолчанию пустая строка). +- **`flags`**: Дополнительные метаданные или признаки сообщения (по умолчанию пустой словарь). + +**Пример использования конструктора**: + +```python +from anb_python_components.models.action_state_message import ActionStateMessage, MessageType + +state_message = ActionStateMessage(MessageType.SUCCESS, "Действие выполнено успешно.", {"has_errors": False}) +``` + +## Пример полного использования + +Вот пример использования класса `ActionStateMessage`: + +```python +from anb_python_components.models.action_state_message import ActionStateMessage, MessageType + +# Создание сообщения успешного действия +success_message = ActionStateMessage(MessageType.SUCCESS, "Операция завершилась успешно.") + +# Создание сообщения с дополнительной информацией +info_message = ActionStateMessage(MessageType.INFO, "Начало обработки данных.", {"in_progress": False}) + +# Печать сообщений +print(success_message.message) # Операция завершилась успешно. +print(info_message.message) # Начало обработки данных. +``` + +## Заключение + +Модель `ActionStateMessage` полезна для унификации способов оповещения пользователей или администратора о ходе +выполнения тех или иных процессов. Четкая структура сообщений облегчает чтение и дальнейшее развитие приложения. + +[На главную](../../index.md) \ No newline at end of file diff --git a/help/class_desc/types/guid.md b/help/class_desc/types/guid.md new file mode 100644 index 0000000..c6301b1 --- /dev/null +++ b/help/class_desc/types/guid.md @@ -0,0 +1,67 @@ +# Класс `GUID` + +Класс `GUID` представляет реализацию уникального глобального идентификатора (Global Unique Identifier), широко +применяемого в разработке программного обеспечения для уникальной идентификации сущностей. Данный класс добавляет +дополнительную безопасность и контроль правильности передачи и обработки уникальных идентификаторов. + +## Основная информация + +- **Имя файла**: anb_python_components\types\guid.py +- **Автор**: Александр Бабаев +- **Версия**: 1.0.0 +- **Дата начала поддержки**: с версии 1.0 + +## Атрибуты и методы класса + +### Свойства и конструкторы + +- **Атрибут `EMPTY`**: Представляет пустое значение GUID (стандартное значение для незаданных GUID). +- **Конструктор (`__init__`)**: Осуществляет создание нового экземпляра GUID, принимает либо строку, либо другой + экземпляр GUID и проверяет правильность передаваемых данных. + +### Специальные методы + +- **Метод `__str__`**: Предоставляет текстовое представление текущего GUID. +- **Метод `__eq__`**: Определяет правило сравнения двух GUID друг с другом. + +### Вспомогательные методы + +- **Метод `validate_str`**: Проверяет строку на соответствие стандартному формату GUID (регулярные выражения + используются для проверки синтаксиса). + +## Исключения + +- **Ошибка `WrongTypeException`**: Генератор исключений срабатывает при передаче неверного типа данных или нарушении + формата GUID. + +## Пример использования + +Создание экземпляра GUID: + +```python +from anb_python_components.types.guid import GUID + +my_guid = GUID("123e4567-e89b-12d3-a456-426655440000") +print(my_guid) # 123e4567-e89b-12d3-a456-426655440000 +``` + +Сравнение двух GUID: + +```python +from anb_python_components.types.guid import GUID + +first_guid = GUID("123e4567-e89b-12d3-a456-426655440000") +second_guid = GUID("00000000-0000-0000-0000-000000000000") + +if first_guid == second_guid: + print("GUID совпадают") +else: + print("GUID различаются") +``` + +## Заключение + +Класс `GUID` позволяет грамотно управлять уникальным идентификатором, обеспечивая автоматическое преобразование и +проверку корректности данных. Такой подход минимизирует вероятность ошибок и улучшает надежность приложения. + +[На главную](../../index.md) \ No newline at end of file diff --git a/help/class_desc/types/two_dim_size.md b/help/class_desc/types/two_dim_size.md new file mode 100644 index 0000000..fe09b7a --- /dev/null +++ b/help/class_desc/types/two_dim_size.md @@ -0,0 +1,165 @@ +# Класс `TwoDimSize` + +Класс `TwoDimSize` предназначен для работы с двумерными размерами, такими как ширина и высота. Он предоставляет ряд +инструментов для эффективного управления и обработки размеров, включая ограничение минимальных/максимальных границ, +арифметические операции и удобное представление в строковом виде. + +## Основная информация + +- **Имя файла**: anb_python_components\types\two_dim_size.py +- **Автор**: Александр Бабаев +- **Версия**: 1.0.0 +- **Дата начала поддержки**: с версии 1.0 + +## Атрибуты и методы класса + +### Конструктор (`__init__`) + +Принимает начальные значения ширины и высоты, а также минимальные и максимальные границы, если требуется ограничить +диапазон значений. + +**Параметры**: + +- `width: int`: Начальная ширина (по умолчанию 0). +- `height: int`: Начальная высота (по умолчанию 0). +- `min_width: int | None = 0`, `min_height: int | None = 0`: Минимальное значение ширины и высоты (по умолчанию, `0`). + При значении `None` считаются неограниченными. +- `max_width: int | None = None`, `max_height: int | None = None`: Максимальное значение ширины и высоты (по умолчанию, + `None`). При значении `None` считаются неограниченными. + +**Пример использования**: + +```python +from anb_python_components.types.two_dim_size import TwoDimSize + +size = TwoDimSize(800, 600, min_width = 400, max_width = 1200) +``` + +### Свойства класса + +Свойства позволяют гибко устанавливать и считывать значения ширины и высоты, автоматически соблюдая введённые +ограничения. Рассмотрим подробнее каждый из них. + +#### Свойство `width` + +Управляет значением ширины, выполняя необходимую проверку ограничений при установке. + +**Пример использования**: + +```python +from anb_python_components.types.two_dim_size import TwoDimSize + +size = TwoDimSize(min_width = 400) +size.width = 500 +print(size.width) # Вывод: 500 +size.width = 300 +print(size.width) # Вывод: 400 +``` + +#### Свойство `height` + +Аналогично свойствам ширины, управляет значением высоты. + +**Пример использования**: + +```python +from anb_python_components.types.two_dim_size import TwoDimSize + +size = TwoDimSize(max_height = 1000) +size.height = 200 +print(size.height) # Вывод: 200 +size.height = 1200 +print(size.height) # Вывод: 1000 +``` + +#### Свойства границ (`min_width`, `max_width`, `min_height`, `max_height`) + +Эти свойства отвечают за изменение граничных значений ширины и высоты, контролируя их согласованность изменений +(например, максимальная граница не должна быть меньше минимальной). + +**Пример использования**: + +```python +from anb_python_components.types.two_dim_size import TwoDimSize + +size = TwoDimSize() + +# И тут мы вспомнили (а как обычно и бывает!), что забыли дать ограничения! =) +# Установим минимальную ширину +size.min_width = 500 +# и максимальную высоту +size.max_height = 1000 + +# Включаем внутреннего Пушного и запускаем рубрику "Эксперименты" +size.height = 600 +size.width = 600 +print(str(size)) # Вывод: 600:600 +size.height = 900 +size.width = 100 +print(str(size)) # Вывод: 500:900 +# Мы в этом примере задали число, меньшее минимальной ширины, вот она и вернула минимальную. +# Так же будет, если мы превысим максимальную высоту +size.height = 1200 +size.width = 600 +print(str(size)) # Вывод: 600:1000 +# И даже если не соблюдём оба +size.height = 100 +size.width = 1200 +print(str(size)) # Вывод: 500:1000 +``` + +### Ключевые методы + +#### Метод `as_str` + +Форматирует объект в строку вида `"ширина:высота"`, используя разделитель (по умолчанию двоеточие). + +**Пример использования**: + +```python +from anb_python_components.types.two_dim_size import TwoDimSize + +size = TwoDimSize(800, 600) +print(size.as_str()) # Вывод: 800:600 +``` + +#### Метод `as_tuple` + +Возвращает размеры в виде кортежа `(ширина, высота)`. + +**Пример использования**: + +```python +from anb_python_components.types.two_dim_size import TwoDimSize + +size = TwoDimSize(800, 600) +print(size.as_tuple()) # Вывод: (800, 600) +``` + +#### Метод `parse` + +Анализирует строку вида `"ширина:высота"` и создает объект `TwoDimSize` из нее. + +**Пример использования**: + +```python +from anb_python_components.types.two_dim_size import TwoDimSize + +size = TwoDimSize.parse("1920:1080") +print(size.width) # 1920 +print(size.height) # 1080 +``` + +### Магические методы + +- `__str__`, `__repr__`: Отвечают за отображение объекта в строковом виде. +- `__eq__`: Определяет равенство двух объектов по ширине и высоте. +- `__hash__`: Позволяет использовать объект в структурах вроде множества или словаря. +- `__add__`, `__mul__`: Поддерживают сложение и умножение размеров соответственно. + +## Заключение + +Класс `TwoDimSize` идеально подходит для работы с размерами графических объектов, элементами UI и подобными ситуациями, +требующими четкого контроля за пределами размеров и легкости манипуляций. + +[На главную](../../index.md) \ No newline at end of file diff --git a/help/index.md b/help/index.md index 464d54c..8121d30 100644 --- a/help/index.md +++ b/help/index.md @@ -8,11 +8,18 @@ ## Описание интерфейсов, классов и перечислений: +### Классы + +**Расположение модулей**: anb_python_components\classes\* + +- [класс `ActionState`](class_desc/classes/action_state.md) + ### Перечисления **Расположение модулей**: anb_python_components\enums\* - [перечисление `NotBoolAction`](class_desc/enums/not_bool_action.md) +- [перечисление `MessageType`](class_desc/enums/message_type.md) ### Исключения @@ -28,4 +35,18 @@ - [класс `StringExtension`](class_desc/extensions/string_extension.md) - [класс `BoolExtension`](class_desc/extensions/bool_extension.md) - [класс `TypeExtension`](class_desc/extensions/type_extension.md) -- [класс `ArrayExtension`](class_desc/extensions/array_extension.md) \ No newline at end of file +- [класс `ArrayExtension`](class_desc/extensions/array_extension.md) +- [класс `GUIDExtension`](class_desc/extensions/guid_extension.md) + +### Типы + +**Расположение модулей**: anb_python_components\types\* + +- [тип `GUID`](class_desc/types/guid.md) +- [тип `TwoDimSize`](class_desc/types/two_dim_size.md) + +### Модели + +**Расположение модулей**: anb_python_components\models\* + +- [модель `ActionStateMessage`](class_desc/models/action_state_message.md) \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py index 4a50878..4f54c83 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +1 @@ -# anb_python_components/__init__.py \ No newline at end of file +# tests/__init__.py \ No newline at end of file diff --git a/tests/classes/__init__.py b/tests/classes/__init__.py new file mode 100644 index 0000000..1fb4c98 --- /dev/null +++ b/tests/classes/__init__.py @@ -0,0 +1 @@ +# tests/classes/__init__.py \ No newline at end of file diff --git a/tests/classes/action_state_test.py b/tests/classes/action_state_test.py new file mode 100644 index 0000000..46283e7 --- /dev/null +++ b/tests/classes/action_state_test.py @@ -0,0 +1,138 @@ +# action_state_test.py + +import unittest + +from anb_python_components.classes.action_state import ActionState, ActionStateMessage, MessageType + +class ActionStateTest(unittest.TestCase): + def test_init (self): + state = ActionState[bool](False) + self.assertIsInstance(state, ActionState) + self.assertFalse(state.value) + state.value = True + self.assertTrue(state.value) + + def test_add_message (self): + message = ActionStateMessage(MessageType.INFO, "Test message") + + state = ActionState[bool](False) + state.add_message(message) + + self.assertEqual(1, state.count()) + + @staticmethod + def get_test_state (no_warning: bool = False, no_error: bool = False, state_value: bool = False) -> ActionState[ + bool]: + """ + Генерирует тестовое состояние. + :param no_warning: Без предупреждений. + :param no_error: Без ошибок. + :param state_value: Значение состояния. + :return: Тестовое состояние. + """ + state = ActionState[bool](False) + + state.add_info("Тестовое сообщение1") + if not no_error: + state.add_error("Тестовое сообщение2") + state.add_info("Тестовое сообщение3") + state.add_info("Тестовое сообщение4") + if not no_warning: + state.add_warning("Тестовое сообщение5") + state.add_info("Тестовое сообщение6") + state.add_info("Тестовое сообщение7") + state.add_info("Тестовое сообщение8") + if not no_warning: + state.add_warning("Тестовое сообщение9") + if not no_error: + state.add_error("Тестовое сообщение10") + + state.value = state_value + + return state + + def test_add_state (self): + state1 = ActionStateTest.get_test_state(True, True, True) + + state2 = ActionStateTest.get_test_state(state_value = False) + + state1.add_state(state2) + + self.assertEqual(16, state1.count()) + + def test_get_messages (self): + state = ActionStateTest.get_test_state() + + state_messages = state.get_messages() + + self.assertEqual(10, len(state_messages)) + + count_errors = 0 + + for message in state_messages: + if message.message_type == MessageType.ERROR: + count_errors += 1 + + self.assertEqual(2, count_errors) + + def test_get_string_messages (self): + state = ActionStateTest.get_test_state() + + state_message_string = state.get_string_messages(ActionState.get_string_error_only()) + + need_string = "Тестовое сообщение2\nТестовое сообщение10" + + self.assertEqual(state_message_string, need_string) + + def test_has_infos (self): + state = ActionStateTest.get_test_state() + + self.assertTrue(state.has_infos()) + + def test_has_warnings (self): + state = ActionStateTest.get_test_state() + + self.assertTrue(state.has_warnings()) + + def test_has_errors (self): + state = ActionStateTest.get_test_state() + + self.assertTrue(state.has_errors()) + + def test_is_success (self): + state_fail = ActionStateTest.get_test_state() + state_success = ActionStateTest.get_test_state(no_warning = True, no_error = True) + state_success_no_warning = ActionStateTest.get_test_state(no_error = True) + + self.assertTrue(state_success.is_success()) + self.assertTrue(state_success_no_warning.is_success(True)) + self.assertFalse(state_fail.is_success()) + + def test_clear (self): + state = ActionStateTest.get_test_state() + + state.clear(lambda message: message.message_type == MessageType.WARNING) + + self.assertEqual(8, len(state.get_messages())) + + state.clear() + + self.assertEqual(0, len(state.get_messages())) + + def test_count (self): + state = ActionStateTest.get_test_state() + + count_all = state.count() + count_warnings = state.count(lambda message: message.message_type == MessageType.WARNING) + count_errors = state.count(lambda message: message.message_type == MessageType.ERROR) + count_errors_and_warnings = state.count( + lambda message: message.message_type == MessageType.WARNING or message.message_type == MessageType.ERROR + ) + + self.assertEqual(10, count_all) + self.assertEqual(2, count_errors) + self.assertEqual(2, count_warnings) + self.assertEqual(4, count_errors_and_warnings) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/tests/extensions/__init__.py b/tests/extensions/__init__.py index 7ba3d65..0ed9bd6 100644 --- a/tests/extensions/__init__.py +++ b/tests/extensions/__init__.py @@ -1 +1 @@ -# anb_python_components/extensions/__init__.py \ No newline at end of file +# tests/extensions/__init__.py \ No newline at end of file diff --git a/tests/extensions/bool_extension_test.py b/tests/extensions/bool_extension_test.py index 301627a..bbf1f05 100644 --- a/tests/extensions/bool_extension_test.py +++ b/tests/extensions/bool_extension_test.py @@ -1,21 +1,19 @@ -# string_extension_test.py +# bool_extension_test.py import unittest from anb_python_components.extensions.bool_extension import * - class BoolExtensionTest(unittest.TestCase): - def test_to_str(self): + def test_to_str (self): self.assertEqual(BoolExtension.to_str(True, "да", "нет"), "да") self.assertEqual(BoolExtension.to_str(False, "да", "нет"), "нет") - - def test_true_count(self): + + def test_true_count (self): self.assertEqual(BoolExtension.true_count([False, True, False, True, True, False, False]), 3) - - def test_any_true(self): + + def test_any_true (self): self.assertTrue(BoolExtension.any_true([False, True, False, True, True, False, False])) - if __name__ == '__main__': - unittest.main() + unittest.main() \ No newline at end of file diff --git a/tests/types/__init__.py b/tests/types/__init__.py new file mode 100644 index 0000000..c77facc --- /dev/null +++ b/tests/types/__init__.py @@ -0,0 +1 @@ +# tests/types/__init__.py \ No newline at end of file diff --git a/tests/types/guid_test.py b/tests/types/guid_test.py new file mode 100644 index 0000000..bf63a82 --- /dev/null +++ b/tests/types/guid_test.py @@ -0,0 +1,78 @@ +# anb_python_components/types/guid.py +import re + +from anb_python_components.exceptions.wrong_type_exception import WrongTypeException + +class GUID: + """ + Тип GUID. + """ + + # Константа пустого GUID + EMPTY: str = "00000000-0000-0000-0000-000000000000" + + def __init__ (self, guid = EMPTY): + """ + Инициализация расширения. + :param guid: str | GUID - Передаваемый GUID + """ + # Проверка типа аргумента guid + # - если аргумент не является строкой + if not isinstance(guid, str): + # -- если аргумент является экземпляром GUID + if isinstance(guid, GUID): + # - преобразуем его в строку + guid = str(guid) + else: + # -- иначе генерируем исключение + raise WrongTypeException("Неверный тип аргумента!", "GUID", str(type(guid)), "guid") + + # Проверка GUID на валидность + if not self.validate_str(guid): + # и если GUID невалидный, то генерируем исключение + raise WrongTypeException("Неверный формат GUID!", "GUID", guid, "guid") + + # Инициализируем приватный атрибут __value (текстовое представление хранящегося GUID) + self.__value = guid + + def __str__ (self): + """ + Переопределение метода __str__. + :return: Текстовое представление GUID. + """ + return self.__value if self.__value else self.EMPTY + + def __eq__ (self, other): + """ + Переопределение метода __eq__. + :param other: Объект для сравнения. + :return: True, если GUID равны, иначе False. + """ + + # Если аргумент не является экземпляром GUID + if not isinstance(other, GUID): + # - генерируем исключение + raise WrongTypeException("Неверный тип аргумента!", "GUID", type(other), "other") + + # Преобразование второго аргумента в строку + other_str = str(other) + + # Сравниваем строки + return self.__value == other_str + + @staticmethod + def validate_str (guid: str | None) -> bool: + """ + Проверка строки на валидность GUID. + :return: True, если GUID валидный, иначе False. + """ + + # Проверка на пустоту + if not guid: + return False + + # Регулярное выражение для проверки формата GUID + pattern = r'^[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}$' + + # Проверка на соответствие формату + return bool(re.fullmatch(pattern, guid)) \ No newline at end of file diff --git a/tests/types/two_dim_size_test.py b/tests/types/two_dim_size_test.py new file mode 100644 index 0000000..f7909f9 --- /dev/null +++ b/tests/types/two_dim_size_test.py @@ -0,0 +1 @@ +# two_dim_size_test.py \ No newline at end of file diff --git a/tests/types/version_info_test.py b/tests/types/version_info_test.py new file mode 100644 index 0000000..ee0af7e --- /dev/null +++ b/tests/types/version_info_test.py @@ -0,0 +1,51 @@ +# version_info_test.py + +import unittest + +from anb_python_components.types.version_info import VersionInfo + +class VersionInfoTest(unittest.TestCase): + def test_init (self): + version_info = VersionInfo(1, 2, 3, 4, "Тестовая версия", 1) + version_info_str = '1.2.3.4 Тестовая версия 1' + + self.assertEqual(version_info_str, str(version_info)) + + def test_math (self): + version_info_1 = VersionInfo(1, 2, 3, 4, "Тестовая версия", 1) + version_info_2 = VersionInfo(1, 2, 3, 4, "Тестовая версия", 1) + version_info_3 = VersionInfo(2, 1, 3, 10, "Тестовая версия", 2) + version_info_4 = VersionInfo(3, 5, 3, 12, "Тестовая версия", 3) + + self.assertTrue(version_info_1 == version_info_2) + self.assertTrue(version_info_3 > version_info_2) + self.assertTrue(version_info_3 >= version_info_1) + self.assertTrue(version_info_1 < version_info_4) + self.assertTrue(version_info_3.in_range(version_info_1, version_info_4)) + self.assertFalse(version_info_3.in_range(version_info_1, version_info_3, end_inclusive = False)) + self.assertTrue(version_info_3.in_range(version_info_1)) + self.assertTrue(version_info_3.in_range()) + + def test_parse (self): + str_ver_1 = '1.2.3.4 Тестовая 1' + version_info_1 = VersionInfo(1, 2, 3, 4, "Тестовая", 1) + str_ver_2 = "1.2.3.4 Тестовая" + version_info_2 = VersionInfo(1, 2, 3, 4, "Тестовая", 0) + str_ver_3 = "1.2.3.4" + version_info_3 = VersionInfo(1, 2, 3, 4, "", 0) + str_ver_4 = "1.2.3 Тестовая 1" + version_info_4 = VersionInfo(1, 2, 3, 0, "Тестовая", 1) + str_ver_5 = "1.2 Тестовая 1" + version_info_5 = VersionInfo(1, 2, 0, 0, "Тестовая", 1) + str_ver_6 = "1 Тестовая 1" + version_info_6 = VersionInfo(1, 0, 0, 0, "Тестовая", 1) + + self.assertEqual(version_info_1, VersionInfo.parse(str_ver_1)) + self.assertEqual(version_info_2, VersionInfo.parse(str_ver_2)) + self.assertEqual(version_info_3, VersionInfo.parse(str_ver_3)) + self.assertEqual(version_info_4, VersionInfo.parse(str_ver_4)) + self.assertEqual(version_info_5, VersionInfo.parse(str_ver_5)) + self.assertEqual(version_info_6, VersionInfo.parse(str_ver_6)) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file