anb-python-components 1.2.1__tar.gz → 1.4.0__tar.gz
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-1.4.0/PKG-INFO +148 -0
- anb_python_components-1.4.0/anb_python_components/__init__.py +46 -0
- anb_python_components-1.4.0/anb_python_components/classes/__init__.py +8 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/classes/action_state.py +2 -1
- anb_python_components-1.4.0/anb_python_components/classes/dataclass_analyzer.py +97 -0
- anb_python_components-1.4.0/anb_python_components/classes/interface.py +192 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/classes/shortcode_parser.py +1 -1
- anb_python_components-1.4.0/anb_python_components/custom_types/__init__.py +7 -0
- anb_python_components-1.4.0/anb_python_components/decorators/__init__.py +2 -0
- anb_python_components-1.4.0/anb_python_components/decorators/interface_decorators.py +74 -0
- anb_python_components-1.4.0/anb_python_components/enums/__init__.py +5 -0
- anb_python_components-1.4.0/anb_python_components/exceptions/__init__.py +3 -0
- anb_python_components-1.4.0/anb_python_components/extensions/__init__.py +7 -0
- anb_python_components-1.4.0/anb_python_components/extensions/dataclass_extension.py +59 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/extensions/string_extension_constant.py +0 -1
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/extensions/type_extension.py +26 -1
- anb_python_components-1.4.0/anb_python_components/models/__init__.py +4 -0
- anb_python_components-1.4.0/anb_python_components.egg-info/PKG-INFO +148 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components.egg-info/SOURCES.txt +9 -0
- anb_python_components-1.4.0/anb_python_components.egg-info/not-zip-safe +1 -0
- anb_python_components-1.4.0/anb_python_components.egg-info/requires.txt +1 -0
- anb_python_components-1.4.0/setup.py +74 -0
- anb_python_components-1.4.0/tests/classes/dataclass_analyzer_test.py +62 -0
- anb_python_components-1.4.0/tests/classes/interface_test.py +47 -0
- anb_python_components-1.2.1/PKG-INFO +0 -12
- anb_python_components-1.2.1/anb_python_components/__init__.py +0 -1
- anb_python_components-1.2.1/anb_python_components/classes/__init__.py +0 -1
- anb_python_components-1.2.1/anb_python_components/custom_types/__init__.py +0 -3
- anb_python_components-1.2.1/anb_python_components/enums/__init__.py +0 -1
- anb_python_components-1.2.1/anb_python_components/exceptions/__init__.py +0 -1
- anb_python_components-1.2.1/anb_python_components/extensions/__init__.py +0 -1
- anb_python_components-1.2.1/anb_python_components/models/__init__.py +0 -1
- anb_python_components-1.2.1/anb_python_components.egg-info/PKG-INFO +0 -12
- anb_python_components-1.2.1/setup.py +0 -26
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/LICENSE +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/README.md +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/classes/directory.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/classes/file.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/custom_types/guid.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/custom_types/object_array.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/custom_types/shortcode_attributes.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/custom_types/two_dim_size.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/custom_types/version_info.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/enums/message_type.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/enums/not_bool_action.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/enums/type_copy_strategy.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/exceptions/wrong_type_exception.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/extensions/array_extension.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/extensions/bool_extension.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/extensions/string_extension.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/models/action_state_message.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components/models/shortcode_model.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components.egg-info/dependency_links.txt +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/anb_python_components.egg-info/top_level.txt +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/setup.cfg +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/__init__.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/classes/__init__.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/classes/action_state_test.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/classes/directory_test.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/classes/file_test.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/classes/shortcode_parser_test.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/custom_types/__init__.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/custom_types/guid_test.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/custom_types/object_array_test.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/custom_types/two_dim_size_test.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/custom_types/version_info_test.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/extensions/__init__.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/extensions/array_extension_test.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/extensions/bool_extension_test.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/extensions/string_extension_test.py +0 -0
- {anb_python_components-1.2.1 → anb_python_components-1.4.0}/tests/extensions/type_extension_test.py +0 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: anb_python_components
|
|
3
|
+
Version: 1.4.0
|
|
4
|
+
Summary: Набор компонентов Python, которые упрощают разработку / A set of Python components that simplify development
|
|
5
|
+
Home-page: https://gitflic.ru/project/babaev-an/anb-python-components
|
|
6
|
+
Author: Александр Бабаев
|
|
7
|
+
Author-email: contact_with_us@babaev-an.ru
|
|
8
|
+
Project-URL: Documentation, https://gitflic.ru/project/babaev-an/anb-python-components/wiki
|
|
9
|
+
Project-URL: Source, https://gitflic.ru/project/babaev-an/anb-python-components
|
|
10
|
+
Project-URL: Tracker, https://gitflic.ru/project/babaev-an/anb-python-components/issue?page=0
|
|
11
|
+
Keywords: python components development utils
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.14.0
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: setuptools>=80.9.0
|
|
23
|
+
Dynamic: author
|
|
24
|
+
Dynamic: author-email
|
|
25
|
+
Dynamic: classifier
|
|
26
|
+
Dynamic: description
|
|
27
|
+
Dynamic: description-content-type
|
|
28
|
+
Dynamic: home-page
|
|
29
|
+
Dynamic: keywords
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
Dynamic: project-url
|
|
32
|
+
Dynamic: requires-dist
|
|
33
|
+
Dynamic: requires-python
|
|
34
|
+
Dynamic: summary
|
|
35
|
+
|
|
36
|
+
# 📌🇷🇺 Русская версия
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Набор компонентов Python для упрощения разработки приложений
|
|
41
|
+
|
|
42
|
+
**ANB Python Components** — это библиотека полезных классов и модулей, предназначенная для ускорения процесса разработки
|
|
43
|
+
программного обеспечения на языке программирования Python. Библиотека включает широкий спектр инструментов, облегчающих
|
|
44
|
+
выполнение повседневных задач разработчика, от обработки файлов и сетевых запросов до автоматизации тестирования и
|
|
45
|
+
интеграции с популярными фреймворками.
|
|
46
|
+
|
|
47
|
+
### ✅ Основные возможности:
|
|
48
|
+
|
|
49
|
+
- Удобные инструменты для работы с файлами и каталогами.
|
|
50
|
+
- Классы для работы с файлами и директориями.
|
|
51
|
+
- Классы для удобной передачи состояния.
|
|
52
|
+
- Новые
|
|
53
|
+
типы [GUID](https://gitflic.ru/project/babaev-an/anb-python-components/wiki/page?file=class_desc%2Ftypes%2Fguid.md),
|
|
54
|
+
[TwoDimSize](https://gitflic.ru/project/babaev-an/anb-python-components/wiki/page?file=class_desc%2Ftypes%2Ftwo_dim_size.md)
|
|
55
|
+
и [VersionInfo](https://gitflic.ru/project/babaev-an/anb-python-components/wiki/page?file=class_desc%2Ftypes%2Fversion_info.md).
|
|
56
|
+
- Расширение массивов, типа `bool`, типа `GUID`, типа `str`.
|
|
57
|
+
- Класс для перевода любого типа в строку и наоборот.
|
|
58
|
+
|
|
59
|
+
### ⚙️ Установка и использование:
|
|
60
|
+
|
|
61
|
+
Установить пакет можно через pip:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install anb-python-components --index-url https://registry.gitflic.ru/project/babaev-an/anb-python-components/package/-/pypi/simple
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Подключайте необходимые модули и начните пользоваться ими прямо сейчас:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from anb_python_components.custom_types.two_dim_size import TwoDimSize
|
|
71
|
+
|
|
72
|
+
# Пример использования
|
|
73
|
+
two_dim = TwoDimSize.parse('100x150', 'x')
|
|
74
|
+
|
|
75
|
+
# Теперь присваиваем какому-либо объекту
|
|
76
|
+
some_object_width = two_dim.width
|
|
77
|
+
some_object_height = two_dim.height
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 🛠️ Поддерживаемые технологии:
|
|
81
|
+
|
|
82
|
+
- `Python` версии 3.13.7 и выше
|
|
83
|
+
- Совместима с большинством популярных веб-фреймворков и библиотек Python
|
|
84
|
+
- Оптимизирован для работы с большими объемами данных и высоконагруженными приложениями
|
|
85
|
+
|
|
86
|
+
### 💬 Вопросы и поддержка:
|
|
87
|
+
|
|
88
|
+
Для подробной справки обратитесь к руководству по библиотеке в разделе документации:
|
|
89
|
+
|
|
90
|
+
🔗 Справочное руководство → [Документация библиотеки](https://gitflic.ru/project/babaev-an/anb-python-components/wiki)
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
# 📌🇬🇧 English Version
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Python Component Collection for Streamlining Application Development
|
|
99
|
+
|
|
100
|
+
**ANB Python Components** is a collection of useful classes and modules specifically tailored to accelerate the process
|
|
101
|
+
of developing software using the Python programming language. It offers a broad spectrum of tools that simplify everyday
|
|
102
|
+
developer tasks, from file manipulation and network requests to test automation and seamless integration with widely
|
|
103
|
+
used frameworks.
|
|
104
|
+
|
|
105
|
+
### ✅ Main Features:
|
|
106
|
+
|
|
107
|
+
- Handy tools for working with files and directories.
|
|
108
|
+
- Classes for handling files and folders.
|
|
109
|
+
- Classes for convenient state transfer.
|
|
110
|
+
- New custom_types such
|
|
111
|
+
as [GUID](https://gitflic.ru/project/babaev-an/anb-python-components/wiki/page?file=class_desc%2Ftypes%2Fguid.md), [TwoDimSize](https://gitflic.ru/project/babaev-an/anb-python-components/wiki/page?file=class_desc%2Ftypes%2Ftwo_dim_size.md),
|
|
112
|
+
and [VersionInfo](https://gitflic.ru/project/babaev-an/anb-python-components/wiki/page?file=class_desc%2Ftypes%2Fversion_info.md).
|
|
113
|
+
- Extensions for arrays, `bool`, `GUID`, and `str` custom_types.
|
|
114
|
+
- Class for converting any type into string representation and vice versa.
|
|
115
|
+
|
|
116
|
+
### ⚙️ Installation and Usage:
|
|
117
|
+
|
|
118
|
+
You can install this package via pip:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
pip install anb-python-components --index-url https://registry.gitflic.ru/project/babaev-an/anb-python-components/package/-/pypi/simple
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Import required modules and start using them immediately:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from anb_python_components.custom_types.two_dim_size import TwoDimSize
|
|
128
|
+
|
|
129
|
+
# Example usage
|
|
130
|
+
two_dim = TwoDimSize.parse('100x150', 'x')
|
|
131
|
+
|
|
132
|
+
# Now assign it to some object
|
|
133
|
+
some_object_width = two_dim.width
|
|
134
|
+
some_object_height = two_dim.height
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 🛠️ Supported Technologies:
|
|
138
|
+
|
|
139
|
+
- **Python**: version 3.13.7 or higher
|
|
140
|
+
- Compatibility with most popular Python web frameworks and libraries
|
|
141
|
+
- Optimized for performance with large-scale datasets and highly loaded applications
|
|
142
|
+
|
|
143
|
+
### 💬 Questions and Support:
|
|
144
|
+
|
|
145
|
+
For comprehensive guidance and support, consult the library's documentation section:
|
|
146
|
+
|
|
147
|
+
🔗 Reference Guide → [Library Documentation](https://gitflic.ru/project/babaev-an/anb-python-components/wiki) (in Russian
|
|
148
|
+
Only)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# anb_python_components/__init__.py
|
|
2
|
+
|
|
3
|
+
# classes
|
|
4
|
+
from anb_python_components.classes import ActionState, DataclassAnalyzer, Directory, File, Interface, ShortCodeParser
|
|
5
|
+
# custom_types
|
|
6
|
+
from anb_python_components.custom_types import GUID, ObjectArray, ShortCodeAttributes, TwoDimSize, VersionInfo
|
|
7
|
+
# decorators
|
|
8
|
+
from anb_python_components.decorators import implement, interface_required
|
|
9
|
+
# enums
|
|
10
|
+
from anb_python_components.enums import MessageType, NotBoolAction, TypeCopyStrategy
|
|
11
|
+
# exceptions
|
|
12
|
+
from anb_python_components.exceptions import WrongTypeException
|
|
13
|
+
# extensions
|
|
14
|
+
from anb_python_components.extensions import (
|
|
15
|
+
ArrayExtension, BoolExtension, DataClassExtension, StringExtension,
|
|
16
|
+
TypeExtension
|
|
17
|
+
)
|
|
18
|
+
# models
|
|
19
|
+
from anb_python_components.models import ActionStateMessage, ShortCodeModel
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
'ActionState',
|
|
23
|
+
'Directory',
|
|
24
|
+
'File',
|
|
25
|
+
'DataclassAnalyzer',
|
|
26
|
+
'Interface',
|
|
27
|
+
'ShortCodeParser',
|
|
28
|
+
'GUID',
|
|
29
|
+
'ObjectArray',
|
|
30
|
+
'ShortCodeAttributes',
|
|
31
|
+
'TwoDimSize',
|
|
32
|
+
'VersionInfo',
|
|
33
|
+
'interface_required',
|
|
34
|
+
'implement',
|
|
35
|
+
'MessageType',
|
|
36
|
+
'NotBoolAction',
|
|
37
|
+
'TypeCopyStrategy',
|
|
38
|
+
'WrongTypeException',
|
|
39
|
+
'ArrayExtension',
|
|
40
|
+
'BoolExtension',
|
|
41
|
+
'StringExtension',
|
|
42
|
+
"TypeExtension",
|
|
43
|
+
"DataClassExtension",
|
|
44
|
+
'ActionStateMessage',
|
|
45
|
+
'ShortCodeModel'
|
|
46
|
+
]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# anb_python_components/classes/__init__.py
|
|
2
|
+
|
|
3
|
+
from anb_python_components.classes.action_state import ActionState
|
|
4
|
+
from anb_python_components.classes.dataclass_analyzer import DataclassAnalyzer
|
|
5
|
+
from anb_python_components.classes.directory import Directory
|
|
6
|
+
from anb_python_components.classes.file import File
|
|
7
|
+
from anb_python_components.classes.interface import Interface
|
|
8
|
+
from anb_python_components.classes.shortcode_parser import ShortCodeParser
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
import copy
|
|
3
3
|
from typing import Callable
|
|
4
4
|
|
|
5
|
+
from anb_python_components.enums.message_type import MessageType
|
|
5
6
|
from anb_python_components.exceptions.wrong_type_exception import WrongTypeException
|
|
6
|
-
from anb_python_components.models.action_state_message import ActionStateMessage
|
|
7
|
+
from anb_python_components.models.action_state_message import ActionStateMessage
|
|
7
8
|
|
|
8
9
|
class ActionState[T]:
|
|
9
10
|
"""
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# anb_python_components/classes/dataclass_analyzer.py
|
|
2
|
+
from dataclasses import fields, is_dataclass
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
class DataclassAnalyzer:
|
|
6
|
+
"""
|
|
7
|
+
Класс анализа классов отмеченных как dataclass.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def analyze_properties (cls, prop_name: str) -> dict[str, Any]:
|
|
12
|
+
"""
|
|
13
|
+
Анализирует декораторы (метаданные) указанного свойства класса.
|
|
14
|
+
:param cls: Класс (должен реализовывать dataclass).
|
|
15
|
+
:param prop_name: Имя свойства.
|
|
16
|
+
:return: Словарь с информацией о применённых декораторах.
|
|
17
|
+
"""
|
|
18
|
+
# Проверяем, что класс является dataclass
|
|
19
|
+
if not is_dataclass(cls):
|
|
20
|
+
# - иначе бросаем исключение
|
|
21
|
+
raise TypeError(f"Класс {cls.__name__} не является dataclass / Class {cls.__name__} is not a dataclass")
|
|
22
|
+
|
|
23
|
+
# Находим поле по имени
|
|
24
|
+
# - задаём начальное значение None
|
|
25
|
+
field_obj = None
|
|
26
|
+
|
|
27
|
+
# Перебираем поля класса
|
|
28
|
+
for field in fields(cls):
|
|
29
|
+
# - ищем поле с указанным именем
|
|
30
|
+
if field.name == prop_name:
|
|
31
|
+
# - если нашли, сохраняем объект поля
|
|
32
|
+
field_obj = field
|
|
33
|
+
# - и выходим из цикла
|
|
34
|
+
break
|
|
35
|
+
|
|
36
|
+
# Если поле не найдено
|
|
37
|
+
if field_obj is None:
|
|
38
|
+
# - бросаем исключение
|
|
39
|
+
raise AttributeError(
|
|
40
|
+
f"Свойство '{prop_name}' не найдено в классе {cls.__name__} / Property '{prop_name}' not found in class {cls.__name__}"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Создаем словарь для результатов
|
|
44
|
+
results = {}
|
|
45
|
+
|
|
46
|
+
# Собираем метаданные
|
|
47
|
+
metadata = field_obj.metadata or {}
|
|
48
|
+
|
|
49
|
+
# Перебираем метаданные
|
|
50
|
+
for key, value in metadata.items():
|
|
51
|
+
# - добавляем в словарь
|
|
52
|
+
results[key] = value
|
|
53
|
+
|
|
54
|
+
# Возвращаем результаты
|
|
55
|
+
return results
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def analyze_class (cls) -> dict[str, dict[str, Any]]:
|
|
59
|
+
"""
|
|
60
|
+
Анализирует все свойства dataclass-класса и класс‑уровневые метаданные.
|
|
61
|
+
:param cls: Dataclass-класс.
|
|
62
|
+
:return: Словарь с метаданными полей и класса.
|
|
63
|
+
"""
|
|
64
|
+
# Проверяем, что класс является dataclass
|
|
65
|
+
if not is_dataclass(cls):
|
|
66
|
+
# - иначе бросаем исключение
|
|
67
|
+
raise TypeError(f"Класс {cls.__name__} не является dataclass / Class {cls.__name__} is not a dataclass")
|
|
68
|
+
|
|
69
|
+
results = {}
|
|
70
|
+
|
|
71
|
+
# Собираем класс‑уровневые метаданные (всё, что начинается с __meta_)
|
|
72
|
+
# - создаём пустой словарь
|
|
73
|
+
class_metadata = {}
|
|
74
|
+
|
|
75
|
+
# Перебираем атрибуты класса
|
|
76
|
+
for attr_name in dir(cls):
|
|
77
|
+
# - ищем атрибуты, начинающиеся с __meta_
|
|
78
|
+
if attr_name.startswith("__meta_"):
|
|
79
|
+
# -- удаляем префикс __meta_ и берём значение
|
|
80
|
+
key = attr_name[len("__meta_"):]
|
|
81
|
+
# -- добавляем в словарь
|
|
82
|
+
class_metadata[key] = getattr(cls, attr_name)
|
|
83
|
+
|
|
84
|
+
# Если есть метаданные
|
|
85
|
+
if class_metadata:
|
|
86
|
+
# - добавляем в результаты
|
|
87
|
+
results["__CLASS__"] = class_metadata
|
|
88
|
+
|
|
89
|
+
# Анализируем каждое поле
|
|
90
|
+
for field in fields(cls):
|
|
91
|
+
# - получаем имя поля
|
|
92
|
+
field_name = field.name
|
|
93
|
+
# - анализируем поле и сохраняем результаты в словарь
|
|
94
|
+
results[field_name] = DataclassAnalyzer.analyze_properties(cls, field_name)
|
|
95
|
+
|
|
96
|
+
# Возвращаем результаты
|
|
97
|
+
return results
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# anb_python_components/classes/interface.py
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
from functools import lru_cache
|
|
5
|
+
from typing import Any, Protocol, get_origin, runtime_checkable
|
|
6
|
+
|
|
7
|
+
@runtime_checkable
|
|
8
|
+
class Interface(Protocol):
|
|
9
|
+
"""
|
|
10
|
+
Базовый класс для объявления интерфейсов с расширенной проверкой.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
@lru_cache(maxsize = 128)
|
|
15
|
+
def verify (cls, obj_or_class) -> tuple[bool, list[str]]:
|
|
16
|
+
"""
|
|
17
|
+
Проверяет, что объект или класс реализует интерфейс.
|
|
18
|
+
:param obj_or_class: Объект (созданный класс) или класс (или, точнее, его имя), который должен реализовывать интерфейс.
|
|
19
|
+
:return: Кортеж (is_ok, problems), где is_ok — True, если объект или класс реализует интерфейс, иначе False,
|
|
20
|
+
а problems — список сообщений об ошибках, если is_ok == False.
|
|
21
|
+
"""
|
|
22
|
+
# Разбираем аргумент на объект или имя класса. Если аргумент — имя класса, то получаем его класс. В противном случае,
|
|
23
|
+
# возвращаем аргумент как объект.
|
|
24
|
+
target = obj_or_class if isinstance(obj_or_class, type) else obj_or_class.__class__
|
|
25
|
+
|
|
26
|
+
# Инициализируем список проблем
|
|
27
|
+
problems = []
|
|
28
|
+
|
|
29
|
+
# Собираем все методы интерфейса (включая унаследованные)
|
|
30
|
+
interface_methods = cls._get_all_interface_methods()
|
|
31
|
+
|
|
32
|
+
# Проходим по методам интерфейса
|
|
33
|
+
for method_name, signature in interface_methods.items():
|
|
34
|
+
# - если метод отсутствует в объекте
|
|
35
|
+
if not hasattr(target, method_name):
|
|
36
|
+
# -- добавляем в список проблем
|
|
37
|
+
problems.append(f"отсутствует метод '{method_name}' / missing method '{method_name}'")
|
|
38
|
+
# -- идём к следующему методу
|
|
39
|
+
continue
|
|
40
|
+
|
|
41
|
+
# - получаем именованный атрибут метода
|
|
42
|
+
target_method = getattr(target, method_name)
|
|
43
|
+
|
|
44
|
+
# - проверяем, что метод — вызываемый
|
|
45
|
+
if not callable(target_method):
|
|
46
|
+
# -- если нет, добавляем в список проблем
|
|
47
|
+
problems.append(f"'{method_name}' it is not callable")
|
|
48
|
+
# -- идём к следующему методу
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
# - получаем сигнатуру метода
|
|
53
|
+
target_signature = inspect.signature(target_method)
|
|
54
|
+
|
|
55
|
+
# - сравниваем сигнатуры метода и интерфейса
|
|
56
|
+
cls._compare_signatures(signature, target_signature, method_name, problems)
|
|
57
|
+
except (ValueError, TypeError) as e:
|
|
58
|
+
# Если сигнатуру нельзя получить, добавляем в список проблем
|
|
59
|
+
problems.append(f"ошибка проверки сигнатуры / signature verification error '{method_name}': {e}")
|
|
60
|
+
|
|
61
|
+
# В результат вернём кортеж (is_ok, problems), где is_ok — True, если проблем нет, иначе False, а problems — список проблем.
|
|
62
|
+
return len(problems) == 0, problems
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def check (cls, obj_or_class) -> bool:
|
|
66
|
+
"""
|
|
67
|
+
Строгая проверка — поднимает исключение при несоответствии.
|
|
68
|
+
:param obj_or_class: Объект (созданный класс) или класс (или, точнее, его имя), который должен реализовывать интерфейс.
|
|
69
|
+
:return: True, если объект или класс реализует интерфейс.
|
|
70
|
+
"""
|
|
71
|
+
# Проверяем, что объект или класс реализует интерфейс методом verify и получаем результат проверки
|
|
72
|
+
is_ok, problems = cls.verify(obj_or_class)
|
|
73
|
+
|
|
74
|
+
# Если объект или класс не реализует интерфейс
|
|
75
|
+
if not is_ok:
|
|
76
|
+
# - получаем имя объекта или класса
|
|
77
|
+
target_name = obj_or_class.__name__ if isinstance(obj_or_class, type) else obj_or_class.__class__.__name__
|
|
78
|
+
|
|
79
|
+
# - генерируем исключение с проблемами
|
|
80
|
+
raise TypeError(
|
|
81
|
+
f"{target_name} не реализует интерфейс {cls.__name__}.\n" +
|
|
82
|
+
f"{target_name} not implements interface {cls.__name__}.\n" +
|
|
83
|
+
f"Проблемы / Problems:\n" +
|
|
84
|
+
"\n ".join(problems)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Возвращаем True, если объект или класс реализует интерфейс
|
|
88
|
+
return True
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
def get_methods (cls) -> dict[str, inspect.Signature]:
|
|
92
|
+
"""
|
|
93
|
+
Возвращает словарь методов интерфейса с их сигнатурами.
|
|
94
|
+
:return: Словарь методов интерфейса с их сигнатурами.
|
|
95
|
+
"""
|
|
96
|
+
return cls._get_interface_methods(cls)
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def _get_all_interface_methods (cls) -> dict[str, inspect.Signature]:
|
|
100
|
+
"""
|
|
101
|
+
Собирает все методы из иерархии наследования интерфейсов.
|
|
102
|
+
:return: Словарь методов с их сигнатурами.
|
|
103
|
+
"""
|
|
104
|
+
# Создаем словарь методов
|
|
105
|
+
methods = {}
|
|
106
|
+
|
|
107
|
+
# Проходим по иерархии наследования интерфейсов рекурсивно
|
|
108
|
+
for base in reversed(cls.__mro__):
|
|
109
|
+
# - пропускаем базовый класс и сам интерфейс
|
|
110
|
+
if base is Interface or not issubclass(base, Interface):
|
|
111
|
+
continue
|
|
112
|
+
|
|
113
|
+
# - добавляем методы базового интерфейса
|
|
114
|
+
methods.update(cls._get_interface_methods(base))
|
|
115
|
+
|
|
116
|
+
# Возвращаем собранные методы
|
|
117
|
+
return methods
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def _get_interface_methods (interface_cls) -> dict[str, inspect.Signature]:
|
|
121
|
+
"""Извлекает методы интерфейса и их сигнатуры."""
|
|
122
|
+
methods = {}
|
|
123
|
+
for attr_name in dir(interface_cls):
|
|
124
|
+
if attr_name.startswith('_') or attr_name == 'verify':
|
|
125
|
+
continue
|
|
126
|
+
attr = getattr(interface_cls, attr_name)
|
|
127
|
+
if callable(attr):
|
|
128
|
+
try:
|
|
129
|
+
signature = inspect.signature(attr)
|
|
130
|
+
methods[attr_name] = signature
|
|
131
|
+
except (ValueError, TypeError):
|
|
132
|
+
# Если сигнатуру нельзя получить, оставляем пустую
|
|
133
|
+
methods[attr_name] = inspect.Signature()
|
|
134
|
+
return methods
|
|
135
|
+
|
|
136
|
+
@staticmethod
|
|
137
|
+
def _compare_signatures (
|
|
138
|
+
expected: inspect.Signature,
|
|
139
|
+
actual: inspect.Signature,
|
|
140
|
+
method_name: str,
|
|
141
|
+
problems: list[str]
|
|
142
|
+
) -> None:
|
|
143
|
+
"""
|
|
144
|
+
Сравнивает сигнатуры методов.
|
|
145
|
+
:param expected: Ожидаемая сигнатура.
|
|
146
|
+
:param actual: Текущая сигнатура.
|
|
147
|
+
:param method_name: Имя метода.
|
|
148
|
+
:param problems: Список проблем.
|
|
149
|
+
:return: None
|
|
150
|
+
"""
|
|
151
|
+
# Проверяем количество параметров
|
|
152
|
+
expected_params = list(expected.parameters.values())
|
|
153
|
+
actual_params = list(actual.parameters.values())
|
|
154
|
+
|
|
155
|
+
# Проверяем количество параметров
|
|
156
|
+
if len(expected_params) != len(actual_params):
|
|
157
|
+
# - если количество параметров не совпадает, добавляем в список проблем
|
|
158
|
+
problems.append(
|
|
159
|
+
f"'{method_name}': ожидается {len(expected_params)} параметров, получено {len(actual_params)} / {method_name}': expected {len(expected_params)} parameters, got {len(actual_params)}"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# - прерываем выполнение
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
# Проверяем типы параметров (если указаны)
|
|
166
|
+
for i, (exp_param, act_param) in enumerate(zip(expected_params, actual_params)):
|
|
167
|
+
# - проверяем, что у ожидаемого параметра (exp_param) есть аннотация
|
|
168
|
+
if exp_param.annotation is not exp_param.empty:
|
|
169
|
+
# -- получаем тип ожидаемого параметра
|
|
170
|
+
expected_type = exp_param.annotation
|
|
171
|
+
# -- получаем тип текущего параметра
|
|
172
|
+
actual_type = act_param.annotation if act_param.annotation is not act_param.empty else Any
|
|
173
|
+
|
|
174
|
+
# -- сравниваем типы
|
|
175
|
+
if get_origin(expected_type) != get_origin(actual_type):
|
|
176
|
+
# --- если типы не совпадают, добавляем в список проблем
|
|
177
|
+
problems.append(
|
|
178
|
+
f"Тип аннотации не совпадает для / annotation type mismatch for parameter '{exp_param.name}'"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
@classmethod
|
|
182
|
+
def register (cls, target_class):
|
|
183
|
+
"""
|
|
184
|
+
Регистрирует класс как реализующий интерфейс (для документации).
|
|
185
|
+
:param target_class: Класс, который должен реализовывать интерфейс.
|
|
186
|
+
:return: Регистрированный класс.
|
|
187
|
+
"""
|
|
188
|
+
# Проверяем, что класс реализует интерфейс
|
|
189
|
+
cls.check(target_class)
|
|
190
|
+
|
|
191
|
+
# Возвращаем класс
|
|
192
|
+
return target_class
|
|
@@ -4,8 +4,8 @@ import re
|
|
|
4
4
|
from typing import Callable, Dict
|
|
5
5
|
|
|
6
6
|
from anb_python_components.classes.action_state import ActionState
|
|
7
|
-
from anb_python_components.models.shortcode_model import ShortCodeModel
|
|
8
7
|
from anb_python_components.custom_types.shortcode_attributes import ShortCodeAttributes
|
|
8
|
+
from anb_python_components.models.shortcode_model import ShortCodeModel
|
|
9
9
|
|
|
10
10
|
class ShortCodeParser:
|
|
11
11
|
"""
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# anb_python_components/custom_types/__init__.py
|
|
2
|
+
|
|
3
|
+
from anb_python_components.custom_types.guid import GUID
|
|
4
|
+
from anb_python_components.custom_types.object_array import ObjectArray
|
|
5
|
+
from anb_python_components.custom_types.shortcode_attributes import ShortCodeAttributes
|
|
6
|
+
from anb_python_components.custom_types.two_dim_size import TwoDimSize
|
|
7
|
+
from anb_python_components.custom_types.version_info import VersionInfo
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# anb_python_components/decorators/interface_decorators.py
|
|
2
|
+
import inspect
|
|
3
|
+
from typing import Callable
|
|
4
|
+
|
|
5
|
+
from anb_python_components import Interface
|
|
6
|
+
|
|
7
|
+
def interface_required (signature: Callable | None = None, **kwargs) -> Callable:
|
|
8
|
+
"""
|
|
9
|
+
Декоратор для явного указания обязательных методов интерфейса.
|
|
10
|
+
Позволяет задать сигнатуру через аннотации.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def decorator (func) -> Callable:
|
|
14
|
+
"""
|
|
15
|
+
Декоратор для проверки сигнатуры.
|
|
16
|
+
:param func: Функция для проверки.
|
|
17
|
+
:return: Проверенная функция.
|
|
18
|
+
"""
|
|
19
|
+
# Если сигнатура указана
|
|
20
|
+
if signature is not None:
|
|
21
|
+
# - сохраняем сигнатуру в атрибуте сигнатуры
|
|
22
|
+
func.__signature__ = inspect.signature(signature)
|
|
23
|
+
else:
|
|
24
|
+
# - иначе сохраняем сигнатуру в атрибуте аннотаций
|
|
25
|
+
func.__annotations__ = kwargs
|
|
26
|
+
|
|
27
|
+
# Возвращаем функцию
|
|
28
|
+
return func
|
|
29
|
+
|
|
30
|
+
# Возвращаем функцию-декоратор
|
|
31
|
+
return decorator
|
|
32
|
+
|
|
33
|
+
def implement (interface):
|
|
34
|
+
"""
|
|
35
|
+
Декоратор для явного указания реализации интерфейса.
|
|
36
|
+
:param interface: Класс-интерфейс (наследник Interface)
|
|
37
|
+
:raise: TypeError: если класс не реализует интерфейс
|
|
38
|
+
:return: Оригинальный класс (с добавленным атрибутом __implements__)
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def decorator (cls):
|
|
42
|
+
"""
|
|
43
|
+
Декоратор для реализации интерфейса.
|
|
44
|
+
:param cls: Класс для реализации интерфейса.
|
|
45
|
+
:return: Оригинальный класс с реализацией интерфейса.
|
|
46
|
+
"""
|
|
47
|
+
# 1. Проверяем, что interface действительно является интерфейсом
|
|
48
|
+
if not isinstance(interface, type) or not issubclass(interface, Interface):
|
|
49
|
+
raise TypeError(
|
|
50
|
+
f"{interface} не является интерфейсом (не наследуется от Interface) / is not an interface (does not inherit from Interface)"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# 2. Выполняем строгую проверку реализации
|
|
54
|
+
try:
|
|
55
|
+
interface.check(cls)
|
|
56
|
+
except TypeError as e:
|
|
57
|
+
raise TypeError(
|
|
58
|
+
f"Класс {cls.__name__} не реализует интерфейс {interface.__name__} / class {cls.__name__} does not implement interface {interface.__name__}\n {e}"
|
|
59
|
+
) from e
|
|
60
|
+
|
|
61
|
+
# 3. Добавляем метаданные о реализации
|
|
62
|
+
if not hasattr(cls, '__implements__'):
|
|
63
|
+
cls.__implements__ = []
|
|
64
|
+
cls.__implements__.append(interface)
|
|
65
|
+
|
|
66
|
+
# 4. Сохраняем ссылку на интерфейс для интроспекции
|
|
67
|
+
impl_attr = f'__implements_{interface.__name__}'
|
|
68
|
+
setattr(cls, impl_attr, True)
|
|
69
|
+
|
|
70
|
+
# 5. Декоратор должен вернуть оригинальный класс
|
|
71
|
+
return cls
|
|
72
|
+
|
|
73
|
+
# Возвращаем функцию-декоратор
|
|
74
|
+
return decorator
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# anb_python_components/extensions/__init__.py
|
|
2
|
+
|
|
3
|
+
from anb_python_components.extensions.array_extension import ArrayExtension
|
|
4
|
+
from anb_python_components.extensions.bool_extension import BoolExtension
|
|
5
|
+
from anb_python_components.extensions.dataclass_extension import DataClassExtension
|
|
6
|
+
from anb_python_components.extensions.string_extension import StringExtension
|
|
7
|
+
from anb_python_components.extensions.type_extension import TypeExtension
|