omlish 0.0.0.dev470__py3-none-any.whl → 0.0.0.dev472__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/inject/__init__.py +4 -0
- omlish/inject/elements.py +17 -0
- omlish/inject/impl/elements.py +17 -2
- omlish/inject/impl/injector.py +17 -19
- omlish/inject/impl/maysync.py +3 -4
- omlish/inject/impl/sync.py +3 -4
- omlish/inject/injector.py +31 -2
- omlish/inject/maysync.py +2 -4
- omlish/inject/sync.py +5 -4
- omlish/lang/imports/proxy.py +10 -1
- omlish/lite/abstract.py +54 -24
- omlish/lite/dataclasses.py +14 -0
- omlish/term/pager.py +235 -0
- omlish/term/terminfo.py +935 -0
- omlish/term/termstate.py +67 -0
- omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
- {omlish-0.0.0.dev470.dist-info → omlish-0.0.0.dev472.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev470.dist-info → omlish-0.0.0.dev472.dist-info}/RECORD +24 -21
- /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
- {omlish-0.0.0.dev470.dist-info → omlish-0.0.0.dev472.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev470.dist-info → omlish-0.0.0.dev472.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev470.dist-info → omlish-0.0.0.dev472.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev470.dist-info → omlish-0.0.0.dev472.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/inject/__init__.py
CHANGED
omlish/inject/elements.py
CHANGED
|
@@ -6,6 +6,12 @@ from .. import lang
|
|
|
6
6
|
from .impl.origins import HasOriginsImpl
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
if ta.TYPE_CHECKING:
|
|
10
|
+
from .impl import elements as _elements
|
|
11
|
+
else:
|
|
12
|
+
_elements = lang.proxy_import('.impl.elements', __package__)
|
|
13
|
+
|
|
14
|
+
|
|
9
15
|
##
|
|
10
16
|
|
|
11
17
|
|
|
@@ -74,3 +80,14 @@ def iter_elements(*args: Elemental) -> ta.Iterator[Element]:
|
|
|
74
80
|
yield from a
|
|
75
81
|
else:
|
|
76
82
|
raise TypeError(a)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
##
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class CollectedElements(lang.PackageSealed, lang.Abstract):
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def collect_elements(es: Elements | CollectedElements) -> CollectedElements:
|
|
93
|
+
return _elements.collect_elements(es)
|
omlish/inject/impl/elements.py
CHANGED
|
@@ -27,6 +27,7 @@ from ... import collections as col
|
|
|
27
27
|
from ... import lang
|
|
28
28
|
from ..bindings import Binding
|
|
29
29
|
from ..eagers import Eager
|
|
30
|
+
from ..elements import CollectedElements
|
|
30
31
|
from ..elements import Element
|
|
31
32
|
from ..elements import Elements
|
|
32
33
|
from ..errors import ConflictingKeyError
|
|
@@ -47,7 +48,7 @@ from .multis import make_multi_provider_impl
|
|
|
47
48
|
from .origins import Origins
|
|
48
49
|
from .origins import set_origins
|
|
49
50
|
from .providers import ProviderImpl
|
|
50
|
-
from .
|
|
51
|
+
from .providersmap import make_provider_impl
|
|
51
52
|
from .scopes import make_scope_impl
|
|
52
53
|
|
|
53
54
|
|
|
@@ -80,7 +81,7 @@ _NON_BINDING_ELEMENT_TYPES: tuple[type[Element], ...] = (
|
|
|
80
81
|
)
|
|
81
82
|
|
|
82
83
|
|
|
83
|
-
class ElementCollection(lang.Final):
|
|
84
|
+
class ElementCollection(CollectedElements, lang.Final):
|
|
84
85
|
def __init__(self, es: Elements) -> None:
|
|
85
86
|
super().__init__()
|
|
86
87
|
|
|
@@ -208,6 +209,10 @@ class ElementCollection(lang.Final):
|
|
|
208
209
|
|
|
209
210
|
##
|
|
210
211
|
|
|
212
|
+
@lang.cached_function
|
|
213
|
+
def scope_binding_scopes(self) -> ta.Sequence[Scope]:
|
|
214
|
+
return [sb.scope for sb in self.elements_of_type(ScopeBinding)]
|
|
215
|
+
|
|
211
216
|
@lang.cached_function
|
|
212
217
|
def eager_keys_by_scope(self) -> ta.Mapping[Scope, ta.Sequence[Key]]:
|
|
213
218
|
bim = self.binding_impl_map()
|
|
@@ -216,3 +221,13 @@ class ElementCollection(lang.Final):
|
|
|
216
221
|
bi = bim[e.key]
|
|
217
222
|
ret.setdefault(bi.scope, []).append(e.key)
|
|
218
223
|
return ret
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
##
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def collect_elements(es: Elements | CollectedElements) -> ElementCollection:
|
|
230
|
+
if isinstance(es, CollectedElements):
|
|
231
|
+
return check.isinstance(es, ElementCollection)
|
|
232
|
+
else:
|
|
233
|
+
return ElementCollection(es)
|
omlish/inject/impl/injector.py
CHANGED
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
TODO:
|
|
3
3
|
- ** can currently bind in a child/private scope shadowing an external parent binding **
|
|
4
4
|
- better source tracking
|
|
5
|
-
- cache/export ElementCollections lol
|
|
6
5
|
- scope bindings, auto in root
|
|
7
6
|
- injector-internal / blacklisted bindings (Injector itself, default scopes) without rebuilding ElementCollection
|
|
8
7
|
- config - proxies, impl select, etc
|
|
9
8
|
- config is probably shared with ElementCollection... but not 'bound', must be shared everywhere
|
|
10
9
|
- InjectorRoot object?
|
|
11
10
|
- ** eagers in any scope, on scope init/open
|
|
12
|
-
- injection listeners
|
|
13
11
|
- unions - raise on ambiguous - usecase: sql.AsyncEngineLike
|
|
14
12
|
- multiple live request scopes on single injector - use private injectors?
|
|
15
13
|
- more listeners - UnboundKeyListener
|
|
@@ -24,7 +22,7 @@ import weakref
|
|
|
24
22
|
from ... import check
|
|
25
23
|
from ... import lang
|
|
26
24
|
from ...logs import all as logs
|
|
27
|
-
from ..elements import
|
|
25
|
+
from ..elements import CollectedElements
|
|
28
26
|
from ..errors import CyclicDependencyError
|
|
29
27
|
from ..errors import UnboundKeyError
|
|
30
28
|
from ..injector import AsyncInjector
|
|
@@ -33,7 +31,6 @@ from ..keys import Key
|
|
|
33
31
|
from ..keys import as_key
|
|
34
32
|
from ..listeners import ProvisionListener
|
|
35
33
|
from ..listeners import ProvisionListenerBinding
|
|
36
|
-
from ..scopes import ScopeBinding
|
|
37
34
|
from ..scopes import Singleton
|
|
38
35
|
from ..scopes import ThreadScope
|
|
39
36
|
from ..types import Scope
|
|
@@ -61,14 +58,12 @@ DEFAULT_SCOPES: list[Scope] = [
|
|
|
61
58
|
class AsyncInjectorImpl(AsyncInjector, lang.Final):
|
|
62
59
|
def __init__(
|
|
63
60
|
self,
|
|
64
|
-
ec:
|
|
61
|
+
ec: CollectedElements,
|
|
65
62
|
p: ta.Optional['AsyncInjectorImpl'] = None,
|
|
66
63
|
*,
|
|
67
64
|
internal_consts: dict[Key, ta.Any] | None = None,
|
|
68
65
|
) -> None:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
self._ec = check.isinstance(ec, ElementCollection)
|
|
66
|
+
self._ec = (ec := check.isinstance(ec, ElementCollection))
|
|
72
67
|
self._p: AsyncInjectorImpl | None = check.isinstance(p, (AsyncInjectorImpl, None))
|
|
73
68
|
|
|
74
69
|
self._internal_consts: dict[Key, ta.Any] = {
|
|
@@ -77,28 +72,31 @@ class AsyncInjectorImpl(AsyncInjector, lang.Final):
|
|
|
77
72
|
}
|
|
78
73
|
|
|
79
74
|
self._bim = ec.binding_impl_map()
|
|
75
|
+
|
|
80
76
|
self._ekbs = ec.eager_keys_by_scope()
|
|
77
|
+
|
|
81
78
|
self._pls: tuple[ProvisionListener, ...] = tuple(
|
|
82
79
|
b.listener # type: ignore[attr-defined]
|
|
83
80
|
for b in itertools.chain(
|
|
84
81
|
ec.elements_of_type(ProvisionListenerBinding),
|
|
85
|
-
|
|
82
|
+
p._pls if p is not None else (), # noqa
|
|
86
83
|
)
|
|
87
84
|
)
|
|
88
85
|
|
|
89
|
-
self._cs: weakref.WeakSet[AsyncInjectorImpl] | None = None # noqa
|
|
90
86
|
self._root: AsyncInjectorImpl = p._root if p is not None else self # noqa
|
|
91
87
|
|
|
92
|
-
self.__cur_req: AsyncInjectorImpl._Request | None = None
|
|
93
|
-
|
|
94
|
-
ss = [
|
|
95
|
-
*DEFAULT_SCOPES,
|
|
96
|
-
*[sb.scope for sb in ec.elements_of_type(ScopeBinding)],
|
|
97
|
-
]
|
|
98
88
|
self._scopes: dict[Scope, ScopeImpl] = {
|
|
99
|
-
s: make_scope_impl(s)
|
|
89
|
+
s: make_scope_impl(s)
|
|
90
|
+
for s in itertools.chain(
|
|
91
|
+
DEFAULT_SCOPES,
|
|
92
|
+
ec.scope_binding_scopes(),
|
|
93
|
+
)
|
|
100
94
|
}
|
|
101
95
|
|
|
96
|
+
_cs: weakref.WeakSet['AsyncInjectorImpl'] | None = None # noqa
|
|
97
|
+
|
|
98
|
+
__cur_req: ta.Optional['AsyncInjectorImpl._Request'] = None
|
|
99
|
+
|
|
102
100
|
#
|
|
103
101
|
|
|
104
102
|
_has_run_init: bool = False
|
|
@@ -259,7 +257,7 @@ class AsyncInjectorImpl(AsyncInjector, lang.Final):
|
|
|
259
257
|
return obj(**kws)
|
|
260
258
|
|
|
261
259
|
|
|
262
|
-
async def create_async_injector(
|
|
263
|
-
i = AsyncInjectorImpl(
|
|
260
|
+
async def create_async_injector(ce: CollectedElements) -> AsyncInjector:
|
|
261
|
+
i = AsyncInjectorImpl(ce)
|
|
264
262
|
await i._init() # noqa
|
|
265
263
|
return i
|
omlish/inject/impl/maysync.py
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import typing as ta
|
|
2
2
|
|
|
3
3
|
from ... import lang
|
|
4
|
-
from ..elements import
|
|
4
|
+
from ..elements import CollectedElements
|
|
5
5
|
from ..injector import AsyncInjector
|
|
6
6
|
from ..inspect import KwargsTarget
|
|
7
7
|
from ..keys import Key
|
|
8
8
|
from ..maysync import MaysyncInjector
|
|
9
9
|
from ..sync import Injector
|
|
10
|
-
from .elements import ElementCollection
|
|
11
10
|
from .injector import AsyncInjectorImpl
|
|
12
11
|
|
|
13
12
|
|
|
@@ -30,10 +29,10 @@ class MaysyncInjectorImpl(MaysyncInjector, lang.Final):
|
|
|
30
29
|
return lang.run_maysync(self._ai.inject(obj))
|
|
31
30
|
|
|
32
31
|
|
|
33
|
-
def create_maysync_injector(
|
|
32
|
+
def create_maysync_injector(ce: CollectedElements) -> MaysyncInjector:
|
|
34
33
|
si = MaysyncInjectorImpl()
|
|
35
34
|
ai = AsyncInjectorImpl(
|
|
36
|
-
|
|
35
|
+
ce,
|
|
37
36
|
internal_consts={
|
|
38
37
|
Key(MaysyncInjector): si,
|
|
39
38
|
Key(Injector): si,
|
omlish/inject/impl/sync.py
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import typing as ta
|
|
2
2
|
|
|
3
3
|
from ... import lang
|
|
4
|
-
from ..elements import
|
|
4
|
+
from ..elements import CollectedElements
|
|
5
5
|
from ..injector import AsyncInjector
|
|
6
6
|
from ..inspect import KwargsTarget
|
|
7
7
|
from ..keys import Key
|
|
8
8
|
from ..sync import Injector
|
|
9
|
-
from .elements import ElementCollection
|
|
10
9
|
from .injector import AsyncInjectorImpl
|
|
11
10
|
|
|
12
11
|
|
|
@@ -29,10 +28,10 @@ class InjectorImpl(Injector, lang.Final):
|
|
|
29
28
|
return lang.sync_await(self._ai.inject(obj))
|
|
30
29
|
|
|
31
30
|
|
|
32
|
-
def create_injector(
|
|
31
|
+
def create_injector(ce: CollectedElements) -> Injector:
|
|
33
32
|
si = InjectorImpl()
|
|
34
33
|
ai = AsyncInjectorImpl(
|
|
35
|
-
|
|
34
|
+
ce,
|
|
36
35
|
internal_consts={
|
|
37
36
|
Key(Injector): si,
|
|
38
37
|
},
|
omlish/inject/injector.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import typing as ta
|
|
3
3
|
|
|
4
|
+
from .. import check
|
|
4
5
|
from .. import lang
|
|
6
|
+
from .elements import CollectedElements
|
|
5
7
|
from .elements import Elemental
|
|
6
8
|
from .elements import as_elements
|
|
9
|
+
from .elements import collect_elements
|
|
7
10
|
from .inspect import KwargsTarget
|
|
8
11
|
from .keys import Key
|
|
9
12
|
|
|
@@ -44,5 +47,31 @@ class AsyncInjector(lang.Abstract):
|
|
|
44
47
|
return self.provide(target)
|
|
45
48
|
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
##
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@ta.final
|
|
54
|
+
class _InjectorCreator(ta.Generic[T]):
|
|
55
|
+
def __init__(self, fac: ta.Callable[[CollectedElements], T]) -> None:
|
|
56
|
+
self._fac = fac
|
|
57
|
+
|
|
58
|
+
@ta.overload
|
|
59
|
+
def __call__(self, es: CollectedElements, /) -> T: ...
|
|
60
|
+
|
|
61
|
+
@ta.overload
|
|
62
|
+
def __call__(self, *es: Elemental) -> T: ...
|
|
63
|
+
|
|
64
|
+
def __call__(self, arg0, *argv):
|
|
65
|
+
ce: CollectedElements
|
|
66
|
+
if isinstance(arg0, CollectedElements):
|
|
67
|
+
check.arg(not argv)
|
|
68
|
+
ce = arg0
|
|
69
|
+
else:
|
|
70
|
+
ce = collect_elements(as_elements(arg0, *argv))
|
|
71
|
+
return self._fac(ce)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
##
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
create_async_injector = _InjectorCreator[ta.Awaitable[AsyncInjector]](lambda ce: _injector.create_async_injector(ce))
|
omlish/inject/maysync.py
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import typing as ta
|
|
2
2
|
|
|
3
3
|
from .. import lang
|
|
4
|
-
from .
|
|
5
|
-
from .elements import as_elements
|
|
4
|
+
from .injector import _InjectorCreator
|
|
6
5
|
from .sync import Injector
|
|
7
6
|
|
|
8
7
|
|
|
@@ -25,5 +24,4 @@ class MaysyncInjector(Injector, lang.Abstract):
|
|
|
25
24
|
##
|
|
26
25
|
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
return _maysync.create_maysync_injector(as_elements(*args))
|
|
27
|
+
create_maysync_injector = _InjectorCreator[MaysyncInjector](lambda ce: _maysync.create_maysync_injector(ce))
|
omlish/inject/sync.py
CHANGED
|
@@ -2,8 +2,7 @@ import abc
|
|
|
2
2
|
import typing as ta
|
|
3
3
|
|
|
4
4
|
from .. import lang
|
|
5
|
-
from .
|
|
6
|
-
from .elements import as_elements
|
|
5
|
+
from .injector import _InjectorCreator
|
|
7
6
|
from .inspect import KwargsTarget
|
|
8
7
|
from .keys import Key
|
|
9
8
|
|
|
@@ -44,5 +43,7 @@ class Injector(lang.Abstract):
|
|
|
44
43
|
return self.provide(target)
|
|
45
44
|
|
|
46
45
|
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
##
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
create_injector = _InjectorCreator[Injector](lambda ce: _sync.create_injector(ce))
|
omlish/lang/imports/proxy.py
CHANGED
|
@@ -342,6 +342,8 @@ def proxy_import(
|
|
|
342
342
|
spec: str,
|
|
343
343
|
package: str | None = None,
|
|
344
344
|
extras: ta.Iterable[str] | None = None,
|
|
345
|
+
*,
|
|
346
|
+
no_cache: bool = False,
|
|
345
347
|
) -> types.ModuleType:
|
|
346
348
|
"""'Legacy' proxy import mechanism."""
|
|
347
349
|
|
|
@@ -352,12 +354,19 @@ def proxy_import(
|
|
|
352
354
|
|
|
353
355
|
def __getattr__(att): # noqa
|
|
354
356
|
nonlocal omod
|
|
357
|
+
|
|
355
358
|
if omod is None:
|
|
356
359
|
omod = importlib.import_module(spec, package=package)
|
|
357
360
|
if extras:
|
|
358
361
|
for x in extras:
|
|
359
362
|
importlib.import_module(f'{spec}.{x}', package=package)
|
|
360
|
-
|
|
363
|
+
|
|
364
|
+
v = getattr(omod, att)
|
|
365
|
+
|
|
366
|
+
if not no_cache:
|
|
367
|
+
setattr(lmod, att, v)
|
|
368
|
+
|
|
369
|
+
return v
|
|
361
370
|
|
|
362
371
|
lmod = types.ModuleType(spec)
|
|
363
372
|
lmod.__getattr__ = __getattr__ # type: ignore[method-assign]
|
omlish/lite/abstract.py
CHANGED
|
@@ -3,6 +3,9 @@ import abc
|
|
|
3
3
|
import typing as ta
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
T = ta.TypeVar('T')
|
|
7
|
+
|
|
8
|
+
|
|
6
9
|
##
|
|
7
10
|
|
|
8
11
|
|
|
@@ -14,25 +17,49 @@ def is_abstract_method(obj: ta.Any) -> bool:
|
|
|
14
17
|
return bool(getattr(obj, _IS_ABSTRACT_METHOD_ATTR, False))
|
|
15
18
|
|
|
16
19
|
|
|
17
|
-
def
|
|
20
|
+
def compute_abstract_methods(cls: type) -> ta.FrozenSet[str]:
|
|
21
|
+
# ~> https://github.com/python/cpython/blob/f3476c6507381ca860eec0989f53647b13517423/Modules/_abc.c#L358
|
|
22
|
+
|
|
23
|
+
# Stage 1: direct abstract methods
|
|
24
|
+
|
|
25
|
+
abstracts = {
|
|
26
|
+
a
|
|
27
|
+
# Get items as a list to avoid mutation issues during iteration
|
|
28
|
+
for a, v in list(cls.__dict__.items())
|
|
29
|
+
if is_abstract_method(v)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Stage 2: inherited abstract methods
|
|
33
|
+
|
|
34
|
+
for base in cls.__bases__:
|
|
35
|
+
# Get __abstractmethods__ from base if it exists
|
|
36
|
+
if (base_abstracts := getattr(base, _ABSTRACT_METHODS_ATTR, None)) is None:
|
|
37
|
+
continue
|
|
38
|
+
|
|
39
|
+
# Iterate over abstract methods in base
|
|
40
|
+
for key in base_abstracts:
|
|
41
|
+
# Check if this class has an attribute with this name
|
|
42
|
+
try:
|
|
43
|
+
value = getattr(cls, key)
|
|
44
|
+
except AttributeError:
|
|
45
|
+
# Attribute not found in this class, skip
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
# Check if it's still abstract
|
|
49
|
+
if is_abstract_method(value):
|
|
50
|
+
abstracts.add(key)
|
|
51
|
+
|
|
52
|
+
return frozenset(abstracts)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def update_abstracts(cls: ta.Type[T], *, force: bool = False) -> ta.Type[T]:
|
|
18
56
|
if not force and not hasattr(cls, _ABSTRACT_METHODS_ATTR):
|
|
19
57
|
# Per stdlib: We check for __abstractmethods__ here because cls might by a C implementation or a python
|
|
20
58
|
# implementation (especially during testing), and we want to handle both cases.
|
|
21
59
|
return cls
|
|
22
60
|
|
|
23
|
-
abstracts
|
|
24
|
-
|
|
25
|
-
for scls in cls.__bases__:
|
|
26
|
-
for name in getattr(scls, _ABSTRACT_METHODS_ATTR, ()):
|
|
27
|
-
value = getattr(cls, name, None)
|
|
28
|
-
if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
|
|
29
|
-
abstracts.add(name)
|
|
30
|
-
|
|
31
|
-
for name, value in cls.__dict__.items():
|
|
32
|
-
if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
|
|
33
|
-
abstracts.add(name)
|
|
34
|
-
|
|
35
|
-
setattr(cls, _ABSTRACT_METHODS_ATTR, frozenset(abstracts))
|
|
61
|
+
abstracts = compute_abstract_methods(cls)
|
|
62
|
+
setattr(cls, _ABSTRACT_METHODS_ATTR, abstracts)
|
|
36
63
|
return cls
|
|
37
64
|
|
|
38
65
|
|
|
@@ -86,23 +113,26 @@ class Abstract:
|
|
|
86
113
|
super().__init_subclass__(**kwargs)
|
|
87
114
|
|
|
88
115
|
if not (Abstract in cls.__bases__ or abc.ABC in cls.__bases__):
|
|
89
|
-
ams
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
116
|
+
if ams := compute_abstract_methods(cls):
|
|
117
|
+
amd = {
|
|
118
|
+
a: mcls
|
|
119
|
+
for mcls in cls.__mro__[::-1]
|
|
120
|
+
for a in ams
|
|
121
|
+
if a in mcls.__dict__
|
|
122
|
+
}
|
|
95
123
|
|
|
96
|
-
if ams:
|
|
97
124
|
raise AbstractTypeError(
|
|
98
125
|
f'Cannot subclass abstract class {cls.__name__} with abstract methods: ' +
|
|
99
126
|
', '.join(sorted([
|
|
100
127
|
'.'.join([
|
|
101
|
-
*([
|
|
102
|
-
|
|
128
|
+
*([
|
|
129
|
+
*([m] if (m := getattr(c, '__module__')) else []),
|
|
130
|
+
getattr(c, '__qualname__', getattr(c, '__name__')),
|
|
131
|
+
] if c is not None else '?'),
|
|
103
132
|
a,
|
|
104
133
|
])
|
|
105
|
-
for a
|
|
134
|
+
for a in ams
|
|
135
|
+
for c in [amd.get(a)]
|
|
106
136
|
])),
|
|
107
137
|
)
|
|
108
138
|
|
omlish/lite/dataclasses.py
CHANGED
|
@@ -191,3 +191,17 @@ def dataclass_kw_only_init():
|
|
|
191
191
|
return cls
|
|
192
192
|
|
|
193
193
|
return inner
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
##
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@dc.dataclass()
|
|
200
|
+
class DataclassFieldRequiredError(Exception):
|
|
201
|
+
name: str
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def dataclass_field_required(name: str) -> ta.Callable[[], ta.Any]:
|
|
205
|
+
def inner() -> ta.NoReturn:
|
|
206
|
+
raise DataclassFieldRequiredError(name)
|
|
207
|
+
return inner
|