python-datamodel 0.10.6__tar.gz → 0.10.8__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.
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/PKG-INFO +1 -1
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/abstract.py +171 -146
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/base.py +4 -2
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/converters.c +3540 -2483
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/converters.html +3009 -2327
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/converters.pyx +76 -17
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/parsers/json.cpp +694 -536
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/parsers/json.html +519 -455
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/parsers/json.pyx +9 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/validation.cpp +5108 -3246
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/validation.html +1722 -621
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/validation.pyx +125 -4
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/version.py +1 -1
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/python_datamodel.egg-info/PKG-INFO +1 -1
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/python_datamodel.egg-info/SOURCES.txt +1 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_inherit.py +21 -2
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_tuples.py +20 -1
- python_datamodel-0.10.8/tests/test_unions.py +124 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_validations.py +9 -1
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/CHANGELOG.md +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/CONTRIBUTING.md +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/LICENSE +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/MANIFEST.in +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/Makefile +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/README.md +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/SECURITY.md +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/__init__.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/adaptive/__init__.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/adaptive/models.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/aliases/__init__.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/exceptions.c +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/exceptions.html +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/exceptions.pxd +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/exceptions.pyx +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/fields.cpp +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/fields.html +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/fields.pyx +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/functions.cpp +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/functions.html +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/functions.pxd +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/functions.pyx +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/jsonld/__init__.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/jsonld/models.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/libs/__init__.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/libs/mapping.c +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/libs/mapping.html +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/libs/mapping.pxd +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/libs/mapping.pyx +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/libs/mutables.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/models.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/parsers/__init__.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/parsers/encoders.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/profiler.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/py.typed +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/rs_core/Cargo.toml +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/rs_core/src/lib.rs +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/rs_parsers/Cargo.toml +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/rs_parsers/src/lib.rs +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/rs_validators/Cargo.toml +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/rs_validators/src/lib.rs +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/typedefs/__init__.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/typedefs/singleton.c +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/typedefs/singleton.html +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/typedefs/singleton.pxd +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/typedefs/singleton.pyx +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/typedefs/types.c +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/typedefs/types.html +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/typedefs/types.pxd +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/typedefs/types.pyx +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/types.c +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/types.html +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/datamodel/types.pyx +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/pyproject.toml +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/python_datamodel.egg-info/dependency_links.txt +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/python_datamodel.egg-info/not-zip-safe +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/python_datamodel.egg-info/requires.txt +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/python_datamodel.egg-info/top_level.txt +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/setup.cfg +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/setup.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/__init__.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_aliases.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_classdict.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_converter.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_dashboards.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_data.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_descriptors.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_dict.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_field.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_json.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_method.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_model.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_primitives.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_qsdriver.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_qsmodel.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_qsobject.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_ticket.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_tickets.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_type_user.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_types.py +0 -0
- {python_datamodel-0.10.6 → python_datamodel-0.10.8}/tests/test_valid_callables.py +0 -0
|
@@ -67,7 +67,8 @@ def _dc_method_setattr_(self, name: str, value: Any) -> None:
|
|
|
67
67
|
Simplified __setattr__ for dataclass-like objects.
|
|
68
68
|
|
|
69
69
|
This version separates the known-field assignment (with optional validation)
|
|
70
|
-
from the “extra field” assignment and uses a helper
|
|
70
|
+
from the “extra field” assignment and uses a helper
|
|
71
|
+
to perform conversion/validation.
|
|
71
72
|
"""
|
|
72
73
|
if (name.startswith('__') and name.endswith('__')):
|
|
73
74
|
# or check explicitly if name in ('__values__', '__valid__', ...)
|
|
@@ -152,157 +153,183 @@ class ModelMeta(type):
|
|
|
152
153
|
__fields__: List
|
|
153
154
|
__field_types__: List
|
|
154
155
|
__aliases__: Dict
|
|
156
|
+
# Class-level cache
|
|
157
|
+
_base_class_cache = {}
|
|
155
158
|
|
|
156
|
-
|
|
159
|
+
@staticmethod
|
|
160
|
+
def _initialize_fields(attrs, annotations, strict):
|
|
157
161
|
cols = OrderedDict()
|
|
158
|
-
|
|
159
|
-
cls.__field_types__ = {}
|
|
160
|
-
cls.__typing_args__ = {}
|
|
161
|
-
cls.__aliases__ = {}
|
|
162
|
-
_types = {}
|
|
162
|
+
_types_local = {}
|
|
163
163
|
_typing_args = {}
|
|
164
164
|
aliases = {}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
165
|
+
for field, _type in annotations.items():
|
|
166
|
+
if isinstance(_type, InitVar) or _type == InitVar:
|
|
167
|
+
# Skip InitVar fields;
|
|
168
|
+
# they should not be part of the dataclass instance
|
|
169
|
+
continue
|
|
170
|
+
if isinstance(_type, NewType):
|
|
171
|
+
# Get the corresponding type of the NewType.
|
|
172
|
+
_type = _type.__supertype__
|
|
173
|
+
|
|
174
|
+
origin = get_origin(_type)
|
|
175
|
+
if origin is ClassVar:
|
|
176
|
+
continue
|
|
177
|
+
|
|
178
|
+
# Check if the field's default value is a descriptor
|
|
179
|
+
default_value = attrs.get(field, None)
|
|
180
|
+
is_descriptor = any(
|
|
181
|
+
hasattr(default_value, method)
|
|
182
|
+
for method in ("__get__", "__set__", "__delete__")
|
|
183
|
+
)
|
|
184
|
+
# Handle the descriptor field
|
|
185
|
+
if is_descriptor:
|
|
186
|
+
default_value._type_category = 'descriptor'
|
|
187
|
+
cols[field] = default_value
|
|
188
|
+
_types_local[field] = 'descriptor'
|
|
189
|
+
continue
|
|
190
|
+
|
|
191
|
+
if isinstance(_type, Field):
|
|
192
|
+
_type = _type.type
|
|
193
|
+
df = attrs.get(
|
|
194
|
+
field,
|
|
195
|
+
Field(type=_type, required=False, default=None)
|
|
196
|
+
)
|
|
197
|
+
if df is not None and isinstance(df, Field):
|
|
198
|
+
if alias := df.metadata.get("alias", None):
|
|
199
|
+
aliases[alias] = field
|
|
200
|
+
if not isinstance(df, Field):
|
|
201
|
+
df = Field(required=False, type=_type, default=df)
|
|
202
|
+
df.name = field
|
|
203
|
+
df.type = _type
|
|
204
|
+
|
|
205
|
+
# Cache reflection info so we DON’T need to call
|
|
206
|
+
# get_origin/get_args repeatedly:
|
|
207
|
+
args = get_args(_type)
|
|
208
|
+
_default = df.default
|
|
209
|
+
_is_dc = is_dataclass(_type)
|
|
210
|
+
_is_prim = is_primitive(_type)
|
|
211
|
+
_is_alias = isinstance(_type, GenericAlias)
|
|
212
|
+
_is_typing = hasattr(_type, '__module__') and _type.__module__ == 'typing' # noqa
|
|
213
|
+
|
|
214
|
+
# Store the type info in the field object:
|
|
215
|
+
df.is_dc = _is_dc
|
|
216
|
+
df.is_primitive = _is_prim
|
|
217
|
+
df.is_typing = _is_typing
|
|
218
|
+
df.origin = origin
|
|
219
|
+
df.args = args
|
|
220
|
+
df.type_args = getattr(_type, '__args__', None)
|
|
221
|
+
df._default_callable = callable(_default)
|
|
222
|
+
# Current Field have an Encoder Function.
|
|
223
|
+
custom_encoder = df.metadata.get("encoder")
|
|
224
|
+
try:
|
|
225
|
+
df.parser = encoders[_type]
|
|
226
|
+
except (TypeError, KeyError):
|
|
227
|
+
df.parser = None
|
|
228
|
+
if custom_encoder:
|
|
229
|
+
df.parser = lambda value, _type=_type, encoder=custom_encoder: parse_basic(_type, value, encoder) # noqa
|
|
230
|
+
# Caching Validator:
|
|
231
|
+
try:
|
|
232
|
+
df.validator = validators[_type]
|
|
233
|
+
except (KeyError, TypeError):
|
|
234
|
+
df.validator = None
|
|
235
|
+
|
|
236
|
+
# check type of field:
|
|
237
|
+
if _is_prim:
|
|
238
|
+
_type_category = 'primitive'
|
|
239
|
+
elif origin == type:
|
|
240
|
+
_type_category = 'type'
|
|
241
|
+
elif _is_dc:
|
|
242
|
+
_type_category = 'dataclass'
|
|
243
|
+
elif _is_typing or _is_alias: # noqa
|
|
244
|
+
if df.origin is not None and (df.origin is list and df.args):
|
|
245
|
+
df._inner_targs = df.args
|
|
246
|
+
df._inner_type = args[0]
|
|
247
|
+
df._inner_origin = get_origin(df._inner_type)
|
|
248
|
+
df._typing_args = get_args(df._inner_type)
|
|
249
|
+
df._inner_is_dc = is_dataclass(df._inner_type)
|
|
237
250
|
try:
|
|
238
|
-
df.
|
|
251
|
+
df._encoder_fn = encoders[df._inner_type]
|
|
239
252
|
except (TypeError, KeyError):
|
|
240
|
-
df.
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
253
|
+
df._encoder_fn = None
|
|
254
|
+
if origin is list:
|
|
255
|
+
df._inner_targs = df.args
|
|
256
|
+
df._inner_type = args[0]
|
|
244
257
|
try:
|
|
245
|
-
df.
|
|
246
|
-
except (
|
|
247
|
-
df.
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
# _type_category = 'typing'
|
|
286
|
-
else:
|
|
287
|
-
# TODO: making parser for complex types
|
|
288
|
-
_type_category = 'complex'
|
|
289
|
-
_types_local[field] = _type_category
|
|
290
|
-
df._type_category = _type_category
|
|
291
|
-
|
|
292
|
-
# Store them in a dict keyed by field name:
|
|
293
|
-
_typing_args[field] = (origin, args)
|
|
294
|
-
# Assign the field object to the attrs so dataclass can pick it up
|
|
295
|
-
attrs[field] = df
|
|
296
|
-
cols[field] = df
|
|
297
|
-
return cols, _types_local, _typing_args, aliases
|
|
298
|
-
|
|
299
|
-
# Initialize the fields
|
|
300
|
-
cols, _types, _typing_args, aliases = _initialize_fields(
|
|
301
|
-
attrs, annotations, strict
|
|
302
|
-
)
|
|
258
|
+
df._encoder_fn = encoders[df._inner_type]
|
|
259
|
+
except (TypeError, KeyError):
|
|
260
|
+
df._encoder_fn = None
|
|
261
|
+
elif origin is Union:
|
|
262
|
+
df._inner_targs = df.args
|
|
263
|
+
df._inner_type = args[0]
|
|
264
|
+
df._inner_is_dc = is_dataclass(df._inner_type)
|
|
265
|
+
df._inner_priv = is_primitive(df._inner_type)
|
|
266
|
+
df._inner_origin = get_origin(df._inner_type)
|
|
267
|
+
df._typing_args = get_args(df._inner_type)
|
|
268
|
+
_type_category = 'typing'
|
|
269
|
+
elif isclass(_type):
|
|
270
|
+
_type_category = 'class'
|
|
271
|
+
# elif _is_alias:
|
|
272
|
+
# _type_category = 'typing'
|
|
273
|
+
else:
|
|
274
|
+
# TODO: making parser for complex types
|
|
275
|
+
_type_category = 'complex'
|
|
276
|
+
_types_local[field] = _type_category
|
|
277
|
+
df._type_category = _type_category
|
|
278
|
+
|
|
279
|
+
# Store them in a dict keyed by field name:
|
|
280
|
+
_typing_args[field] = (origin, args)
|
|
281
|
+
# Assign the field object to the attrs so dataclass can pick it up
|
|
282
|
+
attrs[field] = df
|
|
283
|
+
cols[field] = df
|
|
284
|
+
return cols, _types_local, _typing_args, aliases
|
|
285
|
+
|
|
286
|
+
def __new__(cls, name, bases, attrs, **kwargs): # noqa
|
|
287
|
+
annotations = attrs.get('__annotations__', {})
|
|
288
|
+
base_key = (tuple(bases), tuple(sorted(annotations.items())))
|
|
289
|
+
strict = getattr(attrs.get('Meta', Meta), 'strict', False)
|
|
290
|
+
|
|
291
|
+
if base_key in cls._base_class_cache:
|
|
292
|
+
# Check the Cache First:
|
|
293
|
+
cached = cls._base_class_cache[base_key]
|
|
294
|
+
cols = cached['cols'].copy()
|
|
295
|
+
_types = cached['types'].copy()
|
|
296
|
+
_typing_args = cached['_typing_args'].copy()
|
|
297
|
+
aliases = cached['aliases'].copy()
|
|
303
298
|
else:
|
|
304
|
-
#
|
|
299
|
+
# Compute field from Bases:
|
|
305
300
|
cols = OrderedDict()
|
|
301
|
+
_types = {}
|
|
302
|
+
_typing_args = {}
|
|
303
|
+
aliases = {}
|
|
304
|
+
|
|
305
|
+
# Step 1: Collect fields from parent classes
|
|
306
|
+
for base in bases:
|
|
307
|
+
cols.update(getattr(base, '__columns__', {}))
|
|
308
|
+
if hasattr(base, '__field_types__'):
|
|
309
|
+
_types |= base.__field_types__
|
|
310
|
+
if hasattr(base, '__typing_args__'):
|
|
311
|
+
_typing_args |= base.__typing_args__
|
|
312
|
+
if hasattr(base, '__aliases__'):
|
|
313
|
+
aliases |= base.__aliases__
|
|
314
|
+
|
|
315
|
+
# Now initialize subclass-specific fields
|
|
316
|
+
new_cols, new_types, new_typing_args, new_aliases = cls._initialize_fields(
|
|
317
|
+
attrs, annotations, strict
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
# Merge new fields with inherited fields
|
|
321
|
+
cols.update(new_cols)
|
|
322
|
+
_types.update(new_types)
|
|
323
|
+
_typing_args.update(new_typing_args)
|
|
324
|
+
aliases.update(new_aliases)
|
|
325
|
+
|
|
326
|
+
# Store computed results in cache
|
|
327
|
+
cls._base_class_cache[base_key] = {
|
|
328
|
+
'cols': cols.copy(),
|
|
329
|
+
'types': _types.copy(),
|
|
330
|
+
'_typing_args': _typing_args.copy(),
|
|
331
|
+
'aliases': aliases.copy(),
|
|
332
|
+
}
|
|
306
333
|
|
|
307
334
|
_columns = cols.keys()
|
|
308
335
|
cls.__slots__ = tuple(_columns)
|
|
@@ -339,14 +366,12 @@ class ModelMeta(type):
|
|
|
339
366
|
)
|
|
340
367
|
|
|
341
368
|
# If there's a __model_init__ method, call it
|
|
342
|
-
|
|
369
|
+
with contextlib.suppress(AttributeError):
|
|
343
370
|
new_cls.__model_init__(
|
|
344
371
|
new_cls,
|
|
345
372
|
name,
|
|
346
373
|
attrs
|
|
347
374
|
)
|
|
348
|
-
except AttributeError:
|
|
349
|
-
pass
|
|
350
375
|
|
|
351
376
|
# Now that fields are in attrs, decorate the class as a dataclass
|
|
352
377
|
dc = dataclass(
|
|
@@ -43,8 +43,10 @@ class BaseModel(ModelMixin, metaclass=ModelMeta):
|
|
|
43
43
|
if errors := processing_fields(self, columns):
|
|
44
44
|
if self.Meta.strict is True:
|
|
45
45
|
raise ValidationError(
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
(
|
|
47
|
+
f"{self.modelName}: There are errors in Model.\n"
|
|
48
|
+
"Hint: please check the 'payload' attribute in the exception."
|
|
49
|
+
),
|
|
48
50
|
payload=errors
|
|
49
51
|
)
|
|
50
52
|
object.__setattr__(self, '__errors__', errors)
|