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
@@ -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,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,179 @@
|
|
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
|
+
class Verbose(ta.NamedTuple):
|
40
|
+
b: bool
|
41
|
+
|
42
|
+
|
43
|
+
@register_processor_type(priority=ProcessorPriority.GENERATION)
|
44
|
+
class GeneratorProcessor(Processor):
|
45
|
+
class Mode(abc.ABC):
|
46
|
+
@abc.abstractmethod
|
47
|
+
def _process(self, gp: 'GeneratorProcessor', cls: type) -> None:
|
48
|
+
raise NotImplementedError
|
49
|
+
|
50
|
+
class ExecutorMode(Mode):
|
51
|
+
def _process(self, gp: 'GeneratorProcessor', cls: type) -> None:
|
52
|
+
opx = OpExecutor(
|
53
|
+
cls,
|
54
|
+
gp.prepare().ref_map,
|
55
|
+
)
|
56
|
+
|
57
|
+
for op in gp.ops():
|
58
|
+
opx.execute(op)
|
59
|
+
|
60
|
+
class CompilerMode(Mode):
|
61
|
+
def _process(self, gp: 'GeneratorProcessor', cls: type) -> None:
|
62
|
+
compiler = OpCompiler(
|
63
|
+
OpCompiler.AotStyle(),
|
64
|
+
# OpCompiler.JitStyle(),
|
65
|
+
)
|
66
|
+
|
67
|
+
fn_name = '_process_dataclass__' + IDENT_MANGLER.mangle(cls.__qualname__)
|
68
|
+
|
69
|
+
comp = compiler.compile(
|
70
|
+
fn_name,
|
71
|
+
gp.ops(),
|
72
|
+
)
|
73
|
+
|
74
|
+
if (vo := gp._ctx.option(Verbose)) is not None and vo.b: # noqa
|
75
|
+
print(gp.prepare().plans.render(), file=sys.stderr)
|
76
|
+
print(file=sys.stderr)
|
77
|
+
print(comp.src, file=sys.stderr)
|
78
|
+
print(file=sys.stderr)
|
79
|
+
|
80
|
+
ns: dict = {}
|
81
|
+
ns.update(compiler.style.globals_ns()) # noqa
|
82
|
+
|
83
|
+
exec(comp.src, ns)
|
84
|
+
o_fn = ns[comp.fn_name]
|
85
|
+
|
86
|
+
if cls.__module__ in sys.modules:
|
87
|
+
gl = sys.modules[cls.__module__].__dict__
|
88
|
+
else:
|
89
|
+
gl = {}
|
90
|
+
|
91
|
+
fn = lang.new_function(**{
|
92
|
+
**lang.new_function_kwargs(o_fn),
|
93
|
+
**dict(
|
94
|
+
globals=gl,
|
95
|
+
),
|
96
|
+
})
|
97
|
+
|
98
|
+
kw: dict = {CLS_IDENT: cls}
|
99
|
+
kw.update({
|
100
|
+
k.ident: v.value
|
101
|
+
for k, v in FN_GLOBALS.items()
|
102
|
+
# if v.src.startswith('.')
|
103
|
+
})
|
104
|
+
orm = gp.prepare().ref_map
|
105
|
+
for r in comp.refs:
|
106
|
+
if isinstance(r, OpRef):
|
107
|
+
kw[r.ident()] = orm[r]
|
108
|
+
elif isinstance(r, FnGlobal):
|
109
|
+
pass
|
110
|
+
else:
|
111
|
+
raise TypeError(r)
|
112
|
+
|
113
|
+
fn(**kw)
|
114
|
+
|
115
|
+
#
|
116
|
+
|
117
|
+
def __init__(
|
118
|
+
self,
|
119
|
+
ctx: ProcessingContext,
|
120
|
+
*,
|
121
|
+
mode: Mode = ExecutorMode(),
|
122
|
+
# mode: Mode = CompilerMode(),
|
123
|
+
) -> None:
|
124
|
+
super().__init__(ctx)
|
125
|
+
|
126
|
+
self._mode = mode
|
127
|
+
|
128
|
+
@dc.dataclass(frozen=True)
|
129
|
+
class Prepared:
|
130
|
+
plans: Plans
|
131
|
+
ref_map: OpRefMap
|
132
|
+
|
133
|
+
def __post_init__(self) -> None:
|
134
|
+
hash(self.plans)
|
135
|
+
|
136
|
+
@lang.cached_function(no_wrapper_update=True)
|
137
|
+
def prepare(self) -> Prepared:
|
138
|
+
gs = [g_ty() for g_ty in all_generator_types()]
|
139
|
+
|
140
|
+
pll: list[Plan] = []
|
141
|
+
orm: dict[OpRef, ta.Any] = {}
|
142
|
+
for g in gs:
|
143
|
+
if (pr := g.plan(self._ctx)) is None:
|
144
|
+
continue
|
145
|
+
|
146
|
+
for k, v in (pr.ref_map or {}).items():
|
147
|
+
if k in orm:
|
148
|
+
check.equal(orm[k], v)
|
149
|
+
else:
|
150
|
+
orm[k] = v
|
151
|
+
|
152
|
+
pll.append(pr.plan)
|
153
|
+
|
154
|
+
plans = Plans(tuple(pll))
|
155
|
+
|
156
|
+
return self.Prepared(
|
157
|
+
plans,
|
158
|
+
orm,
|
159
|
+
)
|
160
|
+
|
161
|
+
@lang.cached_function(no_wrapper_update=True)
|
162
|
+
def ops(self) -> ta.Sequence[Op]:
|
163
|
+
prepared = self.prepare()
|
164
|
+
|
165
|
+
ops: list[Op] = []
|
166
|
+
for pl in prepared.plans:
|
167
|
+
g = generator_type_for_plan_type(type(pl))()
|
168
|
+
ops.extend(g.generate(pl))
|
169
|
+
|
170
|
+
return ops
|
171
|
+
|
172
|
+
#
|
173
|
+
|
174
|
+
def process(self, cls: type) -> type:
|
175
|
+
if (po := self._ctx.option(PlanOnly)) is not None and po.b:
|
176
|
+
self.prepare()
|
177
|
+
else:
|
178
|
+
self._mode._process(self, cls) # noqa
|
179
|
+
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
|
+
))
|