omlish 0.0.0.dev1__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.

Potentially problematic release.


This version of omlish might be problematic. Click here for more details.

Files changed (187) hide show
  1. omlish/__about__.py +7 -0
  2. omlish/__init__.py +0 -0
  3. omlish/argparse.py +223 -0
  4. omlish/asyncs/__init__.py +17 -0
  5. omlish/asyncs/anyio.py +23 -0
  6. omlish/asyncs/asyncio.py +19 -0
  7. omlish/asyncs/asyncs.py +76 -0
  8. omlish/asyncs/futures.py +179 -0
  9. omlish/asyncs/trio.py +11 -0
  10. omlish/c3.py +173 -0
  11. omlish/cached.py +9 -0
  12. omlish/check.py +231 -0
  13. omlish/collections/__init__.py +63 -0
  14. omlish/collections/_abc.py +156 -0
  15. omlish/collections/_io_abc.py +78 -0
  16. omlish/collections/cache/__init__.py +11 -0
  17. omlish/collections/cache/descriptor.py +188 -0
  18. omlish/collections/cache/impl.py +485 -0
  19. omlish/collections/cache/types.py +37 -0
  20. omlish/collections/coerce.py +337 -0
  21. omlish/collections/frozen.py +148 -0
  22. omlish/collections/identity.py +106 -0
  23. omlish/collections/indexed.py +75 -0
  24. omlish/collections/mappings.py +127 -0
  25. omlish/collections/ordered.py +81 -0
  26. omlish/collections/persistent.py +36 -0
  27. omlish/collections/skiplist.py +193 -0
  28. omlish/collections/sorted.py +126 -0
  29. omlish/collections/treap.py +228 -0
  30. omlish/collections/treapmap.py +144 -0
  31. omlish/collections/unmodifiable.py +174 -0
  32. omlish/collections/utils.py +110 -0
  33. omlish/configs/__init__.py +0 -0
  34. omlish/configs/flattening.py +147 -0
  35. omlish/configs/props.py +64 -0
  36. omlish/dataclasses/__init__.py +83 -0
  37. omlish/dataclasses/impl/__init__.py +6 -0
  38. omlish/dataclasses/impl/api.py +260 -0
  39. omlish/dataclasses/impl/as_.py +76 -0
  40. omlish/dataclasses/impl/exceptions.py +2 -0
  41. omlish/dataclasses/impl/fields.py +148 -0
  42. omlish/dataclasses/impl/frozen.py +55 -0
  43. omlish/dataclasses/impl/hashing.py +85 -0
  44. omlish/dataclasses/impl/init.py +173 -0
  45. omlish/dataclasses/impl/internals.py +118 -0
  46. omlish/dataclasses/impl/main.py +150 -0
  47. omlish/dataclasses/impl/metaclass.py +126 -0
  48. omlish/dataclasses/impl/metadata.py +74 -0
  49. omlish/dataclasses/impl/order.py +47 -0
  50. omlish/dataclasses/impl/params.py +150 -0
  51. omlish/dataclasses/impl/processing.py +16 -0
  52. omlish/dataclasses/impl/reflect.py +173 -0
  53. omlish/dataclasses/impl/replace.py +40 -0
  54. omlish/dataclasses/impl/repr.py +34 -0
  55. omlish/dataclasses/impl/simple.py +92 -0
  56. omlish/dataclasses/impl/slots.py +80 -0
  57. omlish/dataclasses/impl/utils.py +167 -0
  58. omlish/defs.py +193 -0
  59. omlish/dispatch/__init__.py +3 -0
  60. omlish/dispatch/dispatch.py +137 -0
  61. omlish/dispatch/functions.py +52 -0
  62. omlish/dispatch/methods.py +162 -0
  63. omlish/docker.py +149 -0
  64. omlish/dynamic.py +220 -0
  65. omlish/graphs/__init__.py +0 -0
  66. omlish/graphs/dot/__init__.py +19 -0
  67. omlish/graphs/dot/items.py +162 -0
  68. omlish/graphs/dot/rendering.py +147 -0
  69. omlish/graphs/dot/utils.py +30 -0
  70. omlish/graphs/trees.py +249 -0
  71. omlish/http/__init__.py +0 -0
  72. omlish/http/consts.py +20 -0
  73. omlish/http/wsgi.py +34 -0
  74. omlish/inject/__init__.py +85 -0
  75. omlish/inject/binder.py +12 -0
  76. omlish/inject/bindings.py +49 -0
  77. omlish/inject/eagers.py +21 -0
  78. omlish/inject/elements.py +43 -0
  79. omlish/inject/exceptions.py +49 -0
  80. omlish/inject/impl/__init__.py +0 -0
  81. omlish/inject/impl/bindings.py +19 -0
  82. omlish/inject/impl/elements.py +154 -0
  83. omlish/inject/impl/injector.py +182 -0
  84. omlish/inject/impl/inspect.py +98 -0
  85. omlish/inject/impl/private.py +109 -0
  86. omlish/inject/impl/providers.py +132 -0
  87. omlish/inject/impl/scopes.py +198 -0
  88. omlish/inject/injector.py +40 -0
  89. omlish/inject/inspect.py +14 -0
  90. omlish/inject/keys.py +43 -0
  91. omlish/inject/managed.py +24 -0
  92. omlish/inject/overrides.py +18 -0
  93. omlish/inject/private.py +29 -0
  94. omlish/inject/providers.py +111 -0
  95. omlish/inject/proxy.py +48 -0
  96. omlish/inject/scopes.py +84 -0
  97. omlish/inject/types.py +21 -0
  98. omlish/iterators.py +184 -0
  99. omlish/json.py +194 -0
  100. omlish/lang/__init__.py +112 -0
  101. omlish/lang/cached.py +267 -0
  102. omlish/lang/classes/__init__.py +24 -0
  103. omlish/lang/classes/abstract.py +74 -0
  104. omlish/lang/classes/restrict.py +137 -0
  105. omlish/lang/classes/simple.py +120 -0
  106. omlish/lang/classes/test/__init__.py +0 -0
  107. omlish/lang/classes/test/test_abstract.py +89 -0
  108. omlish/lang/classes/test/test_restrict.py +71 -0
  109. omlish/lang/classes/test/test_simple.py +58 -0
  110. omlish/lang/classes/test/test_virtual.py +72 -0
  111. omlish/lang/classes/virtual.py +130 -0
  112. omlish/lang/clsdct.py +67 -0
  113. omlish/lang/cmp.py +63 -0
  114. omlish/lang/contextmanagers.py +249 -0
  115. omlish/lang/datetimes.py +67 -0
  116. omlish/lang/descriptors.py +52 -0
  117. omlish/lang/functions.py +126 -0
  118. omlish/lang/imports.py +153 -0
  119. omlish/lang/iterables.py +54 -0
  120. omlish/lang/maybes.py +136 -0
  121. omlish/lang/objects.py +103 -0
  122. omlish/lang/resolving.py +50 -0
  123. omlish/lang/strings.py +128 -0
  124. omlish/lang/typing.py +92 -0
  125. omlish/libc.py +532 -0
  126. omlish/logs/__init__.py +9 -0
  127. omlish/logs/_abc.py +247 -0
  128. omlish/logs/configs.py +62 -0
  129. omlish/logs/filters.py +9 -0
  130. omlish/logs/formatters.py +67 -0
  131. omlish/logs/utils.py +20 -0
  132. omlish/marshal/__init__.py +52 -0
  133. omlish/marshal/any.py +25 -0
  134. omlish/marshal/base.py +201 -0
  135. omlish/marshal/base64.py +25 -0
  136. omlish/marshal/dataclasses.py +115 -0
  137. omlish/marshal/datetimes.py +90 -0
  138. omlish/marshal/enums.py +43 -0
  139. omlish/marshal/exceptions.py +7 -0
  140. omlish/marshal/factories.py +129 -0
  141. omlish/marshal/global_.py +33 -0
  142. omlish/marshal/iterables.py +57 -0
  143. omlish/marshal/mappings.py +66 -0
  144. omlish/marshal/naming.py +17 -0
  145. omlish/marshal/objects.py +106 -0
  146. omlish/marshal/optionals.py +49 -0
  147. omlish/marshal/polymorphism.py +147 -0
  148. omlish/marshal/primitives.py +43 -0
  149. omlish/marshal/registries.py +57 -0
  150. omlish/marshal/standard.py +80 -0
  151. omlish/marshal/utils.py +23 -0
  152. omlish/marshal/uuids.py +29 -0
  153. omlish/marshal/values.py +30 -0
  154. omlish/math.py +184 -0
  155. omlish/os.py +32 -0
  156. omlish/reflect.py +359 -0
  157. omlish/replserver/__init__.py +5 -0
  158. omlish/replserver/__main__.py +4 -0
  159. omlish/replserver/console.py +247 -0
  160. omlish/replserver/server.py +146 -0
  161. omlish/runmodule.py +28 -0
  162. omlish/stats.py +342 -0
  163. omlish/term.py +222 -0
  164. omlish/testing/__init__.py +7 -0
  165. omlish/testing/pydevd.py +225 -0
  166. omlish/testing/pytest/__init__.py +8 -0
  167. omlish/testing/pytest/helpers.py +35 -0
  168. omlish/testing/pytest/inject/__init__.py +1 -0
  169. omlish/testing/pytest/inject/harness.py +159 -0
  170. omlish/testing/pytest/plugins/__init__.py +20 -0
  171. omlish/testing/pytest/plugins/_registry.py +6 -0
  172. omlish/testing/pytest/plugins/logging.py +13 -0
  173. omlish/testing/pytest/plugins/pycharm.py +54 -0
  174. omlish/testing/pytest/plugins/repeat.py +19 -0
  175. omlish/testing/pytest/plugins/skips.py +32 -0
  176. omlish/testing/pytest/plugins/spacing.py +19 -0
  177. omlish/testing/pytest/plugins/switches.py +70 -0
  178. omlish/testing/testing.py +102 -0
  179. omlish/text/__init__.py +0 -0
  180. omlish/text/delimit.py +171 -0
  181. omlish/text/indent.py +50 -0
  182. omlish/text/parts.py +265 -0
  183. omlish-0.0.0.dev1.dist-info/LICENSE +21 -0
  184. omlish-0.0.0.dev1.dist-info/METADATA +17 -0
  185. omlish-0.0.0.dev1.dist-info/RECORD +187 -0
  186. omlish-0.0.0.dev1.dist-info/WHEEL +5 -0
  187. omlish-0.0.0.dev1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,150 @@
