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.
- anb_python_components/__init__.py +1 -0
- anb_python_components/classes/__init__.py +1 -0
- anb_python_components/classes/action_state.py +211 -0
- anb_python_components/classes/directory.py +115 -0
- anb_python_components/classes/file.py +226 -0
- anb_python_components/classes/shortcode_parser.py +195 -0
- anb_python_components/custom_types/__init__.py +3 -0
- anb_python_components/custom_types/guid.py +169 -0
- anb_python_components/custom_types/object_array.py +625 -0
- anb_python_components/custom_types/shortcode_attributes.py +128 -0
- anb_python_components/custom_types/two_dim_size.py +413 -0
- anb_python_components/custom_types/version_info.py +461 -0
- anb_python_components/enums/__init__.py +1 -0
- anb_python_components/enums/message_type.py +44 -0
- anb_python_components/enums/not_bool_action.py +24 -0
- anb_python_components/enums/type_copy_strategy.py +47 -0
- anb_python_components/exceptions/__init__.py +1 -0
- anb_python_components/exceptions/wrong_type_exception.py +20 -0
- anb_python_components/extensions/__init__.py +1 -0
- anb_python_components/extensions/array_extension.py +34 -0
- anb_python_components/extensions/bool_extension.py +87 -0
- anb_python_components/extensions/string_extension.py +259 -0
- anb_python_components/extensions/string_extension_constant.py +80 -0
- anb_python_components/extensions/type_extension.py +112 -0
- anb_python_components/models/__init__.py +1 -0
- anb_python_components/models/action_state_message.py +27 -0
- anb_python_components/models/shortcode_model.py +31 -0
- anb_python_components-1.2.1.dist-info/METADATA +12 -0
- anb_python_components-1.2.1.dist-info/RECORD +48 -0
- anb_python_components-1.2.1.dist-info/WHEEL +5 -0
- anb_python_components-1.2.1.dist-info/licenses/LICENSE +235 -0
- anb_python_components-1.2.1.dist-info/top_level.txt +2 -0
- tests/__init__.py +1 -0
- tests/classes/__init__.py +1 -0
- tests/classes/action_state_test.py +138 -0
- tests/classes/directory_test.py +19 -0
- tests/classes/file_test.py +79 -0
- tests/classes/shortcode_parser_test.py +105 -0
- tests/custom_types/__init__.py +1 -0
- tests/custom_types/guid_test.py +14 -0
- tests/custom_types/object_array_test.py +160 -0
- tests/custom_types/two_dim_size_test.py +37 -0
- tests/custom_types/version_info_test.py +51 -0
- tests/extensions/__init__.py +1 -0
- tests/extensions/array_extension_test.py +21 -0
- tests/extensions/bool_extension_test.py +19 -0
- tests/extensions/string_extension_test.py +55 -0
- 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,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.')
|