omlish 0.0.0.dev2__py3-none-any.whl → 0.0.0.dev4__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 -2
- omlish/__init__.py +8 -0
- omlish/argparse.py +4 -4
- omlish/asyncs/__init__.py +23 -2
- omlish/asyncs/anyio.py +13 -11
- omlish/asyncs/asyncs.py +1 -3
- omlish/asyncs/flavors.py +201 -0
- omlish/asyncs/futures.py +10 -9
- omlish/asyncs/trio_asyncio.py +41 -0
- omlish/c3.py +1 -1
- omlish/check.py +3 -3
- 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 +8 -8
- omlish/collections/cache/impl.py +24 -17
- omlish/collections/cache/types.py +1 -1
- omlish/collections/coerce.py +1 -1
- omlish/collections/frozen.py +6 -6
- omlish/collections/identity.py +3 -4
- omlish/collections/mappings.py +2 -2
- omlish/collections/ordered.py +7 -7
- omlish/collections/skiplist.py +1 -1
- omlish/collections/sorted.py +1 -1
- omlish/collections/treap.py +25 -0
- omlish/collections/treapmap.py +57 -5
- omlish/collections/unmodifiable.py +9 -9
- omlish/collections/utils.py +1 -1
- omlish/configs/flattening.py +7 -6
- omlish/configs/props.py +3 -3
- omlish/dataclasses/__init__.py +1 -1
- omlish/dataclasses/impl/__init__.py +17 -1
- omlish/dataclasses/impl/api.py +10 -11
- omlish/dataclasses/impl/as_.py +4 -4
- omlish/dataclasses/impl/exceptions.py +1 -1
- omlish/dataclasses/impl/fields.py +7 -7
- omlish/dataclasses/impl/frozen.py +2 -2
- omlish/dataclasses/impl/init.py +5 -5
- omlish/dataclasses/impl/internals.py +1 -1
- omlish/dataclasses/impl/metaclass.py +1 -1
- omlish/dataclasses/impl/order.py +1 -1
- omlish/dataclasses/impl/replace.py +1 -1
- omlish/dataclasses/impl/repr.py +4 -4
- omlish/dataclasses/impl/utils.py +6 -6
- omlish/defs.py +13 -17
- omlish/{procfs.py → diag/procfs.py} +22 -24
- omlish/diag/ps.py +47 -0
- omlish/{replserver → diag/replserver}/console.py +18 -20
- omlish/{replserver → diag/replserver}/server.py +8 -8
- omlish/dispatch/dispatch.py +5 -8
- omlish/dispatch/functions.py +1 -1
- omlish/dispatch/methods.py +4 -5
- omlish/docker.py +1 -1
- omlish/dynamic.py +10 -10
- omlish/fnpairs.py +400 -0
- omlish/graphs/trees.py +13 -13
- omlish/inject/__init__.py +7 -7
- omlish/inject/elements.py +1 -1
- omlish/inject/exceptions.py +7 -7
- omlish/inject/impl/elements.py +17 -5
- omlish/inject/impl/injector.py +12 -10
- omlish/inject/impl/inspect.py +2 -2
- omlish/inject/impl/scopes.py +12 -11
- omlish/inject/proxy.py +5 -5
- omlish/iterators.py +19 -24
- omlish/json.py +143 -6
- omlish/lang/__init__.py +13 -4
- omlish/lang/cached.py +2 -5
- omlish/lang/classes/__init__.py +2 -2
- omlish/lang/classes/abstract.py +2 -2
- omlish/lang/classes/restrict.py +14 -14
- omlish/lang/classes/simple.py +1 -1
- omlish/lang/classes/virtual.py +5 -5
- omlish/lang/clsdct.py +1 -1
- omlish/lang/cmp.py +2 -2
- omlish/lang/contextmanagers.py +12 -14
- omlish/lang/descriptors.py +16 -4
- omlish/lang/exceptions.py +1 -1
- omlish/lang/functions.py +58 -22
- omlish/lang/imports.py +22 -27
- omlish/lang/iterables.py +2 -2
- omlish/lang/maybes.py +1 -0
- omlish/lang/objects.py +15 -9
- omlish/lang/resolving.py +1 -1
- omlish/lang/strings.py +1 -1
- omlish/lang/sys.py +7 -0
- omlish/lang/typing.py +3 -3
- omlish/libc.py +9 -5
- 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 +3 -3
- omlish/marshal/exceptions.py +1 -1
- omlish/marshal/global_.py +10 -4
- omlish/marshal/objects.py +1 -2
- omlish/marshal/registries.py +3 -3
- omlish/marshal/utils.py +2 -2
- omlish/marshal/values.py +1 -1
- omlish/math.py +9 -9
- omlish/reflect.py +3 -3
- omlish/sql/__init__.py +9 -0
- omlish/sql/asyncs.py +148 -0
- omlish/stats.py +4 -5
- omlish/term.py +1 -1
- omlish/testing/pydevd.py +28 -6
- omlish/testing/pytest/inject/harness.py +1 -1
- omlish/testing/pytest/plugins/pydevd.py +1 -1
- omlish/testing/pytest/plugins/switches.py +1 -1
- omlish/text/delimit.py +3 -6
- {omlish-0.0.0.dev2.dist-info → omlish-0.0.0.dev4.dist-info}/METADATA +4 -1
- omlish-0.0.0.dev4.dist-info/RECORD +195 -0
- {omlish-0.0.0.dev2.dist-info → omlish-0.0.0.dev4.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-0.0.0.dev2.dist-info/RECORD +0 -193
- /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/sql/{_abcs.py → _abc.py} +0 -0
- {omlish-0.0.0.dev2.dist-info → omlish-0.0.0.dev4.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev2.dist-info → omlish-0.0.0.dev4.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/__init__.py
CHANGED
omlish/argparse.py
CHANGED
|
@@ -164,7 +164,7 @@ class _CliMeta(type):
|
|
|
164
164
|
namespace['_parser'] = parser
|
|
165
165
|
|
|
166
166
|
subparsers = parser.add_subparsers()
|
|
167
|
-
for
|
|
167
|
+
for att, obj in objs.items():
|
|
168
168
|
if isinstance(obj, Command):
|
|
169
169
|
if obj.parent is not None:
|
|
170
170
|
raise NotImplementedError
|
|
@@ -174,14 +174,14 @@ class _CliMeta(type):
|
|
|
174
174
|
cparser.set_defaults(_cmd=obj)
|
|
175
175
|
|
|
176
176
|
elif isinstance(obj, Arg):
|
|
177
|
-
if
|
|
178
|
-
akwargs = get_arg_ann_kwargs(anns[
|
|
177
|
+
if att in anns:
|
|
178
|
+
akwargs = get_arg_ann_kwargs(anns[att])
|
|
179
179
|
obj.kwargs = {**akwargs, **obj.kwargs}
|
|
180
180
|
if not obj.dest:
|
|
181
181
|
if 'dest' in obj.kwargs:
|
|
182
182
|
obj.dest = obj.kwargs['dest']
|
|
183
183
|
else:
|
|
184
|
-
obj.dest = obj.kwargs['dest'] =
|
|
184
|
+
obj.dest = obj.kwargs['dest'] = att # type: ignore
|
|
185
185
|
parser.add_argument(*obj.args, **obj.kwargs)
|
|
186
186
|
|
|
187
187
|
else:
|
omlish/asyncs/__init__.py
CHANGED
|
@@ -6,12 +6,33 @@ from .asyncs import ( # noqa
|
|
|
6
6
|
syncable_iterable,
|
|
7
7
|
)
|
|
8
8
|
|
|
9
|
+
from .flavors import ( # noqa
|
|
10
|
+
ContextManagerAdapter,
|
|
11
|
+
Flavor,
|
|
12
|
+
adapt,
|
|
13
|
+
adapt_context,
|
|
14
|
+
from_anyio,
|
|
15
|
+
from_anyio_context,
|
|
16
|
+
from_asyncio,
|
|
17
|
+
from_asyncio_context,
|
|
18
|
+
from_trio,
|
|
19
|
+
from_trio_context,
|
|
20
|
+
get_flavor,
|
|
21
|
+
mark_anyio,
|
|
22
|
+
mark_asyncio,
|
|
23
|
+
mark_flavor,
|
|
24
|
+
mark_trio,
|
|
25
|
+
)
|
|
9
26
|
|
|
10
27
|
from .futures import ( # noqa
|
|
11
|
-
|
|
12
|
-
|
|
28
|
+
FutureError,
|
|
29
|
+
FutureTimeoutError,
|
|
13
30
|
ImmediateExecutor,
|
|
14
31
|
new_thread_or_immediate_executor,
|
|
15
32
|
wait_dependent_futures,
|
|
16
33
|
wait_futures,
|
|
17
34
|
)
|
|
35
|
+
|
|
36
|
+
from .trio_asyncio import ( # noqa
|
|
37
|
+
with_trio_asyncio_loop,
|
|
38
|
+
)
|
omlish/asyncs/anyio.py
CHANGED
|
@@ -39,11 +39,12 @@ def split_memory_object_streams(
|
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
# FIXME: https://github.com/python/mypy/issues/15238
|
|
42
|
-
#
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
# FIXME: https://youtrack.jetbrains.com/issues?q=tag:%20%7BPEP%20695%7D
|
|
43
|
+
def create_memory_object_stream[T](max_buffer_size: float = 0) -> tuple[
|
|
44
|
+
anyio.streams.memory.MemoryObjectSendStream[T],
|
|
45
|
+
anyio.streams.memory.MemoryObjectReceiveStream[T],
|
|
46
|
+
]:
|
|
47
|
+
return anyio.create_memory_object_stream[T](max_buffer_size) # noqa
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
def staple_memory_object_stream(
|
|
@@ -57,12 +58,13 @@ def staple_memory_object_stream(
|
|
|
57
58
|
|
|
58
59
|
|
|
59
60
|
# FIXME: https://github.com/python/mypy/issues/15238
|
|
60
|
-
#
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
#
|
|
61
|
+
# FIXME: https://youtrack.jetbrains.com/issues?q=tag:%20%7BPEP%20695%7D
|
|
62
|
+
def staple_memory_object_stream2[T](max_buffer_size: float = 0) -> anyio.streams.stapled.StapledObjectStream[T]:
|
|
63
|
+
send, receive = anyio.create_memory_object_stream[T](max_buffer_size)
|
|
64
|
+
return anyio.streams.stapled.StapledObjectStream(
|
|
65
|
+
check.isinstance(send, anyio.streams.memory.MemoryObjectSendStream), # type: ignore
|
|
66
|
+
check.isinstance(receive, anyio.streams.memory.MemoryObjectReceiveStream), # type: ignore
|
|
67
|
+
)
|
|
66
68
|
|
|
67
69
|
|
|
68
70
|
async def gather(*fns: ta.Callable[..., ta.Awaitable[T]], take_first: bool = False) -> list[lang.Maybe[T]]:
|
omlish/asyncs/asyncs.py
CHANGED
|
@@ -26,10 +26,8 @@ def sync_await(fn: ta.Callable[..., T], *args, **kwargs) -> T:
|
|
|
26
26
|
|
|
27
27
|
cr = gate()
|
|
28
28
|
with contextlib.closing(cr):
|
|
29
|
-
|
|
29
|
+
with contextlib.suppress(StopIteration):
|
|
30
30
|
cr.send(None)
|
|
31
|
-
except StopIteration:
|
|
32
|
-
pass
|
|
33
31
|
if ret is missing or cr.cr_await is not None or cr.cr_running:
|
|
34
32
|
raise TypeError('Not terminated')
|
|
35
33
|
|
omlish/asyncs/flavors.py
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- 'get current'? -> sniffio..
|
|
4
|
+
- mark whole class / module?
|
|
5
|
+
- sync/greenlet bridge
|
|
6
|
+
"""
|
|
7
|
+
import abc
|
|
8
|
+
import dataclasses as dc
|
|
9
|
+
import enum
|
|
10
|
+
import typing as ta
|
|
11
|
+
|
|
12
|
+
from .. import lang
|
|
13
|
+
from .trio_asyncio import check_trio_asyncio
|
|
14
|
+
|
|
15
|
+
if ta.TYPE_CHECKING:
|
|
16
|
+
import sniffio
|
|
17
|
+
import trio_asyncio
|
|
18
|
+
else:
|
|
19
|
+
sniffio = lang.proxy_import('sniffio')
|
|
20
|
+
trio_asyncio = lang.proxy_import('trio_asyncio')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
T = ta.TypeVar('T')
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
_FLAVOR_ATTR = '__async_flavor__'
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class _MISSING(lang.Marker):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Flavor(enum.Enum):
|
|
37
|
+
ASYNCIO = enum.auto()
|
|
38
|
+
TRIO = enum.auto()
|
|
39
|
+
ANYIO = enum.auto()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def mark_flavor(f: Flavor):
|
|
43
|
+
if not isinstance(f, Flavor):
|
|
44
|
+
raise TypeError(f)
|
|
45
|
+
|
|
46
|
+
def inner(fn):
|
|
47
|
+
setattr(fn, _FLAVOR_ATTR, f)
|
|
48
|
+
return fn
|
|
49
|
+
|
|
50
|
+
return inner
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
mark_asyncio = mark_flavor(Flavor.ASYNCIO)
|
|
54
|
+
mark_anyio = mark_flavor(Flavor.ANYIO)
|
|
55
|
+
mark_trio = mark_flavor(Flavor.TRIO)
|
|
56
|
+
|
|
57
|
+
PACKAGE_FLAVORS: ta.MutableMapping[str, Flavor] = {
|
|
58
|
+
'anyio': Flavor.ANYIO,
|
|
59
|
+
'asyncio': Flavor.ASYNCIO,
|
|
60
|
+
'trio': Flavor.TRIO,
|
|
61
|
+
|
|
62
|
+
'sqlalchemy.ext.asyncio': Flavor.ASYNCIO,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
_MODULE_FLAVOR_CACHE: dict[str, Flavor | None] = {}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _get_module_flavor(p: str) -> Flavor | None:
|
|
69
|
+
try:
|
|
70
|
+
return _MODULE_FLAVOR_CACHE[p]
|
|
71
|
+
except KeyError:
|
|
72
|
+
pass
|
|
73
|
+
pf: Flavor | None = None
|
|
74
|
+
for cp, cf in PACKAGE_FLAVORS.items():
|
|
75
|
+
if p.startswith(cp) and (len(cp) == len(p) or p[len(cp)] == '.'):
|
|
76
|
+
pf = cf
|
|
77
|
+
break
|
|
78
|
+
_MODULE_FLAVOR_CACHE[p] = pf
|
|
79
|
+
return pf
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def get_flavor(obj: ta.Any, default: ta.Union[Flavor, type[_MISSING], None] = _MISSING) -> Flavor:
|
|
83
|
+
u = lang.unwrap_func(obj)
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
return getattr(u, _FLAVOR_ATTR)
|
|
87
|
+
except AttributeError:
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
if (mn := getattr(u, '__module__', None)) is not None:
|
|
91
|
+
if (pf := _get_module_flavor(mn)):
|
|
92
|
+
return pf
|
|
93
|
+
|
|
94
|
+
if default is not _MISSING:
|
|
95
|
+
return default # type: ignore
|
|
96
|
+
|
|
97
|
+
raise TypeError(f'not marked with flavor: {obj}')
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
##
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class Adapter(lang.Abstract):
|
|
104
|
+
_FROM_METHODS_BY_FLAVOR: ta.ClassVar[ta.Mapping[Flavor, str]] = {
|
|
105
|
+
Flavor.ANYIO: 'from_anyio',
|
|
106
|
+
Flavor.ASYNCIO: 'from_asyncio',
|
|
107
|
+
Flavor.TRIO: 'from_trio',
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
def adapt(self, fn, fl=None):
|
|
111
|
+
if fl is None:
|
|
112
|
+
fl = get_flavor(fn)
|
|
113
|
+
return getattr(self, self._FROM_METHODS_BY_FLAVOR[fl])(fn)
|
|
114
|
+
|
|
115
|
+
#
|
|
116
|
+
|
|
117
|
+
def from_anyio(self, fn):
|
|
118
|
+
return fn
|
|
119
|
+
|
|
120
|
+
@abc.abstractmethod
|
|
121
|
+
def from_asyncio(self, fn):
|
|
122
|
+
raise NotImplementedError
|
|
123
|
+
|
|
124
|
+
@abc.abstractmethod
|
|
125
|
+
def from_trio(self, fn):
|
|
126
|
+
raise NotImplementedError
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class AsyncioAdapter(Adapter):
|
|
130
|
+
def from_asyncio(self, fn):
|
|
131
|
+
return fn
|
|
132
|
+
|
|
133
|
+
def from_trio(self, fn):
|
|
134
|
+
check_trio_asyncio()
|
|
135
|
+
return trio_asyncio.trio_as_aio(fn)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class TrioAdapter(Adapter):
|
|
139
|
+
def from_asyncio(self, fn):
|
|
140
|
+
check_trio_asyncio()
|
|
141
|
+
return trio_asyncio.aio_as_trio(fn)
|
|
142
|
+
|
|
143
|
+
def from_trio(self, fn):
|
|
144
|
+
return fn
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
_ADAPTERS_BY_BACKEND: ta.Mapping[str, Adapter] = {
|
|
148
|
+
'asyncio': AsyncioAdapter(),
|
|
149
|
+
'trio': TrioAdapter(),
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def get_adapter() -> Adapter:
|
|
154
|
+
return _ADAPTERS_BY_BACKEND[sniffio.current_async_library()]
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def adapt(fn, fl=None):
|
|
158
|
+
return get_adapter().adapt(fn, fl)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def from_anyio(fn):
|
|
162
|
+
return get_adapter().from_anyio(fn)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def from_asyncio(fn):
|
|
166
|
+
return get_adapter().from_asyncio(fn)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def from_trio(fn):
|
|
170
|
+
return get_adapter().from_trio(fn)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
##
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@dc.dataclass(frozen=True)
|
|
177
|
+
class ContextManagerAdapter(ta.Generic[T]):
|
|
178
|
+
obj: ta.AsyncContextManager[T]
|
|
179
|
+
adapt: ta.Callable[[ta.Callable], ta.Callable]
|
|
180
|
+
|
|
181
|
+
async def __aenter__(self, *args: ta.Any, **kwargs: ta.Any) -> T:
|
|
182
|
+
return await self.adapt(self.obj.__aenter__)(*args, **kwargs)
|
|
183
|
+
|
|
184
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
185
|
+
return await self.adapt(self.obj.__aexit__)(exc_type, exc_val, exc_tb)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def adapt_context(obj):
|
|
189
|
+
return ContextManagerAdapter(obj, get_adapter().adapt)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def from_anyio_context(obj):
|
|
193
|
+
return ContextManagerAdapter(obj, get_adapter().from_anyio)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def from_asyncio_context(obj):
|
|
197
|
+
return ContextManagerAdapter(obj, get_adapter().from_asyncio)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def from_trio_context(obj):
|
|
201
|
+
return ContextManagerAdapter(obj, get_adapter().from_trio)
|
omlish/asyncs/futures.py
CHANGED
|
@@ -5,12 +5,13 @@ import typing as ta
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
T = ta.TypeVar('T')
|
|
8
|
+
P = ta.ParamSpec('P')
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
##
|
|
11
12
|
|
|
12
13
|
|
|
13
|
-
class
|
|
14
|
+
class FutureError(Exception, ta.Generic[T]):
|
|
14
15
|
|
|
15
16
|
def __init__(self, future: cf.Future, target: T | None = None) -> None:
|
|
16
17
|
super().__init__()
|
|
@@ -37,7 +38,7 @@ class FutureException(Exception, ta.Generic[T]):
|
|
|
37
38
|
__str__ = __repr__
|
|
38
39
|
|
|
39
40
|
|
|
40
|
-
class
|
|
41
|
+
class FutureTimeoutError(Exception):
|
|
41
42
|
pass
|
|
42
43
|
|
|
43
44
|
|
|
@@ -61,14 +62,14 @@ def wait_futures(
|
|
|
61
62
|
if cancel_on_exception:
|
|
62
63
|
for cancel_fut in not_done:
|
|
63
64
|
cancel_fut.cancel()
|
|
64
|
-
raise
|
|
65
|
+
raise FutureError(fut) from fut.exception()
|
|
65
66
|
|
|
66
67
|
not_done -= done
|
|
67
68
|
if not not_done:
|
|
68
69
|
return True
|
|
69
70
|
|
|
70
71
|
if time.time() >= (start + timeout_s):
|
|
71
|
-
raise
|
|
72
|
+
raise FutureTimeoutError
|
|
72
73
|
time.sleep(tick_interval_s)
|
|
73
74
|
|
|
74
75
|
return False
|
|
@@ -91,7 +92,7 @@ def wait_dependent_futures(
|
|
|
91
92
|
if fn in dependency_sets_by_fn[dep]:
|
|
92
93
|
raise Exception(f'Cyclic dependencies: {fn} <-> {dep}', fn, dep)
|
|
93
94
|
|
|
94
|
-
dependent_sets_by_fn:
|
|
95
|
+
dependent_sets_by_fn: dict[ta.Callable, set[ta.Callable]] = {fn: set() for fn in dependency_sets_by_fn}
|
|
95
96
|
for fn, deps in dependency_sets_by_fn.items():
|
|
96
97
|
for dep in deps:
|
|
97
98
|
dependent_sets_by_fn[dep].add(fn)
|
|
@@ -114,7 +115,7 @@ def wait_dependent_futures(
|
|
|
114
115
|
for fut in done:
|
|
115
116
|
if fut.exception():
|
|
116
117
|
cancel()
|
|
117
|
-
raise
|
|
118
|
+
raise FutureError(fut) from fut.exception()
|
|
118
119
|
|
|
119
120
|
fn = fns_by_fut[fut]
|
|
120
121
|
for dependent_fn in dependent_sets_by_fn.get(fn, set()):
|
|
@@ -128,11 +129,11 @@ def wait_dependent_futures(
|
|
|
128
129
|
|
|
129
130
|
if time.time() >= (start + timeout_s):
|
|
130
131
|
cancel()
|
|
131
|
-
raise
|
|
132
|
+
raise FutureTimeoutError
|
|
132
133
|
|
|
133
134
|
remaining_fns = {fn: deps for fn, deps in remaining_dep_sets_by_fn.items() if deps}
|
|
134
135
|
if remaining_fns:
|
|
135
|
-
raise Exception(f
|
|
136
|
+
raise Exception(f'Unfinished fns: {remaining_fns}', remaining_fns)
|
|
136
137
|
|
|
137
138
|
futs_by_fn = {fn: fut for fut, fn in fns_by_fut.items()}
|
|
138
139
|
return futs_by_fn
|
|
@@ -147,7 +148,7 @@ class ImmediateExecutor(cf.Executor):
|
|
|
147
148
|
super().__init__()
|
|
148
149
|
self._immediate_exceptions = immediate_exceptions
|
|
149
150
|
|
|
150
|
-
def submit(self, fn, *args, **kwargs):
|
|
151
|
+
def submit(self, fn: ta.Callable[P, T], /, *args: P.args, **kwargs: P.kwargs) -> cf.Future[T]:
|
|
151
152
|
future: ta.Any = cf.Future()
|
|
152
153
|
try:
|
|
153
154
|
result = fn(*args, **kwargs)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from .. import lang
|
|
5
|
+
|
|
6
|
+
if ta.TYPE_CHECKING:
|
|
7
|
+
import asyncio
|
|
8
|
+
import sniffio
|
|
9
|
+
import trio_asyncio
|
|
10
|
+
else:
|
|
11
|
+
asyncio = lang.proxy_import('asyncio')
|
|
12
|
+
sniffio = lang.proxy_import('sniffio')
|
|
13
|
+
trio_asyncio = lang.proxy_import('trio_asyncio')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def check_trio_asyncio() -> None:
|
|
17
|
+
if trio_asyncio.current_loop.get() is None:
|
|
18
|
+
raise RuntimeError('trio_asyncio loop not running')
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def with_trio_asyncio_loop(fn, *, wait=False):
|
|
22
|
+
@functools.wraps(fn)
|
|
23
|
+
async def inner(*args, **kwargs):
|
|
24
|
+
if trio_asyncio.current_loop.get() is not None:
|
|
25
|
+
await fn(*args, **kwargs)
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
if sniffio.current_async_library() != 'trio':
|
|
29
|
+
raise RuntimeError('trio loop not running')
|
|
30
|
+
|
|
31
|
+
loop: asyncio.BaseEventLoop
|
|
32
|
+
async with trio_asyncio.open_loop() as loop:
|
|
33
|
+
try:
|
|
34
|
+
await fn(*args, **kwargs)
|
|
35
|
+
finally:
|
|
36
|
+
if wait:
|
|
37
|
+
# FIXME: lol
|
|
38
|
+
while asyncio.all_tasks(loop):
|
|
39
|
+
await asyncio.sleep(.2)
|
|
40
|
+
|
|
41
|
+
return inner
|
omlish/c3.py
CHANGED
|
@@ -113,7 +113,7 @@ def mro(
|
|
|
113
113
|
abstract_c3_mros = [mro(base, abcs=abcs, get_bases=get_bases, is_subclass=is_subclass) for base in abstract_bases]
|
|
114
114
|
other_c3_mros = [mro(base, abcs=abcs, get_bases=get_bases, is_subclass=is_subclass) for base in other_bases]
|
|
115
115
|
return merge(
|
|
116
|
-
[[cls]] +
|
|
116
|
+
[[cls]] + # noqa
|
|
117
117
|
explicit_c3_mros + abstract_c3_mros + other_c3_mros +
|
|
118
118
|
[explicit_bases] + [abstract_bases] + [other_bases],
|
|
119
119
|
)
|
omlish/check.py
CHANGED
|
@@ -9,7 +9,7 @@ import typing as ta
|
|
|
9
9
|
T = ta.TypeVar('T')
|
|
10
10
|
SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
|
|
11
11
|
|
|
12
|
-
Message =
|
|
12
|
+
Message = str | ta.Callable[..., str | None] | None
|
|
13
13
|
|
|
14
14
|
_NONE_TYPE = type(None)
|
|
15
15
|
|
|
@@ -52,7 +52,7 @@ def _unpack_isinstance_spec(spec: ta.Any) -> tuple:
|
|
|
52
52
|
if not _isinstance(spec, tuple):
|
|
53
53
|
spec = (spec,)
|
|
54
54
|
if None in spec:
|
|
55
|
-
spec = tuple(filter(None, spec)) + (_NONE_TYPE,)
|
|
55
|
+
spec = tuple(filter(None, spec)) + (_NONE_TYPE,) # noqa
|
|
56
56
|
if ta.Any in spec:
|
|
57
57
|
spec = (object,)
|
|
58
58
|
return spec
|
|
@@ -206,7 +206,7 @@ def is_not(v: T, *os: ta.Any, msg: Message = None) -> T:
|
|
|
206
206
|
return v
|
|
207
207
|
|
|
208
208
|
|
|
209
|
-
def callable(v: T, msg: Message = None) -> T:
|
|
209
|
+
def callable(v: T, msg: Message = None) -> T: # noqa
|
|
210
210
|
if not _callable(v):
|
|
211
211
|
_raise(TypeError, 'Must be callable', msg, v)
|
|
212
212
|
return v # type: ignore
|
omlish/collections/_abc.py
CHANGED
omlish/collections/_io_abc.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# ruff: noqa: ANN204
|
|
2
|
+
|
|
1
3
|
class IOBase:
|
|
2
4
|
def seek(self, pos, whence=0): ...
|
|
3
5
|
|
|
@@ -16,7 +18,7 @@ class IOBase:
|
|
|
16
18
|
def writable(self): ...
|
|
17
19
|
|
|
18
20
|
@property
|
|
19
|
-
def closed(self): ...
|
|
21
|
+
def closed(self): ... # noqa
|
|
20
22
|
|
|
21
23
|
def __enter__(self): ...
|
|
22
24
|
|
|
@@ -64,7 +66,7 @@ class TextIOBase(IOBase):
|
|
|
64
66
|
|
|
65
67
|
def truncate(self, pos=None): ...
|
|
66
68
|
|
|
67
|
-
def readline(self): ...
|
|
69
|
+
def readline(self, size=-1): ...
|
|
68
70
|
|
|
69
71
|
def detach(self): ...
|
|
70
72
|
|
|
@@ -26,7 +26,7 @@ class _HashedSeq(list):
|
|
|
26
26
|
|
|
27
27
|
def __init__(
|
|
28
28
|
self,
|
|
29
|
-
tup:
|
|
29
|
+
tup: tuple,
|
|
30
30
|
hasher: ta.Callable[[ta.Any], int] = hash,
|
|
31
31
|
) -> None:
|
|
32
32
|
super().__init__()
|
|
@@ -34,19 +34,19 @@ class _HashedSeq(list):
|
|
|
34
34
|
self[:] = tup
|
|
35
35
|
self.hash_value = hasher(tup)
|
|
36
36
|
|
|
37
|
-
def __hash__(self):
|
|
37
|
+
def __hash__(self) -> int: # type: ignore
|
|
38
38
|
return self.hash_value
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
def _make_key(
|
|
42
|
-
args:
|
|
43
|
-
kwargs:
|
|
42
|
+
args: tuple,
|
|
43
|
+
kwargs: dict[str, ta.Any],
|
|
44
44
|
typed: bool,
|
|
45
45
|
kwd_mark=(object(),),
|
|
46
46
|
fasttypes=frozenset([int, str, frozenset, type(None)]),
|
|
47
|
-
tuple=tuple,
|
|
48
|
-
type=type,
|
|
49
|
-
len=len,
|
|
47
|
+
tuple=tuple, # noqa
|
|
48
|
+
type=type, # noqa
|
|
49
|
+
len=len, # noqa
|
|
50
50
|
) -> ta.Any:
|
|
51
51
|
key = args
|
|
52
52
|
if kwargs:
|
|
@@ -109,7 +109,7 @@ class _CacheDescriptor:
|
|
|
109
109
|
def _build(self, fn: ta.Callable, cache: Cache):
|
|
110
110
|
def miss(key, result):
|
|
111
111
|
if isinstance(result, Ignore):
|
|
112
|
-
return result._value
|
|
112
|
+
return result._value # noqa
|
|
113
113
|
else:
|
|
114
114
|
cache[key] = result
|
|
115
115
|
return result
|