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
@@ -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
@@ -1,75 +0,0 @@
1
- import types
2
- import typing as ta
3
-
4
- from ... import lang
5
-
6
-
7
- METADATA_ATTR = '__dataclass_metadata__'
8
-
9
- Metadata: ta.TypeAlias = ta.Mapping[ta.Any, ta.Any]
10
-
11
- EMPTY_METADATA: Metadata = types.MappingProxyType({})
12
-
13
- _CLASS_MERGED_KEYS: set[str] = set()
14
- CLASS_MERGED_KEYS: ta.AbstractSet = _CLASS_MERGED_KEYS
15
-
16
-
17
- def _class_merged(o):
18
- _CLASS_MERGED_KEYS.add(o)
19
- return o
20
-
21
-
22
- def get_merged_metadata(obj: ta.Any) -> Metadata:
23
- cls = obj if isinstance(obj, type) else type(obj)
24
- dct: dict[ta.Any, ta.Any] = {}
25
- for cur in cls.__mro__[::-1]:
26
- if not (smd := cur.__dict__.get(METADATA_ATTR)):
27
- continue
28
- for k, v in smd.items():
29
- if k in CLASS_MERGED_KEYS:
30
- dct.setdefault(k, []).extend(v)
31
- else:
32
- dct[k] = v
33
- return dct
34
-
35
-
36
- def _append_cls_md(k, v):
37
- lang.get_caller_cls_dct(1).setdefault(METADATA_ATTR, {}).setdefault(k, []).append(v)
38
-
39
-
40
- ##
41
-
42
-
43
- @_class_merged
44
- class UserMetadata(lang.Marker):
45
- pass
46
-
47
-
48
- @lang.cls_dct_fn()
49
- def metadata(cls_dct, *args) -> None:
50
- cls_dct.setdefault(METADATA_ATTR, {}).setdefault(UserMetadata, []).extend(args)
51
-
52
-
53
- ##
54
-
55
-
56
- @_class_merged
57
- class Validate(lang.Marker):
58
- pass
59
-
60
-
61
- def validate(fn: ta.Callable[..., bool] | staticmethod) -> None:
62
- _append_cls_md(Validate, fn)
63
-
64
-
65
- ##
66
-
67
-
68
- @_class_merged
69
- class Init(lang.Marker):
70
- pass
71
-
72
-
73
- def init(obj):
74
- _append_cls_md(Init, obj)
75
- return obj
@@ -1,57 +0,0 @@
1
- import typing as ta
2
-
3
- from .processing import Processor
4
- from .utils import Namespace
5
- from .utils import create_fn
6
- from .utils import set_new_attribute
7
- from .utils import tuple_str
8
-
9
-
10
- def cmp_fn(
11
- name: str,
12
- op: str,
13
- self_tuple: str,
14
- other_tuple: str,
15
- globals: Namespace, # noqa
16
- ) -> ta.Callable:
17
- return create_fn(
18
- name,
19
- ('self', 'other'),
20
- [
21
- 'if other.__class__ is self.__class__:',
22
- f' return {self_tuple}{op}{other_tuple}',
23
- 'return NotImplemented',
24
- ],
25
- globals=globals,
26
- )
27
-
28
-
29
- class OrderProcessor(Processor):
30
- def _process(self) -> None:
31
- if not self._info.params.order:
32
- return
33
-
34
- flds = [f for f in self._info.instance_fields if f.compare]
35
- self_tuple = tuple_str('self', flds)
36
- other_tuple = tuple_str('other', flds)
37
- for name, op in [
38
- ('__lt__', '<'),
39
- ('__le__', '<='),
40
- ('__gt__', '>'),
41
- ('__ge__', '>='),
42
- ]:
43
- if set_new_attribute(
44
- self._cls, # noqa
45
- name,
46
- cmp_fn(
47
- name,
48
- op,
49
- self_tuple,
50
- other_tuple,
51
- globals=self._info.globals,
52
- ),
53
- ):
54
- raise TypeError(
55
- f'Cannot overwrite attribute {name} in class {self._cls.__name__}. '
56
- f'Consider using functools.total_ordering',
57
- )
@@ -1,53 +0,0 @@
1
- from ... import lang
2
- from .fields import field_assign
3
- from .params import get_field_extras
4
- from .processing import Processor
5
- from .utils import create_fn
6
- from .utils import set_new_attribute
7
-
8
-
9
- class OverridesProcessor(Processor):
10
- def _process(self) -> None:
11
- for f in self._info.instance_fields:
12
- fx = get_field_extras(f)
13
- if not (fx.override or self._info.params_extras.override):
14
- continue
15
-
16
- if self._info.params.slots:
17
- raise TypeError
18
-
19
- self_name = '__dataclass_self__' if 'self' in self._info.fields else 'self'
20
-
21
- getter = create_fn(
22
- f.name,
23
- (self_name,),
24
- [f'return {self_name}.__dict__[{f.name!r}]'],
25
- globals=self._info.globals,
26
- return_type=lang.just(f.type),
27
- )
28
- prop = property(getter)
29
-
30
- if not self._info.params.frozen:
31
- setter = create_fn(
32
- f.name,
33
- (self_name, f'{f.name}: __dataclass_type_{f.name}__'),
34
- [
35
- field_assign(
36
- self._info.params.frozen,
37
- f.name,
38
- f.name,
39
- self_name,
40
- True,
41
- ),
42
- ],
43
- globals=self._info.globals,
44
- locals={f'__dataclass_type_{f.name}__': f.type},
45
- return_type=lang.just(None),
46
- )
47
- prop = prop.setter(setter)
48
-
49
- set_new_attribute(
50
- self._cls,
51
- f.name,
52
- prop,
53
- )
@@ -1,128 +0,0 @@
1
- """
2
- @dc.dataclass(frozen=True)
3
- class Field_:
4
- name: str | None = None
5
- type: Any = None
6
- default: Any | MISSING = MISSING
7
- default_factory: Any | MISSING = MISSING
8
- repr: bool = True
9
- hash: bool | None = None
10
- init: bool = True
11
- compare: bool = True
12
- metadata: Metadata | None = None
13
- kw_only: bool | MISSING = MISSING
14
-
15
- if sys.version_info >= (3, 13):
16
- doc: str | None = None
17
-
18
- _field_type: Any = None
19
-
20
-
21
- @dc.dataclass(frozen=True)
22
- class Params_:
23
- init: bool = True
24
- repr: bool = True
25
- eq: bool = True
26
- order: bool = False
27
- unsafe_hash: bool = False
28
- frozen: bool = False
29
-
30
- match_args: bool = True
31
- kw_only: bool = False
32
- slots: bool = False
33
- weakref_slot: bool = False
34
- """
35
- import dataclasses as dc
36
- import typing as ta
37
-
38
- from ... import lang
39
- from .internals import PARAMS_ATTR
40
- from .internals import Params
41
- from .metadata import EMPTY_METADATA
42
- from .metadata import METADATA_ATTR
43
-
44
-
45
- ##
46
-
47
-
48
- @dc.dataclass(frozen=True, kw_only=True)
49
- class FieldExtras(lang.Final):
50
- derive: ta.Callable[..., ta.Any] | None = None # TODO
51
- coerce: bool | ta.Callable[[ta.Any], ta.Any] | None = None
52
- validate: ta.Callable[[ta.Any], bool] | None = None
53
- check_type: bool | type | tuple[type | None, ...] | None = None
54
- override: bool = False
55
- repr_fn: ta.Callable[[ta.Any], str | None] | None = None
56
- repr_priority: int | None = None
57
- frozen: bool | None = None # TODO
58
-
59
-
60
- DEFAULT_FIELD_EXTRAS = FieldExtras()
61
-
62
-
63
- def get_field_extras(f: dc.Field) -> FieldExtras:
64
- if not isinstance(f, dc.Field):
65
- raise TypeError(f)
66
- return f.metadata.get(FieldExtras, DEFAULT_FIELD_EXTRAS)
67
-
68
-
69
- ##
70
-
71
-
72
- def get_params_cls(obj: ta.Any) -> type | None:
73
- if not isinstance(obj, type):
74
- obj = type(obj)
75
- for cur in obj.__mro__:
76
- if PARAMS_ATTR in cur.__dict__:
77
- return cur
78
- return None
79
-
80
-
81
- def get_params(obj: ta.Any) -> Params:
82
- if not hasattr(obj, PARAMS_ATTR):
83
- raise TypeError(obj)
84
- return getattr(obj, PARAMS_ATTR)
85
-
86
-
87
- ##
88
-
89
-
90
- @dc.dataclass(frozen=True, kw_only=True)
91
- class ParamsExtras(lang.Final):
92
- reorder: bool = False
93
- cache_hash: bool = False
94
- generic_init: bool = False
95
- override: bool = False
96
- repr_id: bool = False
97
-
98
-
99
- DEFAULT_PARAMS_EXTRAS = ParamsExtras()
100
-
101
-
102
- def get_params_extras(obj: ta.Any) -> ParamsExtras:
103
- if (pcls := get_params_cls(obj)) is None:
104
- raise TypeError(pcls)
105
-
106
- md = pcls.__dict__.get(METADATA_ATTR, EMPTY_METADATA)
107
- return md.get(ParamsExtras, DEFAULT_PARAMS_EXTRAS)
108
-
109
-
110
- ##
111
-
112
-
113
- @dc.dataclass(frozen=True)
114
- class MetaclassParams:
115
- confer: frozenset[str] = frozenset()
116
- final_subclasses: bool = False
117
- abstract_immediate_subclasses: bool = False
118
-
119
-
120
- DEFAULT_METACLASS_PARAMS = MetaclassParams()
121
-
122
-
123
- def get_metaclass_params(obj: ta.Any) -> MetaclassParams:
124
- if (pcls := get_params_cls(obj)) is None:
125
- raise TypeError(pcls)
126
-
127
- md = pcls.__dict__.get(METADATA_ATTR, EMPTY_METADATA)
128
- return md.get(MetaclassParams, DEFAULT_METACLASS_PARAMS)
@@ -1,24 +0,0 @@
1
- import typing as ta
2
-
3
- from ... import lang
4
-
5
-
6
- if ta.TYPE_CHECKING:
7
- from .reflect import ClassInfo
8
-
9
-
10
- class Processor(lang.Abstract):
11
- def __init__(self, info: 'ClassInfo') -> None:
12
- super().__init__()
13
- self._cls = info.cls
14
- self._info = info
15
-
16
- def check(self) -> None:
17
- pass
18
-
19
- @lang.cached_function
20
- def process(self) -> None:
21
- self._process()
22
-
23
- def _process(self) -> None:
24
- raise NotImplementedError
@@ -1,40 +0,0 @@
1
- import dataclasses as dc
2
-
3
- from .fields import field_type
4
- from .internals import FIELDS_ATTR
5
- from .internals import FieldType
6
- from .internals import is_dataclass_instance
7
- from .processing import Processor
8
- from .utils import set_new_attribute
9
-
10
-
11
- MISSING = dc.MISSING
12
-
13
-
14
- def replace(obj, /, **changes): # noqa
15
- if not is_dataclass_instance(obj):
16
- raise TypeError('replace() should be called on dataclass instances')
17
- return _replace(obj, **changes)
18
-
19
-
20
- def _replace(obj, /, **changes):
21
- for f in getattr(obj, FIELDS_ATTR).values():
22
- if (ft := field_type(f)) is FieldType.CLASS:
23
- continue
24
-
25
- if not f.init:
26
- if f.name in changes:
27
- raise TypeError(f'field {f.name} is declared with init=False, it cannot be specified with replace()')
28
- continue
29
-
30
- if f.name not in changes:
31
- if ft is FieldType.INIT and f.default is MISSING:
32
- raise TypeError(f'InitVar {f.name!r} must be specified with replace()')
33
- changes[f.name] = getattr(obj, f.name)
34
-
35
- return obj.__class__(**changes)
36
-
37
-
38
- class ReplaceProcessor(Processor):
39
- def _process(self) -> None:
40
- set_new_attribute(self._cls, '__replace__', _replace)