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
@@ -0,0 +1,195 @@
1
+ """
2
+ TODO:
3
+ - 'setup' locals - for FrozenGenerator's `condition += ' or name in {' + ', '.join(repr(f.name) for f in fields) + '}'`
4
+ allow for `condition += ' or name in {fields_frozenset_ident}' with fields_frozenset_ident setup in preamble
5
+ """
6
+ import sys
7
+ import types
8
+ import typing as ta
9
+
10
+ from ... import lang
11
+ from ..utils import repr_round_trip_value
12
+ from .globals import FN_GLOBAL_VALUES
13
+ from .globals import FnGlobal
14
+ from .idents import CLS_IDENT
15
+ from .ops import AddMethodOp
16
+ from .ops import AddPropertyOp
17
+ from .ops import IfAttrPresent
18
+ from .ops import Op
19
+ from .ops import OpRef
20
+ from .ops import OpRefMap
21
+ from .ops import Ref
22
+ from .ops import SetAttrOp
23
+
24
+
25
+ T = ta.TypeVar('T')
26
+
27
+
28
+ ##
29
+
30
+
31
+ class OpExecutor:
32
+ def __init__(self, cls: type, orm: OpRefMap) -> None:
33
+ super().__init__()
34
+
35
+ self._cls = cls
36
+ self._orm = orm
37
+
38
+ #
39
+
40
+ @lang.cached_function(no_wrapper_update=True)
41
+ def _cls_globals(self) -> dict[str, ta.Any]:
42
+ if self._cls.__module__ in sys.modules:
43
+ return sys.modules[self._cls.__module__].__dict__
44
+ else:
45
+ return {}
46
+
47
+ #
48
+
49
+ def _set_fn_qualname(self, fn: types.FunctionType, name: str | None = None) -> types.FunctionType:
50
+ if name is None:
51
+ name = fn.__name__
52
+ fn.__qualname__ = f'{self._cls.__qualname__}.{name}'
53
+ return fn
54
+
55
+ def _create_fn(
56
+ self,
57
+ name: str,
58
+ src: str,
59
+ refs: ta.Iterable[Ref] = (),
60
+ ) -> types.FunctionType:
61
+ lo: dict = {
62
+ CLS_IDENT: self._cls,
63
+ **FN_GLOBAL_VALUES,
64
+ }
65
+ for r in refs:
66
+ if isinstance(r, OpRef):
67
+ lo[r.ident()] = self._orm[r]
68
+ elif isinstance(r, FnGlobal):
69
+ pass
70
+ else:
71
+ raise TypeError(r)
72
+
73
+ x_src = '\n'.join([
74
+ f'def __create_fn__(',
75
+ f' *,',
76
+ *[
77
+ f' {k},'
78
+ for k in lo
79
+ ],
80
+ f'):',
81
+ *[
82
+ f' {l}'
83
+ for l in src.splitlines()
84
+ ],
85
+ f'',
86
+ f' return {name}',
87
+ ])
88
+
89
+ ns: dict = {}
90
+ exec(
91
+ x_src,
92
+ self._cls_globals(),
93
+ ns,
94
+ )
95
+ x_fn = ns['__create_fn__']
96
+ fn = x_fn(**lo)
97
+
98
+ if not isinstance(fn, types.FunctionType):
99
+ raise TypeError(fn)
100
+
101
+ self._set_fn_qualname(fn, name)
102
+
103
+ return fn
104
+
105
+ def _create_opt_fn(
106
+ self,
107
+ name: str,
108
+ src: str | None,
109
+ *args: ta.Any,
110
+ **kwargs: ta.Any,
111
+ ) -> types.FunctionType | None:
112
+ if src is None:
113
+ return None
114
+
115
+ return self._create_fn(
116
+ name,
117
+ src,
118
+ *args,
119
+ **kwargs,
120
+ )
121
+
122
+ #
123
+
124
+ def _execute_set_attr(
125
+ self,
126
+ attr_name: str,
127
+ value: ta.Any,
128
+ if_present: IfAttrPresent,
129
+ ) -> None:
130
+ if attr_name in self._cls.__dict__:
131
+ if if_present == 'skip':
132
+ return
133
+ elif if_present == 'replace':
134
+ pass
135
+ elif if_present == 'error':
136
+ raise TypeError(f'Cannot overwrite attribute {attr_name} in class {self._cls.__name__}')
137
+ else:
138
+ raise ValueError(if_present)
139
+
140
+ setattr(self._cls, attr_name, value)
141
+
142
+ def execute(self, op: Op) -> None:
143
+ if isinstance(op, SetAttrOp):
144
+ if isinstance(v := op.value, OpRef):
145
+ v = self._orm[v]
146
+ if isinstance(v, types.FunctionType):
147
+ self._set_fn_qualname(v)
148
+ else:
149
+ v = repr_round_trip_value(v)
150
+
151
+ self._execute_set_attr(
152
+ op.name,
153
+ v,
154
+ op.if_present,
155
+ )
156
+
157
+ elif isinstance(op, AddMethodOp):
158
+ fn = self._create_fn(
159
+ op.name,
160
+ op.src,
161
+ op.refs,
162
+ )
163
+
164
+ self._execute_set_attr(
165
+ op.name,
166
+ fn,
167
+ op.if_present,
168
+ )
169
+
170
+ elif isinstance(op, AddPropertyOp):
171
+ get_fn = self._create_opt_fn(
172
+ op.name,
173
+ op.get_src,
174
+ op.refs,
175
+ )
176
+ set_fn = self._create_opt_fn(
177
+ op.name,
178
+ op.set_src,
179
+ op.refs,
180
+ )
181
+ del_fn = self._create_opt_fn(
182
+ op.name,
183
+ op.del_src,
184
+ op.refs,
185
+ )
186
+ prop = property(
187
+ get_fn,
188
+ set_fn,
189
+ del_fn,
190
+ )
191
+
192
+ setattr(self._cls, op.name, prop)
193
+
194
+ else:
195
+ raise TypeError(op)
@@ -0,0 +1,83 @@
1
+ import dataclasses as dc
2
+ import reprlib
3
+ import types
4
+ import typing as ta
5
+
6
+ from ..errors import FieldFnValidationError
7
+ from ..errors import FieldTypeValidationError
8
+ from ..errors import FnValidationError
9
+ from .idents import IDENT_PREFIX
10
+
11
+
12
+ ##
13
+
14
+
15
+ class FnGlobal(ta.NamedTuple):
16
+ ident: str
17
+
18
+
19
+ class FnGlobalValue(ta.NamedTuple):
20
+ value: ta.Any
21
+ src: str # Leading '.' denotes a dataclasses-internal symbol
22
+
23
+
24
+ FN_GLOBAL_IMPORTS: ta.Mapping[str, types.ModuleType] = {
25
+ 'dataclasses': dc,
26
+ 'reprlib': reprlib,
27
+ 'types': types,
28
+ }
29
+
30
+
31
+ FN_GLOBALS: ta.Mapping[FnGlobal, FnGlobalValue] = {
32
+ (ISINSTANCE_GLOBAL := FnGlobal(IDENT_PREFIX + 'isinstance')): FnGlobalValue(isinstance, 'isinstance'),
33
+ (NONE_GLOBAL := FnGlobal(IDENT_PREFIX + 'None')): FnGlobalValue(None, 'None'),
34
+ (PROPERTY_GLOBAL := FnGlobal(IDENT_PREFIX + 'property')): FnGlobalValue(property, 'property'),
35
+ (TYPE_ERROR_GLOBAL := FnGlobal(IDENT_PREFIX + 'TypeError')): FnGlobalValue(TypeError, 'TypeError'),
36
+
37
+ (OBJECT_SETATTR_GLOBAL := FnGlobal(IDENT_PREFIX + 'object_setattr')): FnGlobalValue(
38
+ object.__setattr__,
39
+ 'object.__setattr__',
40
+ ),
41
+
42
+ (FROZEN_INSTANCE_ERROR_GLOBAL := FnGlobal(IDENT_PREFIX + 'FrozenInstanceError')): FnGlobalValue(
43
+ dc.FrozenInstanceError,
44
+ 'dataclasses.FrozenInstanceError',
45
+ ),
46
+ (HAS_DEFAULT_FACTORY_GLOBAL := FnGlobal(IDENT_PREFIX + 'HAS_DEFAULT_FACTORY')): FnGlobalValue(
47
+ dc._HAS_DEFAULT_FACTORY, # type: ignore[attr-defined] # noqa
48
+ 'dataclasses._HAS_DEFAULT_FACTORY',
49
+ ),
50
+ (MISSING_GLOBAL := FnGlobal(IDENT_PREFIX + 'MISSING')): FnGlobalValue(
51
+ dc.MISSING, # noqa
52
+ 'dataclasses.MISSING',
53
+ ),
54
+
55
+ (REPRLIB_RECURSIVE_REPR_GLOBAL := FnGlobal(IDENT_PREFIX + '_recursive_repr')): FnGlobalValue(
56
+ reprlib.recursive_repr,
57
+ 'reprlib.recursive_repr',
58
+ ),
59
+
60
+ (FUNCTION_TYPE_GLOBAL := FnGlobal(IDENT_PREFIX + 'FunctionType')): FnGlobalValue(
61
+ types.FunctionType,
62
+ 'types.FunctionType',
63
+ ),
64
+
65
+ (FIELD_FN_VALIDATION_ERROR_GLOBAL := FnGlobal(IDENT_PREFIX + 'FieldFnValidationError')): FnGlobalValue(
66
+ FieldFnValidationError,
67
+ '.errors.FieldFnValidationError',
68
+ ),
69
+ (FIELD_TYPE_VALIDATION_ERROR_GLOBAL := FnGlobal(IDENT_PREFIX + 'FieldTypeValidationError')): FnGlobalValue(
70
+ FieldTypeValidationError,
71
+ '.errors.FieldTypeValidationError',
72
+ ),
73
+ (FN_VALIDATION_ERROR_GLOBAL := FnGlobal(IDENT_PREFIX + 'FnValidationError')): FnGlobalValue(
74
+ FnValidationError,
75
+ '.errors.FnValidationError',
76
+ ),
77
+ }
78
+
79
+
80
+ FN_GLOBAL_VALUES: ta.Mapping[str, ta.Any] = {
81
+ k.ident: v.value
82
+ for k, v in FN_GLOBALS.items()
83
+ }
@@ -0,0 +1,6 @@
1
+ IDENT_PREFIX = '__dataclass__'
2
+
3
+ CLS_IDENT = IDENT_PREFIX + 'cls'
4
+ SELF_IDENT = IDENT_PREFIX + 'self'
5
+ SELF_DICT_IDENT = IDENT_PREFIX + 'self_dict'
6
+ VALUE_IDENT = IDENT_PREFIX + 'value'
@@ -0,0 +1,18 @@
1
+ from ...text.mangle import StringMangler
2
+
3
+
4
+ ##
5
+
6
+
7
+ ESCAPED_IDENT_CHARS = ''.join(
8
+ chr(i)
9
+ for i in range(128)
10
+ if not (
11
+ chr(i).isalpha() or
12
+ chr(i).isdigit() or
13
+ chr(i) == '_' or
14
+ ord(chr(i)) > 127
15
+ )
16
+ )
17
+
18
+ IDENT_MANGLER = StringMangler.of('_', ESCAPED_IDENT_CHARS)
@@ -0,0 +1,20 @@
1
+ import dataclasses as dc
2
+ import json
3
+
4
+ from .plans import Plans
5
+
6
+
7
+ ##
8
+
9
+
10
+ @dc.dataclass(frozen=True)
11
+ class DataclassTransformManifest:
12
+ qualname: str
13
+ digest: str
14
+ plans: Plans
15
+
16
+ def to_json(self) -> str:
17
+ return json.dumps(
18
+ dc.asdict(self), # noqa
19
+ indent=2,
20
+ )
@@ -0,0 +1,97 @@
1
+ import abc
2
+ import dataclasses as dc
3
+ import typing as ta
4
+
5
+ from .globals import FnGlobal
6
+ from .idents import IDENT_PREFIX
7
+
8
+
9
+ T = ta.TypeVar('T')
10
+
11
+
12
+ ##
13
+
14
+
15
+ class _OpRef(ta.NamedTuple, ta.Generic[T]):
16
+ name: str
17
+
18
+
19
+ class OpRef(_OpRef[T]):
20
+ def __repr__(self) -> str:
21
+ return f'OpRef(name={self.name!r})'
22
+
23
+ #
24
+
25
+ _ident: str
26
+
27
+ def ident(self) -> str:
28
+ try:
29
+ return self._ident
30
+ except AttributeError:
31
+ pass
32
+
33
+ ident = IDENT_PREFIX + self.name.replace('.', '__')
34
+ self._ident = ident # noqa
35
+ return ident
36
+
37
+
38
+ ##
39
+
40
+
41
+ OpRefMap: ta.TypeAlias = ta.Mapping[OpRef, ta.Any]
42
+
43
+ Ref: ta.TypeAlias = OpRef | FnGlobal
44
+
45
+
46
+ ##
47
+
48
+
49
+ IfAttrPresent: ta.TypeAlias = ta.Literal['skip', 'replace', 'error']
50
+
51
+
52
+ @dc.dataclass(frozen=True)
53
+ class Op(abc.ABC): # noqa
54
+ pass
55
+
56
+
57
+ @dc.dataclass(frozen=True)
58
+ class SetAttrOp(Op):
59
+ name: str
60
+ value: Ref | ta.Any
61
+
62
+ if_present: IfAttrPresent = dc.field(default='replace', kw_only=True)
63
+
64
+
65
+ @dc.dataclass(frozen=True)
66
+ class AddMethodOp(Op):
67
+ name: str
68
+ src: str
69
+ refs: frozenset[Ref] = dc.field(default=frozenset())
70
+
71
+ if_present: IfAttrPresent = dc.field(default='error', kw_only=True)
72
+
73
+
74
+ @dc.dataclass(frozen=True)
75
+ class AddPropertyOp(Op):
76
+ name: str
77
+ get_src: str | None = None
78
+ set_src: str | None = None
79
+ del_src: str | None = None
80
+ refs: frozenset[Ref] = frozenset()
81
+
82
+
83
+ ##
84
+
85
+
86
+ def get_op_refs(op: Op) -> frozenset[Ref]:
87
+ if isinstance(op, SetAttrOp):
88
+ if isinstance(v := op.value, (OpRef, FnGlobal)):
89
+ return frozenset([v])
90
+ else:
91
+ return frozenset()
92
+
93
+ elif isinstance(op, (AddMethodOp, AddPropertyOp)):
94
+ return op.refs
95
+
96
+ else:
97
+ raise TypeError(op)
@@ -0,0 +1,35 @@
1
+ """
2
+ TODO:
3
+ - sha1 is slow :/ key by repr but name by sha1
4
+ """
5
+ import dataclasses as dc
6
+ import re
7
+ import typing as ta
8
+
9
+ from ... import lang
10
+ from .base import Plan
11
+
12
+
13
+ ##
14
+
15
+
16
+ @dc.dataclass(frozen=True)
17
+ class Plans:
18
+ tup: tuple[Plan, ...]
19
+
20
+ def __iter__(self) -> ta.Iterator[Plan]:
21
+ return iter(self.tup)
22
+
23
+ @lang.cached_function(no_wrapper_update=True)
24
+ def render(self) -> str:
25
+ return _render(self)
26
+
27
+
28
+ ##
29
+
30
+
31
+ _WS_PAT = re.compile(r'\s+')
32
+
33
+
34
+ def _render(plans: Plans) -> str:
35
+ return _WS_PAT.sub('', repr(plans.tup))
@@ -0,0 +1,174 @@
1
+ """
2
+ TODO:
3
+ - untangle compilation from here
4
+ - populate linecache
5
+ """
6
+ import abc
7
+ import dataclasses as dc
8
+ import sys
9
+ import typing as ta
10
+
11
+ from ... import check
12
+ from ... import lang
13
+ from ..processing.base import ProcessingContext
14
+ from ..processing.base import Processor
15
+ from ..processing.priority import ProcessorPriority
16
+ from ..processing.registry import register_processor_type
17
+ from .base import Plan
18
+ from .compilation import OpCompiler
19
+ from .execution import OpExecutor
20
+ from .globals import FN_GLOBALS
21
+ from .globals import FnGlobal
22
+ from .idents import CLS_IDENT
23
+ from .mangling import IDENT_MANGLER
24
+ from .ops import Op
25
+ from .ops import OpRef
26
+ from .ops import OpRefMap
27
+ from .plans import Plans
28
+ from .registry import all_generator_types
29
+ from .registry import generator_type_for_plan_type
30
+
31
+
32
+ ##
33
+
34
+
35
+ class PlanOnly(ta.NamedTuple):
36
+ b: bool
37
+
38
+
39
+ @register_processor_type(priority=ProcessorPriority.GENERATION)
40
+ class GeneratorProcessor(Processor):
41
+ class Mode(abc.ABC):
42
+ @abc.abstractmethod
43
+ def _process(self, gp: 'GeneratorProcessor', cls: type) -> None:
44
+ raise NotImplementedError
45
+
46
+ class ExecutorMode(Mode):
47
+ def _process(self, gp: 'GeneratorProcessor', cls: type) -> None:
48
+ opx = OpExecutor(
49
+ cls,
50
+ gp.prepare().ref_map,
51
+ )
52
+
53
+ for op in gp.ops():
54
+ opx.execute(op)
55
+
56
+ class CompilerMode(Mode):
57
+ def _process(self, gp: 'GeneratorProcessor', cls: type) -> None:
58
+ compiler = OpCompiler(
59
+ OpCompiler.AotStyle(),
60
+ # OpCompiler.JitStyle(),
61
+ )
62
+
63
+ fn_name = '_process_dataclass__' + IDENT_MANGLER.mangle(cls.__qualname__)
64
+
65
+ comp = compiler.compile(
66
+ fn_name,
67
+ gp.ops(),
68
+ )
69
+
70
+ # print(gp.prepare().plans.render())
71
+ # print()
72
+ # print(comp.src)
73
+ # print()
74
+
75
+ ns: dict = {}
76
+ ns.update(compiler.style.globals_ns()) # noqa
77
+
78
+ exec(comp.src, ns)
79
+ o_fn = ns[comp.fn_name]
80
+
81
+ if cls.__module__ in sys.modules:
82
+ gl = sys.modules[cls.__module__].__dict__
83
+ else:
84
+ gl = {}
85
+
86
+ fn = lang.new_function(**{
87
+ **lang.new_function_kwargs(o_fn),
88
+ **dict(
89
+ globals=gl,
90
+ ),
91
+ })
92
+
93
+ kw: dict = {CLS_IDENT: cls}
94
+ kw.update({
95
+ k.ident: v.value
96
+ for k, v in FN_GLOBALS.items()
97
+ # if v.src.startswith('.')
98
+ })
99
+ orm = gp.prepare().ref_map
100
+ for r in comp.refs:
101
+ if isinstance(r, OpRef):
102
+ kw[r.ident()] = orm[r]
103
+ elif isinstance(r, FnGlobal):
104
+ pass
105
+ else:
106
+ raise TypeError(r)
107
+
108
+ fn(**kw)
109
+
110
+ #
111
+
112
+ def __init__(
113
+ self,
114
+ ctx: ProcessingContext,
115
+ *,
116
+ mode: Mode = ExecutorMode(),
117
+ # mode: Mode = CompilerMode(),
118
+ ) -> None:
119
+ super().__init__(ctx)
120
+
121
+ self._mode = mode
122
+
123
+ @dc.dataclass(frozen=True)
124
+ class Prepared:
125
+ plans: Plans
126
+ ref_map: OpRefMap
127
+
128
+ def __post_init__(self) -> None:
129
+ hash(self.plans)
130
+
131
+ @lang.cached_function(no_wrapper_update=True)
132
+ def prepare(self) -> Prepared:
133
+ gs = [g_ty() for g_ty in all_generator_types()]
134
+
135
+ pll: list[Plan] = []
136
+ orm: dict[OpRef, ta.Any] = {}
137
+ for g in gs:
138
+ if (pr := g.plan(self._ctx)) is None:
139
+ continue
140
+
141
+ for k, v in (pr.ref_map or {}).items():
142
+ if k in orm:
143
+ check.equal(orm[k], v)
144
+ else:
145
+ orm[k] = v
146
+
147
+ pll.append(pr.plan)
148
+
149
+ plans = Plans(tuple(pll))
150
+
151
+ return self.Prepared(
152
+ plans,
153
+ orm,
154
+ )
155
+
156
+ @lang.cached_function(no_wrapper_update=True)
157
+ def ops(self) -> ta.Sequence[Op]:
158
+ prepared = self.prepare()
159
+
160
+ ops: list[Op] = []
161
+ for pl in prepared.plans:
162
+ g = generator_type_for_plan_type(type(pl))()
163
+ ops.extend(g.generate(pl))
164
+
165
+ return ops
166
+
167
+ #
168
+
169
+ def process(self, cls: type) -> type:
170
+ if (po := self._ctx.option(PlanOnly)) is not None and po.b:
171
+ self.prepare()
172
+ else:
173
+ self._mode._process(self, cls) # noqa
174
+ return cls
@@ -0,0 +1,42 @@
1
+ import typing as ta
2
+
3
+ from ... import check
4
+ from ... import lang
5
+ from ..utils import SealableRegistry
6
+ from .base import Generator
7
+ from .base import Plan
8
+
9
+
10
+ K = ta.TypeVar('K')
11
+ V = ta.TypeVar('V')
12
+
13
+ GeneratorT = ta.TypeVar('GeneratorT', bound=Generator)
14
+
15
+
16
+ ##
17
+
18
+
19
+ _GENERATOR_TYPES_BY_PLAN_TYPE: SealableRegistry[type[Plan], type[Generator]] = SealableRegistry()
20
+
21
+
22
+ def register_generator_type(pl_ty: type[Plan]) -> ta.Callable[[type[GeneratorT]], type[GeneratorT]]:
23
+ def inner(g_ty):
24
+ check.issubclass(g_ty, Generator)
25
+ _GENERATOR_TYPES_BY_PLAN_TYPE[pl_ty] = g_ty
26
+ return g_ty
27
+
28
+ check.issubclass(pl_ty, Plan)
29
+ return inner
30
+
31
+
32
+ @lang.cached_function
33
+ def generator_type_for_plan_type(pl_ty: type[Plan]) -> type[Generator]:
34
+ return _GENERATOR_TYPES_BY_PLAN_TYPE[pl_ty]
35
+
36
+
37
+ @lang.cached_function
38
+ def all_generator_types() -> tuple[type[Generator], ...]:
39
+ return tuple(sorted(
40
+ {v for _, v in _GENERATOR_TYPES_BY_PLAN_TYPE.items()},
41
+ key=lambda g_ty: g_ty.__name__,
42
+ ))