anb-python-components 1.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. anb_python_components/__init__.py +1 -0
  2. anb_python_components/classes/__init__.py +1 -0
  3. anb_python_components/classes/action_state.py +211 -0
  4. anb_python_components/classes/directory.py +115 -0
  5. anb_python_components/classes/file.py +226 -0
  6. anb_python_components/classes/shortcode_parser.py +195 -0
  7. anb_python_components/custom_types/__init__.py +3 -0
  8. anb_python_components/custom_types/guid.py +169 -0
  9. anb_python_components/custom_types/object_array.py +625 -0
  10. anb_python_components/custom_types/shortcode_attributes.py +128 -0
  11. anb_python_components/custom_types/two_dim_size.py +413 -0
  12. anb_python_components/custom_types/version_info.py +461 -0
  13. anb_python_components/enums/__init__.py +1 -0
  14. anb_python_components/enums/message_type.py +44 -0
  15. anb_python_components/enums/not_bool_action.py +24 -0
  16. anb_python_components/enums/type_copy_strategy.py +47 -0
  17. anb_python_components/exceptions/__init__.py +1 -0
  18. anb_python_components/exceptions/wrong_type_exception.py +20 -0
  19. anb_python_components/extensions/__init__.py +1 -0
  20. anb_python_components/extensions/array_extension.py +34 -0
  21. anb_python_components/extensions/bool_extension.py +87 -0
  22. anb_python_components/extensions/string_extension.py +259 -0
  23. anb_python_components/extensions/string_extension_constant.py +80 -0
  24. anb_python_components/extensions/type_extension.py +112 -0
  25. anb_python_components/models/__init__.py +1 -0
  26. anb_python_components/models/action_state_message.py +27 -0
  27. anb_python_components/models/shortcode_model.py +31 -0
  28. anb_python_components-1.2.1.dist-info/METADATA +12 -0
  29. anb_python_components-1.2.1.dist-info/RECORD +48 -0
  30. anb_python_components-1.2.1.dist-info/WHEEL +5 -0
  31. anb_python_components-1.2.1.dist-info/licenses/LICENSE +235 -0
  32. anb_python_components-1.2.1.dist-info/top_level.txt +2 -0
  33. tests/__init__.py +1 -0
  34. tests/classes/__init__.py +1 -0
  35. tests/classes/action_state_test.py +138 -0
  36. tests/classes/directory_test.py +19 -0
  37. tests/classes/file_test.py +79 -0
  38. tests/classes/shortcode_parser_test.py +105 -0
  39. tests/custom_types/__init__.py +1 -0
  40. tests/custom_types/guid_test.py +14 -0
  41. tests/custom_types/object_array_test.py +160 -0
  42. tests/custom_types/two_dim_size_test.py +37 -0
  43. tests/custom_types/version_info_test.py +51 -0
  44. tests/extensions/__init__.py +1 -0
  45. tests/extensions/array_extension_test.py +21 -0
  46. tests/extensions/bool_extension_test.py +19 -0
  47. tests/extensions/string_extension_test.py +55 -0
  48. tests/extensions/type_extension_test.py +38 -0
