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 +13 -0
- datamodel/abstract.py +189 -0
- datamodel/base.py +639 -0
- datamodel/converters.c +25347 -0
- datamodel/converters.cp313-win_amd64.pyd +0 -0
- datamodel/exceptions.c +13079 -0
- datamodel/exceptions.cp313-win_amd64.pyd +0 -0
- datamodel/fields.cp313-win_amd64.pyd +0 -0
- datamodel/fields.cpp +16655 -0
- datamodel/libs/__init__.py +0 -0
- datamodel/libs/mapping.c +14796 -0
- datamodel/libs/mapping.cp313-win_amd64.pyd +0 -0
- datamodel/libs/mutables.py +116 -0
- datamodel/models.py +111 -0
- datamodel/parsers/__init__.py +0 -0
- datamodel/parsers/encoders.py +15 -0
- datamodel/parsers/json.cp313-win_amd64.pyd +0 -0
- datamodel/parsers/json.cpp +12976 -0
- datamodel/profiler.py +21 -0
- datamodel/types.c +7137 -0
- datamodel/types.cp313-win_amd64.pyd +0 -0
- datamodel/validation.cp313-win_amd64.pyd +0 -0
- datamodel/validation.cpp +13275 -0
- datamodel/version.py +10 -0
- python_datamodel-0.6.28.dist-info/LICENSE +29 -0
- python_datamodel-0.6.28.dist-info/METADATA +316 -0
- python_datamodel-0.6.28.dist-info/RECORD +29 -0
- python_datamodel-0.6.28.dist-info/WHEEL +5 -0
- python_datamodel-0.6.28.dist-info/top_level.txt +1 -0
|
Binary file
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from collections.abc import MutableMapping, Iterator, Iterable
|
|
3
|
+
from typing import Any, Optional, Union
|
|
4
|
+
from datamodel import Field
|
|
5
|
+
|
|
6
|
+
if sys.version_info < (3, 10):
|
|
7
|
+
from typing_extensions import ParamSpec
|
|
8
|
+
else:
|
|
9
|
+
from typing import ParamSpec
|
|
10
|
+
P = ParamSpec("P")
|
|
11
|
+
|
|
12
|
+
class ClassDict(dict, MutableMapping):
|
|
13
|
+
"""ClassDict.
|
|
14
|
+
|
|
15
|
+
Mapping that works like both a simple Dictionary or a Mutable Object.
|
|
16
|
+
"""
|
|
17
|
+
def __init__(self, *args: P.args, data: Optional[Union[tuple, dict]] = None, default: Any = None, **kwargs: P.kwargs):
|
|
18
|
+
self._columns: list = []
|
|
19
|
+
self.mapping = {}
|
|
20
|
+
self.default = default
|
|
21
|
+
self.mapping.update(*args, **kwargs)
|
|
22
|
+
self.update(data, **kwargs)
|
|
23
|
+
|
|
24
|
+
def update(self, items: Optional[Iterable] = None, **kwargs): # pylint: disable=W0221
|
|
25
|
+
if isinstance(items, dict):
|
|
26
|
+
for key, value in items.items():
|
|
27
|
+
# self.mapping[key] = value
|
|
28
|
+
self.mapping[key] = value
|
|
29
|
+
else:
|
|
30
|
+
for k,v in kwargs.items():
|
|
31
|
+
attr = getattr(self, k)
|
|
32
|
+
if isinstance(attr, Field):
|
|
33
|
+
try:
|
|
34
|
+
fn = attr.default
|
|
35
|
+
if callable(fn):
|
|
36
|
+
v = fn(v)
|
|
37
|
+
setattr(self, k, v)
|
|
38
|
+
else:
|
|
39
|
+
setattr(self, k, fn)
|
|
40
|
+
except KeyError:
|
|
41
|
+
pass
|
|
42
|
+
# self.mapping[k] = v
|
|
43
|
+
self.mapping[k] = v
|
|
44
|
+
self._columns = list(self.mapping.keys())
|
|
45
|
+
|
|
46
|
+
def __missing__(self, key):
|
|
47
|
+
return self.default
|
|
48
|
+
|
|
49
|
+
def items(self) -> zip: # type: ignore
|
|
50
|
+
return zip(self._columns, self.mapping)
|
|
51
|
+
|
|
52
|
+
def keys(self) -> list:
|
|
53
|
+
return self._columns
|
|
54
|
+
|
|
55
|
+
def set(self, key, value) -> None:
|
|
56
|
+
self.mapping[key] = value
|
|
57
|
+
if not key in self._columns:
|
|
58
|
+
self._columns.append(key)
|
|
59
|
+
|
|
60
|
+
### Section: Simple magic methods
|
|
61
|
+
def __len__(self) -> int:
|
|
62
|
+
return len(self.mapping)
|
|
63
|
+
|
|
64
|
+
def __str__(self) -> str:
|
|
65
|
+
return f"<{type(self).__name__}({self.mapping})>"
|
|
66
|
+
|
|
67
|
+
def __repr__(self) -> str:
|
|
68
|
+
return f"<{type(self).__name__}({self.mapping})>"
|
|
69
|
+
|
|
70
|
+
def __contains__(self, key: str) -> bool:
|
|
71
|
+
return key in self._columns
|
|
72
|
+
|
|
73
|
+
def __delitem__(self, key) -> None:
|
|
74
|
+
value = self[key]
|
|
75
|
+
del self.mapping[key]
|
|
76
|
+
self._columns.remove(key)
|
|
77
|
+
self.pop(value, None)
|
|
78
|
+
|
|
79
|
+
def __delattr__(self, name: str) -> None:
|
|
80
|
+
value = self[name]
|
|
81
|
+
del self.mapping[name]
|
|
82
|
+
self._columns.remove(name)
|
|
83
|
+
self.pop(value, None)
|
|
84
|
+
|
|
85
|
+
def __setitem__(self, key, value):
|
|
86
|
+
self.mapping[key] = value
|
|
87
|
+
if not key in self._columns:
|
|
88
|
+
self._columns.append(key)
|
|
89
|
+
|
|
90
|
+
def __getitem__(self, key: Union[str, int]) -> Any:
|
|
91
|
+
"""
|
|
92
|
+
Sequence-like operators
|
|
93
|
+
"""
|
|
94
|
+
try:
|
|
95
|
+
return self.mapping[key]
|
|
96
|
+
except (KeyError, TypeError):
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
def __getattr__(self, attr: str) -> Any:
|
|
100
|
+
"""
|
|
101
|
+
Attributes for dict keys
|
|
102
|
+
"""
|
|
103
|
+
try:
|
|
104
|
+
return self.__getitem__(attr)
|
|
105
|
+
except KeyError as ex:
|
|
106
|
+
raise KeyError(
|
|
107
|
+
f"User Error: invalid field name {attr} on {self.mapping!r}"
|
|
108
|
+
) from ex
|
|
109
|
+
except TypeError as ex:
|
|
110
|
+
raise TypeError(
|
|
111
|
+
f"User Error: invalid attribute value on {self.mapping!r} for {attr}"
|
|
112
|
+
) from ex
|
|
113
|
+
|
|
114
|
+
def __iter__(self) -> Iterator:
|
|
115
|
+
for value in self.mapping:
|
|
116
|
+
yield value
|
datamodel/models.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Any
|
|
3
|
+
# Dataclass
|
|
4
|
+
from dataclasses import asdict as as_dict
|
|
5
|
+
from operator import attrgetter
|
|
6
|
+
from datamodel.fields import fields
|
|
7
|
+
from .abstract import ModelMeta, Meta
|
|
8
|
+
from .fields import Field
|
|
9
|
+
|
|
10
|
+
class ModelMixin:
|
|
11
|
+
"""Interface for shared methods on Model classes.
|
|
12
|
+
"""
|
|
13
|
+
def __unicode__(self):
|
|
14
|
+
return str(__class__)
|
|
15
|
+
|
|
16
|
+
def columns(self):
|
|
17
|
+
return self.__columns__
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def get_columns(cls):
|
|
21
|
+
return cls.__columns__
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def get_column(cls, name: str) -> Field:
|
|
25
|
+
try:
|
|
26
|
+
return cls.__columns__[name]
|
|
27
|
+
except KeyError:
|
|
28
|
+
raise AttributeError(
|
|
29
|
+
f"{cls.__name__} has no column {name}"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def get_fields(self):
|
|
33
|
+
return self.__fields__
|
|
34
|
+
|
|
35
|
+
def __getitem__(self, item):
|
|
36
|
+
return getattr(self, item)
|
|
37
|
+
|
|
38
|
+
def reset_values(self):
|
|
39
|
+
try:
|
|
40
|
+
self.__values__ = {}
|
|
41
|
+
except AttributeError:
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
def old_value(self, name: str) -> Any:
|
|
45
|
+
"""
|
|
46
|
+
old_value.
|
|
47
|
+
Get the old value of an attribute.
|
|
48
|
+
Args:
|
|
49
|
+
name (str): name of the attribute.
|
|
50
|
+
Returns:
|
|
51
|
+
Any: value of the attribute.
|
|
52
|
+
"""
|
|
53
|
+
try:
|
|
54
|
+
return self.__values__[name]
|
|
55
|
+
except KeyError:
|
|
56
|
+
raise AttributeError(
|
|
57
|
+
f"{self.__class__.__name__} has no attribute {name}"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def column(self, name: str) -> Field:
|
|
61
|
+
return self.__columns__[name]
|
|
62
|
+
|
|
63
|
+
def __repr__(self) -> str:
|
|
64
|
+
f_repr = ", ".join(f"{f.name}={getattr(self, f.name)}" for f in fields(self))
|
|
65
|
+
return f"{self.__class__.__name__}({f_repr})"
|
|
66
|
+
|
|
67
|
+
def remove_nulls(self, obj: Any) -> dict[str, Any]:
|
|
68
|
+
"""Recursively removes any fields with None values from the given object."""
|
|
69
|
+
if isinstance(obj, list):
|
|
70
|
+
return [self.remove_nulls(item) for item in obj]
|
|
71
|
+
elif isinstance(obj, dict):
|
|
72
|
+
return {
|
|
73
|
+
key: self.remove_nulls(value) for key, value in obj.items()
|
|
74
|
+
if value is not None
|
|
75
|
+
}
|
|
76
|
+
else:
|
|
77
|
+
return obj
|
|
78
|
+
|
|
79
|
+
def to_dict(self):
|
|
80
|
+
if self.Meta.remove_nulls is True:
|
|
81
|
+
return self.remove_nulls(as_dict(self, dict_factory=dict))
|
|
82
|
+
return as_dict(self)
|
|
83
|
+
|
|
84
|
+
def json(self, **kwargs):
|
|
85
|
+
encoder = self.__encoder__(**kwargs)
|
|
86
|
+
return encoder(as_dict(self))
|
|
87
|
+
|
|
88
|
+
to_json = json
|
|
89
|
+
|
|
90
|
+
def is_valid(self) -> bool:
|
|
91
|
+
"""is_valid.
|
|
92
|
+
|
|
93
|
+
returns True when current Model is valid under datatype validations.
|
|
94
|
+
Returns:
|
|
95
|
+
bool: True if current model is valid.
|
|
96
|
+
"""
|
|
97
|
+
return bool(self.__valid__)
|
|
98
|
+
|
|
99
|
+
class Model(ModelMixin, metaclass=ModelMeta):
|
|
100
|
+
"""Model.
|
|
101
|
+
|
|
102
|
+
Basic dataclass-based Model.
|
|
103
|
+
"""
|
|
104
|
+
Meta = Meta
|
|
105
|
+
|
|
106
|
+
def __post_init__(self) -> None:
|
|
107
|
+
"""
|
|
108
|
+
Post init method.
|
|
109
|
+
Useful for making Post-validations of Model.
|
|
110
|
+
"""
|
|
111
|
+
self.__initialised__ = True
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
JSON Encoders.
|
|
3
|
+
"""
|
|
4
|
+
from .json import JSONContent, json_encoder
|
|
5
|
+
|
|
6
|
+
DefaultEncoder = JSONContent
|
|
7
|
+
|
|
8
|
+
class BaseEncoder:
|
|
9
|
+
"""
|
|
10
|
+
Encoder replacement for json.dumps using orjson
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, *args, **kwargs):
|
|
14
|
+
encoder = DefaultEncoder(*args, **kwargs)
|
|
15
|
+
self.encode = encoder.__call__
|
|
Binary file
|