python-datamodel 0.10.7__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.
Files changed (100) hide show
  1. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/PKG-INFO +1 -1
  2. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/abstract.py +171 -146
  3. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/base.py +4 -2
  4. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/converters.c +3294 -2424
  5. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/converters.html +2826 -2268
  6. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/converters.pyx +70 -17
  7. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/validation.cpp +5108 -3246
  8. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/validation.html +1722 -621
  9. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/validation.pyx +125 -4
  10. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/version.py +1 -1
  11. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/python_datamodel.egg-info/PKG-INFO +1 -1
  12. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/python_datamodel.egg-info/SOURCES.txt +1 -0
  13. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_inherit.py +21 -2
  14. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_tuples.py +20 -1
  15. python_datamodel-0.10.8/tests/test_unions.py +124 -0
  16. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_validations.py +9 -1
  17. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/CHANGELOG.md +0 -0
  18. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/CONTRIBUTING.md +0 -0
  19. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/LICENSE +0 -0
  20. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/MANIFEST.in +0 -0
  21. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/Makefile +0 -0
  22. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/README.md +0 -0
  23. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/SECURITY.md +0 -0
  24. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/__init__.py +0 -0
  25. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/adaptive/__init__.py +0 -0
  26. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/adaptive/models.py +0 -0
  27. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/aliases/__init__.py +0 -0
  28. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/exceptions.c +0 -0
  29. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/exceptions.html +0 -0
  30. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/exceptions.pxd +0 -0
  31. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/exceptions.pyx +0 -0
  32. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/fields.cpp +0 -0
  33. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/fields.html +0 -0
  34. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/fields.pyx +0 -0
  35. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/functions.cpp +0 -0
  36. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/functions.html +0 -0
  37. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/functions.pxd +0 -0
  38. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/functions.pyx +0 -0
  39. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/jsonld/__init__.py +0 -0
  40. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/jsonld/models.py +0 -0
  41. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/libs/__init__.py +0 -0
  42. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/libs/mapping.c +0 -0
  43. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/libs/mapping.html +0 -0
  44. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/libs/mapping.pxd +0 -0
  45. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/libs/mapping.pyx +0 -0
  46. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/libs/mutables.py +0 -0
  47. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/models.py +0 -0
  48. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/parsers/__init__.py +0 -0
  49. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/parsers/encoders.py +0 -0
  50. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/parsers/json.cpp +0 -0
  51. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/parsers/json.html +0 -0
  52. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/parsers/json.pyx +0 -0
  53. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/profiler.py +0 -0
  54. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/py.typed +0 -0
  55. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/rs_core/Cargo.toml +0 -0
  56. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/rs_core/src/lib.rs +0 -0
  57. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/rs_parsers/Cargo.toml +0 -0
  58. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/rs_parsers/src/lib.rs +0 -0
  59. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/rs_validators/Cargo.toml +0 -0
  60. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/rs_validators/src/lib.rs +0 -0
  61. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/typedefs/__init__.py +0 -0
  62. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/typedefs/singleton.c +0 -0
  63. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/typedefs/singleton.html +0 -0
  64. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/typedefs/singleton.pxd +0 -0
  65. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/typedefs/singleton.pyx +0 -0
  66. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/typedefs/types.c +0 -0
  67. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/typedefs/types.html +0 -0
  68. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/typedefs/types.pxd +0 -0
  69. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/typedefs/types.pyx +0 -0
  70. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/types.c +0 -0
  71. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/types.html +0 -0
  72. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/datamodel/types.pyx +0 -0
  73. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/pyproject.toml +0 -0
  74. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/python_datamodel.egg-info/dependency_links.txt +0 -0
  75. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/python_datamodel.egg-info/not-zip-safe +0 -0
  76. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/python_datamodel.egg-info/requires.txt +0 -0
  77. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/python_datamodel.egg-info/top_level.txt +0 -0
  78. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/setup.cfg +0 -0
  79. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/setup.py +0 -0
  80. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/__init__.py +0 -0
  81. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_aliases.py +0 -0
  82. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_classdict.py +0 -0
  83. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_converter.py +0 -0
  84. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_dashboards.py +0 -0
  85. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_data.py +0 -0
  86. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_descriptors.py +0 -0
  87. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_dict.py +0 -0
  88. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_field.py +0 -0
  89. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_json.py +0 -0
  90. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_method.py +0 -0
  91. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_model.py +0 -0
  92. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_primitives.py +0 -0
  93. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_qsdriver.py +0 -0
  94. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_qsmodel.py +0 -0
  95. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_qsobject.py +0 -0
  96. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_ticket.py +0 -0
  97. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_tickets.py +0 -0
  98. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_type_user.py +0 -0
  99. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_types.py +0 -0
  100. {python_datamodel-0.10.7 → python_datamodel-0.10.8}/tests/test_valid_callables.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-datamodel
3
- Version: 0.10.7
3
+ Version: 0.10.8
4
4
  Summary: simple library based on python +3.8 to use Dataclass-syntaxfor interacting with Data
5
5
  Home-page: https://github.com/phenobarbital/python-datamodel
