omlish 0.0.0.dev284__py3-none-any.whl → 0.0.0.dev286__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- omlish/__about__.py +6 -2
- omlish/dataclasses/__init__.py +58 -60
- omlish/dataclasses/api/__init__.py +25 -0
- omlish/dataclasses/api/classes/__init__.py +0 -0
- omlish/dataclasses/api/classes/conversion.py +30 -0
- omlish/dataclasses/api/classes/decorator.py +145 -0
- omlish/dataclasses/api/classes/make.py +109 -0
- omlish/dataclasses/api/classes/metadata.py +133 -0
- omlish/dataclasses/api/classes/params.py +78 -0
- omlish/dataclasses/api/fields/__init__.py +0 -0
- omlish/dataclasses/api/fields/building.py +120 -0
- omlish/dataclasses/api/fields/constructor.py +56 -0
- omlish/dataclasses/api/fields/conversion.py +191 -0
- omlish/dataclasses/api/fields/metadata.py +94 -0
- omlish/dataclasses/concerns/__init__.py +17 -0
- omlish/dataclasses/concerns/abc.py +15 -0
- omlish/dataclasses/concerns/copy.py +63 -0
- omlish/dataclasses/concerns/doc.py +53 -0
- omlish/dataclasses/concerns/eq.py +60 -0
- omlish/dataclasses/concerns/fields.py +119 -0
- omlish/dataclasses/concerns/frozen.py +133 -0
- omlish/dataclasses/concerns/hash.py +165 -0
- omlish/dataclasses/concerns/init.py +453 -0
- omlish/dataclasses/concerns/matchargs.py +27 -0
- omlish/dataclasses/concerns/mro.py +16 -0
- omlish/dataclasses/concerns/order.py +87 -0
- omlish/dataclasses/concerns/override.py +98 -0
- omlish/dataclasses/concerns/params.py +14 -0
- omlish/dataclasses/concerns/replace.py +48 -0
- omlish/dataclasses/concerns/repr.py +95 -0
- omlish/dataclasses/{impl → concerns}/slots.py +25 -1
- omlish/dataclasses/debug.py +2 -0
- omlish/dataclasses/errors.py +115 -0
- omlish/dataclasses/generation/__init__.py +0 -0
- omlish/dataclasses/generation/base.py +38 -0
- omlish/dataclasses/generation/compilation.py +258 -0
- omlish/dataclasses/generation/execution.py +195 -0
- omlish/dataclasses/generation/globals.py +83 -0
- omlish/dataclasses/generation/idents.py +6 -0
- omlish/dataclasses/generation/mangling.py +18 -0
- omlish/dataclasses/generation/manifests.py +20 -0
- omlish/dataclasses/generation/ops.py +97 -0
- omlish/dataclasses/generation/plans.py +35 -0
- omlish/dataclasses/generation/processor.py +179 -0
- omlish/dataclasses/generation/registry.py +42 -0
- omlish/dataclasses/generation/utils.py +83 -0
- omlish/dataclasses/{impl/reflect.py → inspect.py} +53 -90
- omlish/dataclasses/{impl/internals.py → internals.py} +26 -32
- omlish/dataclasses/metaclass/__init__.py +0 -0
- omlish/dataclasses/metaclass/bases.py +69 -0
- omlish/dataclasses/metaclass/confer.py +65 -0
- omlish/dataclasses/metaclass/meta.py +115 -0
- omlish/dataclasses/metaclass/specs.py +38 -0
- omlish/dataclasses/processing/__init__.py +0 -0
- omlish/dataclasses/processing/base.py +83 -0
- omlish/dataclasses/processing/driving.py +49 -0
- omlish/dataclasses/processing/priority.py +13 -0
- omlish/dataclasses/processing/registry.py +81 -0
- omlish/dataclasses/reflection.py +81 -0
- omlish/dataclasses/specs.py +224 -0
- omlish/dataclasses/tools/__init__.py +0 -0
- omlish/dataclasses/{impl → tools}/as_.py +23 -8
- omlish/dataclasses/tools/iter.py +27 -0
- omlish/dataclasses/tools/modifiers.py +52 -0
- omlish/dataclasses/tools/replace.py +17 -0
- omlish/dataclasses/tools/repr.py +12 -0
- omlish/dataclasses/{static.py → tools/static.py} +25 -4
- omlish/dataclasses/utils.py +54 -109
- omlish/diag/__init__.py +4 -4
- omlish/inject/impl/origins.py +1 -1
- omlish/lang/cached/function.py +4 -2
- omlish/lang/maybes.py +17 -0
- omlish/lite/maybes.py +17 -0
- omlish/marshal/objects/dataclasses.py +3 -7
- omlish/marshal/objects/helpers.py +3 -3
- omlish/secrets/marshal.py +1 -1
- omlish/secrets/secrets.py +1 -1
- omlish/sql/queries/base.py +1 -1
- omlish/text/minja.py +81 -25
- omlish/text/templating.py +116 -0
- omlish/typedvalues/marshal.py +2 -2
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/METADATA +4 -1
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/RECORD +87 -46
- omlish/dataclasses/impl/LICENSE +0 -279
- omlish/dataclasses/impl/__init__.py +0 -33
- omlish/dataclasses/impl/api.py +0 -278
- omlish/dataclasses/impl/copy.py +0 -30
- omlish/dataclasses/impl/errors.py +0 -53
- omlish/dataclasses/impl/fields.py +0 -245
- omlish/dataclasses/impl/frozen.py +0 -93
- omlish/dataclasses/impl/hashing.py +0 -86
- omlish/dataclasses/impl/init.py +0 -199
- omlish/dataclasses/impl/main.py +0 -93
- omlish/dataclasses/impl/metaclass.py +0 -235
- omlish/dataclasses/impl/metadata.py +0 -75
- omlish/dataclasses/impl/order.py +0 -57
- omlish/dataclasses/impl/overrides.py +0 -53
- omlish/dataclasses/impl/params.py +0 -128
- omlish/dataclasses/impl/processing.py +0 -24
- omlish/dataclasses/impl/replace.py +0 -40
- omlish/dataclasses/impl/repr.py +0 -66
- omlish/dataclasses/impl/simple.py +0 -50
- omlish/dataclasses/impl/utils.py +0 -167
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev286.dist-info}/top_level.txt +0 -0
@@ -1,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)
|
omlish/dataclasses/impl/init.py
DELETED
@@ -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
|
-
)
|
omlish/dataclasses/impl/main.py
DELETED
@@ -1,93 +0,0 @@
|
|
1
|
-
import abc
|
2
|
-
import dataclasses as dc
|
3
|
-
import typing as ta
|
4
|
-
|
5
|
-
from ... import check
|
6
|
-
from ... import lang
|
7
|
-
from .copy import CopyProcessor
|
8
|
-
from .fields import FieldsProcessor
|
9
|
-
from .frozen import FrozenProcessor
|
10
|
-
from .hashing import HashProcessor
|
11
|
-
from .init import InitProcessor
|
12
|
-
from .internals import FIELDS_ATTR
|
13
|
-
from .internals import PARAMS_ATTR
|
14
|
-
from .internals import Params
|
15
|
-
from .order import OrderProcessor
|
16
|
-
from .overrides import OverridesProcessor
|
17
|
-
from .params import ParamsExtras
|
18
|
-
from .processing import Processor
|
19
|
-
from .reflect import ClassInfo
|
20
|
-
from .replace import ReplaceProcessor
|
21
|
-
from .repr import ReprProcessor
|
22
|
-
from .simple import DocProcessor
|
23
|
-
from .simple import EqProcessor
|
24
|
-
from .simple import MatchArgsProcessor
|
25
|
-
from .slots import add_slots
|
26
|
-
|
27
|
-
|
28
|
-
MISSING = dc.MISSING
|
29
|
-
|
30
|
-
|
31
|
-
class MainProcessor:
|
32
|
-
def __init__(self, cls: type) -> None:
|
33
|
-
super().__init__()
|
34
|
-
|
35
|
-
self._cls = check.isinstance(cls, type)
|
36
|
-
self._info = info = ClassInfo(cls, _constructing=True)
|
37
|
-
|
38
|
-
check.not_in(FIELDS_ATTR, cls.__dict__)
|
39
|
-
check.is_(check.isinstance(cls.__dict__[PARAMS_ATTR], Params), info.params)
|
40
|
-
check.is_(check.isinstance(check.not_none(info.cls_metadata)[ParamsExtras], ParamsExtras), info.params_extras) # noqa
|
41
|
-
|
42
|
-
def _check_params(self) -> None:
|
43
|
-
if self._info.params.order and not self._info.params.eq:
|
44
|
-
raise ValueError('eq must be true if order is true')
|
45
|
-
|
46
|
-
@lang.cached_function
|
47
|
-
def _transform_slots(self) -> None:
|
48
|
-
if self._info.params.weakref_slot and not self._info.params.slots:
|
49
|
-
raise TypeError('weakref_slot is True but slots is False')
|
50
|
-
if not self._info.params.slots:
|
51
|
-
return
|
52
|
-
self._cls = add_slots(
|
53
|
-
self._cls,
|
54
|
-
is_frozen=self._info.params.frozen,
|
55
|
-
weakref_slot=self._info.params.weakref_slot,
|
56
|
-
)
|
57
|
-
|
58
|
-
PROCESSOR_TYPES: ta.ClassVar[ta.Sequence[type[Processor]]] = [
|
59
|
-
FieldsProcessor,
|
60
|
-
InitProcessor,
|
61
|
-
OverridesProcessor,
|
62
|
-
ReprProcessor,
|
63
|
-
EqProcessor,
|
64
|
-
OrderProcessor,
|
65
|
-
FrozenProcessor,
|
66
|
-
HashProcessor,
|
67
|
-
DocProcessor,
|
68
|
-
MatchArgsProcessor,
|
69
|
-
ReplaceProcessor,
|
70
|
-
CopyProcessor,
|
71
|
-
]
|
72
|
-
|
73
|
-
@lang.cached_function
|
74
|
-
def process(self) -> type:
|
75
|
-
self._check_params()
|
76
|
-
|
77
|
-
ps = [pcls(self._info) for pcls in self.PROCESSOR_TYPES]
|
78
|
-
|
79
|
-
for p in ps:
|
80
|
-
p.check()
|
81
|
-
|
82
|
-
for p in ps:
|
83
|
-
p.process()
|
84
|
-
|
85
|
-
self._transform_slots()
|
86
|
-
|
87
|
-
abc.update_abstractmethods(self._cls) # noqa
|
88
|
-
|
89
|
-
return self._cls
|
90
|
-
|
91
|
-
|
92
|
-
def process_class(cls: type) -> type:
|
93
|
-
return MainProcessor(cls).process()
|
@@ -1,235 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
TODO:
|
3
|
-
- Rewrite lol
|
4
|
-
- Enum - enforce Abstract or Final
|
5
|
-
"""
|
6
|
-
import abc
|
7
|
-
import collections
|
8
|
-
import dataclasses as dc
|
9
|
-
import typing as ta
|
10
|
-
|
11
|
-
from ... import lang
|
12
|
-
from .api import MISSING
|
13
|
-
from .api import dataclass
|
14
|
-
from .api import field # noqa
|
15
|
-
from .params import MetaclassParams
|
16
|
-
from .params import get_metaclass_params
|
17
|
-
from .params import get_params
|
18
|
-
from .params import get_params_extras
|
19
|
-
|
20
|
-
|
21
|
-
T = ta.TypeVar('T')
|
22
|
-
|
23
|
-
|
24
|
-
##
|
25
|
-
|
26
|
-
|
27
|
-
_CONFER_PARAMS: tuple[str, ...] = (
|
28
|
-
'eq',
|
29
|
-
'frozen',
|
30
|
-
'kw_only',
|
31
|
-
)
|
32
|
-
|
33
|
-
_CONFER_PARAMS_EXTRAS: tuple[str, ...] = (
|
34
|
-
'reorder',
|
35
|
-
'cache_hash',
|
36
|
-
'generic_init',
|
37
|
-
'override',
|
38
|
-
)
|
39
|
-
|
40
|
-
_CONFER_METACLASS_PARAMS: tuple[str, ...] = (
|
41
|
-
'confer',
|
42
|
-
'final_subclasses',
|
43
|
-
'abstract_immediate_subclasses',
|
44
|
-
)
|
45
|
-
|
46
|
-
|
47
|
-
def confer_kwarg(out: dict[str, ta.Any], k: str, v: ta.Any) -> None:
|
48
|
-
if k in out:
|
49
|
-
if out[k] != v:
|
50
|
-
raise ValueError
|
51
|
-
else:
|
52
|
-
out[k] = v
|
53
|
-
|
54
|
-
|
55
|
-
def confer_kwargs(
|
56
|
-
bases: ta.Sequence[type],
|
57
|
-
kwargs: ta.Mapping[str, ta.Any],
|
58
|
-
) -> dict[str, ta.Any]:
|
59
|
-
out: dict[str, ta.Any] = {}
|
60
|
-
for base in bases:
|
61
|
-
if not dc.is_dataclass(base):
|
62
|
-
continue
|
63
|
-
|
64
|
-
if not (bmp := get_metaclass_params(base)).confer:
|
65
|
-
continue
|
66
|
-
|
67
|
-
for ck in bmp.confer:
|
68
|
-
if ck in kwargs:
|
69
|
-
continue
|
70
|
-
|
71
|
-
if ck in _CONFER_PARAMS:
|
72
|
-
confer_kwarg(out, ck, getattr(get_params(base), ck))
|
73
|
-
|
74
|
-
elif ck in _CONFER_PARAMS_EXTRAS:
|
75
|
-
confer_kwarg(out, ck, getattr(get_params_extras(base), ck))
|
76
|
-
|
77
|
-
elif ck in _CONFER_METACLASS_PARAMS:
|
78
|
-
confer_kwarg(out, ck, getattr(bmp, ck))
|
79
|
-
|
80
|
-
else:
|
81
|
-
raise KeyError(ck)
|
82
|
-
|
83
|
-
return out
|
84
|
-
|
85
|
-
|
86
|
-
##
|
87
|
-
|
88
|
-
|
89
|
-
class DataMeta(abc.ABCMeta):
|
90
|
-
def __new__(
|
91
|
-
mcls,
|
92
|
-
name,
|
93
|
-
bases,
|
94
|
-
namespace,
|
95
|
-
*,
|
96
|
-
|
97
|
-
abstract=False,
|
98
|
-
sealed=False,
|
99
|
-
final=False,
|
100
|
-
|
101
|
-
metadata=None,
|
102
|
-
**kwargs,
|
103
|
-
):
|
104
|
-
ckw = confer_kwargs(bases, kwargs)
|
105
|
-
nkw = {**kwargs, **ckw}
|
106
|
-
|
107
|
-
mcp = MetaclassParams(**{
|
108
|
-
mpa: nkw.pop(mpa)
|
109
|
-
for mpa in _CONFER_METACLASS_PARAMS
|
110
|
-
if mpa in nkw
|
111
|
-
})
|
112
|
-
|
113
|
-
mmd = {
|
114
|
-
MetaclassParams: mcp,
|
115
|
-
}
|
116
|
-
if metadata is not None:
|
117
|
-
metadata = collections.ChainMap(mmd, metadata)
|
118
|
-
else:
|
119
|
-
metadata = mmd
|
120
|
-
|
121
|
-
#
|
122
|
-
|
123
|
-
xbs: list[type] = []
|
124
|
-
|
125
|
-
if any(get_metaclass_params(b).abstract_immediate_subclasses for b in bases if dc.is_dataclass(b)):
|
126
|
-
abstract = True
|
127
|
-
|
128
|
-
final |= (mcp.final_subclasses and not abstract)
|
129
|
-
|
130
|
-
if final and abstract:
|
131
|
-
raise TypeError(f'Class cannot be abstract and final: {name!r}')
|
132
|
-
|
133
|
-
if abstract:
|
134
|
-
xbs.append(lang.Abstract)
|
135
|
-
if sealed:
|
136
|
-
xbs.append(lang.Sealed)
|
137
|
-
if final:
|
138
|
-
xbs.append(lang.Final)
|
139
|
-
|
140
|
-
if xbs:
|
141
|
-
if bases and bases[-1] is ta.Generic:
|
142
|
-
bases = (*bases[:-1], *xbs, bases[-1])
|
143
|
-
else:
|
144
|
-
bases = (*bases, *xbs)
|
145
|
-
if ob := namespace.get('__orig_bases__'):
|
146
|
-
if getattr(ob[-1], '__origin__', None) is ta.Generic:
|
147
|
-
namespace['__orig_bases__'] = (*ob[:-1], *xbs, ob[-1])
|
148
|
-
else:
|
149
|
-
namespace['__orig_bases__'] = (*ob, *xbs)
|
150
|
-
|
151
|
-
#
|
152
|
-
|
153
|
-
ofs: set[str] = set()
|
154
|
-
if any(issubclass(b, lang.Abstract) for b in bases) and nkw.get('override'):
|
155
|
-
ofs.update(a for a in namespace.get('__annotations__', []) if a not in namespace)
|
156
|
-
namespace.update((a, MISSING) for a in ofs)
|
157
|
-
|
158
|
-
#
|
159
|
-
|
160
|
-
cls = lang.super_meta(
|
161
|
-
super(),
|
162
|
-
mcls,
|
163
|
-
name,
|
164
|
-
bases,
|
165
|
-
namespace,
|
166
|
-
)
|
167
|
-
|
168
|
-
#
|
169
|
-
|
170
|
-
for a in ofs:
|
171
|
-
delattr(cls, a)
|
172
|
-
|
173
|
-
#
|
174
|
-
|
175
|
-
return dataclass(cls, metadata=metadata, **nkw)
|
176
|
-
|
177
|
-
|
178
|
-
##
|
179
|
-
|
180
|
-
|
181
|
-
# @ta.dataclass_transform(field_specifiers=(field,)) # FIXME: ctor
|
182
|
-
class Data(
|
183
|
-
eq=False,
|
184
|
-
order=False,
|
185
|
-
confer=frozenset([
|
186
|
-
'confer',
|
187
|
-
'final_subclasses',
|
188
|
-
]),
|
189
|
-
metaclass=DataMeta,
|
190
|
-
):
|
191
|
-
def __init__(self, *args, **kwargs):
|
192
|
-
# Typechecking barrier
|
193
|
-
super().__init__(*args, **kwargs)
|
194
|
-
|
195
|
-
def __init_subclass__(cls, **kwargs):
|
196
|
-
# Typechecking barrier
|
197
|
-
super().__init_subclass__(**kwargs)
|
198
|
-
|
199
|
-
|
200
|
-
class Frozen(
|
201
|
-
Data,
|
202
|
-
frozen=True,
|
203
|
-
eq=False,
|
204
|
-
order=False,
|
205
|
-
confer=frozenset([
|
206
|
-
*get_metaclass_params(Data).confer,
|
207
|
-
'frozen',
|
208
|
-
'reorder',
|
209
|
-
'cache_hash',
|
210
|
-
'override',
|
211
|
-
]),
|
212
|
-
):
|
213
|
-
pass
|
214
|
-
|
215
|
-
|
216
|
-
class Case(
|
217
|
-
Frozen,
|
218
|
-
abstract=True,
|
219
|
-
override=True,
|
220
|
-
final_subclasses=True,
|
221
|
-
abstract_immediate_subclasses=True,
|
222
|
-
):
|
223
|
-
pass
|
224
|
-
|
225
|
-
|
226
|
-
class Box(
|
227
|
-
Frozen,
|
228
|
-
ta.Generic[T],
|
229
|
-
generic_init=True,
|
230
|
-
confer=frozenset([
|
231
|
-
*get_metaclass_params(Frozen).confer,
|
232
|
-
'generic_init',
|
233
|
-
]),
|
234
|
-
):
|
235
|
-
v: T
|