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.
Files changed (107) hide show
  1. omlish/__about__.py +6 -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 +179 -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 +49 -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/lang/maybes.py +17 -0
  73. omlish/lite/maybes.py +17 -0
  74. omlish/marshal/objects/dataclasses.py +3 -7
  75. omlish/marshal/objects/helpers.py +3 -3
  76. omlish/secrets/marshal.py +1 -1
  77. omlish/secrets/secrets.py +1 -1
  78. omlish/sql/queries/base.py +1 -1
  79. omlish/text/minja.py +81 -25
  80. omlish/text/templating.py +116 -0
  81. omlish/typedvalues/marshal.py +2 -2
  82. {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/METADATA +4 -1
  83. {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/RECORD +87 -46
  84. omlish/dataclasses/impl/LICENSE +0 -279
  85. omlish/dataclasses/impl/__init__.py +0 -33
  86. omlish/dataclasses/impl/api.py +0 -278
  87. omlish/dataclasses/impl/copy.py +0 -30
  88. omlish/dataclasses/impl/errors.py +0 -53
  89. omlish/dataclasses/impl/fields.py +0 -245
  90. omlish/dataclasses/impl/frozen.py +0 -93
  91. omlish/dataclasses/impl/hashing.py +0 -86
  92. omlish/dataclasses/impl/init.py +0 -199
  93. omlish/dataclasses/impl/main.py +0 -93
  94. omlish/dataclasses/impl/metaclass.py +0 -235
  95. omlish/dataclasses/impl/metadata.py +0 -75
  96. omlish/dataclasses/impl/order.py +0 -57
  97. omlish/dataclasses/impl/overrides.py +0 -53
  98. omlish/dataclasses/impl/params.py +0 -128
  99. omlish/dataclasses/impl/processing.py +0 -24
  100. omlish/dataclasses/impl/replace.py +0 -40
  101. omlish/dataclasses/impl/repr.py +0 -66
  102. omlish/dataclasses/impl/simple.py +0 -50
  103. omlish/dataclasses/impl/utils.py +0 -167
  104. {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/WHEEL +0 -0
  105. {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/entry_points.txt +0 -0
  106. {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/licenses/LICENSE +0 -0
  107. {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,453 @@
1
+ """
2
+ TODO:
3
+ - ensure all 'init' fields work - non-instance
4
+ - special case 'None' default, most common
5
+ """
6
+ import dataclasses as dc
7
+ import itertools
8
+ import typing as ta
9
+
10
+ from ... import check
11
+ from ..generation.base import Generator
12
+ from ..generation.base import Plan
13
+ from ..generation.base import PlanResult
14
+ from ..generation.globals import FIELD_FN_VALIDATION_ERROR_GLOBAL
15
+ from ..generation.globals import FIELD_TYPE_VALIDATION_ERROR_GLOBAL
16
+ from ..generation.globals import FN_VALIDATION_ERROR_GLOBAL
17
+ from ..generation.globals import HAS_DEFAULT_FACTORY_GLOBAL
18
+ from ..generation.globals import ISINSTANCE_GLOBAL
19
+ from ..generation.globals import NONE_GLOBAL
20
+ from ..generation.idents import SELF_IDENT
21
+ from ..generation.ops import AddMethodOp
22
+ from ..generation.ops import Op
23
+ from ..generation.ops import OpRef
24
+ from ..generation.ops import Ref
25
+ from ..generation.registry import register_generator_type
26
+ from ..generation.utils import SetattrSrcBuilder
27
+ from ..inspect import FieldsInspection
28
+ from ..internals import STD_POST_INIT_NAME
29
+ from ..processing.base import ProcessingContext
30
+ from ..specs import CoerceFn
31
+ from ..specs import DefaultFactory
32
+ from ..specs import FieldSpec
33
+ from ..specs import FieldType
34
+ from ..specs import InitFn
35
+ from ..specs import ValidateFn
36
+ from .fields import InitFields
37
+ from .mro import MroDict
38
+
39
+
40
+ ##
41
+
42
+
43
+ @dc.dataclass(frozen=True, kw_only=True)
44
+ class InitPlan(Plan):
45
+ @dc.dataclass(frozen=True)
46
+ class Field:
47
+ name: str
48
+ annotation: OpRef[ta.Any]
49
+
50
+ default: OpRef[ta.Any] | None
51
+ default_factory: OpRef[ta.Any] | None
52
+
53
+ init: bool
54
+
55
+ override: bool
56
+
57
+ field_type: FieldType
58
+
59
+ coerce: bool | OpRef[CoerceFn] | None
60
+ validate: OpRef[ValidateFn] | None
61
+
62
+ check_type: OpRef[type | tuple[type, ...]] | None
63
+
64
+ fields: tuple[Field, ...]
65
+
66
+ self_param: str
67
+ std_params: tuple[str, ...]
68
+ kw_only_params: tuple[str, ...]
69
+
70
+ frozen: bool
71
+
72
+ slots: bool
73
+
74
+ post_init_params: tuple[str, ...] | None
75
+
76
+ init_fns: tuple[OpRef[InitFn], ...]
77
+
78
+ @dc.dataclass(frozen=True)
79
+ class ValidateFnWithParams:
80
+ fn: OpRef[ValidateFn]
81
+ params: tuple[str, ...]
82
+
83
+ validate_fns: ta.Sequence[ValidateFnWithParams] | None
84
+
85
+
86
+ @register_generator_type(InitPlan)
87
+ class InitGenerator(Generator[InitPlan]):
88
+ def _plan_field(
89
+ self,
90
+ ctx: ProcessingContext,
91
+ i: int,
92
+ f: FieldSpec,
93
+ ann: ta.Any,
94
+ ref_map: dict,
95
+ ) -> InitPlan.Field:
96
+ ann_ref: OpRef = OpRef(f'init.fields.{i}.annotation')
97
+ ref_map[ann_ref] = ann
98
+
99
+ default_ref: OpRef[ta.Any] | None = None
100
+ default_factory_ref: OpRef[ta.Any] | None = None
101
+ if f.default.present:
102
+ dfl = f.default.must()
103
+ if isinstance(dfl, DefaultFactory):
104
+ default_factory_ref = OpRef(f'init.fields.{i}.default_factory')
105
+ ref_map[default_factory_ref] = dfl.fn
106
+ else:
107
+ default_ref = OpRef(f'init.fields.{i}.default')
108
+ ref_map[default_ref] = dfl
109
+
110
+ coerce: bool | OpRef[CoerceFn] | None = None
111
+ if isinstance(f.coerce, bool):
112
+ coerce = f.coerce
113
+ elif f.coerce is not None:
114
+ coerce = OpRef(f'init.fields.{i}.coerce')
115
+ ref_map[coerce] = f.coerce
116
+
117
+ validate_ref: OpRef[ValidateFn] | None = None
118
+ if f.validate is not None:
119
+ validate_ref = OpRef(f'init.fields.{i}.validate')
120
+ ref_map[validate_ref] = f.validate
121
+
122
+ check_type_ref: OpRef[type | tuple[type, ...]] | None = None
123
+ if f.check_type is not None and f.check_type is not False:
124
+ check_type_arg: ta.Any
125
+ if isinstance(f.check_type, tuple):
126
+ check_type_arg = tuple(type(None) if e is None else check.isinstance(e, type) for e in f.check_type)
127
+ elif isinstance(f.check_type, type):
128
+ check_type_arg = f.check_type
129
+ elif f.check_type is True:
130
+ check_type_arg = f.annotation
131
+ else:
132
+ raise TypeError(f.check_type)
133
+ check_type_ref = OpRef(f'init.fields.{i}.check_type')
134
+ ref_map[check_type_ref] = check_type_arg
135
+
136
+ return InitPlan.Field(
137
+ name=f.name,
138
+ annotation=ann_ref,
139
+
140
+ default=default_ref,
141
+ default_factory=default_factory_ref,
142
+
143
+ init=f.init,
144
+
145
+ override=f.override or ctx.cs.override,
146
+
147
+ field_type=f.field_type,
148
+
149
+ coerce=coerce,
150
+ validate=validate_ref,
151
+
152
+ check_type=check_type_ref,
153
+ )
154
+
155
+ def plan(self, ctx: ProcessingContext) -> PlanResult[InitPlan] | None:
156
+ if '__init__' in ctx.cls.__dict__ or not ctx.cs.init:
157
+ return None
158
+
159
+ init_fields = ctx[InitFields]
160
+ seen_default = None
161
+ for f in init_fields.std:
162
+ if not f.init:
163
+ continue
164
+ if f.default.present:
165
+ seen_default = f
166
+ elif seen_default:
167
+ raise TypeError(f'non-default argument {f.name!r} follows default argument {seen_default.name!r}')
168
+
169
+ if ctx.cs.generic_init:
170
+ gr_field_anns = ctx[FieldsInspection].generic_replaced_field_annotations
171
+ get_field_ann = lambda f: gr_field_anns[f.name]
172
+ else:
173
+ get_field_ann = lambda f: f.annotation
174
+
175
+ ref_map: dict = {}
176
+
177
+ plan_fields: list[InitPlan.Field] = []
178
+ for i, f in enumerate(ctx.cs.fields):
179
+ plan_fields.append(self._plan_field(
180
+ ctx,
181
+ i,
182
+ f,
183
+ get_field_ann(f),
184
+ ref_map,
185
+ ))
186
+
187
+ mro_v_ids = set(map(id, ctx[MroDict].values()))
188
+ props_by_fget_id = {
189
+ id(v.fget): v
190
+ for v in ctx[MroDict].values()
191
+ if isinstance(v, property)
192
+ and v.fget is not None
193
+ }
194
+ init_fns: list[OpRef[InitFn]] = []
195
+ for i, init_fn in enumerate(ctx.cs.init_fns or []):
196
+ if (obj_id := id(init_fn)) not in mro_v_ids and obj_id in props_by_fget_id:
197
+ init_fn = props_by_fget_id[obj_id].__get__
198
+ elif isinstance(init_fn, property):
199
+ init_fn = init_fn.__get__
200
+ init_fn_ref: OpRef = OpRef(f'init.init_fns.{i}')
201
+ ref_map[init_fn_ref] = init_fn
202
+ init_fns.append(init_fn_ref)
203
+
204
+ validate_fns: list[InitPlan.ValidateFnWithParams] = []
205
+ for i, validate_fn in enumerate(ctx.cs.validate_fns or []):
206
+ validate_fn_ref: OpRef = OpRef(f'init.validate_fns.{i}')
207
+ ref_map[validate_fn_ref] = validate_fn.fn
208
+ validate_fns.append(InitPlan.ValidateFnWithParams(
209
+ fn=validate_fn_ref,
210
+ params=tuple(validate_fn.params),
211
+ ))
212
+
213
+ post_init_params: tuple[str, ...] | None = None
214
+ if hasattr(ctx.cls, STD_POST_INIT_NAME):
215
+ post_init_params = tuple(f.name for f in init_fields.all if f.field_type is FieldType.INIT_VAR)
216
+
217
+ return PlanResult(
218
+ InitPlan(
219
+ fields=tuple(plan_fields),
220
+
221
+ self_param=SELF_IDENT if 'self' in ctx.cs.fields_by_name else 'self',
222
+ std_params=tuple(f.name for f in init_fields.std),
223
+ kw_only_params=tuple(f.name for f in init_fields.kw_only),
224
+
225
+ frozen=ctx.cs.frozen,
226
+
227
+ slots=ctx.cs.slots,
228
+
229
+ post_init_params=post_init_params,
230
+
231
+ init_fns=tuple(init_fns),
232
+
233
+ validate_fns=tuple(validate_fns),
234
+ ),
235
+ ref_map,
236
+ )
237
+
238
+ def generate(self, plan: InitPlan) -> ta.Iterable[Op]:
239
+ refs: set[Ref] = set()
240
+
241
+ fields_by_name = {f.name: f for f in plan.fields}
242
+
243
+ # proto
244
+
245
+ params: list[str] = []
246
+ seen_kw_only = False
247
+ for fn, kw_only in itertools.chain(
248
+ [(fn, False) for fn in plan.std_params],
249
+ [(fn, True) for fn in plan.kw_only_params],
250
+ ):
251
+ f = fields_by_name[fn]
252
+ if kw_only:
253
+ if not seen_kw_only:
254
+ params.append('*')
255
+ seen_kw_only = True
256
+ elif seen_kw_only:
257
+ raise TypeError(f'non-keyword-only argument {f.name!r} follows keyword-only argument(s)')
258
+
259
+ refs.add(f.annotation)
260
+ p = f'{f.name}: {f.annotation.ident()}'
261
+
262
+ if f.default_factory is not None:
263
+ check.none(f.default)
264
+ p += f' = {HAS_DEFAULT_FACTORY_GLOBAL.ident}'
265
+ refs.add(HAS_DEFAULT_FACTORY_GLOBAL)
266
+ elif f.default is not None:
267
+ check.none(f.default_factory)
268
+ refs.add(f.default)
269
+ p += f' = {f.default.ident()}'
270
+
271
+ params.append(p)
272
+
273
+ proto_lines = [
274
+ f'def __init__(',
275
+ f' {plan.self_param},',
276
+ *[
277
+ f' {p},'
278
+ for p in params
279
+ ],
280
+ f') -> {NONE_GLOBAL.ident}:',
281
+ ]
282
+ refs.add(NONE_GLOBAL)
283
+
284
+ # body
285
+
286
+ lines = []
287
+
288
+ # defaults
289
+
290
+ values: dict[str, str] = {
291
+ plan.self_param: plan.self_param,
292
+ }
293
+
294
+ for f in plan.fields:
295
+ if f.default_factory is not None:
296
+ check.none(f.default)
297
+ refs.add(f.default_factory)
298
+ if f.init:
299
+ lines.extend([
300
+ f' if {f.name} is {HAS_DEFAULT_FACTORY_GLOBAL.ident}:',
301
+ f' {f.name} = {f.default_factory.ident()}()',
302
+ ])
303
+ refs.add(HAS_DEFAULT_FACTORY_GLOBAL)
304
+ else:
305
+ lines.append(
306
+ f' {f.name} = {f.default_factory.ident()}()',
307
+ )
308
+ values[f.name] = f.name
309
+
310
+ elif f.init:
311
+ if f.default is not None:
312
+ check.none(f.default_factory)
313
+ values[f.name] = f.name
314
+
315
+ else:
316
+ values[f.name] = f.name
317
+
318
+ elif plan.slots and f.default is not None:
319
+ refs.add(f.default)
320
+ lines.append(
321
+ f' {f.name} = {f.default.ident()}',
322
+ )
323
+ values[f.name] = f.name
324
+
325
+ # coercion
326
+
327
+ for f in plan.fields:
328
+ if isinstance(f.coerce, bool) and f.coerce:
329
+ lines.append(
330
+ f' {f.name} = {f.annotation.ident()}({values[f.name]})',
331
+ )
332
+ values[f.name] = f.name
333
+ elif isinstance(f.coerce, OpRef):
334
+ refs.add(f.coerce)
335
+ lines.append(
336
+ f' {f.name} = {f.coerce.ident()}({values[f.name]})',
337
+ )
338
+ values[f.name] = f.name
339
+
340
+ # field validation
341
+
342
+ for f in plan.fields:
343
+ if f.check_type is None:
344
+ continue
345
+ refs.add(f.check_type)
346
+ lines.extend([
347
+ f' if not {ISINSTANCE_GLOBAL.ident}({values[f.name]}, {f.check_type.ident()}): ',
348
+ f' raise {FIELD_TYPE_VALIDATION_ERROR_GLOBAL.ident}(',
349
+ f' obj={plan.self_param},',
350
+ f' type={f.check_type.ident()},',
351
+ f' field={f.name!r},',
352
+ f' value={values[f.name]},',
353
+ f' )',
354
+ ])
355
+ refs.add(ISINSTANCE_GLOBAL)
356
+ refs.add(FIELD_TYPE_VALIDATION_ERROR_GLOBAL)
357
+
358
+ for f in plan.fields:
359
+ if f.validate is None:
360
+ continue
361
+ refs.add(f.validate)
362
+ lines.extend([
363
+ f' if not {f.validate.ident()}({values[f.name]}): ',
364
+ f' raise {FIELD_FN_VALIDATION_ERROR_GLOBAL.ident}(',
365
+ f' obj={plan.self_param},',
366
+ f' fn={f.validate.ident()},',
367
+ f' field={f.name!r},',
368
+ f' value={values[f.name]},',
369
+ f' )',
370
+ ])
371
+ refs.add(FIELD_FN_VALIDATION_ERROR_GLOBAL)
372
+
373
+ # setattr
374
+
375
+ sab = SetattrSrcBuilder(
376
+ object_ident=plan.self_param,
377
+ )
378
+ for f in plan.fields:
379
+ if f.name not in values or f.field_type != FieldType.INSTANCE:
380
+ continue
381
+ lines.extend([
382
+ f' {l}'
383
+ for l in sab(
384
+ f.name,
385
+ values[f.name],
386
+ frozen=plan.frozen,
387
+ override=f.override,
388
+ )
389
+ ])
390
+ refs.update(sab.refs)
391
+
392
+ # fn validation
393
+
394
+ for vfn in plan.validate_fns or []:
395
+ refs.add(vfn.fn)
396
+ if vfn.params:
397
+ lines.extend([
398
+ f' if not {vfn.fn.ident()}(',
399
+ *[
400
+ f' {values[p]},'
401
+ for p in vfn.params
402
+ ],
403
+ f' ):',
404
+ ])
405
+ else:
406
+ lines.append(
407
+ f' if not {vfn.fn.ident()}():',
408
+ )
409
+ lines.extend([
410
+ f' raise {FN_VALIDATION_ERROR_GLOBAL.ident}(',
411
+ f' obj={plan.self_param},',
412
+ f' fn={vfn.fn.ident()},',
413
+ f' )',
414
+ ])
415
+ refs.add(FN_VALIDATION_ERROR_GLOBAL)
416
+
417
+ # post-init
418
+
419
+ if (pia := plan.post_init_params) is not None:
420
+ if pia:
421
+ lines.extend([
422
+ f' {plan.self_param}.{STD_POST_INIT_NAME}(',
423
+ *[
424
+ f' {values[p]},'
425
+ for p in pia
426
+ ],
427
+ f' )',
428
+ ])
429
+ else:
430
+ lines.append(
431
+ f' {plan.self_param}.{STD_POST_INIT_NAME}()',
432
+ )
433
+
434
+ for init_fn in plan.init_fns:
435
+ refs.add(init_fn)
436
+ lines.append(
437
+ f' {init_fn.ident()}({plan.self_param})',
438
+ )
439
+
440
+ #
441
+
442
+ if not lines:
443
+ lines.append(
444
+ ' pass',
445
+ )
446
+
447
+ return [
448
+ AddMethodOp(
449
+ '__init__',
450
+ '\n'.join([*proto_lines, *lines]),
451
+ frozenset(refs),
452
+ ),
453
+ ]
@@ -0,0 +1,27 @@
1
+ from ... import check
2
+ from ..processing.base import Processor
3
+ from ..processing.priority import ProcessorPriority
4
+ from ..processing.registry import register_processor_type
5
+ from ..utils import set_new_attribute
6
+ from .fields import InitFields
7
+
8
+
9
+ ##
10
+
11
+
12
+ @register_processor_type(priority=ProcessorPriority.POST_GENERATION)
13
+ class MatchArgsProcessor(Processor):
14
+ def check(self) -> None:
15
+ check.not_none(self._ctx[InitFields])
16
+
17
+ def process(self, cls: type) -> type:
18
+ if not self._ctx.cs.match_args or '__match_args__' in self._ctx.cls.__dict__:
19
+ return cls
20
+
21
+ set_new_attribute(
22
+ cls,
23
+ '__match_args__',
24
+ tuple(f.name for f in self._ctx[InitFields].std),
25
+ )
26
+
27
+ return cls
@@ -0,0 +1,16 @@
1
+ import typing as ta
2
+
3
+ from ... import lang
4
+ from ..processing.base import ProcessingContext
5
+ from ..processing.registry import register_processing_context_item_factory
6
+
7
+
8
+ ##
9
+
10
+
11
+ MroDict = ta.NewType('MroDict', ta.Mapping[str, ta.Any])
12
+
13
+
14
+ @register_processing_context_item_factory(MroDict)
15
+ def _mro_dict_processing_context_item_factory(ctx: ProcessingContext) -> MroDict:
16
+ return MroDict(lang.mro_dict(ctx.cls))
@@ -0,0 +1,87 @@
1
+ import dataclasses as dc
2
+ import typing as ta
3
+
4
+ from ..generation.base import Generator
5
+ from ..generation.base import Plan
6
+ from ..generation.base import PlanResult
7
+ from ..generation.ops import AddMethodOp
8
+ from ..generation.ops import Op
9
+ from ..generation.registry import register_generator_type
10
+ from ..generation.utils import build_attr_tuple_body_src_lines
11
+ from ..processing.base import ProcessingContext
12
+ from .fields import InstanceFields
13
+
14
+
15
+ ##
16
+
17
+
18
+ NAME_OP_PAIRS = [
19
+ ('__lt__', '<'),
20
+ ('__le__', '<='),
21
+ ('__gt__', '>'),
22
+ ('__ge__', '>='),
23
+ ]
24
+
25
+
26
+ ##
27
+
28
+
29
+ @dc.dataclass(frozen=True)
30
+ class OrderPlan(Plan):
31
+ fields: tuple[str, ...]
32
+
33
+
34
+ @register_generator_type(OrderPlan)
35
+ class OrderGenerator(Generator[OrderPlan]):
36
+ def plan(self, ctx: ProcessingContext) -> PlanResult[OrderPlan] | None:
37
+ if not ctx.cs.order:
38
+ return None
39
+
40
+ for name, _ in NAME_OP_PAIRS:
41
+ if name in ctx.cls.__dict__:
42
+ raise TypeError(
43
+ f'Cannot overwrite attribute {name} in class {ctx.cls.__name__}. '
44
+ f'Consider using functools.total_ordering',
45
+ )
46
+
47
+ return PlanResult(OrderPlan(
48
+ tuple(f.name for f in ctx[InstanceFields] if f.compare),
49
+ ))
50
+
51
+ def generate(self, pl: OrderPlan) -> ta.Iterable[Op]:
52
+ ops: list[AddMethodOp] = []
53
+
54
+ for name, op in NAME_OP_PAIRS:
55
+ ret_lines: list[str] = []
56
+ if pl.fields:
57
+ ret_lines.extend([
58
+ f' return (',
59
+ *build_attr_tuple_body_src_lines(
60
+ 'self',
61
+ *pl.fields,
62
+ prefix=' ',
63
+ ),
64
+ f' ) {op} (',
65
+ *build_attr_tuple_body_src_lines(
66
+ 'other',
67
+ *pl.fields,
68
+ prefix=' ',
69
+ ),
70
+ f' )',
71
+ ])
72
+ else:
73
+ ret_lines.append(
74
+ f' return {"True" if "=" in op else "False"}',
75
+ )
76
+
77
+ ops.append(AddMethodOp(
78
+ name,
79
+ '\n'.join([
80
+ f'def {name}(self, other):',
81
+ f' if other.__class__ is not self.__class__:',
82
+ f' return NotImplemented',
83
+ *ret_lines,
84
+ ]),
85
+ ))
86
+
87
+ return ops
@@ -0,0 +1,98 @@
1
+ import dataclasses as dc
2
+ import typing as ta
3
+
4
+ from ..generation.base import Generator
5
+ from ..generation.base import Plan
6
+ from ..generation.base import PlanResult
7
+ from ..generation.globals import NONE_GLOBAL
8
+ from ..generation.idents import SELF_IDENT
9
+ from ..generation.idents import VALUE_IDENT
10
+ from ..generation.ops import AddPropertyOp
11
+ from ..generation.ops import Op
12
+ from ..generation.ops import OpRef
13
+ from ..generation.ops import Ref
14
+ from ..generation.registry import register_generator_type
15
+ from ..generation.utils import SetattrSrcBuilder
16
+ from ..processing.base import ProcessingContext
17
+ from .fields import InstanceFields
18
+
19
+
20
+ ##
21
+
22
+
23
+ @dc.dataclass(frozen=True)
24
+ class OverridePlan(Plan):
25
+ @dc.dataclass(frozen=True)
26
+ class Field:
27
+ name: str
28
+ annotation: OpRef[ta.Any]
29
+
30
+ fields: tuple[Field, ...]
31
+
32
+ frozen: bool
33
+
34
+
35
+ @register_generator_type(OverridePlan)
36
+ class OverrideGenerator(Generator[OverridePlan]):
37
+ def plan(self, ctx: ProcessingContext) -> PlanResult[OverridePlan] | None:
38
+ orm = {}
39
+
40
+ flds: list[OverridePlan.Field] = []
41
+ for i, f in enumerate(ctx[InstanceFields]):
42
+ if not (f.override or ctx.cs.override):
43
+ continue
44
+ r: OpRef = OpRef(f'override.fields.{i}.annotation')
45
+ orm[r] = f.annotation
46
+ flds.append(OverridePlan.Field(
47
+ f.name,
48
+ r,
49
+ ))
50
+
51
+ if not flds:
52
+ return None
53
+
54
+ return PlanResult(
55
+ OverridePlan(
56
+ tuple(flds),
57
+ ctx.cs.frozen,
58
+ ),
59
+ orm,
60
+ )
61
+
62
+ def generate(self, pl: OverridePlan) -> ta.Iterable[Op]:
63
+ ops: list[Op] = []
64
+
65
+ for f in pl.fields:
66
+ op_refs: set[Ref] = {f.annotation}
67
+
68
+ get_src = '\n'.join([
69
+ f'def {f.name}({SELF_IDENT}) -> {f.annotation.ident()}:',
70
+ f' return {SELF_IDENT}.__dict__[{f.name!r}]',
71
+ ])
72
+
73
+ set_src: str | None = None
74
+ if not pl.frozen:
75
+ sab = SetattrSrcBuilder()
76
+ set_src = '\n'.join([
77
+ f'def {f.name}({SELF_IDENT}, {VALUE_IDENT}) -> {NONE_GLOBAL.ident}:',
78
+ *[
79
+ f' {l}'
80
+ for l in sab(
81
+ f.name,
82
+ VALUE_IDENT,
83
+ frozen=pl.frozen,
84
+ override=True,
85
+ )
86
+ ],
87
+ ])
88
+ op_refs.add(NONE_GLOBAL)
89
+ op_refs.update(sab.refs)
90
+
91
+ ops.append(AddPropertyOp(
92
+ f.name,
93
+ get_src=get_src,
94
+ set_src=set_src,
95
+ refs=frozenset(op_refs),
96
+ ))
97
+
98
+ return ops
@@ -0,0 +1,14 @@
1
+ from ... import check
2
+ from ..internals import STD_PARAMS_ATTR
3
+ from ..processing.base import Processor
4
+ from ..processing.priority import ProcessorPriority
5
+ from ..processing.registry import register_processor_type
6
+
7
+
8
+ ##
9
+
10
+
11
+ @register_processor_type(priority=ProcessorPriority.BOOTSTRAP)
12
+ class ParamsProcessor(Processor):
13
+ def check(self) -> None:
14
+ check.in_(STD_PARAMS_ATTR, self._ctx.cls.__dict__)