anb-python-components 1.3.0__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.
Files changed (64) hide show
  1. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/PKG-INFO +3 -1
  2. anb_python_components-1.4.0/anb_python_components/__init__.py +46 -0
  3. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/classes/__init__.py +1 -0
  4. anb_python_components-1.4.0/anb_python_components/classes/dataclass_analyzer.py +97 -0
  5. anb_python_components-1.4.0/anb_python_components/exceptions/__init__.py +3 -0
  6. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/extensions/__init__.py +1 -0
  7. anb_python_components-1.4.0/anb_python_components/extensions/dataclass_extension.py +59 -0
  8. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/extensions/type_extension.py +26 -1
  9. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components.egg-info/PKG-INFO +3 -1
  10. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components.egg-info/SOURCES.txt +4 -0
  11. anb_python_components-1.4.0/anb_python_components.egg-info/requires.txt +1 -0
  12. anb_python_components-1.4.0/setup.py +74 -0
  13. anb_python_components-1.4.0/tests/classes/dataclass_analyzer_test.py +62 -0
  14. anb_python_components-1.3.0/anb_python_components/__init__.py +0 -55
  15. anb_python_components-1.3.0/anb_python_components/exceptions/__init__.py +0 -3
  16. anb_python_components-1.3.0/setup.py +0 -38
  17. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/LICENSE +0 -0
  18. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/README.md +0 -0
  19. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/classes/action_state.py +0 -0
  20. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/classes/directory.py +0 -0
  21. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/classes/file.py +0 -0
  22. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/classes/interface.py +0 -0
  23. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/classes/shortcode_parser.py +0 -0
  24. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/custom_types/__init__.py +0 -0
  25. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/custom_types/guid.py +0 -0
  26. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/custom_types/object_array.py +0 -0
  27. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/custom_types/shortcode_attributes.py +0 -0
  28. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/custom_types/two_dim_size.py +0 -0
  29. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/custom_types/version_info.py +0 -0
  30. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/decorators/__init__.py +0 -0
  31. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/decorators/interface_decorators.py +0 -0
  32. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/enums/__init__.py +0 -0
  33. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/enums/message_type.py +0 -0
  34. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/enums/not_bool_action.py +0 -0
  35. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/enums/type_copy_strategy.py +0 -0
  36. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/exceptions/wrong_type_exception.py +0 -0
  37. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/extensions/array_extension.py +0 -0
  38. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/extensions/bool_extension.py +0 -0
  39. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/extensions/string_extension.py +0 -0
  40. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/extensions/string_extension_constant.py +0 -0
  41. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/models/__init__.py +0 -0
  42. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/models/action_state_message.py +0 -0
  43. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components/models/shortcode_model.py +0 -0
  44. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components.egg-info/dependency_links.txt +0 -0
  45. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components.egg-info/not-zip-safe +0 -0
  46. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/anb_python_components.egg-info/top_level.txt +0 -0
  47. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/setup.cfg +0 -0
  48. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/__init__.py +0 -0
  49. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/classes/__init__.py +0 -0
  50. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/classes/action_state_test.py +0 -0
  51. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/classes/directory_test.py +0 -0
  52. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/classes/file_test.py +0 -0
  53. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/classes/interface_test.py +0 -0
  54. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/classes/shortcode_parser_test.py +0 -0
  55. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/custom_types/__init__.py +0 -0
  56. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/custom_types/guid_test.py +0 -0
  57. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/custom_types/object_array_test.py +0 -0
  58. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/custom_types/two_dim_size_test.py +0 -0
  59. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/custom_types/version_info_test.py +0 -0
  60. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/extensions/__init__.py +0 -0
  61. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/extensions/array_extension_test.py +0 -0
  62. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/extensions/bool_extension_test.py +0 -0
  63. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/extensions/string_extension_test.py +0 -0
  64. {anb_python_components-1.3.0 → anb_python_components-1.4.0}/tests/extensions/type_extension_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anb_python_components
3
- Version: 1.3.0
3
+ Version: 1.4.0
4
4
  Summary: Набор компонентов Python, которые упрощают разработку / A set of Python components that simplify development
5
5
  Home-page: https://gitflic.ru/project/babaev-an/anb-python-components
6
6
  Author: Александр Бабаев
@@ -19,6 +19,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
19
  Requires-Python: >=3.14.0
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
+ Requires-Dist: setuptools>=80.9.0
22
23
  Dynamic: author
23
24
  Dynamic: author-email
