omlish 0.0.0.dev284__py3-none-any.whl → 0.0.0.dev286__py3-none-any.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.
- omlish/__about__.py +6 -2
- omlish/dataclasses/__init__.py +58 -60
- omlish/dataclasses/api/__init__.py +25 -0
- omlish/dataclasses/api/classes/__init__.py +0 -0
- omlish/dataclasses/api/classes/conversion.py +30 -0
- omlish/dataclasses/api/classes/decorator.py +145 -0
- omlish/dataclasses/api/classes/make.py +109 -0
- omlish/dataclasses/api/classes/metadata.py +133 -0
- omlish/dataclasses/api/classes/params.py +78 -0
- omlish/dataclasses/api/fields/__init__.py +0 -0
- omlish/dataclasses/api/fields/building.py +120 -0
- omlish/dataclasses/api/fields/constructor.py +56 -0
- omlish/dataclasses/api/fields/conversion.py +191 -0
- omlish/dataclasses/api/fields/metadata.py +94 -0
- omlish/dataclasses/concerns/__init__.py +17 -0
- omlish/dataclasses/concerns/abc.py +15 -0
- omlish/dataclasses/concerns/copy.py +63 -0
- omlish/dataclasses/concerns/doc.py +53 -0
- omlish/dataclasses/concerns/eq.py +60 -0
- omlish/dataclasses/concerns/fields.py +119 -0
- omlish/dataclasses/concerns/frozen.py +133 -0
- omlish/dataclasses/concerns/hash.py +165 -0
- omlish/dataclasses/concerns/init.py +453 -0
- omlish/dataclasses/concerns/matchargs.py +27 -0
- omlish/dataclasses/concerns/mro.py +16 -0
- omlish/dataclasses/concerns/order.py +87 -0
- omlish/dataclasses/concerns/override.py +98 -0
- omlish/dataclasses/concerns/params.py +14 -0
- omlish/dataclasses/concerns/replace.py +48 -0
- omlish/dataclasses/concerns/repr.py +95 -0
- omlish/dataclasses/{impl → concerns}/slots.py +25 -1
- omlish/dataclasses/debug.py +2 -0
- omlish/dataclasses/errors.py +115 -0
- omlish/dataclasses/generation/__init__.py +0 -0
- omlish/dataclasses/generation/base.py +38 -0
- omlish/dataclasses/generation/compilation.py +258 -0
- omlish/dataclasses/generation/execution.py +195 -0
- omlish/dataclasses/generation/globals.py +83 -0
- omlish/dataclasses/generation/idents.py +6 -0
- omlish/dataclasses/generation/mangling.py +18 -0
- omlish/dataclasses/generation/manifests.py +20 -0
- omlish/dataclasses/generation/ops.py +97 -0
- omlish/dataclasses/generation/plans.py +35 -0
- omlish/dataclasses/generation/processor.py +179 -0
- omlish/dataclasses/generation/registry.py +42 -0
- omlish/dataclasses/generation/utils.py +83 -0
- omlish/dataclasses/{impl/reflect.py → inspect.py} +53 -90
- omlish/dataclasses/{impl/internals.py → internals.py} +26 -32
- omlish/dataclasses/metaclass/__init__.py +0 -0
- omlish/dataclasses/metaclass/bases.py +69 -0
- omlish/dataclasses/metaclass/confer.py +65 -0
- omlish/dataclasses/metaclass/meta.py +115 -0
- omlish/dataclasses/metaclass/specs.py +38 -0
- omlish/dataclasses/processing/__init__.py +0 -0
- omlish/dataclasses/processing/base.py +83 -0
- omlish/dataclasses/processing/driving.py +49 -0
- omlish/dataclasses/processing/priority.py +13 -0
- omlish/dataclasses/processing/registry.py +81 -0
- omlish/dataclasses/reflection.py +81 -0
- omlish/dataclasses/specs.py +224 -0
- omlish/dataclasses/tools/__init__.py +0 -0
- omlish/dataclasses/{impl → tools}/as_.py +23 -8
- omlish/dataclasses/tools/iter.py +27 -0
- omlish/dataclasses/tools/modifiers.py +52 -0
- omlish/dataclasses/tools/replace.py +17 -0
- omlish/dataclasses/tools/repr.py +12 -0
- omlish/dataclasses/{static.py → tools/static.py} +25 -4
- omlish/dataclasses/utils.py +54 -109
- omlish/diag/__init__.py +4 -4
- omlish/inject/impl/origins.py +1 -1
- omlish/lang/cached/function.py +4 -2
- omlish/lang/maybes.py +17 -0
- omlish/lite/maybes.py +17 -0
- omlish/marshal/objects/dataclasses.py +3 -7
- omlish/marshal/objects/helpers.py +3 -3
- omlish/secrets/marshal.py +1 -1
- omlish/secrets/secrets.py +1 -1
- omlish/sql/queries/base.py +1 -1
- omlish/text/minja.py +81 -25
- omlish/text/templating.py +116 -0
- omlish/typedvalues/marshal.py +2 -2
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/METADATA +4 -1
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/RECORD +87 -46
- omlish/dataclasses/impl/LICENSE +0 -279
- omlish/dataclasses/impl/__init__.py +0 -33
- omlish/dataclasses/impl/api.py +0 -278
- omlish/dataclasses/impl/copy.py +0 -30
- omlish/dataclasses/impl/errors.py +0 -53
- omlish/dataclasses/impl/fields.py +0 -245
- omlish/dataclasses/impl/frozen.py +0 -93
- omlish/dataclasses/impl/hashing.py +0 -86
- omlish/dataclasses/impl/init.py +0 -199
- omlish/dataclasses/impl/main.py +0 -93
- omlish/dataclasses/impl/metaclass.py +0 -235
- omlish/dataclasses/impl/metadata.py +0 -75
- omlish/dataclasses/impl/order.py +0 -57
- omlish/dataclasses/impl/overrides.py +0 -53
- omlish/dataclasses/impl/params.py +0 -128
- omlish/dataclasses/impl/processing.py +0 -24
- omlish/dataclasses/impl/replace.py +0 -40
- omlish/dataclasses/impl/repr.py +0 -66
- omlish/dataclasses/impl/simple.py +0 -50
- omlish/dataclasses/impl/utils.py +0 -167
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/top_level.txt +0 -0
@@ -1,33 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
TODO:
|
3
|
-
- metaclass:
|
4
|
-
- cleanup confer
|
5
|
-
- descriptors - check_type/validators don't handle setters lol
|
6
|
-
- deep_frozen?
|
7
|
-
- field:
|
8
|
-
- frozen
|
9
|
-
- pickle/transient
|
10
|
-
- mangled
|
11
|
-
- doc
|
12
|
-
- derive
|
13
|
-
- check_type
|
14
|
-
- class
|
15
|
-
- strict_eq
|
16
|
-
- allow_setattr
|
17
|
-
- mangler
|
18
|
-
- observable
|
19
|
-
- c/py gen
|
20
|
-
- iterable
|
21
|
-
- proto/jsonschema gen
|
22
|
-
- enums
|
23
|
-
- nodal
|
24
|
-
- embedding? forward kwargs in general? or only for replace?
|
25
|
-
|
26
|
-
TODO refs:
|
27
|
-
- batch up exec calls
|
28
|
-
- https://github.com/python/cpython/commit/8945b7ff55b87d11c747af2dad0e3e4d631e62d6
|
29
|
-
- add doc parameter to dataclasses.field
|
30
|
-
- https://github.com/python/cpython/commit/9c7657f09914254724683d91177aed7947637be5
|
31
|
-
- add decorator argument to make_dataclass
|
32
|
-
- https://github.com/python/cpython/commit/3e3a4d231518f91ff2f3c5a085b3849e32f1d548
|
33
|
-
"""
|
omlish/dataclasses/impl/api.py
DELETED
@@ -1,278 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
TODO:
|
3
|
-
- fix code redundancy
|
4
|
-
"""
|
5
|
-
import collections.abc
|
6
|
-
import contextlib
|
7
|
-
import dataclasses as dc
|
8
|
-
import keyword
|
9
|
-
import sys
|
10
|
-
import types
|
11
|
-
import typing as ta
|
12
|
-
|
13
|
-
from ... import check as check_
|
14
|
-
from ... import lang
|
15
|
-
from .internals import PARAMS_ATTR
|
16
|
-
from .internals import Params
|
17
|
-
from .main import process_class
|
18
|
-
from .metadata import METADATA_ATTR
|
19
|
-
from .metadata import Metadata
|
20
|
-
from .params import FieldExtras
|
21
|
-
from .params import ParamsExtras
|
22
|
-
|
23
|
-
|
24
|
-
MISSING = dc.MISSING
|
25
|
-
|
26
|
-
|
27
|
-
def field( # noqa
|
28
|
-
default=MISSING,
|
29
|
-
*,
|
30
|
-
default_factory=MISSING,
|
31
|
-
init=True,
|
32
|
-
repr=True, # noqa
|
33
|
-
hash=None, # noqa
|
34
|
-
compare=True,
|
35
|
-
metadata=None,
|
36
|
-
kw_only=MISSING,
|
37
|
-
|
38
|
-
derive: ta.Callable[..., ta.Any] | None = None,
|
39
|
-
coerce: bool | ta.Callable[[ta.Any], ta.Any] | None = None,
|
40
|
-
validate: ta.Callable[[ta.Any], bool] | None = None,
|
41
|
-
check_type: bool | type | tuple[type | None, ...] | None = None,
|
42
|
-
override: bool = False,
|
43
|
-
repr_fn: ta.Callable[[ta.Any], str | None] | None = None,
|
44
|
-
repr_priority: int | None = None,
|
45
|
-
frozen: bool | None = None,
|
46
|
-
): # -> dc.Field
|
47
|
-
if default is not MISSING and default_factory is not MISSING:
|
48
|
-
raise ValueError('cannot specify both default and default_factory')
|
49
|
-
|
50
|
-
fx = FieldExtras()
|
51
|
-
if metadata is not None:
|
52
|
-
fx = metadata.get(FieldExtras, fx)
|
53
|
-
|
54
|
-
fx = dc.replace(fx, **lang.opt_kw(
|
55
|
-
derive=derive,
|
56
|
-
coerce=coerce,
|
57
|
-
validate=validate,
|
58
|
-
check_type=check_type,
|
59
|
-
override=override,
|
60
|
-
repr_fn=repr_fn,
|
61
|
-
repr_priority=repr_priority,
|
62
|
-
frozen=frozen,
|
63
|
-
))
|
64
|
-
|
65
|
-
md: ta.Mapping = {FieldExtras: fx}
|
66
|
-
if metadata is not None:
|
67
|
-
md = collections.ChainMap(md, check_.isinstance(metadata, collections.abc.Mapping)) # type: ignore
|
68
|
-
|
69
|
-
return dc.Field(
|
70
|
-
default,
|
71
|
-
default_factory, # noqa
|
72
|
-
init,
|
73
|
-
repr,
|
74
|
-
hash,
|
75
|
-
compare,
|
76
|
-
types.MappingProxyType(md),
|
77
|
-
kw_only, # noqa
|
78
|
-
)
|
79
|
-
|
80
|
-
|
81
|
-
def _strip_missing_values(d):
|
82
|
-
return {k: v for k, v in d.items() if v is not MISSING}
|
83
|
-
|
84
|
-
|
85
|
-
def dataclass( # noqa
|
86
|
-
cls=None,
|
87
|
-
/,
|
88
|
-
*,
|
89
|
-
init=True,
|
90
|
-
repr=True, # noqa
|
91
|
-
eq=True,
|
92
|
-
order=False,
|
93
|
-
unsafe_hash=False,
|
94
|
-
frozen=False,
|
95
|
-
match_args=True,
|
96
|
-
kw_only=False,
|
97
|
-
slots=False,
|
98
|
-
weakref_slot=False,
|
99
|
-
|
100
|
-
metadata=None,
|
101
|
-
|
102
|
-
reorder=MISSING,
|
103
|
-
cache_hash=MISSING,
|
104
|
-
generic_init=MISSING,
|
105
|
-
override=MISSING,
|
106
|
-
repr_id=MISSING,
|
107
|
-
):
|
108
|
-
def wrap(cls):
|
109
|
-
pkw = dict(
|
110
|
-
init=init,
|
111
|
-
repr=repr,
|
112
|
-
eq=eq,
|
113
|
-
order=order,
|
114
|
-
unsafe_hash=unsafe_hash,
|
115
|
-
frozen=frozen,
|
116
|
-
match_args=match_args,
|
117
|
-
kw_only=kw_only,
|
118
|
-
slots=slots,
|
119
|
-
weakref_slot=weakref_slot,
|
120
|
-
)
|
121
|
-
|
122
|
-
dmd = cls.__dict__.get(METADATA_ATTR)
|
123
|
-
|
124
|
-
epk = dict(dmd.get(_ExtraParamsKwargs, ()) if dmd is not None else ())
|
125
|
-
epk.update(_strip_missing_values(dict(
|
126
|
-
reorder=reorder,
|
127
|
-
cache_hash=cache_hash,
|
128
|
-
generic_init=generic_init,
|
129
|
-
override=override,
|
130
|
-
repr_id=repr_id,
|
131
|
-
)))
|
132
|
-
pex = ParamsExtras(**epk)
|
133
|
-
|
134
|
-
mmd: dict = {
|
135
|
-
ParamsExtras: pex,
|
136
|
-
}
|
137
|
-
|
138
|
-
md: Metadata = mmd
|
139
|
-
cmds = []
|
140
|
-
if metadata is not None:
|
141
|
-
cmds.append(check_.isinstance(metadata, collections.abc.Mapping))
|
142
|
-
if dmd is not None:
|
143
|
-
cmds.append(dmd)
|
144
|
-
if cmds:
|
145
|
-
md = collections.ChainMap(md, *cmds) # type: ignore
|
146
|
-
|
147
|
-
setattr(cls, PARAMS_ATTR, Params(**pkw))
|
148
|
-
setattr(cls, METADATA_ATTR, types.MappingProxyType(md))
|
149
|
-
|
150
|
-
return process_class(cls)
|
151
|
-
|
152
|
-
if cls is None:
|
153
|
-
return wrap
|
154
|
-
|
155
|
-
return wrap(cls)
|
156
|
-
|
157
|
-
|
158
|
-
def make_dataclass( # noqa
|
159
|
-
cls_name,
|
160
|
-
fields,
|
161
|
-
*,
|
162
|
-
bases=(),
|
163
|
-
namespace=None,
|
164
|
-
init=True,
|
165
|
-
repr=True, # noqa
|
166
|
-
eq=True,
|
167
|
-
order=False,
|
168
|
-
unsafe_hash=False,
|
169
|
-
frozen=False,
|
170
|
-
match_args=True,
|
171
|
-
kw_only=False,
|
172
|
-
slots=False,
|
173
|
-
weakref_slot=False,
|
174
|
-
module=None,
|
175
|
-
|
176
|
-
reorder=MISSING,
|
177
|
-
cache_hash=MISSING,
|
178
|
-
generic_init=MISSING,
|
179
|
-
override=MISSING,
|
180
|
-
repr_id=MISSING,
|
181
|
-
):
|
182
|
-
if namespace is None:
|
183
|
-
namespace = {}
|
184
|
-
|
185
|
-
seen = set()
|
186
|
-
annotations = {}
|
187
|
-
defaults = {}
|
188
|
-
for item in fields:
|
189
|
-
if isinstance(item, str):
|
190
|
-
name = item
|
191
|
-
tp = 'typing.Any'
|
192
|
-
elif len(item) == 2:
|
193
|
-
name, tp, = item
|
194
|
-
elif len(item) == 3:
|
195
|
-
name, tp, spec = item
|
196
|
-
defaults[name] = spec
|
197
|
-
else:
|
198
|
-
raise TypeError(f'Invalid field: {item!r}')
|
199
|
-
if not isinstance(name, str) or not name.isidentifier():
|
200
|
-
raise TypeError(f'Field names must be valid identifiers: {name!r}')
|
201
|
-
if keyword.iskeyword(name):
|
202
|
-
raise TypeError(f'Field names must not be keywords: {name!r}')
|
203
|
-
if name in seen:
|
204
|
-
raise TypeError(f'Field name duplicated: {name!r}')
|
205
|
-
|
206
|
-
seen.add(name)
|
207
|
-
annotations[name] = tp
|
208
|
-
|
209
|
-
def exec_body_callback(ns):
|
210
|
-
ns.update(namespace)
|
211
|
-
ns.update(defaults)
|
212
|
-
ns['__annotations__'] = annotations
|
213
|
-
|
214
|
-
cls = types.new_class(cls_name, bases, {}, exec_body_callback)
|
215
|
-
|
216
|
-
if module is None:
|
217
|
-
try:
|
218
|
-
module = sys._getframemodulename(1) or '__main__' # type: ignore # noqa
|
219
|
-
except AttributeError:
|
220
|
-
with contextlib.suppress(AttributeError, ValueError):
|
221
|
-
module = sys._getframe(1).f_globals.get('__name__', '__main__') # noqa
|
222
|
-
if module is not None:
|
223
|
-
cls.__module__ = module
|
224
|
-
|
225
|
-
return dataclass(
|
226
|
-
cls,
|
227
|
-
init=init,
|
228
|
-
repr=repr,
|
229
|
-
eq=eq,
|
230
|
-
order=order,
|
231
|
-
unsafe_hash=unsafe_hash,
|
232
|
-
frozen=frozen,
|
233
|
-
match_args=match_args,
|
234
|
-
kw_only=kw_only,
|
235
|
-
slots=slots,
|
236
|
-
weakref_slot=weakref_slot,
|
237
|
-
|
238
|
-
reorder=reorder,
|
239
|
-
cache_hash=cache_hash,
|
240
|
-
generic_init=generic_init,
|
241
|
-
override=override,
|
242
|
-
repr_id=repr_id,
|
243
|
-
)
|
244
|
-
|
245
|
-
|
246
|
-
class _ExtraParamsKwargs:
|
247
|
-
pass
|
248
|
-
|
249
|
-
|
250
|
-
def extra_class_params( # noqa
|
251
|
-
*,
|
252
|
-
reorder=MISSING,
|
253
|
-
cache_hash=MISSING,
|
254
|
-
generic_init=MISSING,
|
255
|
-
override=MISSING,
|
256
|
-
repr_id=MISSING,
|
257
|
-
):
|
258
|
-
def inner(cls):
|
259
|
-
if PARAMS_ATTR in cls.__dict__:
|
260
|
-
raise TypeError(cls)
|
261
|
-
try:
|
262
|
-
md = cls.__dict__[METADATA_ATTR]
|
263
|
-
except KeyError:
|
264
|
-
md = {}
|
265
|
-
setattr(cls, METADATA_ATTR, md)
|
266
|
-
if _ExtraParamsKwargs in md:
|
267
|
-
raise TypeError(cls)
|
268
|
-
|
269
|
-
md[_ExtraParamsKwargs] = _strip_missing_values(dict(
|
270
|
-
reorder=reorder,
|
271
|
-
cache_hash=cache_hash,
|
272
|
-
generic_init=generic_init,
|
273
|
-
override=override,
|
274
|
-
repr_id=repr_id,
|
275
|
-
))
|
276
|
-
|
277
|
-
return cls
|
278
|
-
return inner
|
omlish/dataclasses/impl/copy.py
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
TODO:
|
3
|
-
- __deepcopy__
|
4
|
-
"""
|
5
|
-
import dataclasses as dc
|
6
|
-
|
7
|
-
from .fields import field_type
|
8
|
-
from .internals import FIELDS_ATTR
|
9
|
-
from .internals import FieldType
|
10
|
-
from .processing import Processor
|
11
|
-
from .utils import set_new_attribute
|
12
|
-
|
13
|
-
|
14
|
-
MISSING = dc.MISSING
|
15
|
-
|
16
|
-
|
17
|
-
def _copy(obj):
|
18
|
-
kw = {}
|
19
|
-
|
20
|
-
for f in getattr(obj, FIELDS_ATTR).values():
|
21
|
-
if field_type(f) is FieldType.CLASS:
|
22
|
-
continue
|
23
|
-
kw[f.name] = getattr(obj, f.name)
|
24
|
-
|
25
|
-
return obj.__class__(**kw)
|
26
|
-
|
27
|
-
|
28
|
-
class CopyProcessor(Processor):
|
29
|
-
def _process(self) -> None:
|
30
|
-
set_new_attribute(self._cls, '__copy__', _copy)
|
@@ -1,53 +0,0 @@
|
|
1
|
-
import types
|
2
|
-
import typing as ta
|
3
|
-
|
4
|
-
|
5
|
-
##
|
6
|
-
|
7
|
-
|
8
|
-
class ValidationError(Exception):
|
9
|
-
pass
|
10
|
-
|
11
|
-
|
12
|
-
def _hands_off_repr(obj: ta.Any) -> str:
|
13
|
-
return f'{obj.__class__.__qualname__}@{hex(id(obj))[2:]}'
|
14
|
-
|
15
|
-
|
16
|
-
def _fn_repr(fn: ta.Callable) -> str:
|
17
|
-
if (co := getattr(fn, '__code__', None)) is None or not isinstance(co, types.CodeType):
|
18
|
-
return repr(fn)
|
19
|
-
|
20
|
-
if not (co_filename := co.co_filename):
|
21
|
-
return repr(fn)
|
22
|
-
|
23
|
-
return f'{fn!r} ({co_filename}:{co.co_firstlineno})'
|
24
|
-
|
25
|
-
|
26
|
-
class FieldValidationError(ValidationError):
|
27
|
-
def __init__(
|
28
|
-
self,
|
29
|
-
obj: ta.Any,
|
30
|
-
field: str,
|
31
|
-
fn: ta.Callable,
|
32
|
-
value: ta.Any,
|
33
|
-
) -> None:
|
34
|
-
super().__init__(
|
35
|
-
f'{self.__class__.__name__} '
|
36
|
-
f'for field {field!r} '
|
37
|
-
f'on object {_hands_off_repr(obj)} '
|
38
|
-
f'in validator {_fn_repr(fn)} '
|
39
|
-
f'with value {value!r}',
|
40
|
-
)
|
41
|
-
|
42
|
-
self.obj = obj
|
43
|
-
self.field = field
|
44
|
-
self.fn = fn
|
45
|
-
self.value = value
|
46
|
-
|
47
|
-
def __repr__(self) -> str:
|
48
|
-
return f'{self.__class__.__name__}({", ".join([
|
49
|
-
f"obj={_hands_off_repr(self.obj)}",
|
50
|
-
f"field={self.field!r}",
|
51
|
-
f"fn={_fn_repr(self.fn)}",
|
52
|
-
f"value={self.value!r}",
|
53
|
-
])})'
|
@@ -1,245 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
TODO:
|
3
|
-
- point validate / check exceptions to lambdas
|
4
|
-
"""
|
5
|
-
import dataclasses as dc
|
6
|
-
import types
|
7
|
-
import typing as ta
|
8
|
-
|
9
|
-
from ... import check as check_
|
10
|
-
from ... import lang
|
11
|
-
from .errors import FieldValidationError
|
12
|
-
from .internals import FIELDS_ATTR
|
13
|
-
from .internals import FieldType
|
14
|
-
from .internals import is_classvar
|
15
|
-
from .internals import is_initvar
|
16
|
-
from .internals import is_kw_only
|
17
|
-
from .params import get_field_extras
|
18
|
-
from .processing import Processor
|
19
|
-
|
20
|
-
|
21
|
-
if ta.TYPE_CHECKING:
|
22
|
-
from . import api
|
23
|
-
else:
|
24
|
-
api = lang.proxy_import('.api', __package__)
|
25
|
-
|
26
|
-
|
27
|
-
MISSING = dc.MISSING
|
28
|
-
|
29
|
-
|
30
|
-
##
|
31
|
-
|
32
|
-
|
33
|
-
def raise_field_validation_error(
|
34
|
-
obj: ta.Any,
|
35
|
-
field: str,
|
36
|
-
fn: ta.Callable,
|
37
|
-
value: ta.Any,
|
38
|
-
) -> ta.NoReturn:
|
39
|
-
raise FieldValidationError(
|
40
|
-
obj,
|
41
|
-
field,
|
42
|
-
fn,
|
43
|
-
value,
|
44
|
-
)
|
45
|
-
|
46
|
-
|
47
|
-
##
|
48
|
-
|
49
|
-
|
50
|
-
def field_type(f: dc.Field) -> FieldType:
|
51
|
-
if (ft := getattr(f, '_field_type')) is not None:
|
52
|
-
return FieldType(ft)
|
53
|
-
else:
|
54
|
-
return FieldType.INSTANCE
|
55
|
-
|
56
|
-
|
57
|
-
def has_default(f: dc.Field) -> bool:
|
58
|
-
return not (f.default is MISSING and f.default_factory is MISSING)
|
59
|
-
|
60
|
-
|
61
|
-
##
|
62
|
-
|
63
|
-
|
64
|
-
class FieldsProcessor(Processor):
|
65
|
-
def _process(self) -> None:
|
66
|
-
cls = self._info.cls
|
67
|
-
fields: dict[str, dc.Field] = {}
|
68
|
-
|
69
|
-
for b in cls.__mro__[-1:0:-1]:
|
70
|
-
base_fields = getattr(b, FIELDS_ATTR, None)
|
71
|
-
if base_fields is not None:
|
72
|
-
for f in base_fields.values():
|
73
|
-
fields[f.name] = f
|
74
|
-
|
75
|
-
cls_fields: list[dc.Field] = []
|
76
|
-
|
77
|
-
kw_only = self._info.params.kw_only
|
78
|
-
kw_only_seen = False
|
79
|
-
for name, ann in self._info.cls_annotations.items():
|
80
|
-
if is_kw_only(cls, ann):
|
81
|
-
if kw_only_seen:
|
82
|
-
raise TypeError(f'{name!r} is KW_ONLY, but KW_ONLY has already been specified')
|
83
|
-
kw_only_seen = True
|
84
|
-
kw_only = True
|
85
|
-
else:
|
86
|
-
cls_fields.append(preprocess_field(cls, name, ann, kw_only))
|
87
|
-
|
88
|
-
for f in cls_fields:
|
89
|
-
fields[f.name] = f
|
90
|
-
if isinstance(getattr(cls, f.name, None), dc.Field):
|
91
|
-
if f.default is MISSING:
|
92
|
-
delattr(cls, f.name)
|
93
|
-
else:
|
94
|
-
setattr(cls, f.name, f.default)
|
95
|
-
|
96
|
-
for name, value in cls.__dict__.items():
|
97
|
-
if isinstance(value, dc.Field) and name not in self._info.cls_annotations:
|
98
|
-
raise TypeError(f'{name!r} is a field but has no type annotation')
|
99
|
-
|
100
|
-
setattr(cls, FIELDS_ATTR, fields)
|
101
|
-
|
102
|
-
|
103
|
-
##
|
104
|
-
|
105
|
-
|
106
|
-
def preprocess_field(
|
107
|
-
cls: type,
|
108
|
-
a_name: str,
|
109
|
-
a_type: ta.Any,
|
110
|
-
default_kw_only: bool,
|
111
|
-
) -> dc.Field:
|
112
|
-
default = getattr(cls, a_name, MISSING)
|
113
|
-
if isinstance(default, dc.Field):
|
114
|
-
f = default
|
115
|
-
else:
|
116
|
-
if isinstance(default, types.MemberDescriptorType):
|
117
|
-
# This is a field in __slots__, so it has no default value.
|
118
|
-
default = MISSING
|
119
|
-
f = api.field(default=default)
|
120
|
-
|
121
|
-
f.name = a_name
|
122
|
-
f.type = a_type
|
123
|
-
|
124
|
-
ft = FieldType.INSTANCE
|
125
|
-
if is_classvar(cls, f.type):
|
126
|
-
ft = FieldType.CLASS
|
127
|
-
if is_initvar(cls, f.type):
|
128
|
-
ft = FieldType.INIT
|
129
|
-
if ft in (FieldType.CLASS, FieldType.INIT):
|
130
|
-
if f.default_factory is not MISSING:
|
131
|
-
raise TypeError(f'field {f.name} cannot have a default factory')
|
132
|
-
f._field_type = ft.value # type: ignore # noqa
|
133
|
-
|
134
|
-
if ft in (FieldType.INSTANCE, FieldType.INIT):
|
135
|
-
if f.kw_only is MISSING:
|
136
|
-
f.kw_only = default_kw_only
|
137
|
-
else:
|
138
|
-
check_.arg(ft is FieldType.CLASS)
|
139
|
-
if f.kw_only is not MISSING:
|
140
|
-
raise TypeError(f'field {f.name} is a ClassVar but specifies kw_only')
|
141
|
-
|
142
|
-
if ft is FieldType.INSTANCE and f.default is not MISSING and f.default.__class__.__hash__ is None:
|
143
|
-
raise ValueError(f'mutable default {type(f.default)} for field {f.name} is not allowed: use default_factory')
|
144
|
-
|
145
|
-
return f
|
146
|
-
|
147
|
-
|
148
|
-
def field_assign(
|
149
|
-
frozen: bool,
|
150
|
-
name: str,
|
151
|
-
value: ta.Any,
|
152
|
-
self_name: str,
|
153
|
-
override: bool,
|
154
|
-
) -> str:
|
155
|
-
if override:
|
156
|
-
return f'{self_name}.__dict__[{name!r}] = {value}'
|
157
|
-
if frozen:
|
158
|
-
return f'__dataclass_builtins_object__.__setattr__({self_name}, {name!r}, {value})'
|
159
|
-
return f'{self_name}.{name} = {value}'
|
160
|
-
|
161
|
-
|
162
|
-
def field_init(
|
163
|
-
f: dc.Field,
|
164
|
-
frozen: bool,
|
165
|
-
locals: dict[str, ta.Any], # noqa
|
166
|
-
self_name: str,
|
167
|
-
slots: bool,
|
168
|
-
cls_override: bool,
|
169
|
-
) -> ta.Sequence[str]:
|
170
|
-
default_name = f'__dataclass_dflt_{f.name}__'
|
171
|
-
fx = get_field_extras(f)
|
172
|
-
|
173
|
-
lines = []
|
174
|
-
|
175
|
-
value: str | None = None
|
176
|
-
if f.default_factory is not MISSING:
|
177
|
-
locals[default_name] = f.default_factory
|
178
|
-
if f.init:
|
179
|
-
lines.append(f'if {f.name} is __dataclass_HAS_DEFAULT_FACTORY__: {f.name} = {default_name}()')
|
180
|
-
else:
|
181
|
-
lines.append(f'{f.name} = {default_name}()')
|
182
|
-
value = f.name
|
183
|
-
|
184
|
-
elif f.init:
|
185
|
-
if f.default is MISSING:
|
186
|
-
value = f.name
|
187
|
-
elif f.default is not MISSING:
|
188
|
-
locals[default_name] = f.default # Not referenced her, just useful / consistent to have in function scope
|
189
|
-
value = f.name
|
190
|
-
|
191
|
-
elif slots and f.default is not MISSING:
|
192
|
-
locals[default_name] = f.default
|
193
|
-
lines.append(f'{f.name} = {default_name}')
|
194
|
-
value = default_name
|
195
|
-
|
196
|
-
else:
|
197
|
-
pass
|
198
|
-
|
199
|
-
if fx.derive is not None:
|
200
|
-
raise NotImplementedError
|
201
|
-
|
202
|
-
if fx.frozen:
|
203
|
-
raise NotImplementedError
|
204
|
-
|
205
|
-
if fx.coerce is not None:
|
206
|
-
cn = f'__dataclass_coerce__{f.name}__'
|
207
|
-
locals[cn] = fx.coerce
|
208
|
-
lines.append(f'{value} = {cn}({value})')
|
209
|
-
|
210
|
-
if fx.validate is not None:
|
211
|
-
cn = f'__dataclass_validate__{f.name}__'
|
212
|
-
locals[cn] = fx.validate
|
213
|
-
lines.append(
|
214
|
-
f'if not {cn}({value}): '
|
215
|
-
f'__dataclass_raise_field_validation_error__({self_name}, {f.name!r}, {cn}, {value})',
|
216
|
-
)
|
217
|
-
|
218
|
-
if fx.check_type:
|
219
|
-
cn = f'__dataclass_check_type__{f.name}__'
|
220
|
-
ct: ta.Any
|
221
|
-
if isinstance(fx.check_type, tuple):
|
222
|
-
ct = tuple(type(None) if e is None else check_.isinstance(e, type) for e in fx.check_type)
|
223
|
-
elif isinstance(fx.check_type, (type, tuple)):
|
224
|
-
ct = fx.check_type
|
225
|
-
# FIXME:
|
226
|
-
# elif info.params_extras.generic_init:
|
227
|
-
# ct = info.generic_replaced_field_annotations[f.name]
|
228
|
-
else:
|
229
|
-
ct = f.type
|
230
|
-
locals[cn] = ct
|
231
|
-
lines.append(
|
232
|
-
f'if not __dataclass_builtins_isinstance__({value}, {cn}): '
|
233
|
-
f'raise __dataclass_builtins_TypeError__({value}, {cn})',
|
234
|
-
)
|
235
|
-
|
236
|
-
if value is not None and field_type(f) is not FieldType.INIT:
|
237
|
-
lines.append(field_assign(
|
238
|
-
frozen,
|
239
|
-
f.name,
|
240
|
-
value,
|
241
|
-
self_name,
|
242
|
-
fx.override or cls_override,
|
243
|
-
))
|
244
|
-
|
245
|
-
return lines
|