@@ -0,0 +1,195 @@
1
+ # anb_python_components/classes/shortcode_parser.py
2
+
3
+ import re
4
+ from typing import Callable, Dict
5
+
6
+ from anb_python_components.classes.action_state import ActionState
7
+ from anb_python_components.models.shortcode_model import ShortCodeModel
8
+ from anb_python_components.custom_types.shortcode_attributes import ShortCodeAttributes
9
+
10
+ class ShortCodeParser:
11
+ """
12
+ Класс для обработки текста на шорткоды.
13
+ """
14
+
15
+ # Список всех зарегистрированных шорткодов.
16
+ __short_codes: Dict[str, dict[str, Callable]]
17
+
18
+ def __init__ (self):
19
+ """
20
+ Инициализация парсера шорткодов.
21
+ """
22
+ self.__short_codes: Dict[str, dict[str, Callable]] = {}
23
+
24
+ # noinspection PyUnusedLocal
25
+ @staticmethod
26
+ def default_set_unset (content: str, params: ShortCodeAttributes) -> str:
27
+ """
28
+ Функция по умолчанию для задания и удаления шорткодов.
29
+ Просто возвращает неизмененный контент.
30
+ :param content: Содержание шорткода.
31
+ :type content: str
32
+ :param params: Параметры шорткода.
33
+ :type params: ShortCodeAttributes
34
+ :return: Отформатированное содержимое.
35
+ :rtype: str
36
+ """
37
+ return content
38
+
39
+ # noinspection PyUnusedLocal
40
+ @staticmethod
41
+ def any_valid (content: str, params: ShortCodeAttributes) -> bool:
42
+ """
43
+ Метод проверки валидности шорткода, утверждающий, что шорткод валиден при любых условиях.
44
+ :param content: Содержание шорткода.
45
+ :type content: str
46
+ :param params: Параметры шорткода.
47
+ :type params: ShortCodeAttributes
48
+ :return: Объект ActionState с результатом проверки валидности.
49
+ :rtype: ActionState
50
+ """
51
+ return True
52
+
53
+ def add_short_code (
54
+ self, name: str, on_set: Callable | None = None, on_unset: Callable | None = None,
55
+ on_validate: Callable | None = None
56
+ ) -> None:
57
+ """
58
+ Добавляет новый шорткод.
59
+
60
+ :param name: Название шорткода
61
+ :type name: str
62
+ :param on_set: Метод, вызываемый при обработке текста с включённым шорткодом
63
+ :type on_set: Callable
64
+ :param on_unset: Метод, вызываемый при обработке текста с отключённым шорткодом
65
+ :type on_unset: Callable
66
+ :param on_validate: Метод, вызываемый при проверке валидности шорткода
67
+ :type on_validate: Callable
68
+ :rtype: None
69
+ """
70
+ # Используем стандартные значения, если они не указаны
71
+ on_set = on_set or self.default_set_unset
72
+ on_unset = on_unset or self.default_set_unset
73
+ on_validate = on_validate or self.any_valid
74
+
75
+ # Сохраняем обработчики в словарь
76
+ self.__short_codes[name] = {'set': on_set, 'unset': on_unset, 'validate': on_validate}
77
+
78
+ def add_short_codes (self, short_codes: list[ShortCodeModel]) -> None:
79
+ """
80
+ Добавляет шорткоды из списка объектов ShortcodeModel.
81
+
82
+ :param short_codes: Список объектов ShortcodeModel
83
+ :type short_codes: list[ShortCodeModel]
84
+ :rtype: None
85
+ """
86
+ # Для каждого шорткода
87
+ for sc_model in short_codes:
88
+ # - добавляем его в словарь
89
+ self.add_short_code(sc_model.shortcode, sc_model.on_set, sc_model.on_unset, sc_model.on_validate)
90
+
91
+ def __get_shortcode_regex (self) -> str:
92
+ """
93
+ Генерирует регулярное выражение для поиска шорткодов.
94
+
95
+ :copyright: WordPress (сам шаблон).
96
+ :link: https://developer.wordpress.org/plugins/shortcodes/
97
+ :return: Регулярное выражение.
98
+ :rtype: str
99
+ """
100
+ # Получаем список имён зарегистрированных шорткодов
101
+ tag_names = list(self.__short_codes.keys())
102
+
103
+ # Преобразуем каждое имя в экранированную версию (чтобы спецсимволы были защищены)
104
+ quoted_tags = map(re.escape, tag_names)
105
+
106
+ # Формируем теги для регулярного выражения
107
+ tags = '|'.join(quoted_tags)
108
+
109
+ # Создаём шаблон регулярного выражения для поиска шорткодов и выводим его
110
+ return rf'\[(\[?)({tags})(?![\w-])([^\]\/]*(?:\/(?!\])[^\]\/]*)*?)(?:(\/)\]|\](?:([^\[]*+(?:\[(?!\/\2\])[^\[]*+)*+)\[\/\2\])?)(\]?)'
111
+
112
+ def __handle_match (self, match: re.Match, ignore_included_shortcodes: bool = False, is_unset: bool = False) -> str:
113
+ """
114
+ Обрабатывает совпадение шорткода.
115
+ :param match: Совпадение шорткода.
116
+ :type match: re.Match
117
+ :param ignore_included_shortcodes: Флаг игнорирования вложенных шорткодов.
118
+ :type ignore_included_shortcodes: bool
119
+ :param is_unset: Флаг удаления шорткода.
120
+ :type is_unset: bool
121
+ :return: Преобразованное содержание.
122
+ :rtype: str
123
+ """
124
+
125
+ # Если совпадение не найдено
126
+ if not match:
127
+ # - то просто возвращаем контент
128
+ return ''
129
+
130
+ # Получаем имя шорткода
131
+ tag_name = match.group(2).strip()
132
+ # Получаем параметры шорткода
133
+ attributes_str = str(match.group(3) or '').lstrip().rstrip()
134
+ attributes = ShortCodeAttributes(attributes_str)
135
+ # Получаем содержимое шорткода
136
+ content = match.group(5) or ''
137
+
138
+ # Если текущая глубина вложенности не превышает ЗАДАННУЮ максимальную или максимальная глубина не задана
139
+ if not ignore_included_shortcodes:
140
+ # - то вызываем рекурсию
141
+ content = self.parse(content, False, is_unset)
142
+
143
+ # Если шорткод не зарегистрирован
144
+ if tag_name not in self.__short_codes:
145
+ # - то просто возвращаем его
146
+ return f'[{match}]'
147
+
148
+ # Получаем функцию валидации
149
+ validate_function = self.__short_codes[tag_name].get('validate') or self.any_valid
150
+
151
+ # Валидируем контент шорткода
152
+ validation_result = validate_function(content, attributes)
153
+
154
+ # Если валидация не прошла
155
+ if not validation_result:
156
+ # - то возвращаем контент
157
+ return f'{match}'
158
+
159
+ # Получаем функции обработки
160
+ # - если шорткод устанавливается
161
+ set_function = self.__short_codes[tag_name].get('set') or self.default_set_unset
162
+ # - если шорткод удаляется
163
+ unset_function = self.__short_codes[tag_name].get('unset') or self.default_set_unset
164
+
165
+ # Выбираем функцию обработки
166
+ function = set_function if not is_unset else unset_function
167
+
168
+ # Возвращаем обработанный контент
169
+ return function(content, attributes)
170
+
171
+ def parse (self, content: str, ignore_included_shortcodes: bool = False, is_unset: bool = False) -> str:
172
+ """
173
+ Производит поиск и обработку шорткодов в тексте.
174
+
175
+ :param content: Исходный текст.
176
+ :type content: str
177
+ :param ignore_included_shortcodes: Флаг игнорирования вложенных шорткодов.
178
+ :type ignore_included_shortcodes: bool
179
+ :param is_unset: Флаг удаления шорткода.
180
+ :type is_unset: bool
181
+ :return: Преобразованный текст.
182
+ :rtype: str
183
+ """
184
+
185
+ # Получаем регулярное выражение для поиска шорткодов
186
+ regex = self.__get_shortcode_regex()
187
+
188
+ # Заменяем все найденные шорткоды
189
+ result = re.sub(
190
+ regex, lambda match: self.__handle_match(match, ignore_included_shortcodes, is_unset), content,
191
+ flags = re.MULTILINE | re.IGNORECASE | re.VERBOSE
192
+ )
193
+
194
+ # Возвращаем результат
195
+ return result
@@ -0,0 +1,3 @@
1
+ # anb_python_components/custom_types/__init__.py
2
+
3
+ from collections.abc import *
@@ -0,0 +1,169 @@
1
+ # anb_python_components/custom_types/guid.py
2
+ from __future__ import annotations
3
+
4
+ import re
5
+ import secrets
6
+
7
+ from anb_python_components.exceptions.wrong_type_exception import WrongTypeException
8
+
9
+ class GUID:
10
+ """
11
+ Тип GUID.
12
+ """
13
+
14
+ # Константа пустого GUID
15
+ EMPTY: str = "00000000-0000-0000-0000-000000000000"
16
+
17
+ def __init__ (self, guid: str | GUID = EMPTY):
18
+ """
19
+ Инициализация расширения.
20
+ :param guid: str | GUID - Передаваемый GUID
21
+ """
22
+ # Проверка типа аргумента guid
23
+ if not isinstance(guid, str) or not isinstance(guid, GUID):
24
+ # - если аргумент не является строкой или экземпляром GUID, то генерируем исключение
25
+ raise WrongTypeException("Неверный тип аргумента!", "GUID", str(type(guid)), "guid")
26
+
27
+ # Проверка GUID на валидность
28
+ if not GUID.validate(guid):
29
+ # и если GUID невалидный, то генерируем исключение
30
+ raise WrongTypeException("Неверный формат GUID!", "GUID", str(type(guid)), "guid")
31
+
32
+ # Инициализируем приватный атрибут __value (текстовое представление хранящегося GUID)
33
+ self.__value = str(guid)
34
+
35
+ def __str__ (self):
36
+ """
37
+ Переопределение метода __str__.
38
+ :return: Текстовое представление GUID.
39
+ """
40
+ return self.__value if self.__value else self.EMPTY
41
+
42
+ def __eq__ (self, other):
43
+ """
44
+ Переопределение метода __eq__.
45
+ :param other: Объект для сравнения.
46
+ :return: True, если GUID равны, иначе False.
47
+ """
48
+
49
+ # Если аргумент не является экземпляром GUID
50
+ if not isinstance(other, GUID):
51
+ # - генерируем исключение
52
+ raise WrongTypeException("Неверный тип аргумента!", "GUID", type(other), "other")
53
+
54
+ # Преобразование второго аргумента в строку
55
+ other_str = str(other)
56
+
57
+ # Сравниваем строки
58
+ return self.__value == other_str
59
+
60
+ @staticmethod
61
+ def generate () -> GUID:
62
+ """
63
+ Генерирует уникальный идентификатор GUID согласно стандарту RFC 4122.
64
+ """
65
+ # Генерация отдельных компонентов GUID
66
+ time_low = secrets.randbits(32) & 0xffffffff
67
+ time_mid = secrets.randbits(16) & 0xffff
68
+ time_hi_and_version = ((secrets.randbits(12) << 4) | 0x4000) & 0xffff
69
+ clock_seq_hi_and_reserved = ((secrets.randbits(14) << 2) | 0x8000) & 0xffff
70
+ node_id = secrets.randbits(48) & 0xffffffffffff
71
+
72
+ # Объединение компонентов в единый GUID
73
+ guid_parts = [
74
+ time_low >> 16, time_low & 0xffff,
75
+ time_mid,
76
+ time_hi_and_version,
77
+ clock_seq_hi_and_reserved,
78
+ node_id >> 32, (node_id >> 16) & 0xffff, node_id & 0xffff
79
+ ]
80
+
81
+ # Форматируем компоненты в виде строки GUID
82
+ guid_str = "-".join(
83
+ [
84
+ "%08x" % guid_parts[0],
85
+ "%04x" % guid_parts[1],
86
+ "%04x" % guid_parts[2],
87
+ "%04x" % guid_parts[3],
88
+ ("%04x%04x%04x" % tuple(guid_parts[4:])),
89
+ ]
90
+ )
91
+
92
+ # Возвращаем экземпляр GUID
93
+ return GUID(guid_str)
94
+
95
+ @staticmethod
96
+ def is_equal (guid1: str | GUID, guid2: str | GUID) -> bool:
97
+
98
+ # Если guid1 не является GUID или строкой
99
+ if not isinstance(guid1, GUID) or not isinstance(guid1, str):
100
+ # - генерируем исключение
101
+ raise WrongTypeException("Неверный тип аргумента!", "GUID|str", var_name = "guid1")
102
+
103
+ # Если guid2 не является GUID или строкой
104
+ if not isinstance(guid2, GUID) or not isinstance(guid2, str):
105
+ # - генерируем исключение
106
+ raise WrongTypeException("Неверный тип аргумента!", "GUID|str", var_name = "guid2")
107
+
108
+ # Если guid1 является строкой
109
+ if not isinstance(guid1, GUID):
110
+ # - преобразуем её в GUID
111
+ guid1 = GUID(guid1)
112
+
113
+ # Если guid2 является строкой
114
+ if not isinstance(guid2, GUID):
115
+ # - преобразуем её в GUID
116
+ guid2 = GUID(guid2)
117
+
118
+ # Сравниваем GUID
119
+ return guid1 == guid2
120
+
121
+ @staticmethod
122
+ def validate (guid: str | GUID) -> bool:
123
+ """
124
+ Проверка GUID на валидность.
125
+ :param guid: GUID для проверки.
126
+ :return: True, если GUID валидный, иначе False.
127
+ """
128
+ # Если guid не является строкой, то преобразуем его в неё
129
+ guid = guid if isinstance(guid, str) else str(guid)
130
+
131
+ # Регулярное выражение для проверки формата GUID
132
+ pattern = r'^[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}$'
133
+
134
+ # Проверка на соответствие формату
135
+ return bool(re.fullmatch(pattern, guid))
136
+
137
+ @staticmethod
138
+ def is_invalid_or_empty (guid: str | GUID) -> bool:
139
+ """
140
+ Проверка GUID на валидность и не пустоту.
141
+ :param guid: Класс или строка GUID для проверки.
142
+ :return: True, если GUID не валидный или пустой, иначе False.
143
+ """
144
+ # Если guid не является строкой, то преобразуем его в неё
145
+ guid = guid if isinstance(guid, str) else str(guid)
146
+
147
+ # Проверяем GUID
148
+ return not GUID.validate(guid) or guid.lower() == GUID.EMPTY.lower()
149
+
150
+ @classmethod
151
+ def parse (cls, guid_string: str, empty_if_not_valid: bool = True) -> GUID:
152
+ """
153
+ Парсинг GUID из строки.
154
+ :param guid_string: Строка GUID.
155
+ :param empty_if_not_valid: Если True, то возвращается пустой GUID, если GUID недействителен.
156
+ """
157
+
158
+ # Проверяем строку на соответствие формату GUID
159
+ if cls.validate(guid_string):
160
+ # - если GUID действителен, возвращаем экземпляр GUID
161
+ return GUID(guid_string)
162
+
163
+ # Если же GUID недействителен и запрещено выбрасывать исключение
164
+ if empty_if_not_valid:
165
+ # -- то возвращаем пустой GUID
166
+ return GUID(GUID.EMPTY)
167
+ else:
168
+ # -- иначе выбрасываем исключение
169
+ raise WrongTypeException('Предан неверный GUID / Wrong GUID.')