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.
- omlish/__about__.py +2 -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 +174 -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 +45 -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/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/typedvalues/marshal.py +2 -2
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev285.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev285.dist-info}/RECORD +83 -43
- 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.dev285.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev285.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev285.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev284.dist-info → omlish-0.0.0.dev285.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/dataclasses/__init__.py
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
##
|
2
|
+
# stdlib interface
|
3
|
+
|
1
4
|
from dataclasses import ( # noqa
|
2
5
|
FrozenInstanceError,
|
3
6
|
|
@@ -15,46 +18,28 @@ from dataclasses import ( # noqa
|
|
15
18
|
fields,
|
16
19
|
|
17
20
|
is_dataclass,
|
18
|
-
|
19
|
-
# asdict,
|
20
|
-
# astuple,
|
21
|
-
|
22
|
-
# replace,
|
23
21
|
)
|
24
22
|
|
25
|
-
from .
|
26
|
-
field as xfield,
|
27
|
-
|
23
|
+
from .api import ( # noqa
|
28
24
|
dataclass as xdataclass,
|
25
|
+
|
29
26
|
make_dataclass as xmake_dataclass,
|
30
27
|
|
31
|
-
|
28
|
+
field as xfield,
|
32
29
|
)
|
33
30
|
|
34
|
-
from .
|
31
|
+
from .tools.as_ import ( # noqa
|
35
32
|
asdict,
|
36
33
|
astuple,
|
37
34
|
)
|
38
35
|
|
39
|
-
from .
|
40
|
-
FieldExtras,
|
41
|
-
get_field_extras,
|
42
|
-
|
43
|
-
get_params,
|
44
|
-
|
45
|
-
get_params_extras,
|
46
|
-
|
47
|
-
MetaclassParams,
|
48
|
-
get_metaclass_params,
|
49
|
-
)
|
50
|
-
|
51
|
-
from .impl.replace import ( # noqa
|
36
|
+
from .concerns.replace import ( # noqa
|
52
37
|
replace,
|
53
38
|
)
|
54
39
|
|
55
40
|
|
56
41
|
##
|
57
|
-
|
42
|
+
# globals hack
|
58
43
|
|
59
44
|
globals()['field'] = xfield
|
60
45
|
|
@@ -63,69 +48,82 @@ globals()['make_dataclass'] = xmake_dataclass
|
|
63
48
|
|
64
49
|
|
65
50
|
##
|
51
|
+
# additional interface
|
66
52
|
|
53
|
+
from .api import ( # noqa
|
54
|
+
append_class_metadata,
|
55
|
+
extra_class_params,
|
56
|
+
init,
|
57
|
+
metadata,
|
58
|
+
validate,
|
67
59
|
|
68
|
-
|
60
|
+
extra_field_params,
|
61
|
+
set_field_metadata,
|
62
|
+
update_extra_field_params,
|
63
|
+
)
|
64
|
+
|
65
|
+
from .errors import ( # noqa
|
66
|
+
FieldFnValidationError,
|
67
|
+
FieldTypeValidationError,
|
69
68
|
FieldValidationError,
|
69
|
+
FnValidationError,
|
70
|
+
TypeValidationError,
|
70
71
|
ValidationError,
|
71
72
|
)
|
72
73
|
|
73
|
-
from .
|
74
|
-
|
74
|
+
from .metaclass.bases import ( # noqa
|
75
|
+
Box,
|
76
|
+
Case,
|
75
77
|
Data,
|
76
78
|
Frozen,
|
77
|
-
Case,
|
78
|
-
Box,
|
79
79
|
)
|
80
80
|
|
81
|
-
from .
|
82
|
-
|
83
|
-
|
84
|
-
get_merged_metadata,
|
85
|
-
|
86
|
-
UserMetadata,
|
87
|
-
metadata,
|
88
|
-
|
89
|
-
Validate,
|
90
|
-
validate,
|
81
|
+
from .metaclass.meta import ( # noqa
|
82
|
+
DataMeta,
|
83
|
+
)
|
91
84
|
|
92
|
-
|
93
|
-
|
85
|
+
from .metaclass.specs import ( # noqa
|
86
|
+
get_metaclass_spec,
|
94
87
|
)
|
95
88
|
|
96
|
-
from .
|
97
|
-
ClassInfo,
|
89
|
+
from .reflection import ( # noqa
|
98
90
|
reflect,
|
99
91
|
)
|
100
92
|
|
101
|
-
from .
|
102
|
-
|
93
|
+
from .tools.as_ import ( # noqa
|
94
|
+
shallow_asdict,
|
95
|
+
shallow_astuple,
|
103
96
|
)
|
104
97
|
|
105
|
-
from .
|
106
|
-
opt_repr,
|
107
|
-
truthy_repr,
|
108
|
-
|
98
|
+
from .tools.iter import ( # noqa
|
109
99
|
fields_dict,
|
100
|
+
|
101
|
+
iter_items,
|
102
|
+
iter_keys,
|
103
|
+
iter_values,
|
104
|
+
)
|
105
|
+
|
106
|
+
from .tools.modifiers import ( # noqa
|
110
107
|
field_modifier,
|
111
|
-
chain_metadata,
|
112
|
-
update_class_metadata,
|
113
|
-
update_field_metadata,
|
114
|
-
update_field_extras,
|
115
108
|
update_fields,
|
116
|
-
|
117
|
-
|
118
|
-
shallow_astuple,
|
119
|
-
shallow_asdict,
|
109
|
+
)
|
120
110
|
|
111
|
+
from .tools.replace import ( # noqa
|
121
112
|
deep_replace,
|
113
|
+
)
|
122
114
|
|
123
|
-
|
124
|
-
|
125
|
-
|
115
|
+
from .tools.repr import ( # noqa
|
116
|
+
opt_repr,
|
117
|
+
truthy_repr,
|
126
118
|
)
|
127
119
|
|
120
|
+
from .tools.static import ( # noqa
|
121
|
+
Static,
|
122
|
+
)
|
123
|
+
|
124
|
+
|
128
125
|
##
|
126
|
+
# lite imports
|
129
127
|
|
130
128
|
from ..lite.dataclasses import ( # noqa
|
131
129
|
is_immediate_dataclass,
|
@@ -0,0 +1,25 @@
|
|
1
|
+
from .classes.decorator import ( # noqa
|
2
|
+
dataclass,
|
3
|
+
)
|
4
|
+
|
5
|
+
from .classes.make import ( # noqa
|
6
|
+
make_dataclass,
|
7
|
+
)
|
8
|
+
|
9
|
+
from .classes.metadata import ( # noqa
|
10
|
+
append_class_metadata,
|
11
|
+
extra_class_params,
|
12
|
+
init,
|
13
|
+
metadata,
|
14
|
+
validate,
|
15
|
+
)
|
16
|
+
|
17
|
+
from .fields.metadata import ( # noqa
|
18
|
+
extra_field_params,
|
19
|
+
set_field_metadata,
|
20
|
+
update_extra_field_params,
|
21
|
+
)
|
22
|
+
|
23
|
+
from .fields.constructor import ( # noqa
|
24
|
+
field,
|
25
|
+
)
|
File without changes
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import typing as ta
|
2
|
+
|
3
|
+
from .... import check
|
4
|
+
from ...internals import StdParams
|
5
|
+
from ...specs import ClassSpec
|
6
|
+
from ...specs import FieldSpec
|
7
|
+
|
8
|
+
|
9
|
+
#
|
10
|
+
|
11
|
+
|
12
|
+
def std_params_to_class_spec(
|
13
|
+
p: StdParams,
|
14
|
+
fields: ta.Sequence[FieldSpec],
|
15
|
+
) -> ClassSpec:
|
16
|
+
return ClassSpec(
|
17
|
+
fields=fields,
|
18
|
+
|
19
|
+
init=check.isinstance(p.init, bool),
|
20
|
+
repr=check.isinstance(p.repr, bool),
|
21
|
+
eq=check.isinstance(p.eq, bool),
|
22
|
+
order=check.isinstance(p.order, bool),
|
23
|
+
unsafe_hash=check.isinstance(p.unsafe_hash, bool),
|
24
|
+
frozen=check.isinstance(p.frozen, bool),
|
25
|
+
|
26
|
+
match_args=check.isinstance(p.match_args, bool),
|
27
|
+
kw_only=check.isinstance(p.kw_only, bool),
|
28
|
+
slots=check.isinstance(p.slots, bool),
|
29
|
+
weakref_slot=check.isinstance(p.weakref_slot, bool),
|
30
|
+
)
|
@@ -0,0 +1,145 @@
|
|
1
|
+
"""
|
2
|
+
TODO:
|
3
|
+
- collect init_fn's / validate_fns from superclass ClassSpecs
|
4
|
+
"""
|
5
|
+
import inspect
|
6
|
+
import typing as ta
|
7
|
+
|
8
|
+
from .... import check
|
9
|
+
from .... import lang
|
10
|
+
from ...internals import STD_FIELDS_ATTR
|
11
|
+
from ...internals import STD_PARAMS_ATTR
|
12
|
+
from ...processing.driving import drive_cls_processing
|
13
|
+
from ...specs import ClassSpec
|
14
|
+
from ...utils import class_decorator
|
15
|
+
from ..fields.building import build_cls_std_fields
|
16
|
+
from ..fields.conversion import std_field_to_field_spec
|
17
|
+
from .metadata import extract_cls_metadata
|
18
|
+
from .params import build_spec_std_params
|
19
|
+
|
20
|
+
|
21
|
+
##
|
22
|
+
|
23
|
+
|
24
|
+
@class_decorator
|
25
|
+
def dataclass(
|
26
|
+
cls=None,
|
27
|
+
/,
|
28
|
+
*,
|
29
|
+
|
30
|
+
init=True,
|
31
|
+
repr=True, # noqa
|
32
|
+
eq=True,
|
33
|
+
order=False,
|
34
|
+
unsafe_hash=False,
|
35
|
+
frozen=False,
|
36
|
+
|
37
|
+
match_args=True,
|
38
|
+
kw_only=False,
|
39
|
+
slots=False,
|
40
|
+
weakref_slot=False,
|
41
|
+
|
42
|
+
#
|
43
|
+
|
44
|
+
metadata: ta.Sequence[ta.Any] | None = None,
|
45
|
+
|
46
|
+
reorder: bool | None = None,
|
47
|
+
cache_hash: bool | None = None,
|
48
|
+
generic_init: bool | None = None,
|
49
|
+
override: bool | None = None,
|
50
|
+
repr_id: bool | None = None,
|
51
|
+
|
52
|
+
#
|
53
|
+
|
54
|
+
_plan_only: bool = False,
|
55
|
+
):
|
56
|
+
if isinstance(metadata, ta.Mapping):
|
57
|
+
raise TypeError(metadata)
|
58
|
+
|
59
|
+
#
|
60
|
+
|
61
|
+
cls = check.not_none(cls)
|
62
|
+
|
63
|
+
#
|
64
|
+
|
65
|
+
fields = build_cls_std_fields(
|
66
|
+
cls,
|
67
|
+
kw_only=kw_only,
|
68
|
+
)
|
69
|
+
setattr(cls, STD_FIELDS_ATTR, fields)
|
70
|
+
|
71
|
+
#
|
72
|
+
|
73
|
+
fsl = [
|
74
|
+
std_field_to_field_spec(
|
75
|
+
f,
|
76
|
+
set_metadata=True,
|
77
|
+
)
|
78
|
+
for f in fields.values()
|
79
|
+
]
|
80
|
+
|
81
|
+
#
|
82
|
+
|
83
|
+
cmd = extract_cls_metadata(cls, deep=True)
|
84
|
+
|
85
|
+
vfp_lst: list[ClassSpec.ValidateFnWithParams] = []
|
86
|
+
for md_vf in cmd.validate_fns or []:
|
87
|
+
if isinstance(md_vf, staticmethod):
|
88
|
+
md_vf = md_vf.__func__
|
89
|
+
vfp_lst.append(ClassSpec.ValidateFnWithParams(
|
90
|
+
md_vf,
|
91
|
+
[p.name for p in inspect.signature(md_vf).parameters.values()],
|
92
|
+
))
|
93
|
+
|
94
|
+
#
|
95
|
+
|
96
|
+
cs = ClassSpec(
|
97
|
+
fields=fsl,
|
98
|
+
|
99
|
+
init=init,
|
100
|
+
repr=repr,
|
101
|
+
eq=eq,
|
102
|
+
order=order,
|
103
|
+
unsafe_hash=unsafe_hash,
|
104
|
+
frozen=frozen,
|
105
|
+
|
106
|
+
match_args=match_args,
|
107
|
+
kw_only=kw_only,
|
108
|
+
slots=slots,
|
109
|
+
weakref_slot=weakref_slot,
|
110
|
+
|
111
|
+
#
|
112
|
+
|
113
|
+
metadata=(
|
114
|
+
*(cmd.user_metadata or []),
|
115
|
+
*(metadata or []),
|
116
|
+
) or None,
|
117
|
+
|
118
|
+
**{
|
119
|
+
**(cmd.extra_params or {}),
|
120
|
+
**lang.opt_kw(
|
121
|
+
reorder=reorder,
|
122
|
+
cache_hash=cache_hash,
|
123
|
+
generic_init=generic_init,
|
124
|
+
override=override,
|
125
|
+
repr_id=repr_id,
|
126
|
+
),
|
127
|
+
},
|
128
|
+
|
129
|
+
init_fns=cmd.init_fns or None,
|
130
|
+
validate_fns=vfp_lst or None,
|
131
|
+
)
|
132
|
+
|
133
|
+
#
|
134
|
+
|
135
|
+
std_params = build_spec_std_params(cs)
|
136
|
+
check.not_in(STD_PARAMS_ATTR, cls.__dict__)
|
137
|
+
setattr(cls, STD_PARAMS_ATTR, std_params)
|
138
|
+
|
139
|
+
#
|
140
|
+
|
141
|
+
return drive_cls_processing(
|
142
|
+
cls,
|
143
|
+
cs,
|
144
|
+
plan_only=_plan_only,
|
145
|
+
)
|
@@ -0,0 +1,109 @@
|
|
1
|
+
import contextlib
|
2
|
+
import keyword
|
3
|
+
import sys
|
4
|
+
import types
|
5
|
+
import typing as ta
|
6
|
+
|
7
|
+
from .decorator import dataclass
|
8
|
+
|
9
|
+
|
10
|
+
##
|
11
|
+
|
12
|
+
|
13
|
+
def make_dataclass( # noqa
|
14
|
+
cls_name,
|
15
|
+
fields,
|
16
|
+
*,
|
17
|
+
bases=(),
|
18
|
+
namespace=None,
|
19
|
+
|
20
|
+
init=True,
|
21
|
+
repr=True, # noqa
|
22
|
+
eq=True,
|
23
|
+
order=False,
|
24
|
+
unsafe_hash=False,
|
25
|
+
frozen=False,
|
26
|
+
|
27
|
+
match_args=True,
|
28
|
+
kw_only=False,
|
29
|
+
slots=False,
|
30
|
+
weakref_slot=False,
|
31
|
+
|
32
|
+
module=None,
|
33
|
+
|
34
|
+
#
|
35
|
+
|
36
|
+
metadata: ta.Sequence[ta.Any] | None = None,
|
37
|
+
|
38
|
+
reorder: bool | None = None,
|
39
|
+
cache_hash: bool | None = None,
|
40
|
+
generic_init: bool | None = None,
|
41
|
+
override: bool | None = None,
|
42
|
+
repr_id: bool | None = None,
|
43
|
+
):
|
44
|
+
if namespace is None:
|
45
|
+
namespace = {}
|
46
|
+
|
47
|
+
seen = set()
|
48
|
+
annotations = {}
|
49
|
+
defaults = {}
|
50
|
+
for item in fields:
|
51
|
+
if isinstance(item, str):
|
52
|
+
name = item
|
53
|
+
tp = 'typing.Any'
|
54
|
+
elif len(item) == 2:
|
55
|
+
name, tp, = item
|
56
|
+
elif len(item) == 3:
|
57
|
+
name, tp, spec = item
|
58
|
+
defaults[name] = spec
|
59
|
+
else:
|
60
|
+
raise TypeError(f'Invalid field: {item!r}')
|
61
|
+
if not isinstance(name, str) or not name.isidentifier():
|
62
|
+
raise TypeError(f'Field names must be valid identifiers: {name!r}')
|
63
|
+
if keyword.iskeyword(name):
|
64
|
+
raise TypeError(f'Field names must not be keywords: {name!r}')
|
65
|
+
if name in seen:
|
66
|
+
raise TypeError(f'Field name duplicated: {name!r}')
|
67
|
+
|
68
|
+
seen.add(name)
|
69
|
+
annotations[name] = tp
|
70
|
+
|
71
|
+
def exec_body_callback(ns):
|
72
|
+
ns.update(namespace)
|
73
|
+
ns.update(defaults)
|
74
|
+
ns['__annotations__'] = annotations
|
75
|
+
|
76
|
+
cls = types.new_class(cls_name, bases, {}, exec_body_callback)
|
77
|
+
|
78
|
+
if module is None:
|
79
|
+
try:
|
80
|
+
module = sys._getframemodulename(1) or '__main__' # type: ignore # noqa
|
81
|
+
except AttributeError:
|
82
|
+
with contextlib.suppress(AttributeError, ValueError):
|
83
|
+
module = sys._getframe(1).f_globals.get('__name__', '__main__') # noqa
|
84
|
+
if module is not None:
|
85
|
+
cls.__module__ = module
|
86
|
+
|
87
|
+
return dataclass(
|
88
|
+
cls,
|
89
|
+
init=init,
|
90
|
+
repr=repr,
|
91
|
+
eq=eq,
|
92
|
+
order=order,
|
93
|
+
unsafe_hash=unsafe_hash,
|
94
|
+
frozen=frozen,
|
95
|
+
match_args=match_args,
|
96
|
+
kw_only=kw_only,
|
97
|
+
slots=slots,
|
98
|
+
weakref_slot=weakref_slot,
|
99
|
+
|
100
|
+
#
|
101
|
+
|
102
|
+
metadata=metadata,
|
103
|
+
|
104
|
+
reorder=reorder,
|
105
|
+
cache_hash=cache_hash,
|
106
|
+
generic_init=generic_init,
|
107
|
+
override=override,
|
108
|
+
repr_id=repr_id,
|
109
|
+
)
|
@@ -0,0 +1,133 @@
|
|
1
|
+
import dataclasses as dc
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from .... import check
|
5
|
+
from .... import lang
|
6
|
+
from ....lite.dataclasses import is_immediate_dataclass
|
7
|
+
from ...specs import InitFn
|
8
|
+
|
9
|
+
|
10
|
+
T = ta.TypeVar('T')
|
11
|
+
|
12
|
+
|
13
|
+
##
|
14
|
+
|
15
|
+
|
16
|
+
METADATA_ATTR = '__dataclass_metadata__'
|
17
|
+
|
18
|
+
|
19
|
+
def _get_cls_metadata_dct(cls: type) -> dict:
|
20
|
+
check.isinstance(cls, type)
|
21
|
+
check.arg(not is_immediate_dataclass(cls))
|
22
|
+
try:
|
23
|
+
return cls.__dict__[METADATA_ATTR]
|
24
|
+
except KeyError:
|
25
|
+
pass
|
26
|
+
dct: dict = {}
|
27
|
+
setattr(cls, METADATA_ATTR, dct)
|
28
|
+
return dct
|
29
|
+
|
30
|
+
|
31
|
+
def _append_cls_metadata(cls, k, v):
|
32
|
+
_get_cls_metadata_dct(cls).setdefault(k, []).append(v)
|
33
|
+
|
34
|
+
|
35
|
+
def _append_cls_dct_metadata(k, *vs):
|
36
|
+
lang.get_caller_cls_dct(1).setdefault(METADATA_ATTR, {}).setdefault(k, []).extend(vs)
|
37
|
+
|
38
|
+
|
39
|
+
##
|
40
|
+
|
41
|
+
|
42
|
+
class _ExtraClassParamsMetadata(lang.Marker):
|
43
|
+
pass
|
44
|
+
|
45
|
+
|
46
|
+
def extra_class_params(**kwargs):
|
47
|
+
def inner(cls):
|
48
|
+
_append_cls_metadata(cls, _ExtraClassParamsMetadata, kwargs)
|
49
|
+
return cls
|
50
|
+
return inner
|
51
|
+
|
52
|
+
|
53
|
+
##
|
54
|
+
|
55
|
+
|
56
|
+
class _UserMetadata(lang.Marker):
|
57
|
+
pass
|
58
|
+
|
59
|
+
|
60
|
+
def metadata(*objs) -> None:
|
61
|
+
_append_cls_dct_metadata(_InitMetadata, *objs)
|
62
|
+
|
63
|
+
|
64
|
+
def append_class_metadata(cls: type[T], *args: ta.Any) -> type[T]:
|
65
|
+
_append_cls_metadata(cls, _UserMetadata, *args)
|
66
|
+
return cls
|
67
|
+
|
68
|
+
|
69
|
+
#
|
70
|
+
|
71
|
+
|
72
|
+
class _InitMetadata(lang.Marker):
|
73
|
+
pass
|
74
|
+
|
75
|
+
|
76
|
+
def init(obj):
|
77
|
+
_append_cls_dct_metadata(_InitMetadata, obj)
|
78
|
+
return obj
|
79
|
+
|
80
|
+
|
81
|
+
#
|
82
|
+
|
83
|
+
|
84
|
+
class _ValidateMetadata(lang.Marker):
|
85
|
+
pass
|
86
|
+
|
87
|
+
|
88
|
+
def validate(obj):
|
89
|
+
_append_cls_dct_metadata(_ValidateMetadata, obj)
|
90
|
+
return obj
|
91
|
+
|
92
|
+
|
93
|
+
##
|
94
|
+
|
95
|
+
|
96
|
+
@dc.dataclass(frozen=True, kw_only=True, eq=False)
|
97
|
+
class ClassMetadata:
|
98
|
+
extra_params: ta.Mapping[str, ta.Any] | None = None
|
99
|
+
|
100
|
+
user_metadata: ta.Sequence[ta.Any] | None = None
|
101
|
+
init_fns: ta.Sequence[InitFn | property] | None = None
|
102
|
+
validate_fns: ta.Sequence[ta.Any] | None = None
|
103
|
+
|
104
|
+
|
105
|
+
def extract_cls_metadata(
|
106
|
+
cls: type,
|
107
|
+
*,
|
108
|
+
deep: bool,
|
109
|
+
) -> ClassMetadata:
|
110
|
+
extra_params = {}
|
111
|
+
|
112
|
+
user_metadata: list[ta.Any] = []
|
113
|
+
init_fns: list[InitFn | property] = []
|
114
|
+
validate_fns: list[ta.Any] = []
|
115
|
+
|
116
|
+
for b_cls in (cls.__mro__[-2::-1] if deep else (cls,)):
|
117
|
+
cls_md_dct = b_cls.__dict__.get(METADATA_ATTR, {})
|
118
|
+
|
119
|
+
if b_cls is cls:
|
120
|
+
for kw in cls_md_dct.get(_ExtraClassParamsMetadata, []):
|
121
|
+
extra_params.update(kw)
|
122
|
+
|
123
|
+
user_metadata.extend(cls_md_dct.get(_UserMetadata) or [])
|
124
|
+
init_fns.extend(cls_md_dct.get(_InitMetadata) or [])
|
125
|
+
validate_fns.extend(cls_md_dct.get(_ValidateMetadata) or [])
|
126
|
+
|
127
|
+
return ClassMetadata(
|
128
|
+
extra_params=extra_params or None,
|
129
|
+
|
130
|
+
user_metadata=user_metadata,
|
131
|
+
init_fns=init_fns,
|
132
|
+
validate_fns=validate_fns,
|
133
|
+
)
|
@@ -0,0 +1,78 @@
|
|
1
|
+
import typing as ta
|
2
|
+
|
3
|
+
from .... import check
|
4
|
+
from ...internals import STD_PARAMS_ATTR
|
5
|
+
from ...internals import StdParams
|
6
|
+
from ...specs import ClassSpec
|
7
|
+
|
8
|
+
|
9
|
+
##
|
10
|
+
|
11
|
+
|
12
|
+
class SpecDataclassParams(StdParams):
|
13
|
+
__slots__ = (*StdParams.__slots__, '_spec')
|
14
|
+
|
15
|
+
def __init__(
|
16
|
+
self,
|
17
|
+
init,
|
18
|
+
repr, # noqa
|
19
|
+
eq,
|
20
|
+
order,
|
21
|
+
unsafe_hash,
|
22
|
+
frozen,
|
23
|
+
match_args,
|
24
|
+
kw_only,
|
25
|
+
slots,
|
26
|
+
weakref_slot,
|
27
|
+
*args: ta.Any,
|
28
|
+
spec: ClassSpec,
|
29
|
+
**kwargs: ta.Any,
|
30
|
+
) -> None:
|
31
|
+
super().__init__(
|
32
|
+
init,
|
33
|
+
repr,
|
34
|
+
eq,
|
35
|
+
order,
|
36
|
+
unsafe_hash,
|
37
|
+
frozen,
|
38
|
+
match_args,
|
39
|
+
kw_only,
|
40
|
+
slots,
|
41
|
+
weakref_slot,
|
42
|
+
*args,
|
43
|
+
**kwargs,
|
44
|
+
)
|
45
|
+
|
46
|
+
self._spec = spec
|
47
|
+
|
48
|
+
def __repr__(self) -> str:
|
49
|
+
r = super().__repr__()
|
50
|
+
return f'{self.__class__.__name__}{r[r.index("("):]}'
|
51
|
+
|
52
|
+
|
53
|
+
def build_spec_std_params(cs: ClassSpec) -> 'SpecDataclassParams':
|
54
|
+
return SpecDataclassParams(
|
55
|
+
init=cs.init,
|
56
|
+
repr=cs.repr,
|
57
|
+
eq=cs.eq,
|
58
|
+
order=cs.order,
|
59
|
+
unsafe_hash=cs.unsafe_hash,
|
60
|
+
frozen=cs.frozen,
|
61
|
+
|
62
|
+
match_args=cs.match_args,
|
63
|
+
kw_only=cs.kw_only,
|
64
|
+
slots=cs.slots,
|
65
|
+
weakref_slot=cs.weakref_slot,
|
66
|
+
|
67
|
+
spec=cs,
|
68
|
+
)
|
69
|
+
|
70
|
+
|
71
|
+
def get_class_spec(cls: type) -> ClassSpec | None:
|
72
|
+
check.isinstance(cls, type)
|
73
|
+
|
74
|
+
sp = getattr(cls, STD_PARAMS_ATTR)
|
75
|
+
if not isinstance(sp, SpecDataclassParams):
|
76
|
+
return None
|
77
|
+
|
78
|
+
return check.isinstance(sp._spec, ClassSpec) # noqa
|
File without changes
|