This commit is contained in:
2025-10-05 23:11:19 +03:00
parent 4cbb69181c
commit b20f18788d
24 changed files with 2275 additions and 12 deletions

View File

@@ -0,0 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Тест ActionState" type="tests" factoryName="Autodetect">
<module name="anb_python_components" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/tests/classes" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="_new_additionalArguments" value="&quot;&quot;" />
<option name="_new_target" value="&quot;$PROJECT_DIR$/tests/classes/action_state_test.py&quot;" />
<option name="_new_targetType" value="&quot;PATH&quot;" />
<method v="2" />
</configuration>
</component>

View File

@@ -0,0 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Тест VersionInfo" type="tests" factoryName="Autodetect">
<module name="anb_python_components" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/tests/types" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="_new_additionalArguments" value="&quot;&quot;" />
<option name="_new_target" value="&quot;$PROJECT_DIR$/tests/types/version_info_test.py&quot;" />
<option name="_new_targetType" value="&quot;PATH&quot;" />
<method v="2" />
</configuration>
</component>

View File

@@ -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)

View File

@@ -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

View File

@@ -0,0 +1 @@
# anb_python_components/models/__init__.py

View File

@@ -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}"

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)
- [класс `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)

View File

@@ -1 +1 @@
# anb_python_components/__init__.py
# tests/__init__.py

View File

@@ -0,0 +1 @@
# tests/classes/__init__.py

View File

@@ -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()

View File

@@ -1 +1 @@
# anb_python_components/extensions/__init__.py
# tests/extensions/__init__.py

View File

@@ -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()

1
tests/types/__init__.py Normal file
View File

@@ -0,0 +1 @@
# tests/types/__init__.py

78
tests/types/guid_test.py Normal file
View File

@@ -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))

View File

@@ -0,0 +1 @@
# two_dim_size_test.py

View File

@@ -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()