1
+ import abc
2
+ import dataclasses as dc
3
+ import typing as ta
4
+
5
+ from ... import check
6
+ from ... import lang
7
+ from .fields import preprocess_field
8
+ from .frozen import FrozenProcessor
9
+ from .hashing import HashProcessor
10
+ from .init import InitProcessor
11
+ from .internals import FIELDS_ATTR
12
+ from .internals import PARAMS_ATTR
13
+ from .internals import Params
14
+ from .internals import is_kw_only
15
+ from .order import OrderProcessor
16
+ from .params import ParamsExtras
17
+ from .processing import Processor
18
+ from .reflect import ClassInfo
19
+ from .replace import ReplaceProcessor
20
+ from .repr import ReprProcessor
21
+ from .simple import DocProcessor
22
+ from .simple import EqProcessor
23
+ from .simple import MatchArgsProcessor
24
+ from .simple import OverridesProcessor
25
+ from .slots import add_slots
26
+
27
+ if ta.TYPE_CHECKING:
28
+ from . import metaclass
29
+ else:
30
+ metaclass = lang.proxy_import('.metaclass', __package__)
31
+
32
+
33
+ MISSING = dc.MISSING
34
+
35
+
36
+ class MainProcessor:
37
+ def __init__(self, cls: type) -> None:
38
+ super().__init__()
39
+
40
+ self._cls = check.isinstance(cls, type)
41
+ self._info = info = ClassInfo(cls, _constructing=True)
42
+
43
+ check.not_in(FIELDS_ATTR, cls.__dict__)
44
+ check.is_(check.isinstance(cls.__dict__[PARAMS_ATTR], Params), info.params)
45
+ check.is_(check.isinstance(check.not_none(info.cls_metadata)[ParamsExtras], ParamsExtras), info.params_extras) # noqa
46
+
47
+ def _check_params(self) -> None:
48
+ if self._info.params.order and not self._info.params.eq:
49
+ raise ValueError('eq must be true if order is true')
50
+
51
+ def _check_frozen_bases(self) -> None:
52
+ mc_base = getattr(metaclass, 'Data', None)
53
+ all_frozen_bases = None
54
+ any_frozen_base = False
55
+ has_dataclass_bases = False
56
+ for b in self._cls.__mro__[-1:0:-1]:
57
+ if b is mc_base:
58
+ continue
59
+ base_fields = getattr(b, FIELDS_ATTR, None)
60
+ if base_fields is not None:
61
+ has_dataclass_bases = True
62
+ if all_frozen_bases is None:
63
+ all_frozen_bases = True
64
+ current_frozen = getattr(b, PARAMS_ATTR).frozen
65
+ all_frozen_bases = all_frozen_bases and current_frozen
66
+ any_frozen_base = any_frozen_base or current_frozen
67
+
68
+ if has_dataclass_bases:
69
+ if any_frozen_base and not self._info.params.frozen:
70
+ raise TypeError('cannot inherit non-frozen dataclass from a frozen one')
71
+
72
+ if all_frozen_bases is False and self._info.params.frozen:
73
+ raise TypeError('cannot inherit frozen dataclass from a non-frozen one')
74
+
75
+ @lang.cached_function
76
+ def _process_fields(self) -> None:
77
+ fields: dict[str, dc.Field] = {}
78
+
79
+ for b in self._cls.__mro__[-1:0:-1]:
80
+ base_fields = getattr(b, FIELDS_ATTR, None)
81
+ if base_fields is not None:
82
+ for f in base_fields.values():
83
+ fields[f.name] = f
84
+
85
+ cls_fields: list[dc.Field] = []
86
+
87
+ kw_only = self._info.params12.kw_only
88
+ kw_only_seen = False
89
+ for name, ann in self._info.cls_annotations.items():
90
+ if is_kw_only(self._cls, ann):
91
+ if kw_only_seen:
92
+ raise TypeError(f'{name!r} is KW_ONLY, but KW_ONLY has already been specified')
93
+ kw_only_seen = True
94
+ kw_only = True
95
+ else:
96
+ cls_fields.append(preprocess_field(self._cls, name, ann, kw_only))
97
+
98
+ for f in cls_fields:
99
+ fields[f.name] = f
100
+ if isinstance(getattr(self._cls, f.name, None), dc.Field):
101
+ if f.default is MISSING:
102
+ delattr(self._cls, f.name)
103
+ else:
104
+ setattr(self._cls, f.name, f.default)
105
+
106
+ for name, value in self._cls.__dict__.items():
107
+ if isinstance(value, dc.Field) and name not in self._info.cls_annotations:
108
+ raise TypeError(f'{name!r} is a field but has no type annotation')
109
+
110
+ setattr(self._cls, FIELDS_ATTR, fields)
111
+
112
+ @lang.cached_function
113
+ def _transform_slots(self) -> None:
114
+ if self._info.params12.weakref_slot and not self._info.params12.slots:
115
+ raise TypeError('weakref_slot is True but slots is False')
116
+ if not self._info.params12.slots:
117
+ return
118
+ self._cls = add_slots(self._cls, self._info.params.frozen, self._info.params12.weakref_slot)
119
+
120
+ @lang.cached_function
121
+ def process(self) -> type:
122
+ self._check_params()
123
+ self._check_frozen_bases()
124
+
125
+ self._process_fields()
126
+
127
+ pcls: type[Processor]
128
+ for pcls in [
129
+ InitProcessor,
130
+ OverridesProcessor,
131
+ ReprProcessor,
132
+ EqProcessor,
133
+ OrderProcessor,
134
+ FrozenProcessor,
135
+ HashProcessor,
136
+ DocProcessor,
137
+ MatchArgsProcessor,
138
+ ReplaceProcessor,
139
+ ]:
140
+ pcls(self._info).process()
141
+
142
+ self._transform_slots()
143
+
144
+ abc.update_abstractmethods(self._cls) # noqa
145
+
146
+ return self._cls
147
+
148
+
149
+ def process_class(cls: type) -> type:
150
+ return MainProcessor(cls).process()
@@ -0,0 +1,126 @@
1
+ """
2
+ TODO:
3
+ - Enum
4
+ """
5
+ import abc
6
+ import collections
7
+ import dataclasses as dc
8
+ import typing as ta
9
+
10
+ from ... import lang
11
+ from .api import dataclass
12
+ from .api import field # noqa
13
+ from .params import MetaclassParams
14
+ from .params import get_metaclass_params
15
+ from .params import get_params
16
+
17
+
18
+ T = ta.TypeVar('T')
19
+
20
+
21
+ def confer_kwarg(out: dict[str, ta.Any], k: str, v: ta.Any) -> None:
22
+ if k in out:
23
+ if out[k] != v:
24
+ raise ValueError
25
+ else:
26
+ out[k] = v
27
+
28
+
29
+ def confer_kwargs(
30
+ bases: ta.Sequence[type],
31
+ kwargs: ta.Mapping[str, ta.Any],
32
+ ) -> dict[str, ta.Any]:
33
+ out: dict[str, ta.Any] = {}
34
+ for base in bases:
35
+ if not dc.is_dataclass(base):
36
+ continue
37
+ if not (bmp := get_metaclass_params(base)).confer:
38
+ continue
39
+ for ck in bmp.confer:
40
+ if ck in kwargs:
41
+ continue
42
+ if ck in ('frozen', 'generic_init', 'kw_only'):
43
+ confer_kwarg(out, ck, get_params(base).frozen)
44
+ elif ck == 'confer':
45
+ confer_kwarg(out, 'confer', bmp.confer)
46
+ else:
47
+ raise KeyError(ck)
48
+ return out
49
+
50
+
51
+ class DataMeta(abc.ABCMeta):
52
+ def __new__(
53
+ mcls,
54
+ name,
55
+ bases,
56
+ namespace,
57
+ *,
58
+
59
+ # confer=frozenset(),
60
+
61
+ metadata=None,
62
+ **kwargs
63
+ ):
64
+ cls = lang.super_meta(
65
+ super(),
66
+ mcls,
67
+ name,
68
+ bases,
69
+ namespace,
70
+ )
71
+
72
+ ckw = confer_kwargs(bases, kwargs)
73
+ nkw = {**kwargs, **ckw}
74
+
75
+ mcp = MetaclassParams(
76
+ confer=nkw.pop('confer', frozenset()),
77
+ )
78
+
79
+ mmd = {
80
+ MetaclassParams: mcp,
81
+ }
82
+ if metadata is not None:
83
+ metadata = collections.ChainMap(mmd, metadata)
84
+ else:
85
+ metadata = mmd
86
+
87
+ return dataclass(cls, metadata=metadata, **nkw)
88
+
89
+
90
+ # @ta.dataclass_transform(field_specifiers=(field,)) # FIXME: ctor
91
+ class Data(metaclass=DataMeta):
92
+ def __init__(self, *args, **kwargs):
93
+ super().__init__(*args, **kwargs)
94
+
95
+ def __post_init__(self, *args, **kwargs) -> None:
96
+ try:
97
+ spi = super().__post_init__ # type: ignore # noqa
98
+ except AttributeError:
99
+ if args or kwargs:
100
+ raise TypeError(args, kwargs)
101
+ else:
102
+ spi(*args, **kwargs)
103
+
104
+
105
+ class Frozen(
106
+ Data,
107
+ frozen=True,
108
+ confer=frozenset([
109
+ 'frozen',
110
+ 'confer',
111
+ ]),
112
+ ):
113
+ pass
114
+
115
+
116
+ class Box(
117
+ Frozen,
118
+ ta.Generic[T],
119
+ generic_init=True,
120
+ confer=frozenset([
121
+ 'frozen',
122
+ 'generic_init',
123
+ 'confer',
124
+ ]),
125
+ ):
126
+ v: T
@@ -0,0 +1,74 @@
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 Check(lang.Marker):
58
+ pass
59
+
60
+
61
+ def check(fn: ta.Union[ta.Callable[..., bool], staticmethod]) -> None:
62
+ _append_cls_md(Check, fn)
63
+
64
+
65
+ ##
66
+
67
+
68
+ @_class_merged
69
+ class Init(lang.Marker):
70
+ pass
71
+
72
+
73
+ def init(fn: ta.Callable[..., None]) -> None:
74
+ _append_cls_md(Init, fn)
@@ -0,0 +1,47 @@
1
+ import typing as ta
2
+
3
+ from .utils import Namespace
4
+ from .processing import Processor
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,
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(self._cls, name, cmp_fn(name, op, self_tuple, other_tuple, globals=self._info.globals)): # noqa
44
+ raise TypeError(
45
+ f'Cannot overwrite attribute {name} in class {self._cls.__name__}. '
46
+ f'Consider using functools.total_ordering'
47
+ )
@@ -0,0 +1,150 @@
1
+ """
2
+ Field:
3
+ name: str | None = None
4
+ type: Any = None
5
+ default: Any | MISSING = MISSING
6
+ default_factory: Any | MISSING = MISSING
7
+ repr: bool = True
8
+ hash: bool | None = None
9
+ init: bool = True
10
+ compare: bool = True
11
+ metadata: Metadata | None = None
12
+ kw_only: bool | MISSING = MISSING
13
+
14
+ _field_type: Any = None
15
+
16
+
17
+ Params:
18
+ init: bool = True
19
+ repr: bool = True
20
+ eq: bool = True
21
+ order: bool = False
22
+ unsafe_hash: bool = False
23
+ frozen: bool = False
24
+
25
+ match_args: bool = True
26
+ kw_only: bool = False
27
+ slots: bool = False
28
+ weakref_slot: bool = False
29
+ """
30
+ import dataclasses as dc
31
+ import sys
32
+ import typing as ta
33
+
34
+ from ... import lang
35
+ from .internals import PARAMS_ATTR
36
+ from .internals import Params
37
+ from .metadata import EMPTY_METADATA
38
+ from .metadata import METADATA_ATTR
39
+
40
+
41
+ IS_12 = sys.version_info[1] >= 12
42
+
43
+
44
+ ##
45
+
46
+
47
+ @dc.dataclass(frozen=True)
48
+ class FieldExtras(lang.Final):
49
+ coerce: ta.Optional[ta.Union[bool, ta.Callable[[ta.Any], ta.Any]]] = None
50
+ check: ta.Optional[ta.Callable[[ta.Any], bool]] = None
51
+ check_type: ta.Optional[bool] = None
52
+ override: bool = False
53
+
54
+
55
+ DEFAULT_FIELD_EXTRAS = FieldExtras()
56
+
57
+
58
+ def get_field_extras(f: dc.Field) -> FieldExtras:
59
+ if not isinstance(f, dc.Field):
60
+ raise TypeError(f)
61
+ return f.metadata.get(FieldExtras, DEFAULT_FIELD_EXTRAS)
62
+
63
+
64
+ ##
65
+
66
+
67
+ def get_params_cls(obj: ta.Any) -> type | None:
68
+ if not isinstance(obj, type):
69
+ obj = type(obj)
70
+ for cur in obj.__mro__:
71
+ if PARAMS_ATTR in cur.__dict__:
72
+ return cur
73
+ return None
74
+
75
+
76
+ def get_params(obj: ta.Any) -> Params:
77
+ if not hasattr(obj, PARAMS_ATTR):
78
+ raise TypeError(obj)
79
+ return getattr(obj, PARAMS_ATTR)
80
+
81
+
82
+ ##
83
+
84
+
85
+ @dc.dataclass(frozen=True)
86
+ class Params12(lang.Final):
87
+ match_args: bool = True
88
+ kw_only: bool = False
89
+ slots: bool = False
90
+ weakref_slot: bool = False
91
+
92
+
93
+ DEFAULT_PARAMS12 = Params12()
94
+
95
+
96
+ def get_params12(obj: ta.Any) -> Params12:
97
+ if IS_12:
98
+ p = get_params(obj)
99
+ return Params12(
100
+ match_args=p.match_args,
101
+ kw_only=p.kw_only,
102
+ slots=p.slots,
103
+ weakref_slot=p.weakref_slot,
104
+ )
105
+
106
+ if (pcls := get_params_cls(obj)) is None:
107
+ raise TypeError(pcls)
108
+
109
+ md = pcls.__dict__.get(METADATA_ATTR, EMPTY_METADATA)
110
+ return md.get(Params12, DEFAULT_PARAMS12)
111
+
112
+
113
+ ##
114
+
115
+
116
+ @dc.dataclass(frozen=True)
117
+ class ParamsExtras(lang.Final):
118
+ reorder: bool = False
119
+ cache_hash: bool = False
120
+ generic_init: bool = False
121
+
122
+
123
+ DEFAULT_PARAMS_EXTRAS = ParamsExtras()
124
+
125
+
126
+ def get_params_extras(obj: ta.Any) -> ParamsExtras:
127
+ if (pcls := get_params_cls(obj)) is None:
128
+ raise TypeError(pcls)
129
+
130
+ md = pcls.__dict__.get(METADATA_ATTR, EMPTY_METADATA)
131
+ return md.get(ParamsExtras, DEFAULT_PARAMS_EXTRAS)
132
+
133
+
134
+ ##
135
+
136
+
137
+ @dc.dataclass(frozen=True)
138
+ class MetaclassParams:
139
+ confer: frozenset[str] = frozenset()
140
+
141
+
142
+ DEFAULT_METACLASS_PARAMS = MetaclassParams()
143
+
144
+
145
+ def get_metaclass_params(obj: ta.Any) -> MetaclassParams:
146
+ if (pcls := get_params_cls(obj)) is None:
147
+ raise TypeError(pcls)
148
+
149
+ md = pcls.__dict__.get(METADATA_ATTR, EMPTY_METADATA)
150
+ return md.get(MetaclassParams, DEFAULT_METACLASS_PARAMS)
@@ -0,0 +1,16 @@
1
+ from ... import lang
2
+ from .reflect import ClassInfo
3
+
4
+
5
+ class Processor(lang.Abstract):
6
+ def __init__(self, info: ClassInfo) -> None:
7
+ super().__init__()
8
+ self._cls = info.cls
9
+ self._info = info
10
+
11
+ @lang.cached_function
12
+ def process(self) -> None:
13
+ self._process()
14
+
15
+ def _process(self) -> None:
16
+ raise NotImplementedError