omlish 0.0.0.dev1__py3-none-any.whl → 0.0.0.dev3__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 +2 -3
- omlish/argparse.py +8 -8
- omlish/asyncs/__init__.py +2 -2
- omlish/asyncs/anyio.py +64 -1
- omlish/asyncs/asyncs.py +1 -3
- omlish/asyncs/futures.py +16 -15
- omlish/c3.py +5 -5
- omlish/check.py +8 -8
- omlish/collections/__init__.py +98 -63
- omlish/collections/_abc.py +2 -0
- omlish/collections/_io_abc.py +4 -2
- omlish/collections/cache/__init__.py +1 -1
- omlish/collections/cache/descriptor.py +12 -12
- omlish/collections/cache/impl.py +27 -20
- omlish/collections/cache/types.py +1 -1
- omlish/collections/coerce.py +44 -44
- omlish/collections/frozen.py +9 -9
- omlish/collections/identity.py +4 -5
- omlish/collections/mappings.py +5 -5
- omlish/collections/ordered.py +8 -8
- omlish/collections/skiplist.py +7 -7
- omlish/collections/sorted.py +4 -4
- omlish/collections/treap.py +42 -17
- omlish/collections/treapmap.py +59 -7
- omlish/collections/unmodifiable.py +25 -24
- omlish/collections/utils.py +1 -1
- omlish/configs/flattening.py +8 -7
- omlish/configs/props.py +3 -3
- omlish/dataclasses/__init__.py +1 -1
- omlish/dataclasses/impl/__init__.py +18 -0
- omlish/dataclasses/impl/api.py +15 -24
- omlish/dataclasses/impl/as_.py +4 -4
- omlish/dataclasses/impl/exceptions.py +1 -1
- omlish/dataclasses/impl/fields.py +8 -8
- omlish/dataclasses/impl/frozen.py +2 -2
- omlish/dataclasses/impl/init.py +6 -6
- omlish/dataclasses/impl/internals.py +16 -1
- omlish/dataclasses/impl/main.py +4 -4
- omlish/dataclasses/impl/metaclass.py +2 -2
- omlish/dataclasses/impl/metadata.py +1 -1
- omlish/dataclasses/impl/order.py +2 -2
- omlish/dataclasses/impl/params.py +4 -38
- omlish/dataclasses/impl/reflect.py +1 -7
- omlish/dataclasses/impl/replace.py +1 -1
- omlish/dataclasses/impl/repr.py +24 -6
- omlish/dataclasses/impl/simple.py +2 -2
- omlish/dataclasses/impl/slots.py +2 -2
- omlish/dataclasses/impl/utils.py +7 -7
- omlish/defs.py +13 -17
- omlish/diag/procfs.py +334 -0
- omlish/diag/ps.py +47 -0
- omlish/{replserver → diag/replserver}/console.py +26 -28
- omlish/{replserver → diag/replserver}/server.py +12 -12
- omlish/dispatch/dispatch.py +14 -16
- omlish/dispatch/functions.py +1 -1
- omlish/dispatch/methods.py +6 -7
- omlish/docker.py +8 -6
- omlish/dynamic.py +13 -13
- omlish/fnpairs.py +311 -0
- omlish/graphs/dot/items.py +1 -1
- omlish/graphs/trees.py +25 -31
- omlish/inject/__init__.py +7 -7
- omlish/inject/elements.py +2 -2
- omlish/inject/exceptions.py +8 -8
- omlish/inject/impl/elements.py +4 -4
- omlish/inject/impl/injector.py +6 -6
- omlish/inject/impl/inspect.py +3 -3
- omlish/inject/impl/scopes.py +9 -9
- omlish/inject/injector.py +1 -1
- omlish/inject/providers.py +2 -2
- omlish/inject/proxy.py +5 -5
- omlish/iterators.py +62 -26
- omlish/json.py +7 -6
- omlish/lang/__init__.py +172 -112
- omlish/lang/cached.py +15 -10
- omlish/lang/classes/__init__.py +35 -24
- omlish/lang/classes/abstract.py +3 -3
- omlish/lang/classes/restrict.py +14 -14
- omlish/lang/classes/simple.py +2 -2
- omlish/lang/classes/virtual.py +5 -5
- omlish/lang/clsdct.py +2 -2
- omlish/lang/cmp.py +2 -2
- omlish/lang/contextmanagers.py +31 -25
- omlish/lang/datetimes.py +1 -1
- omlish/lang/descriptors.py +51 -6
- omlish/lang/exceptions.py +2 -0
- omlish/lang/functions.py +101 -35
- omlish/lang/imports.py +25 -30
- omlish/lang/iterables.py +2 -2
- omlish/lang/maybes.py +2 -1
- omlish/lang/objects.py +17 -11
- omlish/lang/resolving.py +1 -1
- omlish/lang/strings.py +1 -1
- omlish/lang/timeouts.py +53 -0
- omlish/lang/typing.py +5 -5
- omlish/libc.py +15 -11
- omlish/logs/_abc.py +5 -1
- omlish/logs/filters.py +2 -0
- omlish/logs/formatters.py +6 -2
- omlish/logs/utils.py +1 -1
- omlish/marshal/base.py +9 -9
- omlish/marshal/dataclasses.py +2 -2
- omlish/marshal/enums.py +2 -2
- omlish/marshal/exceptions.py +1 -1
- omlish/marshal/factories.py +10 -10
- omlish/marshal/global_.py +10 -4
- omlish/marshal/iterables.py +2 -2
- omlish/marshal/mappings.py +2 -2
- omlish/marshal/objects.py +1 -2
- omlish/marshal/optionals.py +4 -4
- omlish/marshal/polymorphism.py +4 -4
- omlish/marshal/registries.py +3 -3
- omlish/marshal/standard.py +6 -6
- omlish/marshal/utils.py +3 -3
- omlish/marshal/values.py +1 -1
- omlish/math.py +9 -9
- omlish/os.py +13 -4
- omlish/reflect.py +5 -15
- omlish/sql/__init__.py +0 -0
- omlish/sql/_abc.py +65 -0
- omlish/sql/dbs.py +90 -0
- omlish/stats.py +7 -8
- omlish/term.py +1 -1
- omlish/testing/pydevd.py +30 -12
- omlish/testing/pytest/inject/__init__.py +7 -0
- omlish/testing/pytest/inject/harness.py +24 -2
- omlish/testing/pytest/plugins/__init__.py +1 -1
- omlish/testing/pytest/plugins/pydevd.py +12 -0
- omlish/testing/pytest/plugins/switches.py +3 -3
- omlish/testing/testing.py +5 -5
- omlish/text/delimit.py +3 -6
- omlish/text/parts.py +3 -3
- omlish-0.0.0.dev3.dist-info/METADATA +31 -0
- omlish-0.0.0.dev3.dist-info/RECORD +191 -0
- {omlish-0.0.0.dev1.dist-info → omlish-0.0.0.dev3.dist-info}/WHEEL +1 -1
- omlish/lang/classes/test/test_abstract.py +0 -89
- omlish/lang/classes/test/test_restrict.py +0 -71
- omlish/lang/classes/test/test_simple.py +0 -58
- omlish/lang/classes/test/test_virtual.py +0 -72
- omlish/testing/pytest/plugins/pycharm.py +0 -54
- omlish-0.0.0.dev1.dist-info/METADATA +0 -17
- omlish-0.0.0.dev1.dist-info/RECORD +0 -187
- /omlish/{lang/classes/test → diag}/__init__.py +0 -0
- /omlish/{replserver → diag/replserver}/__init__.py +0 -0
- /omlish/{replserver → diag/replserver}/__main__.py +0 -0
- {omlish-0.0.0.dev1.dist-info → omlish-0.0.0.dev3.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev1.dist-info → omlish-0.0.0.dev3.dist-info}/top_level.txt +0 -0
omlish/lang/contextmanagers.py
CHANGED
|
@@ -19,16 +19,16 @@ class ContextManaged:
|
|
|
19
19
|
|
|
20
20
|
def __exit__(
|
|
21
21
|
self,
|
|
22
|
-
exc_type:
|
|
23
|
-
exc_val:
|
|
24
|
-
exc_tb:
|
|
25
|
-
) ->
|
|
22
|
+
exc_type: type[BaseException] | None,
|
|
23
|
+
exc_val: BaseException | None,
|
|
24
|
+
exc_tb: types.TracebackType | None,
|
|
25
|
+
) -> bool | None:
|
|
26
26
|
return None
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class NopContextManaged(ContextManaged):
|
|
30
30
|
|
|
31
|
-
def __init_subclass__(cls, **kwargs):
|
|
31
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
|
32
32
|
raise TypeError
|
|
33
33
|
|
|
34
34
|
|
|
@@ -37,7 +37,7 @@ NOP_CONTEXT_MANAGED = NopContextManaged()
|
|
|
37
37
|
|
|
38
38
|
class NopContextManager:
|
|
39
39
|
|
|
40
|
-
def __init_subclass__(cls, **kwargs):
|
|
40
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
|
41
41
|
raise TypeError
|
|
42
42
|
|
|
43
43
|
def __call__(self, *args, **kwargs):
|
|
@@ -51,7 +51,7 @@ NOP_CONTEXT_MANAGER = NopContextManager()
|
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
@contextlib.contextmanager
|
|
54
|
-
def defer(fn: ta.Callable) -> ta.
|
|
54
|
+
def defer(fn: ta.Callable) -> ta.Generator[ta.Callable, None, None]:
|
|
55
55
|
try:
|
|
56
56
|
yield fn
|
|
57
57
|
finally:
|
|
@@ -59,7 +59,7 @@ def defer(fn: ta.Callable) -> ta.Iterator[ta.Callable]:
|
|
|
59
59
|
|
|
60
60
|
|
|
61
61
|
@contextlib.asynccontextmanager
|
|
62
|
-
async def a_defer(fn: ta.Awaitable) -> ta.
|
|
62
|
+
async def a_defer(fn: ta.Awaitable) -> ta.AsyncGenerator[ta.Awaitable, None]:
|
|
63
63
|
try:
|
|
64
64
|
yield fn
|
|
65
65
|
finally:
|
|
@@ -84,11 +84,11 @@ def disposing(obj: T, attr: str = 'dispose') -> ta.Iterator[T]:
|
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
@contextlib.contextmanager
|
|
87
|
-
def breakpoint_on_exception():
|
|
87
|
+
def breakpoint_on_exception(): # noqa
|
|
88
88
|
try:
|
|
89
89
|
yield
|
|
90
90
|
except Exception as e: # noqa
|
|
91
|
-
breakpoint()
|
|
91
|
+
breakpoint() # noqa
|
|
92
92
|
raise
|
|
93
93
|
|
|
94
94
|
|
|
@@ -102,7 +102,7 @@ def context_var_setting(var: contextvars.ContextVar[T], val: T) -> ta.Iterator[T
|
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
@contextlib.contextmanager
|
|
105
|
-
def attr_setting(obj, attr, val, *, default=None):
|
|
105
|
+
def attr_setting(obj, attr, val, *, default=None): # noqa
|
|
106
106
|
not_set = object()
|
|
107
107
|
orig = getattr(obj, attr, not_set)
|
|
108
108
|
try:
|
|
@@ -146,10 +146,10 @@ class ExitStacked:
|
|
|
146
146
|
|
|
147
147
|
def __exit__(
|
|
148
148
|
self,
|
|
149
|
-
exc_type:
|
|
150
|
-
exc_val:
|
|
151
|
-
exc_tb:
|
|
152
|
-
) ->
|
|
149
|
+
exc_type: type[BaseException] | None,
|
|
150
|
+
exc_val: BaseException | None,
|
|
151
|
+
exc_tb: types.TracebackType | None,
|
|
152
|
+
) -> bool | None:
|
|
153
153
|
self._exit_stack.__exit__(exc_type, exc_val, exc_tb)
|
|
154
154
|
try:
|
|
155
155
|
superfn = super().__exit__ # type: ignore
|
|
@@ -162,20 +162,28 @@ class ExitStacked:
|
|
|
162
162
|
##
|
|
163
163
|
|
|
164
164
|
|
|
165
|
-
ContextWrappable: ta.TypeAlias = ta.
|
|
165
|
+
ContextWrappable: ta.TypeAlias = ta.ContextManager | str | ta.Callable[..., ta.ContextManager]
|
|
166
166
|
|
|
167
167
|
|
|
168
168
|
class ContextWrapped:
|
|
169
169
|
|
|
170
|
-
def __init__(self, fn: ta.Callable, cm:
|
|
170
|
+
def __init__(self, fn: ta.Callable, cm: str | ContextWrappable) -> None:
|
|
171
171
|
super().__init__()
|
|
172
172
|
|
|
173
|
-
self._fn = fn
|
|
173
|
+
self._fn = (fn,)
|
|
174
174
|
self._cm = cm
|
|
175
175
|
self._name: str | None = None
|
|
176
176
|
|
|
177
177
|
functools.update_wrapper(self, fn)
|
|
178
178
|
|
|
179
|
+
@property
|
|
180
|
+
def _fn(self):
|
|
181
|
+
return self.__fn
|
|
182
|
+
|
|
183
|
+
@_fn.setter
|
|
184
|
+
def _fn(self, x):
|
|
185
|
+
self.__fn = x
|
|
186
|
+
|
|
179
187
|
def __set_name__(self, owner, name):
|
|
180
188
|
if name is not None:
|
|
181
189
|
if self._name is not None:
|
|
@@ -187,7 +195,7 @@ class ContextWrapped:
|
|
|
187
195
|
def __get__(self, instance, owner=None):
|
|
188
196
|
if instance is None and owner is None:
|
|
189
197
|
return self
|
|
190
|
-
fn = self._fn.__get__(instance, owner) # noqa
|
|
198
|
+
fn = self._fn[0].__get__(instance, owner) # noqa
|
|
191
199
|
cm: ta.Any = self._cm
|
|
192
200
|
if isinstance(self._cm, str):
|
|
193
201
|
if instance is not None:
|
|
@@ -204,10 +212,8 @@ class ContextWrapped:
|
|
|
204
212
|
raise TypeError(cm)
|
|
205
213
|
ret = type(self)(fn, cm)
|
|
206
214
|
if self._name is not None:
|
|
207
|
-
|
|
215
|
+
with contextlib.suppress(TypeError):
|
|
208
216
|
instance.__dict__[self._name] = ret
|
|
209
|
-
except TypeError:
|
|
210
|
-
pass
|
|
211
217
|
return ret
|
|
212
218
|
|
|
213
219
|
def __call__(self, *args, **kwargs):
|
|
@@ -217,10 +223,10 @@ class ContextWrapped:
|
|
|
217
223
|
if not hasattr(cm, '__enter__') and callable(cm):
|
|
218
224
|
cm = cm(*args, **kwargs)
|
|
219
225
|
with cm: # type: ignore
|
|
220
|
-
return self._fn(*args, **kwargs)
|
|
226
|
+
return self._fn[0](*args, **kwargs)
|
|
221
227
|
|
|
222
228
|
|
|
223
|
-
def context_wrapped(cm): # ContextWrappable -> ta.Callable[[CallableT], CallableT]:
|
|
229
|
+
def context_wrapped(cm): # ContextWrappable -> ta.Callable[[CallableT], CallableT]: # noqa
|
|
224
230
|
def inner(fn):
|
|
225
231
|
return ContextWrapped(fn, cm)
|
|
226
232
|
return inner
|
|
@@ -230,7 +236,7 @@ def context_wrapped(cm): # ContextWrappable -> ta.Callable[[CallableT], Callabl
|
|
|
230
236
|
|
|
231
237
|
|
|
232
238
|
Lockable = ta.Callable[[], ta.ContextManager]
|
|
233
|
-
DefaultLockable =
|
|
239
|
+
DefaultLockable = bool | Lockable | ta.ContextManager | None
|
|
234
240
|
|
|
235
241
|
|
|
236
242
|
def default_lock(value: DefaultLockable, default: DefaultLockable) -> Lockable:
|
omlish/lang/datetimes.py
CHANGED
|
@@ -62,6 +62,6 @@ def parse_timedelta(s: str) -> datetime.timedelta:
|
|
|
62
62
|
for k, v in match.groupdict().items()
|
|
63
63
|
if k != 'negative' and v is not None}
|
|
64
64
|
if not timedelta_kwargs:
|
|
65
|
-
raise ValueError
|
|
65
|
+
raise ValueError
|
|
66
66
|
sign = -1 if match.groupdict().get('negative') else 1
|
|
67
67
|
return sign * datetime.timedelta(**timedelta_kwargs)
|
omlish/lang/descriptors.py
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import operator
|
|
1
3
|
import typing as ta
|
|
2
4
|
|
|
3
5
|
|
|
6
|
+
T = ta.TypeVar('T')
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
##
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def attr_property(n: str):
|
|
13
|
+
return property(operator.attrgetter(n))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def item_property(n: str):
|
|
17
|
+
return property(operator.itemgetter(n))
|
|
18
|
+
|
|
19
|
+
|
|
4
20
|
##
|
|
5
21
|
|
|
6
22
|
|
|
@@ -24,29 +40,58 @@ def unwrap_method_descriptors(fn: ta.Callable) -> ta.Callable:
|
|
|
24
40
|
##
|
|
25
41
|
|
|
26
42
|
|
|
27
|
-
class
|
|
43
|
+
class AccessForbiddenError(Exception):
|
|
28
44
|
|
|
29
|
-
def __init__(self, name:
|
|
45
|
+
def __init__(self, name: str | None = None, *args: ta.Any, **kwargs: ta.Any) -> None:
|
|
30
46
|
super().__init__(*((name,) if name is not None else ()), *args, **kwargs) # noqa
|
|
31
47
|
self.name = name
|
|
32
48
|
|
|
33
49
|
|
|
34
50
|
class AccessForbiddenDescriptor:
|
|
35
51
|
|
|
36
|
-
def __init__(self, name:
|
|
52
|
+
def __init__(self, name: str | None = None) -> None:
|
|
37
53
|
super().__init__()
|
|
38
54
|
|
|
39
55
|
self._name = name
|
|
40
56
|
|
|
41
|
-
def __set_name__(self, owner:
|
|
57
|
+
def __set_name__(self, owner: type, name: str) -> None:
|
|
42
58
|
if self._name is None:
|
|
43
59
|
self._name = name
|
|
44
60
|
elif name != self._name:
|
|
45
61
|
raise NameError(name)
|
|
46
62
|
|
|
47
63
|
def __get__(self, instance, owner=None):
|
|
48
|
-
raise
|
|
64
|
+
raise AccessForbiddenError(self._name)
|
|
49
65
|
|
|
50
66
|
|
|
51
|
-
def access_forbidden():
|
|
67
|
+
def access_forbidden(): # noqa
|
|
52
68
|
return AccessForbiddenDescriptor()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
##
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class _ClassOnly:
|
|
75
|
+
def __init__(self, mth):
|
|
76
|
+
if not isinstance(mth, classmethod):
|
|
77
|
+
raise TypeError(f'must be classmethod: {mth}')
|
|
78
|
+
super().__init__()
|
|
79
|
+
self._mth = (mth,)
|
|
80
|
+
functools.update_wrapper(self, mth) # type: ignore
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def _mth(self):
|
|
84
|
+
return self.__mth
|
|
85
|
+
|
|
86
|
+
@_mth.setter
|
|
87
|
+
def _mth(self, x):
|
|
88
|
+
self.__mth = x
|
|
89
|
+
|
|
90
|
+
def __get__(self, instance, owner):
|
|
91
|
+
if instance is not None:
|
|
92
|
+
raise TypeError(f'method must not be used on instance: {self._mth}')
|
|
93
|
+
return self._mth[0].__get__(instance, owner)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def classonly(obj: T) -> T: # noqa
|
|
97
|
+
return _ClassOnly(obj) # type: ignore
|
omlish/lang/functions.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import dataclasses as dc
|
|
1
2
|
import functools
|
|
2
3
|
import time
|
|
3
4
|
import typing as ta
|
|
@@ -6,6 +7,11 @@ from .descriptors import is_method_descriptor
|
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
T = ta.TypeVar('T')
|
|
10
|
+
P = ta.ParamSpec('P')
|
|
11
|
+
CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
##
|
|
9
15
|
|
|
10
16
|
|
|
11
17
|
def is_lambda(f: ta.Any) -> bool:
|
|
@@ -13,6 +19,9 @@ def is_lambda(f: ta.Any) -> bool:
|
|
|
13
19
|
return isinstance(f, type(l)) and f.__name__ == l.__name__
|
|
14
20
|
|
|
15
21
|
|
|
22
|
+
##
|
|
23
|
+
|
|
24
|
+
|
|
16
25
|
def maybe_call(obj: ta.Any, att: str, *args, default: ta.Any = None, **kwargs) -> ta.Any:
|
|
17
26
|
try:
|
|
18
27
|
fn = getattr(obj, att)
|
|
@@ -22,6 +31,16 @@ def maybe_call(obj: ta.Any, att: str, *args, default: ta.Any = None, **kwargs) -
|
|
|
22
31
|
return fn(*args, **kwargs)
|
|
23
32
|
|
|
24
33
|
|
|
34
|
+
def recurse(fn: ta.Callable[..., T], *args, **kwargs) -> T:
|
|
35
|
+
def rec(*args, **kwargs) -> T:
|
|
36
|
+
return fn(rec, *args, **kwargs)
|
|
37
|
+
|
|
38
|
+
return rec(*args, **kwargs)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
##
|
|
42
|
+
|
|
43
|
+
|
|
25
44
|
def unwrap_func(fn: ta.Callable) -> ta.Callable:
|
|
26
45
|
fn, _ = unwrap_func_with_partials(fn)
|
|
27
46
|
return fn
|
|
@@ -39,38 +58,51 @@ def unwrap_func_with_partials(fn: ta.Callable) -> tuple[ta.Callable, list[functo
|
|
|
39
58
|
nxt = getattr(fn, '__wrapped__', None)
|
|
40
59
|
if not callable(nxt):
|
|
41
60
|
break
|
|
42
|
-
|
|
61
|
+
if nxt is fn:
|
|
43
62
|
raise TypeError(fn)
|
|
44
63
|
fn = nxt
|
|
45
64
|
return fn, ps
|
|
46
65
|
|
|
47
66
|
|
|
67
|
+
##
|
|
68
|
+
|
|
69
|
+
|
|
48
70
|
def raise_(o: BaseException) -> ta.NoReturn:
|
|
49
71
|
raise o
|
|
50
72
|
|
|
51
73
|
|
|
74
|
+
def raising(o: BaseException) -> ta.Callable[..., ta.NoReturn]:
|
|
75
|
+
def inner(*args, **kwargs):
|
|
76
|
+
raise o
|
|
77
|
+
return inner
|
|
78
|
+
|
|
79
|
+
|
|
52
80
|
def try_(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return inner
|
|
81
|
+
fn: ta.Callable[P, T],
|
|
82
|
+
exc: type[Exception] | ta.Iterable[type[Exception]] = Exception,
|
|
83
|
+
default: T | None = None,
|
|
84
|
+
) -> ta.Callable[P, T]:
|
|
85
|
+
def inner(*args, **kwargs):
|
|
86
|
+
try:
|
|
87
|
+
return fn(*args, **kwargs)
|
|
88
|
+
except exct:
|
|
89
|
+
return default
|
|
64
90
|
|
|
65
91
|
exct = (exc,) if isinstance(exc, type) else tuple(exc)
|
|
66
|
-
return
|
|
92
|
+
return inner
|
|
67
93
|
|
|
68
94
|
|
|
69
|
-
def
|
|
70
|
-
def
|
|
71
|
-
|
|
95
|
+
def finally_(fn: ta.Callable[P, T], fin: ta.Callable) -> ta.Callable[P, T]:
|
|
96
|
+
def inner(*args, **kwargs):
|
|
97
|
+
try:
|
|
98
|
+
return fn(*args, **kwargs)
|
|
99
|
+
finally:
|
|
100
|
+
fin()
|
|
72
101
|
|
|
73
|
-
return
|
|
102
|
+
return inner
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
##
|
|
74
106
|
|
|
75
107
|
|
|
76
108
|
def identity(obj: T) -> T:
|
|
@@ -96,31 +128,65 @@ def is_not_none(o: ta.Any) -> bool:
|
|
|
96
128
|
return o is not None
|
|
97
129
|
|
|
98
130
|
|
|
99
|
-
class
|
|
131
|
+
class VoidError(Exception):
|
|
100
132
|
pass
|
|
101
133
|
|
|
102
134
|
|
|
103
135
|
class Void:
|
|
104
136
|
|
|
105
|
-
def __new__(cls, *args, **kwargs):
|
|
106
|
-
raise
|
|
137
|
+
def __new__(cls, *args: ta.Any, **kwargs: ta.Any) -> None: # type: ignore # noqa
|
|
138
|
+
raise VoidError
|
|
139
|
+
|
|
140
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
|
141
|
+
raise VoidError
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def void(*args: ta.Any, **kwargs: ta.Any) -> ta.NoReturn:
|
|
145
|
+
raise VoidError
|
|
146
|
+
|
|
107
147
|
|
|
108
|
-
|
|
109
|
-
raise VoidException
|
|
148
|
+
##
|
|
110
149
|
|
|
111
150
|
|
|
112
|
-
|
|
113
|
-
raise VoidException
|
|
151
|
+
_MISSING = object()
|
|
114
152
|
|
|
115
153
|
|
|
116
|
-
def
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
154
|
+
def periodically(
|
|
155
|
+
fn: CallableT,
|
|
156
|
+
interval_s: float,
|
|
157
|
+
initial: ta.Any = _MISSING,
|
|
158
|
+
*,
|
|
159
|
+
include_runtime: bool = False,
|
|
160
|
+
) -> CallableT:
|
|
161
|
+
nxt = time.time() + interval_s
|
|
162
|
+
ret = initial
|
|
163
|
+
|
|
164
|
+
@functools.wraps(fn)
|
|
165
|
+
def inner(*args, **kwargs):
|
|
166
|
+
nonlocal nxt, ret
|
|
167
|
+
if time.time() >= nxt or ret is _MISSING:
|
|
168
|
+
if include_runtime:
|
|
169
|
+
nxt = time.time() + interval_s
|
|
170
|
+
ret = fn(*args, **kwargs)
|
|
171
|
+
if not include_runtime:
|
|
172
|
+
nxt = time.time() + interval_s
|
|
173
|
+
return ret
|
|
174
|
+
|
|
175
|
+
return inner # type: ignore
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
##
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@dc.dataclass(init=False)
|
|
182
|
+
class Args:
|
|
183
|
+
args: ta.Sequence[ta.Any]
|
|
184
|
+
kwargs: ta.Mapping[str, ta.Any]
|
|
185
|
+
|
|
186
|
+
def __init__(self, *args: ta.Any, **kwargs: ta.Any) -> None:
|
|
187
|
+
super().__init__()
|
|
188
|
+
self.args = args
|
|
189
|
+
self.kwargs = kwargs
|
|
190
|
+
|
|
191
|
+
def __call__(self, fn: ta.Callable[..., T]) -> T:
|
|
192
|
+
return fn(*self.args, **self.kwargs)
|
omlish/lang/imports.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import functools
|
|
2
|
-
import importlib
|
|
3
|
+
import importlib.resources
|
|
3
4
|
import sys
|
|
4
5
|
import types
|
|
5
6
|
import typing as ta
|
|
@@ -10,14 +11,14 @@ from .cached import cached_function
|
|
|
10
11
|
##
|
|
11
12
|
|
|
12
13
|
|
|
13
|
-
def lazy_import(name: str, package:
|
|
14
|
+
def lazy_import(name: str, package: str | None = None) -> ta.Callable[[], ta.Any]:
|
|
14
15
|
return cached_function(functools.partial(importlib.import_module, name, package=package))
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
def proxy_import(name: str, package:
|
|
18
|
+
def proxy_import(name: str, package: str | None = None) -> types.ModuleType:
|
|
18
19
|
omod = None
|
|
19
20
|
|
|
20
|
-
def __getattr__(att):
|
|
21
|
+
def __getattr__(att): # noqa
|
|
21
22
|
nonlocal omod
|
|
22
23
|
if omod is None:
|
|
23
24
|
omod = importlib.import_module(name, package=package)
|
|
@@ -31,9 +32,6 @@ def proxy_import(name: str, package: ta.Optional[str] = None) -> types.ModuleTyp
|
|
|
31
32
|
##
|
|
32
33
|
|
|
33
34
|
|
|
34
|
-
_pkg_resources = lazy_import('pkg_resources')
|
|
35
|
-
|
|
36
|
-
|
|
37
35
|
def import_module(dotted_path: str) -> types.ModuleType:
|
|
38
36
|
if not dotted_path:
|
|
39
37
|
raise ImportError(dotted_path)
|
|
@@ -42,7 +40,7 @@ def import_module(dotted_path: str) -> types.ModuleType:
|
|
|
42
40
|
try:
|
|
43
41
|
mod = getattr(mod, name)
|
|
44
42
|
except AttributeError:
|
|
45
|
-
raise AttributeError('Module
|
|
43
|
+
raise AttributeError(f'Module {mod!r} has no attribute {name!r}') from None
|
|
46
44
|
return mod
|
|
47
45
|
|
|
48
46
|
|
|
@@ -52,7 +50,7 @@ def import_module_attr(dotted_path: str) -> ta.Any:
|
|
|
52
50
|
try:
|
|
53
51
|
return getattr(mod, class_name)
|
|
54
52
|
except AttributeError:
|
|
55
|
-
raise AttributeError('Module
|
|
53
|
+
raise AttributeError(f'Module {module_name!r} has no attr {class_name!r}') from None
|
|
56
54
|
|
|
57
55
|
|
|
58
56
|
SPECIAL_IMPORTABLE: ta.AbstractSet[str] = frozenset([
|
|
@@ -65,46 +63,43 @@ def yield_importable(
|
|
|
65
63
|
package_root: str,
|
|
66
64
|
*,
|
|
67
65
|
recursive: bool = False,
|
|
68
|
-
filter: ta.
|
|
66
|
+
filter: ta.Callable[[str], bool] | None = None, # noqa
|
|
69
67
|
include_special: bool = False,
|
|
70
68
|
) -> ta.Iterator[str]:
|
|
71
|
-
def rec(
|
|
72
|
-
if
|
|
69
|
+
def rec(cur):
|
|
70
|
+
if cur.split('.')[-1] == '__pycache__':
|
|
73
71
|
return
|
|
74
72
|
|
|
75
73
|
try:
|
|
76
|
-
module = sys.modules[
|
|
74
|
+
module = sys.modules[cur]
|
|
77
75
|
except KeyError:
|
|
78
76
|
try:
|
|
79
|
-
__import__(
|
|
77
|
+
__import__(cur)
|
|
80
78
|
except ImportError:
|
|
81
79
|
return
|
|
82
|
-
module = sys.modules[
|
|
80
|
+
module = sys.modules[cur]
|
|
83
81
|
|
|
84
82
|
# FIXME: pyox
|
|
85
83
|
if getattr(module, '__file__', None) is None:
|
|
86
84
|
return
|
|
87
85
|
|
|
88
|
-
for file in
|
|
89
|
-
if file.endswith('.py'):
|
|
90
|
-
if not (include_special or file not in SPECIAL_IMPORTABLE):
|
|
86
|
+
for file in importlib.resources.files(cur).iterdir():
|
|
87
|
+
if file.is_file() and file.name.endswith('.py'):
|
|
88
|
+
if not (include_special or file.name not in SPECIAL_IMPORTABLE):
|
|
91
89
|
continue
|
|
92
90
|
|
|
93
|
-
name =
|
|
91
|
+
name = cur + '.' + file.name[:-3]
|
|
94
92
|
if filter is not None and not filter(name):
|
|
95
93
|
continue
|
|
96
94
|
|
|
97
95
|
yield name
|
|
98
96
|
|
|
99
|
-
elif recursive and
|
|
100
|
-
name =
|
|
97
|
+
elif recursive and file.is_dir():
|
|
98
|
+
name = cur + '.' + file.name
|
|
101
99
|
if filter is not None and not filter(name):
|
|
102
100
|
continue
|
|
103
|
-
|
|
104
|
-
try:
|
|
101
|
+
with contextlib.suppress(ImportError, NotImplementedError):
|
|
105
102
|
yield from rec(name)
|
|
106
|
-
except (ImportError, NotImplementedError):
|
|
107
|
-
pass
|
|
108
103
|
|
|
109
104
|
yield from rec(package_root)
|
|
110
105
|
|
|
@@ -112,10 +107,10 @@ def yield_importable(
|
|
|
112
107
|
def yield_import_all(
|
|
113
108
|
package_root: str,
|
|
114
109
|
*,
|
|
115
|
-
globals:
|
|
116
|
-
locals:
|
|
110
|
+
globals: dict[str, ta.Any] | None = None, # noqa
|
|
111
|
+
locals: dict[str, ta.Any] | None = None, # noqa
|
|
117
112
|
recursive: bool = False,
|
|
118
|
-
filter: ta.
|
|
113
|
+
filter: ta.Callable[[str], bool] | None = None, # noqa
|
|
119
114
|
include_special: bool = False,
|
|
120
115
|
) -> ta.Iterator[str]:
|
|
121
116
|
for import_path in yield_importable(
|
|
@@ -132,7 +127,7 @@ def import_all(
|
|
|
132
127
|
package_root: str,
|
|
133
128
|
*,
|
|
134
129
|
recursive: bool = False,
|
|
135
|
-
filter: ta.
|
|
130
|
+
filter: ta.Callable[[str], bool] | None = None, # noqa
|
|
136
131
|
include_special: bool = False,
|
|
137
132
|
) -> None:
|
|
138
133
|
for _ in yield_import_all(
|
|
@@ -144,7 +139,7 @@ def import_all(
|
|
|
144
139
|
pass
|
|
145
140
|
|
|
146
141
|
|
|
147
|
-
def try_import(spec: str) ->
|
|
142
|
+
def try_import(spec: str) -> types.ModuleType | None:
|
|
148
143
|
s = spec.lstrip('.')
|
|
149
144
|
l = len(spec) - len(s)
|
|
150
145
|
try:
|
omlish/lang/iterables.py
CHANGED
|
@@ -28,13 +28,13 @@ def exhaust(it: ta.Iterable[ta.Any]) -> None:
|
|
|
28
28
|
pass
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
def peek(vs: ta.Iterable[T]) ->
|
|
31
|
+
def peek(vs: ta.Iterable[T]) -> tuple[T, ta.Iterator[T]]:
|
|
32
32
|
it = iter(vs)
|
|
33
33
|
v = next(it)
|
|
34
34
|
return v, itertools.chain(iter((v,)), it)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
Rangeable: ta.TypeAlias =
|
|
37
|
+
Rangeable: ta.TypeAlias = int | tuple[int] | tuple[int, int] | ta.Iterable[int]
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
def asrange(i: Rangeable) -> ta.Iterable[int]:
|
omlish/lang/maybes.py
CHANGED
|
@@ -63,6 +63,7 @@ class Maybe(abc.ABC, ta.Generic[T]):
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
class _Maybe(Maybe[T], tuple):
|
|
66
|
+
__slots__ = ()
|
|
66
67
|
|
|
67
68
|
@property
|
|
68
69
|
def present(self) -> bool:
|
|
@@ -130,7 +131,7 @@ def empty() -> Maybe[T]:
|
|
|
130
131
|
return _empty # noqa
|
|
131
132
|
|
|
132
133
|
|
|
133
|
-
def maybe(o:
|
|
134
|
+
def maybe(o: T | None) -> Maybe[T]:
|
|
134
135
|
if o is None:
|
|
135
136
|
return _empty # noqa
|
|
136
137
|
return just(o)
|