omlish 0.0.0.dev318__py3-none-any.whl → 0.0.0.dev319__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/dispatch/methods.py +84 -19
- omlish/lang/__init__.py +9 -0
- omlish/lang/overrides.py +82 -0
- {omlish-0.0.0.dev318.dist-info → omlish-0.0.0.dev319.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev318.dist-info → omlish-0.0.0.dev319.dist-info}/RECORD +10 -9
- {omlish-0.0.0.dev318.dist-info → omlish-0.0.0.dev319.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev318.dist-info → omlish-0.0.0.dev319.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev318.dist-info → omlish-0.0.0.dev319.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev318.dist-info → omlish-0.0.0.dev319.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/dispatch/methods.py
CHANGED
@@ -29,6 +29,7 @@ class Method(ta.Generic[P, R]):
|
|
29
29
|
func: ta.Callable,
|
30
30
|
*,
|
31
31
|
installable: bool = False,
|
32
|
+
requires_override: bool = False,
|
32
33
|
) -> None:
|
33
34
|
super().__init__()
|
34
35
|
|
@@ -37,6 +38,7 @@ class Method(ta.Generic[P, R]):
|
|
37
38
|
|
38
39
|
self._func = func
|
39
40
|
self._installable = installable
|
41
|
+
self._requires_override = requires_override
|
40
42
|
|
41
43
|
self._impls: ta.MutableMapping[ta.Callable, frozenset[type] | None] = weakref.WeakKeyDictionary()
|
42
44
|
|
@@ -97,33 +99,73 @@ class Method(ta.Generic[P, R]):
|
|
97
99
|
|
98
100
|
return impl
|
99
101
|
|
102
|
+
def _is_impl(self, obj: ta.Any) -> bool:
|
103
|
+
try:
|
104
|
+
hash(obj)
|
105
|
+
except TypeError:
|
106
|
+
return False
|
107
|
+
|
108
|
+
return obj in self._impls
|
109
|
+
|
100
110
|
def build_attr_dispatcher(self, instance_cls: type, owner_cls: type | None = None) -> Dispatcher[str]:
|
101
|
-
|
111
|
+
if owner_cls is None:
|
112
|
+
owner_cls = instance_cls
|
102
113
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
114
|
+
mro = instance_cls.__mro__[-2::-1]
|
115
|
+
try:
|
116
|
+
mro_pos = mro.index(owner_cls)
|
117
|
+
except ValueError:
|
118
|
+
raise TypeError(f'Owner class {owner_cls} not in mro of instance class {instance_cls}') from None
|
119
|
+
|
120
|
+
mro_dct: dict[str, list[tuple[type, ta.Any]]] = {}
|
121
|
+
for cur_cls in mro[:mro_pos + 1]:
|
122
|
+
for att, obj in cur_cls.__dict__.items():
|
123
|
+
if att not in mro_dct:
|
124
|
+
if not self._is_impl(obj):
|
125
|
+
continue
|
126
|
+
|
127
|
+
try:
|
128
|
+
lst = mro_dct[att]
|
129
|
+
except KeyError:
|
130
|
+
lst = mro_dct[att] = []
|
131
|
+
lst.append((cur_cls, obj))
|
110
132
|
|
111
|
-
|
133
|
+
#
|
134
|
+
|
135
|
+
disp: Dispatcher[str] = Dispatcher()
|
136
|
+
|
137
|
+
seen: dict[ta.Any, str] = {}
|
138
|
+
for att, lst in mro_dct.items():
|
139
|
+
if not lst:
|
140
|
+
raise RuntimeError
|
141
|
+
_, obj = lst[-1]
|
142
|
+
|
143
|
+
if len(lst) > 1:
|
144
|
+
if self._requires_override and not lang.is_override(obj):
|
145
|
+
raise lang.RequiresOverrideError(
|
146
|
+
att,
|
147
|
+
instance_cls,
|
148
|
+
lst[-1][0],
|
149
|
+
lst[0][0],
|
150
|
+
)
|
151
|
+
|
152
|
+
if not self._is_impl(obj):
|
112
153
|
continue
|
113
|
-
cls_set = self._impls[att]
|
114
154
|
|
155
|
+
cls_set = self._impls[obj]
|
115
156
|
if cls_set is None:
|
116
|
-
cls_set = get_impl_func_cls_set(
|
117
|
-
self._impls[
|
157
|
+
cls_set = get_impl_func_cls_set(obj, arg_offset=1)
|
158
|
+
self._impls[obj] = cls_set
|
118
159
|
|
119
160
|
try:
|
120
|
-
|
161
|
+
ex_att = seen[obj]
|
121
162
|
except KeyError:
|
122
163
|
pass
|
123
164
|
else:
|
124
|
-
raise TypeError(f'Duplicate impl: {owner_cls} {instance_cls} {
|
165
|
+
raise TypeError(f'Duplicate impl: {owner_cls} {instance_cls} {att} {ex_att}')
|
166
|
+
seen[obj] = att
|
125
167
|
|
126
|
-
disp.register(
|
168
|
+
disp.register(att, cls_set)
|
127
169
|
|
128
170
|
return disp
|
129
171
|
|
@@ -186,19 +228,42 @@ class Method(ta.Generic[P, R]):
|
|
186
228
|
|
187
229
|
|
188
230
|
@ta.overload
|
189
|
-
def method(
|
231
|
+
def method(
|
232
|
+
func: ta.Callable[P, R],
|
233
|
+
/,
|
234
|
+
*,
|
235
|
+
installable: bool = False,
|
236
|
+
requires_override: bool = False,
|
237
|
+
) -> Method[P, R]: # noqa
|
190
238
|
...
|
191
239
|
|
192
240
|
|
193
241
|
@ta.overload
|
194
|
-
def method(
|
242
|
+
def method(
|
243
|
+
func: None = None,
|
244
|
+
/,
|
245
|
+
*,
|
246
|
+
installable: bool = False,
|
247
|
+
requires_override: bool = False,
|
248
|
+
) -> ta.Callable[[ta.Callable[P, R]], Method[P, R]]: # noqa
|
195
249
|
...
|
196
250
|
|
197
251
|
|
198
|
-
def method(
|
199
|
-
|
252
|
+
def method(
|
253
|
+
func=None,
|
254
|
+
/,
|
255
|
+
*,
|
256
|
+
installable=False,
|
257
|
+
requires_override=False,
|
258
|
+
):
|
259
|
+
kw = dict(
|
260
|
+
installable=installable,
|
261
|
+
requires_override=requires_override,
|
262
|
+
)
|
263
|
+
|
200
264
|
if func is None:
|
201
265
|
return functools.partial(Method, **kw)
|
266
|
+
|
202
267
|
return Method(func, **kw)
|
203
268
|
|
204
269
|
|
omlish/lang/__init__.py
CHANGED
@@ -252,6 +252,15 @@ from .outcomes import ( # noqa
|
|
252
252
|
value,
|
253
253
|
)
|
254
254
|
|
255
|
+
from .overrides import ( # noqa
|
256
|
+
needs_override,
|
257
|
+
|
258
|
+
is_override,
|
259
|
+
|
260
|
+
RequiresOverrideError,
|
261
|
+
RequiresOverride,
|
262
|
+
)
|
263
|
+
|
255
264
|
from .params import ( # noqa
|
256
265
|
ArgsParam,
|
257
266
|
KwOnlyParam,
|
omlish/lang/overrides.py
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
import types
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from .descriptors import unwrap_func
|
5
|
+
|
6
|
+
|
7
|
+
##
|
8
|
+
|
9
|
+
|
10
|
+
class _EmptyClass:
|
11
|
+
pass
|
12
|
+
|
13
|
+
|
14
|
+
_OVERRIDE_NOT_NEEDED_ATTRS: ta.AbstractSet[str] = {
|
15
|
+
*_EmptyClass.__dict__,
|
16
|
+
}
|
17
|
+
|
18
|
+
|
19
|
+
def needs_override(attr: str, obj: ta.Any) -> bool:
|
20
|
+
if attr in _OVERRIDE_NOT_NEEDED_ATTRS:
|
21
|
+
return False
|
22
|
+
|
23
|
+
return True
|
24
|
+
|
25
|
+
|
26
|
+
##
|
27
|
+
|
28
|
+
|
29
|
+
def is_override(obj: ta.Any) -> bool:
|
30
|
+
if isinstance(obj, types.FunctionType):
|
31
|
+
fn = unwrap_func(obj)
|
32
|
+
if getattr(fn, '__override__', False):
|
33
|
+
return True
|
34
|
+
|
35
|
+
return False
|
36
|
+
|
37
|
+
|
38
|
+
##
|
39
|
+
|
40
|
+
|
41
|
+
class RequiresOverrideError(TypeError):
|
42
|
+
def __init__(
|
43
|
+
self,
|
44
|
+
att: str | None = None,
|
45
|
+
cls: type | None = None,
|
46
|
+
mro_cls: type | None = None,
|
47
|
+
owner_cls: type | None = None,
|
48
|
+
) -> None:
|
49
|
+
super().__init__(' '.join([
|
50
|
+
'Attribute',
|
51
|
+
*([f'{att!r}'] if att is not None else []),
|
52
|
+
*([f'on class {cls.__qualname__}'] if cls is not None else []),
|
53
|
+
*([f'from mro class {mro_cls.__qualname__}'] if mro_cls is not None and mro_cls is not cls else []),
|
54
|
+
f'is not marked as a @typing.override',
|
55
|
+
*([f'from owning class {owner_cls.__qualname__}'] if owner_cls is not None else []),
|
56
|
+
]))
|
57
|
+
|
58
|
+
self.att = att
|
59
|
+
self.cls = cls
|
60
|
+
self.mro_cls = mro_cls
|
61
|
+
self.owner_cls = owner_cls
|
62
|
+
|
63
|
+
|
64
|
+
class RequiresOverride:
|
65
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
66
|
+
super().__init_subclass__(**kwargs)
|
67
|
+
|
68
|
+
req_ovr_dct: dict[str, type] = {}
|
69
|
+
for m_cls in reversed(cls.__mro__):
|
70
|
+
for a, o in m_cls.__dict__.items():
|
71
|
+
if a in req_ovr_dct and not is_override(o):
|
72
|
+
raise RequiresOverrideError(
|
73
|
+
a,
|
74
|
+
cls,
|
75
|
+
m_cls,
|
76
|
+
req_ovr_dct[a],
|
77
|
+
)
|
78
|
+
|
79
|
+
if RequiresOverride in m_cls.__bases__:
|
80
|
+
for a, o in m_cls.__dict__.items():
|
81
|
+
if needs_override(a, o):
|
82
|
+
req_ovr_dct.setdefault(a, m_cls)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
omlish/.manifests.json,sha256=orgsRvtpHu8tdhaCvlP9v3P495OJopYYiHKjK68WtWg,8587
|
2
|
-
omlish/__about__.py,sha256=
|
2
|
+
omlish/__about__.py,sha256=BUm8C0RUF_mH5RRsEW0hH9yf1ygPXOnka9jzpOJkyck,3478
|
3
3
|
omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
|
4
4
|
omlish/c3.py,sha256=rer-TPOFDU6fYq_AWio_AmA-ckZ8JDY5shIzQ_yXfzA,8414
|
5
5
|
omlish/cached.py,sha256=MLap_p0rdGoDIMVhXVHm1tsbcWobJF0OanoodV03Ju8,542
|
@@ -225,7 +225,7 @@ omlish/dispatch/_dispatch3.py,sha256=9Zjd7bINAC3keiaBdssc4v5dY0-8OI6XooV2DR9U7Z0
|
|
225
225
|
omlish/dispatch/dispatch.py,sha256=KA5l49AiGLRjp4J7RDJW9RiDp9WUD1ewR1AOPEF8g38,3062
|
226
226
|
omlish/dispatch/functions.py,sha256=cwNzGIg2ZIalEgn9I03cnJVbMTHjWloyDTaowlO3UPs,1524
|
227
227
|
omlish/dispatch/impls.py,sha256=K_okKvpZml4NkTHJmTVyMQSrIaIJcqTEgkreGwukaOw,1895
|
228
|
-
omlish/dispatch/methods.py,sha256=
|
228
|
+
omlish/dispatch/methods.py,sha256=sSGA52swdrdHEbm_jAPBZVoKP9xMIfVcw2MnTvIJtrI,8913
|
229
229
|
omlish/docker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
230
230
|
omlish/docker/all.py,sha256=xXRgJgLGPwAtr7bDMJ_Dp9jTfOwfGvohNhc6LsoELJc,514
|
231
231
|
omlish/docker/cli.py,sha256=gtb9kitVfGnd4cr587NsVVk8D5Ok5y5SAsqD_SwGrSA,2565
|
@@ -403,7 +403,7 @@ omlish/iterators/iterators.py,sha256=RxW35yQ5ed8vBQ22IqpDXFx-i5JiLQdp7-pkMZXhJJ8
|
|
403
403
|
omlish/iterators/recipes.py,sha256=wOwOZg-zWG9Zc3wcAxJFSe2rtavVBYwZOfG09qYEx_4,472
|
404
404
|
omlish/iterators/tools.py,sha256=c4hArZEVV8y9_dFfmRwakusv1cWJLT4MkTkGRjnGN5U,2556
|
405
405
|
omlish/iterators/unique.py,sha256=Nw0pSaNEcHAkve0ugfLPvJcirDOn9ECyC5wIL8JlJKI,1395
|
406
|
-
omlish/lang/__init__.py,sha256=
|
406
|
+
omlish/lang/__init__.py,sha256=ZtAmS2HiXPaJexoyPXD2fCl-8DNA4s8zlErlvhU_2o0,6045
|
407
407
|
omlish/lang/attrs.py,sha256=i7euRF81uNF8QDmUVXSK_BtqLGshaMi4VVdUnMjiMwg,5050
|
408
408
|
omlish/lang/casing.py,sha256=cFUlbDdXLhwnWwcYx4qnM5c4zGX7hIRUfcjiZbxUD28,4636
|
409
409
|
omlish/lang/clsdct.py,sha256=HAGIvBSbCefzRjXriwYSBLO7QHKRv2UsE78jixOb-fA,1828
|
@@ -419,6 +419,7 @@ omlish/lang/imports.py,sha256=y9W9Y-d_cQ35QCLuSIPoa6vnEqSErFCz8b-34IH128U,10552
|
|
419
419
|
omlish/lang/iterables.py,sha256=StoGp9yaP3njdLKHoWYcEevO3eE8SHEPYl5_avZob24,2149
|
420
420
|
omlish/lang/objects.py,sha256=nbxBHfQHVw0OG4qeSTP2GvIiFIcH2tbbitY8y-mYPPo,5959
|
421
421
|
omlish/lang/outcomes.py,sha256=mpFy_VoM-b74L1aCFsjsZVUHx_icZ1AHMOKeVesjOp4,8628
|
422
|
+
omlish/lang/overrides.py,sha256=IBzK6ljfLX6TLgIyKTSjhqTLcuKRkQNVtEOnBLS4nuA,2095
|
422
423
|
omlish/lang/params.py,sha256=sfbNoGrKCsAtubFufj_uh_WKshIgA8fqJ4PmLH1PH00,6639
|
423
424
|
omlish/lang/recursion.py,sha256=1VfSqzKO-8Is3t9LKw0W4jwPfE0aBS70EUlbUxAx4eE,1900
|
424
425
|
omlish/lang/resolving.py,sha256=ei9LDyJexsMMHB9z8diUkNmynWhd_da7h7TqrMYM6lA,1611
|
@@ -853,9 +854,9 @@ omlish/typedvalues/holder.py,sha256=ZTnHiw-K38ciOBLEdwgrltr7Xp8jjEs_0Lp69DH-G-o,
|
|
853
854
|
omlish/typedvalues/marshal.py,sha256=hWHRLcrGav7lvXJDtb9bNI0ickl4SKPQ6F4BbTpqw3A,4219
|
854
855
|
omlish/typedvalues/reflect.py,sha256=Ih1YgU-srUjsvBn_P7C66f73_VCvcwqE3ffeBnZBgt4,674
|
855
856
|
omlish/typedvalues/values.py,sha256=ym46I-q2QJ_6l4UlERqv3yj87R-kp8nCKMRph0xQ3UA,1307
|
856
|
-
omlish-0.0.0.
|
857
|
-
omlish-0.0.0.
|
858
|
-
omlish-0.0.0.
|
859
|
-
omlish-0.0.0.
|
860
|
-
omlish-0.0.0.
|
861
|
-
omlish-0.0.0.
|
857
|
+
omlish-0.0.0.dev319.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
858
|
+
omlish-0.0.0.dev319.dist-info/METADATA,sha256=AJfjWjvSfK3uESd-jYGGUmEgPjkW22rJLSBh3h6hGbk,4416
|
859
|
+
omlish-0.0.0.dev319.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
860
|
+
omlish-0.0.0.dev319.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
|
861
|
+
omlish-0.0.0.dev319.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
|
862
|
+
omlish-0.0.0.dev319.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|