6
6
  Author: Jesus Lara
@@ -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 to perform conversion/validation.
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
- def __new__(cls, name, bases, attrs, **kwargs): # noqa
159
+ @staticmethod
160
+ def _initialize_fields(attrs, annotations, strict):
157
161
  cols = OrderedDict()
158
- strict = False
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
- if "__annotations__" in attrs:
167
- annotations = attrs.get('__annotations__', {})
168
- with contextlib.suppress(TypeError, AttributeError, KeyError):
169
- strict = attrs['Meta'].strict
170
-
171
- @staticmethod
172
- def _initialize_fields(attrs, annotations, strict):
173
- cols = OrderedDict()
174
- _types_local = {}
175
- _typing_args = {}
176
- aliases = {}
177
- for field, _type in annotations.items():
178
- if isinstance(_type, InitVar) or _type == InitVar:
179
- # Skip InitVar fields;
180
- # they should not be part of the dataclass instance
181
- continue
182
- if isinstance(_type, NewType):
183
- # Get the corresponding type of the NewType.
184
- _type = _type.__supertype__
185
-
186
- origin = get_origin(_type)
187
- if origin is ClassVar:
188
- continue
189
-
190
- # Check if the field's default value is a descriptor
191
- default_value = attrs.get(field, None)
192
- is_descriptor = any(
193
- hasattr(default_value, method)
194
- for method in ("__get__", "__set__", "__delete__")
195
- )
196
- # Handle the descriptor field
197
- if is_descriptor:
198
- default_value._type_category = 'descriptor'
199
- cols[field] = default_value
200
- _types_local[field] = 'descriptor'
201
- continue
202
-
203
- if isinstance(_type, Field):
204
- _type = _type.type
205
- df = attrs.get(
206
- field,
207
- Field(type=_type, required=False, default=None)
208
- )
209
- if df is not None and isinstance(df, Field):
210
- alias = df.metadata.get("alias", None)
211
- if alias:
212
- aliases[alias] = field
213
- if not isinstance(df, Field):
214
- df = Field(required=False, type=_type, default=df)
215
- df.name = field
216
- df.type = _type
217
-
218
- # Cache reflection info so we DON’T need to call
219
- # get_origin/get_args repeatedly:
220
- args = get_args(_type)
221
- _default = df.default
222
- _is_dc = is_dataclass(_type)
223
- _is_prim = is_primitive(_type)
224
- _is_alias = isinstance(_type, GenericAlias)
225
- _is_typing = hasattr(_type, '__module__') and _type.__module__ == 'typing' # noqa
226
-
227
- # Store the type info in the field object:
228
- df.is_dc = _is_dc
229
- df.is_primitive = _is_prim
230
- df.is_typing = _is_typing
231
- df.origin = origin
232
- df.args = args
233
- df.type_args = getattr(_type, '__args__', None)
234
- df._default_callable = callable(_default)
235
- # Current Field have an Encoder Function.
236
- custom_encoder = df.metadata.get("encoder")
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.parser = encoders[_type]
251
+ df._encoder_fn = encoders[df._inner_type]
239
252
  except (TypeError, KeyError):
240
- df.parser = None
241
- if custom_encoder:
242
- df.parser = lambda value, _type=_type, encoder=custom_encoder: parse_basic(_type, value, encoder) # noqa
243
- # Caching Validator:
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.validator = validators[_type]
246
- except (KeyError, TypeError):
247
- df.validator = None
248
-
249
- # check type of field:
250
- if _is_prim:
251
- _type_category = 'primitive'
252
- elif origin == type:
253
- _type_category = 'type'
254
- elif _is_dc:
255
- _type_category = 'dataclass'
256
- elif _is_typing or _is_alias: # noqa
257
- if df.origin is not None and (df.origin is list and df.args):
258
- df._inner_targs = df.args
259
- df._inner_type = args[0]
260
- df._inner_origin = get_origin(df._inner_type)
261
- df._typing_args = get_args(df._inner_type)
262
- df._inner_is_dc = is_dataclass(df._inner_type)
263
- try:
264
- df._encoder_fn = encoders[df._inner_type]
265
- except (TypeError, KeyError):
266
- df._encoder_fn = None
267
- if origin is list:
268
- df._inner_targs = df.args
269
- df._inner_type = args[0]
270
- try:
271
- df._encoder_fn = encoders[df._inner_type]
272
- except (TypeError, KeyError):
273
- df._encoder_fn = None
274
- elif origin is Union:
275
- df._inner_targs = df.args
276
- df._inner_type = args[0]
277
- df._inner_is_dc = is_dataclass(df._inner_type)
278
- df._inner_priv = is_primitive(df._inner_type)
279
- df._inner_origin = get_origin(df._inner_type)
280
- df._typing_args = get_args(df._inner_type)
281
- _type_category = 'typing'
282
- elif isclass(_type):
283
- _type_category = 'class'
284
- # elif _is_alias:
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
- # if no __annotations__, cols is empty:
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
- try:
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
- f"""{self.modelName}: There are errors in Model. \
47
- Hint: please check the "payload" attribute in the exception.""",
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)