omlish 0.0.0.dev5__py3-none-any.whl → 0.0.0.dev6__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.
- omlish/__about__.py +1 -1
- omlish/asyncs/__init__.py +9 -0
- omlish/asyncs/anyio.py +83 -19
- omlish/asyncs/asyncio.py +23 -0
- omlish/asyncs/asyncs.py +9 -6
- omlish/asyncs/bridge.py +316 -0
- omlish/asyncs/trio_asyncio.py +7 -3
- omlish/collections/__init__.py +1 -0
- omlish/collections/identity.py +7 -0
- omlish/configs/strings.py +94 -0
- omlish/dataclasses/__init__.py +9 -0
- omlish/dataclasses/impl/copy.py +30 -0
- omlish/dataclasses/impl/exceptions.py +6 -0
- omlish/dataclasses/impl/fields.py +24 -25
- omlish/dataclasses/impl/init.py +4 -2
- omlish/dataclasses/impl/main.py +2 -0
- omlish/dataclasses/utils.py +44 -0
- omlish/diag/__init__.py +4 -0
- omlish/diag/procfs.py +2 -2
- omlish/{testing → diag}/pydevd.py +35 -0
- omlish/dispatch/_dispatch2.py +65 -0
- omlish/dispatch/_dispatch3.py +104 -0
- omlish/docker.py +1 -1
- omlish/fnpairs.py +11 -0
- omlish/http/asgi.py +2 -1
- omlish/http/collections.py +15 -0
- omlish/http/consts.py +16 -1
- omlish/http/sessions.py +10 -3
- omlish/inject/__init__.py +45 -17
- omlish/inject/binder.py +185 -5
- omlish/inject/bindings.py +3 -36
- omlish/inject/eagers.py +2 -8
- omlish/inject/elements.py +30 -9
- omlish/inject/exceptions.py +1 -1
- omlish/inject/impl/elements.py +37 -12
- omlish/inject/impl/injector.py +19 -2
- omlish/inject/impl/inspect.py +33 -5
- omlish/inject/impl/origins.py +75 -0
- omlish/inject/impl/{private.py → privates.py} +2 -2
- omlish/inject/impl/scopes.py +6 -2
- omlish/inject/injector.py +8 -4
- omlish/inject/inspect.py +18 -0
- omlish/inject/keys.py +8 -14
- omlish/inject/listeners.py +26 -0
- omlish/inject/managed.py +76 -10
- omlish/inject/multis.py +68 -18
- omlish/inject/origins.py +27 -0
- omlish/inject/overrides.py +5 -4
- omlish/inject/{private.py → privates.py} +6 -10
- omlish/inject/providers.py +12 -85
- omlish/inject/scopes.py +13 -6
- omlish/inject/types.py +3 -1
- omlish/lang/__init__.py +8 -2
- omlish/lang/cached.py +2 -2
- omlish/lang/classes/restrict.py +2 -1
- omlish/lang/classes/simple.py +18 -8
- omlish/lang/contextmanagers.py +12 -3
- omlish/lang/descriptors.py +131 -0
- omlish/lang/functions.py +8 -28
- omlish/lang/iterables.py +20 -1
- omlish/lang/typing.py +5 -0
- omlish/lifecycles/__init__.py +34 -0
- omlish/lifecycles/abstract.py +43 -0
- omlish/lifecycles/base.py +51 -0
- omlish/lifecycles/contextmanagers.py +74 -0
- omlish/lifecycles/controller.py +116 -0
- omlish/lifecycles/manager.py +161 -0
- omlish/lifecycles/states.py +43 -0
- omlish/lifecycles/transitions.py +64 -0
- omlish/logs/formatters.py +1 -1
- omlish/marshal/__init__.py +4 -0
- omlish/marshal/naming.py +4 -0
- omlish/marshal/objects.py +1 -0
- omlish/marshal/polymorphism.py +4 -4
- omlish/reflect.py +134 -19
- omlish/secrets/__init__.py +7 -0
- omlish/secrets/marshal.py +41 -0
- omlish/secrets/passwords.py +120 -0
- omlish/secrets/secrets.py +47 -0
- omlish/serde/__init__.py +0 -0
- omlish/{configs → serde}/dotenv.py +12 -24
- omlish/{json.py → serde/json.py} +2 -1
- omlish/serde/yaml.py +223 -0
- omlish/sql/dbs.py +1 -1
- omlish/sql/duckdb.py +136 -0
- omlish/sql/sqlean.py +17 -0
- omlish/term.py +1 -1
- omlish/testing/pytest/__init__.py +3 -2
- omlish/testing/pytest/inject/harness.py +3 -3
- omlish/testing/pytest/marks.py +4 -7
- omlish/testing/pytest/plugins/__init__.py +1 -0
- omlish/testing/pytest/plugins/asyncs.py +136 -0
- omlish/testing/pytest/plugins/pydevd.py +1 -1
- omlish/text/glyphsplit.py +92 -0
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/RECORD +100 -72
- /omlish/{configs → serde}/props.py +0 -0
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/top_level.txt +0 -0
omlish/inject/providers.py
CHANGED
|
@@ -1,111 +1,38 @@
|
|
|
1
|
-
import abc
|
|
2
1
|
import typing as ta
|
|
3
2
|
|
|
4
3
|
from .. import check
|
|
5
4
|
from .. import dataclasses as dc
|
|
6
5
|
from .. import lang
|
|
7
|
-
from .. import reflect as rfl
|
|
8
|
-
from .elements import Element
|
|
9
|
-
from .elements import Elements
|
|
10
|
-
from .impl.inspect import signature
|
|
11
6
|
from .keys import Key
|
|
12
|
-
from .keys import as_key
|
|
13
7
|
|
|
14
8
|
|
|
15
9
|
class _Missing(lang.NotInstantiable):
|
|
16
10
|
pass
|
|
17
11
|
|
|
18
12
|
|
|
19
|
-
##
|
|
20
|
-
|
|
21
|
-
|
|
22
13
|
class Provider(lang.Abstract):
|
|
23
|
-
|
|
24
|
-
def provided_ty(self) -> rfl.Type | None:
|
|
25
|
-
raise NotImplementedError
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
##
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def as_provider(o: ta.Any) -> Provider:
|
|
32
|
-
check.not_isinstance(o, (Element, Elements))
|
|
33
|
-
if isinstance(o, Provider):
|
|
34
|
-
return o
|
|
35
|
-
if isinstance(o, Key):
|
|
36
|
-
return link(o)
|
|
37
|
-
if isinstance(o, type):
|
|
38
|
-
return ctor(o)
|
|
39
|
-
if callable(o):
|
|
40
|
-
return fn(o)
|
|
41
|
-
return const(o)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
##
|
|
14
|
+
pass
|
|
45
15
|
|
|
46
16
|
|
|
47
|
-
@dc.dataclass(frozen=True
|
|
17
|
+
@dc.dataclass(frozen=True)
|
|
18
|
+
@dc.extra_params(cache_hash=True)
|
|
48
19
|
class FnProvider(Provider):
|
|
49
|
-
fn: ta.Any
|
|
50
|
-
ty: rfl.Type | None = None
|
|
51
|
-
|
|
52
|
-
def provided_ty(self) -> rfl.Type | None:
|
|
53
|
-
return self.ty
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def fn(fn: ta.Any, ty: rfl.Type | None = _Missing) -> Provider:
|
|
57
|
-
check.not_isinstance(fn, type)
|
|
58
|
-
check.callable(fn)
|
|
59
|
-
if ty is _Missing:
|
|
60
|
-
sig = signature(fn)
|
|
61
|
-
ty = check.isinstance(sig.return_annotation, type)
|
|
62
|
-
return FnProvider(fn, ty)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
##
|
|
20
|
+
fn: ta.Any = dc.xfield(check=callable)
|
|
66
21
|
|
|
67
22
|
|
|
68
|
-
@dc.dataclass(frozen=True
|
|
23
|
+
@dc.dataclass(frozen=True)
|
|
24
|
+
@dc.extra_params(cache_hash=True)
|
|
69
25
|
class CtorProvider(Provider):
|
|
70
|
-
ty: type
|
|
26
|
+
ty: type = dc.xfield(coerce=check.of_isinstance(type))
|
|
71
27
|
|
|
72
|
-
def provided_ty(self) -> type:
|
|
73
|
-
return self.ty
|
|
74
28
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
check.isinstance(ty, type)
|
|
78
|
-
return CtorProvider(ty)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
##
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
@dc.dataclass(frozen=True, eq=False)
|
|
29
|
+
@dc.dataclass(frozen=True)
|
|
30
|
+
@dc.extra_params(cache_hash=True)
|
|
85
31
|
class ConstProvider(Provider):
|
|
86
32
|
v: ta.Any
|
|
87
|
-
ty: rfl.Type | None = None
|
|
88
|
-
|
|
89
|
-
def provided_ty(self) -> rfl.Type | None:
|
|
90
|
-
return self.ty
|
|
91
|
-
|
|
92
33
|
|
|
93
|
-
def const(v: ta.Any, ty: rfl.Type | None = _Missing) -> Provider:
|
|
94
|
-
if ty is _Missing:
|
|
95
|
-
ty = type(v)
|
|
96
|
-
return ConstProvider(v, ty)
|
|
97
34
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
@dc.dataclass(frozen=True, eq=False)
|
|
35
|
+
@dc.dataclass(frozen=True)
|
|
36
|
+
@dc.extra_params(cache_hash=True)
|
|
103
37
|
class LinkProvider(Provider):
|
|
104
|
-
k: Key
|
|
105
|
-
|
|
106
|
-
def provided_ty(self) -> rfl.Type | None:
|
|
107
|
-
return None
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def link(k: ta.Any) -> Provider:
|
|
111
|
-
return LinkProvider(as_key(k))
|
|
38
|
+
k: Key = dc.xfield(coerce=check.of_isinstance(Key))
|
omlish/inject/scopes.py
CHANGED
|
@@ -7,7 +7,6 @@ from .. import dataclasses as dc
|
|
|
7
7
|
from .. import lang
|
|
8
8
|
from .. import reflect as rfl
|
|
9
9
|
from .bindings import Binding
|
|
10
|
-
from .bindings import as_binding
|
|
11
10
|
from .elements import Element
|
|
12
11
|
from .keys import Key
|
|
13
12
|
from .keys import as_key
|
|
@@ -24,6 +23,9 @@ else:
|
|
|
24
23
|
##
|
|
25
24
|
|
|
26
25
|
|
|
26
|
+
SCOPE_ALIASES: dict[str, Scope] = {}
|
|
27
|
+
|
|
28
|
+
|
|
27
29
|
@dc.dataclass(frozen=True)
|
|
28
30
|
@dc.extra_params(cache_hash=True)
|
|
29
31
|
class ScopeBinding(Element, lang.Final):
|
|
@@ -34,22 +36,26 @@ def bind_scope(sc: Scope) -> Element:
|
|
|
34
36
|
return ScopeBinding(sc)
|
|
35
37
|
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
return dc.replace(as_binding(b), scope=check.isinstance(sc, Scope))
|
|
39
|
+
##
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
class Singleton(Scope, lang.Singleton, lang.Final):
|
|
42
43
|
pass
|
|
43
44
|
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
SCOPE_ALIASES['singleton'] = Singleton()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
##
|
|
47
50
|
|
|
48
51
|
|
|
49
52
|
class Thread(Scope, lang.Singleton, lang.Final):
|
|
50
53
|
pass
|
|
51
54
|
|
|
52
55
|
|
|
56
|
+
SCOPE_ALIASES['thread'] = Thread()
|
|
57
|
+
|
|
58
|
+
|
|
53
59
|
##
|
|
54
60
|
|
|
55
61
|
|
|
@@ -64,7 +70,8 @@ class SeededScope(Scope, lang.Final):
|
|
|
64
70
|
raise NotImplementedError
|
|
65
71
|
|
|
66
72
|
|
|
67
|
-
@dc.dataclass(frozen=True
|
|
73
|
+
@dc.dataclass(frozen=True)
|
|
74
|
+
@dc.extra_params(cache_hash=True)
|
|
68
75
|
class ScopeSeededProvider(Provider):
|
|
69
76
|
ss: SeededScope = dc.xfield(coerce=check.of_isinstance(SeededScope))
|
|
70
77
|
key: Key = dc.xfield(coerce=check.of_isinstance(Key))
|
omlish/inject/types.py
CHANGED
omlish/lang/__init__.py
CHANGED
|
@@ -80,9 +80,13 @@ from .descriptors import ( # noqa
|
|
|
80
80
|
access_forbidden,
|
|
81
81
|
attr_property,
|
|
82
82
|
classonly,
|
|
83
|
+
decorator,
|
|
83
84
|
is_method_descriptor,
|
|
84
85
|
item_property,
|
|
86
|
+
unwrap_func,
|
|
87
|
+
unwrap_func_with_partials,
|
|
85
88
|
unwrap_method_descriptors,
|
|
89
|
+
update_wrapper_except_dict,
|
|
86
90
|
)
|
|
87
91
|
|
|
88
92
|
from .exceptions import ( # noqa
|
|
@@ -99,14 +103,14 @@ from .functions import ( # noqa
|
|
|
99
103
|
is_lambda,
|
|
100
104
|
is_none,
|
|
101
105
|
is_not_none,
|
|
106
|
+
isinstance_of,
|
|
107
|
+
issubclass_of,
|
|
102
108
|
maybe_call,
|
|
103
109
|
periodically,
|
|
104
110
|
raise_,
|
|
105
111
|
raising,
|
|
106
112
|
recurse,
|
|
107
113
|
try_,
|
|
108
|
-
unwrap_func,
|
|
109
|
-
unwrap_func_with_partials,
|
|
110
114
|
void,
|
|
111
115
|
)
|
|
112
116
|
|
|
@@ -127,8 +131,10 @@ from .iterables import ( # noqa
|
|
|
127
131
|
asrange,
|
|
128
132
|
exhaust,
|
|
129
133
|
ilen,
|
|
134
|
+
itergen,
|
|
130
135
|
peek,
|
|
131
136
|
prodrange,
|
|
137
|
+
renumerate,
|
|
132
138
|
take,
|
|
133
139
|
)
|
|
134
140
|
|
omlish/lang/cached.py
CHANGED
|
@@ -11,8 +11,8 @@ import typing as ta
|
|
|
11
11
|
|
|
12
12
|
from .contextmanagers import DefaultLockable
|
|
13
13
|
from .contextmanagers import default_lock
|
|
14
|
-
from .
|
|
15
|
-
from .
|
|
14
|
+
from .descriptors import unwrap_func
|
|
15
|
+
from .descriptors import unwrap_func_with_partials
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
P = ta.ParamSpec('P')
|
omlish/lang/classes/restrict.py
CHANGED
|
@@ -78,7 +78,8 @@ class PackageSealed:
|
|
|
78
78
|
for base in cls.__bases__:
|
|
79
79
|
if base is not Abstract:
|
|
80
80
|
if PackageSealed in base.__bases__:
|
|
81
|
-
|
|
81
|
+
pfx = base.__module__.split('.')[:-1]
|
|
82
|
+
if cls.__module__.split('.')[:len(pfx)] != pfx:
|
|
82
83
|
raise SealedError(base)
|
|
83
84
|
super().__init_subclass__(**kwargs)
|
|
84
85
|
|
omlish/lang/classes/simple.py
CHANGED
|
@@ -14,13 +14,25 @@ V = ta.TypeVar('V')
|
|
|
14
14
|
##
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class
|
|
17
|
+
class _NamespaceMeta(abc.ABCMeta):
|
|
18
|
+
def __new__(mcls, name, bases, namespace):
|
|
19
|
+
if bases:
|
|
20
|
+
for nc in (NotInstantiable,):
|
|
21
|
+
if nc not in bases:
|
|
22
|
+
bases += (nc,)
|
|
23
|
+
return super().__new__(mcls, name, bases, namespace)
|
|
18
24
|
|
|
19
|
-
def
|
|
20
|
-
|
|
25
|
+
def __iter__(cls) -> ta.Iterator[tuple[str, ta.Any]]:
|
|
26
|
+
for a in dir(cls):
|
|
27
|
+
if not a.startswith('_'):
|
|
28
|
+
yield (a, getattr(cls, a))
|
|
21
29
|
|
|
30
|
+
def __getitem__(cls, n: str) -> ta.Any:
|
|
31
|
+
return getattr(cls, n)
|
|
22
32
|
|
|
23
|
-
|
|
33
|
+
|
|
34
|
+
class Namespace(metaclass=_NamespaceMeta):
|
|
35
|
+
pass
|
|
24
36
|
|
|
25
37
|
|
|
26
38
|
##
|
|
@@ -54,8 +66,6 @@ class _MarkerMeta(abc.ABCMeta):
|
|
|
54
66
|
class Marker(NotInstantiable, metaclass=_MarkerMeta):
|
|
55
67
|
"""A marker."""
|
|
56
68
|
|
|
57
|
-
__slots__ = ()
|
|
58
|
-
|
|
59
69
|
|
|
60
70
|
##
|
|
61
71
|
|
|
@@ -77,7 +87,7 @@ _SINGLETON_LOCK = threading.RLock()
|
|
|
77
87
|
|
|
78
88
|
|
|
79
89
|
def _set_singleton_instance(inst):
|
|
80
|
-
cls =
|
|
90
|
+
cls = inst.__class__
|
|
81
91
|
if _SINGLETON_INSTANCE_ATTR in cls.__dict__:
|
|
82
92
|
raise TypeError(cls)
|
|
83
93
|
|
|
@@ -86,7 +96,7 @@ def _set_singleton_instance(inst):
|
|
|
86
96
|
|
|
87
97
|
@functools.wraps(old_init)
|
|
88
98
|
def new_init(self):
|
|
89
|
-
if
|
|
99
|
+
if self.__class__ is not cls:
|
|
90
100
|
old_init(self) # noqa
|
|
91
101
|
|
|
92
102
|
setattr(cls, '__init__', new_init)
|
omlish/lang/contextmanagers.py
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- AsyncExitStacked
|
|
4
|
+
"""
|
|
1
5
|
import abc
|
|
2
6
|
import contextlib
|
|
3
7
|
import contextvars
|
|
@@ -15,7 +19,7 @@ T = ta.TypeVar('T')
|
|
|
15
19
|
|
|
16
20
|
class ContextManaged:
|
|
17
21
|
|
|
18
|
-
def __enter__(self
|
|
22
|
+
def __enter__(self) -> ta.Self:
|
|
19
23
|
return self
|
|
20
24
|
|
|
21
25
|
def __exit__(
|
|
@@ -77,7 +81,7 @@ class ContextManager(abc.ABC, ta.Generic[T]):
|
|
|
77
81
|
exc_type: type[BaseException] | None,
|
|
78
82
|
exc_val: BaseException | None,
|
|
79
83
|
exc_tb: types.TracebackType | None,
|
|
80
|
-
) -> None:
|
|
84
|
+
) -> bool | None:
|
|
81
85
|
return self._contextmanager.__exit__(exc_type, exc_val, exc_tb)
|
|
82
86
|
|
|
83
87
|
|
|
@@ -198,7 +202,7 @@ class ExitStacked:
|
|
|
198
202
|
def _enter_context(self, context_manager: ta.ContextManager[T]) -> T:
|
|
199
203
|
return self._exit_stack.enter_context(ta.cast(ta.ContextManager, context_manager))
|
|
200
204
|
|
|
201
|
-
def __enter__(self
|
|
205
|
+
def __enter__(self) -> ta.Self:
|
|
202
206
|
try:
|
|
203
207
|
superfn = super().__enter__ # type: ignore
|
|
204
208
|
except AttributeError:
|
|
@@ -306,14 +310,19 @@ DefaultLockable = bool | Lockable | ta.ContextManager | None
|
|
|
306
310
|
def default_lock(value: DefaultLockable, default: DefaultLockable) -> Lockable:
|
|
307
311
|
if value is None:
|
|
308
312
|
value = default
|
|
313
|
+
|
|
309
314
|
if value is True:
|
|
310
315
|
lock = threading.RLock()
|
|
311
316
|
return lambda: lock
|
|
317
|
+
|
|
312
318
|
elif value is False or value is None:
|
|
313
319
|
return NOP_CONTEXT_MANAGER
|
|
320
|
+
|
|
314
321
|
elif callable(value):
|
|
315
322
|
return value
|
|
323
|
+
|
|
316
324
|
elif isinstance(value, ta.ContextManager):
|
|
317
325
|
return lambda: value
|
|
326
|
+
|
|
318
327
|
else:
|
|
319
328
|
raise TypeError(value)
|
omlish/lang/descriptors.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import functools
|
|
2
2
|
import operator
|
|
3
|
+
import types
|
|
3
4
|
import typing as ta
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
T = ta.TypeVar('T')
|
|
8
|
+
P = ta.ParamSpec('P')
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
##
|
|
@@ -31,6 +33,19 @@ def is_method_descriptor(obj: ta.Any) -> bool:
|
|
|
31
33
|
return isinstance(obj, (*BUILTIN_METHOD_DESCRIPTORS, _MethodDescriptor))
|
|
32
34
|
|
|
33
35
|
|
|
36
|
+
def _has_method_descriptor(obj: ta.Any) -> bool:
|
|
37
|
+
while True:
|
|
38
|
+
if is_method_descriptor(obj):
|
|
39
|
+
return True
|
|
40
|
+
elif isinstance(obj, functools.partial):
|
|
41
|
+
obj = obj.func
|
|
42
|
+
else:
|
|
43
|
+
try:
|
|
44
|
+
obj = getattr(obj, '__wrapped__')
|
|
45
|
+
except AttributeError:
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
|
|
34
49
|
def unwrap_method_descriptors(fn: ta.Callable) -> ta.Callable:
|
|
35
50
|
while is_method_descriptor(fn):
|
|
36
51
|
fn = fn.__func__ # type: ignore # noqa
|
|
@@ -40,6 +55,122 @@ def unwrap_method_descriptors(fn: ta.Callable) -> ta.Callable:
|
|
|
40
55
|
##
|
|
41
56
|
|
|
42
57
|
|
|
58
|
+
def unwrap_func(fn: ta.Callable) -> ta.Callable:
|
|
59
|
+
fn, _ = unwrap_func_with_partials(fn)
|
|
60
|
+
return fn
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def unwrap_func_with_partials(fn: ta.Callable) -> tuple[ta.Callable, list[functools.partial]]:
|
|
64
|
+
ps = []
|
|
65
|
+
while True:
|
|
66
|
+
if is_method_descriptor(fn) or isinstance(fn, types.MethodType):
|
|
67
|
+
fn = fn.__func__ # type: ignore
|
|
68
|
+
elif hasattr(fn, '__wrapped__'):
|
|
69
|
+
nxt = fn.__wrapped__
|
|
70
|
+
if not callable(nxt):
|
|
71
|
+
raise TypeError(nxt)
|
|
72
|
+
if nxt is fn:
|
|
73
|
+
raise TypeError(fn)
|
|
74
|
+
fn = nxt
|
|
75
|
+
# NOTE: __wrapped__ takes precedence - a partial might point to a bound Method when the important information is
|
|
76
|
+
# still the unbound func. see _decorator_descriptor for an example of this.
|
|
77
|
+
elif isinstance(fn, functools.partial):
|
|
78
|
+
ps.append(fn)
|
|
79
|
+
fn = fn.func
|
|
80
|
+
else:
|
|
81
|
+
break
|
|
82
|
+
return fn, ps
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
##
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
WRAPPER_UPDATES_EXCEPT_DICT = tuple(a for a in functools.WRAPPER_UPDATES if a != '__dict__')
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def update_wrapper_except_dict(
|
|
92
|
+
wrapper,
|
|
93
|
+
wrapped,
|
|
94
|
+
assigned=functools.WRAPPER_ASSIGNMENTS,
|
|
95
|
+
updated=WRAPPER_UPDATES_EXCEPT_DICT,
|
|
96
|
+
):
|
|
97
|
+
return functools.update_wrapper(
|
|
98
|
+
wrapper,
|
|
99
|
+
wrapped,
|
|
100
|
+
assigned=assigned,
|
|
101
|
+
updated=updated,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
##
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
_DECORATOR_HANDLES_UNBOUND_METHODS = True
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class _decorator_descriptor: # noqa
|
|
112
|
+
if not _DECORATOR_HANDLES_UNBOUND_METHODS:
|
|
113
|
+
def __init__(self, wrapper, fn):
|
|
114
|
+
self._wrapper, self._fn = wrapper, fn
|
|
115
|
+
update_wrapper_except_dict(self, fn)
|
|
116
|
+
|
|
117
|
+
def __get__(self, instance, owner):
|
|
118
|
+
return functools.update_wrapper(functools.partial(self._wrapper, fn := self._fn.__get__(instance, owner)), fn) # noqa
|
|
119
|
+
|
|
120
|
+
else:
|
|
121
|
+
def __init__(self, wrapper, fn):
|
|
122
|
+
self._wrapper, self._fn = wrapper, fn
|
|
123
|
+
self._md = _has_method_descriptor(fn)
|
|
124
|
+
update_wrapper_except_dict(self, fn)
|
|
125
|
+
|
|
126
|
+
def __get__(self, instance, owner):
|
|
127
|
+
fn = self._fn.__get__(instance, owner)
|
|
128
|
+
if self._md or instance is not None:
|
|
129
|
+
@functools.wraps(fn)
|
|
130
|
+
def inner(*args, **kwargs):
|
|
131
|
+
return self._wrapper(fn, *args, **kwargs)
|
|
132
|
+
return inner
|
|
133
|
+
else:
|
|
134
|
+
@functools.wraps(fn)
|
|
135
|
+
def outer(this, *args, **kwargs):
|
|
136
|
+
@functools.wraps(self._fn)
|
|
137
|
+
def inner(*args2, **kwargs2):
|
|
138
|
+
return fn(this, *args2, **kwargs2)
|
|
139
|
+
return self._wrapper(inner, *args, **kwargs)
|
|
140
|
+
return outer
|
|
141
|
+
|
|
142
|
+
def __repr__(self):
|
|
143
|
+
return f'{self.__class__.__name__}<{self._wrapper}, {self._fn}>'
|
|
144
|
+
|
|
145
|
+
def __call__(self, *args, **kwargs):
|
|
146
|
+
return self._wrapper(self._fn, *args, **kwargs)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class _decorator: # noqa
|
|
150
|
+
def __init__(self, wrapper):
|
|
151
|
+
self._wrapper = wrapper
|
|
152
|
+
update_wrapper_except_dict(self, wrapper)
|
|
153
|
+
|
|
154
|
+
def __repr__(self):
|
|
155
|
+
return f'{self.__class__.__name__}<{self._wrapper}>'
|
|
156
|
+
|
|
157
|
+
def __call__(self, fn):
|
|
158
|
+
return _decorator_descriptor(self._wrapper, fn) # noqa
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# FIXME:
|
|
162
|
+
# def decorator(
|
|
163
|
+
# wrapper: ta.Callable[ta.Concatenate[ta.Any, P], T], # FIXME: https://youtrack.jetbrains.com/issue/PY-72164 # noqa
|
|
164
|
+
# ) -> ta.Callable[[ta.Callable[P, T]], ta.Callable[P, T]]:
|
|
165
|
+
# return _decorator(wrapper)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
decorator = _decorator
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
##
|
|
172
|
+
|
|
173
|
+
|
|
43
174
|
class AccessForbiddenError(Exception):
|
|
44
175
|
|
|
45
176
|
def __init__(self, name: str | None = None, *args: ta.Any, **kwargs: ta.Any) -> None:
|
omlish/lang/functions.py
CHANGED
|
@@ -3,8 +3,6 @@ import functools
|
|
|
3
3
|
import time
|
|
4
4
|
import typing as ta
|
|
5
5
|
|
|
6
|
-
from .descriptors import is_method_descriptor
|
|
7
|
-
|
|
8
6
|
|
|
9
7
|
T = ta.TypeVar('T')
|
|
10
8
|
P = ta.ParamSpec('P')
|
|
@@ -41,32 +39,6 @@ def recurse(fn: ta.Callable[..., T], *args, **kwargs) -> T:
|
|
|
41
39
|
##
|
|
42
40
|
|
|
43
41
|
|
|
44
|
-
def unwrap_func(fn: ta.Callable) -> ta.Callable:
|
|
45
|
-
fn, _ = unwrap_func_with_partials(fn)
|
|
46
|
-
return fn
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def unwrap_func_with_partials(fn: ta.Callable) -> tuple[ta.Callable, list[functools.partial]]:
|
|
50
|
-
ps = []
|
|
51
|
-
while True:
|
|
52
|
-
if is_method_descriptor(fn):
|
|
53
|
-
fn = fn.__func__ # type: ignore
|
|
54
|
-
elif isinstance(fn, functools.partial):
|
|
55
|
-
ps.append(fn)
|
|
56
|
-
fn = fn.func
|
|
57
|
-
else:
|
|
58
|
-
nxt = getattr(fn, '__wrapped__', None)
|
|
59
|
-
if not callable(nxt):
|
|
60
|
-
break
|
|
61
|
-
if nxt is fn:
|
|
62
|
-
raise TypeError(fn)
|
|
63
|
-
fn = nxt
|
|
64
|
-
return fn, ps
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
##
|
|
68
|
-
|
|
69
|
-
|
|
70
42
|
def raise_(o: BaseException) -> ta.NoReturn:
|
|
71
43
|
raise o
|
|
72
44
|
|
|
@@ -128,6 +100,14 @@ def is_not_none(o: ta.Any) -> bool:
|
|
|
128
100
|
return o is not None
|
|
129
101
|
|
|
130
102
|
|
|
103
|
+
def isinstance_of(class_or_tuple: ta.Any) -> ta.Callable[[ta.Any], bool]:
|
|
104
|
+
return lambda o: isinstance(o, class_or_tuple)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def issubclass_of(class_or_tuple: ta.Any) -> ta.Callable[[ta.Any], bool]:
|
|
108
|
+
return lambda o: issubclass(o, class_or_tuple)
|
|
109
|
+
|
|
110
|
+
|
|
131
111
|
class VoidError(Exception):
|
|
132
112
|
pass
|
|
133
113
|
|
omlish/lang/iterables.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import dataclasses as dc
|
|
1
2
|
import itertools
|
|
2
3
|
import typing as ta
|
|
3
4
|
|
|
@@ -34,7 +35,13 @@ def peek(vs: ta.Iterable[T]) -> tuple[T, ta.Iterator[T]]:
|
|
|
34
35
|
return v, itertools.chain(iter((v,)), it)
|
|
35
36
|
|
|
36
37
|
|
|
37
|
-
Rangeable: ta.TypeAlias =
|
|
38
|
+
Rangeable: ta.TypeAlias = ta.Union[ # noqa
|
|
39
|
+
int,
|
|
40
|
+
tuple[int],
|
|
41
|
+
tuple[int, int],
|
|
42
|
+
tuple[int, int, int],
|
|
43
|
+
ta.Iterable[int],
|
|
44
|
+
]
|
|
38
45
|
|
|
39
46
|
|
|
40
47
|
def asrange(i: Rangeable) -> ta.Iterable[int]:
|
|
@@ -52,3 +59,15 @@ def prodrange(*dims: Rangeable) -> ta.Iterable[ta.Sequence[int]]:
|
|
|
52
59
|
if not dims:
|
|
53
60
|
return []
|
|
54
61
|
return itertools.product(*map(asrange, dims))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dc.dataclass(frozen=True)
|
|
65
|
+
class itergen(ta.Generic[T]): # noqa
|
|
66
|
+
fn: ta.Callable[[], ta.Iterable[T]]
|
|
67
|
+
|
|
68
|
+
def __iter__(self):
|
|
69
|
+
return iter(self.fn())
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def renumerate(it: ta.Iterable[T]) -> ta.Iterable[tuple[T, int]]:
|
|
73
|
+
return ((e, i) for i, e in enumerate(it))
|
omlish/lang/typing.py
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from .abstract import ( # noqa
|
|
2
|
+
AbstractLifecycle,
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
from .base import ( # noqa
|
|
6
|
+
CallbackLifecycle,
|
|
7
|
+
Lifecycle,
|
|
8
|
+
LifecycleCallback,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from .contextmanagers import ( # noqa
|
|
12
|
+
ContextManagerLifecycle,
|
|
13
|
+
LifecycleContextManager,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from .controller import ( # noqa
|
|
17
|
+
LifecycleController,
|
|
18
|
+
LifecycleListener,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from .manager import ( # noqa
|
|
22
|
+
LifecycleManager,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from .states import ( # noqa
|
|
26
|
+
LifecycleState,
|
|
27
|
+
LifecycleStateError,
|
|
28
|
+
LifecycleStates,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
from .transitions import ( # noqa
|
|
32
|
+
LifecycleTransition,
|
|
33
|
+
LifecycleTransitions,
|
|
34
|
+
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from .. import cached
|
|
4
|
+
from .. import dataclasses as dc
|
|
5
|
+
from .. import lang
|
|
6
|
+
from .base import Lifecycle
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
AbstractLifecycleT = ta.TypeVar('AbstractLifecycleT', bound='AbstractLifecycle')
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AbstractLifecycle(lang.Abstract):
|
|
13
|
+
@dc.dataclass(frozen=True)
|
|
14
|
+
class _Lifecycle(Lifecycle, lang.Final, ta.Generic[AbstractLifecycleT]):
|
|
15
|
+
obj: AbstractLifecycleT
|
|
16
|
+
|
|
17
|
+
def lifecycle_construct(self) -> None:
|
|
18
|
+
self.obj._lifecycle_construct() # noqa
|
|
19
|
+
|
|
20
|
+
def lifecycle_start(self) -> None:
|
|
21
|
+
self.obj._lifecycle_start() # noqa
|
|
22
|
+
|
|
23
|
+
def lifecycle_stop(self) -> None:
|
|
24
|
+
self.obj._lifecycle_stop() # noqa
|
|
25
|
+
|
|
26
|
+
def lifecycle_destroy(self) -> None:
|
|
27
|
+
self.obj._lifecycle_destroy() # noqa
|
|
28
|
+
|
|
29
|
+
@cached.property
|
|
30
|
+
def _lifecycle(self) -> _Lifecycle[ta.Self]:
|
|
31
|
+
return AbstractLifecycle._Lifecycle(self)
|
|
32
|
+
|
|
33
|
+
def _lifecycle_construct(self) -> None:
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
def _lifecycle_start(self) -> None:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
def _lifecycle_stop(self) -> None:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def _lifecycle_destroy(self) -> None:
|
|
43
|
+
pass
|