24
25
  Dynamic: classifier
@@ -28,6 +29,7 @@ Dynamic: home-page
28
29
  Dynamic: keywords
29
30
  Dynamic: license-file
30
31
  Dynamic: project-url
32
+ Dynamic: requires-dist
31
33
  Dynamic: requires-python
32
34
  Dynamic: summary
33
35
 
@@ -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
+ ]
@@ -1,6 +1,7 @@
1
1
  # anb_python_components/classes/__init__.py
2
2
 
3
3
  from anb_python_components.classes.action_state import ActionState
4
+ from anb_python_components.classes.dataclass_analyzer import DataclassAnalyzer
4
5
  from anb_python_components.classes.directory import Directory
5
6
  from anb_python_components.classes.file import File
6
7
  from anb_python_components.classes.interface import Interface
@@ -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,3 @@
1
+ # anb_python_components/exceptions/__init__.py
2
+
3
+ from anb_python_components.exceptions.wrong_type_exception import WrongTypeException
@@ -2,5 +2,6 @@
2
2
 
3
3
  from anb_python_components.extensions.array_extension import ArrayExtension
4
4
  from anb_python_components.extensions.bool_extension import BoolExtension
5
+ from anb_python_components.extensions.dataclass_extension import DataClassExtension
5
6
  from anb_python_components.extensions.string_extension import StringExtension
6
7
  from anb_python_components.extensions.type_extension import TypeExtension
@@ -0,0 +1,59 @@
1
+ # anb_python_components/extensions/dataclass_extension.py
2
+ import copy
3
+ from dataclasses import Field, field
4
+ from typing import Any
5
+
6
+ from anb_python_components.extensions.type_extension import TypeExtension
7
+
8
+ # Базовая структура для представления расширенного типа
9
+ class DataClassExtension:
10
+ @staticmethod
11
+ def define (default: Any = None, metadata: dict[str, Any] = None) -> Field:
12
+ """
13
+ Определяет поле с расширенными метаданными.
14
+ :param default: Значение по умолчанию для поля.
15
+ :param metadata: Метаданные для поля.
16
+ :return: Объект поля.
17
+ """
18
+ # Создаём словарь метаданных
19
+ meta = {} if metadata is None else metadata
20
+
21
+ # Если значение по умолчанию не передано
22
+ if default is None:
23
+ # - то создаём поле без значения по умолчанию
24
+ return field(metadata = meta)
25
+
26
+ # Если значение по умолчанию передано неизменяемым типом, то создаём поле с этим значением по умолчанию, иначе с функцией-фабрикой
27
+ return field(default = default, metadata = meta) if TypeExtension.check_immutability(default) else field(
28
+ default_factory = lambda: copy.copy(default), metadata = meta
29
+ )
30
+
31
+ @staticmethod
32
+ def defines (default: Any, *fields: Field) -> Field:
33
+ """
34
+ Определяет поле с расширенными метаданными и переданными полями.
35
+ :param default: Значение по умолчанию для поля.
36
+ :param fields: Поля для расширения.
37
+ :return: Объект поля.
38
+ """
39
+ # Создаём словарь метаданных
40
+ metas = {}
41
+
42
+ # Проверяем, что все аргументы — экземпляры Field
43
+ for i, f in enumerate(fields):
44
+ # - если хоть один аргумент не является полем
45
+ if not isinstance(f, Field):
46
+ # -- то создаём текстовое описание ошибки
47
+ error = [
48
+ f"Аргумент fields[{i}] должен быть экземпляром dataclasses.Field, получено {type(f).__name__}",
49
+ f"Argument fields[{i}] must be an instance of dataclasses.Field, got {type(f).__name__}"
50
+ ]
51
+ # -- и выбрасываем исключение
52
+ raise TypeError("\n".join(error))
53
+
54
+ # Собираем все метаданные из переданных полей
55
+ for f in fields:
56
+ if f.metadata:
57
+ metas.update(f.metadata)
58
+
59
+ return DataClassExtension.define(default, metas)
@@ -109,4 +109,29 @@ class TypeExtension:
109
109
  inner_type = args[0] # Получаем T из list[T]
110
110
  return TypeExtension.is_immutable_type(inner_type) # Проверяем T
111
111
 
