omlish 0.0.0.dev284__py3-none-any.whl → 0.0.0.dev285__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.
Files changed (103) hide show
  1. omlish/__about__.py +2 -2
  2. omlish/dataclasses/__init__.py +58 -60
  3. omlish/dataclasses/api/__init__.py +25 -0
  4. omlish/dataclasses/api/classes/__init__.py +0 -0
  5. omlish/dataclasses/api/classes/conversion.py +30 -0
  6. omlish/dataclasses/api/classes/decorator.py +145 -0
  7. omlish/dataclasses/api/classes/make.py +109 -0
  8. omlish/dataclasses/api/classes/metadata.py +133 -0
  9. omlish/dataclasses/api/classes/params.py +78 -0
  10. omlish/dataclasses/api/fields/__init__.py +0 -0
  11. omlish/dataclasses/api/fields/building.py +120 -0
  12. omlish/dataclasses/api/fields/constructor.py +56 -0
  13. omlish/dataclasses/api/fields/conversion.py +191 -0
  14. omlish/dataclasses/api/fields/metadata.py +94 -0
  15. omlish/dataclasses/concerns/__init__.py +17 -0
  16. omlish/dataclasses/concerns/abc.py +15 -0
  17. omlish/dataclasses/concerns/copy.py +63 -0
  18. omlish/dataclasses/concerns/doc.py +53 -0
  19. omlish/dataclasses/concerns/eq.py +60 -0
  20. omlish/dataclasses/concerns/fields.py +119 -0
  21. omlish/dataclasses/concerns/frozen.py +133 -0
  22. omlish/dataclasses/concerns/hash.py +165 -0
  23. omlish/dataclasses/concerns/init.py +453 -0
  24. omlish/dataclasses/concerns/matchargs.py +27 -0
  25. omlish/dataclasses/concerns/mro.py +16 -0
  26. omlish/dataclasses/concerns/order.py +87 -0
  27. omlish/dataclasses/concerns/override.py +98 -0
  28. omlish/dataclasses/concerns/params.py +14 -0
  29. omlish/dataclasses/concerns/replace.py +48 -0
  30. omlish/dataclasses/concerns/repr.py +95 -0
  31. omlish/dataclasses/{impl → concerns}/slots.py +25 -1
  32. omlish/dataclasses/debug.py +2 -0
  33. omlish/dataclasses/errors.py +115 -0
  34. omlish/dataclasses/generation/__init__.py +0 -0
  35. omlish/dataclasses/generation/base.py +38 -0
  36. omlish/dataclasses/generation/compilation.py +258 -0
  37. omlish/dataclasses/generation/execution.py +195 -0
  38. omlish/dataclasses/generation/globals.py +83 -0
  39. omlish/dataclasses/generation/idents.py +6 -0
  40. omlish/dataclasses/generation/mangling.py +18 -0
  41. omlish/dataclasses/generation/manifests.py +20 -0
  42. omlish/dataclasses/generation/ops.py +97 -0
  43. omlish/dataclasses/generation/plans.py +35 -0
  44. omlish/dataclasses/generation/processor.py +174 -0
  45. omlish/dataclasses/generation/registry.py +42 -0
  46. omlish/dataclasses/generation/utils.py +83 -0
  47. omlish/dataclasses/{impl/reflect.py → inspect.py} +53 -90
  48. omlish/dataclasses/{impl/internals.py → internals.py} +26 -32
  49. omlish/dataclasses/metaclass/__init__.py +0 -0
  50. omlish/dataclasses/metaclass/bases.py +69 -0
  51. omlish/dataclasses/metaclass/confer.py +65 -0
  52. omlish/dataclasses/metaclass/meta.py +115 -0
  53. omlish/dataclasses/metaclass/specs.py +38 -0
  54. omlish/dataclasses/processing/__init__.py +0 -0
  55. omlish/dataclasses/processing/base.py +83 -0
  56. omlish/dataclasses/processing/driving.py +45 -0
  57. omlish/dataclasses/processing/priority.py +13 -0
  58. omlish/dataclasses/processing/registry.py +81 -0
  59. omlish/dataclasses/reflection.py +81 -0
  60. omlish/dataclasses/specs.py +224 -0
  61. omlish/dataclasses/tools/__init__.py +0 -0
  62. omlish/dataclasses/{impl → tools}/as_.py +23 -8
  63. omlish/dataclasses/tools/iter.py +27 -0
  64. omlish/dataclasses/tools/modifiers.py +52 -0
  65. omlish/dataclasses/tools/replace.py +17 -0
  66. omlish/dataclasses/tools/repr.py +12 -0
  67. omlish/dataclasses/{static.py → tools/static.py} +25 -4
  68. omlish/dataclasses/utils.py +54 -109
  69. omlish/diag/__init__.py +4 -4
  70. omlish/inject/impl/origins.py +1 -1
  71. omlish/lang/cached/function.py +4 -2
  72. omlish/marshal/objects/dataclasses.py +3 -7
  73. omlish/marshal/objects/helpers.py +3 -3
  74. omlish/secrets/marshal.py +1 -1
  75. omlish/secrets/secrets.py +1 -1
  76. omlish/sql/queries/base.py +1 -1
  77. omlish/typedvalues/marshal.py +2 -2
  78. {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev285.dist-info}/METADATA +1 -1
  79. {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev285.dist-info}/RECORD +83 -43
  80. omlish/dataclasses/impl/LICENSE +0 -279
  81. omlish/dataclasses/impl/__init__.py +0 -33
  82. omlish/dataclasses/impl/api.py +0 -278
  83. omlish/dataclasses/impl/copy.py +0 -30
  84. omlish/dataclasses/impl/errors.py +0 -53
  85. omlish/dataclasses/impl/fields.py +0 -245
  86. omlish/dataclasses/impl/frozen.py +0 -93
  87. omlish/dataclasses/impl/hashing.py +0 -86
  88. omlish/dataclasses/impl/init.py +0 -199
  89. omlish/dataclasses/impl/main.py +0 -93
  90. omlish/dataclasses/impl/metaclass.py +0 -235
  91. omlish/dataclasses/impl/metadata.py +0 -75
  92. omlish/dataclasses/impl/order.py +0 -57
  93. omlish/dataclasses/impl/overrides.py +0 -53
  94. omlish/dataclasses/impl/params.py +0 -128
  95. omlish/dataclasses/impl/processing.py +0 -24
  96. omlish/dataclasses/impl/replace.py +0 -40
  97. omlish/dataclasses/impl/repr.py +0 -66
  98. omlish/dataclasses/impl/simple.py +0 -50
  99. omlish/dataclasses/impl/utils.py +0 -167
  100. {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev285.dist-info}/WHEEL +0 -0
  101. {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev285.dist-info}/entry_points.txt +0 -0
  102. {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev285.dist-info}/licenses/LICENSE +0 -0
  103. {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev285.dist-info}/top_level.txt +0 -0
