python-datamodel 0.6.28__cp313-cp313-win_amd64.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.
datamodel/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ # -*- coding: utf-8 -*-
2
+ """DataModels.
3
+
4
+ DataModel is a reimplementation of dataclasses with true inheritance and composition.
5
+ """
6
+ from datamodel.fields import Field, Column, fields
7
+ from .models import Model
8
+ from .base import BaseModel
9
+ from .version import (
10
+ __title__, __description__, __version__, __author__, __author_email__
11
+ )
12
+
13
+ __all__ = ('fields', 'Field', 'Column', 'Model', 'BaseModel', )
datamodel/abstract.py ADDED
@@ -0,0 +1,189 @@
1
+ import logging
2
+ from typing import Optional, Union, Any
3
+ from collections.abc import Callable
4
+ from collections import OrderedDict
5
+ import types
6
+ from dataclasses import dataclass
7
+ from .parsers.json import JSONContent
8
+ from .fields import Field
9
+
10
+ class Meta:
11
+ """
12
+ Metadata information about Model.
13
+ """
14
+ name: str = ""
15
+ description: str = ""
16
+ schema: str = ""
17
+ app_label: str = ""
18
+ frozen: bool = False
19
+ strict: bool = True
20
+ driver: str = None
21
+ credentials: dict = Optional[dict]
22
+ dsn: Optional[str] = None
23
+ datasource: Optional[str] = None
24
+ connection: Optional[Callable] = None
25
+ remove_nulls: bool = False
26
+ endpoint: str = ""
27
+
28
+
29
+ def set_connection(cls, conn: Callable):
30
+ cls.connection = conn
31
+
32
+
33
+ def _dc_method_setattr_(
34
+ self,
35
+ name: str,
36
+ value: Any,
37
+ ) -> None:
38
+ """
39
+ _dc_method_setattr_.
40
+ Method for overwrite the "setattr" on Dataclasses.
41
+ """
42
+ # Initialize __values__ if it doesn't exist
43
+ if not hasattr(self, '__values__'):
44
+ object.__setattr__(self, '__values__', {})
45
+
46
+ # Check if the attribute is a field
47
+ if name in self.__fields__:
48
+ # Only store the initial value:
49
+ if name not in self.__values__:
50
+ # Store the initial value in __values__
51
+ self.__values__[name] = value
52
+
53
+ if self.Meta.frozen is True and name not in self.__fields__:
54
+ raise TypeError(
55
+ f"Cannot add New attribute {name} on {self.modelName}, "
56
+ "This DataClass is frozen (read-only class)"
57
+ )
58
+ else:
59
+ value = None if callable(value) else value
60
+ object.__setattr__(self, name, value)
61
+ if name == '__values__':
62
+ return
63
+ if name not in self.__fields__:
64
+ if self.Meta.strict is True:
65
+ return False
66
+ try:
67
+ # create a new Field on Model.
68
+ f = Field(required=False, default=value)
69
+ f.name = name
70
+ f.type = type(value)
71
+ self.__columns__[name] = f
72
+ self.__fields__.append(name)
73
+ setattr(self, name, value)
74
+ except Exception as err:
75
+ logging.exception(err, stack_info=True)
76
+ raise
77
+
78
+
79
+ def create_dataclass(
80
+ new_cls: Union[object, Any],
81
+ strict: bool = False,
82
+ frozen: bool = False
83
+ ) -> Callable:
84
+ """
85
+ create_dataclass.
86
+ Create a Dataclass from a simple Class.
87
+ """
88
+ dc = dataclass(
89
+ unsafe_hash=strict,
90
+ repr=False,
91
+ init=True,
92
+ order=False,
93
+ eq=True,
94
+ frozen=frozen
95
+ )(new_cls)
96
+ dc.__values__: dict = {}
97
+ # adding a properly internal json encoder:
98
+ dc.__encoder__: Any = JSONContent
99
+ dc.__valid__: bool = False
100
+ dc.__errors__: Union[list, None] = None
101
+ dc.__frozen__: bool = strict
102
+ dc.__initialised__: bool = False
103
+ setattr(dc, "__setattr__", _dc_method_setattr_)
104
+ dc.modelName = dc.__name__
105
+ return dc
106
+
107
+
108
+ class ModelMeta(type):
109
+
110
+ def __new__(cls, name, bases, attrs, **kwargs):
111
+ cols = OrderedDict()
112
+ strict = False
113
+ if "__annotations__" in attrs:
114
+ annotations = attrs.get('__annotations__', {})
115
+ try:
116
+ strict = attrs['Meta'].strict
117
+ except (TypeError, AttributeError, KeyError):
118
+ pass
119
+
120
+ @staticmethod
121
+ def _initialize_fields(attrs, annotations, strict):
122
+ cols = OrderedDict()
123
+ for field, _type in annotations.items():
124
+ if isinstance(_type, Field):
125
+ _type = _type.type
126
+ df = attrs.get(
127
+ field,
128
+ Field(type=_type, required=False, default=None)
129
+ )
130
+ if not isinstance(df, Field):
131
+ df = Field(required=False, type=_type, default=df)
132
+ df.name = field
133
+ df.type = _type
134
+ cols[field] = df
135
+ attrs[field] = df
136
+ return cols
137
+
138
+ cols = _initialize_fields(attrs, annotations, strict)
139
+ _columns = cols.keys()
140
+ cls.__slots__ = tuple(_columns)
141
+ attr_meta = attrs.pop("Meta", None)
142
+ new_cls = super().__new__(cls, name, bases, attrs, **kwargs)
143
+ new_cls.Meta = attr_meta or getattr(new_cls, "Meta", Meta)
144
+ new_cls.__dataclass_fields__ = cols
145
+ if not new_cls.Meta:
146
+ new_cls.Meta = Meta
147
+ new_cls.Meta.set_connection = types.MethodType(
148
+ set_connection, new_cls.Meta
149
+ )
150
+ try:
151
+ frozen = new_cls.Meta.frozen
152
+ except AttributeError:
153
+ new_cls.Meta.frozen = False
154
+ frozen = False
155
+ # mix values from Meta to an existing Meta Class
156
+ new_cls.Meta.__annotations__ = Meta.__annotations__
157
+ for key, _ in Meta.__annotations__.items():
158
+ if not hasattr(new_cls.Meta, key):
159
+ try:
160
+ setattr(new_cls.Meta, key, None)
161
+ except AttributeError as e:
162
+ logging.warning(
163
+ f'Missing Meta Key: {key}, {e}'
164
+ )
165
+ # adding a "class init method"
166
+ try:
167
+ new_cls.__model_init__(
168
+ new_cls,
169
+ name,
170
+ attrs
171
+ )
172
+ except AttributeError:
173
+ pass
174
+
175
+ # Create the dataclass once
176
+ dc = create_dataclass(
177
+ new_cls,
178
+ strict=new_cls.Meta.strict,
179
+ frozen=frozen
180
+ )
181
+ dc.__columns__ = cols
182
+ dc.__fields__ = list(_columns)
183
+ return dc
184
+
185
+ def __init__(cls, *args, **kwargs) -> None:
186
+ # Initialized Data Model = True
187
+ cls.__initialised__ = True
188
+ cls.__errors__ = None
189
+ super().__init__(*args, **kwargs)