112
- return False
112
+ return False
113
+
114
+ @staticmethod
115
+ def check_immutability (value: Any) -> bool:
116
+ """
117
+ Проверяет, является ли значение `value` неизменяемым по типу.
118
+ :param value: Проверяемое значение.
119
+ :return: True, если тип значения неизменяемый, иначе False.
120
+ """
121
+ # None — неизменяемый
122
+ if value is None:
123
+ return True
124
+
125
+ # Получаем фактический тип значения
126
+ value_type = type(value)
127
+
128
+ # Используем get_origin/get_args для анализа
129
+ origin = get_origin(value_type)
130
+
131
+ # Если это параметризованный тип (list[int], Union[str, int] и т.п.),
132
+ if origin is not None:
133
+ # - передаём сам параметризованный тип в is_immutable_type
134
+ return TypeExtension.is_immutable_type(origin)
135
+ else:
136
+ # - иначе проверяем тип напрямую (для простых типов, таких как int, str и т.п.)
137
+ return TypeExtension.is_immutable_type(value_type)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anb_python_components
3
- Version: 1.3.0
3
+ Version: 1.4.0
4
4
  Summary: Набор компонентов Python, которые упрощают разработку / A set of Python components that simplify development
5
5
  Home-page: https://gitflic.ru/project/babaev-an/anb-python-components
6
6
  Author: Александр Бабаев
@@ -19,6 +19,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
19
  Requires-Python: >=3.14.0
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
+ Requires-Dist: setuptools>=80.9.0
22
23
  Dynamic: author
23
24
  Dynamic: author-email
24
25
  Dynamic: classifier
@@ -28,6 +29,7 @@ Dynamic: home-page
28
29
  Dynamic: keywords
29
30
  Dynamic: license-file
30
31
  Dynamic: project-url
32
+ Dynamic: requires-dist
31
33
  Dynamic: requires-python
32
34
  Dynamic: summary
33
35
 
@@ -6,9 +6,11 @@ anb_python_components.egg-info/PKG-INFO
6
6
  anb_python_components.egg-info/SOURCES.txt
7
7
  anb_python_components.egg-info/dependency_links.txt
8
8
  anb_python_components.egg-info/not-zip-safe
9
+ anb_python_components.egg-info/requires.txt
9
10
  anb_python_components.egg-info/top_level.txt
10
11
  anb_python_components/classes/__init__.py
11
12
  anb_python_components/classes/action_state.py
13
+ anb_python_components/classes/dataclass_analyzer.py
12
14
  anb_python_components/classes/directory.py
13
15
  anb_python_components/classes/file.py
14
16
  anb_python_components/classes/interface.py
@@ -30,6 +32,7 @@ anb_python_components/exceptions/wrong_type_exception.py
30
32
  anb_python_components/extensions/__init__.py
31
33
  anb_python_components/extensions/array_extension.py
32
34
  anb_python_components/extensions/bool_extension.py
35
+ anb_python_components/extensions/dataclass_extension.py
33
36
  anb_python_components/extensions/string_extension.py
34
37
  anb_python_components/extensions/string_extension_constant.py
35
38
  anb_python_components/extensions/type_extension.py
@@ -39,6 +42,7 @@ anb_python_components/models/shortcode_model.py
39
42
  tests/__init__.py
40
43
  tests/classes/__init__.py
41
44
  tests/classes/action_state_test.py
45
+ tests/classes/dataclass_analyzer_test.py
42
46
  tests/classes/directory_test.py
43
47
  tests/classes/file_test.py
44
48
  tests/classes/interface_test.py