@@ -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
@@ -1,93 +0,0 @@
1
- import dataclasses as dc
2
- import typing as ta
3
-
4
- from ... import lang
5
- from .internals import FIELDS_ATTR
6
- from .internals import PARAMS_ATTR
7
- from .processing import Processor
8
- from .reflect import ClassInfo
9
- from .utils import Namespace
10
- from .utils import create_fn
11
- from .utils import set_new_attribute
12
-
13
-
14
- if ta.TYPE_CHECKING:
15
- from . import metaclass
16
- else:
17
- metaclass = lang.proxy_import('.metaclass', __package__)
18
-
19
-
20
- def check_frozen_bases(info: ClassInfo) -> None:
21
- mc_base = getattr(metaclass, 'Data', None)
22
- all_frozen_bases = None
23
- any_frozen_base = False
24
- has_dataclass_bases = False
25
- for b in info.cls.__mro__[-1:0:-1]:
26
- if b is mc_base:
27
- continue
28
- base_fields = getattr(b, FIELDS_ATTR, None)
29
- if base_fields is not None:
30
- has_dataclass_bases = True
31
- if all_frozen_bases is None:
32
- all_frozen_bases = True
33
- current_frozen = getattr(b, PARAMS_ATTR).frozen
34
- all_frozen_bases = all_frozen_bases and current_frozen
35
- any_frozen_base = any_frozen_base or current_frozen
36
-
37
- if has_dataclass_bases:
38
- if any_frozen_base and not info.params.frozen:
39
- raise TypeError('cannot inherit non-frozen dataclass from a frozen one')
40
-
41
- if all_frozen_bases is False and info.params.frozen:
42
- raise TypeError('cannot inherit frozen dataclass from a non-frozen one')
43
-
44
-
45
- def frozen_get_del_attr(
46
- cls: type,
47
- fields: ta.Sequence[dc.Field],
48
- globals: Namespace, # noqa
49
- ) -> tuple[ta.Callable, ta.Callable]:
50
- locals = { # noqa
51
- 'cls': cls,
52
- 'FrozenInstanceError': dc.FrozenInstanceError,
53
- }
54
- condition = 'type(self) is cls'
55
- if fields:
56
- condition += ' or name in {' + ', '.join(repr(f.name) for f in fields) + '}'
57
- return (
58
- create_fn(
59
- '__setattr__',
60
- ('self', 'name', 'value'),
61
- [
62
- f'if {condition}:',
63
- ' raise FrozenInstanceError(f"cannot assign to field {name!r}")',
64
- f'super(cls, self).__setattr__(name, value)',
65
- ],
66
- locals=locals,
67
- globals=globals,
68
- ),
69
- create_fn(
70
- '__delattr__',
71
- ('self', 'name'),
72
- [
73
- f'if {condition}:',
74
- ' raise FrozenInstanceError(f"cannot delete field {name!r}")',
75
- f'super(cls, self).__delattr__(name)',
76
- ],
77
- locals=locals,
78
- globals=globals,
79
- ),
80
- )
81
-
82
-
83
- class FrozenProcessor(Processor):
84
- def check(self) -> None:
85
- check_frozen_bases(self._info)
86
-
87
- def _process(self) -> None:
88
- if not self._info.params.frozen:
89
- return
90
-
91
- for fn in frozen_get_del_attr(self._cls, self._info.instance_fields, self._info.globals):
92
- if set_new_attribute(self._cls, fn.__name__, fn):
93
- raise TypeError(f'Cannot overwrite attribute {fn.__name__} in class {self._cls.__name__}')
@@ -1,86 +0,0 @@
1
- import dataclasses as dc
2
- import enum
3
- import typing as ta
4
-
5
- from .processing import Processor
6
- from .utils import create_fn
7
- from .utils import set_qualname
8
- from .utils import tuple_str
9
-
10
-
11
- class HashAction(enum.Enum):
12
- SET_NONE = enum.auto()
13
- ADD = enum.auto()
14
- EXCEPTION = enum.auto()
15
-
16
-
17
- # See https://bugs.python.org/issue32929#msg312829 for an if-statement version of this table.
18
- HASH_ACTIONS: ta.Mapping[tuple[bool, bool, bool, bool], HashAction | None] = {
19
- #
20
- # +-------------------------------------- unsafe_hash?
21
- # | +------------------------------- eq?
22
- # | | +------------------------ frozen?
23
- # | | | +---------------- has-explicit-hash?
24
- # v v v v
25
- (False, False, False, False): None,
26
- (False, False, False, True): None,
27
- (False, False, True, False): None,
28
- (False, False, True, True): None,
29
- (False, True, False, False): HashAction.SET_NONE,
30
- (False, True, False, True): None,
31
- (False, True, True, False): HashAction.ADD,
32
- (False, True, True, True): None,
33
- (True, False, False, False): HashAction.ADD,
34
- (True, False, False, True): HashAction.EXCEPTION,
35
- (True, False, True, False): HashAction.ADD,
36
- (True, False, True, True): HashAction.EXCEPTION,
37
- (True, True, False, False): HashAction.ADD,
38
- (True, True, False, True): HashAction.EXCEPTION,
39
- (True, True, True, False): HashAction.ADD,
40
- (True, True, True, True): HashAction.EXCEPTION,
41
- }
42
-
43
-
44
- class HashProcessor(Processor):
45
- CACHED_HASH_ATTR = '__dataclass_hash__'
46
-
47
- def _build_hash_fn(self) -> ta.Callable:
48
- flds = [f for f in self._info.instance_fields if (f.compare if f.hash is None else f.hash)]
49
- self_tuple = tuple_str('self', flds)
50
- if self._info.params_extras.cache_hash:
51
- body = [
52
- f'try: return self.{self.CACHED_HASH_ATTR}',
53
- f'except AttributeError: pass',
54
- f'object.__setattr__(self, {self.CACHED_HASH_ATTR!r}, h := hash({self_tuple}))',
55
- f'return h',
56
- ]
57
- else:
58
- body = [f'return hash({self_tuple})']
59
- hash_fn = create_fn(
60
- '__hash__',
61
- ('self',),
62
- body,
63
- globals=self._info.globals,
64
- )
65
- return set_qualname(self._cls, hash_fn) # noqa
66
-
67
- def _process(self) -> None:
68
- class_hash = self._cls.__dict__.get('__hash__', dc.MISSING)
69
- has_explicit_hash = not (class_hash is dc.MISSING or (class_hash is None and '__eq__' in self._cls.__dict__))
70
-
71
- match (hash_action := HASH_ACTIONS[(
72
- bool(self._info.params.unsafe_hash),
73
- bool(self._info.params.eq),
74
- bool(self._info.params.frozen),
75
- has_explicit_hash,
76
- )]):
77
- case HashAction.SET_NONE:
78
- self._cls.__hash__ = None # type: ignore
79
- case HashAction.ADD:
80
- self._cls.__hash__ = self._build_hash_fn() # type: ignore
81
- case HashAction.EXCEPTION:
82
- raise TypeError(f'Cannot overwrite attribute __hash__ in class {self._cls.__name__}')
83
- case None:
84
- pass
85
- case _:
86
- raise ValueError(hash_action)
@@ -1,199 +0,0 @@
1
- import dataclasses as dc
2
- import inspect
3
- import typing as ta
4
-
5
- from ... import lang
6
- from .errors import ValidationError
7
- from .fields import field_init
8
- from .fields import field_type
9
- from .fields import has_default
10
- from .fields import raise_field_validation_error
11
- from .internals import HAS_DEFAULT_FACTORY
12
- from .internals import POST_INIT_NAME
13
- from .internals import FieldType
14
- from .metadata import Init
15
- from .metadata import Validate
16
- from .processing import Processor
17
- from .reflect import ClassInfo
18
- from .utils import Namespace
19
- from .utils import create_fn
20
- from .utils import set_new_attribute
21
-
22
-
23
- MISSING = dc.MISSING
24
-
25
-
26
- ##
27
-
28
-
29
- def raise_validation_error(
30
- obj: ta.Any,
31
- fn: ta.Callable,
32
- ) -> ta.NoReturn:
33
- raise ValidationError(obj, fn)
34
-
35
-
36
- ##
37
-
38
-
39
- class InitFields(ta.NamedTuple):
40
- all: ta.Sequence[dc.Field]
41
- ordered: ta.Sequence[dc.Field]
42
- std: ta.Sequence[dc.Field]
43
- kw_only: ta.Sequence[dc.Field]
44
-
45
-
46
- def get_init_fields(fields: ta.Iterable[dc.Field], *, reorder: bool = False) -> InitFields:
47
- all_init_fields = [f for f in fields if field_type(f) in (FieldType.INSTANCE, FieldType.INIT)]
48
- ordered_init_fields = list(all_init_fields)
49
- if reorder:
50
- ordered_init_fields.sort(key=lambda f: (has_default(f), not f.kw_only))
51
- std_init_fields, kw_only_init_fields = (
52
- tuple(f1 for f1 in ordered_init_fields if f1.init and not f1.kw_only),
53
- tuple(f1 for f1 in ordered_init_fields if f1.init and f1.kw_only),
54
- )
55
- return InitFields(
56
- all=all_init_fields,
57
- ordered=ordered_init_fields,
58
- std=std_init_fields,
59
- kw_only=kw_only_init_fields,
60
- )
61
-
62
-
63
- def init_param(f: dc.Field) -> str:
64
- if not has_default(f):
65
- default = ''
66
- elif f.default is not MISSING:
67
- default = f' = __dataclass_dflt_{f.name}__'
68
- elif f.default_factory is not MISSING:
69
- default = ' = __dataclass_HAS_DEFAULT_FACTORY__'
70
- return f'{f.name}: __dataclass_type_{f.name}__{default}' # noqa
71
-
72
-
73
- ##
74
-
75
-
76
- class InitBuilder:
77
- def __init__(
78
- self,
79
- info: ClassInfo,
80
- fields: ta.Mapping[str, dc.Field],
81
- has_post_init: bool,
82
- self_name: str,
83
- globals: Namespace, # noqa
84
- ) -> None:
85
- super().__init__()
86
-
87
- self._info = info
88
- self._fields = fields
89
- self._has_post_init = has_post_init
90
- self._self_name = self_name
91
- self._globals = globals
92
-
93
- @lang.cached_function
94
- def build(self) -> ta.Callable:
95
- ifs = get_init_fields(self._fields.values(), reorder=self._info.params_extras.reorder)
96
-
97
- seen_default = None
98
- for f in ifs.std:
99
- if f.init:
100
- if has_default(f):
101
- seen_default = f
102
- elif seen_default:
103
- raise TypeError(f'non-default argument {f.name!r} follows default argument {seen_default.name!r}')
104
-
105
- locals: dict[str, ta.Any] = {} # noqa
106
-
107
- if self._info.params_extras.generic_init:
108
- get_fty = lambda f: self._info.generic_replaced_field_annotations[f.name]
109
- else:
110
- get_fty = lambda f: f.type
111
- locals.update({f'__dataclass_type_{f.name}__': get_fty(f) for f in ifs.all})
112
-
113
- locals.update({
114
- '__dataclass_HAS_DEFAULT_FACTORY__': HAS_DEFAULT_FACTORY,
115
- '__dataclass_builtins_object__': object,
116
- '__dataclass_builtins_isinstance__': isinstance,
117
- '__dataclass_builtins_TypeError__': TypeError,
118
- '__dataclass_raise_validation_error__': raise_validation_error,
119
- '__dataclass_raise_field_validation_error__': raise_field_validation_error,
120
- })
121
-
122
- body_lines: list[str] = []
123
- for f in ifs.all:
124
- f_lines = field_init(
125
- f,
126
- self._info.params.frozen,
127
- locals,
128
- self._self_name,
129
- self._info.params.slots,
130
- self._info.params_extras.override,
131
- )
132
-
133
- if f_lines:
134
- body_lines.extend(f_lines)
135
-
136
- if self._has_post_init:
137
- params_str = ','.join(f.name for f in ifs.all if field_type(f) is FieldType.INIT)
138
- body_lines.append(f'{self._self_name}.{POST_INIT_NAME}({params_str})')
139
-
140
- for i, fn in enumerate(self._info.merged_metadata.get(Validate, [])):
141
- if isinstance(fn, staticmethod):
142
- fn = fn.__func__
143
- cn = f'__dataclass_validate_{i}__'
144
- locals[cn] = fn
145
- csig = inspect.signature(fn)
146
- cas = ', '.join(p.name for p in csig.parameters.values())
147
- body_lines.append(f'if not {cn}({cas}): __dataclass_raise_validation_error__({self._self_name}, {cn})')
148
-
149
- inits = self._info.merged_metadata.get(Init, [])
150
- mro_dct = lang.mro_dict(self._info.cls)
151
- mro_v_ids = set(map(id, mro_dct.values()))
152
- props_by_fget_id = {id(v.fget): v for v in mro_dct.values() if isinstance(v, property) and v.fget is not None}
153
- for i, obj in enumerate(inits):
154
- if (obj_id := id(obj)) not in mro_v_ids and obj_id in props_by_fget_id:
155
- obj = props_by_fget_id[obj_id].__get__
156
- elif isinstance(obj, property):
157
- obj = obj.__get__
158
- cn = f'__dataclass_init_{i}__'
159
- locals[cn] = obj
160
- body_lines.append(f'{cn}({self._self_name})')
161
-
162
- if not body_lines:
163
- body_lines = ['pass']
164
-
165
- _init_params = [init_param(f) for f in ifs.std]
166
- if ifs.kw_only:
167
- _init_params += ['*']
168
- _init_params += [init_param(f) for f in ifs.kw_only]
169
-
170
- return create_fn(
171
- '__init__',
172
- [self._self_name, *_init_params],
173
- body_lines,
174
- locals=locals,
175
- globals=self._globals,
176
- return_type=lang.just(None),
177
- )
178
-
179
-
180
- class InitProcessor(Processor):
181
- def _process(self) -> None:
182
- if not self._info.params.init:
183
- return
184
-
185
- has_post_init = hasattr(self._cls, POST_INIT_NAME)
186
- self_name = '__dataclass_self__' if 'self' in self._info.fields else 'self'
187
-
188
- init = InitBuilder(
189
- ClassInfo(self._cls),
190
- self._info.fields,
191
- has_post_init,
192
- self_name,
193
- self._info.globals,
194
- ).build()
195
- set_new_attribute(
196
- self._cls,
197
- '__init__',
198
- init,
199
- )