@@ -0,0 +1,74 @@
1
+ from os import path
2
+
3
+ from setuptools import find_packages, setup
4
+
5
+ # Получаем текущую директорию
6
+ this_directory = path.abspath(path.dirname(__file__))
7
+
8
+ # Функция для чтения requirements.txt
9
+ def read_requirements () -> list[str]:
10
+ """
11
+ Читает файл requirements.txt и возвращает список зависимостей.
12
+ :return: Список зависимостей.
13
+ """
14
+ # Путь к файлу requirements.txt в текущей директории
15
+ requirements_path = path.join(this_directory, 'requirements.txt')
16
+
17
+ # Проверяем, существует ли файл requirements.txt
18
+ if not path.exists(requirements_path):
19
+ # - если нет, возвращаем пустой список
20
+ return []
21
+
22
+ # Читаем файл requirements.txt
23
+ with open(requirements_path, encoding = 'utf-8') as f:
24
+ # - возвращаем список зависимостей, предварительно убрав пустые строки и комментарии
25
+ lines = [line.strip() for line in f if line.strip() and not line.startswith('#')]
26
+
27
+ # Возвращаем список зависимостей
28
+ return lines
29
+
30
+ def read_readme () -> str:
31
+ """
32
+ Получает содержимое файла README.md и возвращает его в виде строки.
33
+ :return: Содержимое файла README.md.
34
+ """
35
+ # Задаём переменную для хранения содержимого файла README.md
36
+ readme_content = ''
37
+
38
+ # Открываем файл README.md
39
+ with open(path.join(this_directory, 'README.md'), encoding = 'utf-8') as f:
40
+ # - и читаем его содержимое
41
+ readme_content = f.read()
42
+
43
+ # Возвращаем содержимое файла README.md
44
+ return readme_content
45
+
46
+ setup(
47
+ name = 'anb_python_components',
48
+ version = '1.4.0',
49
+ description = 'Набор компонентов Python, которые упрощают разработку / A set of Python components that simplify development',
50
+ long_description = read_readme(),
51
+ long_description_content_type = 'text/markdown',
52
+ author = 'Александр Бабаев',
53
+ author_email = 'contact_with_us@babaev-an.ru',
54
+ url = 'https://gitflic.ru/project/babaev-an/anb-python-components',
55
+ packages = find_packages(),
56
+ install_requires = read_requirements(),
57
+ python_requires = '>=3.14.0',
58
+ classifiers = [
59
+ 'Development Status :: 5 - Production/Stable',
60
+ 'Intended Audience :: Developers',
61
+ 'License :: OSI Approved :: MIT License',
62
+ 'Programming Language :: Python :: 3',
63
+ 'Programming Language :: Python :: 3.14',
64
+ 'Operating System :: OS Independent',
65
+ 'Topic :: Software Development :: Libraries :: Python Modules',
66
+ ],
67
+ keywords = 'python components development utils',
68
+ project_urls = {
69
+ 'Documentation': 'https://gitflic.ru/project/babaev-an/anb-python-components/wiki',
70
+ 'Source': 'https://gitflic.ru/project/babaev-an/anb-python-components',
71
+ 'Tracker': 'https://gitflic.ru/project/babaev-an/anb-python-components/issue?page=0',
72
+ },
73
+ zip_safe = False,
74
+ )
@@ -0,0 +1,62 @@
1
+ # tests/dataclass_analyzer_test.py
2
+
3
+ import unittest
4
+ from dataclasses import dataclass, is_dataclass
5
+
6
+ from anb_python_components import DataClassExtension, DataclassAnalyzer
7
+
8
+ class Fields:
9
+ @staticmethod
10
+ def primary_key (default = None):
11
+ return DataClassExtension.define(default, metadata = {'db_primary_key': True})
12
+
13
+ @staticmethod
14
+ def unique (default = None):
15
+ return DataClassExtension.define(default, metadata = {'db_unique': True})
16
+
17
+ @staticmethod
18
+ def indexed (default = None):
19
+ return DataClassExtension.define(default, metadata = {'db_indexed': True})
20
+
21
+ @staticmethod
22
+ def compose (default, *fields):
23
+ return DataClassExtension.defines(default, *fields)
24
+
25
+ def table_name (name: str):
26
+ """
27
+ Декоратор для dataclass-класса, задающий имя таблицы в СУБД.
28
+ :param name: Имя таблицы.
29
+ :return: Обогащённый класс.
30
+ """
31
+
32
+ def decorator (cls):
33
+ if not is_dataclass(cls):
34
+ raise TypeError(f"Класс {cls.__name__} должен быть dataclass")
35
+
36
+ # Сохраняем имя таблицы как атрибут класса (начинается с __meta_)
37
+ cls.__meta_table_name = name
38
+ return cls
39
+
40
+ return decorator
41
+
42
+ @table_name("ANB")
43
+ @dataclass
44
+ class A:
45
+ a: int = Fields.primary_key(0)
46
+ b: str = Fields.compose("", Fields.unique(), Fields.indexed())
47
+
48
+ class DataclassAnalyzerTest(unittest.TestCase):
49
+ def test_analyze (self):
50
+ analyze_result = DataclassAnalyzer.analyze_class(A)
51
+ self.assertEqual("ANB", analyze_result["__CLASS__"]["table_name"])
52
+ self.assertTrue(analyze_result["a"]["db_primary_key"])
53
+
54
+ analyze_result = DataclassAnalyzer.analyze_properties(A, "a")
55
+ self.assertTrue(analyze_result["db_primary_key"])
56
+
57
+ analyze_result = DataclassAnalyzer.analyze_properties(A, "b")
58
+ self.assertTrue(analyze_result["db_unique"])
59
+ self.assertTrue(analyze_result["db_indexed"])
60
+
61
+ if __name__ == '__main__':
62
+ unittest.main()
@@ -1,55 +0,0 @@
1
- # anb_python_components/__init__.py
2
-
3
- # classes
4
- from anb_python_components.classes.action_state import ActionState
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
9
- # custom_types
10
- from anb_python_components.custom_types.guid import GUID
11
- from anb_python_components.custom_types.object_array import ObjectArray
12
- from anb_python_components.custom_types.shortcode_attributes import ShortCodeAttributes
13
- from anb_python_components.custom_types.two_dim_size import TwoDimSize
14
- from anb_python_components.custom_types.version_info import VersionInfo
15
- # decorators
16
- from anb_python_components.decorators.interface_decorators import implement, interface_required
17
- # enums
18
- from anb_python_components.enums.message_type import MessageType
19
- from anb_python_components.enums.not_bool_action import NotBoolAction
20
- from anb_python_components.enums.type_copy_strategy import TypeCopyStrategy
21
- # exceptions
22
- from anb_python_components.exceptions.wrong_type_exception import WrongTypeException
23
- # extensions
24
- from anb_python_components.extensions.array_extension import ArrayExtension
25
- from anb_python_components.extensions.bool_extension import BoolExtension
26
- from anb_python_components.extensions.string_extension import StringExtension
27
- from anb_python_components.extensions.type_extension import TypeExtension
28
- # models
29
- from anb_python_components.models.action_state_message import ActionStateMessage
30
- from anb_python_components.models.shortcode_model import ShortCodeModel
31
-
32
- __all__ = [
33
- 'ActionState',
34
- 'Directory',
35
- 'File',
36
- 'Interface',
37
- 'ShortCodeParser',
38
- 'GUID',
39
- 'ObjectArray',
40
- 'ShortCodeAttributes',
41
- 'TwoDimSize',
42
- 'VersionInfo',
43
- 'interface_required',
44
- 'implement',
45
- 'MessageType',
46
- 'NotBoolAction',
47
- 'TypeCopyStrategy',
48
- 'WrongTypeException',
49
- 'ArrayExtension',
50
- 'BoolExtension',
51
- 'StringExtension',
52
- "TypeExtension",
53
- 'ActionStateMessage',
54
- 'ShortCodeModel'
55
- ]
@@ -1,3 +0,0 @@
1
- # anb_python_components/exceptions/__init__.py
2
-
3
- # from anb_python_components.exceptions.wrong_type_exception import WrongTypeException
@@ -1,38 +0,0 @@
1
- from os import path
2
-
3
- from setuptools import find_packages, setup
4
-
5
- # Получаем длинное описание из README.md
6
- this_directory = path.abspath(path.dirname(__file__))
7
- with open(path.join(this_directory, 'README.md'), encoding = 'utf-8') as f:
8
- long_description = f.read()
9
-
10
- setup(
11
- name = 'anb_python_components',
12
- version = '1.3.0',
13
- description = 'Набор компонентов Python, которые упрощают разработку / A set of Python components that simplify development',
14
- long_description = long_description,
15
- long_description_content_type = 'text/markdown',
16
- author = 'Александр Бабаев',
17
- author_email = 'contact_with_us@babaev-an.ru',
18
- url = 'https://gitflic.ru/project/babaev-an/anb-python-components',
19
- packages = find_packages(),
20
- install_requires = [],
21
- python_requires = '>=3.14.0',
22
- classifiers = [
23
- 'Development Status :: 5 - Production/Stable',
24
- 'Intended Audience :: Developers',
25
- 'License :: OSI Approved :: MIT License',
26
- 'Programming Language :: Python :: 3',
27
- 'Programming Language :: Python :: 3.14',
28
- 'Operating System :: OS Independent',
29
- 'Topic :: Software Development :: Libraries :: Python Modules',
30
- ],
31
- keywords = 'python components development utils',
32
- project_urls = {
33
- 'Documentation': 'https://gitflic.ru/project/babaev-an/anb-python-components/wiki',
34
- 'Source': 'https://gitflic.ru/project/babaev-an/anb-python-components',
35
- 'Tracker': 'https://gitflic.ru/project/babaev-an/anb-python-components/issue?page=0',
36
- },
37
- zip_safe = False,
38
- )